basilisk-engine 0.0.1__py3-none-any.whl → 0.0.3__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/bsk_assets/__init__.py +0 -0
- basilisk/collisions/__init__.py +0 -0
- basilisk/collisions/broad/__init__.py +0 -0
- basilisk/collisions/broad/broad_aabb.py +96 -0
- basilisk/collisions/broad/broad_bvh.py +102 -0
- basilisk/collisions/collider.py +75 -0
- basilisk/collisions/collider_handler.py +163 -0
- basilisk/collisions/narrow/__init__.py +0 -0
- basilisk/collisions/narrow/epa.py +86 -0
- basilisk/collisions/narrow/gjk.py +66 -0
- basilisk/collisions/narrow/helper.py +23 -0
- basilisk/draw/__init__.py +0 -0
- basilisk/draw/draw.py +101 -0
- basilisk/draw/draw_handler.py +208 -0
- basilisk/draw/font_renderer.py +28 -0
- basilisk/generic/__init__.py +0 -0
- basilisk/generic/abstract_bvh.py +16 -0
- basilisk/generic/collisions.py +26 -0
- basilisk/generic/input_validation.py +28 -0
- basilisk/generic/math.py +7 -0
- basilisk/generic/matrices.py +34 -0
- basilisk/generic/meshes.py +73 -0
- basilisk/generic/quat.py +119 -0
- basilisk/generic/quat_methods.py +8 -0
- basilisk/generic/vec3.py +112 -0
- basilisk/input/__init__.py +0 -0
- basilisk/input/mouse.py +60 -0
- basilisk/mesh/__init__.py +0 -0
- basilisk/mesh/built-in/__init__.py +0 -0
- basilisk/mesh/cube.py +20 -0
- basilisk/mesh/mesh.py +216 -0
- basilisk/mesh/mesh_from_data.py +48 -0
- basilisk/mesh/model.py +272 -0
- basilisk/mesh/narrow_aabb.py +81 -0
- basilisk/mesh/narrow_bvh.py +84 -0
- basilisk/mesh/narrow_primative.py +24 -0
- basilisk/nodes/__init__.py +0 -0
- basilisk/nodes/node.py +508 -0
- basilisk/nodes/node_handler.py +94 -0
- basilisk/physics/__init__.py +0 -0
- basilisk/physics/physics_body.py +36 -0
- basilisk/physics/physics_engine.py +37 -0
- basilisk/render/__init__.py +0 -0
- basilisk/render/batch.py +85 -0
- basilisk/render/camera.py +166 -0
- basilisk/render/chunk.py +85 -0
- basilisk/render/chunk_handler.py +139 -0
- basilisk/render/frame.py +182 -0
- basilisk/render/image.py +76 -0
- basilisk/render/image_handler.py +119 -0
- basilisk/render/light.py +97 -0
- basilisk/render/light_handler.py +54 -0
- basilisk/render/material.py +196 -0
- basilisk/render/material_handler.py +123 -0
- basilisk/render/shader_handler.py +95 -0
- basilisk/render/sky.py +118 -0
- basilisk/shaders/__init__.py +0 -0
- {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/METADATA +1 -1
- basilisk_engine-0.0.3.dist-info/RECORD +65 -0
- basilisk_engine-0.0.1.dist-info/RECORD +0 -8
- {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/top_level.txt +0 -0
basilisk/generic/vec3.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Vec3():
|
|
6
|
+
def __init__(self, *args, callback=None):
|
|
7
|
+
self.callback = callback
|
|
8
|
+
self.set_data(*args)
|
|
9
|
+
|
|
10
|
+
def normalize(self):
|
|
11
|
+
"""
|
|
12
|
+
Inplace normalizes the vector
|
|
13
|
+
"""
|
|
14
|
+
self.data = glm.normalize(self.data)
|
|
15
|
+
|
|
16
|
+
def set_data(self, *args):
|
|
17
|
+
"""
|
|
18
|
+
Sets the internal vector inplace
|
|
19
|
+
"""
|
|
20
|
+
# overload constructor TODO nvernest this, definitely possible
|
|
21
|
+
if len(args) == 1:
|
|
22
|
+
if isinstance(args[0], Vec3):
|
|
23
|
+
self.data = glm.vec3(args[0].data)
|
|
24
|
+
self.callback = args[0].callback
|
|
25
|
+
elif isinstance(args[0], glm.vec3): self.data = glm.vec3(args[0])
|
|
26
|
+
elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
|
|
27
|
+
if len(args[0]) != 3: raise ValueError(f'Vec3: Expected 3 values from incoming vector, got {len(args[0])}')
|
|
28
|
+
self.data = glm.vec3(args[0])
|
|
29
|
+
else: raise ValueError(f'Vec3: Unexpected incoming vector type {args[0]}')
|
|
30
|
+
elif len(args) == 3: self.data = glm.vec3(args[0], args[1], args[2])
|
|
31
|
+
else: raise ValueError(f'Vec3: Expected either 1 vector or 3 numbers, got {len(args)} values')
|
|
32
|
+
|
|
33
|
+
# override _= operators
|
|
34
|
+
def __iadd__(self, other):
|
|
35
|
+
if isinstance(other, glm.vec3): self.data += other
|
|
36
|
+
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
37
|
+
if len(other) != 3: raise ValueError(f'Vec3: Number of added values must be 3, got {len(other)}')
|
|
38
|
+
self.data += other
|
|
39
|
+
elif isinstance(other, Vec3): self.data += other.data
|
|
40
|
+
else: raise ValueError(f'Vec3: Not an accepted type for addition, got {type(other)}')
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
def __isub__(self, other):
|
|
44
|
+
if isinstance(other, glm.vec3): self.data -= other
|
|
45
|
+
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
46
|
+
if len(other) != 3: raise ValueError(f'Vec3: Number of added values must be 3, got {len(other)}')
|
|
47
|
+
self.data -= other
|
|
48
|
+
elif isinstance(other, Vec3): self.data -= other.data
|
|
49
|
+
else: raise ValueError(f'Vec3: Not an accepted type for addition, got {type(other)}')
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def __imul__(self, other):
|
|
53
|
+
# TODO add checks for number types
|
|
54
|
+
self.data *= other
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
def __idiv__(self, other):
|
|
58
|
+
# TODO add checks for number types
|
|
59
|
+
self.data /= other
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
def __ifloordiv__(self, other):
|
|
63
|
+
# TODO add checks for number types
|
|
64
|
+
self.data //= other
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
# override [_] accessor
|
|
68
|
+
def __getitem__(self, index):
|
|
69
|
+
if int(index) != index: raise IndexError(f'Vec3: index must be an int, got {type(index)}') # check if index is a float
|
|
70
|
+
if index < 0 or index > 2: raise IndexError(f'Vec3: index out of bounds, got {index}')
|
|
71
|
+
return self.data[index]
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, index, value):
|
|
74
|
+
if int(index) != index: raise IndexError(f'Vec3: index must be an int, got {type(index)}') # check if index is a float
|
|
75
|
+
if index < 0 or index > 2: raise IndexError(f'Vec3: index out of bounds, got {index}')
|
|
76
|
+
try: self.data[index] = value
|
|
77
|
+
except: raise ValueError(f'Vec3: Invalid element type, got {type(value)}')
|
|
78
|
+
|
|
79
|
+
def __repr__(self):
|
|
80
|
+
return str(self.data)
|
|
81
|
+
|
|
82
|
+
def __iter__(self):
|
|
83
|
+
return iter((self.x, self.y, self.z))
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def data(self): return self._data
|
|
87
|
+
@property
|
|
88
|
+
def x(self): return self.data.x
|
|
89
|
+
@property
|
|
90
|
+
def y(self): return self.data.y
|
|
91
|
+
@property
|
|
92
|
+
def z(self): return self.data.z
|
|
93
|
+
|
|
94
|
+
@data.setter
|
|
95
|
+
def data(self, value: glm.vec3):
|
|
96
|
+
self._data = value
|
|
97
|
+
if self.callback and all(abs(self.data[i] - value[i]) > 1e-12 for i in range(3)): self.callback()
|
|
98
|
+
|
|
99
|
+
@x.setter
|
|
100
|
+
def x(self, value):
|
|
101
|
+
self.data.x = value
|
|
102
|
+
if self.callback: self.callback()
|
|
103
|
+
|
|
104
|
+
@y.setter
|
|
105
|
+
def y(self, value):
|
|
106
|
+
self.data.y = value
|
|
107
|
+
if self.callback: self.callback()
|
|
108
|
+
|
|
109
|
+
@z.setter
|
|
110
|
+
def z(self, value):
|
|
111
|
+
self.data.z = value
|
|
112
|
+
if self.callback: self.callback()
|
|
File without changes
|
basilisk/input/mouse.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import pygame as pg
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Mouse():
|
|
5
|
+
def __init__(self, grab=True):
|
|
6
|
+
self.x, self.y = pg.mouse.get_pos()
|
|
7
|
+
self.buttons = pg.mouse.get_pressed()
|
|
8
|
+
self.previous_buttons = pg.mouse.get_pressed()
|
|
9
|
+
self.grab = grab
|
|
10
|
+
|
|
11
|
+
def update(self, events):
|
|
12
|
+
"""
|
|
13
|
+
Updates all mouse state variables.
|
|
14
|
+
Checks for mouse-related events.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
self.x, self.y = pg.mouse.get_pos()
|
|
18
|
+
self.previous_buttons = self.buttons
|
|
19
|
+
self.buttons = pg.mouse.get_pressed()
|
|
20
|
+
|
|
21
|
+
for event in events:
|
|
22
|
+
if event.type == pg.KEYUP:
|
|
23
|
+
if event.key == pg.K_ESCAPE and self.grab:
|
|
24
|
+
# Unlock mouse
|
|
25
|
+
pg.event.set_grab(False)
|
|
26
|
+
pg.mouse.set_visible(True)
|
|
27
|
+
if event.type == pg.MOUSEBUTTONUP and self.grab:
|
|
28
|
+
# Lock mouse
|
|
29
|
+
pg.event.set_grab(True)
|
|
30
|
+
pg.mouse.set_visible(False)
|
|
31
|
+
|
|
32
|
+
def set_pos(self, x, y):
|
|
33
|
+
"""Set the mouse position"""
|
|
34
|
+
pg.mouse.set_pos(x, y)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def click(self): return self.previous_buttons[0] and not self.buttons[0]
|
|
38
|
+
@property
|
|
39
|
+
def middle_click(self): return self.previous_buttons[1] and not self.buttons[1]
|
|
40
|
+
@property
|
|
41
|
+
def right_click(self): return self.previous_buttons[2] and not self.buttons[2]
|
|
42
|
+
@property
|
|
43
|
+
def left_down(self): return self.buttons[0]
|
|
44
|
+
@property
|
|
45
|
+
def middle_down(self): return self.buttons[1]
|
|
46
|
+
@property
|
|
47
|
+
def right_down(self): return self.buttons[2]
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def grab(self): return self._grab
|
|
51
|
+
|
|
52
|
+
@grab.setter
|
|
53
|
+
def grab(self, value):
|
|
54
|
+
self._grab = value
|
|
55
|
+
if self._grab:
|
|
56
|
+
pg.event.set_grab(True)
|
|
57
|
+
pg.mouse.set_visible(False)
|
|
58
|
+
else:
|
|
59
|
+
pg.event.set_grab(False)
|
|
60
|
+
pg.mouse.set_visible(True)
|
|
File without changes
|
|
File without changes
|
basilisk/mesh/cube.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
import os
|
|
3
|
+
from .mesh import Mesh
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Cube(Mesh):
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
# built-in cube mesh with custom functions
|
|
9
|
+
dire = os.path.dirname(__file__)
|
|
10
|
+
path = os.path.join(dire, 'built-in', 'cube.obj')
|
|
11
|
+
super().__init__(path)
|
|
12
|
+
|
|
13
|
+
def get_best_dot(self, vec: glm.vec3) -> glm.vec3:
|
|
14
|
+
"""
|
|
15
|
+
Gets the best dot point of a cube
|
|
16
|
+
"""
|
|
17
|
+
return glm.vec3([-1 if v < 0 else 1 for v in vec])
|
|
18
|
+
|
|
19
|
+
# create instance of cube mesh to be used by both the user and the package. Needs to be the same cube object for internal comparisons. Do not allow the user to access the Cube class to prevent them from making other Cube objects.
|
|
20
|
+
cube = Cube()
|
basilisk/mesh/mesh.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import glm
|
|
3
|
+
import os
|
|
4
|
+
# from pyobjloader import load_model
|
|
5
|
+
from .model import load_model
|
|
6
|
+
from .narrow_bvh import NarrowBVH
|
|
7
|
+
from ..generic.matrices import compute_inertia_moment, compute_inertia_product
|
|
8
|
+
from ..generic.meshes import get_extreme_points_np, moller_trumbore
|
|
9
|
+
from .mesh_from_data import from_data
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Mesh():
|
|
13
|
+
data: np.ndarray
|
|
14
|
+
"""The mesh vertex data stored as a 4-byte float numpy array. Format will be [position.xyz, uv.xy, normal.xyz, tangent.xyz, bitangent.xyz]"""
|
|
15
|
+
points: np.ndarray
|
|
16
|
+
"""All the unique points of the mesh given by the model file"""
|
|
17
|
+
indices: np.ndarray
|
|
18
|
+
"""Indices of the triangles corresponding to the points array"""
|
|
19
|
+
bvh: any
|
|
20
|
+
"""Data structure allowing the access of closest points more efficiently"""
|
|
21
|
+
volume: float
|
|
22
|
+
"""The volume of the unscaled mesh"""
|
|
23
|
+
geometric_center: glm.vec3
|
|
24
|
+
"""The geometric center of the mesh"""
|
|
25
|
+
center_of_mass: glm.vec3
|
|
26
|
+
"""The center of mass of the mesh calculated from the inertia tensor algorithm"""
|
|
27
|
+
half_dimensions: glm.vec3
|
|
28
|
+
"""The aligned half dimensions to the untransformed mesh"""
|
|
29
|
+
bvh: NarrowBVH
|
|
30
|
+
"""BVH for accessing triangle intersections with a line"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, data: str | os.PathLike | np.ndarray) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Mesh object containing all the data needed to render an object and perform physics/collisions on it
|
|
35
|
+
Args:
|
|
36
|
+
data: str
|
|
37
|
+
path to the .obj file of the model or an array of the mesh data
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Verify the path type
|
|
41
|
+
if isinstance(data, str) or isinstance(data, os.PathLike): # Load the model from file
|
|
42
|
+
model = load_model(data, calculate_tangents=True)
|
|
43
|
+
elif isinstance(data, np.ndarray): # Load the model from array of data
|
|
44
|
+
model = from_data(data)
|
|
45
|
+
else: # Invalid data type
|
|
46
|
+
raise TypeError(f'Invalid path type: {type(data)}. Expected a string or os.path')
|
|
47
|
+
|
|
48
|
+
# Get the vertex data
|
|
49
|
+
if len(model.vertex_data[0]) == 8:
|
|
50
|
+
self.data = model.vertex_data.copy()
|
|
51
|
+
else:
|
|
52
|
+
self.data = np.zeros(shape=(len(model.vertex_data), 8))
|
|
53
|
+
self.data[:,:3] = model.vertex_data[:,:3]
|
|
54
|
+
self.data[:,5:] = model.vertex_data[:,3:]
|
|
55
|
+
|
|
56
|
+
# Get tangent data
|
|
57
|
+
if len(model.tangent_data[0]) == 6:
|
|
58
|
+
self.data = np.hstack([self.data, model.tangent_data])
|
|
59
|
+
else:
|
|
60
|
+
tangents = np.zeros(shape=(len(self.data), 6))
|
|
61
|
+
tangents[:,:] += [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
|
|
62
|
+
self.data = np.hstack([self.data, tangents])
|
|
63
|
+
|
|
64
|
+
# Mesh points and triangles used for physics/collisions
|
|
65
|
+
self.points = model.vertex_points.copy()
|
|
66
|
+
self.indices = model.point_indices.copy()
|
|
67
|
+
|
|
68
|
+
# Model will no longer be used
|
|
69
|
+
del model
|
|
70
|
+
|
|
71
|
+
# generate geometric data
|
|
72
|
+
maximum, minimum = get_extreme_points_np(self.points)
|
|
73
|
+
self.geometric_center = (glm.vec3(maximum) + glm.vec3(minimum)) / 2
|
|
74
|
+
self.half_dimensions = maximum - self.geometric_center
|
|
75
|
+
|
|
76
|
+
# volume and center of mass
|
|
77
|
+
self.volume = 0
|
|
78
|
+
self.center_of_mass = glm.vec3(0.0)
|
|
79
|
+
for triangle in self.indices:
|
|
80
|
+
pts = [glm.vec3(self.points[t]) for t in triangle]
|
|
81
|
+
det_j = glm.dot(pts[0], glm.cross(pts[1], pts[2]))
|
|
82
|
+
tet_volume = det_j / 6
|
|
83
|
+
self.volume += tet_volume
|
|
84
|
+
self.center_of_mass += tet_volume * (pts[0] + pts[1] + pts[2]) / 4
|
|
85
|
+
self.center_of_mass /= self.volume
|
|
86
|
+
|
|
87
|
+
# data structrues
|
|
88
|
+
self.bvh = NarrowBVH(self)
|
|
89
|
+
|
|
90
|
+
def get_inertia_tensor(self, scale: glm.vec3) -> glm.mat3x3:
|
|
91
|
+
"""
|
|
92
|
+
Gets the inertia tensor of the mesh with the given scale and mass 1
|
|
93
|
+
"""
|
|
94
|
+
# scale variables
|
|
95
|
+
center_of_mass = self.center_of_mass * scale
|
|
96
|
+
volume = self.volume * scale.x * scale.y * scale.z
|
|
97
|
+
|
|
98
|
+
# uses density = 1 to calculate variables, should be the same for mass = 1 since they are only spatial variables
|
|
99
|
+
points = self.points.copy()
|
|
100
|
+
points[:, 0] *= scale.x
|
|
101
|
+
points[:, 1] *= scale.y
|
|
102
|
+
points[:, 2] *= scale.z
|
|
103
|
+
|
|
104
|
+
ia = ib = ic = iap = ibp = icp = 0
|
|
105
|
+
for triangle in self.indices:
|
|
106
|
+
pts = [points[t] for t in triangle]
|
|
107
|
+
det_j = glm.dot(pts[0], glm.cross(pts[1], pts[2]))
|
|
108
|
+
|
|
109
|
+
ia += det_j * (compute_inertia_moment(pts, 1) + compute_inertia_moment(pts, 2))
|
|
110
|
+
ib += det_j * (compute_inertia_moment(pts, 0) + compute_inertia_moment(pts, 2))
|
|
111
|
+
ic += det_j * (compute_inertia_moment(pts, 0) + compute_inertia_moment(pts, 1))
|
|
112
|
+
iap += det_j * compute_inertia_product(pts, 1, 2)
|
|
113
|
+
ibp += det_j * compute_inertia_product(pts, 0, 1)
|
|
114
|
+
icp += det_j * compute_inertia_product(pts, 0, 2)
|
|
115
|
+
|
|
116
|
+
# since tensor was calc with density = 1. we say mass = density / volume = 1 / volume
|
|
117
|
+
ia = ia / volume / 60 - volume * (center_of_mass[1] ** 2 + center_of_mass[2] ** 2)
|
|
118
|
+
ib = ib / volume / 60 - volume * (center_of_mass[0] ** 2 + center_of_mass[2] ** 2)
|
|
119
|
+
ic = ic / volume / 60 - volume * (center_of_mass[0] ** 2 + center_of_mass[1] ** 2)
|
|
120
|
+
iap = iap / volume / 120 - volume * center_of_mass[1] * center_of_mass[2]
|
|
121
|
+
ibp = ibp / volume / 120 - volume * center_of_mass[0] * center_of_mass[1]
|
|
122
|
+
icp = icp / volume / 120 - volume * center_of_mass[0] * center_of_mass[2]
|
|
123
|
+
|
|
124
|
+
return glm.mat3x3(
|
|
125
|
+
ia, -ibp, -icp,
|
|
126
|
+
-ibp, ib, -iap,
|
|
127
|
+
-icp, -iap, ic
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def get_best_triangle(self, point: glm.vec3, vec: glm.vec3) -> int:
|
|
131
|
+
"""
|
|
132
|
+
Gets the triangle with the closest intersection, -1 if no intersection is found
|
|
133
|
+
"""
|
|
134
|
+
indices = self.bvh.get_possible_triangles(point, vec)
|
|
135
|
+
best_distance = -1
|
|
136
|
+
best_index = -1
|
|
137
|
+
|
|
138
|
+
point = glm.vec3(point)
|
|
139
|
+
vec = glm.vec3(vec)
|
|
140
|
+
|
|
141
|
+
for triangle in indices:
|
|
142
|
+
|
|
143
|
+
# check if triangle intersects
|
|
144
|
+
intersection = moller_trumbore(point, vec, [self.points[t] for t in self.indices[triangle]])
|
|
145
|
+
if not intersection: continue
|
|
146
|
+
|
|
147
|
+
# check if triangle is on correct side of line
|
|
148
|
+
difference = intersection - self.geometric_center
|
|
149
|
+
if glm.dot(difference, vec) < 0: continue
|
|
150
|
+
|
|
151
|
+
# determine best distance
|
|
152
|
+
distance = glm.length(difference)
|
|
153
|
+
if best_distance < 0 or distance < best_distance:
|
|
154
|
+
best_distance = distance
|
|
155
|
+
best_index = triangle
|
|
156
|
+
|
|
157
|
+
return best_index
|
|
158
|
+
|
|
159
|
+
def get_best_triangle_brute(self, point: glm.vec3, vec: glm.vec3) -> int:
|
|
160
|
+
"""
|
|
161
|
+
Gets the triangle with the closest intersection, -1 if no intersection is found. Uses a brute force method
|
|
162
|
+
"""
|
|
163
|
+
best_distance = -1
|
|
164
|
+
best_index = -1
|
|
165
|
+
|
|
166
|
+
point = glm.vec3(point)
|
|
167
|
+
vec = glm.vec3(vec)
|
|
168
|
+
|
|
169
|
+
for index, triangle in enumerate(self.indices):
|
|
170
|
+
|
|
171
|
+
# check if triangle intersects
|
|
172
|
+
intersection = moller_trumbore(point, vec, [self.points[t] for t in triangle])
|
|
173
|
+
if not intersection: continue
|
|
174
|
+
|
|
175
|
+
# check if triangle is on correct side of line
|
|
176
|
+
difference = intersection - self.geometric_center
|
|
177
|
+
if glm.dot(difference, vec) < 0: continue
|
|
178
|
+
|
|
179
|
+
# determine best distance
|
|
180
|
+
distance = glm.length(difference)
|
|
181
|
+
if best_distance < 0 or distance < best_distance:
|
|
182
|
+
best_distance = distance
|
|
183
|
+
best_index = index
|
|
184
|
+
|
|
185
|
+
return best_index
|
|
186
|
+
|
|
187
|
+
def get_best_dot(self, vec: glm.vec3) -> glm.vec3:
|
|
188
|
+
"""
|
|
189
|
+
Gets the point with the highest normalized dot product to the given vector
|
|
190
|
+
"""
|
|
191
|
+
triangle = self.bvh.get_best_dot(vec)
|
|
192
|
+
if triangle == -1: return None
|
|
193
|
+
index = max(self.indices[triangle], key=lambda t: glm.dot(glm.normalize(self.points[t]), vec))
|
|
194
|
+
return glm.vec3(self.points[index])
|
|
195
|
+
|
|
196
|
+
def get_best_dot_old(self, vec):
|
|
197
|
+
best_dot = -1e10
|
|
198
|
+
best = None
|
|
199
|
+
for point in self.points:
|
|
200
|
+
dot = glm.dot(glm.normalize(point), vec)
|
|
201
|
+
if dot > best_dot: best_dot, best = dot, glm.vec3(point)
|
|
202
|
+
return best
|
|
203
|
+
|
|
204
|
+
def __repr__(self) -> str:
|
|
205
|
+
size = (self.data.nbytes + self.points.nbytes + self.indices.nbytes) / 1024 / 1024
|
|
206
|
+
return f'<Basilisk Mesh | {len(self.data)} vertices, {size:.2} mb>'
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def top_right(self): return self.bvh.root.top_right
|
|
210
|
+
@property
|
|
211
|
+
def bottom_left(self): return self.bvh.root.bottom_left
|
|
212
|
+
@property
|
|
213
|
+
def aabb_points(self):
|
|
214
|
+
x1, y1, z1 = self.top_right
|
|
215
|
+
x2, y2, z2 = self.bottom_left
|
|
216
|
+
return [glm.vec3(x, y, z) for z in (z1, z2) for y in (y1, y2) for x in (x1, x2)]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def from_data(data: np.ndarray) -> np.ndarray:
|
|
5
|
+
"""
|
|
6
|
+
Converts data given to a format compatable with basilisk models
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
shape = data.shape
|
|
10
|
+
|
|
11
|
+
if shape[1] == 3: # Just given position
|
|
12
|
+
pos_norm_data = get_normals(data)
|
|
13
|
+
print(pos_norm_data.shape)
|
|
14
|
+
data = np.zeros(shape=(len(data), 14))
|
|
15
|
+
data[:,:6] = pos_norm_data
|
|
16
|
+
return data
|
|
17
|
+
|
|
18
|
+
elif shape[1] == 6: # Given position and normals, but no UV
|
|
19
|
+
pos_norm_data = data
|
|
20
|
+
data = np.zeros(shape=(len(data), 14))
|
|
21
|
+
data[:][:6] = pos_norm_data
|
|
22
|
+
return data
|
|
23
|
+
|
|
24
|
+
elif shape[1] == 8: # Given position, normals and UV
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
elif shape[1] == 14: #Given position, normals, UV, bitangents, and tangents, no change needed
|
|
28
|
+
return data
|
|
29
|
+
|
|
30
|
+
raise ValueError(f"Could not find valid format for the given model data of shape {shape}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_normals(positions: np.ndarray) -> np.ndarray:
|
|
34
|
+
"""
|
|
35
|
+
Gets the normals for a position array and returns a concatinated array
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# Create empty array for the normals
|
|
39
|
+
normals = np.zeros(shape=positions.shape)
|
|
40
|
+
|
|
41
|
+
# Loop through each triangle and calculate the normal of the surface
|
|
42
|
+
for tri in range(positions.shape[0] // 3):
|
|
43
|
+
normal = np.cross(positions[tri] - positions[tri + 1], positions[tri] - positions[tri + 2])
|
|
44
|
+
normals[tri ] = normal
|
|
45
|
+
normals[tri + 1] = normal
|
|
46
|
+
normals[tri + 2] = normal
|
|
47
|
+
|
|
48
|
+
return np.hstack([positions, normals])
|