πŸ“˜

3D Map Editor β€” Documentation

What works today, what doesn't yet, and how to get a composition onto your server

Open the editor β†’

β–Ά Video walkthrough

A run through the whole setup β€” pointing the editor at your own game files, loading terrain and the map layout, building, and exporting SQF for the server. The fastest way in if you would rather watch than read.

β–Ά Watch on YouTube

πŸ”’ Your files stay on your computer

The editor runs entirely in your browser. Projects are saved to your browser's own IndexedDB database on your machine, and any SQF or asset-pack files you open are read locally in the page. Nothing you load, build or export is uploaded to ArmaTools β€” there is no account and no server-side processing. (The site's host adds a cookieless page-view beacon to every page; details in Projects and local data below.)

1 What works now

Everything listed here is implemented and tested.

2 Reading your ArmA files

Open Assets β†’ Choose addons folder and select something like @DayZ_Epoch/addons. Everything is read locally in the page; nothing is uploaded, and no game data is written anywhere except your own browser.

What the editor reads, and why:

πŸ“
The difference is not subtle. ASC_EU_BulbBLUB was estimated at 0.5 Γ— 0.5 Γ— 6 m from its name; it is actually a 15 cm bulb. Once your files are loaded, walls line up because the boxes are the right size.

Encrypted or obfuscated PBOs (some mods ship them) are reported and skipped rather than producing nonsense. Base Bohemia archives and the Epoch addons are ordinary uncompressed PBOs and read fine.

3 What does not work yet

Stated plainly, so you know what you are getting.

4 Basic workflow

Steps 1–3 are the one-time setup that turns the editor from a box-pusher into something that shows you your actual map. Skip them and everything still works β€” you just build against a flat grey plane with proxy shapes.

─── one-time setup (per browser) ─────────────────────────

Assets β†’ Choose addons folder…
   β”‚      point at @DayZ_Epoch/addons and your ArmA 2 addons
   β”‚      β†’ every classname gets its real size, mesh and colour
   β”‚      β†’ the mod's own classnames join the Objects list
   ↓
Terrain β†’ run the sampler script on your server, paste the log
   β”‚      β†’ real elevation: coastline, valleys, ridges
   β”‚      β†’ click-to-place lands on the hillside, not sea level
   β”‚      β†’ "Align to terrain" and "Drop to ground" start working
   ↓
Assets β†’ Draw map layout   (optional, needs the folder above)
          β†’ the island's own buildings and roads, as scenery
          β†’ build against the real Cherno, not empty ground

─── every session ───────────────────────────────────────

Create a project
   ↓
Choose your map (sets the world boundary)
   ↓
Search for a classname in the Objects panel
   ↓
Click it, then click in the viewport to place
   ↓
Adjust position and rotation (gizmo or the inspector)
   ↓
Export SQF  β†’  Epoch server map addition
   ↓
Drop the .sqf into your dayz_server addon and call it
πŸ’‘
The folder pick and the terrain are both remembered by this browser, so the setup is once, not once per session. If Chrome asks about folder access, choosing β€œAllow on every visit” makes the reconnect automatic after every refresh β€” otherwise a Reconnect ArmA files button appears in the toolbar and one click restores it.

Getting the export onto your server

The Epoch server map addition preset produces the same shape you already see in a server's map-addition scripts β€” wherever you keep them. Epoch has no standard folder for these; most owners make one (buildings/, AddedBuildings/, mapaddons/) inside @DayZ_Epoch_Server/addons/dayz_server/ and call it from server_functions.sqf:

if (isServer) then {

_vehicle_1 = objNull;
if (true) then
{
  _this = createVehicle ["Land_HBarrier_large", [6321.4,7822.7,0.15], [], 0, "CAN_COLLIDE"];
  _vehicle_1 = _this;
  _this setDir 183.5;
  _this setPos [6321.4,7822.7,0.15];
};

};

Save it into your server addon folder and execute it from server_functions.sqf (or wherever your build already calls its map additions), for example:

// "buildings" here is just a folder you make - Epoch does not ship one.
[] ExecVM "\z\addons\dayz_server\buildings\my_trader_city.sqf";

