Skip to content

feat(euryopa): two-way live bridge with INU_Tools (Blender) - #11

Open
INU-ez wants to merge 8 commits into
Dryxio:masterfrom
INU-ez:master
Open

feat(euryopa): two-way live bridge with INU_Tools (Blender)#11
INU-ez wants to merge 8 commits into
Dryxio:masterfrom
INU-ez:master

Conversation

@INU-ez

@INU-ez INU-ez commented Aug 12, 2026

Copy link
Copy Markdown

What this is

A file-based, real-time two-way bridge between an Ariane (euryopa) map session and the
INU_Tools Blender addon. You select objects in Ariane, hit Export to Blender, edit them
in Blender, and the changes come back into the running session without a restart — geometry,
textures, collision, transforms, camera and selection.

No networking is involved. Both sides atomically write and read small files under
<game>\ariane\bridge\{inbox,outbox,live,overrides} (via the existing GetArianeDataPath()),
Ariane polling at ~30 Hz and Blender at ~20 Hz.

Everything here is additive: it sits on top of the existing custom-import / hot-reload /
override machinery and does not change any existing behavior.

What it does

  • Export to Blender — sends the selected models out as DFF, with optional TXD, IDE, IPL,
    LOD and COL. Toggles for Auto TXD and Vanilla sit next to the button.
  • Live moves, both directions — dragging an instance in either app moves it in the other.
  • Live camera, both directions — the Blender viewport and the Ariane camera follow each other.
  • Live selection sync.
  • Create instance / create model from Blender — new objects can be spawned into the session.
  • Deletes, propagated from Blender.
  • Hot-reload — an edited DFF/TXD/COL is picked up by registering a runtime loose-file
    override, so the model updates in place.

Instance identity

Instances are addressed by a stable GUID derived from the IPL file and the instance's index in
it. Deleted rows are commented out rather than removed, so indices stay stable and the same
object keeps its ID across a save/reload cycle.

Two non-obvious bits, in case they matter during review

  • The camera up-vector is transmitted explicitly. Ariane's camera is roll-free by design
    (m_up is always (0,0,1) and Camera::TurnUpOrDown only ever fits m_up_z), so from Blender
    only the sign of the up-vector is taken, and a clean roll-free up is rebuilt in the new frame.
    Feeding a tilted up straight into m_up tilts the horizon permanently.
  • The camera is applied before timecycle::Update(). Previously the Blender-driven snap
    landed later in the frame, so during a fast move the sky colours were still computed for the
    previous position while the geometry was already rendered at the new one — the top of the
    screen flashed white. The order is now: Ariane input → Blender snap → update() → timecycle
    → render.

Files touched

File Lines What
tools/euryopa/objectinst.cpp +1022 bridge core: export, pollers, GUID, live state
tools/euryopa/gui.cpp +163 Export to Blender button, toggles, poller calls
tools/euryopa/colstore.cpp +92 keep the COL version header so an exact .col can be rebuilt
tools/euryopa/modloader.cpp/.h +41 register a runtime loose-file override for live-reload
tools/euryopa/euryopa.cpp/.h +44 poller calls + declarations
tools/euryopa/objectdef.cpp +21 ObjectDef::Reload()
tools/euryopa/txdstore.cpp +17 ForceTxdReload()
tools/euryopa/collision.cpp/.h +24 inline COL header fields
.gitignore +5 ignore premake-generated VS project files

Roughly 1400 lines, all inside tools/euryopa/.

Status — opened as a draft on purpose

San Andreas: working.
GTA III / Vice City: working.

Notes

Happy to split this into smaller PRs, put it behind a build flag, or reshape anything to fit
how you'd rather structure it — just say the word.

INU-ez added 8 commits August 6, 2026 22:17
File-based bridge under <game>/ariane/bridge that keeps an Ariane map
session and the INU_Tools Blender addon in sync in real time. Instances
are tracked by a stable GUID (IPL filter key + index) that survives
save/reload.

Live sync (30 Hz Ariane / 20 Hz Blender, throttled + freshness-gated):
- moves: selected instance transforms stream both ways (PollBlenderLiveIn
  / WriteArianeLiveState); whoever moved last wins.
- camera: eye/target/up streamed both ways. Ariane's camera is roll-free
  by design, so only the up-vector sign is taken and a clean up is rebuilt
  in the view plane; Blender rebuilds its view_rotation from forward+up
  (no to_track_quat roll flip). Camera is applied before Timecycle so the
  whole frame uses one position (fixes the sky/top flashing on fast moves).
- selection: two-way, last change wins.

Authoring:
- CreateBridgeInstance: spawn a new instance of an existing model (E-1),
  driven by duplicate-detection on the Blender side.
- CreateBridgeModel: register a brand-new model from loose DFF/TXD/COL
  plus an optional linked LOD, then spawn+select it (E-2).
- soft delete/restore synced from Blender (PollBlenderDeletes), with a
  mass-vanish guard so a file reload can't wipe the scene.

Arbitration: each side only drives while its window is OS-foreground, so
the two never fight over the camera/selection and the app doesn't lag when
the other window has focus.
…ltip the options

Export to Blender (ExportSelectedToBlender):
- Emit a placement line for EVERY selected instance, exporting each model's .dff/.txd/.col only once (std::unordered_set). The old per-model dedup skipped the whole instance, so reused models (GTA III roads/land are one model placed many times) only sent a single copy. Return the placement count, not the model count; toast "Sent N object(s)".
- New HD companion (bool hdToo, "HD" checkbox): if a selected model is a LOD (LOD<name>), also send its high-detail model at the LOD instance's transform (iid=-1, so moving it in Blender won't move the LOD instance) — mirrors the existing LOD-from-HD option, so exporting a distant LOD brings the real one too.
- Tooltips on the button and every option (Auto TXD / Vanilla / IDE / IPL / LOD / COL / HD) explaining what each sends.
…sections

The bridge guid is <iplFilterKey>#<m_iplIndex>, and it must be unique per
instance or the reverse sync moves the wrong one and the re-import dedup
misfires. Streamed binary IPL instances (iplstore.cpp) collided: SA loads one
map as a text IPL PLUS many binary sections (SetupRelatedIPLs loads
<name>_stream0/1/… all sharing the same filter key), so a per-section index made
LAw2_stream0#60 == LAw2_stream1#60 — moving one moved a different model.

Fix: one continuous index across a map's text IPL and all its streamed sections.
FileLoader::NextIplInstIndex() (returns iplInstCounter++, reset per map in
LoadScene) is now called from both the text loader and iplstore's LoadIpl, so
every instance of a map gets a unique, stable #index. Declared inside
namespace FileLoader (fileloader.cpp is wrapped in it — a global decl gave an
LNK2019). GTA III (text IPLs only) is unaffected.
@Dryxio

Dryxio commented Aug 15, 2026

Copy link
Copy Markdown
Owner

hi i'll check when i have some time, thanks for the PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants