Automated pipeline that converts DarkOrbit game assets — Away3D .awd meshes and
Adobe .atf textures — into modern 3D formats (.glb / .gltf / .obj),
renders turntable sprite sequences, and bakes Away3D particle effects to sprite
frames. Driven from a single CLI or a local web UI.
It replaces a manual, GUI-driven workflow (ATF2PNG → Prefab3D → Blender) with one
repeatable command, while preserving auxiliary scene nodes (engine_*,
laserpoint_*, light_position) as reference points for later use.
- ATF decoder — pure-Python, covers every ATF format in the asset set: DXT1, DXT5, raw RGB/RGBA, and raw DXT5 (LZMA + JPEG-XR where needed).
- AWD parser — pure-Python AWD2 reader: geometry, named scene-graph instances with transforms, materials, and vertex (pose) animation clips.
- Export — glb / gltf / obj via headless Blender, with PBR materials wired
from DarkOrbit texture channels (
diffuse,normal,specular,glow,alpha,ao,gal). Each animation clip is exported as a separate named glTF animation (morph targets). - Reference points —
engine_*/laserpoint_*/light_positionnodes are preserved as Empties parented to the main body. - Turntable renderer — reproducible headless lighting with a DarkOrbit AS3 visual profile, any frame count, optional per-frame screen coordinates of the reference points.
- Particle effects —
.awpeffects simulated and composited to sprite frames. - Interfaces — unified
do3dCLI and a dependency-free local web UI.
| Dependency | Version | Used for |
|---|---|---|
| Python | 3.10+ | pipeline and decoders |
| Pillow | ≥ 10 | PNG I/O, sprite cropping |
| NumPy | ≥ 1.26 | DXT decode |
| imagecodecs | ≥ 2024.1 | JPEG-XR decode |
| Blender | 5.x | scene build, export, rendering |
pip install -r requirements.txt # runtime dependencies
pip install -e . # optional: install the `do3d` commandBlender is located at the default Steam path; override it with the BLENDER
environment variable (see src/config.py).
do3d list meshes # list convertible meshes
do3d info sibelon # inspect objects, points, clips, textures
do3d convert sibelon --gltf --obj # AWD/ATF -> glb (+ gltf, obj)
do3d convert sibelon --output-name boss_ship
do3d render sibelon --frames 32 # turntable sprite sequence + Coords.json
do3d fx explosion0 # particle effect -> sprite frames
do3d ui # local web UIEvery command is also available as python -m src <command>, and the individual
modules (python -m src.pipeline / src.render / src.fx_render) accept the
same options as their subcommand.
| Command | Purpose |
|---|---|
convert |
Convert an AWD mesh (+ ATF textures) to glb/gltf/obj |
render |
Render a mesh turntable to a sprite sequence |
fx |
Render an .awp particle effect to sprite frames |
list |
List meshes / fx meshes / effects / textures |
info |
Inspect a mesh (objects, points, clips, textures) |
extract-awp |
Unpack fx/*.zip archives into fx/awp/ |
ui |
Launch the local web UI |
A mesh name (without extension) is the positional argument for convert,
render, and info; omit it and pass --all to process the whole set.
| Option | Description |
|---|---|
--all |
Convert every mesh in meshes/ (or fx/ with --fx). |
--fx |
Source from fx/; output under out/fx/<mesh>/. |
--gltf |
Also export .gltf (separate) into model/gltf/. |
--obj |
Also export .obj (+ .mtl) into model/obj/. |
--no-blender |
Decode textures and emit the scene JSON only; skip Blender. |
--overlay NAME |
Overlay another mesh (AWD) on top of the main model. |
--output-name NAME |
Use a custom basename for exported files. Alias: --export-name. |
--mode selects how reference points are handled:
| Mode | Behaviour |
|---|---|
auto |
Track points and write Coords.json if present; otherwise plain render. (default) |
ship |
Force point tracking and Coords.json. |
item |
Plain render (ore, items) — no point tracking, no Coords.json. |
Turntable
| Option | Default | Description |
|---|---|---|
--frames N |
72 | Frame count. |
--total-degrees D |
360 | Total sweep; per-frame step = D / frames. |
--deg-per-frame D |
— | Explicit step (overrides total-degrees / frames). |
--start-angle D |
90 | Rotation at frame 1 (front faces screen-right). |
--frame-start N |
1 | First frame number in filenames. |
--clip NAME |
all | Play/export a single animation clip. |
--no-rotation |
off | Disable Z rotation (turntable) during rendering. |
--anim-frame-start N |
1 | Start frame of the animation clip. |
--anim-frame-end N |
— | End frame of the animation clip. |
--output-name NAME |
mesh name | Custom basename for the GLB, sprite frames, and Coords.json. |
Output & quality
| Option | Default | Description |
|---|---|---|
--resolution PX |
256 | Square render resolution. |
--samples N |
96 | EEVEE render samples. |
--engine NAME |
BLENDER_EEVEE |
Render engine. |
--view-transform NAME |
Standard |
Colour management (Standard/AgX/Filmic). |
--no-crop |
off | Disable the global stable crop. |
--no-transparent |
off | Render on an opaque background. |
--origin MODE |
TOP_LEFT |
Coordinate origin (TOP_LEFT/BOTTOM_LEFT). |
--quality NAME |
medium |
Quality preset (extra_low/low/medium/high/extra_high/custom). |
Camera & lighting
| Option | Default | Description |
|---|---|---|
--profile NAME |
darkorbit |
Visual profile: darkorbit or studio. |
--hdri FILE |
studio.exr |
Bundled world HDRI; also enables HDRI. |
--use-hdri / --no-hdri |
off in darkorbit |
Toggle Blender HDRI world lighting. |
--world-strength F |
0.2 | DarkOrbit ambient/world strength. |
--sun-energy F |
1.0 | Sun lamp energy. |
--emission F |
1.0 | Glow/emission map multiplier. |
--camera-model NAME |
darkorbit |
darkorbit Observer3D tilt/pan or orbit. |
--cam-tilt D |
135 | DarkOrbit Observer3D camera tilt. |
--cam-pan D |
25 | DarkOrbit 3D map camera pan. |
--fov D |
30 | Perspective field of view. |
--cam-distance D |
fit object | Fixed camera distance; use 1740 for raw Observer3D distance. |
--elevation D |
55 | Orbit-model camera elevation. |
--azimuth D |
-90 | Orbit-model camera azimuth around Z. |
--persp / --ortho |
perspective | Force camera projection. |
--light-model NAME |
darkorbit |
DarkOrbit LightSettings tilt/pan or Blender Euler sun. |
--sun-tilt D |
100 | DarkOrbit sun directionTilt. |
--sun-pan D |
35 | DarkOrbit sun directionPan. |
--light-quality NAME |
medium |
low disables sun; high adds hero point light. |
--hero-light / --no-hero-light |
off | Toggle hero-position point light. |
--margin F |
1.15 | Framing padding factor (> 1 zooms out). |
--sun-color HEX |
#ffffff |
Sun light color. |
--world-color HEX |
#ffb2ae |
DarkOrbit ambient color. |
--overlay NAME |
— | Overlay another mesh (AWD) on top of the main model. |
Bundled HDRIs: studio · city · courtyard · forest · interior · night
· sunrise · sunset. They are used by --profile studio or --use-hdri.
All defaults live in RENDER_DEFAULTS
(src/config.py).
| Option | Default | Description |
|---|---|---|
--all |
— | Render every effect (fx/*.zip). |
--frames N |
30 | Frames across the effect duration. |
--resolution PX |
256 | Square sprite resolution. |
--margin F |
1.2 | Canvas padding factor. |
--output-name NAME |
effect name | Custom basename for sprite frames. |
out/<mesh>/
model/
<mesh>.glb # primary, self-contained (textures embedded)
textures/ # decoded source PNGs
gltf/ # <mesh>.gltf + .bin + textures
obj/ # <mesh>.obj + .mtl
sprites/
<mesh>_1.png … _N.png
<mesh>_Coords.json # per-frame reference-point screen positions
work/ # intermediates (scene / config / meta JSON)
<mesh>_Coords.json is a flat map of per-frame screen positions; "OFF" marks a
frame where the point is off-screen, and coordinates are relative to the cropped
sprite:
{
"engine_0": [[19, 107], [20, 113], "OFF"],
"laserpoint_leftFrontOuter": [[168, 119], [167, 121], [166, 124]]
}do3d ui # serves http://127.0.0.1:8765A single vanilla-JS page served by the Python standard library — no framework, no
build step. Browse meshes / fx meshes / effects, inspect an asset, and convert or
render directly from the page. Rendered turntables play back inline; long Blender
runs stream live progress and queue behind the active job instead of launching
parallel Blender processes. Per-channel texture fields let you assign any .atf
when auto-detection misses, an export-name field controls output basenames, and
an animation-clip selector picks which clip to play. Every action maps to the
same functions as the CLI.
The fx/ folder holds particle assets: fx_*.awd meshes, .atf textures, and
<name>.zip archives each containing one <name>.awp. An .awp is plain JSON
describing an Away3D particle effect.
do3d fx <name> simulates the particles in 3D and composites them as
camera-facing billboards (additive / alpha blend), decoding referenced textures
straight from the .atf assets. Supported nodes: time, position, velocity,
acceleration, scale, segmented/initial colour, rotation, billboard, orbit,
oscillator, sprite-sheet (flip-book), and UV scroll.
The plain fx_*.awd meshes (rings, spheres, shards) convert and render with the
--fx flag, which sources meshes and textures from fx/ and writes under
out/fx/<mesh>/.
.awd ──▶ AWD2 parser ──▶ geometry + named nodes + transforms ─┐
├─▶ scene JSON ──▶ Blender ──▶ glb / gltf / obj
.atf ──▶ ATF decoder ──▶ PNG (diffuse/normal/specular/glow/alpha/ao/gal) ──┘ │
└──▶ turntable render ──▶ sprites + Coords.json
System-side Python (NumPy / imagecodecs) handles decoding and parsing; Blender
runs headless with only bpy and the standard library. The two sides communicate
through JSON, so neither depends on the other's libraries.
src/
cli.py unified CLI entry point
pipeline.py AWD/ATF -> glb conversion orchestrator
render.py mesh turntable render orchestrator
fx_render.py particle-effect render orchestrator
server.py local web UI backend (stdlib http.server)
config.py paths and render defaults
atf/ ATF texture decoder
awd/ AWD2 mesh parser + scene model
blender/ headless scene builder + sprite renderer (run inside Blender)
fx/ .awp particle parser + 2D billboard renderer
web/ single-page web UI
tools/ standalone inspection / preview helpers
docs/ format research, architecture, roadmap
tests/ unit tests (synthetic assets, no Blender)
pip install -r requirements-dev.txt
pytestTests run on synthetic AWD/ATF byte streams, so no game assets are required. They
cover the AWD2 parser, the ATF decoder, the intermediate model, the render
stable-crop / coordinate logic, the .awp particle parser, and the CLI. The
Blender scripts are syntax-checked rather than executed. CI runs the suite on
Python 3.10–3.12 (.github/workflows/ci.yml).
| Document | Contents |
|---|---|
docs/00_overview.md |
Goals, manual vs. automated workflow |
docs/01_formats.md |
AWD & ATF binary format findings |
docs/02_architecture.md |
Pipeline architecture & decisions |
docs/03_roadmap.md |
Phased implementation status |
docs/04_blender_scripts.md |
Notes on the original Blender scripts |
docs/05_open_questions.md |
Resolved decisions & open questions |
MIT © 2026 Samet Ozturk
For educational and personal use. Game assets (meshes/, textures/, fx/) are
the property of their respective owners and are not included in this
repository.