import arcade import assets # !!! CHATGPT !!! class TextureRenderer: def __init__(self, ctx): self.ctx = ctx self.program = ctx.program( vertex_shader=""" #version 330 in vec2 in_vert; in vec2 in_uv; out vec2 uv; uniform vec2 screen_size; uniform vec4 bounds; void main() { // Convert quad coordinates from [-1, 1] to [0, 1] vec2 local = in_vert * 0.5 + 0.5; // bounds = x, y, width, height in pixels vec2 pixel_pos = bounds.xy + local * bounds.zw; // Convert pixels to NDC vec2 ndc = pixel_pos / screen_size * 2.0 - 1.0; gl_Position = vec4(ndc, 0.0, 1.0); uv = in_uv; } """, fragment_shader=""" #version 330 uniform sampler2D tex; in vec2 uv; out vec4 color; void main() { color = texture(tex, uv); } """, ) self.quad = arcade.gl.geometry.quad_2d_fs() def render(self, texture, x, y, width, height): self.program["screen_size"] = (self.ctx.screen.width, self.ctx.screen.height) self.program["bounds"] = (x, y, width, height) texture.use(0) self.program["tex"] = 0 self.quad.render(self.program) # !!! CHATGPT !!! class NineSlicesPanel: SLICE_SIZE = 16 def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h self.sprites = [] self.rebuild() def rebuild(self): self.sprites.clear() sheet = assets.sprites["panel"] s = self.SLICE_SIZE # Crop a specific sub-region from the spritesheet def tex(col, row, w=s, h=s): return sheet.get_texture( arcade.LBWH(col * s, row * s, w, h) ) # Base 9-slice textures textures = { "bl": tex(0, 2), "b": tex(1, 0), "br": tex(2, 2), "l": tex(0, 1), "c": tex(1, 1), "r": tex(2, 1), "tl": tex(0, 0), "t": tex(1, 2), "tr": tex(2, 0), } def add(texture, x, y, w, h): if w > 0 and h > 0: self.sprites.append(( texture, arcade.XYWH(x + w / 2, y + h / 2, w, h) )) # Corners add(textures["bl"], self.x, self.y, s, s) add(textures["br"], self.x + self.w - s, self.y, s, s) add(textures["tl"], self.x, self.y + self.h - s, s, s) add(textures["tr"], self.x + self.w - s, self.y + self.h - s, s, s) middle_w = self.w - s * 2 middle_h = self.h - s * 2 # Tile horizontal edges def tile_horizontal(col, row, x, y, width): curr_x = x rem_w = width while rem_w > 0: tile_w = min(s, rem_w) t = tex(col, row, tile_w, s) add(t, curr_x, y, tile_w, s) curr_x += tile_w rem_w -= tile_w # Tile vertical edges def tile_vertical(col, row, x, y, height): curr_y = y rem_h = height while rem_h > 0: tile_h = min(s, rem_h) # Crop from top of sprite when filling upwards t = tex(col, row, s, tile_h) add(t, x, curr_y, s, tile_h) curr_y += tile_h rem_h -= tile_h # Render borders tile_horizontal(1, 2, self.x + s, self.y, middle_w) # Bottom tile_horizontal(1, 0, self.x + s, self.y + self.h - s, middle_w) # Top tile_vertical(0, 1, self.x, self.y + s, middle_h) # Left tile_vertical(2, 1, self.x + self.w - s, self.y + s, middle_h) # Right # Render center grid curr_y = self.y + s rem_h = middle_h while rem_h > 0: tile_h = min(s, rem_h) curr_x = self.x + s rem_w = middle_w while rem_w > 0: tile_w = min(s, rem_w) t = tex(1, 1, tile_w, tile_h) add(t, curr_x, curr_y, tile_w, tile_h) curr_x += tile_w rem_w -= tile_w curr_y += tile_h rem_h -= tile_h def render(self): for texture, rect in self.sprites: arcade.draw_texture_rect( texture, rect, pixelated=True )