Architecture

System design and component overview

Contents

Overview

Render Engine is a 2D graphics library built on OpenGL and LWJGL. It provides a complete framework for games and interactive applications with a clean separation between logic updates (tick) and rendering.

Components

RenderEngineCore

Base class for all applications. Manages the game loop, window, and provides access to all subsystems.

  • onEnable() — initialization
  • tick() — logic update (fixed rate, default 60/sec)
  • onRender() — rendering (variable rate)
  • onDisable() — cleanup

Input System

Keyboard and mouse handling with three event types:

  • Press — fires once when key/button goes down
  • Pressed — fires every frame while held
  • Release — fires once when released
keyboard.onKeyPress(GLFW_KEY_SPACE) { jump() }
keyboard.onKeyPressed(GLFW_KEY_W) { moveForward() }
mouse.onMouseClick(GLFW_MOUSE_BUTTON_LEFT) { x, y -> shoot(x, y) }
mouse.onMouseScroll { offset -> zoom(offset) }

Camera

2D camera with target following, zoom, and coordinate transformation.

camera.initialize(player)  // Follow player
camera.bounds = CameraBounds(0f, 0f, worldWidth, worldHeight)
camera.minZoom = 0.5f
camera.maxZoom = 3f

// In onRender:
val worldPos = camera.screenToScene(screenX, screenY)

Resource System

Minecraft-style resource packs with priority ordering and fallback to JAR resources.

// Automatically checks resource packs first, then JAR
val path = ResourcePackManager.getResourcePath("textures/player.png")

// Manage packs
ResourcePackManager.enablePack("MyPack")
ResourcePackManager.movePackUp("MyPack")

Texture Registry

Automatic texture atlas generation for draw call optimization.

textureRegistry.registerForAtlas("textures/player.png")
textureRegistry.registerForAtlas("textures/enemy.png")
textureRegistry.buildAtlas()

val tex = textureRegistry.getFromAtlas("textures/player.png")

Font Registry

TrueType/OpenType fonts with Unicode support.

fontRegistry.register("main", "fonts/Inter.ttf", 24)
fontRegistry.register("title", "fonts/Inter.ttf", 48)
fontRegistry.initialize()

val font = fontRegistry.get("main")

Render Engine

Low-level rendering with sprite, geometry, and text renderers.

renderEngine.spriteRenderer.renderSprite(x, y, w, h, texture)
renderEngine.geometryRenderer.renderRectangle(x, y, w, h, color)
renderEngine.textRenderer.renderText("Score: 100", x, y, font)
renderEngine.flush()  // Send to GPU

Utilities

// Cooldown timer
val attackCooldown = Cooldown(500)  // 500ms
if (attackCooldown.isEnded()) {
    attack()
    attackCooldown.start()
}

// 2D Point
val pos = Point(100f, 200f)

// Colors
val color = Color.RED.rgba

Application Lifecycle

  1. main() — entry point, GLFW initialization
  2. Engine.enable() — engine start and window creation
  3. onEnable() — resource initialization, game setup
  4. Game Loop — main loop:
    • tick() — logic update (fixed rate, default 60/sec)
    • onRender() — rendering (max possible rate)
  5. onDisable() — resource cleanup, window close

Timing & Interpolation

The engine separates logic updates and rendering. Logic is updated at a fixed rate (tick), while rendering can happen more frequently. Interpolation is used for smooth transitions between states.
Main parameters:

For smooth animation, use the interpolation pattern:
val interpolated = lerp(statePrev, stateNext, partialTick)

In the Interpolatable class, store two states (current and previous) and use getInterpolatedState() for rendering.

Input Processing

Extending

Custom Renderer

class MyEngine : RenderEngineCore(...) {
    override fun setupRenderEngines() {
        super.setupRenderEngines()
        myRenderer = MyCustomRenderer()
        myRenderer.register()
    }
}

Custom Resource Provider

object MyResources : ResourceProvider {
    override fun getResourcePath(path: String): String {
        // Custom resolution logic
    }
}

ResourceProvider.register(MyResources)