From 054125bc32ddddb6ee53402721d1454cf27d34e4 Mon Sep 17 00:00:00 2001 From: paulaboks Date: Wed, 11 Mar 2026 10:05:43 -0300 Subject: [PATCH] Separate renderer into own folder --- client/assets.ts | 2 +- client/client_world.ts | 2 +- client/components/dimension.ts | 2 +- client/components/sprite.ts | 2 +- client/debug_ui.ts | 2 +- client/game.ts | 2 +- client/gui/gui_chest.ts | 2 +- client/gui/gui_furnace.ts | 2 +- client/gui/gui_player_inventory.ts | 2 +- client/gui/gui_screen.ts | 2 +- client/main.ts | 2 +- client/main_menu.ts | 2 +- client/renderer/core.ts | 285 +++++++++++++++++++++++ client/renderer/math.ts | 24 ++ client/renderer/mod.ts | 5 + client/renderer/shapes.ts | 34 +++ client/renderer/text.ts | 107 +++++++++ client/renderer/textures.ts | 93 ++++++++ client/renderer/types.ts | 32 +++ client/systems/clickable_system.ts | 2 +- client/systems/dimension_logic.ts | 2 +- client/systems/player_controls.ts | 2 +- client/systems/render_system.ts | 2 +- client/systems/rendering/dimension.ts | 2 +- client/systems/rendering/player.ts | 2 +- client/systems/rendering/render_utils.ts | 2 +- client/systems/rendering/sprites.ts | 2 +- client/systems/ui_render_system.ts | 2 +- 28 files changed, 601 insertions(+), 21 deletions(-) create mode 100644 client/renderer/core.ts create mode 100644 client/renderer/math.ts create mode 100644 client/renderer/mod.ts create mode 100644 client/renderer/shapes.ts create mode 100644 client/renderer/text.ts create mode 100644 client/renderer/textures.ts create mode 100644 client/renderer/types.ts diff --git a/client/assets.ts b/client/assets.ts index 38097ee..caa7df6 100644 --- a/client/assets.ts +++ b/client/assets.ts @@ -1,4 +1,4 @@ -import { load_texture, Texture } from "./renderer.ts"; +import { load_texture, Texture } from "./renderer/mod.ts"; type Asset = Texture | HTMLAudioElement | string | Record; diff --git a/client/client_world.ts b/client/client_world.ts index bd970da..7ca042e 100644 --- a/client/client_world.ts +++ b/client/client_world.ts @@ -8,7 +8,7 @@ import { UIRenderSystem } from "$/client/systems/ui_render_system.ts"; import { create_main_menu } from "./main_menu.ts"; import { ClickableSystem } from "./systems/clickable_system.ts"; import { start_game } from "./game.ts"; -import { canvas, resize_canvas } from "./renderer.ts"; +import { canvas, resize_canvas } from "./renderer/mod.ts"; import { DimensionLogicSystem } from "./systems/dimension_logic.ts"; import { Dimension } from "./components/dimension.ts"; import { GuiRenderSystem, GuiTickSystem } from "./gui/gui_systems.ts"; diff --git a/client/components/dimension.ts b/client/components/dimension.ts index b98e1e1..a9d7cbb 100644 --- a/client/components/dimension.ts +++ b/client/components/dimension.ts @@ -2,7 +2,7 @@ import { Component } from "$/common/ecs/mod.ts"; import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts"; import { AssetManager } from "../assets.ts"; import { ClientWorld } from "../client_world.ts"; -import { Texture } from "../renderer.ts"; +import { Texture } from "../renderer/mod.ts"; export interface Tile { x: number; diff --git a/client/components/sprite.ts b/client/components/sprite.ts index 029a1ce..a7f53ec 100644 --- a/client/components/sprite.ts +++ b/client/components/sprite.ts @@ -1,6 +1,6 @@ import { Component } from "$/common/ecs/mod.ts"; import { AssetManager } from "$/client/assets.ts"; -import { Texture } from "../renderer.ts"; +import { Texture } from "../renderer/mod.ts"; export class Sprite extends Component { image: Texture; diff --git a/client/debug_ui.ts b/client/debug_ui.ts index ba99624..f9de59f 100644 --- a/client/debug_ui.ts +++ b/client/debug_ui.ts @@ -1,6 +1,6 @@ import { point_inside_rec } from "$/common/utils.ts"; import { InputManager } from "$/client/input_manager.ts"; -import { begin_clip, draw_rect, draw_rect_stroke, draw_text, end_clip, measure_text } from "./renderer.ts"; +import { begin_clip, draw_rect, draw_rect_stroke, draw_text, end_clip, measure_text } from "./renderer/mod.ts"; const HEADER_HEIGHT = 24; const RESIZE_SIZE = 12; diff --git a/client/game.ts b/client/game.ts index 5cd3380..76b836c 100644 --- a/client/game.ts +++ b/client/game.ts @@ -6,7 +6,7 @@ import { create_player } from "./player.ts"; import { UIButton } from "./components/ui_components.ts"; import { open_about } from "./about.ts"; import { generate } from "./generation.ts"; -import { canvas } from "./renderer.ts"; +import { canvas } from "./renderer/mod.ts"; export function start_game(world: ClientWorld) { world.state = "game"; diff --git a/client/gui/gui_chest.ts b/client/gui/gui_chest.ts index 3d8b6b2..9452a9a 100644 --- a/client/gui/gui_chest.ts +++ b/client/gui/gui_chest.ts @@ -1,6 +1,6 @@ import { Inventory, PlayerInventory } from "../inventory.ts"; import { add_player_hotbar, add_player_inventory, GuiScreen, Slot } from "./gui_screen.ts"; -import { canvas, draw_rect, Texture } from "$/client/renderer.ts"; +import { canvas, draw_rect, Texture } from "$/client/renderer/mod.ts"; import { AssetManager } from "../assets.ts"; import { SLOT_SIZE } from "../../common/constants.ts"; import { draw_nine_slice } from "../systems/rendering/render_utils.ts"; diff --git a/client/gui/gui_furnace.ts b/client/gui/gui_furnace.ts index e968835..363bb14 100644 --- a/client/gui/gui_furnace.ts +++ b/client/gui/gui_furnace.ts @@ -1,6 +1,6 @@ import { Inventory, PlayerInventory } from "../inventory.ts"; import { add_player_hotbar, add_player_inventory, GuiScreen, Slot } from "./gui_screen.ts"; -import { canvas, draw_rect, draw_text, Texture } from "$/client/renderer.ts"; +import { canvas, draw_rect, draw_text, Texture } from "$/client/renderer/mod.ts"; import { AssetManager } from "../assets.ts"; import { SLOT_SIZE } from "../../common/constants.ts"; import { draw_nine_slice } from "../systems/rendering/render_utils.ts"; diff --git a/client/gui/gui_player_inventory.ts b/client/gui/gui_player_inventory.ts index 3e55a83..cff89c9 100644 --- a/client/gui/gui_player_inventory.ts +++ b/client/gui/gui_player_inventory.ts @@ -1,6 +1,6 @@ import { Container, Inventory, ItemStack, PlayerInventory } from "../inventory.ts"; import { add_player_hotbar, add_player_inventory, GuiScreen, Slot } from "./gui_screen.ts"; -import { canvas, draw_rect, Texture } from "$/client/renderer.ts"; +import { canvas, draw_rect, Texture } from "$/client/renderer/mod.ts"; import { AssetManager } from "../assets.ts"; import { SLOT_SIZE } from "../../common/constants.ts"; import { draw_nine_slice } from "../systems/rendering/render_utils.ts"; diff --git a/client/gui/gui_screen.ts b/client/gui/gui_screen.ts index 1760c9e..1ee17fc 100644 --- a/client/gui/gui_screen.ts +++ b/client/gui/gui_screen.ts @@ -3,7 +3,7 @@ import { point_inside_rec } from "../../common/utils.ts"; import { AssetManager } from "../assets.ts"; import { InputManager } from "../input_manager.ts"; import { Inventory, ItemStack, PlayerInventory } from "../inventory.ts"; -import { canvas, Texture } from "../renderer.ts"; +import { canvas, Texture } from "../renderer/mod.ts"; import { draw_item, draw_nine_slice } from "../systems/rendering/render_utils.ts"; export class Slot { diff --git a/client/main.ts b/client/main.ts index 6d5d643..3172801 100644 --- a/client/main.ts +++ b/client/main.ts @@ -1,7 +1,7 @@ import { AssetManager } from "./assets.ts"; import { ClientWorld } from "./client_world.ts"; import { InputManager } from "./input_manager.ts"; -import { begin_drawing, end_drawing, init_font, init_window } from "./renderer.ts"; +import { begin_drawing, end_drawing, init_font, init_window } from "./renderer/mod.ts"; await import("./tiles/mod.ts"); await import("./items/mod.ts"); diff --git a/client/main_menu.ts b/client/main_menu.ts index 65ac29c..0423bc5 100644 --- a/client/main_menu.ts +++ b/client/main_menu.ts @@ -4,7 +4,7 @@ import { ClientWorld } from "./client_world.ts"; import { UIButton } from "./components/ui_components.ts"; import { open_about } from "./about.ts"; import { start_game } from "./game.ts"; -import { canvas } from "./renderer.ts"; +import { canvas } from "./renderer/mod.ts"; export function create_main_menu(world: ClientWorld) { const play_button = new Entity("play"); diff --git a/client/renderer/core.ts b/client/renderer/core.ts new file mode 100644 index 0000000..a466d0a --- /dev/null +++ b/client/renderer/core.ts @@ -0,0 +1,285 @@ +import { Camera } from "../components/camera.ts"; + +const MAX_SPRITES = 10000; +const VERTS_PER_SPRITE = 6; +const FLOATS_PER_VERT = 8; + +export let gl: WebGLRenderingContext; +export let canvas: HTMLCanvasElement; +let program: WebGLProgram; +let buffer: WebGLBuffer; + +const default_camera = new Camera(); +let camera: Camera = default_camera; + +let camera_pos_loc: WebGLUniformLocation | null; +let camera_zoom_loc: WebGLUniformLocation | null; +let camera_rot_loc: WebGLUniformLocation | null; +let camera_offset_loc: WebGLUniformLocation | null; + +const vertex_data = new Float32Array(MAX_SPRITES * VERTS_PER_SPRITE * FLOATS_PER_VERT); +let vert_index = 0; + +let pos_loc: GLint; +let uv_loc: GLint; +let resolution_loc: WebGLUniformLocation | null; +let _texture_loc: WebGLUniformLocation | null; + +let current_texture: WebGLTexture | null = null; +export let white_tex: WebGLTexture | null = null; + +const vertex_src = ` +attribute vec2 aPos; +attribute vec2 aUV; +attribute vec4 aColor; + +varying vec2 vUV; +varying vec4 vColor; + +uniform vec2 uResolution; +uniform vec2 uCameraPos; +uniform float uCameraZoom; +uniform float uCameraRot; +uniform vec2 uCameraOffset; + +void main(){ + + // world → camera space + vec2 pos = aPos - uCameraPos; + + // rotation + float c = cos(uCameraRot); + float s = sin(uCameraRot); + pos = vec2( + pos.x * c - pos.y * s, + pos.x * s + pos.y * c + ); + + // zoom + pos *= uCameraZoom; + + // screen offset (center camera) + pos += uCameraOffset; + + // convert to clip space + vec2 zero = pos / uResolution; + vec2 clip = zero * 2.0 - 1.0; + + gl_Position = vec4(clip * vec2(1.0,-1.0),0.0,1.0); + + vUV = aUV; + vColor = aColor; +} +`; + +const fragment_src = ` +precision mediump float; +varying vec2 vUV; +varying vec4 vColor; +uniform sampler2D uTex; + +void main() { + vec4 texColor = texture2D(uTex, vUV); + gl_FragColor = texColor * vColor; +} +`; + +export function init_window(canvas_element: HTMLCanvasElement) { + canvas = canvas_element; + canvas.width = 1800; + canvas.height = 900; + const ctx = canvas.getContext("webgl"); + if (!ctx) { + throw new Error("WebGL not supported"); + } + + gl = ctx; + + program = create_program(vertex_src, fragment_src); + + buffer = gl.createBuffer()!; + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + const stride = FLOATS_PER_VERT * 4; + + pos_loc = gl.getAttribLocation(program, "aPos"); + gl.enableVertexAttribArray(pos_loc); + gl.vertexAttribPointer(pos_loc, 2, gl.FLOAT, false, stride, 0); + + uv_loc = gl.getAttribLocation(program, "aUV"); + gl.enableVertexAttribArray(uv_loc); + gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 8); + + const color_loc = gl.getAttribLocation(program, "aColor"); + gl.enableVertexAttribArray(color_loc); + gl.vertexAttribPointer(color_loc, 4, gl.FLOAT, false, FLOATS_PER_VERT * 4, 16); + + resolution_loc = gl.getUniformLocation(program, "uResolution"); + _texture_loc = gl.getUniformLocation(program, "uTex"); + + camera_pos_loc = gl.getUniformLocation(program, "uCameraPos"); + camera_zoom_loc = gl.getUniformLocation(program, "uCameraZoom"); + camera_rot_loc = gl.getUniformLocation(program, "uCameraRot"); + camera_offset_loc = gl.getUniformLocation(program, "uCameraOffset"); + + gl.useProgram(program); + gl.uniform2f(resolution_loc, canvas.width, canvas.height); + + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + + create_white_texture(); +} + +export function begin_drawing() { + vert_index = 0; +} + +export function end_drawing() { + flush_batch(); +} + +export function begin_mode_2d(new_camera: Camera) { + camera = new_camera; +} + +export function end_mode_2d() { + flush_batch(); + camera = default_camera; +} + +export function begin_clip(x: number, y: number, width: number, height: number) { + gl.enable(gl.SCISSOR_TEST); + + const flipped_y = canvas.height - (y + height); + + gl.scissor(x, flipped_y, width, height); +} + +export function end_clip() { + gl.disable(gl.SCISSOR_TEST); +} + +export function clear_background(r: number, g: number, b: number, a = 1) { + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT); +} + +export function flush_batch() { + if (vert_index === 0 || !current_texture) { + return; + } + + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, vertex_data.subarray(0, vert_index), gl.STREAM_DRAW); + + gl.useProgram(program); + + // const stride = FLOATS_PER_VERT * 4; + // gl.enableVertexAttribArray(pos_loc); + // gl.vertexAttribPointer(pos_loc, 2, gl.FLOAT, false, stride, 0); + // gl.enableVertexAttribArray(uv_loc); + // gl.vertexAttribPointer(uv_loc, 2, gl.FLOAT, false, stride, 8); + + gl.uniform2f(camera_pos_loc, Math.floor(camera.x), Math.floor(camera.y)); + gl.uniform1f(camera_zoom_loc, camera.zoom); + gl.uniform1f(camera_rot_loc, camera.rotation); + gl.uniform2f(camera_offset_loc, camera.offset_x, camera.offset_y); + + gl.bindTexture(gl.TEXTURE_2D, current_texture); + gl.drawArrays(gl.TRIANGLES, 0, vert_index / 8); + vert_index = 0; +} + +export function push_quad( + dx: number, + dy: number, + dw: number, + dh: number, + u0: number, + v0: number, + u1: number, + v1: number, + r = 1, + g = 1, + b = 1, + a = 1, +) { + const x2 = dx + dw; + const y2 = dy + dh; + + // deno-fmt-ignore + const v = [ + dx, dy, u0, v0, r, g, b, a, + x2, dy, u1, v0, r, g, b, a, + dx, y2, u0, v1, r, g, b, a, + + x2, dy, u1, v0, r, g, b, a, + x2, y2, u1, v1, r, g, b, a, + dx, y2, u0, v1, r, g, b, a, + ]; + + for (let i = 0; i < v.length; i += 1) { + vertex_data[vert_index++] = v[i]; + } +} + +export function resize_canvas() { + const width = self.innerWidth; + const height = self.innerHeight; + + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; + canvas.height = height; + canvas.style.width = canvas.width + "px"; + canvas.style.height = canvas.height + "px"; + + gl.viewport(0, 0, width, height); + + gl.useProgram(program); + gl.uniform2f(resolution_loc, width, height); + } +} + +export function get_current_texture(): WebGLTexture | null { + return current_texture; +} + +export function set_current_texture(texture: WebGLTexture) { + current_texture = texture; +} + +function create_shader(type: number, src: string): WebGLShader { + const s = gl.createShader(type)!; + gl.shaderSource(s, src); + gl.compileShader(s); + return s; +} + +function create_program(vs: string, fs: string): WebGLProgram { + const program = gl.createProgram(); + + const v = create_shader(gl.VERTEX_SHADER, vs); + const f = create_shader(gl.FRAGMENT_SHADER, fs); + + gl.attachShader(program, v); + gl.attachShader(program, f); + gl.linkProgram(program); + + return program; +} + +function create_white_texture() { + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + const white_pixel = new Uint8Array([255, 255, 255, 255]); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, white_pixel); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + white_tex = tex; +} diff --git a/client/renderer/math.ts b/client/renderer/math.ts new file mode 100644 index 0000000..f643f7f --- /dev/null +++ b/client/renderer/math.ts @@ -0,0 +1,24 @@ +export function perspective(out: Float32Array, fov: number, aspect: number, near: number, far: number) { + const f = 1 / Math.tan(fov / 2); + const nf = 1 / (near - far); + + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; +} diff --git a/client/renderer/mod.ts b/client/renderer/mod.ts new file mode 100644 index 0000000..62651b5 --- /dev/null +++ b/client/renderer/mod.ts @@ -0,0 +1,5 @@ +export * from "./core.ts"; +export * from "./shapes.ts"; +export * from "./textures.ts"; +export * from "./text.ts"; +export * from "./types.ts"; diff --git a/client/renderer/shapes.ts b/client/renderer/shapes.ts new file mode 100644 index 0000000..540edc5 --- /dev/null +++ b/client/renderer/shapes.ts @@ -0,0 +1,34 @@ +import { flush_batch, get_current_texture, push_quad, set_current_texture, white_tex } from "./core.ts"; + +export function draw_rect( + x: number, + y: number, + w: number, + h: number, + color: [number, number, number, number] = [1, 1, 1, 1], +) { + if (get_current_texture() !== white_tex) { + flush_batch(); + set_current_texture(white_tex!); + } + + push_quad(x, y, w, h, 0, 0, 1, 1, color[0], color[1], color[2], color[3]); +} + +export function draw_rect_stroke( + x: number, + y: number, + w: number, + h: number, + color: [number, number, number, number] = [1, 1, 1, 1], + thickness: number = 1, +) { + // top + draw_rect(x, y, w, thickness, color); + // bottom + draw_rect(x, y + h - thickness, w, thickness, color); + // left + draw_rect(x, y, thickness, h, color); + // right + draw_rect(x + w - thickness, y, thickness, h, color); +} diff --git a/client/renderer/text.ts b/client/renderer/text.ts new file mode 100644 index 0000000..a16255a --- /dev/null +++ b/client/renderer/text.ts @@ -0,0 +1,107 @@ +import { AssetManager } from "../assets.ts"; +import { flush_batch, get_current_texture, push_quad, set_current_texture } from "./core.ts"; +import { FntChar, FntFont, Texture } from "./types.ts"; + +let font_texture: Texture; +let font_fnt: FntFont; + +export function init_font() { + font_texture = AssetManager.instance.get("bworld:m6x11"); + + font_fnt = parse_fnt(AssetManager.instance.get("bworld:m6x11_fnt")); +} + +function parse_fnt(text: string): FntFont { + const lines = text.split(/\r?\n/); + + const font: FntFont = { + line_height: 0, + chars: {}, + pages: [], + }; + + for (const line of lines) { + const parts = line.split(" "); + const type = parts[0]; + + const data: Record = {}; + + for (const part of parts.slice(1)) { + const [k, v] = part.split("="); + if (!k) { + continue; + } + data[k] = v?.replace(/"/g, ""); + } + + if (type === "common") { + font.line_height = Number(data.lineHeight); + } + + if (type === "page") { + font.pages.push(data.file); + } + + if (type === "char") { + const char: FntChar = { + id: Number(data.id), + x: Number(data.x), + y: Number(data.y), + width: Number(data.width), + height: Number(data.height), + xoffset: Number(data.xoffset), + yoffset: Number(data.yoffset), + xadvance: Number(data.xadvance), + page: Number(data.page), + }; + + font.chars[char.id] = char; + } + } + + return font; +} + +export function draw_text(str: string, x: number, y: number, scale: number = 1, color = [1, 1, 1, 1]) { + if (!font_texture) { + return; + } + + if (get_current_texture() !== font_texture.tex) { + flush_batch(); + set_current_texture(font_texture.tex); + } + + for (const ch of str) { + const glyph = font_fnt.chars[ch.charCodeAt(0)]; + if (!glyph) { + continue; + } + + const dx = x + glyph.xoffset * scale; + const dy = y + glyph.yoffset * scale; + const dw = glyph.width * scale; + const dh = glyph.height * scale; + + const u0 = glyph.x / font_texture.width; + const v0 = glyph.y / font_texture.height; + const u1 = (glyph.x + glyph.width) / font_texture.width; + const v1 = (glyph.y + glyph.height) / font_texture.height; + + push_quad(dx, dy, dw, dh, u0, v0, u1, v1, color[0], color[1], color[2], color[3]); + + x += glyph.xadvance * scale; + } +} + +export function measure_text(str: string, scale: number = 1) { + let x = 0; + for (const ch of str) { + const glyph = font_fnt.chars[ch.charCodeAt(0)]; + if (!glyph) { + continue; + } + x += glyph.xadvance * scale; + } + return x; +} diff --git a/client/renderer/textures.ts b/client/renderer/textures.ts new file mode 100644 index 0000000..8ffd184 --- /dev/null +++ b/client/renderer/textures.ts @@ -0,0 +1,93 @@ +import { flush_batch, get_current_texture, gl, push_quad, set_current_texture } from "./core.ts"; +import { Texture } from "./types.ts"; + +export function load_texture(image: HTMLImageElement): Texture { + const texture = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + gl.texImage2D( + gl.TEXTURE_2D, + 0, + gl.RGBA, + gl.RGBA, + gl.UNSIGNED_BYTE, + image, + ); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + return { + tex: texture, + width: image.width, + height: image.height, + }; +} + +export function draw_texture( + texture: Texture, + x: number, + y: number, + w: number, + h: number, + flip_x = false, + flip_y = false, +) { + if (get_current_texture() !== texture.tex) { + flush_batch(); + set_current_texture(texture.tex); + } + + let u0 = 0, v0 = 0, u1 = 1, v1 = 1; + + if (flip_x) { + [u0, u1] = [u1, u0]; + } + if (flip_y) { + [v0, v1] = [v1, v0]; + } + + push_quad(x, y, w, h, u0, v0, u1, v1); +} + +export function draw_texture_region( + texture: Texture, + sx: number, + sy: number, + sw: number, + sh: number, + dx: number, + dy: number, + dw: number, + dh: number, + flip_x = false, + flip_y = false, + r = 1, + g = 1, + b = 1, + a = 1, +) { + if (get_current_texture() !== texture.tex) { + flush_batch(); + set_current_texture(texture.tex); + } + + let u0 = sx / texture.width; + let v0 = sy / texture.height; + let u1 = (sx + sw) / texture.width; + let v1 = (sy + sh) / texture.height; + + if (flip_x) { + [u0, u1] = [u1, u0]; + } + if (flip_y) { + [v0, v1] = [v1, v0]; + } + + push_quad(dx, dy, dw, dh, u0, v0, u1, v1, r, g, b, a); +} diff --git a/client/renderer/types.ts b/client/renderer/types.ts new file mode 100644 index 0000000..e4875e6 --- /dev/null +++ b/client/renderer/types.ts @@ -0,0 +1,32 @@ +export interface Texture { + tex: WebGLTexture; + width: number; + height: number; +} + +export interface FntChar { + id: number; + x: number; + y: number; + width: number; + height: number; + xoffset: number; + yoffset: number; + xadvance: number; + page: number; +} + +export interface FntFont { + line_height: number; + chars: Record; + pages: string[]; +} + +export class Camera3D { + x = 0; + y = 0; + z = 0; + + pitch = 0; + yaw = 0; +} diff --git a/client/systems/clickable_system.ts b/client/systems/clickable_system.ts index 4575104..50e27a8 100644 --- a/client/systems/clickable_system.ts +++ b/client/systems/clickable_system.ts @@ -6,7 +6,7 @@ import { ClickableSprite } from "../components/clickable.ts"; import { point_inside_rec } from "$/common/utils.ts"; import { InputManager } from "../input_manager.ts"; import { Camera, screen_to_world } from "../components/camera.ts"; -import { canvas } from "../renderer.ts"; +import { canvas } from "../renderer/mod.ts"; export class ClickableSystem extends System { update(world: ClientWorld, _delta: number): void { diff --git a/client/systems/dimension_logic.ts b/client/systems/dimension_logic.ts index 0f6f817..1b0b839 100644 --- a/client/systems/dimension_logic.ts +++ b/client/systems/dimension_logic.ts @@ -3,7 +3,7 @@ import { ClientWorld } from "../client_world.ts"; import { Dimension } from "../components/dimension.ts"; import { Camera, screen_to_world } from "../components/camera.ts"; import { InputManager } from "../input_manager.ts"; -import { canvas } from "../renderer.ts"; +import { canvas } from "../renderer/mod.ts"; import { TICK_DELTA, TILE_SIZE } from "$/common/constants.ts"; import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts"; import { Position } from "$/common/components/position.ts"; diff --git a/client/systems/player_controls.ts b/client/systems/player_controls.ts index 88218c3..414a56c 100644 --- a/client/systems/player_controls.ts +++ b/client/systems/player_controls.ts @@ -6,7 +6,7 @@ import { ClientWorld } from "../client_world.ts"; import { PlayerControls } from "../components/player_controls.ts"; import { Camera } from "../components/camera.ts"; import { Position } from "../../common/components/position.ts"; -import { canvas } from "../renderer.ts"; +import { canvas } from "../renderer/mod.ts"; import { PlayerComponent } from "../player.ts"; import { GuiPlayerInventory } from "../gui/gui_player_inventory.ts"; diff --git a/client/systems/render_system.ts b/client/systems/render_system.ts index 3ffca2a..b98a82d 100644 --- a/client/systems/render_system.ts +++ b/client/systems/render_system.ts @@ -8,7 +8,7 @@ import { Camera } from "$/client/components/camera.ts"; import { render_animated_sprite, render_sprite } from "./rendering/sprites.ts"; import { render_dimension } from "./rendering/dimension.ts"; import { render_player_hotbar } from "./rendering/player.ts"; -import { begin_mode_2d, end_mode_2d } from "../renderer.ts"; +import { begin_mode_2d, end_mode_2d } from "../renderer/mod.ts"; import { PlayerComponent } from "../player.ts"; export class RenderSystem extends System { diff --git a/client/systems/rendering/dimension.ts b/client/systems/rendering/dimension.ts index b457f8c..fb9da57 100644 --- a/client/systems/rendering/dimension.ts +++ b/client/systems/rendering/dimension.ts @@ -2,7 +2,7 @@ import { get_sprite_region } from "$/common/utils.ts"; import { Dimension } from "$/client/components/dimension.ts"; import { TEXTURE_SIZE, TILE_SIZE } from "$/common/constants.ts"; import { Camera, world_to_screen } from "$/client/components/camera.ts"; -import { canvas, draw_texture_region } from "$/client/renderer.ts"; +import { canvas, draw_texture_region } from "$/client/renderer/mod.ts"; import { EverythingRegistry, TileRegistry } from "$/common/everything_registry.ts"; export function render_dimension(dimension: Dimension, camera: Camera) { diff --git a/client/systems/rendering/player.ts b/client/systems/rendering/player.ts index 05e148b..680b0e1 100644 --- a/client/systems/rendering/player.ts +++ b/client/systems/rendering/player.ts @@ -2,7 +2,7 @@ import { SLOT_SIZE } from "$/common/constants.ts"; import { AssetManager } from "$/client/assets.ts"; import { PlayerInventory } from "../../inventory.ts"; import { draw_item, draw_nine_slice } from "./render_utils.ts"; -import { canvas, Texture } from "$/client/renderer.ts"; +import { canvas, Texture } from "$/client/renderer/mod.ts"; const PADDING = 10; diff --git a/client/systems/rendering/render_utils.ts b/client/systems/rendering/render_utils.ts index d56253b..f8524a7 100644 --- a/client/systems/rendering/render_utils.ts +++ b/client/systems/rendering/render_utils.ts @@ -2,7 +2,7 @@ import { SLOT_SIZE } from "$/common/constants.ts"; import { get_sprite_region } from "$/common/utils.ts"; import { AssetManager } from "$/client/assets.ts"; import { ItemStack } from "../../inventory.ts"; -import { draw_text, draw_texture_region, Texture } from "$/client/renderer.ts"; +import { draw_text, draw_texture_region, Texture } from "$/client/renderer/mod.ts"; export function draw_nine_slice( image: Texture, diff --git a/client/systems/rendering/sprites.ts b/client/systems/rendering/sprites.ts index 15dffba..e9b6a7a 100644 --- a/client/systems/rendering/sprites.ts +++ b/client/systems/rendering/sprites.ts @@ -1,6 +1,6 @@ import { Position } from "$/common/components/position.ts"; import { AnimatedSprite, Sprite } from "$/client/components/sprite.ts"; -import { draw_texture_region } from "$/client/renderer.ts"; +import { draw_texture_region } from "$/client/renderer/mod.ts"; import { Camera } from "$/client/components/camera.ts"; // TODO: camera scale diff --git a/client/systems/ui_render_system.ts b/client/systems/ui_render_system.ts index 49f2c3b..5955616 100644 --- a/client/systems/ui_render_system.ts +++ b/client/systems/ui_render_system.ts @@ -4,7 +4,7 @@ import { ClientWorld } from "$/client/client_world.ts"; import { UIButton } from "$/client/components/ui_components.ts"; import { draw_nine_slice } from "./rendering/render_utils.ts"; import { AssetManager } from "../assets.ts"; -import { draw_text, Texture } from "../renderer.ts"; +import { draw_text, Texture } from "../renderer/mod.ts"; const SCALE = 4;