diff --git a/game/graphics/opengl_renderer/BucketRenderer.h b/game/graphics/opengl_renderer/BucketRenderer.h index 05ec7cf726..79efc78e86 100644 --- a/game/graphics/opengl_renderer/BucketRenderer.h +++ b/game/graphics/opengl_renderer/BucketRenderer.h @@ -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 fog_color = math::Vector{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; }; /*! diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index b3e6e2980d..10beeb2593 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -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 texture_pool, std::shared_ptr 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("multicam", BucketCategory::OTHER, + BucketId::BUCKET2); // 3 : SKY_DRAW init_bucket_renderer("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. } diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.h b/game/graphics/opengl_renderer/OpenGLRenderer.h index caabb961d4..0c8198c402 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.h +++ b/game/graphics/opengl_renderer/OpenGLRenderer.h @@ -103,6 +103,7 @@ class OpenGLRenderer { } SharedRenderState m_render_state; + std::vector m_skip_multi_buckets; Profiler m_profiler; SmallProfiler m_small_profiler; SubtitleEditor* m_subtitle_editor = nullptr; diff --git a/game/graphics/opengl_renderer/ShadowRenderer.cpp b/game/graphics/opengl_renderer/ShadowRenderer.cpp index 8f3fa3f646..6fef541711 100644 --- a/game/graphics/opengl_renderer/ShadowRenderer.cpp +++ b/game/graphics/opengl_renderer/ShadowRenderer.cpp @@ -2,6 +2,8 @@ #include +#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); } { diff --git a/game/graphics/opengl_renderer/background/TFragment.cpp b/game/graphics/opengl_renderer/background/TFragment.cpp index a4efc78286..79c2a9eff0 100644 --- a/game/graphics/opengl_renderer/background/TFragment.cpp +++ b/game/graphics/opengl_renderer/background/TFragment.cpp @@ -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) { diff --git a/game/graphics/opengl_renderer/background/Tie3.cpp b/game/graphics/opengl_renderer/background/Tie3.cpp index 51dc589451..3194464760 100644 --- a/game/graphics/opengl_renderer/background/Tie3.cpp +++ b/game/graphics/opengl_renderer/background/Tie3.cpp @@ -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 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++) { diff --git a/game/graphics/opengl_renderer/background/background_common.cpp b/game/graphics/opengl_renderer/background/background_common.cpp index a61604fac0..6e2c25fbe2 100644 --- a/game/graphics/opengl_renderer/background/background_common.cpp +++ b/game/graphics/opengl_renderer/background/background_common.cpp @@ -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]); diff --git a/game/graphics/opengl_renderer/buckets.h b/game/graphics/opengl_renderer/buckets.h index 5601dab09e..f6ba3cfe34 100644 --- a/game/graphics/opengl_renderer/buckets.h +++ b/game/graphics/opengl_renderer/buckets.h @@ -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, diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 9c7c3032c2..90d8276da4 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -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]; diff --git a/game/graphics/opengl_renderer/sprite/Sprite3.cpp b/game/graphics/opengl_renderer/sprite/Sprite3.cpp index 41e043a829..00c75fcb63 100644 --- a/game/graphics/opengl_renderer/sprite/Sprite3.cpp +++ b/game/graphics/opengl_renderer/sprite/Sprite3.cpp @@ -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()); diff --git a/goal_src/jak1/engine/draw/drawable.gc b/goal_src/jak1/engine/draw/drawable.gc index b3a38c5e73..6021b2fd39 100644 --- a/goal_src/jak1/engine/draw/drawable.gc +++ b/goal_src/jak1/engine/draw/drawable.gc @@ -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.