mirror of
https://github.com/open-goal/jak-project
synced 2026-07-08 22:45:00 -04:00
[merc2] Support texscroll, use in jak 1 in more places, fix envmap bug (#2303)
Three main changes: - Adds support for the texture scrolling effect used on conveyor belts, and turn it on for jak 2. - Use merc instead of generic in jak 1 for ripple/water/texscroll stuff (non-ocean water, lava, dark eco, etc). This is a pretty big speedup in a lot of places. - Fix a really old bug with blending mode used to draw environment maps. The effect is that envmaps were half as bright as they should have been. As usual, there's a flag to go back to the old behavior on jak 1. Set these to `#t` to use generic like we used to. ``` *texscroll-force-generic* *ripple-force-generic* ``` The format has changed, and everything must be rebuilt (C++, FR3's, GOAL code)
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -179,6 +179,7 @@ struct MercExtraInfo {
|
||||
std::optional<MercShader> shader;
|
||||
};
|
||||
|
||||
constexpr int kTextureScrollEffectBit = 1;
|
||||
constexpr int kRippleEffectBit = 4; // true in jak 1 and jak 2
|
||||
|
||||
struct MercEffect {
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@ class Merc2 : public BucketRenderer {
|
||||
struct UnpackTempVtx {
|
||||
float pos[4];
|
||||
float nrm[4];
|
||||
float uv[2];
|
||||
};
|
||||
std::vector<UnpackTempVtx> 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,
|
||||
|
||||
@@ -27,6 +27,4 @@ void main() {
|
||||
color.rgb = vtx_color;
|
||||
color.a = 1;
|
||||
}
|
||||
|
||||
color.xyz *= 0.5;
|
||||
}
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -1509,7 +1509,7 @@
|
||||
)
|
||||
|
||||
; ;; Run other merc effects that modify vertices
|
||||
; (texscroll-execute)
|
||||
(texscroll-execute)
|
||||
(ripple-execute)
|
||||
(region-execute)
|
||||
|
||||
|
||||
@@ -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)))
|
||||
|
||||
Reference in New Issue
Block a user