basilisk-engine 0.1.6__py3-none-any.whl → 0.1.7__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 +14 -13
  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 +224 -224
  7. basilisk/collisions/narrow/contact_manifold.py +95 -95
  8. basilisk/collisions/narrow/dataclasses.py +34 -34
  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 +202 -200
  21. basilisk/generic/abstract_bvh.py +15 -15
  22. basilisk/generic/abstract_custom.py +133 -133
  23. basilisk/generic/collisions.py +72 -72
  24. basilisk/generic/input_validation.py +66 -66
  25. basilisk/generic/math.py +6 -6
  26. basilisk/generic/matrices.py +35 -35
  27. basilisk/generic/meshes.py +72 -72
  28. basilisk/generic/quat.py +142 -142
  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 -14
  34. basilisk/mesh/cube.py +33 -33
  35. basilisk/mesh/mesh.py +230 -230
  36. basilisk/mesh/mesh_from_data.py +130 -130
  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 +682 -682
  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 +206 -206
  51. basilisk/render/chunk.py +99 -99
  52. basilisk/render/chunk_handler.py +154 -154
  53. basilisk/render/frame.py +101 -99
  54. basilisk/render/framebuffer.py +130 -105
  55. basilisk/render/image.py +87 -87
  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 +132 -132
  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 +264 -256
  66. basilisk/shaders/batch.frag +276 -276
  67. basilisk/shaders/batch.vert +115 -115
  68. basilisk/shaders/crt.frag +31 -31
  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.6.dist-info → basilisk_engine-0.1.7.dist-info}/METADATA +39 -45
  83. basilisk_engine-0.1.7.dist-info/RECORD +101 -0
  84. {basilisk_engine-0.1.6.dist-info → basilisk_engine-0.1.7.dist-info}/WHEEL +1 -1
  85. basilisk_engine-0.1.6.dist-info/RECORD +0 -101
  86. {basilisk_engine-0.1.6.dist-info → basilisk_engine-0.1.7.dist-info}/top_level.txt +0 -0
