basilisk-engine 0.1.3__py3-none-any.whl → 0.1.5__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 (86) hide show
  1. basilisk/__init__.py +13 -12
  2. basilisk/bsk_assets/cube.obj +48 -48
  3. basilisk/collisions/broad/broad_aabb.py +102 -102
  4. basilisk/collisions/broad/broad_bvh.py +137 -137
  5. basilisk/collisions/collider.py +95 -95
  6. basilisk/collisions/collider_handler.py +225 -225
  7. basilisk/collisions/narrow/contact_manifold.py +90 -90
  8. basilisk/collisions/narrow/dataclasses.py +32 -32
  9. basilisk/collisions/narrow/deprecated.py +46 -46
  10. basilisk/collisions/narrow/epa.py +91 -91
  11. basilisk/collisions/narrow/gjk.py +66 -66
  12. basilisk/collisions/narrow/graham_scan.py +24 -24
  13. basilisk/collisions/narrow/helper.py +29 -29
  14. basilisk/collisions/narrow/line_intersections.py +106 -106
  15. basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
  16. basilisk/config.py +2 -2
  17. basilisk/draw/draw.py +100 -100
  18. basilisk/draw/draw_handler.py +179 -179
  19. basilisk/draw/font_renderer.py +28 -28
  20. basilisk/engine.py +200 -195
  21. basilisk/generic/abstract_bvh.py +15 -15
  22. basilisk/generic/abstract_custom.py +133 -133
  23. basilisk/generic/collisions.py +70 -70
  24. basilisk/generic/input_validation.py +66 -66
  25. basilisk/generic/math.py +6 -6
  26. basilisk/generic/matrices.py +33 -33
  27. basilisk/generic/meshes.py +72 -72
  28. basilisk/generic/quat.py +137 -137
  29. basilisk/generic/quat_methods.py +7 -7
  30. basilisk/generic/raycast_result.py +23 -23
  31. basilisk/generic/vec3.py +143 -143
  32. basilisk/input/mouse.py +61 -61
  33. basilisk/input/path.py +14 -0
  34. basilisk/mesh/cube.py +33 -33
  35. basilisk/mesh/mesh.py +230 -230
  36. basilisk/mesh/mesh_from_data.py +130 -132
  37. basilisk/mesh/model.py +271 -271
  38. basilisk/mesh/narrow_aabb.py +89 -89
  39. basilisk/mesh/narrow_bvh.py +91 -91
  40. basilisk/mesh/narrow_primative.py +23 -23
  41. basilisk/nodes/helper.py +28 -28
  42. basilisk/nodes/node.py +680 -680
  43. basilisk/nodes/node_handler.py +95 -95
  44. basilisk/particles/particle_handler.py +63 -63
  45. basilisk/particles/particle_renderer.py +87 -87
  46. basilisk/physics/impulse.py +112 -112
  47. basilisk/physics/physics_body.py +43 -43
  48. basilisk/physics/physics_engine.py +35 -35
  49. basilisk/render/batch.py +86 -86
  50. basilisk/render/camera.py +204 -204
  51. basilisk/render/chunk.py +99 -99
  52. basilisk/render/chunk_handler.py +154 -154
  53. basilisk/render/frame.py +99 -182
  54. basilisk/render/framebuffer.py +106 -0
  55. basilisk/render/image.py +87 -75
  56. basilisk/render/image_handler.py +122 -122
  57. basilisk/render/light.py +96 -96
  58. basilisk/render/light_handler.py +58 -58
  59. basilisk/render/material.py +219 -219
  60. basilisk/render/material_handler.py +135 -135
  61. basilisk/render/post_process.py +133 -0
  62. basilisk/render/shader.py +109 -109
  63. basilisk/render/shader_handler.py +79 -79
  64. basilisk/render/sky.py +120 -120
  65. basilisk/scene.py +256 -250
  66. basilisk/shaders/batch.frag +276 -276
  67. basilisk/shaders/batch.vert +115 -115
  68. basilisk/shaders/crt.frag +32 -0
  69. basilisk/shaders/draw.frag +21 -21
  70. basilisk/shaders/draw.vert +21 -21
  71. basilisk/shaders/filter.frag +22 -22
  72. basilisk/shaders/frame.frag +12 -12
  73. basilisk/shaders/frame.vert +13 -13
  74. basilisk/shaders/geometry.frag +8 -8
  75. basilisk/shaders/geometry.vert +41 -41
  76. basilisk/shaders/normal.frag +59 -59
  77. basilisk/shaders/normal.vert +96 -96
  78. basilisk/shaders/particle.frag +71 -71
  79. basilisk/shaders/particle.vert +84 -84
  80. basilisk/shaders/sky.frag +9 -9
  81. basilisk/shaders/sky.vert +13 -13
  82. {basilisk_engine-0.1.3.dist-info → basilisk_engine-0.1.5.dist-info}/METADATA +38 -45
  83. basilisk_engine-0.1.5.dist-info/RECORD +101 -0
  84. {basilisk_engine-0.1.3.dist-info → basilisk_engine-0.1.5.dist-info}/WHEEL +1 -1
  85. basilisk_engine-0.1.3.dist-info/RECORD +0 -97
  86. {basilisk_engine-0.1.3.dist-info → basilisk_engine-0.1.5.dist-info}/top_level.txt +0 -0
