Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 85 additions & 33 deletions apps/pyglet_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

Notice: slow! no transparency!
"""

import logging

logger = logging.getLogger(__name__)
Expand All @@ -17,11 +18,15 @@
logger.addHandler(ch)
logger.setLevel(logging.INFO)

import pyglet
from collections.abc import Iterator
from pathlib import Path
from typing import Optional

import pyglet
from pyglet.sprite import Sprite

from pytmx import *
from pytmx.pytmx import ColorLike, PointLike
from pytmx.util_pyglet import load_pyglet


Expand All @@ -32,21 +37,68 @@ class TiledRenderer:
no shape drawing yet
"""

def __init__(self, filename) -> None:
def __init__(self, filename: str) -> None:
tm = load_pyglet(filename)
self.size = tm.width * tm.tilewidth, tm.height * tm.tileheight
self.tmx_data = tm
self.batch = pyglet.graphics.Batch()
self.sprites = [] # container for tiles
self.generate_sprites()

def draw_rect(self, color, rect, width) -> None:
# TODO: use pyglet.shapes
pass

def draw_lines(self, color, closed, points, width) -> None:
# TODO: use pyglet.shapes
pass
def draw_rect(
self, color: ColorLike, rect: tuple[int, int, int, int], width: int
) -> None:
x, y, w, h = rect
y = self.size[1] - y - h # Adjust Y if needed for pixel origin
rect_shape = pyglet.shapes.Rectangle(x, y, w, h, color=color, batch=self.batch)
rect_shape.opacity = 128 # optional: make it semi-transparent
if width > 0:
border_color = (
max(0, color[0] - 50),
max(0, color[1] - 50),
max(0, color[2] - 50),
)
pyglet.shapes.Line(
x, y, x + w, y, thickness=width, color=border_color, batch=self.batch
) # bottom
pyglet.shapes.Line(
x,
y + h,
x + w,
y + h,
thickness=width,
color=border_color,
batch=self.batch,
) # top
pyglet.shapes.Line(
x, y, x, y + h, thickness=width, color=border_color, batch=self.batch
) # left
pyglet.shapes.Line(
x + w,
y,
x + w,
y + h,
thickness=width,
color=border_color,
batch=self.batch,
) # right

def draw_lines(
self, color: ColorLike, closed: bool, points: list[PointLike], width: int
) -> None:
# Flip Y-axis if necessary
flipped_points = [(x, self.size[1] - y) for x, y in points]

if closed:
polygon = pyglet.shapes.Polygon(
*flipped_points, color=color, batch=self.batch
)
polygon.opacity = 128
else:
for (x1, y1), (x2, y2) in zip(flipped_points, flipped_points[1:]):
line = pyglet.shapes.Line(
x1, y1, x2, y2, thickness=width, color=color, batch=self.batch
)

def generate_sprites(self) -> None:
tw = self.tmx_data.tilewidth
Expand Down Expand Up @@ -100,19 +152,19 @@ def generate_sprites(self) -> None:
sprite = Sprite(layer.image, x, y, batch=self.batch)
self.sprites.append(sprite)

def draw(self):
def draw(self) -> None:
self.batch.draw()


class SimpleTest:
def __init__(self, filename) -> None:
self.renderer = None
self.running = False
self.dirty = False
self.exit_status = 0
def __init__(self, filename: str) -> None:
self.renderer: TiledRenderer
self.running: bool = False
self.dirty: bool = False
self.exit_status: int = 0
self.load_map(filename)

def load_map(self, filename) -> None:
def load_map(self, filename: str) -> None:
self.renderer = TiledRenderer(filename)

logger.info("Objects in map:")
Expand All @@ -129,37 +181,37 @@ def draw(self) -> None:
self.renderer.draw()


def all_filenames():
import glob
import os.path

_list = glob.glob(os.path.join("data", "*.tmx"))
try:
while _list:
yield _list.pop(0)
except IndexError:
pyglet.app.exit()
def all_filenames() -> Iterator[str]:
data_dir = Path("apps/data")
for path in sorted(data_dir.glob("*.tmx")):
yield str(path)
pyglet.app.exit()


