mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
[jak3] Fix eye slot assignment and textures (#3603)
I found two issues with Jak 3 eyes. The first was simple - we were missing a `-pc` texture upload in `texture.gc` for `pris2` textures, which has eye textures for a few characters, like torn or damas. The second was a little more annoying. Unlike jak 2 and jak 1, jak 3 can dynamically assign eye slots when merc models are loaded. This involves modifying eye data to tell the eye renderer where to render, and modifying the merc model's adgif shaders to point to the correct eye texture. The modification to the merc adgif shader is problematic since our PC port of merc assumes this slot is constant. My solution here was to bypass this whole slot system entirely for jak 3. I modified the GOAL eye renderer to tell the c++ eye renderer the name of the merc-ctrl containing the eye. Then, the PC C++ Merc renderer can just look up the merc-ctrl by name. To make this fit nicely in the existing memory layout, I used a 64-bit fnv hash of the name. (which honestly is how we should have handled a lot of other texture/model names stuff...) Unrelated fix to Overlord2 so it handles the case where file size changes after the game starts, I had this in jak2/jak1 and forgot it for jak 3.
This commit is contained in:
parent
e81431bd21
commit
9d80ada016
19
common/util/fnv.h
Normal file
19
common/util/fnv.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#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());
|
||||
}
|
|
@ -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::SingleEyeDraws> 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<SingleEyeDraws>& 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<u64> EyeRenderer::lookup_eye_texture(u8 eye_id) {
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<u64> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,10 +22,11 @@ class EyeRenderer : public BucketRenderer {
|
|||
|
||||
void handle_eye_dma2(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
|
||||
std::optional<u64> lookup_eye_texture(u8 eye_id);
|
||||
std::optional<u64> 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;
|
||||
|
|
|
@ -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<GLuint>* 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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue