[custom levels] fix various collision bugs (#1522)

* [custom levels] fix various collision bugs

* include
This commit is contained in:
water111 2022-06-22 22:43:42 -04:00 committed by GitHub
parent 56a84cd923
commit b3f2b81481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 32 deletions

View File

@ -99,7 +99,7 @@ struct PackedTfragVertices {
struct Vertex {
u16 xoff, yoff, zoff;
u16 cluster_idx;
u16 s, t;
s16 s, t;
u16 color_index;
};

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}