The Custom Map Addons guide covers the server-side setup in full, including the mission-file method if you do not have server addon access.

Injecting a build into a server that is already running

A file that is perfectly fine at server start can take a running server down. SQF is cooperative: several hundred createVehicle calls in one uninterrupted pass hold the scheduler for as long as they take, and past a couple of hundred objects players time out or the session drops outright.

Tick Live SQF injector in the export dialog for that case. It wraps the whole script in [] spawn { } and inserts a pause every N objects β€” between object blocks, never inside one β€” so the server keeps ticking while the build lands:

[] spawn {
  diag_log format ["[MapAddition] building 340 objects, 100 per batch"];
  if (isServer) then {

  ... 100 objects ...

    diag_log format ["[MapAddition] %1 objects placed, pausing 7s", 100];
    uiSleep 7;

  ... and so on ...

  };
  diag_log "[MapAddition] done - 340 objects";
};

Use the plain file at server start β€” the pauses only slow the boot there. The live build still re-imports into this editor unchanged, so you never have to keep two copies in sync. Downloads get a -live suffix so the two files are not mistaken for each other.

Camera

Middle-drag
Orbit
Alt+left-drag
Orbit, for mice with no middle button
Right-drag
Pan
Scroll
Zoom
G
Fly mode on / off β€” or the Fly button in the toolbar
W A S D
Fly, in fly mode or while holding middle/right mouse
Q / E
Fly down / up (Shift for 4Γ— speed)
Arrow keys
Turn the camera when nothing is selected

Flight is a mode because W, E and R are also the gizmo shortcuts. Turn it on and those three stand down; turn it off and they select the gizmo again. Holding a camera mouse button flies without the mode, the way Unreal and Unity do it.

Keyboard shortcuts

W E R
Move / rotate / scale gizmo (outside fly mode)
X
Toggle world / local gizmo space
F
Focus the selection
M
Toggle 3D / 2D map view
T
Top-down view (frames everything in 2D)
Esc
Stop placing, then clear the selection
Delete
Delete the selection (or the βœ• on a row in the Scene list)
Ctrl+Z / Ctrl+Y
Undo / redo
Ctrl+C / Ctrl+V
Copy / paste at the camera target
Ctrl+D
Duplicate
Ctrl+A
Select all
Ctrl+G
Group the selection
P
Draw a path and fill it with the chosen object
Enter
Finish the path (or double-click)
Backspace
Remove the last path point
Ctrl+B
Compositions β€” save or place a prefab
[ ]
Rotate the composition you are placing
Ctrl+S
Save now
Ctrl+O
Projects
Ctrl+I / Ctrl+E
Import / export SQF
Ctrl+F
Jump to the object search box
↑ ↓ ← β†’
Nudge the selection (hold Shift for 10Γ—)
Shift+drag
Box select
Middle-drag
Orbit the camera
Alt+left-drag
Orbit, for mice with no middle button
Right-drag
Pan the camera
Scroll
Zoom
W A S D
Fly, while holding middle or right mouse
Q / E
Fly down / up (hold Shift for 4Γ— speed)
↑ ↓ ← β†’
Turn the camera when nothing is selected

Left-drag is selection: it picks objects and draws the box-select marquee, so the camera lives on the other two buttons. W, E and R stay bound to the move/rotate/scale gizmo β€” holding a camera button is what turns WASD into flight, the same way Unreal and Unity do it.

5 Importing existing SQF

Imported SQF is parsed, never executed. There is no eval and no dynamic code execution anywhere in the importer β€” it tokenises the file and pattern-matches the statements it understands.

Statements the importer understands

