EPI (EDGE Platform Interface) is a lightweight C++ game library designed for cross-platform game development. Its design prioritises constrained and embedded targets – particularly the Sega Dreamcast and Sony PlayStation Vita – while remaining fully usable on Linux, macOS, and Windows. The current libEPI tree also includes newer subsystems brought across from EDGE and Dream in the Dark, including camera helpers, WAD/archive editing, extra image and sound codecs, DSP filters, and the optional RGL render layer.
| Platform | Toolchain / SDK | C++ Standard |
|---|---|---|
| Linux | GCC / Clang (host) | C++20 |
| macOS | Apple Clang (host) | C++20 |
| Windows | MSVC / MinGW | C++20 |
| Dreamcast | KallistiOS + sh-elf-g++ | C++20 |
| PS Vita | VitaSDK + arm-vita-eabi-g++ | C++20 |
- CMake ≥ 3.20
- A C++20-capable compiler
cmake -B build
cmake --build build- Install KallistiOS and set the
KOS_BASEandKOS_CC_BASEenvironment variables as documented by KOS. - Configure and build:
cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/Dreamcast.cmake \
-B build/dreamcast
cmake --build build/dreamcastOptional flags:
-DEPI_ENABLE_SH4_ACCEL=ON– enables SH-4 hardware fixed-point math acceleration (faster distance / projection calculations).
- Install VitaSDK and set the
VITASDKenvironment variable to the SDK root (e.g./usr/local/vitasdk). - Configure and build:
cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/Vita.cmake \
-B build/vita
cmake --build build/vita-DEPI_ENABLE_SH4_ACCEL=ON– enable Dreamcast SH-4 fixed-point math helpers-DEPI_ENABLE_RGL=ON– build the optional render layer (r_texcache,r_shader,r_effect,model_skin); enabled automatically on Dreamcast-DEPI_ENABLE_PHYSFS=ON– enable PhysicsFS-backed virtual file access-DEPI_ENABLE_COAL2=ON– enable COAL2 scripting integration hooks
- Memory management –
epi_dreamcast.ccqueriesHW_MEMSIZE(set by KallistiOS at boot) to detect whether 16 MB or 32 MB RAM is present, then carves out a secondary memory pool for large allocations beyond the system heap baseline. Useepi::DualAlloc/epi::DualFree/epi::DualReallocto benefit from the dual-pool allocator. - SH-4 math acceleration –
fxp_vector_sh4.hprovidesepi::sh4::Dist2,epi::sh4::Dist3,epi::sh4::PerpDist, andepi::sh4::AlongDistusing the SH-4 hardwarefsqrtand reciprocal instructions. Enabled byDITD_ENABLE_EPI_SH4_ACCEL=1or the-DEPI_ENABLE_SH4_ACCEL=ONCMake option. - Input –
input_dreamcast.ccmaps the KallistiOS Maple-bus controller API (cont_state_t) to the portableepi::inputinterface.
- Memory management –
epi_vita.ccqueries the kernel viasceKernelTotalFreeMemSize()at runtime and creates a secondary EPI memory pool from the headroom above the 64 MB CRT baseline, giving applications a typical extra pool of ~176 MB on a 256 MB budget. - Input –
input_vita.cchandles:- Digital buttons and dual analogue sticks (
SceCtrlData) - Front and rear capacitive touch panels (
SceTouchData, up to 8 contacts each) - 6-DOF IMU: 3-axis accelerometer + 3-axis gyroscope
(
SceMotionSensorState)
- Digital buttons and dual analogue sticks (
#include "input.h"
// Initialise after platform EPI::Init()
epi::input::Init();
// Game loop
while (running) {
epi::input::Poll();
// Digital buttons
if (epi::input::ButtonsPressed() & epi::input::BTN_A)
jump();
// Analogue sticks
epi::input::AnalogAxes axes;
epi::input::Axes(&axes);
move(axes.left_x, axes.left_y);
// Touch (Vita only; safe to call on Dreamcast – returns empty state)
epi::input::TouchState touch;
epi::input::Touch(&touch);
for (size_t i = 0; i < touch.front_count; ++i)
handle_tap(touch.front[i].x, touch.front[i].y);
// Motion (Vita only)
epi::input::MotionState motion;
epi::input::Motion(&motion);
tilt(motion.accel_x, motion.accel_y);
}
epi::input::Shutdown();// Prefer the extra pool for large allocations; fall back to system heap.
void* buf = epi::DualAlloc(64 * 1024, /*preferExtra=*/1);
// … use buf …
epi::DualFree(buf);
// Query pool status
if (epi::HasExtraMemoryPool()) {
printf("Extra pool: %u bytes free\n", epi::GetExtraMemoryPoolBytes());
}#include "timer_utility.h"
epi::TimerInit(35); // 35 tics/sec (classic Doom rate)
while (running) {
epi::TimerTick(); // advance clock once per iteration
float dt = epi::GetDeltaTime();// seconds since last TimerTick()
float fps = epi::GetFPS(); // rolling 16-frame FPS average
// Fixed-timestep simulation at the configured tic rate:
while (epi::GetPendingTics() > 0) {
epi::ConsumeTic();
UpdateGame();
}
RenderFrame(dt);
epi::LimitFPS(60); // cap render rate to 60 fps
}| Function | Returns |
|---|---|
GetTimeMS() |
u32_t – milliseconds since init |
GetTimeUS() |
u64_t – microseconds since init |
GetTic() |
int – total elapsed tics |
GetPendingTics() |
int – tics not yet consumed |
GetDeltaTime() |
float – seconds since last TimerTick |
GetFPS() |
float – rolling 16-frame average FPS |
Recent libEPI additions imported from EDGE / Dream in the Dark include:
- Camera system –
camera.hadds a reusable 3D camera with perspective / orthographic projections, frustum extraction, coarse visibility tests, and interpolation helpers. - Archive editing –
archive.handarchive_wad.hexpose archive and WAD inspection / mutation APIs suitable for tooling and runtime asset handling. - Extra image codecs – Dreamcast-specific KMG support now sits alongside a validated PVR loader / decoder for RGB565 VQ textures.
- Audio utilities – sound loading covers WAV / VOC detection,
mus_2_midiconverts classic MUS data to MIDI, andDSP_filter.hadds reusable low-pass / high-pass filtering. - Optional render layer – the imported RGL pieces provide texture cache, shader, model skin, and render-effect helpers for OpenGL / GLdc-based code.
EPI supports several 3D model formats through a common model_data_c
container (see model_data.h):
| Format | Loader class | model_format_e constant |
|---|---|---|
| Quake 2 MD2 | MD2Loader |
MDL_FORMAT_MD2 |
| Quake 3 MD3 | MD3Loader |
MDL_FORMAT_MD3 |
| Half-Life 1 MDL (studio) | HLMDLLoader |
MDL_FORMAT_HLMDL |
| Doom 3 MD5 | MD5Loader |
MDL_FORMAT_MD5 |
| Alone in the Dark body | AITDBodyLoader |
MDL_FORMAT_AITDBODY |
#include "model_md2.h"
// Locate the "run" clip by frame-name prefix.
int first, last;
epi::MD2_FindFrameRange(mdl, "run", first, last);
// Create a looping playback state.
epi::MD2AnimState state(first, last, /*fps=*/10.0f, /*loop=*/true);
// Each render frame:
std::vector<epi::model_vert_c> interp_verts;
epi::MD2_LerpFrame(mdl, /*body=*/0, state, dt, interp_verts);
// … render interp_verts …#include "model_aitdbody.h"
auto *loader = static_cast<epi::AITDBodyLoader *>(
epi::MDL_GetAITDBodyLoader());
// Supply the 256-entry RGB palette from the game's resource archive.
loader->SetPalette(palette_rgb768); // 768 bytes: R G B × 256
epi::model_data_c *mdl = epi::MDL_Load(body_file,
epi::MDL_FORMAT_AITDBODY);
// Flat-colour polygons → skins[i].name == "aitd:color:N"
// Textured polygons (9/10) → skins[i].name == "aitd:tex:N:T"| Module | Files | Description |
|---|---|---|
| Camera | camera.* |
Reusable 3D camera, projection, frustum culling |
| Platform backend | epi_dreamcast.*, epi_vita.*, … |
Init/Shutdown + dual-memory allocator |
| Input | input.h, input_dreamcast.*, input_vita.* |
Cross-platform input abstraction |
| Memory manager | memmanager.*, epi_dual_memory.h |
Slab allocator + dual-pool helpers |
| Fixed-point math | fxp_*.h/cc, fxp_vector_sh4.h |
SH-4-accelerated fixed-point math |
| Image loading | image_*.h/cc, stb_image.* |
PNG, JPEG, TGA, KMG, PVR image codecs |
| Sound / DSP | sound_*.h/cc, mus_2_midi.*, DSP_filter.*, lowpass_filter.* |
WAV, VOC, MUS→MIDI conversion, filtering |
| Archives | archive.*, archive_wad.* |
WAD archive inspection and editing APIs |
| Timer / framerate | timer_utility.* |
ms/µs/tics, delta-time, FPS, frame limiter |
| 3D models | model_*.h/cc |
MD2 (+ interpolation), MD3, HLMDL, MD5, AITD body |
| Legacy id helpers | kmq2/* |
Quake II byte-order, hunk, and parsing helpers |
| Containers | arrays.*, tarray.h, pri_heap.* |
Lightweight collections |
| Math | math_*.h/cc |
Vectors, matrices, quaternions, colour |
| Render layer | r_texcache.*, r_shader.*, r_effect.*, model_skin.*, rgl_vertex.h |
Optional OpenGL / GLdc rendering helpers |
| Filesystem | filesystem.*, file.*, path.* |
Platform-abstracted file I/O |
GNU General Public License v2 or later – see LICENSE for details.