[merc] support eyes through merc (#2300)

Support rendering eyes with merc for both jak 1 and jak 2.

For jak 1, everything should look the same, but merc will be used to
draw eyes. This means that jak is now fully drawn with merc!

For jak 2, eyes should draw, but there are still a few issues:
- the tbp/clut ptr trick is used a lot for these eye textures, so
there's a lot that use the wrong texture
- I had to enable a bunch more "texture uploads" (basically emulating
the ps2 texture system) in order to get the eyes to upload. It would be
much better if the eye renderer could somehow grab the texture from the
merc model info, skipping the vram addressing stuff entirely. I plan to
return to this.
- I disabled some sky draws in `sky-tng`. After turning on pris2
uploads, the sky flashed in a really annoying way.
This commit is contained in:
water111
2023-03-08 18:18:35 -05:00
committed by GitHub
parent 00d7065790
commit 0b8b927315
16 changed files with 267 additions and 280 deletions
+1
View File
@@ -254,6 +254,7 @@ void CollisionMesh::serialize(Serializer& ser) {
void MercDraw::serialize(Serializer& ser) {
ser.from_ptr(&mode);
ser.from_ptr(&tree_tex_id);
ser.from_ptr(&eye_id);
ser.from_ptr(&first_index);
ser.from_ptr(&index_count);
ser.from_ptr(&num_triangles);
+2 -2
View File
@@ -73,7 +73,7 @@ struct MemoryUsageTracker {
void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; }
};
constexpr int TFRAG3_VERSION = 25;
constexpr int TFRAG3_VERSION = 26;
// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
@@ -393,7 +393,7 @@ static_assert(sizeof(MercVertex) == 64);
struct MercDraw {
DrawMode mode;
u32 tree_tex_id = 0; // the texture that should be bound for the draw
u8 eye_id = 0xff; // 0xff if not eyes, (slot << 1) | (is_r)
u32 first_index;
u32 index_count;
u32 num_triangles;
+14 -1
View File
@@ -8,6 +8,11 @@
#include "third-party/fmt/core.h"
namespace decompiler {
void MercEyeCtrl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
eye_slot = read_plain_data_field<s8>(tr, "eye-slot", dts);
}
void MercCtrlHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
st_magic = read_plain_data_field<u32>(tr, "st-magic", dts);
xyz_scale = read_plain_data_field<float>(tr, "xyz-scale", dts);
@@ -35,7 +40,14 @@ void MercCtrlHeader::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
cross_copy_count = read_plain_data_field<u16>(tr, "cross-copy-count", dts);
num_verts = read_plain_data_field<u16>(tr, "num-verts", dts);
longest_edge = read_plain_data_field<float>(tr, "longest-edge", dts);
// todo masksk
auto fr = get_field_ref(tr, "eye-ctrl", dts);
const auto& word = fr.data->words_by_seg.at(fr.seg).at(fr.byte_offset / 4);
if (word.kind() == LinkedWord::PTR) {
eye_ctrl.emplace();
eye_ctrl->from_ref(TypedRef(deref_label(fr), dts.ts.lookup_type("merc-eye-ctrl")), dts);
}
// todo masks
envmap_tint = read_plain_data_field<u32>(tr, "envmap-tint", dts);
needs_clip = read_plain_data_field<u8>(tr, "needs-clip", dts);
use_isometric = read_plain_data_field<u8>(tr, "use-isometric", dts);
@@ -427,6 +439,7 @@ void MercCtrl::from_ref(TypedRef tr, const DecompilerTypeSystem& dts) {
effects.emplace_back().from_ref(eff_ref, dts, header);
eff_ref.ref.byte_offset += 32; //
}
// debug_print_blerc();
}
+7 -1
View File
@@ -12,6 +12,12 @@
namespace decompiler {
struct MercEyeCtrl {
s8 eye_slot;
// there's more...
void from_ref(TypedRef tr, const DecompilerTypeSystem& dts);
};
/*!
* per-ctrl information. the first qw is uploaded to vu1
*/
@@ -42,7 +48,7 @@ struct MercCtrlHeader {
u16 cross_copy_count;
u16 num_verts;
float longest_edge;
// todo (eye-ctrl merc-eye-ctrl :offset-assert 64)
std::optional<MercEyeCtrl> eye_ctrl;
u32 masks[3];
// (dummy-bytes uint8 48 :offset 32)
u32 envmap_tint;
+46 -4
View File
@@ -117,6 +117,7 @@ struct ConvertedMercEffect {
bool has_envmap = false;
DrawMode envmap_mode;
u32 envmap_texture;
std::optional<s8> eye_slot;
};
/*!
@@ -682,7 +683,10 @@ std::string debug_dump_to_ply(const std::vector<MercDraw>& draws,
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 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) {
@@ -710,6 +714,38 @@ int find_or_add_texture_to_level(tfrag3::Level& out,
}
}
// 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;
}
@@ -726,6 +762,9 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
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);
@@ -738,7 +777,8 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
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);
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) {
@@ -757,7 +797,8 @@ ConvertedMercEffect convert_merc_effect(const MercEffect& input_effect,
ASSERT_NOT_REACHED();
}
result.envmap_texture = find_or_add_texture_to_level(out, tex_db, "envmap-default", tex_combo);
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);
@@ -1264,7 +1305,8 @@ void extract_merc(const ObjectFileData& ag_data,
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);
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;
pc_draw = &pc_effect.all_draws.at(pc_draw_idx);
+153 -221
View File
@@ -17,35 +17,16 @@ void EyeRenderer::init_textures(TexturePool& texture_pool, GameVersion) {
for (int lr = 0; lr < 2; lr++) {
u32 tidx = pair_idx * 2 + lr;
// CPU
{
GLuint gl_tex;
glGenTextures(1, &gl_tex);
u32 tbp = EYE_BASE_BLOCK + pair_idx * 2 + lr;
TextureInput in;
in.gpu_texture = gl_tex;
in.w = 32;
in.h = 32;
in.debug_page_name = "PC-EYES";
in.debug_name = fmt::format("{}-eye-cpu-{}", lr ? "left" : "right", pair_idx);
in.id = texture_pool.allocate_pc_port_texture();
auto* gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp);
m_cpu_eye_textures[tidx] = {gl_tex, gpu_tex, tbp};
}
// GPU
{
u32 tbp = EYE_BASE_BLOCK + pair_idx * 2 + lr;
TextureInput in;
in.gpu_texture = m_gpu_eye_textures[tidx].fb.texture();
in.w = 32;
in.h = 32;
in.debug_page_name = "PC-EYES";
in.debug_name = fmt::format("{}-eye-gpu-{}", lr ? "left" : "right", pair_idx);
in.id = texture_pool.allocate_pc_port_texture();
m_gpu_eye_textures[tidx].gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp);
m_gpu_eye_textures[tidx].tbp = tbp;
}
u32 tbp = EYE_BASE_BLOCK + pair_idx * 2 + lr;
TextureInput in;
in.gpu_texture = m_gpu_eye_textures[tidx].fb.texture();
in.w = 32;
in.h = 32;
in.debug_page_name = "PC-EYES";
in.debug_name = fmt::format("{}-eye-gpu-{}", lr ? "left" : "right", pair_idx);
in.id = texture_pool.allocate_pc_port_texture();
m_gpu_eye_textures[tidx].gpu_tex = texture_pool.give_texture_and_load_to_vram(in, tbp);
m_gpu_eye_textures[tidx].tbp = tbp;
}
}
@@ -110,13 +91,8 @@ void EyeRenderer::render(DmaFollower& dma,
}
void EyeRenderer::draw_debug_window() {
ImGui::Checkbox("Use GPU", &m_use_gpu);
ImGui::Text("Time: %.3f ms\n", m_average_time_ms);
ImGui::Text("Debug:\n%s", m_debug.c_str());
if (!m_use_gpu) {
ImGui::Checkbox("bilinear", &m_use_bilinear);
}
ImGui::Checkbox("alpha hack", &m_alpha_hack);
}
//////////////////////
@@ -241,13 +217,23 @@ std::vector<EyeRenderer::SingleEyeDraws> EyeRenderer::get_draws(DmaFollower& dma
u32 pair_idx = -1;
// first draw. this is the background. It reads 0,0 of the texture uses that color everywhere.
// we'll also figure out the eye index here.
bool using_64 = false;
{
auto draw0 = read_eye_draw(dma);
ASSERT(draw0.sprite.uv0[0] == 0);
ASSERT(draw0.sprite.uv0[1] == 0);
ASSERT(draw0.sprite.uv1[0] == 0);
ASSERT(draw0.sprite.uv1[1] == 0);
if (draw0.scissor.y1 - draw0.scissor.y0 == 63) {
using_64 = true;
l_draw.using_64 = true;
r_draw.using_64 = true;
}
u32 y0 = (draw0.sprite.xyz0[1] - 512) >> 4;
if (using_64) {
y0 = (draw0.sprite.xyz0[1] - 1024) >> 5;
y0 *= 4;
}
pair_idx = y0 / SINGLE_EYE_SIZE;
l_draw.pair = pair_idx;
r_draw.pair = pair_idx;
@@ -257,6 +243,7 @@ std::vector<EyeRenderer::SingleEyeDraws> EyeRenderer::get_draws(DmaFollower& dma
l_draw.clear_color = tex_val;
r_draw.clear_color = tex_val;
} else {
fmt::print("clear lookup failed\n");
l_draw.clear_color = 0;
r_draw.clear_color = 0;
}
@@ -265,14 +252,29 @@ std::vector<EyeRenderer::SingleEyeDraws> EyeRenderer::get_draws(DmaFollower& dma
// up next is the pupil background
{
l_draw.iris = read_eye_draw(dma);
r_draw.iris = read_eye_draw(dma);
l_draw.iris_tex = tex0;
r_draw.iris_tex = tex0;
l_draw.iris_gl_tex = *render_state->texture_pool->lookup(adgif0.tex0().tbp0());
r_draw.iris_gl_tex = l_draw.iris_gl_tex;
if (dma.current_tag().qwc == 6) {
// change adgif!
auto r_iris_adgif = dma.read_and_advance();
ASSERT(r_iris_adgif.size_bytes == 96); // 5 adgifs a+d's plus tag
ASSERT(r_iris_adgif.vif0() == 0);
ASSERT(r_iris_adgif.vifcode1().kind == VifCode::Kind::DIRECT);
AdgifHelper r_iris_helper(r_iris_adgif.data + 16);
r_draw.iris = read_eye_draw(dma);
r_draw.iris_tex =
render_state->texture_pool->lookup_gpu_texture(r_iris_helper.tex0().tbp0());
r_draw.iris_gl_tex = *render_state->texture_pool->lookup(r_iris_helper.tex0().tbp0());
} else {
// same adgif
r_draw.iris = read_eye_draw(dma);
r_draw.iris_tex = tex0;
r_draw.iris_gl_tex = l_draw.iris_gl_tex;
}
}
// now we'll draw the iris on top of that
// now we'll draw the pupil on top of that
auto test1 = dma.read_and_advance();
(void)test1;
auto adgif1_dma = dma.read_and_advance();
@@ -284,11 +286,26 @@ std::vector<EyeRenderer::SingleEyeDraws> EyeRenderer::get_draws(DmaFollower& dma
if (tex1 && tex1->get_data_ptr()) {
l_draw.pupil = read_eye_draw(dma);
r_draw.pupil = read_eye_draw(dma);
l_draw.pupil_tex = tex1;
r_draw.pupil_tex = tex1;
l_draw.pupil_gl_tex = *render_state->texture_pool->lookup(adgif1.tex0().tbp0());
r_draw.pupil_gl_tex = l_draw.pupil_gl_tex;
}
if (dma.current_tag().qwc == 6) {
auto r_pupil_adgif = dma.read_and_advance();
ASSERT(r_pupil_adgif.size_bytes == 96); // 5 adgifs a+d's plus tag
ASSERT(r_pupil_adgif.vif0() == 0);
ASSERT(r_pupil_adgif.vifcode1().kind == VifCode::Kind::DIRECT);
AdgifHelper r_pupil_helper(r_pupil_adgif.data + 16);
r_draw.pupil = read_eye_draw(dma);
r_draw.pupil_tex =
render_state->texture_pool->lookup_gpu_texture(r_pupil_helper.tex0().tbp0());
r_draw.pupil_gl_tex = *render_state->texture_pool->lookup(r_pupil_helper.tex0().tbp0());
} else {
if (tex1 && tex1->get_data_ptr()) {
r_draw.pupil = read_eye_draw(dma);
r_draw.pupil_tex = tex1;
r_draw.pupil_gl_tex = l_draw.pupil_gl_tex;
}
}
// and finally the eyelid
@@ -303,17 +320,31 @@ std::vector<EyeRenderer::SingleEyeDraws> EyeRenderer::get_draws(DmaFollower& dma
{
l_draw.lid = read_eye_draw(dma);
r_draw.lid = read_eye_draw(dma);
l_draw.lid_tex = tex2;
r_draw.lid_tex = tex2;
l_draw.lid_gl_tex = *render_state->texture_pool->lookup(adgif2.tex0().tbp0());
}
if (dma.current_tag().qwc == 6) {
auto r_lid_adgif = dma.read_and_advance();
ASSERT(r_lid_adgif.size_bytes == 96); // 5 adgifs a+d's plus tag
ASSERT(r_lid_adgif.vif0() == 0);
ASSERT(r_lid_adgif.vifcode1().kind == VifCode::Kind::DIRECT);
AdgifHelper r_lid_helper(r_lid_adgif.data + 16);
r_draw.lid = read_eye_draw(dma);
r_draw.lid_tex = render_state->texture_pool->lookup_gpu_texture(r_lid_helper.tex0().tbp0());
r_draw.lid_gl_tex = *render_state->texture_pool->lookup(r_lid_helper.tex0().tbp0());
} else {
r_draw.lid = read_eye_draw(dma);
r_draw.lid_tex = tex2;
r_draw.lid_gl_tex = l_draw.lid_gl_tex;
}
auto end = dma.read_and_advance();
ASSERT(end.size_bytes == 0);
ASSERT(end.vif0() == 0);
ASSERT(end.vif1() == 0);
if (render_state->version == GameVersion::Jak1) {
auto end = dma.read_and_advance();
ASSERT(end.size_bytes == 0);
ASSERT(end.vif0() == 0);
ASSERT(end.vif1() == 0);
}
}
return draws;
}
@@ -336,185 +367,30 @@ void EyeRenderer::handle_eye_dma2(DmaFollower& dma,
ASSERT(alpha_setup.vifcode0().kind == VifCode::Kind::NOP);
ASSERT(alpha_setup.vifcode1().kind == VifCode::Kind::DIRECT);
// from the add to bucket
ASSERT(dma.current_tag().kind == DmaTag::Kind::NEXT);
ASSERT(dma.current_tag().qwc == 0);
ASSERT(dma.current_tag_vif0() == 0);
ASSERT(dma.current_tag_vif1() == 0);
dma.read_and_advance();
if (render_state->version == GameVersion::Jak1) {
// from the add to bucket
ASSERT(dma.current_tag().kind == DmaTag::Kind::NEXT);
ASSERT(dma.current_tag().qwc == 0);
ASSERT(dma.current_tag_vif0() == 0);
ASSERT(dma.current_tag_vif1() == 0);
dma.read_and_advance();
}
auto draws = get_draws(dma, render_state);
if (m_use_gpu) {
run_gpu(draws, render_state);
} else {
run_cpu(draws, render_state);
}
run_gpu(draws, render_state);
float time_ms = timer.getMs();
m_average_time_ms = m_average_time_ms * 0.95 + time_ms * 0.05;
}
//////////////////////
// CPU Drawing
//////////////////////
u32 bilinear_sample_eye(const u8* tex, float tx, float ty, int texw) {
int tx0 = tx;
int ty0 = ty;
int tx1 = tx0 + 1;
int ty1 = ty0 + 1;
tx1 = std::min(tx1, texw - 1);
ty1 = std::min(ty1, texw - 1);
u8 tex0[4];
u8 tex1[4];
u8 tex2[4];
u8 tex3[4];
memcpy(tex0, tex + (4 * (tx0 + ty0 * texw)), 4);
memcpy(tex1, tex + (4 * (tx1 + ty0 * texw)), 4);
memcpy(tex2, tex + (4 * (tx0 + ty1 * texw)), 4);
memcpy(tex3, tex + (4 * (tx1 + ty1 * texw)), 4);
u8 result[4] = {0, 0, 0, 0};
float x0w = float(tx1) - tx;
float y0w = float(ty1) - ty;
float weights[4] = {x0w * y0w, (1.f - x0w) * y0w, x0w * (1.f - y0w), (1.f - x0w) * (1.f - y0w)};
for (int i = 0; i < 4; i++) {
float total = 0;
total += weights[0] * tex0[i];
total += weights[1] * tex1[i];
total += weights[2] * tex2[i];
total += weights[3] * tex3[i];
result[i] = total;
}
// clamp
u32 tex_out;
memcpy(&tex_out, result, 4);
return tex_out;
}
template <bool blend, bool bilinear>
void draw_eye_impl(u32* out,
const EyeRenderer::EyeDraw& draw,
const GpuTexture& tex,
int pair,
int lr,
bool flipx) {
// first, figure out the rectangle we'd cover if there was no scissoring
int x0 = ((((int)draw.sprite.xyz0[0]) - 512) >> 4);
int x1 = ((((int)draw.sprite.xyz1[0]) - 512) >> 4);
if (flipx) {
std::swap(x0, x1);
}
int y0 = ((((int)draw.sprite.xyz0[1]) - 512) >> 4);
int y1 = ((((int)draw.sprite.xyz1[1]) - 512) >> 4);
// then the offset because the game tries to draw to a big texture, but we do an eye at a time
int x_off = lr * SINGLE_EYE_SIZE;
int y_off = pair * SINGLE_EYE_SIZE;
// apply scissoring bounds
int x0s = std::max(x0, (int)draw.scissor.x0) - x_off;
int y0s = std::max(y0, (int)draw.scissor.y0) - y_off;
int x1s = std::min(x1, (int)draw.scissor.x1) - x_off;
int y1s = std::min(y1, (int)draw.scissor.y1) - y_off;
// compute inverse lengths (of non-scissored)
float inv_xl = .999f / ((float)(x1 - x0));
float inv_yl = .999f / ((float)(y1 - y0));
// starts
float tx0 = tex.w * (x0s - x0 + x_off) * inv_xl;
float ty0 = tex.h * (y0s - y0 + y_off) * inv_yl;
// steps
float txs = tex.w * inv_xl;
float tys = tex.h * inv_yl;
float ty = ty0;
for (int yd = y0s; yd < y1s; yd++) {
float tx = tx0;
for (int xd = x0s; xd < x1s; xd++) {
u32 val;
if (bilinear) {
val = bilinear_sample_eye(tex.get_data_ptr(), tx, ty, tex.w);
} else {
int tc = int(tx) + tex.w * int(ty);
memcpy(&val, tex.get_data_ptr() + (4 * tc), 4);
}
if (blend) {
if ((val >> 24) != 0) {
out[xd + yd * SINGLE_EYE_SIZE] = val;
}
} else {
out[xd + yd * SINGLE_EYE_SIZE] = val;
}
tx += txs;
}
ty += tys;
}
}
template <bool blend>
void draw_eye(u32* out,
const EyeRenderer::EyeDraw& draw,
const GpuTexture& tex,
int pair,
int lr,
bool flipx,
bool bilinear) {
if (bilinear) {
draw_eye_impl<blend, true>(out, draw, tex, pair, lr, flipx);
} else {
draw_eye_impl<blend, false>(out, draw, tex, pair, lr, flipx);
}
}
void EyeRenderer::run_cpu(const std::vector<SingleEyeDraws>& draws,
SharedRenderState* render_state) {
for (auto& draw : draws) {
for (auto& x : m_temp_tex) {
x = draw.clear_color;
}
if (draw.iris_tex) {
draw_eye<false>(m_temp_tex, draw.iris, *draw.iris_tex, draw.pair, draw.lr, false,
m_use_bilinear);
}
if (draw.pupil_tex) {
draw_eye<true>(m_temp_tex, draw.pupil, *draw.pupil_tex, draw.pair, draw.lr, false,
m_use_bilinear);
}
if (draw.lid_tex) {
draw_eye<false>(m_temp_tex, draw.lid, *draw.lid_tex, draw.pair, draw.lr, draw.lr == 1,
m_use_bilinear);
}
if (m_alpha_hack) {
for (auto& a : m_temp_tex) {
a |= 0xff000000;
}
}
// update GPU:
auto& tex = m_cpu_eye_textures[draw.pair * 2 + draw.lr];
glBindTexture(GL_TEXTURE_2D, tex.gl_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
m_temp_tex);
// make sure they are still in vram
render_state->texture_pool->move_existing_to_vram(tex.gpu_tex, tex.tbp);
}
}
int add_draw_to_buffer(int idx, const EyeRenderer::EyeDraw& draw, float* data, int pair, int lr) {
int add_draw_to_buffer_32(int idx,
const EyeRenderer::EyeDraw& draw,
float* data,
int pair,
int lr) {
int x_off = lr * SINGLE_EYE_SIZE * 16;
int y_off = pair * SINGLE_EYE_SIZE * 16;
data[idx++] = draw.sprite.xyz0[0] - x_off;
data[idx++] = draw.sprite.xyz0[1] - y_off;
data[idx++] = 0;
@@ -537,6 +413,36 @@ int add_draw_to_buffer(int idx, const EyeRenderer::EyeDraw& draw, float* data, i
return idx;
}
int add_draw_to_buffer_64(int idx,
const EyeRenderer::EyeDraw& draw,
float* data,
int pair,
int lr) {
int x_off = lr * SINGLE_EYE_SIZE * 32;
int y_off = (pair / 4) * SINGLE_EYE_SIZE * 32;
data[idx++] = (draw.sprite.xyz0[0] - x_off) / 2;
data[idx++] = (draw.sprite.xyz0[1] - y_off) / 2;
data[idx++] = 0;
data[idx++] = 0;
data[idx++] = (draw.sprite.xyz1[0] - x_off) / 2;
data[idx++] = (draw.sprite.xyz0[1] - y_off) / 2;
data[idx++] = 1;
data[idx++] = 0;
data[idx++] = (draw.sprite.xyz0[0] - x_off) / 2;
data[idx++] = (draw.sprite.xyz1[1] - y_off) / 2;
data[idx++] = 0;
data[idx++] = 1;
data[idx++] = (draw.sprite.xyz1[0] - x_off) / 2;
data[idx++] = (draw.sprite.xyz1[1] - y_off) / 2;
data[idx++] = 1;
data[idx++] = 1;
return idx;
}
void EyeRenderer::run_gpu(const std::vector<SingleEyeDraws>& draws,
SharedRenderState* render_state) {
if (draws.empty()) {
@@ -549,10 +455,21 @@ void EyeRenderer::run_gpu(const std::vector<SingleEyeDraws>& draws,
// the first thing we'll do is prepare the vertices
int buffer_idx = 0;
for (const auto& draw : draws) {
buffer_idx = add_draw_to_buffer(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr);
buffer_idx =
add_draw_to_buffer(buffer_idx, draw.pupil, m_gpu_vertex_buffer, draw.pair, draw.lr);
buffer_idx = add_draw_to_buffer(buffer_idx, draw.lid, m_gpu_vertex_buffer, draw.pair, draw.lr);
if (draw.using_64) {
buffer_idx =
add_draw_to_buffer_64(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr);
buffer_idx =
add_draw_to_buffer_64(buffer_idx, draw.pupil, m_gpu_vertex_buffer, draw.pair, draw.lr);
buffer_idx =
add_draw_to_buffer_64(buffer_idx, draw.lid, m_gpu_vertex_buffer, draw.pair, draw.lr);
} else {
buffer_idx =
add_draw_to_buffer_32(buffer_idx, draw.iris, m_gpu_vertex_buffer, draw.pair, draw.lr);
buffer_idx =
add_draw_to_buffer_32(buffer_idx, draw.pupil, m_gpu_vertex_buffer, draw.pair, draw.lr);
buffer_idx =
add_draw_to_buffer_32(buffer_idx, draw.lid, m_gpu_vertex_buffer, draw.pair, draw.lr);
}
}
ASSERT(buffer_idx <= VTX_BUFFER_FLOATS);
int check = buffer_idx;
@@ -626,6 +543,21 @@ void EyeRenderer::run_gpu(const std::vector<SingleEyeDraws>& draws,
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
std::optional<u64> EyeRenderer::lookup_eye_texture(u8 eye_id) {
eye_id = (eye_id % 40);
if ((s32)eye_id >= NUM_EYE_PAIRS * 2) {
fmt::print("lookup eye failed for {} (1)\n", eye_id);
return {};
}
auto* gpu_tex = m_gpu_eye_textures[eye_id].gpu_tex;
if (gpu_tex) {
return gpu_tex->gpu_textures.at(0).gl;
} else {
fmt::print("lookup eye failed for {}\n", eye_id);
return {};
}
}
//////////////////////
// DMA Decode
//////////////////////
+4 -17
View File
@@ -7,7 +7,7 @@
#include "game/graphics/pipelines/opengl.h"
constexpr int EYE_BASE_BLOCK = 8160;
constexpr int NUM_EYE_PAIRS = 11;
constexpr int NUM_EYE_PAIRS = 20;
constexpr int SINGLE_EYE_SIZE = 32;
class EyeRenderer : public BucketRenderer {
@@ -19,6 +19,7 @@ class EyeRenderer : public BucketRenderer {
void init_textures(TexturePool& texture_pool, GameVersion) override;
void handle_eye_dma2(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
std::optional<u64> lookup_eye_texture(u8 eye_id);
struct SpriteInfo {
u8 a;
@@ -46,22 +47,8 @@ class EyeRenderer : public BucketRenderer {
std::string m_debug;
float m_average_time_ms = 0;
bool m_use_bilinear = true;
bool m_alpha_hack = true;
u32 m_temp_tex[SINGLE_EYE_SIZE * SINGLE_EYE_SIZE];
bool m_use_gpu = true;
struct CpuEyeTex {
u64 gl_tex;
GpuTexture* gpu_tex;
u32 tbp;
};
CpuEyeTex m_cpu_eye_textures[NUM_EYE_PAIRS * 2];
struct GpuEyeTex {
GpuTexture* gpu_tex;
GpuTexture* gpu_tex = nullptr;
u32 tbp;
FramebufferTexturePair fb;
@@ -78,6 +65,7 @@ class EyeRenderer : public BucketRenderer {
struct SingleEyeDraws {
int lr;
int pair;
bool using_64 = false;
int tex_slot() const { return pair * 2 + lr; }
u32 clear_color;
@@ -95,6 +83,5 @@ class EyeRenderer : public BucketRenderer {
};
std::vector<SingleEyeDraws> get_draws(DmaFollower& dma, SharedRenderState* render_state);
void run_cpu(const std::vector<SingleEyeDraws>& draws, SharedRenderState* render_state);
void run_gpu(const std::vector<SingleEyeDraws>& draws, SharedRenderState* render_state);
};
@@ -322,6 +322,10 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
BucketId::DEBUG_NO_ZBUF2, 0x8000);
init_bucket_renderer<DirectRenderer>("debug3", BucketCategory::OTHER, BucketId::DEBUG3, 0x8000);
auto eye_renderer = std::make_unique<EyeRenderer>("eyes", 0);
m_render_state.eye_renderer = eye_renderer.get();
m_jak2_eye_renderer = std::move(eye_renderer);
// for now, for any unset renderers, just set them to an EmptyBucketRenderer.
for (size_t i = 0; i < m_bucket_renderers.size(); i++) {
if (!m_bucket_renderers[i]) {
@@ -332,6 +336,10 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
m_bucket_renderers[i]->init_shaders(m_render_state.shaders);
m_bucket_renderers[i]->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
}
m_jak2_eye_renderer->init_shaders(m_render_state.shaders);
m_jak2_eye_renderer->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
m_render_state.loader->load_common(*m_render_state.texture_pool, "GAME");
}
/*!
@@ -697,6 +705,12 @@ void OpenGLRenderer::draw_renderer_selection_window() {
m_render_state.texture_pool->draw_debug_window();
ImGui::TreePop();
}
if (m_jak2_eye_renderer) {
if (ImGui::TreeNode("Eyes")) {
m_jak2_eye_renderer->draw_debug_window();
ImGui::TreePop();
}
}
ImGui::End();
}
@@ -161,5 +161,6 @@ class OpenGLRenderer {
Fbo* render_fbo = nullptr; // the selected fbo from the three above to use for rendering
} m_fbo_state;
std::unique_ptr<BucketRenderer> m_jak2_eye_renderer;
GameVersion m_version;
};
@@ -2,6 +2,7 @@
#include "common/global_profiler/GlobalProfiler.h"
#include "game/graphics/opengl_renderer/EyeRenderer.h"
#include "game/graphics/opengl_renderer/background/background_common.h"
#include "third-party/imgui/imgui.h"
@@ -455,14 +456,6 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// allocate lights
u32 lights = alloc_lights(current_lights);
for (int i = 0; i < 3; i++) {
float debug_length = current_lights.direction0[i] * current_lights.direction0[i] +
current_lights.direction1[i] * current_lights.direction1[i] +
current_lights.direction2[i] * current_lights.direction2[i];
if (debug_length > 0.01 && debug_length < 0.98) {
fmt::print("likely incorrect merc light direction {}\n", debug_length);
}
}
// loop over effects, creating draws for each
for (size_t ei = 0; ei < model->effects.size(); ei++) {
@@ -930,7 +923,7 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw,
draw->first_index = mdraw.first_index;
draw->index_count = mdraw.index_count;
draw->mode = mdraw.mode;
draw->texture = mdraw.tree_tex_id;
draw->texture = mdraw.eye_id == 0xff ? mdraw.tree_tex_id : (0xffffff00 | mdraw.eye_id);
draw->first_bone = first_bone;
draw->light_idx = lights;
draw->num_triangles = mdraw.num_triangles;
@@ -1041,13 +1034,14 @@ void Merc2::do_draws(const Draw* draw_array,
const Uniforms& uniforms,
ScopedProfilerNode& prof,
bool set_fade,
SharedRenderState*) {
SharedRenderState* render_state) {
glBindVertexArray(m_vao);
int last_tex = -1;
int last_light = -1;
bool normal_vtx_buffer_bound = true;
for (u32 di = 0; di < num_draws; di++) {
auto& draw = draw_array[di];
auto mode = draw.mode;
if (draw.flags & MOD_VTX) {
glBindVertexArray(draw.mod_vtx_buffer.vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lev->merc_indices);
@@ -1065,6 +1059,13 @@ void Merc2::do_draws(const Draw* draw_array,
if ((int)draw.texture != last_tex) {
if (draw.texture < lev->textures.size()) {
glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture));
} else if ((draw.texture & 0xffffff00) == 0xffffff00) {
auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff);
if (maybe_eye) {
glBindTexture(GL_TEXTURE_2D, *maybe_eye);
}
mode.set_filt_enable(false);
} else {
fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture);
}
@@ -1081,7 +1082,7 @@ void Merc2::do_draws(const Draw* draw_array,
set_uniform(uniforms.light_ambient, m_lights_buffer[draw.light_idx].ambient);
last_light = draw.light_idx;
}
setup_opengl_from_draw_mode(draw.mode, GL_TEXTURE0, true);
setup_opengl_from_draw_mode(mode, GL_TEXTURE0, true);
glUniform1i(uniforms.decal, draw.mode.get_decal());
-2
View File
@@ -729,7 +729,6 @@ Ptr<Type> alloc_and_init_type(Ptr<Symbol4<Ptr<Type>>> sym,
if (!force_global_type &&
u32_in_fixed_sym(FIX_SYM_LOADING_LEVEL) != u32_in_fixed_sym(FIX_SYM_GLOBAL_HEAP)) {
printf("using level types!\n"); // added
u32 type_list_ptr = LevelTypeList->value();
if (type_list_ptr == 0) {
// we don't have a type-list... just alloc on global
@@ -954,7 +953,6 @@ u64 new_type(u32 symbol, u32 parent, u64 flags) {
MsgWarn("dkernel: loading-level init of type %s, but was interned global (this is okay)\n",
sym_to_string(new_type_obj->symbol)->data());
} else {
printf("case 2 for new_type level types\n");
new_type_obj->memusage_method.offset = original_type_list_value;
}
}
@@ -1405,19 +1405,6 @@
)
((nonzero? (-> geom effect effect-idx envmap-usage))
;; if we need envmap, set it up.
;; hack:
;; the game switches from mercneric to merc after the envmap is faded out.
;; this would normally be fine in the PC port too, but we want the eyes to remain
;; using mercneric (dynamic texture updates) in PC.
;; so we force mercneric on models with envmap.
;; it seems to work so far...
(#when PC_PORT
(when (and (-> geom header eye-ctrl)
(nonzero? (-> geom header eye-ctrl)))
(set! pc-force-mercneric #t)
)
)
(let* ((v1-83 (-> geom effect effect-idx extra-info))
;; pointer to envmap tint data
(v1-84 (the-as structure (+ (the-as uint v1-83) (* (-> v1-83 envmap-tint-offset) 16))))
+1 -1
View File
@@ -2098,7 +2098,7 @@
;; unclear why eyes are here... maybe they rely on textures that were just remapped.
; (-> *display* frames (-> *display* on-screen) global-buf)
; (update-eyes)
(update-eyes)
;; end each normal bucket with the standard GS state reset
@@ -5,6 +5,9 @@
;; name in dgo: eye-h
;; dgos: ENGINE, GAME
(define-extern update-eyes (function none))
;; DECOMP BEGINS
(deftype eye (structure)
+4 -2
View File
@@ -37,6 +37,8 @@ vf30: cam 1 (premultiplied by hmge)
vf31: cam 0 (premultiplied by hmge)
|#
;; TODO: cloud drawing is disabled.
;; DECOMP BEGINS
;; set the xy component of vf24 (texture offset for large poly renderer) to values / 65,536
@@ -941,11 +943,11 @@ vf31: cam 0 (premultiplied by hmge)
(moon-dma (the-as sky-work s4-0) s3-0)
(draw-haze (the-as sky-work s4-0) s3-0)
(when (nonzero? *sky-texture-anim-array*) ;; added check
(draw-clouds (the-as sky-work s4-0) s3-0)
; (draw-clouds (the-as sky-work s4-0) s3-0) ;; DISABLED!
)
(draw-base (the-as sky-work s4-0) s3-0)
(when (nonzero? *sky-texture-anim-array*) ;; added check
(draw-fog (the-as sky-work s4-0) s3-0)
;(draw-fog (the-as sky-work s4-0) s3-0)
)
(let ((a3-5 (-> s3-0 base)))
(let ((v1-45 (the-as dma-packet (-> s3-0 base))))
+5 -5
View File
@@ -1225,9 +1225,9 @@ additionally, some texture pages have a chunk system that allows more specific c
)
;; not used at all. we don't set merc masks on PC, so this should happen most of the time.
(when (not any-uploads)
(return 0)
)
; (when (not any-uploads)
; (return 0)
; )
;; but non-merc users of this function (like map/hud) will get here
@@ -1670,7 +1670,7 @@ additionally, some texture pages have a chunk system that allows more specific c
)
(((tpage-category pris))
;; pris work like normal, but only 1 tpage.
(set! (-> lev upload-size 1) (upload-vram-pages-pris
(set! (-> lev upload-size 1) (upload-vram-pages-pris-pc
pool
(-> pool segment-common)
tpage
@@ -1700,7 +1700,7 @@ additionally, some texture pages have a chunk system that allows more specific c
)
(((tpage-category pris2))
;; pris2 is normal, 1 tpage.
(set! (-> lev upload-size 6) (upload-vram-pages-pris
(set! (-> lev upload-size 6) (upload-vram-pages-pris-pc
pool
(-> pool segment-common)
tpage