_v = createVehicle ["Class",[x,y,z],[],0,"CAN_COLLIDE"];
_v = createVehicleLocal [...];
_v = "Class" createVehicle [x,y,z];
_u = _grp createUnit ["Class",[x,y,z],[],0,"FORM"];
_v = objNull;              _vehicle_1 = _this;     (aliasing)
_v setDir 183.5;
_v setPos    [x,y,z];      β†’  setPos on export
_v setPosATL [x,y,z];      β†’  setPosATL on export
_v setPosASL [x,y,z];      β†’  setPosASL on export
_v setVectorDirAndUp [[..],[..]];
_v setVectorDir [..];  /  _v setVectorUp [..];
_v setVehicleInit "...";
_v enableSimulation false;  (and setVehicleLock, lock, setFuel,
                             setVariable, setDamage … preserved verbatim)

[] spawn { … };             stepped into, not skipped β€” objects
[args] call { … };          inside a scheduler block import normally

Relative compositions work too. If the file defines its own origin, the importer resolves it rather than dropping everything at 0,0:

_center = [3960.23, 10914.5, 0.381744];
_this setPosATL [((_center select 0) + 0.27),
                 ((_center select 1) + 1.82),
                 (0.36) + (_z_adjustment)];
πŸ’‘
Objects commented out with /* … */ are skipped, exactly as the game would skip them. That is the normal way server owners disable part of a composition, so the importer respects it.

Player-built bases, from the database

A live server does not keep player bases in a script β€” they live in MySQL, in object_data, one row per object. Paste those rows into the import box and the editor reads them: either INSERT statements (straight from mysqldump or phpMyAdmin) or a CSV/TSV export with a header row, which is what a GUI client's copy/export produces. The format is detected β€” there is nothing to choose.

Everything about an object's placement is packed into one text column:

[dir, [x,y,z], "ownerUID", [[dirVec],[upVec]], "buildUID"]

Only the first two are guaranteed and different forks append different things, so the reader identifies members by shape rather than by position β€” a bare number is the direction, a three-number array is the position, whichever order they arrive in. Columns are found by name, so extra ones (base_id, StorageCoins, Hitpoints) are ignored rather than throwing the parse off.

πŸ’‘
Database rows import as setPosASL. The stored Z is an absolute world height, so a base built on a hillside only comes back where the player put it if it is read against sea level.
πŸ’‘
An imported object keeps its other columns. Inventory, CharacterID (which is the safe code), Hitpoints, Fuel, Damage and StorageCoins ride along on the object and are written back by the object_data export β€” so moving a crate in the editor does not empty it. They are listed under From the database in the inspector, and every SQF preset ignores them.

The matching Database β€” object_data INSERT rows export preset writes them back, with vectors for anything tilted. ObjectID and ObjectUID are deliberately left out of the column list so MySQL assigns them: they are AUTO_INCREMENT keys, and re-using imported ones collides with rows the server already has. Set the Instance to match your server, and the Owner UID to a SteamID or 0 for a server-owned build. Back the table up first.

When something isn't understood

The import dialog lists every unrecognised statement with its line number before you commit. Everything the importer does understand is still imported β€” one odd line never costs you the whole file. Statements it skips are left alone, not run and not guessed at.

Round-trip fidelity

Classname, position, rotation and placement mode survive an export β†’ import β†’ export cycle unchanged. This is verified automatically against every real map-addition file on a production Epoch 1.0.7.1 server β€” 1,945 objects across 27 files, with zero differences. Formatting is normalised (a setDir -31.390728 comes back as its equivalent 328.609272), but nothing moves.

Why new objects export as setPosATL

Objects you place in the editor default to setPosATL, not setPos. This is deliberate, and it is the single most common way a hand-built composition goes wrong in game.

setPos measures height from whatever surface is beneath the object β€” and that includes other objects. Put a second crate where one already stands and the engine seats it on the first crate's roof. Run a wall across a road and the pieces over the road sit higher than the pieces beside it. The error only appears once a build gets dense, which is exactly when it is most expensive to fix, and it is the classic failure of the in-game ArmA 2 editor. setPosATL measures from the terrain and nothing else, so a piece lands where you put it regardless of what is next to it.

Both are heights above ground, so nothing moves in the viewport and nothing changes about files you import β€” each object keeps the command it was written with, which is what keeps the round trip lossless. Per-object control lives in the inspector, and Export β†’ Placement can force the whole file onto one command.

6 Projects and local data

Exactly what is stored, and where.

