|
@@ -16,10 +16,10 @@ popular belief) you can also use their math in 2D:
|
|
|
|
|
|
Unit vectors that are perpendicular to a surface (so, they describe the
|
|
Unit vectors that are perpendicular to a surface (so, they describe the
|
|
orientation of the surface) are called **unit normal vectors**. Though,
|
|
orientation of the surface) are called **unit normal vectors**. Though,
|
|
-usually they are just abbreviated as \*normals. Normals appear in
|
|
|
|
|
|
+usually they are just abbreviated as *normals*. Normals appear in
|
|
planes, 3D geometry (to determine where each face or vertex is siding),
|
|
planes, 3D geometry (to determine where each face or vertex is siding),
|
|
etc. A **normal** *is* a **unit vector**, but it's called *normal*
|
|
etc. A **normal** *is* a **unit vector**, but it's called *normal*
|
|
-because of its usage. (Just like we call Origin to (0,0)!).
|
|
|
|
|
|
+because of its usage. (Just like we call (0,0) the Origin!).
|
|
|
|
|
|
It's as simple as it looks. The plane passes by the origin and the
|
|
It's as simple as it looks. The plane passes by the origin and the
|
|
surface of it is perpendicular to the unit vector (or *normal*). The
|
|
surface of it is perpendicular to the unit vector (or *normal*). The
|
|
@@ -37,10 +37,15 @@ The dot product between a **unit vector** and any **point in space**
|
|
(yes, this time we do dot product between vector and position), returns
|
|
(yes, this time we do dot product between vector and position), returns
|
|
the **distance from the point to the plane**:
|
|
the **distance from the point to the plane**:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var distance = normal.dot(point)
|
|
var distance = normal.dot(point)
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var distance = normal.Dot(point);
|
|
|
|
+
|
|
But not just the absolute distance, if the point is in the negative half
|
|
But not just the absolute distance, if the point is in the negative half
|
|
space the distance will be negative, too:
|
|
space the distance will be negative, too:
|
|
|
|
|
|
@@ -74,43 +79,69 @@ for both. It's the same as before, but D is the distance from the origin
|
|
to the plane, travelling in N direction. As an example, imagine you want
|
|
to the plane, travelling in N direction. As an example, imagine you want
|
|
to reach a point in the plane, you will just do:
|
|
to reach a point in the plane, you will just do:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var point_in_plane = N*D
|
|
var point_in_plane = N*D
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var pointInPlane = N * D;
|
|
|
|
+
|
|
This will stretch (resize) the normal vector and make it touch the
|
|
This will stretch (resize) the normal vector and make it touch the
|
|
plane. This math might seem confusing, but it's actually much simpler
|
|
plane. This math might seem confusing, but it's actually much simpler
|
|
than it seems. If we want to tell, again, the distance from the point to
|
|
than it seems. If we want to tell, again, the distance from the point to
|
|
the plane, we do the same but adjusting for distance:
|
|
the plane, we do the same but adjusting for distance:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var distance = N.dot(point) - D
|
|
var distance = N.dot(point) - D
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var distance = N.Dot(point) - D;
|
|
|
|
+
|
|
The same thing, using a built-in function:
|
|
The same thing, using a built-in function:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var distance = plane.distance_to(point)
|
|
var distance = plane.distance_to(point)
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var distance = plane.DistanceTo(point);
|
|
|
|
+
|
|
This will, again, return either a positive or negative distance.
|
|
This will, again, return either a positive or negative distance.
|
|
|
|
|
|
Flipping the polarity of the plane is also very simple, just negate both
|
|
Flipping the polarity of the plane is also very simple, just negate both
|
|
N and D. This will result in a plane in the same position, but with
|
|
N and D. This will result in a plane in the same position, but with
|
|
inverted negative and positive half spaces:
|
|
inverted negative and positive half spaces:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
N = -N
|
|
N = -N
|
|
D = -D
|
|
D = -D
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ N = -N;
|
|
|
|
+ D = -D;
|
|
|
|
+
|
|
Of course, Godot also implements this operator in :ref:`Plane <class_Plane>`,
|
|
Of course, Godot also implements this operator in :ref:`Plane <class_Plane>`,
|
|
so doing:
|
|
so doing:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var inverted_plane = -plane
|
|
var inverted_plane = -plane
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var invertedPlane = -plane;
|
|
|
|
+
|
|
Will work as expected.
|
|
Will work as expected.
|
|
|
|
|
|
So, remember, a plane is just that and its main practical use is
|
|
So, remember, a plane is just that and its main practical use is
|
|
@@ -129,18 +160,25 @@ In the case of a normal and a point, most of the work is done, as the
|
|
normal is already computed, so just calculate D from the dot product of
|
|
normal is already computed, so just calculate D from the dot product of
|
|
the normal and the point.
|
|
the normal and the point.
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var N = normal
|
|
var N = normal
|
|
var D = normal.dot(point)
|
|
var D = normal.dot(point)
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var N = normal;
|
|
|
|
+ var D = normal.Dot(point);
|
|
|
|
+
|
|
For two points in space, there are actually two planes that pass through
|
|
For two points in space, there are actually two planes that pass through
|
|
them, sharing the same space but with normal pointing to the opposite
|
|
them, sharing the same space but with normal pointing to the opposite
|
|
directions. To compute the normal from the two points, the direction
|
|
directions. To compute the normal from the two points, the direction
|
|
vector must be obtained first, and then it needs to be rotated 90°
|
|
vector must be obtained first, and then it needs to be rotated 90°
|
|
degrees to either side:
|
|
degrees to either side:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
# calculate vector from a to b
|
|
# calculate vector from a to b
|
|
var dvec = (point_b - point_a).normalized()
|
|
var dvec = (point_b - point_a).normalized()
|
|
@@ -150,16 +188,34 @@ degrees to either side:
|
|
# var normal = Vector2(-dvec.y, dvec.x)
|
|
# var normal = Vector2(-dvec.y, dvec.x)
|
|
# depending the desired side of the normal
|
|
# depending the desired side of the normal
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ // calculate vector from a to b
|
|
|
|
+ var dvec = (pointB - pointA).Normalized();
|
|
|
|
+ // rotate 90 degrees
|
|
|
|
+ var normal = new Vector2(dvec.y, -dvec.x);
|
|
|
|
+ // or alternatively
|
|
|
|
+ // var normal = new Vector2(-dvec.y, dvec.x);
|
|
|
|
+ // depending the desired side of the normal
|
|
|
|
+
|
|
The rest is the same as the previous example, either point_a or
|
|
The rest is the same as the previous example, either point_a or
|
|
point_b will work since they are in the same plane:
|
|
point_b will work since they are in the same plane:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var N = normal
|
|
var N = normal
|
|
var D = normal.dot(point_a)
|
|
var D = normal.dot(point_a)
|
|
# this works the same
|
|
# this works the same
|
|
# var D = normal.dot(point_b)
|
|
# var D = normal.dot(point_b)
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var N = normal;
|
|
|
|
+ var D = normal.Dot(pointA);
|
|
|
|
+ // this works the same
|
|
|
|
+ // var D = normal.Dot(pointB);
|
|
|
|
+
|
|
Doing the same in 3D is a little more complex and will be explained
|
|
Doing the same in 3D is a little more complex and will be explained
|
|
further down.
|
|
further down.
|
|
|
|
|
|
@@ -183,15 +239,29 @@ can't, then the point is inside.
|
|
|
|
|
|
Code should be something like this:
|
|
Code should be something like this:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var inside = true
|
|
var inside = true
|
|
for p in planes:
|
|
for p in planes:
|
|
# check if distance to plane is positive
|
|
# check if distance to plane is positive
|
|
- if (N.dot(point) - D > 0):
|
|
|
|
|
|
+ if (p.distance_to(point) > 0):
|
|
inside = false
|
|
inside = false
|
|
break # with one that fails, it's enough
|
|
break # with one that fails, it's enough
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var inside = true;
|
|
|
|
+ foreach (var p in planes)
|
|
|
|
+ {
|
|
|
|
+ // check if distance to plane is positive
|
|
|
|
+ if (p.DistanceTo(point) > 0)
|
|
|
|
+ {
|
|
|
|
+ inside = false;
|
|
|
|
+ break; // with one that fails, it's enough
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
Pretty cool, huh? But this gets much better! With a little more effort,
|
|
Pretty cool, huh? But this gets much better! With a little more effort,
|
|
similar logic will let us know when two convex polygons are overlapping
|
|
similar logic will let us know when two convex polygons are overlapping
|
|
too. This is called the Separating Axis Theorem (or SAT) and most
|
|
too. This is called the Separating Axis Theorem (or SAT) and most
|
|
@@ -199,7 +269,7 @@ physics engines use this to detect collision.
|
|
|
|
|
|
The idea is really simple! With a point, just checking if a plane
|
|
The idea is really simple! With a point, just checking if a plane
|
|
returns a positive distance is enough to tell if the point is outside.
|
|
returns a positive distance is enough to tell if the point is outside.
|
|
-With another polygon, we must find a plane where *all* *the* ***other***
|
|
|
|
|
|
+With another polygon, we must find a plane where *all* *the* *other*
|
|
*polygon* *points* return a positive distance to it. This check is
|
|
*polygon* *points* return a positive distance to it. This check is
|
|
performed with the planes of A against the points of B, and then with
|
|
performed with the planes of A against the points of B, and then with
|
|
the planes of B against the points of A:
|
|
the planes of B against the points of A:
|
|
@@ -208,7 +278,8 @@ the planes of B against the points of A:
|
|
|
|
|
|
Code should be something like this:
|
|
Code should be something like this:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var overlapping = true
|
|
var overlapping = true
|
|
|
|
|
|
@@ -242,6 +313,60 @@ Code should be something like this:
|
|
if (overlapping):
|
|
if (overlapping):
|
|
print("Polygons Collided!")
|
|
print("Polygons Collided!")
|
|
|
|
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var overlapping = true;
|
|
|
|
+
|
|
|
|
+ foreach (Plane plane in planesOfA)
|
|
|
|
+ {
|
|
|
|
+ var allOut = true;
|
|
|
|
+ foreach (Vector3 point in pointsOfB)
|
|
|
|
+ {
|
|
|
|
+ if (plane.DistanceTo(point) < 0)
|
|
|
|
+ {
|
|
|
|
+ allOut = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (allOut)
|
|
|
|
+ {
|
|
|
|
+ // a separating plane was found
|
|
|
|
+ // do not continue testing
|
|
|
|
+ overlapping = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (overlapping)
|
|
|
|
+ {
|
|
|
|
+ // only do this check if no separating plane
|
|
|
|
+ // was found in planes of A
|
|
|
|
+ foreach (Plane plane in planesOfB)
|
|
|
|
+ {
|
|
|
|
+ var allOut = true;
|
|
|
|
+ foreach (Vector3 point in pointsOfA)
|
|
|
|
+ {
|
|
|
|
+ if (plane.DistanceTo(point) < 0)
|
|
|
|
+ {
|
|
|
|
+ allOut = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (allOut)
|
|
|
|
+ {
|
|
|
|
+ overlapping = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (overlapping)
|
|
|
|
+ {
|
|
|
|
+ GD.Print("Polygons Collided!");
|
|
|
|
+ }
|
|
|
|
+
|
|
As you can see, planes are quite useful, and this is the tip of the
|
|
As you can see, planes are quite useful, and this is the tip of the
|
|
iceberg. You might be wondering what happens with non convex polygons.
|
|
iceberg. You might be wondering what happens with non convex polygons.
|
|
This is usually just handled by splitting the concave polygon into
|
|
This is usually just handled by splitting the concave polygon into
|
|
@@ -285,7 +410,8 @@ edges of polygon B
|
|
|
|
|
|
So the final algorithm is something like:
|
|
So the final algorithm is something like:
|
|
|
|
|
|
-::
|
|
|
|
|
|
+.. tabs::
|
|
|
|
+ .. code-tab:: gdscript GDScript
|
|
|
|
|
|
var overlapping = true
|
|
var overlapping = true
|
|
|
|
|
|
@@ -333,20 +459,16 @@ So the final algorithm is something like:
|
|
|
|
|
|
for v in points_of_A:
|
|
for v in points_of_A:
|
|
var d = n.dot(v)
|
|
var d = n.dot(v)
|
|
- if (d > max_A):
|
|
|
|
- max_A = d
|
|
|
|
- if (d < min_A):
|
|
|
|
- min_A = d
|
|
|
|
|
|
+ max_A = max(max_A, d)
|
|
|
|
+ min_A = min(min_A, d)
|
|
|
|
|
|
var max_B = -1e20 # tiny number
|
|
var max_B = -1e20 # tiny number
|
|
var min_B = 1e20 # huge number
|
|
var min_B = 1e20 # huge number
|
|
|
|
|
|
for v in points_of_B:
|
|
for v in points_of_B:
|
|
var d = n.dot(v)
|
|
var d = n.dot(v)
|
|
- if (d > max_B):
|
|
|
|
- max_B = d
|
|
|
|
- if (d < min_B):
|
|
|
|
- min_B = d
|
|
|
|
|
|
+ max_B = max(max_B, d)
|
|
|
|
+ min_B = min(min_B, d)
|
|
|
|
|
|
if (min_A > max_B or min_B > max_A):
|
|
if (min_A > max_B or min_B > max_A):
|
|
# not overlapping!
|
|
# not overlapping!
|
|
@@ -358,3 +480,110 @@ So the final algorithm is something like:
|
|
|
|
|
|
if (overlapping):
|
|
if (overlapping):
|
|
print("Polygons collided!")
|
|
print("Polygons collided!")
|
|
|
|
+
|
|
|
|
+ .. code-tab:: csharp
|
|
|
|
+
|
|
|
|
+ var overlapping = true;
|
|
|
|
+
|
|
|
|
+ foreach (Plane plane in planesOfA)
|
|
|
|
+ {
|
|
|
|
+ var allOut = true;
|
|
|
|
+ foreach (Vector3 point in pointsOfB)
|
|
|
|
+ {
|
|
|
|
+ if (plane.DistanceTo(point) < 0)
|
|
|
|
+ {
|
|
|
|
+ allOut = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (allOut)
|
|
|
|
+ {
|
|
|
|
+ // a separating plane was found
|
|
|
|
+ // do not continue testing
|
|
|
|
+ overlapping = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (overlapping)
|
|
|
|
+ {
|
|
|
|
+ // only do this check if no separating plane
|
|
|
|
+ // was found in planes of A
|
|
|
|
+ foreach (Plane plane in planesOfB)
|
|
|
|
+ {
|
|
|
|
+ var allOut = true;
|
|
|
|
+ foreach (Vector3 point in pointsOfA)
|
|
|
|
+ {
|
|
|
|
+ if (plane.DistanceTo(point) < 0)
|
|
|
|
+ {
|
|
|
|
+ allOut = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (allOut)
|
|
|
|
+ {
|
|
|
|
+ overlapping = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (overlapping)
|
|
|
|
+ {
|
|
|
|
+ foreach (Vector3 edgeA in edgesOfA)
|
|
|
|
+ {
|
|
|
|
+ foreach (Vector3 edgeB in edgesOfB)
|
|
|
|
+ {
|
|
|
|
+ var normal = edgeA.Cross(edgeB);
|
|
|
|
+ if (normal.Length() == 0)
|
|
|
|
+ {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var maxA = float.MinValue; // tiny number
|
|
|
|
+ var minA = float.MaxValue; // huge number
|
|
|
|
+
|
|
|
|
+ // we are using the dot product directly
|
|
|
|
+ // so we can map a maximum and minimum range
|
|
|
|
+ // for each polygon, then check if they
|
|
|
|
+ // overlap.
|
|
|
|
+
|
|
|
|
+ foreach (Vector3 point in pointsOfA)
|
|
|
|
+ {
|
|
|
|
+ var distance = normal.Dot(point);
|
|
|
|
+ maxA = Mathf.Max(maxA, distance);
|
|
|
|
+ minA = Mathf.Min(minA, distance);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var maxB = float.MinValue; // tiny number
|
|
|
|
+ var minB = float.MaxValue; // huge number
|
|
|
|
+
|
|
|
|
+ foreach (Vector3 point in pointsOfB)
|
|
|
|
+ {
|
|
|
|
+ var distance = normal.Dot(point);
|
|
|
|
+ maxB = Mathf.Max(maxB, distance);
|
|
|
|
+ minB = Mathf.Min(minB, distance);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (minA > maxB || minB > maxA)
|
|
|
|
+ {
|
|
|
|
+ // not overlapping!
|
|
|
|
+ overlapping = false;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!overlapping)
|
|
|
|
+ {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (overlapping)
|
|
|
|
+ {
|
|
|
|
+ GD.Print("Polygons Collided!");
|
|
|
|
+ }
|