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.

Files changed (62) hide show
  1. basilisk/bsk_assets/__init__.py +0 -0
  2. basilisk/collisions/__init__.py +0 -0
  3. basilisk/collisions/broad/__init__.py +0 -0
  4. basilisk/collisions/broad/broad_aabb.py +96 -0
  5. basilisk/collisions/broad/broad_bvh.py +102 -0
  6. basilisk/collisions/collider.py +75 -0
  7. basilisk/collisions/collider_handler.py +163 -0
  8. basilisk/collisions/narrow/__init__.py +0 -0
  9. basilisk/collisions/narrow/epa.py +86 -0
  10. basilisk/collisions/narrow/gjk.py +66 -0
  11. basilisk/collisions/narrow/helper.py +23 -0
  12. basilisk/draw/__init__.py +0 -0
  13. basilisk/draw/draw.py +101 -0
  14. basilisk/draw/draw_handler.py +208 -0
  15. basilisk/draw/font_renderer.py +28 -0
  16. basilisk/generic/__init__.py +0 -0
  17. basilisk/generic/abstract_bvh.py +16 -0
  18. basilisk/generic/collisions.py +26 -0
  19. basilisk/generic/input_validation.py +28 -0
  20. basilisk/generic/math.py +7 -0
  21. basilisk/generic/matrices.py +34 -0
  22. basilisk/generic/meshes.py +73 -0
  23. basilisk/generic/quat.py +119 -0
  24. basilisk/generic/quat_methods.py +8 -0
  25. basilisk/generic/vec3.py +112 -0
  26. basilisk/input/__init__.py +0 -0
  27. basilisk/input/mouse.py +60 -0
  28. basilisk/mesh/__init__.py +0 -0
  29. basilisk/mesh/built-in/__init__.py +0 -0
  30. basilisk/mesh/cube.py +20 -0
  31. basilisk/mesh/mesh.py +216 -0
  32. basilisk/mesh/mesh_from_data.py +48 -0
  33. basilisk/mesh/model.py +272 -0
  34. basilisk/mesh/narrow_aabb.py +81 -0
  35. basilisk/mesh/narrow_bvh.py +84 -0
  36. basilisk/mesh/narrow_primative.py +24 -0
  37. basilisk/nodes/__init__.py +0 -0
  38. basilisk/nodes/node.py +508 -0
  39. basilisk/nodes/node_handler.py +94 -0
  40. basilisk/physics/__init__.py +0 -0
  41. basilisk/physics/physics_body.py +36 -0
  42. basilisk/physics/physics_engine.py +37 -0
  43. basilisk/render/__init__.py +0 -0
  44. basilisk/render/batch.py +85 -0
  45. basilisk/render/camera.py +166 -0
  46. basilisk/render/chunk.py +85 -0
  47. basilisk/render/chunk_handler.py +139 -0
  48. basilisk/render/frame.py +182 -0
  49. basilisk/render/image.py +76 -0
  50. basilisk/render/image_handler.py +119 -0
  51. basilisk/render/light.py +97 -0
  52. basilisk/render/light_handler.py +54 -0
  53. basilisk/render/material.py +196 -0
  54. basilisk/render/material_handler.py +123 -0
  55. basilisk/render/shader_handler.py +95 -0
  56. basilisk/render/sky.py +118 -0
  57. basilisk/shaders/__init__.py +0 -0
  58. {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/METADATA +1 -1
  59. basilisk_engine-0.0.3.dist-info/RECORD +65 -0
  60. basilisk_engine-0.0.1.dist-info/RECORD +0 -8
  61. {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/WHEEL +0 -0
  62. {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,85 @@
1
+ import numpy as np
2
+ import moderngl as mgl
3
+
4
+
5
+ class Batch():
6
+ chunk: ...
7
+ """Reference to the parent chunk of the batch"""
8
+ ctx: mgl.Context
9
+ """Reference to the context of the parent engine"""
10
+ program: mgl.Program
11
+ """Reference to the program used by batches"""
12
+ vao: mgl.VertexArray
13
+ """The vertex array of the batch. Used for rendering"""
14
+ vbo: mgl.Buffer
15
+ """Buffer containing all the batch data"""
16
+
17
+ def __init__(self, chunk) -> None:
18
+ """
19
+ Basilik batch object
20
+ Contains all the data for a chunk batch to be stored and rendered
21
+ """
22
+
23
+ # Back references
24
+ self.chunk = chunk
25
+ self.ctx = chunk.chunk_handler.engine.ctx
26
+ self.program = chunk.chunk_handler.program
27
+
28
+ # Set intial values
29
+ self.vbo = None
30
+ self.vao = None
31
+
32
+ def batch(self) -> bool:
33
+ """
34
+ Batches all the node meshes in the chunks bounds.
35
+ Returns True if batch was successful.
36
+ """
37
+
38
+ # Empty list to contain all vertex data of models in the chunk
39
+ batch_data = []
40
+
41
+ # Loop through each node in the chunk, adding the nodes's mesh to batch_data
42
+ index = 0
43
+ for node in self.chunk.nodes:
44
+ # Check that the node should be used
45
+ if not node.mesh: continue
46
+ if node.static != self.chunk.static: continue
47
+
48
+ # Get the data from the node
49
+ node_data = node.get_data()
50
+ # Update the index
51
+ node.data_index = index
52
+ index += len(node_data)
53
+ # Add to the chunk mesh
54
+ batch_data.append(node_data)
55
+
56
+ # Combine all meshes into a single array
57
+ if len(batch_data) > 1: batch_data = np.vstack(batch_data)
58
+ else: batch_data = np.array(batch_data, dtype='f4')
59
+
60
+
61
+ # If there are no verticies, delete the chunk
62
+ if len(batch_data) == 0: return False
63
+
64
+ if self.vbo: self.vbo.release()
65
+ if self.vao: self.vao.release()
66
+
67
+ # Create the vbo and the vao from mesh data
68
+ self.vbo = self.ctx.buffer(batch_data)
69
+ self.vao = self.ctx.vertex_array(self.program, [(self.vbo,
70
+ '3f 2f 3f 3f 3f 3f 4f 3f 1f',
71
+ *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent', 'obj_position', 'obj_rotation', 'obj_scale', 'obj_material'])],
72
+ skip_errors=True)
73
+
74
+ return True
75
+
76
+ def __repr__(self) -> str:
77
+ return f'<Basilisk Batch | {self.chunk.chunk_key}, {self.vbo.size / 1024 / 1024} mb>'
78
+
79
+ def __del__(self) -> None:
80
+ """
81
+ Deallocates the mesh vbo and vao
82
+ """
83
+
84
+ if self.vbo: self.vbo.release()
85
+ if self.vao: self.vao.release()
@@ -0,0 +1,166 @@
1
+ import pygame as pg
2
+ import glm
3
+ import numpy as np
4
+
5
+ # Camera view constants
6
+ FOV = 50 # Degrees
7
+ NEAR = 0.1
8
+ FAR = 350
9
+
10
+ # Camera movement constants
11
+ SPEED = 10
12
+ SENSITIVITY = 0.15
13
+
14
+ class Camera:
15
+ engine: ...
16
+ """Back reference to the parent engine"""
17
+ scene: ...
18
+ """Back reference to the parent scene"""
19
+ aspect_ratio: float
20
+ """Aspect ratio of the engine window"""
21
+ position: glm.vec3
22
+ """Location of the camera (maters)"""
23
+
24
+ def __init__(self, position=(0, 0, 20), yaw=-90, pitch=0) -> None:
25
+ """
26
+ Camera object to get view and projection matricies. Movement built in
27
+ """
28
+ # Back references
29
+ self.scene = None
30
+ self.engine = None
31
+ # The initial aspect ratio of the screen
32
+ self.aspect_ratio = 1.0
33
+ # Position
34
+ self.position = glm.vec3(position)
35
+ # k vector for vertical movement
36
+ self.UP = glm.vec3(0, 1, 0)
37
+ # Movement vectors
38
+ self.up = glm.vec3(0, 1, 0)
39
+ self.right = glm.vec3(1, 0, 0)
40
+ self.forward = glm.vec3(0, 0, -1)
41
+ # Look directions in degrees
42
+ self.yaw = yaw
43
+ self.pitch = pitch
44
+ # View matrix
45
+ self.m_view = self.get_view_matrix()
46
+ # Projection matrix
47
+ self.m_proj = self.get_projection_matrix()
48
+
49
+ def update(self) -> None:
50
+ """
51
+ Updates the camera view matrix
52
+ """
53
+
54
+ self.update_camera_vectors()
55
+ self.m_view = self.get_view_matrix()
56
+
57
+ def update_camera_vectors(self) -> None:
58
+ """
59
+ Computes the forward vector based on the pitch and yaw. Computes horizontal and vertical vectors with cross product.
60
+ """
61
+ yaw, pitch = glm.radians(self.yaw), glm.radians(self.pitch)
62
+
63
+ self.forward.x = glm.cos(yaw) * glm.cos(pitch)
64
+ self.forward.y = glm.sin(pitch)
65
+ self.forward.z = glm.sin(yaw) * glm.cos(pitch)
66
+
67
+ self.forward = glm.normalize(self.forward)
68
+ self.right = glm.normalize(glm.cross(self.forward, self.UP))
69
+ self.up = glm.normalize(glm.cross(self.right, self.forward))
70
+
71
+ def use(self):
72
+ # Updated aspect ratio of the screen
73
+ self.aspect_ratio = self.engine.win_size[0] / self.engine.win_size[1]
74
+ # View matrix
75
+ self.m_view = self.get_view_matrix()
76
+ # Projection matrix
77
+ self.m_proj = self.get_projection_matrix()
78
+
79
+ def get_view_matrix(self) -> glm.mat4x4:
80
+ return glm.lookAt(self.position, self.position + self.forward, self.up)
81
+
82
+ def get_projection_matrix(self) -> glm.mat4x4:
83
+ return glm.perspective(glm.radians(FOV), self.aspect_ratio, NEAR, FAR)
84
+
85
+ def get_params(self) -> tuple:
86
+ return self.engine, self.position, self.yaw, self.pitch
87
+
88
+ def __repr__(self):
89
+ return f'<Basilisk Camera | Position: {self.position}, Direction: {self.forward}>'
90
+
91
+ @property
92
+ def scene(self): return self._scene
93
+ @property
94
+ def position(self): return self._position
95
+ @property
96
+ def direction(self): return self.forward
97
+
98
+ @scene.setter
99
+ def scene(self, value):
100
+ if value == None: return
101
+ self._scene = value
102
+ self.engine = self._scene.engine
103
+ self.use()
104
+ @position.setter
105
+ def position(self, value: tuple | list | glm.vec3 | np.ndarray):
106
+ if isinstance(value, glm.vec3): self._position = glm.vec3(value)
107
+ elif isinstance(value, tuple) or isinstance(value, list) or isinstance(value, np.ndarray):
108
+ if len(value) != 3: raise ValueError(f'Camera: Invalid number of values for position. Expected 3, got {len(value)}')
109
+ self._position = glm.vec3(value)
110
+ else: raise TypeError(f'Camera: Invalid position value type {type(value)}')
111
+ @direction.setter
112
+ def direction(self, value: tuple | list | glm.vec3 | np.ndarray):
113
+ if isinstance(value, glm.vec3): self.direction = glm.normalize(glm.vec3(value))
114
+ elif isinstance(value, tuple) or isinstance(value, list) or isinstance(value, np.ndarray):
115
+ if len(value) != 3: raise ValueError(f'Camera: Invalid number of values for direction. Expected 3, got {len(value)}')
116
+ self.forward = glm.normalize(glm.vec3(value))
117
+ else: raise TypeError(f'Camera: Invalid direction value type {type(value)}')
118
+
119
+
120
+ class FreeCamera(Camera):
121
+ def __init__(self, position=(0, 0, 20), yaw=-90, pitch=0):
122
+ super().__init__(position, yaw, pitch)
123
+
124
+ def update(self) -> None:
125
+ """
126
+ Updates the camera position and rotaiton based on user input
127
+ """
128
+
129
+ self.move()
130
+ self.rotate()
131
+ self.update_camera_vectors()
132
+ self.m_view = self.get_view_matrix()
133
+
134
+ def rotate(self) -> None:
135
+ """
136
+ Rotates the camera based on the amount of mouse movement.
137
+ """
138
+ rel_x, rel_y = pg.mouse.get_rel()
139
+ self.yaw += rel_x * SENSITIVITY
140
+ self.pitch -= rel_y * SENSITIVITY
141
+ self.yaw = self.yaw % 360
142
+ self.pitch = max(-89, min(89, self.pitch))
143
+
144
+ def move(self) -> None:
145
+ """
146
+ Checks for button presses and updates vectors accordingly.
147
+ """
148
+ velocity = SPEED * self.engine.delta_time
149
+ keys = self.engine.keys
150
+ if keys[pg.K_w]:
151
+ self.position += glm.normalize(glm.vec3(self.forward.x, 0, self.forward.z)) * velocity
152
+ if keys[pg.K_s]:
153
+ self.position -= glm.normalize(glm.vec3(self.forward.x, 0, self.forward.z)) * velocity
154
+ if keys[pg.K_a]:
155
+ self.position -= self.right * velocity
156
+ if keys[pg.K_d]:
157
+ self.position += self.right * velocity
158
+ if keys[pg.K_SPACE]:
159
+ self.position += self.UP * velocity
160
+ if keys[pg.K_LSHIFT]:
161
+ self.position -= self.UP * velocity
162
+
163
+
164
+ class StaticCamera(Camera):
165
+ def __init__(self, position=(0, 0, 20), yaw=-90, pitch=0):
166
+ super().__init__(position, yaw, pitch)
@@ -0,0 +1,85 @@
1
+ from .batch import Batch
2
+
3
+
4
+ class Chunk():
5
+ chunk_handler: ...
6
+ """Back refrence to the parent chunk handler"""
7
+ chunk_key: tuple
8
+ """The position of the chunk. Used as a key in the chunk handler"""
9
+ batch: Batch
10
+ """Batched mesh of the chunk"""
11
+ nodes: set
12
+ """Set conaining references to all nodes in the chunk"""
13
+ static: bool
14
+ """Type of node that the chunk recognizes"""
15
+
16
+ def __init__(self, chunk_handler, chunk_key: tuple, static: bool) -> None:
17
+ """
18
+ Basilisk chunk object.
19
+ Contains references to all nodes in the chunk.
20
+ Handles batching for its own nodes
21
+ """
22
+
23
+ # Back references
24
+ self.chunk_handler = chunk_handler
25
+ self.chunk_key = chunk_key
26
+
27
+ self.static = static
28
+
29
+ # Create empty batch
30
+ self.batch = Batch(self)
31
+
32
+ # Create empty set for chunk's nodes
33
+ self.nodes = set()
34
+
35
+ def render(self) -> None:
36
+ """
37
+ Renders the chunk mesh
38
+ """
39
+
40
+ if self.batch.vao: self.batch.vao.render()
41
+
42
+ def update(self) -> bool:
43
+ """
44
+ Batches all the node meshes in the chunk
45
+ """
46
+
47
+ # Check if there are no nodes in the chunk
48
+ if not self.nodes: return False
49
+ # Batch the chunk nodes, return success bit
50
+ return self.batch.batch()
51
+
52
+ def node_update_callback(self, node):
53
+ if not self.batch.vbo: return
54
+
55
+ data = node.get_data()
56
+ self.batch.vbo.write(data, node.data_index * 25 * 4)
57
+
58
+ def add(self, node):
59
+ """
60
+ Adds an existing node to the chunk. Updates the node's chunk reference
61
+ """
62
+
63
+ self.nodes.add(node)
64
+ node.chunk = self
65
+
66
+ return node
67
+
68
+ def remove(self, node):
69
+ """
70
+ Removes a node from the chunk
71
+ """
72
+
73
+ self.nodes.remove(node)
74
+
75
+ return node
76
+
77
+ def __repr__(self) -> str:
78
+ return f'<Basilisk Chunk | {self.chunk_key}, {len(self.nodes)} nodes, {'static' if self.static else 'dynamic'}>'
79
+
80
+ def __del__(self) -> None:
81
+ """
82
+ Deletes the batch if this chunk is deleted
83
+ """
84
+
85
+ del self.batch
@@ -0,0 +1,139 @@
1
+ import numpy as np
2
+ import moderngl as mgl
3
+ from .chunk import Chunk
4
+ from ..nodes.node import Node
5
+
6
+
7
+ class ChunkHandler():
8
+ engine: ...
9
+ """Back reference to the parent engine"""
10
+ scene: ...
11
+ """Back reference to the parent scene"""
12
+ ctx: mgl.Context
13
+ """Back reference to the parent context"""
14
+ program: mgl.Program
15
+ """Reference to the shader program used by batches"""
16
+ chunks: list[dict]
17
+ """List containing two dictionaries for dynamic and static chunks repsectivly"""
18
+ updated_chunks: list[set]
19
+ """List containing two dictionaries for recently updated dynamic and static chunks repsectivly"""
20
+
21
+ def __init__(self, scene) -> None:
22
+ # Reference to the scene hadlers and variables
23
+ """
24
+ Handles all the chunking of all the nodes in the scene
25
+ """
26
+
27
+ # Back references
28
+ self.scene = scene
29
+ self.engine = scene.engine
30
+ self.ctx = scene.engine.ctx
31
+ self.program = scene.shader_handler.programs['batch']
32
+
33
+ # List for the dynamic and static chunk dictionaries | [dyanmic: dict, static: dict]
34
+ self.chunks = [{} , {} ]
35
+ self.updated_chunks = [set(), set()]
36
+
37
+
38
+ def render(self) -> None:
39
+ """
40
+ Renders all the chunk batches in the camera's range
41
+ Includes some view culling, but not frustum culling.
42
+ """
43
+
44
+ # Gets a rectanglur prism of chunks in the cameras view
45
+ render_range_x, render_range_y, render_range_z = self.get_render_range()
46
+
47
+ chunk_keys = [(x, y, z) for x in range(*render_range_x) for y in range(*render_range_y) for z in range(*render_range_z)]
48
+
49
+ # Loop through all chunks in view and render
50
+ for chunk in chunk_keys:
51
+ # Render the chunk if it exists
52
+ if chunk in self.chunks[0]: self.chunks[0][chunk].render()
53
+ if chunk in self.chunks[1]: self.chunks[1][chunk].render()
54
+
55
+
56
+ def update(self) -> None:
57
+ """
58
+ Updates all the chunks that have been updated since the last frame.
59
+ """
60
+
61
+ # Loop through the set of updated chunk keys and update the chunk
62
+ removes = []
63
+
64
+ for chunk in self.updated_chunks[0]:
65
+ if chunk.update(): continue
66
+ removes.append((0, chunk))
67
+ for chunk in self.updated_chunks[1]:
68
+ if chunk.update(): continue
69
+ removes.append((1, chunk))
70
+
71
+ # Remove any empty chunks
72
+ for chunk_tuple in removes:
73
+ if chunk_tuple[1] not in self.chunks[chunk_tuple[0]]: continue
74
+ del self.chunks[chunk_tuple[0]][chunk_tuple[1]]
75
+
76
+ # Clears the set of updated chunks so that they are not updated unless they are updated again
77
+ self.updated_chunks = [set(), set()]
78
+
79
+ def add(self, node: Node) -> Node:
80
+ """
81
+ Adds an existing node to its chunk. Updates the node's chunk reference
82
+ """
83
+
84
+ # The key of the chunk the node will be added to
85
+ chunk_size = self.engine.config.chunk_size
86
+ chunk_key = (int(node.x // chunk_size), int(node.y // chunk_size), int(node.z // chunk_size))
87
+
88
+ # Ensure that the chunk exists
89
+ if chunk_key not in self.chunks[node.static]:
90
+ self.chunks[node.static][chunk_key] = Chunk(self, chunk_key, node.static)
91
+
92
+ # Add the node to the chunk
93
+ self.chunks[node.static][chunk_key].add(node)
94
+
95
+ # Update the chunk
96
+ self.updated_chunks[node.static].add(self.chunks[node.static][chunk_key])
97
+
98
+ return Node
99
+
100
+ def remove(self, node) -> None:
101
+ """
102
+ Removes a node from the its chunk
103
+ """
104
+
105
+ # Remove the node
106
+ chunk = node.chunk
107
+ chunk.remove(node)
108
+ node.chunk = None
109
+
110
+ # Update the chunk
111
+ self.updated_chunks.add(chunk)
112
+
113
+ def get_render_range(self) -> tuple:
114
+ """
115
+ Returns a rectangluar prism of chunks that are in the camera's view.
116
+ Tuple return is in form ((x1, x2), (y1, y2), (z1, z2))
117
+ """
118
+
119
+ cam_position = self.scene.camera.position # glm.vec3(x, y, z)
120
+ fov = 40 # The range in which a direction will not be culled
121
+
122
+ # Default to a cube of chunks around the camera extending view_distance chunks in each direction
123
+ chunk_size = self.engine.config.chunk_size
124
+ render_distance = self.engine.config.render_distance
125
+ render_range_x = [int(cam_position.x // chunk_size - render_distance), int(cam_position.x // chunk_size + render_distance + 1)]
126
+ render_range_y = [int(cam_position.y // chunk_size - render_distance), int(cam_position.y // chunk_size + render_distance + 1)]
127
+ render_range_z = [int(cam_position.z // chunk_size - render_distance), int(cam_position.z // chunk_size + render_distance + 1)]
128
+
129
+ # Remove chunks that the camera is facing away from
130
+ render_range_x[1] -= render_distance * (180 - fov < self.scene.camera.yaw < 180 + fov) - 1
131
+ render_range_x[0] += render_distance * (-fov < self.scene.camera.yaw < fov or self.scene.camera.yaw > 360 - fov) - 1
132
+
133
+ render_range_y[0] += render_distance * (self.scene.camera.pitch > 25) - 1
134
+ render_range_y[1] -= render_distance * (self.scene.camera.pitch < -25) - 1
135
+
136
+ render_range_z[1] -= render_distance * (270 - fov < self.scene.camera.yaw < 270 + fov) - 1
137
+ render_range_z[0] += render_distance * (90 - fov < self.scene.camera.yaw < 90 + fov) - 1
138
+
139
+ return (render_range_x, render_range_y, render_range_z)
@@ -0,0 +1,182 @@
1
+ import numpy as np
2
+ import moderngl as mgl
3
+ from PIL import Image
4
+
5
+
6
+ class Frame:
7
+ program: mgl.Program=None
8
+ vbo: mgl.Buffer=None
9
+ vao: mgl.VertexArray=None
10
+ frame_texture: mgl.Texture=None
11
+ depth_texture: mgl.Texture=None
12
+ framebuffer: mgl.Framebuffer=None
13
+ pingpong_frame_texture: mgl.Texture=None
14
+ pingpong_depth_texture: mgl.Texture=None
15
+ pingpong_framebuffer: mgl.Framebuffer=None
16
+ postprocess: dict=None
17
+
18
+ def __init__(self, scene) -> None:
19
+ """
20
+ Basilisk render destination.
21
+ Can be used to render to the screen or for headless rendering
22
+ """
23
+
24
+ self.scene = scene
25
+ self.engine = scene.engine
26
+ self.ctx = scene.ctx
27
+
28
+
29
+ self.load_program()
30
+ self.set_textures()
31
+ self.set_renderer()
32
+
33
+ self.postprocess = {}
34
+ self.load_post_shader('frame', 'filter')
35
+
36
+ def render(self):
37
+ """
38
+ Renders the current frame to the screen
39
+ """
40
+
41
+ # self.apply_postprocess('filter')
42
+
43
+ self.ctx.screen.use()
44
+ self.program['screenTexture'] = 0
45
+ self.framebuffer.color_attachments[0].use(location=0)
46
+ self.vao.render()
47
+
48
+ def use(self):
49
+ """
50
+ Uses the frame as a render target
51
+ """
52
+
53
+ self.framebuffer.use()
54
+ self.framebuffer.clear()
55
+
56
+ def save(self, destination: str=None):
57
+ """
58
+ Saves the frame as an image to the given file destination
59
+ """
60
+
61
+ path = destination if destination else 'screenshot'
62
+
63
+ data = self.framebuffer.read(components=3, alignment=1)
64
+ img = Image.frombytes('RGB', self.framebuffer.size, data).transpose(Image.FLIP_TOP_BOTTOM)
65
+ img.save(f'{path}.png')
66
+
67
+
68
+ def load_program(self) -> None:
69
+ """
70
+ Loads the frame shaders
71
+ """
72
+
73
+ # Release any existing memory
74
+ if self.program: self.program.release()
75
+
76
+ # Read the shaders
77
+ with open(self.engine.root + '/shaders/frame.vert') as file:
78
+ vertex_shader = file.read()
79
+ with open(self.engine.root + '/shaders/frame.frag') as file:
80
+ fragment_shader = file.read()
81
+
82
+ # Create the program
83
+ self.program = self.ctx.program(vertex_shader=vertex_shader, fragment_shader=fragment_shader)
84
+
85
+ def load_post_shader(self, vert: str, frag: str) -> None:
86
+ """
87
+ Loads a post processing shader
88
+ """
89
+
90
+ # Read the shaders
91
+ with open(f'basilisk/shaders/{vert}.vert') as file:
92
+ vertex_shader = file.read()
93
+ with open(f'basilisk/shaders/{frag}.frag') as file:
94
+ fragment_shader = file.read()
95
+
96
+ # Create the program
97
+ program = self.ctx.program(vertex_shader=vertex_shader, fragment_shader=fragment_shader)
98
+ self.postprocess[frag] = self.ctx.vertex_array(program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
99
+
100
+ def apply_postprocess(self, shader: str):
101
+ self.pingpong_framebuffer.use()
102
+ self.pingpong_framebuffer.clear()
103
+ self.postprocess[shader].program['screenTexture'] = 0
104
+ self.framebuffer.color_attachments[0].use(location=0)
105
+ self.postprocess[shader].render()
106
+
107
+
108
+ temp = self.framebuffer
109
+ self.framebuffer = self.pingpong_framebuffer
110
+ self.pingpong_framebuffer = temp
111
+
112
+ # self.use()
113
+ # self.postprocess[shader].program['screenTexture'] = 0
114
+ # self.pingpong_frame_texture.use(location=0)
115
+ # self.vao.render()
116
+
117
+
118
+ def set_textures(self, viewport: tuple=None) -> None:
119
+ """
120
+ Sets the framebuffer textures
121
+ """
122
+
123
+ # Release any existing memory in case of a resize
124
+ if self.frame_texture: self.frame_texture.release()
125
+ if self.depth_texture: self.depth_texture.release()
126
+ if self.framebuffer: self.framebuffer.release()
127
+ if self.pingpong_frame_texture: self.pingpong_frame_texture.release()
128
+ if self.pingpong_depth_texture: self.pingpong_depth_texture.release()
129
+ if self.pingpong_framebuffer: self.pingpong_framebuffer.release()
130
+
131
+ # Get the size from the engine window if the not specified by the function call
132
+ size = viewport if viewport else self.engine.win_size
133
+
134
+ # Create textures and frame buffer object
135
+ self.frame_texture = self.ctx.texture(size, components=4)
136
+ self.depth_texture = self.ctx.depth_texture(size)
137
+ self.framebuffer = self.ctx.framebuffer([self.frame_texture], self.depth_texture)
138
+ self.pingpong_frame_texture = self.ctx.texture(size, components=4)
139
+ self.pingpong_depth_texture = self.ctx.depth_texture(size)
140
+ self.pingpong_framebuffer = self.ctx.framebuffer([self.pingpong_frame_texture], self.pingpong_depth_texture)
141
+
142
+ def set_renderer(self) -> None:
143
+ """
144
+ Sets the vertex data and vao for the frame
145
+ """
146
+
147
+ # Release any existing memory
148
+ if self.vbo: self.vbo.release()
149
+ if self.vao: self.vao.release()
150
+
151
+ # Vertex and index info for the frame
152
+ verticies = [[-1, -1, 0], [ 1, -1, 0], [ 1, 1, 0], [-1, 1, 0]]
153
+ indicies = [(3, 0, 1), (2, 3, 1)]
154
+ uv_verticies = [(0, 0), (1, 0), (1, 1), (0, 1)]
155
+ uv_indicies = [(3, 0, 1),(2, 3, 1)]
156
+
157
+ # Format the data
158
+ vertex_data = [verticies[ind] for triangle in indicies for ind in triangle]
159
+ vertex_data = np.array(vertex_data, dtype='f4')
160
+ uv_data = [uv_verticies[ind] for triangle in uv_indicies for ind in triangle]
161
+ uv_data = np.array(uv_data, dtype='f4')
162
+
163
+ vertex_data = np.hstack([vertex_data, uv_data])
164
+
165
+ # Create moderngl objects
166
+ self.vbo = self.ctx.buffer(vertex_data)
167
+ self.vao = self.ctx.vertex_array(self.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
168
+
169
+ def __del__(self) -> None:
170
+ """
171
+ Releases memory used by the frame
172
+ """
173
+
174
+ if self.program: self.program.release()
175
+ if self.vbo: self.vbo.release()
176
+ if self.vao: self.vao.release()
177
+ if self.frame_texture: self.frame_texture.release()
178
+ if self.depth_texture: self.depth_texture.release()
179
+ if self.framebuffer: self.framebuffer.release()
180
+ if self.pingpong_frame_texture: self.frame_texture.release()
181
+ if self.pingpong_depth_texture: self.depth_texture.release()
182
+ if self.pingpong_framebuffer: self.framebuffer.release()