mirror of
https://github.com/open-goal/jak-project
synced 2026-05-23 23:05:43 -04:00
3d4dfb2077
- Add "tfrag-water" tfrag tree support (may just be the same as Jak 1's 'dirt' for the settings) - Add "tfrag-trans" tfrag tree support, reusing "trans-tfrag" from jak 1. - Add a hack to `LinkedObjectFileCreation` to handle `oracle`, which is accidentally multiply defined as a type leftover from jak 1 (an entity in village1), and level info for jak 2. - Add `VI1.DGO` - add `time-of-day.gc`, and a few other stub functions so it works - Set up some time of day stuff in GOAL for jak 2/PC renderers - Clean up time of day in c++ renderers, support the more complicated weight system used by jak 2 (backward compatible with jak 1, thankfully) The mood functions now run, so this could cause problems if they rely on stuff we don't have yet. But it seems fine for ctysluma and prison for now.        
2240 lines
77 KiB
C++
2240 lines
77 KiB
C++
#include "extract_tfrag.h"
|
|
|
|
#include "common/custom_data/pack_helpers.h"
|
|
#include "common/dma/dma.h"
|
|
#include "common/dma/gs.h"
|
|
#include "common/log/log.h"
|
|
#include "common/util/Assert.h"
|
|
#include "common/util/FileUtil.h"
|
|
|
|
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
|
#include "decompiler/util/Error.h"
|
|
|
|
namespace decompiler {
|
|
namespace {
|
|
|
|
static constexpr int GEOM_MAX = 3;
|
|
|
|
/*!
|
|
* Get the index of the first draw node in an array. Works for node or tfrag.
|
|
*/
|
|
u16 get_first_idx(const level_tools::DrawableInlineArray* array) {
|
|
auto as_tfrags = dynamic_cast<const level_tools::DrawableInlineArrayTFrag*>(array);
|
|
auto as_nodes = dynamic_cast<const level_tools::DrawableInlineArrayNode*>(array);
|
|
if (as_tfrags) {
|
|
return as_tfrags->tfragments.at(0).id;
|
|
} else if (as_nodes) {
|
|
return as_nodes->draw_nodes.at(0).id;
|
|
} else {
|
|
ASSERT(false);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Verify node indices follow the patterns we expect. Takes start as the expected first,
|
|
* writes the end.
|
|
*/
|
|
bool verify_node_indices_from_array(const level_tools::DrawableInlineArray* array,
|
|
u16 start,
|
|
u16* end) {
|
|
auto as_tfrags = dynamic_cast<const level_tools::DrawableInlineArrayTFrag*>(array);
|
|
auto as_nodes = dynamic_cast<const level_tools::DrawableInlineArrayNode*>(array);
|
|
|
|
if (as_tfrags) {
|
|
for (auto& elt : as_tfrags->tfragments) {
|
|
if (elt.id != start) {
|
|
lg::error("bad frag: exp {} got {}", start, elt.id);
|
|
return false;
|
|
}
|
|
start++;
|
|
}
|
|
*end = start;
|
|
return true;
|
|
} else if (as_nodes) {
|
|
for (auto& elt : as_nodes->draw_nodes) {
|
|
if (elt.id != start) {
|
|
lg::error("bad node: exp {} got {}", start, elt.id);
|
|
return false;
|
|
}
|
|
start++;
|
|
}
|
|
*end = start;
|
|
return true;
|
|
} else {
|
|
lg::error("bad node array type: {}", array->my_type());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* Verify all node indices in a tree.
|
|
*/
|
|
bool verify_node_indices(const level_tools::DrawableTreeTfrag* tree) {
|
|
u16 start = get_first_idx(tree->arrays.at(0).get());
|
|
for (auto& array : tree->arrays) {
|
|
if (!verify_node_indices_from_array(array.get(), start, &start)) {
|
|
return false;
|
|
}
|
|
start = (start + 31) & ~(31);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* Extract the visibility tree.
|
|
* This does not insert nodes for the bottom level.
|
|
*/
|
|
VisNodeTree extract_vis_data(const level_tools::DrawableTreeTfrag* tree, u16 first_child) {
|
|
VisNodeTree result;
|
|
result.first_child_node = first_child;
|
|
result.last_child_node = first_child;
|
|
|
|
if (tree->arrays.size() == 0) {
|
|
} else if (tree->arrays.size() == 1) {
|
|
auto array =
|
|
dynamic_cast<const level_tools::DrawableInlineArrayTFrag*>(tree->arrays.at(0).get());
|
|
ASSERT(array);
|
|
result.first_root = array->tfragments.at(0).id;
|
|
result.num_roots = array->tfragments.size();
|
|
result.only_children = true;
|
|
} else {
|
|
auto array =
|
|
dynamic_cast<const level_tools::DrawableInlineArrayNode*>(tree->arrays.at(0).get());
|
|
ASSERT(array);
|
|
result.first_root = array->draw_nodes.at(0).id;
|
|
result.num_roots = array->draw_nodes.size();
|
|
result.only_children = false;
|
|
}
|
|
|
|
result.vis_nodes.resize(first_child - result.first_root);
|
|
|
|
// may run 0 times, if there are only children.
|
|
for (int i = 0; i < ((int)tree->arrays.size()) - 1; i++) {
|
|
bool expecting_leaves = i == ((int)tree->arrays.size()) - 2;
|
|
|
|
auto array =
|
|
dynamic_cast<const level_tools::DrawableInlineArrayNode*>(tree->arrays.at(i).get());
|
|
ASSERT(array);
|
|
u16 idx = first_child;
|
|
for (auto& elt : array->draw_nodes) {
|
|
auto& vis = result.vis_nodes.at(elt.id - result.first_root);
|
|
ASSERT(vis.num_kids == 0xff);
|
|
for (int j = 0; j < 4; j++) {
|
|
vis.bsphere[j] = elt.bsphere.data[j];
|
|
}
|
|
vis.num_kids = elt.child_count;
|
|
vis.flags = elt.flags;
|
|
vis.my_id = elt.id;
|
|
ASSERT(vis.flags == expecting_leaves ? 0 : 1);
|
|
ASSERT(vis.num_kids > 0);
|
|
ASSERT(vis.num_kids <= 8);
|
|
ASSERT(elt.children.size() == vis.num_kids);
|
|
if (expecting_leaves) {
|
|
for (int leaf = 0; leaf < (int)vis.num_kids; leaf++) {
|
|
auto l = dynamic_cast<level_tools::TFragment*>(elt.children.at(leaf).get());
|
|
ASSERT(l);
|
|
|
|
ASSERT(idx == l->id);
|
|
|
|
ASSERT(l->id >= result.first_child_node);
|
|
if (leaf == 0) {
|
|
vis.child_id = l->id;
|
|
}
|
|
result.last_child_node = std::max((u16)l->id, result.last_child_node);
|
|
idx++;
|
|
}
|
|
|
|
} else {
|
|
u16 arr_idx = 0;
|
|
for (int child = 0; child < (int)vis.num_kids; child++) {
|
|
auto l = dynamic_cast<level_tools::DrawNode*>(elt.children.at(child).get());
|
|
ASSERT(l);
|
|
if (child == 0) {
|
|
arr_idx = l->id;
|
|
} else {
|
|
ASSERT(arr_idx < l->id);
|
|
arr_idx = l->id;
|
|
}
|
|
if (child == 0) {
|
|
vis.child_id = l->id;
|
|
}
|
|
|
|
ASSERT(l->id < result.first_child_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// The tfrag drawing process consists of:
|
|
// - loading occlusion culling data (skipping, at least for now in the PC port)
|
|
// - running draw-inline-array-tfrag and draw-inline-array-tfrag-near to generate DMA lists
|
|
// - VU1 VIF unpacks to load data
|
|
// - VU1 MSCALs to render. There are several programs. We have ported program 6 which can render
|
|
// tfrags at a fixed LOD, including the highest LOD.
|
|
|
|
// our strategy is to figure out how the game would generate the highest LOD upload for all tfrags
|
|
// normally there's culling/not drawing near ones with the far renderer/etc decided in the
|
|
// draw-inline-array-tfrag functions. But we don't want any of that.
|
|
|
|
struct TFragExtractStats {
|
|
int num_l1 = 0;
|
|
int num_l0 = 0;
|
|
int num_base = 0;
|
|
};
|
|
|
|
struct UnpackState {
|
|
int wl = 4;
|
|
int cl = 4;
|
|
u32 row[4] = {0, 0, 0, 0};
|
|
bool row_init = false;
|
|
u8 stmod = 0;
|
|
};
|
|
|
|
namespace {
|
|
|
|
int handle_unpack_v4_8_mode0(const VifCode& code,
|
|
const u8* dma,
|
|
int offset_word,
|
|
int cl,
|
|
int wl,
|
|
u8* out) {
|
|
VifCodeUnpack unpack(code);
|
|
ASSERT(unpack.use_tops_flag);
|
|
int offset = offset_word * 4;
|
|
|
|
// CL x (num/WL)+(num%WL)
|
|
|
|
if (unpack.is_unsigned) {
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 2);
|
|
ASSERT(wl == 1);
|
|
ASSERT(code.num);
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + 2 * i;
|
|
ASSERT(dest_qw <= 328);
|
|
u32 qw[4];
|
|
qw[0] = dma[offset++];
|
|
qw[1] = dma[offset++];
|
|
qw[2] = dma[offset++];
|
|
qw[3] = dma[offset++];
|
|
memcpy(out + (dest_qw * 16), qw, 16);
|
|
}
|
|
} else {
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 4);
|
|
ASSERT(wl == 4);
|
|
ASSERT(code.num);
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i;
|
|
ASSERT(dest_qw <= 328);
|
|
s32 qw[4];
|
|
qw[0] = (s8)dma[offset++];
|
|
qw[1] = (s8)dma[offset++];
|
|
qw[2] = (s8)dma[offset++];
|
|
qw[3] = (s8)dma[offset++];
|
|
memcpy(out + (dest_qw * 16), qw, 16);
|
|
}
|
|
}
|
|
|
|
ASSERT((offset % 4) == 0);
|
|
return offset / 4;
|
|
}
|
|
|
|
int handle_unpack_v4_8_mode1(const VifCode& code,
|
|
const u8* dma,
|
|
int offset_word,
|
|
int cl,
|
|
int wl,
|
|
const u32 row[4],
|
|
u8* out) {
|
|
VifCodeUnpack unpack(code);
|
|
ASSERT(unpack.use_tops_flag);
|
|
int offset = offset_word * 4;
|
|
// CL x (num/WL)+(num%WL)
|
|
|
|
if (unpack.is_unsigned) {
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 4);
|
|
ASSERT(wl == 4);
|
|
ASSERT(code.num);
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i;
|
|
ASSERT(dest_qw <= 328);
|
|
u32 qw[4];
|
|
qw[0] = row[0] + dma[offset++];
|
|
qw[1] = row[1] + dma[offset++];
|
|
qw[2] = row[2] + dma[offset++];
|
|
qw[3] = row[3] + dma[offset++];
|
|
memcpy(out + (dest_qw * 16), qw, 16);
|
|
}
|
|
} else {
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 4);
|
|
ASSERT(wl == 4);
|
|
ASSERT(code.num);
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i;
|
|
ASSERT(dest_qw <= 328);
|
|
s32 qw[4];
|
|
qw[0] = row[0] + (s8)dma[offset++];
|
|
qw[1] = row[1] + (s8)dma[offset++];
|
|
qw[2] = row[2] + (s8)dma[offset++];
|
|
qw[3] = row[3] + (s8)dma[offset++];
|
|
memcpy(out + (dest_qw * 16), qw, 16);
|
|
}
|
|
}
|
|
|
|
ASSERT((offset % 4) == 0);
|
|
return offset / 4;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
T deref_ptr(const U* x) {
|
|
T result;
|
|
memcpy(&result, x, sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
int handle_unpack_v4_16_mode0(const VifCode& code,
|
|
const u8* dma,
|
|
int offset_word,
|
|
int cl,
|
|
int wl,
|
|
u8* vu_mem) {
|
|
VifCodeUnpack unpack(code);
|
|
ASSERT(unpack.use_tops_flag);
|
|
ASSERT(unpack.is_unsigned);
|
|
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 4);
|
|
ASSERT(wl == 4);
|
|
|
|
int offset = offset_word * 4;
|
|
|
|
ASSERT(code.num);
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i;
|
|
ASSERT(dest_qw <= 328);
|
|
u32 qw[4];
|
|
qw[0] = deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
qw[1] = deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
qw[2] = deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
qw[3] = deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
memcpy(vu_mem + (dest_qw * 16), qw, 16);
|
|
}
|
|
ASSERT((offset % 4) == 0);
|
|
return offset / 4;
|
|
}
|
|
|
|
int handle_unpack_v4_16_mode1(const VifCode& code,
|
|
const u8* dma,
|
|
int offset_word,
|
|
int cl,
|
|
int wl,
|
|
const u32 row[4],
|
|
u8* vu_mem) {
|
|
VifCodeUnpack unpack(code);
|
|
ASSERT(unpack.use_tops_flag);
|
|
ASSERT(unpack.is_unsigned);
|
|
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 4);
|
|
ASSERT(wl == 4);
|
|
|
|
ASSERT(code.num);
|
|
int offset = offset_word * 4;
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i;
|
|
ASSERT(dest_qw <= 328);
|
|
u32 qw[4];
|
|
qw[0] = row[0] + (u32)deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
qw[1] = row[1] + (u32)deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
qw[2] = row[2] + (u32)deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
qw[3] = row[3] + (u32)deref_ptr<u16>(dma + offset);
|
|
offset += 2;
|
|
|
|
// lg::print(" unpack rgba?: {:x} {:x} {:x} {:x}\n", qw[0], qw[1], qw[2], qw[3]);
|
|
memcpy(vu_mem + (dest_qw * 16), qw, 16);
|
|
}
|
|
ASSERT((offset % 4) == 0);
|
|
return offset / 4;
|
|
}
|
|
|
|
int handle_unpack_v3_32(const VifCode& code,
|
|
const u8* dma,
|
|
int offset_word,
|
|
int cl,
|
|
int wl,
|
|
u8* vu_mem) {
|
|
VifCodeUnpack unpack(code);
|
|
ASSERT(unpack.use_tops_flag);
|
|
ASSERT(!unpack.is_unsigned);
|
|
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 2);
|
|
ASSERT(wl == 1);
|
|
|
|
ASSERT(code.num);
|
|
int offset = offset_word * 4;
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i * 2;
|
|
ASSERT(dest_qw <= 328);
|
|
u32 qw[4];
|
|
qw[0] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
qw[1] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
qw[2] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
qw[3] = 0x80; // this can be anything...
|
|
memcpy(vu_mem + (dest_qw * 16), qw, 16);
|
|
}
|
|
ASSERT((offset % 4) == 0);
|
|
return offset / 4;
|
|
}
|
|
|
|
int handle_unpack_v4_32(const VifCode& code,
|
|
const u8* dma,
|
|
int offset_word,
|
|
int cl,
|
|
int wl,
|
|
u8* vu_mem) {
|
|
VifCodeUnpack unpack(code);
|
|
ASSERT(unpack.use_tops_flag);
|
|
ASSERT(!unpack.is_unsigned);
|
|
|
|
// note: formulas below assume this!
|
|
ASSERT(cl == 4);
|
|
ASSERT(wl == 4);
|
|
int offset = offset_word * 4;
|
|
ASSERT(code.num);
|
|
for (int i = 0; i < code.num; i++) {
|
|
// write every other qw
|
|
int dest_qw = unpack.addr_qw + i;
|
|
ASSERT(dest_qw <= 328);
|
|
u32 qw[4];
|
|
qw[0] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
qw[1] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
qw[2] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
qw[3] = deref_ptr<u32>(dma + offset);
|
|
offset += 4;
|
|
memcpy(vu_mem + (dest_qw * 16), qw, 16);
|
|
}
|
|
ASSERT((offset % 4) == 0);
|
|
return offset / 4;
|
|
|
|
// u8* write_base = get_upload_buffer();
|
|
// ASSERT(code.num + unpack.addr_qw <= 328);
|
|
// memcpy(write_base + (unpack.addr_qw * 16), dma.data + offset, code.num * 16);
|
|
// return offset + code.num * 16;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void emulate_chain(UnpackState& state, u32 max_words, const u32* start, u8* vu_mem) {
|
|
u32 word = 0;
|
|
|
|
while (word < max_words) {
|
|
VifCode code(start[word]);
|
|
word++;
|
|
// lg::print("{}\n", code.print());
|
|
switch (code.kind) {
|
|
case VifCode::Kind::STROW:
|
|
state.row_init = true;
|
|
memcpy(state.row, start + word, 16);
|
|
word += 4;
|
|
break;
|
|
case VifCode::Kind::STMOD:
|
|
if (state.stmod == 0) {
|
|
ASSERT(code.immediate == 1);
|
|
} else {
|
|
ASSERT(state.stmod == 1);
|
|
ASSERT(code.immediate == 0 || code.immediate == 1); // kinda weird.
|
|
}
|
|
state.stmod = code.immediate;
|
|
break;
|
|
case VifCode::Kind::UNPACK_V4_8:
|
|
if (state.stmod == 0) {
|
|
word = handle_unpack_v4_8_mode0(code, (const u8*)start, word, state.cl, state.wl, vu_mem);
|
|
} else if (state.stmod == 1) {
|
|
ASSERT(state.row_init);
|
|
word = handle_unpack_v4_8_mode1(code, (const u8*)start, word, state.cl, state.wl,
|
|
state.row, vu_mem);
|
|
} else {
|
|
ASSERT(false);
|
|
}
|
|
break;
|
|
case VifCode::Kind::UNPACK_V4_16:
|
|
if (state.stmod == 0) {
|
|
word =
|
|
handle_unpack_v4_16_mode0(code, (const u8*)start, word, state.cl, state.wl, vu_mem);
|
|
} else if (state.stmod == 1) {
|
|
ASSERT(state.row_init);
|
|
word = handle_unpack_v4_16_mode1(code, (const u8*)start, word, state.cl, state.wl,
|
|
state.row, vu_mem);
|
|
} else {
|
|
ASSERT(false);
|
|
}
|
|
break;
|
|
case VifCode::Kind::UNPACK_V4_32:
|
|
ASSERT(state.stmod == 0);
|
|
word = handle_unpack_v4_32(code, (const u8*)start, word, state.cl, state.wl, vu_mem);
|
|
break;
|
|
case VifCode::Kind::UNPACK_V3_32:
|
|
ASSERT(state.stmod == 0);
|
|
word = handle_unpack_v3_32(code, (const u8*)start, word, state.cl, state.wl, vu_mem);
|
|
break;
|
|
case VifCode::Kind::NOP:
|
|
break;
|
|
case VifCode::Kind::STCYCL:
|
|
|
|
{
|
|
VifCodeStcycl ss(code.immediate);
|
|
state.cl = ss.cl;
|
|
state.wl = ss.wl;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
throw Error("unknown vif: {}", code.print());
|
|
}
|
|
}
|
|
|
|
ASSERT(word == max_words);
|
|
ASSERT(state.stmod == 0);
|
|
}
|
|
|
|
struct TFragColorUnpack {
|
|
// std::vector<math::Vector<u16, 4>> data;
|
|
std::vector<u16> indices;
|
|
u32 unpack_qw_addr = 0;
|
|
|
|
u16 load_color_idx(u32 qw) {
|
|
if (qw < unpack_qw_addr) {
|
|
return 0xffff;
|
|
}
|
|
int past = qw - unpack_qw_addr;
|
|
past /= 2;
|
|
if (past < (int)indices.size()) {
|
|
return indices.at(past);
|
|
} else {
|
|
return 0xffff;
|
|
}
|
|
}
|
|
};
|
|
|
|
void emulate_dma_building_for_tfrag(int geom,
|
|
const level_tools::TFragment& frag,
|
|
std::vector<u8>& vu_mem,
|
|
TFragColorUnpack& color_indices,
|
|
TFragExtractStats* stats) {
|
|
UnpackState state;
|
|
// all the templates do this...
|
|
state.wl = 4;
|
|
state.cl = 4;
|
|
|
|
// do the "canned" unpacks
|
|
if (frag.num_level0_colors == 0 || geom == 2) {
|
|
// we're using base
|
|
// ASSERT(frag.num_level1_colors == 0);
|
|
stats->num_base++;
|
|
emulate_chain(state, frag.dma_qwc[1] * 4, (const u32*)frag.dma_base.data(), vu_mem.data());
|
|
|
|
} else if (frag.num_level1_colors == 0 || geom == 1) {
|
|
stats->num_l0++;
|
|
emulate_chain(state, frag.dma_qwc[3] * 4, (const u32*)frag.dma_common_and_level0.data(),
|
|
vu_mem.data());
|
|
} else {
|
|
stats->num_l1++;
|
|
// common
|
|
emulate_chain(state, frag.dma_qwc[0] * 4, (const u32*)frag.dma_common_and_level0.data(),
|
|
vu_mem.data());
|
|
|
|
state.wl = 4;
|
|
state.cl = 4;
|
|
// l1
|
|
emulate_chain(state, frag.dma_qwc[2] * 4, (const u32*)frag.dma_level1.data(), vu_mem.data());
|
|
}
|
|
|
|
// the colors are copied to the dma-buffer directly.
|
|
// these change per-frame based on time of day lighting.
|
|
// 64 = color-tmp
|
|
|
|
// sb 12, color-offset
|
|
// sh 0, color qwc (round up, divide by 4)
|
|
// sb 14, num-colors
|
|
|
|
// the actual colors go in
|
|
// color-tmpl is
|
|
// :dma (new 'static 'dma-tag :id (dma-tag-id cnt))
|
|
// :vif0 (new 'static 'vif-tag :imm #x102 :cmd (vif-cmd stcycl)) ;; cl = 2, wl = 1
|
|
|
|
// :vif1 (new 'static 'vif-tag :imm #xc000 :cmd (vif-cmd unpack-v4-8)) ;; flg, unsigned
|
|
color_indices.indices = frag.color_indices;
|
|
color_indices.unpack_qw_addr = frag.color_offset;
|
|
}
|
|
|
|
/*
|
|
|
|
// val = [0.5, 1.0, 2048.0 0.0]
|
|
// adgif = gif tag for 5x a+d's
|
|
// strgif = the gif tag for drawing
|
|
// pre = (new 'static 'gs-prim :prim (gs-prim-type tri-strip) :iip #x1 :tme #x1 :fge #x1 :abe
|
|
arg1)
|
|
// regs = st, rgbaq, xyzf2
|
|
// hvdf = changes
|
|
// hmge = changes
|
|
// fog = changes
|
|
|
|
// common tfrag setup
|
|
lq.xyzw vf02, 657(vi00) | nop ;;
|
|
lq.xyzw vf05, 660(vi00) | addw.z vf28, vf00, vf00
|
|
lq.xyzw vf06, 658(vi00) | nop
|
|
lq.xyzw vf10, 661(vi00) | nop
|
|
lq.xyzw vf11, 662(vi00) | nop
|
|
lq.xyzw vf01, 656(vi00) | addz.z vf28, vf28, vf02
|
|
ilw.w vi08, 4(vi14) | nop
|
|
ilw.z vi09, 4(vi14) | nop
|
|
ilw.y vi03, 3(vi14) | nop
|
|
fcset 0x0 | nop
|
|
iaddi vi07, vi00, -0x1 | nop
|
|
lq.xyzw vf04, 5(vi14) | mulw.xyzw vf16, vf00, vf00
|
|
lq.xyzw vf07, 6(vi14) | mulw.xyzw vf17, vf00, vf00
|
|
ibne vi00, vi14, L136 | mulw.xyzw vf18, vf00, vf00
|
|
lq.xyzw vf08, 7(vi14) | mulw.xyzw vf19, vf00, vf00
|
|
|
|
*/
|
|
|
|
struct VuMemWrapper {
|
|
VuMemWrapper(const std::vector<u8>& vu_mem) : mem(&vu_mem) {}
|
|
const std::vector<u8>* mem = nullptr;
|
|
u16 ilw_data(int offset, int xyzw) {
|
|
u16 result;
|
|
|
|
ASSERT(offset < 328);
|
|
ASSERT(offset >= 0);
|
|
int mem_offset = (xyzw * 4) + (offset * 16);
|
|
memcpy(&result, mem->data() + mem_offset, 2);
|
|
return result;
|
|
}
|
|
|
|
math::Vector4f load_vector_data(int offset) {
|
|
math::Vector4f result;
|
|
// offset = offset & 0x3ff; // not super happy with this...
|
|
ASSERT(offset < 328);
|
|
ASSERT(offset >= 0);
|
|
memcpy(&result, mem->data() + (offset * 16), 16);
|
|
return result;
|
|
}
|
|
};
|
|
|
|
using math::Vector3f;
|
|
using math::Vector4f;
|
|
|
|
u32 float_2_u32(float x) {
|
|
u32 y;
|
|
memcpy(&y, &x, 4);
|
|
return y;
|
|
}
|
|
|
|
Vector4f itof0(const Vector4f& vec) {
|
|
Vector4f result;
|
|
for (int i = 0; i < 4; i++) {
|
|
s32 val;
|
|
memcpy(&val, vec.data() + i, 4);
|
|
result[i] = val;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct TFragVertexData {
|
|
Vector4f pre_cam_trans_pos;
|
|
Vector3f stq; // stq?
|
|
u16 rgba; // unlike actual tfrag, these are still indices
|
|
bool end_of_strip = false;
|
|
|
|
// pos = cam.rot * pctp.xyz + cam.trans
|
|
// q = fog.x / pctp.w
|
|
// pos *= q
|
|
// pos += hvdf_offset
|
|
// pos.w = min(pos.w, fog.z)
|
|
// pos.w = max(pow.w, fog.y)
|
|
// pos.w += fog.w
|
|
|
|
// unk *= q
|
|
};
|
|
|
|
struct TFragDraw {
|
|
u8 adgif_data[16 * 5];
|
|
|
|
u16 tpage = 0;
|
|
u16 tex_in_page = 0;
|
|
|
|
u16 tfrag_id = 0;
|
|
|
|
DrawMode mode;
|
|
|
|
u64 get_adgif_val(int adgif) {
|
|
u64 result;
|
|
memcpy(&result, adgif_data + (adgif * 16), 8);
|
|
return result;
|
|
}
|
|
|
|
u64 get_adgif_upper(int adgif) {
|
|
u64 result;
|
|
memcpy(&result, adgif_data + (adgif * 16) + 8, 8);
|
|
return result;
|
|
}
|
|
|
|
GsRegisterAddress get_adgif_addr(int adgif) {
|
|
return (GsRegisterAddress)(u8)get_adgif_upper(adgif);
|
|
}
|
|
|
|
u32 dvert = 0;
|
|
std::vector<TFragVertexData> verts;
|
|
};
|
|
|
|
template <bool DEBUG>
|
|
bool emulate_kick_subroutine(VuMemWrapper& mem,
|
|
TFragDraw& current_draw,
|
|
std::vector<TFragDraw>& all_draws,
|
|
u16& vi05_end_of_vert_kick_data,
|
|
u16& vi06_kick_zone_ptr,
|
|
u16& vi07,
|
|
u16& vi08_adgif_base,
|
|
u16& vi09_draw_addr_book,
|
|
u16& vi10_start_of_vert_kick_data,
|
|
u16& vi12_vert_count,
|
|
u16& vi13_adgifs,
|
|
u16& vf24_u16) {
|
|
// KICK ZONE!
|
|
// we reach here if we need to issue strgif/adgifs before the next vertex.
|
|
|
|
// L122:
|
|
// fcset 0x0
|
|
// m_clip_and_3ffff = false; // ??
|
|
// iaddi vi07, vi00, -0x1
|
|
vi07 = -1; // not actually used?
|
|
// iblez vi12, L123
|
|
// iaddi vi09, vi09, 0x1
|
|
vi09_draw_addr_book++; // on to the next chunk
|
|
// lg::print("VI09 now {}\n", vars.vi09);
|
|
|
|
// no need for new adgifs, just a new strgif.
|
|
if (((s16)vi12_vert_count) > 0) {
|
|
// ior vi10, vi06, vi00
|
|
vi10_start_of_vert_kick_data = vi06_kick_zone_ptr; // start of next chunk
|
|
// iadd vi01, vi12, vi12
|
|
u16 vi01 = vi12_vert_count + vi12_vert_count;
|
|
// iadd vi01, vi01, vi12
|
|
vi01 += vi12_vert_count; // qw of verts (not including 1 qw of strgif)
|
|
// iadd vi05, vi06, vi01
|
|
vi05_end_of_vert_kick_data = vi06_kick_zone_ptr + vi01; // last qw to write before coming here
|
|
// sqi.xyzw vf06, vi06
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr, m_tfrag_data.str_gif);
|
|
vi06_kick_zone_ptr++;
|
|
// isw.x vi12, -1(vi06)
|
|
// store_u32_kick_zone(vars.vi12, vars.vi06_kick_zone_ptr - 1, 0); // store verts
|
|
current_draw.dvert = vi12_vert_count;
|
|
// jr vi15
|
|
// ilwr.x vi12, vi09
|
|
vi12_vert_count = mem.ilw_data(vi09_draw_addr_book, 0); // next vert count!
|
|
if (DEBUG) {
|
|
lg::debug("continue with this adgif, but new strgif. next {} verts (kick zone now {})",
|
|
vi12_vert_count, vi06_kick_zone_ptr);
|
|
}
|
|
// lg::print("didn't kick, vi12 now {}\n", vars.vi12);
|
|
all_draws.push_back(current_draw);
|
|
current_draw.verts.clear();
|
|
return false;
|
|
}
|
|
|
|
// L123:
|
|
// ilw.y vi01, -1(vi09)
|
|
u16 vi01 = mem.ilw_data(vi09_draw_addr_book - 1, 1); // ?
|
|
// ilw.z vi13, -1(vi09)
|
|
vi13_adgifs = mem.ilw_data(vi09_draw_addr_book - 1, 2); // load new adgif addr
|
|
// lg::print("VI09 loads: {} {}\n", m_ptrs.vi01, vars.vi13);
|
|
// ibeq vi00, vi12, L126
|
|
// ilwr.x vi14, vi10
|
|
// lg::print("val is {}: {}\n", vars.vi10, ilw_kick_zone(vars.vi10, 0));
|
|
// vars.vi14 = mem.ilw_kick_zone(vi10_start_of_vert_kick_data, 0); old vert count
|
|
if (vi12_vert_count != 0) {
|
|
// ibltz vi01, L124
|
|
// iaddiu vi12, vi12, 0x80
|
|
vi12_vert_count += 0x80;
|
|
if (((s16)vi01) >= 0) {
|
|
all_draws.push_back(current_draw);
|
|
current_draw.verts.clear();
|
|
// iadd vi13, vi13, vi08
|
|
vi13_adgifs += vi08_adgif_base;
|
|
// lqi.xyzw vf29, vi13
|
|
auto vf29_adg0 = mem.load_vector_data(vi13_adgifs++);
|
|
// lqi.xyzw vf30, vi13
|
|
auto vf30_adg1 = mem.load_vector_data(vi13_adgifs++);
|
|
// lqi.xyzw vf31, vi13
|
|
auto vf31_adg2 = mem.load_vector_data(vi13_adgifs++);
|
|
// sqi.xyzw vf05, vi06
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr++, m_tfrag_data.ad_gif);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf29, vi06
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr++, vars.vf29);
|
|
memcpy(current_draw.adgif_data, vf29_adg0.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf30, vi06
|
|
memcpy(current_draw.adgif_data + 16, vf30_adg1.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf31, vi06
|
|
memcpy(current_draw.adgif_data + 32, vf31_adg2.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// lqi.xyzw vf29, vi13
|
|
vf29_adg0 = mem.load_vector_data(vi13_adgifs++);
|
|
// lqi.xyzw vf30, vi13
|
|
vf30_adg1 = mem.load_vector_data(vi13_adgifs++);
|
|
// iadd vi01, vi12, vi12
|
|
vi01 = vi12_vert_count + vi12_vert_count;
|
|
// iadd vi01, vi01, vi12
|
|
vi01 += vi12_vert_count;
|
|
// sqi.xyzw vf29, vi06
|
|
memcpy(current_draw.adgif_data + (16 * 3), vf29_adg0.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
// sqi.xyzw vf30, vi06
|
|
memcpy(current_draw.adgif_data + (16 * 4), vf30_adg1.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
// ior vi10, vi06, vi00
|
|
vi10_start_of_vert_kick_data = vi06_kick_zone_ptr;
|
|
// iadd vi05, vi06, vi01
|
|
vi05_end_of_vert_kick_data = vi06_kick_zone_ptr + vi01;
|
|
// sqi.xyzw vf06, vi06
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr++, m_tfrag_data.str_gif);
|
|
vi06_kick_zone_ptr++;
|
|
// isw.x vi12, -1(vi06)
|
|
// store_u32_kick_zone(vars.vi12, vars.vi06_kick_zone_ptr - 1, 0);
|
|
current_draw.dvert = vi12_vert_count;
|
|
// jr vi15
|
|
// ilwr.x vi12, vi09
|
|
vi12_vert_count = mem.ilw_data(vi09_draw_addr_book, 0);
|
|
if (DEBUG) {
|
|
lg::debug("done with adgifs but not packet, now moving on to another with {}",
|
|
(s16)vi12_vert_count);
|
|
}
|
|
// lg::print("didn't kick 2, vi12 now {}\n", vars.vi12);
|
|
return false;
|
|
}
|
|
|
|
// L124:
|
|
// mtir vi01, vf24.w
|
|
vi01 = vf24_u16;
|
|
// mtir vi06, vf03.y
|
|
vi06_kick_zone_ptr = 0;
|
|
// mr32.xyzw vf03, vf03
|
|
|
|
// iadd vi14, vi14, vi11
|
|
// vars.vi14 += vars.vi11; sets eop, ignore.
|
|
|
|
// ibgez vi13, L125
|
|
// iswr.x vi14, vi10
|
|
// lg::print("kick zone store: {}\n", vars.vi14);
|
|
// store_u32_kick_zone(vars.vi14, vars.vi10, 0); set eop.
|
|
if (((s16)vi13_adgifs) < 0) {
|
|
// xgkick vi01
|
|
all_draws.push_back(current_draw);
|
|
current_draw.verts.clear();
|
|
// ior vi10, vi06, vi00
|
|
vi10_start_of_vert_kick_data =
|
|
vi06_kick_zone_ptr; // xgkick delay slots, doesn't seem to matter.
|
|
// mfir.w vf24, vi06
|
|
vf24_u16 = vi06_kick_zone_ptr;
|
|
// iadd vi01, vi12, vi12
|
|
vi01 = vi12_vert_count + vi12_vert_count;
|
|
// iadd vi01, vi01, vi12
|
|
vi01 += vi12_vert_count;
|
|
// iadd vi05, vi06, vi01
|
|
vi05_end_of_vert_kick_data = vi06_kick_zone_ptr + vi01;
|
|
// sqi.xyzw vf06, vi06
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr++, m_tfrag_data.str_gif);
|
|
vi06_kick_zone_ptr++;
|
|
// isw.x vi12, -1(vi06)
|
|
// store_u32_kick_zone(vars.vi12, vars.vi06_kick_zone_ptr - 1, 0);
|
|
// jr vi15
|
|
// ilwr.x vi12, vi09
|
|
vi12_vert_count = mem.ilw_data(vi09_draw_addr_book, 0);
|
|
return false;
|
|
}
|
|
|
|
// L125:
|
|
// iadd vi13, vi13, vi08
|
|
vi13_adgifs += vi08_adgif_base;
|
|
// xgkick vi01
|
|
all_draws.push_back(current_draw);
|
|
current_draw.verts.clear();
|
|
|
|
// lqi.xyzw vf29, vi13
|
|
auto vf29_adg0 = mem.load_vector_data(vi13_adgifs++);
|
|
// lqi.xyzw vf30, vi13
|
|
auto vf30_adg1 = mem.load_vector_data(vi13_adgifs++);
|
|
// lqi.xyzw vf31, vi13
|
|
auto vf31_adg2 = mem.load_vector_data(vi13_adgifs++);
|
|
// mfir.w vf24, vi06
|
|
vf24_u16 = vi06_kick_zone_ptr;
|
|
// sqi.xyzw vf05, vi06
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr++, m_tfrag_data.ad_gif);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf29, vi06
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr++, vars.vf29);
|
|
memcpy(current_draw.adgif_data, vf29_adg0.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf30, vi06
|
|
memcpy(current_draw.adgif_data + 16, vf30_adg1.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf31, vi06
|
|
memcpy(current_draw.adgif_data + 32, vf31_adg2.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// lqi.xyzw vf29, vi13
|
|
vf29_adg0 = mem.load_vector_data(vi13_adgifs++);
|
|
// lqi.xyzw vf30, vi13
|
|
vf30_adg1 = mem.load_vector_data(vi13_adgifs++);
|
|
// iadd vi01, vi12, vi12
|
|
vi01 = vi12_vert_count + vi12_vert_count;
|
|
// iadd vi01, vi01, vi12
|
|
vi01 += vi12_vert_count;
|
|
// sqi.xyzw vf29, vi06
|
|
memcpy(current_draw.adgif_data + (16 * 3), vf29_adg0.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
// sqi.xyzw vf30, vi06
|
|
memcpy(current_draw.adgif_data + (16 * 4), vf30_adg1.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
// nop
|
|
// ior vi10, vi06, vi00
|
|
vi10_start_of_vert_kick_data = vi06_kick_zone_ptr;
|
|
// iadd vi05, vi06, vi01
|
|
vi05_end_of_vert_kick_data = vi06_kick_zone_ptr + vi01;
|
|
// sqi.xyzw vf06, vi06
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr++, m_tfrag_data.str_gif);
|
|
vi06_kick_zone_ptr++;
|
|
// isw.x vi12, -1(vi06)
|
|
// store_u32_kick_zone(vars.vi12, vars.vi06_kick_zone_ptr - 1, 0);
|
|
// jr vi15
|
|
// ilwr.x vi12, vi09
|
|
vi12_vert_count = mem.ilw_data(vi09_draw_addr_book, 0);
|
|
// lg::print("did kick, vi12 now {}\n", vars.vi12);
|
|
return false;
|
|
}
|
|
|
|
// L126:
|
|
// mtir vi01, vf24.w
|
|
// m_ptrs.vi01 = float_2_u32(vars.vf24.w());
|
|
// mr32.xyzw vf03, vf03
|
|
// auto temp = m_ptrs.vf03_x;
|
|
// m_ptrs.vf03_x = m_ptrs.vf03_y;
|
|
// m_ptrs.vf03_y = m_ptrs.vf03_z;
|
|
// m_ptrs.vf03_z = m_ptrs.vf03_w;
|
|
// m_ptrs.vf03_w = temp;
|
|
all_draws.push_back(current_draw);
|
|
current_draw.verts.clear();
|
|
// iadd vi14, vi14, vi11
|
|
// lg::print("before add: {}\n", vars.vi14);
|
|
// vars.vi14 += vars.vi11;
|
|
// iswr.x vi14, vi10
|
|
// lg::print("kick zone store: {}\n", vars.vi14);
|
|
// store_u32_kick_zone(vars.vi14, vars.vi10, 0);
|
|
// lq.xyzw vf04, 664(vi00)
|
|
// todo don't think I needed that load of ambient
|
|
// XGKICK<DEBUG>(m_ptrs.vi01, render_state, prof);
|
|
// xgkick vi01
|
|
// nop | nop :e
|
|
return true;
|
|
}
|
|
|
|
template <bool DEBUG>
|
|
std::vector<TFragDraw> emulate_tfrag_execution(const level_tools::TFragment& frag,
|
|
VuMemWrapper& mem,
|
|
TFragColorUnpack& color_indices,
|
|
TFragExtractStats* /*stats*/) {
|
|
// lg::print("tfrag exec. offset of colors = {}\n", color_indices.unpack_qw_addr);
|
|
std::vector<TFragDraw> all_draws;
|
|
TFragDraw current_draw;
|
|
|
|
TFragVertexData vertex_pipeline[4];
|
|
|
|
u16 vi14 = 0;
|
|
|
|
float vf28_z = 2049;
|
|
|
|
// ilw.w vi08, 4(vi14) | nop
|
|
u16 vi08_adgif_base = mem.ilw_data(4 + vi14, 3); // is an address, v4/32 unpack.
|
|
// lg::print("------------- VI08 init: {}\n", vars.vi08);
|
|
// ilw.z vi09, 4(vi14) | nop
|
|
u16 vi09_draw_addr_book =
|
|
mem.ilw_data(4 + vi14, 2); // is an input address, v4/8 unpack (seems small?)
|
|
// ilw.y vi03, 3(vi14) | nop
|
|
u16 vi03_vert_addr_book = mem.ilw_data(
|
|
3 + vi14,
|
|
1); // is an input address (v4-8 with strow). a list of addresses for v4-16's with strow
|
|
|
|
// lg::print("-------VI03 init: {}\n", vars.vi03);
|
|
|
|
if (DEBUG) {
|
|
// small, like 9, 54, 66
|
|
level_tools::PrintSettings settings;
|
|
settings.print_tfrag = true;
|
|
lg::debug("{}", frag.print(settings, 0));
|
|
lg::debug("ints: {} {} {}", vi08_adgif_base, vi09_draw_addr_book, vi03_vert_addr_book);
|
|
}
|
|
|
|
// lg::print("vi09: #x{:x} ({})\n", vars.vi09, vars.vi14);
|
|
|
|
// fcset 0x0 | nop
|
|
// iaddi vi07, vi00, -0x1 | nop
|
|
u16 vi07 = -1;
|
|
|
|
// lq.xyzw vf04, 5(vi14) | mulw.xyzw vf16, vf00, vf00
|
|
// inputs.vf04_cam_mat_x = load_vector_data(vars.vi14 + 5);
|
|
Vector4f vf16_scaled_pos_0 = Vector4f(0, 0, 0, 1);
|
|
|
|
// lq.xyzw vf07, 6(vi14) | mulw.xyzw vf17, vf00, vf00
|
|
// inputs.vf07_cam_mat_y = load_vector_data(vars.vi14 + 6);
|
|
Vector4f vf17_scaled_pos_1 = Vector4f(0, 0, 0, 1);
|
|
|
|
// ibne vi00, vi14, L136 | mulw.xyzw vf18, vf00, vf00
|
|
Vector4f vf18_scaled_pos_2 = Vector4f(0, 0, 0, 1);
|
|
// lq.xyzw vf08, 7(vi14) | mulw.xyzw vf19, vf00, vf00
|
|
Vector4f vf19_scaled_pos_3 = Vector4f(0, 0, 0, 1);
|
|
// inputs.vf08_cam_mat_z = load_vector_data(vars.vi14 + 7);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ilwr.x vi02, vi03 | nop
|
|
ASSERT(vi03_vert_addr_book < 328); // should be a buffer 0 addr
|
|
u16 vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 0); // is an addr? v4/16 with strom
|
|
if (DEBUG) {
|
|
lg::debug("vi02-warmup 0: {}", vi02_pre_vtx_ptr);
|
|
}
|
|
|
|
// lq.xyzw vf09, 8(vi14) | nop
|
|
// vars.vf09_cam_trans = load_vector_data(vars.vi14 + 8);
|
|
|
|
// correct addrs
|
|
// iadd vi08, vi08, vi14 | nop
|
|
// iadd vi09, vi09, vi14 | nop
|
|
|
|
// lq.xyw vf28, 0(vi02) | nop
|
|
auto vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
float vf28_x = vf28_load_temp.x();
|
|
float vf28_y = vf28_load_temp.y();
|
|
float vf28_w_addr_of_next_vtx = vf28_load_temp.w(); // addr, of v3-32, with 2, 1
|
|
|
|
if (DEBUG) {
|
|
lg::debug("vf28 load 0: x_f {} y_f {} z_u32 {}", vf28_x, vf28_y,
|
|
float_2_u32(vf28_w_addr_of_next_vtx));
|
|
};
|
|
|
|
// they rotate vi06 to alternate the kick zone buffer.
|
|
// it's loaded with [a, b, a, b]
|
|
// mtir vi06, vf03.x | nop
|
|
// vars.vi06_kick_zone_ptr = m_ptrs.vf03_x;
|
|
u16 vi06_kick_zone_ptr = 0; // moving kick zone to 0.
|
|
|
|
// ilwr.x vi12, vi09 | nop
|
|
u16 vi12_vert_count = mem.ilw_data(vi09_draw_addr_book, 0); // some sort of counter?
|
|
if (DEBUG) {
|
|
lg::debug("vi12: 0x{:x}", vi12_vert_count);
|
|
}
|
|
|
|
// ilwr.z vi13, vi09 | nop
|
|
u16 vi13_adgifs = mem.ilw_data(vi09_draw_addr_book, 2);
|
|
if (DEBUG) {
|
|
lg::debug("vi13: 0x{:x}", vi13_adgifs);
|
|
}
|
|
|
|
// mtir vi04, vf28.w | subz.xyz vf24, vf28, vf02
|
|
u16 vi04_vtx_ptr =
|
|
float_2_u32(vf28_w_addr_of_next_vtx); // addr, of v3-32, with 2, 1 VERTEX POINTER
|
|
Vector4f vf24_stq_0;
|
|
vf24_stq_0.x() = vf28_x - 2048.f;
|
|
vf24_stq_0.y() = vf28_y - 2048.f;
|
|
vf24_stq_0.z() = vf28_z - 2048.f;
|
|
vf24_stq_0.w() = 0; // set later.
|
|
|
|
// iaddiu vi11, vi00, 0x4000 | nop
|
|
u16 vi11 = 0x4000;
|
|
|
|
// iaddiu vi11, vi11, 0x4000 | nop
|
|
vi11 += 0x4000;
|
|
|
|
// ilwr.y vi02, vi03 | nop
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 1);
|
|
if (DEBUG) {
|
|
lg::debug("vi02-warmup 1: {}", vi02_pre_vtx_ptr);
|
|
}
|
|
|
|
// vertex load
|
|
// lq.xyzw vf12, 0(vi04) | nop
|
|
// integer vertex position
|
|
Vector4f vf12_vtx_pos_0 = mem.load_vector_data(vi04_vtx_ptr);
|
|
|
|
// lq.xyzw vf20, 1(vi04) | nop
|
|
// ??? something with the vertex.
|
|
// Vector4f vf20_vtx_rgba_0 = mem.load_vector_data(vi04_vtx_ptr + 1);
|
|
u16 vf20_vtx_rgba_0 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
|
|
// iaddiu vi12, vi12, 0x80 | nop
|
|
vi12_vert_count += 0x80; // ??
|
|
|
|
// iadd vi13, vi13, vi08 | nop
|
|
vi13_adgifs += vi08_adgif_base;
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf12, vf12
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w(); // addr, of v3-32, with 2, 1
|
|
vf12_vtx_pos_0 = itof0(vf12_vtx_pos_0);
|
|
|
|
if (DEBUG) {
|
|
lg::debug("vf28 load 0: x_f {} y_f {} w_u32 {}", vf28_x, vf28_y,
|
|
float_2_u32(vf28_w_addr_of_next_vtx));
|
|
lg::debug("vtx w0: {}", vf12_vtx_pos_0.to_string_aligned());
|
|
};
|
|
|
|
// mfir.w vf24, vi06 | nop
|
|
// remember the start of the kick zone, I guess?
|
|
u16 vf24_w_u16 = vi06_kick_zone_ptr;
|
|
|
|
// lqi.xyzw vf29, vi13 | nop
|
|
auto vf29_adgif0 = mem.load_vector_data(vi13_adgifs);
|
|
vi13_adgifs++;
|
|
|
|
// lqi.xyzw vf30, vi13 | nop
|
|
auto vf30_adgif1 = mem.load_vector_data(vi13_adgifs);
|
|
vi13_adgifs++;
|
|
|
|
// lqi.xyzw vf31, vi13 | nop
|
|
auto vf31_adgif2 = mem.load_vector_data(vi13_adgifs);
|
|
vi13_adgifs++;
|
|
|
|
// sqi.xyzw vf05, vi06 | subz.xyz vf25, vf28, vf02
|
|
// store_gif_kick_zone(vars.vi06_kick_zone_ptr, m_tfrag_data.ad_gif); sets the adgif header.
|
|
vi06_kick_zone_ptr++;
|
|
Vector4f vf25_stq_1;
|
|
vf25_stq_1.x() = vf28_x - 2048.f;
|
|
vf25_stq_1.y() = vf28_y - 2048.f;
|
|
vf25_stq_1.z() = vf28_z - 2048.f;
|
|
vf25_stq_1.w() = 0; // set later.
|
|
|
|
// sqi.xyzw vf29, vi06 | mulaw.xyzw ACC, vf09, vf00
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf29);
|
|
memcpy(current_draw.adgif_data, vf29_adgif0.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// mtir vi04, vf28.w | nop
|
|
vi04_vtx_ptr = float_2_u32(vf28_w_addr_of_next_vtx);
|
|
|
|
// sqi.xyzw vf30, vi06 | maddax.xyzw ACC, vf04, vf12
|
|
memcpy(current_draw.adgif_data + 16, vf30_adgif1.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf31, vi06 | nop
|
|
memcpy(current_draw.adgif_data + 32, vf31_adgif2.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// ilwr.z vi02, vi03 | nop
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 2);
|
|
if (DEBUG) {
|
|
lg::debug("pre-vtx-vi02-warmup 2: {}", vi02_pre_vtx_ptr);
|
|
}
|
|
|
|
// lq.xyzw vf13, 0(vi04) | madday.xyzw ACC, vf07, vf12
|
|
Vector4f vf13_vtx_pos_1 = mem.load_vector_data(vi04_vtx_ptr);
|
|
// acc += in.vf07_cam_mat_y * vars.vf12_root_pos_0.y();
|
|
|
|
// lq.xyzw vf21, 1(vi04) | maddz.xyzw vf12, vf08, vf12
|
|
// Vector4f vf21_vtx_unk_1 = mem.load_vector_data(vi04_vtx_ptr + 1);
|
|
u16 vf21_vtx_rgba_1 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
|
|
// vars.vf12_root_pos_0 = acc + in.vf08_cam_mat_z * vars.vf12_root_pos_0.z();
|
|
|
|
// lqi.xyzw vf29, vi13 | nop
|
|
vf29_adgif0 = mem.load_vector_data(vi13_adgifs);
|
|
vi13_adgifs++;
|
|
|
|
// lqi.xyzw vf30, vi13 | nop
|
|
vf30_adgif1 = mem.load_vector_data(vi13_adgifs);
|
|
vi13_adgifs++;
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf13, vf13
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w(); // addr, of v3-32, with 2, 1
|
|
vf13_vtx_pos_1 = itof0(vf13_vtx_pos_1);
|
|
|
|
// div Q, vf01.x, vf12.w | mul.xyzw vf16, vf12, vf11
|
|
// float q = m_tfrag_data.fog.x() / vars.vf12_root_pos_0.w();
|
|
// vars.vf16_scaled_pos_0 = vars.vf12_root_pos_0.elementwise_multiply(m_tfrag_data.hmge_scale);
|
|
|
|
// sqi.xyzw vf29, vi06 | nop
|
|
memcpy(current_draw.adgif_data + (16 * 3), vf29_adgif0.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// sqi.xyzw vf30, vi06 | nop
|
|
memcpy(current_draw.adgif_data + (16 * 4), vf30_adgif1.data(), 16);
|
|
vi06_kick_zone_ptr++;
|
|
|
|
// iadd vi01, vi12, vi12 | subz.xyz vf26, vf28, vf02
|
|
u16 vi01 = vi12_vert_count + vi12_vert_count;
|
|
Vector4f vf26_stq_2;
|
|
vf26_stq_2.x() = vf28_x - 2048.f;
|
|
vf26_stq_2.y() = vf28_y - 2048.f;
|
|
vf26_stq_2.z() = vf28_z - 2048.f;
|
|
vf26_stq_2.w() = 0; // set later.
|
|
|
|
// iadd vi01, vi01, vi12 | mulaw.xyzw ACC, vf09, vf00
|
|
vi01 += vi12_vert_count; // vi01 is now vi12 * 3, so qwc for gs vert data (3 regs/vert)
|
|
|
|
// mtir vi04, vf28.w | nop
|
|
vi04_vtx_ptr = float_2_u32(vf28_w_addr_of_next_vtx);
|
|
|
|
// iadd vi05, vi06, vi01 | maddax.xyzw ACC, vf04, vf13
|
|
u16 vi05_end_of_vert_kick_data = vi06_kick_zone_ptr + vi01;
|
|
|
|
// ior vi10, vi06, vi00 | mul.xyz vf12, vf12, Q
|
|
u16 vi10_start_of_vert_kick_data = vi06_kick_zone_ptr;
|
|
// vars.vf12_root_pos_0.x() *= q;
|
|
// vars.vf12_root_pos_0.y() *= q;
|
|
// vars.vf12_root_pos_0.z() *= q;
|
|
|
|
// ilwr.w vi02, vi03 | mul.xyz vf24, vf24, Q
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 3);
|
|
if (DEBUG) {
|
|
lg::debug("pre-vtx-vi02-warmup 3: {}", vi02_pre_vtx_ptr);
|
|
}
|
|
|
|
// lq.xyzw vf14, 0(vi04) | madday.xyzw ACC, vf07, vf13
|
|
Vector4f vf14_vtx_pos_2 = mem.load_vector_data(vi04_vtx_ptr);
|
|
|
|
// lq.xyzw vf22, 1(vi04) | maddz.xyzw vf13, vf08, vf13
|
|
// Vector4f vf22_vtx_rgba_2 = mem.load_vector_data(vi04_vtx_ptr + 1);
|
|
u16 vf22_vtx_rgba_2 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
|
|
// sqi.xyzw vf06, vi06 | add.xyzw vf12, vf12, vf10
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf12_root_pos_0 += m_tfrag_data.hvdf_offset; not precomputed
|
|
|
|
// isw.x vi12, -1(vi06) | nop
|
|
// store_u32_kick_zone(vars.vi12, vars.vi06_kick_zone_ptr - 1, 0);
|
|
// stores dvert count
|
|
current_draw.dvert = vi12_vert_count;
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf14, vf14
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w(); // addr, of v3-32, with 2, 1
|
|
vf14_vtx_pos_2 = itof0(vf14_vtx_pos_2);
|
|
|
|
// div Q, vf01.x, vf13.w | mul.xyzw vf17, vf13, vf11
|
|
// m_q = m_tfrag_data.fog.x() / vars.vf13_root_pos_1.w();
|
|
// vars.vf17_scaled_pos_1 = vars.vf13_root_pos_1.elementwise_multiply(m_tfrag_data.hmge_scale);
|
|
|
|
// iaddi vi09, vi09, 0x1 | miniz.w vf12, vf12, vf01
|
|
vi09_draw_addr_book++;
|
|
|
|
// ilwr.x vi12, vi09 | clipw.xyz vf16, vf16
|
|
vi12_vert_count = mem.ilw_data(vi09_draw_addr_book, 0);
|
|
// m_clip_and_3ffff = clip_xyz_plus_minus(vars.vf16_scaled_pos_0);
|
|
|
|
Vector4f vf27_vtx_stq_3;
|
|
u16 vf23_vtx_rgba_3;
|
|
Vector4f vf15_vtx_pos_3;
|
|
|
|
while (true) {
|
|
//////////// L128
|
|
|
|
// Part 0 for X
|
|
// iaddi vi03, vi03, 0x1 | subz.xyz vf27, vf28, vf02
|
|
vi03_vert_addr_book++;
|
|
vf27_vtx_stq_3.x() = vf28_x - 2048.f;
|
|
vf27_vtx_stq_3.y() = vf28_y - 2048.f;
|
|
vf27_vtx_stq_3.z() = vf28_z - 2048.f;
|
|
|
|
// iaddi vi07, vi07, 0x1 | mulaw.xyzw ACC, vf09, vf00
|
|
vi07++; // ?
|
|
// m_acc = vars.vf09_cam_trans;
|
|
|
|
// mtir vi04, vf28.w | maxy.w vf12, vf12, vf01
|
|
vi04_vtx_ptr = float_2_u32(vf28_w_addr_of_next_vtx);
|
|
// vars.vf12_root_pos_0.w() = std::max(vars.vf12_root_pos_0.w(), m_tfrag_data.fog.y());
|
|
|
|
// fcand vi01, 0x3ffff | maddax.xyzw ACC, vf04, vf14
|
|
// m_acc += in.vf04_cam_mat_x * vars.vf14_loop_pos_0.x();
|
|
// fcand already calculated
|
|
|
|
// ibeq vi00, vi01, L129 | mul.xyz vf13, vf13, Q
|
|
// branch made after next instr
|
|
// vars.vf13_root_pos_1.x() *= m_q;
|
|
// vars.vf13_root_pos_1.y() *= m_q;
|
|
// vars.vf13_root_pos_1.z() *= m_q;
|
|
|
|
// ilwr.x vi02, vi03 | mul.xyz vf25, vf25, Q
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 0);
|
|
|
|
// skipped if we take the branch
|
|
// nop | addw.w vf12, vf12, vf01
|
|
// if (m_clip_and_3ffff) {
|
|
// vars.vf12_root_pos_0.w() += m_tfrag_data.fog.w();
|
|
// }
|
|
|
|
////////////////// L129
|
|
|
|
// Part 1 for X
|
|
// lq.xyzw vf15, 0(vi04) | madday.xyzw ACC, vf07, vf14
|
|
vf15_vtx_pos_3 = mem.load_vector_data(vi04_vtx_ptr);
|
|
// m_acc += in.vf07_cam_mat_y * vars.vf14_loop_pos_0.y();
|
|
|
|
// lq.xyzw vf23, 1(vi04) | maddz.xyzw vf14, vf08, vf14
|
|
vf23_vtx_rgba_3 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
// vars.vf14_loop_pos_0 = m_acc + in.vf08_cam_mat_z * vars.vf14_loop_pos_0.z();
|
|
|
|
// sqi.xyz vf24, vi06 | add.xyzw vf13, vf13, vf10
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf24);
|
|
vertex_pipeline[0].stq[0] = vf24_stq_0.x();
|
|
vertex_pipeline[0].stq[1] = vf24_stq_0.y();
|
|
vertex_pipeline[0].stq[2] = vf24_stq_0.z();
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf13_root_pos_1 += m_tfrag_data.hvdf_offset;
|
|
|
|
// sqi.xyzw vf20, vi06 | ftoi4.xyzw vf12, vf12
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf20);
|
|
vertex_pipeline[0].rgba = vf20_vtx_rgba_0;
|
|
// leaving out ftoi4
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf12_root_pos_0 = ftoi4(vars.vf12_root_pos_0);
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf15, vf15
|
|
if (vi02_pre_vtx_ptr < 328) { // HACK added
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
if (float_2_u32(vf28_load_temp.w()) < 328) {
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w();
|
|
}
|
|
}
|
|
vf15_vtx_pos_3 = itof0(vf15_vtx_pos_3);
|
|
|
|
// div Q, vf01.x, vf14.w | mul.xyzw vf18, vf14, vf11
|
|
// m_q = m_tfrag_data.fog.x() / vars.vf14_loop_pos_0.w();
|
|
// vars.vf18_scaled_pos_2 = vars.vf14_loop_pos_0.elementwise_multiply(m_tfrag_data.hmge_scale);
|
|
|
|
// ibeq vi05, vi06, L133 | miniz.w vf13, vf13, vf01
|
|
bool take_branch = (vi05_end_of_vert_kick_data == vi06_kick_zone_ptr);
|
|
// lg::print("L129 prog: {} {}\n", vars.vi05, vars.vi06_kick_zone_ptr);
|
|
// vars.vf13_root_pos_1.w() = std::min(vars.vf13_root_pos_1.w(), m_tfrag_data.fog.z());
|
|
|
|
// sqi.xyzw vf12, vi06 | clipw.xyz vf17, vf17
|
|
vertex_pipeline[0].pre_cam_trans_pos = vf12_vtx_pos_0; // todo move down?
|
|
// lg::print("C: vf12 store: {}\n", int_vec_debug(vars.vf12_root_pos_0));
|
|
current_draw.verts.push_back(vertex_pipeline[0]);
|
|
vi06_kick_zone_ptr++;
|
|
// m_clip_and_3ffff = clip_xyz_plus_minus(vars.vf17_scaled_pos_1);
|
|
|
|
if (take_branch) {
|
|
// kick zone is full, time for another kick
|
|
if (emulate_kick_subroutine<DEBUG>(mem, current_draw, all_draws, vi05_end_of_vert_kick_data,
|
|
vi06_kick_zone_ptr, vi07, vi08_adgif_base,
|
|
vi09_draw_addr_book, vi10_start_of_vert_kick_data,
|
|
vi12_vert_count, vi13_adgifs, vf24_w_u16)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
//////////////////////// L6A1
|
|
|
|
// part 1 for 1
|
|
// nop | subz.xyz vf24, vf28, vf02
|
|
vf24_stq_0.x() = vf28_x - 2048.f;
|
|
vf24_stq_0.y() = vf28_y - 2048.f;
|
|
vf24_stq_0.z() = vf28_z - 2048.f;
|
|
|
|
// iaddi vi07, vi07, 0x1 | mulaw.xyzw ACC, vf09, vf00
|
|
vi07++;
|
|
// m_acc = vars.vf09_cam_trans;
|
|
|
|
// mtir vi04, vf28.w | maxy.w vf13, vf13, vf01
|
|
vi04_vtx_ptr = float_2_u32(vf28_w_addr_of_next_vtx);
|
|
// vars.vf13_root_pos_1.w() = std::max(vars.vf13_root_pos_1.w(), m_tfrag_data.fog.y());
|
|
|
|
// fcand vi01, 0x3ffff | maddax.xyzw ACC, vf04, vf15
|
|
// m_acc += in.vf04_cam_mat_x * vars.vf15_loop_pos_1.x();
|
|
// fcand already calculated
|
|
|
|
// ibeq vi00, vi01, L130 | mul.xyz vf14, vf14, Q
|
|
// vars.vf14_loop_pos_0.x() *= m_q;
|
|
// vars.vf14_loop_pos_0.y() *= m_q;
|
|
// vars.vf14_loop_pos_0.z() *= m_q;
|
|
|
|
// ilwr.y vi02, vi03 | mul.xyz vf26, vf26, Q
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 1);
|
|
// vars.vf26.x() *= m_q;
|
|
// vars.vf26.y() *= m_q;
|
|
// vars.vf26.z() *= m_q;
|
|
|
|
// nop | addw.w vf13, vf13, vf0
|
|
// if (m_clip_and_3ffff) {
|
|
// vars.vf13_root_pos_1.w() += m_tfrag_data.fog.w();
|
|
// }
|
|
|
|
//////////////////////////////// L130
|
|
|
|
// lq.xyzw vf12, 0(vi04) | madday.xyzw ACC, vf07, vf15
|
|
vf12_vtx_pos_0 = mem.load_vector_data(vi04_vtx_ptr);
|
|
// m_acc += in.vf07_cam_mat_y * vars.vf15_loop_pos_1.y();
|
|
|
|
// lq.xyzw vf20, 1(vi04) | maddz.xyzw vf15, vf08, vf15
|
|
vf20_vtx_rgba_0 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
// vars.vf15_loop_pos_1 = m_acc + in.vf08_cam_mat_z * vars.vf15_loop_pos_1.z();
|
|
|
|
// sqi.xyzw vf25, vi06 | add.xyzw vf14, vf14, vf10
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf25);
|
|
vertex_pipeline[1].stq[0] = vf25_stq_1[0];
|
|
vertex_pipeline[1].stq[1] = vf25_stq_1[1];
|
|
vertex_pipeline[1].stq[2] = vf25_stq_1[2];
|
|
// lg::print("A: vf25 store: {}\n", vars.vf25.to_string_aligned());
|
|
vi06_kick_zone_ptr++;
|
|
// vf14 += m_tfrag_data.hvdf_offset;
|
|
|
|
// sqi.xyzw vf21, vi06 | ftoi4.xyzw vf13, vf13
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf21);
|
|
vertex_pipeline[1].rgba = vf21_vtx_rgba_1;
|
|
// lg::print("B: vf21 store: {}\n", int_vec_debug(vars.vf21));
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf13_root_pos_1 = ftoi4(vars.vf13_root_pos_1);
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf12, vf12
|
|
if (vi02_pre_vtx_ptr < 328) { // HACK added
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
if (float_2_u32(vf28_load_temp.w()) < 328) {
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w();
|
|
}
|
|
}
|
|
|
|
// vars.vf12_root_pos_0 = itof0(vars.vf12_root_pos_0);
|
|
vf12_vtx_pos_0 = itof0(vf12_vtx_pos_0);
|
|
|
|
// div Q, vf01.x, vf15.w | mul.xyzw vf19, vf15, vf11
|
|
// m_q = m_tfrag_data.fog.x() / vars.vf15_loop_pos_1.w();
|
|
// vars.vf19_scaled_pos_3 = vars.vf15_loop_pos_1.elementwise_multiply(m_tfrag_data.hmge_scale);
|
|
|
|
// ibeq vi05, vi06, L134 | miniz.w vf14, vf14, vf01
|
|
take_branch = (vi05_end_of_vert_kick_data == vi06_kick_zone_ptr);
|
|
// vars.vf14_loop_pos_0.w() = std::min(vars.vf14_loop_pos_0.w(), m_tfrag_data.fog.z());
|
|
|
|
// sqi.xyzw vf13, vi06 | clipw.xyz vf18, vf18
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf13_root_pos_1);
|
|
vertex_pipeline[1].pre_cam_trans_pos = vf13_vtx_pos_1;
|
|
current_draw.verts.push_back(vertex_pipeline[1]);
|
|
// lg::print("C: vf13 store: {}\n", int_vec_debug(vars.vf13_root_pos_1));
|
|
vi06_kick_zone_ptr++;
|
|
// m_clip_and_3ffff = clip_xyz_plus_minus(vars.vf18_scaled_pos_2);
|
|
|
|
if (take_branch) {
|
|
// kick zone is full, time for another kick
|
|
if (emulate_kick_subroutine<DEBUG>(mem, current_draw, all_draws, vi05_end_of_vert_kick_data,
|
|
vi06_kick_zone_ptr, vi07, vi08_adgif_base,
|
|
vi09_draw_addr_book, vi10_start_of_vert_kick_data,
|
|
vi12_vert_count, vi13_adgifs, vf24_w_u16)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/////////////////// L6B0
|
|
// nop | subz.xyz vf25, vf28, vf02
|
|
vf25_stq_1.x() = vf28_x - 2048.f;
|
|
vf25_stq_1.y() = vf28_y - 2048.f;
|
|
vf25_stq_1.z() = vf28_z - 2048.f;
|
|
|
|
// iaddi vi07, vi07, 0x1 | mulaw.xyzw ACC, vf09, vf00
|
|
vi07++;
|
|
// m_acc = vars.vf09_cam_trans;
|
|
|
|
// mtir vi04, vf28.w | maxy.w vf14, vf14, vf01
|
|
vi04_vtx_ptr = float_2_u32(vf28_w_addr_of_next_vtx);
|
|
// vars.vf14_loop_pos_0.w() = std::max(vars.vf14_loop_pos_0.w(), m_tfrag_data.fog.y());
|
|
|
|
// fcand vi01, 0x3ffff | maddax.xyzw ACC, vf04, vf12
|
|
// m_acc += in.vf04_cam_mat_x * vars.vf12_root_pos_0.x();
|
|
// fcand already calculated
|
|
|
|
// ibeq vi00, vi01, L131 | mul.xyz vf15, vf15, Q
|
|
// vars.vf15_loop_pos_1.x() *= m_q;
|
|
// vars.vf15_loop_pos_1.y() *= m_q;
|
|
// vars.vf15_loop_pos_1.z() *= m_q;
|
|
|
|
// ilwr.z vi02, vi03 | mul.xyz vf27, vf27, Q
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 2);
|
|
// vars.vf27.x() *= m_q;
|
|
// vars.vf27.y() *= m_q;
|
|
// vars.vf27.z() *= m_q;
|
|
|
|
// nop | addw.w vf14, vf14, vf01
|
|
// if (m_clip_and_3ffff) {
|
|
// vars.vf14_loop_pos_0.w() += m_tfrag_data.fog.w();
|
|
// }
|
|
|
|
///////////////////// L131
|
|
// lq.xyzw vf13, 0(vi04) | madday.xyzw ACC, vf07, vf12
|
|
vf13_vtx_pos_1 = mem.load_vector_data(vi04_vtx_ptr);
|
|
// m_acc += in.vf07_cam_mat_y * vars.vf12_root_pos_0.y();
|
|
|
|
// lq.xyzw vf21, 1(vi04) | maddz.xyzw vf12, vf08, vf12
|
|
vf21_vtx_rgba_1 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
// lg::print("vf21 load from: {}\n", vars.vi04 + 1);
|
|
// vars.vf12_root_pos_0 = m_acc + in.vf08_cam_mat_z * vars.vf12_root_pos_0.z();
|
|
|
|
// sqi.xyzw vf26, vi06 | add.xyzw vf15, vf15, vf10
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf26);
|
|
vertex_pipeline[2].stq[0] = vf26_stq_2[0];
|
|
vertex_pipeline[2].stq[1] = vf26_stq_2[1];
|
|
vertex_pipeline[2].stq[2] = vf26_stq_2[2];
|
|
// lg::print("A: vf26 store: {}\n", vars.vf26.to_string_aligned());
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf15_loop_pos_1 += m_tfrag_data.hvdf_offset;
|
|
|
|
// sqi.xyzw vf22, vi06 | ftoi4.xyzw vf14, vf14
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf22);
|
|
vertex_pipeline[2].rgba = vf22_vtx_rgba_2;
|
|
// lg::print("B: vf22 store: {}\n", int_vec_debug(vars.vf22));
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf14_loop_pos_0 = ftoi4(vars.vf14_loop_pos_0);
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf13, vf13
|
|
if (vi02_pre_vtx_ptr < 328) { // HACK added
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
if (float_2_u32(vf28_load_temp.w()) < 328) {
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w();
|
|
}
|
|
}
|
|
vf13_vtx_pos_1 = itof0(vf13_vtx_pos_1);
|
|
|
|
// div Q, vf01.x, vf12.w | mul.xyzw vf16, vf12, vf11
|
|
// m_q = m_tfrag_data.fog.x() / vars.vf12_root_pos_0.w();
|
|
// vars.vf16_scaled_pos_0 = vars.vf12_root_pos_0.elementwise_multiply(m_tfrag_data.hmge_scale);
|
|
|
|
// ibeq vi05, vi06, L135 | miniz.w vf15, vf15, vf01
|
|
take_branch = (vi05_end_of_vert_kick_data == vi06_kick_zone_ptr);
|
|
// vars.vf15_loop_pos_1.w() = std::min(vars.vf15_loop_pos_1.w(), m_tfrag_data.fog.z());
|
|
|
|
// sqi.xyzw vf14, vi06 | clipw.xyz vf19, vf19
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf14_loop_pos_0);
|
|
vertex_pipeline[2].pre_cam_trans_pos = vf14_vtx_pos_2;
|
|
current_draw.verts.push_back(vertex_pipeline[2]);
|
|
|
|
// lg::print("C: vf14 store: {}\n", int_vec_debug(vars.vf14_loop_pos_0));
|
|
vi06_kick_zone_ptr++;
|
|
// m_clip_and_3ffff = clip_xyz_plus_minus(vars.vf19_scaled_pos_3);
|
|
|
|
if (take_branch) {
|
|
// kick zone is full, time for another kick
|
|
if (emulate_kick_subroutine<DEBUG>(mem, current_draw, all_draws, vi05_end_of_vert_kick_data,
|
|
vi06_kick_zone_ptr, vi07, vi08_adgif_base,
|
|
vi09_draw_addr_book, vi10_start_of_vert_kick_data,
|
|
vi12_vert_count, vi13_adgifs, vf24_w_u16)) {
|
|
goto end;
|
|
};
|
|
}
|
|
|
|
///////////////////// 6bf
|
|
// nop | subz.xyz vf26, vf28, vf02
|
|
vf26_stq_2.x() = vf28_x - 2048.f;
|
|
vf26_stq_2.y() = vf28_y - 2048.f;
|
|
vf26_stq_2.z() = vf28_z - 2048.f;
|
|
|
|
// iaddi vi07, vi07, 0x1 | mulaw.xyzw ACC, vf09, vf00
|
|
vi07++;
|
|
// m_acc = vars.vf09_cam_trans;
|
|
|
|
// mtir vi04, vf28.w | maxy.w vf15, vf15, vf01
|
|
vi04_vtx_ptr = float_2_u32(vf28_w_addr_of_next_vtx); // L131 previously
|
|
// ASSERT(vars.vi04 != 0xbeef); // hit
|
|
// vars.vf15_loop_pos_1.w() = std::max(vars.vf15_loop_pos_1.w(), m_tfrag_data.fog.y());
|
|
|
|
// fcand vi01, 0x3ffff | maddax.xyzw ACC, vf04, vf13
|
|
// m_acc += in.vf04_cam_mat_x * vars.vf13_root_pos_1.x();
|
|
|
|
// ibeq vi00, vi01, L132 | mul.xyz vf12, vf12, Q
|
|
// vars.vf12_root_pos_0.x() *= m_q;
|
|
// vars.vf12_root_pos_0.y() *= m_q;
|
|
// vars.vf12_root_pos_0.z() *= m_q;
|
|
|
|
// ilwr.w vi02, vi03 | mul.xyz vf24, vf24, Q
|
|
vi02_pre_vtx_ptr = mem.ilw_data(vi03_vert_addr_book, 3);
|
|
// vars.vf24.x() *= m_q;
|
|
// vars.vf24.y() *= m_q;
|
|
// vars.vf24.z() *= m_q;
|
|
|
|
// nop | addw.w vf15, vf15, vf01
|
|
// if (m_clip_and_3ffff) {
|
|
// vars.vf15_loop_pos_1.w() += m_tfrag_data.fog.w();
|
|
// }
|
|
//
|
|
/////////////////////////////// L132
|
|
// lq.xyzw vf14, 0(vi04) | madday.xyzw ACC, vf07, vf13
|
|
// vars.vf14_loop_pos_0 = load_vector_data(vars.vi04); // bad here, in L0x6BF_PART0_W prev
|
|
vf14_vtx_pos_2 = mem.load_vector_data(vi04_vtx_ptr);
|
|
// m_acc += in.vf07_cam_mat_y * vars.vf13_root_pos_1.y();
|
|
|
|
// lq.xyzw vf22, 1(vi04) | maddz.xyzw vf13, vf08, vf13
|
|
vf22_vtx_rgba_2 = color_indices.load_color_idx(vi04_vtx_ptr + 1);
|
|
// vars.vf13_root_pos_1 = m_acc + in.vf08_cam_mat_z * vars.vf13_root_pos_1.z();
|
|
|
|
// sqi.xyzw vf27, vi06 | add.xyzw vf12, vf12, vf10
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf27);
|
|
vertex_pipeline[3].stq[0] = vf27_vtx_stq_3[0];
|
|
vertex_pipeline[3].stq[1] = vf27_vtx_stq_3[1];
|
|
vertex_pipeline[3].stq[2] = vf27_vtx_stq_3[2];
|
|
// lg::print("A: vf27 store: {}\n", vars.vf27.to_string_aligned());
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf12_root_pos_0 += m_tfrag_data.hvdf_offset;
|
|
|
|
// sqi.xyzw vf23, vi06 | ftoi4.xyzw vf15, vf15
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf23);
|
|
vertex_pipeline[3].rgba = vf23_vtx_rgba_3;
|
|
|
|
// lg::print("B: vf23 store: {}\n", int_vec_debug(vars.vf23));
|
|
vi06_kick_zone_ptr++;
|
|
// vars.vf15_loop_pos_1 = ftoi4(vars.vf15_loop_pos_1);
|
|
|
|
// lq.xyw vf28, 0(vi02) | itof0.xyzw vf14, vf14
|
|
if (vi02_pre_vtx_ptr < 328) { // HACK added
|
|
vf28_load_temp = mem.load_vector_data(vi02_pre_vtx_ptr);
|
|
vf28_x = vf28_load_temp.x();
|
|
vf28_y = vf28_load_temp.y();
|
|
if (float_2_u32(vf28_load_temp.w()) < 328) {
|
|
vf28_w_addr_of_next_vtx = vf28_load_temp.w();
|
|
}
|
|
}
|
|
vf14_vtx_pos_2 = itof0(vf14_vtx_pos_2);
|
|
|
|
// div Q, vf01.x, vf13.w | mul.xyzw vf17, vf13, vf11
|
|
// m_q = m_tfrag_data.fog.x() / vars.vf13_root_pos_1.w();
|
|
// vars.vf17_scaled_pos_1 = vars.vf13_root_pos_1.elementwise_multiply(m_tfrag_data.hmge_scale);
|
|
|
|
// ibne vi05, vi06, L128 | miniz.w vf12, vf12, vf01
|
|
take_branch = (vi05_end_of_vert_kick_data != vi06_kick_zone_ptr);
|
|
// lg::print("kick check: {} {}\n", vars.vi05, vars.vi06_kick_zone_ptr);
|
|
// vars.vf12_root_pos_0.w() = std::min(vars.vf12_root_pos_0.w(), m_tfrag_data.fog.z());
|
|
|
|
// sqi.xyzw vf15, vi06 | clipw.xyz vf16, vf16
|
|
// store_vector_kick_zone(vars.vi06_kick_zone_ptr, vars.vf15_loop_pos_1);
|
|
vertex_pipeline[3].pre_cam_trans_pos = vf15_vtx_pos_3;
|
|
current_draw.verts.push_back(vertex_pipeline[3]);
|
|
vi06_kick_zone_ptr++;
|
|
// lg::print("C: vf15 store: {}\n", int_vec_debug(vars.vf15_loop_pos_1));
|
|
// m_clip_and_3ffff = clip_xyz_plus_minus(vars.vf16_scaled_pos_0);
|
|
|
|
if (!take_branch) {
|
|
if (emulate_kick_subroutine<DEBUG>(mem, current_draw, all_draws, vi05_end_of_vert_kick_data,
|
|
vi06_kick_zone_ptr, vi07, vi08_adgif_base,
|
|
vi09_draw_addr_book, vi10_start_of_vert_kick_data,
|
|
vi12_vert_count, vi13_adgifs, vf24_w_u16)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
// ASSERT(false);
|
|
}
|
|
|
|
end:
|
|
int total_dvert = 0;
|
|
for (auto& draw : all_draws) {
|
|
total_dvert += draw.verts.size();
|
|
draw.tfrag_id = frag.id;
|
|
}
|
|
|
|
return all_draws;
|
|
}
|
|
|
|
std::string debug_dump_to_obj(const std::vector<TFragDraw>& draws) {
|
|
std::vector<Vector4f> verts;
|
|
std::vector<math::Vector<float, 2>> tcs;
|
|
std::vector<math::Vector<int, 3>> faces;
|
|
|
|
for (auto& draw : draws) {
|
|
// add verts...
|
|
ASSERT(draw.verts.size() >= 3);
|
|
|
|
int vert_idx = 0;
|
|
|
|
int vtx_idx_queue[3];
|
|
|
|
int q_idx = 0;
|
|
int startup = 0;
|
|
while (vert_idx < (int)draw.verts.size()) {
|
|
verts.push_back(draw.verts.at(vert_idx).pre_cam_trans_pos / 65536);
|
|
tcs.push_back(
|
|
math::Vector<float, 2>{draw.verts.at(vert_idx).stq.x(), draw.verts.at(vert_idx).stq.y()});
|
|
vert_idx++;
|
|
vtx_idx_queue[q_idx++] = verts.size();
|
|
|
|
// wrap the index
|
|
if (q_idx == 3) {
|
|
q_idx = 0;
|
|
}
|
|
|
|
// bump the startup
|
|
if (startup < 3) {
|
|
startup++;
|
|
}
|
|
|
|
if (startup >= 3) {
|
|
faces.push_back(math::Vector<int, 3>{vtx_idx_queue[0], vtx_idx_queue[1], vtx_idx_queue[2]});
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string result;
|
|
for (auto& vert : verts) {
|
|
result += fmt::format("v {} {} {}\n", vert.x(), vert.y(), vert.z());
|
|
}
|
|
for (auto& tc : tcs) {
|
|
result += fmt::format("vt {} {}\n", tc.x(), tc.y());
|
|
}
|
|
for (auto& face : faces) {
|
|
result += fmt::format("f {}/{} {}/{} {}/{}\n", face.x(), face.x(), face.y(), face.y(), face.z(),
|
|
face.z());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void update_mode_from_alpha1(u64 val, DrawMode& mode) {
|
|
GsAlpha reg(val);
|
|
if (reg.a_mode() == GsAlpha::BlendMode::SOURCE && reg.b_mode() == GsAlpha::BlendMode::DEST &&
|
|
reg.c_mode() == GsAlpha::BlendMode::SOURCE && reg.d_mode() == GsAlpha::BlendMode::DEST) {
|
|
// (Cs - Cd) * As + Cd
|
|
// Cs * As + (1 - As) * Cd
|
|
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
|
|
|
|
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
|
|
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
|
|
reg.c_mode() == GsAlpha::BlendMode::SOURCE &&
|
|
reg.d_mode() == GsAlpha::BlendMode::DEST) {
|
|
// (Cs - 0) * As + Cd
|
|
// Cs * As + (1) * CD
|
|
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_SRC_DST);
|
|
} else if (reg.a_mode() == GsAlpha::BlendMode::SOURCE &&
|
|
reg.b_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
|
|
reg.c_mode() == GsAlpha::BlendMode::ZERO_OR_FIXED &&
|
|
reg.d_mode() == GsAlpha::BlendMode::DEST) {
|
|
ASSERT(reg.fix() == 128);
|
|
// Cv = (Cs - 0) * FIX + Cd
|
|
// if fix = 128, it works out to 1.0
|
|
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_FIX_DST);
|
|
// src plus dest
|
|
} else {
|
|
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
|
|
ASSERT_MSG(false, fmt::format("unsupported blend: a {} b {} c {} d {}", (int)reg.a_mode(),
|
|
(int)reg.b_mode(), (int)reg.c_mode(), (int)reg.d_mode()));
|
|
}
|
|
}
|
|
|
|
void update_mode_from_test1(u64 val, DrawMode& mode) {
|
|
// ate, atst, aref, afail, date, datm, zte, ztest
|
|
GsTest test(val);
|
|
|
|
// ATE
|
|
mode.set_at(test.alpha_test_enable());
|
|
|
|
// ATST
|
|
switch (test.alpha_test()) {
|
|
case GsTest::AlphaTest::ALWAYS:
|
|
mode.set_alpha_test(DrawMode::AlphaTest::ALWAYS);
|
|
break;
|
|
case GsTest::AlphaTest::GEQUAL:
|
|
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
|
|
break;
|
|
case GsTest::AlphaTest::NEVER:
|
|
mode.set_alpha_test(DrawMode::AlphaTest::NEVER);
|
|
break;
|
|
default:
|
|
mode.set_alpha_test(DrawMode::AlphaTest::ALWAYS);
|
|
ASSERT_MSG(false, fmt::format("Alpha test: {} not supported", (int)test.alpha_test()));
|
|
}
|
|
|
|
// AREF
|
|
mode.set_aref(test.aref());
|
|
|
|
// AFAIL
|
|
mode.set_alpha_fail(test.afail());
|
|
|
|
// DATE
|
|
ASSERT(test.date() == false);
|
|
|
|
// DATM
|
|
// who cares, if date is off
|
|
|
|
// ZTE
|
|
mode.set_zt(test.zte());
|
|
|
|
// ZTST
|
|
mode.set_depth_test(test.ztest());
|
|
}
|
|
|
|
u32 remap_texture(u32 original, const std::vector<level_tools::TextureRemap>& map) {
|
|
auto masked = original & 0xffffff00;
|
|
for (auto& t : map) {
|
|
if (t.original_texid == masked) {
|
|
lg::info("OKAY! remapped!");
|
|
return t.new_texid | 20;
|
|
}
|
|
}
|
|
return original;
|
|
}
|
|
|
|
void process_draw_mode(std::vector<TFragDraw>& all_draws,
|
|
const std::vector<level_tools::TextureRemap>& map,
|
|
tfrag3::TFragmentTreeKind tree_kind) {
|
|
// set up the draw mode based on the code in background.gc and tfrag-methods.gc
|
|
DrawMode mode;
|
|
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL);
|
|
mode.enable_depth_write();
|
|
|
|
mode.enable_at();
|
|
mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_DST_SRC_DST);
|
|
|
|
// note: includes the test reg, and also the str-gif template prim abe.
|
|
switch (tree_kind) {
|
|
case tfrag3::TFragmentTreeKind::NORMAL:
|
|
case tfrag3::TFragmentTreeKind::LOWRES:
|
|
mode.enable_at(); // :ate #x1
|
|
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL); // :atst (gs-atest greater-equal)
|
|
mode.set_aref(0x26); // :aref #x26
|
|
mode.set_alpha_fail(GsTest::AlphaFail::KEEP);
|
|
mode.enable_zt(); // :zte #x1
|
|
mode.set_depth_test(GsTest::ZTest::GEQUAL); // :ztst (gs-ztest greater-equal))
|
|
mode.disable_ab();
|
|
break;
|
|
case tfrag3::TFragmentTreeKind::TRANS:
|
|
case tfrag3::TFragmentTreeKind::LOWRES_TRANS:
|
|
mode.enable_at(); // :ate #x1
|
|
mode.set_alpha_test(DrawMode::AlphaTest::GEQUAL); // :atst (gs-atest greater-equal)
|
|
mode.set_aref(0x7e); // :aref #x7e
|
|
mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY);
|
|
mode.enable_zt(); // :zte #x1
|
|
mode.set_depth_test(GsTest::ZTest::GEQUAL); // :ztst (gs-ztest greater-equal))
|
|
mode.enable_ab();
|
|
break;
|
|
case tfrag3::TFragmentTreeKind::DIRT:
|
|
// (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal))
|
|
mode.enable_at();
|
|
mode.set_alpha_test(DrawMode::AlphaTest::NEVER);
|
|
mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY);
|
|
mode.set_aref(0);
|
|
mode.enable_zt();
|
|
mode.set_depth_test(GsTest::ZTest::GEQUAL);
|
|
mode.enable_ab();
|
|
break;
|
|
case tfrag3::TFragmentTreeKind::ICE:
|
|
mode.enable_at(); // :ate #x1
|
|
mode.set_alpha_test(DrawMode::AlphaTest::ALWAYS); // :atst (gs-atest always)
|
|
mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY); // :afail #x1
|
|
mode.set_aref(0);
|
|
mode.enable_zt(); // :zte #x1
|
|
mode.set_depth_test(GsTest::ZTest::GEQUAL); // :ztst (gs-ztest greater-equal)
|
|
mode.enable_ab();
|
|
break;
|
|
case tfrag3::TFragmentTreeKind::WATER:
|
|
// (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest greater-equal))
|
|
mode.enable_at();
|
|
mode.set_alpha_test(DrawMode::AlphaTest::NEVER);
|
|
mode.set_alpha_fail(GsTest::AlphaFail::FB_ONLY);
|
|
mode.set_aref(0);
|
|
mode.enable_zt();
|
|
mode.set_depth_test(GsTest::ZTest::GEQUAL);
|
|
mode.enable_ab();
|
|
break;
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
|
|
for (auto& draw : all_draws) {
|
|
for (int ad_idx = 0; ad_idx < 5; ad_idx++) {
|
|
auto addr = draw.get_adgif_addr(ad_idx);
|
|
u64 val = draw.get_adgif_val(ad_idx);
|
|
switch (addr) {
|
|
case GsRegisterAddress::TEST_1:
|
|
ASSERT(false);
|
|
update_mode_from_test1(val, mode);
|
|
break;
|
|
case GsRegisterAddress::TEX0_1:
|
|
// ASSERT(val == 0); HACK jak 2 sets this.
|
|
break;
|
|
case GsRegisterAddress::TEX1_1:
|
|
ASSERT(val == 0x120); // some flag
|
|
{
|
|
u32 original_tex = draw.get_adgif_upper(ad_idx);
|
|
u32 new_tex = remap_texture(original_tex, map);
|
|
if (original_tex != new_tex) {
|
|
lg::info("map from 0x{:x} to 0x{:x}", original_tex, new_tex);
|
|
}
|
|
u32 tpage = new_tex >> 20;
|
|
u32 tidx = (new_tex >> 8) & 0b1111'1111'1111;
|
|
// lg::print("texture: {} : {}\n", tpage, tidx);
|
|
draw.tpage = tpage;
|
|
draw.tex_in_page = tidx;
|
|
}
|
|
|
|
break;
|
|
case GsRegisterAddress::MIPTBP1_1:
|
|
break;
|
|
case GsRegisterAddress::CLAMP_1:
|
|
if (!(val == 0b101 || val == 0 || val == 1 || val == 0b100)) {
|
|
ASSERT_MSG(false, fmt::format("clamp: 0x{:x}", val));
|
|
}
|
|
|
|
mode.set_clamp_s_enable(val & 0b1);
|
|
mode.set_clamp_t_enable(val & 0b100);
|
|
break;
|
|
case GsRegisterAddress::ALPHA_1:
|
|
update_mode_from_alpha1(val, mode);
|
|
break;
|
|
default:
|
|
lg::info("Address {} ({}) is not supported in process_draw_mode",
|
|
register_address_name(addr), ad_idx);
|
|
}
|
|
}
|
|
draw.mode = mode;
|
|
}
|
|
}
|
|
|
|
struct TFragStrip {
|
|
std::vector<TFragVertexData> verts;
|
|
u16 tfrag_id = 0;
|
|
};
|
|
|
|
struct GroupedDraw {
|
|
DrawMode mode;
|
|
u16 tpage;
|
|
u16 tex_id;
|
|
std::vector<TFragStrip> strips;
|
|
};
|
|
|
|
std::map<u32, std::vector<GroupedDraw>> make_draw_groups(std::vector<TFragDraw>& all_draws) {
|
|
std::map<u32, std::vector<GroupedDraw>> result;
|
|
|
|
for (auto& draw : all_draws) {
|
|
u32 tex_combo = (((u32)draw.tpage) << 16) | draw.tex_in_page;
|
|
auto& group_list = result[tex_combo];
|
|
|
|
bool added = false;
|
|
for (auto& existing_group : group_list) {
|
|
if (draw.mode == existing_group.mode) {
|
|
existing_group.strips.push_back({draw.verts, draw.tfrag_id});
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!added) {
|
|
GroupedDraw new_group;
|
|
new_group.mode = draw.mode;
|
|
new_group.tpage = draw.tpage;
|
|
new_group.tex_id = draw.tex_in_page;
|
|
new_group.strips.push_back({draw.verts, draw.tfrag_id});
|
|
group_list.push_back(new_group);
|
|
}
|
|
}
|
|
|
|
int dc = 0;
|
|
for (auto& group_list : result) {
|
|
for (auto& group : group_list.second) {
|
|
(void)group;
|
|
dc++;
|
|
}
|
|
}
|
|
|
|
// lg::print(" grouped to get {} draw calls\n", dc);
|
|
|
|
return result;
|
|
}
|
|
|
|
void make_tfrag3_data(std::map<u32, std::vector<GroupedDraw>>& draws,
|
|
tfrag3::TfragTree& tree_out,
|
|
std::vector<tfrag3::PreloadedVertex>& vertices,
|
|
std::vector<tfrag3::Texture>& texture_pool,
|
|
const TextureDB& tdb,
|
|
const std::vector<std::pair<int, int>>& expected_missing_textures) {
|
|
// we will set:
|
|
// draws
|
|
// color_indices_per_vertex
|
|
// and link textures.
|
|
|
|
for (auto& [combo_tex_id, draw_list] : draws) {
|
|
// first, let's see if we have a texture for this.
|
|
u32 tfrag3_tex_id = UINT32_MAX;
|
|
for (u32 i = 0; i < texture_pool.size(); i++) {
|
|
if (texture_pool[i].combo_id == combo_tex_id) {
|
|
tfrag3_tex_id = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tfrag3_tex_id == UINT32_MAX) {
|
|
// nope. we are a new texture.
|
|
auto tex_it = tdb.textures.find(combo_tex_id);
|
|
if (tex_it == tdb.textures.end()) {
|
|
int tpage = combo_tex_id >> 16;
|
|
int idx = combo_tex_id & 0xffff;
|
|
bool ok_to_miss =
|
|
std::find(expected_missing_textures.begin(), expected_missing_textures.end(),
|
|
std::make_pair(tpage, idx)) != expected_missing_textures.end();
|
|
if (ok_to_miss) {
|
|
// we're missing a texture, just use the first one.
|
|
tex_it = tdb.textures.begin();
|
|
} else {
|
|
ASSERT_MSG(
|
|
false,
|
|
fmt::format("texture {} wasn't found. make sure it is loaded somehow. You may need "
|
|
"to include "
|
|
"ART.DGO or GAME.DGO in addition to the level DGOs for shared textures."
|
|
"tpage is {}. id is {} (0x{:x})",
|
|
combo_tex_id, combo_tex_id >> 16, combo_tex_id & 0xffff,
|
|
combo_tex_id & 0xffff));
|
|
}
|
|
}
|
|
tfrag3_tex_id = texture_pool.size();
|
|
texture_pool.emplace_back();
|
|
auto& new_tex = texture_pool.back();
|
|
new_tex.combo_id = 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 = tdb.tpage_names.at(tex_it->second.page);
|
|
new_tex.data = tex_it->second.rgba_bytes;
|
|
}
|
|
|
|
// now, add draws
|
|
for (auto& draw : draw_list) {
|
|
tfrag3::StripDraw tdraw;
|
|
tdraw.mode = draw.mode;
|
|
tdraw.tree_tex_id = tfrag3_tex_id;
|
|
|
|
for (auto& strip : draw.strips) {
|
|
tfrag3::StripDraw::VisGroup vgroup;
|
|
vgroup.vis_idx_in_pc_bvh = strip.tfrag_id; // associate with the tfrag for culling
|
|
vgroup.num_inds = strip.verts.size() + 1; // one for the primitive restart!
|
|
vgroup.num_tris = strip.verts.size() - 2;
|
|
|
|
tdraw.num_triangles += strip.verts.size() - 2;
|
|
tfrag3::StripDraw::VertexRun run;
|
|
run.vertex0 = vertices.size();
|
|
run.length = strip.verts.size();
|
|
for (auto& vert : strip.verts) {
|
|
// convert vert.
|
|
tfrag3::PreloadedVertex vtx;
|
|
vtx.x = vert.pre_cam_trans_pos.x();
|
|
vtx.y = vert.pre_cam_trans_pos.y();
|
|
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);
|
|
vtx.color_index = vert.rgba / 4;
|
|
// ASSERT((vert.rgba >> 2) < 1024); spider cave has 2048?
|
|
ASSERT((vert.rgba & 3) == 0);
|
|
|
|
vertices.push_back(vtx);
|
|
}
|
|
tdraw.runs.push_back(run);
|
|
tdraw.vis_groups.push_back(vgroup);
|
|
}
|
|
|
|
tree_out.draws.push_back(tdraw);
|
|
}
|
|
}
|
|
}
|
|
|
|
void emulate_tfrags(int geom,
|
|
const std::vector<level_tools::TFragment>& frags,
|
|
const std::string& debug_name,
|
|
const std::vector<level_tools::TextureRemap>& map,
|
|
tfrag3::Level& level_out,
|
|
tfrag3::TfragTree& tree_out,
|
|
std::vector<tfrag3::PreloadedVertex>& vertices,
|
|
const TextureDB& tdb,
|
|
const std::vector<std::pair<int, int>>& expected_missing_textures,
|
|
bool dump_level) {
|
|
TFragExtractStats stats;
|
|
|
|
std::vector<u8> vu_mem;
|
|
vu_mem.resize(16 * 1024);
|
|
|
|
std::vector<TFragDraw> all_draws;
|
|
|
|
for (auto& frag : frags) {
|
|
TFragColorUnpack color_indices;
|
|
emulate_dma_building_for_tfrag(geom, frag, vu_mem, color_indices, &stats);
|
|
VuMemWrapper mem(vu_mem);
|
|
auto draws = emulate_tfrag_execution<false>(frag, mem, color_indices, &stats);
|
|
all_draws.insert(all_draws.end(), draws.begin(), draws.end());
|
|
}
|
|
|
|
process_draw_mode(all_draws, map, tree_out.kind);
|
|
auto groups = make_draw_groups(all_draws);
|
|
|
|
make_tfrag3_data(groups, tree_out, vertices, level_out.textures, tdb, expected_missing_textures);
|
|
|
|
if (dump_level) {
|
|
auto debug_out = debug_dump_to_obj(all_draws);
|
|
file_util::write_text_file(
|
|
file_util::get_file_path({"debug_out", fmt::format("tfrag-{}.obj", debug_name)}),
|
|
debug_out);
|
|
}
|
|
}
|
|
|
|
void extract_time_of_day(const level_tools::DrawableTreeTfrag* tree, tfrag3::TfragTree& out) {
|
|
out.colors.resize(tree->time_of_day.height);
|
|
for (int i = 0; i < (int)tree->time_of_day.height; i++) {
|
|
for (int j = 0; j < 8; j++) {
|
|
memcpy(out.colors[i].rgba[j].data(), &tree->time_of_day.colors[i * 8 + j], 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
void merge_groups(std::vector<tfrag3::StripDraw::VisGroup>& grps) {
|
|
std::vector<tfrag3::StripDraw::VisGroup> result;
|
|
result.push_back(grps.at(0));
|
|
for (size_t i = 1; i < grps.size(); i++) {
|
|
if (grps[i].vis_idx_in_pc_bvh == result.back().vis_idx_in_pc_bvh) {
|
|
result.back().num_inds += grps[i].num_inds;
|
|
result.back().num_tris += grps[i].num_tris;
|
|
} else {
|
|
result.push_back(grps[i]);
|
|
}
|
|
}
|
|
std::swap(result, grps);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void extract_tfrag(const level_tools::DrawableTreeTfrag* tree,
|
|
const std::string& debug_name,
|
|
const std::vector<level_tools::TextureRemap>& map,
|
|
const TextureDB& tex_db,
|
|
const std::vector<std::pair<int, int>>& expected_missing_textures,
|
|
tfrag3::Level& out,
|
|
bool dump_level) {
|
|
// go through 3 lods(?)
|
|
for (int geom = 0; geom < GEOM_MAX; ++geom) {
|
|
tfrag3::TfragTree this_tree;
|
|
if (tree->my_type() == "drawable-tree-tfrag") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::NORMAL;
|
|
} else if (tree->my_type() == "drawable-tree-dirt-tfrag") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::DIRT;
|
|
} else if (tree->my_type() == "drawable-tree-ice-tfrag") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::ICE;
|
|
} else if (tree->my_type() == "drawable-tree-lowres-tfrag") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::LOWRES;
|
|
} else if (tree->my_type() == "drawable-tree-trans-tfrag") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::TRANS;
|
|
} else if (tree->my_type() == "drawable-tree-tfrag-trans") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::TRANS;
|
|
} else if (tree->my_type() == "drawable-tree-tfrag-water") {
|
|
this_tree.kind = tfrag3::TFragmentTreeKind::WATER;
|
|
} else {
|
|
ASSERT_MSG(false, fmt::format("unknown tfrag tree kind: {}", tree->my_type()));
|
|
}
|
|
|
|
ASSERT(tree->length == (int)tree->arrays.size());
|
|
ASSERT(tree->length > 0);
|
|
|
|
auto last_array = tree->arrays.back().get();
|
|
|
|
auto as_tfrag_array = dynamic_cast<level_tools::DrawableInlineArrayTFrag*>(last_array);
|
|
ASSERT(as_tfrag_array);
|
|
ASSERT(as_tfrag_array->length == (int)as_tfrag_array->tfragments.size());
|
|
ASSERT(as_tfrag_array->length > 0);
|
|
u16 idx = as_tfrag_array->tfragments.front().id;
|
|
for (auto& elt : as_tfrag_array->tfragments) {
|
|
ASSERT(elt.id == idx);
|
|
idx++;
|
|
}
|
|
bool ok = verify_node_indices(tree);
|
|
ASSERT(ok);
|
|
// lg::print(" tree has {} arrays and {} tfragments\n", tree->length,
|
|
// as_tfrag_array->length);
|
|
|
|
auto vis_nodes = extract_vis_data(tree, as_tfrag_array->tfragments.front().id);
|
|
this_tree.bvh.first_leaf_node = vis_nodes.first_child_node;
|
|
this_tree.bvh.last_leaf_node = vis_nodes.last_child_node;
|
|
this_tree.bvh.num_roots = vis_nodes.num_roots;
|
|
this_tree.bvh.only_children = vis_nodes.only_children;
|
|
this_tree.bvh.first_root = vis_nodes.first_root;
|
|
this_tree.bvh.vis_nodes = std::move(vis_nodes.vis_nodes);
|
|
|
|
std::unordered_map<int, int> tfrag_parents;
|
|
// for (auto& node : this_tree.vis_nodes) {
|
|
for (size_t node_idx = 0; node_idx < this_tree.bvh.vis_nodes.size(); node_idx++) {
|
|
const auto& node = this_tree.bvh.vis_nodes[node_idx];
|
|
if (node.flags == 0) {
|
|
for (int i = 0; i < node.num_kids; i++) {
|
|
tfrag_parents[node.child_id + i] = node_idx;
|
|
}
|
|
}
|
|
}
|
|
// ASSERT(result.vis_nodes.last_child_node + 1 == idx);
|
|
|
|
std::vector<tfrag3::PreloadedVertex> vertices;
|
|
emulate_tfrags(geom, as_tfrag_array->tfragments, debug_name, map, out, this_tree, vertices,
|
|
tex_db, expected_missing_textures, dump_level);
|
|
pack_tfrag_vertices(&this_tree.packed_vertices, vertices);
|
|
extract_time_of_day(tree, this_tree);
|
|
|
|
for (auto& draw : this_tree.draws) {
|
|
for (auto& str : draw.vis_groups) {
|
|
auto it = tfrag_parents.find(str.vis_idx_in_pc_bvh);
|
|
if (it == tfrag_parents.end()) {
|
|
str.vis_idx_in_pc_bvh = UINT32_MAX;
|
|
} else {
|
|
str.vis_idx_in_pc_bvh = it->second;
|
|
}
|
|
}
|
|
merge_groups(draw.vis_groups);
|
|
}
|
|
out.tfrag_trees[geom].push_back(this_tree);
|
|
}
|
|
}
|
|
} // namespace decompiler
|