[jak 2] ETIE (#2326)

Definitely needs a clean up pass, but I think the functionality is very
close.

There's a few "hacks" still:
- I am using the emerc logic for environment mapping, which doesn't care
about the length of the normals. I can't figure out how the normal
scaling worked in etie. I want to do a little bit more experimentation
with this before merging.
- There is some part about adgifs for TIE and ETIE that I don't
understand. The clearly correct behavior of TIE/ETIE is that the alpha
settings from the adgif shader are overwritten by the settings from the
renderer. But I can't figure out how this happens in all cases.
- Fade out is completely disabled. I think this is fine because the
performance difference isn't bad. But if you are comparing screenshots
with PCSX2, it will make things look a tiny bit different.
This commit is contained in:
water111 2023-03-17 20:35:26 -04:00 committed by GitHub
parent 6670124296
commit 2fa4a23ea1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 9101 additions and 858 deletions

View file

@ -72,6 +72,104 @@ void TfragTree::serialize(Serializer& ser) {
ser.from_ptr(&use_strips);
}
math::Vector3f vopmula(math::Vector3f a, math::Vector3f b) {
return math::Vector3f(a.y() * b.z(), a.z() * b.x(), a.x() * b.y());
}
math::Vector3f vopmsub(math::Vector3f acc, math::Vector3f a, math::Vector3f b) {
return acc - vopmula(a, b);
}
/*!
* Compute the normal transformation for a TIE from the TIE matrix.
* Note that this isn't identical to the original game - we're missing the vf14 scaling factor
* For now, I just set this to 1, then normalize in the shader. Though I think we could avoid
* this by figuring out the value of vf14 here (I am just too lazy right now).
*/
std::array<math::Vector3f, 3> tie_normal_transform_v2(const std::array<math::Vector4f, 4>& m) {
// let:
// vf10, vf11, vf12, vf13 be the input matrix m
std::array<math::Vector3f, 3> result;
auto& vf10 = m[0];
auto& vf11 = m[1];
// auto& vf12 = m[2];
// vmulx.xyz vf16, vf10, vf14
math::Vector3f vf16 = vf10.xyz() * 1.0; // TODO VF14
// vopmula.xyz acc, vf11, vf16
math::Vector3f acc = vopmula(vf11.xyz(), vf16);
// vopmsub.xyz vf17, vf16, vf11
math::Vector3f vf17 = vopmsub(acc, vf16, vf11.xyz());
// vopmula.xyz acc, vf16, vf17
acc = vopmula(vf16, vf17);
// vopmsub.xyz vf17, vf17, vf16
vf17 = vopmsub(acc, vf17, vf16);
// vmul.xyz vf14, vf17, vf17
math::Vector3f vf14 = vf17.elementwise_multiply(vf17);
// vmulax.w acc, vf0, vf14
// vmadday.w acc, vf0, vf14
// vmaddz.w vf14, vf0, vf14
float sum = vf14.x() + vf14.y() + vf14.z();
// vrsqrt Q, vf0.w, vf14.w
float Q = 1.f / std::sqrt(sum);
// vmulax.xyzw acc, vf24, vf16
// vmadday.xyzw acc, vf25, vf16
// vmaddz.xyzw vf10, vf26, vf16
// vf10 = vf16; // assume cam is identity here.
result[0] = vf16;
// vwaitq
// vmulq.xyz vf17, vf17, Q
vf17 *= Q;
// vopmula.xyz acc, vf16, vf17
acc = vopmula(vf16, vf17);
// vopmsub.xyz vf18, vf17, vf16
math::Vector3f vf18 = vopmsub(acc, vf17, vf16);
// vmulax.xyzw acc, vf24, vf17
// vmadday.xyzw acc, vf25, vf17
// vmaddz.xyzw vf11, vf26, vf17
result[1] = vf17;
// vmulax.xyzw acc, vf24, vf18
// vmadday.xyzw acc, vf25, vf18
// vmaddz.xyzw vf12, vf26, vf18
result[2] = vf18;
return result;
//
// sqc2 vf10, -112(t8)
// sqc2 vf11, -96(t8)
// sqc2 vf12, -80(t8)
}
/*!
* Unpack tie normal by transforming and converting to s16 for OpenGL.
*/
math::Vector<s16, 3> unpack_tie_normal(const std::array<math::Vector3f, 3>& mat,
s8 nx,
s8 ny,
s8 nz) {
// rotate the normal
math::Vector3f nrm = math::Vector3f::zero();
nrm += mat[0] * nx;
nrm += mat[1] * ny;
nrm += mat[2] * nz;
// convert to s16 for OpenGL renderer
nrm *= 0.0078125; // number from EE asm
nrm *= 256.f * 128.f; // for normalize s16 -> float conversion by OpenGL.
return nrm.cast<s16>();
}
void TieTree::unpack() {
unpacked.vertices.resize(packed_vertices.color_indices.size());
size_t i = 0;
@ -84,13 +182,21 @@ void TieTree::unpack() {
vtx.x = proto_vtx.x;
vtx.y = proto_vtx.y;
vtx.z = proto_vtx.z;
vtx.q_unused = 1.f;
vtx.s = proto_vtx.s;
vtx.t = proto_vtx.t;
vtx.nx = proto_vtx.nx << 8;
vtx.ny = proto_vtx.ny << 8;
vtx.nz = proto_vtx.nz << 8;
vtx.r = proto_vtx.r;
vtx.g = proto_vtx.g;
vtx.b = proto_vtx.b;
vtx.a = proto_vtx.a;
i++;
}
} else {
const auto& mat = packed_vertices.matrices[grp.matrix_idx];
auto nmat = tie_normal_transform_v2(mat);
for (u32 src_idx = grp.start_vert; src_idx < grp.end_vert; src_idx++) {
auto& vtx = unpacked.vertices[i];
vtx.color_index = packed_vertices.color_indices[i];
@ -99,9 +205,16 @@ void TieTree::unpack() {
vtx.x = temp.x();
vtx.y = temp.y();
vtx.z = temp.z();
vtx.q_unused = 1.f;
vtx.s = proto_vtx.s;
vtx.t = proto_vtx.t;
auto nrm = unpack_tie_normal(nmat, proto_vtx.nx, proto_vtx.ny, proto_vtx.nz);
vtx.nx = nrm.x();
vtx.ny = nrm.y();
vtx.nz = nrm.z();
vtx.r = proto_vtx.r;
vtx.g = proto_vtx.g;
vtx.b = proto_vtx.b;
vtx.a = proto_vtx.a;
i++;
}
}
@ -159,7 +272,6 @@ void TfragTree::unpack() {
o.z = cz + in.zoff * rescale;
o.s = in.s / (1024.f);
o.t = in.t / (1024.f);
o.q_unused = 1.f;
o.color_index = in.color_index;
}
@ -188,6 +300,8 @@ void TieTree::serialize(Serializer& ser) {
draw.serialize(ser);
}
ser.from_ptr(&category_draw_indices);
if (ser.is_saving()) {
ser.save<size_t>(instanced_wind_draws.size());
} else {

View file

@ -73,17 +73,22 @@ struct MemoryUsageTracker {
void add(MemoryUsageCategory category, u32 size_bytes) { data[category] += size_bytes; }
};
constexpr int TFRAG3_VERSION = 27;
constexpr int TFRAG3_VERSION = 28;
// These vertices should be uploaded to the GPU at load time and don't change
struct PreloadedVertex {
// the vertex position
float x, y, z;
float x = 0, y = 0, z = 0;
// texture coordinates
float s, t, q_unused;
float s = 0, t = 0;
u8 r = 0, g = 0, b = 0, a = 0; // envmap tint color, not used in == or hash.
// color table index
u16 color_index;
u16 pad[3];
u16 color_index = 0;
// not used in == or hash!!
s16 nx = 0, ny = 0, nz = 0;
struct hash {
std::size_t operator()(const PreloadedVertex& x) const;
@ -100,6 +105,8 @@ struct PackedTieVertices {
struct Vertex {
float x, y, z;
float s, t;
s8 nx, ny, nz;
u8 r, g, b, a;
};
struct MatrixGroup {
@ -313,10 +320,66 @@ struct TieWindInstance {
void serialize(Serializer& ser);
};
// Tie draws are split into categories.
enum class TieCategory {
// normal tie buckets
NORMAL,
TRANS, // also called alpha
WATER,
// first draw (normal base draw) for envmapped stuff
NORMAL_ENVMAP,
TRANS_ENVMAP,
WATER_ENVMAP,
// second draw (shiny) for envmapped ties.
NORMAL_ENVMAP_SECOND_DRAW,
TRANS_ENVMAP_SECOND_DRAW,
WATER_ENVMAP_SECOND_DRAW,
};
constexpr int kNumTieCategories = 9;
constexpr bool is_envmap_first_draw_category(tfrag3::TieCategory category) {
switch (category) {
case tfrag3::TieCategory::NORMAL_ENVMAP:
case tfrag3::TieCategory::WATER_ENVMAP:
case tfrag3::TieCategory::TRANS_ENVMAP:
return true;
default:
return false;
}
}
constexpr bool is_envmap_second_draw_category(tfrag3::TieCategory category) {
switch (category) {
case tfrag3::TieCategory::NORMAL_ENVMAP_SECOND_DRAW:
case tfrag3::TieCategory::WATER_ENVMAP_SECOND_DRAW:
case tfrag3::TieCategory::TRANS_ENVMAP_SECOND_DRAW:
return true;
default:
return false;
}
}
constexpr TieCategory get_second_draw_category(tfrag3::TieCategory category) {
switch (category) {
case TieCategory::NORMAL_ENVMAP:
return TieCategory::NORMAL_ENVMAP_SECOND_DRAW;
case TieCategory::TRANS_ENVMAP:
return TieCategory::TRANS_ENVMAP_SECOND_DRAW;
case TieCategory::WATER_ENVMAP:
return TieCategory::WATER_ENVMAP_SECOND_DRAW;
default:
return TieCategory::NORMAL_ENVMAP;
}
}
// A tie model
struct TieTree {
BVH bvh;
std::vector<StripDraw> static_draws; // the actual topology and settings
std::vector<StripDraw> static_draws;
// Category n uses draws: static_draws[cdi[n]] to static_draws[cdi[n + 1]]
std::array<u32, kNumTieCategories + 1> category_draw_indices;
PackedTieVertices packed_vertices;
std::vector<TimeOfDayColor> colors; // vertex colors (pre-interpolation)

View file

@ -225,6 +225,8 @@ VuDisassembler::VuDisassembler(VuKind kind) : m_kind(kind) {
add_op(VuInstrK::SQRT, "sqrt").fsf_zero().ftf_0().vis_zero().dst_q().src_vft();
add_op(VuInstrK::SQD, "sqd").dst_mask().src_vfs().src_vit();
add_op(VuInstrK::ERLENG, "erleng").dst_mask().vft_zero().src_vfs().dst_p();
add_op(VuInstrK::ESUM, "esum").dst_mask().vft_zero().src_vfs().dst_p();
add_op(VuInstrK::ESADD, "esadd").dst_mask().vft_zero().src_vfs().dst_p();
add_op(VuInstrK::ELENG, "eleng").dst_mask().vft_zero().src_vfs().dst_p();
add_op(VuInstrK::MFP, "mfp").dst_mask().dst_vft().src_p();
}
@ -294,10 +296,14 @@ VuInstrK VuDisassembler::lower_kind(u32 in) {
return VuInstrK::XTOP;
case 0b11011'1111'00:
return VuInstrK::XGKICK;
case 0b11100'1111'00:
return VuInstrK::ESADD;
case 0b11100'1111'10:
return VuInstrK::ELENG;
case 0b11100'1111'11:
return VuInstrK::ERLENG;
case 0b11101'1111'10:
return VuInstrK::ESUM;
case 0b11110'1111'11:
return VuInstrK::WAITP;
}

View file

@ -126,13 +126,13 @@ enum class VuInstrK {
JALR,
MFP,
WAITP,
// ESADD,
ESADD,
// ERSADD,
ELENG,
ERLENG,
// EATANxy,
// EATANxz,
// ESUM,
ESUM,
// ERCPR,
// ESQRT,
// ERSQRT,

View file

@ -24991,13 +24991,14 @@
;; etie-vu1 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#|
(deftype etie-consts (structure)
((gifbufs qword :inline :offset-assert 0)
(adgif qword :inline :offset-assert 16)
(alpha qword :inline :offset-assert 32)
(strgif qword :inline :offset-assert 48)
(envgif qword :inline :offset-assert 64)
;(adgif qword :inline :offset-assert 16)
(adgif gs-gif-tag :inline :offset-assert 16)
;;(alpha qword :inline :offset-assert 32)
(alpah gs-adcmd :inline)
(strgif gs-gif-tag :inline :offset-assert 48)
(envgif gs-gif-tag :inline :offset-assert 64)
(envmap adgif-shader :inline :offset-assert 80)
(pers0 vector :inline :offset-assert 160)
(pers1 vector :inline :offset-assert 176)
@ -25006,27 +25007,24 @@
:size-assert #xc0
:flag-assert #x9000000c0
)
|#
#|
(deftype etie-matrix (structure)
((rmtx matrix :inline :offset-assert 0)
(nmtx matrix3 :inline :offset-assert 64)
(morph float :offset-assert 76)
(fog float :offset-assert 92)
(fade uint32 :offset-assert 108)
(morph float :offset 76)
(fog float :offset 92)
(fade uint32 :offset 108)
(tint qword :inline :offset-assert 112)
)
:method-count-assert 9
:size-assert #x80
:flag-assert #x900000080
)
|#
;; (define-extern etie-vu1-block object)
;; (define-extern etie-magic function)
;; (define-extern etie-init-consts function)
(define-extern etie-init-engine (function dma-buffer none))
(define-extern etie-vu1-block vu-function)
(define-extern etie-magic (function int int))
(define-extern etie-init-consts (function etie-consts gs-alpha none))
(define-extern etie-init-engine (function dma-buffer gs-alpha gs-test none))
(define-extern etie-end-buffer (function dma-buffer none))
;; (define-extern etie-float-reg-bp function)
;; (define-extern etie-float-reg function)
@ -25201,7 +25199,7 @@
(define-extern draw-drawable-tree-instance-tie (function drawable-tree-instance-tie level none))
(define-extern tie-init-scissor-buf (function bucket-id gs-alpha gs-test gs-test none))
(define-extern tie-init-buf (function bucket-id gs-alpha gs-test gs-test none))
(define-extern tie-init-envmap-buf (function bucket-id gs-alpha int int none))
(define-extern tie-init-envmap-buf (function bucket-id gs-alpha gs-test none))
(define-extern tie-init-envmap-scissor-buf (function bucket-id gs-alpha int int none))
(define-extern *tie-init-table* (inline-array tie-init-data))
(define-extern tie-vu1-init-buffers (function none))

View file

@ -11005,5 +11005,17 @@
"(method 98 gun-buoy)": [
[46, "v1", "collide-shape-moving"],
[76, "v1", "collide-shape-moving"]
],
"etie-init-engine": [
[[3, 10], "a0", "dma-packet"],
[[12, 19], "a0", "gs-gif-tag"],
[24, "a0", "(pointer gs-reg64)"],
[[34, 48], "a0", "dma-packet"],
[[58, 66], "a0", "dma-packet"],
[[68, 75], "a0", "dma-packet"],
[[79, 86], "a0", "(pointer uint32)"],
[[87, 94], "a0", "(pointer vif-tag)"]
]
}

View file

@ -16,7 +16,8 @@ void TextureDB::add_texture(u32 tpage,
u16 h,
const std::string& tex_name,
const std::string& tpage_name,
const std::vector<std::string>& level_names) {
const std::vector<std::string>& level_names,
u32 num_mips) {
auto existing_tpage_name = tpage_names.find(tpage);
if (existing_tpage_name == tpage_names.end()) {
tpage_names[tpage] = tpage_name;
@ -32,6 +33,7 @@ void TextureDB::add_texture(u32 tpage,
ASSERT(existing_tex->second.h == h);
ASSERT(existing_tex->second.rgba_bytes == data);
ASSERT(existing_tex->second.page == tpage);
ASSERT(existing_tex->second.num_mips == num_mips);
} else {
auto& new_tex = textures[combo_id];
new_tex.rgba_bytes = data;
@ -39,6 +41,7 @@ void TextureDB::add_texture(u32 tpage,
new_tex.w = w;
new_tex.h = h;
new_tex.page = tpage;
new_tex.num_mips = num_mips;
}
for (const auto& level_name : level_names) {
texture_ids_per_level[level_name].insert(combo_id);

View file

@ -15,6 +15,7 @@ struct TextureDB {
std::string name;
u32 page;
std::vector<u32> rgba_bytes;
u32 num_mips = -1;
};
std::unordered_map<u32, TextureData> textures;
@ -28,7 +29,8 @@ struct TextureDB {
u16 h,
const std::string& tex_name,
const std::string& tpage_name,
const std::vector<std::string>& level_names);
const std::vector<std::string>& level_names,
u32 num_mips);
void replace_textures(const fs::path& path);
};

View file

@ -537,7 +537,7 @@ TPageResultStats process_tpage(ObjectFileData& data,
file_util::write_rgba_png(texture_dump_dir / fmt::format("{}.png", tex.name), out.data(),
tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name, level_names);
texture_page.name, level_names, tex.num_mips);
stats.successful_textures++;
} else if (tex.psm == int(PSM::PSMT8) && tex.clutpsm == int(CPSM::PSMCT16)) {
// will store output pixels, rgba (8888)
@ -580,7 +580,7 @@ TPageResultStats process_tpage(ObjectFileData& data,
file_util::write_rgba_png(texture_dump_dir / fmt::format("{}.png", tex.name), out.data(),
tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name, level_names);
texture_page.name, level_names, tex.num_mips);
stats.successful_textures++;
} else if (tex.psm == int(PSM::PSMCT16) && tex.clutpsm == 0) {
// not a clut.
@ -605,7 +605,7 @@ TPageResultStats process_tpage(ObjectFileData& data,
file_util::write_rgba_png(texture_dump_dir / fmt::format("{}.png", tex.name), out.data(),
tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name, level_names);
texture_page.name, level_names, tex.num_mips);
stats.successful_textures++;
} else if (tex.psm == int(PSM::PSMT4) && tex.clutpsm == int(CPSM::PSMCT16)) {
// will store output pixels, rgba (8888)
@ -646,7 +646,7 @@ TPageResultStats process_tpage(ObjectFileData& data,
file_util::write_rgba_png(texture_dump_dir / fmt::format("{}.png", tex.name), out.data(),
tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name, level_names);
texture_page.name, level_names, tex.num_mips);
stats.successful_textures++;
} else if (tex.psm == int(PSM::PSMT4) && tex.clutpsm == int(CPSM::PSMCT32)) {
// will store output pixels, rgba (8888)
@ -687,7 +687,7 @@ TPageResultStats process_tpage(ObjectFileData& data,
file_util::write_rgba_png(texture_dump_dir / fmt::format("{}.png", tex.name), out.data(),
tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name, level_names);
texture_page.name, level_names, tex.num_mips);
stats.successful_textures++;
} else if (tex.psm == int(PSM::PSMCT32) && tex.clutpsm == 0) {
// not a clut.
@ -712,7 +712,7 @@ TPageResultStats process_tpage(ObjectFileData& data,
file_util::write_rgba_png(texture_dump_dir / fmt::format("{}.png", tex.name), out.data(),
tex.w, tex.h);
texture_db.add_texture(texture_page.id, tex_id, out, tex.w, tex.h, tex.name,
texture_page.name, level_names);
texture_page.name, level_names, tex.num_mips);
stats.successful_textures++;
}

View file

@ -445,6 +445,15 @@ std::string TFragment::print(const PrintSettings& settings, int indent) const {
return result;
}
void memcpy_plain_data(u8* dst, const Ref& ref, size_t size_bytes) {
const auto& words = ref.data->words_by_seg.at(ref.seg);
for (size_t i = 0; i < size_bytes; i++) {
size_t byte_offset = ref.byte_offset + i;
u8 byte = words.at(byte_offset / 4).get_byte(byte_offset % 4);
memcpy(dst + i, &byte, sizeof(u8));
}
}
void TieFragment::read_from_file(TypedRef ref,
const decompiler::DecompilerTypeSystem& dts,
DrawStats* stats,
@ -492,6 +501,15 @@ void TieFragment::read_from_file(TypedRef ref,
}
stats->total_tie_prototype_tris += num_tris;
if (version > GameVersion::Jak1) {
u16 normals_qwc = read_plain_data_field<u16>(ref, "normal-count", dts);
if (normals_qwc) {
normals.resize(16 * normals_qwc);
auto normals_data_ref = deref_label(get_field_ref(ref, "normal-ref", dts));
memcpy_plain_data((u8*)normals.data(), normals_data_ref, normals_qwc * 16);
}
}
}
std::string TieFragment::print(const PrintSettings& /*settings*/, int indent) const {
@ -1037,6 +1055,24 @@ void PrototypeBucketTie::read_from_file(TypedRef ref,
for (int i = 0; i < int(8 * time_of_day.height); i++) {
time_of_day.colors.push_back(deref_u32(palette, 3 + i));
}
if (version > GameVersion::Jak1) {
auto fr = get_field_ref(ref, "envmap-shader", dts);
const auto& word = fr.data->words_by_seg.at(fr.seg).at(fr.byte_offset / 4);
if (word.kind() == decompiler::LinkedWord::PTR) {
has_envmap_shader = true;
Ref envmap_shader_ref(deref_label(fr));
for (int i = 0; i < 5 * 16; i++) {
int byte = envmap_shader_ref.byte_offset + i;
u8 val =
ref.ref.data->words_by_seg.at(envmap_shader_ref.seg).at(byte / 4).get_byte(byte % 4);
envmap_shader[i] = val;
}
}
u32 tint = read_plain_data_field<u32>(ref, "tint-color", dts);
memcpy(tint_color.data(), &tint, 4);
}
}
std::string PrototypeBucketTie::print(const PrintSettings& settings, int indent) const {

View file

@ -6,6 +6,7 @@
#include <vector>
#include "common/common_types.h"
#include "common/math/Vector.h"
#include "common/versions.h"
#include "decompiler/level_extractor/common_formats.h"
@ -406,6 +407,8 @@ struct TieFragment : public Drawable {
std::string debug_label_name;
std::vector<s8> normals;
// todo, lots more
};
@ -479,7 +482,9 @@ struct PrototypeBucketTie {
TimeOfDayPalette time_of_day;
// todo envmap shader
bool has_envmap_shader = false;
u8 envmap_shader[5 * 16]; // jak 2 only
math::Vector<u8, 4> tint_color;
// todo collide-frag
DrawableInlineArrayCollideFragment collide_frag;
// todo tie-colors

View file

@ -2070,9 +2070,8 @@ void make_tfrag3_data(std::map<u32, std::vector<GroupedDraw>>& draws,
vtx.z = vert.pre_cam_trans_pos.z();
vtx.s = vert.stq.x();
vtx.t = vert.stq.y();
vtx.q_unused = vert.stq.z();
// if this is true, we can remove a divide in the shader
ASSERT(vtx.q_unused == 1.f);
// because this is true, we can remove a divide in the shader
ASSERT(vert.stq.z() == 1.f);
vtx.color_index = vert.rgba / 4;
// ASSERT((vert.rgba >> 2) < 1024); spider cave has 2048?
ASSERT((vert.rgba & 3) == 0);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -48,7 +48,15 @@ struct SharedRenderState {
LevelVis occlusion_vis[6];
math::Vector4f camera_planes[4];
// including transformation, rotation, perspective
math::Vector4f camera_matrix[4];
// including transformation, rotation
math::Vector4f camera_no_persp[4];
// just the perspective
math::Vector4f camera_persp[4];
math::Vector4f camera_hvdf_off;
math::Vector4f camera_fog;
math::Vector4f camera_pos;
@ -78,6 +86,7 @@ struct SharedRenderState {
int bucket_for_vis_copy = 0;
int num_vis_to_copy = 0;
GameVersion version;
u64 frame_idx = 0;
};
/*!

View file

@ -99,7 +99,7 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
init_bucket_renderer<OceanMidAndFar>("ocean-mid-far", BucketCategory::OCEAN,
BucketId::OCEAN_MID_FAR);
for (int i = 0; i < 6; ++i) {
#define GET_BUCKET_ID_FOR_LIST(bkt1, bkt2, idx) ((int)bkt1 + ((int)bkt2 - (int)bkt1) * idx)
#define GET_BUCKET_ID_FOR_LIST(bkt1, bkt2, idx) ((int)(bkt1) + ((int)(bkt2) - (int)(bkt1)) * (idx))
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-tfrag", i), BucketCategory::TEX,
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_TFRAG, BucketId::TEX_L1_TFRAG, i));
@ -107,9 +107,13 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
fmt::format("tfrag-l{}-tfrag", i), BucketCategory::TFRAG,
GET_BUCKET_ID_FOR_LIST(BucketId::TFRAG_L0_TFRAG, BucketId::TFRAG_L1_TFRAG, i),
std::vector{tfrag3::TFragmentTreeKind::NORMAL}, false, i);
init_bucket_renderer<Tie3>(
Tie3* tie = init_bucket_renderer<Tie3>(
fmt::format("tie-l{}-tfrag", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::TIE_L0_TFRAG, BucketId::TIE_L1_TFRAG, i), i);
init_bucket_renderer<Tie3AnotherCategory>(
fmt::format("etie-l{}-tfrag", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::ETIE_L0_TFRAG, BucketId::ETIE_L1_TFRAG, i), tie,
tfrag3::TieCategory::NORMAL_ENVMAP);
init_bucket_renderer<Merc2>(
fmt::format("merc-l{}-tfrag", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_TFRAG, BucketId::MERC_L1_TFRAG, i));
@ -131,6 +135,14 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
fmt::format("tfrag-t-l{}-alpha", i), BucketCategory::TFRAG,
GET_BUCKET_ID_FOR_LIST(BucketId::TFRAG_T_L0_ALPHA, BucketId::TFRAG_T_L1_ALPHA, i),
std::vector{tfrag3::TFragmentTreeKind::TRANS}, false, i);
init_bucket_renderer<Tie3AnotherCategory>(
fmt::format("tie-t-l{}-alpha", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::TIE_T_L0_ALPHA, BucketId::TIE_T_L1_ALPHA, i), tie,
tfrag3::TieCategory::TRANS);
init_bucket_renderer<Tie3AnotherCategory>(
fmt::format("etie-t-l{}-alpha", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::ETIE_T_L0_ALPHA, BucketId::ETIE_T_L1_ALPHA, i), tie,
tfrag3::TieCategory::TRANS_ENVMAP);
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-pris", i), BucketCategory::TEX,
@ -156,6 +168,14 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
fmt::format("tfrag-w-l{}-alpha", i), BucketCategory::TFRAG,
GET_BUCKET_ID_FOR_LIST(BucketId::TFRAG_W_L0_WATER, BucketId::TFRAG_W_L1_WATER, i),
std::vector{tfrag3::TFragmentTreeKind::WATER}, false, i);
init_bucket_renderer<Tie3AnotherCategory>(
fmt::format("tie-w-l{}-water", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::TIE_W_L0_WATER, BucketId::TIE_W_L1_WATER, i), tie,
tfrag3::TieCategory::WATER);
init_bucket_renderer<Tie3AnotherCategory>(
fmt::format("etie-w-l{}-water", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::ETIE_W_L0_WATER, BucketId::ETIE_W_L1_WATER, i), tie,
tfrag3::TieCategory::WATER_ENVMAP);
#undef GET_BUCKET_ID_FOR_LIST
}
// 180
@ -911,6 +931,7 @@ void OpenGLRenderer::dispatch_buckets(DmaFollower dma,
ScopedProfilerNode& prof,
bool sync_after_buckets) {
m_render_state.version = m_version;
m_render_state.frame_idx++;
switch (m_version) {
case GameVersion::Jak1:
dispatch_buckets_jak1(dma, prof, sync_after_buckets);

View file

@ -104,6 +104,8 @@ ShaderLibrary::ShaderLibrary(GameVersion version) {
at(ShaderId::GLOW_PROBE_READ_DEBUG) = {"glow_probe_read_debug", version};
at(ShaderId::GLOW_PROBE_DOWNSAMPLE) = {"glow_probe_downsample", version};
at(ShaderId::GLOW_DRAW) = {"glow_draw", version};
at(ShaderId::ETIE_BASE) = {"etie_base", version};
at(ShaderId::ETIE) = {"etie", version};
for (auto& shader : m_shaders) {
ASSERT_MSG(shader.okay(), "error compiling shader");

View file

@ -54,6 +54,8 @@ enum class ShaderId {
GLOW_PROBE_READ_DEBUG = 27,
GLOW_PROBE_DOWNSAMPLE = 28,
GLOW_DRAW = 29,
ETIE_BASE = 30,
ETIE = 31,
MAX_SHADERS
};

File diff suppressed because it is too large Load diff

View file

@ -19,27 +19,58 @@ struct TieProtoVisibility {
bool all_visible = true;
};
struct EtieUniforms {
GLuint persp0, persp1, cam_no_persp, envmap_tod_tint, decal;
};
class Tie3 : public BucketRenderer {
public:
Tie3(const std::string& name, int my_id, int level_id);
// by default, only render the specified category on the call to render.
// to render the other categories, use the Tie3AnotherCategory renderer below.
Tie3(const std::string& name,
int my_id,
int level_id,
tfrag3::TieCategory category = tfrag3::TieCategory::NORMAL);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
~Tie3();
void render_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
const u8* proto_vis_data,
size_t proto_vis_data_size);
void render_tree(int idx,
int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
const u8* proto_vis_data,
size_t proto_vis_data_size);
bool setup_for_level(const std::string& str, SharedRenderState* render_state);
bool set_up_common_data_from_dma(DmaFollower& dma, SharedRenderState* render_state);
void setup_all_trees(int geom,
const TfragRenderSettings& settings,
const u8* proto_vis_data,
size_t proto_vis_data_size,
bool use_multidraw,
ScopedProfilerNode& prof);
void setup_tree(int idx,
int geom,
const TfragRenderSettings& settings,
const u8* proto_vis_data,
size_t proto_vis_data_size,
bool use_multidraw,
ScopedProfilerNode& prof);
void draw_matching_draws_for_all_trees(int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
tfrag3::TieCategory category);
void draw_matching_draws_for_tree(int idx,
int geom,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
tfrag3::TieCategory category);
bool try_loading_level(const std::string& str, SharedRenderState* render_state);
void render_from_another(SharedRenderState* render_state,
ScopedProfilerNode& prof,
tfrag3::TieCategory category);
struct WindWork {
u32 paused;
@ -55,7 +86,7 @@ class Tie3 : public BucketRenderer {
int lod() const { return Gfx::g_global_settings.lod_tie; }
private:
void update_load(const LevelData* loader_data);
void load_from_fr3_data(const LevelData* loader_data);
void discard_tree_cache();
void render_tree_wind(int idx,
int geom,
@ -63,13 +94,22 @@ class Tie3 : public BucketRenderer {
SharedRenderState* render_state,
ScopedProfilerNode& prof);
struct CommonData {
// data that the AnotherCategory renderers can use.
TfragRenderSettings settings;
const u8* proto_vis_data = nullptr;
u32 proto_vis_data_size = 0;
math::Vector4f envmap_color;
u64 frame_idx = -1;
} m_common_data;
struct Tree {
GLuint vertex_buffer;
GLuint index_buffer;
GLuint single_draw_index_buffer;
GLuint time_of_day_texture;
GLuint vao;
u32 vert_count;
std::array<u32, tfrag3::kNumTieCategories + 1> category_draw_indices;
const std::vector<tfrag3::StripDraw>* draws = nullptr;
const std::vector<tfrag3::InstancedStripDraw>* wind_draws = nullptr;
const std::vector<tfrag3::TieWindInstance>* instance_info = nullptr;
@ -77,56 +117,39 @@ class Tie3 : public BucketRenderer {
const tfrag3::BVH* vis = nullptr;
const u32* index_data = nullptr;
SwizzledTimeOfDay tod_cache;
std::vector<std::array<math::Vector4f, 4>> wind_matrix_cache;
bool has_wind = false;
GLuint wind_vertex_index_buffer;
std::vector<u32> wind_vertex_index_offsets;
bool has_proto_visibility = false;
TieProtoVisibility proto_visibility;
struct {
u32 draws = 0;
u32 wind_draws = 0;
Filtered<float> cull_time;
Filtered<float> index_time;
Filtered<float> tod_time;
Filtered<float> proto_vis_time;
Filtered<float> setup_time;
Filtered<float> draw_time;
Filtered<float> tree_time;
} perf;
};
std::array<std::vector<Tree>, 4> m_trees; // includes 4 lods!
std::string m_level_name;
const std::vector<GLuint>* m_textures;
u64 m_load_id = -1;
struct Cache {
std::vector<std::pair<int, int>> draw_idx_temp;
std::vector<u32> index_temp;
std::vector<u8> vis_temp;
std::vector<std::pair<int, int>> multidraw_offset_per_stripdraw;
std::vector<GLsizei> multidraw_count_buffer;
std::vector<void*> multidraw_index_offset_buffer;
} m_cache;
};
void envmap_second_pass_draw(const Tree& tree,
const TfragRenderSettings& settings,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
tfrag3::TieCategory category);
std::array<std::vector<Tree>, 4> m_trees; // includes 4 lods!
std::string m_level_name;
const std::vector<GLuint>* m_textures;
u64 m_load_id = -1;
std::vector<math::Vector<u8, 4>> m_color_result;
static constexpr int TIME_OF_DAY_COLOR_COUNT = 8192;
bool m_has_level = false;
char m_user_level[255] = "vi1";
std::optional<std::string> m_pending_user_level = std::nullopt;
bool m_override_level = false;
bool m_use_fast_time_of_day = true;
bool m_debug_wireframe = false;
bool m_debug_all_visible = false;
bool m_hide_wind = false;
Filtered<float> m_all_tree_time;
TfragPcPortData m_pc_port_data;
@ -136,5 +159,27 @@ class Tie3 : public BucketRenderer {
int m_level_id;
tfrag3::TieCategory m_default_category;
struct {
GLuint decal;
} m_uniforms;
EtieUniforms m_etie_uniforms, m_etie_base_uniforms;
static_assert(sizeof(WindWork) == 84 * 16);
};
class Tie3AnotherCategory : public BucketRenderer {
public:
Tie3AnotherCategory(const std::string& name,
int my_id,
Tie3* parent,
tfrag3::TieCategory category);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
private:
Tie3* m_parent;
tfrag3::TieCategory m_category;
};

View file

@ -36,9 +36,14 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap)
DoubleDraw double_draw;
bool should_enable_blend = false;
if (mode.get_ab_enable() && mode.get_alpha_blend() != DrawMode::AlphaBlend::DISABLED) {
glEnable(GL_BLEND);
should_enable_blend = true;
switch (mode.get_alpha_blend()) {
case DrawMode::AlphaBlend::SRC_SRC_SRC_SRC:
should_enable_blend = false;
// (SRC - SRC) * alpha + SRC = SRC, no blend.
break;
case DrawMode::AlphaBlend::SRC_DST_SRC_DST:
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
@ -71,6 +76,12 @@ DoubleDraw setup_opengl_from_draw_mode(DrawMode mode, u32 tex_unit, bool mipmap)
default:
ASSERT(false);
}
} else {
should_enable_blend = false;
}
if (should_enable_blend) {
glEnable(GL_BLEND);
} else {
glDisable(GL_BLEND);
}
@ -159,6 +170,8 @@ void first_tfrag_draw_setup(const TfragRenderSettings& settings,
sh.activate();
auto id = sh.id();
glUniform1i(glGetUniformLocation(id, "gfx_hack_no_tex"), Gfx::g_global_settings.hack_no_tex);
glUniform1i(glGetUniformLocation(id, "decal"), false);
glUniform1i(glGetUniformLocation(id, "tex_T0"), 0);
glUniformMatrix4fv(glGetUniformLocation(id, "camera"), 1, GL_FALSE, settings.math_camera.data());
glUniform4f(glGetUniformLocation(id, "hvdf_offset"), settings.hvdf_offset[0],
@ -752,6 +765,8 @@ void update_render_state_from_pc_settings(SharedRenderState* state, const TfragP
for (int i = 0; i < 4; i++) {
state->camera_planes[i] = data.planes[i];
state->camera_matrix[i] = data.camera[i];
state->camera_no_persp[i] = data.camera_rot[i];
state->camera_persp[i] = data.camera_perspective[i];
}
state->camera_pos = data.cam_trans;
state->camera_hvdf_off = data.hvdf_off;

View file

@ -13,8 +13,13 @@ struct TfragPcPortData {
math::Vector4f hvdf_off;
math::Vector4f fog;
math::Vector4f cam_trans;
math::Vector4f camera_rot[4];
math::Vector4f camera_perspective[4];
char level_name[16];
};
static_assert(sizeof(TfragPcPortData) == 16 * 24);
// inputs to background renderers.
struct TfragRenderSettings {

View file

@ -104,9 +104,7 @@ void main() {
// 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).
// this is because we flip vtx_pos above with a negative sign.
st_mod.x = 1 - vf10.x;
st_mod.y = 1 - vf10.y;

View file

@ -0,0 +1,31 @@
#version 430 core
out vec4 color;
in vec4 fragment_color;
in vec3 tex_coord;
in float fogginess;
uniform sampler2D tex_T0;
uniform float alpha_min;
uniform float alpha_max;
uniform vec4 fog_color;
uniform int gfx_hack_no_tex;
void main() {
if (gfx_hack_no_tex == 0) {
//vec4 T0 = texture(tex_T0, tex_coord);
vec4 T0 = texture(tex_T0, tex_coord.xy);
color = fragment_color * T0;
} else {
color = fragment_color/2;
}
if (color.a < alpha_min || color.a > alpha_max) {
discard;
}
color.rgb = mix(color.rgb, fog_color.rgb, clamp(fogginess * fog_color.a, 0, 1));
}

View file

@ -0,0 +1,165 @@
#version 430 core
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec3 tex_coord_in;
layout (location = 2) in int time_of_day_index;
layout (location = 3) in vec3 normal;
layout (location = 4) in vec4 proto_tint;
uniform vec4 hvdf_offset;
uniform mat4 camera;
uniform float fog_constant;
uniform float fog_min;
uniform float fog_max;
uniform vec4 envmap_tod_tint;
layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose.
uniform int decal;
out vec4 fragment_color;
out vec3 tex_coord;
out float fogginess;
// etie stuff
uniform vec4 persp0;
uniform vec4 persp1;
uniform mat4 cam_no_persp;
void main() {
// maybe we could do fog faster using intermediate results from below, but it doesn't seem significant.
float fog1 = camera[3].w + camera[0].w * position_in.x + camera[1].w * position_in.y + camera[2].w * position_in.z;
fogginess = 255 - clamp(fog1 + hvdf_offset.w, fog_min, fog_max);
// rotate the normal
vec3 nrm_vf23 = cam_no_persp[0].xyz * normal.x
+ cam_no_persp[1].xyz * normal.y
+ cam_no_persp[2].xyz * normal.z;
vec3 r_nrm = nrm_vf23;
// transform the point
vec4 vf17 = cam_no_persp[3];
vf17 += cam_no_persp[0] * position_in.x;
vf17 += cam_no_persp[1] * position_in.y;
vf17 += cam_no_persp[2] * position_in.z;
/*
// This is the ETIE math.
// It seems right only if the nrm_vf23 is normalized first
// (and in this case it's identical the emerc math below)
// There is no reason to prefer the emerc version over etie - they are identical.
// likely on PS2, their normal transformation matrix was scaled correctly.
// ours isn't. (yes, the camera matrix is a pure rotation and that doesn't matter, but the
// instance matrix used to de-instance the mesh needs the correction, and we don't have it)
if (use_emerc_math == 0) {
nrm_vf23 = nrm_vf23;
// nrm.z -= 1
//subw.z vf23, vf23, vf00
nrm_vf23.z -= 1.f;
// dot = nrm.xyz * pt.xyz
//mul.xyz vf13, vf17, vf23
//esum.xyzw P, vf13
//mfp.x vf13, P
float nrm_dot = dot(vf17.xyz, nrm_vf23);
// rfl = pt.xzy * nrm.z
//mulz.xyz vf14, vf17, vf23
vec3 rfl_vf14 = vf17.xyz * nrm_vf23.z;
//;; Q_envmap = vf02.w / norm(rfl.xyz)
//esadd.xyz P, vf14
//mfp.x vf30, P
//rsqrt Q, vf02.w, vf30.x
float Q_envmap = -0.5 / length(rfl_vf14);
//
//;; nrm.xy *= dot.x
//mulx.xy vf23, vf23, vf13
nrm_vf23.xy *= nrm_dot;
//
//;; nrm.xy += rfl.xy
//add.xy vf23, vf23, vf14
nrm_vf23.xy += rfl_vf14.xy;
//
//;; nrm.z = 1.0
//addw.z vf23, vf00, vf00
nrm_vf23.z = 1.0;
//
//;; nrm.xy *= Q_envmap
//mul.xy vf23, vf23, Q
nrm_vf23.xy *= Q_envmap;
//
//
//;; nrm.xy += vf03.w
//addw.xy vf23, vf23, vf03
nrm_vf23.xy += 0.5;
tex_coord = nrm_vf23;
}*/
//;; perspective transform
//mula.xy ACC, vf10, vf17 ;; acc build 1
//mulaw.zw ACC, vf10, vf00 ;; acc build 2
vec4 p_proj = vec4(persp1.x * vf17.x, persp1.y * vf17.y, persp1.z, persp1.w);
//maddz.xyzw vf18, vf09, vf17 ;; acc star
p_proj += persp0 * vf17.z;
//;; perspective divide
//div Q, vf00.w, vf18.w
float pQ = 1.f / p_proj.w;
// EMERC version of the math
{
// emerc hack
vec3 vf10 = normalize(r_nrm);
//subw.z vf10, vf10, vf00 ;; subtract 1 from z
vf10.z -= 1;
//addw.z vf09, vf00, vf09 ;; xyww the unperspected thing
vec4 vf09 = vf17;
//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
vec3 ACC = vf09.xyz;
//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 = 0.5 / P;
//addaz.xyzw vf00, vf23
ACC = vec3(0.5, 0.5, 0.5);
//madd.xyzw vf10, vf10, Q
vf10 = ACC + vf10 * qqq;
tex_coord = vf10.xyz;
}
vec4 transformed = p_proj * pQ;
transformed.w = p_proj.w;
// correct xy offset
transformed.xy -= (2048.);
// correct z scale
transformed.z /= (8388608);
transformed.z -= 1;
// correct xy scale
transformed.x /= (256);
transformed.y /= -(128);
// hack
transformed.xyz *= transformed.w;
// scissoring area adjust
transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE;
gl_Position = transformed;
fragment_color = proto_tint * envmap_tod_tint;
}

View file

@ -0,0 +1,32 @@
#version 430 core
out vec4 color;
in vec4 fragment_color;
in vec3 tex_coord;
in float fogginess;
uniform sampler2D tex_T0;
uniform float alpha_min;
uniform float alpha_max;
uniform vec4 fog_color;
uniform int gfx_hack_no_tex;
void main() {
if (gfx_hack_no_tex == 0) {
//vec4 T0 = texture(tex_T0, tex_coord);
vec4 T0 = texture(tex_T0, tex_coord.xy);
color = fragment_color * T0;
} else {
color = fragment_color/2;
}
if (color.a < alpha_min || color.a > alpha_max) {
discard;
}
color.rgb = mix(color.rgb, fog_color.rgb, clamp(fogginess * fog_color.a, 0, 1));
}

View file

@ -0,0 +1,70 @@
#version 430 core
layout (location = 0) in vec3 position_in;
layout (location = 1) in vec3 tex_coord_in;
layout (location = 2) in int time_of_day_index;
uniform vec4 hvdf_offset;
uniform mat4 camera;
uniform float fog_constant;
uniform float fog_min;
uniform float fog_max;
layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose.
uniform int decal;
out vec4 fragment_color;
out vec3 tex_coord;
out float fogginess;
// etie stuff
uniform vec4 persp0;
uniform vec4 persp1;
uniform mat4 cam_no_persp;
void main() {
float fog1 = camera[3].w + camera[0].w * position_in.x + camera[1].w * position_in.y + camera[2].w * position_in.z;
fogginess = 255 - clamp(fog1 + hvdf_offset.w, fog_min, fog_max);
vec4 vf17 = cam_no_persp[3];
vf17 += cam_no_persp[0] * position_in.x;
vf17 += cam_no_persp[1] * position_in.y;
vf17 += cam_no_persp[2] * position_in.z;
vec4 p_proj = vec4(persp1.x * vf17.x, persp1.y * vf17.y, persp1.z, persp1.w);
p_proj += persp0 * vf17.z;
float pQ = 1.f / p_proj.w;
vec4 transformed = p_proj * pQ;
transformed.w = p_proj.w;
// correct xy offset
transformed.xy -= (2048.);
// correct z scale
transformed.z /= (8388608);
transformed.z -= 1;
// correct xy scale
transformed.x /= (256);
transformed.y /= -(128);
// hack
transformed.xyz *= transformed.w;
// scissoring area adjust
transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE;
gl_Position = transformed;
if (decal == 1) {
fragment_color = vec4(1.0, 1.0, 1.0, 1.0);
} else {
// time of day lookup
fragment_color = texelFetch(tex_T1, time_of_day_index, 0);
// color adjustment
fragment_color *= 2;
fragment_color.a *= 2;
}
// fog hack
if (fragment_color.r < 0.005 && fragment_color.g < 0.005 && fragment_color.b < 0.005) {
fogginess = 0;
}
tex_coord = tex_coord_in;
}

View file

@ -13,6 +13,7 @@ uniform vec4 fog_color;
uniform int gfx_hack_no_tex;
void main() {
if (gfx_hack_no_tex == 0) {
//vec4 T0 = texture(tex_T0, tex_coord);

View file

@ -10,6 +10,7 @@ uniform float fog_constant;
uniform float fog_min;
uniform float fog_max;
layout (binding = 10) uniform sampler1D tex_T1; // note, sampled in the vertex shader on purpose.
uniform int decal;
out vec4 fragment_color;
out vec3 tex_coord;
@ -62,12 +63,15 @@ void main() {
transformed.y *= SCISSOR_ADJUST * HEIGHT_SCALE;
gl_Position = transformed;
// time of day lookup
fragment_color = texelFetch(tex_T1, time_of_day_index, 0);
// color adjustment
fragment_color *= 2;
fragment_color.a *= 2;
if (decal == 1) {
fragment_color = vec4(1.0, 1.0, 1.0, 1.0);
} else {
// time of day lookup
fragment_color = texelFetch(tex_T1, time_of_day_index, 0);
// color adjustment
fragment_color *= 2;
fragment_color.a *= 2;
}
// fog hack
if (fragment_color.r < 0.005 && fragment_color.g < 0.005 && fragment_color.b < 0.005) {

View file

@ -91,8 +91,8 @@ int main(int argc, char** argv) {
//
// Only handling args the launcher provides, all others can be changed
// in this repo at the time of merge.
std::vector<char*> adjusted_argv_vals;
std::vector<char*> adjusted_argv_vals_passthru;
std::vector<const char*> adjusted_argv_vals;
std::vector<const char*> adjusted_argv_vals_passthru;
for (int i = 0; i < argc; i++) {
const auto& val = std::string(argv[i]);
// Handle all args that aren't passed through
@ -117,7 +117,7 @@ int main(int argc, char** argv) {
}
}
std::vector<char*> new_argv;
std::vector<const char*> new_argv;
if (!adjusted_argv_vals.empty() || !adjusted_argv_vals_passthru.empty()) {
new_argv.push_back(argv[0]);
for (const auto& arg : adjusted_argv_vals) {
@ -129,7 +129,7 @@ int main(int argc, char** argv) {
new_argv.push_back(arg);
}
}
argv = new_argv.data();
argv = (char**)new_argv.data();
argc = new_argv.size();
}
// --- END temporary shim
@ -209,7 +209,7 @@ int main(int argc, char** argv) {
bool force_debug_next_time = false;
// always start with an empty arg, as internally kmachine starts at `1` not `0`
std::vector<char*> arg_ptrs = {""};
std::vector<const char*> arg_ptrs = {""};
for (auto& str : game_args) {
arg_ptrs.push_back(str.data());
}

View file

@ -75,7 +75,7 @@ GameVersion g_game_version = GameVersion::Jak1;
namespace {
int g_argc = 0;
char** g_argv = nullptr;
const char** g_argv = nullptr;
/*!
* SystemThread function for running the DECI2 communication with the GOAL compiler.
@ -310,7 +310,7 @@ void dmac_runner(SystemThreadInterface& iface) {
* Main function to launch the runtime.
* GOAL kernel arguments are currently ignored.
*/
RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, char** argv) {
RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const char** argv) {
g_argc = argc;
g_argv = argv;
g_main_thread_id = std::this_thread::get_id();

View file

@ -16,6 +16,6 @@
extern u8* g_ee_main_mem;
extern GameVersion g_game_version;
RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, char** argv);
RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const char** argv);
extern std::thread::id g_main_thread_id;

View file

@ -37,7 +37,7 @@
(defmethod mem-usage tfragment ((obj tfragment) (arg0 memory-usage-block) (arg1 int))
"Compute the memory usage of a tfragment"
;; seems like this flag does colors?
(when (logtest? arg1 2)
(+! (-> arg0 data 19 count) 1)
@ -47,18 +47,18 @@
)
(return obj)
)
(let ((s4-0 1))
(set! (-> arg0 length) (max (-> arg0 length) (+ s4-0 8)))
(set! (-> arg0 data s4-0 name) (symbol->string 'tfragment))
(+! (-> arg0 data s4-0 count) 1)
;; the size of the actual tfragment
(let ((v1-22 (asize-of obj)))
(+! (-> arg0 data s4-0 used) v1-22)
(+! (-> arg0 data s4-0 total) (logand -16 (+ v1-22 15)))
)
;; the size of the "base" DMA
(set! (-> arg0 data (+ s4-0 1) name) "tfragment-base")
(+! (-> arg0 data (+ s4-0 1) count) 1)
@ -66,7 +66,7 @@
(+! (-> arg0 data (+ s4-0 1) used) v1-33)
(+! (-> arg0 data (+ s4-0 1) total) v1-33)
)
;; the size of the "common" DMA
(set! (-> arg0 data (+ s4-0 2) name) "tfragment-common")
(+! (-> arg0 data (+ s4-0 2) count) 1)
@ -74,7 +74,7 @@
(+! (-> arg0 data (+ s4-0 2) used) v1-43)
(+! (-> arg0 data (+ s4-0 2) total) v1-43)
)
;; the size of the "level0" DMA
(set! (-> arg0 data (+ s4-0 3) name) "tfragment-level0")
(when (nonzero? (-> obj num-level0-colors))
@ -84,7 +84,7 @@
(+! (-> arg0 data (+ s4-0 3) total) v1-55)
)
)
;; The size of the "level0" DMA. Note that the dma chains can overlap, so this is a bit weird.
(set! (-> arg0 data (+ s4-0 4) name) "tfragment-level1")
(when (not (or (= (-> obj dma-level-1) (-> obj dma-common))
@ -104,7 +104,7 @@
(+! (-> arg0 data (+ s4-0 4) total) v1-70)
)
)
;; colors
(set! (-> arg0 data (+ s4-0 5) name) "tfragment-color")
(+! (-> arg0 data (+ s4-0 5) count) 1)
@ -117,11 +117,11 @@
(+! (-> arg0 data (+ s4-0 5) used) v1-79)
(+! (-> arg0 data (+ s4-0 5) total) (logand -16 (+ v1-79 15)))
)
;; debug (unused)
(set! (-> arg0 data (+ s4-0 6) name) "tfragment-debug")
)
obj
)
@ -322,12 +322,12 @@
(defun add-pc-tfrag3-data ((dma-buf dma-buffer) (lev level))
"Add PC-port specific tfrag data"
(let ((packet (the-as dma-packet (-> dma-buf base))))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 16))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 24))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! (-> dma-buf base) (the pointer (&+ packet 16)))
)
;; first 4 quadwords are planes, then itimes
(let ((data-ptr (the-as (pointer uint128) (-> dma-buf base))))
(set! (-> data-ptr 0) (-> *math-camera* plane 0 quad))
@ -349,9 +349,18 @@
(set! (-> vec z) (-> *math-camera* fog-max))
)
(set! (-> data-ptr 14) (-> (math-camera-pos) quad))
(charp<-string (the (pointer uint8) (&-> data-ptr 15)) (symbol->string (-> lev nickname)))
(set! (-> data-ptr 15) (-> *math-camera* camera-rot vector 0 quad))
(set! (-> data-ptr 16) (-> *math-camera* camera-rot vector 1 quad))
(set! (-> data-ptr 17) (-> *math-camera* camera-rot vector 2 quad))
(set! (-> data-ptr 18) (-> *math-camera* camera-rot vector 3 quad))
(set! (-> data-ptr 19) (-> *math-camera* perspective vector 0 quad))
(set! (-> data-ptr 20) (-> *math-camera* perspective vector 1 quad))
(set! (-> data-ptr 21) (-> *math-camera* perspective vector 2 quad))
(set! (-> data-ptr 22) (-> *math-camera* perspective vector 3 quad))
(charp<-string (the (pointer uint8) (&-> data-ptr 23)) (symbol->string (-> lev nickname)))
)
(&+! (-> dma-buf base) (* 16 16))
(&+! (-> dma-buf base) (* 16 24))
)
;;;;;;;;;;;;;;;;;;;;;

View file

@ -607,7 +607,7 @@
(defun add-pc-tfrag3-data ((dma-buf dma-buffer) (lev level))
"Add PC-port specific tfrag data"
(let ((packet (the-as dma-packet (-> dma-buf base))))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 16))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 24))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! (-> dma-buf base) (the pointer (&+ packet 16)))
@ -638,6 +638,17 @@
(set! (-> vec z) (-> *math-camera* fog-max))
)
(set! (-> data-ptr 14) (-> *math-camera* trans-other quad))
(set! (-> data-ptr 15) (-> *math-camera* camera-rot-other vector 0 quad))
(set! (-> data-ptr 16) (-> *math-camera* camera-rot-other vector 1 quad))
(set! (-> data-ptr 17) (-> *math-camera* camera-rot-other vector 2 quad))
(set! (-> data-ptr 18) (-> *math-camera* camera-rot-other vector 3 quad))
(set! (-> data-ptr 19) (-> *math-camera* perspective vector 0 quad))
(set! (-> data-ptr 20) (-> *math-camera* perspective vector 1 quad))
(set! (-> data-ptr 21) (-> *math-camera* perspective vector 2 quad))
(set! (-> data-ptr 22) (-> *math-camera* perspective vector 3 quad))
)
(else
(set! (-> data-ptr 0) (-> *math-camera* plane 0 quad))
@ -659,10 +670,20 @@
(set! (-> vec z) (-> *math-camera* fog-max))
)
(set! (-> data-ptr 14) (-> *math-camera* trans quad))
(set! (-> data-ptr 15) (-> *math-camera* camera-rot vector 0 quad))
(set! (-> data-ptr 16) (-> *math-camera* camera-rot vector 1 quad))
(set! (-> data-ptr 17) (-> *math-camera* camera-rot vector 2 quad))
(set! (-> data-ptr 18) (-> *math-camera* camera-rot vector 3 quad))
(set! (-> data-ptr 19) (-> *math-camera* perspective vector 0 quad))
(set! (-> data-ptr 20) (-> *math-camera* perspective vector 1 quad))
(set! (-> data-ptr 21) (-> *math-camera* perspective vector 2 quad))
(set! (-> data-ptr 22) (-> *math-camera* perspective vector 3 quad))
)
)
(charp<-string (the (pointer uint8) (&-> data-ptr 15)) (symbol->string (-> lev nickname)))
(charp<-string (the (pointer uint8) (&-> data-ptr 23)) (symbol->string (-> lev nickname)))
)
(&+! (-> dma-buf base) (* 16 16))
(&+! (-> dma-buf base) (* 16 24))
)

View file

@ -110,6 +110,23 @@
)
)
(defun pc-add-tie-envmap-info ((dma-buf dma-buffer))
(let ((packet (the-as dma-packet (-> dma-buf base))))
(set! (-> packet dma) (new 'static 'dma-tag :id (dma-tag-id cnt) :qwc 1))
(set! (-> packet vif0) (new 'static 'vif-tag))
(set! (-> packet vif1) (new 'static 'vif-tag :cmd (vif-cmd pc-port)))
(set! (-> dma-buf base) (the pointer (&+ packet 16)))
(set! (-> (the (pointer uint128) (-> dma-buf base)))
(if (and *time-of-day-context*
(nonzero? *time-of-day-context*))
(-> *time-of-day-context* current-env-color quad)
(the uint128 0)
)
)
(set! (-> dma-buf base) (the pointer (&+ packet 32)))
)
)
(defun instance-tie-patch-buckets ((arg0 dma-buffer) (arg1 level))
; (when (logtest? (-> *display* vu1-enable-user) (vu1-renderer-mask tie))
; (when (nonzero? (-> *prototype-tie-work* scissor-count))
@ -220,6 +237,7 @@
; (&+! (-> v1-42 base) 16)
(add-pc-tfrag3-data v1-42 arg1)
(pc-add-tie-vis-mask arg1 v1-42)
(pc-add-tie-envmap-info v1-42)
(let ((a3-16 (-> v1-42 base)))
(let ((a0-20 (the-as dma-packet (-> v1-42 base))))
(set! (-> a0-20 dma) (new 'static 'dma-tag :id (dma-tag-id next)))
@ -1442,14 +1460,14 @@
(none)
)
(defun tie-init-envmap-buf ((arg0 bucket-id) (arg1 gs-alpha) (arg2 int) (arg3 int))
;; nothing yet
(defun tie-init-envmap-buf ((arg0 bucket-id) (arg1 gs-alpha) (arg2 gs-test))
;; PC doesn't need this, skip it.
; (let ((s5-0 (-> *display* frames (-> *display* on-screen) bucket-group arg0)))
; (when (!= s5-0 (-> s5-0 last))
; (let* ((s4-0 (-> *display* frames (-> *display* on-screen) global-buf))
; (s3-1 (-> s4-0 base))
; )
; (etie-init-engine s4-0)
; (etie-init-engine s4-0 arg1 arg2)
; (let ((v1-12 (the-as dma-packet (-> s4-0 base))))
; (set! (-> v1-12 dma) (new 'static 'dma-tag :id (dma-tag-id next) :addr (-> s5-0 next)))
; (set! (-> v1-12 vif0) (new 'static 'vif-tag))
@ -1632,7 +1650,11 @@
(a3-1 #x50000)
)
(t9-1 a0-3 (the-as gs-alpha a1-2) (the-as gs-test a2-1) (the-as gs-test a3-1))
(tie-init-envmap-buf (-> s2-0 tie-envmap-bucket) (the-as gs-alpha gp-0) #x50000 a3-1)
(tie-init-envmap-buf
(-> s2-0 tie-envmap-bucket)
(the-as gs-alpha gp-0)
(new 'static 'gs-test :zte #x1 :ztst (gs-ztest greater-equal))
)
(tie-init-envmap-scissor-buf (-> s2-0 tie-envmap-scissor-bucket) (the-as gs-alpha gp-0) #x50000 a3-1)
)
(tie-init-buf
@ -1666,7 +1688,11 @@
(a3-4 #x50000)
)
(t9-6 a0-8 (the-as gs-alpha a1-7) (the-as gs-test a2-6) (the-as gs-test a3-4))
(tie-init-envmap-buf (-> s2-0 tie-envmap-trans-bucket) (the-as gs-alpha s4-0) #x50000 a3-4)
(tie-init-envmap-buf
(-> s2-0 tie-envmap-trans-bucket)
(the-as gs-alpha s4-0)
(new 'static 'gs-test :zte #x1 :ztst (gs-ztest greater-equal))
)
(tie-init-envmap-scissor-buf (-> s2-0 tie-envmap-scissor-trans-bucket) (the-as gs-alpha s4-0) #x50000 a3-4)
)
(tie-init-buf
@ -1682,7 +1708,11 @@
(a3-6 #x51001)
)
(t9-10 a0-12 (the-as gs-alpha a1-11) (the-as gs-test a2-10) (the-as gs-test a3-6))
(tie-init-envmap-buf (-> s2-0 tie-envmap-water-bucket) (the-as gs-alpha s4-0) #x51001 a3-6)
(tie-init-envmap-buf
(-> s2-0 tie-envmap-water-bucket)
(the-as gs-alpha s4-0)
(new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal))
)
(tie-init-envmap-scissor-buf (-> s2-0 tie-envmap-scissor-water-bucket) (the-as gs-alpha s4-0) #x51001 a3-6)
)
)

View file

@ -77,6 +77,7 @@ bool run_build_level(const std::string& input_file,
add_ambients_from_json(level_json.at("ambients"), ambients, level_json.value("base_id", 12345));
file.ambients = std::move(ambients);
auto& ambient_drawable_tree = file.drawable_trees.ambients.emplace_back();
(void)ambient_drawable_tree;
// cameras
// nodes
// boxes

View file

@ -241,11 +241,6 @@ ExtractedVertices gltf_vertices(const tinygltf::Model& model,
v.s = 0;
v.t = 0;
}
v.q_unused = 0;
v.pad[0] = 0;
v.pad[1] = 0;
v.pad[2] = 0;
}
// TODO: other properties
return {result, vtx_colors, normals};

View file

@ -107,6 +107,13 @@ TEST(VuDisasm, Tie_Jak2) {
EXPECT_EQ(disasm.to_string(prog), get_expected("jak2/tie"));
}
TEST(VuDisasm, etie_Jak2) {
auto data = get_test_data("jak2/etie-vu1");
VuDisassembler disasm(VuDisassembler::VuKind::VU1);
auto prog = disasm.disassemble(data.data(), data.size() * 4, false);
EXPECT_EQ(disasm.to_string(prog), get_expected("jak2/etie-vu1"));
}
TEST(VuDisasm, SpriteDistort) {
auto data = get_test_data("sprite-distort");
VuDisassembler disasm(VuDisassembler::VuKind::VU1);
@ -146,7 +153,6 @@ TEST(VuDisasm, ForegroundVu0_Jak2) {
auto data = get_test_data("jak2/foreground-vu0");
VuDisassembler disasm(VuDisassembler::VuKind::VU0);
auto prog = disasm.disassemble(data.data(), data.size() * 4, false);
// fmt::print("{}\n", disasm.to_string(prog));
EXPECT_EQ(disasm.to_string(prog), get_expected("jak2/foreground-vu0"));
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -111,7 +111,7 @@ void runtime_no_kernel_jak1() {
const char* argv[argc] = {"", "-fakeiso", "-debug", "-nokernel", "-nosound"};
GameLaunchOptions game_options;
game_options.disable_display = true;
exec_runtime(game_options, argc, const_cast<char**>(argv));
exec_runtime(game_options, argc, argv);
}
void runtime_no_kernel_jak2() {
@ -120,7 +120,7 @@ void runtime_no_kernel_jak2() {
GameLaunchOptions game_options;
game_options.disable_display = true;
game_options.game_version = GameVersion::Jak2;
exec_runtime(game_options, argc, const_cast<char**>(argv));
exec_runtime(game_options, argc, argv);
}
void runtime_with_kernel_jak1() {
@ -128,7 +128,7 @@ void runtime_with_kernel_jak1() {
const char* argv[argc] = {"", "-fakeiso", "-debug", "-nosound"};
GameLaunchOptions game_options;
game_options.disable_display = true;
exec_runtime(game_options, argc, const_cast<char**>(argv));
exec_runtime(game_options, argc, argv);
}
void runtime_with_kernel_jak2() {
@ -137,7 +137,7 @@ void runtime_with_kernel_jak2() {
GameLaunchOptions game_options;
game_options.disable_display = true;
game_options.game_version = GameVersion::Jak2;
exec_runtime(game_options, argc, const_cast<char**>(argv));
exec_runtime(game_options, argc, argv);
}
void runtime_with_kernel_no_debug_segment() {
@ -145,7 +145,7 @@ void runtime_with_kernel_no_debug_segment() {
const char* argv[argc] = {"", "-fakeiso", "-debug-mem", "-nosound"};
GameLaunchOptions game_options;
game_options.disable_display = true;
exec_runtime(game_options, argc, const_cast<char**>(argv));
exec_runtime(game_options, argc, argv);
}
void createDirIfAbsent(const std::string& path) {