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