#include "meshcollider.hpp" #include "std.hpp" static const int MAX_COLL_TRIS = 16; static vector tri_centres; extern float stats3d[10]; extern gxRuntime* gx_runtime; static bool triTest(const Vector a[3], const Vector b[3]) { bool pb0 = false, pb1 = false, pb2 = false; Plane p(a[0], a[1], a[2]), p0, p1, p2; for (int k = 0; k < 3; ++k) { Line l(b[k], b[(k + 1) % 3] - b[k]); float t = p.t_intersect(l); if (t < 0 || t > 1) continue; Vector i = l * t; if (!pb0) { p0 = Plane(a[0] + p.n, a[1], a[0]); pb0 = true; } if (p0.distance(i) < 0) continue; if (!pb1) { p1 = Plane(a[1] + p.n, a[2], a[1]); pb1 = true; } if (p1.distance(i) < 0) continue; if (!pb2) { p2 = Plane(a[2] + p.n, a[0], a[2]); pb2 = true; } if (p2.distance(i) < 0) continue; return true; } return false; } static bool trisIntersect(const Vector a[3], const Vector b[3]) { return triTest(a, b) || triTest(b, a); } MeshCollider::MeshCollider(const vector& verts, const vector& tris) : vertices(verts), triangles(tris) { vector ts; tri_centres.clear(); for (int k = 0; k < triangles.size(); ++k) { const MeshCollider::Triangle& t = triangles[k]; const Vector& v0 = vertices[t.verts[0]].coords; const Vector& v1 = vertices[t.verts[1]].coords; const Vector& v2 = vertices[t.verts[2]].coords; tri_centres.push_back((v0 + v1 + v2) / 3); ts.push_back(k); } tree = createNode(ts); } MeshCollider::~MeshCollider() { delete tree; } bool MeshCollider::collide(const Line& line, float radius, Collision* curr_coll, const Transform& t) { if (!tree) return false; //create local box Box box(line); box.expand(radius); Box local_box = -t * box; return collide(local_box, line, radius, t, curr_coll, tree); } bool MeshCollider::collide(const Box& line_box, const Line& line, float radius, const Transform& tform, Collision* curr_coll, MeshCollider::Node* node) { if (!line_box.overlaps(node->box)) { return false; } bool hit = false; if (!node->triangles.size()) { if (node->left) hit |= collide(line_box, line, radius, tform, curr_coll, node->left); if (node->right) hit |= collide(line_box, line, radius, tform, curr_coll, node->right); return hit; } stats3d[0] += node->triangles.size(); for (int k = 0; k < node->triangles.size(); ++k) { const Triangle& tri = triangles[node->triangles[k]]; const Vector& t_v0 = vertices[tri.verts[0]].coords; const Vector& t_v1 = vertices[tri.verts[1]].coords; const Vector& t_v2 = vertices[tri.verts[2]].coords; //tri box Box tri_box(t_v0); tri_box.update(t_v1); tri_box.update(t_v2); if (!tri_box.overlaps(line_box)) continue; if (!curr_coll->triangleCollide(line, radius, tform * t_v0, tform * t_v1, tform * t_v2)) continue; curr_coll->surface = tri.surface; curr_coll->index = tri.index; hit = true; } return hit; } Box MeshCollider::nodeBox(const vector& tris) { Box box; for (int k = 0; k < tris.size(); ++k) { const Triangle& t = triangles[tris[k]]; for (int j = 0; j < 3; ++j) box.update(vertices[t.verts[j]].coords); } return box; } MeshCollider::Node* MeshCollider::createLeaf(const vector& tris) { Node* c = new Node; c->box = nodeBox(tris); c->triangles = tris; leaves.push_back(c); return c; } MeshCollider::Node* MeshCollider::createNode(const vector& tris) { if (tris.size() <= MAX_COLL_TRIS) return createLeaf(tris); Node* c = new Node; c->box = nodeBox(tris); //find longest axis // float max = c->box.width(); if (c->box.height() > max) max = c->box.height(); if (c->box.depth() > max) max = c->box.depth(); int axis = 0; if (max == c->box.height()) axis = 1; else if (max == c->box.depth()) axis = 2; //sort by axis // int k; multimap axis_map; for (k = 0; k < tris.size(); ++k) { pair p(tri_centres[tris[k]][axis], tris[k]); axis_map.insert(p); } //generate left node // vector new_tris; multimap::iterator it = axis_map.begin(); for (k = axis_map.size() / 2; k--; ++it) { new_tris.push_back(it->second); } c->left = createNode(new_tris); //generate right node // new_tris.clear(); for (; it != axis_map.end(); ++it) { new_tris.push_back(it->second); } c->right = createNode(new_tris); return c; } bool MeshCollider::intersects(const MeshCollider& c, const Transform& t) const { static Vector a[MAX_COLL_TRIS][3], b[3]; if (!(t * tree->box).overlaps(c.tree->box)) return false; for (int k = 0; k < leaves.size(); ++k) { Node* p = leaves[k]; Box box = t * p->box; bool tformed = false; for (int j = 0; j < c.leaves.size(); ++j) { Node* q = c.leaves[j]; if (!box.overlaps(q->box)) continue; if (!tformed) { for (int n = 0; n < p->triangles.size(); ++n) { const Triangle& tri = triangles[p->triangles[n]]; a[n][0] = t * vertices[tri.verts[0]].coords; a[n][1] = t * vertices[tri.verts[1]].coords; a[n][2] = t * vertices[tri.verts[2]].coords; } tformed = true; } for (int n = 0; n < q->triangles.size(); ++n) { const Triangle& tri = c.triangles[q->triangles[n]]; b[0] = c.vertices[tri.verts[0]].coords; b[1] = c.vertices[tri.verts[1]].coords; b[2] = c.vertices[tri.verts[2]].coords; for (int t = 0; t < p->triangles.size(); ++t) { if (trisIntersect(a[t], b)) return true; } } } } return false; }