@@ -1,106 +1,131 @@
1
- import moderngl as mgl
2
- from PIL import Image
3
-
4
-
5
-
6
- class Framebuffer:
7
- engine: ...
8
- """Reference to the parent engine"""
9
- fbo: mgl.Framebuffer = None
10
- """The core framebuffer the object provides abstraction for."""
11
- texture: mgl.Texture = None
12
- """The color texture of the framebuffer"""
13
- depth: mgl.Texture = None
14
- """The depth texture of the framebuffer"""
15
- size: tuple[int]
16
- """The dimensions of the framebuffer (x, y)"""
17
-
18
- def __init__(self, engine, size: tuple[int]=None, components: int=4, filter=(mgl.LINEAR, mgl.LINEAR)) -> None:
19
- """
20
- Abstraction to the mgl framebuffer
21
- Args:
22
- engine: mgl.Engine:
23
- The parent engine
24
- size: tuple[int]:
25
- The dimensions of the framebuffer (x, y)
26
- """
27
-
28
- # Set attributes
29
- self.engine = engine
30
- self.ctx = engine.ctx
31
- self.size = size if size else engine.win_size
32
- self.components = components
33
-
34
- # Create the fbo
35
- self.texture = self.ctx.texture(self.size, components=self.components)
36
- self.depth = self.ctx.depth_texture(self.size)
37
- self.fbo = self.ctx.framebuffer([self.texture], self.depth)
38
-
39
- print()
40
-
41
- self.filter = filter
42
-
43
- def use(self) -> None:
44
- """
45
- Select this framebuffer for use
46
- """
47
-
48
- self.fbo.use()
49
-
50
- def clear(self) -> None:
51
- """
52
- Clear all data currently in the textures (set to black)
53
- """
54
-
55
- self.fbo.clear()
56
-
57
- def save(self, destination: str=None) -> None:
58
- """
59
- Saves the frame as an image to the given file destination
60
- """
61
-
62
- path = destination if destination else 'screenshot'
63
-
64
- data = self.fbo.read(components=3, alignment=1)
65
- img = Image.frombytes('RGB', self.size, data).transpose(Image.FLIP_TOP_BOTTOM)
66
- img.save(f'{path}.png')
67
-
68
- def resize(self, size: tuple[int]=None) -> None:
69
- """
70
- Resize the buffer to the given size. None for window size
71
- """
72
-
73
- # Release old memory
74
- self.__del__()
75
-
76
- # Set/get size attribute
77
- self.size = size if size else self.engine.win_size
78
-
79
- # Create the fbo
80
- self.texture = self.ctx.texture(self.size, components=self.components)
81
- self.depth = self.ctx.depth_texture(self.size)
82
- self.fbo = self.ctx.framebuffer([self.texture], self.depth)
83
-
84
- self.filter = self._filter
85
-
86
- @property
87
- def data(self):
88
- return self.fbo.read(components=3, alignment=1)
89
-
90
- @property
91
- def filter(self):
92
- return self.texture.filter
93
-
94
- @filter.setter
95
- def filter(self, value):
96
- self._filter = value
97
- self.texture.filter = value
98
-
99
- def __repr__(self) -> str:
100
- return f'<bsk.Framebuffer | size: {self.size}>'
101
-
102
- def __del__(self) -> None:
103
- # Release any existing memory in case of a resize
104
- if self.texture: self.texture.release()
105
- if self.depth: self.depth.release()
1
+ import numpy as np
2
+ import moderngl as mgl
3
+ from PIL import Image
4
+ from ..render.shader import Shader
5
+
6
+
7
+ class Framebuffer:
8
+ engine: ...
9
+ """Reference to the parent engine"""
10
+ fbo: mgl.Framebuffer = None
11
+ """The core framebuffer the object provides abstraction for."""
12
+ texture: mgl.Texture = None
13
+ """The color texture of the framebuffer"""
14
+ depth: mgl.Texture = None
15
+ """The depth texture of the framebuffer"""
16
+ size: tuple[int]
17
+ """The dimensions of the framebuffer (x, y)"""
18
+
19
+ def __init__(self, engine, size: tuple[int]=None, resolution_scale: float=1.0, components: int=4, filter=(mgl.LINEAR, mgl.LINEAR)) -> None:
20
+ """
21
+ Abstraction to the mgl framebuffer
22
+ Args:
23
+ engine: mgl.Engine:
24
+ The parent engine
25
+ size: tuple[int]:
26
+ The dimensions of the framebuffer (x, y)
27
+ """
28
+
29
+ # Set attributes
30
+ self.engine = engine
31
+ self.ctx = engine.ctx
32
+ self.components = components
33
+
34
+ # Set the size
35
+ self.resolution_scale = resolution_scale
36
+ self._size = size
37
+ self.size = self._size if self._size else self.engine.win_size
38
+ self.size = (int(self.size[0] * resolution_scale), int(self.size[1] * resolution_scale))
39
+
40
+ # Create the fbo
41
+ self.texture = self.ctx.texture(self.size, components=self.components)
42
+ self.depth = self.ctx.depth_texture(self.size)
43
+ self.fbo = self.ctx.framebuffer([self.texture], self.depth)
44
+
45
+ # Load Shaders
46
+ self.shader = Shader(self.engine, self.engine.root + '/shaders/frame.vert', self.engine.root + '/shaders/frame.frag')
47
+ self.engine.scene.shader_handler.add(self.shader)
48
+
49
+ # Load VAO
50
+ 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'))
51
+ self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '3f 2f', 'in_position', 'in_uv')], skip_errors=True)
52
+
53
+ # Save the filter
54
+ self.filter = filter
55
+
56
+ # Add to the engine for updates
57
+ self.engine.fbos.append(self)
58
+
59
+ def render(self) -> None:
60
+ self.shader.program['screenTexture'] = 0
61
+ self.texture.use(location=0)
62
+ self.vao.render()
63
+
64
+ def use(self) -> None:
65
+ """
66
+ Select this framebuffer for use
67
+ """
68
+
69
+ self.fbo.use()
70
+
71
+ def clear(self) -> None:
72
+ """
73
+ Clear all data currently in the textures (set to black)
74
+ """
75
+
76
+ self.fbo.clear()
77
+
78
+ def save(self, destination: str=None) -> None:
79
+ """
80
+ Saves the frame as an image to the given file destination
81
+ """
82
+
83
+ path = destination if destination else 'screenshot'
84
+
85
+ data = self.fbo.read(components=3, alignment=1)
86
+ img = Image.frombytes('RGB', self.size, data).transpose(Image.FLIP_TOP_BOTTOM)
87
+ img.save(f'{path}.png')
88
+
89
+ def resize(self, size: tuple[int]=None) -> None:
90
+ """
91
+ Resize the buffer to the given size. None for window size
92
+ """
93
+
94
+ # Release old memory
95
+ self.__del__()
96
+
97
+ # Set/get size attribute
98
+ if size:
99
+ self.size = size
100
+ else:
101
+ self.size = self._size if self._size else self.engine.win_size
102
+ self.size = (int(self.size[0] * self.resolution_scale), int(self.size[1] * self.resolution_scale))
103
+
104
+ # Create the fbo
105
+ self.texture = self.ctx.texture(self.size, components=self.components)
106
+ self.depth = self.ctx.depth_texture(self.size)
107
+ self.fbo = self.ctx.framebuffer([self.texture], self.depth)
108
+
109
+ self.filter = self._filter
110
+
111
+ @property
112
+ def data(self):
113
+ return self.fbo.read(components=3, alignment=1)
114
+
115
+ @property
116
+ def filter(self):
117
+ return self.texture.filter
118
+
119
+ @filter.setter
120
+ def filter(self, value):
121
+ self._filter = value
122
+ self.texture.filter = value
123
+
124
+ def __repr__(self) -> str:
125
+ return f'<bsk.Framebuffer | size: {self.size}>'
126
+
127
+ def __del__(self) -> None:
128
+ # Release any existing memory in case of a resize
129
+ if self.texture: self.texture.release()
130
+ if self.depth: self.depth.release()
106
131
  if self.fbo: self.fbo.release()
