From 753d8257be82ba3917ce0326806967f67bcc252c Mon Sep 17 00:00:00 2001 From: water111 Date: Mon, 16 Jun 2025 22:28:35 -0400 Subject: [PATCH] single sided tris working --- common/custom_data/TFrag3Data.cpp | 13 +- common/custom_data/Tfrag3Data.h | 24 +- decompiler/level_extractor/extract_shadow.cpp | 98 ++++---- game/CMakeLists.txt | 1 + .../opengl_renderer/foreground/Shadow3.cpp | 212 +++++++++++----- .../opengl_renderer/foreground/Shadow3.h | 14 ++ .../opengl_renderer/foreground/Shadow3CPU.cpp | 230 ++++++++++++++++++ .../opengl_renderer/foreground/Shadow3CPU.h | 45 ++++ .../opengl_renderer/loader/Loader.cpp | 1 - .../opengl_renderer/loader/LoaderStages.cpp | 21 -- .../opengl_renderer/loader/LoaderStages.h | 1 - game/graphics/opengl_renderer/loader/common.h | 1 - .../opengl_renderer/shaders/shadow3.vert | 33 +-- 13 files changed, 536 insertions(+), 158 deletions(-) create mode 100644 game/graphics/opengl_renderer/foreground/Shadow3CPU.cpp create mode 100644 game/graphics/opengl_renderer/foreground/Shadow3CPU.h diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index 9b3a887216..754e85b67d 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -590,15 +590,17 @@ void MercModelGroup::serialize(Serializer& ser) { void ShadowModel::serialize(Serializer& ser) { ser.from_str(&name); ser.from_ptr(&max_bones); - ser.from_ptr(&single_tris); - ser.from_ptr(&double_tris); - ser.from_ptr(&single_edges); - ser.from_ptr(&double_edges); + ser.from_ptr(&first_vertex); + ser.from_ptr(&num_one_bone_vertices); + ser.from_ptr(&num_two_bone_vertices); + ser.from_pod_vector(&single_tris); + ser.from_pod_vector(&double_tris); + ser.from_pod_vector(&single_edges); + ser.from_pod_vector(&double_edges); } void ShadowModelGroup::serialize(Serializer& ser) { ser.from_pod_vector(&vertices); - ser.from_pod_vector(&indices); if (ser.is_saving()) { ser.save(models.size()); } else { @@ -794,7 +796,6 @@ void Hfragment::memory_usage(tfrag3::MemoryUsageTracker* tracker) const { void ShadowModelGroup::memory_usage(MemoryUsageTracker* tracker) const { tracker->add(SHADOW_VERTS, vertices.size() * sizeof(ShadowVertex)); - tracker->add(SHADOW_INDEX, indices.size() * sizeof(u32)); } void Level::memory_usage(MemoryUsageTracker* tracker) const { diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index 907f86e3c3..83844e5502 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -623,23 +623,35 @@ struct ShadowVertex { u8 mats[2]; u8 flags; }; +static_assert(sizeof(ShadowVertex) == 20); + +struct ShadowTri { + u8 verts[3]; +}; + +struct ShadowEdge { + u8 ind[2]; + u8 tri[2]; +}; struct ShadowModel { + static constexpr int kMaxVertices = 254; + static constexpr int kMaxTris = 254; std::string name; u32 max_bones; - struct Run { - u32 first_index; - u32 count; - }; - Run single_tris, double_tris, single_edges, double_edges; + std::vector single_tris, double_tris; + std::vector single_edges, double_edges; + + u32 first_vertex; + u32 num_one_bone_vertices; + u32 num_two_bone_vertices; void serialize(Serializer& ser); }; struct ShadowModelGroup { std::vector vertices; - std::vector indices; std::vector models; void serialize(Serializer& ser); void memory_usage(MemoryUsageTracker* tracker) const; diff --git a/decompiler/level_extractor/extract_shadow.cpp b/decompiler/level_extractor/extract_shadow.cpp index 546dd7565e..f55c503be3 100644 --- a/decompiler/level_extractor/extract_shadow.cpp +++ b/decompiler/level_extractor/extract_shadow.cpp @@ -276,6 +276,41 @@ std::vector convert_vertices(const ShadowData& data) { return result; } +tfrag3::ShadowTri convert_tri(const ShadowTri& tri) { + tfrag3::ShadowTri result; + for (int i = 0; i < 3; i++) { + result.verts[i] = tri.verts[i]; + } + return result; +} + +tfrag3::ShadowEdge convert_edge(const ShadowEdge& edge) { + tfrag3::ShadowEdge result; + for (int i = 0; i < 2; i++) { + result.ind[i] = edge.ind[i]; + result.tri[i] = edge.tri[i]; + } + return result; +} + +std::vector convert_tris(const std::vector& tris) { + std::vector result; + result.reserve(tris.size()); + for (auto& tri : tris) { + result.push_back(convert_tri(tri)); + } + return result; +} + +std::vector convert_edges(const std::vector& edges) { + std::vector result; + result.reserve(edges.size()); + for (auto& edge : edges) { + result.push_back(convert_edge(edge)); + } + return result; +} + void extract_shadow(const ObjectFileData& ag_data, const DecompilerTypeSystem& dts, tfrag3::Level& out, @@ -297,8 +332,22 @@ void extract_shadow(const ObjectFileData& ag_data, for (auto loc : geo_locations) { const ShadowData data = extract_shadow_data(ag_data.linked_data, dts, loc); + auto& model = sd.models.emplace_back(); + model.name = data.name; + model.max_bones = data.num_joints; + model.single_tris = convert_tris(data.single_tris); + model.double_tris = convert_tris(data.double_tris); + model.single_edges = convert_edges(data.single_edges); + model.double_edges = convert_edges(data.double_edges); + const u32 vertex_offset = sd.vertices.size(); - const u32 num_vertices = data.one_bone_vertices.size() + data.two_bone_vertices.size(); + + model.first_vertex = vertex_offset; + model.num_one_bone_vertices = data.one_bone_vertices.size(); + model.num_two_bone_vertices = data.two_bone_vertices.size(); + ASSERT(model.num_one_bone_vertices + model.num_two_bone_vertices <= + tfrag3::ShadowModel::kMaxVertices); + ASSERT(model.single_tris.size() + model.double_tris.size() <= tfrag3::ShadowModel::kMaxTris); // insert top vertices auto vertices = convert_vertices(data); @@ -310,53 +359,6 @@ void extract_shadow(const ObjectFileData& ag_data, } sd.vertices.insert(sd.vertices.end(), vertices.begin(), vertices.end()); - auto& model = sd.models.emplace_back(); - model.name = data.name; - model.max_bones = data.num_joints; - - // single triangles - model.single_tris.first_index = sd.indices.size(); - for (auto& stri : data.single_tris) { - sd.indices.push_back(static_cast(stri.verts[0]) + vertex_offset); - sd.indices.push_back(static_cast(stri.verts[1]) + vertex_offset); - sd.indices.push_back(static_cast(stri.verts[2]) + vertex_offset); - } - model.single_tris.count = sd.indices.size() - model.single_tris.first_index; - - // double triangles - model.double_tris.first_index = sd.indices.size(); - for (auto& dtri : data.double_tris) { - sd.indices.push_back(static_cast(dtri.verts[0]) + vertex_offset + num_vertices); - sd.indices.push_back(static_cast(dtri.verts[1]) + vertex_offset + num_vertices); - sd.indices.push_back(static_cast(dtri.verts[2]) + vertex_offset + num_vertices); - } - model.double_tris.count = sd.indices.size() - model.double_tris.first_index; - - // single edges - model.single_edges.first_index = sd.indices.size(); - for (auto& se : data.single_edges) { - sd.indices.push_back(static_cast(se.ind[0]) + vertex_offset + num_vertices); - sd.indices.push_back(static_cast(se.ind[0]) + vertex_offset); - sd.indices.push_back(static_cast(se.ind[1]) + vertex_offset + num_vertices); - - sd.indices.push_back(static_cast(se.ind[1]) + vertex_offset + num_vertices); - sd.indices.push_back(static_cast(se.ind[0]) + vertex_offset); - sd.indices.push_back(static_cast(se.ind[1]) + vertex_offset); - } - model.single_edges.count = sd.indices.size() - model.single_edges.first_index; - - model.double_edges.first_index = sd.indices.size(); - for (auto& se : data.double_edges) { - sd.indices.push_back(static_cast(se.ind[0]) + vertex_offset + num_vertices); - sd.indices.push_back(static_cast(se.ind[0]) + vertex_offset); - sd.indices.push_back(static_cast(se.ind[1]) + vertex_offset + num_vertices); - - sd.indices.push_back(static_cast(se.ind[1]) + vertex_offset + num_vertices); - sd.indices.push_back(static_cast(se.ind[0]) + vertex_offset); - sd.indices.push_back(static_cast(se.ind[1]) + vertex_offset); - } - model.double_edges.count = sd.indices.size() - model.double_edges.first_index; - if (dump_level) { auto file_path = file_util::get_file_path( {"debug_out/shadow", fmt::format("{}_{}.ply", ag_data.name_in_dgo, i)}); diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index a02c504612..a6ffd03b13 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -56,6 +56,7 @@ set(RUNTIME_SOURCE graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp graphics/opengl_renderer/foreground/Shadow2.cpp graphics/opengl_renderer/foreground/Shadow3.cpp + graphics/opengl_renderer/foreground/Shadow3CPU.cpp graphics/opengl_renderer/loader/Loader.cpp graphics/opengl_renderer/loader/LoaderStages.cpp graphics/opengl_renderer/ocean/CommonOceanRenderer.cpp diff --git a/game/graphics/opengl_renderer/foreground/Shadow3.cpp b/game/graphics/opengl_renderer/foreground/Shadow3.cpp index 09d5e5d49d..4370f3e7ee 100644 --- a/game/graphics/opengl_renderer/foreground/Shadow3.cpp +++ b/game/graphics/opengl_renderer/foreground/Shadow3.cpp @@ -6,6 +6,9 @@ Shadow3::Shadow3(ShaderLibrary& shaders) { glGenVertexArrays(1, &m_opengl.vao); glBindVertexArray(m_opengl.vao); + glGenBuffers(1, &m_opengl.indices); + glGenBuffers(1, &m_opengl.debug_verts); + glGenBuffers(1, &m_opengl.bones_buffer); glBindBuffer(GL_UNIFORM_BUFFER, m_opengl.bones_buffer); @@ -43,13 +46,14 @@ Shadow3::Shadow3(ShaderLibrary& shaders) { Shadow3::~Shadow3() { glDeleteBuffers(1, &m_opengl.bones_buffer); + glDeleteBuffers(1, &m_opengl.indices); + glDeleteBuffers(1, &m_opengl.debug_verts); glDeleteVertexArrays(1, &m_opengl.vao); } void Shadow3::setup_for_level(SharedRenderState* render_state, const LevelData* level_data) { glBindVertexArray(m_opengl.vao); glBindBuffer(GL_ARRAY_BUFFER, level_data->shadow_vertices); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, level_data->shadow_indices); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); @@ -95,11 +99,25 @@ void set_uniform(GLuint uniform, const math::Vector3f& val) { void set_uniform(GLuint uniform, const math::Vector4f& val) { glUniform4f(uniform, val.x(), val.y(), val.z(), val.w()); } + } // namespace void Shadow3::draw_model(SharedRenderState* render_state, ShadowRequest* request, ScopedProfilerNode& prof) { + ShadowCPUInput input{ + .origin = request->origin, + .top_plane = request->top_plane, + .bottom_plane = request->bottom_plane, + .light_dir = request->light_dir, + .bones = request->bones, + .model = request->model.model, + .vertices = &request->model.level->level->shadow_data.vertices, + .flags = request->flags, + .debug_highlight_tri = m_debug_tri, + }; + calc_shadow_indices(input, &m_cpu_workspace, &m_cpu_output); + glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_opengl.bones_buffer, sizeof(math::Vector4f) * request->bone_idx, 128 * 16 * 4); const auto* geo = request->model.model; @@ -108,53 +126,118 @@ void Shadow3::draw_model(SharedRenderState* render_state, set_uniform(m_uniforms.top_plane, request->top_plane); set_uniform(m_uniforms.bottom_plane, request->bottom_plane); - // enable stencil! - glEnable(GL_STENCIL_TEST); - glStencilMask(0xFF); - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - glDepthFunc(GL_GEQUAL); - // glDepthMask(GL_FALSE); // no depth writes. + // glDrawElements(GL_TRIANGLES, m_cpu_output.num_indices, GL_UNSIGNED_INT, nullptr); + if (m_hacks) { + auto* model = request->model.model; + int num_verts = model->num_one_bone_vertices + model->num_two_bone_vertices; + std::vector verts; + for (size_t i = 0; i < num_verts; ++i) { + auto& out = verts.emplace_back(); + out.flags = 255; + out.mats[0] = 255; + out.mats[1] = 255; + out.pos[0] = m_cpu_workspace.vertices[i].x(); + out.pos[1] = m_cpu_workspace.vertices[i].y(); + out.pos[2] = m_cpu_workspace.vertices[i].z(); + out.weight = m_cpu_workspace.vertices[i].w(); + } - auto do_draw = [&](const tfrag3::ShadowModel::Run& run, const math::Vector3f& color) { - set_uniform(m_uniforms.debug_color, color); - glDrawElements(GL_TRIANGLES, run.count, GL_UNSIGNED_INT, - (void*)(sizeof(u32) * run.first_index)); + for (size_t i = 0; i < num_verts; ++i) { + auto& out = verts.emplace_back(); + out.flags = 255; + out.mats[0] = 255; + out.mats[1] = 255; + out.pos[0] = m_cpu_workspace.dual_vertices[i].x(); + out.pos[1] = m_cpu_workspace.dual_vertices[i].y(); + out.pos[2] = m_cpu_workspace.dual_vertices[i].z(); + out.weight = m_cpu_workspace.dual_vertices[i].w(); + } + + for (int i = 0; i < m_cpu_output.num_f0_indices; i++) { + m_cpu_output.f0_indices[i] -= model->first_vertex; + } + for (int i = 0; i < m_cpu_output.num_f1_indices; i++) { + m_cpu_output.f1_indices[i] -= model->first_vertex; + } + glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); - set_uniform(m_uniforms.debug_color,color); - // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - // glDrawElements(GL_TRIANGLES, run.count, GL_UNSIGNED_INT, - // (void*)(sizeof(u32) * run.first_index)); - // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - }; + glDepthFunc(GL_GEQUAL); + glDepthMask(GL_TRUE); + // glEnable(GL_CULL_FACE); + // glCullFace(GL_BACK); + glBindBuffer(GL_ARRAY_BUFFER, m_opengl.debug_verts); + glBufferData(GL_ARRAY_BUFFER, num_verts * 2 * sizeof(tfrag3::ShadowVertex), verts.data(), + GL_DYNAMIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_opengl.indices); + set_uniform(m_uniforms.debug_color, math::Vector3f(0.5f, 0.5f, 0.5f)); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_cpu_output.num_f0_indices * sizeof(u32), + m_cpu_output.f0_indices, GL_DYNAMIC_DRAW); + glDrawElements(GL_TRIANGLES, m_cpu_output.num_f0_indices, GL_UNSIGNED_INT, nullptr); + set_uniform(m_uniforms.debug_color, math::Vector3f(0.f, 0.f, 0.f)); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDrawElements(GL_TRIANGLES, m_cpu_output.num_f0_indices, GL_UNSIGNED_INT, nullptr); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - auto do_all_draws = [&]( const math::Vector3f& color) { - glUniform1i(m_uniforms.bottom_cap, 0); - do_draw(geo->single_tris, color); - // do_draw(geo->double_tris, math::Vector3f(0.8, 0.5, 0.5)); - do_draw(geo->single_edges, color); - // do_draw(geo->double_edges, math::Vector3f(0.5, 0.8, 0.5)); - // glUniform1i(m_uniforms.bottom_cap, 1); - // do_draw(geo->single_tris, math::Vector3f(0.5, 0.5, 0.8)); - // do_draw(geo->double_tris, math::Vector3f(0.5, 0.5, 0.8)); - }; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_cpu_output.num_f1_indices * sizeof(u32), + m_cpu_output.f1_indices, GL_DYNAMIC_DRAW); + set_uniform(m_uniforms.debug_color, math::Vector3f(0.f, 0.f, 0.f)); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDrawElements(GL_TRIANGLES, m_cpu_output.num_f1_indices, GL_UNSIGNED_INT, nullptr); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - // using glCullFace(GL_FRONT) seems to give us back faces. + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + set_uniform(m_uniforms.debug_color, math::Vector3f(0.5f, 0.78f, 0.5f)); + glDrawElements(GL_TRIANGLES, m_cpu_output.num_f1_indices, GL_UNSIGNED_INT, nullptr); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glStencilFunc(GL_ALWAYS, 0, 0); // always pass stencil - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment on depth pass. - do_all_draws({0.1f, 0.1f, 0.8f}); - glCullFace(GL_FRONT); - glStencilFunc(GL_ALWAYS, 0, 0); - glStencilOp(GL_KEEP, GL_KEEP, GL_DECR); // decrement on depth pass. - do_all_draws({0.8f, 0.1f, 0.1f}); - glDisable(GL_CULL_FACE); + glBindBuffer(GL_ARRAY_BUFFER, request->model.level->shadow_vertices); + glDisable(GL_CULL_FACE); + + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_opengl.indices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_cpu_output.num_indices * sizeof(u32), + m_cpu_output.indices, GL_DYNAMIC_DRAW); + // enable stencil! + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no color writes. + glEnable(GL_STENCIL_TEST); + glStencilMask(0xFF); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDepthFunc(GL_GEQUAL); + glDepthMask(GL_FALSE); // no depth writes. + + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + glStencilFunc(GL_ALWAYS, 0, 0); // always pass stencil + glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); // increment on depth fail + glDrawElements(GL_TRIANGLES, m_cpu_output.num_indices, GL_UNSIGNED_INT, nullptr); + glCullFace(GL_BACK); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); // decrement on depth pass. + glDrawElements(GL_TRIANGLES, m_cpu_output.num_indices, GL_UNSIGNED_INT, nullptr); + glDisable(GL_CULL_FACE); + } } void Shadow3::finish(SharedRenderState* render_state, ScopedProfilerNode& prof) { + // finally, draw shadow. + if (!m_hacks) { + glStencilFunc(GL_NOTEQUAL, 0, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDepthFunc(GL_ALWAYS); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ZERO); + glColorMask(true, true, true, false); + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + m_full_screen_draw.draw(math::Vector4f(0.5, 0.4, 0.3, 0.5), render_state, prof); + } + // restore + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_TRUE); + glDisable(GL_STENCIL_TEST); } void Shadow3::flush_requests(SharedRenderState* render_state, ScopedProfilerNode& prof) { @@ -202,7 +285,11 @@ void Shadow3::first_time_setup(SharedRenderState* render_state) { set_uniform(m_uniforms.hvdf_offset, render_state->camera_hvdf_off); } -void Shadow3::draw_debug_window() {} +void Shadow3::draw_debug_window() { + ImGui::Checkbox("hacks", &m_hacks); + ImGui::Checkbox("near_plane", &m_near_plane_hack); + ImGui::InputInt("Tri", &m_debug_tri); +} void Shadow3::render_jak1(DmaFollower& dma, SharedRenderState* render_state, @@ -283,6 +370,8 @@ void Shadow3::render_jak1(DmaFollower& dma, // (somewhere in the model) and following the shadow direction backward. request.origin = game_request.settings.center + game_request.settings.shadow_dir * game_request.settings.dist_to_locus; + request.bones = g_ee_main_mem + game_request.mtx; + request.flags = game_request.settings.flags; // copy bones to buffer constexpr int in_stride = 8 * 4 * sizeof(float); @@ -299,11 +388,14 @@ void Shadow3::render_jak1(DmaFollower& dma, request.top_plane = game_request.settings.top_plane; request.bottom_plane = game_request.settings.bot_plane; if (!(kAbsolutePlanes & game_request.settings.flags)) { - printf("relative plane mode, base is %f, move by %f\n", - -request.bottom_plane.w() / 4096.0, game_request.settings.center.y() / 4096.0); + // printf("relative plane mode, base is %f, move by %f\n", + // -request.bottom_plane.w() / 4096.0, game_request.settings.center.y() / 4096.0); // in relative planes mode, the height of the plane is adjusted to be relative to the // height of the center, so the planes move and down with the model request.top_plane.w() -= game_request.settings.center.y(); + if (m_near_plane_hack) { + request.bottom_plane.w() = 4096; + } request.bottom_plane.w() -= game_request.settings.center.y(); } @@ -312,7 +404,7 @@ void Shadow3::render_jak1(DmaFollower& dma, if (render_state->camera_pos.xyz().dot(request.bottom_plane.xyz()) + request.bottom_plane.w() < 0) { - printf(" SKIP: camera below lower clipping plane.\n"); + // printf(" SKIP: camera below lower clipping plane.\n"); m_next_request--; continue; } @@ -321,15 +413,15 @@ void Shadow3::render_jak1(DmaFollower& dma, // detect if the origin is below the clipping plane and if so, move it up. const float dot = request.bottom_plane.xyz().dot(request.origin); if (dot + request.bottom_plane.w() > 0) { - printf(" the origin is below the clipping plane, moving it up.\n"); - printf(" center was %s\n", game_request.settings.center.to_string_aligned().c_str()); - printf(" dir was %s\n", game_request.settings.shadow_dir.to_string_aligned().c_str()); - printf(" locus %f\n", game_request.settings.dist_to_locus); - printf(" bottom plane was %s\n", - game_request.settings.bot_plane.to_string_aligned().c_str()); - printf(" adjusted bottom plane was %s\n", - request.bottom_plane.to_string_aligned().c_str()); - printf(" abs flag %d\n", game_request.settings.flags & kAbsolutePlanes); + // printf(" the origin is below the clipping plane, moving it up.\n"); + // printf(" center was %s\n", game_request.settings.center.to_string_aligned().c_str()); + // printf(" dir was %s\n", game_request.settings.shadow_dir.to_string_aligned().c_str()); + // printf(" locus %f\n", game_request.settings.dist_to_locus); + // printf(" bottom plane was %s\n", + // game_request.settings.bot_plane.to_string_aligned().c_str()); + // printf(" adjusted bottom plane was %s\n", + // request.bottom_plane.to_string_aligned().c_str()); + // printf(" abs flag %d\n", game_request.settings.flags & kAbsolutePlanes); request.bottom_plane.w() = -dot; } @@ -353,20 +445,20 @@ void Shadow3::render_jak1(DmaFollower& dma, return math::Vector4f(xyz.x(), xyz.y(), xyz.z(), in.w() - xyz.dot(cam_rot[3].xyz())); }; - printf("plane offset before: %f\n", - game_request.settings.center.dot(request.bottom_plane.xyz()) + - request.bottom_plane.w()); + // printf("plane offset before: %f\n", + // game_request.settings.center.dot(request.bottom_plane.xyz()) + + // request.bottom_plane.w()); request.light_dir = rotate(request.light_dir); request.top_plane = rotate_plane(request.top_plane); request.bottom_plane = rotate_plane(request.bottom_plane); request.origin = transform(request.origin); - printf("plane offset after: %f\n", - transform(game_request.settings.center).dot(request.bottom_plane.xyz()) + - request.bottom_plane.w()); - printf("rot3: %s\n", cam_rot[3].to_string_aligned().c_str()); - printf(" 2: %s\n", cam_pos.to_string_aligned().c_str()); + // printf("plane offset after: %f\n", + // transform(game_request.settings.center).dot(request.bottom_plane.xyz()) + + // request.bottom_plane.w()); + // printf("rot3: %s\n", cam_rot[3].to_string_aligned().c_str()); + // printf(" 2: %s\n", cam_pos.to_string_aligned().c_str()); // printf(" origin: %s\n", (request.origin / 4096.f).to_string_aligned().c_str()); } diff --git a/game/graphics/opengl_renderer/foreground/Shadow3.h b/game/graphics/opengl_renderer/foreground/Shadow3.h index 154bf2e7c1..b70180a00e 100644 --- a/game/graphics/opengl_renderer/foreground/Shadow3.h +++ b/game/graphics/opengl_renderer/foreground/Shadow3.h @@ -1,5 +1,7 @@ #pragma once #include "game/graphics/opengl_renderer/BucketRenderer.h" +#include "game/graphics/opengl_renderer/foreground/Shadow3CPU.h" +#include "game/graphics/opengl_renderer/opengl_utils.h" struct Jak1ShadowSettings { math::Vector center; @@ -38,7 +40,9 @@ class Shadow3 { math::Vector4f top_plane, bottom_plane; math::Vector3f light_dir; ShadowRequest* next = nullptr; + const u8* bones = nullptr; u32 bone_idx = 0; + u32 flags = 0; }; struct LevelChain { @@ -62,6 +66,8 @@ class Shadow3 { struct { GLuint vao = -1; + GLuint indices = -1; + GLuint debug_verts = -1; GLuint bones_buffer = -1; int buffer_alignment = 0; } m_opengl; @@ -78,4 +84,12 @@ class Shadow3 { GLuint bottom_cap = 0; } m_uniforms; bool m_did_first_time_setup = false; + + bool m_hacks = false; + bool m_near_plane_hack = false; + int m_debug_tri = 0; + + ShadowCPUWorkspace m_cpu_workspace; + ShadowCPUOutput m_cpu_output; + FullScreenDraw m_full_screen_draw; }; \ No newline at end of file diff --git a/game/graphics/opengl_renderer/foreground/Shadow3CPU.cpp b/game/graphics/opengl_renderer/foreground/Shadow3CPU.cpp new file mode 100644 index 0000000000..7dce3c9e37 --- /dev/null +++ b/game/graphics/opengl_renderer/foreground/Shadow3CPU.cpp @@ -0,0 +1,230 @@ +#include "Shadow3CPU.h" + +#include + +/* +*- `xform-verts` transform mesh vertices into camera space (no perspective) +- `init-vars` transform settings to camera space +- `calc-dual-verts` project vertices to plane +- `scissor-top` (only executed if shdf03 is set), clip vertices to top plane, if above +- `scissor-edges`, clip vertices to near plane +- `find-facing-single-tris`, set face bit to indicate orientation, cull backward ones +- `find-single-edges`, find edges that, when extruded, should be drawn +- `find-facing-double-tris`, set face bit indicate orientation. double sided tris, so no culling +- `find-double-edges`, find edges to extrude from the double-sided tris +- `add-verts` +- `add-facing-single-tris` +- `add-single-edges` +- `add-double-tris` +- `add-double-edges` +*/ + +void transform_vertices(const ShadowCPUInput& input, ShadowCPUWorkspace* work) { + struct Bone { + math::Vector4f mat[4]; + u8 pad[16 * 4]; + }; + static_assert(sizeof(Bone) == 128); + const tfrag3::ShadowVertex* vertex_ptr = &input.vertices->operator[](input.model->first_vertex); + math::Vector4f* out_ptr = work->vertices; + const Bone* first_bone_ptr = (const Bone*)(3 * 8 * 4 * sizeof(float) + input.bones); + + for (int i = 0; i < input.model->num_one_bone_vertices; i++) { + const Bone& bone = first_bone_ptr[vertex_ptr->mats[0]]; + *out_ptr = bone.mat[3] + // + bone.mat[0] * vertex_ptr->pos[0] + // + bone.mat[1] * vertex_ptr->pos[1] + // + bone.mat[2] * vertex_ptr->pos[2]; + vertex_ptr++; + out_ptr++; + } + + for (int i = 0; i < input.model->num_two_bone_vertices; i++) { + const Bone& bone0 = first_bone_ptr[vertex_ptr->mats[0]]; + math::Vector4f p0 = bone0.mat[3] + // + bone0.mat[0] * vertex_ptr->pos[0] + // + bone0.mat[1] * vertex_ptr->pos[1] + // + bone0.mat[2] * vertex_ptr->pos[2]; + p0 *= vertex_ptr->weight; + const Bone& bone1 = first_bone_ptr[vertex_ptr->mats[1]]; + math::Vector4f p1 = bone1.mat[3] + // + bone1.mat[0] * vertex_ptr->pos[0] + // + bone1.mat[1] * vertex_ptr->pos[1] + // + bone1.mat[2] * vertex_ptr->pos[2]; + p1 *= (1.f - vertex_ptr->weight); + *out_ptr = p0 + p1; + + out_ptr++; + vertex_ptr++; + } +} + +void calc_dual_verts(const ShadowCPUInput& input, ShadowCPUWorkspace* work) { + int num_verts = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices; + for (int i = 0; i < num_verts; i++) { + math::Vector4f origin(input.origin.x(), input.origin.y(), input.origin.z(), 1.f); + math::Vector4f p = work->vertices[i]; + math::Vector4f offset = origin - p; + math::Vector4f plane = input.bottom_plane; + work->dual_vertices[i] = p - offset * p.dot(plane) / offset.xyz().dot(plane.xyz()); + } +} + +void scissor_top(const ShadowCPUInput& input, ShadowCPUWorkspace* work) { + // TODO +} + +void scissor_edges(const ShadowCPUInput& input, ShadowCPUWorkspace* work) { + // TODO +} + +void find_facing_single_tris(const ShadowCPUInput& input, + ShadowCPUWorkspace* work, + ShadowCPUOutput* output, + const std::vector& tris) { + int edge_offset = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices; + int num_0 = 0; + int num_1 = 0; + for (size_t i = 0; i < tris.size(); i++) { + const auto& tri = tris[i]; + math::Vector3f v0 = work->vertices[tri.verts[0]].xyz(); + math::Vector3f v1 = work->vertices[tri.verts[1]].xyz(); + math::Vector3f v2 = work->vertices[tri.verts[2]].xyz(); + math::Vector3f n = (v1 - v0).cross(v2 - v0); + bool highlight = i == input.debug_highlight_tri; + if (n.dot(input.light_dir) < 0.f) { + num_0++; + work->tri_flags[i] = 1; + output->push_index(tri.verts[0], !highlight); + output->push_index(tri.verts[1], !highlight); + output->push_index(tri.verts[2], !highlight); + } else { + num_1++; + work->tri_flags[i] = 0; + output->push_index(static_cast(tri.verts[0]) + edge_offset, !highlight); + output->push_index(static_cast(tri.verts[1]) + edge_offset, !highlight); + output->push_index(static_cast(tri.verts[2]) + edge_offset, !highlight); + } + } +} + +// void find_facing_double_tris(const ShadowCPUInput& input, +// ShadowCPUWorkspace* work, +// ShadowCPUOutput* output, +// const std::vector& tris) { +// int edge_offset = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices; +// const int flag_offset = input.model->double_tris.size(); +// int num_0 = 0; +// int num_1 = 0; +// for (size_t i = 0; i < tris.size(); i++) { +// const auto& tri = tris[i]; +// math::Vector3f v0 = work->vertices[tri.verts[0]].xyz(); +// math::Vector3f v1 = work->vertices[tri.verts[1]].xyz(); +// math::Vector3f v2 = work->vertices[tri.verts[2]].xyz(); +// math::Vector3f n = (v1 - v0).cross(v2 - v0); +// if (n.dot(input.light_dir) < 0.f) { +// num_0++; +// work->tri_flags[i + flag_offset] = 1; +// +// } else { +// num_1++; +// work->tri_flags[i + flag_offset] = 0; +// } +// +// output->push_index(tri.verts[0], false); +// output->push_index(tri.verts[1], false); +// output->push_index(tri.verts[2], false); +// output->push_index(tri.verts[1], false); +// output->push_index(tri.verts[0], false); +// output->push_index(tri.verts[2], false); +// output->push_index(static_cast(tri.verts[0]) + edge_offset, false); +// output->push_index(static_cast(tri.verts[1]) + edge_offset, false); +// output->push_index(static_cast(tri.verts[2]) + edge_offset, false); +// output->push_index(static_cast(tri.verts[1]) + edge_offset, false); +// output->push_index(static_cast(tri.verts[0]) + edge_offset, false); +// output->push_index(static_cast(tri.verts[2]) + edge_offset, false); +// } +// } + +void find_single_edges(const ShadowCPUInput& input, + ShadowCPUWorkspace* work, + ShadowCPUOutput* output) { + int num_weird = 0; + int num_0 = 0; + int num_1 = 0; + int edge_offset = input.model->num_one_bone_vertices + input.model->num_two_bone_vertices; + for (size_t i = 0; i < input.model->single_edges.size(); i++) { + const auto& e = input.model->single_edges[i]; + bool skip = false; + bool out_back = false; + + if (e.tri[1] == 255) { + out_back = true; + skip = work->tri_flags[e.tri[0]] == 0; + num_weird++; + } else { + u8 f0 = work->tri_flags[e.tri[0]]; + u8 f1 = work->tri_flags[e.tri[1]]; + + if (f0 == f1) { + skip = true; + } else { + if (f0 == 1) { + out_back = true; + num_0++; + } else { + num_1++; + } + } + } + + if (!skip) { + if (out_back) { + output->push_index(e.ind[0], true); + output->push_index(static_cast(e.ind[0]) + edge_offset, true); + output->push_index(static_cast(e.ind[1]) + edge_offset, true); + + output->push_index(e.ind[0], true); + output->push_index(static_cast(e.ind[1]) + edge_offset, true); + output->push_index(e.ind[1], true); + } else { + output->push_index(e.ind[0], true); + output->push_index(static_cast(e.ind[1]) + edge_offset, true); + output->push_index(static_cast(e.ind[0]) + edge_offset, true); + + output->push_index(e.ind[0], true); + output->push_index(e.ind[1], true); + output->push_index(static_cast(e.ind[1]) + edge_offset, true); + } + } + } +} + +void find_facing_double_tris() {} + +void find_double_edges() {} + +void calc_shadow_indices(const ShadowCPUInput& input, + ShadowCPUWorkspace* work, + ShadowCPUOutput* output) { + output->num_indices = 0; + output->num_f0_indices = 0; + output->num_f1_indices = 0; + + // HACK + for (auto& f : work->tri_flags) { + f = 77; + } + + transform_vertices(input, work); + calc_dual_verts(input, work); + scissor_top(input, work); + scissor_edges(input, work); + find_facing_single_tris(input, work, output, input.model->single_tris); + find_single_edges(input, work, output); + // find_facing_double_tris(input, work, output, input.model->double_tris); + + for (int i = 0; i < output->num_indices; i++) { + output->indices[i] += input.model->first_vertex; + } +} diff --git a/game/graphics/opengl_renderer/foreground/Shadow3CPU.h b/game/graphics/opengl_renderer/foreground/Shadow3CPU.h new file mode 100644 index 0000000000..e7d53b3c63 --- /dev/null +++ b/game/graphics/opengl_renderer/foreground/Shadow3CPU.h @@ -0,0 +1,45 @@ +#pragma once +#include "common/custom_data/Tfrag3Data.h" +#include "common/math/Vector.h" + +struct ShadowCPUInput { + math::Vector3f origin; + math::Vector4f top_plane, bottom_plane; + math::Vector3f light_dir; + const u8* bones = nullptr; + const tfrag3::ShadowModel* model = nullptr; + std::vector* vertices = nullptr; + u32 flags = 0; + int debug_highlight_tri = 0; +}; + +struct ShadowCPUOutput { + static constexpr int kMaxIndices = (256 * 3) + (256 * 3 * 2); + + void push_index(u32 i, bool facing) { + indices[num_indices++] = i; + if (!facing) { + f0_indices[num_f0_indices++] = i; + } else { + f1_indices[num_f1_indices++] = i; + } + } + + int num_indices = 0; + u32 indices[kMaxIndices]; + + int num_f0_indices = 0; + u32 f0_indices[kMaxIndices]; + int num_f1_indices = 0; + int f1_indices[kMaxIndices]; +}; + +struct ShadowCPUWorkspace { + math::Vector4f vertices[tfrag3::ShadowModel::kMaxVertices]; + math::Vector4f dual_vertices[tfrag3::ShadowModel::kMaxVertices]; + u8 tri_flags[tfrag3::ShadowModel::kMaxTris]; +}; + +void calc_shadow_indices(const ShadowCPUInput& input, + ShadowCPUWorkspace* work, + ShadowCPUOutput* output); \ No newline at end of file diff --git a/game/graphics/opengl_renderer/loader/Loader.cpp b/game/graphics/opengl_renderer/loader/Loader.cpp index b08b1e71f7..d8fb73d097 100644 --- a/game/graphics/opengl_renderer/loader/Loader.cpp +++ b/game/graphics/opengl_renderer/loader/Loader.cpp @@ -504,7 +504,6 @@ void Loader::update(TexturePool& texture_pool) { m_garbage_buffers.push_back(lev->collide_vertices); m_garbage_buffers.push_back(lev->merc_vertices); m_garbage_buffers.push_back(lev->merc_indices); - m_garbage_buffers.push_back(lev->shadow_indices); m_garbage_buffers.push_back(lev->shadow_vertices); for (auto& model : lev->level->merc_data.models) { diff --git a/game/graphics/opengl_renderer/loader/LoaderStages.cpp b/game/graphics/opengl_renderer/loader/LoaderStages.cpp index c378ae19ba..31320c97e9 100644 --- a/game/graphics/opengl_renderer/loader/LoaderStages.cpp +++ b/game/graphics/opengl_renderer/loader/LoaderStages.cpp @@ -693,7 +693,6 @@ ShadowLoaderStage::ShadowLoaderStage() : LoaderStage("shadow") {} void ShadowLoaderStage::reset() { m_done = false; m_opengl = false; - m_vtx_uploaded = false; m_idx = 0; } @@ -703,12 +702,6 @@ bool ShadowLoaderStage::run(Timer& /*timer*/, LoaderInput& data) { } if (!m_opengl) { - glGenBuffers(1, &data.lev_data->shadow_indices); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.lev_data->shadow_indices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - data.lev_data->level->shadow_data.indices.size() * sizeof(u32), nullptr, - GL_STATIC_DRAW); - glGenBuffers(1, &data.lev_data->shadow_vertices); glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_vertices); glBufferData(GL_ARRAY_BUFFER, @@ -717,20 +710,6 @@ bool ShadowLoaderStage::run(Timer& /*timer*/, LoaderInput& data) { m_opengl = true; } - if (!m_vtx_uploaded) { - u32 start = m_idx; - m_idx = std::min(start + 32768, (u32)data.lev_data->level->shadow_data.indices.size()); - glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_indices); - glBufferSubData(GL_ARRAY_BUFFER, start * sizeof(u32), (m_idx - start) * sizeof(u32), - data.lev_data->level->shadow_data.indices.data() + start); - if (m_idx != data.lev_data->level->shadow_data.indices.size()) { - return false; - } else { - m_idx = 0; - m_vtx_uploaded = true; - } - } - u32 start = m_idx; m_idx = std::min(start + 32768, (u32)data.lev_data->level->shadow_data.vertices.size()); glBindBuffer(GL_ARRAY_BUFFER, data.lev_data->shadow_vertices); diff --git a/game/graphics/opengl_renderer/loader/LoaderStages.h b/game/graphics/opengl_renderer/loader/LoaderStages.h index 7bf783ee8b..e67dea0e42 100644 --- a/game/graphics/opengl_renderer/loader/LoaderStages.h +++ b/game/graphics/opengl_renderer/loader/LoaderStages.h @@ -27,6 +27,5 @@ public: private: bool m_done = false; bool m_opengl = false; - bool m_vtx_uploaded = false; u32 m_idx = 0; }; \ No newline at end of file diff --git a/game/graphics/opengl_renderer/loader/common.h b/game/graphics/opengl_renderer/loader/common.h index 10ac57e2c3..4e53707546 100644 --- a/game/graphics/opengl_renderer/loader/common.h +++ b/game/graphics/opengl_renderer/loader/common.h @@ -29,7 +29,6 @@ struct LevelData { std::unordered_map merc_model_lookup; GLuint shadow_vertices; - GLuint shadow_indices; std::unordered_map shadow_model_lookup; GLuint hfrag_vertices; diff --git a/game/graphics/opengl_renderer/shaders/shadow3.vert b/game/graphics/opengl_renderer/shaders/shadow3.vert index 49169d8691..5ea713652e 100644 --- a/game/graphics/opengl_renderer/shaders/shadow3.vert +++ b/game/graphics/opengl_renderer/shaders/shadow3.vert @@ -51,6 +51,7 @@ vec4 dual(vec4 p, vec4 plane) { } vec4 scissor(vec4 p, vec4 plane) { + return p; float plane_offset = dot(p, plane); if (plane_offset > 0) { vec4 offset = vec4(origin, 1) - p; @@ -62,26 +63,30 @@ vec4 scissor(vec4 p, vec4 plane) { void main() { vec4 p = vec4(position_in, 1); + vec4 vtx_pos; - vec4 vtx_pos = -bones[mats[0] + offset].X * p * weight_in; - - if (weight_in > 1) { - vtx_pos += -bones[mats[1] + offset].X * p * (1.f - weight_in); - } - - - if (bottom_cap) { - vtx_pos = dual(vtx_pos, bottom_plane); + if (mats[0] == 255) { + // debug hack! + vtx_pos = vec4(position_in, weight_in); } else { - if ((flags & uint(1)) != 0) { + vtx_pos = -bones[mats[0] + offset].X * p * weight_in; + + if (weight_in > 1) { + vtx_pos += -bones[mats[1] + offset].X * p * (1.f - weight_in); + } + + + if (bottom_cap) { vtx_pos = dual(vtx_pos, bottom_plane); } else { - vtx_pos = scissor(vtx_pos, top_plane); + if ((flags & uint(1)) != 0) { + vtx_pos = dual(vtx_pos, bottom_plane); + } else { + vtx_pos = scissor(vtx_pos, top_plane); + } } } - - vec4 transformed = perspective_matrix * vtx_pos; float Q = fog_constants.x / transformed[3]; @@ -98,5 +103,5 @@ void main() { gl_Position = transformed; - vtx_color = vec4(debug_color, 1.0); + vtx_color = vec4(debug_color, 0.5); }