CRM64Pro GDK v0.20.0
A free cross-platform game development kit built on top of SDL 3.0
Loading...
Searching...
No Matches
Reference Guide

1. Introduction

The ultimate SDL3 companion for 2D games and tools.

CRM64Pro is a modern, free, cross-platform C++17 Game Development Kit built on SDL3, with rendering, particle effects, 2D lighting, audio, video, Scene tooling, CDC archives and the EditorC64 asset workflow. It is designed to help developers create high-performance 2D games, tools and interactive applications through a clean, modular and hardware-accelerated framework.

CRM64Pro is distributed under the zlib license.

  Modern core

Developed in C++17 and powered by SDL3, featuring fixed-step timing, multi-threaded services, advanced logging utilities, debug windows with live numeric and string watches, an embedded console, and CDCv2 asset archives with compression, >4GB file support, validation, encrypted storage and streaming block I/O.

  Complete toolset

Includes an advanced Scene and world management system with native Tiled map support, Scene particles, animated point and spot lighting, persistent Physics collision sets and configurable object overlays. EditorC64 imports Universal VTT maps and animated GIF, WebP and H.264 sources into Tiled-compatible assets. Images can be loaded from BMP, PNG, JPG/JPEG, PCX, TGA and WebP, saved as BMP or PNG, and processed with built-in color, distortion and alpha filters. The full GUI windowing system includes aligned text controls.

  Hardware accelerated

Supports Direct3D 11/12, Vulkan, Metal, and OpenGL, ensuring fast and smooth rendering across all supported platforms.


Developed by MegaStorm Systems.
Visit the official website for updates, releases, and complete documentation.
MegaStorm Systems

2. Supported platforms

The GDK runs on major operating systems. Thanks to SDL3, it supports a wide variety of hardware-accelerated rendering and low-latency audio drivers.

Platform Requirements Render drivers Audio drivers
Windows 7, 10 and 11
(64-bit only)
Direct3D 9
Direct3D 11
Direct3D 12
Vulkan
GPU
OpenGL
Software
WASAPI
DirectSound
Kernel 4.18+
(64-bit only)
Vulkan
GPU
OpenGL
X11
Wayland
Software
PulseAudio
ALSA
macOS 10.15+
(Catalina or later for x86_64)

macOS 11+
(Big Sur or later for arm64)
Metal
Vulkan
GPU
OpenGL
Software
CoreAudio
Android
Not available yet
Android 5.0+ (API 21+)
OpenGL ES
Vulkan
AAudio
OpenSL ES

3. Installation

This section describes how to install and configure the CRM64Pro GDK for development on the supported platforms.

3.1. Windows (Visual Studio 2022+)

Windows Development

Executable Installer (Inno Setup)

1. Download and Install CRM64Pro is distributed as a standard Windows installer created with Inno Setup (for example: CRM64Pro-X.Y.Z-win-x64.exe).

The installer will deploy headers, libraries and the EditorC64 into the selected destination.

The following libraries are provided:

  • CRM64Pro.lib – Dynamic linking (DLL import library)
  • CRM64Pro.static.lib – Static linking (Release)
  • CRM64Pro.static-debug.lib – Static linking with debug information

2. Configure Visual Studio No global system variables are required. Configure your project locally:

  1. Right-click your project in Solution Explorer and select Properties.
  2. Select All Configurations and x64 platform.
  3. Add the CRM64Pro include/ directory to C/C++ → General → Additional Include Directories.
  4. Add the CRM64Pro lib/ directory to Linker → General → Additional Library Directories.
  5. Add one of the following to Linker → Input → Additional Dependencies:
    • CRM64Pro.lib (Dynamic linking)
    • CRM64Pro.static.lib or CRM64Pro.static-debug.lib (Static linking)

3. Runtime (DLL) When using dynamic linking, ensure that CRM64Pro.dll is located next to your executable or available in the system PATH (the installer allows to update this automatically).

3.2. Linux (GCC 13+)

Linux Development

Self-Extracting Bash Installer

1. Download and Install CRM64Pro for Linux is distributed as a self-extracting Bash installer (for example: CRM64Pro-X.Y.Z-linux-x64.sh).

Run the installer from a terminal:

chmod +x CRM64Pro-X.Y.Z-linux-x64.sh
sudo ./CRM64Pro-X.Y.Z-linux-x64.sh

The installer places headers and libraries in standard system locations (e.g. /usr/local/include and /usr/local/lib).

2. Compiling Once installed, CRM64Pro can be linked using standard compiler flags:

g++ main.cpp -o mygame -lCRM64Pro

3.3. macOS (Clang 13+)

macOS Development

DMG Package (Universal Binary)

1. Download and Install CRM64Pro is distributed as a DMG package. The installer provides a universal library supporting both x86_64 and arm64 (Apple Silicon) architectures.

Headers and libraries are installed into standard locations (e.g. /usr/local or /Library).

