jak-project/decompiler/level_extractor/extract_merc.cpp

1379 lines
48 KiB
C++
Raw Normal View History

#include "extract_merc.h"
#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"
#include "decompiler/util/goal_data_reader.h"
namespace decompiler {
// number of slots on VU1 data memory to store matrices
constexpr int MERC_VU1_MATRIX_SLOTS = 18;
// the size of each "matrix". Includes a transformation and rotation matrix (for normals)
constexpr int MERC_MATRIX_STRIDE = 7;
// converts a vu1 address to an index of a matrix slot.
// as far as I can tell, nothing in the game uses indices, they are always addresses already
u32 vu1_addr_to_matrix_slot(u32 addr) {
ASSERT(addr >= 6);
ASSERT(addr < (6 + MERC_MATRIX_STRIDE * MERC_VU1_MATRIX_SLOTS));
addr -= 6;
ASSERT((addr % MERC_MATRIX_STRIDE) == 0);
return addr / MERC_MATRIX_STRIDE;
}
u32 matrix_slot_to_vu1_addr(u32 slot) {
ASSERT(slot < MERC_VU1_MATRIX_SLOTS);
return 6 + (slot * MERC_MATRIX_STRIDE);
}
/*!
* The GS settings of a merc draw (shader + tex)
*/
struct MercGsState {
// the blending, clamp, etc settings. from adgif shaders
DrawMode mode;
// the texture to use, as a "pc combo" texture index.
u32 pc_combo_tex_id;
u64 as_u64() const { return (((u64)pc_combo_tex_id) << 32) | mode.as_int(); }
};
/*!
* This is all the state that's required to understand how to draw a vertex. including both the GS
* state, and stuff (matrices) left behind in VU memory. The matrix slots are matrix indices into
* the original bones matrices (all the matrices in the skeleton). An index of -1 indicates that
* there is no known matrix in this slot.
*/
struct MercState {
MercGsState merc_draw_mode;
// vu1_matrix_slots[x] = y
// where x is the slot in VU1 memory, and y is the matrix index for the bones/skeleton stuff.
std::array<int, MERC_VU1_MATRIX_SLOTS> vu1_matrix_slots;
MercState() { vu1_matrix_slots.fill(-1); }
};
/*!
* Required information for a "draw", consisting of a strip of triangles (represented as indices for
* OpenGL triangle strip) and the draw settings.
*/
struct MercDraw {
size_t ctrl_idx; // which merc ctrl in the level we correspond to
size_t effect_idx; // which effect within that control
size_t frag_idx; // which frag within that effect
// note that the above triple isn't enough to uniquely identify a draw - there can be multiple
// draws within a fragment.
// draw settings
MercState state;
// opengl indices. currently into a per-effect vertex list (likely to merge into a giant buffer
// eventually)
std::vector<u32> indices;
// where we would be in VU1 data memory (used in construction)
u32 vtx_offset; // relative to writing output zone
u32 vtx_nloop; // nloop that goes in the gif tag drawing us.
};
/*!
* Merc Vertex. Not the format we'll want in the game data files, but most useful as an intermediate
* when building up draws.
*/
struct MercUnpackedVtx {
int kind = 0; // 1, 2, or 3 matrix
math::Vector3f pos; // position
math::Vector3f nrm; // normal (as input to the merc math, pretty sure legnth is bogus)
math::Vector2f st; // texture coordinates
math::Vector<u8, 4> rgba;
int skel_mats[3];
float mat_weights[3];
u16 dst0;
u16 dst1;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
bool can_be_modified = false;
int idx_in_combined_lump4 = -1; // divided by 3
int flump4 = -1;
int frag = -1;
};
/*!
* An entire merc-effect, split into draws.
* Note that copied or multiply-placed vertices will be de-deduplicated, but not identical vertices
* that actually appear in the input to merc.
*/
struct ConvertedMercEffect {
size_t ctrl_idx;
size_t effect_idx;
// draws from all fragments.
std::vector<MercDraw> draws;
std::vector<MercUnpackedVtx> vertices;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
std::vector<u32> verts_per_frag;
bool has_envmap = false;
DrawMode envmap_mode;
u32 envmap_texture;
std::optional<s8> eye_slot;
};
/*!
* Extract a merc-ctrl data structure. This is mostly just copying the GOAL data to C++ classes,
* but does include the effect of processing the DMA data through VIF.
*/
MercCtrl extract_merc_ctrl(const LinkedObjectFile& file,
const DecompilerTypeSystem& dts,
int word_idx) {
Ref ref;
ref.data = &file;
ref.seg = 0;
ref.byte_offset = word_idx * 4;
auto tr = typed_ref_from_basic(ref, dts);
MercCtrl ctrl;
ctrl.from_ref(tr, dts, file.version); // the merc data import
return ctrl;
}
/*!
* Find the word indices for the merc ctrls (the type tags)
*/
std::vector<int> find_merc_ctrls(const LinkedObjectFile& file) {
std::vector<int> result;
for (size_t i = 0; i < file.words_by_seg.at(0).size(); i++) {
const auto& word = file.words_by_seg[0][i];
if (word.kind() == LinkedWord::TYPE_PTR && word.symbol_name() == "merc-ctrl") {
result.push_back(i);
}
}
return result;
}
namespace {
/*!
* Merc models tend to have strange texture ids. I don't really understand why.
* On login, the texture is checked against a list of textures in the bsp, and replaced with this.
* It doesn't seem to be related to sharing textures between levels - the yakow texture uses this.
*/
u32 remap_texture(u32 original, const std::vector<level_tools::TextureRemap>& map) {
auto masked = original & 0xffffff00;
for (auto& t : map) {
if (t.original_texid == masked) {
return t.new_texid | 20;
}
}
return original;
}
/*!
* Set the alpha fields of a DrawMode (for PC renderers) based on gs alpha register
*/
void update_mode_from_alpha1(GsAlpha reg, DrawMode& mode) {
if (reg.a_mode() == GsAlpha::BlendMode::SOURCE && reg.b_mode() == GsAlpha::BlendMode::DEST &&
reg.c_mode() == GsAlpha::BlendMode::SOURCE && reg.d_mode() == GsAlpha::BlendMode::DEST) {
// (Cs - Cd) * As + Cd
// Cs * As + (1 - As) * Cd
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.c_mode() == GsAlpha::BlendMode::SOURCE &&
reg.d_mode() == GsAlpha::BlendMode::DEST) {
// (Cs - 0) * As + Cd
// Cs * As + (1) * CD
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_SRC_DST);
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.c_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.d_mode() == GsAlpha::BlendMode::DEST) {
ASSERT(reg.fix() == 128);
// Cv = (Cs - 0) * FIX + Cd
// if fix = 128, it works out to 1.0
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_FIX_DST);
// src plus dest
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
reg.b_mode() == GsAlpha::BlendMode::DEST &&
reg.c_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.d_mode() == GsAlpha::BlendMode::DEST) {
// Cv = (Cs - Cd) * FIX + Cd
ASSERT(reg.fix() == 64);
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_FIX_DST);
} else if (reg.a_mode() == GsAlpha::BlendMode::DEST &&
reg.b_mode() == GsAlpha::BlendMode::SOURCE &&
reg.c_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.d_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED) {
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
reg.c_mode() == GsAlpha::BlendMode::DEST && reg.d_mode() == GsAlpha::BlendMode::DEST) {
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST);
}
else {
// unsupported blend: a 0 b 1 c 0 d 2 is this part of generic?
lg::warn("unsupported blend: a {} b {} c {} d {}", (int)reg.a_mode(), (int)reg.b_mode(),
(int)reg.c_mode(), (int)reg.d_mode());
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
// ASSERT(false);
}
}
/*!
* Convert merc shader to PC draw mode
*/
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
DrawMode process_draw_mode(const MercShader& info,
bool enable_alpha_test,
bool enable_alpha_blend,
bool depth_write,
bool fge) {
DrawMode mode;
/*
* (new 'static 'gs-test
:ate #x1
:atst (gs-atest greater-equal)
:aref #x26
:zte #x1
:ztst (gs-ztest greater-equal)
)
*/
mode.set_at(enable_alpha_test);
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
mode.set_aref(0x26);
mode.set_alpha_fail(GsTest::AlphaFail::KEEP);
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
mode.enable_zt();
mode.set_depth_write_enable(depth_write);
mode.set_depth_test(GsTest::ZTest::GEQUAL);
// check these
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
mode.set_ab(enable_alpha_blend);
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
mode.set_tcc(info.tex0.tcc());
mode.set_decal(info.tex0.tfx() == GsTex0::TextureFunction::DECAL);
if (info.tex0.tfx() != GsTex0::TextureFunction::DECAL) {
ASSERT(info.tex0.tfx() == GsTex0::TextureFunction::MODULATE);
}
mode.set_filt_enable(info.tex1.mmag());
// the alpha matters (maybe?)
update_mode_from_alpha1(info.alpha, mode);
// the clamp matters
if (!(info.clamp == 0b101 || info.clamp == 0 || info.clamp == 1 || info.clamp == 0b100)) {
ASSERT_MSG(false, fmt::format("clamp: 0x{:x}", info.clamp));
}
mode.set_clamp_s_enable(info.clamp & 0b1);
mode.set_clamp_t_enable(info.clamp & 0b100);
mode.set_fog(fge);
return mode;
}
float u32_as_float(u32 in) {
float out;
memcpy(&out, &in, sizeof(float));
return out;
}
u32 float_as_u32(float in) {
u32 out;
memcpy(&out, &in, sizeof(float));
return out;
}
} // namespace
/*!
* Representing something that can be written to the merc output buffer. 1 qw
*/
struct MercOutputQuadword {
enum class Kind {
INVALID, // uninitialized, or in the middle of another thing
VTX_START, // the first qw of a vertex
SHADER_START, // the first qw of a shader (the giftag)
PRIM_START // the giftag for a list of vertices
} kind = Kind::INVALID;
// if we're a vertex
u32 vtx_idx = -1; // index in the effect's vertex list.
bool adc = false; // if our adc flag is set. this differs between copies, so put it here.
// if we're a primitive giftag, the number of vertices that come after us.
u32 nloop_count = 0;
};
struct MercMemory {
std::array<MercOutputQuadword, 1024> memory;
};
/*!
* Add vertices from a fragment to memory. Emulates the unpacking.
*/
void handle_frag(const std::string& debug_name,
const MercCtrlHeader& ctrl_header,
const MercFragment& frag,
const MercFragmentControl& frag_ctrl,
const MercState& state,
std::vector<MercUnpackedVtx>& effect_vertices,
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
MercMemory& memory,
bool can_be_modified,
int base_lump4,
int frag_idx) {
(void)frag_ctrl;
(void)debug_name;
// lg::print("handling frag: {}\n", debug_name);
// lg::print("{}\n", frag.print());
// we'll iterate through the lump and rgba data
int lump_ptr = 0; // vertex data starts at the beginning of "lump"
int rgba_ptr = frag.header.rgba_off; // rgba is in u4's
int perc_ptr = frag.header.perc_off;
int last_mat2_perc = perc_ptr - 1;
int perc_toggle = 0;
u32 mat1_cnt = frag.header.mat1_cnt;
u32 mat12_cnt = frag.header.mat2_cnt + mat1_cnt;
u32 mat123_cnt = frag.header.mat3_cnt + mat12_cnt;
// loop through vertices.
int prev_mat0 = -1;
int prev_mat1 = -1;
int prev_mat2 = -1;
for (size_t i = 0; i < mat123_cnt; i++) {
u32 current_vtx_idx = effect_vertices.size(); // idx in effect vertex list.
auto& vtx = effect_vertices.emplace_back();
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
vtx.can_be_modified = can_be_modified;
vtx.idx_in_combined_lump4 = lump_ptr / 3 + base_lump4;
vtx.frag = frag_idx;
vtx.flump4 = lump_ptr / 3;
if (i < mat1_cnt) {
vtx.kind = 1; // 1 matrix
} else if (i < mat12_cnt) {
vtx.kind = 2; // 2 matrix
} else {
vtx.kind = 3;
}
// the three quadwords in the source data
auto v0 = frag.lump4_unpacked.at(lump_ptr);
auto v1 = frag.lump4_unpacked.at(lump_ptr + 1);
auto v2 = frag.lump4_unpacked.at(lump_ptr + 2);
// ilwr.x vi08, vi01 ;; load mat0 from vertex
u16 mat0_addr;
memcpy(&mat0_addr, &v0.x(), 2);
u16 mat1_addr;
memcpy(&mat1_addr, &v0.y(), 2);
if (vtx.kind == 1) {
vtx.skel_mats[0] = state.vu1_matrix_slots.at(vu1_addr_to_matrix_slot(mat0_addr));
vtx.skel_mats[1] = 0;
vtx.skel_mats[2] = 0;
vtx.mat_weights[0] = 1.f;
vtx.mat_weights[1] = 0.f;
vtx.mat_weights[2] = 0.f;
ASSERT(vtx.skel_mats[0] >= 0);
} else if (vtx.kind == 2) {
u8 m0 = mat0_addr & 0x7f;
u8 m1 = mat1_addr & 0x7f;
if (m0 == 0x7f) {
ASSERT(prev_mat0 != -1);
} else {
prev_mat0 = vu1_addr_to_matrix_slot(m0);
prev_mat1 = vu1_addr_to_matrix_slot(m1);
}
vtx.skel_mats[0] = state.vu1_matrix_slots.at(prev_mat0);
vtx.skel_mats[1] = state.vu1_matrix_slots.at(prev_mat1);
vtx.skel_mats[2] = 0;
if (m0 != 0x7f && i != mat1_cnt) {
if (perc_toggle) {
perc_ptr++;
}
perc_toggle = !perc_toggle;
}
auto perc = frag.unsigned_four_including_header.at(perc_ptr);
last_mat2_perc = perc_ptr;
if (!perc_toggle) {
vtx.mat_weights[0] = perc.x() / 255.f;
vtx.mat_weights[1] = perc.y() / 255.f;
} else {
vtx.mat_weights[0] = perc.z() / 255.f;
vtx.mat_weights[1] = perc.w() / 255.f;
}
vtx.mat_weights[2] = 0.f;
float sum = vtx.mat_weights[0] + vtx.mat_weights[1];
ASSERT(std::abs(1.f - sum) < 1e-6);
} else if (vtx.kind == 3) {
u8 m0 = mat0_addr & 0x7f;
u8 m1 = mat1_addr & 0x7f;
if (i == mat12_cnt) {
perc_ptr = last_mat2_perc;
}
if (m0 == 0x7f) {
ASSERT(prev_mat0 != -1);
} else {
perc_ptr++;
prev_mat0 = vu1_addr_to_matrix_slot(m0);
prev_mat1 = vu1_addr_to_matrix_slot(m1);
prev_mat2 = vu1_addr_to_matrix_slot(frag.unsigned_four_including_header.at(perc_ptr).w());
}
vtx.skel_mats[0] = state.vu1_matrix_slots.at(prev_mat0);
vtx.skel_mats[1] = state.vu1_matrix_slots.at(prev_mat1);
vtx.skel_mats[2] = state.vu1_matrix_slots.at(prev_mat2);
auto perc = frag.unsigned_four_including_header.at(perc_ptr);
vtx.mat_weights[0] = perc.x() / 255.f;
vtx.mat_weights[1] = perc.y() / 255.f;
vtx.mat_weights[2] = perc.z() / 255.f;
float sum = vtx.mat_weights[0] + vtx.mat_weights[1] + vtx.mat_weights[2];
ASSERT(std::abs(1.f - sum) < 1e-6);
} else {
ASSERT(false);
}
u16 mat1;
memcpy(&mat1, &v0.y(), 2);
u16 mat0;
memcpy(&mat0, &v0.x(), 2);
// add.zw vf08, vf08, vf17 ;; lump offset
// vf17 = [2048, 255, -65537, xyz-add.x] (the ?? is set per fragment)
v0.z() += -65537;
v0.w() += frag.fp_header.x_add;
// add.xyzw vf11, vf11, vf18 ;; lump offset
// vf18 = [st-out-X, st-out-X, -65537, xyz-add.y] (X = a if xtop = 0, X = b otherwise)
v1.x() += u32_as_float(ctrl_header.st_out_a);
v1.y() += u32_as_float(ctrl_header.st_out_a);
v1.z() += -65537;
v1.w() += frag.fp_header.y_add;
// add.xyzw vf14, vf14, vf19 ;; lump offset
// vf19 = [st-magic, st-magic, -65537, xyz-add.z]
v2.x() += u32_as_float(ctrl_header.st_magic);
v2.y() += u32_as_float(ctrl_header.st_magic);
v2.z() += -65537;
v2.w() += frag.fp_header.z_add;
vtx.pos = math::Vector3f(v0.w(), v1.w(), v2.w());
vtx.nrm = math::Vector3f(v0.z(), v1.z(), v2.z());
// vtx.mat1 = 0; // not used like this
vtx.dst0 = float_as_u32(v1.x()) - 371; // xtop to output buffer offset
vtx.dst1 = float_as_u32(v1.y()) - 371;
vtx.rgba = frag.unsigned_four_including_header.at(rgba_ptr);
vtx.st = math::Vector2f(v2.x(), v2.y());
// crazy flag logic to set adc
s16 mat1_flag = mat1;
s16 mat0_flag = mat0;
bool dst0_adc, dst1_adc;
if (vtx.kind == 1) {
ASSERT(mat1_flag == -1 || mat1_flag == 0 || mat1_flag == 1);
dst0_adc = mat1_flag <= 0;
dst1_adc = dst0_adc && (mat1_flag != 0);
dst0_adc = !dst0_adc;
dst1_adc = !dst1_adc;
} else {
// adc logic
// ilw.y vi09, -6(vi01)
s16 vi09 = mat1_flag;
// move.xyzw vf21, vf08
bool vf21_has_adc = false;
bool vf08_has_adc = false;
if (!(vi09 > 0)) {
vf21_has_adc = true;
}
// ibgtz vi09, L47
//
// addx.w vf21, vf21, vf17
//
// L47:
// ilw.x vi09, -9(vi01)
vi09 = mat0_flag;
// ftoi4.xyzw vf21, vf21
//
// sq.xyzw vf21, 2(vi10)
dst0_adc = !vf21_has_adc;
if (!(vi09 >= 0)) {
vf21_has_adc = vf08_has_adc;
}
dst1_adc = !vf21_has_adc;
// ibgez vi09, L50
//
// ftoi4.xyzw vf21, vf08
//
// L50:
// sq.xyzw vf21, 2(vi13)
// dst0_adc = mat1_flag <= 0;
// dst1_adc = dst0_adc && (mat0_flag >= 0);
// dst0_adc = !dst0_adc;
// dst1_adc = !dst1_adc;
// lg::print("{}\n", dst1_adc);
}
// write to two spots in memory
auto& dst0_mem = memory.memory.at(vtx.dst0);
auto& dst1_mem = memory.memory.at(vtx.dst1);
dst0_mem.kind = MercOutputQuadword::Kind::VTX_START;
dst0_mem.vtx_idx = current_vtx_idx;
dst0_mem.adc = dst0_adc;
memory.memory.at(vtx.dst0 + 1).kind = MercOutputQuadword::Kind::INVALID;
memory.memory.at(vtx.dst0 + 2).kind = MercOutputQuadword::Kind::INVALID;
dst1_mem.kind = MercOutputQuadword::Kind::VTX_START;
dst1_mem.vtx_idx = current_vtx_idx;
dst1_mem.adc = dst1_adc;
memory.memory.at(vtx.dst1 + 1).kind = MercOutputQuadword::Kind::INVALID;
memory.memory.at(vtx.dst1 + 2).kind = MercOutputQuadword::Kind::INVALID;
/*
lg::print("place vertex {} @ {} {}: {} (adc {} {}) {}\n", current_vtx_idx, vtx.dst0, vtx.dst1,
vtx.pos.to_string_aligned(), dst0_adc, dst1_adc, mat1_flag);
*/
lump_ptr += 3; // advance 3 qw
rgba_ptr++;
}
}
/*!
* Build OpenGL index list from a single GIF packet.
* TODO: should check we aren't putting in x x R x x R
*/
std::vector<u32> index_list_from_packet(u32 vtx_ptr, u32 nloop, const MercMemory& memory) {
std::vector<u32> result;
u32 prev_vtx = UINT32_MAX;
result.push_back(UINT32_MAX);
while (nloop) {
auto& vtx_mem = memory.memory.at(vtx_ptr);
if (vtx_mem.kind == MercOutputQuadword::Kind::VTX_START) {
bool adc = vtx_mem.adc;
if (adc) {
result.push_back(vtx_mem.vtx_idx);
} else {
result.push_back(UINT32_MAX);
result.push_back(prev_vtx);
result.push_back(vtx_mem.vtx_idx);
}
prev_vtx = vtx_mem.vtx_idx;
} else {
// missing vertex!
lg::warn("MISSING VERTEX at {}", vtx_ptr);
result.push_back(UINT32_MAX);
}
vtx_ptr += 3;
nloop--;
}
result.push_back(UINT32_MAX);
return result;
}
/*!
* Dump draws to obj file.
*/
std::string debug_dump_to_obj(const std::vector<MercDraw>& draws,
const std::vector<MercUnpackedVtx>& vertices) {
std::string result;
std::vector<math::Vector4f> verts;
std::vector<math::Vector<int, 3>> faces;
for (auto& draw : draws) {
// add verts...
int queue[2];
int q_idx = 0;
for (size_t ii = 2; ii < draw.indices.size(); ii++) {
u32 v0 = draw.indices[ii - 2];
u32 v1 = draw.indices[ii - 1];
u32 v2 = draw.indices[ii - 0];
if (v0 != UINT32_MAX && v1 != UINT32_MAX && v2 != UINT32_MAX) {
faces.emplace_back(v0 + 1, v1 + 1, v2 + 1);
}
}
for (auto& idx : draw.indices) {
if (idx == UINT32_MAX) {
q_idx = 0;
} else {
if (q_idx >= 2) {
faces.emplace_back(queue[0] + 1, queue[1] + 1, idx + 1);
}
queue[(q_idx++) % 2] = idx;
}
}
}
for (auto& vtx : vertices) {
result += fmt::format("v {} {} {}\n", vtx.pos.x() / 1024.f, vtx.pos.y() / 1024.f,
vtx.pos.z() / 1024.f);
}
for (auto& face : faces) {
result += fmt::format("f {}/{} {}/{} {}/{}\n", face.x(), face.x(), face.y(), face.y(), face.z(),
face.z());
}
return result;
}
math::Vector4<u16> vtx_to_rgba_bone_debug(const MercUnpackedVtx& vtx) {
math::Vector4<u16> result;
result.fill(0);
for (int i = 0; i < 3; i++) {
if (vtx.skel_mats[i] == -1 || vtx.mat_weights[i] == 0) {
continue;
}
u32 rgba_packed = colors::common_colors[vtx.skel_mats[i] % colors::COLOR_COUNT];
result.x() += ((rgba_packed >> 16) & 0xff) * vtx.mat_weights[i];
result.y() += ((rgba_packed >> 8) & 0xff) * vtx.mat_weights[i];
result.z() += ((rgba_packed >> 0) & 0xff) * vtx.mat_weights[i];
}
return result;
}
std::string debug_dump_to_ply(const std::vector<MercDraw>& draws,
const std::vector<MercUnpackedVtx>& vertices) {
std::vector<math::Vector4f> verts;
std::vector<math::Vector<int, 3>> faces;
for (auto& draw : draws) {
// add verts...
for (size_t ii = 2; ii < draw.indices.size(); ii++) {
u32 v0 = draw.indices[ii - 2];
u32 v1 = draw.indices[ii - 1];
u32 v2 = draw.indices[ii - 0];
if (v0 != UINT32_MAX && v1 != UINT32_MAX && v2 != UINT32_MAX) {
faces.emplace_back(v0, v1, v2);
}
}
}
std::string result = fmt::format(
"ply\nformat ascii 1.0\nelement vertex {}\nproperty float x\nproperty float y\nproperty "
"float z\nproperty uchar red\nproperty uchar green\nproperty uchar blue\nelement face "
"{}\nproperty list uchar int vertex_index\nend_header\n",
vertices.size(), faces.size());
for (auto& vtx : vertices) {
auto rgba = vtx_to_rgba_bone_debug(vtx);
result += fmt::format("{} {} {} {} {} {}\n", vtx.pos.x() / 1024.f, vtx.pos.y() / 1024.f,
vtx.pos.z() / 1024.f, rgba[0], rgba[1], rgba[2]);
}
for (auto& face : faces) {
result += fmt::format("3 {} {} {}\n", face.x(), face.y(), face.z());
}
return result;
}
int find_or_add_texture_to_level(tfrag3::Level& out,
const TextureDB& tex_db,
const std::string& debug_name,
u32 pc_combo_tex_id,
const MercCtrlHeader& hdr,
u8* eye_out,
GameVersion version) {
u32 idx_in_level_texture = UINT32_MAX;
for (u32 i = 0; i < out.textures.size(); i++) {
if (out.textures[i].combo_id == pc_combo_tex_id) {
idx_in_level_texture = i;
break;
}
}
if (idx_in_level_texture == UINT32_MAX) {
// not added to level, add it
auto tex_it = tex_db.textures.find(pc_combo_tex_id);
if (tex_it == tex_db.textures.end()) {
lg::error("merc failed to find texture: 0x{:x} for {}. Should be in tpage {}",
pc_combo_tex_id, debug_name, pc_combo_tex_id >> 16);
idx_in_level_texture = 0;
} else {
idx_in_level_texture = out.textures.size();
auto& new_tex = out.textures.emplace_back();
new_tex.combo_id = pc_combo_tex_id;
new_tex.w = tex_it->second.w;
new_tex.h = tex_it->second.h;
new_tex.debug_name = tex_it->second.name;
new_tex.debug_tpage_name = tex_db.tpage_names.at(tex_it->second.page);
new_tex.data = tex_it->second.rgba_bytes;
}
}
// check eyes
u32 eye_tpage = version == GameVersion::Jak2 ? 0x70c : 0x1cf;
u32 left_id = version == GameVersion::Jak2 ? 7 : 0x6f;
u32 right_id = version == GameVersion::Jak2 ? 8 : 0x70;
if (eye_out && (pc_combo_tex_id >> 16) == eye_tpage) {
auto tex_it = tex_db.textures.find(pc_combo_tex_id);
if (tex_it == tex_db.textures.end()) {
// fmt::print("{} got dynamic merc texture (no known texture)\n", debug_name);
} else {
// fmt::print("{} got dynamic merc texture (will overwrite {})\n", debug_name,
// tex_it->second.name);
}
u32 idx = pc_combo_tex_id & 0xffff;
if (idx == left_id || idx == right_id) {
if (!hdr.eye_ctrl) {
fmt::print("no eye ctrl, but expected one");
if (debug_name != "kor-break-lod0") {
ASSERT(false);
}
}
if (idx == left_id) {
*eye_out = (hdr.eye_ctrl->eye_slot * 2);
} else if (idx == right_id) {
*eye_out = (hdr.eye_ctrl->eye_slot * 2) + 1;
}
} else {
// fmt::print("got unknown tex id in eye page: {}\n", idx);
}
}
return idx_in_level_texture;
}
ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
const MercCtrlHeader& ctrl_header,
const std::vector<level_tools::TextureRemap>& map,
const std::string& debug_name,
size_t ctrl_idx,
size_t effect_idx,
bool dump,
const TextureDB& tex_db,
tfrag3::Level& out,
GameVersion version) {
ConvertedMercEffect result;
result.ctrl_idx = ctrl_idx;
result.effect_idx = effect_idx;
if (ctrl_header.eye_ctrl) {
result.eye_slot = ctrl_header.eye_ctrl->eye_slot;
}
if (input_effect.extra_info.shader) {
result.has_envmap = true;
result.envmap_mode =
process_draw_mode(*input_effect.extra_info.shader, false, false, false, false);
result.envmap_mode.set_ab(true);
u32 new_tex = remap_texture(input_effect.extra_info.shader->original_tex, map);
ASSERT(result.envmap_mode.get_tcc_enable());
ASSERT(result.envmap_mode.get_alpha_blend() == DrawMode::AlphaBlend::SRC_0_DST_DST);
// texture the texture page/texture index, and convert to a PC port texture ID
u32 tpage = new_tex >> 20;
u32 tidx = (new_tex >> 8) & 0b1111'1111'1111;
u32 tex_combo = (((u32)tpage) << 16) | tidx;
result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap", tex_combo,
ctrl_header, nullptr, version);
} else if (input_effect.envmap_or_effect_usage) {
u32 tex_combo = 0;
switch (version) {
case GameVersion::Jak1: {
u32 env = 0x10000000; // jak 1, check for jak 2.
u32 tpage = env >> 20;
u32 tidx = (env >> 8) & 0b1111'1111'1111;
tex_combo = (((u32)tpage) << 16) | tidx;
} break;
case GameVersion::Jak2: {
u32 tpage = 0x1f;
u32 tidx = 2;
tex_combo = (((u32)tpage) << 16) | tidx;
} break;
default:
ASSERT_NOT_REACHED();
}
result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap-default", tex_combo,
ctrl_header, nullptr, version);
DrawMode mode;
mode.set_at(false);
mode.enable_zt();
mode.enable_depth_write();
mode.set_depth_test(GsTest::ZTest::GEQUAL);
// check these
mode.disable_ab();
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST);
mode.set_tcc(true);
mode.set_decal(false);
mode.set_filt_enable(true);
mode.set_clamp_s_enable(true);
mode.set_clamp_t_enable(true);
result.has_envmap = true;
result.envmap_mode = mode;
result.envmap_mode.set_ab(true);
}
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
bool use_alpha_blend = false;
bool depth_write = true;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
if (version == GameVersion::Jak2) {
constexpr int kWaterTexture = 4;
constexpr int kAlphaTexture = 3;
if (input_effect.texture_index == kAlphaTexture) {
use_alpha_blend = true;
}
if (input_effect.texture_index == kWaterTexture) {
depth_write = false;
use_alpha_blend = true;
}
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
}
// full reset of state per effect.
// we have no idea what the previous effect draw will be - it might be given to
// mercneric.
bool shader_set = false; // no previous shader can reliably be known
MercState merc_state; // current gs settings/matrix slots
MercMemory merc_memories[2]; // double buffered output
int memory_buffer_toggle = 0; // which output we're in
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
int combined_lump4_addr = 0;
for (size_t fi = 0; fi < input_effect.frag_ctrl.size(); fi++) {
const auto& frag = input_effect.frag_geo[fi];
const auto& frag_ctrl = input_effect.frag_ctrl[fi];
// first, deal with matrices
for (size_t mi = 0; mi < frag_ctrl.mat_xfer_count; mi++) {
const auto& xfer = frag_ctrl.mat_dest_data[mi];
merc_state.vu1_matrix_slots[vu1_addr_to_matrix_slot(xfer.matrix_dest)] = xfer.matrix_number;
}
if (!shader_set) {
// if we don't have a shader set, we shouldn't have anything that reuses the last shader
ASSERT(frag.header.strip_len == 0);
// and we should set the shader
ASSERT(frag.fp_header.shader_cnt > 0);
shader_set = true;
}
// run the frag.
// this will add vertices to the per-effect vertex lists and also update the merc memory
// to point to these.
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
bool can_be_modified = false;
if (fi < input_effect.blend_ctrl.size()) {
can_be_modified = input_effect.blend_ctrl.at(fi).blend_vtx_count > 0;
}
if (input_effect.effect_bits & kRippleEffectBit) {
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,
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
merc_memories[memory_buffer_toggle], can_be_modified, combined_lump4_addr, fi);
u32 vert_count = frag.lump4_unpacked.size() / 3;
combined_lump4_addr += vert_count;
result.verts_per_frag.push_back(vert_count);
// we'll add draws after this draw, but wait to actually populate the index lists until
// we've processed all the vertices.
size_t first_draw_to_update = result.draws.size();
// continuation of a previous shader
if (frag.header.strip_len) {
// add the continued draw
auto& new_draw = result.draws.emplace_back();
new_draw.ctrl_idx = ctrl_idx;
new_draw.effect_idx = effect_idx;
new_draw.frag_idx = fi;
new_draw.state = merc_state;
new_draw.vtx_offset = 1;
// just remember where it started. we might copy some vertices from a later draw in this
// fragment back to this draw, and at this point we don't know what the adc flags would be.
// (or their index, because we are deduplicated)
new_draw.vtx_nloop = frag.header.strip_len;
}
// loop over fresh shaders
for (size_t i = 0; i < frag.fp_header.shader_cnt; i++) {
const auto& shader = frag.shaders.at(i);
// update merc state from shader (will hold over to next fragment, if needed)
bool fog = ctrl_header.disable_fog == 0;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
merc_state.merc_draw_mode.mode =
process_draw_mode(shader, result.has_envmap, use_alpha_blend, depth_write, fog);
if (!merc_state.merc_draw_mode.mode.get_tcc_enable()) {
ASSERT(false);
}
u32 new_tex = remap_texture(shader.original_tex, map);
// texture the texture page/texture index, and convert to a PC port texture ID
u32 tpage = new_tex >> 20;
u32 tidx = (new_tex >> 8) & 0b1111'1111'1111;
u32 tex_combo = (((u32)tpage) << 16) | tidx;
2022-11-30 22:36:09 -05:00
// remember the texture id
merc_state.merc_draw_mode.pc_combo_tex_id = tex_combo;
// add the draw
auto& new_draw = result.draws.emplace_back();
new_draw.ctrl_idx = ctrl_idx;
new_draw.effect_idx = effect_idx;
new_draw.frag_idx = fi;
new_draw.state = merc_state;
// write the shader to memory
merc_memories[memory_buffer_toggle].memory.at(shader.output_offset).kind =
MercOutputQuadword::Kind::SHADER_START;
for (int mi = 1; mi < 6; mi++) {
// fill 5qw of adgif data with invalid. make sure we don't read verts from here
merc_memories[memory_buffer_toggle].memory.at(shader.output_offset + mi).kind =
MercOutputQuadword::Kind::INVALID;
}
// write the giftag for primitives
auto& prim_packet = merc_memories[memory_buffer_toggle].memory.at(shader.output_offset + 6);
prim_packet.kind = MercOutputQuadword::Kind::PRIM_START;
prim_packet.nloop_count = shader.next_strip_nloop;
// update draw from hidden fields in adgif.
new_draw.vtx_offset = shader.output_offset + 7;
new_draw.vtx_nloop = shader.next_strip_nloop;
}
// copy from other places inside this output buffer
u32 srcdst_ptr = frag.header.srcdest_off;
for (u32 sci = 0; sci < frag.header.samecopy_cnt; sci++) {
auto& cpy = frag.unsigned_four_including_header[srcdst_ptr];
// lg::print("sci: {}\n", cpy.to_string_hex_byte());
u32 src = cpy[0];
auto& vert = merc_memories[memory_buffer_toggle].memory.at(src);
u32 dst = cpy[1];
auto& dvert = merc_memories[memory_buffer_toggle].memory.at(dst);
if (vert.kind == MercOutputQuadword::Kind::VTX_START) {
dvert = vert;
if (cpy[3]) {
// dvert.adc = true;
dvert.adc = !dvert.adc;
}
} else {
lg::warn("sc missing vert");
dvert.kind = MercOutputQuadword::Kind::INVALID;
}
srcdst_ptr++;
}
// "cross" copy from the other output buffer
for (u32 cci = 0; cci < frag.header.crosscopy_cnt; cci++) {
auto& cpy = frag.unsigned_four_including_header[srcdst_ptr];
// lg::print("cci: {}\n", cpy.to_string_hex_byte());
u32 src = cpy[0];
auto& vert = merc_memories[memory_buffer_toggle ^ 1].memory.at(src);
u32 dst = cpy[1];
auto& dvert = merc_memories[memory_buffer_toggle].memory.at(dst);
if (vert.kind == MercOutputQuadword::Kind::VTX_START) {
dvert = vert;
if (cpy[3]) {
// dvert.adc = true;
dvert.adc = !dvert.adc;
}
} else {
lg::warn("cc missing vert");
dvert.kind = MercOutputQuadword::Kind::INVALID;
}
srcdst_ptr++;
}
// now that we've copied all vertices, create index lists.
for (size_t i = first_draw_to_update; i < result.draws.size(); i++) {
auto& draw = result.draws[i];
draw.indices = index_list_from_packet(draw.vtx_offset, draw.vtx_nloop,
merc_memories[memory_buffer_toggle]);
}
memory_buffer_toggle ^= 1;
}
if (dump) {
auto file_path = file_util::get_file_path(
{"debug_out/merc", fmt::format("{}_{}.ply", debug_name, effect_idx)});
file_util::create_dir_if_needed_for_file(file_path);
file_util::write_text_file(file_path, debug_dump_to_ply(result.draws, result.vertices));
}
return result;
}
u8 convert_mat(int in) {
if (in >= 0) {
return in;
} else {
return 0;
}
}
tfrag3::MercVertex convert_vertex(const MercUnpackedVtx& vtx, float xyz_scale) {
tfrag3::MercVertex out;
out.pos[0] = vtx.pos[0] * xyz_scale;
out.pos[1] = vtx.pos[1] * xyz_scale;
out.pos[2] = vtx.pos[2] * xyz_scale;
out.pad0 = 0;
out.normal[0] = vtx.nrm[0];
out.normal[1] = vtx.nrm[1];
out.normal[2] = vtx.nrm[2];
out.pad1 = 0;
out.weights[0] = vtx.mat_weights[0];
out.weights[1] = vtx.mat_weights[1];
out.weights[2] = vtx.mat_weights[2];
out.pad2 = 0;
out.st[0] = vtx.st[0];
out.st[1] = vtx.st[1];
out.rgba[0] = vtx.rgba[0];
out.rgba[1] = vtx.rgba[1];
out.rgba[2] = vtx.rgba[2];
out.rgba[3] = vtx.rgba[3];
out.mats[0] = convert_mat(vtx.skel_mats[0]);
out.mats[1] = convert_mat(vtx.skel_mats[1]);
out.mats[2] = convert_mat(vtx.skel_mats[2]);
out.pad3 = 0;
return out;
}
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
struct VertexSourceInfo {
int combined_lump4;
int frag;
int flump4;
};
void create_modifiable_vertex_data(
const std::vector<bool>& vtx_mod_flag,
const std::vector<VertexSourceInfo>& vtx_srcs,
tfrag3::MercModelGroup& out,
size_t first_out_vertex,
size_t first_out_model,
const std::vector<std::vector<ConvertedMercEffect>>& all_effects) {
ASSERT(vtx_mod_flag.size() + first_out_vertex == out.vertices.size());
// we need to be able to modify some vertices at runtime.
// this can be detected vertex-by-vertex
// the plan is to find MercEffects that contain modifiable vertices, and provide an alternate way
// to draw them. In the case where no vertices should be modified, we can fall back to the normal
// merc drawing path.
// In this modifiable draw path, there will be a list of "fixed draws", which draw vertices that
// cannot be modified. This set is known at build-time.
// The "mod draws" will draw the modifiable vertices. These use the normal index buffer, but
// index into a per-effect modifiable vertex buffer.
// std::vector<tfrag3::MercDraw> fixed_draws, mod_draws;
// some stats
int num_tris = 0; // all triangles
int mod_tris = 0; // triangles in mod draws
// loop over models added from this art-group
for (size_t mi = first_out_model; mi < out.models.size(); mi++) {
auto& model = out.models.at(mi);
// loop over "effects" within this model. the pc format merges all fragments in an effect
// together.
for (size_t ei = 0; ei < model.effects.size(); ei++) {
auto& effect = model.effects[ei];
std::vector<std::vector<u32>> inds_per_mod_draw;
for (const auto& draw : effect.all_draws) {
num_tris += draw.num_triangles;
// first check to see what's in this draw
bool found_mod = false;
bool found_fixed = false;
for (int i = 0; i < (int)draw.index_count; i++) {
u32 idx = out.indices.at(draw.first_index + i);
if (idx == UINT32_MAX) {
continue;
}
ASSERT(idx >= first_out_vertex);
if (vtx_mod_flag.at(idx - first_out_vertex)) {
found_mod = true;
} else {
found_fixed = true;
}
}
if (!found_fixed && !found_mod) {
// nothing found at all, bad
ASSERT_NOT_REACHED();
} else if (found_fixed && !found_mod) {
// only fixed. can just copy the fixed draw
effect.mod.fix_draw.push_back(draw);
} else if (found_mod && !found_fixed) {
// only mod
effect.mod.mod_draw.push_back(draw);
auto& inds_out = inds_per_mod_draw.emplace_back();
for (u32 i = 0; i < draw.index_count; i++) {
inds_out.push_back(out.indices.at(draw.first_index + i));
}
mod_tris += draw.num_triangles;
} else {
// it's a mix...
std::vector<std::vector<u32>> strips;
strips.emplace_back();
for (u32 i = 0; i < draw.index_count; i++) {
u32 val = out.indices.at(draw.first_index + i);
if (val == UINT32_MAX) {
if (!strips.back().empty()) {
strips.emplace_back();
}
} else {
strips.back().push_back(val);
}
}
tfrag3::MercDraw mod = draw;
tfrag3::MercDraw fix = draw;
std::vector<u32> mod_ind, fix_ind;
for (auto& strip : strips) {
bool strip_has_mod = false;
for (auto ind : strip) {
if (vtx_mod_flag.at(ind - first_out_vertex)) {
strip_has_mod = true;
break;
}
}
if (strip_has_mod) {
mod_ind.insert(mod_ind.end(), strip.begin(), strip.end());
mod_ind.push_back(UINT32_MAX);
} else {
fix_ind.insert(fix_ind.end(), strip.begin(), strip.end());
fix_ind.push_back(UINT32_MAX);
}
}
mod.index_count = mod_ind.size();
inds_per_mod_draw.push_back(mod_ind);
fix.first_index = out.indices.size();
fix.index_count = fix_ind.size();
out.indices.insert(out.indices.end(), fix_ind.begin(), fix_ind.end());
effect.mod.mod_draw.push_back(mod);
effect.mod.fix_draw.push_back(fix);
}
} // for draw
// if there are no modifiable draws, we can't possible modify anything, so not worth
// storing the fixed draws
if (effect.mod.mod_draw.empty()) {
effect.mod.fix_draw.clear();
} else {
effect.has_mod_draw = true;
// need to set up the vertex buffer for the modifiable draws
// map of original vertex indices to mod buffer index
std::unordered_map<u32, u32> vtx_to_mod_vtx;
for (size_t mdi = 0; mdi < effect.mod.mod_draw.size(); mdi++) {
auto& draw = effect.mod.mod_draw[mdi];
auto& orig_inds = inds_per_mod_draw.at(mdi);
u32 new_first_index = out.indices.size();
for (auto vidx : orig_inds) {
if (vidx == UINT32_MAX) {
out.indices.push_back(UINT32_MAX);
continue; // strip restart
}
const auto& existing = vtx_to_mod_vtx.find(vidx);
if (existing == vtx_to_mod_vtx.end()) {
// add vertex to mod buffer
auto idx = effect.mod.vertices.size();
vtx_to_mod_vtx[vidx] = idx;
effect.mod.vertices.push_back(out.vertices.at(vidx));
auto src = vtx_srcs.at(vidx - first_out_vertex);
ASSERT(src.combined_lump4 < UINT16_MAX);
effect.mod.vertex_lump4_addr.push_back(src.combined_lump4);
u32 frag_idx = src.frag;
if (frag_idx >= effect.mod.fragment_mask.size()) {
effect.mod.fragment_mask.resize(frag_idx + 1);
}
effect.mod.fragment_mask[frag_idx] = true;
out.indices.push_back(idx);
} else {
out.indices.push_back(existing->second);
}
}
draw.first_index = new_first_index;
}
// splice out masked fragments, the renderer won't index them
const auto& frag_counts = all_effects.at(mi - first_out_model).at(ei).verts_per_frag;
std::unordered_map<u32, u32> old_to_new;
u32 old_idx = 0;
u32 new_idx = 0;
for (size_t fi = 0; fi < effect.mod.fragment_mask.size(); fi++) {
if (effect.mod.fragment_mask[fi]) {
for (u32 vi = 0; vi < frag_counts.at(fi); vi++) {
old_to_new[old_idx] = new_idx;
old_idx++;
new_idx++;
}
} else {
old_idx += frag_counts.at(fi);
}
}
effect.mod.expect_vidx_end = new_idx;
for (auto& v : effect.mod.vertex_lump4_addr) {
v = old_to_new.at(v);
}
}
}
}
}
/*!
* Top-level merc extraction
*/
void extract_merc(const ObjectFileData& ag_data,
const TextureDB& tex_db,
const DecompilerTypeSystem& dts,
const std::vector<level_tools::TextureRemap>& map,
tfrag3::Level& out,
bool dump_level,
GameVersion version) {
if (dump_level) {
file_util::create_dir_if_needed(file_util::get_file_path({"debug_out/merc"}));
}
// find all merc-ctrls in the object file
auto ctrl_locations = find_merc_ctrls(ag_data.linked_data);
// extract them. this does very basic unpacking of data, as done by the VIF/DMA on PS2.
std::vector<MercCtrl> ctrls;
for (auto location : ctrl_locations) {
auto ctrl = extract_merc_ctrl(ag_data.linked_data, dts, location);
ctrls.push_back(ctrl);
}
// extract draws. this does no regrouping yet.
std::vector<std::vector<ConvertedMercEffect>> all_effects;
for (size_t ci = 0; ci < ctrls.size(); ci++) {
auto& effects_in_ctrl = all_effects.emplace_back();
for (size_t ei = 0; ei < ctrls[ci].effects.size(); ei++) {
2022-11-30 22:36:09 -05:00
effects_in_ctrl.push_back(convert_merc_effect(ctrls[ci].effects[ei], ctrls[ci].header, map,
ctrls[ci].name, ci, ei, dump_level, tex_db, out,
version));
}
}
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
size_t first_out_vertex = out.merc_data.vertices.size();
// convert to PC format
// first pass, before merging indices
u32 first_model = out.merc_data.models.size();
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
std::vector<bool> vertex_modify_flags;
std::vector<VertexSourceInfo> vertex_srcs;
std::vector<std::vector<std::vector<std::vector<u32>>>> indices_temp; // ctrl, effect, draw, vtx
for (size_t ci = 0; ci < ctrls.size(); ci++) {
indices_temp.emplace_back();
auto& pc_ctrl = out.merc_data.models.emplace_back();
auto& ctrl = ctrls[ci];
pc_ctrl.name = ctrl.name;
pc_ctrl.max_draws = 0;
pc_ctrl.max_bones = 0;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
pc_ctrl.st_vif_add = ctrl.header.st_vif_add;
pc_ctrl.st_magic = u32_as_float(ctrl.header.st_magic);
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
pc_ctrl.xyz_scale = ctrl.header.xyz_scale;
for (size_t ei = 0; ei < ctrls[ci].effects.size(); ei++) {
indices_temp[ci].emplace_back();
auto& pc_effect = pc_ctrl.effects.emplace_back();
auto& effect = all_effects[ci][ei];
pc_effect.has_envmap = effect.has_envmap;
pc_effect.envmap_texture = effect.envmap_texture;
pc_effect.envmap_mode = effect.envmap_mode;
u32 first_vertex = out.merc_data.vertices.size();
for (auto& vtx : effect.vertices) {
auto cvtx = convert_vertex(vtx, ctrl.header.xyz_scale);
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
vertex_modify_flags.push_back(vtx.can_be_modified);
vertex_srcs.push_back({vtx.idx_in_combined_lump4, vtx.frag, vtx.flump4});
out.merc_data.vertices.push_back(cvtx);
for (int i = 0; i < 3; i++) {
pc_ctrl.max_bones = std::max(pc_ctrl.max_bones, (u32)cvtx.mats[i]);
}
}
// can do two types of de-duplication: toggling back and forth shaders and matrices
std::map<u64, u64> draw_mode_dedup;
for (auto& draw : effect.draws) {
indices_temp[ci][ei].emplace_back();
// find draw to add to, or create a new one
const auto& existing = draw_mode_dedup.find(draw.state.merc_draw_mode.as_u64());
tfrag3::MercDraw* pc_draw = nullptr;
u64 pc_draw_idx = -1;
if (existing == draw_mode_dedup.end()) {
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
pc_draw_idx = pc_effect.all_draws.size();
draw_mode_dedup[draw.state.merc_draw_mode.as_u64()] = pc_draw_idx;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
pc_draw = &pc_effect.all_draws.emplace_back();
pc_draw->mode = draw.state.merc_draw_mode.mode;
pc_draw->tree_tex_id = find_or_add_texture_to_level(
out, tex_db, ctrl.name, draw.state.merc_draw_mode.pc_combo_tex_id, ctrl.header,
&pc_draw->eye_id, version);
} else {
pc_draw_idx = existing->second;
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
pc_draw = &pc_effect.all_draws.at(pc_draw_idx);
}
for (auto idx : draw.indices) {
if (idx == UINT32_MAX) {
indices_temp[ci][ei][pc_draw_idx].push_back(idx);
} else {
indices_temp[ci][ei][pc_draw_idx].push_back(idx + first_vertex);
}
}
}
}
}
// merge indices
for (size_t ci = 0; ci < ctrls.size(); ci++) {
auto& pc_ctrl = out.merc_data.models.at(ci + first_model);
for (size_t ei = 0; ei < ctrls[ci].effects.size(); ei++) {
auto& pc_effect = pc_ctrl.effects.at(ei);
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
for (size_t di = 0; di < pc_effect.all_draws.size(); di++) {
auto& pc_draw = pc_effect.all_draws.at(di);
auto& inds = indices_temp[ci][ei][di];
pc_draw.num_triangles = clean_up_vertex_indices(inds);
pc_draw.first_index = out.merc_data.indices.size();
pc_draw.index_count = inds.size();
out.merc_data.indices.insert(out.merc_data.indices.end(), inds.begin(), inds.end());
}
}
}
[merc2] support vertex updates, use this for blerc in jak 1 and jak 2 (#2179) This PR adds a feature to merc2 to update vertices. This will be needed to efficient do effects like blerc/ripple/texture scroll. It's enabled for blerc in jak 1 and jak 2, but with a few disclaimers: - currently we still use the mips2c blerc implementation, which is slow and has some "jittering" because of integer precision. When porting to PC, there was an additional synchronization problem because blerc overwrites the merc data as its being read by the renderers. I _think_ this wasn't an issue on PS2 because the blerc dma is higher priority than the VIF1 DMA, but I'm not certain. Either way, I had to add a mutex for this on PC to avoid very slight flickering/gaps. This isn't ideal for performance, but still beats generic by a significant amount in every place I tested. If you see merc taking 2ms to draw, it is likely because it is stuck waiting on blerc to finish. This will go away once blerc itself is ported to C++. - in jak 1, we end up using generic in some cases where we could use merc. In particular maia in village3 hut. This will be fixed later once we can use merc in more places. I don't want to mess with the merc/generic selection logic when we're hopefully going to get rid of it soon. - There is no support for ripple or texture scroll. These use generic on jak 1, and remain broken on jak 2. - Like with `emerc`, jak 1 has a toggle to go back to the old behavior `*blerc-hack*`. - In most cases, toggling this causes no visual differences. One exception is Gol's teeth. I believe this is caused by texture coordinate rounding issues, where generic has an additional float -> int -> float compared to PC merc. It is very hard to notice so I'm not going to worry about it.
2023-01-31 18:23:39 -05:00
create_modifiable_vertex_data(vertex_modify_flags, vertex_srcs, out.merc_data, first_out_vertex,
first_model, all_effects);
// compute max draws
for (u32 mi = first_model; mi < out.merc_data.models.size(); mi++) {
auto& model = out.merc_data.models[mi];
model.max_draws = 0;
for (auto& e : model.effects) {
model.max_draws += e.all_draws.size();
if (e.has_mod_draw) {
model.max_draws += e.mod.mod_draw.size() + e.mod.fix_draw.size();
}
}
}
}
} // namespace decompiler