basilisk-engine 0.1.41__py3-none-any.whl → 0.1.43__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of basilisk-engine might be problematic. Click here for more details.
- basilisk/__init__.py +26 -26
- basilisk/audio/sound.py +27 -27
- basilisk/bsk_assets/cube.obj +48 -48
- basilisk/collisions/broad/broad_aabb.py +102 -102
- basilisk/collisions/broad/broad_bvh.py +137 -137
- basilisk/collisions/collider.py +95 -95
- basilisk/collisions/collider_handler.py +225 -225
- basilisk/collisions/narrow/contact_manifold.py +95 -95
- basilisk/collisions/narrow/dataclasses.py +34 -34
- basilisk/collisions/narrow/deprecated.py +46 -46
- basilisk/collisions/narrow/epa.py +91 -91
- basilisk/collisions/narrow/gjk.py +66 -66
- basilisk/collisions/narrow/graham_scan.py +24 -24
- basilisk/collisions/narrow/helper.py +29 -29
- basilisk/collisions/narrow/line_intersections.py +106 -106
- basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
- basilisk/config.py +53 -53
- basilisk/draw/draw.py +100 -100
- basilisk/draw/draw_handler.py +178 -178
- basilisk/draw/font_renderer.py +28 -28
- basilisk/engine.py +169 -171
- basilisk/generic/abstract_bvh.py +15 -15
- basilisk/generic/abstract_custom.py +133 -133
- basilisk/generic/collisions.py +70 -70
- basilisk/generic/input_validation.py +82 -82
- basilisk/generic/math.py +17 -17
- basilisk/generic/matrices.py +35 -35
- basilisk/generic/meshes.py +72 -72
- basilisk/generic/quat.py +142 -142
- basilisk/generic/quat_methods.py +7 -7
- basilisk/generic/raycast_result.py +26 -26
- basilisk/generic/vec3.py +143 -143
- basilisk/input_output/IO_handler.py +91 -91
- basilisk/input_output/clock.py +49 -49
- basilisk/input_output/keys.py +43 -43
- basilisk/input_output/mouse.py +90 -90
- basilisk/input_output/path.py +14 -14
- basilisk/mesh/cube.py +33 -33
- basilisk/mesh/mesh.py +233 -233
- basilisk/mesh/mesh_from_data.py +150 -150
- basilisk/mesh/model.py +271 -271
- basilisk/mesh/narrow_aabb.py +89 -89
- basilisk/mesh/narrow_bvh.py +91 -91
- basilisk/mesh/narrow_primative.py +23 -23
- basilisk/nodes/helper.py +28 -28
- basilisk/nodes/node.py +709 -709
- basilisk/nodes/node_handler.py +97 -97
- basilisk/particles/particle_handler.py +64 -64
- basilisk/particles/particle_renderer.py +93 -93
- basilisk/physics/impulse.py +112 -112
- basilisk/physics/physics_body.py +43 -43
- basilisk/physics/physics_engine.py +35 -35
- basilisk/render/batch.py +103 -103
- basilisk/render/bloom.py +118 -124
- basilisk/render/camera.py +260 -260
- basilisk/render/chunk.py +113 -108
- basilisk/render/chunk_handler.py +167 -167
- basilisk/render/frame.py +130 -128
- basilisk/render/framebuffer.py +192 -212
- basilisk/render/image.py +120 -120
- basilisk/render/image_handler.py +120 -120
- basilisk/render/light.py +96 -96
- basilisk/render/light_handler.py +58 -58
- basilisk/render/material.py +232 -232
- basilisk/render/material_handler.py +133 -133
- basilisk/render/post_process.py +180 -180
- basilisk/render/shader.py +135 -134
- basilisk/render/shader_handler.py +109 -94
- basilisk/render/sky.py +119 -121
- basilisk/scene.py +287 -288
- basilisk/shaders/batch.frag +293 -293
- basilisk/shaders/batch.vert +117 -117
- basilisk/shaders/bloom_downsample.frag +23 -42
- basilisk/shaders/bloom_upsample.frag +33 -33
- basilisk/shaders/crt.frag +34 -34
- basilisk/shaders/draw.frag +27 -27
- basilisk/shaders/draw.vert +25 -25
- basilisk/shaders/filter.frag +22 -22
- basilisk/shaders/frame.frag +13 -13
- basilisk/shaders/frame.vert +13 -13
- basilisk/shaders/frame_hdr.frag +27 -27
- basilisk/shaders/geometry.frag +10 -10
- basilisk/shaders/geometry.vert +41 -41
- basilisk/shaders/normal.frag +62 -62
- basilisk/shaders/normal.vert +96 -96
- basilisk/shaders/particle.frag +81 -81
- basilisk/shaders/particle.vert +86 -86
- basilisk/shaders/sky.frag +23 -23
- basilisk/shaders/sky.vert +13 -13
- {basilisk_engine-0.1.41.dist-info → basilisk_engine-0.1.43.dist-info}/METADATA +89 -89
- basilisk_engine-0.1.43.dist-info/RECORD +111 -0
- {basilisk_engine-0.1.41.dist-info → basilisk_engine-0.1.43.dist-info}/WHEEL +1 -1
- basilisk_engine-0.1.41.dist-info/RECORD +0 -111
- {basilisk_engine-0.1.41.dist-info → basilisk_engine-0.1.43.dist-info}/top_level.txt +0 -0
basilisk/render/frame.py
CHANGED
|
@@ -1,129 +1,131 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import moderngl as mgl
|
|
3
|
-
from .shader import Shader
|
|
4
|
-
from .image import Image
|
|
5
|
-
from .framebuffer import Framebuffer
|
|
6
|
-
from .post_process import PostProcess
|
|
7
|
-
from .bloom import Bloom
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Frame:
|
|
11
|
-
shader: Shader=None
|
|
12
|
-
vbo: mgl.Buffer=None
|
|
13
|
-
vao: mgl.VertexArray=None
|
|
14
|
-
|
|
15
|
-
def __init__(self, engine, scale: float=1.0, linear_filter: bool=False) -> None:
|
|
16
|
-
"""
|
|
17
|
-
Basilisk render destination.
|
|
18
|
-
Can be used to render to the screen or for headless rendering
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
self.engine = engine
|
|
22
|
-
self.ctx = engine.ctx
|
|
23
|
-
|
|
24
|
-
# Load framebuffer
|
|
25
|
-
self.output_buffer = Framebuffer(self.engine, scale=scale, n_color_attachments=2, linear_filter=linear_filter)
|
|
26
|
-
self.input_buffer = Framebuffer(self.engine, scale=scale, n_color_attachments=3, linear_filter=linear_filter)
|
|
27
|
-
self.ping_pong_buffer = Framebuffer(self.engine, scale=scale, n_color_attachments=3, linear_filter=linear_filter)
|
|
28
|
-
|
|
29
|
-
# Load Shaders
|
|
30
|
-
self.shader = Shader(self.engine, self.engine.root + '/shaders/frame.vert', self.engine.root + '/shaders/frame_hdr.frag')
|
|
31
|
-
|
|
32
|
-
# Load VAO
|
|
33
|
-
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'))
|
|
34
|
-
self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
|
|
35
|
-
|
|
36
|
-
self.bloom = Bloom(self)
|
|
37
|
-
|
|
38
|
-
# TEMP TESTING
|
|
39
|
-
self.post_processes = []
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def scene_render(self, target=None) -> None:
|
|
43
|
-
"""
|
|
44
|
-
Renders the current frame to the screen or the given target
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
if self.engine.event_resize: self.bloom.generate_bloom_buffers()
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# for process in self.post_processes:
|
|
51
|
-
# self.ping_pong_buffer = process.apply([('screenTexture', self.input_buffer)], self.ping_pong_buffer)
|
|
52
|
-
|
|
53
|
-
# temp = self.input_buffer
|
|
54
|
-
# self.input_buffer = self.ping_pong_buffer
|
|
55
|
-
# self.ping_pong_buffer = temp
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
self.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
self.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"""
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
self.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"""
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
self.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"""
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@property
|
|
1
|
+
import numpy as np
|
|
2
|
+
import moderngl as mgl
|
|
3
|
+
from .shader import Shader
|
|
4
|
+
from .image import Image
|
|
5
|
+
from .framebuffer import Framebuffer
|
|
6
|
+
from .post_process import PostProcess
|
|
7
|
+
from .bloom import Bloom
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Frame:
|
|
11
|
+
shader: Shader=None
|
|
12
|
+
vbo: mgl.Buffer=None
|
|
13
|
+
vao: mgl.VertexArray=None
|
|
14
|
+
|
|
15
|
+
def __init__(self, engine, scale: float=1.0, linear_filter: bool=False) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Basilisk render destination.
|
|
18
|
+
Can be used to render to the screen or for headless rendering
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
self.engine = engine
|
|
22
|
+
self.ctx = engine.ctx
|
|
23
|
+
|
|
24
|
+
# Load framebuffer
|
|
25
|
+
self.output_buffer = Framebuffer(self.engine, scale=scale, n_color_attachments=2, linear_filter=linear_filter)
|
|
26
|
+
self.input_buffer = Framebuffer(self.engine, scale=scale, n_color_attachments=3, linear_filter=linear_filter)
|
|
27
|
+
self.ping_pong_buffer = Framebuffer(self.engine, scale=scale, n_color_attachments=3, linear_filter=linear_filter)
|
|
28
|
+
|
|
29
|
+
# Load Shaders
|
|
30
|
+
self.shader = Shader(self.engine, self.engine.root + '/shaders/frame.vert', self.engine.root + '/shaders/frame_hdr.frag')
|
|
31
|
+
|
|
32
|
+
# Load VAO
|
|
33
|
+
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'))
|
|
34
|
+
self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
|
|
35
|
+
|
|
36
|
+
self.bloom = Bloom(self)
|
|
37
|
+
|
|
38
|
+
# TEMP TESTING
|
|
39
|
+
self.post_processes = []
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def scene_render(self, target=None) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Renders the current frame to the screen or the given target
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if self.engine.event_resize: self.bloom.generate_bloom_buffers()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# for process in self.post_processes:
|
|
51
|
+
# self.ping_pong_buffer = process.apply([('screenTexture', self.input_buffer)], self.ping_pong_buffer)
|
|
52
|
+
|
|
53
|
+
# temp = self.input_buffer
|
|
54
|
+
# self.input_buffer = self.ping_pong_buffer
|
|
55
|
+
# self.ping_pong_buffer = temp
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if self.engine.config.bloom_enabled:
|
|
59
|
+
self.bloom.render()
|
|
60
|
+
self.shader.bind(self.bloom.texture, 'bloomTexture', 1)
|
|
61
|
+
|
|
62
|
+
target.use() if target else self.output_buffer.use()
|
|
63
|
+
self.shader.bind(self.input_buffer.texture, 'screenTexture', 0)
|
|
64
|
+
self.vao.render()
|
|
65
|
+
|
|
66
|
+
def render(self, target=None) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Renders the current frame to the screen or the given target
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
self.output_buffer.render(target=target)
|
|
72
|
+
|
|
73
|
+
def use(self) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Uses the frame as a render target
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
self.input_buffer.use()
|
|
79
|
+
|
|
80
|
+
def add_post_process(self, post_process: PostProcess) -> PostProcess:
|
|
81
|
+
"""
|
|
82
|
+
Add a post process to the frames post process stack
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
self.post_processes.append(post_process)
|
|
86
|
+
return post_process
|
|
87
|
+
|
|
88
|
+
def save(self, destination: str=None) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Saves the frame as an image to the given file destination
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
self.output_buffer.save(destination)
|
|
94
|
+
|
|
95
|
+
def clear(self):
|
|
96
|
+
"""
|
|
97
|
+
Clears the framebuffer of the frame
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
self.input_buffer.clear()
|
|
101
|
+
self.output_buffer.clear()
|
|
102
|
+
self.bloom.clear()
|
|
103
|
+
|
|
104
|
+
def bind(self, sampler: mgl.Texture | mgl.TextureArray | mgl.TextureCube | Image, name: str, slot: int=None):
|
|
105
|
+
"""
|
|
106
|
+
Binds a texture to the fbo's shader
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
self.shader.bind(sampler, name, slot)
|
|
110
|
+
|
|
111
|
+
def resize(self) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Resize the frame to the given size. None for window size
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
self.input_buffer.resize()
|
|
117
|
+
self.ping_pong_buffer.resize()
|
|
118
|
+
self.generate_bloom_buffers()
|
|
119
|
+
|
|
120
|
+
def __del__(self) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Releases memory used by the frame
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
if self.vbo: self.vbo.release()
|
|
126
|
+
if self.vao: self.vao.release()
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def texture(self): return self.output_buffer.texture
|
|
130
|
+
@property
|
|
129
131
|
def depth(self): return self.output_buffer.depth
|
basilisk/render/framebuffer.py
CHANGED
|
@@ -1,213 +1,193 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import moderngl as mgl
|
|
3
|
-
from PIL import Image
|
|
4
|
-
from ..render.shader import Shader
|
|
5
|
-
from ..generic.input_validation import validate_int
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Framebuffer:
|
|
9
|
-
engine: ...
|
|
10
|
-
"""Reference to the parent engine"""
|
|
11
|
-
size: tuple[int] | None=None
|
|
12
|
-
"""The dimensions of the framebuffer (x, y). Defaults to window size if None"""
|
|
13
|
-
scale: float=1.0
|
|
14
|
-
"""Scaling factor applied to the size. Best for use with default size"""
|
|
15
|
-
texture_filter: tuple[int]=(mgl.NEAREST, mgl.NEAREST)
|
|
16
|
-
"""The filter applied to the texture when rendering"""
|
|
17
|
-
fbo: mgl.Framebuffer=None
|
|
18
|
-
"""The core framebuffer the object provides abstraction for."""
|
|
19
|
-
texture: mgl.Texture=None
|
|
20
|
-
"""The color texture of the framebuffer"""
|
|
21
|
-
depth: mgl.Texture=None
|
|
22
|
-
"""The depth texture of the framebuffer"""
|
|
23
|
-
_color_attachments = None
|
|
24
|
-
""""""
|
|
25
|
-
_depth_attachment = None
|
|
26
|
-
""""""
|
|
27
|
-
|
|
28
|
-
def __init__(self, engine: ..., shader: Shader=None, size: tuple[int]=None, n_color_attachments: int=1, scale: float=1.0, linear_filter: bool=True) -> None:
|
|
29
|
-
"""
|
|
30
|
-
Abstraction of the MGL framebuffer.
|
|
31
|
-
Has the given number of color attachements (4-component) and 1 depth attachment.
|
|
32
|
-
All textures are of uniform size.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
self.engine = engine
|
|
36
|
-
self.ctx = engine.ctx
|
|
37
|
-
self._size = size
|
|
38
|
-
self.scale = scale
|
|
39
|
-
self.shader = shader
|
|
40
|
-
self.texture_filter = (mgl.LINEAR, mgl.LINEAR) if linear_filter else (mgl.NEAREST, mgl.NEAREST)
|
|
41
|
-
self.n_attachments = n_color_attachments
|
|
42
|
-
|
|
43
|
-
self.load_pipeline()
|
|
44
|
-
self.generate_fbo()
|
|
45
|
-
|
|
46
|
-
self.engine.fbos.append(self)
|
|
47
|
-
|
|
48
|
-
def generate_fbo(self):
|
|
49
|
-
"""
|
|
50
|
-
Generates fresh depth texture and color textures and creates an FBO
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
# Release existing memory
|
|
54
|
-
if self._color_attachments: [tex.release() for tex in self._color_attachments]
|
|
55
|
-
if self._depth_attachment: self._depth_attachment.release()
|
|
56
|
-
|
|
57
|
-
# Create textures
|
|
58
|
-
self._color_attachments = [self.ctx.texture(self.size, components=4, dtype='f4') for i in range(self.n_attachments)]
|
|
59
|
-
for tex in self._color_attachments:
|
|
60
|
-
tex.filter = self.texture_filter
|
|
61
|
-
tex.repeat_x = False
|
|
62
|
-
tex.repeat_y = False
|
|
63
|
-
|
|
64
|
-
self._depth_attachment = self.ctx.depth_texture(self.size)
|
|
65
|
-
|
|
66
|
-
# Create the internal fbo
|
|
67
|
-
self.fbo = self.ctx.framebuffer(self._color_attachments, self._depth_attachment)
|
|
68
|
-
|
|
69
|
-
# Set the show attachment to default
|
|
70
|
-
self._show = -1
|
|
71
|
-
self.show = 0
|
|
72
|
-
|
|
73
|
-
def resize(self, new_size: tuple[int]=None) -> None:
|
|
74
|
-
"""
|
|
75
|
-
Update set size framebuffers with the given size.
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
# Check that we are not updating the size to the existing size and
|
|
79
|
-
if self._size and self._size == new_size: return
|
|
80
|
-
|
|
81
|
-
# If we have a set size, update with the given size
|
|
82
|
-
if self._size and new_size: self._size = new_size
|
|
83
|
-
|
|
84
|
-
# Update the textures and fbo
|
|
85
|
-
self.generate_fbo()
|
|
86
|
-
|
|
87
|
-
def load_pipeline(self) -> None:
|
|
88
|
-
"""
|
|
89
|
-
Loads the shader, vbo, and vao used to display the fbo
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
# Load Shaders
|
|
93
|
-
if not self.shader: self.shader = Shader(self.engine, self.engine.root + '/shaders/frame.vert', self.engine.root + '/shaders/frame.frag')
|
|
94
|
-
self.engine.shader_handler.add(self.shader)
|
|
95
|
-
|
|
96
|
-
# Load VAO
|
|
97
|
-
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'))
|
|
98
|
-
self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
|
|
99
|
-
|
|
100
|
-
def render(self, target=None, color_attachment: int=0, auto_bind=True) -> None:
|
|
101
|
-
"""
|
|
102
|
-
Render the fbo to the screen
|
|
103
|
-
If the fbo has multiple attachments, show will specifiy which is shown
|
|
104
|
-
Depth is considered the last show element
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
# if not isinstance(show, type(None)): self.show = show
|
|
108
|
-
|
|
109
|
-
target.use() if target else self.ctx.screen.use()
|
|
110
|
-
|
|
111
|
-
if auto_bind: self.bind(self.color_attachments[min(color_attachment, len(self.color_attachments) - 1)], 'screenTexture', 0)
|
|
112
|
-
self.vao.render()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def use(self) -> None:
|
|
116
|
-
"""
|
|
117
|
-
Select this framebuffer for use
|
|
118
|
-
"""
|
|
119
|
-
|
|
120
|
-
self.fbo.use()
|
|
121
|
-
|
|
122
|
-
def clear(self, color: tuple=(0, 0, 0, 0)) -> None:
|
|
123
|
-
"""
|
|
124
|
-
Clear all data currently in the textures (set to black)
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
self.fbo.clear()
|
|
128
|
-
|
|
129
|
-
def bind(self, sampler: mgl.Texture | mgl.TextureArray | mgl.TextureCube , name: str, slot: int=None):
|
|
130
|
-
"""
|
|
131
|
-
Binds a texture to the fbo's shader
|
|
132
|
-
"""
|
|
133
|
-
|
|
134
|
-
self.shader.bind(sampler, name, slot)
|
|
135
|
-
|
|
136
|
-
def save(self, destination: str=None) -> None:
|
|
137
|
-
"""
|
|
138
|
-
Saves the frame as an image to the given file destination
|
|
139
|
-
"""
|
|
140
|
-
|
|
141
|
-
path = destination if destination else 'screenshot'
|
|
142
|
-
|
|
143
|
-
data = self.fbo.read(components=3, alignment=1)
|
|
144
|
-
img = Image.frombytes('RGB', self.size, data).transpose(Image.FLIP_TOP_BOTTOM)
|
|
145
|
-
img.save(f'{path}.png')
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@property
|
|
149
|
-
def size(self) -> tuple[int]:
|
|
150
|
-
"""Size of the textures in the fbo in pixels (x: int, y: int)"""
|
|
151
|
-
size = self._size if self._size else self.engine.win_size
|
|
152
|
-
size = tuple(map((lambda x: int(x * self.scale)), size))
|
|
153
|
-
return size
|
|
154
|
-
@property
|
|
155
|
-
def texture(self) -> mgl.Texture:
|
|
156
|
-
"""First color attachment in the fbo"""
|
|
157
|
-
return self._color_attachments[0]
|
|
158
|
-
@property
|
|
159
|
-
def color_attachments(self) -> list[mgl.Texture]:
|
|
160
|
-
"""List of all color attachments in the fbo"""
|
|
161
|
-
return self._color_attachments
|
|
162
|
-
@property
|
|
163
|
-
def depth(self) -> mgl.Texture:
|
|
164
|
-
"""Depth attachment of the fbo"""
|
|
165
|
-
return self._depth_attachment
|
|
166
|
-
@property
|
|
167
|
-
def data(self) -> bytes:
|
|
168
|
-
"""Reads the data from the fbo"""
|
|
169
|
-
return self.fbo.read()
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return self.size
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
# self.shader.program['screenTexture'] = value+1
|
|
194
|
-
src.use(location=value+1)
|
|
195
|
-
|
|
196
|
-
def __repr__(self) -> str:
|
|
197
|
-
return f'<bsk.Framebuffer | size: {self.size}>'
|
|
198
|
-
|
|
199
|
-
def __del__(self) -> None:
|
|
200
|
-
"""
|
|
201
|
-
Releases all memory used by the fbo
|
|
202
|
-
"""
|
|
203
|
-
|
|
204
|
-
if self._color_attachments: [tex.release() for tex in self._color_attachments]
|
|
205
|
-
if self._depth_attachment: self._depth_attachment.release()
|
|
206
|
-
if self.fbo: self.fbo.release()
|
|
207
|
-
if self.vbo: self.vbo.release()
|
|
208
|
-
if self.vao: self.vao.release()
|
|
209
|
-
if self.shader:
|
|
210
|
-
if self.shader in self.engine.shader_handler.shaders: self.engine.shader_handler.shaders.remove(self.shader)
|
|
211
|
-
self.shader.__del__()
|
|
212
|
-
|
|
1
|
+
import numpy as np
|
|
2
|
+
import moderngl as mgl
|
|
3
|
+
from PIL import Image
|
|
4
|
+
from ..render.shader import Shader
|
|
5
|
+
from ..generic.input_validation import validate_int
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Framebuffer:
|
|
9
|
+
engine: ...
|
|
10
|
+
"""Reference to the parent engine"""
|
|
11
|
+
size: tuple[int] | None=None
|
|
12
|
+
"""The dimensions of the framebuffer (x, y). Defaults to window size if None"""
|
|
13
|
+
scale: float=1.0
|
|
14
|
+
"""Scaling factor applied to the size. Best for use with default size"""
|
|
15
|
+
texture_filter: tuple[int]=(mgl.NEAREST, mgl.NEAREST)
|
|
16
|
+
"""The filter applied to the texture when rendering"""
|
|
17
|
+
fbo: mgl.Framebuffer=None
|
|
18
|
+
"""The core framebuffer the object provides abstraction for."""
|
|
19
|
+
texture: mgl.Texture=None
|
|
20
|
+
"""The color texture of the framebuffer"""
|
|
21
|
+
depth: mgl.Texture=None
|
|
22
|
+
"""The depth texture of the framebuffer"""
|
|
23
|
+
_color_attachments = None
|
|
24
|
+
""""""
|
|
25
|
+
_depth_attachment = None
|
|
26
|
+
""""""
|
|
27
|
+
|
|
28
|
+
def __init__(self, engine: ..., shader: Shader=None, size: tuple[int]=None, n_color_attachments: int=1, scale: float=1.0, linear_filter: bool=True) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Abstraction of the MGL framebuffer.
|
|
31
|
+
Has the given number of color attachements (4-component) and 1 depth attachment.
|
|
32
|
+
All textures are of uniform size.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
self.engine = engine
|
|
36
|
+
self.ctx = engine.ctx
|
|
37
|
+
self._size = size
|
|
38
|
+
self.scale = scale
|
|
39
|
+
self.shader = shader
|
|
40
|
+
self.texture_filter = (mgl.LINEAR, mgl.LINEAR) if linear_filter else (mgl.NEAREST, mgl.NEAREST)
|
|
41
|
+
self.n_attachments = n_color_attachments
|
|
42
|
+
|
|
43
|
+
self.load_pipeline()
|
|
44
|
+
self.generate_fbo()
|
|
45
|
+
|
|
46
|
+
self.engine.fbos.append(self)
|
|
47
|
+
|
|
48
|
+
def generate_fbo(self):
|
|
49
|
+
"""
|
|
50
|
+
Generates fresh depth texture and color textures and creates an FBO
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
# Release existing memory
|
|
54
|
+
if self._color_attachments: [tex.release() for tex in self._color_attachments]
|
|
55
|
+
if self._depth_attachment: self._depth_attachment.release()
|
|
56
|
+
|
|
57
|
+
# Create textures
|
|
58
|
+
self._color_attachments = [self.ctx.texture(self.size, components=4, dtype='f4') for i in range(self.n_attachments)]
|
|
59
|
+
for tex in self._color_attachments:
|
|
60
|
+
tex.filter = self.texture_filter
|
|
61
|
+
tex.repeat_x = False
|
|
62
|
+
tex.repeat_y = False
|
|
63
|
+
|
|
64
|
+
self._depth_attachment = self.ctx.depth_texture(self.size)
|
|
65
|
+
|
|
66
|
+
# Create the internal fbo
|
|
67
|
+
self.fbo = self.ctx.framebuffer(self._color_attachments, self._depth_attachment)
|
|
68
|
+
|
|
69
|
+
# Set the show attachment to default
|
|
70
|
+
self._show = -1
|
|
71
|
+
self.show = 0
|
|
72
|
+
|
|
73
|
+
def resize(self, new_size: tuple[int]=None) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Update set size framebuffers with the given size.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# Check that we are not updating the size to the existing size and
|
|
79
|
+
if self._size and self._size == new_size: return
|
|
80
|
+
|
|
81
|
+
# If we have a set size, update with the given size
|
|
82
|
+
if self._size and new_size: self._size = new_size
|
|
83
|
+
|
|
84
|
+
# Update the textures and fbo
|
|
85
|
+
self.generate_fbo()
|
|
86
|
+
|
|
87
|
+
def load_pipeline(self) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Loads the shader, vbo, and vao used to display the fbo
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
# Load Shaders
|
|
93
|
+
if not self.shader: self.shader = Shader(self.engine, self.engine.root + '/shaders/frame.vert', self.engine.root + '/shaders/frame.frag')
|
|
94
|
+
self.engine.shader_handler.add(self.shader)
|
|
95
|
+
|
|
96
|
+
# Load VAO
|
|
97
|
+
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'))
|
|
98
|
+
self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
|
|
99
|
+
|
|
100
|
+
def render(self, target=None, color_attachment: int=0, auto_bind=True) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Render the fbo to the screen
|
|
103
|
+
If the fbo has multiple attachments, show will specifiy which is shown
|
|
104
|
+
Depth is considered the last show element
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
# if not isinstance(show, type(None)): self.show = show
|
|
108
|
+
|
|
109
|
+
target.use() if target else self.ctx.screen.use()
|
|
110
|
+
|
|
111
|
+
if auto_bind: self.bind(self.color_attachments[min(color_attachment, len(self.color_attachments) - 1)], 'screenTexture', 0)
|
|
112
|
+
self.vao.render()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def use(self) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Select this framebuffer for use
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
self.fbo.use()
|
|
121
|
+
|
|
122
|
+
def clear(self, color: tuple=(0, 0, 0, 0)) -> None:
|
|
123
|
+
"""
|
|
124
|
+
Clear all data currently in the textures (set to black)
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
self.fbo.clear(color=color)
|
|
128
|
+
|
|
129
|
+
def bind(self, sampler: mgl.Texture | mgl.TextureArray | mgl.TextureCube , name: str, slot: int=None):
|
|
130
|
+
"""
|
|
131
|
+
Binds a texture to the fbo's shader
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
self.shader.bind(sampler, name, slot)
|
|
135
|
+
|
|
136
|
+
def save(self, destination: str=None) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Saves the frame as an image to the given file destination
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
path = destination if destination else 'screenshot'
|
|
142
|
+
|
|
143
|
+
data = self.fbo.read(components=3, alignment=1)
|
|
144
|
+
img = Image.frombytes('RGB', self.size, data).transpose(Image.FLIP_TOP_BOTTOM)
|
|
145
|
+
img.save(f'{path}.png')
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def size(self) -> tuple[int]:
|
|
150
|
+
"""Size of the textures in the fbo in pixels (x: int, y: int)"""
|
|
151
|
+
size = self._size if self._size else self.engine.win_size
|
|
152
|
+
size = tuple(map((lambda x: int(x * self.scale)), size))
|
|
153
|
+
return size
|
|
154
|
+
@property
|
|
155
|
+
def texture(self) -> mgl.Texture:
|
|
156
|
+
"""First color attachment in the fbo"""
|
|
157
|
+
return self._color_attachments[0]
|
|
158
|
+
@property
|
|
159
|
+
def color_attachments(self) -> list[mgl.Texture]:
|
|
160
|
+
"""List of all color attachments in the fbo"""
|
|
161
|
+
return self._color_attachments
|
|
162
|
+
@property
|
|
163
|
+
def depth(self) -> mgl.Texture:
|
|
164
|
+
"""Depth attachment of the fbo"""
|
|
165
|
+
return self._depth_attachment
|
|
166
|
+
@property
|
|
167
|
+
def data(self) -> bytes:
|
|
168
|
+
"""Reads the data from the fbo"""
|
|
169
|
+
return self.fbo.read()
|
|
170
|
+
|
|
171
|
+
@size.setter
|
|
172
|
+
def size(self, value: tuple[int]=None) -> tuple[int]:
|
|
173
|
+
self.resize(value)
|
|
174
|
+
return self.size
|
|
175
|
+
|
|
176
|
+
def __repr__(self) -> str:
|
|
177
|
+
return f'<bsk.Framebuffer | size: {self.size}>'
|
|
178
|
+
|
|
179
|
+
def __del__(self) -> None:
|
|
180
|
+
"""
|
|
181
|
+
Releases all memory used by the fbo
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
if self._color_attachments: [tex.release() for tex in self._color_attachments]
|
|
185
|
+
if self._depth_attachment: self._depth_attachment.release()
|
|
186
|
+
if self.fbo: self.fbo.release()
|
|
187
|
+
if self.vbo: self.vbo.release()
|
|
188
|
+
if self.vao: self.vao.release()
|
|
189
|
+
if self.shader:
|
|
190
|
+
if self.shader in self.engine.shader_handler.shaders: self.engine.shader_handler.shaders.remove(self.shader)
|
|
191
|
+
self.shader.__del__()
|
|
192
|
+
|
|
213
193
|
if self in self.engine.fbos: self.engine.fbos.remove(self)
|