2. Compiling You can compile from the terminal or configure Xcode normally:

clang++ main.cpp -o mygame -lCRM64Pro

4. Getting started

Not available yet.

5. Game architecture

CRM64Pro abstracts the complexity of modern game loops into a flexible "Governor" pattern (Main::update). This allows developers to choose the synchronization strategy that best fits their game genre, from simple variable time-step loops to high-precision interpolated fixed-step systems.

Threading and frame execution

The main application thread calls Main::update(), runs application logic, owns public GDK state and performs every render operation. Public methods are main-thread operations unless their documentation explicitly says otherwise. See Threading conventions for the API contract.

Internal work never changes that contract. Audio mixing and NetTCP socket I/O run independently, while particle workers run only during a synchronous ParticleMgr update batch. Scene layer state, object transforms, callbacks, resources and all GFX calls remain on the main thread.

Frame execution timeline

Particle update rule:
Update standalone emitters through ParticleMgr::update(). Scene-owned emitters are updated automatically by Scene::update(); do not update them separately through ParticleMgr. Calling ParticleEmitter::update() directly remains the main-thread serial alternative for a deliberately unmanaged emitter. Every update call returns only after its worker jobs complete.

Game loop methods

From basic to professional approaches

Timing values
Main exposes three timing values. They are intentionally separate because logic updates and rendering do not always run at the same rate:

Method Use it for Fixed logic mode Variable logic mode
Main::getLogicDeltaTime() Game simulation, physics, AI, particles, Scene updates and playback advancement. Returns the fixed logic step, exactly 1.0 / LFR, for every consumed logic update. Returns the current frame delta.
Main::getInterpolationFactor() Render-only interpolation between the previous and current logic state. Returns the accumulator fraction in the [0.0, 1.0) range. Returns 0.0.
Main::getDeltaTime() Wall-clock frame elapsed time, profiling, diagnostics, or effects that intentionally depend on real frame time. Returns the last engine-frame elapsed time; this is not the logic step. Returns the current frame delta.

Rule:
Use Main::getLogicDeltaTime() inside logic code. Use Main::getInterpolationFactor() only while rendering. Use Main::getDeltaTime() only when actual elapsed wall-clock frame time is required.

When the application calls Scene::update(), Scene reads Main::getLogicDeltaTime() internally, so Scene simulation speed is independent from render rate. Video playback is advanced by Main with the same logic delta. Sprite position interpolation uses Main::getInterpolationFactor() during rendering; Sprite animation timing remains internally managed by the Sprite module.

5.1. Variable Time Step (Synchronous)

This is the classic "render-as-fast-as-possible" loop. Logic and Graphics updates occur sequentially in the same iteration. The "Main loop" will run at the maximum speed provided by the CPU.

  • Mechanism: update() processes events and returns control codes. You run logic when it returns 0, and render when the engine emits ::EC_RENDER.
  • Pros: Extremely simple to implement and debug.
  • Cons: Physics and game speed are tied to framerate. Requires carefully managed "Delta Time" for movement, or the game will run faster on better hardware.
  • Best for: Simple demos, visual novels, or learning projects.

Performance Note:
You can use CRM64Pro::ConfigMgr::setMTFriendly() to specify a minimum wait time (milliseconds) per frame. This yields execution control back to the operating system to avoid unnecessary 100% CPU usage.

Warning: Do not try to use this parameter for "time control" (e.g., setting it to 10ms to get 100fps). OS scheduling is not guaranteed; some iterations could take 10ms and others more, breaking the "smoothness" of the loop.

Code Example: Variable Step
Main &mC64 = Main::instance();
SDL_Event myEvent;
Sint32 iUpdate = 1;
Uint8 bRunning = 1;
while(bRunning)
{
// 1. Engine Governor (Process Input/Events)
while((iUpdate = mC64.update(&myEvent)) != 0)
{
if(iUpdate == SDL_EVENT_QUIT || myEvent.type == SDL_EVENT_QUIT) bRunning = 0;
}
if(!bRunning) break;
// 2. Logic
float fDeltaTime = mC64.getLogicDeltaTime();
MyGame_Update(fDeltaTime);
// 3. Render
// MyGame_Draw();
}

5.2. Fixed Logic Step with Independent Rendering

In this mode, logic runs at a guaranteed deterministic rate (e.g., 20 Hz), while rendering is scheduled independently on the main thread at the monitor's refresh rate (e.g., 144 Hz or VSync). This is scheduling, not a separate rendering thread.

  • Mechanism: You configure timer() with a target rate. The update() method manages the accumulator and triggers logic updates only when required. Graphics are executed via the ::EC_RENDER event.
  • Pros: Deterministic physics (essential for collision stability). Logic speed is independent of framerate.
  • Cons: Without interpolation, moving objects may appear to "jitter" or "stutter" if the render rate doesn't perfectly match the logic rate.
  • Best for: GUI-heavy applications.

