basilisk-engine 0.0.9__py3-none-any.whl → 0.1.1__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 +3 -1
- basilisk/collisions/broad/broad_aabb.py +8 -1
- basilisk/collisions/broad/broad_bvh.py +38 -2
- basilisk/collisions/collider.py +20 -10
- basilisk/collisions/collider_handler.py +97 -32
- basilisk/collisions/narrow/contact_manifold.py +91 -0
- basilisk/collisions/narrow/dataclasses.py +27 -0
- basilisk/collisions/narrow/deprecated.py +47 -0
- basilisk/collisions/narrow/epa.py +21 -15
- basilisk/collisions/narrow/gjk.py +15 -14
- basilisk/collisions/narrow/graham_scan.py +25 -0
- basilisk/collisions/narrow/helper.py +14 -7
- basilisk/collisions/narrow/line_intersections.py +107 -0
- basilisk/collisions/narrow/sutherland_hodgman.py +76 -0
- basilisk/draw/draw_handler.py +7 -5
- basilisk/engine.py +28 -6
- basilisk/generic/abstract_custom.py +134 -0
- basilisk/generic/collisions.py +47 -2
- basilisk/generic/quat.py +84 -65
- basilisk/generic/vec3.py +99 -67
- basilisk/input/mouse.py +3 -3
- basilisk/mesh/cube.py +20 -2
- basilisk/mesh/mesh.py +69 -54
- basilisk/mesh/mesh_from_data.py +106 -21
- basilisk/mesh/narrow_aabb.py +10 -1
- basilisk/mesh/narrow_bvh.py +9 -1
- basilisk/nodes/node.py +211 -101
- basilisk/nodes/node_handler.py +58 -33
- basilisk/particles/__init__.py +0 -0
- basilisk/particles/particle_handler.py +55 -0
- basilisk/particles/particle_renderer.py +87 -0
- basilisk/physics/impulse.py +113 -0
- basilisk/physics/physics_body.py +10 -2
- basilisk/physics/physics_engine.py +2 -3
- basilisk/render/batch.py +3 -1
- basilisk/render/camera.py +35 -1
- basilisk/render/chunk.py +19 -4
- basilisk/render/chunk_handler.py +39 -23
- basilisk/render/image.py +1 -1
- basilisk/render/image_handler.py +17 -14
- basilisk/render/light_handler.py +16 -11
- basilisk/render/material.py +38 -14
- basilisk/render/material_handler.py +31 -18
- basilisk/render/shader.py +110 -0
- basilisk/render/shader_handler.py +20 -35
- basilisk/render/sky.py +8 -5
- basilisk/scene.py +116 -33
- basilisk/shaders/batch.frag +40 -11
- basilisk/shaders/batch.vert +14 -7
- basilisk/shaders/geometry.frag +9 -0
- basilisk/shaders/geometry.vert +42 -0
- basilisk/shaders/normal.frag +60 -0
- basilisk/shaders/normal.vert +97 -0
- basilisk/shaders/particle.frag +72 -0
- basilisk/shaders/particle.vert +85 -0
- {basilisk_engine-0.0.9.dist-info → basilisk_engine-0.1.1.dist-info}/METADATA +5 -5
- basilisk_engine-0.1.1.dist-info/RECORD +95 -0
- basilisk/shaders/image.png +0 -0
- basilisk_engine-0.0.9.dist-info/RECORD +0 -78
- {basilisk_engine-0.0.9.dist-info → basilisk_engine-0.1.1.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.0.9.dist-info → basilisk_engine-0.1.1.dist-info}/top_level.txt +0 -0
basilisk/render/material.py
CHANGED
|
@@ -41,7 +41,7 @@ class Material():
|
|
|
41
41
|
clearcoat_gloss: float
|
|
42
42
|
"""The glossiness of the clearcoat layer. 0 For a satin appearance, 1 for a gloss appearance"""
|
|
43
43
|
|
|
44
|
-
def __init__(self, name: str=None, color: tuple=(255.0, 255.0, 255.0), texture: Image=None, normal: Image=None,
|
|
44
|
+
def __init__(self, name: str=None, color: tuple=(255.0, 255.0, 255.0), texture: Image=None, normal: Image=None, roughness_map: Image=None, ao_map: Image=None,
|
|
45
45
|
roughness: float=0.7, subsurface: float=0.2, sheen: float=0.5, sheen_tint: float=0.5,
|
|
46
46
|
anisotropic: float=0.0, specular: float=1.0, metallicness: float=0.0, specular_tint: float=0.0,
|
|
47
47
|
clearcoat: float=0.5, clearcoat_gloss: float=0.25) -> None:
|
|
@@ -67,6 +67,8 @@ class Material():
|
|
|
67
67
|
self.color = color
|
|
68
68
|
self.texture = texture
|
|
69
69
|
self.normal = normal
|
|
70
|
+
self.roughness_map = roughness_map
|
|
71
|
+
self.ao_map = ao_map
|
|
70
72
|
self.roughness = roughness
|
|
71
73
|
self.subsurface = subsurface
|
|
72
74
|
self.sheen = sheen
|
|
@@ -96,6 +98,14 @@ class Material():
|
|
|
96
98
|
if self.normal: data.extend([1, self.normal.index.x, self.normal.index.y])
|
|
97
99
|
else: data.extend([0, 0, 0])
|
|
98
100
|
|
|
101
|
+
# Add roughness data
|
|
102
|
+
if self.roughness_map: data.extend([1, self.roughness_map.index.x, self.roughness_map.index.y])
|
|
103
|
+
else: data.extend([0, 0, 0])
|
|
104
|
+
|
|
105
|
+
# Add ao data
|
|
106
|
+
if self.ao_map: data.extend([1, self.ao_map.index.x, self.ao_map.index.y])
|
|
107
|
+
else: data.extend([0, 0, 0])
|
|
108
|
+
|
|
99
109
|
return data
|
|
100
110
|
|
|
101
111
|
def __repr__(self) -> str:
|
|
@@ -109,6 +119,10 @@ class Material():
|
|
|
109
119
|
@property
|
|
110
120
|
def normal(self): return self._normal
|
|
111
121
|
@property
|
|
122
|
+
def roughness_map(self): return self._roughness_map
|
|
123
|
+
@property
|
|
124
|
+
def ao_map(self): return self._ao_map
|
|
125
|
+
@property
|
|
112
126
|
def roughness(self): return self._roughness
|
|
113
127
|
@property
|
|
114
128
|
def subsurface(self): return self._subsurface
|
|
@@ -133,64 +147,74 @@ class Material():
|
|
|
133
147
|
@color.setter
|
|
134
148
|
def color(self, value: tuple | list | glm.vec3 | np.ndarray):
|
|
135
149
|
self._color = validate_glm_vec3("Material", "color", value)
|
|
136
|
-
if self.material_handler: self.material_handler.write()
|
|
150
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
137
151
|
|
|
138
152
|
@texture.setter
|
|
139
153
|
def texture(self, value: Image | None):
|
|
140
154
|
self._texture = validate_image("Material", "texture", value)
|
|
141
|
-
if self.material_handler: self.material_handler.write()
|
|
155
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
142
156
|
|
|
143
157
|
@normal.setter
|
|
144
158
|
def normal(self, value: Image | None):
|
|
145
159
|
self._normal = validate_image("Material", "normal map", value)
|
|
146
|
-
if self.material_handler: self.material_handler.write()
|
|
160
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
161
|
+
|
|
162
|
+
@roughness_map.setter
|
|
163
|
+
def roughness_map(self, value: Image | None):
|
|
164
|
+
self._roughness_map = validate_image("Material", "roughness_map", value)
|
|
165
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
166
|
+
|
|
167
|
+
@ao_map.setter
|
|
168
|
+
def ao_map(self, value: Image | None):
|
|
169
|
+
self._ao_map = validate_image("Material", "ao_map map", value)
|
|
170
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
147
171
|
|
|
148
172
|
@roughness.setter
|
|
149
173
|
def roughness(self, value: float | int | glm.float32):
|
|
150
174
|
self._roughness = validate_float("Material", "roughness", value)
|
|
151
|
-
if self.material_handler: self.material_handler.write()
|
|
175
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
152
176
|
|
|
153
177
|
@subsurface.setter
|
|
154
178
|
def subsurface(self, value: float | int | glm.float32):
|
|
155
179
|
self._subsurface = validate_float("Material", "subsurface", value)
|
|
156
|
-
if self.material_handler: self.material_handler.write()
|
|
180
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
157
181
|
|
|
158
182
|
@sheen.setter
|
|
159
183
|
def sheen(self, value: float | int | glm.float32):
|
|
160
184
|
self._sheen = validate_float("Material", "sheen", value)
|
|
161
|
-
if self.material_handler: self.material_handler.write()
|
|
185
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
162
186
|
|
|
163
187
|
@sheen_tint.setter
|
|
164
188
|
def sheen_tint(self, value: float | int | glm.float32):
|
|
165
189
|
self._sheen_tint = validate_float("Material", "sheen tint", value)
|
|
166
|
-
if self.material_handler: self.material_handler.write()
|
|
190
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
167
191
|
|
|
168
192
|
@anisotropic.setter
|
|
169
193
|
def anisotropic(self, value: float | int | glm.float32):
|
|
170
194
|
self._anisotropic = validate_float("Material", "anisotropic", value)
|
|
171
|
-
if self.material_handler: self.material_handler.write()
|
|
195
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
172
196
|
|
|
173
197
|
@specular.setter
|
|
174
198
|
def specular(self, value: float | int | glm.float32):
|
|
175
199
|
self._specular = validate_float("Material", "specular", value)
|
|
176
|
-
if self.material_handler: self.material_handler.write()
|
|
200
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
177
201
|
|
|
178
202
|
@metallicness.setter
|
|
179
203
|
def metallicness(self, value: float | int | glm.float32):
|
|
180
204
|
self._metallicness = validate_float("Material", "metallicness", value)
|
|
181
|
-
if self.material_handler: self.material_handler.write()
|
|
205
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
182
206
|
|
|
183
207
|
@specular_tint.setter
|
|
184
208
|
def specular_tint(self, value: float | int | glm.float32):
|
|
185
209
|
self._specular_tint = validate_float("Material", "specular tint", value)
|
|
186
|
-
if self.material_handler: self.material_handler.write()
|
|
210
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
187
211
|
|
|
188
212
|
@clearcoat.setter
|
|
189
213
|
def clearcoat(self, value: float | int | glm.float32):
|
|
190
214
|
self._clearcoat = validate_float("Material", "clearcoat", value)
|
|
191
|
-
if self.material_handler: self.material_handler.write()
|
|
215
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
192
216
|
|
|
193
217
|
@clearcoat_gloss.setter
|
|
194
218
|
def clearcoat_gloss(self, value: float | int | glm.float32):
|
|
195
219
|
self._clearcoat_gloss = validate_float("Material", "clearcoat gloss", value)
|
|
196
|
-
if self.material_handler: self.material_handler.write()
|
|
220
|
+
if self.material_handler: self.material_handler.write(regenerate=True)
|
|
@@ -39,18 +39,27 @@ class MaterialHandler():
|
|
|
39
39
|
Adds the given material to the handler if it is not already present
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
write = False
|
|
43
|
+
|
|
44
|
+
if isinstance(material, Material): material = [material]
|
|
45
|
+
|
|
46
|
+
for mtl in material:
|
|
47
|
+
# Check that the material is not already in the scene
|
|
48
|
+
if mtl in self.materials: continue
|
|
49
|
+
# Update the material's handler
|
|
50
|
+
mtl.material_handler = self
|
|
51
|
+
# Add images
|
|
52
|
+
if mtl.texture: self.image_handler.add(mtl.texture)
|
|
53
|
+
if mtl.normal: self.image_handler.add(mtl.normal)
|
|
54
|
+
|
|
55
|
+
# Add the material
|
|
56
|
+
self.materials.append(mtl)
|
|
57
|
+
|
|
58
|
+
write = True
|
|
59
|
+
|
|
60
|
+
|
|
52
61
|
# Write materials
|
|
53
|
-
self.write()
|
|
62
|
+
if write: self.write(regenerate=True)
|
|
54
63
|
|
|
55
64
|
def generate_material_texture(self) -> None:
|
|
56
65
|
"""
|
|
@@ -64,7 +73,7 @@ class MaterialHandler():
|
|
|
64
73
|
if self.data_texture: self.data_texture.release()
|
|
65
74
|
|
|
66
75
|
# Create empty texture data
|
|
67
|
-
material_data = np.zeros(shape=(len(self.materials),
|
|
76
|
+
material_data = np.zeros(shape=(len(self.materials), 25), dtype="f4")
|
|
68
77
|
|
|
69
78
|
# Get data from the materials
|
|
70
79
|
for i, mtl in enumerate(self.materials):
|
|
@@ -75,17 +84,20 @@ class MaterialHandler():
|
|
|
75
84
|
material_data = np.ravel(material_data)
|
|
76
85
|
self.data_texture = self.ctx.texture((1, len(material_data)), components=1, dtype='f4', data=material_data)
|
|
77
86
|
|
|
78
|
-
def write(self,
|
|
87
|
+
def write(self, regenerate=False) -> None:
|
|
79
88
|
"""
|
|
80
|
-
Writes all material data to
|
|
89
|
+
Writes all material data to relavent shaders
|
|
81
90
|
"""
|
|
82
91
|
|
|
83
|
-
if
|
|
92
|
+
if regenerate: self.generate_material_texture()
|
|
84
93
|
|
|
85
|
-
self.
|
|
94
|
+
if not self.data_texture: return
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
for shader in self.engine.scene.shader_handler.shaders:
|
|
97
|
+
if 'materialsTexture' not in shader.uniforms: continue
|
|
98
|
+
|
|
99
|
+
shader.program['materialsTexture'] = 9
|
|
100
|
+
self.data_texture.use(location=9)
|
|
89
101
|
|
|
90
102
|
def get(self, identifier: str | int) -> any:
|
|
91
103
|
"""
|
|
@@ -113,6 +125,7 @@ class MaterialHandler():
|
|
|
113
125
|
|
|
114
126
|
self.base = Material('Base')
|
|
115
127
|
self.materials.append(self.base)
|
|
128
|
+
self.generate_material_texture()
|
|
116
129
|
self.write()
|
|
117
130
|
|
|
118
131
|
def __del__(self) -> None:
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import moderngl as mgl
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
attribute_mappings = {
|
|
5
|
+
'in_position' : [0, 1, 2],
|
|
6
|
+
'in_uv' : [3, 4],
|
|
7
|
+
'in_normal' : [5, 6, 7],
|
|
8
|
+
'in_tangent' : [8, 9, 10],
|
|
9
|
+
'in_bitangent' : [11, 12, 13],
|
|
10
|
+
'obj_position' : [14, 15, 16],
|
|
11
|
+
'obj_rotation' : [17, 18, 19, 20],
|
|
12
|
+
'obj_scale' : [21, 22, 23],
|
|
13
|
+
'obj_material' : [24],
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Shader:
|
|
18
|
+
program: mgl.Program=None
|
|
19
|
+
"""Shader program for the vertex and fragment shader"""
|
|
20
|
+
vertex_shader: str
|
|
21
|
+
"""String representation of the vertex shader"""
|
|
22
|
+
fragment_shader: str
|
|
23
|
+
"""String representation of the vertex shader"""
|
|
24
|
+
uniforms: list[str]=[]
|
|
25
|
+
"""List containg the names of all uniforms in the shader"""
|
|
26
|
+
attribute_indices: list[int]
|
|
27
|
+
"""List of indices that map all possible shader attributes to the ones used byu the shader"""
|
|
28
|
+
fmt: str
|
|
29
|
+
"""String representation of the format for building vaos"""
|
|
30
|
+
attributes: list[str]
|
|
31
|
+
"""List representation of the attributes for building vaos"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, engine, vert: str=None, frag: str=None) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Basilisk shader object. Contains shader program and shader attrbibute/uniform information
|
|
36
|
+
Args:
|
|
37
|
+
vert: str=None
|
|
38
|
+
Path to the vertex shader. Defaults to internal if none is given
|
|
39
|
+
frag: str=None
|
|
40
|
+
Path to the fragment shader. Defaults to internal if none is given
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
self.engine = engine
|
|
44
|
+
self.ctx = engine.ctx
|
|
45
|
+
|
|
46
|
+
# Default class attributes values
|
|
47
|
+
self.uniforms = []
|
|
48
|
+
self.attribute_indices = []
|
|
49
|
+
self.fmt = ''
|
|
50
|
+
self.attributes = []
|
|
51
|
+
|
|
52
|
+
# Default vertex and fragment shaders
|
|
53
|
+
if vert == None: vert = self.engine.root + '/shaders/batch.vert'
|
|
54
|
+
if frag == None: frag = self.engine.root + '/shaders/batch.frag'
|
|
55
|
+
|
|
56
|
+
# Read the shaders
|
|
57
|
+
with open(vert) as file:
|
|
58
|
+
self.vertex_shader = file.read()
|
|
59
|
+
with open(frag) as file:
|
|
60
|
+
self.fragment_shader = file.read()
|
|
61
|
+
|
|
62
|
+
# Hash value for references
|
|
63
|
+
if vert == None and frag == None:
|
|
64
|
+
self.hash = hash((self.vertex_shader, self.fragment_shader, 'default'))
|
|
65
|
+
else:
|
|
66
|
+
self.hash = hash((self.vertex_shader, self.fragment_shader))
|
|
67
|
+
|
|
68
|
+
# Create a string of all lines in both shaders
|
|
69
|
+
lines = f'{self.vertex_shader}\n{self.fragment_shader}'.split('\n')
|
|
70
|
+
|
|
71
|
+
# Parse through shader to find uniforms and attributes
|
|
72
|
+
for line in lines:
|
|
73
|
+
tokens = line.strip().split(' ')
|
|
74
|
+
|
|
75
|
+
# Add uniforms
|
|
76
|
+
if tokens[0] == 'uniform' and len(tokens) > 2:
|
|
77
|
+
self.uniforms.append(tokens[-1][:-1])
|
|
78
|
+
|
|
79
|
+
# Add attributes
|
|
80
|
+
if tokens[0] == 'layout' and len(tokens) > 2 and 'in' in line:
|
|
81
|
+
self.attributes.append(tokens[-1][:-1])
|
|
82
|
+
|
|
83
|
+
if tokens[-1][:-1] not in attribute_mappings: continue
|
|
84
|
+
indices = attribute_mappings[tokens[-1][:-1]]
|
|
85
|
+
self.attribute_indices.extend(indices)
|
|
86
|
+
self.fmt += f'{len(indices)}f '
|
|
87
|
+
|
|
88
|
+
# Create a program with shaders
|
|
89
|
+
self.program = self.ctx.program(vertex_shader=self.vertex_shader, fragment_shader=self.fragment_shader)
|
|
90
|
+
|
|
91
|
+
def set_main(self):
|
|
92
|
+
"""
|
|
93
|
+
Selects a shader for use
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
self.engine.scene.shader_handler.add(self)
|
|
97
|
+
self.engine.scene.node_handler.chunk_handler.update_all()
|
|
98
|
+
|
|
99
|
+
def write(self, name: str, value) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Writes a uniform to the shader program
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
self.program[name].write(value)
|
|
105
|
+
|
|
106
|
+
def __del__(self) -> int:
|
|
107
|
+
if self.program: self.program.release()
|
|
108
|
+
|
|
109
|
+
def __hash__(self) -> int:
|
|
110
|
+
return self.hash
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import moderngl as mgl
|
|
2
2
|
import glm
|
|
3
|
-
|
|
4
|
-
# Predefined uniforms that do not change each frame
|
|
5
|
-
single_frame_uniforms = ['m_proj']
|
|
3
|
+
from .shader import Shader
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
class ShaderHandler:
|
|
@@ -12,10 +10,8 @@ class ShaderHandler:
|
|
|
12
10
|
"""Back reference to the parent scene"""
|
|
13
11
|
ctx: mgl.Context
|
|
14
12
|
"""Back reference to the parent context"""
|
|
15
|
-
|
|
13
|
+
shaders: set
|
|
16
14
|
"""Dictionary containing all the shaders"""
|
|
17
|
-
shader_uniforms: dict = {}
|
|
18
|
-
"""Dictionary all the uniforms present in a shader"""
|
|
19
15
|
uniform_values: dict = {}
|
|
20
16
|
"""Dictionary containing uniform values"""
|
|
21
17
|
|
|
@@ -30,38 +26,27 @@ class ShaderHandler:
|
|
|
30
26
|
self.ctx = scene.engine.ctx
|
|
31
27
|
|
|
32
28
|
# Initalize dictionaries
|
|
33
|
-
self.
|
|
34
|
-
self.
|
|
35
|
-
|
|
36
|
-
self.load('batch', self.engine.root + '/shaders/batch.vert', self.engine.root + '/shaders/batch.frag')
|
|
37
|
-
self.load('draw', self.engine.root + '/shaders/draw.vert', self.engine.root + '/shaders/draw.frag')
|
|
38
|
-
self.load('sky', self.engine.root + '/shaders/sky.vert', self.engine.root + '/shaders/sky.frag')
|
|
29
|
+
self.shaders = set()
|
|
30
|
+
self.add(self.engine.shader)
|
|
39
31
|
|
|
40
|
-
def
|
|
32
|
+
def add(self, shader: Shader) -> None:
|
|
41
33
|
"""
|
|
42
34
|
Creates a shader program from a file name.
|
|
43
35
|
Parses through shaders to identify uniforms and save for writting
|
|
44
36
|
"""
|
|
45
37
|
|
|
46
|
-
# Read the shaders
|
|
47
|
-
with open(vert_path) as file:
|
|
48
|
-
vertex_shader = file.read()
|
|
49
|
-
with open(frag_path) as file:
|
|
50
|
-
fragment_shader = file.read()
|
|
51
|
-
|
|
52
|
-
# Create blank list for uniforms
|
|
53
|
-
self.shader_uniforms[name] = []
|
|
54
|
-
# Create a list of all lines in both shaders
|
|
55
|
-
lines = f'{vertex_shader}\n{fragment_shader}'.split('\n')
|
|
56
|
-
# Parse through shader to find uniform variables
|
|
57
|
-
for line in lines:
|
|
58
|
-
tokens = line.strip().split(' ')
|
|
59
|
-
if tokens[0] == 'uniform' and len(tokens) > 2:
|
|
60
|
-
self.shader_uniforms[name].append(tokens[2][:-1])
|
|
61
38
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
39
|
+
if not shader: return None
|
|
40
|
+
if shader in self.shaders: return shader
|
|
41
|
+
|
|
42
|
+
self.shaders.add(shader)
|
|
43
|
+
|
|
44
|
+
if self.scene.material_handler:
|
|
45
|
+
self.scene.light_handler.write()
|
|
46
|
+
self.scene.material_handler.write()
|
|
47
|
+
self.scene.material_handler.image_handler.write()
|
|
48
|
+
|
|
49
|
+
return shader
|
|
65
50
|
|
|
66
51
|
def get_uniforms_values(self) -> None:
|
|
67
52
|
"""
|
|
@@ -83,13 +68,13 @@ class ShaderHandler:
|
|
|
83
68
|
|
|
84
69
|
self.get_uniforms_values()
|
|
85
70
|
for uniform in self.uniform_values:
|
|
86
|
-
for
|
|
87
|
-
if not uniform in
|
|
88
|
-
|
|
71
|
+
for shader in self.shaders:
|
|
72
|
+
if not uniform in shader.uniforms: continue # Does not write uniforms not in the shader
|
|
73
|
+
shader.write(uniform, self.uniform_values[uniform])
|
|
89
74
|
|
|
90
75
|
def release(self) -> None:
|
|
91
76
|
"""
|
|
92
77
|
Releases all shader programs in handler
|
|
93
78
|
"""
|
|
94
79
|
|
|
95
|
-
[
|
|
80
|
+
[shader.__del__() for shader in self.shaders]
|
basilisk/render/sky.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from PIL import Image as PIL_Image
|
|
3
|
+
from .shader import Shader
|
|
3
4
|
|
|
4
5
|
class Sky:
|
|
5
6
|
texture_cube=None
|
|
@@ -26,11 +27,12 @@ class Sky:
|
|
|
26
27
|
|
|
27
28
|
def write(self):
|
|
28
29
|
# Write the texture cube to the sky shader
|
|
29
|
-
self.program['skyboxTexture'] = 8
|
|
30
|
+
self.shader.program['skyboxTexture'] = 8
|
|
30
31
|
self.texture_cube.use(location = 8)
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
shader = self.scene.engine.shader
|
|
34
|
+
if 'skyboxTexture' not in shader.uniforms: return
|
|
35
|
+
shader.program['skyboxTexture'] = 8
|
|
34
36
|
self.texture_cube.use(location = 8)
|
|
35
37
|
|
|
36
38
|
|
|
@@ -105,8 +107,9 @@ class Sky:
|
|
|
105
107
|
|
|
106
108
|
# Create a renderable vao
|
|
107
109
|
self.vbo = self.ctx.buffer(vertex_data)
|
|
108
|
-
|
|
109
|
-
self.
|
|
110
|
+
root = self.scene.engine.root
|
|
111
|
+
self.shader = self.scene.shader_handler.add(Shader(self.scene.engine, root + '/shaders/sky.vert', root + '/shaders/sky.frag'))
|
|
112
|
+
self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f', 'in_position')], skip_errors=True)
|
|
110
113
|
|
|
111
114
|
def __del__(self):
|
|
112
115
|
"""
|
basilisk/scene.py
CHANGED
|
@@ -12,7 +12,9 @@ from .collisions.collider_handler import ColliderHandler
|
|
|
12
12
|
from .draw.draw_handler import DrawHandler
|
|
13
13
|
from .render.sky import Sky
|
|
14
14
|
from .render.frame import Frame
|
|
15
|
-
|
|
15
|
+
from .particles.particle_handler import ParticleHandler
|
|
16
|
+
from .nodes.node import Node
|
|
17
|
+
from .generic.collisions import moller_trumbore
|
|
16
18
|
|
|
17
19
|
class Scene():
|
|
18
20
|
engine: any
|
|
@@ -42,8 +44,10 @@ class Scene():
|
|
|
42
44
|
Updates the physics and in the scene
|
|
43
45
|
"""
|
|
44
46
|
|
|
45
|
-
self.camera.update()
|
|
46
47
|
self.node_handler.update()
|
|
48
|
+
self.particle.update()
|
|
49
|
+
self.camera.update()
|
|
50
|
+
self.collider_handler.resolve_collisions()
|
|
47
51
|
|
|
48
52
|
def render(self) -> None:
|
|
49
53
|
"""
|
|
@@ -52,39 +56,50 @@ class Scene():
|
|
|
52
56
|
|
|
53
57
|
self.frame.use()
|
|
54
58
|
self.shader_handler.write()
|
|
55
|
-
self.sky.render()
|
|
59
|
+
if self.sky: self.sky.render()
|
|
56
60
|
self.node_handler.render()
|
|
61
|
+
self.particle.render()
|
|
57
62
|
self.draw_handler.render()
|
|
58
63
|
|
|
59
64
|
if self.engine.headless: return
|
|
60
65
|
self.frame.render()
|
|
61
66
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
material: Material=None,
|
|
69
|
-
velocity: glm.vec3=None,
|
|
70
|
-
rotational_velocity: glm.quat=None,
|
|
71
|
-
physics: bool=False,
|
|
72
|
-
mass: float=None,
|
|
73
|
-
collisions: bool=False,
|
|
74
|
-
collider: str=None,
|
|
75
|
-
static_friction: float=None,
|
|
76
|
-
kinetic_friction: float=None,
|
|
77
|
-
elasticity: float=None,
|
|
78
|
-
collision_group : float=None,
|
|
79
|
-
name: str='',
|
|
80
|
-
tags: list[str]=None,
|
|
81
|
-
static: bool=True):
|
|
67
|
+
def add(self, bsk_object: ...) -> ...:
|
|
68
|
+
"""
|
|
69
|
+
Adds an object to the scene. Can pass in any scene objects:
|
|
70
|
+
Argument overloads:
|
|
71
|
+
object: Node - Adds the given node to the scene.
|
|
72
|
+
"""
|
|
82
73
|
|
|
83
|
-
if
|
|
84
|
-
|
|
74
|
+
if isinstance(bsk_object, type(None)):
|
|
75
|
+
# Considered well defined behavior
|
|
76
|
+
return
|
|
77
|
+
elif isinstance(bsk_object, Node):
|
|
78
|
+
# Add a node to the scene
|
|
79
|
+
return self.node_handler.add(bsk_object)
|
|
80
|
+
# Light
|
|
81
|
+
|
|
82
|
+
# Mesh
|
|
85
83
|
|
|
86
|
-
|
|
84
|
+
else:
|
|
85
|
+
raise ValueError(f'scene.add: Incompatable object add type {type(bsk_object)}')
|
|
87
86
|
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def remove(self, bsk_object):
|
|
90
|
+
"""
|
|
91
|
+
Removes the given baskilsk object from the scene
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
if isinstance(bsk_object, type(None)):
|
|
95
|
+
# Considered well defined behavior
|
|
96
|
+
return
|
|
97
|
+
elif isinstance(bsk_object, Node):
|
|
98
|
+
self.node_handler.remove(bsk_object)
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError(f'scene.remove: Incompatable object remove type {type(bsk_object)}')
|
|
101
|
+
|
|
102
|
+
return None
|
|
88
103
|
|
|
89
104
|
def set_engine(self, engine: any) -> None:
|
|
90
105
|
"""
|
|
@@ -96,14 +111,84 @@ class Scene():
|
|
|
96
111
|
|
|
97
112
|
self.camera = FreeCamera()
|
|
98
113
|
self.shader_handler = ShaderHandler(self)
|
|
114
|
+
self.material_handler = MaterialHandler(self)
|
|
115
|
+
self.light_handler = LightHandler(self)
|
|
99
116
|
self.physics_engine = PhysicsEngine()
|
|
100
117
|
self.node_handler = NodeHandler(self)
|
|
118
|
+
self.particle = ParticleHandler(self)
|
|
101
119
|
self.collider_handler = ColliderHandler(self)
|
|
102
|
-
self.material_handler = MaterialHandler(self)
|
|
103
|
-
self.light_handler = LightHandler(self)
|
|
104
120
|
self.draw_handler = DrawHandler(self)
|
|
105
121
|
self.frame = Frame(self)
|
|
106
122
|
self.sky = Sky(self.engine)
|
|
123
|
+
|
|
124
|
+
def raycast(self, position: glm.vec3=None, forward: glm.vec3=None, max_distance: float=1e5, has_collisions: bool=None, has_physics: bool=None, tags: list[str]=[]) -> tuple[Node, glm.vec3]:
|
|
125
|
+
"""
|
|
126
|
+
Ray cast from any posiiton and forward vector and returns the nearest node. If no position or forward is given, uses the scene camera's current position and forward
|
|
127
|
+
"""
|
|
128
|
+
if not position: position = self.camera.position
|
|
129
|
+
if not forward: forward = self.camera.forward
|
|
130
|
+
forward = glm.normalize(forward)
|
|
131
|
+
|
|
132
|
+
# if we are filtering for collisions, use the broad BVH to improve performance
|
|
133
|
+
if has_collisions:
|
|
134
|
+
colliders = self.collider_handler.bvh.get_line_collided(position, forward)
|
|
135
|
+
nodes = [collider.node for collider in colliders]
|
|
136
|
+
|
|
137
|
+
def is_valid(node: Node) -> bool:
|
|
138
|
+
return all([
|
|
139
|
+
has_collisions is None or bool(node.collider) == has_collisions,
|
|
140
|
+
has_physics is None or bool(node.physics_body) == has_physics,
|
|
141
|
+
all(tag in node.tags for tag in tags)
|
|
142
|
+
])
|
|
143
|
+
|
|
144
|
+
nodes: list[Node] = list(filter(lambda node: is_valid(node), nodes))
|
|
145
|
+
|
|
146
|
+
# if we are not filtering for collisions, filter nodes and
|
|
147
|
+
else: nodes = self.node_handler.get_all(collisions=has_collisions, physics=has_physics, tags=tags)
|
|
148
|
+
|
|
149
|
+
# determine closest node
|
|
150
|
+
best_distance, best_point, best_node = max_distance, None, None
|
|
151
|
+
position_two = position + forward
|
|
152
|
+
for node in nodes:
|
|
153
|
+
|
|
154
|
+
inv_mat = glm.inverse(node.model_matrix)
|
|
155
|
+
relative_position = inv_mat * position
|
|
156
|
+
relative_forward = glm.normalize(inv_mat * position_two - relative_position)
|
|
157
|
+
|
|
158
|
+
triangles = [node.mesh.indices[i] for i in node.mesh.get_line_collided(relative_position, relative_forward)]
|
|
159
|
+
|
|
160
|
+
for triangle in triangles:
|
|
161
|
+
intersection = moller_trumbore(relative_position, relative_forward, [node.mesh.points[i] for i in triangle])
|
|
162
|
+
if not intersection: continue
|
|
163
|
+
intersection = node.model_matrix * intersection
|
|
164
|
+
distance = glm.length(intersection - position)
|
|
165
|
+
if distance < best_distance:
|
|
166
|
+
best_distance = distance
|
|
167
|
+
best_point = intersection
|
|
168
|
+
best_node = node
|
|
169
|
+
|
|
170
|
+
return best_node, best_point
|
|
171
|
+
|
|
172
|
+
def raycast_mouse(self, position: tuple[int, int] | glm.vec2, max_distance: float=1e5, has_collisions: bool=None, has_pshyics: bool=None, tags: list[str]=[]) -> tuple[Node, glm.vec3]:
|
|
173
|
+
"""
|
|
174
|
+
Ray casts from the mouse position with respect to the camera. Returns the nearest node that was clicked, if none was clicked, returns None.
|
|
175
|
+
"""
|
|
176
|
+
# derive forward vector from mouse click position
|
|
177
|
+
position = glm.vec2(position)
|
|
178
|
+
inv_proj, inv_view = glm.inverse(self.camera.m_proj), glm.inverse(self.camera.m_view)
|
|
179
|
+
ndc = glm.vec4(2 * position[0] / self.engine.win_size[0] - 1, 1 - 2 * position[1] / self.engine.win_size[1], 1, 1)
|
|
180
|
+
point = inv_proj * ndc
|
|
181
|
+
point /= point.w
|
|
182
|
+
forward = glm.normalize(glm.vec3(inv_view * glm.vec4(point.x, point.y, point.z, 0)))
|
|
183
|
+
|
|
184
|
+
return self.raycast(
|
|
185
|
+
position=self.camera.position,
|
|
186
|
+
forward=forward,
|
|
187
|
+
max_distance=max_distance,
|
|
188
|
+
has_collisions=has_collisions,
|
|
189
|
+
has_physics=has_pshyics,
|
|
190
|
+
tags=tags
|
|
191
|
+
)
|
|
107
192
|
|
|
108
193
|
@property
|
|
109
194
|
def camera(self): return self._camera
|
|
@@ -120,9 +205,7 @@ class Scene():
|
|
|
120
205
|
|
|
121
206
|
@sky.setter
|
|
122
207
|
def sky(self, value: Sky):
|
|
123
|
-
if not value:
|
|
124
|
-
|
|
125
|
-
raise TypeError(f'Scene: Invalid sky type: {type(value)}. Expected type bsk.Sky')
|
|
208
|
+
if not isinstance(value, Sky) and not isinstance(value, type(None)):
|
|
209
|
+
raise TypeError(f'Scene: Invalid sky type: {type(value)}. Expected type bsk.Sky or None')
|
|
126
210
|
self._sky = value
|
|
127
|
-
self._sky.write()
|
|
128
|
-
|
|
211
|
+
if value: self._sky.write()
|