[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:
water111
2023-03-09 20:01:22 -05:00
committed by GitHub
parent 6d99f1bfc1
commit 0db9b288e4
11 changed files with 98 additions and 32 deletions
+1
View File
@@ -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) {
+2 -1
View File
@@ -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;
};
+1
View File
@@ -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(&current_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;
}
+31 -6
View File
@@ -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))))
+1 -1
View File
@@ -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)))