mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
[jak2] Work-in-progress texture animations (#2819)
This commit is contained in:
parent
e546fce370
commit
6f244b11ef
|
@ -56,6 +56,7 @@ add_library(common
|
|||
serialization/subtitles/subtitles.cpp
|
||||
serialization/text/text_ser.cpp
|
||||
sqlite/sqlite.cpp
|
||||
texture/texture_slots.cpp
|
||||
type_system/defenum.cpp
|
||||
type_system/deftype.cpp
|
||||
type_system/state.cpp
|
||||
|
|
|
@ -433,6 +433,17 @@ void Texture::serialize(Serializer& ser) {
|
|||
ser.from_ptr(&load_to_pool);
|
||||
}
|
||||
|
||||
void IndexTexture::serialize(Serializer& ser) {
|
||||
ser.from_ptr(&w);
|
||||
ser.from_ptr(&h);
|
||||
ser.from_ptr(&combo_id);
|
||||
ser.from_pod_vector(&index_data);
|
||||
ser.from_ptr(&color_table);
|
||||
ser.from_str(&name);
|
||||
ser.from_str(&tpage_name);
|
||||
ser.from_string_vector(&level_names);
|
||||
}
|
||||
|
||||
void CollisionMesh::serialize(Serializer& ser) {
|
||||
ser.from_pod_vector(&vertices);
|
||||
}
|
||||
|
@ -545,6 +556,15 @@ void Level::serialize(Serializer& ser) {
|
|||
tex.serialize(ser);
|
||||
}
|
||||
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(index_textures.size());
|
||||
} else {
|
||||
index_textures.resize(ser.load<size_t>());
|
||||
}
|
||||
for (auto& tex : index_textures) {
|
||||
tex.serialize(ser);
|
||||
}
|
||||
|
||||
for (int geom = 0; geom < 3; ++geom) {
|
||||
if (ser.is_saving()) {
|
||||
ser.save<size_t>(tfrag_trees[geom].size());
|
||||
|
@ -687,6 +707,11 @@ void Texture::memory_usage(MemoryUsageTracker* tracker) const {
|
|||
tracker->add(MemoryUsageCategory::TEXTURE, data.size() * sizeof(u32));
|
||||
}
|
||||
|
||||
void IndexTexture::memory_usage(MemoryUsageTracker* tracker) const {
|
||||
tracker->add(MemoryUsageCategory::SPECIAL_TEXTURE, index_data.size());
|
||||
tracker->add(MemoryUsageCategory::SPECIAL_TEXTURE, 256 * 4); // clut
|
||||
}
|
||||
|
||||
void Level::memory_usage(MemoryUsageTracker* tracker) const {
|
||||
for (const auto& texture : textures) {
|
||||
texture.memory_usage(tracker);
|
||||
|
@ -715,6 +740,7 @@ void print_memory_usage(const tfrag3::Level& lev, int uncompressed_data_size) {
|
|||
|
||||
std::vector<std::pair<std::string, int>> known_categories = {
|
||||
{"texture", mem_use.data[tfrag3::MemoryUsageCategory::TEXTURE]},
|
||||
{"special-texture", mem_use.data[tfrag3::MemoryUsageCategory::SPECIAL_TEXTURE]},
|
||||
{"tie-deinst-vis", mem_use.data[tfrag3::MemoryUsageCategory::TIE_DEINST_VIS]},
|
||||
{"tie-deinst-idx", mem_use.data[tfrag3::MemoryUsageCategory::TIE_DEINST_INDEX]},
|
||||
{"tie-inst-vis", mem_use.data[tfrag3::MemoryUsageCategory::TIE_INST_VIS]},
|
||||
|
|
|
@ -18,11 +18,13 @@ namespace tfrag3 {
|
|||
// - if changing any large things (vertices, vis, bvh, colors, textures) update get_memory_usage
|
||||
// - if adding a new category to the memory usage, update extract_level to print it.
|
||||
|
||||
constexpr int TFRAG3_VERSION = 37;
|
||||
constexpr int TFRAG3_VERSION = 38;
|
||||
|
||||
enum MemoryUsageCategory {
|
||||
TEXTURE,
|
||||
|
||||
SPECIAL_TEXTURE,
|
||||
|
||||
TIE_DEINST_VIS,
|
||||
TIE_DEINST_INDEX,
|
||||
TIE_INST_VIS,
|
||||
|
@ -292,6 +294,18 @@ struct Texture {
|
|||
void memory_usage(MemoryUsageTracker* tracker) const;
|
||||
};
|
||||
|
||||
struct IndexTexture {
|
||||
u16 w, h;
|
||||
u32 combo_id = 0;
|
||||
std::vector<u8> index_data;
|
||||
std::vector<std::string> level_names;
|
||||
std::string name;
|
||||
std::string tpage_name;
|
||||
std::array<math::Vector4<u8>, 256> color_table;
|
||||
void serialize(Serializer& ser);
|
||||
void memory_usage(MemoryUsageTracker* tracker) const;
|
||||
};
|
||||
|
||||
// Tfrag trees have several kinds:
|
||||
enum class TFragmentTreeKind { NORMAL, TRANS, DIRT, ICE, LOWRES, LOWRES_TRANS, WATER, INVALID };
|
||||
|
||||
|
@ -458,7 +472,7 @@ static_assert(sizeof(MercVertex) == 64);
|
|||
|
||||
struct MercDraw {
|
||||
DrawMode mode;
|
||||
u32 tree_tex_id = 0; // the texture that should be bound for the draw
|
||||
s32 tree_tex_id = 0; // the texture that should be bound for the draw (negative for anim slot)
|
||||
u8 eye_id = 0xff; // 0xff if not eyes, (slot << 1) | (is_r)
|
||||
u32 first_index;
|
||||
u32 index_count;
|
||||
|
@ -542,6 +556,7 @@ struct Level {
|
|||
u16 version = TFRAG3_VERSION;
|
||||
std::string level_name;
|
||||
std::vector<Texture> textures;
|
||||
std::vector<IndexTexture> index_textures;
|
||||
std::array<std::vector<TfragTree>, TFRAG_GEOS> tfrag_trees;
|
||||
std::array<std::vector<TieTree>, TIE_GEOS> tie_trees;
|
||||
std::vector<ShrubTree> shrub_trees;
|
||||
|
|
|
@ -276,6 +276,7 @@ std::string GsTest::print() const {
|
|||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
result += '\n';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ const std::unordered_map<GameVersion, std::vector<std::string>> locale_lookup =
|
|||
|
||||
std::string lookup_locale_code(const GameVersion game_version, const int language_id) {
|
||||
if (locale_lookup.find(game_version) == locale_lookup.end() ||
|
||||
locale_lookup.at(game_version).size() < language_id) {
|
||||
(int)locale_lookup.at(game_version).size() < language_id) {
|
||||
return "";
|
||||
}
|
||||
return locale_lookup.at(game_version).at(language_id);
|
||||
|
|
|
@ -132,7 +132,7 @@ std::pair<SubtitleMetadataFile, SubtitleFile> convert_v1_to_v2(
|
|||
} else {
|
||||
if (v1_lines_file.speakers.find(line_meta.speaker) == v1_lines_file.speakers.end() ||
|
||||
v1_lines_file.hints.find(hint_name) == v1_lines_file.hints.end() ||
|
||||
line_idx >= v1_lines_file.hints.at(hint_name).size()) {
|
||||
line_idx >= (int)v1_lines_file.hints.at(hint_name).size()) {
|
||||
lg::warn(
|
||||
"{} Couldn't find {} in line file, or line list is too small, or speaker could not "
|
||||
"be resolved {}!",
|
||||
|
@ -145,7 +145,7 @@ std::pair<SubtitleMetadataFile, SubtitleFile> convert_v1_to_v2(
|
|||
}
|
||||
}
|
||||
// Verify we added the amount of lines we expected to
|
||||
if (lines_added != hint_info.lines.size()) {
|
||||
if (lines_added != (int)hint_info.lines.size()) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("Hint: '{}' has a mismatch in metadata lines vs text lines. Expected {} "
|
||||
"only added {} lines",
|
||||
|
@ -222,7 +222,7 @@ GameSubtitlePackage read_json_files_v1(const GameSubtitleDefinitionFile& file_in
|
|||
}
|
||||
}
|
||||
|
||||
SubtitleMetadataFileV1 dump_bank_meta_v1(const GameVersion game_version,
|
||||
SubtitleMetadataFileV1 dump_bank_meta_v1(const GameVersion /*game_version*/,
|
||||
std::shared_ptr<GameSubtitleBank> bank) {
|
||||
auto meta_file = SubtitleMetadataFileV1();
|
||||
for (const auto& [scene_name, scene_info] : bank->m_scenes) {
|
||||
|
|
|
@ -196,13 +196,13 @@ GameSubtitleSceneInfo GameSubtitleBank::new_scene_from_meta(
|
|||
// In either case, we acknowledge that there is a line, but there is no text to retrieve at that
|
||||
// index.
|
||||
if (line_meta.merge || (relevant_lines.find(scene_name) != relevant_lines.end() &&
|
||||
relevant_lines.at(scene_name).size() > line_idx &&
|
||||
(int)relevant_lines.at(scene_name).size() > line_idx &&
|
||||
relevant_lines.at(scene_name).at(line_idx).empty())) {
|
||||
new_scene.m_lines.push_back({"", line_meta});
|
||||
lines_added++;
|
||||
} else if (m_speakers.find(line_meta.speaker) == m_speakers.end() ||
|
||||
relevant_lines.find(scene_name) == relevant_lines.end() ||
|
||||
line_idx >= relevant_lines.at(scene_name).size()) {
|
||||
line_idx >= (int)relevant_lines.at(scene_name).size()) {
|
||||
lg::warn(
|
||||
"{} Couldn't find {} in line file, or line list is too small, or speaker could not "
|
||||
"be resolved {}!",
|
||||
|
@ -290,6 +290,7 @@ SubtitleMetadataFile dump_bank_meta_v2(const GameVersion game_version,
|
|||
std::shared_ptr<GameSubtitleBank> bank) {
|
||||
const auto dump_with_duplicates =
|
||||
dump_language_with_duplicates_from_base(game_version, bank->m_lang_id);
|
||||
(void)dump_with_duplicates;
|
||||
auto meta_file = SubtitleMetadataFile();
|
||||
for (const auto& [scene_name, scene_info] : bank->m_scenes) {
|
||||
// Avoid dumping duplicates
|
||||
|
|
|
@ -89,7 +89,7 @@ struct GameSubtitleSceneInfo {
|
|||
return false;
|
||||
}
|
||||
// Check each line
|
||||
for (int i = 0; i < m_lines.size(); i++) {
|
||||
for (size_t i = 0; i < m_lines.size(); i++) {
|
||||
if (m_lines.at(i).text != other.m_lines.at(i).text) {
|
||||
return false;
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ struct GameSubtitleSceneInfo {
|
|||
return false;
|
||||
}
|
||||
// Check each line's metadata
|
||||
for (int i = 0; i < m_lines.size(); i++) {
|
||||
for (size_t i = 0; i < m_lines.size(); i++) {
|
||||
if (m_lines.at(i).metadata != other.m_lines.at(i).metadata) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ sqlite::GenericResponse sqlite::SQLiteDatabase::run_query(const std::string& sql
|
|||
|
||||
const auto rc = sqlite3_exec(
|
||||
m_db.value().get(), sql.data(),
|
||||
[](void* data, int argc, char** argv, char** azColName) {
|
||||
[](void* data, int argc, char** argv, char** /*azColName*/) {
|
||||
GenericResponse* resp = static_cast<GenericResponse*>(data);
|
||||
std::vector<std::string> row = {};
|
||||
for (int i = 0; i < argc; i++) {
|
||||
|
|
31
common/texture/texture_slots.cpp
Normal file
31
common/texture/texture_slots.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "texture_slots.h"
|
||||
|
||||
namespace {
|
||||
std::vector<std::string> jak2_slots = {
|
||||
"jakbsmall-eyebrow",
|
||||
"jakbsmall-face",
|
||||
"jakbsmall-finger",
|
||||
"jakbsmall-hair",
|
||||
"jak-orig-arm-formorph",
|
||||
"jak-orig-eyebrow-formorph",
|
||||
"jak-orig-eyelid-formorph",
|
||||
"jak-orig-finger-formorph",
|
||||
"jakb-facelft",
|
||||
"jakb-facert",
|
||||
"jakb-hairtrans",
|
||||
"jakb-eyelid",
|
||||
"jakb-finger",
|
||||
"jakb-eyebrow",
|
||||
//"kor-eyeeffect-formorph",
|
||||
//"kor-hair-formorph",
|
||||
//"kor-head-formorph",
|
||||
//"kor-head-formorph-noreflect",
|
||||
//"kor-lowercaps-formorph",
|
||||
//"kor-uppercaps-formorph",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const std::vector<std::string>& jak2_animated_texture_slots() {
|
||||
return jak2_slots;
|
||||
}
|
6
common/texture/texture_slots.h
Normal file
6
common/texture/texture_slots.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
const std::vector<std::string>& jak2_animated_texture_slots();
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "common/link_types.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/texture/texture_slots.h"
|
||||
#include "common/util/BinaryReader.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
@ -698,7 +699,9 @@ void ObjectFileDB::find_and_write_scripts(const fs::path& output_dir) {
|
|||
lg::info(" Total {:.3f} ms", timer.getMs());
|
||||
}
|
||||
|
||||
std::string ObjectFileDB::process_tpages(TextureDB& tex_db, const fs::path& output_path) {
|
||||
std::string ObjectFileDB::process_tpages(TextureDB& tex_db,
|
||||
const fs::path& output_path,
|
||||
const Config& cfg) {
|
||||
lg::info("- Finding textures in tpages...");
|
||||
std::string tpage_string = "tpage-";
|
||||
int total = 0, success = 0;
|
||||
|
@ -706,10 +709,25 @@ std::string ObjectFileDB::process_tpages(TextureDB& tex_db, const fs::path& outp
|
|||
u64 total_px = 0;
|
||||
Timer timer;
|
||||
|
||||
std::vector<std::string> animated_slots;
|
||||
switch (m_version) {
|
||||
case GameVersion::Jak1: // no animated
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
animated_slots = jak2_animated_texture_slots();
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < animated_slots.size(); i++) {
|
||||
tex_db.animated_tex_output_to_anim_slot[animated_slots[i]] = i;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
for_each_obj([&](ObjectFileData& data) {
|
||||
if (data.name_in_dgo.substr(0, tpage_string.length()) == tpage_string) {
|
||||
auto statistics = process_tpage(data, tex_db, output_path);
|
||||
auto statistics = process_tpage(data, tex_db, output_path, cfg.animated_textures);
|
||||
total += statistics.total_textures;
|
||||
success += statistics.successful_textures;
|
||||
total_px += statistics.num_px;
|
||||
|
|
|
@ -247,7 +247,7 @@ class ObjectFileDB {
|
|||
const std::vector<std::string>& imports,
|
||||
const std::unordered_set<std::string>& skip_functions);
|
||||
|
||||
std::string process_tpages(TextureDB& tex_db, const fs::path& output_path);
|
||||
std::string process_tpages(TextureDB& tex_db, const fs::path& output_path, const Config& cfg);
|
||||
std::string process_game_count_file();
|
||||
std::string process_game_text_files(const Config& cfg);
|
||||
std::string process_all_spool_subtitles(const Config& cfg, const fs::path& image_out);
|
||||
|
|
|
@ -267,6 +267,11 @@ Config make_config_via_json(nlohmann::json& json) {
|
|||
config.levels_to_extract = inputs_json.at("levels_to_extract").get<std::vector<std::string>>();
|
||||
config.levels_extract = json.at("levels_extract").get<bool>();
|
||||
|
||||
if (inputs_json.contains("animated_textures")) {
|
||||
config.animated_textures =
|
||||
inputs_json.at("animated_textures").get<std::unordered_set<std::string>>();
|
||||
}
|
||||
|
||||
auto art_info_json = read_json_file_from_config(json, "art_info_file");
|
||||
config.art_groups_by_file =
|
||||
art_info_json.at("files").get<std::unordered_map<std::string, std::string>>();
|
||||
|
|
|
@ -159,6 +159,8 @@ struct Config {
|
|||
|
||||
std::unordered_map<std::string, int> bad_format_strings;
|
||||
|
||||
std::unordered_set<std::string> animated_textures;
|
||||
|
||||
std::vector<std::string> levels_to_extract;
|
||||
bool levels_extract;
|
||||
|
||||
|
|
|
@ -443,6 +443,76 @@
|
|||
|
||||
"streamed_audio_file_names": [],
|
||||
|
||||
// Textures that should be extracted in a special way for use in animated textures.
|
||||
"animated_textures": [
|
||||
// Dark Jak (small gameplay model)
|
||||
"jakbsmall-eyebrow",
|
||||
"jakbsmall-eyebrow-norm",
|
||||
"jakbsmall-eyebrow-dark",
|
||||
"jakbsmall-face",
|
||||
"jakbsmall-face-norm",
|
||||
"jakbsmall-face-dark",
|
||||
"jakbsmall-finger",
|
||||
"jakbsmall-finger-norm",
|
||||
"jakbsmall-finger-dark",
|
||||
"jakbsmall-hair",
|
||||
"jakbsmall-hair-norm",
|
||||
"jakbsmall-hair-dark",
|
||||
|
||||
// Prison Jak
|
||||
"jak-orig-arm-formorph",
|
||||
"jak-orig-arm-formorph-start",
|
||||
"jak-orig-arm-formorph-end",
|
||||
"jak-orig-eyebrow-formorph",
|
||||
"jak-orig-eyebrow-formorph-start",
|
||||
"jak-orig-eyebrow-formorph-end",
|
||||
"jak-orig-eyelid-formorph",
|
||||
"jak-orig-eyelid-formorph-start",
|
||||
"jak-orig-eyelid-formorph-end",
|
||||
"jak-orig-finger-formorph",
|
||||
"jak-orig-finger-formorph-start",
|
||||
"jak-orig-finger-formorph-end",
|
||||
"jakb-facelft",
|
||||
"jakb-facelft-norm",
|
||||
"jakb-facelft-dark",
|
||||
"jakb-facert",
|
||||
"jakb-facert-norm",
|
||||
"jakb-facert-dark",
|
||||
"jakb-hairtrans",
|
||||
"jakb-hairtrans-norm",
|
||||
"jakb-hairtrans-dark",
|
||||
|
||||
// Oracle Jak (and nest jak)
|
||||
"jakb-eyebrow",
|
||||
"jakb-eyebrow-norm",
|
||||
"jakb-eyebrow-dark",
|
||||
"jakb-eyelid",
|
||||
"jakb-eyelid-norm",
|
||||
"jakb-eyelid-dark",
|
||||
"jakb-finger",
|
||||
"jakb-finger-norm",
|
||||
"jakb-finger-dark"
|
||||
|
||||
// "kor-eyeeffect-formorph",
|
||||
// "kor-eyeeffect-formorph-start",
|
||||
// "kor-eyeeffect-formorph-end",
|
||||
// "kor-hair-formorph",
|
||||
// "kor-hair-formorph-start",
|
||||
// "kor-hair-formorph-end",
|
||||
// "kor-head-formorph",
|
||||
// "kor-head-formorph-start",
|
||||
// "kor-head-formorph-end",
|
||||
// "kor-head-formorph-noreflect",
|
||||
// "kor-head-formorph-noreflect-start",
|
||||
// "kor-head-formorph-noreflect-end",
|
||||
// "kor-lowercaps-formorph",
|
||||
// "kor-lowercaps-formorph-start",
|
||||
// "kor-lowercaps-formorph-end",
|
||||
// "kor-uppercaps-formorph",
|
||||
// "kor-uppercaps-formorph-start",
|
||||
// "kor-uppercaps-formorph-end"
|
||||
],
|
||||
|
||||
"levels_to_extract": [
|
||||
"ATE.DGO",
|
||||
"ATO.DGO",
|
||||
|
|
|
@ -51,6 +51,48 @@ void TextureDB::add_texture(u32 tpage,
|
|||
}
|
||||
}
|
||||
|
||||
void TextureDB::add_index_texture(u32 tpage,
|
||||
u32 texid,
|
||||
const std::vector<u8>& index_data,
|
||||
const std::array<math::Vector4<u8>, 256>& clut,
|
||||
u16 w,
|
||||
u16 h,
|
||||
const std::string& tex_name,
|
||||
const std::string& tpage_name,
|
||||
const std::vector<std::string>& level_names) {
|
||||
auto existing_tpage_name = tpage_names.find(tpage);
|
||||
if (existing_tpage_name == tpage_names.end()) {
|
||||
tpage_names[tpage] = tpage_name;
|
||||
} else {
|
||||
ASSERT(existing_tpage_name->second == tpage_name);
|
||||
}
|
||||
|
||||
u32 combo_id = (tpage << 16) | texid;
|
||||
auto existing_tex = index_textures_by_combo_id.find(combo_id);
|
||||
if (existing_tex != index_textures_by_combo_id.end()) {
|
||||
ASSERT(existing_tex->second.name == tex_name);
|
||||
ASSERT(existing_tex->second.w == w);
|
||||
ASSERT(existing_tex->second.h == h);
|
||||
ASSERT(existing_tex->second.index_data == index_data);
|
||||
ASSERT(existing_tex->second.combo_id == combo_id);
|
||||
ASSERT(existing_tex->second.color_table == clut);
|
||||
ASSERT(existing_tex->second.tpage_name == tpage_name);
|
||||
for (auto& ln : level_names) {
|
||||
existing_tex->second.level_names.push_back(ln);
|
||||
}
|
||||
} else {
|
||||
auto& new_tex = index_textures_by_combo_id[combo_id];
|
||||
new_tex.index_data = index_data;
|
||||
new_tex.color_table = clut;
|
||||
new_tex.name = tex_name;
|
||||
new_tex.w = w;
|
||||
new_tex.h = h;
|
||||
new_tex.tpage_name = tpage_name;
|
||||
new_tex.combo_id = combo_id;
|
||||
new_tex.level_names = level_names;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureDB::replace_textures(const fs::path& path) {
|
||||
fs::path base_path(path);
|
||||
for (auto& tex : textures) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace decompiler {
|
||||
|
@ -24,6 +25,11 @@ struct TextureDB {
|
|||
std::unordered_map<u32, std::string> tpage_names;
|
||||
std::unordered_map<std::string, std::set<u32>> texture_ids_per_level;
|
||||
|
||||
// special textures for animation.
|
||||
std::map<u32, tfrag3::IndexTexture> index_textures_by_combo_id;
|
||||
|
||||
std::unordered_map<std::string, u32> animated_tex_output_to_anim_slot;
|
||||
|
||||
void add_texture(u32 tpage,
|
||||
u32 texid,
|
||||
const std::vector<u32>& data,
|
||||
|
@ -35,6 +41,16 @@ struct TextureDB {
|
|||
u32 num_mips,
|
||||
u32 dest);
|
||||
|
||||
void add_index_texture(u32 tpage,
|
||||
u32 texid,
|
||||
const std::vector<u8>& index_data,
|
||||
const std::array<math::Vector4<u8>, 256>& clut,
|
||||
u16 w,
|
||||
u16 h,
|
||||
const std::string& tex_name,
|
||||
const std::string& tpage_name,
|
||||
const std::vector<std::string>& level_names);
|
||||
|
||||
void replace_textures(const fs::path& path);
|
||||
|
||||
std::string generate_texture_dest_adjustment_table() const;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "tpage.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/texture/texture_conversion.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/versions/versions.h"
|
||||
|
@ -431,7 +432,8 @@ TexturePage read_texture_page(ObjectFileData& data,
|
|||
*/
|
||||
TPageResultStats process_tpage(ObjectFileData& data,
|
||||
TextureDB& texture_db,
|
||||
const fs::path& output_path) {
|
||||
const fs::path& output_path,
|
||||
const std::unordered_set<std::string>& animated_textures) {
|
||||
TPageResultStats stats;
|
||||
auto& words = data.linked_data.words_by_seg.at(0);
|
||||
const auto& level_names = data.dgo_names;
|
||||
|
@ -451,6 +453,12 @@ TPageResultStats process_tpage(ObjectFileData& data,
|
|||
|
||||
// Read the texture_page struct
|
||||
TexturePage texture_page = read_texture_page(data, words, 0, end_of_texture_page);
|
||||
bool ignore_animated = texture_page.name == "sewesc-vis-pris";
|
||||
if (ignore_animated) {
|
||||
lg::warn(
|
||||
"Ignoring animated textures from this tpage ({}) because of weird jakbsmall-finger issue",
|
||||
texture_page.name);
|
||||
}
|
||||
auto texture_dump_dir = output_path / texture_page.name;
|
||||
file_util::create_dir_if_needed(texture_dump_dir);
|
||||
|
||||
|
@ -494,9 +502,57 @@ TPageResultStats process_tpage(ObjectFileData& data,
|
|||
stats.total_textures++;
|
||||
stats.num_px += tex.w * tex.h;
|
||||
|
||||
if (tex.psm == int(PSM::PSMT8) && tex.clutpsm == int(CPSM::PSMCT32)) {
|
||||
// this is the only supported texture format for now.
|
||||
if (animated_textures.count(tex.name) && !ignore_animated) {
|
||||
switch (tex.psm) {
|
||||
case int(PSM::PSMT8):
|
||||
ASSERT(tex.clutpsm == int(CPSM::PSMCT32));
|
||||
{
|
||||
// will store output pixels, index (u8)
|
||||
std::vector<u8> index_out;
|
||||
|
||||
// width is like the TEX0 register, in 64 texel units.
|
||||
// not sure what the other widths are yet.
|
||||
int read_width = 64 * tex.width[0];
|
||||
|
||||
// loop over pixels in output texture image
|
||||
for (int y = 0; y < tex.h; y++) {
|
||||
for (int x = 0; x < tex.w; x++) {
|
||||
// read as the PSMT8 type. The dest field tells us a block offset.
|
||||
auto addr8 = psmt8_addr(x, y, read_width) + tex.dest[0] * 256;
|
||||
u8 value = vram[addr8];
|
||||
index_out.push_back(value);
|
||||
}
|
||||
}
|
||||
std::array<math::Vector4<u8>, 256> unscrambled_clut;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
u32 clut_chunk = i / 16;
|
||||
u32 off_in_chunk = i % 16;
|
||||
u8 clx = 0, cly = 0;
|
||||
if (clut_chunk & 1) {
|
||||
clx = 8;
|
||||
}
|
||||
cly = (clut_chunk >> 1) * 2;
|
||||
if (off_in_chunk >= 8) {
|
||||
off_in_chunk -= 8;
|
||||
cly++;
|
||||
}
|
||||
clx += off_in_chunk;
|
||||
u32 clut_addr = psmct32_addr(clx, cly, 64) + tex.clutdest * 256;
|
||||
memcpy(&unscrambled_clut[i], vram.data() + clut_addr, 4);
|
||||
}
|
||||
|
||||
// lg::warn("Adding index texture {} from {}\n", texture_page.name, tex.name);
|
||||
texture_db.add_index_texture(texture_page.id, tex_id, index_out, unscrambled_clut,
|
||||
tex.w, tex.h, tex.name, texture_page.name, level_names);
|
||||
stats.successful_textures++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lg::die("Animated texture {} format {}\n", tex.name, tex.psm);
|
||||
}
|
||||
}
|
||||
|
||||
if (tex.psm == int(PSM::PSMT8) && tex.clutpsm == int(CPSM::PSMCT32)) {
|
||||
// will store output pixels, rgba (8888)
|
||||
std::vector<u32> out;
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
|
||||
|
@ -13,5 +15,6 @@ struct TPageResultStats {
|
|||
|
||||
TPageResultStats process_tpage(ObjectFileData& data,
|
||||
TextureDB& texture_db,
|
||||
const fs::path& output_path);
|
||||
const fs::path& output_path,
|
||||
const std::unordered_set<std::string>& animated_textures);
|
||||
} // namespace decompiler
|
||||
|
|
|
@ -163,7 +163,7 @@ void decompile(const fs::path& iso_data_path, const std::string& data_subfolder)
|
|||
auto textures_out = out_folder / "textures";
|
||||
file_util::create_dir_if_needed(textures_out);
|
||||
file_util::write_text_file(textures_out / "tpage-dir.txt",
|
||||
db.process_tpages(tex_db, textures_out));
|
||||
db.process_tpages(tex_db, textures_out, config));
|
||||
// texture replacements
|
||||
auto replacements_path = file_util::get_jak_project_dir() / "texture_replacements";
|
||||
if (fs::exists(replacements_path)) {
|
||||
|
|
|
@ -85,7 +85,7 @@ void add_all_textures_from_level(tfrag3::Level& lev,
|
|||
new_tex.w = tex.w;
|
||||
new_tex.h = tex.h;
|
||||
new_tex.debug_tpage_name = tex_db.tpage_names.at(tex.page);
|
||||
new_tex.debug_name = new_tex.debug_tpage_name + tex.name;
|
||||
new_tex.debug_name = tex.name;
|
||||
new_tex.data = tex.rgba_bytes;
|
||||
new_tex.combo_id = id;
|
||||
new_tex.load_to_pool = true;
|
||||
|
@ -241,6 +241,11 @@ void extract_common(const ObjectFileDB& db,
|
|||
add_all_textures_from_level(tfrag_level, dgo_name, tex_db);
|
||||
extract_art_groups_from_level(db, tex_db, {}, dgo_name, tfrag_level);
|
||||
|
||||
// put _all_ index textures in common.
|
||||
for (const auto& [id, tex] : tex_db.index_textures_by_combo_id) {
|
||||
tfrag_level.index_textures.push_back(tex);
|
||||
}
|
||||
|
||||
Serializer ser;
|
||||
tfrag_level.serialize(ser);
|
||||
auto compressed =
|
||||
|
|
|
@ -726,22 +726,22 @@ std::string debug_dump_to_ply(const std::vector<MercDraw>& draws,
|
|||
return result;
|
||||
}
|
||||
|
||||
int find_or_add_texture_to_level(tfrag3::Level& out,
|
||||
s32 find_or_add_texture_to_level(tfrag3::Level& out,
|
||||
const TextureDB& tex_db,
|
||||
const std::string& debug_name,
|
||||
u32 pc_combo_tex_id,
|
||||
const MercCtrlHeader& hdr,
|
||||
u8* eye_out,
|
||||
GameVersion version) {
|
||||
u32 idx_in_level_texture = UINT32_MAX;
|
||||
for (u32 i = 0; i < out.textures.size(); i++) {
|
||||
s32 idx_in_level_texture = INT32_MAX;
|
||||
for (s32 i = 0; i < (int)out.textures.size(); i++) {
|
||||
if (out.textures[i].combo_id == pc_combo_tex_id) {
|
||||
idx_in_level_texture = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx_in_level_texture == UINT32_MAX) {
|
||||
if (idx_in_level_texture == INT32_MAX) {
|
||||
// not added to level, add it
|
||||
auto tex_it = tex_db.textures.find(pc_combo_tex_id);
|
||||
if (tex_it == tex_db.textures.end()) {
|
||||
|
@ -792,6 +792,16 @@ int find_or_add_texture_to_level(tfrag3::Level& out,
|
|||
}
|
||||
}
|
||||
|
||||
// check anim output
|
||||
const auto& level_tex = out.textures.at(idx_in_level_texture);
|
||||
const auto& it = tex_db.animated_tex_output_to_anim_slot.find(level_tex.debug_name);
|
||||
if (it != tex_db.animated_tex_output_to_anim_slot.end()) {
|
||||
lg::error("Animated slot {} -> {}", level_tex.debug_name, it->second);
|
||||
return -int(it->second) - 1;
|
||||
} else {
|
||||
// lg::warn("no anim: {}", level_tex.debug_name);
|
||||
}
|
||||
|
||||
return idx_in_level_texture;
|
||||
}
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ int main(int argc, char** argv) {
|
|||
if (config.process_tpages || config.levels_extract) {
|
||||
auto textures_out = out_folder / "textures";
|
||||
file_util::create_dir_if_needed(textures_out);
|
||||
auto result = db.process_tpages(tex_db, textures_out);
|
||||
auto result = db.process_tpages(tex_db, textures_out, config);
|
||||
if (!result.empty() && config.process_tpages) {
|
||||
file_util::write_text_file(textures_out / "tpage-dir.txt", result);
|
||||
file_util::write_text_file(textures_out / "tex-remap.txt",
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "game/graphics/opengl_renderer/OpenGLRenderer.h"
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "game/graphics/opengl_renderer/Fbo.h"
|
||||
|
||||
BlitDisplays::BlitDisplays(const std::string& name, int my_id) : BucketRenderer(name, my_id) {}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "game/graphics/opengl_renderer/Profiler.h"
|
||||
#include "game/graphics/opengl_renderer/Shader.h"
|
||||
#include "game/graphics/opengl_renderer/TextureAnimator.h"
|
||||
#include "game/graphics/opengl_renderer/buckets.h"
|
||||
#include "game/graphics/opengl_renderer/loader/Loader.h"
|
||||
#include "game/graphics/texture/TexturePool.h"
|
||||
|
@ -32,7 +31,6 @@ struct SharedRenderState {
|
|||
ShaderLibrary shaders;
|
||||
std::shared_ptr<TexturePool> texture_pool;
|
||||
std::shared_ptr<Loader> loader;
|
||||
std::shared_ptr<TextureAnimator> texture_animator;
|
||||
|
||||
u32 buckets_base = 0; // address of buckets array.
|
||||
u32 next_bucket = 0; // address of next bucket that we haven't started rendering in buckets
|
||||
|
|
|
@ -140,6 +140,13 @@ void DirectRenderer::reset_state() {
|
|||
m_stats = {};
|
||||
}
|
||||
|
||||
void DirectRenderer::init_shaders(ShaderLibrary& sl) {
|
||||
auto id = sl[ShaderId::DIRECT_BASIC_TEXTURED].id();
|
||||
m_uniforms.alpha_min = glGetUniformLocation(id, "alpha_min");
|
||||
m_uniforms.alpha_max = glGetUniformLocation(id, "alpha_max");
|
||||
m_uniforms.normal_shader_id = id;
|
||||
}
|
||||
|
||||
void DirectRenderer::draw_debug_window() {
|
||||
ImGui::Checkbox("Wireframe", &m_debug_state.wireframe);
|
||||
ImGui::SameLine();
|
||||
|
@ -252,8 +259,40 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state, ScopedProfil
|
|||
game_height[render_state->version], viewport_size[2], viewport_size[3]);
|
||||
|
||||
int draw_count = 0;
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
|
||||
draw_count++;
|
||||
int num_tris = 0;
|
||||
|
||||
if (m_test_state_needs_double_draw && current_shader == m_uniforms.normal_shader_id) {
|
||||
// this batch thing is a hack to make the sky in jak 2 draw correctly.
|
||||
// This is the usual atest with FB_ONLY issue.
|
||||
// we should check what pcsx2 does
|
||||
int n_batch = m_prim_buffer.vert_count;
|
||||
if (n_batch > 50 && n_batch < 700 && (n_batch % 2) == 0) {
|
||||
n_batch = n_batch / 2;
|
||||
} else {
|
||||
// printf("not splitting batch %d\n", n_batch);
|
||||
}
|
||||
int offset = 0;
|
||||
while (offset < m_prim_buffer.vert_count) {
|
||||
glDepthMask(GL_TRUE);
|
||||
glUniform1f(m_uniforms.alpha_min, m_double_draw_aref);
|
||||
glUniform1f(m_uniforms.alpha_max, 10);
|
||||
glDrawArrays(GL_TRIANGLES, offset, n_batch);
|
||||
glDepthMask(GL_FALSE);
|
||||
glUniform1f(m_uniforms.alpha_min, -10);
|
||||
glUniform1f(m_uniforms.alpha_max, m_double_draw_aref);
|
||||
glDrawArrays(GL_TRIANGLES, offset, n_batch);
|
||||
offset += n_batch;
|
||||
draw_count += 2;
|
||||
num_tris += n_batch / 3;
|
||||
}
|
||||
m_test_state_needs_double_draw = false;
|
||||
m_test_state_needs_gl_update = true;
|
||||
m_prim_gl_state_needs_gl_update = true;
|
||||
} else {
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_prim_buffer.vert_count);
|
||||
num_tris += m_prim_buffer.vert_count / 3;
|
||||
draw_count++;
|
||||
}
|
||||
|
||||
if (m_debug_state.wireframe) {
|
||||
render_state->shaders[ShaderId::DEBUG_RED].activate();
|
||||
|
@ -268,10 +307,9 @@ void DirectRenderer::flush_pending(SharedRenderState* render_state, ScopedProfil
|
|||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindVertexArray(0);
|
||||
int n_tris = draw_count * (m_prim_buffer.vert_count / 3);
|
||||
prof.add_tri(n_tris);
|
||||
prof.add_tri(num_tris);
|
||||
prof.add_draw_call(draw_count);
|
||||
m_stats.triangles += n_tris;
|
||||
m_stats.triangles += num_tris;
|
||||
m_stats.draw_calls += draw_count;
|
||||
m_prim_buffer.vert_count = 0;
|
||||
}
|
||||
|
@ -280,14 +318,16 @@ void DirectRenderer::update_gl_prim(SharedRenderState* render_state) {
|
|||
// currently gouraud is handled in setup.
|
||||
const auto& state = m_prim_gl_state;
|
||||
if (state.texture_enable) {
|
||||
float alpha_reject = 0.0;
|
||||
float alpha_min = 0.0;
|
||||
float alpha_max = 10;
|
||||
if (m_test_state.alpha_test_enable) {
|
||||
switch (m_test_state.alpha_test) {
|
||||
case GsTest::AlphaTest::ALWAYS:
|
||||
break;
|
||||
case GsTest::AlphaTest::GEQUAL:
|
||||
case GsTest::AlphaTest::GREATER: // todo
|
||||
alpha_reject = m_test_state.aref / 128.f;
|
||||
alpha_min = m_test_state.aref / 128.f;
|
||||
m_double_draw_aref = alpha_min;
|
||||
break;
|
||||
case GsTest::AlphaTest::NEVER:
|
||||
break;
|
||||
|
@ -298,8 +338,11 @@ void DirectRenderer::update_gl_prim(SharedRenderState* render_state) {
|
|||
|
||||
render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].activate();
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].id(),
|
||||
"alpha_reject"),
|
||||
alpha_reject);
|
||||
"alpha_min"),
|
||||
alpha_min);
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].id(),
|
||||
"alpha_max"),
|
||||
alpha_max);
|
||||
glUniform1f(glGetUniformLocation(render_state->shaders[ShaderId::DIRECT_BASIC_TEXTURED].id(),
|
||||
"color_mult"),
|
||||
m_ogl.color_mult);
|
||||
|
@ -475,6 +518,14 @@ void DirectRenderer::update_gl_test() {
|
|||
bool alpha_trick_to_disable = m_test_state.alpha_test_enable &&
|
||||
m_test_state.alpha_test == GsTest::AlphaTest::NEVER &&
|
||||
m_test_state.afail == GsTest::AlphaFail::FB_ONLY;
|
||||
|
||||
if (m_test_state.afail == GsTest::AlphaFail::FB_ONLY ||
|
||||
m_test_state.afail == GsTest::AlphaFail::RGB_ONLY) {
|
||||
m_test_state_needs_double_draw = true;
|
||||
} else {
|
||||
m_test_state_needs_double_draw = false;
|
||||
}
|
||||
|
||||
if (state.depth_writes && !alpha_trick_to_disable) {
|
||||
glDepthMask(GL_TRUE);
|
||||
} else {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
class DirectRenderer : public BucketRenderer {
|
||||
public:
|
||||
DirectRenderer(const std::string& name, int my_id, int batch_size);
|
||||
void init_shaders(ShaderLibrary& sl) override;
|
||||
~DirectRenderer();
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
|
||||
virtual void pre_render() {}
|
||||
|
@ -289,6 +290,11 @@ class DirectRenderer : public BucketRenderer {
|
|||
float alpha_mult = 1.0;
|
||||
} m_ogl;
|
||||
|
||||
struct {
|
||||
GLint alpha_min, alpha_max;
|
||||
GLint normal_shader_id = -1;
|
||||
} m_uniforms;
|
||||
|
||||
struct {
|
||||
bool disable_texture = false;
|
||||
bool wireframe = false;
|
||||
|
@ -314,6 +320,8 @@ class DirectRenderer : public BucketRenderer {
|
|||
|
||||
bool m_prim_gl_state_needs_gl_update = true;
|
||||
bool m_test_state_needs_gl_update = true;
|
||||
bool m_test_state_needs_double_draw = false;
|
||||
float m_double_draw_aref = 0;
|
||||
bool m_blend_state_needs_gl_update = true;
|
||||
|
||||
struct SpriteMode {
|
||||
|
|
51
game/graphics/opengl_renderer/Fbo.h
Normal file
51
game/graphics/opengl_renderer/Fbo.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
|
||||
struct Fbo {
|
||||
bool valid = false; // do we have an OpenGL fbo_id?
|
||||
GLuint fbo_id = -1;
|
||||
|
||||
// optional rgba/zbuffer/stencil data.
|
||||
std::optional<GLuint> tex_id;
|
||||
std::optional<GLuint> zbuf_stencil_id;
|
||||
|
||||
bool multisampled = false;
|
||||
int multisample_count = 0; // Should be 1 if multisampled is disabled
|
||||
|
||||
bool is_window = false;
|
||||
int width = 640;
|
||||
int height = 480;
|
||||
|
||||
// Does this fbo match the given format? MSAA = 1 will accept a normal buffer, or a multisample 1x
|
||||
bool matches(int w, int h, int msaa) const {
|
||||
int effective_msaa = multisampled ? multisample_count : 1;
|
||||
return valid && width == w && height == h && effective_msaa == msaa;
|
||||
}
|
||||
|
||||
bool matches(const Fbo& other) const {
|
||||
return matches(other.width, other.height, other.multisample_count);
|
||||
}
|
||||
|
||||
// Free opengl resources, if we have any.
|
||||
void clear() {
|
||||
if (valid) {
|
||||
glDeleteFramebuffers(1, &fbo_id);
|
||||
fbo_id = -1;
|
||||
|
||||
if (tex_id) {
|
||||
glDeleteTextures(1, &tex_id.value());
|
||||
tex_id.reset();
|
||||
}
|
||||
|
||||
if (zbuf_stencil_id) {
|
||||
glDeleteRenderbuffers(1, &zbuf_stencil_id.value());
|
||||
zbuf_stencil_id.reset();
|
||||
}
|
||||
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -85,7 +85,25 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool,
|
|||
|
||||
lg::debug("OpenGL context information: {}", (const char*)glGetString(GL_VERSION));
|
||||
|
||||
m_merc2 = std::make_shared<Merc2>(m_render_state.shaders);
|
||||
const tfrag3::Level* common_level = nullptr;
|
||||
{
|
||||
auto p = scoped_prof("load-common");
|
||||
common_level = &m_render_state.loader->load_common(*m_render_state.texture_pool, "GAME");
|
||||
}
|
||||
|
||||
// initialize all renderers
|
||||
switch (m_version) {
|
||||
case GameVersion::Jak1:
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
m_texture_animator = std::make_shared<TextureAnimator>(m_render_state.shaders, common_level);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
m_merc2 = std::make_shared<Merc2>(m_render_state.shaders,
|
||||
m_texture_animator ? m_texture_animator->slots() : nullptr);
|
||||
m_generic2 = std::make_shared<Generic2>(m_render_state.shaders);
|
||||
|
||||
// initialize all renderers
|
||||
|
@ -95,7 +113,6 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool,
|
|||
init_bucket_renderers_jak1();
|
||||
break;
|
||||
case GameVersion::Jak2:
|
||||
m_render_state.texture_animator = std::make_shared<TextureAnimator>();
|
||||
init_bucket_renderers_jak2();
|
||||
break;
|
||||
default:
|
||||
|
@ -112,7 +129,7 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
init_bucket_renderer<VisDataHandler>("vis", BucketCategory::OTHER, BucketId::BUCKET_2);
|
||||
init_bucket_renderer<BlitDisplays>("blit", BucketCategory::OTHER, BucketId::BUCKET_3);
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-lcom-sky-pre", BucketCategory::TEX,
|
||||
BucketId::TEX_LCOM_SKY_PRE);
|
||||
BucketId::TEX_LCOM_SKY_PRE, m_texture_animator);
|
||||
init_bucket_renderer<DirectRenderer>("sky-draw", BucketCategory::OTHER, BucketId::SKY_DRAW, 1024);
|
||||
init_bucket_renderer<OceanMidAndFar>("ocean-mid-far", BucketCategory::OCEAN,
|
||||
BucketId::OCEAN_MID_FAR);
|
||||
|
@ -121,7 +138,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
#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));
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_TFRAG, BucketId::TEX_L1_TFRAG, i),
|
||||
m_texture_animator);
|
||||
init_bucket_renderer<TFragment>(
|
||||
fmt::format("tfrag-l{}-tfrag", i), BucketCategory::TFRAG,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TFRAG_L0_TFRAG, BucketId::TFRAG_L1_TFRAG, i),
|
||||
|
@ -143,7 +161,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
|
||||
init_bucket_renderer<TextureUploadHandler>(
|
||||
fmt::format("tex-l{}-shrub", i), BucketCategory::TEX,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_SHRUB, BucketId::TEX_L1_SHRUB, i));
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_SHRUB, BucketId::TEX_L1_SHRUB, i),
|
||||
m_texture_animator);
|
||||
init_bucket_renderer<Shrub>(
|
||||
fmt::format("shrub-l{}-shrub", i), BucketCategory::SHRUB,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::SHRUB_L0_SHRUB, BucketId::SHRUB_L1_SHRUB, i));
|
||||
|
@ -157,7 +176,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
|
||||
init_bucket_renderer<TextureUploadHandler>(
|
||||
fmt::format("tex-l{}-alpha", i), BucketCategory::TEX,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_ALPHA, BucketId::TEX_L1_ALPHA, i));
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_ALPHA, BucketId::TEX_L1_ALPHA, i),
|
||||
m_texture_animator);
|
||||
init_bucket_renderer<TFragment>(
|
||||
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),
|
||||
|
@ -180,7 +200,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
|
||||
init_bucket_renderer<TextureUploadHandler>(
|
||||
fmt::format("tex-l{}-pris", i), BucketCategory::TEX,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_PRIS, BucketId::TEX_L1_PRIS, i));
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_PRIS, BucketId::TEX_L1_PRIS, i),
|
||||
m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>(
|
||||
fmt::format("merc-l{}-pris", i), BucketCategory::MERC,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS, BucketId::MERC_L1_PRIS, i), m_merc2);
|
||||
|
@ -191,7 +212,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
|
||||
init_bucket_renderer<TextureUploadHandler>(
|
||||
fmt::format("tex-l{}-pris2", i), BucketCategory::TEX,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_PRIS2, BucketId::TEX_L1_PRIS2, i));
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_PRIS2, BucketId::TEX_L1_PRIS2, i),
|
||||
m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>(
|
||||
fmt::format("merc-l{}-pris2", i), BucketCategory::MERC,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS2, BucketId::MERC_L1_PRIS2, i), m_merc2);
|
||||
|
@ -202,7 +224,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
|
||||
init_bucket_renderer<TextureUploadHandler>(
|
||||
fmt::format("tex-l{}-water", i), BucketCategory::TEX,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_WATER, BucketId::TEX_L1_WATER, i));
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_WATER, BucketId::TEX_L1_WATER, i),
|
||||
m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>(
|
||||
fmt::format("merc-l{}-water", i), BucketCategory::MERC,
|
||||
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_WATER, BucketId::MERC_L1_WATER, i), m_merc2);
|
||||
|
@ -226,12 +249,12 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
}
|
||||
// 180
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-lcom-tfrag", BucketCategory::TEX,
|
||||
BucketId::TEX_LCOM_TFRAG);
|
||||
BucketId::TEX_LCOM_TFRAG, m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-tfrag", BucketCategory::MERC,
|
||||
BucketId::MERC_LCOM_TFRAG, m_merc2);
|
||||
// 190
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-lcom-shrub", BucketCategory::TEX,
|
||||
BucketId::TEX_LCOM_SHRUB);
|
||||
BucketId::TEX_LCOM_SHRUB, m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-shrub", BucketCategory::MERC,
|
||||
BucketId::MERC_LCOM_SHRUB, m_merc2);
|
||||
init_bucket_renderer<Generic2BucketRenderer>("gmerc-lcom-tfrag", BucketCategory::GENERIC,
|
||||
|
@ -240,30 +263,30 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
init_bucket_renderer<Shadow2>("shadow", BucketCategory::OTHER, BucketId::SHADOW);
|
||||
// 220
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-lcom-pris", BucketCategory::TEX,
|
||||
BucketId::TEX_LCOM_PRIS);
|
||||
BucketId::TEX_LCOM_PRIS, m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-pris", BucketCategory::MERC,
|
||||
BucketId::MERC_LCOM_PRIS, m_merc2);
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-lcom-water", BucketCategory::TEX,
|
||||
BucketId::TEX_LCOM_WATER);
|
||||
BucketId::TEX_LCOM_WATER, m_texture_animator);
|
||||
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-water", BucketCategory::MERC,
|
||||
BucketId::MERC_LCOM_WATER, m_merc2);
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-lcom-sky-post", BucketCategory::TEX,
|
||||
BucketId::TEX_LCOM_SKY_POST);
|
||||
BucketId::TEX_LCOM_SKY_POST, m_texture_animator);
|
||||
// 310
|
||||
init_bucket_renderer<OceanNear>("ocean-near", BucketCategory::OCEAN, BucketId::OCEAN_NEAR);
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-all-sprite", BucketCategory::TEX,
|
||||
BucketId::TEX_ALL_SPRITE);
|
||||
BucketId::TEX_ALL_SPRITE, m_texture_animator);
|
||||
init_bucket_renderer<Sprite3>("particles", BucketCategory::SPRITE, BucketId::PARTICLES);
|
||||
init_bucket_renderer<Shadow2>("shadow2", BucketCategory::OTHER, BucketId::SHADOW2);
|
||||
init_bucket_renderer<Generic2BucketRenderer>("effects", BucketCategory::OTHER, BucketId::EFFECTS,
|
||||
m_generic2, Generic2::Mode::LIGHTNING);
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-all-warp", BucketCategory::TEX,
|
||||
BucketId::TEX_ALL_WARP);
|
||||
BucketId::TEX_ALL_WARP, m_texture_animator);
|
||||
init_bucket_renderer<Warp>("warp", BucketCategory::GENERIC, BucketId::GMERC_WARP, m_generic2);
|
||||
init_bucket_renderer<DirectRenderer>("debug-no-zbuf1", BucketCategory::OTHER,
|
||||
BucketId::DEBUG_NO_ZBUF1, 0x8000);
|
||||
init_bucket_renderer<TextureUploadHandler>("tex-all-map", BucketCategory::TEX,
|
||||
BucketId::TEX_ALL_MAP);
|
||||
BucketId::TEX_ALL_MAP, m_texture_animator);
|
||||
// 320
|
||||
init_bucket_renderer<ProgressRenderer>("progress", BucketCategory::OTHER, BucketId::PROGRESS,
|
||||
0x1000);
|
||||
|
@ -295,10 +318,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
|
|||
m_jak2_eye_renderer->init_shaders(m_render_state.shaders);
|
||||
m_jak2_eye_renderer->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
|
||||
}
|
||||
|
||||
auto p = scoped_prof("load-common");
|
||||
m_render_state.loader->load_common(*m_render_state.texture_pool, "GAME");
|
||||
}
|
||||
|
||||
/*!
|
||||
* Construct bucket renderers. We can specify different renderers for different buckets
|
||||
*/
|
||||
|
@ -331,7 +352,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
//-----------------------
|
||||
// 5 : TFRAG_TEX_LEVEL0
|
||||
init_bucket_renderer<TextureUploadHandler>("l0-tfrag-tex", BucketCategory::TEX,
|
||||
BucketId::TFRAG_TEX_LEVEL0);
|
||||
BucketId::TFRAG_TEX_LEVEL0, m_texture_animator);
|
||||
// 6 : TFRAG_LEVEL0
|
||||
init_bucket_renderer<TFragment>("l0-tfrag-tfrag", BucketCategory::TFRAG, BucketId::TFRAG_LEVEL0,
|
||||
normal_tfrags, false, 0);
|
||||
|
@ -353,7 +374,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
//-----------------------
|
||||
// 12 : TFRAG_TEX_LEVEL1
|
||||
init_bucket_renderer<TextureUploadHandler>("l1-tfrag-tex", BucketCategory::TEX,
|
||||
BucketId::TFRAG_TEX_LEVEL1);
|
||||
BucketId::TFRAG_TEX_LEVEL1, m_texture_animator);
|
||||
// 13 : TFRAG_LEVEL1
|
||||
init_bucket_renderer<TFragment>("l1-tfrag-tfrag", BucketCategory::TFRAG, BucketId::TFRAG_LEVEL1,
|
||||
normal_tfrags, false, 1);
|
||||
|
@ -375,7 +396,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
//-----------------------
|
||||
// 19 : SHRUB_TEX_LEVEL0
|
||||
init_bucket_renderer<TextureUploadHandler>("l0-shrub-tex", BucketCategory::TEX,
|
||||
BucketId::SHRUB_TEX_LEVEL0);
|
||||
BucketId::SHRUB_TEX_LEVEL0, m_texture_animator);
|
||||
// 20 : SHRUB_NORMAL_LEVEL0
|
||||
init_bucket_renderer<Shrub>("l0-shrub", BucketCategory::SHRUB, BucketId::SHRUB_NORMAL_LEVEL0);
|
||||
// 21 : ???
|
||||
|
@ -391,7 +412,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
//-----------------------
|
||||
// 25 : SHRUB_TEX_LEVEL1
|
||||
init_bucket_renderer<TextureUploadHandler>("l1-shrub-tex", BucketCategory::TEX,
|
||||
BucketId::SHRUB_TEX_LEVEL1);
|
||||
BucketId::SHRUB_TEX_LEVEL1, m_texture_animator);
|
||||
// 26 : SHRUB_NORMAL_LEVEL1
|
||||
init_bucket_renderer<Shrub>("l1-shrub", BucketCategory::SHRUB, BucketId::SHRUB_NORMAL_LEVEL1);
|
||||
// 27 : ???
|
||||
|
@ -406,7 +427,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// LEVEL 0 alpha texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("l0-alpha-tex", BucketCategory::TEX,
|
||||
BucketId::ALPHA_TEX_LEVEL0); // 31
|
||||
BucketId::ALPHA_TEX_LEVEL0, m_texture_animator); // 31
|
||||
init_bucket_renderer<SkyBlendHandler>("l0-alpha-sky-blend-and-tfrag-trans", BucketCategory::OTHER,
|
||||
BucketId::TFRAG_TRANS0_AND_SKY_BLEND_LEVEL0, 0,
|
||||
sky_gpu_blender, sky_cpu_blender); // 32
|
||||
|
@ -423,7 +444,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// LEVEL 1 alpha texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("l1-alpha-tex", BucketCategory::TEX,
|
||||
BucketId::ALPHA_TEX_LEVEL1); // 38
|
||||
BucketId::ALPHA_TEX_LEVEL1, m_texture_animator); // 38
|
||||
init_bucket_renderer<SkyBlendHandler>("l1-alpha-sky-blend-and-tfrag-trans", BucketCategory::OTHER,
|
||||
BucketId::TFRAG_TRANS1_AND_SKY_BLEND_LEVEL1, 1,
|
||||
sky_gpu_blender, sky_cpu_blender); // 39
|
||||
|
@ -448,7 +469,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// LEVEL 0 pris texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("l0-pris-tex", BucketCategory::TEX,
|
||||
BucketId::PRIS_TEX_LEVEL0); // 48
|
||||
BucketId::PRIS_TEX_LEVEL0, m_texture_animator); // 48
|
||||
init_bucket_renderer<Merc2BucketRenderer>("l0-pris-merc", BucketCategory::MERC,
|
||||
BucketId::MERC_PRIS_LEVEL0, m_merc2); // 49
|
||||
init_bucket_renderer<Generic2BucketRenderer>("l0-pris-generic", BucketCategory::GENERIC,
|
||||
|
@ -459,7 +480,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// LEVEL 1 pris texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("l1-pris-tex", BucketCategory::TEX,
|
||||
BucketId::PRIS_TEX_LEVEL1); // 51
|
||||
BucketId::PRIS_TEX_LEVEL1, m_texture_animator); // 51
|
||||
init_bucket_renderer<Merc2BucketRenderer>("l1-pris-merc", BucketCategory::MERC,
|
||||
BucketId::MERC_PRIS_LEVEL1, m_merc2); // 52
|
||||
init_bucket_renderer<Generic2BucketRenderer>("l1-pris-generic", BucketCategory::GENERIC,
|
||||
|
@ -481,7 +502,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// LEVEL 0 water texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("l0-water-tex", BucketCategory::TEX,
|
||||
BucketId::WATER_TEX_LEVEL0); // 57
|
||||
BucketId::WATER_TEX_LEVEL0, m_texture_animator); // 57
|
||||
init_bucket_renderer<Merc2BucketRenderer>("l0-water-merc", BucketCategory::MERC,
|
||||
BucketId::MERC_WATER_LEVEL0, m_merc2); // 58
|
||||
init_bucket_renderer<Generic2BucketRenderer>("l0-water-generic", BucketCategory::GENERIC,
|
||||
|
@ -492,7 +513,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// LEVEL 1 water texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("l1-water-tex", BucketCategory::TEX,
|
||||
BucketId::WATER_TEX_LEVEL1); // 60
|
||||
BucketId::WATER_TEX_LEVEL1, m_texture_animator); // 60
|
||||
init_bucket_renderer<Merc2BucketRenderer>("l1-water-merc", BucketCategory::MERC,
|
||||
BucketId::MERC_WATER_LEVEL1, m_merc2); // 61
|
||||
init_bucket_renderer<Generic2BucketRenderer>("l1-water-generic", BucketCategory::GENERIC,
|
||||
|
@ -510,7 +531,7 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
// COMMON texture
|
||||
//-----------------------
|
||||
init_bucket_renderer<TextureUploadHandler>("common-tex", BucketCategory::TEX,
|
||||
BucketId::PRE_SPRITE_TEX); // 65
|
||||
BucketId::PRE_SPRITE_TEX, m_texture_animator); // 65
|
||||
|
||||
init_bucket_renderer<Sprite3>("sprite", BucketCategory::SPRITE, BucketId::SPRITE); // 66
|
||||
|
||||
|
@ -532,7 +553,6 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
|
|||
}
|
||||
sky_cpu_blender->init_textures(*m_render_state.texture_pool, m_version);
|
||||
sky_gpu_blender->init_textures(*m_render_state.texture_pool, m_version);
|
||||
m_render_state.loader->load_common(*m_render_state.texture_pool, "GAME");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/CollideMeshRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/Fbo.h"
|
||||
#include "game/graphics/opengl_renderer/Profiler.h"
|
||||
#include "game/graphics/opengl_renderer/Shader.h"
|
||||
#include "game/graphics/opengl_renderer/TextureAnimator.h"
|
||||
#include "game/graphics/opengl_renderer/foreground/Generic2.h"
|
||||
#include "game/graphics/opengl_renderer/foreground/Merc2.h"
|
||||
#include "game/graphics/opengl_renderer/opengl_utils.h"
|
||||
|
@ -51,52 +53,6 @@ struct RenderOptions {
|
|||
bool gpu_sync = false;
|
||||
};
|
||||
|
||||
struct Fbo {
|
||||
bool valid = false; // do we have an OpenGL fbo_id?
|
||||
GLuint fbo_id = -1;
|
||||
|
||||
// optional rgba/zbuffer/stencil data.
|
||||
std::optional<GLuint> tex_id;
|
||||
std::optional<GLuint> zbuf_stencil_id;
|
||||
|
||||
bool multisampled = false;
|
||||
int multisample_count = 0; // Should be 1 if multisampled is disabled
|
||||
|
||||
bool is_window = false;
|
||||
int width = 640;
|
||||
int height = 480;
|
||||
|
||||
// Does this fbo match the given format? MSAA = 1 will accept a normal buffer, or a multisample 1x
|
||||
bool matches(int w, int h, int msaa) const {
|
||||
int effective_msaa = multisampled ? multisample_count : 1;
|
||||
return valid && width == w && height == h && effective_msaa == msaa;
|
||||
}
|
||||
|
||||
bool matches(const Fbo& other) const {
|
||||
return matches(other.width, other.height, other.multisample_count);
|
||||
}
|
||||
|
||||
// Free opengl resources, if we have any.
|
||||
void clear() {
|
||||
if (valid) {
|
||||
glDeleteFramebuffers(1, &fbo_id);
|
||||
fbo_id = -1;
|
||||
|
||||
if (tex_id) {
|
||||
glDeleteTextures(1, &tex_id.value());
|
||||
tex_id.reset();
|
||||
}
|
||||
|
||||
if (zbuf_stencil_id) {
|
||||
glDeleteRenderbuffers(1, &zbuf_stencil_id.value());
|
||||
zbuf_stencil_id.reset();
|
||||
}
|
||||
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Main OpenGL renderer.
|
||||
* This handles the glClear and all game rendering, but not actual setup, synchronization or imgui
|
||||
|
@ -150,6 +106,7 @@ class OpenGLRenderer {
|
|||
|
||||
std::shared_ptr<Merc2> m_merc2;
|
||||
std::shared_ptr<Generic2> m_generic2;
|
||||
std::shared_ptr<TextureAnimator> m_texture_animator;
|
||||
std::vector<std::unique_ptr<BucketRenderer>> m_bucket_renderers;
|
||||
std::vector<BucketCategory> m_bucket_categories;
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ ShaderLibrary::ShaderLibrary(GameVersion version) {
|
|||
at(ShaderId::ETIE_BASE) = {"etie_base", version};
|
||||
at(ShaderId::ETIE) = {"etie", version};
|
||||
at(ShaderId::SHADOW2) = {"shadow2", version};
|
||||
at(ShaderId::TEX_ANIM) = {"tex_anim", version};
|
||||
|
||||
for (auto& shader : m_shaders) {
|
||||
ASSERT_MSG(shader.okay(), "error compiling shader");
|
||||
|
|
|
@ -58,6 +58,7 @@ enum class ShaderId {
|
|||
ETIE = 31,
|
||||
SHADOW2 = 32,
|
||||
DIRECT_BASIC_TEXTURED_MULTI_UNIT = 33,
|
||||
TEX_ANIM = 34,
|
||||
MAX_SHADERS
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,215 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "common/dma/dma_chain_read.h"
|
||||
#include "common/dma/gs.h"
|
||||
#include "common/math/Vector.h"
|
||||
|
||||
#include "game/graphics/opengl_renderer/Shader.h"
|
||||
#include "game/graphics/opengl_renderer/opengl_utils.h"
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "game/graphics/texture/TextureConverter.h"
|
||||
#include "game/graphics/texture/TextureID.h"
|
||||
|
||||
struct GpuTexture;
|
||||
|
||||
struct VramEntry {
|
||||
enum class Kind { CLUT16_16_IN_PSM32, GENERIC_PSM32, GENERIC_PSMT8, GPU, INVALID } kind;
|
||||
std::vector<u8> data;
|
||||
|
||||
int tex_width = 0;
|
||||
int tex_height = 0;
|
||||
int dest_texture_address = 0;
|
||||
int cbp = 0;
|
||||
std::optional<FramebufferTexturePair> tex;
|
||||
// math::Vector<u8, 4> rgba_clear;
|
||||
|
||||
bool needs_pool_update = false;
|
||||
GpuTexture* pool_gpu_tex = nullptr;
|
||||
|
||||
void reset() {
|
||||
data.clear();
|
||||
kind = Kind::INVALID;
|
||||
tex_height = 0;
|
||||
tex_width = 0;
|
||||
cbp = 0;
|
||||
// tex.reset();
|
||||
needs_pool_update = false;
|
||||
// pool_gpu_tex = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct ShaderContext {
|
||||
GsTex0 tex0;
|
||||
GsTex1 tex1;
|
||||
GsTest test;
|
||||
bool clamp_u, clamp_v;
|
||||
GsAlpha alpha;
|
||||
bool source_texture_set = false;
|
||||
};
|
||||
|
||||
struct OpenGLTexturePool {
|
||||
OpenGLTexturePool();
|
||||
~OpenGLTexturePool();
|
||||
GLuint allocate(u64 w, u64 h);
|
||||
void free(GLuint texture, u64 w, u64 h);
|
||||
std::unordered_map<u64, std::vector<GLuint>> textures;
|
||||
};
|
||||
|
||||
class ClutBlender {
|
||||
public:
|
||||
ClutBlender(const std::string& dest,
|
||||
const std::vector<std::string>& sources,
|
||||
const std::optional<std::string>& level_name,
|
||||
const tfrag3::Level* level,
|
||||
OpenGLTexturePool* tpool);
|
||||
GLuint run(const float* weights);
|
||||
GLuint texture() const { return m_texture; }
|
||||
|
||||
private:
|
||||
const tfrag3::IndexTexture* m_dest;
|
||||
std::vector<const std::array<math::Vector4<u8>, 256>*> m_cluts;
|
||||
std::vector<float> m_current_weights;
|
||||
GLuint m_texture;
|
||||
std::array<math::Vector4<u8>, 256> m_temp_clut;
|
||||
std::vector<u32> m_temp_rgba;
|
||||
};
|
||||
|
||||
class TexturePool;
|
||||
|
||||
class TextureAnimator {
|
||||
public:
|
||||
void handle_texture_anim_data(DmaFollower& dma);
|
||||
TextureAnimator(ShaderLibrary& shaders, const tfrag3::Level* common_level);
|
||||
~TextureAnimator();
|
||||
void handle_texture_anim_data(DmaFollower& dma, const u8* ee_mem, TexturePool* texture_pool);
|
||||
GLuint get_by_slot(int idx);
|
||||
const std::vector<GLuint>* slots() { return &m_output_slots; }
|
||||
|
||||
private:
|
||||
void handle_upload_clut_16_16(const DmaTransfer& tf, const u8* ee_mem);
|
||||
void handle_generic_upload(const DmaTransfer& tf, const u8* ee_mem);
|
||||
void handle_erase_dest(DmaFollower& dma);
|
||||
void handle_set_shader(DmaFollower& dma);
|
||||
void handle_draw(DmaFollower& dma, TexturePool& texture_pool);
|
||||
void handle_rg_to_ba(const DmaTransfer& tf);
|
||||
void handle_set_clut_alpha(const DmaTransfer& tf);
|
||||
void handle_copy_clut_alpha(const DmaTransfer& tf);
|
||||
|
||||
VramEntry* setup_vram_entry_for_gpu_texture(int w, int h, int tbp);
|
||||
|
||||
void set_up_opengl_for_shader(const ShaderContext& shader,
|
||||
std::optional<GLuint> texture,
|
||||
bool prim_abe);
|
||||
|
||||
void load_clut_to_converter();
|
||||
const u32* get_clut_16_16_psm32(int cbp);
|
||||
|
||||
GLuint make_temp_gpu_texture(const u32* data, u32 width, u32 height);
|
||||
|
||||
GLuint make_or_get_gpu_texture_for_current_shader(TexturePool& texture_pool);
|
||||
void force_to_gpu(int tbp);
|
||||
|
||||
struct DrawData {
|
||||
u8 tmpl1[16];
|
||||
math::Vector<u32, 4> color;
|
||||
|
||||
math::Vector<float, 4> st0;
|
||||
math::Vector<u32, 4> pos0;
|
||||
|
||||
math::Vector<float, 4> st1;
|
||||
math::Vector<u32, 4> pos1;
|
||||
|
||||
math::Vector<float, 4> st2;
|
||||
math::Vector<u32, 4> pos2;
|
||||
|
||||
math::Vector<float, 4> st3;
|
||||
math::Vector<u32, 4> pos3;
|
||||
};
|
||||
|
||||
void set_uniforms_from_draw_data(const DrawData& dd, int dest_w, int dest_h);
|
||||
|
||||
PcTextureId get_id_for_tbp(TexturePool* pool, u32 tbp);
|
||||
|
||||
VramEntry* m_tex_looking_for_clut = nullptr;
|
||||
const tfrag3::Level* m_common_level = nullptr;
|
||||
std::unordered_map<u32, VramEntry> m_textures;
|
||||
std::unordered_map<u32, PcTextureId> m_ids_by_vram;
|
||||
|
||||
std::set<u32> m_erased_on_this_frame;
|
||||
|
||||
struct TempTexture {
|
||||
GLuint tex;
|
||||
u32 w, h;
|
||||
};
|
||||
std::vector<TempTexture> m_in_use_temp_textures;
|
||||
|
||||
ShaderContext m_current_shader;
|
||||
TextureConverter m_converter;
|
||||
int m_current_dest_tbp = -1;
|
||||
|
||||
GLuint m_vao;
|
||||
GLuint m_vertex_buffer;
|
||||
struct Vertex {
|
||||
u32 index;
|
||||
u32 pad1;
|
||||
u32 pad2;
|
||||
u32 pad3;
|
||||
};
|
||||
|
||||
struct {
|
||||
GLuint rgba;
|
||||
GLuint positions;
|
||||
GLuint uvs;
|
||||
GLuint enable_tex;
|
||||
GLuint channel_scramble;
|
||||
GLuint tcc;
|
||||
} m_uniforms;
|
||||
|
||||
GLuint m_shader_id;
|
||||
GLuint m_dummy_texture;
|
||||
|
||||
u8 m_index_to_clut_addr[256];
|
||||
OpenGLTexturePool m_opengl_texture_pool;
|
||||
|
||||
std::vector<GLuint> m_output_slots;
|
||||
|
||||
struct ClutBlenderGroup {
|
||||
std::vector<ClutBlender> blenders;
|
||||
std::vector<int> outputs;
|
||||
};
|
||||
std::vector<ClutBlenderGroup> m_clut_blender_groups;
|
||||
|
||||
int m_darkjak_clut_blender_idx = -1;
|
||||
int m_jakb_prison_clut_blender_idx = -1;
|
||||
int m_jakb_oracle_clut_blender_idx = -1;
|
||||
int m_jakb_nest_clut_blender_idx = -1;
|
||||
int m_kor_transform_clut_blender_idx = -1;
|
||||
|
||||
int create_clut_blender_group(const std::vector<std::string>& textures,
|
||||
const std::string& suffix0,
|
||||
const std::string& suffix1,
|
||||
const std::optional<std::string>& dgo);
|
||||
void add_to_clut_blender_group(int idx,
|
||||
const std::vector<std::string>& textures,
|
||||
const std::string& suffix0,
|
||||
const std::string& suffix1,
|
||||
const std::optional<std::string>& dgo);
|
||||
void run_clut_blender_group(DmaTransfer& tf, int idx);
|
||||
|
||||
// std::vector<ClutBlender> m_darkjak_blenders;
|
||||
// std::vector<int> m_darkjak_output_slots;
|
||||
//
|
||||
// std::vector<ClutBlender> m_jakb_prison_blenders;
|
||||
// std::vector<int> m_jakb_prison_output_slots;
|
||||
//
|
||||
// std::vector<ClutBlender> m_jakb_oracle_blenders;
|
||||
// std::vector<int> m_jakb_oracle_slots;
|
||||
//
|
||||
// std::vector<ClutBlender> m_jakb_nest_blenders;
|
||||
// std::vector<int> m_jakb_nest_slots;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "TextureUploadHandler.h"
|
||||
|
||||
#include "common/global_profiler/GlobalProfiler.h"
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "game/graphics/opengl_renderer/EyeRenderer.h"
|
||||
|
@ -8,8 +9,10 @@
|
|||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
TextureUploadHandler::TextureUploadHandler(const std::string& name, int my_id)
|
||||
: BucketRenderer(name, my_id) {}
|
||||
TextureUploadHandler::TextureUploadHandler(const std::string& name,
|
||||
int my_id,
|
||||
std::shared_ptr<TextureAnimator> texture_animator)
|
||||
: BucketRenderer(name, my_id), m_texture_animator(texture_animator) {}
|
||||
|
||||
void TextureUploadHandler::render(DmaFollower& dma,
|
||||
SharedRenderState* render_state,
|
||||
|
@ -25,7 +28,9 @@ void TextureUploadHandler::render(DmaFollower& dma,
|
|||
auto vif0 = dma.current_tag_vifcode0();
|
||||
if (vif0.kind == VifCode::Kind::PC_PORT && vif0.immediate == 12) {
|
||||
dma.read_and_advance();
|
||||
render_state->texture_animator->handle_texture_anim_data(dma);
|
||||
auto p = scoped_prof("texture-animator");
|
||||
m_texture_animator->handle_texture_anim_data(dma, (const u8*)render_state->ee_main_memory,
|
||||
render_state->texture_pool.get());
|
||||
}
|
||||
// does it look like data to do eye rendering?
|
||||
if (dma_tag.qwc == (128 / 16)) {
|
||||
|
@ -63,6 +68,7 @@ void TextureUploadHandler::render(DmaFollower& dma,
|
|||
|
||||
void TextureUploadHandler::flush_uploads(std::vector<TextureUpload>& uploads,
|
||||
SharedRenderState* render_state) {
|
||||
auto p = scoped_prof("flush-uploads");
|
||||
if (m_fake_uploads) {
|
||||
uploads.clear();
|
||||
} else {
|
||||
|
@ -73,7 +79,7 @@ void TextureUploadHandler::flush_uploads(std::vector<TextureUpload>& uploads,
|
|||
const u8* ee_mem = (const u8*)render_state->ee_main_memory;
|
||||
for (auto& upload : uploads) {
|
||||
render_state->texture_pool->handle_upload_now(ee_mem + upload.page, upload.mode, ee_mem,
|
||||
render_state->offset_of_s7);
|
||||
render_state->offset_of_s7, m_my_id == 999);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/graphics/opengl_renderer/BucketRenderer.h"
|
||||
#include "game/graphics/opengl_renderer/TextureAnimator.h"
|
||||
#include "game/graphics/texture/TexturePool.h"
|
||||
|
||||
/*!
|
||||
|
@ -10,7 +11,9 @@
|
|||
*/
|
||||
class TextureUploadHandler : public BucketRenderer {
|
||||
public:
|
||||
TextureUploadHandler(const std::string& name, int my_id);
|
||||
TextureUploadHandler(const std::string& name,
|
||||
int my_id,
|
||||
std::shared_ptr<TextureAnimator> texture_animator);
|
||||
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
|
||||
void draw_debug_window() override;
|
||||
|
||||
|
@ -22,4 +25,5 @@ class TextureUploadHandler : public BucketRenderer {
|
|||
void flush_uploads(std::vector<TextureUpload>& uploads, SharedRenderState* render_state);
|
||||
bool m_fake_uploads = false;
|
||||
int m_upload_count = 0;
|
||||
std::shared_ptr<TextureAnimator> m_texture_animator;
|
||||
};
|
||||
|
|
|
@ -48,7 +48,8 @@
|
|||
|
||||
std::mutex g_merc_data_mutex;
|
||||
|
||||
Merc2::Merc2(ShaderLibrary& shaders) {
|
||||
Merc2::Merc2(ShaderLibrary& shaders, const std::vector<GLuint>* anim_slot_array)
|
||||
: m_anim_slot_array(anim_slot_array) {
|
||||
// Set up main vertex array. This will point to the data stored in the .FR3 level file, and will
|
||||
// be uploaded to the GPU by the Loader.
|
||||
glGenVertexArrays(1, &m_vao);
|
||||
|
@ -1083,7 +1084,7 @@ Merc2::Draw* Merc2::alloc_normal_draw(const tfrag3::MercDraw& mdraw,
|
|||
// but don't toggle it the other way?
|
||||
}
|
||||
|
||||
draw->texture = mdraw.eye_id == 0xff ? mdraw.tree_tex_id : (0xffffff00 | mdraw.eye_id);
|
||||
draw->texture = mdraw.eye_id == 0xff ? mdraw.tree_tex_id : (0xefffff00 | mdraw.eye_id);
|
||||
draw->first_bone = first_bone;
|
||||
draw->light_idx = lights;
|
||||
draw->num_triangles = mdraw.num_triangles;
|
||||
|
@ -1233,15 +1234,18 @@ void Merc2::do_draws(const Draw* draw_array,
|
|||
fog_on = true;
|
||||
}
|
||||
bool use_mipmaps_for_filtering = true;
|
||||
if ((int)draw.texture != last_tex) {
|
||||
if (draw.texture < lev->textures.size()) {
|
||||
if (draw.texture != last_tex) {
|
||||
if (draw.texture < (int)lev->textures.size() && draw.texture >= 0) {
|
||||
glBindTexture(GL_TEXTURE_2D, lev->textures.at(draw.texture));
|
||||
} else if ((draw.texture & 0xffffff00) == 0xffffff00) {
|
||||
} else if ((draw.texture & 0xffffff00) == 0xefffff00) {
|
||||
auto maybe_eye = render_state->eye_renderer->lookup_eye_texture(draw.texture & 0xff);
|
||||
if (maybe_eye) {
|
||||
glBindTexture(GL_TEXTURE_2D, *maybe_eye);
|
||||
}
|
||||
use_mipmaps_for_filtering = false;
|
||||
} else if (draw.texture < 0) {
|
||||
int slot = -(draw.texture + 1);
|
||||
glBindTexture(GL_TEXTURE_2D, m_anim_slot_array->at(slot));
|
||||
} else {
|
||||
fmt::print("Invalid draw.texture is {}, would have crashed.\n", draw.texture);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ struct MercDebugStats {
|
|||
|
||||
class Merc2 {
|
||||
public:
|
||||
Merc2(ShaderLibrary& shaders);
|
||||
Merc2(ShaderLibrary& shaders, const std::vector<GLuint>* anim_slot_array);
|
||||
~Merc2();
|
||||
void draw_debug_window(MercDebugStats* stats);
|
||||
void render(DmaFollower& dma,
|
||||
|
@ -50,6 +50,7 @@ class Merc2 {
|
|||
static constexpr int kMaxBlerc = 40;
|
||||
|
||||
private:
|
||||
const std::vector<GLuint>* m_anim_slot_array;
|
||||
enum MercDataMemory {
|
||||
LOW_MEMORY = 0,
|
||||
BUFFER_BASE = 442,
|
||||
|
@ -182,7 +183,7 @@ class Merc2 {
|
|||
u32 first_index;
|
||||
u32 index_count;
|
||||
DrawMode mode;
|
||||
u32 texture;
|
||||
s32 texture;
|
||||
u32 num_triangles;
|
||||
u16 first_bone;
|
||||
u16 light_idx;
|
||||
|
|
|
@ -249,7 +249,7 @@ void Loader::loader_thread() {
|
|||
* Load a "common" FR3 file that has non-level textures.
|
||||
* This should be called during initialization, before any threaded loading goes on.
|
||||
*/
|
||||
void Loader::load_common(TexturePool& tex_pool, const std::string& name) {
|
||||
const tfrag3::Level& Loader::load_common(TexturePool& tex_pool, const std::string& name) {
|
||||
auto data = file_util::read_binary_file(m_base_path / fmt::format("{}.fr3", name));
|
||||
|
||||
auto decomp_data = compression::decompress_zstd(data.data(), data.size());
|
||||
|
@ -270,6 +270,7 @@ void Loader::load_common(TexturePool& tex_pool, const std::string& name) {
|
|||
while (!done) {
|
||||
done = mls.run(tim, input);
|
||||
}
|
||||
return *m_common_level.level;
|
||||
}
|
||||
|
||||
bool Loader::upload_textures(Timer& timer, LevelData& data, TexturePool& texture_pool) {
|
||||
|
|
|
@ -22,7 +22,7 @@ class Loader {
|
|||
void update_blocking(TexturePool& tex_pool);
|
||||
const LevelData* get_tfrag3_level(const std::string& level_name);
|
||||
std::optional<MercRef> get_merc_model(const char* model_name);
|
||||
void load_common(TexturePool& tex_pool, const std::string& name);
|
||||
const tfrag3::Level& load_common(TexturePool& tex_pool, const std::string& name);
|
||||
void set_want_levels(const std::vector<std::string>& levels);
|
||||
std::vector<LevelData*> get_in_use_levels();
|
||||
void draw_debug_window();
|
||||
|
|
|
@ -11,13 +11,11 @@ constexpr float LOAD_BUDGET = 2.5f;
|
|||
*/
|
||||
u64 add_texture(TexturePool& pool, const tfrag3::Texture& tex, bool is_common) {
|
||||
GLuint gl_tex;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glGenTextures(1, &gl_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.w, tex.h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
tex.data.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_tex);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
float aniso = 0.0f;
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &aniso);
|
||||
|
|
|
@ -71,6 +71,9 @@ FramebufferTexturePair::FramebufferTexturePair(int w, int h, u64 texture_format,
|
|||
}
|
||||
|
||||
FramebufferTexturePair::~FramebufferTexturePair() {
|
||||
if (m_moved_from) {
|
||||
return;
|
||||
}
|
||||
glDeleteFramebuffers(m_framebuffers.size(), m_framebuffers.data());
|
||||
glDeleteTextures(1, &m_texture);
|
||||
}
|
||||
|
|
|
@ -17,14 +17,35 @@ class FramebufferTexturePair {
|
|||
|
||||
GLuint texture() const { return m_texture; }
|
||||
|
||||
void update_texture_size(int w, int h) {
|
||||
m_w = w;
|
||||
m_h = h;
|
||||
}
|
||||
|
||||
void update_texture_unsafe(GLuint texture) { m_texture = texture; }
|
||||
|
||||
FramebufferTexturePair(const FramebufferTexturePair&) = delete;
|
||||
FramebufferTexturePair& operator=(const FramebufferTexturePair&) = delete;
|
||||
FramebufferTexturePair(FramebufferTexturePair&& other) {
|
||||
if (this == &other) {
|
||||
return;
|
||||
}
|
||||
ASSERT(!m_moved_from && !other.m_moved_from);
|
||||
other.m_moved_from = true;
|
||||
m_w = other.m_w;
|
||||
m_h = other.m_h;
|
||||
m_texture = other.m_texture;
|
||||
m_framebuffers = std::move(other.m_framebuffers);
|
||||
}
|
||||
int width() const { return m_w; }
|
||||
int height() const { return m_h; }
|
||||
|
||||
private:
|
||||
friend class FramebufferTexturePairContext;
|
||||
std::vector<GLuint> m_framebuffers;
|
||||
GLuint m_texture;
|
||||
int m_w, m_h;
|
||||
bool m_moved_from = false;
|
||||
};
|
||||
|
||||
class FramebufferTexturePairContext {
|
||||
|
|
|
@ -6,7 +6,8 @@ in vec4 fragment_color;
|
|||
in vec3 tex_coord;
|
||||
flat in uint use_uv;
|
||||
in vec4 gs_scissor;
|
||||
uniform float alpha_reject;
|
||||
uniform float alpha_min;
|
||||
uniform float alpha_max;
|
||||
uniform float color_mult;
|
||||
uniform float alpha_mult;
|
||||
uniform float alpha_sub;
|
||||
|
@ -82,7 +83,7 @@ void main() {
|
|||
color *= 2;
|
||||
color.xyz *= color_mult;
|
||||
color.w *= alpha_mult;
|
||||
if (color.a < alpha_reject) {
|
||||
if (color.a < alpha_min || color.a > alpha_max) {
|
||||
discard;
|
||||
}
|
||||
if (tex_info.w == 1) {
|
||||
|
|
31
game/graphics/opengl_renderer/shaders/tex_anim.frag
Normal file
31
game/graphics/opengl_renderer/shaders/tex_anim.frag
Normal file
|
@ -0,0 +1,31 @@
|
|||
#version 430 core
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform vec4 rgba;
|
||||
uniform int enable_tex;
|
||||
uniform int tcc;
|
||||
uniform ivec4 channel_scramble;
|
||||
|
||||
in vec2 uv;
|
||||
|
||||
layout (binding = 0) uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
|
||||
if (enable_tex == 1) {
|
||||
vec4 tex_color = texture(tex, uv);
|
||||
vec4 unscambled_tex = vec4(tex_color[channel_scramble[0]],
|
||||
tex_color[channel_scramble[1]],
|
||||
tex_color[channel_scramble[2]],
|
||||
tex_color[channel_scramble[3]]);
|
||||
color = rgba / 128.;
|
||||
if (tcc == 1) {
|
||||
color *= unscambled_tex;
|
||||
} else {
|
||||
color.xyz *= unscambled_tex.xyz;
|
||||
}
|
||||
} else {
|
||||
color = (rgba / 128.);
|
||||
}
|
||||
}
|
15
game/graphics/opengl_renderer/shaders/tex_anim.vert
Normal file
15
game/graphics/opengl_renderer/shaders/tex_anim.vert
Normal file
|
@ -0,0 +1,15 @@
|
|||
#version 430 core
|
||||
|
||||
layout (location = 0) in int vertex_index;
|
||||
|
||||
uniform vec3 positions[4];
|
||||
uniform vec2 uvs[4];
|
||||
uniform vec4 rgba;
|
||||
// TODO flags and stuff
|
||||
|
||||
out vec2 uv;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(-1. + (positions[vertex_index] * 2), 1);
|
||||
uv = uvs[vertex_index];
|
||||
}
|
|
@ -697,7 +697,7 @@ void gl_texture_upload_now(const u8* tpage, int mode, u32 s7_ptr) {
|
|||
// just pass it to the texture pool.
|
||||
// the texture pool will take care of locking.
|
||||
// we don't want to lock here for the entire duration of the conversion.
|
||||
g_gfx_data->texture_pool->handle_upload_now(tpage, mode, g_ee_main_mem, s7_ptr);
|
||||
g_gfx_data->texture_pool->handle_upload_now(tpage, mode, g_ee_main_mem, s7_ptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,16 @@ void TextureConverter::upload(const u8* data, u32 dest, u32 size_vram_words) {
|
|||
}
|
||||
}
|
||||
|
||||
void TextureConverter::upload_width(const u8* data, u32 dest, u32 width, u32 height) {
|
||||
for (u32 y = 0; y < height; y++) {
|
||||
for (u32 x = 0; x < width; x++) {
|
||||
// VRAM address (bytes)
|
||||
auto addr32 = psmct32_addr(x, y, width) + dest * 256;
|
||||
*(u32*)(m_vram.data() + addr32) = *((const u32*)(data) + (x + y * width));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextureConverter::download_rgba8888(u8* result,
|
||||
u32 vram_addr,
|
||||
u32 goal_tex_width,
|
||||
|
@ -199,7 +209,3 @@ void TextureConverter::download_rgba8888(u8* result,
|
|||
|
||||
ASSERT(out_offset == expected_size_bytes);
|
||||
}
|
||||
|
||||
void TextureConverter::serialize(Serializer& ser) {
|
||||
ser.from_pod_vector(&m_vram);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/Serializer.h"
|
||||
|
||||
class TextureConverter {
|
||||
public:
|
||||
TextureConverter();
|
||||
void upload(const u8* data, u32 dest, u32 size_vram_words);
|
||||
void upload_width(const u8* data, u32 dest, u32 width, u32 height);
|
||||
void download_rgba8888(u8* result,
|
||||
u32 vram_addr,
|
||||
u32 goal_tex_width,
|
||||
|
@ -18,7 +18,6 @@ class TextureConverter {
|
|||
u32 clut_psm,
|
||||
u32 clut_vram_addr,
|
||||
u32 expected_size_bytes);
|
||||
void serialize(Serializer& ser);
|
||||
|
||||
private:
|
||||
std::vector<u8> m_vram;
|
||||
|
|
15
game/graphics/texture/TextureID.h
Normal file
15
game/graphics/texture/TextureID.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
struct PcTextureId {
|
||||
u16 page = -1;
|
||||
u16 tex = -1;
|
||||
|
||||
PcTextureId(u16 p, u16 t) : page(p), tex(t) {}
|
||||
PcTextureId() = default;
|
||||
|
||||
static PcTextureId from_combo_id(u32 val) { return PcTextureId(val >> 16, val & 0xffff); }
|
||||
|
||||
bool operator==(const PcTextureId& other) const { return page == other.page && tex == other.tex; }
|
||||
};
|
|
@ -110,6 +110,21 @@ void TexturePool::move_existing_to_vram(GpuTexture* tex, u32 slot_addr) {
|
|||
}
|
||||
}
|
||||
|
||||
void TexturePool::update_gl_texture(GpuTexture* gpu_texture,
|
||||
u32 new_w,
|
||||
u32 new_h,
|
||||
GLuint new_gl_texture) {
|
||||
ASSERT(gpu_texture->gpu_textures.size() == 1);
|
||||
gpu_texture->gpu_textures[0].gl = new_gl_texture;
|
||||
gpu_texture->w = new_w;
|
||||
gpu_texture->h = new_h;
|
||||
for (int si : gpu_texture->slots) {
|
||||
auto& slot = m_textures[si];
|
||||
ASSERT(slot.source == gpu_texture);
|
||||
slot.gpu_texture = new_gl_texture;
|
||||
}
|
||||
}
|
||||
|
||||
void TexturePool::refresh_links(GpuTexture& texture) {
|
||||
u64 tex_to_use =
|
||||
texture.is_placeholder ? m_placeholder_texture_id : texture.gpu_textures.front().gl;
|
||||
|
@ -174,7 +189,11 @@ void GpuTexture::add_slot(u32 slot) {
|
|||
* We could store textures in the right format to begin with, or spread the conversion out over
|
||||
* multiple frames.
|
||||
*/
|
||||
void TexturePool::handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr) {
|
||||
void TexturePool::handle_upload_now(const u8* tpage,
|
||||
int mode,
|
||||
const u8* memory_base,
|
||||
u32 s7_ptr,
|
||||
bool debug) {
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
// extract the texture-page object. This is just a description of the page data.
|
||||
GoalTexturePage texture_page;
|
||||
|
@ -201,6 +220,12 @@ void TexturePool::handle_upload_now(const u8* tpage, int mode, const u8* memory_
|
|||
for (int tex_idx = 0; tex_idx < texture_page.length; tex_idx++) {
|
||||
GoalTexture tex;
|
||||
if (texture_page.try_copy_texture_description(&tex, tex_idx, memory_base, tpage, s7_ptr)) {
|
||||
if (debug) {
|
||||
fmt::print("Pool upload {} to {}\n",
|
||||
std::string(goal_string(texture_page.name_ptr, memory_base)) +
|
||||
goal_string(tex.name_ptr, memory_base),
|
||||
tex.dest[0]);
|
||||
}
|
||||
// each texture may have multiple mip levels.
|
||||
for (int mip_idx = 0; mip_idx < tex.num_mips; mip_idx++) {
|
||||
if (has_segment[tex.segment_of_mip(mip_idx)]) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "game/graphics/texture/TextureConverter.h"
|
||||
#include "game/graphics/texture/TextureID.h"
|
||||
|
||||
// verify all texture lookups.
|
||||
// will make texture lookups slower and likely caused dropped frames when loading
|
||||
|
@ -72,18 +73,6 @@ constexpr int SKY_TEXTURE_VRAM_ADDRS[2] = {8064, 8096};
|
|||
* The game will inform us when it uploads to VRAM
|
||||
*/
|
||||
|
||||
struct PcTextureId {
|
||||
u16 page = -1;
|
||||
u16 tex = -1;
|
||||
|
||||
PcTextureId(u16 p, u16 t) : page(p), tex(t) {}
|
||||
PcTextureId() = default;
|
||||
|
||||
static PcTextureId from_combo_id(u32 val) { return PcTextureId(val >> 16, val & 0xffff); }
|
||||
|
||||
bool operator==(const PcTextureId& other) const { return page == other.page && tex == other.tex; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TextureMap {
|
||||
public:
|
||||
|
@ -303,10 +292,11 @@ struct GoalTexturePage {
|
|||
class TexturePool {
|
||||
public:
|
||||
TexturePool(GameVersion version);
|
||||
void handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr);
|
||||
void handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr, bool debug);
|
||||
GpuTexture* give_texture(const TextureInput& in);
|
||||
GpuTexture* give_texture_and_load_to_vram(const TextureInput& in, u32 vram_slot);
|
||||
void unload_texture(PcTextureId tex_id, u64 gpu_id);
|
||||
void update_gl_texture(GpuTexture* texture, u32 new_w, u32 new_h, GLuint new_gl_texture);
|
||||
|
||||
/*!
|
||||
* Look up an OpenGL texture by vram address. Return std::nullopt if the game hasn't loaded
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
#include "common/common_types.h"
|
||||
|
||||
const std::vector<u32>& get_jak1_tpage_dir();
|
||||
constexpr u32 EXTRA_PC_PORT_TEXTURE_COUNT = 50;
|
||||
constexpr u32 EXTRA_PC_PORT_TEXTURE_COUNT = 64;
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
#include "kdgo.h"
|
||||
|
||||
#include "common/global_profiler/GlobalProfiler.h"
|
||||
#include "common/link_types.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/BitUtils.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/fileio.h"
|
||||
|
@ -45,6 +49,76 @@ void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size)
|
|||
load_and_link_dgo_from_c(name, heap, flag, buffer_size, false);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Faster version of load_and_link_dgo_from_c that skips the IOP and reads the file directly
|
||||
* to GOAL memory.
|
||||
*/
|
||||
void load_and_link_dgo_from_c_fast(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize) {
|
||||
Timer timer;
|
||||
lg::debug("[Load and Link DGO From C (fast)] {}", name);
|
||||
|
||||
// append CGO if needed
|
||||
char name_on_cd[16];
|
||||
kstrcpyup(name_on_cd, name);
|
||||
if (name_on_cd[strlen(name_on_cd) - 4] != '.') {
|
||||
strcat(name_on_cd, ".CGO");
|
||||
}
|
||||
|
||||
// open the DGO file:
|
||||
auto file_path = file_util::get_jak_project_dir() / "out" / game_version_names[g_game_version] /
|
||||
"iso" / name_on_cd;
|
||||
auto fp = fopen(file_path.string().c_str(), "rb");
|
||||
if (!fp) {
|
||||
lg::die("Failed to open DGO: {}, path {}\n", name, file_path.string());
|
||||
}
|
||||
|
||||
// allocate temporary buffers for linking:
|
||||
auto old_heap_top = heap->top;
|
||||
auto buffer1 = kmalloc(heap, bufferSize, KMALLOC_TOP | KMALLOC_ALIGN_64, "dgo-buffer-1");
|
||||
|
||||
// read the header
|
||||
DgoHeader header;
|
||||
if (fread(&header, sizeof(DgoHeader), 1, fp) != 1) {
|
||||
lg::die("failed to read dgo header");
|
||||
}
|
||||
lg::info("got {} objects, name {}\n", header.object_count, header.name);
|
||||
|
||||
// load all but the final
|
||||
for (int i = 0; i < (int)header.object_count - 1; i++) {
|
||||
if (fread(buffer1.c(), sizeof(ObjectHeader), 1, fp) != 1) {
|
||||
lg::die("failed to read object header");
|
||||
}
|
||||
auto* obj_header = (ObjectHeader*)buffer1.c();
|
||||
u32 aligned_size = align16(obj_header->size);
|
||||
auto* obj_dest = buffer1.c() + sizeof(ObjectHeader);
|
||||
if (fread(obj_dest, aligned_size, 1, fp) != 1) {
|
||||
lg::die("Failed to read object data");
|
||||
}
|
||||
link_and_exec(buffer1 + sizeof(ObjectHeader), obj_header->name, obj_header->size, heap,
|
||||
linkFlag, true);
|
||||
}
|
||||
|
||||
auto final_object_dest = Ptr<u8>((heap->current + 0x3f).offset & 0xffffffc0);
|
||||
if (fread(final_object_dest.c(), sizeof(ObjectHeader), 1, fp) != 1) {
|
||||
lg::die("failed to read final object header");
|
||||
}
|
||||
auto* obj_header = (ObjectHeader*)final_object_dest.c();
|
||||
u32 aligned_size = align16(obj_header->size);
|
||||
auto* obj_dest = (final_object_dest + sizeof(ObjectHeader)).c();
|
||||
if (fread(obj_dest, aligned_size, 1, fp) != 1) {
|
||||
lg::die("Failed to read object data");
|
||||
}
|
||||
link_and_exec(final_object_dest + sizeof(ObjectHeader), obj_header->name, obj_header->size, heap,
|
||||
linkFlag, true);
|
||||
|
||||
heap->top = old_heap_top;
|
||||
fclose(fp);
|
||||
lg::info("load_and_link_dgo_from_c_fast took {:.3f} s\n", timer.getSeconds());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Load and link a DGO file.
|
||||
* This does not use the mutli-threaded linker and will block until the entire file is done.e
|
||||
|
@ -54,6 +128,7 @@ void load_and_link_dgo_from_c(const char* name,
|
|||
u32 linkFlag,
|
||||
s32 bufferSize,
|
||||
bool jump_from_c_to_goal) {
|
||||
Timer timer;
|
||||
lg::debug("[Load and Link DGO From C] {}", name);
|
||||
u32 oldShowStall = sShowStallMsg;
|
||||
|
||||
|
@ -116,6 +191,7 @@ void load_and_link_dgo_from_c(const char* name,
|
|||
ContinueLoadingDGO(buffer1, buffer2, Ptr<u8>((heap->current + 0x3f).offset & 0xffffffc0));
|
||||
}
|
||||
}
|
||||
lg::info("load_and_link_dgo_from_c took {:.3f} s\n", timer.getSeconds());
|
||||
sShowStallMsg = oldShowStall;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,4 +10,8 @@ void load_and_link_dgo_from_c(const char* name,
|
|||
s32 bufferSize,
|
||||
bool jump_from_c_to_goal);
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
|
||||
void load_and_link_dgo_from_c_fast(const char* name,
|
||||
Ptr<kheapinfo> heap,
|
||||
u32 linkFlag,
|
||||
s32 bufferSize);
|
||||
} // namespace jak2
|
|
@ -795,9 +795,12 @@ void InitMachineScheme() {
|
|||
*EnableMethodSet = *EnableMethodSet + 1;
|
||||
{
|
||||
auto p = scoped_prof("load-game-dgo");
|
||||
load_and_link_dgo_from_c("game", kglobalheap,
|
||||
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
||||
0x400000, true);
|
||||
// load_and_link_dgo_from_c("game", kglobalheap,
|
||||
// LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE |
|
||||
// LINK_FLAG_PRINT_LOGIN, 0x400000, true);
|
||||
load_and_link_dgo_from_c_fast(
|
||||
"game", kglobalheap, LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
||||
0x400000);
|
||||
}
|
||||
|
||||
*EnableMethodSet = *EnableMethodSet + -1;
|
||||
|
@ -833,6 +836,7 @@ void initialize_sql_db() {
|
|||
|
||||
// Attempt to open the database
|
||||
const auto opened = sql_db.open_db(db_path.string());
|
||||
(void)opened;
|
||||
|
||||
fs::path schema_file =
|
||||
file_util::get_jak_project_dir() / "goal_src" / "jak2" / "tools" / "editable-schema.sql";
|
||||
|
|
|
@ -139,7 +139,7 @@ auto sceCdCallback(void (*callback)(int)) {
|
|||
|
||||
int sceCdRead(int lsn, int num_sectors, void* dest, void* mode) {
|
||||
(void)mode;
|
||||
auto do_read = [lsn, num_sectors, dest, mode](s32 thid) {
|
||||
auto do_read = [lsn, num_sectors, dest](s32 thid) {
|
||||
// printf("sceCdRead %d, %d -> %p\n", lsn, num_sectors, dest);
|
||||
ASSERT(gFakeCd.fp);
|
||||
if (fseek(gFakeCd.fp, lsn * SECTOR_SIZE, SEEK_SET)) {
|
||||
|
|
|
@ -91,7 +91,7 @@ void KeyboardDevice::clear_actions(std::shared_ptr<PadData> data) {
|
|||
|
||||
void KeyboardDevice::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::shared_ptr<PadData> /*data*/,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment) {
|
||||
if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
|
||||
const auto key_event = event.key;
|
||||
|
|
|
@ -61,7 +61,7 @@ void MouseDevice::poll_state(std::shared_ptr<PadData> data) {
|
|||
data->update_analog_sim_tracker(false);
|
||||
ActiveMouseAction action;
|
||||
action.player_movement = true;
|
||||
action.revert_action = [](std::shared_ptr<PadData> data, InputBinding bind) {
|
||||
action.revert_action = [](std::shared_ptr<PadData> data, InputBinding /*bind*/) {
|
||||
data->analog_data.at(1) += 127; // stop moving forward
|
||||
data->update_analog_sim_tracker(true);
|
||||
};
|
||||
|
|
|
@ -48,7 +48,8 @@ void SubtitleEditor::draw_window() {
|
|||
}
|
||||
} else if (m_db_failed_to_load) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color);
|
||||
ImGui::Text(fmt::format("Error Loading - {}!", m_subtitle_db.m_load_error.value()).c_str());
|
||||
ImGui::Text("%s",
|
||||
fmt::format("Error Loading - {}!", m_subtitle_db.m_load_error.value()).c_str());
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::Button("Try Again")) {
|
||||
if (g_game_version == GameVersion::Jak1) {
|
||||
|
@ -334,9 +335,10 @@ void SubtitleEditor::draw_all_non_cutscenes(bool base_cutscenes) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string SubtitleEditor::subtitle_line_summary(const SubtitleLine& line,
|
||||
const SubtitleLineMetadata& line_meta,
|
||||
const std::shared_ptr<GameSubtitleBank> bank) {
|
||||
std::string SubtitleEditor::subtitle_line_summary(
|
||||
const SubtitleLine& line,
|
||||
const SubtitleLineMetadata& line_meta,
|
||||
const std::shared_ptr<GameSubtitleBank> /*bank*/) {
|
||||
// Truncate the text if it's too long, it's supposed to just be a summary at a glance
|
||||
std::string line_text = "";
|
||||
if (!line.text.empty()) {
|
||||
|
|
|
@ -826,11 +826,11 @@ vf31: cam 0 (premultiplied by hmge)
|
|||
(moon-dma (the-as sky-work s4-0) s3-0)
|
||||
(draw-haze (the-as sky-work s4-0) s3-0)
|
||||
(when (nonzero? *sky-texture-anim-array*) ;; added check
|
||||
; (draw-clouds (the-as sky-work s4-0) s3-0) ;; DISABLED!
|
||||
(draw-clouds (the-as sky-work s4-0) s3-0) ;; DISABLED!
|
||||
)
|
||||
(draw-base (the-as sky-work s4-0) s3-0)
|
||||
(when (nonzero? *sky-texture-anim-array*) ;; added check
|
||||
;(draw-fog (the-as sky-work s4-0) s3-0)
|
||||
(draw-fog (the-as sky-work s4-0) s3-0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -51,9 +51,20 @@
|
|||
(t0-1 (/ (-> v1-4 w) 2))
|
||||
(a3-1 (/ (-> v1-4 h) 2))
|
||||
)
|
||||
(upload-vram-data dma-buf (the-as int (-> v1-4 dest 0)) (the-as pointer (-> v1-4 pad 0)) a3-1 t0-1)
|
||||
;; modified
|
||||
;(upload-vram-data dma-buf (the-as int (-> v1-4 dest 0)) (the-as pointer (-> v1-4 pad 0)) a3-1 t0-1)
|
||||
(pc-texture-anim-flag upload-generic-vram dma-buf :qwc 1)
|
||||
(let ((upload-record (the texture-anim-pc-upload (-> dma-buf base))))
|
||||
(set! (-> upload-record data) (the-as pointer (-> v1-4 pad 0))) ;; the clut16x16 object
|
||||
(set! (-> upload-record width) t0-1)
|
||||
(set! (-> upload-record height) a3-1)
|
||||
(set! (-> upload-record dest) (-> v1-4 dest 0))
|
||||
(set! (-> upload-record format) (gs-psm ct32))
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
|
||||
)
|
||||
(dma-buffer-add-gs-set dma-buf (texflush 0))
|
||||
;(dma-buffer-add-gs-set dma-buf (texflush 0)) don't need
|
||||
(texture-anim-layer-add-shader dma-buf layer 1)
|
||||
(texture-anim-layer-draw dma-buf width height layer)
|
||||
)
|
||||
|
@ -198,37 +209,64 @@
|
|||
)
|
||||
)
|
||||
(let ((s5-1 (-> arg1 tex)))
|
||||
(dma-buffer-add-gs-set arg0
|
||||
(bitbltbuf
|
||||
(new 'static 'gs-bitbltbuf :dpsm (the-as int (-> s5-1 psm)) :dbp (-> s5-1 dest 0) :dbw (-> s5-1 width 0))
|
||||
)
|
||||
(trxpos (new 'static 'gs-trxpos))
|
||||
(trxreg (new 'static 'gs-trxreg :rrw (-> s5-1 w) :rrh (-> s5-1 h)))
|
||||
(trxdir (new 'static 'gs-trxdir))
|
||||
)
|
||||
(dma-buffer-add-ref-texture arg0 (the-as pointer (-> s5-1 pad 0)) (-> s5-1 w) (-> s5-1 h) (-> s5-1 psm))
|
||||
(upload-vram-data arg0 (the-as int (-> s5-1 clutdest)) (the-as pointer (-> s5-1 pad 1)) 16 16)
|
||||
|
||||
;; they use bitbltbuf directly to upload in the format of the data, rather than
|
||||
;; the usual PSM32 trick
|
||||
; (dma-buffer-add-gs-set arg0
|
||||
; (bitbltbuf
|
||||
; (new 'static 'gs-bitbltbuf :dpsm (the-as int (-> s5-1 psm)) :dbp (-> s5-1 dest 0) :dbw (-> s5-1 width 0))
|
||||
; )
|
||||
; (trxpos (new 'static 'gs-trxpos))
|
||||
; (trxreg (new 'static 'gs-trxreg :rrw (-> s5-1 w) :rrh (-> s5-1 h)))
|
||||
; (trxdir (new 'static 'gs-trxdir))
|
||||
; )
|
||||
; (dma-buffer-add-ref-texture arg0 (the-as pointer (-> s5-1 pad 0)) (-> s5-1 w) (-> s5-1 h) (-> s5-1 psm))
|
||||
|
||||
(pc-texture-anim-flag upload-generic-vram arg0 :qwc 1)
|
||||
(let ((upload-record (the texture-anim-pc-upload (-> arg0 base))))
|
||||
(set! (-> upload-record data) (the-as pointer (-> s5-1 pad 0))) ;; the texture data
|
||||
(set! (-> upload-record width) (-> s5-1 w)) ;; see fog-texture-anim-init, there's not cropping.
|
||||
(set! (-> upload-record height) (-> s5-1 h))
|
||||
(set! (-> upload-record dest) (-> s5-1 dest 0))
|
||||
(set! (-> upload-record format) (-> s5-1 psm))
|
||||
)
|
||||
(&+! (-> arg0 base) 16)
|
||||
|
||||
;; and the clut
|
||||
;(upload-vram-data arg0 (the-as int (-> s5-1 clutdest)) (the-as pointer (-> s5-1 pad 1)) 16 16)
|
||||
|
||||
(pc-texture-anim-flag upload-clut-16-16 arg0 :qwc 1)
|
||||
(let ((upload-record (the texture-anim-pc-upload (-> arg0 base))))
|
||||
(set! (-> upload-record data) (the-as pointer (-> s5-1 pad 1))) ;; the clut16x16 object
|
||||
(set! (-> upload-record width) 16)
|
||||
(set! (-> upload-record height) 16)
|
||||
(set! (-> upload-record dest) (-> s5-1 clutdest))
|
||||
(set! (-> upload-record format) (gs-psm ct32))
|
||||
)
|
||||
(&+! (-> arg0 base) 16)
|
||||
|
||||
(set! (-> *texture-pool* ids (shr (-> s5-1 clutdest) 6)) (the-as uint 0))
|
||||
(let* ((v1-29 arg0)
|
||||
(a0-51 (the-as object (-> v1-29 base)))
|
||||
)
|
||||
(set! (-> (the-as dma-packet a0-51) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt)))
|
||||
(set! (-> (the-as dma-packet a0-51) vif0) (new 'static 'vif-tag))
|
||||
(set! (-> (the-as dma-packet a0-51) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1))
|
||||
(set! (-> v1-29 base) (&+ (the-as pointer a0-51) 16))
|
||||
)
|
||||
(let* ((v1-30 arg0)
|
||||
(a0-53 (the-as object (-> v1-30 base)))
|
||||
)
|
||||
(set! (-> (the-as gs-gif-tag a0-53) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1))
|
||||
(set! (-> (the-as gs-gif-tag a0-53) regs) GIF_REGS_ALL_AD)
|
||||
(set! (-> v1-30 base) (&+ (the-as pointer a0-53) 16))
|
||||
)
|
||||
(let ((v1-31 (-> arg0 base)))
|
||||
(set! (-> (the-as (pointer int64) v1-31)) 0)
|
||||
(set! (-> (the-as (pointer gs-reg64) v1-31) 1) (gs-reg64 texflush))
|
||||
(set! (-> arg0 base) (&+ v1-31 16))
|
||||
)
|
||||
;; just a texflush.
|
||||
; (let* ((v1-29 arg0)
|
||||
; (a0-51 (the-as object (-> v1-29 base)))
|
||||
; )
|
||||
; (set! (-> (the-as dma-packet a0-51) dma) (new 'static 'dma-tag :qwc #x2 :id (dma-tag-id cnt)))
|
||||
; (set! (-> (the-as dma-packet a0-51) vif0) (new 'static 'vif-tag))
|
||||
; (set! (-> (the-as dma-packet a0-51) vif1) (new 'static 'vif-tag :imm #x2 :cmd (vif-cmd direct) :msk #x1))
|
||||
; (set! (-> v1-29 base) (&+ (the-as pointer a0-51) 16))
|
||||
; )
|
||||
; (let* ((v1-30 arg0)
|
||||
; (a0-53 (the-as object (-> v1-30 base)))
|
||||
; )
|
||||
; (set! (-> (the-as gs-gif-tag a0-53) tag) (new 'static 'gif-tag64 :nloop #x1 :eop #x1 :nreg #x1))
|
||||
; (set! (-> (the-as gs-gif-tag a0-53) regs) GIF_REGS_ALL_AD)
|
||||
; (set! (-> v1-30 base) (&+ (the-as pointer a0-53) 16))
|
||||
; )
|
||||
; (let ((v1-31 (-> arg0 base)))
|
||||
; (set! (-> (the-as (pointer int64) v1-31)) 0)
|
||||
; (set! (-> (the-as (pointer gs-reg64) v1-31) 1) (gs-reg64 texflush))
|
||||
; (set! (-> arg0 base) (&+ v1-31 16))
|
||||
; )
|
||||
(let ((v1-34 (shr (-> s5-1 dest 0) 6)))
|
||||
(dotimes (a0-56 3)
|
||||
(set! (-> *texture-pool* ids (+ v1-34 a0-56)) (the-as uint 0))
|
||||
|
|
|
@ -166,10 +166,57 @@
|
|||
)
|
||||
)
|
||||
|
||||
(defenum texture-anim-pc
|
||||
(start-anim-array 12)
|
||||
(finish-anim-array 13)
|
||||
(erase-and-init 14)
|
||||
(upload-clut-16-16 15)
|
||||
(upload-generic-vram 16)
|
||||
(set-shader 17)
|
||||
(draw 18)
|
||||
(move-rg-to-ba 19)
|
||||
(set-clut-alpha 20)
|
||||
(copy-clut-alpha 21)
|
||||
(darkjak 22)
|
||||
(prison-jak 23)
|
||||
(oracle-jak 24)
|
||||
(nest-jak 25)
|
||||
(kor-transform 26)
|
||||
)
|
||||
|
||||
(deftype texture-anim-pc-upload (structure)
|
||||
((data pointer)
|
||||
(width uint16)
|
||||
(height uint16)
|
||||
(dest uint32)
|
||||
(format gs-psm)
|
||||
(pad uint8 3)
|
||||
)
|
||||
:size-assert 16
|
||||
)
|
||||
|
||||
(deftype texture-anim-pc-texture-transform (structure)
|
||||
((src-tbp uint32)
|
||||
(dest-tbp uint32)
|
||||
(pad0 uint32)
|
||||
(pad1 uint32)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro pc-texture-anim-flag (kind buf &key (qwc 0))
|
||||
`(dma-buffer-add-cnt-vif2
|
||||
,buf
|
||||
,qwc
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port) :imm (texture-anim-pc ,kind))
|
||||
(new 'static 'vif-tag)
|
||||
)
|
||||
)
|
||||
|
||||
(defun texture-anim-layer-add-shader ((arg0 dma-buffer) (arg1 texture-anim-layer) (arg2 int))
|
||||
"Add DMA to set the GS to use the given shader. This can be used to read from the texture."
|
||||
(let ((s5-0 (-> arg1 tex)))
|
||||
(if s5-0
|
||||
(when s5-0
|
||||
(pc-texture-anim-flag set-shader arg0)
|
||||
(dma-buffer-add-gs-set arg0
|
||||
(tex0-1 (new 'static 'gs-tex0
|
||||
:cld #x1
|
||||
|
@ -196,7 +243,8 @@
|
|||
(defun texture-anim-layer-add-clut-shader ((arg0 dma-buffer) (arg1 texture-anim-layer) (arg2 int))
|
||||
"Add DMA to set the GS to use the clut as a ct32 texture. This is used to abuse the GS to blend cluts by pretending they are ct32's."
|
||||
(let ((a1-1 (-> arg1 tex)))
|
||||
(if a1-1
|
||||
(when a1-1
|
||||
(pc-texture-anim-flag set-shader arg0)
|
||||
(dma-buffer-add-gs-set arg0
|
||||
(tex0-1 (new 'static 'gs-tex0 :tbw #x1 :tw #x4 :th #x4 :tcc arg2 :tbp0 (-> a1-1 clutdest)))
|
||||
(tex1-1 (new 'static 'gs-tex1))
|
||||
|
@ -215,6 +263,8 @@
|
|||
Always draws a tristrip with two triangles.
|
||||
"
|
||||
(local-vars (v1-27 float) (sv-224 matrix) (sv-228 matrix) (sv-232 matrix) (sv-236 matrix))
|
||||
(pc-texture-anim-flag draw dma-buf) ;; added
|
||||
|
||||
(rlet ((acc :class vf)
|
||||
(vf0 :class vf)
|
||||
(vf1 :class vf)
|
||||
|
@ -346,6 +396,8 @@
|
|||
(defun blend-clut-texture-anim-layer-func ((dma-buf dma-buffer) (arg1 uint) (width int) (height int) (layer texture-anim-layer) (time float))
|
||||
"Apply texture animation layer to the clut. This interpolates the layer, sets up the GS to _read_ from the given texture's
|
||||
clut as a 16x16 psm32, then does the draw."
|
||||
|
||||
(format 0 "before blend-clut-texture-anim-layer-func ~X~%" (-> dma-buf base))
|
||||
(when (and (>= time (-> layer start-time)) (>= (-> layer end-time) time))
|
||||
(texture-anim-layer-interp layer time)
|
||||
(when (!= (-> layer interpolated-color w) 0.0)
|
||||
|
@ -353,11 +405,26 @@
|
|||
(texture-anim-layer-draw dma-buf 16 16 layer)
|
||||
)
|
||||
)
|
||||
(format 0 "after blend-clut-texture-anim-layer-func ~X~%" (-> dma-buf base))
|
||||
|
||||
0
|
||||
)
|
||||
|
||||
(defun move-rg-to-ba-texture-anim-layer-func ((dma-buf dma-buffer) (fbp-to-draw uint) (width int) (height int) (layer texture-anim-layer) (time float))
|
||||
"Some cursed texture drawing."
|
||||
|
||||
;; HACK added:
|
||||
(pc-texture-anim-flag move-rg-to-ba dma-buf :qwc 1)
|
||||
(let ((transform (the texture-anim-pc-texture-transform (-> dma-buf base))))
|
||||
(set! (-> transform src-tbp) (-> layer tex dest 0))
|
||||
(set! (-> transform dest-tbp) (* 32 fbp-to-draw))
|
||||
(set! (-> transform pad0) 0)
|
||||
(set! (-> transform pad1) 0)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(return 0)
|
||||
;; end added
|
||||
|
||||
(-> layer tex)
|
||||
(let ((tw (log2 (* width 2)))
|
||||
(th (log2 height))
|
||||
|
@ -503,8 +570,123 @@
|
|||
)
|
||||
)
|
||||
|
||||
(define-extern *sky-texture-anim-array* (texture-anim-array texture-anim))
|
||||
(define-extern *darkjak-texture-anim-array* (texture-anim-array texture-anim))
|
||||
(define-extern *jakb-prison-texture-anim-array* (texture-anim-array texture-anim))
|
||||
(define-extern *darkjak-hires-texture-anim-array* (texture-anim-array texture-anim))
|
||||
(define-extern *darkjak-hires-nest-texture-anim-array* (texture-anim-array texture-anim))
|
||||
(define-extern *kor-transform-texture-anim-array* (texture-anim-array texture-anim))
|
||||
|
||||
|
||||
(defun update-texture-anim ((bucket bucket-id) (anim-array texture-anim-array))
|
||||
"Generate all DMA to update all textures in the given list for the given bucket."
|
||||
(cond
|
||||
((or (= anim-array *sky-texture-anim-array*)
|
||||
)
|
||||
;; for sky, we basically emulate the full thing
|
||||
;; (format *stdcon* "doing sky to bucket ~d~%" bucket)
|
||||
)
|
||||
((= anim-array *darkjak-texture-anim-array*)
|
||||
;; darkjak is simple, and we reimplemented it in C++.
|
||||
;; so we just have to send the frame-time value.
|
||||
;; (format *stdcon* "doing darkjak~%")
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
|
||||
bucket
|
||||
)
|
||||
(pc-texture-anim-flag start-anim-array dma-buf)
|
||||
(pc-texture-anim-flag darkjak dma-buf :qwc 1)
|
||||
(let ((morph (-> anim-array array-data 0 frame-time))
|
||||
(vec (the vector (-> dma-buf base)))
|
||||
)
|
||||
(set! (-> vec x) morph)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(pc-texture-anim-flag finish-anim-array dma-buf)
|
||||
)
|
||||
(return #f)
|
||||
)
|
||||
|
||||
((= anim-array *jakb-prison-texture-anim-array*)
|
||||
;; prison is simple, and we reimplemented it in C++.
|
||||
;; so we just have to send the frame-time value.
|
||||
;; (format *stdcon* "doing prison-jak~%")
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
|
||||
bucket
|
||||
)
|
||||
(pc-texture-anim-flag start-anim-array dma-buf)
|
||||
(pc-texture-anim-flag prison-jak dma-buf :qwc 1)
|
||||
(let ((morph (-> anim-array array-data 0 frame-time))
|
||||
(vec (the vector (-> dma-buf base)))
|
||||
)
|
||||
(set! (-> vec x) morph)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(pc-texture-anim-flag finish-anim-array dma-buf)
|
||||
)
|
||||
(return #f)
|
||||
)
|
||||
|
||||
((= anim-array *darkjak-hires-texture-anim-array*)
|
||||
;; oracle is simple, and we reimplemented it in C++.
|
||||
;; so we just have to send the frame-time value.
|
||||
;; (format *stdcon* "doing oracle jak~%")
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
|
||||
bucket
|
||||
)
|
||||
(pc-texture-anim-flag start-anim-array dma-buf)
|
||||
(pc-texture-anim-flag oracle-jak dma-buf :qwc 1)
|
||||
(let ((morph (-> anim-array array-data 0 frame-time))
|
||||
(vec (the vector (-> dma-buf base)))
|
||||
)
|
||||
(set! (-> vec x) morph)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(pc-texture-anim-flag finish-anim-array dma-buf)
|
||||
)
|
||||
(return #f)
|
||||
)
|
||||
((= anim-array *darkjak-hires-nest-texture-anim-array*)
|
||||
;; oracle is simple, and we reimplemented it in C++.
|
||||
;; so we just have to send the frame-time value.
|
||||
;; (format *stdcon* "doing nest jak~%")
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
|
||||
bucket
|
||||
)
|
||||
(pc-texture-anim-flag start-anim-array dma-buf)
|
||||
(pc-texture-anim-flag nest-jak dma-buf :qwc 1)
|
||||
(let ((morph (-> anim-array array-data 0 frame-time))
|
||||
(vec (the vector (-> dma-buf base)))
|
||||
)
|
||||
(set! (-> vec x) morph)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(pc-texture-anim-flag finish-anim-array dma-buf)
|
||||
)
|
||||
(return #f)
|
||||
)
|
||||
((= anim-array *kor-transform-texture-anim-array*)
|
||||
;; kor is simple, and we reimplemented it in C++.
|
||||
;; so we just have to send the frame-time value.
|
||||
;; (format *stdcon* "doing kor~%")
|
||||
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
|
||||
bucket
|
||||
)
|
||||
(pc-texture-anim-flag start-anim-array dma-buf)
|
||||
(pc-texture-anim-flag kor-transform dma-buf :qwc 1)
|
||||
(let ((morph (-> anim-array array-data 0 frame-time))
|
||||
(vec (the vector (-> dma-buf base)))
|
||||
)
|
||||
(set! (-> vec x) morph)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(pc-texture-anim-flag finish-anim-array dma-buf)
|
||||
)
|
||||
(return #f)
|
||||
)
|
||||
(else
|
||||
(return #f) ;; HACK!!!
|
||||
)
|
||||
)
|
||||
|
||||
;;
|
||||
;; (return #f)
|
||||
|
@ -521,17 +703,14 @@
|
|||
bucket
|
||||
)
|
||||
;; added: flag for the PC port to indicate start of texture anim data:
|
||||
(dma-buffer-add-cnt-vif2
|
||||
dma-buf
|
||||
0
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port) :imm 12)
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port))
|
||||
)
|
||||
(pc-texture-anim-flag start-anim-array dma-buf)
|
||||
|
||||
;; loop over animated textures. Each will produce a single texture.
|
||||
(dotimes (anim-idx (-> anim-array length))
|
||||
(let* ((anim (-> anim-array array-data anim-idx))
|
||||
(dest-tex (-> anim tex))
|
||||
)
|
||||
; (format 0 "texture anim dest tex: ~A~%" dest-tex)
|
||||
(when dest-tex
|
||||
0
|
||||
(let ((tex-width (-> dest-tex w)))
|
||||
|
@ -543,7 +722,7 @@
|
|||
fbp-for-tex
|
||||
)
|
||||
(else
|
||||
;; animating the clut.
|
||||
;; not really sure why, but for non-mt8h and non-ct32's we clear the clut here...
|
||||
(set! fbp-for-tex (shr (-> dest-tex clutdest) 5))
|
||||
(set! tex-width 16)
|
||||
(set! tex-height 16)
|
||||
|
@ -551,7 +730,9 @@
|
|||
)
|
||||
)
|
||||
(when (and (nonzero? tex-width) (nonzero? tex-height))
|
||||
; (format 0 " clearing~%")
|
||||
;; configure for drawing to this texture.
|
||||
(pc-texture-anim-flag erase-and-init dma-buf)
|
||||
(dma-buffer-add-gs-set-flusha dma-buf
|
||||
(scissor-1 (new 'static 'gs-scissor :scax1 (+ tex-width -1) :scay1 (+ tex-height -1)))
|
||||
(xyoffset-1 (new 'static 'gs-xy-offset :ofx #x8000 :ofy #x8000))
|
||||
|
@ -605,6 +786,7 @@
|
|||
(set! layer-idx 0)
|
||||
(while (< layer-idx (the-as int (-> anim num-layers)))
|
||||
(let ((layer (-> anim data layer-idx)))
|
||||
; (format 0 " running layer ~D, dma at #x~X~%" layer-idx (-> dma-buf base))
|
||||
;; generate DMA for this layer's effect
|
||||
((-> layer func) dma-buf fbp-for-tex tex-width tex-height layer (-> anim frame-time))
|
||||
)
|
||||
|
@ -612,7 +794,9 @@
|
|||
)
|
||||
)
|
||||
;; some sort of final function (TODO args here)
|
||||
(if (-> anim func)
|
||||
(when (-> anim func)
|
||||
; (format 0 " running final, dma at #x~X~%" layer-idx (-> dma-buf base))
|
||||
|
||||
((-> anim func) dma-buf anim)
|
||||
)
|
||||
|
||||
|
@ -632,13 +816,13 @@
|
|||
)
|
||||
|
||||
;; reset GS registers - we messed with frame/scissor.
|
||||
(reset-display-gs-state *display* dma-buf)
|
||||
;(reset-display-gs-state *display* dma-buf)
|
||||
|
||||
;; added: flag for the PC port to indicate end of texture anim data:
|
||||
(dma-buffer-add-cnt-vif2
|
||||
dma-buf
|
||||
0
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port) :imm 13)
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port) :imm (texture-anim-pc finish-anim-array))
|
||||
(new 'static 'vif-tag :cmd (vif-cmd pc-port))
|
||||
)
|
||||
)
|
||||
|
@ -725,6 +909,17 @@
|
|||
|
||||
(defun copy-clut-alpha-texture-anim-layer-func ((dma-buf dma-buffer) (fbp-to-draw uint) (width int) (height int) (layer texture-anim-layer) (time float))
|
||||
"Copy only alpha from the layer source clut."
|
||||
|
||||
(pc-texture-anim-flag copy-clut-alpha dma-buf :qwc 1)
|
||||
(let ((transform (the texture-anim-pc-texture-transform (-> dma-buf base))))
|
||||
(set! (-> transform src-tbp) (-> layer tex dest 0))
|
||||
(set! (-> transform dest-tbp) (* 32 fbp-to-draw))
|
||||
(set! (-> transform pad0) 0)
|
||||
(set! (-> transform pad1) 0)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(return 0)
|
||||
|
||||
(when (and (>= time (-> layer start-time)) (>= (-> layer end-time) time))
|
||||
(texture-anim-layer-interp layer time)
|
||||
(let ((s4-0 1))
|
||||
|
@ -831,6 +1026,19 @@
|
|||
|
||||
(defun set-clut-alpha-texture-anim-layer-func ((dma-buf dma-buffer) (fbp-to-draw uint) (width int) (height int) (layer texture-anim-layer) (time float))
|
||||
"Set clut alpha to 128."
|
||||
|
||||
;; PC version
|
||||
(pc-texture-anim-flag set-clut-alpha dma-buf :qwc 1)
|
||||
(let ((transform (the texture-anim-pc-texture-transform (-> dma-buf base))))
|
||||
(set! (-> transform src-tbp) (* 32 fbp-to-draw))
|
||||
(set! (-> transform dest-tbp) (* 32 fbp-to-draw))
|
||||
(set! (-> transform pad0) 0)
|
||||
(set! (-> transform pad1) 0)
|
||||
)
|
||||
(&+! (-> dma-buf base) 16)
|
||||
(return 0)
|
||||
|
||||
|
||||
(let ((v1-0 1))
|
||||
(dma-buffer-add-gs-set dma-buf
|
||||
(test-1 (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always)))
|
||||
|
@ -1060,7 +1268,18 @@
|
|||
"Upload the clut for the clouds."
|
||||
(when (-> arg1 tex)
|
||||
(make-cloud-clut (the-as (pointer uint32) (-> arg1 tex pad 0)) (-> arg1 extra y) (-> arg1 extra z))
|
||||
(upload-vram-data arg0 (the-as int (-> arg1 tex clutdest)) (the-as pointer (-> arg1 tex pad 0)) 16 16)
|
||||
|
||||
;(upload-vram-data arg0 (the-as int (-> arg1 tex clutdest)) (the-as pointer (-> arg1 tex pad 0)) 16 16)
|
||||
(pc-texture-anim-flag upload-clut-16-16 arg0 :qwc 1)
|
||||
(let ((upload-record (the texture-anim-pc-upload (-> arg0 base))))
|
||||
(set! (-> upload-record data) (the-as pointer (-> arg1 tex pad 0))) ;; the clut16x16 object
|
||||
(set! (-> upload-record width) 16)
|
||||
(set! (-> upload-record height) 16)
|
||||
(set! (-> upload-record dest) (-> arg1 tex clutdest))
|
||||
(set! (-> upload-record format) (gs-psm ct32))
|
||||
)
|
||||
(&+! (-> arg0 base) 16)
|
||||
|
||||
(set! (-> *texture-pool* ids (shr (-> arg1 tex clutdest) 6)) (the-as uint 0))
|
||||
0
|
||||
)
|
||||
|
@ -1202,7 +1421,18 @@
|
|||
|
||||
(defun texture-anim-alpha-ramp-clut-upload ((arg0 dma-buffer) (arg1 texture-anim))
|
||||
(when (-> arg1 tex)
|
||||
(upload-vram-data arg0 (the-as int (-> arg1 tex clutdest)) (the-as pointer (-> arg1 tex pad 0)) 16 16)
|
||||
;(upload-vram-data arg0 (the-as int (-> arg1 tex clutdest)) (the-as pointer (-> arg1 tex pad 0)) 16 16)
|
||||
|
||||
(pc-texture-anim-flag upload-clut-16-16 arg0 :qwc 1)
|
||||
(let ((upload-record (the texture-anim-pc-upload (-> arg0 base))))
|
||||
(set! (-> upload-record data) (the-as pointer (-> arg1 tex pad 0))) ;; the clut16x16 object
|
||||
(set! (-> upload-record width) 16)
|
||||
(set! (-> upload-record height) 16)
|
||||
(set! (-> upload-record dest) (-> arg1 tex clutdest))
|
||||
(set! (-> upload-record format) (gs-psm ct32))
|
||||
)
|
||||
(&+! (-> arg0 base) 16)
|
||||
|
||||
(set! (-> *texture-pool* ids (shr (-> arg1 tex clutdest) 6)) (the-as uint 0))
|
||||
0
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue