A clean-room, open-source reimplementation of the Acknex-3 (3D GameStudio A3) engine runtime, with the goal of running the 1999 Christian FPS Saints of Virtue natively on modern Windows — and, once parity is reached, improving its graphics using native technologies (Direct3D).
This project follows the OpenMW / engine-remake model: we ship only our own code. The end user supplies their own legally obtained copy of the original game, whose data files our engine reads at runtime. No original game assets, scripts, or executables are distributed with this project.
Status: early implementation. The asset pipeline (WRS/PCX/WDL/WMP/WAV) works and a textured level renders in a Direct3D 11 window with billboard sprites; the WDL interpreter boots/ticks in the viewer, and first-person movement has a first playable debug slice. The actual game simulation is still being built. See Current status for the milestone breakdown, and
docs/recon/for the original research.
- Saints of Virtue (1999) — developed by Shine Studios, published by Cactus Game Design. One of the first Christian first-person shooters.
- Built on Conitec Datensysteme's Acknex-3 engine (later marketed as 3D
GameStudio), a Doom-era 2.5D portal/sector engine scripted in the WDL
language. Ships both a DOS runtime (
VRUN.EXE) and a Windows/DirectX runtime (WVRUN.EXE).
- Parity first. Faithfully reproduce Acknex-3 runtime behavior so the retail game plays correctly, end to end.
- Native & modern. Written in C#/.NET 10, rendering through Direct3D (via Vortice.Windows), following modern Windows game-dev practice.
- Enhance later. Once parity is verified, optionally raise rendering quality (higher resolution, filtering, true 3D lighting) behind toggles.
- Preservation & appreciation. Credit the original authors prominently; this is a fan-preservation effort, not a commercial product.
Early implementation (the asset layer is solid; the simulation is just beginning). What works today:
- Asset pipeline — clean-room readers for
WRS(LZSS archives),PCX(8-bit paletted images),WDL(lexer + parser +INCLUDE/DEFINE/IFDEF/IFNDEFpreprocessor),WMP(text maps),WAV, and 8-bit AutodeskFLI/FLC, validated against retail data via guarded, local-only integration tests. Inspectable with theovtoolCLI. - Headless level load —
LevelLoadercombines a level's flattened WDL with its WMP map into a typed object model (regions, walls, things, actors, skills, actions, textures). Guarded local-only tests cover all six retail archives through level loading, inspection mesh generation, texture decode, and runtime boot/tick. - Direct3D 11 renderer — a windowed viewer draws textured walls and ear-clipped
floors/ceilings with a depth buffer, plus camera-facing billboard sprites for
things and actors (palette-index-0 color-key transparency). It recognizes WDL
FLAGS SKYtextures as a camera-selected textured background, renders open portal wall spans, alpha-tests palette-index-0 holes only on walls markedTRANSPARENT, and combines global/texture/region ambient lighting withPLAYER_LIGHT. Static sprite billboards are laid out in the engine layer and drawn back-to-front for deterministic static scenes. The camera is driven by the WDL-visible player with radius-aware wall, portal, and entity collision. Floor/ceiling queries interpolate WMP vertex heights so authored slopes feed vertical collision, WDL height/slope skills, and the rendered feet/shadows of things and actors. - WDL interpreter — foundation — a full expression evaluator (arithmetic,
comparison, logical
&&/||, bitwise integer operators, modulo, documented single-argument math functions, member access, and A3-style arithmetic aborts) andSET/RULE/IF/WHILE/BREAK/CONTINUE/CALL/GOTO/ENDstatements run against a live skill table (WdlRuntime); a level'sIF_STARTscript boots in the viewer, and a per-frameTickmaintains the fixed-tickTIME_CORRfactor. Skill assignments respect declaredMIN/MAXbounds. Runtime registration now tracks placed things/actors for iteration, exposes unique placement names for script member access, and registers the debug player as a script-visible object in the viewer. A provisional scheduler API dispatches registered per-frame object actions withmyandTIME_CORRbound. The app exposes live keyboard/mouse polling and movement-force skills, consumes script-authored player velocity/rotation, and now runs Apathy's retail label/GOTOmovement action after startup; a guarded integration test verifies sustained input produces valid collision-aware movement. Player hitscan and actor sight checks traverse visible portal openings and dispatch retailIF_HITactions. Player shots use the live directional animation frame and alpha mask, continue through transparent sprite pixels, and publish spriteHIT_X/HIT_Y; wall hits report offset-aware texture coordinates for attached decals. NormalizedSHOOT_X/SHOOT_Ydeviations share the app's livePLAYER_TILT/PLAYER_ARCprojection. Apathy's real left-input weapon path is covered by a guarded runtime test and rendered smoke. The app also renders first-pass WDL text, panels, buttons, sliders, bars, and bitmap widgets; visible text withIF_KLICKuses its live rendered glyph bounds for hit testing.MSPRITEmetadata is preserved separately from its overlay and follows the documentedMOUSE_MODEvisibility and mouse-position/hotspot behavior. The app publishes per-frame virtual-pixelMICKEY_X/Ydeltas, leaves activeMOUSE_X/Ycoordinates under script control, tracksMOUSE_MOVING, dispatchesIF_MSTOP, and hides the native cursor over the custom pointer surface. In mouse modes 0/1, boundedMOUSE_SENSE-scaled deltas feed the retail movement action throughFORCE_ROT/FORCE_TILT; mode 2 remains cursor-only. An eight-voice NAudio host plays overlapping retail WAV effects with immediateCHANNEL/CHANNEL_0..7state, global and per-sound volume, numeric stereo pan, and first-pass object-relativeSDIST/SVDISTattenuation. TextureSOUND,SVOL,SCYCLES, andSLOOPmetadata is retained; grounded movement triggers floor sounds and entering a region starts or loops its ceiling sound through the same eight-channel host. Looping wall, thing, and actor textures occupy channels within earshot and continuously update attenuation and stereo direction. Animated wall and entity textures fire non-looping sounds on their authoredSCYCLESphases after host-side distance filtering. Skill loading preserves whetherVALwas explicitly authored, so bare declarations of predefined engine skills receive their documented defaults without overriding intentional zero initializers. The retail title sequence now decodes and plays its full-screen FLC at the authored cadence while world simulation pauses and globalEACH_TICKremains active. Playback updatesFLIC_FRAME, resumes the invoking action on completion orSTOP_FLIC, and can proceed through the keyboard-driven title/menu flow into Apathy in one host process.
Not yet complete: exact sky scrolling and indexed-palette shading,
Acknex-parity each_cycle discovery/dispatch order, exact player collision,
slope, stacked-region edge semantics, and exact actor AI/path event ordering,
exact FLIC frame-number/audio synchronization, exact audio/UI behavior,
legacy-compatible save/load, and the DOSBox-X oracle-diff harness.
The app is a level viewer with debug walking, not yet a playable game. See the
milestone roadmap for the full plan
and where each piece sits. The immediate work plan is tracked in
docs/planning/near-term-roadmap.md.
- Legally obtain Saints of Virtue (retail CD or archive).
- Point OpenVirtue at the install folder containing the
*.WRSdata files. - Run OpenVirtue. (No game files ship with the engine.)
| Path | Purpose |
|---|---|
docs/recon/ |
Research findings (engine, formats, prior art, legal, tooling). |
docs/adr/ |
Architecture Decision Records (created as we make design choices). |
docs/clean-room/ |
Oracle/invariant discovery protocol and sanitized observation notes. |
docs/planning/ |
Current near-term work plan. |
src/ |
Engine source: OpenVirtue.Formats (asset readers), OpenVirtue.Engine (world model, WDL interpreter, geometry), OpenVirtue.App (Direct3D 11 viewer). |
tools/ |
Local asset-inspection / extraction helpers (not redistributed game data). |
tests/ |
Automated tests. |
_research/ |
Git-ignored. Local-only copies of the game, the SaintsX fan patch, and third-party reference code — for study only, never committed or shipped. |
This project exists out of appreciation for the people who made the original:
- Shine Studios — original game.
- Cactus Game Design — publisher.
- Conitec Datensysteme GmbH — the Acknex / 3D GameStudio engine.
Community research that informs (but is not copied into) this project:
firoball's WDL2CS / WMPio / AcknexCSApi, rickomax's WRS work, and the
SaintsX fan patch. See docs/recon/03-prior-art.md
and the licensing notes before reusing any of it.
GNU GPL v3.0-or-later — see LICENSE and
ADR-0003. The OpenMW model: the engine and its
derivatives stay open, and the GPL is compatible with our deps (.NET runtime and
Vortice.Windows are both MIT).
OpenVirtue is a clean-room reimplementation. We use the original game, the SaintsX patch, and third-party Acknex-3 projects as reference only — never copying their code — to keep this GPLv3 codebase clean of incompatibly-licensed lineage. See ADR-0005. Hidden runtime behavior is discovered through sanitized oracle observation records; see ADR-0007 and
docs/clean-room/.
New source files carry:
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026 The OpenVirtue Authors
"Saints of Virtue", "Acknex", and "3D GameStudio" are trademarks of their respective owners and are used here nominatively only.