class TestWindow(pyglet.window.Window):
def __init__(self, width, height, vsync):
def __init__(self, width: int, height: int, vsync: bool) -> None:
super().__init__(width=width, height=height, vsync=vsync)
self.fps_display = pyglet.window.FPSDisplay(self, color=(50, 255, 50, 255))
self.filenames = all_filenames()
self.fps_display: pyglet.window.FPSDisplay = pyglet.window.FPSDisplay(
self, color=(50, 255, 50, 255)
)
self.filenames: Iterator[str] = all_filenames()
self.contents: Optional[SimpleTest] = None
self.next_map()

def on_draw(self) -> None:
self.clear()
self.contents.draw()
if self.contents:
self.contents.draw()
self.fps_display.draw()

def next_map(self) -> None:
try:
self.contents = SimpleTest(next(self.filenames))
filename = next(self.filenames)
self.contents = SimpleTest(filename)
except StopIteration:
pyglet.app.exit()

def on_key_press(self, symbol, mod):
def on_key_press(self, symbol: int, mod: int) -> None:
if symbol == pyglet.window.key.ESCAPE:
pyglet.app.exit()
else:
Expand All @@ -168,5 +220,5 @@ def on_key_press(self, symbol, mod):

if __name__ == "__main__":
window = TestWindow(600, 600, vsync=False)
pyglet.clock.schedule_interval(window.draw, 1/120)
pyglet.clock.schedule_interval(window.draw, 1 / 120)
pyglet.app.run(None)
64 changes: 50 additions & 14 deletions pytmx/util_pyglet.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import logging

logger = logging.getLogger(__name__)
from pathlib import Path
from typing import Any, Optional

try:
import pyglet
Expand All @@ -28,9 +30,12 @@
raise

import pytmx
from pytmx.pytmx import ColorLike, TileFlags


def pyglet_image_loader(filename, colorkey, **kwargs):
def pyglet_image_loader(
filename: str, colorkey: Optional[ColorLike] = None, **kwargs: Any
):
"""basic image loading with pyglet

returns pyglet Images, not textures
Expand All @@ -46,29 +51,60 @@ def pyglet_image_loader(filename, colorkey, **kwargs):
if colorkey:
logger.debug("colorkey not implemented")

image = pyglet.resource.image(filename)
image_path = Path(filename)
pyglet.resource.path = [str(image_path.parent.resolve())]
pyglet.resource.reindex()

def load_image(rect=None, flags=None):
if rect:
try:
image = pyglet.resource.image(image_path.name)

def load_image(
rect: Optional[tuple[int, int, int, int]] = None,
flags: Optional[TileFlags] = None,
):
try:
if rect:
x, y, w, h = rect
y = image.height - y - h
tile = image.get_region(x, y, w, h)
except:
logger.error("cannot get region %s of image", rect)
raise
else:
tile = image
else:
tile = image

angle, flip_x, flip_y = handle_flags(flags)
tile = tile.get_transform(flip_x=flip_x, flip_y=flip_y, rotate=int(angle))

if flags:
logger.error("tile flags are not implemented")
return tile

return tile
except Exception as e:
logger.error(
"Error extracting or transforming tile %s: %s", rect, e, exc_info=True
)
raise

return load_image


def load_pyglet(filename, *args, **kwargs):
def handle_flags(flags: Optional[TileFlags]) -> tuple[float, bool, bool]:
"""
Convert Tiled tile flip flags into SDL2 rendering parameters.
"""
if not flags:
return 0.0, False, False

flipped_h = flags.flipped_horizontally
flipped_v = flags.flipped_vertically
flipped_d = flags.flipped_diagonally

if flipped_d:
# Diagonal flip overrides horizontal/vertical flips for rotation
if flipped_v:
return 270.0, False, False
else:
return 90.0, False, False

return 0.0, flipped_h, flipped_v


def load_pyglet(filename: str, *args: Any, **kwargs: Any) -> pytmx.TiledMap:
kwargs["image_loader"] = pyglet_image_loader
kwargs["invert_y"] = True
return pytmx.TiledMap(filename, *args, **kwargs)