Performance Note:
In this mode, the graphics logic runs without limit (max CPU/GPU speed). On fast systems, CRM64Pro::ConfigMgr::setMTFriendly() can still be used to yield execution back to the OS and avoid 100% CPU usage on unnecessary rendering frames.

Code Example: Fixed Step
Main &mC64 = Main::instance();
// Configure Logic to run exactly 60 times per second
mC64.timer().init();
mC64.timer().setRate(0, 60);
SDL_Event myEvent;
Sint32 iUpdate = 1;
Uint8 bRunning = 1;
while(bRunning)
{
// 1. Engine Governor
while((iUpdate = mC64.update(&myEvent)) != 0)
{
switch(myEvent.type)
{
case SDL_EVENT_QUIT: bRunning = 0; break;
// 2. Render (Triggered freely, e.g., at 144Hz)
case ET_C64:
if(myEvent.user.code == EC_RENDER)
{
// MyGame_Draw();
}
break;
}
}
if(!bRunning) break;
// 3. Logic (Runs exactly 60Hz)
float fDeltaTime = mC64.getLogicDeltaTime();
MyGame_UpdateFixed(fDeltaTime);
}

5.3. Fixed Time Step with Interpolation (Professional)

This is the gold standard for action games. Logic runs at a fixed, low frequency (e.g., 20 or 30 Hz) for stability, but the Engine renders at maximum framerate by interpolating positions between the previous and current logic states.

  • Mechanism: You provide a RenderCallback. The engine calls this automatically. CRM64Pro sprites and scrolling handle interpolation automatically.
  • Pros: "Buttery smooth" motion even with low CPU usage. High-performance physics stability. Separation of concerns.
  • Cons: Slightly more complex setup. You must not modify game state inside the render callback.
  • Best for: High-performance 2D worlds, complex ARPGs, platformers, and any game requiring pixel-perfect physics and cinematic camera logic.

Performance Note:
Using the callback function will produce a very smooth graphics output. This callback function can be changed dynamically or disabled using nullptr.
On fast systems, CRM64Pro::ConfigMgr::setMTFriendly() can still be used to avoid unnecessary 100% CPU usage.

Code Example: Interpolated
// Initialization stuff
Main &mC64 = Main::instance();
mC64.timer().init();
mC64.timer().setRate(0,20); // Set the Logic Frame Rate to 20
// ...
// Set a render callback for our main screen
Screen *mScreen = mC64.configMgr().get();
mScreen->setRenderCallback([](Sint32 iMode) -> Sint32
{
// My graphics stuff
// ...
return 0;
});
// Main loop
SDL_Event myEvent;
Sint32 iUpdate = 1;
Uint8 bRunning = 1;
while(bRunning)
{
// C64 main governor
while((iUpdate = mC64.update(&myEvent)) != 0)
{
switch(myEvent.type)
{
case SDL_EVENT_QUIT:
bRunning = 0;
break;
}
}
if(!bRunning) break;
// My logic stuff
float fDeltaTime = mC64.getLogicDeltaTime();
MyGame_UpdateFixed(fDeltaTime);
}

6. Configuration system

Configuration system

Built-in launcher and customization

CRM64Pro includes a built-in "Launcher" window (CRM64Pro::ConfigMgr::setup). This allows end-users to configure hardware settings (Resolution, Monitor, Audio Driver) before the engine initializes the full graphical context.

Default Setup Launcher

Key Features:

  • Video Resolution & Window Mode selection.
  • Graphics API & Monitor selection.
  • Audio Driver selection & Output toggling.
  • Automatic configuration saving/loading.

Workflow

  1. Call setup(): Launches the GUI. Returns 0 if the user clicked "OK".
  2. Call load(): Reads the saved configuration and initializes the system.
Code Example: Implementation
// Initialization stuff
Main &mC64 = Main::instance();
mC64.timer().init();
mC64.timer().setRate(0,20); // Set the Logic Frame Rate to 20
// Call to setup method
// Arguments: Config file to save to, Resource pack, Layout XML (optional)
Sint32 iRet = mC64.configMgr().setup("config.xml", "setup.cdc", "setup.xml");
// If configuration selected by user is saved and clicked on OK, then launch the application
if(iRet == 0)
{
if(mC64.configMgr().load("config.xml", "setup.cdc") == 0)
{
Screen* mScreen = mC64.configMgr().get();
if(mScreen && mScreen->show() == 0)
{
// main code
// ...
}
}
}


Customization via XML
The launcher layout is data-driven. You can provide a custom XML file (e.g., setup.xml) to override the default look. This allows you to:

  • Change the logo (Sprite resource).
  • Hide specific options (e.g., force VSync or specific resolutions).
  • Rename window titles and labels.
Customized Launcher Example

In the example above, the window title was changed to "Validation Setup", the 5th resolution option was hidden, and the logo was replaced.