basilisk/render/image.py CHANGED
@@ -1,88 +1,88 @@
1
- import os
2
- import sys
3
- import numpy as np
4
- import moderngl as mgl
5
- import glm
6
- import pygame as pg
7
- from PIL import Image as PIL_Image
8
-
9
-
10
- texture_sizes = (8, 64, 512, 1024, 2048)
11
-
12
-
13
- class Image():
14
- name: str
15
- """Name of the image"""
16
- index: glm.ivec2
17
- """Location of the image in the texture arrays"""
18
- data: np.ndarray
19
- """Array of the texture data"""
20
- size: int
21
- """The width and height in pixels of the image"""
22
-
23
- def __init__(self, path: str | os.PathLike | pg.Surface | mgl.Texture) -> None:
24
- """
25
- A basilisk image object that contains a moderngl texture
26
- Args:
27
- path: str | os.PathLike | pg.Surface
28
- The string path to the image. Can also read a pygame surface
29
- """
30
-
31
- # Check if the user is loading a pygame surface
32
- if isinstance(path, str) or isinstance(path, os.PathLike):
33
- return self._from_path(path)
34
- elif isinstance(path, pg.Surface):
35
- return self._from_surfaces(path)
36
- elif isinstance(path, mgl.Texture):
37
- return self._from_texture(path)
38
-
39
- raise TypeError(f'Invalid path type: {type(path)}. Expected a string or os.PathLike')
40
-
41
- def _from_path(self, path: str | os.PathLike) -> None:
42
- """
43
- Loads a basilisk image from a pygame surface
44
- Args:
45
- """
46
-
47
- # Get name from path
48
- self.name = path.split('/')[-1].split('\\')[-1].split('.')[0]
49
-
50
- # Load image
51
- img = PIL_Image.open(path).convert('RGBA')
52
- # Set the size in one of the size buckets
53
- size_buckets = texture_sizes
54
- self.size = size_buckets[np.argmin(np.array([abs(size - img.size[0]) for size in size_buckets]))]
55
- img = img.resize((self.size, self.size))
56
- # Get the image data
57
- self.data = img.tobytes()
58
-
59
- # Default index value (to be set by image handler)
60
- self.index = glm.ivec2(1, 1)
61
-
62
- def _from_surface(self, surf: pg.Surface) -> None:
63
- """
64
- Loads a basilisk image from a pygame surface
65
- Args:
66
- """
67
-
68
- # Set the size in one of the size buckets
69
- size_buckets = texture_sizes
70
- self.size = size_buckets[np.argmin(np.array([abs(size - surf.get_size()[0]) for size in size_buckets]))]
71
- surf = pg.transform.scale(surf, (self.size, self.size)).convert_alpha()
72
- # Get image data
73
- self.data = pg.image.tobytes(surf, 'RGBA')
74
-
75
- # Default index value (to be set by image handler)
76
- self.index = glm.ivec2(1, 1)
77
-
78
- def _from_texture(self, texture: mgl.Texture):
79
- """
80
-
81
- """
82
- ...
83
-
84
- def __repr__(self) -> str:
85
- """
86
- Returns a string representation of the object
87
- """
1
+ import os
2
+ import sys
3
+ import numpy as np
4
+ import moderngl as mgl
5
+ import glm
6
+ import pygame as pg
7
+ from PIL import Image as PIL_Image
8
+
9
+
10
+ texture_sizes = (8, 64, 512, 1024, 2048)
11
+
12
+
13
+ class Image():
14
+ name: str
15
+ """Name of the image"""
16
+ index: glm.ivec2
17
+ """Location of the image in the texture arrays"""
18
+ data: np.ndarray
19
+ """Array of the texture data"""
20
+ size: int
21
+ """The width and height in pixels of the image"""
22
+
23
+ def __init__(self, path: str | os.PathLike | pg.Surface | mgl.Texture) -> None:
24
+ """
25
+ A basilisk image object that contains a moderngl texture
26
+ Args:
27
+ path: str | os.PathLike | pg.Surface
28
+ The string path to the image. Can also read a pygame surface
29
+ """
30
+
31
+ # Check if the user is loading a pygame surface
32
+ if isinstance(path, str) or isinstance(path, os.PathLike):
33
+ return self._from_path(path)
34
+ elif isinstance(path, pg.Surface):
35
+ return self._from_surfaces(path)
36
+ elif isinstance(path, mgl.Texture):
37
+ return self._from_texture(path)
38
+
39
+ raise TypeError(f'Invalid path type: {type(path)}. Expected a string or os.PathLike')
40
+
41
+ def _from_path(self, path: str | os.PathLike) -> None:
42
+ """
43
+ Loads a basilisk image from a pygame surface
44
+ Args:
45
+ """
46
+
47
+ # Get name from path
48
+ self.name = path.split('/')[-1].split('\\')[-1].split('.')[0]
49
+
50
+ # Load image
51
+ img = PIL_Image.open(path).convert('RGBA')
52
+ # Set the size in one of the size buckets
53
+ size_buckets = texture_sizes
54
+ self.size = size_buckets[np.argmin(np.array([abs(size - img.size[0]) for size in size_buckets]))]
55
+ img = img.resize((self.size, self.size))
56
+ # Get the image data
57
+ self.data = img.tobytes()
58
+
59
+ # Default index value (to be set by image handler)
60
+ self.index = glm.ivec2(1, 1)
61
+
62
+ def _from_surface(self, surf: pg.Surface) -> None:
63
+ """
64
+ Loads a basilisk image from a pygame surface
65
+ Args:
66
+ """
67
+
68
+ # Set the size in one of the size buckets
69
+ size_buckets = texture_sizes
70
+ self.size = size_buckets[np.argmin(np.array([abs(size - surf.get_size()[0]) for size in size_buckets]))]
71
+ surf = pg.transform.scale(surf, (self.size, self.size)).convert_alpha()
72
+ # Get image data
73
+ self.data = pg.image.tobytes(surf, 'RGBA')
74
+
75
+ # Default index value (to be set by image handler)
76
+ self.index = glm.ivec2(1, 1)
77
+
78
+ def _from_texture(self, texture: mgl.Texture):
79
+ """
80
+
81
+ """
82
+ ...
83
+
84
+ def __repr__(self) -> str:
85
+ """
86
+ Returns a string representation of the object
87
+ """
88
88
  return f'<Basilisk Image | {self.name}, ({self.size}x{self.size}), {sys.getsizeof(self.data) / 1024 / 1024:.2} mb>'
