Implement Polyhedron header
This commit is contained in:
10
include/J3ML/Geometry/Line.h
Normal file
10
include/J3ML/Geometry/Line.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
namespace J3ML::Geometry
|
||||||
|
{
|
||||||
|
class Line
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
|
#include "Ray.h"
|
||||||
|
|
||||||
|
|
||||||
namespace J3ML::Geometry
|
namespace J3ML::Geometry
|
||||||
@@ -25,5 +26,7 @@ namespace J3ML::Geometry
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Intersects(J3ML::Geometry::Ray ray, float *pDouble);
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -66,8 +66,27 @@ namespace J3ML::Geometry
|
|||||||
bool Intersects(const Sphere&) const;
|
bool Intersects(const Sphere&) const;
|
||||||
bool Intersects(const Capsule&) const;
|
bool Intersects(const Capsule&) const;
|
||||||
|
|
||||||
|
float FaceContainmentDistance2D(int i, Vector3 vector3) const;
|
||||||
|
|
||||||
|
bool IsClosed() const;
|
||||||
|
|
||||||
|
Plane FacePlane(int faceIndex) const;
|
||||||
|
|
||||||
|
std::vector<Polygon> Faces() const;
|
||||||
|
|
||||||
|
int NumEdges() const;
|
||||||
|
|
||||||
|
LineSegment Edge(int edgeIndex) const;
|
||||||
|
|
||||||
|
std::vector<std::pair<int, int>> EdgeIndices() const;
|
||||||
|
|
||||||
|
std::vector<LineSegment> Edges() const;
|
||||||
|
|
||||||
|
Polygon FacePolygon(int faceIndex) const;
|
||||||
|
|
||||||
|
bool IsConvex() const;
|
||||||
protected:
|
protected:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
@@ -3,9 +3,20 @@
|
|||||||
#include <J3ML/Geometry/Triangle.h>
|
#include <J3ML/Geometry/Triangle.h>
|
||||||
#include <J3ML/Geometry/LineSegment.h>
|
#include <J3ML/Geometry/LineSegment.h>
|
||||||
#include "J3ML/Geometry/Ray.h"
|
#include "J3ML/Geometry/Ray.h"
|
||||||
|
#include <J3ML/Geometry/Polygon.h>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace J3ML::Geometry
|
namespace J3ML::Geometry
|
||||||
{
|
{
|
||||||
|
|
||||||
|
int Polyhedron::NumEdges() const
|
||||||
|
{
|
||||||
|
int numEdges = 0;
|
||||||
|
for(size_t i = 0; i < f.size(); ++i)
|
||||||
|
numEdges += (int)f[i].v.size();
|
||||||
|
return numEdges / 2;
|
||||||
|
}
|
||||||
|
|
||||||
AABB Polyhedron::MinimalEnclosingAABB() const {
|
AABB Polyhedron::MinimalEnclosingAABB() const {
|
||||||
AABB aabb;
|
AABB aabb;
|
||||||
aabb.SetNegativeInfinity();
|
aabb.SetNegativeInfinity();
|
||||||
@@ -18,6 +29,61 @@ namespace J3ML::Geometry
|
|||||||
return v[vertexIndex];
|
return v[vertexIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LineSegment Polyhedron::Edge(int edgeIndex) const
|
||||||
|
{
|
||||||
|
assert(edgeIndex >= 0);
|
||||||
|
std::vector<LineSegment> edges = Edges();
|
||||||
|
assert(edgeIndex < (int)edges.size());
|
||||||
|
return edges[edgeIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygon Polyhedron::FacePolygon(int faceIndex) const
|
||||||
|
{
|
||||||
|
Polygon p;
|
||||||
|
assert(faceIndex >= 0);
|
||||||
|
assert(faceIndex < (int)f.size());
|
||||||
|
|
||||||
|
p.vertices.reserve(f[faceIndex].v.size());
|
||||||
|
for(size_t i = 0; i < f[faceIndex].v.size(); ++i)
|
||||||
|
p.vertices.push_back(Vertex(f[faceIndex].v[i]));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<LineSegment> Polyhedron::Edges() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<int, int> > edges = EdgeIndices();
|
||||||
|
std::vector<LineSegment> edgeLines;
|
||||||
|
edgeLines.reserve(edges.size());
|
||||||
|
for(size_t i = 0; i < edges.size(); ++i)
|
||||||
|
edgeLines.push_back(LineSegment(Vertex(edges[i].first), Vertex(edges[i].second)));
|
||||||
|
|
||||||
|
return edgeLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<int, int> > Polyhedron::EdgeIndices() const
|
||||||
|
{
|
||||||
|
std::set<std::pair<int, int> > uniqueEdges;
|
||||||
|
for(int i = 0; i < NumFaces(); ++i)
|
||||||
|
{
|
||||||
|
assert(f[i].v.size() >= 3);
|
||||||
|
if (f[i].v.size() < 3)
|
||||||
|
continue; // Degenerate face with less than three vertices, skip!
|
||||||
|
int x = f[i].v.back();
|
||||||
|
for(size_t j = 0; j < f[i].v.size(); ++j)
|
||||||
|
{
|
||||||
|
int y = f[i].v[j];
|
||||||
|
uniqueEdges.insert(std::make_pair(std::min(x, y), std::max(x, y)));
|
||||||
|
x = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<int, int> >edges;
|
||||||
|
edges.insert(edges.end(), uniqueEdges.begin(), uniqueEdges.end());
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Polyhedron::Contains(const Vector3 &point) const {
|
bool Polyhedron::Contains(const Vector3 &point) const {
|
||||||
|
|
||||||
if (v.size() <= 3) {
|
if (v.size() <= 3) {
|
||||||
@@ -70,10 +136,283 @@ namespace J3ML::Geometry
|
|||||||
Plane p((Vector3)v[f[i].v[0]] - point, (Vector3)v[f[i].v[1]] - point, (Vector3)v[f[i].v[2]] - point);
|
Plane p((Vector3)v[f[i].v[0]] - point, (Vector3)v[f[i].v[1]] - point, (Vector3)v[f[i].v[2]] - point);
|
||||||
|
|
||||||
float d;
|
float d;
|
||||||
|
// Find the intersection of the plane and the ray.
|
||||||
|
if (p.Intersects(r, &d))
|
||||||
|
{
|
||||||
|
float containmentDistance2D = FaceContainmentDistance2D(i, r.GetPoint(d) + point);
|
||||||
|
if (containmentDistance2D >= 0.f)
|
||||||
|
++numIntersections;
|
||||||
|
faceContainmentDistance = std::min(faceContainmentDistance, std::abs(containmentDistance2D));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (faceContainmentDistance > 1e-2f) // The nearest edge was far enough, we conclude the result is believable.
|
||||||
|
return (numIntersections % 2) == 1;
|
||||||
|
else if (faceContainmentDistance >= bestFaceContainmentDistance)
|
||||||
|
{
|
||||||
|
// The ray passed too close to a face edge. Remember this result, but proceed to another test iteration to see if we can
|
||||||
|
// find a more plausible test ray.
|
||||||
|
bestNumIntersections = numIntersections;
|
||||||
|
bestFaceContainmentDistance = faceContainmentDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We tested rays through each face of the polyhedron, but all rays passed too close to edges of the polyhedron faces. Return
|
||||||
|
// the result from the test that was farthest to any of the face edges.
|
||||||
|
return (bestNumIntersections % 2) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Polyhedron::FaceContainmentDistance2D(int i, Vector3 vector3) const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const LineSegment &lineSegment) const
|
||||||
|
{
|
||||||
|
return Contains(lineSegment.A) && Contains(lineSegment.B);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const Triangle &triangle) const
|
||||||
|
{
|
||||||
|
return Contains(triangle.V0) && Contains(triangle.V1) && Contains(triangle.V2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const Polygon &polygon) const
|
||||||
|
{
|
||||||
|
for(int i = 0; i < polygon.NumVertices(); ++i)
|
||||||
|
if (!Contains(polygon.Vertex(i)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const AABB &aabb) const
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 8; ++i)
|
||||||
|
if (!Contains(aabb.CornerPoint(i)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const OBB &obb) const
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 8; ++i)
|
||||||
|
if (!Contains(obb.CornerPoint(i)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const Frustum &frustum) const
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 8; ++i)
|
||||||
|
if (!Contains(frustum.CornerPoint(i)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::Contains(const Polyhedron &polyhedron) const
|
||||||
|
{
|
||||||
|
assert(polyhedron.IsClosed());
|
||||||
|
for(int i = 0; i < polyhedron.NumVertices(); ++i)
|
||||||
|
if (!Contains(polyhedron.Vertex(i)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::IsClosed() const {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plane Polyhedron::FacePlane(int faceIndex) const
|
||||||
|
{
|
||||||
|
const Face &face = f[faceIndex];
|
||||||
|
if (face.v.size() >= 3)
|
||||||
|
return Plane(v[face.v[0]], v[face.v[1]], v[face.v[2]]);
|
||||||
|
else if (face.v.size() == 2)
|
||||||
|
return Plane(Line(v[face.v[0]], v[face.v[1]]), ((Vector3)v[face.v[0]]-(Vector3)v[face.v[1]]).Perpendicular());
|
||||||
|
else if (face.v.size() == 1)
|
||||||
|
return Plane(v[face.v[0]], DIR_VEC(0,1,0));
|
||||||
|
else
|
||||||
|
return Plane();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Polygon> Polyhedron::Faces() const
|
||||||
|
{
|
||||||
|
std::vector<Polygon> faces;
|
||||||
|
faces.reserve(NumFaces());
|
||||||
|
for(int i = 0; i < NumFaces(); ++i)
|
||||||
|
faces.push_back(FacePolygon(i));
|
||||||
|
return faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector4 PolyFaceNormal(const Polyhedron &poly, int faceIndex)
|
||||||
|
{
|
||||||
|
const Polyhedron::Face &face = poly.f[faceIndex];
|
||||||
|
if (face.v.size() == 3)
|
||||||
|
{
|
||||||
|
Vector4 a = Vector4(poly.v[face.v[0]], 1.f);
|
||||||
|
Vector4 b = Vector4(poly.v[face.v[1]], 1.f);
|
||||||
|
Vector4 c = Vector4(poly.v[face.v[2]], 1.f);
|
||||||
|
Vector4 normal = (b-a).Cross(c-a);
|
||||||
|
normal.Normalize();
|
||||||
|
return normal;
|
||||||
|
// return ((vec)v[face.v[1]]-(vec)v[face.v[0]]).Cross((vec)v[face.v[2]]-(vec)v[face.v[0]]).Normalized();
|
||||||
|
}
|
||||||
|
else if (face.v.size() > 3)
|
||||||
|
{
|
||||||
|
// Use Newell's method of computing the face normal for best stability.
|
||||||
|
// See Christer Ericson, Real-Time Collision Detection, pp. 491-495.
|
||||||
|
Vector4 normal(0, 0, 0, 0);
|
||||||
|
int v0 = face.v.back();
|
||||||
|
for(size_t i = 0; i < face.v.size(); ++i)
|
||||||
|
{
|
||||||
|
int v1 = face.v[i];
|
||||||
|
normal.x += (double(poly.v[v0].y) - poly.v[v1].y) * (double(poly.v[v0].z) + poly.v[v1].z); // Project on yz
|
||||||
|
normal.y += (double(poly.v[v0].z) - poly.v[v1].z) * (double(poly.v[v0].x) + poly.v[v1].x); // Project on xz
|
||||||
|
normal.z += (double(poly.v[v0].x) - poly.v[v1].x) * (double(poly.v[v0].y) + poly.v[v1].y); // Project on xy
|
||||||
|
v0 = v1;
|
||||||
|
}
|
||||||
|
normal.Normalize();
|
||||||
|
return normal;
|
||||||
|
#if 0
|
||||||
|
cv bestNormal;
|
||||||
|
cs bestLen = -FLOAT_INF;
|
||||||
|
cv a = poly.v[face.v[face.v.size()-2]];
|
||||||
|
cv b = poly.v[face.v.back()];
|
||||||
|
for(size_t i = 0; i < face.v.size()-2; ++i)
|
||||||
|
{
|
||||||
|
cv c = poly.v[face.v[i]];
|
||||||
|
cv normal = (c-b).Cross(a-b);
|
||||||
|
float len = normal.Normalize();
|
||||||
|
if (len > bestLen)
|
||||||
|
{
|
||||||
|
bestLen = len;
|
||||||
|
bestNormal = normal;
|
||||||
|
}
|
||||||
|
a = b;
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
assert(bestLen != -FLOAT_INF);
|
||||||
|
return DIR_VEC((float)bestNormal.x, (float)bestNormal.y, (float)bestNormal.z);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Find the longest edge.
|
||||||
|
cs bestLenSq = -FLOAT_INF;
|
||||||
|
cv bestEdge;
|
||||||
|
int v0 = face.v.back();
|
||||||
|
int bestV0 = 0;
|
||||||
|
int bestV1 = 0;
|
||||||
|
for(size_t i = 0; i < face.v.size(); ++i)
|
||||||
|
{
|
||||||
|
int v1 = face.v[i];
|
||||||
|
cv edge = cv(poly.v[v1]) - cv(poly.v[v0]);
|
||||||
|
cs lenSq = edge.Normalize();
|
||||||
|
if (lenSq > bestLenSq)
|
||||||
|
{
|
||||||
|
bestLenSq = lenSq;
|
||||||
|
bestEdge = edge;
|
||||||
|
bestV0 = v0;
|
||||||
|
bestV1 = v1;
|
||||||
|
}
|
||||||
|
v0 = v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv bestNormal;
|
||||||
|
cs bestLen = -FLOAT_INF;
|
||||||
|
for(size_t i = 0; i < face.v.size(); ++i)
|
||||||
|
{
|
||||||
|
if (face.v[i] == bestV0 || face.v[i] == bestV1)
|
||||||
|
continue;
|
||||||
|
cv edge = cv(poly.v[i]) - cv(poly.v[bestV0]);
|
||||||
|
edge.Normalize();
|
||||||
|
cv normal = bestEdge.Cross(edge);
|
||||||
|
cs len = normal.Normalize();
|
||||||
|
if (len > bestLen)
|
||||||
|
{
|
||||||
|
bestLen = len;
|
||||||
|
bestNormal = normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(bestLen != -FLOAT_INF);
|
||||||
|
return DIR_VEC((float)bestNormal.x, (float)bestNormal.y, (float)bestNormal.z);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (face.v.size() == 2)
|
||||||
|
return DIR_TO_FLOAT4(((Vector3)poly.v[face.v[1]]-(Vector3)poly.v[face.v[0]]).Cross(((Vector3)poly.v[face.v[0]]-(Vector3)poly.v[face.v[1]]).Perpendicular()-poly.v[face.v[0]]).Normalized());
|
||||||
|
else if (face.v.size() == 1)
|
||||||
|
return cv(0, 1, 0, 0);
|
||||||
|
else
|
||||||
|
return float4::nan;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec Polyhedron::FaceNormal(int faceIndex) const
|
||||||
|
{
|
||||||
|
cv normal = PolyFaceNormal(*this, faceIndex);
|
||||||
|
return DIR_VEC((float)normal.x, (float)normal.y, (float)normal.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::IsConvex() const
|
||||||
|
{
|
||||||
|
// This function is O(n^2).
|
||||||
|
/** @todo Real-Time Collision Detection, p. 64:
|
||||||
|
A faster O(n) approach is to compute for each face F of P the centroid C of F,
|
||||||
|
and for all neighboring faces G of F test if C lies behind the supporting plane of
|
||||||
|
G. If some C fails to lie behind the supporting plane of one or more neighboring
|
||||||
|
faces, P is concave, and is otherwise assumed convex. However, note that just as the
|
||||||
|
corresponding polygonal convexity test may fail for a pentagram this test may fail for,
|
||||||
|
for example, a pentagram extruded out of its plane and capped at the ends. */
|
||||||
|
|
||||||
|
float farthestD = -INFINITY;
|
||||||
|
int numPointsOutside = 0;
|
||||||
|
for(size_t i = 0; i < f.size(); ++i)
|
||||||
|
{
|
||||||
|
if (f[i].v.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vector3 pointOnFace = v[f[i].v[0]];
|
||||||
|
Vector3 faceNormal = FaceNormal((int)i);
|
||||||
|
for(size_t j = 0; j < v.size(); ++j)
|
||||||
|
{
|
||||||
|
float d = faceNormal.Dot(Vector3(v[j]) - pointOnFace);
|
||||||
|
if (d > 1e-2f)
|
||||||
|
{
|
||||||
|
if (d > farthestD)
|
||||||
|
farthestD = d;
|
||||||
|
++numPointsOutside;
|
||||||
|
// LOGW("Distance of vertex %s/%d from face %s/%d: %f",
|
||||||
|
// Vertex(j).ToString().c_str(), (int)j, FacePlane(i).ToString().c_str(), (int)i, d);
|
||||||
|
// return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (numPointsOutside > 0)
|
||||||
|
{
|
||||||
|
printf("%d point-planes are outside the face planes. Farthest is at distance %f!", numPointsOutside, farthestD);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::ContainsConvex(const Vector3 &point, float epsilon) const
|
||||||
|
{
|
||||||
|
assert(IsConvex());
|
||||||
|
for(int i = 0; i < NumFaces(); ++i)
|
||||||
|
if (FacePlane(i).SignedDistance(point) > epsilon)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::ContainsConvex(const LineSegment &lineSegment) const {
|
||||||
|
return ContainsConvex(lineSegment.A) && ContainsConvex(lineSegment.B);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Polyhedron::ContainsConvex(const Triangle &triangle) const
|
||||||
|
{
|
||||||
|
return ContainsConvex(triangle.V0) && ContainsConvex(triangle.V1) && ContainsConvex(triangle.V2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user