Code Example: setup.xml
<?xml version="1.0" encoding="UTF-8"?>
<c64_setup title="Validation Setup">
<!-- CRM64Pro GDK Setup Layout. MegaStorm Systems (c) -->
<main>
<logo state="1" name="milogo"/>
</main>
<general>
<wgMTfriendly value="10"/>
</general>
<video>
<wgScreenTitle text="AppCarrier"/>
<wgResolution5 state="5"/> <!-- Hide 5th resolution option -->
<wgSpecialVSync value="1"/>
<wgSpecialBatching value="0"/>
</video>
<audio>
<wgAudio value="0"/>
<wgSample32bits value="1"/>
</audio>
</c64_setup>
CRM64Pro GDK.
Definition AudioTrack.cpp:33

6.1 Master Setup XML Reference

The internal Master XML defines the structure. The element names cannot be modified, but their attributes can be overridden in your custom XML.

Attribute Reference

Attribute Description
state 0 (Disabled/Shown), 1 (Enabled/Visible) or 5 (Disabled/Hidden).
name The C64 Sprite resource name (e.g., for logo).
x, y Widget position. Supports CRM64Pro::ePositionHelper.
value The default value for the widget (Checkbox/Slider).
text The label text displayed to the user.
action Used for specific widgets to open a file or URL.
View Master Setup XML
<?xml version="1.0" encoding="UTF-8"?>
<c64_setup title="Setup" cursor="default" icon_img="default" font="default">
<!-- CRM64Pro GDK Setup Layout. MegaStorm Systems (c) -->
<!-- Attributes and values:
state: 0(disabled but shown) , 1(enabled) or 5 (disabled and not shown).
name: the sprite resource name.
x and y: positions. Supports CRM64Pro::ePositionHelpers.
value: for widgets, it is the value.
text: for widgets, it is the text.
action: for widgets: open a file or an url.
-->
<!-- The size of the setup screen is set by the size of the main name resource -->
<main name="default" x="0" y="0">
<logo state="1" name="default" x="59" y="10"/>
<!-- CheckBox widgets group for Tabs -->
<wgTabsGeneral name="default" x="28" y="98" text="General"/>
<wgTabsVideo state="1" name="default" x="94" y="98" text="Video"/>
<wgTabsAudio state="1" name="default" x="160" y="98" text="Audio"/>
<!-- Button widgets -->
<wgSave state="1" name="default" x="44" y="401" text="Save"/>
<wgOK state="1" name="default" x="178" y="401" text="OK"/>
<wgExit state="1" name="default" x="313" y="401" text="Exit"/>
<!-- Label widget -->
<wgInfoCopyright state="1" x="70" y="439" text="Setup Tool - CRM64Pro GDK - MegaStorm Systems (c)"/>
</main>
<general name="default" x="28" y="115">
<!-- Label widgets -->
<wgInfoCPU state="1" x="43" y="37" text="Processor: "/>
<wgInfoMemory state="1" x="43" y="62" text="Memory: "/>
<wgInfoVideoCard state="1" x="43" y="87" text="Video card: "/>
<wgInfoAudioCard state="1" x="43" y="112" text="Audio card: "/>
<!-- CheckBox widget -->
<wgMTfriendly state="1" name="default" x="43" y="132" value="1" text=" Multitasking friendly"/>
<!-- Button widgets -->
<wgReadme state="1" name="default" x="73" y="209" text="Readme" action="readme.txt"/>
<wgWeb state="1" name="default" x="223" y="209" text="Website" action="http://www.megastormsystems.com"/>
</general>
<!-- Video configuration is only for "default" C64 screen -->
<video name="default" x="28" y="115">
<!-- Title for screen -->
<wgScreenTitle text="CRM64Pro GDK Application"/>
<!-- CheckBox widgets group for Renderers -->
<wgRendererSoftware state="1" name="default" x="70" y="15" value="1" text=" Software"/>
<wgRendererOpenGL state="1" name="default" x="142" y="15" value="0" text=" OpenGL"/>
<wgRendererVulkan state="1" name="default" x="214" y="15" value="0" text=" Vulkan"/>
<wgRendererGPU state="1" name="default" x="286" y="15" value="0" text=" GPU"/>
<wgRendererDirect3D9 state="1" name="default" x="70" y="35" value="0" text=" Direct3D9"/>
<wgRendererX11 state="1" name="default" x="70" y="35" value="0" text=" X11"/>
<wgRendererMetal state="1" name="default" x="70" y="35" value="0" text=" Metal"/>
<wgRendererDirect3D11 state="1" name="default" x="142" y="35" value="0" text=" Direct3D11"/>
<wgRendererDirect3D12 state="1" name="default" x="214" y="35" value="0" text=" Direct3D12"/>
<!-- CheckBox widgets group for Resolution -->
<wgResolution1 state="1" name="default" x="30" y="100" value="1" width="800" height="600" text=" 800x600"/>
<wgResolution2 state="1" name="default" x="30" y="120" value="0" width="1280" height="720" text=" 1280x720"/>
<wgResolution3 state="1" name="default" x="30" y="140" value="0" width="1920" height="1080" text=" 1920x1080"/>
<wgResolution4 state="1" name="default" x="30" y="160" value="0" width="2560" height="1440" text=" 2560x1440"/>
<wgResolution5 state="1" name="default" x="30" y="180" value="0" width="3840" height="2160" text=" 3840x2160"/>
<wgResolution6 state="5" name="default" x="30" y="200" value="0" width="c1" height="c1" text=" CustomRes"/>
<!-- CheckBox widgets group for Mode -->
<wgModeWindow state="1" name="default" x="130" y="100" value="1" text=" Window"/>
<wgModeFullscreen state="1" name="default" x="130" y="120" value="0" text=" Fullscreen"/>
<wgModeFullscreenExclusive state="1" name="default" x="130" y="140" value="0" text=" Fullscreen exclusive"/>
<!-- CheckBox widgets -->
<wgSpecialVSync state="1" name="default" x="255" y="100" value="0" text=" Vertical-Sync"/>
<wgSpecialBatching state="1" name="default" x="255" y="120" value="0" text=" Render-Batching"/>
</video>
<audio name="default" x="28" y="115">
<!-- CheckBox widget -->
<wgAudio state="1" name="default" x="100" y="11" value="1" text=" Enable audio"/>
<!-- CheckBox widgets group for Sampling -->
<wgSample8bits state="1" name="default" x="30" y="70" value="0" text=" 8bits"/>
<wgSample16bits state="1" name="default" x="30" y="90" value="1" text=" 16bits"/>
<wgSample32bits state="1" name="default" x="30" y="110" value="0" text=" 32bits"/>
<!-- CheckBox widgets group for Frequency -->
<wgFrequency22Hz state="1" name="default" x="125" y="70" value="0" text=" 22050Hz"/>
<wgFrequency44Hz state="1" name="default" x="125" y="90" value="1" text=" 44100Hz"/>
<wgFrequency48Hz state="1" name="default" x="125" y="110" value="0" text=" 48000Hz"/>
<!-- CheckBox widgets group for Mode -->
<wgModeStereo state="1" name="default" x="242" y="70" value="1" text=" Stereo"/>
<wgModeSurround state="1" name="default" x="242" y="90" value="0" text=" Surround 4ch"/>
<wgModeSurroundPlus state="1" name="default" x="242" y="110" value="0" text=" Surround 6ch"/>
<!-- Horizontal slider widgets -->
<wgVolumeMaster state="1" name="default" x="100" y="140" value="1.0" text="Master volume:"/>
<wgVolumeMusic state="1" name="default" x="100" y="170" value="1.0" text="Music volume:"/>
<wgVolumeSFX state="1" name="default" x="100" y="200" value="1.0" text="SFX volume:"/>
<wgVolumeVoice state="1" name="default" x="100" y="230" value="1.0" text="Voice volume:"/>
<wgVolumeCustom state="5" name="default" x="100" y="260" value="1.0" text="Custom volume:"/>
</audio>
</c64_setup>

