splitscreen hack

w/splitscreen-hack
water 2023-11-28 20:19:08 -05:00
parent 72ee35ada2
commit 2e6bbcbce8
11 changed files with 250 additions and 22 deletions

View File

@ -40,7 +40,7 @@ struct SharedRenderState {
u32 offset_of_s7;
bool use_sky_cpu = true;
bool use_occlusion_culling = true;
bool use_occlusion_culling = false; // bad for multi
math::Vector<u8, 4> fog_color = math::Vector<u8, 4>{0, 0, 0, 0};
float fog_intensity = 1.f;
bool no_multidraw = false;
@ -87,6 +87,23 @@ struct SharedRenderState {
u64 frame_idx = 0;
bool stencil_dirty = false;
// camera matrices from GOAL:
struct MultiCameraGoalMatrices {
// the inv-rot matrix from GOAL. (pose of the primary camera in the world)
math::Matrix4f w_T_cpri;
// the inv-rot matrix of extra cameras from GOAL. (pose of extra camera in the world)
math::Matrix4f w_T_cextra[3];
} multi_camera_goal;
// per-camera info for multi-camera rendering.
struct Cam {
math::Matrix4f w_T_wprime; // for non merc
math::Matrix4f pri_cam_T_cam;
};
Cam cameras[4];
int camera_idx = 0;
};
/*!

View File

@ -2,6 +2,7 @@
#include "common/goal_constants.h"
#include "common/log/log.h"
#include "common/math/geometry.h"
#include "common/util/FileUtil.h"
#include "game/graphics/opengl_renderer/BlitDisplays.h"
@ -66,12 +67,61 @@ void GLAPIENTRY opengl_error_callback(GLenum source,
}
}
class MultiCameraMatrixGrabber : public BucketRenderer {
public:
MultiCameraMatrixGrabber(const std::string& name, int my_id) : BucketRenderer(name, my_id) {}
void draw_debug_window() override {}
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode&) override {
while (dma.current_tag_offset() != render_state->next_bucket) {
auto xfer = dma.read_and_advance();
if (xfer.size_bytes == sizeof(SharedRenderState::MultiCameraGoalMatrices) &&
render_state->camera_idx == 0) {
// grab camera data from GOAL:
memcpy(&render_state->multi_camera_goal, xfer.data,
sizeof(SharedRenderState::MultiCameraGoalMatrices));
math::Matrix4f cpri_T_w = inverse(render_state->multi_camera_goal.w_T_cpri);
// set up some helpful matrices:
for (int i = 0; i < 4; i++) {
auto& cam = render_state->cameras[i];
// what GOAL calls "inverse camera rot", but is really w_T_c
const auto& w_T_c = i == 0 ? render_state->multi_camera_goal.w_T_cpri
: render_state->multi_camera_goal.w_T_cextra[i - 1];
cam.w_T_wprime = render_state->multi_camera_goal.w_T_cpri * inverse(w_T_c);
cam.pri_cam_T_cam = cpri_T_w * w_T_c;
}
}
}
}
};
OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool,
std::shared_ptr<Loader> loader,
GameVersion version)
: m_render_state(texture_pool, loader, version),
m_collide_renderer(version),
m_version(version) {
m_version(version),
m_skip_multi_buckets((int)jak2::BucketId::MAX_BUCKETS) {
for (auto bucket : {
jak1::BucketId::OCEAN_MID_AND_FAR, // too lazy to fix
jak1::BucketId::OCEAN_NEAR, // too lazy to fix
jak1::BucketId::SKY_DRAW, // needs goal fix
jak1::BucketId::TFRAG_TEX_LEVEL0, // texture
jak1::BucketId::TFRAG_TEX_LEVEL1, // texture
jak1::BucketId::SHRUB_TEX_LEVEL0, // texture
jak1::BucketId::SHRUB_TEX_LEVEL1, // texture
jak1::BucketId::ALPHA_TEX_LEVEL0, // texture
jak1::BucketId::ALPHA_TEX_LEVEL1, // texture
jak1::BucketId::PRIS_TEX_LEVEL0, // texture
jak1::BucketId::PRIS_TEX_LEVEL1, // texture
jak1::BucketId::WATER_TEX_LEVEL0, // texture
jak1::BucketId::WATER_TEX_LEVEL1, // texture
jak1::BucketId::PRE_SPRITE_TEX, // texture
}) {
m_skip_multi_buckets[(int)bucket] = true;
}
// requires OpenGL 4.3
#ifndef __APPLE__
// setup OpenGL errors
@ -341,6 +391,8 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
// 0 : ??
// 1 : ??
// 2 : ??
init_bucket_renderer<MultiCameraMatrixGrabber>("multicam", BucketCategory::OTHER,
BucketId::BUCKET2);
// 3 : SKY_DRAW
init_bucket_renderer<SkyRenderer>("sky", BucketCategory::OTHER, BucketId::SKY_DRAW);
// 4 : OCEAN_MID_AND_FAR
@ -983,13 +1035,28 @@ void OpenGLRenderer::dispatch_buckets_jak1(DmaFollower dma,
ASSERT(dma.current_tag_offset() == m_render_state.next_bucket);
m_render_state.next_bucket += 16;
GLint old_viewport[4];
glGetIntegerv(GL_VIEWPORT, old_viewport);
// loop over the buckets!
for (size_t bucket_id = 0; bucket_id < m_bucket_renderers.size(); bucket_id++) {
auto& renderer = m_bucket_renderers[bucket_id];
auto bucket_prof = prof.make_scoped_child(renderer->name_and_id());
g_current_renderer = renderer->name_and_id();
// lg::info("Render: {} start", g_current_renderer);
// render primary camera:
m_render_state.camera_idx = 0;
auto dma_copy = dma;
glViewport(old_viewport[0], old_viewport[1], old_viewport[2] / 2, old_viewport[3] / 2);
renderer->render(dma, &m_render_state, bucket_prof);
if (!m_skip_multi_buckets[bucket_id]) {
m_render_state.camera_idx = 1;
glViewport(old_viewport[0] + old_viewport[2] / 2, old_viewport[1] + old_viewport[3] / 2,
old_viewport[2] / 2, old_viewport[3] / 2);
renderer->render(dma_copy, &m_render_state, bucket_prof);
}
if (sync_after_buckets) {
auto pp = scoped_prof("finish");
glFinish();
@ -1009,6 +1076,8 @@ void OpenGLRenderer::dispatch_buckets_jak1(DmaFollower dma,
}
}
glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]);
// TODO ending data.
}

View File

@ -103,6 +103,7 @@ class OpenGLRenderer {
}
SharedRenderState m_render_state;
std::vector<bool> m_skip_multi_buckets;
Profiler m_profiler;
SmallProfiler m_small_profiler;
SubtitleEditor* m_subtitle_editor = nullptr;

View File

@ -2,6 +2,8 @@
#include <cfloat>
#include "common/math/geometry.h"
#include "third-party/imgui/imgui.h"
ShadowRenderer::ShadowRenderer(const std::string& name, int my_id) : BucketRenderer(name, my_id) {
@ -237,7 +239,15 @@ void ShadowRenderer::render(DmaFollower& dma,
ASSERT(v1.kind == VifCode::Kind::UNPACK_V4_32);
ASSERT(v1.immediate == Vu1Data::MATRIX);
ASSERT(v1.num == 4);
memcpy(m_vu_data + v1.immediate, constants.data, v1.num * 16);
// such disgusting hacks
math::Matrix4f p;
memcpy(p.data(), constants.data, v1.num * 16);
auto& this_cam = render_state->cameras[render_state->camera_idx];
auto new_cam = (p * inverse(this_cam.pri_cam_T_cam));
memcpy(m_vu_data + v1.immediate, new_cam.data(), 16 * 4);
// memcpy(m_vu_data + v1.immediate, constants.data, v1.num * 16);
}
{

View File

@ -447,8 +447,11 @@ void TFragment::render_tree(int geom,
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(UINT32_MAX);
cull_check_all_slow(settings.camera.planes, tree.vis->vis_nodes, settings.occlusion_culling,
m_cache.vis_temp.data());
// cull_check_all_slow(settings.camera.planes, tree.vis->vis_nodes, settings.occlusion_culling,
// m_cache.vis_temp.data());
for (size_t i = 0; i < tree.vis->vis_nodes.size(); i++) {
m_cache.vis_temp.data()[i] = 1;
}
u32 total_tris;
if (render_state->no_multidraw) {

View File

@ -448,9 +448,12 @@ void Tie3::setup_tree(int idx,
}
if (!m_debug_all_visible) {
// need culling data
cull_check_all_slow(settings.camera.planes, tree.vis->vis_nodes, settings.occlusion_culling,
tree.vis_temp.data());
for (size_t i = 0; i < tree.vis->vis_nodes.size(); i++) {
tree.vis_temp.data()[i] = 1;
}
// cull_check_all_slow(settings.camera.planes, tree.vis->vis_nodes,
// settings.occlusion_culling,
// tree.vis_temp.data());
}
u32 num_tris = 0;
@ -504,8 +507,24 @@ void set_uniform(GLuint uniform, const math::Vector4f& val) {
}
} // namespace
void init_etie_cam_uniforms(const EtieUniforms& uniforms, const GoalBackgroundCameraData& data) {
glUniformMatrix4fv(uniforms.cam_no_persp, 1, GL_FALSE, data.rot[0].data());
namespace {
math::Matrix4f matrix_from_col_vectors(const math::Vector4f* vectors) {
math::Matrix4f ret;
for (int c = 0; c < 4; c++) {
for (int r = 0; r < 4; r++) {
ret(r, c) = vectors[c][r];
}
}
return ret;
}
} // namespace
void init_etie_cam_uniforms(const EtieUniforms& uniforms,
const GoalBackgroundCameraData& data,
SharedRenderState* render_state) {
auto& this_cam = render_state->cameras[render_state->camera_idx];
math::Matrix4f s_T_w = matrix_from_col_vectors(data.rot);
glUniformMatrix4fv(uniforms.cam_no_persp, 1, GL_FALSE, (s_T_w * this_cam.w_T_wprime).data());
math::Vector4f perspective[2];
float inv_fog = 1.f / data.fog[0];
@ -550,7 +569,7 @@ void Tie3::draw_matching_draws_for_tree(int idx,
if (use_envmap) {
// if we use envmap, use the envmap-style math for the base draw to avoid rounding issue.
init_etie_cam_uniforms(m_etie_base_uniforms, m_common_data.settings.camera);
init_etie_cam_uniforms(m_etie_base_uniforms, m_common_data.settings.camera, render_state);
}
glBindVertexArray(tree.vao);
@ -654,7 +673,7 @@ void Tie3::envmap_second_pass_draw(const Tree& tree,
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
render_state->no_multidraw ? tree.single_draw_index_buffer : tree.index_buffer);
init_etie_cam_uniforms(m_etie_uniforms, m_common_data.settings.camera);
init_etie_cam_uniforms(m_etie_uniforms, m_common_data.settings.camera, render_state);
set_uniform(m_etie_uniforms.envmap_tod_tint, m_common_data.envmap_color);
int last_texture = -1;
@ -874,6 +893,8 @@ void do_wind_math(u16 wind_idx,
// sd s2, 0(s5)
}
namespace {} // namespace
void Tie3::render_tree_wind(int idx,
int geom,
const TfragRenderSettings& settings,
@ -887,10 +908,17 @@ void Tie3::render_tree_wind(int idx,
// note: this isn't the most efficient because we might compute wind matrices for invisible
// instances. TODO: add vis ids to the instance info to avoid this
memset(tree.wind_matrix_cache.data(), 0, sizeof(float) * 16 * tree.wind_matrix_cache.size());
auto& cam_bad = settings.camera.camera;
auto& this_cam = render_state->cameras[render_state->camera_idx];
math::Matrix4f s_T_w = matrix_from_col_vectors(settings.camera.camera);
auto hacked_cam = s_T_w * this_cam.w_T_wprime;
// auto& cam_bad = settings.camera.camera;
std::array<math::Vector4f, 4> cam;
for (int i = 0; i < 4; i++) {
cam[i] = cam_bad[i];
for (int c = 0; c < 4; c++) {
for (int r = 0; r < 4; r++) {
cam[c][r] = hacked_cam(r, c);
}
}
for (size_t inst_id = 0; inst_id < tree.instance_info->size(); inst_id++) {

View File

@ -1,6 +1,7 @@
#include "background_common.h"
#include "common/math/geometry.h"
#ifdef __aarch64__
#include "third-party/sse2neon/sse2neon.h"
@ -171,6 +172,18 @@ DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode, Sh
return draw_settings;
}
namespace {
math::Matrix4f matrix_from_col_vectors(const math::Vector4f* vectors) {
math::Matrix4f ret;
for (int c = 0; c < 4; c++) {
for (int r = 0; r < 4; r++) {
ret(r, c) = vectors[c][r];
}
}
return ret;
}
}
void first_tfrag_draw_setup(const TfragRenderSettings& settings,
SharedRenderState* render_state,
ShaderId shader) {
@ -180,8 +193,10 @@ void first_tfrag_draw_setup(const TfragRenderSettings& settings,
glUniform1i(glGetUniformLocation(id, "gfx_hack_no_tex"), Gfx::g_global_settings.hack_no_tex);
glUniform1i(glGetUniformLocation(id, "decal"), false);
glUniform1i(glGetUniformLocation(id, "tex_T0"), 0);
auto& this_cam = render_state->cameras[render_state->camera_idx];
math::Matrix4f s_T_w = matrix_from_col_vectors(settings.camera.camera);
glUniformMatrix4fv(glGetUniformLocation(id, "camera"), 1, GL_FALSE,
settings.camera.camera[0].data());
(s_T_w * this_cam.w_T_wprime).data());
glUniform4f(glGetUniformLocation(id, "hvdf_offset"), settings.camera.hvdf_off[0],
settings.camera.hvdf_off[1], settings.camera.hvdf_off[2],
settings.camera.hvdf_off[3]);

View File

@ -6,7 +6,7 @@ namespace jak1 {
enum class BucketId {
BUCKET0 = 0,
BUCKET1 = 1,
// 2
BUCKET2 = 2,
SKY_DRAW = 3,
OCEAN_MID_AND_FAR = 4,
TFRAG_TEX_LEVEL0 = 5,

View File

@ -1,5 +1,8 @@
#include "Merc2.h"
#include "common/math/geometry.h"
#ifdef __aarch64__
#include "third-party/sse2neon/sse2neon.h"
#else
@ -840,6 +843,18 @@ void set_uniform(GLuint uniform, const math::Vector4f& val) {
}
} // namespace
namespace {
math::Matrix4f matrix_from_col_vectors(const math::Vector4f* vectors) {
math::Matrix4f ret;
for (int c = 0; c < 4; c++) {
for (int r = 0; r < 4; r++) {
ret(r, c) = vectors[c][r];
}
}
return ret;
}
} // namespace
void Merc2::handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state) {
auto first = dma.read_and_advance();
@ -882,16 +897,18 @@ void Merc2::handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state)
// 8 qw's of low memory data
memcpy(&m_low_memory, first.data + 16, sizeof(LowMemory));
auto& this_cam = render_state->cameras[render_state->camera_idx];
math::Matrix4f p = matrix_from_col_vectors(m_low_memory.perspective);
auto new_cam = (p * inverse(this_cam.pri_cam_T_cam));
switch_to_merc2(render_state);
set_uniform(m_merc_uniforms.hvdf_offset, m_low_memory.hvdf_offset);
set_uniform(m_merc_uniforms.fog, m_low_memory.fog);
glUniformMatrix4fv(m_merc_uniforms.perspective_matrix, 1, GL_FALSE,
&m_low_memory.perspective[0].x());
glUniformMatrix4fv(m_merc_uniforms.perspective_matrix, 1, GL_FALSE, new_cam.data());
switch_to_emerc(render_state);
set_uniform(m_emerc_uniforms.hvdf_offset, m_low_memory.hvdf_offset);
set_uniform(m_emerc_uniforms.fog, m_low_memory.fog);
glUniformMatrix4fv(m_emerc_uniforms.perspective_matrix, 1, GL_FALSE,
&m_low_memory.perspective[0].x());
glUniformMatrix4fv(m_emerc_uniforms.perspective_matrix, 1, GL_FALSE, new_cam.data());
// 1 qw with another 4 vifcodes.
u32 vifcode_final_data[4];

View File

@ -232,8 +232,12 @@ void Sprite3::render_2d_group0(DmaFollower& dma,
// glUniform4fv(glGetUniformLocation(shid, "hmge_scale"), 1, m_frame_data.hmge_scale.data());
glUniform1f(glGetUniformLocation(shid, "deg_to_rad"), m_frame_data.deg_to_rad);
glUniform1f(glGetUniformLocation(shid, "inv_area"), m_frame_data.inv_area);
auto& this_cam = render_state->cameras[render_state->camera_idx];
glUniformMatrix4fv(glGetUniformLocation(shid, "camera"), 1, GL_FALSE,
m_3d_matrix_data.camera.data());
// m_3d_matrix_data.camera
(m_3d_matrix_data.camera * this_cam.w_T_wprime)
.data());
glUniform4fv(glGetUniformLocation(shid, "xy_array"), 8, m_frame_data.xy_array[0].data());
glUniform4fv(glGetUniformLocation(shid, "xyz_array"), 4, m_frame_data.xyz_array[0].data());
glUniform4fv(glGetUniformLocation(shid, "st_array"), 4, m_frame_data.st_array[0].data());

View File

@ -904,6 +904,16 @@
(define *generic-effect-mode* 0)
;; This contains the camera matrices. There must be one "primary camera" that matches *math-camera*. But the rest of the cameras are up to you.
(deftype multi-camera (structure)
((primary-camera matrix :inline)
(extra-cameras matrix 3 :inline)
)
)
(define *multi-camera* (new 'global 'multi-camera))
(defun real-main-draw-hook ()
"The main drawing function. This dispatches all renderers and is called directly from the display-loop
in main.gc"
@ -937,6 +947,60 @@
(reset! *dma-mem-usage*)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Multi-camera mode: camera placement.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; set primary camera to the normal camera.
(matrix-copy! (-> *multi-camera* primary-camera)
(-> *math-camera* inv-camera-rot)
)
;; set secondary camera to some example different camera. This puts a camera 10 meters above the default, and
;; point it at jak directly:
(let ((cam-pos (vector-copy! (new-stack-vector0) (-> *math-camera* inv-camera-rot vector 3))))
;; shift up by 10 meters
(+! (-> cam-pos y) (meters 5))
;; dir from camera to jak
(let* ((jak-dir (vector-! (new-stack-vector0)
(if *target* (-> *target* root trans)
(new-stack-vector0))
cam-pos
))
(mat (-> *multi-camera* extra-cameras 0))
)
;; create rotation matrix to point camera at jak
(forward-down->inv-matrix
mat
jak-dir
(new 'static 'vector :y -1.))
;; set position.
(vector-copy! (-> mat vector 3) cam-pos)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Multi-camera mode: send data to C++
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf))
(the bucket-id 2))
(dma-buffer-add-cnt-vif2
buf
(/ (size-of multi-camera) 16)
(the vif-tag 0)
(the vif-tag 0)
)
;(&+! (-> buf base) 16)
(mem-copy! (-> buf base)
(the pointer *multi-camera*)
(size-of multi-camera)
)
(&+! (-> buf base) (size-of multi-camera))
)
;; todo shrub matrix
;; initialize dma buckets that are generic sinks.