basilisk-engine 0.1.38__py3-none-any.whl → 0.1.39__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basilisk-engine might be problematic. Click here for more details.
- basilisk/__init__.py +26 -26
- basilisk/audio/sound.py +27 -27
- basilisk/bsk_assets/cube.obj +48 -48
- basilisk/collisions/broad/broad_aabb.py +102 -102
- basilisk/collisions/broad/broad_bvh.py +137 -137
- basilisk/collisions/collider.py +95 -95
- basilisk/collisions/collider_handler.py +226 -226
- basilisk/collisions/narrow/contact_manifold.py +95 -95
- basilisk/collisions/narrow/dataclasses.py +34 -34
- basilisk/collisions/narrow/deprecated.py +46 -46
- basilisk/collisions/narrow/epa.py +91 -91
- basilisk/collisions/narrow/gjk.py +66 -66
- basilisk/collisions/narrow/graham_scan.py +24 -24
- basilisk/collisions/narrow/helper.py +29 -29
- basilisk/collisions/narrow/line_intersections.py +106 -106
- basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
- basilisk/config.py +53 -53
- basilisk/draw/draw.py +100 -100
- basilisk/draw/draw_handler.py +178 -178
- basilisk/draw/font_renderer.py +28 -28
- basilisk/engine.py +165 -165
- basilisk/generic/abstract_bvh.py +15 -15
- basilisk/generic/abstract_custom.py +133 -133
- basilisk/generic/collisions.py +70 -70
- basilisk/generic/input_validation.py +82 -82
- basilisk/generic/math.py +18 -7
- basilisk/generic/matrices.py +35 -35
- basilisk/generic/meshes.py +72 -72
- basilisk/generic/quat.py +142 -142
- basilisk/generic/quat_methods.py +7 -7
- basilisk/generic/raycast_result.py +26 -26
- basilisk/generic/vec3.py +143 -143
- basilisk/input/__init__.py +0 -0
- basilisk/input/mouse.py +62 -0
- basilisk/input/path.py +14 -0
- basilisk/input_output/IO_handler.py +91 -91
- basilisk/input_output/clock.py +49 -49
- basilisk/input_output/keys.py +43 -43
- basilisk/input_output/mouse.py +90 -90
- basilisk/input_output/path.py +14 -14
- basilisk/mesh/cube.py +33 -33
- basilisk/mesh/mesh.py +233 -233
- basilisk/mesh/mesh_from_data.py +150 -150
- basilisk/mesh/model.py +271 -271
- basilisk/mesh/narrow_aabb.py +89 -89
- basilisk/mesh/narrow_bvh.py +91 -91
- basilisk/mesh/narrow_primative.py +23 -23
- basilisk/nodes/helper.py +28 -28
- basilisk/nodes/node.py +709 -704
- basilisk/nodes/node_handler.py +97 -97
- basilisk/particles/particle_handler.py +64 -64
- basilisk/particles/particle_renderer.py +93 -93
- basilisk/physics/impulse.py +112 -112
- basilisk/physics/physics_body.py +43 -43
- basilisk/physics/physics_engine.py +35 -35
- basilisk/render/batch.py +103 -103
- basilisk/render/bloom.py +107 -107
- basilisk/render/camera.py +260 -260
- basilisk/render/chunk.py +108 -108
- basilisk/render/chunk_handler.py +167 -167
- basilisk/render/frame.py +110 -110
- basilisk/render/framebuffer.py +202 -202
- basilisk/render/image.py +120 -120
- basilisk/render/image_handler.py +120 -120
- basilisk/render/light.py +96 -96
- basilisk/render/light_handler.py +58 -58
- basilisk/render/material.py +232 -232
- basilisk/render/material_handler.py +133 -133
- basilisk/render/post_process.py +146 -146
- basilisk/render/shader.py +134 -134
- basilisk/render/shader_handler.py +85 -85
- basilisk/render/sky.py +120 -120
- basilisk/scene.py +290 -290
- basilisk/shaders/batch.frag +289 -289
- basilisk/shaders/batch.vert +117 -117
- basilisk/shaders/bloom_downsample.frag +42 -42
- basilisk/shaders/bloom_frame.frag +24 -24
- basilisk/shaders/bloom_upsample.frag +33 -33
- basilisk/shaders/crt.frag +31 -31
- basilisk/shaders/draw.frag +25 -25
- basilisk/shaders/draw.vert +25 -25
- basilisk/shaders/filter.frag +22 -22
- basilisk/shaders/frame.frag +12 -12
- basilisk/shaders/frame.vert +13 -13
- basilisk/shaders/geometry.frag +10 -10
- basilisk/shaders/geometry.vert +41 -41
- basilisk/shaders/normal.frag +62 -62
- basilisk/shaders/normal.vert +96 -96
- basilisk/shaders/particle.frag +76 -76
- basilisk/shaders/particle.vert +86 -86
- basilisk/shaders/sky.frag +23 -23
- basilisk/shaders/sky.vert +13 -13
- {basilisk_engine-0.1.38.dist-info → basilisk_engine-0.1.39.dist-info}/METADATA +89 -89
- basilisk_engine-0.1.39.dist-info/RECORD +114 -0
- {basilisk_engine-0.1.38.dist-info → basilisk_engine-0.1.39.dist-info}/WHEEL +1 -1
- basilisk_engine-0.1.38.dist-info/RECORD +0 -111
- {basilisk_engine-0.1.38.dist-info → basilisk_engine-0.1.39.dist-info}/top_level.txt +0 -0
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import glm
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
from basilisk.generic.vec3 import Vec3
|
|
5
|
-
# from ...nodes.node import Node
|
|
6
|
-
|
|
7
|
-
# frozen because data does not need to be mutable
|
|
8
|
-
# used in creating polytopes for GJK/EPA
|
|
9
|
-
@dataclass(frozen=True)
|
|
10
|
-
class SupportPoint():
|
|
11
|
-
support_point: glm.vec3
|
|
12
|
-
|
|
13
|
-
index1: int # index of the vertex in the mesh
|
|
14
|
-
vertex1: glm.vec3 # world space location of the vertex at collision
|
|
15
|
-
|
|
16
|
-
index2: int
|
|
17
|
-
vertex2: glm.vec3
|
|
18
|
-
|
|
19
|
-
# used for generating contact points for the contact manifold
|
|
20
|
-
@dataclass(frozen=True)
|
|
21
|
-
class ContactPoint():
|
|
22
|
-
index: int
|
|
23
|
-
vertex: Vec3
|
|
24
|
-
|
|
25
|
-
# contact manifold object used in the contact handler list
|
|
26
|
-
@dataclass
|
|
27
|
-
class ContactManifold():
|
|
28
|
-
normal: glm.vec3
|
|
29
|
-
contact_points1: dict[int : glm.vec3] # contact point index : collision position
|
|
30
|
-
contact_points2: dict[int : glm.vec3]
|
|
31
|
-
|
|
32
|
-
@dataclass
|
|
33
|
-
class Collision():
|
|
34
|
-
node: ...
|
|
1
|
+
import glm
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from basilisk.generic.vec3 import Vec3
|
|
5
|
+
# from ...nodes.node import Node
|
|
6
|
+
|
|
7
|
+
# frozen because data does not need to be mutable
|
|
8
|
+
# used in creating polytopes for GJK/EPA
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class SupportPoint():
|
|
11
|
+
support_point: glm.vec3
|
|
12
|
+
|
|
13
|
+
index1: int # index of the vertex in the mesh
|
|
14
|
+
vertex1: glm.vec3 # world space location of the vertex at collision
|
|
15
|
+
|
|
16
|
+
index2: int
|
|
17
|
+
vertex2: glm.vec3
|
|
18
|
+
|
|
19
|
+
# used for generating contact points for the contact manifold
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class ContactPoint():
|
|
22
|
+
index: int
|
|
23
|
+
vertex: Vec3
|
|
24
|
+
|
|
25
|
+
# contact manifold object used in the contact handler list
|
|
26
|
+
@dataclass
|
|
27
|
+
class ContactManifold():
|
|
28
|
+
normal: glm.vec3
|
|
29
|
+
contact_points1: dict[int : glm.vec3] # contact point index : collision position
|
|
30
|
+
contact_points2: dict[int : glm.vec3]
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Collision():
|
|
34
|
+
node: ...
|
|
35
35
|
normal: glm.vec3
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
# def sat_manifold(self, points1: list[glm.vec3], points2: list[glm.vec3], axis: glm.vec3, plane_point: glm.vec3, digit: int) -> list[glm.vec3]:
|
|
2
|
-
# """
|
|
3
|
-
# Returns the contact manifold from an SAT OBB OBB collision
|
|
4
|
-
# """
|
|
5
|
-
# def get_test_points(contact_plane_normal:glm.vec3, points:list[glm.vec3], count: int):
|
|
6
|
-
# test_points = [(glm.dot(contact_plane_normal, p), p) for p in points]
|
|
7
|
-
# test_points.sort(key=lambda p: p[0])
|
|
8
|
-
# return [p[1] for p in test_points[:count]]
|
|
9
|
-
|
|
10
|
-
# def get_test_points_unknown(contact_plane_normal:glm.vec3, points:list[glm.vec3]):
|
|
11
|
-
# test_points = [(glm.dot(contact_plane_normal, p), p) for p in points]
|
|
12
|
-
# test_points.sort(key=lambda p: p[0])
|
|
13
|
-
# if test_points[2][0] - test_points[0][0] > 1e-3: return [p[1] for p in test_points[:2]]
|
|
14
|
-
# else: return [p[1] for p in test_points[:4]]
|
|
15
|
-
|
|
16
|
-
# if digit < 6: # there must be at least one face in the collision
|
|
17
|
-
# reference, incident = (get_test_points(-axis, points1, 4), get_test_points_unknown(axis, points2)) if digit < 3 else (get_test_points(axis, points2, 4), get_test_points_unknown(-axis, points1))
|
|
18
|
-
|
|
19
|
-
# # project vertices onto the 2d plane
|
|
20
|
-
# reference = project_points(plane_point, axis, reference)
|
|
21
|
-
# incident = project_points(plane_point, axis, incident)
|
|
22
|
-
|
|
23
|
-
# # convert points to 2d for intersection algorithms
|
|
24
|
-
# reference, u1, v1 = points_to_2d(plane_point, axis, reference)
|
|
25
|
-
# incident, u2, v2 = points_to_2d(plane_point, axis, incident, u1, v1)
|
|
26
|
-
|
|
27
|
-
# # convert arbitrary points to polygon
|
|
28
|
-
# reference = graham_scan(reference)
|
|
29
|
-
# if len(incident) == 4: incident = graham_scan(incident)
|
|
30
|
-
|
|
31
|
-
# # run clipping algorithms
|
|
32
|
-
# manifold = []
|
|
33
|
-
# if len(incident) == 2: manifold = line_poly_intersect(incident, reference)
|
|
34
|
-
# else: manifold = sutherland_hodgman(reference, incident)
|
|
35
|
-
|
|
36
|
-
# # # fall back if manifold fails to develope
|
|
37
|
-
# assert len(manifold), 'sat did not generate points'
|
|
38
|
-
|
|
39
|
-
# # # convert inertsection algorithm output to 3d
|
|
40
|
-
# return points_to_3d(u1, v1, plane_point, manifold)
|
|
41
|
-
|
|
42
|
-
# else: # there is an edge edge collision
|
|
43
|
-
|
|
44
|
-
# points1 = get_test_points(-axis, points1, 2)
|
|
45
|
-
# points2 = get_test_points(axis, points2, 2)
|
|
46
|
-
|
|
1
|
+
# def sat_manifold(self, points1: list[glm.vec3], points2: list[glm.vec3], axis: glm.vec3, plane_point: glm.vec3, digit: int) -> list[glm.vec3]:
|
|
2
|
+
# """
|
|
3
|
+
# Returns the contact manifold from an SAT OBB OBB collision
|
|
4
|
+
# """
|
|
5
|
+
# def get_test_points(contact_plane_normal:glm.vec3, points:list[glm.vec3], count: int):
|
|
6
|
+
# test_points = [(glm.dot(contact_plane_normal, p), p) for p in points]
|
|
7
|
+
# test_points.sort(key=lambda p: p[0])
|
|
8
|
+
# return [p[1] for p in test_points[:count]]
|
|
9
|
+
|
|
10
|
+
# def get_test_points_unknown(contact_plane_normal:glm.vec3, points:list[glm.vec3]):
|
|
11
|
+
# test_points = [(glm.dot(contact_plane_normal, p), p) for p in points]
|
|
12
|
+
# test_points.sort(key=lambda p: p[0])
|
|
13
|
+
# if test_points[2][0] - test_points[0][0] > 1e-3: return [p[1] for p in test_points[:2]]
|
|
14
|
+
# else: return [p[1] for p in test_points[:4]]
|
|
15
|
+
|
|
16
|
+
# if digit < 6: # there must be at least one face in the collision
|
|
17
|
+
# reference, incident = (get_test_points(-axis, points1, 4), get_test_points_unknown(axis, points2)) if digit < 3 else (get_test_points(axis, points2, 4), get_test_points_unknown(-axis, points1))
|
|
18
|
+
|
|
19
|
+
# # project vertices onto the 2d plane
|
|
20
|
+
# reference = project_points(plane_point, axis, reference)
|
|
21
|
+
# incident = project_points(plane_point, axis, incident)
|
|
22
|
+
|
|
23
|
+
# # convert points to 2d for intersection algorithms
|
|
24
|
+
# reference, u1, v1 = points_to_2d(plane_point, axis, reference)
|
|
25
|
+
# incident, u2, v2 = points_to_2d(plane_point, axis, incident, u1, v1)
|
|
26
|
+
|
|
27
|
+
# # convert arbitrary points to polygon
|
|
28
|
+
# reference = graham_scan(reference)
|
|
29
|
+
# if len(incident) == 4: incident = graham_scan(incident)
|
|
30
|
+
|
|
31
|
+
# # run clipping algorithms
|
|
32
|
+
# manifold = []
|
|
33
|
+
# if len(incident) == 2: manifold = line_poly_intersect(incident, reference)
|
|
34
|
+
# else: manifold = sutherland_hodgman(reference, incident)
|
|
35
|
+
|
|
36
|
+
# # # fall back if manifold fails to develope
|
|
37
|
+
# assert len(manifold), 'sat did not generate points'
|
|
38
|
+
|
|
39
|
+
# # # convert inertsection algorithm output to 3d
|
|
40
|
+
# return points_to_3d(u1, v1, plane_point, manifold)
|
|
41
|
+
|
|
42
|
+
# else: # there is an edge edge collision
|
|
43
|
+
|
|
44
|
+
# points1 = get_test_points(-axis, points1, 2)
|
|
45
|
+
# points2 = get_test_points(axis, points2, 2)
|
|
46
|
+
|
|
47
47
|
# return closest_two_lines(*points1, *points2)
|
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
import glm
|
|
2
|
-
from .helper import get_support_point
|
|
3
|
-
from .dataclasses import SupportPoint
|
|
4
|
-
from...nodes.node import Node
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
# TODO change these to structs when converting to C++
|
|
8
|
-
face_type = list[tuple[float, glm.vec3, glm.vec3, int, int, int]] # distance, normal, center, index 1, index 2, index 3
|
|
9
|
-
polytope_type = list[SupportPoint] # polytope vertex, node1 vertex, node2 vertex
|
|
10
|
-
|
|
11
|
-
def get_epa_from_gjk(node1: Node, node2: Node, polytope: polytope_type, epsilon: float=0) -> tuple[face_type, polytope_type]: # TODO determine the return type of get_epa_from_gjk and if epsilon is good value
|
|
12
|
-
"""
|
|
13
|
-
Determines the peneration vector from a collision using EPA. The returned face normal is normalized but the rest are not guarunteed to be.
|
|
14
|
-
"""
|
|
15
|
-
# orient faces to point normals counter clockwise
|
|
16
|
-
faces: face_type = []
|
|
17
|
-
for indices in [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]: faces = insert_face(polytope, faces, indices)
|
|
18
|
-
|
|
19
|
-
# develope the polytope until the nearest real face has been found, within epsilon
|
|
20
|
-
while True:
|
|
21
|
-
new_point = get_support_point(node1, node2, faces[0][1])
|
|
22
|
-
if new_point in polytope or glm.length(new_point.support_point) - faces[0][0] < epsilon: return faces, polytope
|
|
23
|
-
faces, polytope = insert_point(polytope, faces, new_point)
|
|
24
|
-
|
|
25
|
-
def insert_point(polytope: polytope_type, faces: face_type, point: glm.vec3, epsilon: float=0) -> tuple[face_type, polytope_type]:
|
|
26
|
-
"""
|
|
27
|
-
Inserts a point into the polytope sorting by distance from the origin
|
|
28
|
-
"""
|
|
29
|
-
# determine which faces are facing the new point
|
|
30
|
-
polytope.append(point)
|
|
31
|
-
support_index = len(polytope) - 1
|
|
32
|
-
visible_faces = [
|
|
33
|
-
face for face in faces
|
|
34
|
-
if glm.dot(face[1], polytope[support_index].support_point) >= epsilon and # if the normal of a face is pointing towards the added point
|
|
35
|
-
glm.dot(face[1], polytope[support_index].support_point - face[2]) >= epsilon # TODO check if this ever occurs
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
# generate new edges
|
|
39
|
-
edges = []
|
|
40
|
-
for face in visible_faces:
|
|
41
|
-
for p1, p2 in get_face_edges(face):
|
|
42
|
-
if (p2, p1) in edges: edges.remove((p2, p1)) # edges can only be shared by two faces, running opposite to each other.
|
|
43
|
-
elif (p1, p2) in edges: # TODO remove this
|
|
44
|
-
edges.remove((p1, p2))
|
|
45
|
-
# print('not reversed')
|
|
46
|
-
else: edges.append((p1, p2))
|
|
47
|
-
|
|
48
|
-
# remove visible faces
|
|
49
|
-
for face in sorted(visible_faces, reverse = True): faces.remove(face)
|
|
50
|
-
|
|
51
|
-
# add new faces
|
|
52
|
-
new_face_indices = [orient_face(polytope, (edge[0], edge[1], support_index)) for edge in edges] # edge[0], edge[1] is already ccw
|
|
53
|
-
for indices in new_face_indices: faces = insert_face(polytope, faces, indices)
|
|
54
|
-
|
|
55
|
-
return faces, polytope
|
|
56
|
-
|
|
57
|
-
def insert_face(polytope: polytope_type, faces: face_type, indices: tuple[int, int, int]) -> face_type:
|
|
58
|
-
"""
|
|
59
|
-
Inserts a face into the face priority queue based on the indices given in the polytope
|
|
60
|
-
"""
|
|
61
|
-
center = (polytope[indices[0]].support_point + polytope[indices[1]].support_point + polytope[indices[2]].support_point) / 3
|
|
62
|
-
normal = glm.cross(polytope[indices[1]].support_point - polytope[indices[0]].support_point, polytope[indices[2]].support_point - polytope[indices[0]].support_point) # closest face normal will be normalized once returned to avoid square roots and division
|
|
63
|
-
if glm.dot(center, normal) < 0:
|
|
64
|
-
normal *= -1
|
|
65
|
-
indices = (indices[2], indices[1], indices[0])
|
|
66
|
-
|
|
67
|
-
# TODO solve cases where face may contain origin
|
|
68
|
-
normal = glm.normalize(normal)
|
|
69
|
-
distance = abs(glm.dot(polytope[indices[0]].support_point, normal))
|
|
70
|
-
new_face = (distance, normal, center, *indices)
|
|
71
|
-
|
|
72
|
-
# insert faces into priority queue based on distance from origin
|
|
73
|
-
for i, face in enumerate(faces):
|
|
74
|
-
if face[0] < distance: continue
|
|
75
|
-
faces.insert(i, new_face)
|
|
76
|
-
break
|
|
77
|
-
else: faces.append(new_face)
|
|
78
|
-
|
|
79
|
-
return faces
|
|
80
|
-
|
|
81
|
-
def orient_face(polytope: polytope_type, indices: tuple[int, int, int]) -> tuple[int, int, int]:
|
|
82
|
-
"""
|
|
83
|
-
Orients the face indices to have a counter clockwise normal
|
|
84
|
-
"""
|
|
85
|
-
if glm.dot(glm.cross(polytope[indices[1]].support_point, polytope[indices[2]].support_point), polytope[indices[0]].support_point) < 0: return (indices[2], indices[1], indices[0])
|
|
86
|
-
return indices
|
|
87
|
-
|
|
88
|
-
def get_face_edges(face: tuple[float, glm.vec3, glm.vec3, int, int, int]) -> list[tuple[int, int]]:
|
|
89
|
-
"""
|
|
90
|
-
Permutes a tuple of three unique numbers (a, b, c) into 3 pairs (x, y), preserving order
|
|
91
|
-
"""
|
|
1
|
+
import glm
|
|
2
|
+
from .helper import get_support_point
|
|
3
|
+
from .dataclasses import SupportPoint
|
|
4
|
+
from...nodes.node import Node
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# TODO change these to structs when converting to C++
|
|
8
|
+
face_type = list[tuple[float, glm.vec3, glm.vec3, int, int, int]] # distance, normal, center, index 1, index 2, index 3
|
|
9
|
+
polytope_type = list[SupportPoint] # polytope vertex, node1 vertex, node2 vertex
|
|
10
|
+
|
|
11
|
+
def get_epa_from_gjk(node1: Node, node2: Node, polytope: polytope_type, epsilon: float=0) -> tuple[face_type, polytope_type]: # TODO determine the return type of get_epa_from_gjk and if epsilon is good value
|
|
12
|
+
"""
|
|
13
|
+
Determines the peneration vector from a collision using EPA. The returned face normal is normalized but the rest are not guarunteed to be.
|
|
14
|
+
"""
|
|
15
|
+
# orient faces to point normals counter clockwise
|
|
16
|
+
faces: face_type = []
|
|
17
|
+
for indices in [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)]: faces = insert_face(polytope, faces, indices)
|
|
18
|
+
|
|
19
|
+
# develope the polytope until the nearest real face has been found, within epsilon
|
|
20
|
+
while True:
|
|
21
|
+
new_point = get_support_point(node1, node2, faces[0][1])
|
|
22
|
+
if new_point in polytope or glm.length(new_point.support_point) - faces[0][0] < epsilon: return faces, polytope
|
|
23
|
+
faces, polytope = insert_point(polytope, faces, new_point)
|
|
24
|
+
|
|
25
|
+
def insert_point(polytope: polytope_type, faces: face_type, point: glm.vec3, epsilon: float=0) -> tuple[face_type, polytope_type]:
|
|
26
|
+
"""
|
|
27
|
+
Inserts a point into the polytope sorting by distance from the origin
|
|
28
|
+
"""
|
|
29
|
+
# determine which faces are facing the new point
|
|
30
|
+
polytope.append(point)
|
|
31
|
+
support_index = len(polytope) - 1
|
|
32
|
+
visible_faces = [
|
|
33
|
+
face for face in faces
|
|
34
|
+
if glm.dot(face[1], polytope[support_index].support_point) >= epsilon and # if the normal of a face is pointing towards the added point
|
|
35
|
+
glm.dot(face[1], polytope[support_index].support_point - face[2]) >= epsilon # TODO check if this ever occurs
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# generate new edges
|
|
39
|
+
edges = []
|
|
40
|
+
for face in visible_faces:
|
|
41
|
+
for p1, p2 in get_face_edges(face):
|
|
42
|
+
if (p2, p1) in edges: edges.remove((p2, p1)) # edges can only be shared by two faces, running opposite to each other.
|
|
43
|
+
elif (p1, p2) in edges: # TODO remove this
|
|
44
|
+
edges.remove((p1, p2))
|
|
45
|
+
# print('not reversed')
|
|
46
|
+
else: edges.append((p1, p2))
|
|
47
|
+
|
|
48
|
+
# remove visible faces
|
|
49
|
+
for face in sorted(visible_faces, reverse = True): faces.remove(face)
|
|
50
|
+
|
|
51
|
+
# add new faces
|
|
52
|
+
new_face_indices = [orient_face(polytope, (edge[0], edge[1], support_index)) for edge in edges] # edge[0], edge[1] is already ccw
|
|
53
|
+
for indices in new_face_indices: faces = insert_face(polytope, faces, indices)
|
|
54
|
+
|
|
55
|
+
return faces, polytope
|
|
56
|
+
|
|
57
|
+
def insert_face(polytope: polytope_type, faces: face_type, indices: tuple[int, int, int]) -> face_type:
|
|
58
|
+
"""
|
|
59
|
+
Inserts a face into the face priority queue based on the indices given in the polytope
|
|
60
|
+
"""
|
|
61
|
+
center = (polytope[indices[0]].support_point + polytope[indices[1]].support_point + polytope[indices[2]].support_point) / 3
|
|
62
|
+
normal = glm.cross(polytope[indices[1]].support_point - polytope[indices[0]].support_point, polytope[indices[2]].support_point - polytope[indices[0]].support_point) # closest face normal will be normalized once returned to avoid square roots and division
|
|
63
|
+
if glm.dot(center, normal) < 0:
|
|
64
|
+
normal *= -1
|
|
65
|
+
indices = (indices[2], indices[1], indices[0])
|
|
66
|
+
|
|
67
|
+
# TODO solve cases where face may contain origin
|
|
68
|
+
normal = glm.normalize(normal)
|
|
69
|
+
distance = abs(glm.dot(polytope[indices[0]].support_point, normal))
|
|
70
|
+
new_face = (distance, normal, center, *indices)
|
|
71
|
+
|
|
72
|
+
# insert faces into priority queue based on distance from origin
|
|
73
|
+
for i, face in enumerate(faces):
|
|
74
|
+
if face[0] < distance: continue
|
|
75
|
+
faces.insert(i, new_face)
|
|
76
|
+
break
|
|
77
|
+
else: faces.append(new_face)
|
|
78
|
+
|
|
79
|
+
return faces
|
|
80
|
+
|
|
81
|
+
def orient_face(polytope: polytope_type, indices: tuple[int, int, int]) -> tuple[int, int, int]:
|
|
82
|
+
"""
|
|
83
|
+
Orients the face indices to have a counter clockwise normal
|
|
84
|
+
"""
|
|
85
|
+
if glm.dot(glm.cross(polytope[indices[1]].support_point, polytope[indices[2]].support_point), polytope[indices[0]].support_point) < 0: return (indices[2], indices[1], indices[0])
|
|
86
|
+
return indices
|
|
87
|
+
|
|
88
|
+
def get_face_edges(face: tuple[float, glm.vec3, glm.vec3, int, int, int]) -> list[tuple[int, int]]:
|
|
89
|
+
"""
|
|
90
|
+
Permutes a tuple of three unique numbers (a, b, c) into 3 pairs (x, y), preserving order
|
|
91
|
+
"""
|
|
92
92
|
return [(face[3], face[4]), (face[4], face[5]), (face[5], face[3])]
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import glm
|
|
2
|
-
from .helper import get_support_point
|
|
3
|
-
from .dataclasses import SupportPoint
|
|
4
|
-
from ...nodes.node import Node
|
|
5
|
-
from ...generic.math import triple_product
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def collide_gjk(node1: Node, node2: Node, iterations: int=20) -> tuple: # TODO figure out return data type
|
|
9
|
-
"""
|
|
10
|
-
Determines if two convex polyhedra collide, returns the polytope if there is a collision.
|
|
11
|
-
"""
|
|
12
|
-
# generate starting values
|
|
13
|
-
dir_vec = node1.position.data - node2.position.data
|
|
14
|
-
simplex = [get_support_point(node1, node2, dir_vec)]
|
|
15
|
-
dir_vec = -simplex[0].support_point # set direction to point away from starting simplex point
|
|
16
|
-
|
|
17
|
-
for _ in range(iterations):
|
|
18
|
-
# gets support point and checks if its across the origin
|
|
19
|
-
test_point = get_support_point(node1, node2, dir_vec)
|
|
20
|
-
if glm.dot(test_point.support_point, dir_vec) < -1e-7: return False, simplex
|
|
21
|
-
|
|
22
|
-
# add point and find new direction vector
|
|
23
|
-
simplex.append(test_point)
|
|
24
|
-
check, dir_vec, simplex = handle_simplex(simplex)
|
|
25
|
-
|
|
26
|
-
if check: return True, simplex
|
|
27
|
-
return False, simplex # timeout due to too many checks, usually float errors
|
|
28
|
-
|
|
29
|
-
def handle_simplex(simplex: list[SupportPoint]) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
30
|
-
"""
|
|
31
|
-
Call proper function based on number of support points
|
|
32
|
-
"""
|
|
33
|
-
num = len(simplex) # not using match case to support Python < 3.10
|
|
34
|
-
if num == 2: return handle_simplex_line(simplex)
|
|
35
|
-
if num == 3: return handle_simplex_triangle(simplex)
|
|
36
|
-
return handle_simplex_tetrahedron(simplex) # simplex must be 4 points
|
|
37
|
-
|
|
38
|
-
def handle_simplex_line(simplex: list[SupportPoint]) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
39
|
-
"""
|
|
40
|
-
Returns the perpendicular vector to the simplex line
|
|
41
|
-
"""
|
|
42
|
-
vec_ab = simplex[1].support_point - simplex[0].support_point
|
|
43
|
-
return False, triple_product(vec_ab, -simplex[0].support_point, vec_ab), simplex
|
|
44
|
-
|
|
45
|
-
def handle_simplex_triangle(simplex: list[SupportPoint]) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
46
|
-
"""
|
|
47
|
-
Returns the normal vector of the triangoe pointed towards the origin
|
|
48
|
-
"""
|
|
49
|
-
dir_vec = glm.cross(simplex[1].support_point - simplex[0].support_point, simplex[2].support_point - simplex[0].support_point)
|
|
50
|
-
return False, -dir_vec if glm.dot(dir_vec, -simplex[0].support_point) < 0 else dir_vec, simplex
|
|
51
|
-
|
|
52
|
-
def handle_simplex_tetrahedron(simplex: list[SupportPoint], epsilon: float=0) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
53
|
-
"""
|
|
54
|
-
Perform collision check and remove support point if no collision is found
|
|
55
|
-
"""
|
|
56
|
-
vec_da = simplex[3].support_point - simplex[0].support_point
|
|
57
|
-
vec_db = simplex[3].support_point - simplex[1].support_point
|
|
58
|
-
vec_dc = simplex[3].support_point - simplex[2].support_point
|
|
59
|
-
vec_do = -simplex[3].support_point
|
|
60
|
-
|
|
61
|
-
vectors = [(glm.cross(vec_da, vec_db), 2), (glm.cross(vec_dc, vec_da), 1), (glm.cross(vec_db, vec_dc), 0)] # TODO determine if this is the best way to do this
|
|
62
|
-
for normal_vec, index in vectors:
|
|
63
|
-
dot_product = glm.dot(normal_vec, vec_do)
|
|
64
|
-
if dot_product > epsilon:
|
|
65
|
-
simplex.pop(index)
|
|
66
|
-
return False, normal_vec, simplex
|
|
1
|
+
import glm
|
|
2
|
+
from .helper import get_support_point
|
|
3
|
+
from .dataclasses import SupportPoint
|
|
4
|
+
from ...nodes.node import Node
|
|
5
|
+
from ...generic.math import triple_product
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def collide_gjk(node1: Node, node2: Node, iterations: int=20) -> tuple: # TODO figure out return data type
|
|
9
|
+
"""
|
|
10
|
+
Determines if two convex polyhedra collide, returns the polytope if there is a collision.
|
|
11
|
+
"""
|
|
12
|
+
# generate starting values
|
|
13
|
+
dir_vec = node1.position.data - node2.position.data
|
|
14
|
+
simplex = [get_support_point(node1, node2, dir_vec)]
|
|
15
|
+
dir_vec = -simplex[0].support_point # set direction to point away from starting simplex point
|
|
16
|
+
|
|
17
|
+
for _ in range(iterations):
|
|
18
|
+
# gets support point and checks if its across the origin
|
|
19
|
+
test_point = get_support_point(node1, node2, dir_vec)
|
|
20
|
+
if glm.dot(test_point.support_point, dir_vec) < -1e-7: return False, simplex
|
|
21
|
+
|
|
22
|
+
# add point and find new direction vector
|
|
23
|
+
simplex.append(test_point)
|
|
24
|
+
check, dir_vec, simplex = handle_simplex(simplex)
|
|
25
|
+
|
|
26
|
+
if check: return True, simplex
|
|
27
|
+
return False, simplex # timeout due to too many checks, usually float errors
|
|
28
|
+
|
|
29
|
+
def handle_simplex(simplex: list[SupportPoint]) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
30
|
+
"""
|
|
31
|
+
Call proper function based on number of support points
|
|
32
|
+
"""
|
|
33
|
+
num = len(simplex) # not using match case to support Python < 3.10
|
|
34
|
+
if num == 2: return handle_simplex_line(simplex)
|
|
35
|
+
if num == 3: return handle_simplex_triangle(simplex)
|
|
36
|
+
return handle_simplex_tetrahedron(simplex) # simplex must be 4 points
|
|
37
|
+
|
|
38
|
+
def handle_simplex_line(simplex: list[SupportPoint]) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
39
|
+
"""
|
|
40
|
+
Returns the perpendicular vector to the simplex line
|
|
41
|
+
"""
|
|
42
|
+
vec_ab = simplex[1].support_point - simplex[0].support_point
|
|
43
|
+
return False, triple_product(vec_ab, -simplex[0].support_point, vec_ab), simplex
|
|
44
|
+
|
|
45
|
+
def handle_simplex_triangle(simplex: list[SupportPoint]) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
46
|
+
"""
|
|
47
|
+
Returns the normal vector of the triangoe pointed towards the origin
|
|
48
|
+
"""
|
|
49
|
+
dir_vec = glm.cross(simplex[1].support_point - simplex[0].support_point, simplex[2].support_point - simplex[0].support_point)
|
|
50
|
+
return False, -dir_vec if glm.dot(dir_vec, -simplex[0].support_point) < 0 else dir_vec, simplex
|
|
51
|
+
|
|
52
|
+
def handle_simplex_tetrahedron(simplex: list[SupportPoint], epsilon: float=0) -> tuple[bool, glm.vec3, list[tuple[glm.vec3, glm.vec3, glm.vec3]]]:
|
|
53
|
+
"""
|
|
54
|
+
Perform collision check and remove support point if no collision is found
|
|
55
|
+
"""
|
|
56
|
+
vec_da = simplex[3].support_point - simplex[0].support_point
|
|
57
|
+
vec_db = simplex[3].support_point - simplex[1].support_point
|
|
58
|
+
vec_dc = simplex[3].support_point - simplex[2].support_point
|
|
59
|
+
vec_do = -simplex[3].support_point
|
|
60
|
+
|
|
61
|
+
vectors = [(glm.cross(vec_da, vec_db), 2), (glm.cross(vec_dc, vec_da), 1), (glm.cross(vec_db, vec_dc), 0)] # TODO determine if this is the best way to do this
|
|
62
|
+
for normal_vec, index in vectors:
|
|
63
|
+
dot_product = glm.dot(normal_vec, vec_do)
|
|
64
|
+
if dot_product > epsilon:
|
|
65
|
+
simplex.pop(index)
|
|
66
|
+
return False, normal_vec, simplex
|
|
67
67
|
return True, None, simplex
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import glm
|
|
2
|
-
from math import atan2
|
|
3
|
-
from .helper import is_ccw_turn
|
|
4
|
-
|
|
5
|
-
def graham_scan(points:list[glm.vec2]) -> None:
|
|
6
|
-
"""converts list of arbitrary points into polygon sorted ccw"""
|
|
7
|
-
# get pivot point
|
|
8
|
-
pivot = min(points, key=lambda p: (p.y, p.x))
|
|
9
|
-
points.remove(pivot)
|
|
10
|
-
|
|
11
|
-
# sort points by polar angle and start hull
|
|
12
|
-
points = sorted(points, key=lambda p: (get_polar_angle(pivot, p), glm.length(pivot - p)))
|
|
13
|
-
hull = [pivot, points.pop(0)]
|
|
14
|
-
|
|
15
|
-
for point in points:
|
|
16
|
-
while len(hull) > 1 and not is_ccw_turn(hull[-2], hull[-1], point):
|
|
17
|
-
hull.pop()
|
|
18
|
-
hull.append(point)
|
|
19
|
-
|
|
20
|
-
return hull
|
|
21
|
-
|
|
22
|
-
def get_polar_angle(pivot:glm.vec2, point:glm.vec2) -> float:
|
|
23
|
-
"""gets the polar angle between two points from the horizontal"""
|
|
24
|
-
vector = point - pivot
|
|
1
|
+
import glm
|
|
2
|
+
from math import atan2
|
|
3
|
+
from .helper import is_ccw_turn
|
|
4
|
+
|
|
5
|
+
def graham_scan(points:list[glm.vec2]) -> None:
|
|
6
|
+
"""converts list of arbitrary points into polygon sorted ccw"""
|
|
7
|
+
# get pivot point
|
|
8
|
+
pivot = min(points, key=lambda p: (p.y, p.x))
|
|
9
|
+
points.remove(pivot)
|
|
10
|
+
|
|
11
|
+
# sort points by polar angle and start hull
|
|
12
|
+
points = sorted(points, key=lambda p: (get_polar_angle(pivot, p), glm.length(pivot - p)))
|
|
13
|
+
hull = [pivot, points.pop(0)]
|
|
14
|
+
|
|
15
|
+
for point in points:
|
|
16
|
+
while len(hull) > 1 and not is_ccw_turn(hull[-2], hull[-1], point):
|
|
17
|
+
hull.pop()
|
|
18
|
+
hull.append(point)
|
|
19
|
+
|
|
20
|
+
return hull
|
|
21
|
+
|
|
22
|
+
def get_polar_angle(pivot:glm.vec2, point:glm.vec2) -> float:
|
|
23
|
+
"""gets the polar angle between two points from the horizontal"""
|
|
24
|
+
vector = point - pivot
|
|
25
25
|
return atan2(vector.y, vector.x)
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import glm
|
|
2
|
-
from ...nodes.node import Node
|
|
3
|
-
from .dataclasses import SupportPoint
|
|
4
|
-
|
|
5
|
-
def get_support_point(node1: Node, node2: Node, dir_vec: glm.vec3) -> SupportPoint:
|
|
6
|
-
"""
|
|
7
|
-
Outputs the best support point to be added to the polytop based on the direction vector.
|
|
8
|
-
"""
|
|
9
|
-
vertex1, index1 = get_furthest_point(node1, dir_vec)
|
|
10
|
-
vertex2, index2 = get_furthest_point(node2, -dir_vec)
|
|
11
|
-
return SupportPoint(vertex1 - vertex2, index1, vertex1, index2, vertex2)
|
|
12
|
-
|
|
13
|
-
def get_furthest_point(node: Node, dir_vec: glm.vec3) -> glm.vec3:
|
|
14
|
-
"""
|
|
15
|
-
Determines the furthest point in a given direction
|
|
16
|
-
"""
|
|
17
|
-
# determine furthest point by using untransformed mesh
|
|
18
|
-
node_dir_vec = node.rotation.data * dir_vec # rotate the world space vector to node space
|
|
19
|
-
index = node.collider.mesh.get_best_dot(node_dir_vec)
|
|
20
|
-
vertex = node.collider.mesh.points[index]
|
|
21
|
-
vertex = node.model_matrix * glm.vec4(vertex, 1.0)
|
|
22
|
-
|
|
23
|
-
# transform point to world space
|
|
24
|
-
return glm.vec3(vertex), index
|
|
25
|
-
|
|
26
|
-
def is_ccw_turn(a:glm.vec2, b:glm.vec2, c:glm.vec2) -> bool:
|
|
27
|
-
"""
|
|
28
|
-
Determines if the series of points results in a left hand turn
|
|
29
|
-
"""
|
|
1
|
+
import glm
|
|
2
|
+
from ...nodes.node import Node
|
|
3
|
+
from .dataclasses import SupportPoint
|
|
4
|
+
|
|
5
|
+
def get_support_point(node1: Node, node2: Node, dir_vec: glm.vec3) -> SupportPoint:
|
|
6
|
+
"""
|
|
7
|
+
Outputs the best support point to be added to the polytop based on the direction vector.
|
|
8
|
+
"""
|
|
9
|
+
vertex1, index1 = get_furthest_point(node1, dir_vec)
|
|
10
|
+
vertex2, index2 = get_furthest_point(node2, -dir_vec)
|
|
11
|
+
return SupportPoint(vertex1 - vertex2, index1, vertex1, index2, vertex2)
|
|
12
|
+
|
|
13
|
+
def get_furthest_point(node: Node, dir_vec: glm.vec3) -> glm.vec3:
|
|
14
|
+
"""
|
|
15
|
+
Determines the furthest point in a given direction
|
|
16
|
+
"""
|
|
17
|
+
# determine furthest point by using untransformed mesh
|
|
18
|
+
node_dir_vec = node.rotation.data * dir_vec # rotate the world space vector to node space
|
|
19
|
+
index = node.collider.mesh.get_best_dot(node_dir_vec)
|
|
20
|
+
vertex = node.collider.mesh.points[index]
|
|
21
|
+
vertex = node.model_matrix * glm.vec4(vertex, 1.0)
|
|
22
|
+
|
|
23
|
+
# transform point to world space
|
|
24
|
+
return glm.vec3(vertex), index
|
|
25
|
+
|
|
26
|
+
def is_ccw_turn(a:glm.vec2, b:glm.vec2, c:glm.vec2) -> bool:
|
|
27
|
+
"""
|
|
28
|
+
Determines if the series of points results in a left hand turn
|
|
29
|
+
"""
|
|
30
30
|
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) > 0 # TODO check formula
|