Add support for multiple triangles per Point

This commit is contained in:
karl 2021-01-16 15:57:41 +01:00
parent ba98ae3386
commit 40dc7850a9
3 changed files with 68 additions and 20 deletions

View File

@ -8,6 +8,8 @@
#include "../Components/Transform.h"
#include "../ECS.h"
#include <map>
using namespace ECS;
class CollisionSystem : public EntitySystem {
@ -16,7 +18,6 @@ class CollisionSystem : public EntitySystem {
// Initialize the kdtree
void build() {
std::vector<Triangle *> triangles;
std::vector<Point *> points;
// ObjMesh
@ -25,7 +26,11 @@ class CollisionSystem : public EntitySystem {
std::vector<unsigned int> indices = mesh->indices;
std::vector<float> vertices = mesh->vertices;
std::map<Vector, Point *> triangle_map;
// TODO: Iterate over vertices, add triangles by also iterating over indices
for (int i = 0; i < mesh->vertex_count; i += 3) {
// Build vertices from this triangle
float v0p0 = vertices[indices[i + 0] * 14 + 0];
float v0p1 = vertices[indices[i + 0] * 14 + 1];
float v0p2 = vertices[indices[i + 0] * 14 + 2];
@ -53,12 +58,30 @@ class CollisionSystem : public EntitySystem {
Vector v3(v3glm.x, v3glm.y, v3glm.z);
Triangle *triangle = new Triangle(v1, v2, v3);
triangles.emplace_back(triangle);
points.emplace_back(new Point(v1, triangle));
points.emplace_back(new Point(v2, triangle));
points.emplace_back(new Point(v3, triangle));
if (triangle_map.count(v1) == 0) {
triangle_map[v1] = new Point(v1, std::list<Triangle *>{triangle});
} else {
triangle_map[v1]->triangles.emplace_back(triangle);
}
if (triangle_map.count(v2) == 0) {
triangle_map[v2] = new Point(v2, std::list<Triangle *>{triangle});
} else {
triangle_map[v2]->triangles.emplace_back(triangle);
}
if (triangle_map.count(v3) == 0) {
triangle_map[v3] = new Point(v3, std::list<Triangle *>{triangle});
} else {
triangle_map[v3]->triangles.emplace_back(triangle);
}
}
// Convert to list
std::transform(
triangle_map.begin(), triangle_map.end(), back_inserter(points),
[](const std::map<Vector, Point *>::value_type &val) { return val.second; });
});
// LODObjMesh
@ -70,8 +93,6 @@ class CollisionSystem : public EntitySystem {
std::cout << "Start building kdtree with " << points.size() << " points" << std::endl;
kdtree = new KDTree(points);
std::cout << "Done" << std::endl;
std::cout << kdtree->to_string() << std::endl;
}
void tick(World *pWorld, float deltaTime) override {
@ -86,7 +107,7 @@ class CollisionSystem : public EntitySystem {
Ray ray(origin, direction);
Vector collision_position(0, 0, 0);
Triangle *result = kdtree->intersect_ray(ray, collision_position);
const Triangle *result = kdtree->intersect_ray(ray, collision_position);
if (result) {
// Output to console

View File

@ -1,5 +1,6 @@
#pragma once
#include <list>
#include <vector>
// Forward declarations
@ -21,6 +22,14 @@ struct Vector {
return Vector(c[0] - other.c[0], c[1] - other.c[1], c[2] - other.c[2]);
}
bool operator<(const Vector &other) const {
if ((c[2] < other.c[2])) { return true; }
if ((c[2] == other.c[2]) && (c[1] < other.c[1])) { return true; }
if ((c[2] == other.c[2]) && (c[1] == other.c[1]) && (c[0] < other.c[0])) { return true; }
return false;
}
Vector operator*(float scalar) const {
return Vector(c[0] * scalar, c[1] * scalar, c[2] * scalar);
}
@ -36,18 +45,22 @@ struct Vector {
};
struct Point {
Point(Vector pos, Triangle *triangle) : pos(pos), triangle(triangle) {}
Point(Vector pos) : pos(pos) {}
Point(Vector pos, std::list<Triangle *> triangles) : pos(pos), triangles(triangles) {}
Vector pos;
Triangle *triangle;
std::list<Triangle *> triangles;
};
struct Triangle {
Triangle(Vector p1, Vector p2, Vector p3) : p1(p1), p2(p2), p3(p3) {}
std::vector<Point *> create_point_objects() {
return std::vector<Point *>{new Point(p1, this), new Point(p2, this), new Point(p3, this)};
return std::vector<Point *>{new Point(p1, std::list<Triangle *>{this}),
new Point(p2, std::list<Triangle *>{this}),
new Point(p3, std::list<Triangle *>{this})};
}
Vector p1;
@ -74,7 +87,7 @@ struct Ray {
Vector direction;
bool intersects_triangle(Triangle *triangle, Vector &result) {
bool intersects_triangle(const Triangle *triangle, Vector &result, float &t) {
// Ray-triangle-intersection with the MöllerTrumbore algorithm
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
const float EPSILON = 0.0000001;
@ -103,7 +116,7 @@ struct Ray {
// At this stage we can compute t to find out where the intersection point is on the
// line.
float t = f * edge2.dot(q);
t = f * edge2.dot(q);
if (t > EPSILON) {
result = origin + direction * t;
return true;

View File

@ -11,7 +11,7 @@ class KDTree {
~KDTree() = default; // TODO: Delete all allocated Nodes
Triangle *intersect_ray(Ray ray, Vector &result) {
const Triangle *intersect_ray(const Ray ray, Vector &result) {
return intersect_ray_recurse(result, ray, root, 1000.0, 0);
}
@ -87,7 +87,7 @@ class KDTree {
build(right_of_median, depth + 1));
}
Triangle *intersect_ray_recurse(Vector &result, Ray ray, Node *node, float max_distance,
const Triangle *intersect_ray_recurse(Vector &result, Ray ray, Node *node, float max_distance,
int depth) {
// Exit condition: There was no collision
if (node == nullptr) { return nullptr; }
@ -108,17 +108,31 @@ class KDTree {
? (node->point->pos[node->axis] - ray.origin[node->axis]) /
ray.direction[node->axis]
: max_distance;
Triangle *near_result = intersect_ray_recurse(result, ray, near, t, depth + 1);
const Triangle *near_result = intersect_ray_recurse(result, ray, near, t, depth + 1);
// If the nearer segment had a collision, we're done! We're only interested in the closest
// collision.
if (near_result != nullptr) { return near_result; }
// No collision in the nearer side, so check for a collision directly here
if (ray.intersects_triangle(node->point->triangle, result)) {
// We do have a collision here, so we're done and can return this point!
return node->point->triangle;
float nearest = 100000; // FIXME
const Triangle *nearest_triangle = nullptr;
for (const Triangle *triangle : node->point->triangles) {
Vector current_result(0, 0, 0);
float current_distance;
if (ray.intersects_triangle(triangle, current_result, current_distance)) {
std::cout << "tested with " << current_distance << std::endl;
if (current_distance < nearest) {
nearest = current_distance;
nearest_triangle = triangle;
result = current_result;
}
}
}
if (nearest_triangle) { return nearest_triangle; }
// No collision here either. Does it make sense to also check the far node?
// Only if the axes are not parallel and if that area is not behind us