diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index 97018729ee..84c9282e4d 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -316,6 +316,7 @@ void MercModel::serialize(Serializer& ser) { ser.from_ptr(&max_bones); ser.from_ptr(&st_vif_add); ser.from_ptr(&xyz_scale); + ser.from_ptr(&st_magic); } void MercModelGroup::serialize(Serializer& ser) { diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index 5da9975aec..5823d57470 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 = 26; +constexpr int TFRAG3_VERSION = 27; // These vertices should be uploaded to the GPU at load time and don't change struct PreloadedVertex { @@ -428,6 +428,7 @@ struct MercModel { u32 max_bones; u32 st_vif_add; float xyz_scale; + float st_magic; void serialize(Serializer& ser); void memory_usage(MemoryUsageTracker* tracker) const; }; diff --git a/decompiler/level_extractor/MercData.h b/decompiler/level_extractor/MercData.h index b2c7f4ea20..b16569c9c3 100644 --- a/decompiler/level_extractor/MercData.h +++ b/decompiler/level_extractor/MercData.h @@ -179,6 +179,7 @@ struct MercExtraInfo { std::optional shader; }; +constexpr int kTextureScrollEffectBit = 1; constexpr int kRippleEffectBit = 4; // true in jak 1 and jak 2 struct MercEffect { diff --git a/decompiler/level_extractor/extract_merc.cpp b/decompiler/level_extractor/extract_merc.cpp index 198c8e4646..4c179b6725 100644 --- a/decompiler/level_extractor/extract_merc.cpp +++ b/decompiler/level_extractor/extract_merc.cpp @@ -3,6 +3,7 @@ #include "common/log/log.h" #include "common/util/FileUtil.h" #include "common/util/colors.h" +#include "common/util/string_util.h" #include "decompiler/level_extractor/MercData.h" #include "decompiler/level_extractor/extract_common.h" @@ -866,6 +867,10 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect, can_be_modified = true; } + if (input_effect.effect_bits & kTextureScrollEffectBit) { + can_be_modified = true; + } + handle_frag(debug_name, ctrl_header, frag, frag_ctrl, merc_state, result.vertices, merc_memories[memory_buffer_toggle], can_be_modified, combined_lump4_addr, fi); u32 vert_count = frag.lump4_unpacked.size() / 3; @@ -1270,6 +1275,7 @@ void extract_merc(const ObjectFileData& ag_data, pc_ctrl.max_draws = 0; pc_ctrl.max_bones = 0; pc_ctrl.st_vif_add = ctrl.header.st_vif_add; + pc_ctrl.st_magic = u32_as_float(ctrl.header.st_magic); pc_ctrl.xyz_scale = ctrl.header.xyz_scale; for (size_t ei = 0; ei < ctrls[ci].effects.size(); ei++) { diff --git a/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp b/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp index 9eee90fad2..c165c1d26c 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp +++ b/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp @@ -129,7 +129,7 @@ void Generic2::setup_opengl_for_draw_mode(const DrawMode& draw_mode, // (Cs - Cd) * As + Cd // Cs * As + (1 - As) * Cd // s, d - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); glBlendEquation(GL_FUNC_ADD); } else if (draw_mode.get_alpha_blend() == DrawMode::AlphaBlend::SRC_0_SRC_DST) { // (Cs - 0) * As + Cd @@ -159,7 +159,7 @@ void Generic2::setup_opengl_for_draw_mode(const DrawMode& draw_mode, // (Cs - 0) * Ad + Cd glBlendFunc(GL_DST_ALPHA, GL_ONE); glBlendEquation(GL_FUNC_ADD); - color_mult = 0.5f; + color_mult = 1.0f; } else if (draw_mode.get_alpha_blend() == DrawMode::AlphaBlend::SRC_0_FIX_DST) { glBlendEquation(GL_FUNC_ADD); glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ZERO); diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index 10b2738065..0723900f8a 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -215,6 +215,13 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, memcpy(¤t_lights, input_data, sizeof(VuLights)); input_data += sizeof(VuLights); + u64 uses_water = 0; + if (render_state->version == GameVersion::Jak1) { + // jak 1 figures out water at runtime sadly + memcpy(&uses_water, input_data, 8); + input_data += 16; + } + // Next part is the matrix slot string. The game sends us a bunch of bone matrices, // but they may not be in order, or include all bones. The matrix slot string tells // us which bones go where. (the game doesn't go in order because it follows the merc format) @@ -347,16 +354,20 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, // loop over vertices in the fragment and unpack for (u32 w = my_u4_count / 4; w < (my_l4_count / 4) - 2; w += 3) { - // just want positions for now. + // positions u32 q0w = 0x4b010000 + frag[w * 4 + (0 * 4) + 3]; u32 q1w = 0x4b010000 + frag[w * 4 + (1 * 4) + 3]; u32 q2w = 0x4b010000 + frag[w * 4 + (2 * 4) + 3]; - // and maybe normals + // normals u32 q0z = 0x47800000 + frag[w * 4 + (0 * 4) + 2]; u32 q1z = 0x47800000 + frag[w * 4 + (1 * 4) + 2]; u32 q2z = 0x47800000 + frag[w * 4 + (2 * 4) + 2]; + // uvs + u32 q2x = model->st_vif_add + frag[w * 4 + (2 * 4) + 0]; + u32 q2y = model->st_vif_add + frag[w * 4 + (2 * 4) + 1]; + auto* pos_array = m_mod_vtx_unpack_temp[vidx].pos; memcpy(&pos_array[0], &q0w, 4); memcpy(&pos_array[1], &q1w, 4); @@ -375,6 +386,13 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, nrm_array[0] += -65537; nrm_array[1] += -65537; nrm_array[2] += -65537; + + auto* uv_array = m_mod_vtx_unpack_temp[vidx].uv; + memcpy(&uv_array[0], &q2x, 4); + memcpy(&uv_array[1], &q2y, 4); + uv_array[0] += model->st_magic; + uv_array[1] += model->st_magic; + vidx++; } } @@ -401,6 +419,8 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, u32 addr = effect.mod.vertex_lump4_addr[vi]; if (addr < vidx) { memcpy(&m_mod_vtx_temp[vi], &m_mod_vtx_unpack_temp[addr], 32); + m_mod_vtx_temp[vi].st[0] = m_mod_vtx_unpack_temp[addr].uv[0]; + m_mod_vtx_temp[vi].st[1] = m_mod_vtx_unpack_temp[addr].uv[1]; } } } @@ -480,22 +500,23 @@ 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); + alloc_normal_draw(fdraw, ignore_alpha, lev_bucket, first_bone, lights, uses_water); if (should_envmap) { try_alloc_envmap_draw(fdraw, effect.envmap_mode, effect.envmap_texture, lev_bucket, - fade_buffer + 4 * ei, first_bone, lights); + fade_buffer + 4 * ei, first_bone, lights, uses_water); } } // do mod draws for (auto& mdraw : effect.mod.mod_draw) { - auto n = alloc_normal_draw(mdraw, ignore_alpha, lev_bucket, first_bone, lights); + auto n = alloc_normal_draw(mdraw, ignore_alpha, lev_bucket, first_bone, lights, uses_water); // 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]; if (should_envmap) { - auto e = try_alloc_envmap_draw(mdraw, effect.envmap_mode, effect.envmap_texture, - lev_bucket, fade_buffer + 4 * ei, first_bone, lights); + auto e = + try_alloc_envmap_draw(mdraw, effect.envmap_mode, effect.envmap_texture, lev_bucket, + fade_buffer + 4 * ei, first_bone, lights, uses_water); if (e) { e->flags |= MOD_VTX; e->mod_vtx_buffer = mod_opengl_buffers[ei]; @@ -507,9 +528,9 @@ void Merc2::handle_pc_model(const DmaTransfer& setup, for (auto& draw : effect.all_draws) { if (should_envmap) { try_alloc_envmap_draw(draw, effect.envmap_mode, effect.envmap_texture, lev_bucket, - fade_buffer + 4 * ei, first_bone, lights); + fade_buffer + 4 * ei, first_bone, lights, uses_water); } - alloc_normal_draw(draw, ignore_alpha, lev_bucket, first_bone, lights); + alloc_normal_draw(draw, ignore_alpha, lev_bucket, first_bone, lights, uses_water); } } } @@ -886,7 +907,8 @@ Merc2::Draw* Merc2::try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw, LevelDrawBucket* lev_bucket, const u8* fade, u32 first_bone, - u32 lights) { + u32 lights, + bool jak1_water_mode) { bool nonzero_fade = false; for (int i = 0; i < 4; i++) { if (fade[i]) { @@ -903,6 +925,10 @@ 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; + if (jak1_water_mode) { + draw->mode.enable_ab(); + draw->mode.disable_depth_write(); + } draw->texture = envmap_texture; draw->first_bone = first_bone; draw->light_idx = lights; @@ -917,12 +943,17 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw, bool ignore_alpha, LevelDrawBucket* lev_bucket, u32 first_bone, - u32 lights) { + u32 lights, + bool jak1_water_mode) { 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; + if (jak1_water_mode) { + draw->mode.set_ab(true); + draw->mode.disable_depth_write(); + } draw->texture = mdraw.eye_id == 0xff ? mdraw.tree_tex_id : (0xffffff00 | mdraw.eye_id); draw->first_bone = first_bone; draw->light_idx = lights; @@ -1056,6 +1087,7 @@ void Merc2::do_draws(const Draw* draw_array, } } glUniform1i(uniforms.ignore_alpha, draw.flags & DrawFlags::IGNORE_ALPHA); + bool use_mipmaps_for_filtering = true; if ((int)draw.texture != last_tex) { if (draw.texture < lev->textures.size()) { glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture)); @@ -1064,8 +1096,7 @@ void Merc2::do_draws(const Draw* draw_array, if (maybe_eye) { glBindTexture(GL_TEXTURE_2D, *maybe_eye); } - - mode.set_filt_enable(false); + use_mipmaps_for_filtering = false; } else { fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture); } @@ -1082,7 +1113,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(mode, GL_TEXTURE0, true); + setup_opengl_from_draw_mode(mode, GL_TEXTURE0, use_mipmaps_for_filtering); glUniform1i(uniforms.decal, draw.mode.get_decal()); diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index dae80c4032..a6d8a294fe 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -138,6 +138,7 @@ class Merc2 : public BucketRenderer { struct UnpackTempVtx { float pos[4]; float nrm[4]; + float uv[2]; }; std::vector m_mod_vtx_unpack_temp; @@ -198,14 +199,16 @@ class Merc2 : public BucketRenderer { bool ignore_alpha, LevelDrawBucket* lev_bucket, u32 first_bone, - u32 lights); + u32 lights, + bool jak1_water_mode); Draw* try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw, const DrawMode& envmap_mode, u32 envmap_texture, LevelDrawBucket* lev_bucket, const u8* fade, u32 first_bone, - u32 lights); + u32 lights, + bool jak1_water_mode); void do_draws(const Draw* draw_array, const LevelData* lev, diff --git a/game/graphics/opengl_renderer/shaders/emerc.frag b/game/graphics/opengl_renderer/shaders/emerc.frag index a6f3f9fe5d..88c22f5634 100644 --- a/game/graphics/opengl_renderer/shaders/emerc.frag +++ b/game/graphics/opengl_renderer/shaders/emerc.frag @@ -27,6 +27,4 @@ void main() { color.rgb = vtx_color; color.a = 1; } - - color.xyz *= 0.5; } diff --git a/goal_src/jak1/engine/gfx/foreground/bones.gc b/goal_src/jak1/engine/gfx/foreground/bones.gc index bc80442024..80fca9917d 100644 --- a/goal_src/jak1/engine/gfx/foreground/bones.gc +++ b/goal_src/jak1/engine/gfx/foreground/bones.gc @@ -952,6 +952,22 @@ ) ) +(defun draw-control-uses-water-bucket ((dc draw-control)) + "Does this draw control use a water bucket?" + (let ((sg (-> dc sink-group))) + (dotimes (i 3) + (let ((lev (-> *level* level i))) + (when lev + (when (= sg (-> lev water-tex-foreground-sink-group)) + (return #t) + ) + ) + ) + ) + #f + ) + ) + (defun pc-merc-draw-request ((dc draw-control) (dma-buf pointer) (matrix-buf pointer) (update-verts symbol)) (let ((start-packet (the-as dma-packet dma-buf)) (qwc-total 0)) @@ -976,7 +992,12 @@ (&+! dma-buf (* 16 7)) (+! qwc-total 7) - + (let ((uses-water (draw-control-uses-water-bucket dc))) + ;; tell PC renderer if we're in a water bucket or not (has different settings.) + (set! (-> (the (pointer uint64) dma-buf)) (if uses-water 1 0)) + (&+! dma-buf 16) + (+! qwc-total 1) + ) (let ((matrix-slot-string (the (pointer uint8) dma-buf)) ;; matrix slot list (so PC knows what order they come in) (enable-mask 0) (ignore-alpha-mask 0) @@ -1088,6 +1109,9 @@ ;; when set, use merc for blerc instead of generic. (define *blerc-hack* #t) +(define *texscroll-force-generic* #f) +(define *ripple-force-generic* #f) + (defun draw-bones ((arg0 draw-control) (dma-buf dma-buffer) (arg2 float)) "Main draw function for all bone-related renderers. Will set up merc, generic and shadow. and also add the bones to the calculation list." @@ -1316,9 +1340,10 @@ (v1-36 (the-as mei-texture-scroll (+ (the-as uint v1-35) (* (-> v1-35 texture-scroll-offset) 16)))) ) (when (< arg2 (-> v1-36 max-dist)) ;; in range to draw? - ;; do scrolling. in PC, always use generic here because merc2 can't update texture coordinates - (#when PC_PORT - (set! pc-force-mercneric #t) + ;; do scrolling. + (if *texscroll-force-generic* + (set! pc-force-mercneric #t) ;; use generic for texscroll + (set! pc-merc-vtx-update #t) ;; use merc2 for texscroll ) (texscroll-make-request (-> geom effect effect-idx)) ) @@ -1328,9 +1353,9 @@ ;; check if this effect uses "ripple" (when (logtest? (-> geom effect effect-idx effect-bits) 4) (when (-> arg0 ripple) - ;; merc2 can't handle ripple, even if it's off. - (#when PC_PORT + (if *ripple-force-generic* (set! pc-force-mercneric #t) + (set! pc-merc-vtx-update #t) ) (set! sv-144 (-> arg0 ripple)) (let* ((f1-4 (/ (- (-> sv-144 far-fade-dist) arg2) (- (-> sv-144 far-fade-dist) (-> sv-144 close-fade-dist)))) diff --git a/goal_src/jak2/engine/game/main.gc b/goal_src/jak2/engine/game/main.gc index bbdb170b26..f66aa1361a 100644 --- a/goal_src/jak2/engine/game/main.gc +++ b/goal_src/jak2/engine/game/main.gc @@ -1509,7 +1509,7 @@ ) ; ;; Run other merc effects that modify vertices - ; (texscroll-execute) + (texscroll-execute) (ripple-execute) (region-execute) diff --git a/goal_src/jak2/engine/gfx/foreground/foreground.gc b/goal_src/jak2/engine/gfx/foreground/foreground.gc index 99e588ca3a..b55e26ed17 100644 --- a/goal_src/jak2/engine/gfx/foreground/foreground.gc +++ b/goal_src/jak2/engine/gfx/foreground/foreground.gc @@ -773,7 +773,7 @@ (let ((use-flags (new 'stack-no-clear 'array 'uint8 7)) (mctrl (-> dc mgeo)) (buckets (-> (scratchpad-object foreground-work) grid level-buckets (-> (scratchpad-object foreground-work) draw-index-map (-> dc level-index)))) - (has-ripple #f) + (has-ripple-or-texscroll #f) ) ;; mark all as unused, until we see a use (dotimes (i 7) (set! (-> use-flags i) 0)) @@ -782,8 +782,8 @@ (dotimes (i (-> mctrl header effect-count)) ;;(format 0 "effect ~d, texture ~d~%" i (-> mctrl effect i texture-index)) (set! (-> use-flags (-> mctrl effect i texture-index)) 1) - (when (logtest? (-> (-> dc lod-set lod (-> dc cur-lod) geo) effect i effect-bits) (effect-bits ripple)) - (set! has-ripple #t) + (when (logtest? (-> (-> dc lod-set lod (-> dc cur-lod) geo) effect i effect-bits) (effect-bits ripple texscroll)) + (set! has-ripple-or-texscroll #t) ) ) @@ -795,7 +795,7 @@ (let ((packet (the-as dma-packet dma-buf)) (vertex-update #f) ) - (when has-ripple + (when has-ripple-or-texscroll (set! vertex-update #t) ) (let* ((pd (the process-drawable (-> dc process)))