|
8 | 8 | from __future__ import annotations |
9 | 9 |
|
10 | 10 | import random |
| 11 | +import struct |
11 | 12 | from abc import abstractmethod |
12 | 13 | from array import array |
13 | 14 | from collections import deque |
@@ -139,6 +140,23 @@ def draw_hit_boxes( |
139 | 140 | """ |
140 | 141 | ... |
141 | 142 |
|
| 143 | + @abstractmethod |
| 144 | + def get_nearby_sprites_gpu(self, pos: Point2, size: Point2) -> list[SpriteType_co]: |
| 145 | + """ |
| 146 | + Get a list of sprites that are nearby the given position and size |
| 147 | + using the gpu. No spatial hashing is needed. This is a very fast method |
| 148 | + to find nearby sprites in large spritelists but is very expensive |
| 149 | + if the method is called many times per frame or if the sprite list |
| 150 | + is small. |
| 151 | +
|
| 152 | + Args: |
| 153 | + pos: The position to check for nearby sprites. |
| 154 | + size: The size of the area to check for nearby sprites. |
| 155 | + Returns: |
| 156 | + A list of sprites nearby the given position and size. |
| 157 | + """ |
| 158 | + ... |
| 159 | + |
142 | 160 | @abstractmethod |
143 | 161 | def _write_sprite_buffers_to_gpu(self) -> None: ... |
144 | 162 |
|
@@ -305,13 +323,13 @@ def _init_deferred(self) -> None: |
305 | 323 |
|
306 | 324 | # NOTE: Instantiate the appropriate spritelist data class here |
307 | 325 | # Desktop GL (with geo shader) |
308 | | - # self._spritelist_data = SpriteListBufferData( |
309 | | - # self.ctx, capacity=self._buf_capacity, atlas=self._atlas |
310 | | - # ) |
311 | | - # WebGL (without geo shader) |
312 | | - self._spritelist_data = SpriteListTextureData( |
| 326 | + self._spritelist_data = SpriteListBufferData( |
313 | 327 | self.ctx, capacity=self._buf_capacity, atlas=self._atlas |
314 | 328 | ) |
| 329 | + # WebGL (without geo shader) |
| 330 | + # self._spritelist_data = SpriteListTextureData( |
| 331 | + # self.ctx, capacity=self._buf_capacity, atlas=self._atlas |
| 332 | + # ) |
315 | 333 |
|
316 | 334 | self._initialized = True |
317 | 335 |
|
@@ -981,6 +999,30 @@ def draw_hit_boxes( |
981 | 999 |
|
982 | 1000 | arcade.draw_lines(points, color=converted_color, line_width=line_thickness) |
983 | 1001 |
|
| 1002 | + def get_nearby_sprites_gpu(self, pos: Point2, size: Point2) -> list[SpriteType]: |
| 1003 | + """ |
| 1004 | + Get a list of sprites that are nearby the given position and size |
| 1005 | + using the gpu. No spatial hashing is needed. This is a very fast method |
| 1006 | + to find nearby sprites in large spritelists but is very expensive |
| 1007 | + if the method is called many times per frame or if the sprite list |
| 1008 | + is small. |
| 1009 | +
|
| 1010 | + Args: |
| 1011 | + pos: The position to check for nearby sprites. |
| 1012 | + size: The size of the area to check for nearby sprites. |
| 1013 | + Returns: |
| 1014 | + A list of sprites nearby the given position and size. |
| 1015 | + """ |
| 1016 | + if not self._initialized: |
| 1017 | + self._init_deferred() |
| 1018 | + |
| 1019 | + if len(self.sprite_list) == 0: |
| 1020 | + return [] |
| 1021 | + |
| 1022 | + self._write_sprite_buffers_to_gpu() |
| 1023 | + indices = self._spritelist_data.get_nearby_sprite_indices(pos, size, len(self.sprite_list)) |
| 1024 | + return [self.sprite_list[i] for i in indices] |
| 1025 | + |
984 | 1026 | def _grow_sprite_buffers(self) -> None: |
985 | 1027 | """Double the internal buffer sizes""" |
986 | 1028 | # Resize sprite buffers if needed |
@@ -1294,6 +1336,19 @@ def render( |
1294 | 1336 | """ |
1295 | 1337 | raise NotImplementedError("This method should be implemented in subclasses.") |
1296 | 1338 |
|
| 1339 | + def get_nearby_sprite_indices(self, pos: Point2, size: Point2, length: int) -> list[int]: |
| 1340 | + """ |
| 1341 | + Get indices of sprites that are nearby the given position and size. |
| 1342 | +
|
| 1343 | + Args: |
| 1344 | + pos: The position to check for nearby sprites. |
| 1345 | + size: The size of the area to check for nearby sprites. |
| 1346 | + length: The number of sprites in the list. |
| 1347 | + Returns: |
| 1348 | + A list of indices of nearby sprites. |
| 1349 | + """ |
| 1350 | + raise NotImplementedError("This method should be implemented in subclasses.") |
| 1351 | + |
1297 | 1352 |
|
1298 | 1353 | class SpriteListBufferData(SpriteListData): |
1299 | 1354 | """Container for all gpu data used by the SpriteList.""" |
@@ -1577,6 +1632,39 @@ def render( |
1577 | 1632 | if blend_function is not None: |
1578 | 1633 | self.ctx.blend_func = prev_blend_func |
1579 | 1634 |
|
| 1635 | + def get_nearby_sprite_indices(self, pos: Point2, size: Point2, length: int) -> list[int]: |
| 1636 | + """ |
| 1637 | + Get indices of sprites that are nearby the given position and size. |
| 1638 | +
|
| 1639 | + Args: |
| 1640 | + pos: The position to check for nearby sprites. |
| 1641 | + size: The size of the area to check for nearby sprites. |
| 1642 | + length: The number of sprites in the spritelist. |
| 1643 | + Returns: |
| 1644 | + A list of indices of nearby sprites. |
| 1645 | + """ |
| 1646 | + ctx = self.ctx |
| 1647 | + ctx.collision_detection_program["check_pos"] = pos |
| 1648 | + ctx.collision_detection_program["check_size"] = size |
| 1649 | + |
| 1650 | + # Ensure the result buffer can fit all the sprites (worst case) |
| 1651 | + buffer = ctx.collision_buffer |
| 1652 | + # NOTE: Right now the limit is 1000 hits |
| 1653 | + # Run the transform shader emitting sprites close to the configured position and size. |
| 1654 | + # This runs in a query so we can measure the number of sprites emitted. |
| 1655 | + with ctx.collision_query: |
| 1656 | + self._geometry.transform( # type: ignore |
| 1657 | + ctx.collision_detection_program, |
| 1658 | + buffer, |
| 1659 | + vertices=length, |
| 1660 | + ) |
| 1661 | + |
| 1662 | + # Store the number of sprites emitted |
| 1663 | + emit_count = ctx.collision_query.primitives_generated |
| 1664 | + if emit_count == 0: |
| 1665 | + return [] |
| 1666 | + return [i for i in struct.unpack(f"{emit_count}i", buffer.read(size=emit_count * 4))] |
| 1667 | + |
1580 | 1668 |
|
1581 | 1669 | class SpriteListTextureData(SpriteListData): |
1582 | 1670 | """Container for all gpu data used by the SpriteList without buffers.""" |
@@ -1753,3 +1841,15 @@ def render( |
1753 | 1841 | self.ctx.disable(self.ctx.BLEND) |
1754 | 1842 | if blend_function is not None: |
1755 | 1843 | self.ctx.blend_func = prev_blend_func |
| 1844 | + |
| 1845 | + # def get_nearby_sprite_indices(self, pos: Point2, size: Point2, length: int) -> list[int]: |
| 1846 | + # """ |
| 1847 | + # Get indices of sprites that are nearby the given position and size. |
| 1848 | + |
| 1849 | + # Args: |
| 1850 | + # pos: The position to check for nearby sprites. |
| 1851 | + # size: The size of the area to check for nearby sprites. |
| 1852 | + # length: The number of sprites in the spritelist. |
| 1853 | + # Returns: |
| 1854 | + # A list of indices of nearby sprites. |
| 1855 | + # """ |
0 commit comments