One caveat, for accuracy: the site is hosted on Cloudflare Pages, which injects its own Cloudflare Web Analytics beacon into every page on the domain. It is cookieless and records page views rather than behaviour, but it is a request to a third-party host and it applies to this page like any other. It is not part of the editor, has no access to your projects or files, and the editor sends it nothing. See the privacy policy.

Autosave runs a couple of seconds after you stop editing, and at least every 30 seconds during continuous work. The status bar shows whether the current state is saved.

⚠️
Browser storage is not a backup. Clearing site data, private-browsing sessions, and some "clean up my disk" tools will remove it. Use Projects β†’ Export current to keep a .armatools.json file somewhere safe for anything you care about.

7 Asset packs (for real models later)

The editor can already load locally converted assets β€” the plumbing is in place, waiting on a converter. No ArmA assets are hosted on this site, and none ever will be: Bohemia Interactive's models and textures are not ours to redistribute.

The intended flow keeps everything on the machine that already owns the game:

You own ArmA 2
      ↓
A local converter reads your installation
      ↓
It writes an ArmaTools asset pack on your disk
      ↓
The editor loads that pack from your disk
      ↓
Real models render; anything missing stays a proxy

Pack layout

ArmaToolsAssetPack/
β”œβ”€β”€ manifest.json
β”œβ”€β”€ models/
β”‚   └── land_hbarrier_large.glb
β”œβ”€β”€ worlds/
β”‚   └── chernarus/
β”‚       β”œβ”€β”€ world.json
β”‚       β”œβ”€β”€ heightmap.*
β”‚       └── chunks/
└── textures/

manifest.json

{
  "format": "armatools-arma2-assets",
  "version": 1,
  "game": "arma2oa",
  "name": "My Chernarus pack",
  "objects": {
    "Land_HBarrier_large": {
      "model": "models/land_hbarrier_large.glb",
      "size": [8, 2.4, 2.4]
    }
  },
  "worlds": {
    "chernarus": { "world": "worlds/chernarus/world.json" }
  }
}

size is optional β€” the bounding box of the GLB is used when it is missing. Classname lookup is case-insensitive, because ArmA is. Models are GLB (binary glTF) because it is the one format a browser can load without a converter library.

Load a pack from Assets in the toolbar. Where the File System Access API is available you can pick a folder directly; otherwise a directory picker or a ZIP works. ZIPs are read with the browser's built-in decompression, so no third-party library is involved.

8 Roadmap

Real terrain and models are being developed separately from the editor foundation, on purpose. Making PBO/P3D/WRP parsing a prerequisite would have meant months before anything was usable. The editor is useful now and gains fidelity later without being rewritten.

9 Technical notes

Coordinates

ArmA uses X = east, Y = north, Z = elevation. Three.js uses Y = up. The editor converts in exactly one place (js/core/coords.js) so the swap can never drift: three.x = arma.x, three.y = arma.z, three.z = -arma.y. setDir is a clockwise compass bearing, so three.rotation.y = -radians(yaw).

Performance

Proxy geometry and materials are shared per category and size, so 3,000 barriers allocate a handful of buffers rather than 3,000. Object matrices are updated manually rather than every frame, and the viewport only renders when something actually changed β€” an idle editor costs no GPU. A 3,000-object scene imports, renders and exports comfortably within a frame budget.

Browser requirements

WebGL 2, ES modules and import maps: current Chrome, Edge, Firefox or Safari on a desktop. The editor detects missing support and explains the problem instead of showing a blank canvas.

About the DZE 3D Map Editor

The DZE 3D Map Editor is a free, browser-based level editor for DayZ Epoch and ArmA 2 server developers. It replaces the awkward loop of launching ArmA, using the in-game 2D editor, exporting to clipboard and hand-cleaning the result β€” you place real ArmA classnames in a 3D viewport and export server-ready SQF directly.

It is aimed at custom map additions: trader cities, military base expansions, roadblocks, bandit camps and similar server-side compositions placed with createVehicle, setDir and setPos. It reads the map addition files your server already runs, and writes files in the same format.

Related tools