diff --git a/common/util/fnv.h b/common/util/fnv.h new file mode 100644 index 0000000000..7ec0320db0 --- /dev/null +++ b/common/util/fnv.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "common/common_types.h" + +inline u64 fnv64(const void* data, u64 len) { + u64 ret = 0xcbf29ce484222325; + const auto* ptr = (const u8*)data; + for (u64 i = 0; i < len; i++) { + ret = 1099511628211 * (((u64)*ptr) ^ ret); + ptr++; + } + return ret; +} + +inline u64 fnv64(const std::string& str) { + return fnv64(str.data(), str.length()); +} \ No newline at end of file diff --git a/game/graphics/opengl_renderer/EyeRenderer.cpp b/game/graphics/opengl_renderer/EyeRenderer.cpp index 10d74cd9ab..c4cca5c2e7 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.cpp +++ b/game/graphics/opengl_renderer/EyeRenderer.cpp @@ -186,7 +186,7 @@ EyeRenderer::SpriteInfo decode_sprite(const DmaTransfer& dma) { memcpy(&result.a, dma.data + 16 + 12, 1); // a // uv0 - memcpy(&result.uv0[0], &dma.data[32], 8); + memcpy(&result.uv0, &dma.data[32], 8); // xyz0 memcpy(&result.xyz0[0], &dma.data[48], 12); @@ -236,8 +236,11 @@ std::vector EyeRenderer::get_draws(DmaFollower& dma 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.uv0[0] == 0); + // ASSERT(draw0.sprite.uv0[1] == 0); + // printf("hashed name is 0x%x 0x%x\n", draw0.sprite.uv0[0], draw0.sprite.uv0[1]); + l_draw.fnv_name_hash = draw0.sprite.uv0; + r_draw.fnv_name_hash = draw0.sprite.uv0; ASSERT(draw0.sprite.uv1[0] == 0); ASSERT(draw0.sprite.uv1[1] == 0); if (draw0.scissor.y1 - draw0.scissor.y0 == 63) { @@ -509,7 +512,9 @@ void EyeRenderer::run_gpu(const std::vector& draws, buffer_idx = 0; for (size_t draw_idx = 0; draw_idx < draws.size(); draw_idx++) { const auto& draw = draws[draw_idx]; - const auto& out_tex = m_gpu_eye_textures[draw.tex_slot()]; + auto& out_tex = m_gpu_eye_textures[draw.tex_slot()]; + out_tex.fnv_name_hash = draw.fnv_name_hash; + out_tex.lr = draw.lr; // first, the clear float clear[4] = {0, 0, 0, 0}; @@ -574,15 +579,30 @@ std::optional EyeRenderer::lookup_eye_texture(u8 eye_id) { } } +std::optional EyeRenderer::lookup_eye_texture_hash(u64 hash, bool lr) { + for (auto& slot : m_gpu_eye_textures) { + if (slot.fnv_name_hash == hash && slot.lr == lr) { + auto* gpu_tex = slot.gpu_tex; + if (gpu_tex) { + return gpu_tex->gpu_textures.at(0).gl; + } else { + fmt::print("lookup eye failed for {} (1)\n", hash); + return {}; + } + } + } + fmt::print("lookup eye failed for {} (2)\n", hash); + return {}; +} + ////////////////////// // DMA Decode ////////////////////// std::string EyeRenderer::SpriteInfo::print() const { std::string result; - result += - fmt::format("a: {:x} uv: ({}, {}), ({}, {}) xyz: ({}, {}, {}), ({}, {}, {})", a, uv0[0], - uv0[1], uv1[0], uv1[1], xyz0[0], xyz0[1], xyz0[2], xyz1[0], xyz1[1], xyz1[2]); + result += fmt::format("a: {:x} uv: ({}), ({}, {}) xyz: ({}, {}, {}), ({}, {}, {})", a, uv0, + uv1[0], uv1[1], xyz0[0], xyz0[1], xyz0[2], xyz1[0], xyz1[1], xyz1[2]); return result; } diff --git a/game/graphics/opengl_renderer/EyeRenderer.h b/game/graphics/opengl_renderer/EyeRenderer.h index b9c24c504d..9a1d151826 100644 --- a/game/graphics/opengl_renderer/EyeRenderer.h +++ b/game/graphics/opengl_renderer/EyeRenderer.h @@ -22,10 +22,11 @@ class EyeRenderer : public BucketRenderer { void handle_eye_dma2(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof); std::optional lookup_eye_texture(u8 eye_id); + std::optional lookup_eye_texture_hash(u64 hash, bool lr); struct SpriteInfo { u8 a; - u32 uv0[2]; + u64 uv0; // stores hashed name of merc-ctrl that reads this eye. u32 uv1[2]; u32 xyz0[3]; u32 xyz1[3]; @@ -53,6 +54,8 @@ class EyeRenderer : public BucketRenderer { GpuTexture* gpu_tex = nullptr; u32 tbp; FramebufferTexturePair fb; + u64 fnv_name_hash = 0; + bool lr = false; // note: eye texture increased to 128x128 (originally 32x32) here. GpuEyeTex() : fb(128, 128, GL_UNSIGNED_INT_8_8_8_8_REV) {} @@ -65,6 +68,7 @@ class EyeRenderer : public BucketRenderer { GLuint m_gl_vertex_buffer; struct SingleEyeDraws { + u64 fnv_name_hash = 0; int lr; int pair; bool using_64 = false; diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 896c3203f6..b359f78707 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -7,6 +7,7 @@ #endif #include "common/global_profiler/GlobalProfiler.h" +#include "common/util/fnv.h" #include "game/graphics/opengl_renderer/EyeRenderer.h" #include "game/graphics/opengl_renderer/background/background_common.h" @@ -54,6 +55,8 @@ std::mutex g_merc_data_mutex; Merc2::Merc2(ShaderLibrary& shaders, const std::vector* anim_slot_array) : m_anim_slot_array(anim_slot_array) { + ASSERT(fnv64("the quick brown fox jumps over the lazy dog") == 0x7404cea13ff89bb0); + // Set up main vertex array. This will point to the data stored in the .FR3 level file, and will // be uploaded to the GPU by the Loader. glGenVertexArrays(1, &m_vao); @@ -612,6 +615,8 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, u32 lights = alloc_lights(current_lights); stats->num_lights++; + u64 hash = fnv64(model->name); + // loop over effects, creating draws for each for (size_t ei = 0; ei < model->effects.size(); ei++) { // game has disabled it? @@ -636,7 +641,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, // do fixed draws: for (auto& fdraw : effect.mod.fix_draw) { alloc_normal_draw(fdraw, ignore_alpha, lev_bucket, first_bone, lights, uses_water, - model_disables_fog); + model_disables_fog, hash); if (should_envmap) { try_alloc_envmap_draw(fdraw, effect.envmap_mode, effect.envmap_texture, lev_bucket, fade_buffer + 4 * ei, first_bone, lights, uses_water); @@ -646,7 +651,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, // do mod draws for (auto& mdraw : effect.mod.mod_draw) { auto n = alloc_normal_draw(mdraw, ignore_alpha, lev_bucket, first_bone, lights, uses_water, - model_disables_fog); + model_disables_fog, hash); // modify the draw, set the mod flag and point it to the opengl buffer n->flags |= MOD_VTX; n->mod_vtx_buffer = mod_opengl_buffers[ei]; @@ -668,7 +673,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, fade_buffer + 4 * ei, first_bone, lights, uses_water); } alloc_normal_draw(draw, ignore_alpha, lev_bucket, first_bone, lights, uses_water, - model_disables_fog); + model_disables_fog, hash); } } } @@ -1056,6 +1061,7 @@ Merc2::Draw* Merc2::try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw, draw->first_index = mdraw.first_index; draw->index_count = mdraw.index_count; draw->mode = envmap_mode; + draw->hash = 0; if (jak1_water_mode) { draw->mode.enable_ab(); draw->mode.disable_depth_write(); @@ -1076,12 +1082,14 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw, u32 first_bone, u32 lights, bool jak1_water_mode, - bool disable_fog) { + bool disable_fog, + u64 hash) { Draw* draw = &lev_bucket->draws[lev_bucket->next_free_draw++]; draw->flags = 0; draw->first_index = mdraw.first_index; draw->index_count = mdraw.index_count; draw->mode = mdraw.mode; + draw->hash = hash; if (jak1_water_mode) { draw->mode.set_ab(true); draw->mode.disable_depth_write(); @@ -1247,10 +1255,19 @@ void Merc2::do_draws(const Draw* draw_array, if (draw.texture < (int)lev->textures.size() && draw.texture >= 0) { glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture)); } else if ((draw.texture & 0xffffff00) == 0xefffff00) { - auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff); - if (maybe_eye) { - glBindTexture(GL_TEXTURE_2D, *maybe_eye); + if (render_state->version >= GameVersion::Jak3) { + auto maybe_eye = + render_state->eye_renderer->lookup_eye_texture_hash(draw.hash, (draw.texture & 1)); + if (maybe_eye) { + glBindTexture(GL_TEXTURE_2D, *maybe_eye); + } + } else { + auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff); + if (maybe_eye) { + glBindTexture(GL_TEXTURE_2D, *maybe_eye); + } } + use_mipmaps_for_filtering = false; } else if (draw.texture < 0) { int slot = -(draw.texture + 1); diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index 166d7d05af..9705907b66 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -192,6 +192,7 @@ class Merc2 { u8 fade[4]; // no strip hack for custom models u8 no_strip; + u64 hash; }; struct LevelDrawBucket { @@ -213,7 +214,8 @@ class Merc2 { u32 first_bone, u32 lights, bool jak1_water_mode, - bool disable_fog); + bool disable_fog, + u64 hash); Draw* try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw, const DrawMode& envmap_mode, diff --git a/game/overlord/jak3/iso_cd.cpp b/game/overlord/jak3/iso_cd.cpp index a10467fb86..4d731df8ab 100644 --- a/game/overlord/jak3/iso_cd.cpp +++ b/game/overlord/jak3/iso_cd.cpp @@ -381,7 +381,15 @@ ISOFileDef* CISOCDFileSystem::FindIN(const jak3::ISOName* name) { * Get the length of a file, in bytes. */ int CISOCDFileSystem::GetLength(const jak3::ISOFileDef* file) { - return file->length; + // return file->length; + lg::info("getlength"); + file_util::assert_file_exists(file->full_path.c_str(), "CISOCDFileSystem GetLength"); + FILE* fp = file_util::open_file(file->full_path.c_str(), "rb"); + ASSERT(fp); + fseek(fp, 0, SEEK_END); + uint32_t len = ftell(fp); + fclose(fp); + return len; } /*! diff --git a/goal_src/jak3/engine/gfx/foreground/eye.gc b/goal_src/jak3/engine/gfx/foreground/eye.gc index 7c8458ec31..fd92bd5bc3 100644 --- a/goal_src/jak3/engine/gfx/foreground/eye.gc +++ b/goal_src/jak3/engine/gfx/foreground/eye.gc @@ -7,6 +7,28 @@ ;; DECOMP BEGINS +;; og:preserve-this +(defun fnv64 ((data pointer) (length int)) + "64-bit hash for strings. This is extremely unlikely to have collisions for all strings + in the game. (modern ND games rely on this and use this hash function as unique ID's for + strings." + (let* ((ret (the uint #xcbf29ce484222325)) + (ptr (the (pointer uint8) data)) + (end (&+ ptr length))) + (while (!= ptr end) + (set! ret (imul64 (logxor (-> ptr) ret) 1099511628211)) + (&+! ptr 1) + ) + ret + ) + ) + +(defun fnv64-string ((str string)) + (fnv64 (-> str data) (length str)) + ) + +(define *current-eye-merc-ctrl-name* (the string #f)) + (define *eye-work* (new 'static 'eye-work :sprite-tmpl (new 'static 'dma-gif-packet :dma-vif (new 'static 'dma-packet @@ -137,6 +159,10 @@ (set! (-> (the-as (inline-array vector4w) v1-25) 1 quad) (-> *eye-work* sprite-tmpl quad 1)) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 2) 128 128 128 128) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 3) 0 0 0 0) + ;; og:preserve-this: stash the hashed name of the merc-ctrl that will read these eyes: + (set! (-> (the (pointer uint64) v1-25) 6) + (fnv64-string *current-eye-merc-ctrl-name*) + ) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 4) (* s4-0 16) (the-as int (* s3-0 16)) #xffffff 0) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 5) 0 0 0 0) (set-vector! @@ -425,6 +451,12 @@ (set! (-> (the-as (inline-array vector4w) v1-25) 1 quad) (-> *eye-work* sprite-tmpl quad 1)) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 2) 128 128 128 128) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 3) 0 0 0 0) + + ;; og:preserve-this: stash the hashed name of the merc-ctrl that will read these eyes: + (set! (-> (the (pointer uint64) v1-25) 6) + (fnv64-string *current-eye-merc-ctrl-name*) + ) + (set-vector! (-> (the-as (inline-array vector4w) v1-25) 4) (* s4-0 16) (the-as int (* s3-0 16)) #xffffff 0) (set-vector! (-> (the-as (inline-array vector4w) v1-25) 5) 0 0 0 0) (set-vector! @@ -680,6 +712,7 @@ (logtest? (-> (the-as process-drawable v1-7) skel status) (joint-control-status eye-anim)) (logtest? (-> (the-as process-drawable v1-7) draw status) (draw-control-status on-screen)) ) + (set! *current-eye-merc-ctrl-name* (-> (the process-drawable v1-7) draw mgeo name)) (when (-> s5-0 shaders) (when (not (paused?)) (cond diff --git a/goal_src/jak3/engine/gfx/texture/texture.gc b/goal_src/jak3/engine/gfx/texture/texture.gc index b94275c493..42b0e92c5e 100644 --- a/goal_src/jak3/engine/gfx/texture/texture.gc +++ b/goal_src/jak3/engine/gfx/texture/texture.gc @@ -1383,7 +1383,7 @@ (+! (-> lev upload-size 8) (upload-vram-pages pool (-> pool segment-common) a2-1 (tex-upload-mode seg0-1-2) bucket) ) - (set! (-> lev upload-size 6) (upload-vram-pages-pris + (set! (-> lev upload-size 6) (upload-vram-pages-pris-pc pool (-> pool segment-common) a2-1