mirror of
https://github.com/open-goal/jak-project
synced 2026-06-14 22:29:25 -04:00
d81b60736a
 
629 lines
21 KiB
C++
629 lines
21 KiB
C++
/*!
|
|
* Mesh extraction for GLTF meshes.
|
|
*/
|
|
|
|
#include "gltf_mesh_extract.h"
|
|
|
|
#include <optional>
|
|
|
|
#include "color_quantization.h"
|
|
|
|
#include "common/log/log.h"
|
|
#include "common/math/geometry.h"
|
|
#include "common/util/Timer.h"
|
|
#include "common/util/gltf_util.h"
|
|
#include "common/util/image_resize.h"
|
|
|
|
using namespace gltf_util;
|
|
constexpr int kColorTreeDepth = 13;
|
|
namespace gltf_mesh_extract {
|
|
|
|
void dedup_tfrag_vertices(TfragOutput& data) {
|
|
Timer timer;
|
|
size_t original_size = data.tfrag_vertices.size();
|
|
std::vector<tfrag3::PreloadedVertex> new_verts;
|
|
std::vector<u32> old_to_new;
|
|
|
|
gltf_util::dedup_vertices(data.tfrag_vertices, new_verts, old_to_new);
|
|
data.tfrag_vertices = std::move(new_verts);
|
|
|
|
// TODO: properly split vertices between trees...
|
|
for (auto drawlist : {&data.normal_strip_draws, &data.trans_strip_draws}) {
|
|
for (auto& draw : *drawlist) {
|
|
ASSERT(draw.runs.empty()); // not supported yet
|
|
for (auto& idx : draw.plain_indices) {
|
|
idx = old_to_new.at(idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
lg::info("Deduplication took {:.2f} ms, {} -> {} ({:.2f} %)", timer.getMs(), original_size,
|
|
data.tfrag_vertices.size(), 100.f * data.tfrag_vertices.size() / original_size);
|
|
}
|
|
|
|
void dedup_tie_vertices(TieOutput& data) {
|
|
Timer timer;
|
|
size_t original_size = data.vertices.size();
|
|
|
|
std::vector<TieFullVertex> old_verts;
|
|
old_verts.reserve(data.vertices.size());
|
|
for (size_t i = 0; i < data.vertices.size(); i++) {
|
|
auto& x = old_verts.emplace_back();
|
|
x.color_index = data.color_indices[i];
|
|
x.vertex = data.vertices[i];
|
|
}
|
|
|
|
std::vector<TieFullVertex> new_verts;
|
|
std::vector<u32> old_to_new;
|
|
|
|
gltf_util::dedup_vertices(old_verts, new_verts, old_to_new);
|
|
data.vertices.clear();
|
|
data.color_indices.clear();
|
|
data.vertices.reserve(new_verts.size());
|
|
data.color_indices.reserve(new_verts.size());
|
|
for (auto& x : new_verts) {
|
|
data.vertices.push_back(x.vertex);
|
|
data.color_indices.push_back(x.color_index);
|
|
}
|
|
|
|
// TODO: properly split vertices between trees...
|
|
for (auto drawlist : {&data.base_draws, &data.envmap_draws}) {
|
|
for (auto& draw : *drawlist) {
|
|
ASSERT(draw.runs.empty()); // not supported yet
|
|
for (auto& idx : draw.plain_indices) {
|
|
idx = old_to_new.at(idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
lg::info("Deduplication took {:.2f} ms, {} -> {} ({:.2f} %)", timer.getMs(), original_size,
|
|
data.vertices.size(), 100.f * data.vertices.size() / original_size);
|
|
}
|
|
|
|
bool prim_needs_tie(const tinygltf::Model& model, const tinygltf::Primitive& prim) {
|
|
if (prim.material >= 0) {
|
|
auto mat = model.materials.at(prim.material);
|
|
return mat.extensions.contains("KHR_materials_specular");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void extract(const Input& in,
|
|
TfragOutput& out,
|
|
const tinygltf::Model& model,
|
|
const std::vector<NodeWithTransform>& all_nodes) {
|
|
std::vector<math::Vector<u8, 4>> all_vtx_colors;
|
|
ASSERT(out.tfrag_vertices.empty());
|
|
|
|
struct MaterialInfo {
|
|
tfrag3::StripDraw draw;
|
|
bool needs_tie = false;
|
|
};
|
|
std::map<int, MaterialInfo> info_by_material;
|
|
int mesh_count = 0;
|
|
int prim_count = 0;
|
|
|
|
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++;
|
|
for (const auto& prim : mesh.primitives) {
|
|
if (prim.material >= 0 && model.materials[prim.material].extras.Has("set_invisible") &&
|
|
model.materials[prim.material].extras.Get("set_invisible").Get<int>()) {
|
|
continue;
|
|
}
|
|
|
|
if (prim_needs_tie(model, prim)) {
|
|
continue;
|
|
}
|
|
prim_count++;
|
|
// extract index buffer
|
|
std::vector<u32> prim_indices =
|
|
gltf_index_buffer(model, prim.indices, out.tfrag_vertices.size());
|
|
ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, "Unsupported triangle mode");
|
|
// extract vertices
|
|
auto verts = gltf_vertices(model, prim.attributes, n.w_T_node, true, false, mesh.name);
|
|
out.tfrag_vertices.insert(out.tfrag_vertices.end(), verts.vtx.begin(), verts.vtx.end());
|
|
all_vtx_colors.insert(all_vtx_colors.end(), verts.vtx_colors.begin(),
|
|
verts.vtx_colors.end());
|
|
ASSERT(all_vtx_colors.size() == out.tfrag_vertices.size());
|
|
|
|
auto& info = info_by_material[prim.material];
|
|
info.draw.mode = make_default_draw_mode(); // todo rm
|
|
info.draw.tree_tex_id = texture_pool_debug_checker(in.tex_pool); // todo rm
|
|
info.draw.num_triangles += prim_indices.size() / 3;
|
|
if (info.draw.vis_groups.empty()) {
|
|
auto& grp = info.draw.vis_groups.emplace_back();
|
|
grp.num_inds += prim_indices.size();
|
|
grp.num_tris += info.draw.num_triangles;
|
|
grp.vis_idx_in_pc_bvh = UINT16_MAX;
|
|
} else {
|
|
auto& grp = info.draw.vis_groups.back();
|
|
grp.num_inds += prim_indices.size();
|
|
grp.num_tris += info.draw.num_triangles;
|
|
grp.vis_idx_in_pc_bvh = UINT16_MAX;
|
|
}
|
|
|
|
info.draw.plain_indices.insert(info.draw.plain_indices.end(), prim_indices.begin(),
|
|
prim_indices.end());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto& [mat_idx, d_] : info_by_material) {
|
|
// out.strip_draws.push_back(d_);
|
|
// auto& draw = out.strip_draws.back();
|
|
tfrag3::StripDraw draw = d_.draw;
|
|
draw.mode = make_default_draw_mode();
|
|
|
|
if (mat_idx == -1) {
|
|
lg::warn("Draw had a material index of -1, using default texture.");
|
|
draw.tree_tex_id = texture_pool_debug_checker(in.tex_pool);
|
|
out.normal_strip_draws.push_back(draw);
|
|
continue;
|
|
}
|
|
|
|
const auto& mat = model.materials[mat_idx];
|
|
setup_alpha_from_material(mat, &draw.mode);
|
|
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 = texture_pool_debug_checker(in.tex_pool);
|
|
if (draw.mode.get_ab_enable()) {
|
|
out.trans_strip_draws.push_back(draw);
|
|
} else {
|
|
out.normal_strip_draws.push_back(draw);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const auto& tex = model.textures[tex_idx];
|
|
ASSERT(tex.sampler >= 0);
|
|
ASSERT(tex.source >= 0);
|
|
setup_draw_mode_from_sampler(model.samplers.at(tex.sampler), &draw.mode);
|
|
|
|
const auto& img = model.images[tex.source];
|
|
draw.tree_tex_id = texture_pool_add_texture(in.tex_pool, img);
|
|
|
|
if (draw.mode.get_ab_enable()) {
|
|
out.trans_strip_draws.push_back(draw);
|
|
} else {
|
|
out.normal_strip_draws.push_back(draw);
|
|
}
|
|
}
|
|
lg::info("total of {} normal, {} transparent unique materials", out.normal_strip_draws.size(),
|
|
out.trans_strip_draws.size());
|
|
|
|
lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count,
|
|
out.tfrag_vertices.size());
|
|
|
|
Timer quantize_timer;
|
|
auto quantized = quantize_colors_kd_tree(all_vtx_colors, kColorTreeDepth);
|
|
for (size_t i = 0; i < out.tfrag_vertices.size(); i++) {
|
|
out.tfrag_vertices[i].color_index = quantized.vtx_to_color[i];
|
|
}
|
|
out.color_palette = std::move(quantized.final_colors);
|
|
lg::info("Color palette generation took {:.2f} ms", quantize_timer.getMs());
|
|
|
|
dedup_tfrag_vertices(out);
|
|
}
|
|
|
|
s8 normal_to_s8(float in) {
|
|
s32 in_s32 = in * 127.f;
|
|
ASSERT(in_s32 <= INT8_MAX);
|
|
ASSERT(in_s32 >= INT8_MIN);
|
|
return in_s32;
|
|
}
|
|
|
|
void add_to_packed_verts(std::vector<tfrag3::PackedTieVertices::Vertex>* out,
|
|
const std::vector<tfrag3::PreloadedVertex>& vtx,
|
|
const std::vector<math::Vector3f>& normals) {
|
|
ASSERT(vtx.size() == normals.size());
|
|
for (size_t i = 0; i < normals.size(); i++) {
|
|
auto& x = out->emplace_back();
|
|
// currently not supported.
|
|
x.r = 255;
|
|
x.g = 255;
|
|
x.b = 255;
|
|
x.a = 255;
|
|
|
|
x.x = vtx[i].x;
|
|
x.y = vtx[i].y;
|
|
x.z = vtx[i].z;
|
|
|
|
x.s = vtx[i].s;
|
|
x.t = vtx[i].t;
|
|
|
|
x.nx = normal_to_s8(normals[i].x());
|
|
x.ny = normal_to_s8(normals[i].y());
|
|
x.nz = normal_to_s8(normals[i].z());
|
|
}
|
|
}
|
|
|
|
void extract(const Input& in,
|
|
TieOutput& out,
|
|
const tinygltf::Model& model,
|
|
const std::vector<NodeWithTransform>& all_nodes) {
|
|
std::vector<math::Vector<u8, 4>> all_vtx_colors;
|
|
|
|
struct MaterialInfo {
|
|
tfrag3::StripDraw draw;
|
|
bool needs_tie = false;
|
|
};
|
|
std::map<int, MaterialInfo> info_by_material;
|
|
int mesh_count = 0;
|
|
int prim_count = 0;
|
|
|
|
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++;
|
|
for (const auto& prim : mesh.primitives) {
|
|
if (prim.material >= 0 && model.materials[prim.material].extras.Has("set_invisible") &&
|
|
model.materials[prim.material].extras.Get("set_invisible").Get<int>()) {
|
|
continue;
|
|
}
|
|
|
|
if (!prim_needs_tie(model, prim)) {
|
|
continue;
|
|
}
|
|
prim_count++;
|
|
// extract index buffer
|
|
std::vector<u32> prim_indices = gltf_index_buffer(model, prim.indices, out.vertices.size());
|
|
ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, "Unsupported triangle mode");
|
|
// extract vertices
|
|
auto verts = gltf_vertices(model, prim.attributes, n.w_T_node, true, true, mesh.name);
|
|
add_to_packed_verts(&out.vertices, verts.vtx, verts.normals);
|
|
all_vtx_colors.insert(all_vtx_colors.end(), verts.vtx_colors.begin(),
|
|
verts.vtx_colors.end());
|
|
ASSERT(all_vtx_colors.size() == out.vertices.size());
|
|
|
|
auto& info = info_by_material[prim.material];
|
|
info.draw.mode = make_default_draw_mode(); // todo rm
|
|
info.draw.tree_tex_id = texture_pool_debug_checker(in.tex_pool); // todo rm
|
|
info.draw.num_triangles += prim_indices.size() / 3;
|
|
if (info.draw.vis_groups.empty()) {
|
|
auto& grp = info.draw.vis_groups.emplace_back();
|
|
grp.num_inds += prim_indices.size();
|
|
grp.num_tris += info.draw.num_triangles;
|
|
grp.vis_idx_in_pc_bvh = UINT16_MAX;
|
|
} else {
|
|
auto& grp = info.draw.vis_groups.back();
|
|
grp.num_inds += prim_indices.size();
|
|
grp.num_tris += info.draw.num_triangles;
|
|
grp.vis_idx_in_pc_bvh = UINT16_MAX;
|
|
}
|
|
|
|
info.draw.plain_indices.insert(info.draw.plain_indices.end(), prim_indices.begin(),
|
|
prim_indices.end());
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto& [mat_idx, d_] : info_by_material) {
|
|
// out.strip_draws.push_back(d_);
|
|
// auto& draw = out.strip_draws.back();
|
|
tfrag3::StripDraw draw = d_.draw;
|
|
draw.mode = make_default_draw_mode();
|
|
|
|
if (mat_idx == -1) {
|
|
lg::warn("Draw had a material index of -1, using default texture.");
|
|
draw.tree_tex_id = texture_pool_debug_checker(in.tex_pool);
|
|
out.base_draws.push_back(draw);
|
|
continue;
|
|
}
|
|
|
|
const auto& mat = model.materials[mat_idx];
|
|
setup_alpha_from_material(mat, &draw.mode);
|
|
int base_tex_idx = mat.pbrMetallicRoughness.baseColorTexture.index;
|
|
if (base_tex_idx == -1) {
|
|
lg::warn("Material {} has no texture, using default texture.", mat.name);
|
|
draw.tree_tex_id = texture_pool_debug_checker(in.tex_pool);
|
|
out.base_draws.push_back(draw);
|
|
continue;
|
|
}
|
|
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);
|
|
setup_draw_mode_from_sampler(model.samplers.at(base_tex.sampler), &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 = texture_pool_add_texture(in.tex_pool, model.images[base_tex.source]);
|
|
draw.tree_tex_id = texture_pool_add_envmap_control_texture(
|
|
in.tex_pool, model, base_tex.source, roughness_tex.source, !draw.mode.get_clamp_s_enable(),
|
|
!draw.mode.get_clamp_t_enable());
|
|
out.base_draws.push_back(draw);
|
|
|
|
// now, setup envmap draw:
|
|
auto envmap_settings = 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);
|
|
draw.mode = make_default_draw_mode();
|
|
setup_draw_mode_from_sampler(model.samplers.at(envmap_tex.sampler), &draw.mode);
|
|
draw.tree_tex_id = texture_pool_add_texture(in.tex_pool, model.images[envmap_tex.source]);
|
|
draw.mode.set_alpha_blend(DrawMode::AlphaBlend::SRC_0_DST_DST);
|
|
draw.mode.enable_ab();
|
|
|
|
out.envmap_draws.push_back(draw);
|
|
}
|
|
lg::info("total of {} normal TIE draws, {} envmap", out.base_draws.size(),
|
|
out.envmap_draws.size());
|
|
|
|
lg::info("Merged {} meshes and {} prims into {} vertices", mesh_count, prim_count,
|
|
out.vertices.size());
|
|
|
|
Timer quantize_timer;
|
|
auto quantized = quantize_colors_kd_tree(all_vtx_colors, kColorTreeDepth);
|
|
for (size_t i = 0; i < out.vertices.size(); i++) {
|
|
out.color_indices.push_back(quantized.vtx_to_color[i]);
|
|
}
|
|
out.color_palette = std::move(quantized.final_colors);
|
|
lg::info("Color palette generation took {:.2f} ms", quantize_timer.getMs());
|
|
|
|
dedup_tie_vertices(out);
|
|
}
|
|
|
|
std::optional<std::vector<jak1::CollideFace>> subdivide_face_if_needed(jak1::CollideFace face_in) {
|
|
math::Vector3f v_min = face_in.v[0];
|
|
v_min.min_in_place(face_in.v[1]);
|
|
v_min.min_in_place(face_in.v[2]);
|
|
v_min -= 16.f;
|
|
bool needs_subdiv = false;
|
|
for (auto& vert : face_in.v) {
|
|
if ((vert - v_min).squared_length() > 154.f * 154.f * 4096.f * 4096.f) {
|
|
needs_subdiv = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (needs_subdiv) {
|
|
math::Vector3f a = (face_in.v[0] + face_in.v[1]) * 0.5f;
|
|
math::Vector3f b = (face_in.v[1] + face_in.v[2]) * 0.5f;
|
|
math::Vector3f c = (face_in.v[2] + face_in.v[0]) * 0.5f;
|
|
math::Vector3f v0 = face_in.v[0];
|
|
math::Vector3f v1 = face_in.v[1];
|
|
math::Vector3f v2 = face_in.v[2];
|
|
jak1::CollideFace fs[4];
|
|
fs[0].v[0] = v0;
|
|
fs[0].v[1] = a;
|
|
fs[0].v[2] = c;
|
|
fs[0].bsphere = math::bsphere_of_triangle(face_in.v);
|
|
|
|
fs[1].v[0] = a;
|
|
fs[1].v[1] = v1;
|
|
fs[1].v[2] = b;
|
|
fs[1].bsphere = math::bsphere_of_triangle(fs[1].v);
|
|
fs[1].pat = face_in.pat;
|
|
|
|
fs[2].v[0] = a;
|
|
fs[2].v[1] = b;
|
|
fs[2].v[2] = c;
|
|
fs[2].bsphere = math::bsphere_of_triangle(fs[2].v);
|
|
fs[2].pat = face_in.pat;
|
|
|
|
fs[3].v[0] = b;
|
|
fs[3].v[1] = v2;
|
|
fs[3].v[2] = c;
|
|
fs[3].bsphere = math::bsphere_of_triangle(fs[3].v);
|
|
fs[3].pat = face_in.pat;
|
|
|
|
std::vector<jak1::CollideFace> result;
|
|
for (auto f : fs) {
|
|
auto next_faces = subdivide_face_if_needed(f);
|
|
if (next_faces) {
|
|
result.insert(result.end(), next_faces->begin(), next_faces->end());
|
|
} else {
|
|
result.push_back(f);
|
|
}
|
|
}
|
|
return result;
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
PatResult custom_props_to_pat(const tinygltf::Value& val, const std::string& /*debug_name*/) {
|
|
PatResult result;
|
|
if (!val.IsObject() || !val.Has("set_collision") || !val.Get("set_collision").Get<int>()) {
|
|
// unset.
|
|
result.set = false;
|
|
return result;
|
|
}
|
|
|
|
result.set = true;
|
|
|
|
if (val.Get("ignore").Get<int>()) {
|
|
result.ignore = true;
|
|
return result;
|
|
}
|
|
result.ignore = false;
|
|
|
|
int mat = val.Get("collide_material").Get<int>();
|
|
ASSERT(mat < (int)jak1::PatSurface::Material::MAX_MATERIAL);
|
|
result.pat.set_material(jak1::PatSurface::Material(mat));
|
|
|
|
int evt = val.Get("collide_event").Get<int>();
|
|
ASSERT(evt < (int)jak1::PatSurface::Event::MAX_EVENT);
|
|
result.pat.set_event(jak1::PatSurface::Event(evt));
|
|
|
|
if (val.Get("nolineofsight").Get<int>()) {
|
|
result.pat.set_nolineofsight(true);
|
|
}
|
|
|
|
if (val.Get("noedge").Get<int>()) {
|
|
result.pat.set_noedge(true);
|
|
}
|
|
|
|
if (val.Has("collide_mode")) {
|
|
int mode = val.Get("collide_mode").Get<int>();
|
|
ASSERT(mode < (int)jak1::PatSurface::Mode::MAX_MODE);
|
|
result.pat.set_mode(jak1::PatSurface::Mode(mode));
|
|
}
|
|
|
|
if (val.Get("nocamera").Get<int>()) {
|
|
result.pat.set_nocamera(true);
|
|
}
|
|
|
|
if (val.Get("noentity").Get<int>()) {
|
|
result.pat.set_noentity(true);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void extract(const Input& in,
|
|
CollideOutput& out,
|
|
const tinygltf::Model& model,
|
|
const std::vector<NodeWithTransform>& all_nodes) {
|
|
[[maybe_unused]] int mesh_count = 0;
|
|
[[maybe_unused]] int prim_count = 0;
|
|
int suspicious_faces = 0;
|
|
|
|
for (const auto& n : all_nodes) {
|
|
const auto& node = model.nodes[n.node_idx];
|
|
PatResult mesh_default_collide = custom_props_to_pat(node.extras, node.name);
|
|
if (node.mesh >= 0) {
|
|
const auto& mesh = model.meshes[node.mesh];
|
|
mesh_count++;
|
|
for (const auto& prim : mesh.primitives) {
|
|
// get material
|
|
const auto& mat_idx = prim.material;
|
|
PatResult pat = mesh_default_collide;
|
|
if (mat_idx != -1) {
|
|
const auto& mat = model.materials[mat_idx];
|
|
auto mat_pat = custom_props_to_pat(mat.extras, mat.name);
|
|
if (mat_pat.set) {
|
|
pat = mat_pat;
|
|
}
|
|
}
|
|
|
|
if (pat.set && pat.ignore) {
|
|
continue; // skip, no collide here
|
|
}
|
|
prim_count++;
|
|
// extract index buffer
|
|
std::vector<u32> prim_indices = gltf_index_buffer(model, prim.indices, 0);
|
|
ASSERT_MSG(prim.mode == TINYGLTF_MODE_TRIANGLES, "Unsupported triangle mode");
|
|
// extract vertices
|
|
auto verts = gltf_vertices(model, prim.attributes, n.w_T_node, false, true, mesh.name);
|
|
|
|
for (size_t iidx = 0; iidx < prim_indices.size(); iidx += 3) {
|
|
jak1::CollideFace face;
|
|
|
|
// get the positions
|
|
for (int j = 0; j < 3; j++) {
|
|
auto& vtx = verts.vtx.at(prim_indices.at(iidx + j));
|
|
face.v[j].x() = vtx.x;
|
|
face.v[j].y() = vtx.y;
|
|
face.v[j].z() = vtx.z;
|
|
}
|
|
|
|
// now face normal
|
|
math::Vector3f face_normal =
|
|
(face.v[2] - face.v[0]).cross(face.v[1] - face.v[0]).normalized();
|
|
|
|
float dots[3];
|
|
for (int j = 0; j < 3; j++) {
|
|
dots[j] = face_normal.dot(verts.normals.at(prim_indices.at(iidx + j)).normalized());
|
|
}
|
|
|
|
if (dots[0] > 1e-3 && dots[1] > 1e-3 && dots[2] > 1e-3) {
|
|
suspicious_faces++;
|
|
auto temp = face.v[2];
|
|
face.v[2] = face.v[1];
|
|
face.v[1] = temp;
|
|
}
|
|
|
|
face.bsphere = math::bsphere_of_triangle(face.v);
|
|
face.bsphere.w() += 1e-1 * 5;
|
|
for (int j = 0; j < 3; j++) {
|
|
float output_dist = face.bsphere.w() - (face.bsphere.xyz() - face.v[j]).length();
|
|
if (output_dist < 0) {
|
|
lg::print("{}\n", output_dist);
|
|
lg::print("BAD:\n{}\n{}\n{}\n", face.v[0].to_string_aligned(),
|
|
face.v[1].to_string_aligned(), face.v[2].to_string_aligned());
|
|
lg::print("bsphere: {}\n", face.bsphere.to_string_aligned());
|
|
}
|
|
}
|
|
face.pat = pat.pat;
|
|
out.faces.push_back(face);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<jak1::CollideFace> fixed_faces;
|
|
int fix_count = 0;
|
|
for (auto& face : out.faces) {
|
|
auto try_fix = subdivide_face_if_needed(face);
|
|
if (try_fix) {
|
|
fix_count++;
|
|
fixed_faces.insert(fixed_faces.end(), try_fix->begin(), try_fix->end());
|
|
} else {
|
|
fixed_faces.push_back(face);
|
|
}
|
|
}
|
|
|
|
if (in.double_sided_collide) {
|
|
size_t os = fixed_faces.size();
|
|
for (size_t i = 0; i < os; i++) {
|
|
auto f0 = fixed_faces.at(i);
|
|
std::swap(f0.v[0], f0.v[1]);
|
|
fixed_faces.push_back(f0);
|
|
}
|
|
}
|
|
|
|
out.faces = std::move(fixed_faces);
|
|
|
|
if (in.auto_wall_enable) {
|
|
lg::info("automatically detecting walls with angle {}", in.auto_wall_angle);
|
|
int wall_count = 0;
|
|
float wall_cos = std::cos(in.auto_wall_angle * 2.f * 3.14159 / 360.f);
|
|
for (auto& face : out.faces) {
|
|
math::Vector3f face_normal =
|
|
(face.v[1] - face.v[0]).cross(face.v[2] - face.v[0]).normalized();
|
|
if (face_normal[1] < wall_cos) {
|
|
face.pat.set_mode(jak1::PatSurface::Mode::WALL);
|
|
wall_count++;
|
|
}
|
|
}
|
|
lg::info("automatic wall: {}/{} converted to walls", wall_count, out.faces.size());
|
|
}
|
|
|
|
lg::info("{} out of {} faces appeared to have wrong orientation and were flipped",
|
|
suspicious_faces, out.faces.size());
|
|
lg::info("{} faces were too big and were subdivided", fix_count);
|
|
// lg::info("Collision extract{} {}", mesh_count, prim_count);
|
|
}
|
|
|
|
void extract(const Input& in, Output& out) {
|
|
lg::info("Reading gltf mesh: {}", in.filename);
|
|
Timer read_timer;
|
|
tinygltf::TinyGLTF loader;
|
|
tinygltf::Model model;
|
|
std::string err, warn;
|
|
bool res = loader.LoadBinaryFromFile(&model, &err, &warn, in.filename);
|
|
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);
|
|
extract(in, out.tfrag, model, all_nodes);
|
|
extract(in, out.collide, model, all_nodes);
|
|
extract(in, out.tie, model, all_nodes);
|
|
lg::info("GLTF total took {:.2f} ms", read_timer.getMs());
|
|
}
|
|
} // namespace gltf_mesh_extract
|