custom models: envmap support (#3777)

Custom models and model replacements now support environment mapping.

Also fixed some cases in Jak 3 where tfrags had missing textures
(defaulting to texture 0 from tpage 0, which in Jak 3 is the default eye
texture) and replaced them with more suitable alternatives.

Fixes #3776 


https://github.com/user-attachments/assets/7c11b0de-b254-40cb-9719-11238dfb3d43


![image](https://github.com/user-attachments/assets/4cc60b55-4965-4cb8-b29d-096560e7b3aa)


![image](https://github.com/user-attachments/assets/cb46d0d1-57d7-482c-b235-15d0e633f62c)
This commit is contained in:
Hat Kid
2024-11-23 14:32:55 +01:00
committed by GitHub
parent 2ff49b9905
commit c263bc2114
11 changed files with 350 additions and 246 deletions
+73
View File
@@ -1,5 +1,7 @@
#include "gltf_util.h"
#include "image_resize.h"
#include "common/log/log.h"
namespace gltf_util {
@@ -424,6 +426,64 @@ int texture_pool_add_texture(TexturePool* pool, const tinygltf::Image& tex, int
return idx;
}
int texture_pool_add_envmap_control_texture(TexturePool* pool,
const tinygltf::Model& model,
int rgb_image_id,
int mr_image_id,
bool wrap_w,
bool wrap_h) {
const auto& existing = pool->envmap_textures_by_gltf_id.find({rgb_image_id, mr_image_id});
if (existing != pool->envmap_textures_by_gltf_id.end()) {
lg::info("Reusing envmap textures");
return existing->second;
}
const auto& rgb_tex = model.images.at(rgb_image_id);
const auto& mr_tex = model.images.at(mr_image_id);
lg::info("new envmap texture {} {}", rgb_tex.name, mr_tex.name);
ASSERT(rgb_tex.bits == 8);
ASSERT(rgb_tex.component == 4);
ASSERT(rgb_tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE);
ASSERT(mr_tex.bits == 8);
ASSERT(mr_tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE);
ASSERT(mr_tex.component == 4);
std::vector<u8> resized_mr_tex;
const u8* mr_src;
if (rgb_tex.width == mr_tex.width && rgb_tex.height == mr_tex.height) {
mr_src = mr_tex.image.data();
} else {
resized_mr_tex.resize(rgb_tex.width * rgb_tex.height * 4);
resize_rgba_image(resized_mr_tex.data(), rgb_tex.width, rgb_tex.height, mr_tex.image.data(),
mr_tex.width, mr_tex.height, wrap_w, wrap_h);
mr_src = resized_mr_tex.data();
}
size_t idx = pool->textures_by_idx.size();
pool->envmap_textures_by_gltf_id[{rgb_image_id, mr_image_id}] = idx;
auto& tt = pool->textures_by_idx.emplace_back();
tt.w = rgb_tex.width;
tt.h = rgb_tex.height;
tt.debug_name = rgb_tex.name;
tt.debug_tpage_name = "custom-level";
tt.load_to_pool = false;
tt.combo_id = 0; // doesn't matter, not a pool tex
tt.data.resize(tt.w * tt.h);
ASSERT(rgb_tex.image.size() >= tt.data.size());
memcpy(tt.data.data(), rgb_tex.image.data(), tt.data.size() * 4);
// adjust alpha from metallic channel
for (size_t i = 0; i < tt.data.size(); i++) {
u32 rgb = tt.data[i];
u32 metal = mr_src[4 * i + 2] / 4;
rgb &= 0xff'ff'ff;
rgb |= (metal << 24);
tt.data[i] = rgb;
}
return idx;
}
math::Matrix4f affine_translation(const math::Vector3f& translation) {
math::Matrix4f result = math::Matrix4f::identity();
result(0, 3) = translation[0];
@@ -607,6 +667,19 @@ void setup_draw_mode_from_sampler(const tinygltf::Sampler& sampler, DrawMode* mo
}
}
EnvmapSettings envmap_settings_from_gltf(const tinygltf::Material& mat) {
EnvmapSettings settings;
ASSERT(mat.extensions.contains("KHR_materials_specular"));
const auto& specular_extension = mat.extensions.at("KHR_materials_specular");
ASSERT(specular_extension.Has("specularColorTexture"));
auto& texture = specular_extension.Get("specularColorTexture");
ASSERT(texture.Has("index"));
settings.texture_idx = texture.Get("index").Get<int>();
return settings;
}
std::optional<int> find_single_skin(const tinygltf::Model& model,
const std::vector<NodeWithTransform>& all_nodes) {
std::optional<int> skin_index;
+12
View File
@@ -65,6 +65,12 @@ struct TexturePool {
};
int texture_pool_add_texture(TexturePool* pool, const tinygltf::Image& tex, int alpha_shift = 1);
int texture_pool_add_envmap_control_texture(TexturePool* pool,
const tinygltf::Model& model,
int rgb_image_id,
int mr_image_id,
bool wrap_w,
bool wrap_h);
int texture_pool_debug_checker(TexturePool* pool);
struct NodeWithTransform {
@@ -114,6 +120,12 @@ std::vector<NodeWithTransform> flatten_nodes_from_all_scenes(const tinygltf::Mod
void setup_alpha_from_material(const tinygltf::Material& material, DrawMode* mode);
void setup_draw_mode_from_sampler(const tinygltf::Sampler& sampler, DrawMode* mode);
struct EnvmapSettings {
int texture_idx = -1;
};
EnvmapSettings envmap_settings_from_gltf(const tinygltf::Material& mat);
/*!
* Find the index of the skin for this model. Returns nullopt if there is no skin, the index of the
* skin if there is a single skin used, or fatal error if there are multiple skins.
@@ -735,10 +735,6 @@
// there are some missing textures. I don't know what the game actually does here.
// the format for entries is [level, tpage, index]
"missing_textures": [
["lfac", 0, 0],
["ltow", 0, 0],
["lcit", 0, 0],
["pow", 0, 0],
["wasintro", 0, 0],
["lfacctyb", 0, 0],
["intpfall", 0, 0],
@@ -2032,6 +2032,20 @@ s32 find_or_add_texture_to_level(u32 combo_tex_id,
if (ok_to_miss) {
// we're missing a texture, just use the first one.
tex_it = tdb.textures.begin();
// some tfrags in jak 3 are missing textures, so we use some suitable
// replacements instead of the default eye texture
static std::map<std::string, std::string> per_level_tex_hacks = {
{"wasintro", "des-rock-01"},
{"intpfall", "common-black"},
{"powergd", "common-black"},
};
auto it = std::find_if(tdb.textures.begin(), tdb.textures.end(),
[&](const std::pair<u32, TextureDB::TextureData> val) {
return val.second.name == per_level_tex_hacks[level_name];
});
if (it != tdb.textures.end()) {
tex_it = it;
}
} else {
ASSERT_MSG(
false,
+90 -18
View File
@@ -3,6 +3,11 @@
using namespace gltf_util;
namespace decompiler {
bool material_has_envmap(const tinygltf::Material& mat) {
return mat.extensions.contains("KHR_materials_specular");
}
void extract(const std::string& name,
MercExtractData& out,
const tinygltf::Model& model,
@@ -16,6 +21,7 @@ void extract(const std::string& name,
std::map<int, tfrag3::MercDraw> draw_by_material;
int mesh_count = 0;
int prim_count = 0;
bool has_envmaps = false;
for (const auto& n : all_nodes) {
const auto& node = model.nodes[n.node_idx];
@@ -72,42 +78,108 @@ void extract(const std::string& name,
}
tfrag3::MercEffect e;
tfrag3::MercEffect envmap_eff;
out.new_model.name = name;
out.new_model.max_bones = 120;
out.new_model.max_bones = 100; // idk
out.new_model.max_draws = 200;
for (const auto& [mat_idx, d_] : draw_by_material) {
e.all_draws.push_back(d_);
auto& draw = e.all_draws.back();
draw.mode = make_default_draw_mode();
auto process_normal_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) {
const auto& mat = model.materials[mat_idx];
eff.all_draws.push_back(d_);
auto& draw = eff.all_draws.back();
draw.mode = gltf_util::make_default_draw_mode();
if (mat_idx == -1) {
lg::warn("Draw had a material index of -1, using bogus texture.");
lg::warn("Draw had a material index of -1, using default texture.");
draw.tree_tex_id = 0;
continue;
return;
}
const auto& mat = model.materials[mat_idx];
int tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index;
if (tex_idx == -1) {
lg::warn("Material {} has no texture, using bogus texture.", mat.name);
lg::warn("Material {} has no texture, using default texture.", mat.name);
draw.tree_tex_id = 0;
continue;
return;
}
const auto& tex = model.textures[tex_idx];
ASSERT(tex.sampler >= 0);
ASSERT(tex.source >= 0);
setup_alpha_from_material(mat, &draw.mode);
setup_draw_mode_from_sampler(model.samplers.at(tex.sampler), &draw.mode);
gltf_util::setup_draw_mode_from_sampler(model.samplers.at(tex.sampler), &draw.mode);
gltf_util::setup_alpha_from_material(mat, &draw.mode);
const auto& img = model.images[tex.source];
draw.tree_tex_id = tex_offset + texture_pool_add_texture(&out.tex_pool, img);
};
auto process_envmap_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) {
const auto& mat = model.materials[mat_idx];
eff.all_draws.push_back(d_);
auto& draw = eff.all_draws.back();
draw.mode = gltf_util::make_default_draw_mode();
if (mat_idx == -1) {
lg::warn("Envmap draw had a material index of -1, using default texture.");
draw.tree_tex_id = texture_pool_debug_checker(&out.tex_pool);
return;
}
int base_tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index;
if (base_tex_idx == -1) {
lg::warn("Envmap material {} has no texture, using default texture.", mat.name);
draw.tree_tex_id = texture_pool_debug_checker(&out.tex_pool);
return;
}
int roughness_tex_idx = mat.pbrMetallicRoughness.metallicRoughnessTexture.index;
ASSERT(roughness_tex_idx >= 0);
const auto& base_tex = model.textures[base_tex_idx];
ASSERT(base_tex.sampler >= 0);
ASSERT(base_tex.source >= 0);
gltf_util::setup_draw_mode_from_sampler(model.samplers.at(base_tex.sampler), &draw.mode);
gltf_util::setup_alpha_from_material(mat, &draw.mode);
const auto& roughness_tex = model.textures.at(roughness_tex_idx);
ASSERT(roughness_tex.sampler >= 0);
ASSERT(roughness_tex.source >= 0);
draw.tree_tex_id =
tex_offset + gltf_util::texture_pool_add_envmap_control_texture(
&out.tex_pool, model, base_tex.source, roughness_tex.source,
!draw.mode.get_clamp_s_enable(), !draw.mode.get_clamp_t_enable());
// now, setup envmap draw:
auto envmap_settings = gltf_util::envmap_settings_from_gltf(mat);
const auto& envmap_tex = model.textures[envmap_settings.texture_idx];
ASSERT(envmap_tex.sampler >= 0);
ASSERT(envmap_tex.source >= 0);
auto env_mode = gltf_util::make_default_draw_mode();
gltf_util::setup_draw_mode_from_sampler(model.samplers.at(envmap_tex.sampler), &env_mode);
eff.envmap_texture = tex_offset + gltf_util::texture_pool_add_texture(
&out.tex_pool, model.images[envmap_tex.source]);
env_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST);
env_mode.enable_ab();
eff.envmap_mode = env_mode;
};
for (const auto& [mat_idx, d_] : draw_by_material) {
const auto& mat = model.materials[mat_idx];
if (!material_has_envmap(mat)) {
process_normal_draw(e, mat_idx, d_);
} else {
has_envmaps = true;
envmap_eff.has_envmap = true;
process_envmap_draw(envmap_eff, mat_idx, d_);
}
}
lg::info("total of {} unique materials ({} normal, {} envmap)",
e.all_draws.size() + envmap_eff.all_draws.size(), e.all_draws.size(),
envmap_eff.all_draws.size());
// in case a model only has envmap draws, we don't push the normal merc effect
if (!e.all_draws.empty()) {
out.new_model.effects.push_back(e);
}
if (has_envmaps) {
out.new_model.effects.push_back(envmap_eff);
}
lg::info("total of {} unique materials", e.all_draws.size());
e.has_mod_draw = false;
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count,
out.new_vertices.size());
@@ -542,10 +542,9 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
bool is_custom_model = model->effects.at(0).all_draws.at(0).no_strip;
u64 current_ignore_alpha_bits = flags->ignore_alpha_mask; // shader settings
u64 current_effect_enable_bits = flags->enable_mask; // mask for game to disable an effect
bool model_uses_mod =
flags->bitflags & 1 && !is_custom_model; // if we should update vertices from game.
bool model_uses_mod = flags->bitflags & 1; // if we should update vertices from game.
bool model_disables_fog = flags->bitflags & 2;
bool model_uses_pc_blerc = flags->bitflags & 4 && !is_custom_model;
bool model_uses_pc_blerc = flags->bitflags & 4;
bool model_disables_envmap = flags->bitflags & 8;
bool model_no_texture = flags->bitflags & 16;
input_data += 32;
@@ -569,9 +568,9 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// will hold opengl buffers for the updated vertices
ModBuffers mod_opengl_buffers[kMaxEffect];
if (model_uses_pc_blerc) {
if (model_uses_pc_blerc && !is_custom_model) {
model_mod_blerc_draws(num_effects, model, lev, mod_opengl_buffers, blerc_weights, stats);
} else if (model_uses_mod) { // only if we've enabled, this path is slow.
} else if (model_uses_mod && !is_custom_model) { // only if we've enabled, this path is slow.
model_mod_draws(num_effects, model, lev, input_data, setup, mod_opengl_buffers, stats);
}
@@ -651,7 +650,8 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
auto& effect = model->effects[ei];
bool should_envmap = effect.has_envmap && !model_disables_envmap;
bool should_mod = (model_uses_pc_blerc || model_uses_mod) && effect.has_mod_draw;
bool should_mod =
!is_custom_model && ((model_uses_pc_blerc || model_uses_mod) && effect.has_mod_draw);
if (should_mod) {
// draw as two parts, fixed and mod
@@ -1079,6 +1079,7 @@ Merc2::Draw* Merc2::try_alloc_envmap_draw(const tfrag3::MercDraw& mdraw,
draw->first_bone = args.first_bone;
draw->light_idx = args.lights;
draw->num_triangles = mdraw.num_triangles;
draw->no_strip = mdraw.no_strip;
for (int i = 0; i < 4; i++) {
draw->fade[i] = args.fade[i];
}
+5
View File
@@ -271,6 +271,11 @@
(define *pause-lock* #f) ;; set to #t when paused and doing a single frame advance with R2.
;; og:preserve-this added
(defmethod print ((this process-tree))
(format #t "#<~A ~S @ #x~X>" (-> this type) (-> this name) this)
this)
(defmethod new process-tree ((allocation symbol) (type-to-make type) (name basic))
"Create a process-tree node"
;; allocate
+85 -12
View File
@@ -1,9 +1,14 @@
#include "MercExtract.h"
#include "common/log/log.h"
#include "common/util/gltf_util.h"
#include "goalc/build_level/common/gltf_mesh_extract.h"
bool material_has_envmap(const tinygltf::Material& mat) {
return mat.extensions.contains("KHR_materials_specular");
}
void extract(const std::string& name,
MercExtractData& out,
const tinygltf::Model& model,
@@ -16,6 +21,7 @@ void extract(const std::string& name,
std::map<int, tfrag3::MercDraw> draw_by_material;
int mesh_count = 0;
int prim_count = 0;
bool has_envmaps = false;
for (const auto& n : all_nodes) {
const auto& node = model.nodes[n.node_idx];
@@ -84,25 +90,27 @@ void extract(const std::string& name,
}
tfrag3::MercEffect e;
tfrag3::MercEffect envmap_eff;
out.new_model.name = name;
out.new_model.max_bones = 120; // idk
out.new_model.max_bones = 100; // idk
out.new_model.max_draws = 200;
for (const auto& [mat_idx, d_] : draw_by_material) {
e.all_draws.push_back(d_);
auto& draw = e.all_draws.back();
auto process_normal_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) {
const auto& mat = model.materials[mat_idx];
eff.all_draws.push_back(d_);
auto& draw = eff.all_draws.back();
draw.mode = gltf_util::make_default_draw_mode();
if (mat_idx == -1) {
lg::warn("Draw had a material index of -1, using default texture.");
draw.tree_tex_id = 0;
continue;
return;
}
const auto& mat = model.materials[mat_idx];
int tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index;
if (tex_idx == -1) {
lg::warn("Material {} has no texture, using default texture.", mat.name);
draw.tree_tex_id = 0;
continue;
return;
}
const auto& tex = model.textures[tex_idx];
@@ -113,12 +121,77 @@ void extract(const std::string& name,
const auto& img = model.images[tex.source];
draw.tree_tex_id = tex_offset + texture_pool_add_texture(&out.tex_pool, img);
};
auto process_envmap_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) {
const auto& mat = model.materials[mat_idx];
eff.all_draws.push_back(d_);
auto& draw = eff.all_draws.back();
draw.mode = gltf_util::make_default_draw_mode();
if (mat_idx == -1) {
lg::warn("Envmap draw had a material index of -1, using default texture.");
draw.tree_tex_id = texture_pool_debug_checker(&out.tex_pool);
return;
}
int base_tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index;
if (base_tex_idx == -1) {
lg::warn("Envmap material {} has no texture, using default texture.", mat.name);
draw.tree_tex_id = texture_pool_debug_checker(&out.tex_pool);
return;
}
int roughness_tex_idx = mat.pbrMetallicRoughness.metallicRoughnessTexture.index;
ASSERT(roughness_tex_idx >= 0);
const auto& base_tex = model.textures[base_tex_idx];
ASSERT(base_tex.sampler >= 0);
ASSERT(base_tex.source >= 0);
gltf_util::setup_draw_mode_from_sampler(model.samplers.at(base_tex.sampler), &draw.mode);
gltf_util::setup_alpha_from_material(mat, &draw.mode);
const auto& roughness_tex = model.textures.at(roughness_tex_idx);
ASSERT(roughness_tex.sampler >= 0);
ASSERT(roughness_tex.source >= 0);
draw.tree_tex_id =
tex_offset + gltf_util::texture_pool_add_envmap_control_texture(
&out.tex_pool, model, base_tex.source, roughness_tex.source,
!draw.mode.get_clamp_s_enable(), !draw.mode.get_clamp_t_enable());
// now, setup envmap draw:
auto envmap_settings = gltf_util::envmap_settings_from_gltf(mat);
const auto& envmap_tex = model.textures[envmap_settings.texture_idx];
ASSERT(envmap_tex.sampler >= 0);
ASSERT(envmap_tex.source >= 0);
auto env_mode = gltf_util::make_default_draw_mode();
gltf_util::setup_draw_mode_from_sampler(model.samplers.at(envmap_tex.sampler), &env_mode);
eff.envmap_texture = tex_offset + gltf_util::texture_pool_add_texture(
&out.tex_pool, model.images[envmap_tex.source]);
env_mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST);
env_mode.enable_ab();
eff.envmap_mode = env_mode;
};
for (const auto& [mat_idx, d_] : draw_by_material) {
const auto& mat = model.materials[mat_idx];
if (!material_has_envmap(mat)) {
process_normal_draw(e, mat_idx, d_);
} else {
has_envmaps = true;
envmap_eff.has_envmap = true;
process_envmap_draw(envmap_eff, mat_idx, d_);
}
}
lg::info("total of {} unique materials ({} normal, {} envmap)",
e.all_draws.size() + envmap_eff.all_draws.size(), e.all_draws.size(),
envmap_eff.all_draws.size());
// in case a model only has envmap draws, we don't push the normal merc effect
if (!e.all_draws.empty()) {
out.new_model.effects.push_back(e);
}
if (has_envmaps) {
out.new_model.effects.push_back(envmap_eff);
}
lg::info("total of {} unique materials", e.all_draws.size());
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
out.new_model.effects.push_back(e);
lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count,
out.new_vertices.size());
+63 -131
View File
@@ -376,107 +376,25 @@ size_t ArtJointAnim::generate(DataObjectGenerator& gen) const {
static size_t gen_dummy_frag_geo(DataObjectGenerator& gen) {
size_t result = gen.current_offset_bytes();
// frag geo stolen from money-lod0
// frag geo stolen from money-lod2
static std::vector<u32> words = {
0xa4320c04, 0x8000026, 0x302a0000, 0x604, 0xa910, 0xac16, 0x100af1c,
0xb23d, 0x100b585, 0x100b870, 0xbb76, 0xbe52, 0x80808080, 0x80808080,
0x51180504, 0x1000013, 0x1b150000, 0x604, 0x5225, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x0, 0x0, 0x89b78086, 0xf1a910a, 0x3a4b7000, 0x5f818086,
0xf1a1094, 0x29347014, 0x49648186, 0x91313, 0x4e5d8024, 0x354b8186, 0xf1a6416,
0x3a497024, 0x40538186, 0x91919, 0x647b8034, 0x24348186, 0xf1a371c, 0x647f7034,
0x495d8186, 0x91f1f, 0x7a9c8044, 0x35498086, 0xf1a2231, 0x8eb57044, 0x5f7b8186,
0x92525, 0x83ad8054, 0x5f7f8186, 0xf1a2828, 0x9fcc7054, 0x759c8186, 0x92b2b,
0x7aa38064, 0x1c257f86, 0x273b2e2e, 0x94b86040, 0xe198186, 0x2737d034, 0x71936038,
0xe188186, 0x273bcd3a, 0x57676030, 0x1c2a8186, 0x2737613d, 0x34456028, 0xe1b8086,
0x485d40c7, 0x2e3c5028, 0x293c8186, 0x485d5b43, 0x131a5020, 0x26398186, 0x6e804646,
0xf164020, 0x4b688186, 0x6e805549, 0x34018, 0x4c688186, 0x979f4c4c, 0x5073018,
0x72978086, 0x979f4fc1, 0x5073010, 0x73988086, 0x6e807352, 0x34010, 0x4c698186,
0x485d6d58, 0x5085018, 0x2f488086, 0x273b5e67, 0x21256020, 0x526d8186, 0x2737a66a,
0x13196018, 0x72988186, 0x485da070, 0x5085010, 0x98c78086, 0x6e80767f, 0xf164008,
0x95c48186, 0x979fc479, 0x13193008, 0xb0e68186, 0x979f7c7c, 0x2e3b3000, 0xb4ea8186,
0x6e808282, 0x2b394000, 0x95c48186, 0x485d9d85, 0x131b5008, 0xb0e68186, 0x485d8888,
0x2e3c5000, 0x8fbb8186, 0x2737978b, 0x212a6008, 0xa2db8186, 0x273b8e8e, 0x34486000,
0x6c998086, 0x273b9aa3, 0x13186010, 0x87f86, 0x485dcaca, 0x51685030, 0x75a37f86,
0x90707, 0x4e648000, 0x5f858186, 0x90d0d, 0x45538014, 0x0, 0x0,
0xcb01005f, 0xcb00fffa, 0xcb010064, 0x5101e01, 0x0, 0x0, 0x306,
0x60030000, 0x120, 0x0, 0x1cf02c14, 0x2008044, 0x0, 0x0,
0x34, 0x81010000, 0x0, 0x0, 0x8, 0x6d100000, 0x44,
0x80, 0x42, 0x30000, 0x86321604, 0x400001c, 0x2422440e, 0x4,
0x1004f28, 0x1008b4c, 0xb225, 0xca4c, 0x1000128, 0x1000422, 0x1000a2e,
0x10064ca, 0x6a34, 0x1006d2e, 0x70ca, 0x1007340, 0x7946, 0x7f4c,
0x100884c, 0x8e4f, 0x9479, 0x9a7c, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x0, 0x0, 0x131a8086,
0x215d0d9d, 0x87c45040, 0x38186, 0x4780a313, 0x65984038, 0x38186, 0x47806116,
0x3d684030, 0x5078186, 0x709fa919, 0x3e693030, 0x13198186, 0x709f5b1c, 0x1b3c3028,
0x21208186, 0x96bbaf1f, 0x21452028, 0x34428186, 0x96bf5522, 0xe242020, 0x3a408086,
0xb6da252e, 0x27411024, 0x647f8186, 0xb6da4628, 0x16261014, 0x647a8186, 0xcbf1402b,
0x32450014, 0x4e528086, 0xcbf1313a, 0x3b5b0024, 0x29268086, 0xb6da34b5, 0x51811034,
0x45458186, 0xcbf13737, 0x51860034, 0x64808186, 0xd3ff3d3d, 0x51800040, 0x7aa58186,
0xcbf14343, 0x3b520000, 0x8ebf8186, 0xb6dac749, 0x27401000, 0x71948186, 0x96bf854c,
0x132010, 0x57668186, 0x96bb8252, 0x122018, 0x2e3b8186, 0x709f7c58, 0x1a3020,
0xf168186, 0x4780765e, 0x18394028, 0x94bb8086, 0x96bb91c4, 0xe202008, 0xa7dc8086,
0x96bf97c1, 0x21422000, 0xf167f86, 0x4780a0a0, 0x8ac74040, 0x5078186, 0x709fbea6,
0x64983038, 0x13138186, 0x96bfb8ac, 0x446c2030, 0x13128186, 0x96bbbbbb, 0x5e9a2038,
0x34458186, 0x370707, 0x94d66048, 0x5088186, 0x215d6710, 0x64975038, 0xcb010064,
0xcb00ffd3, 0xcb010051, 0x5021000, 0x4088003, 0x10306060, 0x3, 0x0,
0x8d301104, 0x300001f, 0x2624430a, 0x4, 0x910a, 0x100a928, 0xc42b,
0x1006aa0, 0x70a6, 0x73bb, 0x9a07, 0xa00d, 0x100a3a0, 0x100a6bb,
0xac34, 0xb237, 0xb83d, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x8ccc8186,
0xf1a4c07, 0x16817074, 0x7bb58186, 0xf1a550a, 0x40b77064, 0x94d68186, 0x2737460d,
0x46bb6068, 0x81b88186, 0x273b8e10, 0x59db6060, 0x87c48186, 0x485d4013, 0x67e65060,
0x64978186, 0x485d8816, 0x75f85058, 0x65988186, 0x6e803a19, 0x7afd4058, 0x3d688186,
0x6e80821c, 0x7afd4050, 0x3e698186, 0x979f341f, 0x75f93050, 0x1b3c8186, 0x979f7c22,
0x67e73048, 0x21458086, 0xbdbb252e, 0x59e02048, 0xe248086, 0xbdbf2876, 0x46be2040,
0x27418186, 0xdddaaf2b, 0x40c01044, 0x446c8186, 0xbdbfc731, 0x67ed2050, 0x64988186,
0x979f3737, 0x75f93058, 0x8ac78186, 0x6e803d3d, 0x6bea4060, 0xa2e58186, 0x485d4343,
0x4cc45068, 0xa2e88186, 0x273b4949, 0x23996070, 0x679c7f86, 0x95252, 0x2ca38064,
0x517f8086, 0xf1a5894, 0x51cc7054, 0x5e938186, 0x27378b5b, 0x67e76058, 0x44678086,
0x273b5e97, 0x67e86050, 0x3e688186, 0x485d8561, 0x75f85050, 0x1b3c8186, 0x485d9d64,
0x67e55048, 0x18398186, 0x6e807f67, 0x6bea4048, 0x1a8186, 0x979f796d, 0x4cc53040,
0x3b5b8086, 0xf2f1b5be, 0x2cae0044, 0x51868186, 0xf2f1bbbb, 0x35bb0054, 0x51818186,
0xdddac1c1, 0x51da1054, 0x67a37f86, 0x90101, 0x648080, 0x70ad7f86, 0x94f04,
0x16858074, 0x0, 0x0, 0x0, 0xcb010051, 0xcb00fffa, 0xcb010016,
0x5021000, 0xc008003, 0x81860080, 0x0, 0x0, 0x92351604, 0x300001f,
0x2826450f, 0x4, 0xac31, 0x100af5e, 0x100c449, 0xa01, 0x1000d07,
0x6143, 0x100643d, 0x10079c1, 0x8537, 0x883d, 0x1008b07, 0x1008e49,
0x100b243, 0xb549, 0x100b837, 0xbe31, 0xc1c1, 0xc7bb, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080,
0x80808080, 0x80808080, 0x0, 0x0, 0x0, 0x808186, 0x707,
0x39800040, 0x2ab78186, 0x141a1010, 0xf4b7080, 0x51e78186, 0x2c379113, 0x2c6d6078,
0x43db8186, 0x2c3b1616, 0x9486080, 0x5ff88186, 0x4d5d9719, 0x26695078, 0x51e68186,
0x4d5d1c1c, 0x33c5080, 0x64fd8186, 0x73809d1f, 0x25684078, 0x55ea8186, 0x73802222,
0x394080, 0x5ff98186, 0x9c9fa325, 0x26683078, 0x51e68186, 0x9c9f2828, 0x33b3080,
0x51ee8186, 0xc2bba92b, 0x2c662078, 0x43dc8186, 0xc2bf2e2e, 0x9422080, 0x3bda8186,
0xe2da4631, 0x397f1074, 0x2abf8186, 0xe2da3434, 0xf401080, 0x1fbb8086, 0xf7f13740,
0x397a0074, 0x16a58186, 0xf7f13a3a, 0x23520080, 0x808186, 0xffffcd3d, 0x39800040,
0x16ae8186, 0xf7f1ca43, 0x4fa50064, 0x2ac08186, 0xe2da7649, 0x63bf1064, 0x51ed8186,
0xc2bfa64c, 0x46942070, 0x43e08186, 0xc2bb734f, 0x69bb2068, 0x5ff98186, 0x9c9fa052,
0x4c973070, 0x51e78186, 0x9c9f6d55, 0x6fc43068, 0x64fd8186, 0x73809a58, 0x4d984070,
0x55ea8186, 0x7380675b, 0x72c74068, 0x5ff88186, 0x4d5d945e, 0x4c985070, 0x36c58186,
0x9c9f826a, 0x8ae63060, 0x30be8186, 0xc2bf7c70, 0x7cdc2060, 0xd9a8086, 0xc2bb7fbb,
0x8aee2058, 0x16a37f86, 0x5090101, 0x23640000, 0x1fad7f86, 0x5090404, 0x39850074,
0x0, 0x0, 0x0, 0xcb010000, 0xcb00ffff, 0xcb010039, 0x5021000,
0x20001b, 0x6c00c102, 0x2, 0x0, 0x210f0904, 0x6, 0xb090b05,
0x4, 0x101, 0x707, 0x1307, 0x1c04, 0x1f07, 0x80808080,
0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x0, 0x538186,
0x90d0d, 0x1f7b0034, 0x95d7f86, 0x91010, 0x359c0044, 0x1f7b8186, 0x91616,
0x3ead0054, 0x359c8186, 0x91919, 0x35a30064, 0x1f857f86, 0x90404, 0x530014,
0x9648186, 0x90a0a, 0x95d0024, 0x0, 0x0, 0xcb01001f, 0xcb00fffa,
0xcb01001f, 0x1021000, 0x223, 0x0, 0x0, 0x0};
0x80808080, 0x80808080, 0x80808080, 0xb4e48086, 0x757d3a0a, 0x1e324000, 0x1e328186,
0x757d3410, 0x1c4020, 0x432e8186, 0x251313, 0x71a28044, 0x1c8186, 0x757d2e16,
0x96ce4040, 0x719b8186, 0x151919, 0x71c08064, 0x96ce8186, 0x757d281c, 0xb4e44060,
0x71d28186, 0x251f1f, 0x435e8080, 0xb4e48186, 0x757d4322, 0x1e324080, 0x71cc7f86,
0xffdb4025, 0x71ad0064, 0x435c8186, 0xffea582b, 0x71bc0044, 0x43348186, 0xffdb5b31,
0x43530024, 0x71a48086, 0xffea3755, 0x43440000, 0x71a47f86, 0xffea3d3d, 0x43440080,
0x432e7f86, 0x254646, 0x71a20044, 0x43657f86, 0x154949, 0x43400024, 0x719b8186,
0x154c4c, 0x71c00064, 0x71d28186, 0x254f4f, 0x435e0000, 0x71d27f86, 0x250707,
0x435e8000, 0x43658186, 0x150d0d, 0x43408024, 0x0, 0x0, 0x0,
0xcb01005a, 0xcb00fffa, 0xcb01005a, 0x2101e01, 0x0, 0x0, 0x306,
0x4030000, 0x120, 0x0, 0x1cf02c14, 0x66c801d, 0x0, 0x0,
0x34, 0x0, 0x0, 0x0, 0x8, 0x0, 0x44,
0x80, 0x42, 0x0,
};
for (auto& word : words) {
gen.add_word(word);
}
@@ -485,12 +403,8 @@ static size_t gen_dummy_frag_geo(DataObjectGenerator& gen) {
size_t gen_dummy_frag_ctrl(DataObjectGenerator& gen) {
size_t result = gen.current_offset_bytes();
gen.add_word(0x1067232);
gen.add_word(0x54320603);
gen.add_word(0x5d300002);
gen.add_word(0x5d350002);
gen.add_word(0x120f0002);
gen.add_word(0x2);
gen.add_word(0x1063918);
gen.add_word(0x603);
gen.add_word(0x0);
gen.add_word(0x0);
return result;
@@ -501,11 +415,11 @@ size_t gen_dummy_frag_ctrl_for_uploads(DataObjectGenerator& gen, int n) {
std::vector<u8> packed_frag_ctrls;
// this is still a bit of a hack - the dummy_frag_ctrl above has 8 fragments, so we need to
// this is still a bit of a hack - the dummy_frag_ctrl above has 4 fragments, so we need to
// provide fragment controls for each. The PC merc renderer will de-duplicate bone uploads over
// all effects and fragments, so we just need to have a single fragment that asks for all bones,
// and everything will work out.
for (int k = 0; k < 8; k++) {
for (int k = 0; k < 4; k++) {
packed_frag_ctrls.push_back(0);
packed_frag_ctrls.push_back(0);
packed_frag_ctrls.push_back(0);
@@ -544,6 +458,32 @@ size_t gen_dummy_extra_info(DataObjectGenerator& gen) {
return result;
}
void generate_merc_effects(DataObjectGenerator& gen, int effect_count, int joints) {
struct EffectLocs {
size_t frag_geo;
size_t frag_ctrl;
size_t extra_info;
};
std::vector<EffectLocs> locs;
for (int i = 0; i < effect_count; i++) {
EffectLocs loc{};
loc.frag_geo = gen.add_word(0); // 112-140 (effect)
loc.frag_ctrl = gen.add_word(0); // 116 (frag-ctrl)
gen.add_word(0x0); // 120 (blend-data)
gen.add_word(0x0); // 124 (blend-ctrl)
gen.add_word(0x10000); // 128
gen.add_word(0x140000); // 132
gen.add_word(0x100001d); // 136
loc.extra_info = gen.add_word(0); // 140 (extra-info)
locs.push_back(loc);
}
for (auto& loc : locs) {
gen.link_word_to_byte(loc.extra_info, gen_dummy_extra_info(gen));
gen.link_word_to_byte(loc.frag_ctrl, gen_dummy_frag_ctrl_for_uploads(gen, joints + 3));
gen.link_word_to_byte(loc.frag_geo, gen_dummy_frag_geo(gen));
}
}
size_t generate_dummy_merc_ctrl(DataObjectGenerator& gen, const ArtGroup& ag) {
gen.align_to_basic();
gen.add_type_tag("merc-ctrl");
@@ -557,37 +497,28 @@ size_t generate_dummy_merc_ctrl(DataObjectGenerator& gen, const ArtGroup& ag) {
gen.add_word(joints); // 20 (num-joints)
gen.add_word(0x0); // 24 (pad)
gen.add_word(0x0); // 28 (pad)
gen.add_word(0x4188ee86); // 32-112 (xyz-scale)
gen.add_word(0x4181b897); // 32-112 (xyz-scale)
gen.add_word(0xc780ff80); // 36 (st-magic)
gen.add_word(0x40798000); // 40 (st-out-a)
gen.add_word(0x40eb4000); // 44 (st-out-b)
gen.add_word(0x4780ff80); // 48 (st-vif-add)
gen.add_word(0x50000); // 52 ((st-int-off << 16) + st-int-scale)
gen.add_word(0x1); // 56 (effect-count)
gen.add_word(ag.merc_effect_count); // 56 (effect-count)
gen.add_word(0x0); // 60 (blend-target-count)
gen.add_word(0xe00005); // 64 ((fragment-count << 16) + tri-count)
gen.add_word(0x860101); // 68
gen.add_word(0x86011b); // 72
gen.add_word(0x0); // 76
gen.add_word(0x0); // 80
gen.add_word(0x120101); // 84
gen.add_word(0x83002c); // 88
gen.add_word(0x3e780184); // 92
gen.add_word(0x0); // 96
gen.add_word(0x0); // 100
gen.add_word(0x0); // 104
gen.add_word(0x0); // 108
auto frag_geo_slot = gen.add_word(0); // 112-140 (effect)
auto frag_ctrl_slot = gen.add_word(0); // 116 (frag-ctrl)
gen.add_word(0x0); // 120 (blend-data)
gen.add_word(0x0); // 124 (blend-ctrl)
gen.add_word(0x50000); // 128
gen.add_word(0xe00000); // 132
gen.add_word(0x100011b); // 136
auto extra_info_slot = gen.add_word(0); // 140 (extra-info)
gen.link_word_to_byte(extra_info_slot, gen_dummy_extra_info(gen));
gen.link_word_to_byte(frag_ctrl_slot, gen_dummy_frag_ctrl_for_uploads(gen, joints + 3));
gen.link_word_to_byte(frag_geo_slot, gen_dummy_frag_geo(gen));
gen.add_word((0x14 * ag.merc_effect_count << 16) +
ag.merc_effect_count); // 64 ((fragment-count << 16) + tri-count)
gen.add_word(0x130101); // 68
gen.add_word(0x13001d); // 72
gen.add_word(0x0); // 76
gen.add_word(0x0); // 80
gen.add_word(0x10101); // 84
gen.add_word(0x130000); // 88
gen.add_word(0x3f319ca9); // 92
gen.add_word(0x0); // 96
gen.add_word(0x0); // 100
gen.add_word(0x0); // 104
gen.add_word(0x0); // 108
generate_merc_effects(gen, ag.merc_effect_count, joints);
return result;
}
@@ -811,6 +742,7 @@ bool run_build_actor(const std::string& mdl_name,
std::vector<Joint> joints;
MercExtractData extract_data;
extract("test", extract_data, model, all_nodes, 0, 0, 0);
ag.merc_effect_count = extract_data.new_model.effects.size();
// MercSwapData out;
// merc_convert(out, extract_data);
// Set up joints:
+1
View File
@@ -207,6 +207,7 @@ struct ArtGroup : Art {
FileInfo info;
std::vector<std::shared_ptr<ArtElement>> elts;
std::map<int, size_t> joint_map;
int merc_effect_count;
explicit ArtGroup(const std::string& file_name) {
info.file_type = "art-group";
@@ -87,23 +87,6 @@ bool prim_needs_tie(const tinygltf::Model& model, const tinygltf::Primitive& pri
return false;
}
struct EnvmapSettings {
int texture_idx = -1;
};
EnvmapSettings envmap_settings_from_gltf(const tinygltf::Material& mat) {
EnvmapSettings settings;
ASSERT(mat.extensions.contains("KHR_materials_specular"));
const auto& specular_extension = mat.extensions.at("KHR_materials_specular");
ASSERT(specular_extension.Has("specularColorTexture"));
auto& texture = specular_extension.Get("specularColorTexture");
ASSERT(texture.Has("index"));
settings.texture_idx = texture.Get("index").Get<int>();
return settings;
}
void extract(const Input& in,
TfragOutput& out,
const tinygltf::Model& model,
@@ -260,64 +243,6 @@ void add_to_packed_verts(std::vector<tfrag3::PackedTieVertices::Vertex>* out,
}
}
int texture_pool_add_envmap_control_texture(TexturePool* pool,
const tinygltf::Model& model,
int rgb_image_id,
int mr_image_id,
bool wrap_w,
bool wrap_h) {
const auto& existing = pool->envmap_textures_by_gltf_id.find({rgb_image_id, mr_image_id});
if (existing != pool->envmap_textures_by_gltf_id.end()) {
lg::info("Reusing envmap textures");
return existing->second;
}
const auto& rgb_tex = model.images.at(rgb_image_id);
const auto& mr_tex = model.images.at(mr_image_id);
lg::info("new envmap texture {} {}", rgb_tex.name, mr_tex.name);
ASSERT(rgb_tex.bits == 8);
ASSERT(rgb_tex.component == 4);
ASSERT(rgb_tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE);
ASSERT(mr_tex.bits == 8);
ASSERT(mr_tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE);
ASSERT(mr_tex.component == 4);
std::vector<u8> resized_mr_tex;
const u8* mr_src;
if (rgb_tex.width == mr_tex.width && rgb_tex.height == mr_tex.height) {
mr_src = mr_tex.image.data();
} else {
resized_mr_tex.resize(rgb_tex.width * rgb_tex.height * 4);
resize_rgba_image(resized_mr_tex.data(), rgb_tex.width, rgb_tex.height, mr_tex.image.data(),
mr_tex.width, mr_tex.height, wrap_w, wrap_h);
mr_src = resized_mr_tex.data();
}
size_t idx = pool->textures_by_idx.size();
pool->envmap_textures_by_gltf_id[{rgb_image_id, mr_image_id}] = idx;
auto& tt = pool->textures_by_idx.emplace_back();
tt.w = rgb_tex.width;
tt.h = rgb_tex.height;
tt.debug_name = rgb_tex.name;
tt.debug_tpage_name = "custom-level";
tt.load_to_pool = false;
tt.combo_id = 0; // doesn't matter, not a pool tex
tt.data.resize(tt.w * tt.h);
ASSERT(rgb_tex.image.size() >= tt.data.size());
memcpy(tt.data.data(), rgb_tex.image.data(), tt.data.size() * 4);
// adjust alpha from metallic channel
for (size_t i = 0; i < tt.data.size(); i++) {
u32 rgb = tt.data[i];
u32 metal = mr_src[4 * i + 2] / 4;
rgb &= 0xff'ff'ff;
rgb |= (metal << 24);
tt.data[i] = rgb;
}
return idx;
}
void extract(const Input& in,
TieOutput& out,
const tinygltf::Model& model,