↓ Skip to main content
  1. Gremlin Engine/

Gremlin Dev Log: March 2026 - One of Everything

VeraShackle
Author
VeraShackle
Tales of computers, gadgets & gizmos.
Gremlin Dev Log - This article is part of a series.
Part 3: This Article

Two of everything…
#

If February was moving house, March was the month I unpacked the boxes and noticed I owned two of everything. Two particle systems (2D and 3D). Several ways of saying something is visible. More collision systems than any sane person needs. Legacy renderers still hanging around next to their replacements. So March was mostly about getting down to one of each.

Conveniently, the games were asking for exactly that. Buccaneer and MiniBlaster both wanted fancier particle effects, and that’s hard to do nicely when there are two half-finished particle systems to choose from.

Visible? Enabled? Tinted?
#

Visibility had grown messily: a separate Visibility component, per-component visible flags, and tint colours duplicated across the sprite, billboard and mesh components. It took three commits to untangle:

  • Renderable now owns what applies to the whole entity - its tint colour, and whether it’s visible at all.
  • The per-component flag is now called Enabled, because that’s what it actually meant all along: ‘process this component’. A disabled component isn’t drawn or updated.
  • Render geometry (sprite, billboard, Mesh3D) lives separately from Renderable, so the duplicated fields are gone.
  • VisibilitySystem reads Renderable directly, and the standalone visibility component has been binned.

Even text labels lost their own colour field in favour of Renderable.tint. One way to say it. Lovely.

One particle system to rule them all
#

The 2D and 3D particle code had drifted apart, as copy-and-paste siblings always do. They’re now a single ParticleSystem with emitters for both 2D and 3D, and I finally sorted out who’s responsible for what between the system, the emitter and the particle.

The games got to play with the results almost immediately:

  • Buccaneer got exhaust trails, explosions, and particle-based firing.
  • A ‘dust field’ in both Buccaneer and MiniBlaster, using new spawn volumes - including uniform distributions on a unit circle and sphere, which gave me a star-field effect almost for free.
  • MiniBlaster got frame-animated explosions, thanks to new shared components that keep a particle’s lifespan in sync with its animation. The explosion finishes exactly when the particle dies, rather than vanishing mid-bang.

Along the way particles picked up rotation and scaling (with initial values and variance), explosive bursts, and access to each emitter’s particle entities after creation, so you can dress them up with a billboard, sprite or mesh.

Two ships in Buccaneer trailing glowing purple exhaust across a starfield
Buccaneer’s exhaust trails, with the debug overlay along for the ride
A MiniBlaster ship flying through a cave, with a cloudy explosion behind it
A MiniBlaster explosion, mid-bang

The sprite component’s texture source also got split out into its own TextureSource component, which made the name SpriteSheet a bit of a fib. It’s now TextureAtlas.

Clearing out the old renderers
#

IMDraw - Gremlin’s immediate-mode drawing helper - and the triangle renderer under it now use shaders owned by February’s AssetManager, and RenderEffect became a managed asset in its own right. Billboards are now pushed through IMDraw’s list rather than drawn separately, which means they can finally be sorted against everything else IMDraw draws.

With that done, the legacy renderers could go. One commit deleted about 1,265 lines, which is my favourite kind of commit. And since the ‘unified’ triangle renderer was now the only triangle renderer, UnifiedTriangleRenderer got a name that says what it actually does: TriangleBatch.

A container choice worth writing down
#

One tiny commit deserves its own paragraph. Component pools moved from std::vector to std::deque. Why? Gremlin’s Transform parent/child links are raw pointers into the component pool, and a vector that reallocates as it grows silently invalidates every single one of them. A deque guarantees that growing at the ends never moves existing elements.

The cost is cache coherency. But Gremlin’s systems walk the components sparsely through entity masks anyway, rather than marching through the whole pool in order, so I judged that a fair trade. Ease of use over performance, as promised.

Networking, with ASIO out of sight
#

Gremlin’s networking started from Javidx9’s excellent ASIO-based approach, which is heavily templated on the message type. Templates everywhere means ASIO everywhere, and I didn’t want my games to have to know ASIO exists. So in March I de-templated the transport layer:

  • NetServer and NetClient now use pimpl, behind a new INetConnection interface.
  • Incoming traffic arrives as NetworkEvents delivered to AppState::HandleNetworkEvent - the same way OS events already arrive.
  • ASIO is now a private dependency. No public Gremlin header includes it, so no app has to compile it.

That’s exactly the abstraction pattern I described in Dependency Decisions, and Tanx, my networked tank game, moved over to the new architecture the same day.

Bits & bobs
#

  • Collision systems consolidated, with VolumeShape broken out for reuse beyond particles.
  • Fixed a half-texel offset that was leaving visible seams on rotated sprites.
  • New EntityBuilder helpers for common sprite set-ups.
  • UI: checkbox styling, and a scrollable panel component with coverflow-like behaviour - CNCWorkbench had more buttons than screen. Layouts also rebuild when a state wakes up, in case the window was resized while it was dormant.
  • At the very end of the month, maths, geometry and CAD concepts started separating into a new Gremlin::Geometry namespace (primitives, AABBs, spans, contours), leaving Gremlin::Cad as just the document model.

What’s next?
#

That last bullet is only the start. The geometry split has barely begun, and the new toolpath work over in CNCWorkbench is hungry for more of it.

Gremlin Dev Log - This article is part of a series.
Part 3: This Article