Files
BlitzNext/Runtime/blitz3d/meshcollider.cpp
T

234 lines
5.6 KiB
C++
Raw Normal View History

2019-01-18 15:55:06 +01:00
#include "meshcollider.hpp"
2019-01-18 21:26:42 +01:00
#include <map>
#include <gxruntime.hpp>
2014-01-31 08:23:00 +13:00
2019-01-18 17:04:17 +01:00
static const int MAX_COLL_TRIS = 16;
2019-01-18 21:26:42 +01:00
static std::vector<Vector> tri_centres;
2014-01-31 08:23:00 +13:00
extern float stats3d[10];
2019-01-18 17:04:17 +01:00
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;
2014-01-31 08:23:00 +13:00
return true;
}
return false;
}
2019-01-18 17:04:17 +01:00
static bool trisIntersect(const Vector a[3], const Vector b[3])
{
return triTest(a, b) || triTest(b, a);
2014-01-31 08:23:00 +13:00
}
2019-01-18 21:26:42 +01:00
MeshCollider::MeshCollider(const std::vector<Vertex>& verts, const std::vector<Triangle>& tris)
: vertices(verts), triangles(tris)
2019-01-18 17:04:17 +01:00
{
2019-01-18 21:26:42 +01:00
std::vector<int> ts;
2014-01-31 08:23:00 +13:00
tri_centres.clear();
2019-01-18 17:04:17 +01:00
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);
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
tree = createNode(ts);
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
MeshCollider::~MeshCollider()
{
2014-01-31 08:23:00 +13:00
delete tree;
}
2019-01-18 17:04:17 +01:00
bool MeshCollider::collide(const Line& line, float radius, Collision* curr_coll, const Transform& t)
{
if (!tree)
return false;
2014-01-31 08:23:00 +13:00
//create local box
2019-01-18 17:04:17 +01:00
Box box(line);
box.expand(radius);
Box local_box = -t * box;
2014-01-31 08:23:00 +13:00
2019-01-18 17:04:17 +01:00
return collide(local_box, line, radius, t, curr_coll, tree);
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
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)) {
2014-01-31 08:23:00 +13:00
return false;
}
2019-01-18 17:04:17 +01:00
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);
2014-01-31 08:23:00 +13:00
return hit;
}
2019-01-18 17:04:17 +01:00
stats3d[0] += node->triangles.size();
2014-01-31 08:23:00 +13:00
2019-01-18 17:04:17 +01:00
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;
2014-01-31 08:23:00 +13:00
//tri box
2019-01-18 17:04:17 +01:00
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;
2014-01-31 08:23:00 +13:00
2019-01-18 17:04:17 +01:00
curr_coll->surface = tri.surface;
curr_coll->index = tri.index;
2014-01-31 08:23:00 +13:00
2019-01-18 17:04:17 +01:00
hit = true;
2014-01-31 08:23:00 +13:00
}
return hit;
}
2019-01-18 21:26:42 +01:00
Box MeshCollider::nodeBox(const std::vector<int>& tris)
2019-01-18 17:04:17 +01:00
{
2014-01-31 08:23:00 +13:00
Box box;
2019-01-18 17:04:17 +01:00
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);
2014-01-31 08:23:00 +13:00
}
return box;
}
2019-01-18 21:26:42 +01:00
MeshCollider::Node* MeshCollider::createLeaf(const std::vector<int>& tris)
2019-01-18 17:04:17 +01:00
{
Node* c = new Node;
c->box = nodeBox(tris);
c->triangles = tris;
leaves.push_back(c);
2014-01-31 08:23:00 +13:00
return c;
}
2019-01-18 21:26:42 +01:00
MeshCollider::Node* MeshCollider::createNode(const std::vector<int>& tris)
2019-01-18 17:04:17 +01:00
{
if (tris.size() <= MAX_COLL_TRIS)
return createLeaf(tris);
2014-01-31 08:23:00 +13:00
2019-01-18 17:04:17 +01:00
Node* c = new Node;
c->box = nodeBox(tris);
2014-01-31 08:23:00 +13:00
//find longest axis
//
2019-01-18 17:04:17 +01:00
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;
2014-01-31 08:23:00 +13:00
//sort by axis
//
2019-01-18 17:04:17 +01:00
int k;
2019-01-18 21:26:42 +01:00
std::multimap<float, int> axis_map;
2019-01-18 17:04:17 +01:00
for (k = 0; k < tris.size(); ++k) {
2019-01-18 21:26:42 +01:00
std::pair<float, int> p(tri_centres[tris[k]][axis], tris[k]);
2019-01-18 17:04:17 +01:00
axis_map.insert(p);
2014-01-31 08:23:00 +13:00
}
//generate left node
//
2019-01-18 21:26:42 +01:00
std::vector<int> new_tris;
std::multimap<float, int>::iterator it = axis_map.begin();
2019-01-18 17:04:17 +01:00
for (k = axis_map.size() / 2; k--; ++it) {
new_tris.push_back(it->second);
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
c->left = createNode(new_tris);
2014-01-31 08:23:00 +13:00
//generate right node
//
new_tris.clear();
2019-01-18 17:04:17 +01:00
for (; it != axis_map.end(); ++it) {
new_tris.push_back(it->second);
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
c->right = createNode(new_tris);
2014-01-31 08:23:00 +13:00
return c;
}
2019-01-18 17:04:17 +01:00
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;
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
tformed = true;
2014-01-31 08:23:00 +13:00
}
2019-01-18 17:04:17 +01:00
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;
2014-01-31 08:23:00 +13:00
}
}
}
}
return false;
}