7. High-level Architecture

CRM64Pro Architecture Diagram

8. Changelog

Log of all notable changes made to CRM64Pro GDK including the date, version, and brief description:

## 🚀 2026-09-06 – v0.20.0 – Scene particles, animated lighting and expanded image support
### Added
- GUI:
- Added left, center and right content-text alignment for TextEdit, NumberEdit, List and ComboBox widgets.
- Added `DebugWindow::addWatch(const string&, const string*)` for watching live string values.
- Image:
- Added JPG/JPEG, PCX, TGA and WebP loading to ImageMgr and EditorC64, saving remains limited to BMP and PNG.
- Added the `IF_BLUR_ALPHA` premultiplied-alpha Gaussian blur filter for creating glow textures.
- Added the `IF_DILATE_ALPHA` morphological filter for expanding alpha silhouettes and their source colors.
- Font:
- Added `setMonospaced()` for fixed font character advances and applied it to the built-in Courier New fonts.
- Scene:
- Added configurable ambient-light intensity through `setLightingAmbientIntensity()` / `getLightingAmbientIntensity()`, persisted in TMX as `c64scn_lighting_ambient_intensity`.
- Added animated `C64Light` point and spot lights with `c64scn_light_animation*` TMX properties, secondary color, radius modulation, shadows, and rotation-driven spotlight direction.
- Added `C64Particle` Scene objects with independent runtime emitters loaded from saved assets or built-in presets, including object-layer ordering and camera/smooth-scroll support.
- Added independently configurable AABB, shape, name, and trigger overlays per Scene object category through `setDebugObjectOverlay()`.
- Added optional tile and TMX object-shape collision layers for Scene particles; both sources can be active together and collision objects opt in with `c64scn_blocks_particles`.
- Added runtime editing for C64Particle autoplay, follow, offsets, integer off-screen update rate, and collision-layer properties.
- Added runtime `C64Portal` open/closed state through `setPortalClosed()` / `isPortalClosed()`. Open portals disable their configured light-occlusion and particle-blocking roles.
- Added reserved weighted movement metadata through `c64scn_movement_cost` (`0` free, `1`-`254` increasing cost, `255` impassable) for future pathfinding.
- Added TMX background-color loading, viewport rendering, and saving.
- Physics:
- Added Physics-owned collision-set handles with rectangle, circle, ellipse, segment and polygon colliders plus shared point-sweep queries with collider identification for Scene and standalone collision geometry.
- Particle:
- Added `ParticleEmitter::setCollisionSet()` for native collision against persistent Physics shapes without requiring a Scene.
- GFX:
- Added `GFXLight` point/spot descriptors with `eGFXLightAnimation` pulse, deterministic flicker, and candle modes, secondary-color blending, and intensity/radius modulation.
- EditorC64:
- Added animated WebP Scene import.
### Changed
- Image:
- Fixed and improved texture recreation when changing the renderer.
- Font:
- Renamed built-in Arial and Courier New fonts to reflect their actual 8 and 10 point sizes.
- Scene:
- Lighting now combines ambient color and intensity before the lightmap pass, allowing local lights to remain visible under neutral white ambient light.
- Object debug helpers are now independent from lighting and particle-effect rendering, render after lighting, and are reported by `Scene::info()`.
- Scene particle object collisions now compile every TMX shape into shared Physics geometry and update only the colliders belonging to moved or resized objects.
- Batched Scene particle emitters through the adaptive ParticleMgr worker pipeline, preserving off-screen update rates and deterministic gameplay callbacks.
- `SceneObject::initialize()` and `SceneObject::update()` now handle engine-managed behavior for built-in object types; derived overrides must call the base methods.
- Physics:
- Optimized re-entrant collision-set sweeps with directly indexed set/collider storage, per-thread query scratch and spatial-cell caching for concurrent particle simulation.
- Particle:
- Added persistent parallel updates for independent emitters with deterministic LPT load balancing and adaptive lane selection based on runtime workload and synchronization costs.
- Updated dependencies:
- libwebp 1.6.0 (Jun 2026)
- stb_image 2.30 (May 2024)
### Breaking Changes
- Scene:
- Replaced `SDO_DISABLE` with `SDO_NONE`, renumbered `eSceneDebugOverlay` values, and moved object overlay configuration from `setDebugOverlay()` to `setDebugObjectOverlay()` with `eSceneDebugObjectType` masks.
- Renamed `c64scn_particle_collider` to `c64scn_blocks_particles`, `c64scn_light_occluder` to `c64scn_occludes_light`, and `c64scn_movement_blocker` to `c64scn_movement_cost`.
- Removed unused `c64scn_portal_freestanding` metadata.
- Physics:
- Extended `PhysicsSweepHit` with collision-set and collider IDs; applications using the public structure must be rebuilt.
- GFX:
- Removed the scalar `GFX::drawLight()` and `GFX::drawSpotLight()` overloads. Configure a `GFXLight` and submit it through `drawLight(const GFXLight&)`.
- Particle:
- Renamed `ParticleEmitter::setCollisionLayer()` and `ParticleEmitter::getCollisionLayer()` to `setCollisionTileLayer()` and `getCollisionTileLayer()`.
## 🚀 2026-07-21 – v0.19.0 – Particle effects and 2D lighting
### Added
- Particle:
- Added the new Particle module with `ParticleMgr` and `ParticleEmitter` for CPU-simulated realtime effects rendered through pixel, rectangle, line, soft, Image and Sprite visuals.
- Added built-in practical presets for environment, fire, weather, water, gameplay impacts, auras, trails, engine thrust, pickups, confetti, bubbles and fireworks.
- Added emitter controls for spawn shapes, emission rate, maximum live particles, duration/looping, lifespan, direction modes, speed, gravity, damping, turbulence,
attraction, orbit, velocity limits, warm-up, local space and deterministic seeds.
- Added lifetime color keys, scale keys, random tint palettes, timed bursts, distance-based emission, draw ordering and optional cached bounds.
- Added `ParticleMgr::playOneShot()` and `ParticleEmitter::burst()` for fire-and-forget and immediate burst effects.
- Added death/collision sub-emitters with bounded recursion, tile-layer collision behavior and optional glow rendering for supported visuals.
- Added CDC save/load/remove/exists support for particle resources, including owned Image and Sprite visual resources.
- GFX:
- Added cached glow helpers: `drawGlow()`, `drawRadialGlow()`, `drawFlash()`, `drawGlowImage()` and `drawGlowSprite()`.
- Added a lightweight 2D lightmap pass with ambient color, soft point lights, spot lights and immediate rect/segment/polygon/circle occluders through `GFX::beginLighting()`, `GFX::addLightOccluder*()`, `GFX::drawLight()`, `GFX::drawSpotLight()` and `GFX::endLighting()`.
- EditorC64:
- Added the Effects tab.
- Added Universal VTT map import with lights, portals, and occluders.
### Changed
- GFX:
- Improved image-target primitive drawing with canonical source surfaces and lazy dirty-texture synchronization; render-state changes no longer rebuild cached textures.
- Added internal batched software drawing helpers for hot image-target paths, reducing repeated target resolution and texture updates.
- Batched screen-target arc and ellipse outline rendering with `SDL_RenderPoints()`.
- `GFX::info()` now reports glow cache, lightmap and occluder runtime state.
### Fixed
- Scene:
- Hardened callback reentrancy, image ownership, tileset/TMX/spatial validation, animated tile timing, and save/render error propagation.
- GFX:
- Fixed Image-target primitives to respect alpha blending and surface clipping.
- Restored renderer draw color and blend state after lighting passes.
- Hardened color conversion output validation and normalized HSV hue handling.
- Fixed software line clipping to use proper line-rectangle clipping instead of endpoint clamping.
- Fixed 24-bit software pixel writes so they no longer write an alpha byte into the adjacent pixel.
- Fixed SDL3 software-renderer polygon artifacts by updating the vendored SDL software renderer.
## 🚀 2026-06-16 – v0.18.0 – Support video playback, new CDCv2 and general improvements
### Added
- Video:
- Added the new Video module with FFmpeg-backed playback, CDC storage/loading, frame extraction, export support and synchronized audio streaming through AudioTrack.
- Supports H.264 video through FFmpeg demuxing, either as raw H.264 elementary streams or inside MP4, MOV, M4V, and AVI containers, with decoded YUV420P/YUVJ420P/NV12 video frames.
- EditorC64:
- Added Scene import tooling for creating maps/layers from Tiled TMX maps, images, supported video formats, static GIFs, and animated GIFs.
- Added frame-sequence-to-animated-tilemap conversion for supported video formats and animated GIFs, with tile deduplication, animated tile generation, and atlas/animation statistics.
### Changed
- Homogenized engine file operations around SDL3 filesystem/IO APIs, reducing platform-specific C runtime file handling and improving portability across desktop and mobile targets.
- Standardized public return contracts: handle-returning methods return ids > 0, status methods return 0 or a negative error code, and bool methods return true/false success states.
- Documented the main-thread public API convention, modules must be called from the main thread unless explicitly documented as thread-safe.
- Enforced exception-free allocation for `CMemAllocator` classes by deleting plain `new/new[]`; engine objects must use `new(std::nothrow)` and check for `nullptr`.
- Refined public return-contract policy: operations that touch SDL, allocation, resource ownership, audio devices, or screen creation now preserve diagnostic error codes
through Sint32 status returns, while simple setters and predicates remain bool or void.
- Archive:
- Replaced the CDC archive backend with the new CDCv2 format, adding support for >4GB files, stronger validation, lightweight confidentiality / non-authenticated encrypted storage for indexes and blocks, streaming reads and writes for raw blocks, safer defragmentation, and automatic CDCv1 upgrade support in EditorC64.
- Audio:
- Improved AudioTrack playback internals with proper ownership of overlapping mixer tracks, safer CDC/file validation, direct PCM video streaming
support, and more reliable live control updates for pitch, panning, position, tag, pause, resume, and cleanup.
- Updated dependencies:
- libpng 1.6.58 (Apr 2026)
- SDL3_mixer 3.2.0 (Mar 2026)
- dr_flac 0.13.3 (Jan 2026)
- libxmp 4.7.0 (Mar 2026)
- SDL 3.5.0 (May 2026)
- SDL3_image 3.5.0 (Jun 2026)
- ffmpeg 8.1.1 (May 2026)
### Fixed
- Tool:
- `fileOpenDialog` and `fileSaveDialog` methods now use SDL3 functions and support Windows, Linux, macOS and Android.
### Breaking Changes
- Standardized `setScroll()` and `setLayerPosition()` to return 0 on success or a negative error code on failure, changed/moved state is now reported through optional bool* output parameters.
- Renamed AudioTrack and Video playback position APIs to `seek()` and `getPlaybackPosition()`, and renamed AudioTrack 3D audio APIs to `setSpatialPosition()` and `setSpatialDistance()`.
- Renamed resource manager `exist()` methods to `exists()` across XML, AudioTrack, Image, Cursor, Tile, Sprite, Font, Video and Particle managers.
- Added `ePlaybackStatus` and changed `Video::status()` and `AudioTrack::status()` to use `PS_STOPPED`, `PS_PLAYING`, `PS_PAUSED` and `PS_FINISHED` playback states instead of `eGeneralStatus` values.
- Resource names are now validated explicitly and are no longer silently truncated when longer than the CDC/name limit.
- Replaced generic platform/environment macros with `CRM64PRO_PLATFORM_*` and `CRM64PRO_ENV_64BIT`, and removed 32-bit target support.
- Renamed macro `CRM64PRO_STATIC` to `CRM64PRO_LINK_STATIC`.
- Changed several public API methods from bool to Sint32: `Screen::show()`, `Screen::setIcon()`, `Screen::setDriver()`, `Screen::setMode()`, `Screen::setSize()`, `Image::assignSurface()`,
`AudioTrack::play()`, and `AudioTrack::fadeIn()`, callers must check result == 0 for success or result < 0 for failure.
- Changed `SceneObject::setSize()` and `SceneLayerObject::setSpatialGridCellSize()` from void to bool so invalid or unapplied changes can be detected.
- Main:
- Changed Main module accessors from I*() names to camelCase names (for example `ITimer()` -> `timer()`, `ILogMgr()` -> `logMgr()`, `IImageMgr()` -> `imageMgr()`).
- Changed `Screen::setRenderCallback()` to use a `function<Sint32(Sint32)>` signature instead of the old function-pointer plus void* user-data signature, which was removed.
- Renamed `ePositionHelpers` to `ePositionHelper`.
- Renamed `CMem::setLogLevel()` to `CMem::setStatsLevel()` to clarify that it controls memory statistics gathering, not log output.
- Renamed audio frequency enum values to value-based names: `CAF_NORMAL` -> `CAF_22K`, `CAF_HIGH` -> `CAF_44K`, and `CAF_ULTRA` -> `CAF_48K`.
- Tool:
- Removed legacy `Tool::xxHash*` 32-bit helpers, use the `Tool::xxHash3*` 64-bit API instead.
- Renamed message box API enums for consistency: `eToolMBT/TMBT_*` became `eMsgBoxType/MBT_*`, and `eMsgBoxButton` values `TMBB_*` became `MBB_*`.
- Renamed `Tool::XOR()` to `Tool::xorBuffer()` to follow camelCase method naming and describe the buffer operation clearly.
- Audio:
- Renamed `ConfigMgr::audioStatus()` to `ConfigMgr::audioIsReady()`.
- Font:
- Renamed `Font::setCursor()` and `Font::getCursor()` to `setTextCursor()` and `getTextCursor()`.
- Cursor:
- Renamed `CursorMgr::getSelect()` to `CursorMgr::getSelectedCursor()` for clearer cursor-selection API naming.
- Sprite:
- Split animation APIs into state-based methods and explicit `*Index()` row methods, game code should use animation states, while EditorC64/tools must use index methods for ordered animation rows.
- Network:
- Updated NetTCP result handling: moved most NR_* values to a dedicated range, aliased `NR_BAD_PARAMETER` and `NR_OUT_OF_MEMORY` to matching `C64_ERR_*` codes,
and changed `getClientsInfo()` and `getPendingWrites()` to return `eNetResult` with output parameters.
- Renamed `NetTCP::queryKillServer()` and `NetTCP::queryKillClient()` to `requestServerClose()` and `requestClientClose()` to distinguish command requests from data queries.
- Changed `NetTCP::sendData()` `bIsQuery` parameter from Uint8 to bool.
- GUI:
- Converted `Widget::disable()`, `Widget::enable()`, `Widget::show()`, `Widget::hide()`, `Widget::setMargin()`, `Widget::getMargin()`,
`Widget::setOnAction()`, `Widget::setOnHoverEnter()`, `Widget::setOnHoverExit()`, `Widget::setOnPressed()`, `Widget::setOnFocusLost()`,
`Widget::setOnValueCommitted()`, `Widget::setOnSelectionChanged()`, `Widget::setAlphaMod()`,
`Widget::setOnValueChanged()`, `Widget::setOnToggled()`, `Widget::setTooltipBackgroundColor()`, `Widget::getTooltipBackgroundColor()`,
`Widget::setTooltipBorderColor()`, `Widget::getTooltipBorderColor()`, `WidgetLabel::getText()`, `WidgetButton::setText()`,
`WidgetButton::getText()`, `WidgetButton::setKey()`, `WidgetCheckBox::setText()`, `WidgetCheckBox::getText()`, `WidgetCheckBox::setKey()`,
`WidgetCheckBox::setGroup()`, `WidgetProgress::setMargin()`, `WidgetSlider::setMargin()`, `WidgetTextEdit::setMargin()`,
`WidgetTextEdit::setText()`, `WidgetTextEdit::getText()`, `WidgetTextEdit::getScroll()`, `WidgetTextEdit::getVisibleItems()`,
`WidgetList::setMargin()`, `WidgetList::getScroll()`, `WidgetList::getVisibleItems()`, `WidgetComboBox::setMargin()`,
`WidgetComboBox::getScroll()`, and `WidgetComboBox::getVisibleItems()` from Sint32 status returns to bool success/failure returns.
....

View the Complete Changelog.


9. License

Copyright (C) 2013-2026 Roberto Prieto <contact@megastormsystems.com>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.