@@ -1,123 +1,123 @@
1
- import moderngl as mgl
2
- import glm
3
- import numpy as np
4
-
5
-
6
- texture_sizes = (8, 64, 512, 1024, 2048)
7
-
8
-
9
- class ImageHandler():
10
- engine: any
11
- """Back refernce to the parent engine"""
12
- scene: any
13
- """Back refernce to the parent scene"""
14
- ctx: mgl.Context
15
- """Back reference to the Context used by the scene/engine"""
16
- images: list
17
- """List of basilisk Images containing all the loaded images given to the scene"""
18
- texture_arrays: dict
19
- """Dictionary of textures arrays for writting textures to GPU"""
20
-
21
- def __init__(self, scene) -> None:
22
- """
23
- Container for all the basilisk image objects in the scene.
24
- Handles the managment and writting of all image textures.
25
- """
26
-
27
- # Set back references
28
- self.scene = scene
29
- self.engine = scene.engine
30
- self.ctx = scene.engine.ctx
31
-
32
- self.images = []
33
- self.texture_arrays = {size : [] for size in texture_sizes}
34
-
35
- def add(self, image: any) -> None:
36
- """
37
- Adds an existing basilisk image object to the handler for writting
38
- Args:
39
- image: bsk.Image
40
- The existing image that is to be added to the scene.
41
- """
42
-
43
- if image in self.images: return
44
-
45
- self.images.append(image)
46
- self.write(regenerate=True)
47
-
48
- def generate_texture_array(self) -> None:
49
- """
50
- Generates texutre arrays for all the images. Updates the index of the image instance
51
- """
52
-
53
- # Release any existsing texture arrays
54
- for texture_array in self.texture_arrays.values():
55
- if not texture_array: continue
56
- texture_array.release()
57
-
58
- self.texture_arrays = {size : [] for size in texture_sizes}
59
-
60
- for image in self.images:
61
- # Add the image data to the array
62
- self.texture_arrays[image.size].append(image.data)
63
- # Update the image index
64
- image.index = glm.ivec2(texture_sizes.index(image.size), len(self.texture_arrays[image.size]) - 1)
65
-
66
-
67
- for size in self.texture_arrays:
68
- # Check that there are textures in the bucket
69
- if not len(self.texture_arrays[size]): continue
70
- array_data = np.array(self.texture_arrays[size])
71
- dim = (size, size, len(self.texture_arrays[size]))
72
-
73
- # Make the array
74
- self.texture_arrays[size] = self.ctx.texture_array(size=dim, components=4, data=array_data)
75
- # Texture OpenGl settings
76
- self.texture_arrays[size].build_mipmaps()
77
- if size > 32: self.texture_arrays[size].filter = (mgl.LINEAR_MIPMAP_LINEAR, mgl.LINEAR)
78
- else: self.texture_arrays[size].filter = (mgl.NEAREST, mgl.NEAREST)
79
- self.texture_arrays[size].anisotropy = 32.0
80
-
81
- def write(self, regenerate=False) -> None:
82
- """
83
- Writes all texture arrays to shaders that use images
84
- """
85
-
86
- if regenerate: self.generate_texture_array()
87
-
88
- if not self.texture_arrays: return
89
-
90
- for shader in self.engine.scene.shader_handler.shaders:
91
- if 'textureArrays[5]' not in shader.uniforms: continue
92
-
93
- for i, size in enumerate(texture_sizes):
94
- if not size in self.texture_arrays: continue
95
- if not self.texture_arrays[size]: continue
96
- shader.program[f'textureArrays[{i}].array'] = i + 3
97
- self.texture_arrays[size].use(location=i+3)
98
-
99
- def get(self, identifier: str | int) -> any:
100
- """
101
- Gets the basilisk image with the given name or index
102
- Args:
103
- identifier: str | int
104
- The name string or index of the desired image
105
- """
106
-
107
- # Simply use index if given
108
- if isinstance(identifier, int): return self.images[identifier]
109
-
110
- # Else, search the list for an image with the given name
111
- for image in self.images:
112
- if image.name != identifier: continue
113
- return image
114
-
115
- # No matching image found
116
- return None
117
-
118
- def __del__(self):
119
- """
120
- Deallocates all texture arrays
121
- """
122
-
1
+ import moderngl as mgl
2
+ import glm
3
+ import numpy as np
4
+
5
+
6
+ texture_sizes = (8, 64, 512, 1024, 2048)
7
+
8
+
9
+ class ImageHandler():
10
+ engine: any
11
+ """Back refernce to the parent engine"""
12
+ scene: any
13
+ """Back refernce to the parent scene"""
14
+ ctx: mgl.Context
15
+ """Back reference to the Context used by the scene/engine"""
16
+ images: list
17
+ """List of basilisk Images containing all the loaded images given to the scene"""
18
+ texture_arrays: dict
19
+ """Dictionary of textures arrays for writting textures to GPU"""
20
+
21
+ def __init__(self, scene) -> None:
22
+ """
23
+ Container for all the basilisk image objects in the scene.
24
+ Handles the managment and writting of all image textures.
25
+ """
26
+
27
+ # Set back references
28
+ self.scene = scene
29
+ self.engine = scene.engine
30
+ self.ctx = scene.engine.ctx
31
+
32
+ self.images = []
33
+ self.texture_arrays = {size : [] for size in texture_sizes}
34
+
35
+ def add(self, image: any) -> None:
36
+ """
37
+ Adds an existing basilisk image object to the handler for writting
38
+ Args:
39
+ image: bsk.Image
40
+ The existing image that is to be added to the scene.
41
+ """
42
+
43
+ if image in self.images: return
44
+
45
+ self.images.append(image)
46
+ self.write(regenerate=True)
47
+
48
+ def generate_texture_array(self) -> None:
49
+ """
50
+ Generates texutre arrays for all the images. Updates the index of the image instance
51
+ """
52
+
53
+ # Release any existsing texture arrays
54
+ for texture_array in self.texture_arrays.values():
55
+ if not texture_array: continue
56
+ texture_array.release()
57
+
58
+ self.texture_arrays = {size : [] for size in texture_sizes}
59
+
60
+ for image in self.images:
61
+ # Add the image data to the array
62
+ self.texture_arrays[image.size].append(image.data)
63
+ # Update the image index
64
+ image.index = glm.ivec2(texture_sizes.index(image.size), len(self.texture_arrays[image.size]) - 1)
65
+
66
+
67
+ for size in self.texture_arrays:
68
+ # Check that there are textures in the bucket
69
+ if not len(self.texture_arrays[size]): continue
70
+ array_data = np.array(self.texture_arrays[size])
71
+ dim = (size, size, len(self.texture_arrays[size]))
72
+
73
+ # Make the array
74
+ self.texture_arrays[size] = self.ctx.texture_array(size=dim, components=4, data=array_data)
75
+ # Texture OpenGl settings
76
+ self.texture_arrays[size].build_mipmaps()
77
+ if size > 32: self.texture_arrays[size].filter = (mgl.LINEAR_MIPMAP_LINEAR, mgl.LINEAR)
78
+ else: self.texture_arrays[size].filter = (mgl.NEAREST, mgl.NEAREST)
79
+ self.texture_arrays[size].anisotropy = 32.0
80
+
81
+ def write(self, regenerate=False) -> None:
82
+ """
83
+ Writes all texture arrays to shaders that use images
84
+ """
85
+
86
+ if regenerate: self.generate_texture_array()
87
+
88
+ if not self.texture_arrays: return
89
+
90
+ for shader in self.engine.scene.shader_handler.shaders:
91
+ if 'textureArrays[5]' not in shader.uniforms: continue
92
+
93
+ for i, size in enumerate(texture_sizes):
94
+ if not size in self.texture_arrays: continue
95
+ if not self.texture_arrays[size]: continue
96
+ shader.program[f'textureArrays[{i}].array'] = i + 3
97
+ self.texture_arrays[size].use(location=i+3)
98
+
99
+ def get(self, identifier: str | int) -> any:
100
+ """
101
+ Gets the basilisk image with the given name or index
102
+ Args:
103
+ identifier: str | int
104
+ The name string or index of the desired image
105
+ """
106
+
107
+ # Simply use index if given
108
+ if isinstance(identifier, int): return self.images[identifier]
109
+
110
+ # Else, search the list for an image with the given name
111
+ for image in self.images:
112
+ if image.name != identifier: continue
113
+ return image
114
+
115
+ # No matching image found
116
+ return None
117
+
118
+ def __del__(self):
119
+ """
120
+ Deallocates all texture arrays
121
+ """
122
+
123
123
  # [texture_array.release() for texture_array in self.texture_arrays]