From 0b8b9273153ac54c22fdb0e075c2ddca4a70a32f Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Wed, 8 Mar 2023 18:18:35 -0500 Subject: [PATCH] [merc] support eyes through merc (#2300) Support rendering eyes with merc for both jak 1 and jak 2. For jak 1, everything should look the same, but merc will be used to draw eyes. This means that jak is now fully drawn with merc! For jak 2, eyes should draw, but there are still a few issues: - the tbp/clut ptr trick is used a lot for these eye textures, so there's a lot that use the wrong texture - I had to enable a bunch more "texture uploads" (basically emulating the ps2 texture system) in order to get the eyes to upload. It would be much better if the eye renderer could somehow grab the texture from the merc model info, skipping the vram addressing stuff entirely. I plan to return to this. - I disabled some sky draws in `sky-tng`. After turning on pris2 uploads, the sky flashed in a really annoying way. --- common/custom_data/TFrag3Data.cpp | 1 + common/custom_data/Tfrag3Data.h | 4 +- decompiler/level_extractor/MercData.cpp | 15 +- decompiler/level_extractor/MercData.h | 8 +- decompiler/level_extractor/extract_merc.cpp | 50 ++- game/graphics/opengl_renderer/EyeRenderer.cpp | 374 +++++++----------- game/graphics/opengl_renderer/EyeRenderer.h | 21 +- .../opengl_renderer/OpenGLRenderer.cpp | 14 + .../graphics/opengl_renderer/OpenGLRenderer.h | 1 + .../opengl_renderer/foreground/Merc2.cpp | 23 +- game/kernel/jak2/kscheme.cpp | 2 - goal_src/jak1/engine/gfx/foreground/bones.gc | 13 - goal_src/jak2/engine/draw/drawable.gc | 2 +- goal_src/jak2/engine/gfx/foreground/eye-h.gc | 3 + goal_src/jak2/engine/gfx/sky/sky-tng.gc | 6 +- goal_src/jak2/engine/gfx/texture/texture.gc | 10 +- 16 files changed, 267 insertions(+), 280 deletions(-) diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index e8138d5d0b..97018729ee 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -254,6 +254,7 @@ void CollisionMesh::serialize(Serializer& ser) { void MercDraw::serialize(Serializer& ser) { ser.from_ptr(&mode); ser.from_ptr(&tree_tex_id); + ser.from_ptr(&eye_id); ser.from_ptr(&first_index); ser.from_ptr(&index_count); ser.from_ptr(&num_triangles); diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index b7be49e69d..5da9975aec 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -73,7 +73,7 @@ struct MemoryUsageTracker { void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; } }; -constexpr int TFRAG3_VERSION = 25; +constexpr int TFRAG3_VERSION = 26; // These vertices should be uploaded to the GPU at load time and don't change struct PreloadedVertex { @@ -393,7 +393,7 @@ static_assert(sizeof(MercVertex) == 64); struct MercDraw { DrawMode mode; u32 tree_tex_id = 0; // the texture that should be bound for the draw - + u8 eye_id = 0xff; // 0xff if not eyes, (slot << 1) | (is_r) u32 first_index; u32 index_count; u32 num_triangles; diff --git a/decompiler/level_extractor/MercData.cpp b/decompiler/level_extractor/MercData.cpp index 267ae1d050..ec8741b3ef 100644 --- a/decompiler/level_extractor/MercData.cpp +++ b/decompiler/level_extractor/MercData.cpp @@ -8,6 +8,11 @@ #include "third-party/fmt/core.h" namespace decompiler { + +void MercEyeCtrl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) { + eye_slot = read_plain_data_field(tr, "eye-slot", dts); +} + void MercCtrlHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) { st_magic = read_plain_data_field(tr, "st-magic", dts); xyz_scale = read_plain_data_field(tr, "xyz-scale", dts); @@ -35,7 +40,14 @@ void MercCtrlHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) { cross_copy_count = read_plain_data_field(tr, "cross-copy-count", dts); num_verts = read_plain_data_field(tr, "num-verts", dts); longest_edge = read_plain_data_field(tr, "longest-edge", dts); - // todo masksk + auto fr = get_field_ref(tr, "eye-ctrl", dts); + const auto& word = fr.data->words_by_seg.at(fr.seg).at(fr.byte_offset / 4); + if (word.kind() == LinkedWord::PTR) { + eye_ctrl.emplace(); + eye_ctrl->from_ref(TypedRef(deref_label(fr), dts.ts.lookup_type("merc-eye-ctrl")), dts); + } + + // todo masks envmap_tint = read_plain_data_field(tr, "envmap-tint", dts); needs_clip = read_plain_data_field(tr, "needs-clip", dts); use_isometric = read_plain_data_field(tr, "use-isometric", dts); @@ -427,6 +439,7 @@ void MercCtrl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) { effects.emplace_back().from_ref(eff_ref, dts, header); eff_ref.ref.byte_offset += 32; // } + // debug_print_blerc(); } diff --git a/decompiler/level_extractor/MercData.h b/decompiler/level_extractor/MercData.h index 9536ea893c..b2c7f4ea20 100644 --- a/decompiler/level_extractor/MercData.h +++ b/decompiler/level_extractor/MercData.h @@ -12,6 +12,12 @@ namespace decompiler { +struct MercEyeCtrl { + s8 eye_slot; + // there's more... + void from_ref(TypedRef tr, const DecompilerTypeSystem& dts); +}; + /*! * per-ctrl information. the first qw is uploaded to vu1 */ @@ -42,7 +48,7 @@ struct MercCtrlHeader { u16 cross_copy_count; u16 num_verts; float longest_edge; - // todo (eye-ctrl merc-eye-ctrl :offset-assert 64) + std::optional eye_ctrl; u32 masks[3]; // (dummy-bytes uint8 48 :offset 32) u32 envmap_tint; diff --git a/decompiler/level_extractor/extract_merc.cpp b/decompiler/level_extractor/extract_merc.cpp index 2869bc8e0e..198c8e4646 100644 --- a/decompiler/level_extractor/extract_merc.cpp +++ b/decompiler/level_extractor/extract_merc.cpp @@ -117,6 +117,7 @@ struct ConvertedMercEffect { bool has_envmap = false; DrawMode envmap_mode; u32 envmap_texture; + std::optional eye_slot; }; /*! @@ -682,7 +683,10 @@ std::string debug_dump_to_ply(const std::vector& draws, int find_or_add_texture_to_level(tfrag3::Level& out, const TextureDB& tex_db, const std::string& debug_name, - u32 pc_combo_tex_id) { + u32 pc_combo_tex_id, + const MercCtrlHeader& hdr, + u8* eye_out, + GameVersion version) { u32 idx_in_level_texture = UINT32_MAX; for (u32 i = 0; i < out.textures.size(); i++) { if (out.textures[i].combo_id == pc_combo_tex_id) { @@ -710,6 +714,38 @@ int find_or_add_texture_to_level(tfrag3::Level& out, } } + // check eyes + u32 eye_tpage = version == GameVersion::Jak2 ? 0x70c : 0x1cf; + u32 left_id = version == GameVersion::Jak2 ? 7 : 0x6f; + u32 right_id = version == GameVersion::Jak2 ? 8 : 0x70; + + if (eye_out && (pc_combo_tex_id >> 16) == eye_tpage) { + auto tex_it = tex_db.textures.find(pc_combo_tex_id); + if (tex_it == tex_db.textures.end()) { + // fmt::print("{} got dynamic merc texture (no known texture)\n", debug_name); + } else { + // fmt::print("{} got dynamic merc texture (will overwrite {})\n", debug_name, + // tex_it->second.name); + } + u32 idx = pc_combo_tex_id & 0xffff; + + if (idx == left_id || idx == right_id) { + if (!hdr.eye_ctrl) { + fmt::print("no eye ctrl, but expected one"); + if (debug_name != "kor-break-lod0") { + ASSERT(false); + } + } + if (idx == left_id) { + *eye_out = (hdr.eye_ctrl->eye_slot * 2); + } else if (idx == right_id) { + *eye_out = (hdr.eye_ctrl->eye_slot * 2) + 1; + } + } else { + // fmt::print("got unknown tex id in eye page: {}\n", idx); + } + } + return idx_in_level_texture; } @@ -726,6 +762,9 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect, ConvertedMercEffect result; result.ctrl_idx = ctrl_idx; result.effect_idx = effect_idx; + if (ctrl_header.eye_ctrl) { + result.eye_slot = ctrl_header.eye_ctrl->eye_slot; + } if (input_effect.extra_info.shader) { result.has_envmap = true; result.envmap_mode = process_draw_mode(*input_effect.extra_info.shader, false, false); @@ -738,7 +777,8 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect, u32 tpage = new_tex >> 20; u32 tidx = (new_tex >> 8) & 0b1111'1111'1111; u32 tex_combo = (((u32)tpage) << 16) | tidx; - result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap", tex_combo); + result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap", tex_combo, + ctrl_header, nullptr, version); } else if (input_effect.envmap_or_effect_usage) { u32 tex_combo = 0; switch (version) { @@ -757,7 +797,8 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect, ASSERT_NOT_REACHED(); } - result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap-default", tex_combo); + result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap-default", tex_combo, + ctrl_header, nullptr, version); DrawMode mode; mode.set_at(false); @@ -1264,7 +1305,8 @@ void extract_merc(const ObjectFileData& ag_data, pc_draw = &pc_effect.all_draws.emplace_back(); pc_draw->mode = draw.state.merc_draw_mode.mode; pc_draw->tree_tex_id = find_or_add_texture_to_level( - out, tex_db, ctrl.name, draw.state.merc_draw_mode.pc_combo_tex_id); + out, tex_db, ctrl.name, draw.state.merc_draw_mode.pc_combo_tex_id, ctrl.header, + &pc_draw->eye_id, version); } else { pc_draw_idx = existing->second; pc_draw = &pc_effect.all_draws.at(pc_draw_idx); diff --git a/game/graphics/opengl_renderer/EyeRenderer.cpp b/game/graphics/opengl_renderer/EyeRenderer.cpp index ef0a9fc018..c87a9bbcdf 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.cpp +++ b/game/graphics/opengl_renderer/EyeRenderer.cpp @@ -17,35 +17,16 @@ void EyeRenderer::init_textures(TexturePool& texture_pool, GameVersion) { for (int lr = 0; lr < 2; lr++) { u32 tidx = pair_idx * 2 + lr; - // CPU - { - GLuint gl_tex; - glGenTextures(1, &gl_tex); - u32 tbp = EYE_BASE_BLOCK + pair_idx * 2 + lr; - TextureInput in; - in.gpu_texture = gl_tex; - in.w = 32; - in.h = 32; - in.debug_page_name = "PC-EYES"; - in.debug_name = fmt::format("{}-eye-cpu-{}", lr ? "left" : "right", pair_idx); - in.id = texture_pool.allocate_pc_port_texture(); - auto* gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp); - m_cpu_eye_textures[tidx] = {gl_tex, gpu_tex, tbp}; - } - - // GPU - { - u32 tbp = EYE_BASE_BLOCK + pair_idx * 2 + lr; - TextureInput in; - in.gpu_texture = m_gpu_eye_textures[tidx].fb.texture(); - in.w = 32; - in.h = 32; - in.debug_page_name = "PC-EYES"; - in.debug_name = fmt::format("{}-eye-gpu-{}", lr ? "left" : "right", pair_idx); - in.id = texture_pool.allocate_pc_port_texture(); - m_gpu_eye_textures[tidx].gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp); - m_gpu_eye_textures[tidx].tbp = tbp; - } + u32 tbp = EYE_BASE_BLOCK + pair_idx * 2 + lr; + TextureInput in; + in.gpu_texture = m_gpu_eye_textures[tidx].fb.texture(); + in.w = 32; + in.h = 32; + in.debug_page_name = "PC-EYES"; + in.debug_name = fmt::format("{}-eye-gpu-{}", lr ? "left" : "right", pair_idx); + in.id = texture_pool.allocate_pc_port_texture(); + m_gpu_eye_textures[tidx].gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp); + m_gpu_eye_textures[tidx].tbp = tbp; } } @@ -110,13 +91,8 @@ void EyeRenderer::render(DmaFollower& dma, } void EyeRenderer::draw_debug_window() { - ImGui::Checkbox("Use GPU", &m_use_gpu); ImGui::Text("Time: %.3f ms\n", m_average_time_ms); ImGui::Text("Debug:\n%s", m_debug.c_str()); - if (!m_use_gpu) { - ImGui::Checkbox("bilinear", &m_use_bilinear); - } - ImGui::Checkbox("alpha hack", &m_alpha_hack); } ////////////////////// @@ -241,13 +217,23 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma u32 pair_idx = -1; // first draw. this is the background. It reads 0,0 of the texture uses that color everywhere. // we'll also figure out the eye index here. + bool using_64 = false; { auto draw0 = read_eye_draw(dma); ASSERT(draw0.sprite.uv0[0] == 0); ASSERT(draw0.sprite.uv0[1] == 0); ASSERT(draw0.sprite.uv1[0] == 0); ASSERT(draw0.sprite.uv1[1] == 0); + if (draw0.scissor.y1 - draw0.scissor.y0 == 63) { + using_64 = true; + l_draw.using_64 = true; + r_draw.using_64 = true; + } u32 y0 = (draw0.sprite.xyz0[1] - 512) >> 4; + if (using_64) { + y0 = (draw0.sprite.xyz0[1] - 1024) >> 5; + y0 *= 4; + } pair_idx = y0 / SINGLE_EYE_SIZE; l_draw.pair = pair_idx; r_draw.pair = pair_idx; @@ -257,6 +243,7 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma l_draw.clear_color = tex_val; r_draw.clear_color = tex_val; } else { + fmt::print("clear lookup failed\n"); l_draw.clear_color = 0; r_draw.clear_color = 0; } @@ -265,14 +252,29 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma // up next is the pupil background { l_draw.iris = read_eye_draw(dma); - r_draw.iris = read_eye_draw(dma); l_draw.iris_tex = tex0; - r_draw.iris_tex = tex0; l_draw.iris_gl_tex = *render_state->texture_pool->lookup(adgif0.tex0().tbp0()); - r_draw.iris_gl_tex = l_draw.iris_gl_tex; + + if (dma.current_tag().qwc == 6) { + // change adgif! + auto r_iris_adgif = dma.read_and_advance(); + ASSERT(r_iris_adgif.size_bytes == 96); // 5 adgifs a+d's plus tag + ASSERT(r_iris_adgif.vif0() == 0); + ASSERT(r_iris_adgif.vifcode1().kind == VifCode::Kind::DIRECT); + AdgifHelper r_iris_helper(r_iris_adgif.data + 16); + r_draw.iris = read_eye_draw(dma); + r_draw.iris_tex = + render_state->texture_pool->lookup_gpu_texture(r_iris_helper.tex0().tbp0()); + r_draw.iris_gl_tex = *render_state->texture_pool->lookup(r_iris_helper.tex0().tbp0()); + } else { + // same adgif + r_draw.iris = read_eye_draw(dma); + r_draw.iris_tex = tex0; + r_draw.iris_gl_tex = l_draw.iris_gl_tex; + } } - // now we'll draw the iris on top of that + // now we'll draw the pupil on top of that auto test1 = dma.read_and_advance(); (void)test1; auto adgif1_dma = dma.read_and_advance(); @@ -284,11 +286,26 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma if (tex1 && tex1->get_data_ptr()) { l_draw.pupil = read_eye_draw(dma); - r_draw.pupil = read_eye_draw(dma); l_draw.pupil_tex = tex1; - r_draw.pupil_tex = tex1; l_draw.pupil_gl_tex = *render_state->texture_pool->lookup(adgif1.tex0().tbp0()); - r_draw.pupil_gl_tex = l_draw.pupil_gl_tex; + } + + if (dma.current_tag().qwc == 6) { + auto r_pupil_adgif = dma.read_and_advance(); + ASSERT(r_pupil_adgif.size_bytes == 96); // 5 adgifs a+d's plus tag + ASSERT(r_pupil_adgif.vif0() == 0); + ASSERT(r_pupil_adgif.vifcode1().kind == VifCode::Kind::DIRECT); + AdgifHelper r_pupil_helper(r_pupil_adgif.data + 16); + r_draw.pupil = read_eye_draw(dma); + r_draw.pupil_tex = + render_state->texture_pool->lookup_gpu_texture(r_pupil_helper.tex0().tbp0()); + r_draw.pupil_gl_tex = *render_state->texture_pool->lookup(r_pupil_helper.tex0().tbp0()); + } else { + if (tex1 && tex1->get_data_ptr()) { + r_draw.pupil = read_eye_draw(dma); + r_draw.pupil_tex = tex1; + r_draw.pupil_gl_tex = l_draw.pupil_gl_tex; + } } // and finally the eyelid @@ -303,17 +320,31 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma { l_draw.lid = read_eye_draw(dma); - r_draw.lid = read_eye_draw(dma); l_draw.lid_tex = tex2; - r_draw.lid_tex = tex2; l_draw.lid_gl_tex = *render_state->texture_pool->lookup(adgif2.tex0().tbp0()); + } + + if (dma.current_tag().qwc == 6) { + auto r_lid_adgif = dma.read_and_advance(); + ASSERT(r_lid_adgif.size_bytes == 96); // 5 adgifs a+d's plus tag + ASSERT(r_lid_adgif.vif0() == 0); + ASSERT(r_lid_adgif.vifcode1().kind == VifCode::Kind::DIRECT); + AdgifHelper r_lid_helper(r_lid_adgif.data + 16); + r_draw.lid = read_eye_draw(dma); + r_draw.lid_tex = render_state->texture_pool->lookup_gpu_texture(r_lid_helper.tex0().tbp0()); + r_draw.lid_gl_tex = *render_state->texture_pool->lookup(r_lid_helper.tex0().tbp0()); + } else { + r_draw.lid = read_eye_draw(dma); + r_draw.lid_tex = tex2; r_draw.lid_gl_tex = l_draw.lid_gl_tex; } - auto end = dma.read_and_advance(); - ASSERT(end.size_bytes == 0); - ASSERT(end.vif0() == 0); - ASSERT(end.vif1() == 0); + if (render_state->version == GameVersion::Jak1) { + auto end = dma.read_and_advance(); + ASSERT(end.size_bytes == 0); + ASSERT(end.vif0() == 0); + ASSERT(end.vif1() == 0); + } } return draws; } @@ -336,185 +367,30 @@ void EyeRenderer::handle_eye_dma2(DmaFollower& dma, ASSERT(alpha_setup.vifcode0().kind == VifCode::Kind::NOP); ASSERT(alpha_setup.vifcode1().kind == VifCode::Kind::DIRECT); - // from the add to bucket - ASSERT(dma.current_tag().kind == DmaTag::Kind::NEXT); - ASSERT(dma.current_tag().qwc == 0); - ASSERT(dma.current_tag_vif0() == 0); - ASSERT(dma.current_tag_vif1() == 0); - dma.read_and_advance(); + if (render_state->version == GameVersion::Jak1) { + // from the add to bucket + ASSERT(dma.current_tag().kind == DmaTag::Kind::NEXT); + ASSERT(dma.current_tag().qwc == 0); + ASSERT(dma.current_tag_vif0() == 0); + ASSERT(dma.current_tag_vif1() == 0); + dma.read_and_advance(); + } auto draws = get_draws(dma, render_state); - if (m_use_gpu) { - run_gpu(draws, render_state); - } else { - run_cpu(draws, render_state); - } + run_gpu(draws, render_state); float time_ms = timer.getMs(); m_average_time_ms = m_average_time_ms * 0.95 + time_ms * 0.05; } -////////////////////// -// CPU Drawing -////////////////////// - -u32 bilinear_sample_eye(const u8* tex, float tx, float ty, int texw) { - int tx0 = tx; - int ty0 = ty; - int tx1 = tx0 + 1; - int ty1 = ty0 + 1; - tx1 = std::min(tx1, texw - 1); - ty1 = std::min(ty1, texw - 1); - - u8 tex0[4]; - u8 tex1[4]; - u8 tex2[4]; - u8 tex3[4]; - memcpy(tex0, tex + (4 * (tx0 + ty0 * texw)), 4); - memcpy(tex1, tex + (4 * (tx1 + ty0 * texw)), 4); - memcpy(tex2, tex + (4 * (tx0 + ty1 * texw)), 4); - memcpy(tex3, tex + (4 * (tx1 + ty1 * texw)), 4); - - u8 result[4] = {0, 0, 0, 0}; - float x0w = float(tx1) - tx; - float y0w = float(ty1) - ty; - float weights[4] = {x0w * y0w, (1.f - x0w) * y0w, x0w * (1.f - y0w), (1.f - x0w) * (1.f - y0w)}; - - for (int i = 0; i < 4; i++) { - float total = 0; - total += weights[0] * tex0[i]; - total += weights[1] * tex1[i]; - total += weights[2] * tex2[i]; - total += weights[3] * tex3[i]; - result[i] = total; - } - - // clamp - u32 tex_out; - memcpy(&tex_out, result, 4); - return tex_out; -} - -template -void draw_eye_impl(u32* out, - const EyeRenderer::EyeDraw& draw, - const GpuTexture& tex, - int pair, - int lr, - bool flipx) { - // first, figure out the rectangle we'd cover if there was no scissoring - - int x0 = ((((int)draw.sprite.xyz0[0]) - 512) >> 4); - int x1 = ((((int)draw.sprite.xyz1[0]) - 512) >> 4); - if (flipx) { - std::swap(x0, x1); - } - - int y0 = ((((int)draw.sprite.xyz0[1]) - 512) >> 4); - int y1 = ((((int)draw.sprite.xyz1[1]) - 512) >> 4); - - // then the offset because the game tries to draw to a big texture, but we do an eye at a time - int x_off = lr * SINGLE_EYE_SIZE; - int y_off = pair * SINGLE_EYE_SIZE; - - // apply scissoring bounds - int x0s = std::max(x0, (int)draw.scissor.x0) - x_off; - int y0s = std::max(y0, (int)draw.scissor.y0) - y_off; - int x1s = std::min(x1, (int)draw.scissor.x1) - x_off; - int y1s = std::min(y1, (int)draw.scissor.y1) - y_off; - - // compute inverse lengths (of non-scissored) - float inv_xl = .999f / ((float)(x1 - x0)); - float inv_yl = .999f / ((float)(y1 - y0)); - - // starts - float tx0 = tex.w * (x0s - x0 + x_off) * inv_xl; - float ty0 = tex.h * (y0s - y0 + y_off) * inv_yl; - - // steps - float txs = tex.w * inv_xl; - float tys = tex.h * inv_yl; - - float ty = ty0; - for (int yd = y0s; yd < y1s; yd++) { - float tx = tx0; - for (int xd = x0s; xd < x1s; xd++) { - u32 val; - if (bilinear) { - val = bilinear_sample_eye(tex.get_data_ptr(), tx, ty, tex.w); - } else { - int tc = int(tx) + tex.w * int(ty); - memcpy(&val, tex.get_data_ptr() + (4 * tc), 4); - } - if (blend) { - if ((val >> 24) != 0) { - out[xd + yd * SINGLE_EYE_SIZE] = val; - } - } else { - out[xd + yd * SINGLE_EYE_SIZE] = val; - } - tx += txs; - } - ty += tys; - } -} - -template -void draw_eye(u32* out, - const EyeRenderer::EyeDraw& draw, - const GpuTexture& tex, - int pair, - int lr, - bool flipx, - bool bilinear) { - if (bilinear) { - draw_eye_impl(out, draw, tex, pair, lr, flipx); - } else { - draw_eye_impl(out, draw, tex, pair, lr, flipx); - } -} - -void EyeRenderer::run_cpu(const std::vector& draws, - SharedRenderState* render_state) { - for (auto& draw : draws) { - for (auto& x : m_temp_tex) { - x = draw.clear_color; - } - - if (draw.iris_tex) { - draw_eye(m_temp_tex, draw.iris, *draw.iris_tex, draw.pair, draw.lr, false, - m_use_bilinear); - } - - if (draw.pupil_tex) { - draw_eye(m_temp_tex, draw.pupil, *draw.pupil_tex, draw.pair, draw.lr, false, - m_use_bilinear); - } - - if (draw.lid_tex) { - draw_eye(m_temp_tex, draw.lid, *draw.lid_tex, draw.pair, draw.lr, draw.lr == 1, - m_use_bilinear); - } - - if (m_alpha_hack) { - for (auto& a : m_temp_tex) { - a |= 0xff000000; - } - } - - // update GPU: - auto& tex = m_cpu_eye_textures[draw.pair * 2 + draw.lr]; - glBindTexture(GL_TEXTURE_2D, tex.gl_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, - m_temp_tex); - // make sure they are still in vram - render_state->texture_pool->move_existing_to_vram(tex.gpu_tex, tex.tbp); - } -} - -int add_draw_to_buffer(int idx, const EyeRenderer::EyeDraw& draw, float* data, int pair, int lr) { +int add_draw_to_buffer_32(int idx, + const EyeRenderer::EyeDraw& draw, + float* data, + int pair, + int lr) { int x_off = lr * SINGLE_EYE_SIZE * 16; int y_off = pair * SINGLE_EYE_SIZE * 16; + data[idx++] = draw.sprite.xyz0[0] - x_off; data[idx++] = draw.sprite.xyz0[1] - y_off; data[idx++] = 0; @@ -537,6 +413,36 @@ int add_draw_to_buffer(int idx, const EyeRenderer::EyeDraw& draw, float* data, i return idx; } +int add_draw_to_buffer_64(int idx, + const EyeRenderer::EyeDraw& draw, + float* data, + int pair, + int lr) { + int x_off = lr * SINGLE_EYE_SIZE * 32; + int y_off = (pair / 4) * SINGLE_EYE_SIZE * 32; + + data[idx++] = (draw.sprite.xyz0[0] - x_off) / 2; + data[idx++] = (draw.sprite.xyz0[1] - y_off) / 2; + data[idx++] = 0; + data[idx++] = 0; + + data[idx++] = (draw.sprite.xyz1[0] - x_off) / 2; + data[idx++] = (draw.sprite.xyz0[1] - y_off) / 2; + data[idx++] = 1; + data[idx++] = 0; + + data[idx++] = (draw.sprite.xyz0[0] - x_off) / 2; + data[idx++] = (draw.sprite.xyz1[1] - y_off) / 2; + data[idx++] = 0; + data[idx++] = 1; + + data[idx++] = (draw.sprite.xyz1[0] - x_off) / 2; + data[idx++] = (draw.sprite.xyz1[1] - y_off) / 2; + data[idx++] = 1; + data[idx++] = 1; + return idx; +} + void EyeRenderer::run_gpu(const std::vector& draws, SharedRenderState* render_state) { if (draws.empty()) { @@ -549,10 +455,21 @@ void EyeRenderer::run_gpu(const std::vector& draws, // the first thing we'll do is prepare the vertices int buffer_idx = 0; for (const auto& draw : draws) { - buffer_idx = add_draw_to_buffer(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr); - buffer_idx = - add_draw_to_buffer(buffer_idx, draw.pupil, m_gpu_vertex_buffer, draw.pair, draw.lr); - buffer_idx = add_draw_to_buffer(buffer_idx, draw.lid, m_gpu_vertex_buffer, draw.pair, draw.lr); + if (draw.using_64) { + buffer_idx = + add_draw_to_buffer_64(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr); + buffer_idx = + add_draw_to_buffer_64(buffer_idx, draw.pupil, m_gpu_vertex_buffer, draw.pair, draw.lr); + buffer_idx = + add_draw_to_buffer_64(buffer_idx, draw.lid, m_gpu_vertex_buffer, draw.pair, draw.lr); + } else { + buffer_idx = + add_draw_to_buffer_32(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr); + buffer_idx = + add_draw_to_buffer_32(buffer_idx, draw.pupil, m_gpu_vertex_buffer, draw.pair, draw.lr); + buffer_idx = + add_draw_to_buffer_32(buffer_idx, draw.lid, m_gpu_vertex_buffer, draw.pair, draw.lr); + } } ASSERT(buffer_idx <= VTX_BUFFER_FLOATS); int check = buffer_idx; @@ -626,6 +543,21 @@ void EyeRenderer::run_gpu(const std::vector& draws, glBindBuffer(GL_ARRAY_BUFFER, 0); } +std::optional EyeRenderer::lookup_eye_texture(u8 eye_id) { + eye_id = (eye_id % 40); + if ((s32)eye_id >= NUM_EYE_PAIRS * 2) { + fmt::print("lookup eye failed for {} (1)\n", eye_id); + return {}; + } + auto* gpu_tex = m_gpu_eye_textures[eye_id].gpu_tex; + if (gpu_tex) { + return gpu_tex->gpu_textures.at(0).gl; + } else { + fmt::print("lookup eye failed for {}\n", eye_id); + return {}; + } +} + ////////////////////// // DMA Decode ////////////////////// diff --git a/game/graphics/opengl_renderer/EyeRenderer.h b/game/graphics/opengl_renderer/EyeRenderer.h index 47deaeae9c..fe4f4d48ce 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.h +++ b/game/graphics/opengl_renderer/EyeRenderer.h @@ -7,7 +7,7 @@ #include "game/graphics/pipelines/opengl.h" constexpr int EYE_BASE_BLOCK = 8160; -constexpr int NUM_EYE_PAIRS = 11; +constexpr int NUM_EYE_PAIRS = 20; constexpr int SINGLE_EYE_SIZE = 32; class EyeRenderer : public BucketRenderer { @@ -19,6 +19,7 @@ class EyeRenderer : public BucketRenderer { void init_textures(TexturePool& texture_pool, GameVersion) override; void handle_eye_dma2(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof); + std::optional lookup_eye_texture(u8 eye_id); struct SpriteInfo { u8 a; @@ -46,22 +47,8 @@ class EyeRenderer : public BucketRenderer { std::string m_debug; float m_average_time_ms = 0; - bool m_use_bilinear = true; - bool m_alpha_hack = true; - - u32 m_temp_tex[SINGLE_EYE_SIZE * SINGLE_EYE_SIZE]; - - bool m_use_gpu = true; - - struct CpuEyeTex { - u64 gl_tex; - GpuTexture* gpu_tex; - u32 tbp; - }; - CpuEyeTex m_cpu_eye_textures[NUM_EYE_PAIRS * 2]; - struct GpuEyeTex { - GpuTexture* gpu_tex; + GpuTexture* gpu_tex = nullptr; u32 tbp; FramebufferTexturePair fb; @@ -78,6 +65,7 @@ class EyeRenderer : public BucketRenderer { struct SingleEyeDraws { int lr; int pair; + bool using_64 = false; int tex_slot() const { return pair * 2 + lr; } u32 clear_color; @@ -95,6 +83,5 @@ class EyeRenderer : public BucketRenderer { }; std::vector get_draws(DmaFollower& dma, SharedRenderState* render_state); - void run_cpu(const std::vector& draws, SharedRenderState* render_state); void run_gpu(const std::vector& draws, SharedRenderState* render_state); }; diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index e9366507b4..436722f5d9 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -322,6 +322,10 @@ void OpenGLRenderer::init_bucket_renderers_jak2() { BucketId::DEBUG_NO_ZBUF2, 0x8000); init_bucket_renderer("debug3", BucketCategory::OTHER, BucketId::DEBUG3, 0x8000); + auto eye_renderer = std::make_unique("eyes", 0); + m_render_state.eye_renderer = eye_renderer.get(); + m_jak2_eye_renderer = std::move(eye_renderer); + // for now, for any unset renderers, just set them to an EmptyBucketRenderer. for (size_t i = 0; i < m_bucket_renderers.size(); i++) { if (!m_bucket_renderers[i]) { @@ -332,6 +336,10 @@ void OpenGLRenderer::init_bucket_renderers_jak2() { m_bucket_renderers[i]->init_shaders(m_render_state.shaders); m_bucket_renderers[i]->init_textures(*m_render_state.texture_pool, GameVersion::Jak2); } + + m_jak2_eye_renderer->init_shaders(m_render_state.shaders); + m_jak2_eye_renderer->init_textures(*m_render_state.texture_pool, GameVersion::Jak2); + m_render_state.loader->load_common(*m_render_state.texture_pool, "GAME"); } /*! @@ -697,6 +705,12 @@ void OpenGLRenderer::draw_renderer_selection_window() { m_render_state.texture_pool->draw_debug_window(); ImGui::TreePop(); } + if (m_jak2_eye_renderer) { + if (ImGui::TreeNode("Eyes")) { + m_jak2_eye_renderer->draw_debug_window(); + ImGui::TreePop(); + } + } ImGui::End(); } diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.h b/game/graphics/opengl_renderer/OpenGLRenderer.h index 7e7f70ad92..d77b1e62a8 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.h +++ b/game/graphics/opengl_renderer/OpenGLRenderer.h @@ -161,5 +161,6 @@ class OpenGLRenderer { Fbo* render_fbo = nullptr; // the selected fbo from the three above to use for rendering } m_fbo_state; + std::unique_ptr m_jak2_eye_renderer; GameVersion m_version; }; diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 3ab5582795..10b2738065 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -2,6 +2,7 @@ #include "common/global_profiler/GlobalProfiler.h" +#include "game/graphics/opengl_renderer/EyeRenderer.h" #include "game/graphics/opengl_renderer/background/background_common.h" #include "third-party/imgui/imgui.h" @@ -455,14 +456,6 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, // allocate lights u32 lights = alloc_lights(current_lights); - for (int i = 0; i < 3; i++) { - float debug_length = current_lights.direction0[i] * current_lights.direction0[i] + - current_lights.direction1[i] * current_lights.direction1[i] + - current_lights.direction2[i] * current_lights.direction2[i]; - if (debug_length > 0.01 && debug_length < 0.98) { - fmt::print("likely incorrect merc light direction {}\n", debug_length); - } - } // loop over effects, creating draws for each for (size_t ei = 0; ei < model->effects.size(); ei++) { @@ -930,7 +923,7 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw, draw->first_index = mdraw.first_index; draw->index_count = mdraw.index_count; draw->mode = mdraw.mode; - draw->texture = mdraw.tree_tex_id; + draw->texture = mdraw.eye_id == 0xff ? mdraw.tree_tex_id : (0xffffff00 | mdraw.eye_id); draw->first_bone = first_bone; draw->light_idx = lights; draw->num_triangles = mdraw.num_triangles; @@ -1041,13 +1034,14 @@ void Merc2::do_draws(const Draw* draw_array, const Uniforms& uniforms, ScopedProfilerNode& prof, bool set_fade, - SharedRenderState*) { + SharedRenderState* render_state) { glBindVertexArray(m_vao); int last_tex = -1; int last_light = -1; bool normal_vtx_buffer_bound = true; for (u32 di = 0; di < num_draws; di++) { auto& draw = draw_array[di]; + auto mode = draw.mode; if (draw.flags & MOD_VTX) { glBindVertexArray(draw.mod_vtx_buffer.vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lev->merc_indices); @@ -1065,6 +1059,13 @@ void Merc2::do_draws(const Draw* draw_array, if ((int)draw.texture != last_tex) { if (draw.texture < lev->textures.size()) { glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture)); + } else if ((draw.texture & 0xffffff00) == 0xffffff00) { + auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff); + if (maybe_eye) { + glBindTexture(GL_TEXTURE_2D, *maybe_eye); + } + + mode.set_filt_enable(false); } else { fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture); } @@ -1081,7 +1082,7 @@ void Merc2::do_draws(const Draw* draw_array, set_uniform(uniforms.light_ambient, m_lights_buffer[draw.light_idx].ambient); last_light = draw.light_idx; } - setup_opengl_from_draw_mode(draw.mode, GL_TEXTURE0, true); + setup_opengl_from_draw_mode(mode, GL_TEXTURE0, true); glUniform1i(uniforms.decal, draw.mode.get_decal()); diff --git a/game/kernel/jak2/kscheme.cpp b/game/kernel/jak2/kscheme.cpp index b646786ba8..789abf19d9 100644 --- a/game/kernel/jak2/kscheme.cpp +++ b/game/kernel/jak2/kscheme.cpp @@ -729,7 +729,6 @@ Ptr alloc_and_init_type(Ptr>> sym, if (!force_global_type && u32_in_fixed_sym(FIX_SYM_LOADING_LEVEL) != u32_in_fixed_sym(FIX_SYM_GLOBAL_HEAP)) { - printf("using level types!\n"); // added u32 type_list_ptr = LevelTypeList->value(); if (type_list_ptr == 0) { // we don't have a type-list... just alloc on global @@ -954,7 +953,6 @@ u64 new_type(u32 symbol, u32 parent, u64 flags) { MsgWarn("dkernel: loading-level init of type %s, but was interned global (this is okay)\n", sym_to_string(new_type_obj->symbol)->data()); } else { - printf("case 2 for new_type level types\n"); new_type_obj->memusage_method.offset = original_type_list_value; } } diff --git a/goal_src/jak1/engine/gfx/foreground/bones.gc b/goal_src/jak1/engine/gfx/foreground/bones.gc index dd2d39315d..bc80442024 100644 --- a/goal_src/jak1/engine/gfx/foreground/bones.gc +++ b/goal_src/jak1/engine/gfx/foreground/bones.gc @@ -1405,19 +1405,6 @@ ) ((nonzero? (-> geom effect effect-idx envmap-usage)) ;; if we need envmap, set it up. - - ;; hack: - ;; the game switches from mercneric to merc after the envmap is faded out. - ;; this would normally be fine in the PC port too, but we want the eyes to remain - ;; using mercneric (dynamic texture updates) in PC. - ;; so we force mercneric on models with envmap. - ;; it seems to work so far... - (#when PC_PORT - (when (and (-> geom header eye-ctrl) - (nonzero? (-> geom header eye-ctrl))) - (set! pc-force-mercneric #t) - ) - ) (let* ((v1-83 (-> geom effect effect-idx extra-info)) ;; pointer to envmap tint data (v1-84 (the-as structure (+ (the-as uint v1-83) (* (-> v1-83 envmap-tint-offset) 16)))) diff --git a/goal_src/jak2/engine/draw/drawable.gc b/goal_src/jak2/engine/draw/drawable.gc index ee37965814..9182db88fd 100644 --- a/goal_src/jak2/engine/draw/drawable.gc +++ b/goal_src/jak2/engine/draw/drawable.gc @@ -2098,7 +2098,7 @@ ;; unclear why eyes are here... maybe they rely on textures that were just remapped. ; (-> *display* frames (-> *display* on-screen) global-buf) - ; (update-eyes) + (update-eyes) ;; end each normal bucket with the standard GS state reset diff --git a/goal_src/jak2/engine/gfx/foreground/eye-h.gc b/goal_src/jak2/engine/gfx/foreground/eye-h.gc index 15dffd2fa5..f02da921fb 100644 --- a/goal_src/jak2/engine/gfx/foreground/eye-h.gc +++ b/goal_src/jak2/engine/gfx/foreground/eye-h.gc @@ -5,6 +5,9 @@ ;; name in dgo: eye-h ;; dgos: ENGINE, GAME +(define-extern update-eyes (function none)) + + ;; DECOMP BEGINS (deftype eye (structure) diff --git a/goal_src/jak2/engine/gfx/sky/sky-tng.gc b/goal_src/jak2/engine/gfx/sky/sky-tng.gc index 1e2fc43dad..d461a6d459 100644 --- a/goal_src/jak2/engine/gfx/sky/sky-tng.gc +++ b/goal_src/jak2/engine/gfx/sky/sky-tng.gc @@ -37,6 +37,8 @@ vf30: cam 1 (premultiplied by hmge) vf31: cam 0 (premultiplied by hmge) |# +;; TODO: cloud drawing is disabled. + ;; DECOMP BEGINS ;; set the xy component of vf24 (texture offset for large poly renderer) to values / 65,536 @@ -941,11 +943,11 @@ vf31: cam 0 (premultiplied by hmge) (moon-dma (the-as sky-work s4-0) s3-0) (draw-haze (the-as sky-work s4-0) s3-0) (when (nonzero? *sky-texture-anim-array*) ;; added check - (draw-clouds (the-as sky-work s4-0) s3-0) + ; (draw-clouds (the-as sky-work s4-0) s3-0) ;; DISABLED! ) (draw-base (the-as sky-work s4-0) s3-0) (when (nonzero? *sky-texture-anim-array*) ;; added check - (draw-fog (the-as sky-work s4-0) s3-0) + ;(draw-fog (the-as sky-work s4-0) s3-0) ) (let ((a3-5 (-> s3-0 base))) (let ((v1-45 (the-as dma-packet (-> s3-0 base)))) diff --git a/goal_src/jak2/engine/gfx/texture/texture.gc b/goal_src/jak2/engine/gfx/texture/texture.gc index 46029bfe57..f3f94cc5f8 100644 --- a/goal_src/jak2/engine/gfx/texture/texture.gc +++ b/goal_src/jak2/engine/gfx/texture/texture.gc @@ -1225,9 +1225,9 @@ additionally, some texture pages have a chunk system that allows more specific c ) ;; not used at all. we don't set merc masks on PC, so this should happen most of the time. - (when (not any-uploads) - (return 0) - ) + ; (when (not any-uploads) + ; (return 0) + ; ) ;; but non-merc users of this function (like map/hud) will get here @@ -1670,7 +1670,7 @@ additionally, some texture pages have a chunk system that allows more specific c ) (((tpage-category pris)) ;; pris work like normal, but only 1 tpage. - (set! (-> lev upload-size 1) (upload-vram-pages-pris + (set! (-> lev upload-size 1) (upload-vram-pages-pris-pc pool (-> pool segment-common) tpage @@ -1700,7 +1700,7 @@ additionally, some texture pages have a chunk system that allows more specific c ) (((tpage-category pris2)) ;; pris2 is normal, 1 tpage. - (set! (-> lev upload-size 6) (upload-vram-pages-pris + (set! (-> lev upload-size 6) (upload-vram-pages-pris-pc pool (-> pool segment-common) tpage