custom models: option to copy mod/eye draws from original model (#3800)

Two new flags were added to the Blender plugin to allow reusing the mod
and/or eye draws of the original model that is being replaced. Works
pretty well for eyes, but the blerc draws can cause some Z-fighting with
the non-moving parts of the model.

Also a small refactor to the merc replacement code to de-duplicate some
code by moving stuff to `gltf_util.cpp`.
This commit is contained in:
Hat Kid
2024-12-09 20:49:56 +01:00
committed by GitHub
parent 5458d865d4
commit c476b84182
12 changed files with 380 additions and 241 deletions
+85
View File
@@ -756,4 +756,89 @@ tfrag3::PackedTimeOfDay pack_time_of_day(const std::vector<math::Vector<u8, 4>>&
return colors;
}
void process_normal_merc_draw(const tinygltf::Model& model,
MercExtractData& out,
u32 tex_offset,
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;
return;
}
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;
return;
}
const auto& tex = model.textures[tex_idx];
ASSERT(tex.sampler >= 0);
ASSERT(tex.source >= 0);
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);
};
void process_envmap_merc_draw(const tinygltf::Model& model,
MercExtractData& out,
u32 tex_offset,
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;
}
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(mat.pbrMetallicRoughness.metallicRoughnessTexture.index);
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;
};
} // namespace gltf_util
+31
View File
@@ -180,4 +180,35 @@ math::Matrix4f matrix_from_trs(const math::Vector3f& trans,
tfrag3::PackedTimeOfDay pack_time_of_day(const std::vector<math::Vector<u8, 4>>& color_palette);
struct MercExtractData {
TexturePool tex_pool;
std::vector<u32> new_indices;
std::vector<tfrag3::PreloadedVertex> new_vertices;
std::vector<math::Vector<u8, 4>> new_colors;
std::vector<math::Vector3f> normals;
std::vector<JointsAndWeights> joints_and_weights;
tfrag3::MercModel new_model;
};
// Data produced by loading a replacement model
struct MercSwapData {
std::vector<u32> new_indices;
std::vector<tfrag3::MercVertex> new_vertices;
std::vector<tfrag3::Texture> new_textures;
tfrag3::MercModel new_model;
};
void process_normal_merc_draw(const tinygltf::Model& model,
MercExtractData& out,
u32 tex_offset,
tfrag3::MercEffect& eff,
int mat_idx,
const tfrag3::MercDraw& d_);
void process_envmap_merc_draw(const tinygltf::Model& model,
MercExtractData& out,
u32 tex_offset,
tfrag3::MercEffect& eff,
int mat_idx,
const tfrag3::MercDraw& d_);
} // namespace gltf_util
@@ -91,6 +91,8 @@ def draw_func_ob(self, context):
ob = context.object
layout.prop(ob, "set_invisible")
layout.prop(ob, "enable_custom_weights")
layout.prop(ob, "copy_eye_draws")
layout.prop(ob, "copy_mod_draws")
layout.prop(ob, "set_collision")
if (ob.set_collision):
layout.prop(ob, "ignore")
@@ -118,6 +120,8 @@ def register():
bpy.types.Object.set_invisible = bpy.props.BoolProperty(name="Invisible")
bpy.types.Object.set_collision = bpy.props.BoolProperty(name="Apply Collision Properties")
bpy.types.Object.enable_custom_weights = bpy.props.BoolProperty(name="Use Custom Bone Weights")
bpy.types.Object.copy_eye_draws = bpy.props.BoolProperty(name="Copy Eye Draws")
bpy.types.Object.copy_mod_draws = bpy.props.BoolProperty(name="Copy Mod Draws")
bpy.types.Object.ignore = bpy.props.BoolProperty(name="ignore")
bpy.types.Object.noedge = bpy.props.BoolProperty(name="No-Edge")
bpy.types.Object.noentity = bpy.props.BoolProperty(name="No-Entity")
@@ -2603,9 +2603,6 @@ void SetVarElement::push_to_stack(const Env& env, FormPool& pool, FormStack& sta
ASSERT(x->parent_form == m_src);
}
if (auto test0 = m_src->to_string(env) == "(* 0.00024414062 (-> arg0 y))") {
printf("");
}
if (m_src->is_single_element()) {
auto src_as_se = dynamic_cast<SimpleExpressionElement*>(m_src->back());
if (src_as_se) {
@@ -113,6 +113,16 @@ void extract_art_groups_from_level(const ObjectFileDB& db,
if (db.obj_files_by_dgo.count(dgo_name)) {
const auto& files = db.obj_files_by_dgo.at(dgo_name);
MercSwapInfo swapped_info;
// build list of models to replace
auto merc_replacements_path = file_util::get_jak_project_dir() / "custom_assets" /
game_version_names[db.version()] / "merc_replacements";
if (file_util::file_exists(merc_replacements_path.string())) {
auto custom_models =
file_util::find_files_in_dir(merc_replacements_path, std::regex(".*\\.glb"));
for (auto& mdl : custom_models) {
swapped_info.add_to_swap_list(mdl.stem().string());
}
}
for (const auto& file : files) {
if (file.name.length() > 3 && !file.name.compare(file.name.length() - 3, 3, "-ag")) {
const auto& ag_file = db.lookup_record(file);
@@ -12,9 +12,14 @@ namespace decompiler {
// info about what models have been replaced/added per level
struct MercSwapInfo {
std::vector<std::string> swap_list;
std::map<std::string, std::vector<std::string>> per_level_merc_swaps;
std::map<std::string, std::vector<std::string>> per_level_custom_mdls;
bool should_swap(const std::string& model) {
return std::find(swap_list.begin(), swap_list.end(), model) != swap_list.end();
}
bool already_swapped(const std::string& model, const std::string& level) {
auto mdls_it = per_level_merc_swaps.find(level);
if (mdls_it != per_level_merc_swaps.end()) {
@@ -35,6 +40,8 @@ struct MercSwapInfo {
return false;
}
void add_to_swap_list(const std::string& model) { swap_list.push_back(model); }
void add_to_swapped_list(const std::string& model, const std::string& level) {
per_level_merc_swaps[level].push_back(model);
}
+20 -18
View File
@@ -1624,7 +1624,7 @@ void replace_model(tfrag3::Level& lvl, tfrag3::MercModel& model, const fs::path&
}
}
auto swap_info = load_replacement_merc_model(model.name, lvl.merc_data.indices.size(),
auto swap_info = load_replacement_merc_model(model, lvl.merc_data.indices.size(),
lvl.merc_data.vertices.size(), lvl.textures.size(),
mdl_path.string(), old_verts, false);
model = swap_info.new_model;
@@ -1647,8 +1647,8 @@ void add_custom_model_to_level(tfrag3::Level& lvl,
auto lvl_name = lvl.level_name == "" ? "common" : lvl.level_name;
lg::info("Adding custom model {} to {}", name, lvl_name);
auto merc_data =
load_replacement_merc_model(name, lvl.merc_data.indices.size(), lvl.merc_data.vertices.size(),
lvl.textures.size(), mdl_path.string(), {}, true);
load_custom_merc_model(name, lvl.merc_data.indices.size(), lvl.merc_data.vertices.size(),
lvl.textures.size(), mdl_path.string(), {}, true);
for (auto& idx : merc_data.new_indices) {
lvl.merc_data.indices.push_back(idx);
}
@@ -1798,23 +1798,25 @@ void extract_merc(const ObjectFileData& ag_data,
// do model replacement if present
for (auto& ctrl : ctrls) {
auto merc_replacements_path = file_util::get_jak_project_dir() / "custom_assets" /
game_version_names[version] / "merc_replacements";
if (!swapped_info.already_swapped(ctrl.name, out.level_name)) {
if (file_util::file_exists(merc_replacements_path.string())) {
std::string file_name(ctrl.name + ".glb");
auto mdl_path = merc_replacements_path / file_name;
if (file_util::file_exists(mdl_path.string())) {
auto it = std::find_if(out.merc_data.models.begin(), out.merc_data.models.end(),
[&](const auto& m) { return m.name == ctrl.name; });
if (it != out.merc_data.models.end()) {
auto& model = *it;
replace_model(out, model, mdl_path);
swapped_info.add_to_swapped_list(ctrl.name, out.level_name);
if (swapped_info.should_swap(ctrl.name)) {
auto merc_replacements_path = file_util::get_jak_project_dir() / "custom_assets" /
game_version_names[version] / "merc_replacements";
if (!swapped_info.already_swapped(ctrl.name, out.level_name)) {
if (file_util::file_exists(merc_replacements_path.string())) {
std::string file_name(ctrl.name + ".glb");
auto mdl_path = merc_replacements_path / file_name;
if (file_util::file_exists(mdl_path.string())) {
auto it = std::find_if(out.merc_data.models.begin(), out.merc_data.models.end(),
[&](const auto& m) { return m.name == ctrl.name; });
if (it != out.merc_data.models.end()) {
auto& model = *it;
replace_model(out, model, mdl_path);
swapped_info.add_to_swapped_list(ctrl.name, out.level_name);
}
}
} else {
lg::info("{} in level {} was already swapped, skipping", ctrl.name, out.level_name);
}
} else {
lg::info("{} in level {} was already swapped, skipping", ctrl.name, out.level_name);
}
}
+179 -79
View File
@@ -4,6 +4,149 @@ using namespace gltf_util;
namespace decompiler {
void extract(tfrag3::MercModel& mdl,
MercExtractData& out,
const tinygltf::Model& model,
const std::vector<NodeWithTransform>& all_nodes,
u32 index_offset,
u32 vertex_offset,
u32 tex_offset,
bool& has_custom_weights) {
ASSERT(out.new_vertices.empty());
std::map<int, tfrag3::MercDraw> draw_by_material;
int mesh_count = 0;
int prim_count = 0;
int joints = 3;
auto skin_idx = find_single_skin(model, all_nodes);
if (skin_idx) {
joints += get_joint_count(model, *skin_idx);
}
bool copy_eye_draws = false;
bool copy_mod_draws = false;
for (const auto& n : all_nodes) {
const auto& node = model.nodes[n.node_idx];
if (node.extras.Has("set_invisible") && node.extras.Get("set_invisible").Get<int>()) {
continue;
}
if (node.mesh >= 0) {
const auto& mesh = model.meshes[node.mesh];
mesh_count++;
has_custom_weights = node.extras.Has("enable_custom_weights") &&
node.extras.Get("enable_custom_weights").Get<int>();
copy_eye_draws =
node.extras.Has("copy_eye_draws") && node.extras.Get("copy_eye_draws").Get<int>();
copy_mod_draws =
node.extras.Has("copy_mod_draws") && node.extras.Get("copy_mod_draws").Get<int>();
for (const auto& prim : mesh.primitives) {
prim_count++;
// extract index buffer
std::vector<u32> prim_indices = gltf_util::gltf_index_buffer(
model, prim.indices, out.new_vertices.size() + vertex_offset);
ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, "Unsupported triangle mode");
// extract vertices
auto verts =
gltf_util::gltf_vertices(model, prim.attributes, n.w_T_node, true, true, mesh.name);
out.new_vertices.insert(out.new_vertices.end(), verts.vtx.begin(), verts.vtx.end());
out.new_colors.insert(out.new_colors.end(), verts.vtx_colors.begin(),
verts.vtx_colors.end());
out.normals.insert(out.normals.end(), verts.normals.begin(), verts.normals.end());
ASSERT(out.new_colors.size() == out.new_vertices.size());
if (prim.attributes.count("JOINTS_0") && prim.attributes.count("WEIGHTS_0")) {
auto joints_and_weights = gltf_util::extract_and_flatten_joints_and_weights(model, prim);
ASSERT(joints_and_weights.size() == verts.vtx.size());
out.joints_and_weights.insert(out.joints_and_weights.end(), joints_and_weights.begin(),
joints_and_weights.end());
} else {
// add fake data for vertices without this data
gltf_util::JointsAndWeights dummy;
dummy.joints[0] = 3;
dummy.weights[0] = 1.f;
for (size_t i = 0; i < out.new_vertices.size(); i++) {
out.joints_and_weights.push_back(dummy);
}
}
// TODO: just putting it all in one material
auto& draw = draw_by_material[prim.material];
draw.mode = gltf_util::make_default_draw_mode(); // todo rm
draw.tree_tex_id = 0; // todo rm
draw.num_triangles += prim_indices.size() / 3;
draw.no_strip = true;
draw.index_count = prim_indices.size();
draw.first_index = index_offset + out.new_indices.size();
out.new_indices.insert(out.new_indices.end(), prim_indices.begin(), prim_indices.end());
}
}
}
tfrag3::MercEffect e;
tfrag3::MercEffect envmap_eff;
envmap_eff.has_envmap = false;
out.new_model.name = mdl.name;
// if we have a skeleton, use that joint count, otherwise use a high default value since the model
// we replace can have more
out.new_model.max_bones = joints != 3 ? joints : 100;
out.new_model.max_draws = 0;
for (const auto& [mat_idx, d_] : draw_by_material) {
const auto& mat = model.materials[mat_idx];
if (mat_idx < 0 || !gltf_util::material_has_envmap(model.materials[mat_idx]) ||
!gltf_util::envmap_is_valid(model.materials[mat_idx])) {
gltf_util::process_normal_merc_draw(model, out, tex_offset, e, mat_idx, d_);
} else {
envmap_eff.has_envmap = true;
gltf_util::process_envmap_merc_draw(model, out, tex_offset, envmap_eff, mat_idx, d_);
}
}
// 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 (envmap_eff.has_envmap) {
out.new_model.effects.push_back(envmap_eff);
}
// copy any effects from the old model that used mod or eye draws
if (copy_eye_draws || copy_mod_draws) {
for (auto& old_eff : mdl.effects) {
tfrag3::MercEffect eff;
bool has_eye_draw = false;
if (copy_eye_draws) {
for (auto& draw : old_eff.all_draws) {
if (draw.eye_id != 0xff) {
has_eye_draw = true;
eff.all_draws.push_back(draw);
}
}
}
if (copy_mod_draws && old_eff.has_mod_draw) {
eff.has_mod_draw = true;
eff.mod = old_eff.mod;
eff.mod.fix_draw.clear();
}
if ((copy_eye_draws || copy_mod_draws) && (eff.has_mod_draw || has_eye_draw)) {
lg::info("adding old effect for {} (mod draw {}, eye draw {})", mdl.name,
old_eff.has_mod_draw, has_eye_draw);
out.new_model.effects.push_back(eff);
}
}
}
for (auto& effect : out.new_model.effects) {
out.new_model.max_draws += effect.all_draws.size();
}
lg::info("total of {} unique materials ({} normal, {} envmap)", out.new_model.max_draws,
e.all_draws.size(), envmap_eff.all_draws.size());
lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count,
out.new_vertices.size());
}
void extract(const std::string& name,
MercExtractData& out,
const tinygltf::Model& model,
@@ -86,88 +229,14 @@ void extract(const std::string& name,
out.new_model.max_bones = joints != 3 ? joints : 100;
out.new_model.max_draws = 0;
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;
return;
}
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;
return;
}
const auto& tex = model.textures[tex_idx];
ASSERT(tex.sampler >= 0);
ASSERT(tex.source >= 0);
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;
}
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(mat.pbrMetallicRoughness.metallicRoughnessTexture.index);
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) || !envmap_is_valid(mat)) {
process_normal_draw(e, mat_idx, d_);
if (mat_idx < 0 || !gltf_util::material_has_envmap(model.materials[mat_idx]) ||
!gltf_util::envmap_is_valid(model.materials[mat_idx])) {
gltf_util::process_normal_merc_draw(model, out, tex_offset, e, mat_idx, d_);
} else {
envmap_eff.has_envmap = true;
process_envmap_draw(envmap_eff, mat_idx, d_);
gltf_util::process_envmap_merc_draw(model, out, tex_offset, envmap_eff, mat_idx, d_);
}
}
@@ -285,7 +354,7 @@ void merc_convert_custom(MercSwapData& out, const MercExtractData& in) {
}
}
MercSwapData load_replacement_merc_model(const std::string& name,
MercSwapData load_replacement_merc_model(tfrag3::MercModel& mdl,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
@@ -303,6 +372,37 @@ MercSwapData load_replacement_merc_model(const std::string& name,
ASSERT_MSG(res, "Failed to load GLTF file!");
auto all_nodes = flatten_nodes_from_all_scenes(model);
MercExtractData extract_data;
auto has_custom_weights = false;
extract(mdl, extract_data, model, all_nodes, current_idx_count, current_vtx_count,
current_tex_count, has_custom_weights);
if (custom_mdl) {
merc_convert_custom(result, extract_data);
} else {
merc_convert_replacement(result, extract_data, old_verts, has_custom_weights);
}
return result;
}
MercSwapData load_custom_merc_model(const std::string& name,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::vector<tfrag3::MercVertex>& old_verts,
bool custom_mdl) {
MercSwapData result;
lg::info("Reading gltf mesh: {}", path);
tinygltf::TinyGLTF loader;
tinygltf::Model model;
std::string err, warn;
bool res = loader.LoadBinaryFromFile(&model, &err, &warn, path);
ASSERT_MSG(warn.empty(), warn.c_str());
ASSERT_MSG(err.empty(), err.c_str());
ASSERT_MSG(res, "Failed to load GLTF file!");
auto all_nodes = flatten_nodes_from_all_scenes(model);
MercExtractData extract_data;
auto has_custom_weights = false;
extract(name, extract_data, model, all_nodes, current_idx_count, current_vtx_count,
+15 -23
View File
@@ -4,29 +4,21 @@
#include "common/util/gltf_util.h"
namespace decompiler {
struct MercExtractData {
gltf_util::TexturePool tex_pool;
std::vector<u32> new_indices;
std::vector<tfrag3::PreloadedVertex> new_vertices;
std::vector<math::Vector<u8, 4>> new_colors;
std::vector<math::Vector3f> normals;
std::vector<gltf_util::JointsAndWeights> joints_and_weights;
tfrag3::MercModel new_model;
};
// Data produced by loading a replacement model
struct MercSwapData {
std::vector<u32> new_indices;
std::vector<tfrag3::MercVertex> new_vertices;
std::vector<tfrag3::Texture> new_textures;
tfrag3::MercModel new_model;
};
gltf_util::MercSwapData load_replacement_merc_model(
tfrag3::MercModel& mdl,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::vector<tfrag3::MercVertex>& old_verts,
bool custom_mdl);
MercSwapData load_replacement_merc_model(const std::string& name,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::vector<tfrag3::MercVertex>& old_verts,
bool custom_mdl);
gltf_util::MercSwapData load_custom_merc_model(const std::string& name,
u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::vector<tfrag3::MercVertex>& old_verts,
bool custom_mdl);
} // namespace decompiler
@@ -538,8 +538,6 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
auto* flags = (const PcMercFlags*)input_data;
int num_effects = flags->effect_count; // mostly just a sanity check
ASSERT(num_effects < kMaxEffect);
// hack for custom models to disable blerc/mod draws
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; // if we should update vertices from game.
@@ -566,11 +564,18 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// Next is pointers to merc data, needed so we can update vertices
// custom models are likely to have a different number of effects than what GOAL reports, update
// the count here (after reading DMA) so we don't potentially go out of bounds when we do
// blerc/mod draws
if (model->effects.at(0).all_draws.at(0).no_strip) {
num_effects = model->effects.size();
}
// will hold opengl buffers for the updated vertices
ModBuffers mod_opengl_buffers[kMaxEffect];
if (model_uses_pc_blerc && !is_custom_model) {
if (model_uses_pc_blerc) {
model_mod_blerc_draws(num_effects, model, lev, mod_opengl_buffers, blerc_weights, stats);
} else if (model_uses_mod && !is_custom_model) { // only if we've enabled, this path is slow.
} else if (model_uses_mod) { // only if we've enabled, this path is slow.
model_mod_draws(num_effects, model, lev, input_data, setup, mod_opengl_buffers, stats);
}
@@ -650,8 +655,7 @@ 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 =
!is_custom_model && ((model_uses_pc_blerc || model_uses_mod) && effect.has_mod_draw);
bool should_mod = (model_uses_pc_blerc || model_uses_mod) && effect.has_mod_draw;
if (should_mod) {
// draw as two parts, fixed and mod
+12 -87
View File
@@ -6,7 +6,7 @@
#include "goalc/build_level/common/gltf_mesh_extract.h"
void extract(const std::string& name,
MercExtractData& out,
gltf_util::MercExtractData& out,
const tinygltf::Model& model,
const std::vector<gltf_util::NodeWithTransform>& all_nodes,
u32 index_offset,
@@ -96,89 +96,14 @@ void extract(const std::string& name,
out.new_model.max_bones = joints;
out.new_model.max_draws = 0;
auto process_normal_draw = [&](tfrag3::MercEffect& eff, int mat_idx, const tfrag3::MercDraw& d_) {
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;
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;
return;
}
const auto& tex = model.textures[tex_idx];
ASSERT(tex.sampler >= 0);
ASSERT(tex.source >= 0);
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;
}
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(mat.pbrMetallicRoughness.metallicRoughnessTexture.index);
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 (mat_idx < 0 || !gltf_util::material_has_envmap(model.materials[mat_idx]) ||
!gltf_util::envmap_is_valid(model.materials[mat_idx])) {
process_normal_draw(e, mat_idx, d_);
gltf_util::process_normal_merc_draw(model, out, tex_offset, e, mat_idx, d_);
} else {
envmap_eff.has_envmap = true;
process_envmap_draw(envmap_eff, mat_idx, d_);
gltf_util::process_envmap_merc_draw(model, out, tex_offset, envmap_eff, mat_idx, d_);
}
}
@@ -200,7 +125,7 @@ void extract(const std::string& name,
out.new_vertices.size());
}
void merc_convert(MercSwapData& out, const MercExtractData& in) {
void merc_convert(gltf_util::MercSwapData& out, const gltf_util::MercExtractData& in) {
// easy
out.new_model = in.new_model;
out.new_indices = in.new_indices;
@@ -231,12 +156,12 @@ void merc_convert(MercSwapData& out, const MercExtractData& in) {
}
}
MercSwapData load_merc_model(u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::string& name) {
MercSwapData result;
gltf_util::MercSwapData load_merc_model(u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::string& name) {
gltf_util::MercSwapData result;
lg::info("Reading gltf mesh: {}", path);
tinygltf::TinyGLTF loader;
tinygltf::Model model;
@@ -247,7 +172,7 @@ MercSwapData load_merc_model(u32 current_idx_count,
ASSERT_MSG(res, "Failed to load GLTF file!");
auto all_nodes = gltf_util::flatten_nodes_from_all_scenes(model);
MercExtractData extract_data;
gltf_util::MercExtractData extract_data;
extract(name, extract_data, model, all_nodes, current_idx_count, current_vtx_count,
current_tex_count);
merc_convert(result, extract_data);
+7 -25
View File
@@ -4,37 +4,19 @@
#include "goalc/build_actor/jak1/build_actor.h"
struct MercExtractData {
gltf_util::TexturePool tex_pool;
std::vector<u32> new_indices;
std::vector<tfrag3::PreloadedVertex> new_vertices;
std::vector<math::Vector<u8, 4>> new_colors;
std::vector<math::Vector3f> normals;
std::vector<gltf_util::JointsAndWeights> joints_and_weights;
tfrag3::MercModel new_model;
};
// Data produced by loading a replacement model
struct MercSwapData {
std::vector<u32> new_indices;
std::vector<tfrag3::MercVertex> new_vertices;
std::vector<tfrag3::Texture> new_textures;
tfrag3::MercModel new_model;
};
void extract(const std::string& name,
MercExtractData& out,
gltf_util::MercExtractData& out,
const tinygltf::Model& model,
const std::vector<gltf_util::NodeWithTransform>& all_nodes,
u32 index_offset,
u32 vertex_offset,
u32 tex_offset);
void merc_convert(MercSwapData& out, const MercExtractData& in);
MercSwapData load_merc_model(u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::string& name);
void merc_convert(gltf_util::MercSwapData& out, const gltf_util::MercExtractData& in);
gltf_util::MercSwapData load_merc_model(u32 current_idx_count,
u32 current_vtx_count,
u32 current_tex_count,
const std::string& path,
const std::string& name);
std::vector<jak1::CollideMesh> gen_collide_mesh_from_model(
const tinygltf::Model& model,
const std::vector<gltf_util::NodeWithTransform>& all_nodes,