@@ -1,155 +1,155 @@
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: set
19
- """Set containing recently updated chunks"""
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.engine.shader.program
32
-
33
- # List for the dynamic and static chunk dictionaries | [dyanmic: dict, static: dict]
34
- self.shader_groups = {None : ({}, {})}
35
- # self.chunks = [{}, {}]
36
- self.updated_chunks = set()
37
-
38
-
39
- def render(self) -> None:
40
- """
41
- Renders all the chunk batches in the camera's range
42
- Includes some view culling, but not frustum culling.
43
- """
44
-
45
- # Gets a rectanglur prism of chunks in the cameras view
46
- render_range_x, render_range_y, render_range_z = self.get_render_range()
47
-
48
- 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)]
49
-
50
- # Loop through all chunks in view and render
51
- for shader, group in self.shader_groups.items():
52
- if shader == None: shader = self.engine.shader
53
- for chunk in chunk_keys:
54
- # Render the chunk if it exists
55
- if chunk in group[0]: group[0][chunk].render()
56
- if chunk in group[1]: group[1][chunk].render()
57
-
58
-
59
- def update(self) -> None:
60
- """
61
- Updates all the chunks that have been updated since the last frame.
62
- """
63
-
64
- self.program = self.scene.engine.shader.program
65
-
66
- # Loop through the set of updated chunk keys and update the chunk
67
- removes = []
68
-
69
- for chunk in self.updated_chunks:
70
- if chunk.update(): continue
71
- removes.append(chunk)
72
-
73
- # Remove any empty chunks
74
- for chunk in removes:
75
- del self.shader_groups[chunk.shader][chunk.static][chunk.position]
76
-
77
- # Clears the set of updated chunks so that they are not updated unless they are updated again
78
- self.updated_chunks.clear()
79
-
80
- def update_all(self):
81
- self.program = self.scene.engine.shader.program
82
- for shader in self.shader_groups.values():
83
- for chunk in shader[0].values():
84
- self.updated_chunks.add(chunk)
85
- for chunk in shader[1].values():
86
- self.updated_chunks.add(chunk)
87
-
88
- def add(self, node: Node) -> Node:
89
- """
90
- Adds an existing node to its chunk. Updates the node's chunk reference
91
- """
92
-
93
- # The key of the chunk the node will be added to
94
- chunk_size = self.engine.config.chunk_size
95
- chunk_key = (int(node.x // chunk_size), int(node.y // chunk_size), int(node.z // chunk_size))
96
- shader = node.shader
97
-
98
- if shader not in self.shader_groups:
99
- self.shader_groups[shader] = ({}, {})
100
-
101
- # Ensure that the chunk exists
102
- if chunk_key not in self.shader_groups[shader][node.static]:
103
- chunk = Chunk(self, chunk_key, node.static, shader)
104
- self.shader_groups[shader][node.static][chunk_key] = chunk
105
-
106
- # Add the node to the chunk
107
- self.shader_groups[shader][node.static][chunk_key].add(node)
108
-
109
- # Update the chunk
110
- self.updated_chunks.add(self.shader_groups[shader][node.static][chunk_key])
111
-
112
- return Node
113
-
114
- def remove(self, node: Node) -> None:
115
- """
116
- Removes a node from the its chunk
117
- """
118
-
119
- if node == None: return
120
-
121
- # Remove the node
122
- chunk = node.chunk
123
- chunk.remove(node)
124
- node.chunk = None
125
-
126
- # Update the chunk
127
- self.updated_chunks.add(chunk)
128
-
129
- def get_render_range(self) -> tuple:
130
- """
131
- Returns a rectangluar prism of chunks that are in the camera's view.
132
- Tuple return is in form ((x1, x2), (y1, y2), (z1, z2))
133
- """
134
-
135
- cam_position = self.scene.camera.position # glm.vec3(x, y, z)
136
- fov = 40 # The range in which a direction will not be culled
137
-
138
- # Default to a cube of chunks around the camera extending view_distance chunks in each direction
139
- chunk_size = self.engine.config.chunk_size
140
- render_distance = self.engine.config.render_distance
141
- render_range_x = [int(cam_position.x // chunk_size - render_distance), int(cam_position.x // chunk_size + render_distance + 1)]
142
- render_range_y = [int(cam_position.y // chunk_size - render_distance), int(cam_position.y // chunk_size + render_distance + 1)]
143
- render_range_z = [int(cam_position.z // chunk_size - render_distance), int(cam_position.z // chunk_size + render_distance + 1)]
144
-
145
- # Remove chunks that the camera is facing away from
146
- render_range_x[1] -= render_distance * (180 - fov < self.scene.camera.yaw < 180 + fov) - 1
147
- render_range_x[0] += render_distance * (-fov < self.scene.camera.yaw < fov or self.scene.camera.yaw > 360 - fov) - 1
148
-
149
- render_range_y[0] += render_distance * (self.scene.camera.pitch > 25) - 1
150
- render_range_y[1] -= render_distance * (self.scene.camera.pitch < -25) - 1
151
-
152
- render_range_z[1] -= render_distance * (270 - fov < self.scene.camera.yaw < 270 + fov) - 1
153
- render_range_z[0] += render_distance * (90 - fov < self.scene.camera.yaw < 90 + fov) - 1
154
-
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: set
19
+ """Set containing recently updated chunks"""
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.engine.shader.program
32
+
33
+ # List for the dynamic and static chunk dictionaries | [dyanmic: dict, static: dict]
34
+ self.shader_groups = {None : ({}, {})}
35
+ # self.chunks = [{}, {}]
36
+ self.updated_chunks = set()
37
+
38
+
39
+ def render(self) -> None:
40
+ """
41
+ Renders all the chunk batches in the camera's range
42
+ Includes some view culling, but not frustum culling.
43
+ """
44
+
45
+ # Gets a rectanglur prism of chunks in the cameras view
46
+ render_range_x, render_range_y, render_range_z = self.get_render_range()
47
+
48
+ 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)]
49
+
50
+ # Loop through all chunks in view and render
51
+ for shader, group in self.shader_groups.items():
52
+ if shader == None: shader = self.engine.shader
53
+ for chunk in chunk_keys:
54
+ # Render the chunk if it exists
55
+ if chunk in group[0]: group[0][chunk].render()
56
+ if chunk in group[1]: group[1][chunk].render()
57
+
58
+
59
+ def update(self) -> None:
60
+ """
61
+ Updates all the chunks that have been updated since the last frame.
62
+ """
63
+
64
+ self.program = self.scene.engine.shader.program
65
+
66
+ # Loop through the set of updated chunk keys and update the chunk
67
+ removes = []
68
+
69
+ for chunk in self.updated_chunks:
70
+ if chunk.update(): continue
71
+ removes.append(chunk)
72
+
73
+ # Remove any empty chunks
74
+ for chunk in removes:
75
+ del self.shader_groups[chunk.shader][chunk.static][chunk.position]
76
+
77
+ # Clears the set of updated chunks so that they are not updated unless they are updated again
78
+ self.updated_chunks.clear()
79
+
80
+ def update_all(self):
81
+ self.program = self.scene.engine.shader.program
82
+ for shader in self.shader_groups.values():
83
+ for chunk in shader[0].values():
84
+ self.updated_chunks.add(chunk)
85
+ for chunk in shader[1].values():
86
+ self.updated_chunks.add(chunk)
87
+
88
+ def add(self, node: Node) -> Node:
89
+ """
90
+ Adds an existing node to its chunk. Updates the node's chunk reference
91
+ """
92
+
93
+ # The key of the chunk the node will be added to
94
+ chunk_size = self.engine.config.chunk_size
95
+ chunk_key = (int(node.x // chunk_size), int(node.y // chunk_size), int(node.z // chunk_size))
96
+ shader = node.shader
97
+
98
+ if shader not in self.shader_groups:
99
+ self.shader_groups[shader] = ({}, {})
100
+
101
+ # Ensure that the chunk exists
102
+ if chunk_key not in self.shader_groups[shader][node.static]:
103
+ chunk = Chunk(self, chunk_key, node.static, shader)
104
+ self.shader_groups[shader][node.static][chunk_key] = chunk
105
+
106
+ # Add the node to the chunk
107
+ self.shader_groups[shader][node.static][chunk_key].add(node)
108
+
109
+ # Update the chunk
110
+ self.updated_chunks.add(self.shader_groups[shader][node.static][chunk_key])
111
+
112
+ return Node
113
+
114
+ def remove(self, node: Node) -> None:
115
+ """
116
+ Removes a node from the its chunk
117
+ """
118
+
119
+ if node == None: return
120
+
121
+ # Remove the node
122
+ chunk = node.chunk
123
+ chunk.remove(node)
124
+ node.chunk = None
125
+
126
+ # Update the chunk
127
+ self.updated_chunks.add(chunk)
128
+
129
+ def get_render_range(self) -> tuple:
130
+ """
131
+ Returns a rectangluar prism of chunks that are in the camera's view.
132
+ Tuple return is in form ((x1, x2), (y1, y2), (z1, z2))
133
+ """
134
+
135
+ cam_position = self.scene.camera.position # glm.vec3(x, y, z)
136
+ fov = 40 # The range in which a direction will not be culled
137
+
138
+ # Default to a cube of chunks around the camera extending view_distance chunks in each direction
139
+ chunk_size = self.engine.config.chunk_size
140
+ render_distance = self.engine.config.render_distance
141
+ render_range_x = [int(cam_position.x // chunk_size - render_distance), int(cam_position.x // chunk_size + render_distance + 1)]
142
+ render_range_y = [int(cam_position.y // chunk_size - render_distance), int(cam_position.y // chunk_size + render_distance + 1)]
143
+ render_range_z = [int(cam_position.z // chunk_size - render_distance), int(cam_position.z // chunk_size + render_distance + 1)]
144
+
145
+ # Remove chunks that the camera is facing away from
146
+ render_range_x[1] -= render_distance * (180 - fov < self.scene.camera.yaw < 180 + fov) - 1
147
+ render_range_x[0] += render_distance * (-fov < self.scene.camera.yaw < fov or self.scene.camera.yaw > 360 - fov) - 1
148
+
149
+ render_range_y[0] += render_distance * (self.scene.camera.pitch > 25) - 1
150
+ render_range_y[1] -= render_distance * (self.scene.camera.pitch < -25) - 1
151
+
152
+ render_range_z[1] -= render_distance * (270 - fov < self.scene.camera.yaw < 270 + fov) - 1
153
+ render_range_z[0] += render_distance * (90 - fov < self.scene.camera.yaw < 90 + fov) - 1
154
+
155
155
  return (render_range_x, render_range_y, render_range_z)
basilisk/render/frame.py CHANGED
@@ -1,182 +1,99 @@
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(self.engine.root + f'/shaders/{vert}.vert') as file:
92
- vertex_shader = file.read()
93
- with open(self.engine.root + f'/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()
1
+ import numpy as np
2
+ import moderngl as mgl
3
+ from .shader import Shader
4
+ from .framebuffer import Framebuffer
5
+
6
+ from .post_process import PostProcess
7
+
8
+ class Frame:
9
+ shader: Shader=None
10
+ vbo: mgl.Buffer=None
11
+ vao: mgl.VertexArray=None
12
+ framebuffer: mgl.Framebuffer=None
13
+
14
+ def __init__(self, scene, resolution=1, filter=(mgl.LINEAR, mgl.LINEAR)) -> None:
15
+ """
16
+ Basilisk render destination.
17
+ Can be used to render to the screen or for headless rendering
18
+ """
19
+
20
+ self.scene = scene
21
+ self.engine = scene.engine
22
+ self.ctx = scene.ctx
23
+
24
+ # Load framebuffer
25
+ self.resolution = resolution
26
+ self.filter = filter
27
+ size = tuple(map(lambda x: int(x * self.resolution), self.engine.win_size))
28
+ self.framebuffer = Framebuffer(self.engine, size=size, filter=self.filter)
29
+ self.ping_pong_buffer = Framebuffer(self.engine, size=size, filter=self.filter)
30
+
31
+ # Load Shaders
32
+ self.shader = Shader(self.engine, self.engine.root + '/shaders/frame.vert', self.engine.root + '/shaders/frame.frag')
33
+ self.scene.shader_handler.add(self.shader)
34
+
35
+ # Load VAO
36
+ self.vbo = self.ctx.buffer(np.array([[-1, -1, 0, 0, 0], [1, -1, 0, 1, 0], [1, 1, 0, 1, 1], [-1, 1, 0, 0, 1], [-1, -1, 0, 0, 0], [1, 1, 0, 1, 1]], dtype='f4'))
37
+ self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
38
+
39
+ # TEMP TESTING
40
+ self.post_processes = []
41
+
42
+
43
+ def render(self) -> None:
44
+ """
45
+ Renders the current frame to the screen
46
+ """
47
+
48
+ for process in self.post_processes:
49
+ process.apply(self.framebuffer, self.ping_pong_buffer)
50
+
51
+ temp = self.framebuffer
52
+ self.framebuffer = self.ping_pong_buffer
53
+ self.ping_pong_buffer = temp
54
+
55
+ self.ctx.screen.use()
56
+ self.shader.program['screenTexture'] = 0
57
+ self.framebuffer.texture.use(location=0)
58
+ self.vao.render()
59
+
60
+
61
+ def use(self) -> None:
62
+ """
63
+ Uses the frame as a render target
64
+ """
65
+
66
+ self.framebuffer.use()
67
+ self.framebuffer.clear()
68
+
69
+ def add_post_process(self, post_process: PostProcess) -> PostProcess:
70
+ """
71
+ Add a post process to the frames post process stack
72
+ """
73
+
74
+ self.post_processes.append(post_process)
75
+ return post_process
76
+
77
+ def save(self, destination: str=None) -> None:
78
+ """
79
+ Saves the frame as an image to the given file destination
80
+ """
81
+
82
+ self.framebuffer.save(destination)
83
+
84
+ def resize(self, size: tuple[int]=None) -> None:
85
+ """
86
+ Resize the frame to the given size. None for window size
87
+ """
88
+
89
+ size = tuple(map(lambda x: int(x * self.resolution), self.engine.win_size))
90
+ self.framebuffer.resize(size)
91
+ self.ping_pong_buffer.resize(size)
92
+
93
+ def __del__(self) -> None:
94
+ """
95
+ Releases memory used by the frame
96
+ """
97
+
98
+ if self.vbo: self.vbo.release()
99
+ if self.vao: self.vao.release()