mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-17 04:27:57 +00:00
[custom levels] fix various collision bugs (#1522)
* [custom levels] fix various collision bugs * include
This commit is contained in:
parent
56a84cd923
commit
b3f2b81481
@ -99,7 +99,7 @@ struct PackedTfragVertices {
|
||||
struct Vertex {
|
||||
u16 xoff, yoff, zoff;
|
||||
u16 cluster_idx;
|
||||
u16 s, t;
|
||||
s16 s, t;
|
||||
u16 color_index;
|
||||
};
|
||||
|
||||
|
@ -10,9 +10,7 @@ void tfrag_from_gltf(const gltf_mesh_extract::TfragOutput& mesh_extract_out,
|
||||
tfrag3::TfragTree& out_pc) {
|
||||
out_pc.kind = tfrag3::TFragmentTreeKind::NORMAL; // todo more types?
|
||||
out_pc.draws = std::move(mesh_extract_out.strip_draws);
|
||||
fmt::print("have {} draws\n", out_pc.draws.size());
|
||||
pack_tfrag_vertices(&out_pc.packed_vertices, mesh_extract_out.vertices);
|
||||
fmt::print("have {} vertices\n", out_pc.packed_vertices.vertices.size());
|
||||
|
||||
for (auto& col : mesh_extract_out.color_palette) {
|
||||
tfrag3::TimeOfDayColor todc;
|
||||
|
@ -15,7 +15,6 @@
|
||||
namespace collide {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int MAX_FACES_IN_FRAG = 100;
|
||||
|
||||
/*!
|
||||
@ -41,7 +40,6 @@ struct BBox {
|
||||
*/
|
||||
void add_to_bbox_recursive(const CNode& node, BBox& bbox) {
|
||||
if (node.faces.empty()) {
|
||||
ASSERT(node.child_nodes.size() == 8);
|
||||
for (auto& child : node.child_nodes) {
|
||||
add_to_bbox_recursive(child, bbox);
|
||||
}
|
||||
@ -68,7 +66,6 @@ BBox bbox_of_node(const CNode& node) {
|
||||
*/
|
||||
void update_bsphere_recursive(const CNode& node, const math::Vector3f& origin, float& r_squared) {
|
||||
if (node.faces.empty()) {
|
||||
ASSERT(node.child_nodes.size() == 8);
|
||||
for (auto& child : node.child_nodes) {
|
||||
update_bsphere_recursive(child, origin, r_squared);
|
||||
}
|
||||
@ -164,28 +161,35 @@ void split_node_to_8_children(CNode& node) {
|
||||
split_node_once(level1[3], &node.child_nodes[6], &node.child_nodes[7]);
|
||||
}
|
||||
|
||||
struct SplitResult {
|
||||
size_t max_leaf_count = 0;
|
||||
float max_bsphere_w = 0;
|
||||
};
|
||||
/*!
|
||||
* Split all leaf nodes. Returns the number of faces in the leaf with the most faces after
|
||||
* splitting.
|
||||
* This slightly unusual recursion pattern is to make sure we split everything to same depth,
|
||||
* which we believe might be a requirement of the collision system.
|
||||
*/
|
||||
size_t split_all_leaves(CNode& node) {
|
||||
size_t worst_leaf_face_count = 0;
|
||||
SplitResult split_all_leaves(CNode& node) {
|
||||
SplitResult result;
|
||||
if (node.child_nodes.empty()) {
|
||||
// we're a leaf!
|
||||
// split us:
|
||||
split_node_to_8_children(node);
|
||||
for (auto& child : node.child_nodes) {
|
||||
worst_leaf_face_count = std::max(worst_leaf_face_count, child.faces.size());
|
||||
result.max_leaf_count = std::max(result.max_leaf_count, child.faces.size());
|
||||
result.max_bsphere_w = std::max(result.max_bsphere_w, child.bsphere.w());
|
||||
}
|
||||
return worst_leaf_face_count;
|
||||
return result;
|
||||
} else {
|
||||
// not a leaf, recurse
|
||||
for (auto& child : node.child_nodes) {
|
||||
worst_leaf_face_count = std::max(worst_leaf_face_count, split_all_leaves(child));
|
||||
auto cret = split_all_leaves(child);
|
||||
result.max_bsphere_w = std::max(result.max_bsphere_w, cret.max_bsphere_w);
|
||||
result.max_leaf_count = std::max(result.max_leaf_count, cret.max_leaf_count);
|
||||
}
|
||||
return worst_leaf_face_count;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,10 +201,11 @@ void split_as_needed(CNode& root) {
|
||||
int num_leaves = 1;
|
||||
bool need_to_split = true;
|
||||
while (need_to_split) {
|
||||
int faces_in_worst = split_all_leaves(root);
|
||||
SplitResult worst = split_all_leaves(root);
|
||||
num_leaves *= 8;
|
||||
lg::info("after splitting, the worst leaf has {} tris", faces_in_worst);
|
||||
if (faces_in_worst < MAX_FACES_IN_FRAG) {
|
||||
lg::info("after splitting, the worst leaf has {} tris, {} radius", worst.max_leaf_count,
|
||||
worst.max_bsphere_w / 4096.f);
|
||||
if (worst.max_leaf_count < MAX_FACES_IN_FRAG && worst.max_bsphere_w < (100.f * 4096.f)) {
|
||||
need_to_split = false;
|
||||
}
|
||||
}
|
||||
|
@ -147,8 +147,6 @@ size_t generate_collide_fragment_array(DataObjectGenerator& gen,
|
||||
gen.add_word(0); // 24
|
||||
gen.add_word(0); // 28
|
||||
|
||||
fmt::print("have: {}\n", meshes.size());
|
||||
|
||||
ASSERT(meshes.size() == frag_mesh_locs.size());
|
||||
for (size_t i = 0; i < meshes.size(); i++) {
|
||||
auto& mesh = meshes[i];
|
||||
@ -210,9 +208,6 @@ size_t generate_collide_draw_node_array(DataObjectGenerator& gen,
|
||||
}
|
||||
|
||||
size_t DrawableTreeCollideFragment::add_to_object_file(DataObjectGenerator& gen) const {
|
||||
for (auto& lev : bvh.node_arrays) {
|
||||
fmt::print("lev: {}\n", lev.nodes.size());
|
||||
}
|
||||
// generate pat array
|
||||
size_t pat_array_loc = generate_pat_array(gen, packed_frags.pats);
|
||||
|
||||
@ -238,7 +233,6 @@ size_t DrawableTreeCollideFragment::add_to_object_file(DataObjectGenerator& gen)
|
||||
collide_frag_meshes, children_refs);
|
||||
u32 flag = 0;
|
||||
while (array_slot >= 0) {
|
||||
fmt::print("sizes: {} {}\n", children_refs.size(), bvh.node_arrays.at(array_slot).nodes.size());
|
||||
ASSERT(children_refs.size() == bvh.node_arrays.at(array_slot).nodes.size());
|
||||
std::vector<size_t> next_children;
|
||||
|
||||
|
@ -36,7 +36,9 @@ float u32_to_float(u32 in) {
|
||||
*/
|
||||
PackedU16Verts pack_verts_to_u16(const std::vector<math::Vector3f>& input) {
|
||||
PackedU16Verts result;
|
||||
ASSERT(!input.empty());
|
||||
if (input.empty()) {
|
||||
lg::warn("Empty collide fragment");
|
||||
}
|
||||
|
||||
// this "magic" offset is a large float where a ulp is 16.f, or 1/256th of a meter.
|
||||
// this means that float -> int can be done as a single addition.
|
||||
@ -45,7 +47,7 @@ PackedU16Verts pack_verts_to_u16(const std::vector<math::Vector3f>& input) {
|
||||
magic_offset.fill(u32_to_float(0x4d000000));
|
||||
|
||||
// we'll be treating everything as an offset from this minimum vertex:
|
||||
math::Vector3f min_vtx = input[0];
|
||||
math::Vector3f min_vtx = input.empty() ? math::Vector3f::zero() : input[0];
|
||||
for (auto& vtx : input) {
|
||||
min_vtx.min_in_place(vtx);
|
||||
}
|
||||
@ -143,8 +145,6 @@ struct Vector3fHash {
|
||||
IndexedFaces dedup_frag_mesh(const collide::CollideFrag& frag, PatMap* pat_map) {
|
||||
IndexedFaces result;
|
||||
std::unordered_map<math::Vector3f, u32, Vector3fHash> vertex_map;
|
||||
// avoid confusion with 0 in strip table. (todo, can probably remove)
|
||||
result.vertices_float.push_back(math::Vector3f::zero());
|
||||
|
||||
for (auto& face_in : frag.faces) {
|
||||
auto& face_out = result.faces.emplace_back();
|
||||
@ -163,7 +163,6 @@ IndexedFaces dedup_frag_mesh(const collide::CollideFrag& frag, PatMap* pat_map)
|
||||
}
|
||||
}
|
||||
}
|
||||
// fmt::print("{} -> {}\n", frag.faces.size() * 3, result.vertices_float.size());
|
||||
result.vertices_u16 = pack_verts_to_u16(result.vertices_float);
|
||||
return result;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Mesh extraction for GLTF meshes.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include "gltf_mesh_extract.h"
|
||||
#include "goalc/build_level/color_quantization.h"
|
||||
#include "third-party/tiny_gltf/tiny_gltf.h"
|
||||
@ -217,6 +218,10 @@ ExtractedVertices gltf_vertices(const tinygltf::Model& model,
|
||||
ASSERT_MSG(attrib_accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT,
|
||||
"NORMAL wasn't float");
|
||||
normals = extract_vec3f(data_ptr, count, byte_stride);
|
||||
for (auto& nrm : normals) {
|
||||
math::Vector4f nrm4(nrm.x(), nrm.y(), nrm.z(), 0.f);
|
||||
nrm = (w_T_local * nrm4).xyz();
|
||||
}
|
||||
ASSERT(normals.size() == result.size());
|
||||
} else {
|
||||
lg::error("No NORMAL attribute for mesh: {}", debug_name);
|
||||
@ -246,8 +251,8 @@ DrawMode make_default_draw_mode() {
|
||||
mode.set_alpha_blend(DrawMode::AlphaBlend::DISABLED);
|
||||
mode.set_aref(0);
|
||||
mode.set_alpha_fail(GsTest::AlphaFail::KEEP);
|
||||
mode.set_clamp_s_enable(true);
|
||||
mode.set_clamp_t_enable(true);
|
||||
mode.set_clamp_s_enable(false);
|
||||
mode.set_clamp_t_enable(false);
|
||||
mode.disable_filt(); // for checkerboard...
|
||||
mode.enable_tcc(); // ?
|
||||
mode.disable_at();
|
||||
@ -527,6 +532,7 @@ void extract(const Input& in,
|
||||
if (in.get_colors) {
|
||||
all_vtx_colors.insert(all_vtx_colors.end(), verts.vtx_colors.begin(),
|
||||
verts.vtx_colors.end());
|
||||
ASSERT(all_vtx_colors.size() == out.vertices.size());
|
||||
}
|
||||
|
||||
// TODO: just putting it all in one material
|
||||
@ -596,6 +602,65 @@ void extract(const Input& in,
|
||||
dedup_vertices(out);
|
||||
}
|
||||
|
||||
std::optional<std::vector<CollideFace>> subdivide_face_if_needed(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];
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
|
||||
void extract(const Input& in,
|
||||
CollideOutput& out,
|
||||
const tinygltf::Model& model,
|
||||
@ -606,7 +671,6 @@ void extract(const Input& in,
|
||||
|
||||
for (const auto& n : all_nodes) {
|
||||
const auto& node = model.nodes[n.node_idx];
|
||||
fmt::print("node: {} {}\n", node.name, node.mesh);
|
||||
if (node.mesh >= 0) {
|
||||
const auto& mesh = model.meshes[node.mesh];
|
||||
if (!mesh.extras.Has("collide")) {
|
||||
@ -644,7 +708,9 @@ void extract(const Input& in,
|
||||
|
||||
if (dots[0] > 1e-3 && dots[1] > 1e-3 && dots[2] > 1e-3) {
|
||||
suspicious_faces++;
|
||||
std::swap(face.v[2], face.v[1]);
|
||||
auto temp = face.v[2];
|
||||
face.v[2] = face.v[1];
|
||||
face.v[1] = temp;
|
||||
}
|
||||
|
||||
face.bsphere = math::bsphere_of_triangle(face.v);
|
||||
@ -665,8 +731,22 @@ void extract(const Input& in,
|
||||
}
|
||||
}
|
||||
|
||||
lg::info("{} out of {} faces were suspicious (a small number is ok)", suspicious_faces,
|
||||
out.faces.size());
|
||||
std::vector<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);
|
||||
}
|
||||
}
|
||||
out.faces = std::move(fixed_faces);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user