[merc2] Support emerc (#2147)

This adds environment mapping support to `Merc2`, and turns it on for
Jak 1 and Jak 2.

- The performance is much better
- Jak 1 can be toggled back to the old behavior with `(set! *emerc-hack*
#f)`. The new environment mapping is identical to the old one everywhere
I checked.
- Jak 1 still falls back to generic for ripple/texscroll/blerc/eyes -
there's still no dynamic texture or vertex updating support. The eye
detection stuff will sometimes flag stuff as eyes which is not eyes,
which is fine, but means that generic will be used in some places where
emerc could be used. For example, the shiny plates on jak's arm will be
drawn with generic because jak has eyes.
- Jak 2 hasn't been checked super carefully against PCSX2 yet.
- Jak 2 still isn't technically using emerc, but instead putting emerc
models in the merc bucket.
- The interface to merc is a lot different now and totally custom
OpenGOAL DMA code. The original merc drawing asm doesn't run anymore.
- The FR3 format changed
- Something funky going on with foreground lighting in escape, but
doesn't seem to be related to this change?

Performance comparison, jak 1, in likely the most generic-merc heavy
spot:

![image](https://user-images.githubusercontent.com/48171810/213882718-feb2ab59-95a9-44a2-b0e5-95fba860c7b0.png)

![image](https://user-images.githubusercontent.com/48171810/213882736-8dbbf4c9-6bbf-4d0b-96ce-78d63274660c.png)
This commit is contained in:
water111 2023-01-22 18:30:31 -05:00 committed by GitHub
parent 50c57e23be
commit 0fcc7eb8e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 3168 additions and 433 deletions

View File

@ -265,6 +265,10 @@ void MercEffect::serialize(Serializer& ser) {
for (auto& draw : draws) {
draw.serialize(ser);
}
ser.from_ptr(&envmap_mode);
ser.from_ptr(&envmap_texture);
ser.from_ptr(&has_envmap);
}
void MercModel::serialize(Serializer& ser) {

View File

@ -53,7 +53,7 @@ enum MemoryUsageCategory {
NUM_CATEGORIES
};
constexpr int TFRAG3_VERSION = 21;
constexpr int TFRAG3_VERSION = 22;
// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
@ -370,6 +370,9 @@ struct MercDraw {
struct MercEffect {
std::vector<MercDraw> draws;
DrawMode envmap_mode;
u32 envmap_texture;
bool has_envmap = false;
void serialize(Serializer& ser);
};

View File

@ -113,6 +113,7 @@ struct VifCode {
ITOP = 0b100,
STMOD = 0b101,
PC_PORT = 0b1000, // not a valid PS2 VIF code, but we use this to signal PC-PORT specific stuff
PC_PORT2 = 0b1001,
MSK3PATH = 0b110,
MARK = 0b111,
FLUSHE = 0b10000,

View File

@ -424,7 +424,7 @@ class DrawMode {
SRC_DST_FIX_DST = 4, // fix = 64
ZERO_SRC_SRC_DST = 5,
SRC_SRC_SRC_SRC = 6,
SRC_0_DST_DST = 7
SRC_0_DST_DST = 7 // Note: requires color_mult tricks
};
enum class AlphaTest {

View File

@ -2,6 +2,7 @@
#include "common/dma/gs.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/util/DecompilerTypeSystem.h"
#include "third-party/fmt/core.h"
@ -199,6 +200,44 @@ std::string MercShader::print() const {
return result;
}
MercShader make_shader(Ref& ref, bool expected_eop) {
// adgif0
MercShader shader;
u8 adgif0_addr = deref_u8(ref, 8);
ASSERT(adgif0_addr == (u8)GsRegisterAddress::TEX0_1);
shader.output_offset = deref_u32(ref, 3);
shader.tex0 = GsTex0(deref_u64(ref, 0));
ref.byte_offset += 16;
// adgif1
u8 adgif1_addr = deref_u8(ref, 8);
ASSERT(adgif1_addr == (u8)GsRegisterAddress::TEX1_1);
shader.tex1 = GsTex1(deref_u64(ref, 0));
u16 stash = deref_u32(ref, 3);
shader.original_tex = deref_u32(ref, 2);
shader.next_strip_nloop = stash & 0x7fff;
ASSERT((!!(stash & 0x8000)) == expected_eop); // set eop on last
ref.byte_offset += 16;
// adgif2
u8 adgif2_addr = deref_u8(ref, 8);
ASSERT(adgif2_addr == (u8)GsRegisterAddress::MIPTBP1_1);
ref.byte_offset += 16;
// adgif3
u8 adgif3_addr = deref_u8(ref, 8);
ASSERT(adgif3_addr == (u8)GsRegisterAddress::CLAMP_1);
shader.clamp = deref_u64(ref, 0);
ref.byte_offset += 16;
// adgif4
u8 adgif4_addr = deref_u8(ref, 8);
ASSERT(adgif4_addr == (u8)GsRegisterAddress::ALPHA_1);
shader.alpha = GsAlpha(deref_u64(ref, 0));
ref.byte_offset += 16;
return shader;
}
TypedRef MercFragment::from_ref(TypedRef tr,
const DecompilerTypeSystem& dts,
const MercFragmentControl& control,
@ -265,40 +304,8 @@ TypedRef MercFragment::from_ref(TypedRef tr,
// fp shaders
for (int i = 0; i < fp_header.shader_cnt; i++) {
auto& shader = shaders.emplace_back();
// adgif0
u8 adgif0_addr = deref_u8(fp_ref, 8);
ASSERT(adgif0_addr == (u8)GsRegisterAddress::TEX0_1);
shader.output_offset = deref_u32(fp_ref, 3);
shader.tex0 = GsTex0(deref_u64(fp_ref, 0));
fp_ref.byte_offset += 16;
// adgif1
u8 adgif1_addr = deref_u8(fp_ref, 8);
ASSERT(adgif1_addr == (u8)GsRegisterAddress::TEX1_1);
shader.tex1 = GsTex1(deref_u64(fp_ref, 0));
u16 stash = deref_u32(fp_ref, 3);
shader.original_tex = deref_u32(fp_ref, 2);
shader.next_strip_nloop = stash & 0x7fff;
ASSERT((!!(stash & 0x8000)) == (i == fp_header.shader_cnt - 1)); // set eop on last
fp_ref.byte_offset += 16;
// adgif2
u8 adgif2_addr = deref_u8(fp_ref, 8);
ASSERT(adgif2_addr == (u8)GsRegisterAddress::MIPTBP1_1);
fp_ref.byte_offset += 16;
// adgif3
u8 adgif3_addr = deref_u8(fp_ref, 8);
ASSERT(adgif3_addr == (u8)GsRegisterAddress::CLAMP_1);
shader.clamp = deref_u64(fp_ref, 0);
fp_ref.byte_offset += 16;
// adgif4
u8 adgif4_addr = deref_u8(fp_ref, 8);
ASSERT(adgif4_addr == (u8)GsRegisterAddress::ALPHA_1);
shader.alpha = GsAlpha(deref_u64(fp_ref, 0));
fp_ref.byte_offset += 16;
bool expected_eop = (i == fp_header.shader_cnt - 1);
shaders.push_back(make_shader(fp_ref, expected_eop));
}
tr.ref.byte_offset += (header.mm_quadword_size) * 16;
@ -361,6 +368,19 @@ void MercEffect::from_ref(TypedRef tr,
for (u32 i = 0; i < frag_count; i++) {
f = frag_geo.emplace_back().from_ref(f, dts, frag_ctrl.at(i), main_control);
}
// do extra info
auto fr = get_field_ref(tr, "extra-info", dts);
const auto& word = fr.data->words_by_seg.at(fr.seg).at(fr.byte_offset / 4);
if (word.kind() == LinkedWord::PTR) {
TypedRef mei(deref_label(fr), dts.ts.lookup_type("merc-extra-info"));
u8 shader_offset = read_plain_data_field<u8>(mei, "shader-offset", dts);
if (shader_offset) {
Ref r = mei.ref;
r.byte_offset += 16 * shader_offset;
extra_info.shader = make_shader(r, false);
}
}
}
std::string MercEffect::print() {

View File

@ -1,5 +1,6 @@
#pragma once
#include <optional>
#include <string>
#include <vector>
@ -161,6 +162,10 @@ struct MercFragment {
std::string print() const;
};
struct MercExtraInfo {
std::optional<MercShader> shader;
};
struct MercEffect {
//((frag-geo merc-fragment :offset-assert 0) ;; ?
std::vector<MercFragment> frag_geo;
@ -177,6 +182,7 @@ struct MercEffect {
// (dummy1 uint8 :offset-assert 26) ??
u8 envmap_or_effect_usage;
// (extra-info merc-extra-info :offset-assert 28) ??
MercExtraInfo extra_info;
void from_ref(TypedRef tr, const DecompilerTypeSystem& dts, const MercCtrlHeader& main_control);
std::string print();

View File

@ -119,7 +119,7 @@ void extract_art_groups_from_level(const ObjectFileDB& db,
for (const auto& file : files) {
if (file.name.length() > 3 && !file.name.compare(file.name.length() - 3, 3, "-ag")) {
const auto& ag_file = db.lookup_record(file);
extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false);
extract_merc(ag_file, tex_db, db.dts, tex_remap, level_data, false, db.version());
}
}
}

View File

@ -107,6 +107,9 @@ struct ConvertedMercEffect {
// draws from all fragments.
std::vector<MercDraw> draws;
std::vector<MercUnpackedVtx> vertices;
bool has_envmap = false;
DrawMode envmap_mode;
u32 envmap_texture;
};
/*!
@ -195,6 +198,10 @@ void update_mode_from_alpha1(GsAlpha reg, DrawMode& mode) {
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 {
@ -209,7 +216,7 @@ void update_mode_from_alpha1(GsAlpha reg, DrawMode& mode) {
/*!
* Convert merc shader to PC draw mode
*/
DrawMode process_draw_mode(const MercShader& info) {
DrawMode process_draw_mode(const MercShader& info, bool enable_alpha_test) {
DrawMode mode;
/*
* (new 'static 'gs-test
@ -220,7 +227,7 @@ DrawMode process_draw_mode(const MercShader& info) {
:ztst (gs-ztest greater-equal)
)
*/
mode.enable_at();
mode.set_at(enable_alpha_test);
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
mode.set_aref(0x26);
mode.set_alpha_fail(GsTest::AlphaFail::KEEP);
@ -234,6 +241,10 @@ DrawMode process_draw_mode(const MercShader& info) {
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?)
@ -652,16 +663,106 @@ std::string debug_dump_to_ply(const std::vector<MercDraw>& draws,
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) {
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;
}
}
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) {
bool dump,
const TextureDB& tex_db,
tfrag3::Level& out,
GameVersion version) {
ConvertedMercEffect result;
result.ctrl_idx = ctrl_idx;
result.effect_idx = effect_idx;
if (input_effect.extra_info.shader) {
result.has_envmap = true;
result.envmap_mode = process_draw_mode(*input_effect.extra_info.shader, 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);
} 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);
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);
}
// full reset of state per effect.
// we have no idea what the previous effect draw will be - it might be given to
// mercneric.
@ -718,7 +819,10 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
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)
merc_state.merc_draw_mode.mode = process_draw_mode(shader);
merc_state.merc_draw_mode.mode = process_draw_mode(shader, result.has_envmap);
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
@ -860,7 +964,8 @@ void extract_merc(const ObjectFileData& ag_data,
const DecompilerTypeSystem& dts,
const std::vector<level_tools::TextureRemap>& map,
tfrag3::Level& out,
bool dump_level) {
bool dump_level,
GameVersion version) {
if (dump_level) {
file_util::create_dir_if_needed(file_util::get_file_path({"debug_out/merc"}));
}
@ -880,7 +985,8 @@ void extract_merc(const ObjectFileData& ag_data,
auto& effects_in_ctrl = all_effects.emplace_back();
for (size_t ei = 0; ei < ctrls[ci].effects.size(); ei++) {
effects_in_ctrl.push_back(convert_merc_effect(ctrls[ci].effects[ei], ctrls[ci].header, map,
ctrls[ci].name, ci, ei, dump_level));
ctrls[ci].name, ci, ei, dump_level, tex_db, out,
version));
}
}
@ -901,6 +1007,9 @@ void extract_merc(const ObjectFileData& ag_data,
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);
@ -925,36 +1034,8 @@ void extract_merc(const ObjectFileData& ag_data,
draw_mode_dedup[draw.state.merc_draw_mode.as_u64()] = pc_draw_idx;
pc_draw = &pc_effect.draws.emplace_back();
pc_draw->mode = draw.state.merc_draw_mode.mode;
u32 idx_in_level_texture = UINT32_MAX;
for (u32 i = 0; i < out.textures.size(); i++) {
if (out.textures[i].combo_id == draw.state.merc_draw_mode.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(draw.state.merc_draw_mode.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 {}",
draw.state.merc_draw_mode.pc_combo_tex_id, ctrl.name,
draw.state.merc_draw_mode.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 = draw.state.merc_draw_mode.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;
}
}
pc_draw->tree_tex_id = idx_in_level_texture;
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);
} else {
pc_draw_idx = existing->second;
pc_draw = &pc_effect.draws.at(pc_draw_idx);

View File

@ -13,5 +13,6 @@ void extract_merc(const ObjectFileData& ag_data,
const DecompilerTypeSystem& dts,
const std::vector<level_tools::TextureRemap>& map,
tfrag3::Level& out,
bool dump_level);
bool dump_level,
GameVersion version);
} // namespace decompiler

View File

@ -9,7 +9,7 @@
;; vf05 = [lt1_color]
;; vf06 = [lt2_color]
;; vf07 = [lt_ambient]
;; vf17 = [2048, 255, -65537, xyz-add.x] (the ?? is set per fragment)
;; vf17 = [2048, 255, -65537, xyz-add.x]
;; vf18 = [st-out-X, st-out-X, -65537, xyz-add.y] (X = a if xtop = 0, X = b otherwise)
;; vf19 = [st-magic, st-magic, -65537, xyz-add.z]
;; vf22 = hvdf-offset (does get set to others, but is always restored)

File diff suppressed because it is too large Load Diff

View File

@ -88,6 +88,7 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
using namespace jak2;
m_bucket_renderers.resize((int)BucketId::MAX_BUCKETS);
m_bucket_categories.resize((int)BucketId::MAX_BUCKETS, BucketCategory::OTHER);
// 0
init_bucket_renderer<VisDataHandler>("vis", BucketCategory::OTHER, BucketId::SPECIAL_BUCKET_2);
init_bucket_renderer<TextureUploadHandler>("tex-lcom-sky-pre", BucketCategory::TEX,

View File

@ -94,6 +94,7 @@ ShaderLibrary::ShaderLibrary(GameVersion version) {
at(ShaderId::SPRITE_DISTORT_INSTANCED) = {"sprite_distort_instanced", version};
at(ShaderId::POST_PROCESSING) = {"post_processing", version};
at(ShaderId::DEPTH_CUE) = {"depth_cue", version};
at(ShaderId::EMERC) = {"emerc", version};
for (auto& shader : m_shaders) {
ASSERT_MSG(shader.okay(), "error compiling shader");

View File

@ -47,6 +47,7 @@ enum class ShaderId {
SPRITE_DISTORT_INSTANCED = 21,
POST_PROCESSING = 22,
DEPTH_CUE = 23,
EMERC = 24,
MAX_SHADERS
};

View File

@ -34,6 +34,8 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap)
glDisable(GL_DEPTH_TEST);
}
DoubleDraw double_draw;
if (mode.get_ab_enable() && mode.get_alpha_blend() != DrawMode::AlphaBlend::DISABLED) {
glEnable(GL_BLEND);
switch (mode.get_alpha_blend()) {
@ -61,6 +63,11 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap)
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ZERO);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
break;
case DrawMode::AlphaBlend::SRC_0_DST_DST:
glBlendFunc(GL_DST_ALPHA, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
double_draw.color_mult = 0.5f;
break;
default:
ASSERT(false);
}
@ -91,7 +98,6 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap)
// for some reason, they set atest NEVER + FB_ONLY to disable depth writes
bool alpha_hack_to_disable_z_write = false;
DoubleDraw double_draw;
float alpha_min = 0.;
if (mode.get_at_enable()) {

View File

@ -34,6 +34,7 @@ struct DoubleDraw {
DoubleDrawKind kind = DoubleDrawKind::NONE;
float aref_first = 0.;
float aref_second = 0.;
float color_mult = 1.;
};
DoubleDraw setup_tfrag_shader(SharedRenderState* render_state, DrawMode mode, ShaderId shader);

View File

@ -31,6 +31,7 @@ void Generic2::draw_debug_window() {
ImGui::Checkbox("Alpha 4", &m_alpha_draw_enable[3]);
ImGui::Checkbox("Alpha 5", &m_alpha_draw_enable[4]);
ImGui::Checkbox("Alpha 6", &m_alpha_draw_enable[5]);
ImGui::Checkbox("Alpha 7", &m_alpha_draw_enable[6]);
ImGui::Text("Max Seen:");
ImGui::Text(" frag: %d/%d %.1f%%", m_max_frags_seen, (int)m_fragments.size(),

View File

@ -268,6 +268,9 @@ void Generic2::do_draws_for_alpha(SharedRenderState* render_state,
setup_opengl_for_draw_mode(first.mode, first.fix, render_state);
setup_opengl_tex(0, first.tbp, first.mode.get_filt_enable(), first.mode.get_clamp_s_enable(),
first.mode.get_clamp_t_enable(), render_state);
// if (alpha == DrawMode::AlphaBlend::SRC_0_DST_DST) {
// glBindTexture(GL_TEXTURE_2D, render_state->texture_pool->get_placeholder_texture());
// }
glDrawElements(GL_TRIANGLE_STRIP, bucket.idx_count, GL_UNSIGNED_INT,
(void*)(sizeof(u32) * bucket.idx_idx));
prof.add_draw_call();

View File

@ -31,6 +31,7 @@ Merc2::Merc2(const std::string& name, int my_id) : BucketRenderer(name, my_id) {
for (int i = 0; i < MAX_LEVELS; i++) {
auto& draws = m_level_draw_buckets.emplace_back();
draws.draws.resize(MAX_DRAWS_PER_LEVEL);
draws.envmap_draws.resize(MAX_ENVMAP_DRAWS_PER_LEVEL);
}
}
@ -38,42 +39,72 @@ Merc2::Merc2(const std::string& name, int my_id) : BucketRenderer(name, my_id) {
* Handle the merc renderer switching to a different model.
*/
void Merc2::init_pc_model(const DmaTransfer& setup, SharedRenderState* render_state) {
// determine the name. We've packed this in a separate PC-port specific packet.
// ;; name (128 char, 8 qw)
// ;; lights (7 qw x 1)
// ;; matrix slot string (128 char, 8 qw)
// ;; matrices (7 qw x N)
// ;; flags (num-effects, effect-alpha-ignore, effect-disable)
// ;; fades (u32 x N), padding to qw aligned
// Part 1: name
const u8* input_data = setup.data;
char name[128];
strcpy(name, (const char*)setup.data);
// get the model from the loader
m_current_model = render_state->loader->get_merc_model(name);
input_data += 128;
// update stats
m_stats.num_models++;
if (m_current_model) {
for (const auto& effect : m_current_model->model->effects) {
m_stats.num_effects++;
m_stats.num_predicted_draws += effect.draws.size();
for (const auto& draw : effect.draws) {
m_stats.num_predicted_tris += draw.num_triangles;
}
// Part 2: lights
memcpy(&m_current_lights, input_data, sizeof(VuLights));
input_data += sizeof(VuLights);
// Part 3: matrix slot string
auto* matrix_array = (const u32*)(input_data + 128);
int i;
for (i = 0; i < 128; i++) {
if (input_data[i] == 0xff) {
break;
}
u32 addr;
memcpy(&addr, &matrix_array[i * 4], 4);
const u8* real_addr = setup.data - setup.data_offset + addr;
memcpy(&m_skel_matrix_buffer[input_data[i]], real_addr, sizeof(MercMat));
}
input_data += 128 + 16 * i;
// Part 4: flags
auto* flags = (const u32*)input_data;
int num_effects = flags[0];
m_current_ignore_alpha_bits = flags[1];
m_current_effect_enable_bits = flags[2];
input_data += 16;
// Part 5: fades
for (int ei = 0; ei < num_effects; ei++) {
for (int j = 0; j < 4; j++) {
m_fade_buffer[ei * 4 + j] = input_data[ei * 4 + j];
}
}
}
/*!
* Once-per-frame initialization
*/
void Merc2::init_for_frame(SharedRenderState* render_state) {
// reset state
m_current_model = std::nullopt;
m_stats = {};
// activate the merc shader used for all draws
render_state->shaders[ShaderId::MERC2].activate();
// set uniforms that we know from render_state
glUniform4f(m_uniforms.fog_color, render_state->fog_color[0] / 255.f,
render_state->fog_color[1] / 255.f, render_state->fog_color[2] / 255.f,
render_state->fog_intensity / 255);
glUniform1i(m_uniforms.gfx_hack_no_tex, Gfx::g_global_settings.hack_no_tex);
if (m_current_model) {
m_stats.num_models++;
for (const auto& effect : m_current_model->model->effects) {
bool envmap = effect.has_envmap;
m_stats.num_effects++;
m_stats.num_predicted_draws += effect.draws.size();
if (envmap) {
m_stats.num_envmap_effects++;
m_stats.num_predicted_draws += effect.draws.size();
}
for (const auto& draw : effect.draws) {
m_stats.num_predicted_tris += draw.num_triangles;
if (envmap) {
m_stats.num_predicted_tris += draw.num_triangles;
}
}
}
} else {
m_stats.num_missing_models++;
}
}
void Merc2::draw_debug_window() {
@ -84,40 +115,67 @@ void Merc2::draw_debug_window() {
ImGui::Text("Bones : %d", m_stats.num_bones_uploaded);
ImGui::Text("Lights : %d", m_stats.num_lights);
ImGui::Text("Dflush : %d", m_stats.num_draw_flush);
ImGui::Text("EEffects : %d", m_stats.num_envmap_effects);
ImGui::Text("ETris : %d", m_stats.num_envmap_tris);
}
void Merc2::init_shaders(ShaderLibrary& shaders) {
const auto& shader = shaders[ShaderId::MERC2];
init_shader_common(shaders[ShaderId::MERC2], &m_merc_uniforms, true);
init_shader_common(shaders[ShaderId::EMERC], &m_emerc_uniforms, false);
m_emerc_uniforms.fade = glGetUniformLocation(shaders[ShaderId::EMERC].id(), "fade");
}
void Merc2::init_shader_common(Shader& shader, Uniforms* uniforms, bool include_lights) {
auto id = shader.id();
shaders[ShaderId::MERC2].activate();
m_uniforms.light_direction[0] = glGetUniformLocation(id, "light_dir0");
m_uniforms.light_direction[1] = glGetUniformLocation(id, "light_dir1");
m_uniforms.light_direction[2] = glGetUniformLocation(id, "light_dir2");
m_uniforms.light_color[0] = glGetUniformLocation(id, "light_col0");
m_uniforms.light_color[1] = glGetUniformLocation(id, "light_col1");
m_uniforms.light_color[2] = glGetUniformLocation(id, "light_col2");
m_uniforms.light_ambient = glGetUniformLocation(id, "light_ambient");
shader.activate();
if (include_lights) {
uniforms->light_direction[0] = glGetUniformLocation(id, "light_dir0");
uniforms->light_direction[1] = glGetUniformLocation(id, "light_dir1");
uniforms->light_direction[2] = glGetUniformLocation(id, "light_dir2");
uniforms->light_color[0] = glGetUniformLocation(id, "light_col0");
uniforms->light_color[1] = glGetUniformLocation(id, "light_col1");
uniforms->light_color[2] = glGetUniformLocation(id, "light_col2");
uniforms->light_ambient = glGetUniformLocation(id, "light_ambient");
}
m_uniforms.hvdf_offset = glGetUniformLocation(id, "hvdf_offset");
m_uniforms.perspective[0] = glGetUniformLocation(id, "perspective0");
m_uniforms.perspective[1] = glGetUniformLocation(id, "perspective1");
m_uniforms.perspective[2] = glGetUniformLocation(id, "perspective2");
m_uniforms.perspective[3] = glGetUniformLocation(id, "perspective3");
uniforms->hvdf_offset = glGetUniformLocation(id, "hvdf_offset");
m_uniforms.fog = glGetUniformLocation(id, "fog_constants");
m_uniforms.decal = glGetUniformLocation(id, "decal_enable");
uniforms->fog = glGetUniformLocation(id, "fog_constants");
uniforms->decal = glGetUniformLocation(id, "decal_enable");
m_uniforms.fog_color = glGetUniformLocation(id, "fog_color");
m_uniforms.perspective_matrix = glGetUniformLocation(id, "perspective_matrix");
m_uniforms.ignore_alpha = glGetUniformLocation(id, "ignore_alpha");
uniforms->fog_color = glGetUniformLocation(id, "fog_color");
uniforms->perspective_matrix = glGetUniformLocation(id, "perspective_matrix");
uniforms->ignore_alpha = glGetUniformLocation(id, "ignore_alpha");
m_uniforms.gfx_hack_no_tex = glGetUniformLocation(id, "gfx_hack_no_tex");
uniforms->gfx_hack_no_tex = glGetUniformLocation(id, "gfx_hack_no_tex");
}
void Merc2::switch_to_merc2(SharedRenderState* render_state) {
render_state->shaders[ShaderId::MERC2].activate();
// set uniforms that we know from render_state
glUniform4f(m_merc_uniforms.fog_color, render_state->fog_color[0] / 255.f,
render_state->fog_color[1] / 255.f, render_state->fog_color[2] / 255.f,
render_state->fog_intensity / 255);
glUniform1i(m_merc_uniforms.gfx_hack_no_tex, Gfx::g_global_settings.hack_no_tex);
}
void Merc2::switch_to_emerc(SharedRenderState* render_state) {
render_state->shaders[ShaderId::EMERC].activate();
// set uniforms that we know from render_state
glUniform4f(m_emerc_uniforms.fog_color, render_state->fog_color[0] / 255.f,
render_state->fog_color[1] / 255.f, render_state->fog_color[2] / 255.f,
render_state->fog_intensity / 255);
glUniform1i(m_emerc_uniforms.gfx_hack_no_tex, Gfx::g_global_settings.hack_no_tex);
}
/*!
* Main merc2 rendering.
*/
void Merc2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
m_stats = {};
// skip if disabled
if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) {
@ -125,8 +183,8 @@ void Merc2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProf
}
return;
}
init_for_frame(render_state);
m_current_model = std::nullopt;
switch_to_merc2(render_state);
// iterate through the dma chain, filling buckets
handle_all_dma(dma, render_state, prof);
@ -146,25 +204,12 @@ u32 Merc2::alloc_lights(const VuLights& lights) {
return light_idx;
}
/*!
* Store light values
*/
void Merc2::set_lights(const DmaTransfer& dma) {
memcpy(&m_current_lights, dma.data, sizeof(VuLights));
}
std::string Merc2::ShaderMercMat::to_string() const {
return fmt::format("tmat:\n{}\n{}\n{}\n{}\n", tmat[0].to_string_aligned(),
tmat[1].to_string_aligned(), tmat[2].to_string_aligned(),
tmat[3].to_string_aligned());
}
void Merc2::handle_matrix_dma(const DmaTransfer& dma) {
int slot = dma.vif0() & 0xff;
ASSERT(slot < MAX_SKEL_BONES);
memcpy(&m_skel_matrix_buffer[slot], dma.data, sizeof(MercMat));
}
/*!
* Main MERC2 function to handle DMA
*/
@ -250,19 +295,17 @@ void Merc2::handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state)
// 8 qw's of low memory data
memcpy(&m_low_memory, first.data + 16, sizeof(LowMemory));
// fmt::print("low\nhvdf: {}\nfog: {}\nmat:\n{}\n{}\n{}\n{}\n\n",
// m_low_memory.hvdf_offset.to_string_aligned(), m_low_memory.fog.to_string_aligned(),
// m_low_memory.perspective[0].to_string_aligned(),
// m_low_memory.perspective[1].to_string_aligned(),
// m_low_memory.perspective[2].to_string_aligned(),
// m_low_memory.perspective[3].to_string_aligned());
set_uniform(m_uniforms.hvdf_offset, m_low_memory.hvdf_offset);
set_uniform(m_uniforms.fog, m_low_memory.fog);
for (int i = 0; i < 4; i++) {
set_uniform(m_uniforms.perspective[i], m_low_memory.perspective[i]);
}
// todo rm.
glUniformMatrix4fv(m_uniforms.perspective_matrix, 1, GL_FALSE, &m_low_memory.perspective[0].x());
switch_to_merc2(render_state);
set_uniform(m_merc_uniforms.hvdf_offset, m_low_memory.hvdf_offset);
set_uniform(m_merc_uniforms.fog, m_low_memory.fog);
glUniformMatrix4fv(m_merc_uniforms.perspective_matrix, 1, GL_FALSE,
&m_low_memory.perspective[0].x());
switch_to_emerc(render_state);
set_uniform(m_emerc_uniforms.hvdf_offset, m_low_memory.hvdf_offset);
set_uniform(m_emerc_uniforms.fog, m_low_memory.fog);
glUniformMatrix4fv(m_emerc_uniforms.perspective_matrix, 1, GL_FALSE,
&m_low_memory.perspective[0].x());
// 1 qw with another 4 vifcodes.
u32 vifcode_final_data[4];
@ -301,10 +344,6 @@ bool tag_is_nothing_next(const DmaFollower& dma) {
return dma.current_tag().kind == DmaTag::Kind::NEXT && dma.current_tag().qwc == 0 &&
dma.current_tag_vif0() == 0 && dma.current_tag_vif1() == 0;
}
bool tag_is_nothing_cnt(const DmaFollower& dma) {
return dma.current_tag().kind == DmaTag::Kind::CNT && dma.current_tag().qwc == 0 &&
dma.current_tag_vif0() == 0 && dma.current_tag_vif1() == 0;
}
} // namespace
void Merc2::handle_merc_chain(DmaFollower& dma,
@ -322,107 +361,31 @@ void Merc2::handle_merc_chain(DmaFollower& dma,
}
auto init = dma.read_and_advance();
if (init.vifcode0().kind == VifCode::Kind::FLUSHA) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
return;
int skip_count = 2;
if (render_state->version == GameVersion::Jak2) {
skip_count = 1;
}
if (init.vifcode1().kind == VifCode::Kind::PC_PORT) {
// we got a PC PORT packet. this contains some extra data to set up the model
while (init.vifcode1().kind == VifCode::Kind::PC_PORT) {
flush_pending_model(render_state, prof);
init_pc_model(init, render_state);
ASSERT(tag_is_nothing_cnt(dma) || tag_is_nothing_next(dma));
init = dma.read_and_advance(); // dummy tag in pc port
if (init.vifcode0().kind != VifCode::Kind::STROW) {
init = dma.read_and_advance();
}
if (init.vifcode0().kind != VifCode::Kind::STROW) {
init = dma.read_and_advance();
}
if (init.vifcode0().kind != VifCode::Kind::STROW) {
while (dma.current_tag_offset() != render_state->next_bucket) {
auto skip = dma.read_and_advance();
ASSERT(skip.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(skip.vifcode1().kind == VifCode::Kind::NOP);
}
return;
for (int i = 0; i < skip_count; i++) {
auto link = dma.read_and_advance();
ASSERT(link.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(link.vifcode1().kind == VifCode::Kind::NOP);
ASSERT(link.size_bytes == 0);
}
init = dma.read_and_advance();
}
// row stuff.
ASSERT(init.vifcode0().kind == VifCode::Kind::STROW);
ASSERT(init.size_bytes == 16);
// m_vif.row[0] = init.vif1();
// memcpy(m_vif.row + 1, init.data, 12);
u32 extra;
memcpy(&extra, init.data + 12, 4);
// ASSERT(extra == 0);
m_current_effect_enable_bits = extra;
m_current_ignore_alpha_bits = extra >> 16;
DmaTransfer next;
bool setting_up = true;
u32 mscal_addr = -1;
while (setting_up) {
next = dma.read_and_advance();
// fmt::print("next: {}", dma.current_tag().print());
u32 offset_in_data = 0;
// fmt::print("START {} : {} {}\n", next.size_bytes, next.vifcode0().print(),
// next.vifcode1().print());
auto vif0 = next.vifcode0();
switch (vif0.kind) {
case VifCode::Kind::NOP:
case VifCode::Kind::FLUSHE:
break;
case VifCode::Kind::STMOD:
ASSERT(vif0.immediate == 0 || vif0.immediate == 1);
// m_vif.stmod = vif0.immediate;
break;
default:
ASSERT(false);
}
auto vif1 = next.vifcode1();
switch (vif1.kind) {
case VifCode::Kind::UNPACK_V4_8: {
VifCodeUnpack up(vif1);
offset_in_data += 4 * vif1.num;
} break;
case VifCode::Kind::UNPACK_V4_32: {
VifCodeUnpack up(vif1);
if (up.addr_qw == 132 && vif1.num == 8) {
set_lights(next);
} else if (vif1.num == 7) {
handle_matrix_dma(next);
}
offset_in_data += 16 * vif1.num;
} break;
case VifCode::Kind::MSCAL:
// fmt::print("cal\n");
mscal_addr = vif1.immediate;
ASSERT(next.size_bytes == 0);
setting_up = false;
break;
default:
ASSERT(false);
}
ASSERT(offset_in_data <= next.size_bytes);
if (offset_in_data < next.size_bytes) {
ASSERT((offset_in_data % 4) == 0);
u32 leftover = next.size_bytes - offset_in_data;
if (leftover < 16) {
for (u32 i = 0; i < leftover; i++) {
ASSERT(next.data[offset_in_data + i] == 0);
}
} else {
ASSERT(false);
}
if (init.vifcode0().kind == VifCode::Kind::FLUSHA) {
int num_skipped = 0;
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
num_skipped++;
}
ASSERT(num_skipped < 4);
return;
}
}
@ -452,16 +415,6 @@ u32 Merc2::alloc_bones(int count) {
shader_mat[bv++] = skel_mat.nmat[j];
}
// we could include the effect of the perspective matrix here.
// for (int j = 0; j < 3; j++) {
// tbone_buffer[i][j] = vf15.elementwise_multiply(bone_mat[j]);
// tbone_buffer[i][j].w() += p.w() * bone_mat[j].z();
// tbone_buffer[i][j] *= scale;
// }
//
// tbone_buffer[i][3] = vf15.elementwise_multiply(bone_mat[3]) +
// m_low_memory.perspective[3]; tbone_buffer[i][3].w() += p.w() * bone_mat[3].z();
m_next_free_bone_vector += 8;
}
@ -531,6 +484,16 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN
return;
}
if (lev_bucket->next_free_envmap_draw + model->max_draws >= lev_bucket->envmap_draws.size()) {
// out of room, flush
fmt::print("MERC2 out of envmap draws, consider increasing MAX_ENVMAP_DRAWS_PER_LEVEL\n");
// or, use a more accurate max_draws for envmap.
flush_draw_buckets(render_state, prof);
// and retry the whole thing.
flush_pending_model(render_state, prof);
return;
}
u32 first_bone = alloc_bones(bone_count);
// allocate lights
@ -543,6 +506,31 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN
u8 ignore_alpha = (m_current_ignore_alpha_bits & (1 << ei));
auto& effect = model->effects[ei];
if (effect.has_envmap) {
bool nonzero_fade = false;
for (int i = 0; i < 4; i++) {
if (m_fade_buffer[4 * ei + i]) {
nonzero_fade = true;
break;
}
}
if (nonzero_fade) {
for (auto& mdraw : effect.draws) {
Draw* draw = &lev_bucket->envmap_draws[lev_bucket->next_free_envmap_draw++];
draw->first_index = mdraw.first_index;
draw->index_count = mdraw.index_count;
draw->mode = effect.envmap_mode;
draw->texture = effect.envmap_texture;
draw->first_bone = first_bone;
draw->light_idx = lights;
draw->num_triangles = mdraw.num_triangles;
draw->ignore_alpha = false;
for (int i = 0; i < 4; i++) {
draw->fade[i] = m_fade_buffer[4 * ei + i];
}
}
}
}
for (auto& mdraw : effect.draws) {
Draw* draw = &lev_bucket->draws[lev_bucket->next_free_draw++];
draw->first_index = mdraw.first_index;
@ -553,13 +541,16 @@ void Merc2::flush_pending_model(SharedRenderState* render_state, ScopedProfilerN
draw->light_idx = lights;
draw->num_triangles = mdraw.num_triangles;
draw->ignore_alpha = ignore_alpha;
for (int i = 0; i < 4; i++) {
draw->fade[i] = 0;
}
}
}
m_current_model = std::nullopt;
}
void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfilerNode& prof) {
void Merc2::flush_draw_buckets(SharedRenderState* render_state, ScopedProfilerNode& prof) {
m_stats.num_draw_flush++;
for (u32 li = 0; li < m_next_free_level_bucket; li++) {
@ -613,7 +604,7 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil
);
glVertexAttribPointer(4, // location 1 in the shader
3, // 3 values per vert
4, // 3 values per vert
GL_UNSIGNED_BYTE, // floats
GL_TRUE, // normalized
sizeof(tfrag3::MercVertex), // stride
@ -627,8 +618,6 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil
(void*)offsetof(tfrag3::MercVertex, mats[0]) // offset in array
);
int last_tex = -1;
int last_light = -1;
m_stats.num_bones_uploaded += m_next_free_bone_vector;
glBindBuffer(GL_UNIFORM_BUFFER, m_bones_buffer);
@ -636,38 +625,13 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil
m_shader_bone_vector_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
for (u32 di = 0; di < lev_bucket.next_free_draw; di++) {
auto& draw = lev_bucket.draws[di];
glUniform1i(m_uniforms.ignore_alpha, draw.ignore_alpha);
if ((int)draw.texture != last_tex) {
if (draw.texture < lev->textures.size()) {
glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture));
} else {
fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture);
}
last_tex = draw.texture;
}
if ((int)draw.light_idx != last_light) {
set_uniform(m_uniforms.light_direction[0], m_lights_buffer[draw.light_idx].direction0);
set_uniform(m_uniforms.light_direction[1], m_lights_buffer[draw.light_idx].direction1);
set_uniform(m_uniforms.light_direction[2], m_lights_buffer[draw.light_idx].direction2);
set_uniform(m_uniforms.light_color[0], m_lights_buffer[draw.light_idx].color0);
set_uniform(m_uniforms.light_color[1], m_lights_buffer[draw.light_idx].color1);
set_uniform(m_uniforms.light_color[2], m_lights_buffer[draw.light_idx].color2);
set_uniform(m_uniforms.light_ambient, m_lights_buffer[draw.light_idx].ambient);
last_light = draw.light_idx;
}
setup_opengl_from_draw_mode(draw.mode, GL_TEXTURE0, true);
glUniform1i(m_uniforms.decal, draw.mode.get_decal());
prof.add_draw_call();
prof.add_tri(draw.num_triangles);
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer,
sizeof(math::Vector4f) * draw.first_bone, 128 * sizeof(ShaderMercMat));
glDrawElements(GL_TRIANGLE_STRIP, draw.index_count, GL_UNSIGNED_INT,
(void*)(sizeof(u32) * draw.first_index));
switch_to_merc2(render_state);
do_draws(lev_bucket.draws.data(), lev, lev_bucket.next_free_draw, m_merc_uniforms, prof, false,
render_state);
if (lev_bucket.next_free_envmap_draw) {
switch_to_emerc(render_state);
do_draws(lev_bucket.envmap_draws.data(), lev, lev_bucket.next_free_envmap_draw,
m_emerc_uniforms, prof, true, render_state);
}
}
@ -675,3 +639,55 @@ void Merc2::flush_draw_buckets(SharedRenderState* /*render_state*/, ScopedProfil
m_next_free_bone_vector = 0;
m_next_free_level_bucket = 0;
}
void Merc2::do_draws(const Draw* draw_array,
const LevelData* lev,
u32 num_draws,
const Uniforms& uniforms,
ScopedProfilerNode& prof,
bool set_fade,
SharedRenderState* render_state) {
int last_tex = -1;
int last_light = -1;
for (u32 di = 0; di < num_draws; di++) {
auto& draw = draw_array[di];
glUniform1i(uniforms.ignore_alpha, draw.ignore_alpha);
if ((int)draw.texture != last_tex) {
if (draw.texture < lev->textures.size()) {
glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture));
} else {
fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture);
}
last_tex = draw.texture;
}
if ((int)draw.light_idx != last_light && !set_fade) {
set_uniform(uniforms.light_direction[0], m_lights_buffer[draw.light_idx].direction0);
set_uniform(uniforms.light_direction[1], m_lights_buffer[draw.light_idx].direction1);
set_uniform(uniforms.light_direction[2], m_lights_buffer[draw.light_idx].direction2);
set_uniform(uniforms.light_color[0], m_lights_buffer[draw.light_idx].color0);
set_uniform(uniforms.light_color[1], m_lights_buffer[draw.light_idx].color1);
set_uniform(uniforms.light_color[2], m_lights_buffer[draw.light_idx].color2);
set_uniform(uniforms.light_ambient, m_lights_buffer[draw.light_idx].ambient);
last_light = draw.light_idx;
}
setup_opengl_from_draw_mode(draw.mode, GL_TEXTURE0, true);
glUniform1i(uniforms.decal, draw.mode.get_decal());
if (set_fade) {
math::Vector4f fade =
math::Vector4f(draw.fade[0], draw.fade[1], draw.fade[2], draw.fade[3]) / 255.f;
set_uniform(uniforms.fade, fade);
ASSERT(draw.mode.get_alpha_blend() == DrawMode::AlphaBlend::SRC_0_DST_DST);
// glBindTexture(GL_TEXTURE_2D, render_state->texture_pool->get_placeholder_texture());
}
prof.add_draw_call();
prof.add_tri(draw.num_triangles);
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_bones_buffer,
sizeof(math::Vector4f) * draw.first_bone, 128 * sizeof(ShaderMercMat));
glDrawElements(GL_TRIANGLE_STRIP, draw.index_count, GL_UNSIGNED_INT,
(void*)(sizeof(u32) * draw.first_index));
}
}

View File

@ -7,9 +7,6 @@ class Merc2 : public BucketRenderer {
void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void handle_merc_chain(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
private:
enum MercDataMemory {
@ -35,30 +32,22 @@ class Merc2 : public BucketRenderer {
u32 w1;
math::Vector3f direction2;
u32 w2;
math::Vector3f color0;
u32 w3;
math::Vector3f color1;
u32 w4;
math::Vector3f color2;
u32 w5;
math::Vector3f ambient;
u32 w6;
math::Vector4f color0;
math::Vector4f color1;
math::Vector4f color2;
math::Vector4f ambient;
};
void init_for_frame(SharedRenderState* render_state);
void init_pc_model(const DmaTransfer& setup, SharedRenderState* render_state);
void handle_all_dma(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
void handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state);
u32 alloc_lights(const VuLights& lights);
void set_lights(const DmaTransfer& dma);
void handle_matrix_dma(const DmaTransfer& dma);
void flush_pending_model(SharedRenderState* render_state, ScopedProfilerNode& prof);
u32 alloc_bones(int count);
std::optional<MercRef> m_current_model = std::nullopt;
u16 m_current_effect_enable_bits = 0;
u16 m_current_ignore_alpha_bits = 0;
static constexpr int kMaxEffect = 16;
u8 m_fade_buffer[4 * kMaxEffect];
struct MercMat {
math::Vector4f tmat[4];
@ -69,7 +58,6 @@ class Merc2 : public BucketRenderer {
math::Vector4f tmat[4];
math::Vector4f nmat[3];
math::Vector4f pad;
std::string to_string() const;
};
@ -79,17 +67,17 @@ class Merc2 : public BucketRenderer {
static constexpr int MAX_LEVELS = 3;
static constexpr int MAX_DRAWS_PER_LEVEL = 1024;
static constexpr int MAX_ENVMAP_DRAWS_PER_LEVEL = 1024;
math::Vector4f m_shader_bone_vector_buffer[MAX_SHADER_BONE_VECTORS];
ShaderMercMat m_skel_matrix_buffer[MAX_SKEL_BONES];
struct {
struct Uniforms {
GLuint light_direction[3];
GLuint light_color[3];
GLuint light_ambient;
GLuint hvdf_offset;
GLuint perspective[4];
GLuint fog;
GLuint tbone;
@ -102,7 +90,23 @@ class Merc2 : public BucketRenderer {
GLuint decal;
GLuint gfx_hack_no_tex;
} m_uniforms;
GLuint fade;
};
Uniforms m_merc_uniforms, m_emerc_uniforms;
void init_shader_common(Shader& shader, Uniforms* uniforms, bool include_lights);
void init_for_frame(SharedRenderState* render_state, ShaderId shader);
void handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state);
void handle_all_dma(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
void flush_pending_model(SharedRenderState* render_state, ScopedProfilerNode& prof);
void handle_merc_chain(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
void switch_to_merc2(SharedRenderState* render_state);
void switch_to_emerc(SharedRenderState* render_state);
GLuint m_vao;
@ -110,6 +114,7 @@ class Merc2 : public BucketRenderer {
struct Stats {
int num_models = 0;
int num_missing_models = 0;
int num_chains = 0;
int num_effects = 0;
int num_predicted_draws = 0;
@ -117,6 +122,9 @@ class Merc2 : public BucketRenderer {
int num_bones_uploaded = 0;
int num_lights = 0;
int num_draw_flush = 0;
int num_envmap_effects = 0;
int num_envmap_tris = 0;
} m_stats;
struct Draw {
@ -128,19 +136,31 @@ class Merc2 : public BucketRenderer {
u16 first_bone;
u16 light_idx;
u8 ignore_alpha;
u8 fade[4];
};
struct LevelDrawBucket {
const LevelData* level = nullptr;
std::vector<Draw> draws;
std::vector<Draw> envmap_draws;
u32 next_free_draw = 0;
u32 next_free_envmap_draw = 0;
void reset() {
level = nullptr;
next_free_draw = 0;
next_free_envmap_draw = 0;
}
};
void do_draws(const Draw* draw_array,
const LevelData* lev,
u32 num_draws,
const Uniforms& uniforms,
ScopedProfilerNode& prof,
bool set_fade,
SharedRenderState* render_state);
static constexpr int MAX_LIGHTS = 1024;
VuLights m_lights_buffer[MAX_LIGHTS];
u32 m_next_free_light = 0;

View File

@ -0,0 +1,32 @@
#version 430 core
out vec4 color;
in vec3 vtx_color;
in vec2 vtx_st;
in float fog;
uniform sampler2D tex_T0;
uniform vec4 fog_color;
uniform int ignore_alpha;
uniform int decal_enable;
uniform int gfx_hack_no_tex;
void main() {
if (gfx_hack_no_tex == 0) {
vec4 T0 = texture(tex_T0, vtx_st);
color.a = T0.a;
color.rgb = T0.rgb * vtx_color;
color *= 2;
} else {
color.rgb = vtx_color;
color.a = 1;
}
color.xyz *= 0.5;
}

View File

@ -0,0 +1,132 @@
#version 430 core
// merc vertex definition
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec3 normal_in;
layout (location = 2) in vec3 weights_in;
layout (location = 3) in vec2 st_in;
layout (location = 4) in vec3 rgba;
layout (location = 5) in uvec3 mats;
// camera control
uniform vec4 hvdf_offset;
uniform vec4 fog_constants;
uniform mat4 perspective_matrix;
uniform vec4 fade;
const float SCISSOR_ADJUST = HEIGHT_SCALE * 512.0/448.0;
// output
out vec3 vtx_color;
out vec2 vtx_st;
out float fog;
struct MercMatrixData {
mat4 X;
mat3 R;
vec4 pad;
};
layout (std140, binding = 1) uniform ub_bones {
MercMatrixData bones[128];
};
void main() {
vec4 p = vec4(position_in, 1);
vec4 vtx_pos = -bones[mats[0]].X * p * weights_in[0];
vec3 rotated_nrm = bones[mats[0]].R * normal_in * weights_in[0];
// game may send garbage bones if the weight is 0, don't let NaNs sneak in.
if (weights_in[1] > 0) {
vtx_pos += -bones[mats[1]].X * p * weights_in[1];
rotated_nrm += bones[mats[1]].R * normal_in * weights_in[1];
}
if (weights_in[2] > 0) {
vtx_pos += -bones[mats[2]].X * p * weights_in[2];
rotated_nrm += bones[mats[2]].R * normal_in * weights_in[2];
}
vec4 transformed = perspective_matrix * vtx_pos;
rotated_nrm = normalize(rotated_nrm);
float Q = fog_constants.x / transformed[3];
fog = 255 - clamp(-transformed.w + hvdf_offset.w, fog_constants.y, fog_constants.z);
// emerc
vec2 st_mod = st_in;
{
vec4 vf10 = vec4(rotated_nrm, 1); // ??
// vf08 = transformed
vec4 vf08 = transformed;
// vf23 = unperspect
// unperspect (1/P(0, 0), 1/P(1, 1), 0.5, 1/P(2, 3))
vec4 vf23 = vec4(1. / perspective_matrix[0][0],
1. / perspective_matrix[1][1],
0.5,
1. / perspective_matrix[2][3]);
// vf14 = rgba-fade
vec4 vf14 = fade;
// vf24 = normal st
// mul.xyzw vf09, vf08, vf23 ;; do unperspect
vec4 vf09 = vf08 * vf23;
//subw.z vf10, vf10, vf00 ;; subtract 1 from z
vf10.z -= 1;
//addw.z vf09, vf00, vf09 ;; xyww the unperspected thing
vf09.z = vf09.w;
//mul.xyz vf15, vf09, vf10 ;;
vec3 vf15 = vf09.xyz * vf10.xyz;
//adday.xyzw vf15, vf15
//maddz.x vf15, vf21, vf15
float vf15_x = vf15.x + vf15.y + vf15.z;
//div Q, vf15.x, vf10.z
float qq = vf15_x / vf10.z;
//mulaw.xyzw ACC, vf09, vf00
vec4 ACC = vf09;
//mul.xyzw vf09, vf08, vf23
vf09 = vf08 * vf23;
//madd.xyzw vf10, vf10, Q
vf10 = ACC + vf10 * qq;
//eleng.xyz P, vf10
float P = length(vf10.xyz);
//mfp.w vf10, P
//div Q, vf23.z, vf10.w
float qqq = vf23.z / P;
//addaz.xyzw vf00, vf23
ACC = vec4(vf23.z, vf23.z, vf23.z, vf23.z + 1.);
//madd.xyzw vf10, vf10, Q
vf10 = ACC + vf10 * qqq;
st_mod = vf10.xy;
// this is required to make jak 1's envmapping look right
// otherwise it behaves like the envmap texture is mirrored.
// TODO: see if this is right for jak 2 or not.
// It _might_ make sense that this exists because we skip the multiply by Q
// below, and Q is negative (no idea how that works out with clamp).
st_mod.x = 1 - vf10.x;
st_mod.y = 1 - vf10.y;
//mulz.xy vf24, vf10, vf24 ;; mul tex by q
}
transformed.xyz *= Q;
transformed.xyz += hvdf_offset.xyz;
transformed.xy -= (2048.);
transformed.z /= (8388608);
transformed.z -= 1;
transformed.x /= (256);
transformed.y /= -(128);
transformed.xyz *= transformed.w;
transformed.y *= SCISSOR_ADJUST;
gl_Position = transformed;
vtx_color = fade.xyz;
vtx_st = st_mod;
}

View File

@ -1,7 +1,7 @@
#version 430 core
out vec4 color;
in vec3 vtx_color;
in vec4 vtx_color;
in vec2 vtx_st;
in float fog;
@ -18,16 +18,16 @@ uniform int gfx_hack_no_tex;
void main() {
if (gfx_hack_no_tex == 0) {
vec4 T0 = texture(tex_T0, vtx_st);
// all merc is tcc=rgba and modulate
if (decal_enable == 0) {
color.rgb = vtx_color * T0.rgb;
color = vtx_color * T0 * 2;
} else {
color.rgb = T0.rgb * 0.5;
color = T0;
}
color.a = T0.a;
color *= 2;
color.a *= 2;
//color.a = T0.a * 4;
} else {
color.rgb = vtx_color;
color.rgb = vtx_color.rgb;
color.a = 1;
}
@ -36,5 +36,5 @@ void main() {
discard;
}
color.xyz = mix(color.xyz, fog_color.rgb, clamp(fog_color.a * fog, 0, 1));
color.xyz = mix(color.xyz, fog_color.rgb, clamp(fog_color.a * fog, 0, 1));
}

View File

@ -5,24 +5,20 @@ layout (location = 0) in vec3 position_in;
layout (location = 1) in vec3 normal_in;
layout (location = 2) in vec3 weights_in;
layout (location = 3) in vec2 st_in;
layout (location = 4) in vec3 rgba;
layout (location = 4) in vec4 rgba;
layout (location = 5) in uvec3 mats;
// light control
uniform vec3 light_dir0;
uniform vec3 light_dir1;
uniform vec3 light_dir2;
uniform vec3 light_col0;
uniform vec3 light_col1;
uniform vec3 light_col2;
uniform vec3 light_ambient;
uniform vec4 light_col0;
uniform vec4 light_col1;
uniform vec4 light_col2;
uniform vec4 light_ambient;
// camera control
uniform vec4 hvdf_offset;
uniform vec4 perspective0;
uniform vec4 perspective1;
uniform vec4 perspective2;
uniform vec4 perspective3;
uniform vec4 fog_constants;
uniform mat4 perspective_matrix;
@ -30,7 +26,7 @@ uniform mat4 perspective_matrix;
const float SCISSOR_ADJUST = HEIGHT_SCALE * 512.0/448.0;
// output
out vec3 vtx_color;
out vec4 vtx_color;
out vec2 vtx_st;
out float fog;
@ -93,7 +89,7 @@ void main() {
vec3 light_intensity = light_dir0 * rotated_nrm.x + light_dir1 * rotated_nrm.y + light_dir2 * rotated_nrm.z;
light_intensity = max(light_intensity, vec3(0, 0, 0));
vec3 light_color = light_ambient
vec4 light_color = light_ambient
+ light_intensity.x * light_col0
+ light_intensity.y * light_col1
+ light_intensity.z * light_col2;

View File

@ -803,6 +803,7 @@ u64 execute(void* ctxt) {
const MercBucketInfo* mbi = (const MercBucketInfo*)(g_ee_main_mem + c->sgpr64(a3));
u16 use_pc_merc_bits = 0;
u16 ignore_alpha_bits = 0;
u32 fade = 0;
for (int i = 0; i < 16; i++) {
if (!mbi->effects[i].use_mercneric) {
use_pc_merc_bits |= (1 << i);
@ -949,6 +950,22 @@ block_3:
c->sw(a3, 28, a2); // sw a3, 28(a2)
c->daddiu(a2, a2, 144); // daddiu a2, a2, 144
// PC ADD BONUS DATA (bonus!)
{
// 10 qw test
u64 dmatag = 5 | (1 << 28);
memcpy(g_ee_main_mem + c->sgpr64(a2), &dmatag, 8);
u32 vif = (0b1001 << 24);
memcpy(g_ee_main_mem + c->sgpr64(a2) + 8, &vif, 4);
for (int i = 0; i < 16; i++) {
memcpy(g_ee_main_mem + c->sgpr64(a2) + 16 + i * 4, mbi->effects[i].color_fade, 4);
}
c->gprs[a2].du32[0] += 6 * 16;
}
// after first frag setup
block_5:
bc = c->sgpr64(s3) == 0; // beq s3, r0, L75

View File

@ -558,6 +558,21 @@ u64 execute(void* ctxt) {
c->sw(a3, 28, a2); // sw a3, 28(a2)
c->daddiu(a2, a2, 144); // daddiu a2, a2, 144
// PC ADD BONUS DATA (bonus!)
{
// 10 qw test
u64 dmatag = 5 | (1 << 28);
memcpy(g_ee_main_mem + c->sgpr64(a2), &dmatag, 8);
u32 vif = (0b1001 << 24);
memcpy(g_ee_main_mem + c->sgpr64(a2) + 8, &vif, 4);
for (int i = 0; i < 16; i++) {
memcpy(g_ee_main_mem + c->sgpr64(a2) + 16 + i * 4, mbi->effects[i].color_fade, 4);
}
c->gprs[a2].du32[0] += 6 * 16;
}
block_8:
bc = c->sgpr64(s3) == 0; // beq s3, r0, L115
c->addiu(s2, r0, 128); // addiu s2, r0, 128

View File

@ -937,35 +937,134 @@
`(set! (-> (the-as (pointer uint32) ,addr)) ,val)
)
(defun pc-draw-bones ((dc draw-control) (dma-buf pointer))
"Add a dma packet to tell the PC renderer which model we are renderering."
(let ((packet (the-as dma-packet dma-buf)))
;; name (128 char, 8 qw)
;; lights (7 qw x 1)
;; matrix slot string (128 char, 8 qw)
;; matrices (7 qw x N)
;; flags (num-effects, effect-alpha-ignore, effect-disable)
;; fades (u32 x N), padding to qw aligned
(defun pc-merc-draw-request ((dc draw-control) (dma-buf pointer) (matrix-buf pointer))
(let ((start-packet (the-as dma-packet dma-buf))
(qwc-total 0))
;; merc draw asm will check this.
(when (zero? (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc))
(set! (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc) packet)
(set! (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc) start-packet)
)
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 10))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! dma-buf (the pointer (&+ packet 16)))
)
(set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt)))
(set! (-> start-packet vif0) (new 'static 'vif-tag))
(set! (-> start-packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! dma-buf (the pointer (&+ start-packet 16)))
(let ((data-ptr (the-as (pointer uint128) dma-buf)))
(charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name))
)
(&+! dma-buf (* 16 10))
;; NAME: 128 char, 8 qw
(let ((data-ptr (the-as (pointer uint128) dma-buf)))
(charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name))
)
(&+! dma-buf (* 16 8))
(+! qwc-total 8)
;; merc linking needs this.
(let ((packet (the-as dma-packet dma-buf)))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 0))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag))
(set! dma-buf (the pointer (&+ packet 16)))
)
;; LIGHTS:
(quad-copy! dma-buf (the pointer (-> *merc-bucket-info* light)) 7)
(&+! dma-buf (* 16 7))
(+! qwc-total 7)
dma-buf
(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)
(matrix-out-idx 0)
)
(&+! dma-buf (* 16 8))
(+! qwc-total 8)
;; flag of matrices we've already transferred and can de-dup
(let ((transferred-matrices (new 'stack-no-clear 'array 'uint8 128)))
(dotimes (i 128) (set! (-> transferred-matrices i) 0))
(let ((merc-ctrl (-> dc lod (-> dc cur-lod) geo)))
;; loop to: grab matrices and populate flags
(dotimes (effect-idx (-> merc-ctrl header effect-count))
(when (zero? (-> *merc-bucket-info* effect effect-idx use-mercneric))
(logior! enable-mask (ash 1 effect-idx))
(when (nonzero? (-> *merc-bucket-info* effect effect-idx ignore-alpha))
(logior! ignore-alpha-mask (ash 1 effect-idx))
)
(let* ((effect (-> merc-ctrl effect effect-idx))
(frag (-> effect frag-ctrl))
)
(dotimes (frag-idx (-> effect frag-count))
(dotimes (mat-xfer-idx (-> frag mat-xfer-count))
(let ((mat-idx (-> frag mat-dest-data mat-xfer-idx matrix-number)))
(if (>= mat-idx 128)
(break!)
)
(when (zero? (-> transferred-matrices mat-idx))
;; transfer it!
(set! (-> transferred-matrices mat-idx) 1)
(set! (-> matrix-slot-string matrix-out-idx) mat-idx)
(+! matrix-out-idx 1)
(set! (-> (the (pointer pointer) dma-buf)) (&+ matrix-buf (* 128 mat-idx)))
;(quad-copy! dma-buf (&+ matrix-buf (* 128 mat-idx)) 7)
(&+! dma-buf 16)
(+! qwc-total 1)
)
)
)
(&+! frag (* 2 (-> frag mat-xfer-count)))
(&+! frag (size-of merc-fragment-control))
)
)
)
) ;; end effect loop
;; end matrix string
(while (< matrix-out-idx 128)
(set! (-> matrix-slot-string matrix-out-idx) #xff)
(+! matrix-out-idx 1)
)
;; flags
(let ((flags (the (pointer uint32) dma-buf)))
(set! (-> flags 0) (-> merc-ctrl header effect-count))
(set! (-> flags 1) ignore-alpha-mask)
(set! (-> flags 2) enable-mask)
)
(&+! dma-buf (* 16 1))
(+! qwc-total 1)
;; fades
(let ((fades (the (pointer uint32) dma-buf)))
(dotimes (i (-> merc-ctrl header effect-count))
(set! (-> fades i) (the-as uint (-> *merc-bucket-info* effect i color-fade)))
)
)
(let ((num-fades (/ (+ (-> merc-ctrl header effect-count) 3) 4)))
(&+! dma-buf (* 16 num-fades))
(+! qwc-total num-fades)
)
)
)
)
(set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc qwc-total))
;; merc linking needs this.
(let ((packet (the-as dma-packet dma-buf)))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 0))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag))
(set! dma-buf (the pointer (&+ packet 16)))
)
dma-buf
)
)
;; when set, render some environment mapped stuff with jak 2's emerc.
;; this is much faster, and does significantly speed up the game thread on finalboss.
(define *emerc-hack* #t)
(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."
@ -1256,6 +1355,13 @@
)
)
;; additional in pc to make envmap fade 0 when envmap is not used
(let ((a0-36 (the-as object (-> *merc-bucket-info* effect effect-idx))))
(dotimes (a2-15 4)
(set! (-> (the-as (pointer int8) a0-36) a2-15) 0)
)
)
;; final mercneric checks
(cond
@ -1321,10 +1427,20 @@
)
(set! (-> (the-as (pointer uint8) a0-36) 3) (the-as uint 0))
)
;; env mapping, so use mercneric.
(set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 1))
(set! used-mercneric 1)
used-mercneric
(cond
((and *emerc-hack* (not pc-force-mercneric))
(set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 0))
(set! (-> *merc-bucket-info* effect effect-idx ignore-alpha) (the-as uint 1))
(set! used-merc 1)
)
(else
;; env mapping, so use mercneric.
(set! (-> *merc-bucket-info* effect effect-idx use-mercneric) (the-as uint 1))
(set! used-mercneric 1)
used-mercneric
)
)
)
(else
;; no env map, don't use mercneric.
@ -1399,12 +1515,12 @@
)
)
)
(set! s2-0 (pc-draw-bones arg0 (the pointer s2-0)))
(if (nonzero? (-> *merc-bucket-info* need-mercprime-if-merc))
(set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 32 17))
(set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 35 20))
)
(set! s2-0 (-> dma-buf base))
(set! s2-0 (pc-merc-draw-request arg0 (the pointer s2-0) (the pointer matrix-data)))
; (if (nonzero? (-> *merc-bucket-info* need-mercprime-if-merc))
; (set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 32 17))
; (set! (-> dma-buf base) (draw-bones-merc arg0 matrix-data s2-0 35 20))
; )
; (set! s2-0 (-> dma-buf base))
)
)
) ;; end geom processing

View File

@ -629,7 +629,125 @@
)
)
(defun pc-draw-bones ((dc draw-control) (dma-buf pointer))
(defun pc-merc-draw-request ((dc draw-control) (dma-buf pointer) (matrix-buf pointer) (tex-idx int))
"Send a request to PC Merc2 to draw the given object.
Only draws the effects which match this texture index.
Just places a single big dma packet, you have to patch the end yourself."
(let ((start-packet (the-as dma-packet dma-buf))
(qwc-total 0))
(set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt)))
(set! (-> start-packet vif0) (new 'static 'vif-tag))
(set! (-> start-packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! dma-buf (the pointer (&+ start-packet 16)))
;; NAME: 128 char, 8 qw
(let ((data-ptr (the-as (pointer uint128) dma-buf)))
(charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name))
)
(&+! dma-buf (* 16 8))
(+! qwc-total 8)
;; LIGHTS:
(quad-copy! dma-buf (the pointer (-> (-> *foreground* merc-bucket-info) light)) 7)
(&+! dma-buf (* 16 7))
(+! qwc-total 7)
(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)
(matrix-out-idx 0)
)
(&+! dma-buf (* 16 8))
(+! qwc-total 8)
;; flag of matrices we've already transferred and can de-dup
(let ((transferred-matrices (new 'stack-no-clear 'array 'uint8 128)))
(dotimes (i 128) (set! (-> transferred-matrices i) 0))
(let ((merc-ctrl (-> dc lod-set lod (-> dc cur-lod) geo)))
;; loop to: grab matrices and populate flags
(dotimes (effect-idx (-> merc-ctrl header effect-count))
;; check to skip fragment. only draw if:
;; We want to use "merc"
;; We aren't disabled (new feature in jak 2)
;; We match the texture index for the bucket.
(when (and (zero? (-> (-> *foreground* merc-bucket-info) effect effect-idx merc-path))
(zero? (-> (-> *foreground* merc-bucket-info) effect effect-idx disable-draw))
(= tex-idx (-> merc-ctrl effect effect-idx texture-index))
)
;; set enable bit for pc render
(logior! enable-mask (ash 1 effect-idx))
;; set alpha bit for pc render
(when (nonzero? (-> (-> *foreground* merc-bucket-info) effect effect-idx ignore-alpha))
(logior! ignore-alpha-mask (ash 1 effect-idx))
)
(let* ((effect (-> merc-ctrl effect effect-idx))
(frag (-> effect frag-ctrl))
)
;; iterate over fragments
(dotimes (frag-idx (-> effect frag-count))
;; matrices
(dotimes (mat-xfer-idx (-> frag mat-xfer-count))
(let ((mat-idx (-> frag mat-dest-data mat-xfer-idx matrix-number)))
(if (>= mat-idx 128) ;; pc merc2 limit
(break!)
)
(when (zero? (-> transferred-matrices mat-idx))
;; transfer it.
(set! (-> transferred-matrices mat-idx) 1)
(set! (-> matrix-slot-string matrix-out-idx) mat-idx)
(+! matrix-out-idx 1)
(set! (-> (the (pointer pointer) dma-buf)) (&+ matrix-buf (* 128 mat-idx)))
(&+! dma-buf 16)
(+! qwc-total 1)
)
)
)
(&+! frag (* 2 (-> frag mat-xfer-count)))
(&+! frag (size-of merc-fragment-control))
)
)
)
) ;; end effect loop
;; end matrix string
(while (< matrix-out-idx 128)
(set! (-> matrix-slot-string matrix-out-idx) #xff)
(+! matrix-out-idx 1)
)
;; flags
(let ((flags (the (pointer uint32) dma-buf)))
(set! (-> flags 0) (-> merc-ctrl header effect-count))
(set! (-> flags 1) ignore-alpha-mask)
(set! (-> flags 2) enable-mask)
)
(&+! dma-buf (* 16 1))
(+! qwc-total 1)
;; fades
(let ((fades (the (pointer uint32) dma-buf)))
(dotimes (i (-> merc-ctrl header effect-count))
(set! (-> fades i) (the-as uint (-> (-> *foreground* merc-bucket-info) effect i color-fade)))
)
)
(let ((num-fades (/ (+ (-> merc-ctrl header effect-count) 3) 4)))
(&+! dma-buf (* 16 num-fades))
(+! qwc-total num-fades)
)
)
)
)
(set! (-> start-packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc qwc-total))
dma-buf
)
)
(defun pc-draw-bones ((dc draw-control) (dma-buf pointer) (matrix-buf pointer))
"Add a dma packet to tell the PC renderer which model we are renderering."
(let ((use-flags (new 'stack-no-clear 'array 'uint8 7))
(mctrl (-> dc mgeo))
@ -650,15 +768,7 @@
;; this one is used, update the model for pc.
;; create dma-packet to send the name:
(let ((packet (the-as dma-packet dma-buf)))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 10))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! dma-buf (the pointer (&+ packet 16)))
;; send the name
(let ((data-ptr (the-as (pointer uint128) dma-buf)))
(charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name))
)
(&+! dma-buf (* 16 10))
(set! dma-buf (pc-merc-draw-request dc dma-buf matrix-buf i))
;; create a patch packet
(let ((patch-packet (the-as dma-packet dma-buf)))
@ -690,56 +800,8 @@
)
)
)
dma-buf
; (let ((packet (the-as dma-packet dma-buf)))
; ;; merc draw asm will check this.
; (when (zero? (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc))
; (set! (-> (scratchpad-object terrain-context) work foreground bone-mem work next-merc) packet)
; )
; (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 10))
; (set! (-> packet vif0) (new 'static 'vif-tag))
; (set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
; (set! dma-buf (the pointer (&+ packet 16)))
; )
; (let ((data-ptr (the-as (pointer uint128) dma-buf)))
; (charp<-string (the (pointer uint8) (&-> data-ptr 0)) (-> dc mgeo name))
; )
; (&+! dma-buf (* 16 10))
; ;; merc linking needs this.
; (let ((packet (the-as dma-packet dma-buf)))
; (set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 0))
; (set! (-> packet vif0) (new 'static 'vif-tag))
; (set! (-> packet vif1) (new 'static 'vif-tag))
; (set! dma-buf (the pointer (&+ packet 16)))
; )
; dma-buf
;; ra is start packet
; lw s4, 0(a3) ;; s4 = first
; lw gp, 4(a3) ;; gp = patch
; movz s4, ra, s4 ;; check first, if zero, set s4 to this packet
; sq r0, 0(a2)
; lui s5, 8192
; sw s4, 0(a3) ;; store first
; or s4, a2, r0
; sw s5, 0(a2)
; daddiu a2, a2, 16
; beq gp, r0, L117
; sw s4, 4(a3)
; branch non-likely
; sll r0, r0, 0
; sw ra, 4(gp)
;; L117
)
(defun foreground-draw ((dc draw-control) (dma-buf dma-buffer) (dist-in float))
@ -991,6 +1053,7 @@
)
(set! (-> (the-as merc-effect-bucket-info v1-74) alpha) (the-as uint 128))
)
;(format 0 "envmap stat: ~A ~A~%" (-> dc process name) (logtest? (-> geo effect effect-idx effect-bits) (effect-bits emerc)))
(cond
((and (logtest? (-> dc status) (draw-control-status force-vu1))
(logtest? (-> geo effect effect-idx effect-bits) (effect-bits emerc))
@ -1006,6 +1069,7 @@
)
)
)
(set! (-> bucket-info effect effect-idx ignore-alpha)
(the-as uint (if (logtest? (-> geo effect effect-idx effect-bits) (effect-bits ignore-alpha))
1
@ -1033,7 +1097,8 @@
)
)
)
)
) ;; end if envmap
(else
(cond
((logtest? (-> geo effect effect-idx effect-bits) (effect-bits cross-fade))
@ -1070,7 +1135,8 @@
(set! effect-mask (shr effect-mask 1))
)
)
;; massive hack
;; massive hack use merc for everything..
(when (or (nonzero? (-> (scratchpad-object foreground-work) regs mercneric-used))
(nonzero? (-> (scratchpad-object foreground-work) regs emerc-used))
)
@ -1079,6 +1145,10 @@
(set! (-> (scratchpad-object foreground-work) regs merc-used) 1)
(dotimes (effect-idx (the-as int (-> geo header effect-count)))
(set! (-> bucket-info effect effect-idx merc-path) 0)
(when (logtest? (-> geo effect effect-idx effect-usage) 1)
(set! (-> bucket-info effect effect-idx ignore-alpha) 1)
)
)
)
@ -1128,12 +1198,12 @@
)
;; added
(set! dma-ptr (pc-draw-bones dc dma-ptr))
(set! dma-ptr (pc-draw-bones dc dma-ptr (the pointer (-> (scratchpad-object foreground-work) regs mtxs))))
(if (nonzero? (-> bucket-info need-mercprime-if-merc))
(set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 32 17 bucket-info))
(set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 35 20 bucket-info))
)
; (if (nonzero? (-> bucket-info need-mercprime-if-merc))
; (set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 32 17 bucket-info))
; (set! dma-ptr (foreground-merc dc (-> (scratchpad-object foreground-work) regs mtxs) dma-ptr 35 20 bucket-info))
; )
)
)
)
@ -1180,4 +1250,5 @@
;; todo foreground-draw-hud
(define *foreground-draw-engine* (new 'global 'engine 'draw 768 connection))
;; changed to perm.
(define-perm *foreground-draw-engine* engine (new 'global 'engine 'draw 768 connection))