<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title><![CDATA[Esenthel Forum - All Forums]]></title>
		<link>https://esenthel.com/forum/</link>
		<description><![CDATA[Esenthel Forum - https://esenthel.com/forum]]></description>
		<pubDate>Sat, 25 Apr 2026 09:02:11 +0000</pubDate>
		<generator>MyBB</generator>
		<item>
			<title><![CDATA[Lag compensation]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11816</link>
			<pubDate>Wed, 22 Apr 2026 01:28:27 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11816</guid>
			<description><![CDATA[I had Claude Code improve and bring into the engine the lag compensation I use in my project, it makes FPS and Chivalry/Mount and Blade weapon trail swing games playable and fair even with some clients having 200+ ms pings (overseas players). I use ENET in my case but I kept it transport layer agnostic here.<br />
<br />
<span style="font-weight: bold;">Networking: Lag Compensation + Physics Rollback</span><br />
<span style="font-style: italic;">Transport-agnostic server-authoritative simulation layer (lag-compensation branch)</span><br />
<br />
A commercial-grade networking substrate built from scratch under *Engine/Net/*. Two independent tiers of temporal correction, built using shipped-game references:<br />
<ul>
<li><span style="font-weight: bold;">Tier 1 — selective hitbox rewind</span> (Photon Fusion 2 / Source / CS:GO model). Each server tick, every replicated actor contributes a flat list of hitbox samples into a 256-tick ring buffer (~4.27 s @ 60 Hz). Spatial queries take a *(tick, frac)* pair, linearly interpolate the two bracketing snapshots, and test against the interpolated hitboxes. The live gameplay scene is never touched — perfect for hitscan / melee where you only need “where was this limb when the client fired”.</li>
<li><span style="font-weight: bold;">Tier 2 — full PhysX rewind + resimulation</span> (UE5 networked-physics model). Snapshots pose / linear + angular velocity / kinematic / sleeping flags for every tracked *Actor* each tick; on demand restores them and drives *Physics.stepOnce(dt)* forward to catch up to the current tick. A *RollbackGuard* (TLS depth counter) is held active for the resim window so gameplay / animation / audio code that checks *RollbackGuard::active()* can suppress side-effects (event fire, notifies, SFX) during replay.<br />
</li></ul>
<br />
All modules are <span style="font-weight: bold;">unconditionally compiled</span> — no CMake flag, no opt-out, no extra link cost (the dependency surface is zero — pure C++, no third-party libraries). The transport layer is <span style="font-weight: bold;">abstract</span>: the engine ships *LoopbackTransport* (in-process queue) and *FakeLatencyTransport* (wraps any transport with configurable latency / jitter / loss), plus sketches for *ENetTransport* and a multi-hop *ForwardingTransport* in *Engine/H/Net/NetTransport.h* header comments. User projects subclass *INetTransport* and forward to ENet / Steam Sockets / WebRTC / whatever fits their deployment.<br />
<br />
<span style="font-weight: bold;">Module layout:</span><br />
<ul>
<li>*Net/NetTick.h/cpp* — *TickSeq* (*ULong*) + *NetTick{seq, frac}* shared tick + sub-tick type</li>
<li>*Net/NetClock.h/cpp* — NTP-style clock sync; rolling 5-sample median-of-offset + RTT + jitter; monotonic server timeline</li>
<li>*Net/NetTransport.h/cpp* — *INetTransport* abstract + *LoopbackTransport* + *FakeLatencyTransport* + ENet / multi-hop sketches</li>
<li>*Net/NetObject.h/cpp* — *INetReplicated* interface, *NetBase* convenience, *NET_REGISTER_CLASS* factory, *HitboxSample*, *NetWriter* / *NetReader* byte-buffer codecs with opt-in *putUIntPacked* (varint) / *putPosQ16* (quantized position, 6 B vs 12 B) compression helpers</li>
<li>*Net/InputCommand.h/cpp* — *InputCommand* POD + *InputBuffer* (client redundancy — re-sends last N unacked cmds) + *InputQueue* (server per-connection, wrap-safe *UShort* seq compare)</li>
<li>*Net/NetReplicator.h/cpp* — spawn / despawn / snapshot build + apply / input routing / ping-pong / per-connection relevance callback hook</li>
<li>*Net/LagCompensation.h/cpp* — <span style="font-weight: bold;">Tier 1</span> 256-tick hitbox ring; *ray* / *sweep* / *sweepArc* / *overlap* / *fetchInterp* CPU-analytic queries (sphere + capsule + box) with *(tick, frac)* OR *(real_time)* addressing for clustered servers</li>
<li>*Net/RollbackGuard.h/cpp* — thread-local depth counter; *RollbackGuard::active()* callable from gameplay / anim code</li>
<li>*Net/NetRollback.h/cpp* — <span style="font-weight: bold;">Tier 2</span> snapshot / restore *Actor* state, *Physics.stepOnce*-driven resim, 8-tick default cap (user-configurable), *InputReplayFn* hook, *AnimCaptureFn* / *AnimRestoreFn* hooks for bone-sourced hitboxes<br />
</li></ul>
<br />
~3,200 lines of new engine code across 18 files, zero third-party links, zero *#ifdef*s outside platform branches.<br />
<br />
<span style="font-weight: bold;">Quick start (single-process, both sides in one *Update()* — this is how every category-20 tutorial is structured):</span><br />
<br />
<div class="codeblock">
<div class="title">Code:<br />
</div><div class="body" dir="ltr"><code>// --- Replicated actor ---<br />
struct Target : NetBase<br />
{<br />
Vec pos;<br />
NetClassID netClassId()C override { return 0x54475431u; /* 'TGT1' */ }<br />
void netSerialize&nbsp;&nbsp;(NetWriter &amp;w, UInt fields)C override { w.putVec(pos); }<br />
void netDeserialize(NetReader &amp;r, UInt fields)&nbsp;&nbsp;override { pos = r.getVec(); }<br />
void netHitboxes(Memc&lt;HitboxSample&gt; &amp;out)C override<br />
{<br />
HitboxSample &amp;s = out.New();<br />
s.shape = HITBOX_SPHERE;<br />
s.group = 0x0001;<br />
s.half_ext.set(0.3f, 0.3f, 0.3f);<br />
s.xform.identity(); s.xform.pos = pos;<br />
}<br />
};<br />
NET_REGISTER_CLASS(Target, 0x54475431u);<br />
<br />
// --- Server ---<br />
NetReplicator SvrRep;&nbsp;&nbsp;NetClock SvrClk;&nbsp;&nbsp;LagComp SvrLag;<br />
SvrClk.initServer(60);<br />
SvrRep.serverInit(&amp;transport, &amp;SvrClk);<br />
<br />
each_tick:<br />
SvrRep.serverDrainInputs();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // apply client inputs via INetReplicated::netApplyInput<br />
// ...gameplay simulation...<br />
SvrLag.recordTick(SvrRep, tick, Time.curTime());<br />
if(tick % 3 == 0) SvrRep.sendSnapshots(tick);&nbsp;&nbsp; // 20 Hz @ 60 tick<br />
<br />
on_client_fire(from, to, client_tick):<br />
LagHit h;<br />
if(SvrLag.ray(client_tick, 0.0f, from, to, h))<br />
apply_damage(h.owner, h.owner_idx);<br />
<br />
// --- Client ---<br />
NetReplicator CliRep;&nbsp;&nbsp;NetClock CliClk;&nbsp;&nbsp;InputBuffer CliInputs;<br />
CliClk.initClient(60);<br />
CliRep.clientInit(&amp;transport, &amp;CliClk, /*server_conn*/1);<br />
<br />
each_frame:<br />
CliRep.sendPingMaybe();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // periodic NTP sync<br />
CliRep.update(Time.curTime());&nbsp;&nbsp;&nbsp;&nbsp;// drains spawn/despawn/snapshot/pong<br />
Vec input = sample_keyboard();<br />
pawn-&gt;predict(input);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // local prediction on owned pawn<br />
CliInputs.push(input, buttons, CliClk.tick());<br />
CliInputs.sendTo(CliRep);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // redundant — includes last N unacked<br />
CliInputs.ack(CliRep.lastAckedSeq());<br />
<br />
on_fire(from, to):<br />
// Ship the compensation tick with the fire cmd so the server can rewind<br />
// to exactly what this client was seeing.<br />
fire_cmd.compensation_tick = CliRep.lastSnapshotTick();</code></div></div>
<br />
<span style="font-weight: bold;">Tier 2 (physics rewind) — only when you actually need resim; most games won't:</span><br />
<br />
<span style="font-style: italic;">NetRollback SvrRoll;</span><br />
<span style="font-style: italic;">SvrRoll.init(1.0f/60.0f, /*max_resim=*/8);</span><br />
<span style="font-style: italic;">for(Actor *a : dynamic_physics_actors) SvrRoll.track(*a);</span><br />
<br />
<span style="font-style: italic;">on_physics_step_completed(tick):</span><br />
<span style="font-style: italic;">SvrRoll.recordTick(tick);</span><br />
<br />
<span style="font-style: italic;">on_authoritative_correction(target_tick):</span><br />
<span style="font-style: italic;">SvrRoll.rewindAndResimulate(target_tick, currentTick, replayInputs, user);</span><br />
<br />
<span style="font-weight: bold;">Physics engine touch points</span> — two small, surgical additions; everything else is pure add-on:<br />
<ul>
<li>*Physics.stepOnce(Flt dt)* — direct *simulate* + *fetchResults* with no accumulator, no *simulation_step_completed* callback. This is what drives Tier 2 resim. Safe only between frames (after *stopSimulation()* or inside *WorldManager::physics_update()*).</li>
<li>*Time.rollbackDt(Flt dt)* — thread-local override for *Time.d()*. When *&gt; 0*, *Time.d()* returns it instead of the real frame delta. Set automatically by *NetRollback::rewindAndResimulate* per resim tick to the recorded *dt_used*, so animation code (*Motion::updateAuto*, *AnimatedSkeleton::animate*) replays at the exact dt the forward sim used — deterministic bone poses during rewind.<br />
</li></ul>
<br />
<span style="font-weight: bold;">Animation sync hooks for bone-driven hitboxes</span> — if your game's hitboxes are attached to *AnimatedSkeleton* bones (limbs parented to bone slots), both forward-sim animation and rollback-resim animation must reproduce the same per-tick bone poses. Two engine hooks pair them:<br />
<br />
<ol type="1">
<li><span style="font-weight: bold;">Forward sim</span> — set *Time.rollbackDt(icf.timeDeltaPhysics)* before animating each player tick, so *Motion::updateAuto* uses the physics dt instead of the variable frame dt. Restore with *Time.rollbackDt(0)*.</li>
<li><span style="font-weight: bold;">Rollback resim</span> — pass *NetRollback::setAnimCallbacks(cap, rest, user)*. *AnimCaptureFn* serialises every tracked *Motion* (via *Motion::save* or direct field writes) into a small opaque blob per tick (~64 B per motion × N motions × 256 ticks — fits easily). *AnimRestoreFn* does the inverse <span style="font-weight: bold;">before</span> the user's *InputReplayFn* fires, so replayed animation code starts from the correct *Motion.time* / *Motion.blend*. Pair this with *if(RollbackGuard::active()) return;* at the top of anim-event consumers (weapon clash SFX, hit sparks) so they don't re-fire during replay.<br />
</li></ol>
<br />
<br />
Includes 7 more tutorials.<br />
<a href="https://postimg.cc/hhh408RZ" target="_blank"><img src="https://i.postimg.cc/gcHwm4Sp/Screenshot-from-2026-04-21-21-12-54.png" border="0" alt="[Image: Screenshot-from-2026-04-21-21-12-54.png]" /></a><br />
<br />
in this fork:<br />
<a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></description>
			<content:encoded><![CDATA[I had Claude Code improve and bring into the engine the lag compensation I use in my project, it makes FPS and Chivalry/Mount and Blade weapon trail swing games playable and fair even with some clients having 200+ ms pings (overseas players). I use ENET in my case but I kept it transport layer agnostic here.<br />
<br />
<span style="font-weight: bold;">Networking: Lag Compensation + Physics Rollback</span><br />
<span style="font-style: italic;">Transport-agnostic server-authoritative simulation layer (lag-compensation branch)</span><br />
<br />
A commercial-grade networking substrate built from scratch under *Engine/Net/*. Two independent tiers of temporal correction, built using shipped-game references:<br />
<ul>
<li><span style="font-weight: bold;">Tier 1 — selective hitbox rewind</span> (Photon Fusion 2 / Source / CS:GO model). Each server tick, every replicated actor contributes a flat list of hitbox samples into a 256-tick ring buffer (~4.27 s @ 60 Hz). Spatial queries take a *(tick, frac)* pair, linearly interpolate the two bracketing snapshots, and test against the interpolated hitboxes. The live gameplay scene is never touched — perfect for hitscan / melee where you only need “where was this limb when the client fired”.</li>
<li><span style="font-weight: bold;">Tier 2 — full PhysX rewind + resimulation</span> (UE5 networked-physics model). Snapshots pose / linear + angular velocity / kinematic / sleeping flags for every tracked *Actor* each tick; on demand restores them and drives *Physics.stepOnce(dt)* forward to catch up to the current tick. A *RollbackGuard* (TLS depth counter) is held active for the resim window so gameplay / animation / audio code that checks *RollbackGuard::active()* can suppress side-effects (event fire, notifies, SFX) during replay.<br />
</li></ul>
<br />
All modules are <span style="font-weight: bold;">unconditionally compiled</span> — no CMake flag, no opt-out, no extra link cost (the dependency surface is zero — pure C++, no third-party libraries). The transport layer is <span style="font-weight: bold;">abstract</span>: the engine ships *LoopbackTransport* (in-process queue) and *FakeLatencyTransport* (wraps any transport with configurable latency / jitter / loss), plus sketches for *ENetTransport* and a multi-hop *ForwardingTransport* in *Engine/H/Net/NetTransport.h* header comments. User projects subclass *INetTransport* and forward to ENet / Steam Sockets / WebRTC / whatever fits their deployment.<br />
<br />
<span style="font-weight: bold;">Module layout:</span><br />
<ul>
<li>*Net/NetTick.h/cpp* — *TickSeq* (*ULong*) + *NetTick{seq, frac}* shared tick + sub-tick type</li>
<li>*Net/NetClock.h/cpp* — NTP-style clock sync; rolling 5-sample median-of-offset + RTT + jitter; monotonic server timeline</li>
<li>*Net/NetTransport.h/cpp* — *INetTransport* abstract + *LoopbackTransport* + *FakeLatencyTransport* + ENet / multi-hop sketches</li>
<li>*Net/NetObject.h/cpp* — *INetReplicated* interface, *NetBase* convenience, *NET_REGISTER_CLASS* factory, *HitboxSample*, *NetWriter* / *NetReader* byte-buffer codecs with opt-in *putUIntPacked* (varint) / *putPosQ16* (quantized position, 6 B vs 12 B) compression helpers</li>
<li>*Net/InputCommand.h/cpp* — *InputCommand* POD + *InputBuffer* (client redundancy — re-sends last N unacked cmds) + *InputQueue* (server per-connection, wrap-safe *UShort* seq compare)</li>
<li>*Net/NetReplicator.h/cpp* — spawn / despawn / snapshot build + apply / input routing / ping-pong / per-connection relevance callback hook</li>
<li>*Net/LagCompensation.h/cpp* — <span style="font-weight: bold;">Tier 1</span> 256-tick hitbox ring; *ray* / *sweep* / *sweepArc* / *overlap* / *fetchInterp* CPU-analytic queries (sphere + capsule + box) with *(tick, frac)* OR *(real_time)* addressing for clustered servers</li>
<li>*Net/RollbackGuard.h/cpp* — thread-local depth counter; *RollbackGuard::active()* callable from gameplay / anim code</li>
<li>*Net/NetRollback.h/cpp* — <span style="font-weight: bold;">Tier 2</span> snapshot / restore *Actor* state, *Physics.stepOnce*-driven resim, 8-tick default cap (user-configurable), *InputReplayFn* hook, *AnimCaptureFn* / *AnimRestoreFn* hooks for bone-sourced hitboxes<br />
</li></ul>
<br />
~3,200 lines of new engine code across 18 files, zero third-party links, zero *#ifdef*s outside platform branches.<br />
<br />
<span style="font-weight: bold;">Quick start (single-process, both sides in one *Update()* — this is how every category-20 tutorial is structured):</span><br />
<br />
<div class="codeblock">
<div class="title">Code:<br />
</div><div class="body" dir="ltr"><code>// --- Replicated actor ---<br />
struct Target : NetBase<br />
{<br />
Vec pos;<br />
NetClassID netClassId()C override { return 0x54475431u; /* 'TGT1' */ }<br />
void netSerialize&nbsp;&nbsp;(NetWriter &amp;w, UInt fields)C override { w.putVec(pos); }<br />
void netDeserialize(NetReader &amp;r, UInt fields)&nbsp;&nbsp;override { pos = r.getVec(); }<br />
void netHitboxes(Memc&lt;HitboxSample&gt; &amp;out)C override<br />
{<br />
HitboxSample &amp;s = out.New();<br />
s.shape = HITBOX_SPHERE;<br />
s.group = 0x0001;<br />
s.half_ext.set(0.3f, 0.3f, 0.3f);<br />
s.xform.identity(); s.xform.pos = pos;<br />
}<br />
};<br />
NET_REGISTER_CLASS(Target, 0x54475431u);<br />
<br />
// --- Server ---<br />
NetReplicator SvrRep;&nbsp;&nbsp;NetClock SvrClk;&nbsp;&nbsp;LagComp SvrLag;<br />
SvrClk.initServer(60);<br />
SvrRep.serverInit(&amp;transport, &amp;SvrClk);<br />
<br />
each_tick:<br />
SvrRep.serverDrainInputs();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // apply client inputs via INetReplicated::netApplyInput<br />
// ...gameplay simulation...<br />
SvrLag.recordTick(SvrRep, tick, Time.curTime());<br />
if(tick % 3 == 0) SvrRep.sendSnapshots(tick);&nbsp;&nbsp; // 20 Hz @ 60 tick<br />
<br />
on_client_fire(from, to, client_tick):<br />
LagHit h;<br />
if(SvrLag.ray(client_tick, 0.0f, from, to, h))<br />
apply_damage(h.owner, h.owner_idx);<br />
<br />
// --- Client ---<br />
NetReplicator CliRep;&nbsp;&nbsp;NetClock CliClk;&nbsp;&nbsp;InputBuffer CliInputs;<br />
CliClk.initClient(60);<br />
CliRep.clientInit(&amp;transport, &amp;CliClk, /*server_conn*/1);<br />
<br />
each_frame:<br />
CliRep.sendPingMaybe();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // periodic NTP sync<br />
CliRep.update(Time.curTime());&nbsp;&nbsp;&nbsp;&nbsp;// drains spawn/despawn/snapshot/pong<br />
Vec input = sample_keyboard();<br />
pawn-&gt;predict(input);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // local prediction on owned pawn<br />
CliInputs.push(input, buttons, CliClk.tick());<br />
CliInputs.sendTo(CliRep);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // redundant — includes last N unacked<br />
CliInputs.ack(CliRep.lastAckedSeq());<br />
<br />
on_fire(from, to):<br />
// Ship the compensation tick with the fire cmd so the server can rewind<br />
// to exactly what this client was seeing.<br />
fire_cmd.compensation_tick = CliRep.lastSnapshotTick();</code></div></div>
<br />
<span style="font-weight: bold;">Tier 2 (physics rewind) — only when you actually need resim; most games won't:</span><br />
<br />
<span style="font-style: italic;">NetRollback SvrRoll;</span><br />
<span style="font-style: italic;">SvrRoll.init(1.0f/60.0f, /*max_resim=*/8);</span><br />
<span style="font-style: italic;">for(Actor *a : dynamic_physics_actors) SvrRoll.track(*a);</span><br />
<br />
<span style="font-style: italic;">on_physics_step_completed(tick):</span><br />
<span style="font-style: italic;">SvrRoll.recordTick(tick);</span><br />
<br />
<span style="font-style: italic;">on_authoritative_correction(target_tick):</span><br />
<span style="font-style: italic;">SvrRoll.rewindAndResimulate(target_tick, currentTick, replayInputs, user);</span><br />
<br />
<span style="font-weight: bold;">Physics engine touch points</span> — two small, surgical additions; everything else is pure add-on:<br />
<ul>
<li>*Physics.stepOnce(Flt dt)* — direct *simulate* + *fetchResults* with no accumulator, no *simulation_step_completed* callback. This is what drives Tier 2 resim. Safe only between frames (after *stopSimulation()* or inside *WorldManager::physics_update()*).</li>
<li>*Time.rollbackDt(Flt dt)* — thread-local override for *Time.d()*. When *&gt; 0*, *Time.d()* returns it instead of the real frame delta. Set automatically by *NetRollback::rewindAndResimulate* per resim tick to the recorded *dt_used*, so animation code (*Motion::updateAuto*, *AnimatedSkeleton::animate*) replays at the exact dt the forward sim used — deterministic bone poses during rewind.<br />
</li></ul>
<br />
<span style="font-weight: bold;">Animation sync hooks for bone-driven hitboxes</span> — if your game's hitboxes are attached to *AnimatedSkeleton* bones (limbs parented to bone slots), both forward-sim animation and rollback-resim animation must reproduce the same per-tick bone poses. Two engine hooks pair them:<br />
<br />
<ol type="1">
<li><span style="font-weight: bold;">Forward sim</span> — set *Time.rollbackDt(icf.timeDeltaPhysics)* before animating each player tick, so *Motion::updateAuto* uses the physics dt instead of the variable frame dt. Restore with *Time.rollbackDt(0)*.</li>
<li><span style="font-weight: bold;">Rollback resim</span> — pass *NetRollback::setAnimCallbacks(cap, rest, user)*. *AnimCaptureFn* serialises every tracked *Motion* (via *Motion::save* or direct field writes) into a small opaque blob per tick (~64 B per motion × N motions × 256 ticks — fits easily). *AnimRestoreFn* does the inverse <span style="font-weight: bold;">before</span> the user's *InputReplayFn* fires, so replayed animation code starts from the correct *Motion.time* / *Motion.blend*. Pair this with *if(RollbackGuard::active()) return;* at the top of anim-event consumers (weapon clash SFX, hit sparks) so they don't re-fire during replay.<br />
</li></ol>
<br />
<br />
Includes 7 more tutorials.<br />
<a href="https://postimg.cc/hhh408RZ" target="_blank"><img src="https://i.postimg.cc/gcHwm4Sp/Screenshot-from-2026-04-21-21-12-54.png" border="0" alt="[Image: Screenshot-from-2026-04-21-21-12-54.png]" /></a><br />
<br />
in this fork:<br />
<a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[RmlUi]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11815</link>
			<pubDate>Mon, 20 Apr 2026 13:58:58 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11815</guid>
			<description><![CDATA[Claude Code was able to one-shot RmlUi integration into Esenthel.<br />
<br />
I'm not a web-dev person, but with Vibe Designing coming soon<br />
( e.g. <a href="https://claude.ai/design" target="_blank">https://claude.ai/design</a> ) , I think there is value in having a UI<br />
system defined by a declarative language like HTML/CSS.<br />
<br />
This also makes it much easier for users to mod or skin the UI if they<br />
want. But really, it's about making it easier for the AI to do it.<br />
Claude Code really seems to struggle with building Esenthel GUIs<br />
programmatically that look good and have the correct font size, etc.,<br />
but with HTML/CSS this problem doesn't exist.<br />
<br />
I haven't profiled this, the implementation has not been optimized,<br />
and I'm sure there is a performance penalty. That said, RmlUi is<br />
highly regarded and is designed for games; it is not a full WebKit.<br />
<br />
An integration of <span style="font-weight: bold;"><a href="https://github.com/mikke89/RmlUi" target="_blank">RmlUi</a> v6.2</span> (MIT) as<br />
a retained-mode HTML/CSS UI library that <span style="font-weight: bold;">coexists</span> with Esenthel's built-in<br />
<span style="font-style: italic;">Gui</span> system without touching it. Both run in the same frame — the native<br />
widget set is still available for lightweight 3D-integrated controls, while<br />
RmlUi handles HTML/CSS/animation-heavy UI (menus, HUDs, settings screens,<br />
chat windows). A single RmlUi render backend lives inside the Engine static<br />
library and drives RmlUi's vertex stream through the engine's existing 2D<br />
batcher, so every renderer backend (DX11, DX12, Vulkan, OpenGL) gets RmlUi<br />
for free with zero backend-specific code.<br />
<br />
Game HUD tutorial<br />
<a href="https://pixeldrain.com/api/file/nWMwv7tq" target="_blank"><img src="https://pixeldrain.com/api/file/nWMwv7tq" width="800" height="339" border="0" alt="[Image: nWMwv7tq]" /></a><br />
<br />
<br />
Using Esenthel GUI elements and RmlUi elements side-by-side tutorial<br />
<a href="https://pixeldrain.com/api/file/3cB1Nk65" target="_blank"><img src="https://pixeldrain.com/api/file/3cB1Nk65" width="800" height="339" border="0" alt="[Image: 3cB1Nk65]" /></a><br />
<br />
<br />
<span style="font-weight: bold;">Quick start:</span><br />
<br />
<div class="codeblock">
<div class="title">Code:<br />
</div><div class="body" dir="ltr"><code>#include "Gui/RmlUi.h"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// EE::RmlUi::* facade<br />
<br />
// In a single .cpp where you author Rml event listeners, DOM manipulation,<br />
// etc. — wrap the RmlUi includes so Esenthel macros and X11 defines don't<br />
// collide with RmlUi's template parameters and enum names:<br />
#include "../../ThirdPartyLibs/begin.h"<br />
#undef T1&nbsp;&nbsp;#undef T2&nbsp;&nbsp;#undef T3&nbsp;&nbsp;#undef T4&nbsp;&nbsp;#undef T5<br />
#undef T6&nbsp;&nbsp;#undef T7&nbsp;&nbsp;#undef T8&nbsp;&nbsp;#undef T9&nbsp;&nbsp;#undef T10&nbsp;&nbsp;#undef T11<br />
#undef Always&nbsp;&nbsp;#undef NotUseful<br />
#include &lt;RmlUi/Core.h&gt;<br />
#include "../../ThirdPartyLibs/end.h"<br />
<br />
static Rml::Context&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *Ctx=null;<br />
static Rml::ElementDocument *Doc=null;<br />
<br />
Bool Init()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::Init();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // install render + system interfaces<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::LoadFontFace("Data/RmlUi/LatoLatin-Regular.ttf");<br />
&nbsp;&nbsp;&nbsp;&nbsp;Ctx=EE::RmlUi::CreateContext();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// sized to D.resW()/D.resH()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Doc=Ctx-&gt;LoadDocument("Data/RmlUi/demo.rml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(Doc)Doc-&gt;Show();<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
}<br />
void Shut()&nbsp;&nbsp; { EE::RmlUi::Shut(); }<br />
Bool Update() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(Kb.bp(KB_ESC))return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Gui.update();<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::PumpInput(Ctx);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Ms/Kb -&gt; context<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::Update&nbsp;&nbsp; (Ctx);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // events, animations, dirty geometry<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
}<br />
void Draw()&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Renderer(RenderScene);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // optional 3D world<br />
&nbsp;&nbsp;&nbsp;&nbsp;Gui.draw();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Esenthel Gui above 3D<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::Render(Ctx);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// RmlUi on top of everything<br />
}</code></div></div>
<br />
<span style="font-weight: bold;">Shipped features:</span><br />
<ul>
<li><span style="font-weight: bold;">Single-TU backend</span> — <span style="font-style: italic;">Engine/Source/Gui/RmlUi.cpp</span> is the only engine<br />
source file that includes RmlUi headers. It subclasses<br />
<span style="font-style: italic;">Rml::RenderInterface</span> and <span style="font-style: italic;">Rml::SystemInterface</span>, owns the 1×1 white<br />
fallback texture, and exports the <span style="font-style: italic;">EE::RmlUi::</span> facade used by app code.<br />
Macro pollution is neutralised locally: Esenthel's template-shortcut<br />
macros <span style="font-style: italic;">T1..T11</span> (from <span style="font-style: italic;">Engine/H/_/defines.h</span>, they collide with<br />
<span style="font-style: italic;">robin_hood.h</span>'s template parameters) and X11's <span style="font-style: italic;">Always</span> / <span style="font-style: italic;">NotUseful</span><br />
(not stripped by <span style="font-style: italic;">Engine/H/_/headers.h</span>) are undefined inside the<br />
<span style="font-style: italic;">ThirdPartyLibs/begin.h</span> / <span style="font-style: italic;">end.h</span> bracket just for this one TU.</li>
<li><span style="font-weight: bold;">API-agnostic rendering</span> — <span style="font-style: italic;">RenderGeometry</span> feeds RmlUi's pixel-space<br />
vertex stream into the engine's existing 2D path. Per-vertex<br />
conversion goes through <span style="font-style: italic;">D.pixelToScreen(Vec2)</span> (canonical helper; no<br />
hand-rolled Y-flip). <span style="font-style: italic;">D.alpha(ALPHA_MERGE)</span> handles RmlUi's<br />
premultiplied RGBA (which it emits since v5). Scissor goes through<br />
<span style="font-style: italic;">D.clip(&amp;engine_rect)</span> / <span style="font-style: italic;">D.pixelToScreen(RectI)</span>. Works identically on<br />
DX11, DX12, Vulkan, OpenGL.</li>
<li><span style="font-weight: bold;">Untextured-path fallback</span> — RmlUi calls <span style="font-style: italic;">RenderGeometry</span> with<br />
<span style="font-style: italic;">texture=0</span> for solid-colour geometry. The backend binds a pre-built<br />
1×1 opaque-white <span style="font-style: italic;">Image</span> so the textured shader path effectively<br />
degenerates to plain vertex colour. This avoids needing a separate<br />
colour-only code path.</li>
<li><span style="font-weight: bold;">FreeType sharing</span> — RmlUi's default FreeType font engine is pointed<br />
at Esenthel's prebuilt <span style="font-style: italic;">EE_FreeType</span> static lib via<br />
<span style="font-style: italic;">FREETYPE_INCLUDE_DIRS</span> / <span style="font-style: italic;">FREETYPE_LIBRARY</span> cache vars in<br />
<span style="font-style: italic;">ThirdPartyLibs/RmlUi/CMakeLists.txt</span>, so one copy of FreeType ships in<br />
the final link. <span style="font-style: italic;">EE_RMLUI_USE_BUNDLED_FREETYPE=ON</span> is an escape hatch<br />
if the prebuilt's config flags ever diverge from what RmlUi expects.</li>
<li><span style="font-weight: bold;">Texture upload</span> — <span style="font-style: italic;">LoadTexture</span> routes to <span style="font-style: italic;">Image.Import</span>, accepting<br />
every format Esenthel's asset pipeline supports (BMP/PNG/JPG/JXL/WEBP/<br />
AVIF/HEIF/TGA/TIF/DDS/PSD/ICO/HDR). <span style="font-style: italic;">GenerateTexture</span> uploads raw<br />
RGBA8 via a soft image, lock/write, row-copy, unlock, and GPU upload<br />
(used for the font atlas and procedural decorator textures).</li>
<li><span style="font-weight: bold;">Dual-mode input pump</span> — <span style="font-style: italic;">EE::RmlUi::PumpInput(ctx)</span> polls<br />
mouse position, mouse buttons, and wheel every frame, walks all<br />
256 <span style="font-style: italic;">KB_KEY</span> values firing key down/up events on edge changes, and<br />
pumps text characters out of <span style="font-style: italic;">Kb.k.c</span>. When the focused RmlUi element<br />
is an input field or textarea the key queue is drained so fast typing<br />
never drops characters; otherwise the head of the queue is peeked<br />
non-destructively so the built-in Gui still sees the same keys on the<br />
same frame.</li>
<li><span style="font-weight: bold;">Opt-in via CMake</span> — <span style="font-style: italic;">option(EE_RMLUI "... " ON)</span> in the root<br />
<span style="font-style: italic;">CMakeLists.txt</span>, default ON on Linux x64 + Windows x64, fatal on iOS<br />
/ Android (deferred until those platforms are tested). With<br />
<span style="font-style: italic;">EE_RMLUI=OFF</span> every integration TU is empty, the<br />
<span style="font-style: italic;">rmlui_core</span> / <span style="font-style: italic;">rmlui_debugger</span> targets are not added to<br />
the build graph, and the tutorial targets are skipped in<br />
<span style="font-style: italic;">Tutorials/CMakeLists.txt</span>.</li>
<li><span style="font-weight: bold;">Built from source</span> — RmlUi v6.2 is vendored at<br />
<span style="font-style: italic;">ThirdPartyLibs/RmlUi/lib/</span> and compiled via subdirectory inclusion so<br />
<span style="font-style: italic;">rmlui_core</span> + <span style="font-style: italic;">rmlui_debugger</span> become Engine interface link deps.<br />
No prebuilt library like the other third-parties, because upstream<br />
ships clean CMake and the library is small. ABI-visible flags are<br />
forced onto both targets so they match Engine's ABI.</li>
<li><span style="font-weight: bold;">Debugger overlay</span> — <span style="font-style: italic;">rmlui_debugger</span> is linked automatically when<br />
<span style="font-style: italic;">EE_RMLUI=ON</span>. Call <span style="font-style: italic;">Rml::Debugger::Initialise(ctx)</span> once, then<br />
<span style="font-style: italic;">Rml::Debugger::SetVisible(true)</span> (or bind to a key) to pop up the live<br />
inspector — Event Log, Element Info, Outlines, Data Models tabs.</li>
<li><span style="font-weight: bold;">Thin facade</span> — <span style="font-style: italic;">Engine/H/Gui/RmlUi.h</span> exposes only the<br />
<span style="font-style: italic;">EE::RmlUi::</span> lifecycle and helper functions. It forward-declares<br />
<span style="font-style: italic;">Rml::Context</span>, <span style="font-style: italic;">ElementDocument</span>, <span style="font-style: italic;">RenderInterface</span>, and<br />
<span style="font-style: italic;">SystemInterface</span>, so app translation units that just drive lifecycle<br />
do not pay to parse all of <span style="font-style: italic;">&amp;lt;RmlUi/Core.h&amp;gt;</span>. App translation units<br />
that want event listeners, DOM mutation, or typed element access can<br />
include <span style="font-style: italic;">&amp;lt;RmlUi/Core.h&amp;gt;</span> themselves.<br />
</li></ul>
<br />
<span style="font-weight: bold;">Non-invasive integration</span> — zero changes to <span style="font-style: italic;">Engine/Source/Gui/Gui.cpp</span>,<br />
<span style="font-style: italic;">Gui.draw</span>, <span style="font-style: italic;">Gui.update</span>, no new state in <span style="font-style: italic;">DisplayClass</span>, no new<br />
alpha mode, no new shaders. The pre-existing <span style="font-style: italic;">VI</span> + <span style="font-style: italic;">D</span> 2D path is the<br />
only rendering surface used; the existing 2D shaders are the only ones<br />
touched. The tutorial harness (<span style="font-style: italic;">Tutorials/TutorialAuto.{h,cpp}</span>,<br />
<span style="font-style: italic;">Tutorials/stdafx.h</span>) is unchanged. <span style="font-style: italic;">EE_RMLUI=OFF</span> builds are<br />
pixel-identical to pre-integration builds (verified against<br />
<span style="font-style: italic;">Tutorial_05_Bars</span>, <span style="font-style: italic;">Tutorial_04_BatchedDrawing</span>,<br />
<span style="font-style: italic;">Tutorial_12_RenderToTexture</span>).<br />
<br />
<span style="font-weight: bold;">RCSS gotcha</span> — RmlUi's transition/animation parser recognises tween<br />
names as defined in <span style="font-style: italic;">Source/Core/PropertyParserAnimation.cpp</span>. Unlike<br />
standard CSS, it does <span style="font-weight: bold;">not</span> accept bare <span style="font-style: italic;">linear</span> or the CSS keyword<br />
<span style="font-style: italic;">ease</span>; use <span style="font-style: italic;">linear-in-out</span> and <span style="font-style: italic;">cubic-in-out</span> (or any of the<br />
supported <span style="font-style: italic;">{back,bounce,circular,cubic,elastic,exponential,linear,quadratic,quartic,quintic&#8203;,sine}-{in,out,in-out}</span><br />
variants). Multi-transitions are comma-separated.<br />
<br />
<span style="font-weight: bold;">Deferred work:</span><br />
<ul>
<li>Custom <span style="font-style: italic;">Rml::FontEngineInterface</span> on top of Esenthel's <span style="font-style: italic;">Font</span> class, so<br />
<span style="font-style: italic;">.pak</span>-packaged fonts could feed RmlUi. Upstream's default FreeType<br />
engine is sufficient for now; TTF files ship alongside the tutorials.</li>
<li><span style="font-style: italic;">Rml::FileInterface</span> on <span style="font-style: italic;">EE::File</span> — would let RmlUi resolve<br />
<span style="font-style: italic;">.rml</span> / <span style="font-style: italic;">.rcss</span> / texture paths through engine <span style="font-style: italic;">.pak</span> archives. The<br />
stdio <span style="font-style: italic;">fopen</span> default is used currently; tutorials ship plain files<br />
under <span style="font-style: italic;">Tutorials/Data/RmlUi/</span>.</li>
<li>Data Binding (MVC layer via <span style="font-style: italic;">Rml::DataModelConstructor</span>) — supported by<br />
the linked <span style="font-style: italic;">rmlui_core</span> but not yet demonstrated by a tutorial.</li>
<li>Lua bindings (<span style="font-style: italic;">rmlui_lua</span>) — disabled in the wrapper (<span style="font-style: italic;">RMLUI_LUA_BINDINGS=OFF</span>);<br />
enabling would require wiring an Esenthel Lua runtime into the engine<br />
first.</li>
<li>Windows smoke sweep via <span style="font-style: italic;">run_smoke_test.ps1</span>. Linux is covered<br />
(all five tutorials + three regression targets pass).<br />
</li></ul>
<br />
Available in this repo:<br />
<a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></description>
			<content:encoded><![CDATA[Claude Code was able to one-shot RmlUi integration into Esenthel.<br />
<br />
I'm not a web-dev person, but with Vibe Designing coming soon<br />
( e.g. <a href="https://claude.ai/design" target="_blank">https://claude.ai/design</a> ) , I think there is value in having a UI<br />
system defined by a declarative language like HTML/CSS.<br />
<br />
This also makes it much easier for users to mod or skin the UI if they<br />
want. But really, it's about making it easier for the AI to do it.<br />
Claude Code really seems to struggle with building Esenthel GUIs<br />
programmatically that look good and have the correct font size, etc.,<br />
but with HTML/CSS this problem doesn't exist.<br />
<br />
I haven't profiled this, the implementation has not been optimized,<br />
and I'm sure there is a performance penalty. That said, RmlUi is<br />
highly regarded and is designed for games; it is not a full WebKit.<br />
<br />
An integration of <span style="font-weight: bold;"><a href="https://github.com/mikke89/RmlUi" target="_blank">RmlUi</a> v6.2</span> (MIT) as<br />
a retained-mode HTML/CSS UI library that <span style="font-weight: bold;">coexists</span> with Esenthel's built-in<br />
<span style="font-style: italic;">Gui</span> system without touching it. Both run in the same frame — the native<br />
widget set is still available for lightweight 3D-integrated controls, while<br />
RmlUi handles HTML/CSS/animation-heavy UI (menus, HUDs, settings screens,<br />
chat windows). A single RmlUi render backend lives inside the Engine static<br />
library and drives RmlUi's vertex stream through the engine's existing 2D<br />
batcher, so every renderer backend (DX11, DX12, Vulkan, OpenGL) gets RmlUi<br />
for free with zero backend-specific code.<br />
<br />
Game HUD tutorial<br />
<a href="https://pixeldrain.com/api/file/nWMwv7tq" target="_blank"><img src="https://pixeldrain.com/api/file/nWMwv7tq" width="800" height="339" border="0" alt="[Image: nWMwv7tq]" /></a><br />
<br />
<br />
Using Esenthel GUI elements and RmlUi elements side-by-side tutorial<br />
<a href="https://pixeldrain.com/api/file/3cB1Nk65" target="_blank"><img src="https://pixeldrain.com/api/file/3cB1Nk65" width="800" height="339" border="0" alt="[Image: 3cB1Nk65]" /></a><br />
<br />
<br />
<span style="font-weight: bold;">Quick start:</span><br />
<br />
<div class="codeblock">
<div class="title">Code:<br />
</div><div class="body" dir="ltr"><code>#include "Gui/RmlUi.h"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// EE::RmlUi::* facade<br />
<br />
// In a single .cpp where you author Rml event listeners, DOM manipulation,<br />
// etc. — wrap the RmlUi includes so Esenthel macros and X11 defines don't<br />
// collide with RmlUi's template parameters and enum names:<br />
#include "../../ThirdPartyLibs/begin.h"<br />
#undef T1&nbsp;&nbsp;#undef T2&nbsp;&nbsp;#undef T3&nbsp;&nbsp;#undef T4&nbsp;&nbsp;#undef T5<br />
#undef T6&nbsp;&nbsp;#undef T7&nbsp;&nbsp;#undef T8&nbsp;&nbsp;#undef T9&nbsp;&nbsp;#undef T10&nbsp;&nbsp;#undef T11<br />
#undef Always&nbsp;&nbsp;#undef NotUseful<br />
#include &lt;RmlUi/Core.h&gt;<br />
#include "../../ThirdPartyLibs/end.h"<br />
<br />
static Rml::Context&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *Ctx=null;<br />
static Rml::ElementDocument *Doc=null;<br />
<br />
Bool Init()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::Init();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // install render + system interfaces<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::LoadFontFace("Data/RmlUi/LatoLatin-Regular.ttf");<br />
&nbsp;&nbsp;&nbsp;&nbsp;Ctx=EE::RmlUi::CreateContext();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// sized to D.resW()/D.resH()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Doc=Ctx-&gt;LoadDocument("Data/RmlUi/demo.rml");<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(Doc)Doc-&gt;Show();<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
}<br />
void Shut()&nbsp;&nbsp; { EE::RmlUi::Shut(); }<br />
Bool Update() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;if(Kb.bp(KB_ESC))return false;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Gui.update();<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::PumpInput(Ctx);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Ms/Kb -&gt; context<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::Update&nbsp;&nbsp; (Ctx);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // events, animations, dirty geometry<br />
&nbsp;&nbsp;&nbsp;&nbsp;return true;<br />
}<br />
void Draw()&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;Renderer(RenderScene);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // optional 3D world<br />
&nbsp;&nbsp;&nbsp;&nbsp;Gui.draw();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Esenthel Gui above 3D<br />
&nbsp;&nbsp;&nbsp;&nbsp;EE::RmlUi::Render(Ctx);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// RmlUi on top of everything<br />
}</code></div></div>
<br />
<span style="font-weight: bold;">Shipped features:</span><br />
<ul>
<li><span style="font-weight: bold;">Single-TU backend</span> — <span style="font-style: italic;">Engine/Source/Gui/RmlUi.cpp</span> is the only engine<br />
source file that includes RmlUi headers. It subclasses<br />
<span style="font-style: italic;">Rml::RenderInterface</span> and <span style="font-style: italic;">Rml::SystemInterface</span>, owns the 1×1 white<br />
fallback texture, and exports the <span style="font-style: italic;">EE::RmlUi::</span> facade used by app code.<br />
Macro pollution is neutralised locally: Esenthel's template-shortcut<br />
macros <span style="font-style: italic;">T1..T11</span> (from <span style="font-style: italic;">Engine/H/_/defines.h</span>, they collide with<br />
<span style="font-style: italic;">robin_hood.h</span>'s template parameters) and X11's <span style="font-style: italic;">Always</span> / <span style="font-style: italic;">NotUseful</span><br />
(not stripped by <span style="font-style: italic;">Engine/H/_/headers.h</span>) are undefined inside the<br />
<span style="font-style: italic;">ThirdPartyLibs/begin.h</span> / <span style="font-style: italic;">end.h</span> bracket just for this one TU.</li>
<li><span style="font-weight: bold;">API-agnostic rendering</span> — <span style="font-style: italic;">RenderGeometry</span> feeds RmlUi's pixel-space<br />
vertex stream into the engine's existing 2D path. Per-vertex<br />
conversion goes through <span style="font-style: italic;">D.pixelToScreen(Vec2)</span> (canonical helper; no<br />
hand-rolled Y-flip). <span style="font-style: italic;">D.alpha(ALPHA_MERGE)</span> handles RmlUi's<br />
premultiplied RGBA (which it emits since v5). Scissor goes through<br />
<span style="font-style: italic;">D.clip(&amp;engine_rect)</span> / <span style="font-style: italic;">D.pixelToScreen(RectI)</span>. Works identically on<br />
DX11, DX12, Vulkan, OpenGL.</li>
<li><span style="font-weight: bold;">Untextured-path fallback</span> — RmlUi calls <span style="font-style: italic;">RenderGeometry</span> with<br />
<span style="font-style: italic;">texture=0</span> for solid-colour geometry. The backend binds a pre-built<br />
1×1 opaque-white <span style="font-style: italic;">Image</span> so the textured shader path effectively<br />
degenerates to plain vertex colour. This avoids needing a separate<br />
colour-only code path.</li>
<li><span style="font-weight: bold;">FreeType sharing</span> — RmlUi's default FreeType font engine is pointed<br />
at Esenthel's prebuilt <span style="font-style: italic;">EE_FreeType</span> static lib via<br />
<span style="font-style: italic;">FREETYPE_INCLUDE_DIRS</span> / <span style="font-style: italic;">FREETYPE_LIBRARY</span> cache vars in<br />
<span style="font-style: italic;">ThirdPartyLibs/RmlUi/CMakeLists.txt</span>, so one copy of FreeType ships in<br />
the final link. <span style="font-style: italic;">EE_RMLUI_USE_BUNDLED_FREETYPE=ON</span> is an escape hatch<br />
if the prebuilt's config flags ever diverge from what RmlUi expects.</li>
<li><span style="font-weight: bold;">Texture upload</span> — <span style="font-style: italic;">LoadTexture</span> routes to <span style="font-style: italic;">Image.Import</span>, accepting<br />
every format Esenthel's asset pipeline supports (BMP/PNG/JPG/JXL/WEBP/<br />
AVIF/HEIF/TGA/TIF/DDS/PSD/ICO/HDR). <span style="font-style: italic;">GenerateTexture</span> uploads raw<br />
RGBA8 via a soft image, lock/write, row-copy, unlock, and GPU upload<br />
(used for the font atlas and procedural decorator textures).</li>
<li><span style="font-weight: bold;">Dual-mode input pump</span> — <span style="font-style: italic;">EE::RmlUi::PumpInput(ctx)</span> polls<br />
mouse position, mouse buttons, and wheel every frame, walks all<br />
256 <span style="font-style: italic;">KB_KEY</span> values firing key down/up events on edge changes, and<br />
pumps text characters out of <span style="font-style: italic;">Kb.k.c</span>. When the focused RmlUi element<br />
is an input field or textarea the key queue is drained so fast typing<br />
never drops characters; otherwise the head of the queue is peeked<br />
non-destructively so the built-in Gui still sees the same keys on the<br />
same frame.</li>
<li><span style="font-weight: bold;">Opt-in via CMake</span> — <span style="font-style: italic;">option(EE_RMLUI "... " ON)</span> in the root<br />
<span style="font-style: italic;">CMakeLists.txt</span>, default ON on Linux x64 + Windows x64, fatal on iOS<br />
/ Android (deferred until those platforms are tested). With<br />
<span style="font-style: italic;">EE_RMLUI=OFF</span> every integration TU is empty, the<br />
<span style="font-style: italic;">rmlui_core</span> / <span style="font-style: italic;">rmlui_debugger</span> targets are not added to<br />
the build graph, and the tutorial targets are skipped in<br />
<span style="font-style: italic;">Tutorials/CMakeLists.txt</span>.</li>
<li><span style="font-weight: bold;">Built from source</span> — RmlUi v6.2 is vendored at<br />
<span style="font-style: italic;">ThirdPartyLibs/RmlUi/lib/</span> and compiled via subdirectory inclusion so<br />
<span style="font-style: italic;">rmlui_core</span> + <span style="font-style: italic;">rmlui_debugger</span> become Engine interface link deps.<br />
No prebuilt library like the other third-parties, because upstream<br />
ships clean CMake and the library is small. ABI-visible flags are<br />
forced onto both targets so they match Engine's ABI.</li>
<li><span style="font-weight: bold;">Debugger overlay</span> — <span style="font-style: italic;">rmlui_debugger</span> is linked automatically when<br />
<span style="font-style: italic;">EE_RMLUI=ON</span>. Call <span style="font-style: italic;">Rml::Debugger::Initialise(ctx)</span> once, then<br />
<span style="font-style: italic;">Rml::Debugger::SetVisible(true)</span> (or bind to a key) to pop up the live<br />
inspector — Event Log, Element Info, Outlines, Data Models tabs.</li>
<li><span style="font-weight: bold;">Thin facade</span> — <span style="font-style: italic;">Engine/H/Gui/RmlUi.h</span> exposes only the<br />
<span style="font-style: italic;">EE::RmlUi::</span> lifecycle and helper functions. It forward-declares<br />
<span style="font-style: italic;">Rml::Context</span>, <span style="font-style: italic;">ElementDocument</span>, <span style="font-style: italic;">RenderInterface</span>, and<br />
<span style="font-style: italic;">SystemInterface</span>, so app translation units that just drive lifecycle<br />
do not pay to parse all of <span style="font-style: italic;">&amp;lt;RmlUi/Core.h&amp;gt;</span>. App translation units<br />
that want event listeners, DOM mutation, or typed element access can<br />
include <span style="font-style: italic;">&amp;lt;RmlUi/Core.h&amp;gt;</span> themselves.<br />
</li></ul>
<br />
<span style="font-weight: bold;">Non-invasive integration</span> — zero changes to <span style="font-style: italic;">Engine/Source/Gui/Gui.cpp</span>,<br />
<span style="font-style: italic;">Gui.draw</span>, <span style="font-style: italic;">Gui.update</span>, no new state in <span style="font-style: italic;">DisplayClass</span>, no new<br />
alpha mode, no new shaders. The pre-existing <span style="font-style: italic;">VI</span> + <span style="font-style: italic;">D</span> 2D path is the<br />
only rendering surface used; the existing 2D shaders are the only ones<br />
touched. The tutorial harness (<span style="font-style: italic;">Tutorials/TutorialAuto.{h,cpp}</span>,<br />
<span style="font-style: italic;">Tutorials/stdafx.h</span>) is unchanged. <span style="font-style: italic;">EE_RMLUI=OFF</span> builds are<br />
pixel-identical to pre-integration builds (verified against<br />
<span style="font-style: italic;">Tutorial_05_Bars</span>, <span style="font-style: italic;">Tutorial_04_BatchedDrawing</span>,<br />
<span style="font-style: italic;">Tutorial_12_RenderToTexture</span>).<br />
<br />
<span style="font-weight: bold;">RCSS gotcha</span> — RmlUi's transition/animation parser recognises tween<br />
names as defined in <span style="font-style: italic;">Source/Core/PropertyParserAnimation.cpp</span>. Unlike<br />
standard CSS, it does <span style="font-weight: bold;">not</span> accept bare <span style="font-style: italic;">linear</span> or the CSS keyword<br />
<span style="font-style: italic;">ease</span>; use <span style="font-style: italic;">linear-in-out</span> and <span style="font-style: italic;">cubic-in-out</span> (or any of the<br />
supported <span style="font-style: italic;">{back,bounce,circular,cubic,elastic,exponential,linear,quadratic,quartic,quintic&#8203;,sine}-{in,out,in-out}</span><br />
variants). Multi-transitions are comma-separated.<br />
<br />
<span style="font-weight: bold;">Deferred work:</span><br />
<ul>
<li>Custom <span style="font-style: italic;">Rml::FontEngineInterface</span> on top of Esenthel's <span style="font-style: italic;">Font</span> class, so<br />
<span style="font-style: italic;">.pak</span>-packaged fonts could feed RmlUi. Upstream's default FreeType<br />
engine is sufficient for now; TTF files ship alongside the tutorials.</li>
<li><span style="font-style: italic;">Rml::FileInterface</span> on <span style="font-style: italic;">EE::File</span> — would let RmlUi resolve<br />
<span style="font-style: italic;">.rml</span> / <span style="font-style: italic;">.rcss</span> / texture paths through engine <span style="font-style: italic;">.pak</span> archives. The<br />
stdio <span style="font-style: italic;">fopen</span> default is used currently; tutorials ship plain files<br />
under <span style="font-style: italic;">Tutorials/Data/RmlUi/</span>.</li>
<li>Data Binding (MVC layer via <span style="font-style: italic;">Rml::DataModelConstructor</span>) — supported by<br />
the linked <span style="font-style: italic;">rmlui_core</span> but not yet demonstrated by a tutorial.</li>
<li>Lua bindings (<span style="font-style: italic;">rmlui_lua</span>) — disabled in the wrapper (<span style="font-style: italic;">RMLUI_LUA_BINDINGS=OFF</span>);<br />
enabling would require wiring an Esenthel Lua runtime into the engine<br />
first.</li>
<li>Windows smoke sweep via <span style="font-style: italic;">run_smoke_test.ps1</span>. Linux is covered<br />
(all five tutorials + three regression targets pass).<br />
</li></ul>
<br />
Available in this repo:<br />
<a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Improved Destruction]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11814</link>
			<pubDate>Mon, 20 Apr 2026 02:21:50 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11814</guid>
			<description><![CDATA[<span style="font-weight: bold;">Fracture Destruction System</span> (improve-destruction-v3 branch)<br />
<br />
A production-grade destructible-object system located at Engine/Source/Physics/Fracture*.cpp plus Engine/Source/Game/Objects/Fractured.cpp.<br />
<br />
The existing DestructMesh / Game::Destructible implementation from Tutorial 23 is left untouched. The new system ships alongside it under new type names.<br />
<br />
<span style="font-weight: bold;">Core idea:</span><br />
<br />
A <span style="font-weight: bold;">FractureMesh asset</span> pre-bakes a mesh into chunks, a cluster hierarchy, and a connection graph that records which chunks share faces, with per-edge breaking strain.<br />
<br />
A <span style="font-weight: bold;">Game::Fractured runtime object</span> owns the per-instance state. At runtime, it exists as either a single intact-cluster Actor (cluster proxy) or an array of per-chunk Actors connected by breakable PhysX joints.<br />
<br />
Damage accumulates per node, or on the cluster as a whole. Once the threshold is crossed, the object transitions from <span style="font-weight: bold;">INTACT</span> to <span style="font-weight: bold;">SHATTERED</span> by destroying the proxy and spawning per-chunk actors.<br />
<br />
Mass is force-set from bake-time chunk volume so the convex-hull proxy’s mass overestimate does not cause a visible pop at the moment of breakup.<br />
<br />
Strain then propagates along the connection graph, so a hit on one chunk weakens its neighbours. This reproduces “spider cracks” style breakage without requiring a full spatial-field solver.<br />
<br />
<a href="https://postimg.cc/34G95frD" target="_blank"><img src="https://i.postimg.cc/j5g1fBGv/Screenshot-from-2026-04-19-22-14-24.png" border="0" alt="[Image: Screenshot-from-2026-04-19-22-14-24.png]" /></a><br />
<br />
<a href="https://postimg.cc/SnZMZL4n" target="_blank"><img src="https://i.postimg.cc/Jh85zKd5/Screenshot-from-2026-04-19-21-51-13.png" border="0" alt="[Image: Screenshot-from-2026-04-19-21-51-13.png]" /></a><br />
<br />
<span style="font-weight: bold;">Shipped features:</span><br />
<ul>
[]<span style="font-weight: bold;">Five bake patterns</span> (FRACTURE_PATTERN_) —<br />
PLANAR_SLICES uses random plane cuts and mirrors the classic DestructMesh algorithm.<br />
UNIFORM_VORONOI and CLUSTERED_VORONOI use iterative SplitMeshSolid with perpendicular-bisector planes; the older CSG-based spike was dropped because it was numerically unstable.<br />
RADIAL produces true pie-slice wedges radiating from an impact_point through the wall’s thickness axis.<br />
BRICK uses a dedicated axis-aligned 6-plane-per-cell path (BakeBrickChunks) with <span style="font-weight: bold;">shared-boundary jitter</span>: adjacent bricks read the same boundary entry for their shared seam, so jitter never produces gaps between neighbours.</li>
<li><span style="font-weight: bold;">Cluster proxy</span> (FractureSettings::build_cluster_proxy) — the bake computes a single convex hull over all chunks and stores it on the root Cluster, plus the sum of per-chunk volumes as mass_sum. At spawn, the Fractured object creates one kinematic Actor using this proxy instead of N per-chunk actors. 500 intact crates cost 500 bodies, not 10,000.</li>
<li><span style="font-weight: bold;">Localised release cascade</span> — when damage crosses cluster_break_threshold, Fractured::releaseRootCluster(impact_pos, velocity_delta) destroys the proxy and spawns per-chunk actors, inheriting the proxy’s linear and angular velocity and force-setting each chunk’s mass to the bake-time volume estimate. A <span style="font-weight: bold;">distance-falloff</span> kick is then applied only to chunks within 1.5 m of the impact (mass-scaled, velocity-capped at 4 m/s), so a hit near the bottom of a wall does not catapult distant chunks and make anchored sections rubber-band through joints.</li>
<li><span style="font-weight: bold;">Deferred release</span> — applyPointDamage is called from inside PhysX’s onContact callback; destroying and creating actors there corrupts the scene and crashes the next PxRigidActor::is&lt;&gt;() cast. Instead, threshold crossing sets a pending flag plus the impact position and velocity, and the next update() tick flushes the release outside any callback.</li>
<li><span style="font-weight: bold;">Connection-graph strain propagation</span> — Fractured::propagateStrain walks live_edges each frame when damage_dirty. Damage above the weakest-link elastic limit (0.9 * break_threshold) bleeds across edges; smaller hits dissipate via damage_decay_tau without creeping. Broken joints are removed by BFS; any connected component that does not reach an anchor becomes dynamic.</li>
<li><span style="font-weight: bold;">Bake-time anchors</span> — FractureSettings::anchor_volume marks chunks whose centroid is inside the box with FRAC_FLAG_ANCHORED_AT_BAKE. They spawn kinematic, or the cluster proxy spawns kinematic if any member is anchored. Runtime Fracture::setAnchor(obj, chunk_index, anchored) can flip the anchor state live.</li>
<li><span style="font-weight: bold;">Damage API</span> —<br />
Fracture::applyRadialDamage(pos, radius, strength, impulse, type)<br />
applyContactDamage(a, b, contacts, n, type)<br />
breakNode(obj, node_index, dir, type)<br />
setAnchor(...)<br />
<br />
All accept a DAMAGE_TYPE enum (KINETIC, EXPLOSIVE, PIERCING, THERMAL, CUSTOM) scaled by a global damage_type_scale[5] table, so game code can do things like make grenades 1.5x stronger against concrete with a single tweak.</li>
<li><span style="font-weight: bold;">Multi-subscriber BreakEventCallback</span> — Fracture::addBreakEventCallback is void-return and never consumes the event. addBreakEventCallbackB is bool-return; returning true consumes the event and stops the chain. Legacy setBreakEventCallback is preserved as a single-slot wrapper.</li>
<li><span style="font-weight: bold;">Debris pool</span> — Fracture::setVfxPoolCap, setSfxVoicesCap, debrisActiveCount, plus low-energy sleep and debris-pool caps in updateDebrisHeuristics. Chunks at rest for debris_sleep_timeout seconds are put to sleep so PhysX stops simulating them.</li>
<li><span style="font-weight: bold;">Save / Load</span> — FractureMesh::save(path) and load(path) round-trip the whole asset (chunks, cooked PhysX convex hulls, cluster, and connection graph) through a versioned CC4('F','R','A','C') binary format.<br />
FractureMesh::adjustStorage(universal, physx, bullet) matches the PhysBody contract and allows the asset to be reloaded on a different backend. A cache (Cache&lt;FractureMesh&gt; FractureMeshes("Fracture Mesh")) is preserved so UID-addressed baked assets can live inside pak files like any other engine asset.<br />
<br />
[]<span style="font-weight: bold;">Instance sharing</span> — many Game::Fractured instances can point at the same FractureMesh. Geometry and cooked hulls are shared; only runtime state (damage, actor transforms, live joints) is per-instance.<br />
</li></ul>
<br />
<span style="font-weight: bold;">Non-invasive integration:</span><br />
<br />
Zero changes to DestructMesh, Game::Destructible, or Tutorial 23.<br />
<br />
PhysX’s Physics.reportContact is <span style="font-weight: bold;">not</span> hijacked. The host app registers its own contact callback and can optionally call Fracture::applyContactDamage from inside it.<br />
<br />
Contact damage is PhysX-only, since Bullet does not deliver contact events in this engine. Radial damage works on both backends.<br />
<br />
<span style="font-weight: bold;">Deferred work:</span><br />
<ul>
<li>Multi-level hierarchical fracture (more than 2 levels deep, with per-level break thresholds and cluster-tree view-level LOD). The data model already supports it via Cluster::parent and Cluster::children; the runtime currently walks one level.</li>
<li>Runtime on-the-fly fracture (CSG against an unbaked mesh). Pre-bake only is the shipping path.</li>
<li>View-distance simulation LOD (far objects deferred until the player enters a bubble). The cluster proxy is the foundation; bubble activation logic is planned as a later follow-up.</li>
<li>Multi-platform cooked-hull round-trip. Saved files are currently PhysX-specific; cross-platform support needs adjustStorage(universal=true, ...) before save.</li>
<li>Titan Editor UI for anchor-volume painting, per-chunk material picking, and interactive fracture preview. This branch is intentionally code-only.</li>
<li>Auto-dispatch BreakPreset VFX/SFX from a material tag, so Tutorial 31’s 40-line OnBreak puff could collapse to 5 lines. The scaffolding is already in place (multi-subscriber callbacks, damage types), but the preset registry is deferred.<br />
</li></ul>
<br />
in this fork:<br />
<a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></description>
			<content:encoded><![CDATA[<span style="font-weight: bold;">Fracture Destruction System</span> (improve-destruction-v3 branch)<br />
<br />
A production-grade destructible-object system located at Engine/Source/Physics/Fracture*.cpp plus Engine/Source/Game/Objects/Fractured.cpp.<br />
<br />
The existing DestructMesh / Game::Destructible implementation from Tutorial 23 is left untouched. The new system ships alongside it under new type names.<br />
<br />
<span style="font-weight: bold;">Core idea:</span><br />
<br />
A <span style="font-weight: bold;">FractureMesh asset</span> pre-bakes a mesh into chunks, a cluster hierarchy, and a connection graph that records which chunks share faces, with per-edge breaking strain.<br />
<br />
A <span style="font-weight: bold;">Game::Fractured runtime object</span> owns the per-instance state. At runtime, it exists as either a single intact-cluster Actor (cluster proxy) or an array of per-chunk Actors connected by breakable PhysX joints.<br />
<br />
Damage accumulates per node, or on the cluster as a whole. Once the threshold is crossed, the object transitions from <span style="font-weight: bold;">INTACT</span> to <span style="font-weight: bold;">SHATTERED</span> by destroying the proxy and spawning per-chunk actors.<br />
<br />
Mass is force-set from bake-time chunk volume so the convex-hull proxy’s mass overestimate does not cause a visible pop at the moment of breakup.<br />
<br />
Strain then propagates along the connection graph, so a hit on one chunk weakens its neighbours. This reproduces “spider cracks” style breakage without requiring a full spatial-field solver.<br />
<br />
<a href="https://postimg.cc/34G95frD" target="_blank"><img src="https://i.postimg.cc/j5g1fBGv/Screenshot-from-2026-04-19-22-14-24.png" border="0" alt="[Image: Screenshot-from-2026-04-19-22-14-24.png]" /></a><br />
<br />
<a href="https://postimg.cc/SnZMZL4n" target="_blank"><img src="https://i.postimg.cc/Jh85zKd5/Screenshot-from-2026-04-19-21-51-13.png" border="0" alt="[Image: Screenshot-from-2026-04-19-21-51-13.png]" /></a><br />
<br />
<span style="font-weight: bold;">Shipped features:</span><br />
<ul>
[]<span style="font-weight: bold;">Five bake patterns</span> (FRACTURE_PATTERN_) —<br />
PLANAR_SLICES uses random plane cuts and mirrors the classic DestructMesh algorithm.<br />
UNIFORM_VORONOI and CLUSTERED_VORONOI use iterative SplitMeshSolid with perpendicular-bisector planes; the older CSG-based spike was dropped because it was numerically unstable.<br />
RADIAL produces true pie-slice wedges radiating from an impact_point through the wall’s thickness axis.<br />
BRICK uses a dedicated axis-aligned 6-plane-per-cell path (BakeBrickChunks) with <span style="font-weight: bold;">shared-boundary jitter</span>: adjacent bricks read the same boundary entry for their shared seam, so jitter never produces gaps between neighbours.</li>
<li><span style="font-weight: bold;">Cluster proxy</span> (FractureSettings::build_cluster_proxy) — the bake computes a single convex hull over all chunks and stores it on the root Cluster, plus the sum of per-chunk volumes as mass_sum. At spawn, the Fractured object creates one kinematic Actor using this proxy instead of N per-chunk actors. 500 intact crates cost 500 bodies, not 10,000.</li>
<li><span style="font-weight: bold;">Localised release cascade</span> — when damage crosses cluster_break_threshold, Fractured::releaseRootCluster(impact_pos, velocity_delta) destroys the proxy and spawns per-chunk actors, inheriting the proxy’s linear and angular velocity and force-setting each chunk’s mass to the bake-time volume estimate. A <span style="font-weight: bold;">distance-falloff</span> kick is then applied only to chunks within 1.5 m of the impact (mass-scaled, velocity-capped at 4 m/s), so a hit near the bottom of a wall does not catapult distant chunks and make anchored sections rubber-band through joints.</li>
<li><span style="font-weight: bold;">Deferred release</span> — applyPointDamage is called from inside PhysX’s onContact callback; destroying and creating actors there corrupts the scene and crashes the next PxRigidActor::is&lt;&gt;() cast. Instead, threshold crossing sets a pending flag plus the impact position and velocity, and the next update() tick flushes the release outside any callback.</li>
<li><span style="font-weight: bold;">Connection-graph strain propagation</span> — Fractured::propagateStrain walks live_edges each frame when damage_dirty. Damage above the weakest-link elastic limit (0.9 * break_threshold) bleeds across edges; smaller hits dissipate via damage_decay_tau without creeping. Broken joints are removed by BFS; any connected component that does not reach an anchor becomes dynamic.</li>
<li><span style="font-weight: bold;">Bake-time anchors</span> — FractureSettings::anchor_volume marks chunks whose centroid is inside the box with FRAC_FLAG_ANCHORED_AT_BAKE. They spawn kinematic, or the cluster proxy spawns kinematic if any member is anchored. Runtime Fracture::setAnchor(obj, chunk_index, anchored) can flip the anchor state live.</li>
<li><span style="font-weight: bold;">Damage API</span> —<br />
Fracture::applyRadialDamage(pos, radius, strength, impulse, type)<br />
applyContactDamage(a, b, contacts, n, type)<br />
breakNode(obj, node_index, dir, type)<br />
setAnchor(...)<br />
<br />
All accept a DAMAGE_TYPE enum (KINETIC, EXPLOSIVE, PIERCING, THERMAL, CUSTOM) scaled by a global damage_type_scale[5] table, so game code can do things like make grenades 1.5x stronger against concrete with a single tweak.</li>
<li><span style="font-weight: bold;">Multi-subscriber BreakEventCallback</span> — Fracture::addBreakEventCallback is void-return and never consumes the event. addBreakEventCallbackB is bool-return; returning true consumes the event and stops the chain. Legacy setBreakEventCallback is preserved as a single-slot wrapper.</li>
<li><span style="font-weight: bold;">Debris pool</span> — Fracture::setVfxPoolCap, setSfxVoicesCap, debrisActiveCount, plus low-energy sleep and debris-pool caps in updateDebrisHeuristics. Chunks at rest for debris_sleep_timeout seconds are put to sleep so PhysX stops simulating them.</li>
<li><span style="font-weight: bold;">Save / Load</span> — FractureMesh::save(path) and load(path) round-trip the whole asset (chunks, cooked PhysX convex hulls, cluster, and connection graph) through a versioned CC4('F','R','A','C') binary format.<br />
FractureMesh::adjustStorage(universal, physx, bullet) matches the PhysBody contract and allows the asset to be reloaded on a different backend. A cache (Cache&lt;FractureMesh&gt; FractureMeshes("Fracture Mesh")) is preserved so UID-addressed baked assets can live inside pak files like any other engine asset.<br />
<br />
[]<span style="font-weight: bold;">Instance sharing</span> — many Game::Fractured instances can point at the same FractureMesh. Geometry and cooked hulls are shared; only runtime state (damage, actor transforms, live joints) is per-instance.<br />
</li></ul>
<br />
<span style="font-weight: bold;">Non-invasive integration:</span><br />
<br />
Zero changes to DestructMesh, Game::Destructible, or Tutorial 23.<br />
<br />
PhysX’s Physics.reportContact is <span style="font-weight: bold;">not</span> hijacked. The host app registers its own contact callback and can optionally call Fracture::applyContactDamage from inside it.<br />
<br />
Contact damage is PhysX-only, since Bullet does not deliver contact events in this engine. Radial damage works on both backends.<br />
<br />
<span style="font-weight: bold;">Deferred work:</span><br />
<ul>
<li>Multi-level hierarchical fracture (more than 2 levels deep, with per-level break thresholds and cluster-tree view-level LOD). The data model already supports it via Cluster::parent and Cluster::children; the runtime currently walks one level.</li>
<li>Runtime on-the-fly fracture (CSG against an unbaked mesh). Pre-bake only is the shipping path.</li>
<li>View-distance simulation LOD (far objects deferred until the player enters a bubble). The cluster proxy is the foundation; bubble activation logic is planned as a later follow-up.</li>
<li>Multi-platform cooked-hull round-trip. Saved files are currently PhysX-specific; cross-platform support needs adjustStorage(universal=true, ...) before save.</li>
<li>Titan Editor UI for anchor-volume painting, per-chunk material picking, and interactive fracture preview. This branch is intentionally code-only.</li>
<li>Auto-dispatch BreakPreset VFX/SFX from a material tag, so Tutorial 31’s 40-line OnBreak puff could collapse to 5 lines. The scaffolding is already in place (multi-subscriber callbacks, damage types), but the preset registry is deferred.<br />
</li></ul>
<br />
in this fork:<br />
<a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Cloth Physics]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11813</link>
			<pubDate>Sat, 18 Apr 2026 12:56:10 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11813</guid>
			<description><![CDATA[Cloth Physics (XPBD)<br />
A first-party CPU cloth solver built into the engine at Engine/Source/Physics/Cloth.{h,cpp} + Engine/Source/Physics/Cloth Solver.{h,cpp}, sitting behind the existing Cloth / ClothMesh public API. Replaces NVIDIA's deprecated PhysX 3.x cloth (removed in PhysX 4+) with an XPBD (Extended Position-Based Dynamics) implementation — stable, deterministic, tunable, and owned entirely by the engine.<br />
<br />
Core idea — each cloth is a set of mass-weighted particles connected by constraints. Per simulate(dt) the solver substeps (default 8) Jakobsen-style: semi-implicit-Euler predict → XPBD Gauss-Seidel constraint solve (Δλ = (-C - α̃λ)/(w_i+w_j+α̃), α̃ = compliance/dt²) → collision projection against author-placed balls/capsules → derive velocity from position delta. Pinned particles (inv_mass == 0) follow skeleton bones each sub-step via smooth prev↔cur lerp so fast character motion doesn't yank the cape.<br />
<br />
<a href="https://postimg.cc/RW665C3M" target="_blank"><img src="https://i.postimg.cc/GmzkDHds/Screenshot-from-2026-04-18-08-13-11.png" border="0" alt="[Image: Screenshot-from-2026-04-18-08-13-11.png]" /></a><br />
<br />
<a href="https://postimg.cc/1fYnjVzg" target="_blank"><img src="https://i.postimg.cc/x19vqKDP/Screenshot-from-2026-04-18-08-15-29.png" border="0" alt="[Image: Screenshot-from-2026-04-18-08-15-29.png]" /></a><br />
<br />
In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
See README.md for more info.]]></description>
			<content:encoded><![CDATA[Cloth Physics (XPBD)<br />
A first-party CPU cloth solver built into the engine at Engine/Source/Physics/Cloth.{h,cpp} + Engine/Source/Physics/Cloth Solver.{h,cpp}, sitting behind the existing Cloth / ClothMesh public API. Replaces NVIDIA's deprecated PhysX 3.x cloth (removed in PhysX 4+) with an XPBD (Extended Position-Based Dynamics) implementation — stable, deterministic, tunable, and owned entirely by the engine.<br />
<br />
Core idea — each cloth is a set of mass-weighted particles connected by constraints. Per simulate(dt) the solver substeps (default 8) Jakobsen-style: semi-implicit-Euler predict → XPBD Gauss-Seidel constraint solve (Δλ = (-C - α̃λ)/(w_i+w_j+α̃), α̃ = compliance/dt²) → collision projection against author-placed balls/capsules → derive velocity from position delta. Pinned particles (inv_mass == 0) follow skeleton bones each sub-step via smooth prev↔cur lerp so fast character motion doesn't yank the cape.<br />
<br />
<a href="https://postimg.cc/RW665C3M" target="_blank"><img src="https://i.postimg.cc/GmzkDHds/Screenshot-from-2026-04-18-08-13-11.png" border="0" alt="[Image: Screenshot-from-2026-04-18-08-13-11.png]" /></a><br />
<br />
<a href="https://postimg.cc/1fYnjVzg" target="_blank"><img src="https://i.postimg.cc/x19vqKDP/Screenshot-from-2026-04-18-08-15-29.png" border="0" alt="[Image: Screenshot-from-2026-04-18-08-15-29.png]" /></a><br />
<br />
In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
See README.md for more info.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Behavior Trees, Animation Graph, IK]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11812</link>
			<pubDate>Sat, 18 Apr 2026 00:06:09 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11812</guid>
			<description><![CDATA[Behavior Trees (Bt*, animation-state-machine branch)<br />
A data-oriented behavior tree framework for AI decision-making, built into the engine at Engine/H/Game/AI/BehaviorTree.h + Engine/Source/Game/AI/BehaviorTree.cpp. Designed for attachment to Game::Chr subclasses: build a tree of BtNode objects once in Chr::create(), call bt.tick(Time.d()) from Chr::update() each frame.<br />
<br />
<a href="https://postimg.cc/ThH741M7" target="_blank"><img src="https://i.postimg.cc/TYTzh5k6/Screenshot-from-2026-04-17-19-48-04.png" border="0" alt="[Image: Screenshot-from-2026-04-17-19-48-04.png]" /></a><br />
<br />
<br />
Animation Graph (Ag*) and IK (animation-state-machine branch)<br />
A production-grade composable pose-graph animation system, built into the engine at Engine/H/Animation/AnimGraph.h + Engine/Source/Animation/AnimGraph.cpp. Sits on top of the existing AnimatedSkeleton::animate() primitives without modifying them, stylistically parallels the BehaviorTree framework on the same branch (Bt* prefix, namespace Game{}, non-virtual update() wrapper + protected doUpdate() dispatch, tree-walking virtuals for viz).<br />
<br />
<a href="https://postimg.cc/JthYxJgf" target="_blank"><img src="https://i.postimg.cc/QNgLXJqd/Screenshot-from-2026-04-17-19-49-22.png" border="0" alt="[Image: Screenshot-from-2026-04-17-19-49-22.png]" /></a><br />
<br />
IK On:<br />
<a href="https://postimg.cc/4n32Ptqk" target="_blank"><img src="https://i.postimg.cc/xjGr03PC/Screenshot-from-2026-04-17-16-02-10.png" border="0" alt="[Image: Screenshot-from-2026-04-17-16-02-10.png]" /></a><br />
<br />
IK Off:<br />
<a href="https://postimg.cc/XXv1LFmM" target="_blank"><img src="https://i.postimg.cc/y6hqVyTN/Screenshot-from-2026-04-17-16-02-24.png" border="0" alt="[Image: Screenshot-from-2026-04-17-16-02-24.png]" /></a><br />
<br />
In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
See the README.md for more info.]]></description>
			<content:encoded><![CDATA[Behavior Trees (Bt*, animation-state-machine branch)<br />
A data-oriented behavior tree framework for AI decision-making, built into the engine at Engine/H/Game/AI/BehaviorTree.h + Engine/Source/Game/AI/BehaviorTree.cpp. Designed for attachment to Game::Chr subclasses: build a tree of BtNode objects once in Chr::create(), call bt.tick(Time.d()) from Chr::update() each frame.<br />
<br />
<a href="https://postimg.cc/ThH741M7" target="_blank"><img src="https://i.postimg.cc/TYTzh5k6/Screenshot-from-2026-04-17-19-48-04.png" border="0" alt="[Image: Screenshot-from-2026-04-17-19-48-04.png]" /></a><br />
<br />
<br />
Animation Graph (Ag*) and IK (animation-state-machine branch)<br />
A production-grade composable pose-graph animation system, built into the engine at Engine/H/Animation/AnimGraph.h + Engine/Source/Animation/AnimGraph.cpp. Sits on top of the existing AnimatedSkeleton::animate() primitives without modifying them, stylistically parallels the BehaviorTree framework on the same branch (Bt* prefix, namespace Game{}, non-virtual update() wrapper + protected doUpdate() dispatch, tree-walking virtuals for viz).<br />
<br />
<a href="https://postimg.cc/JthYxJgf" target="_blank"><img src="https://i.postimg.cc/QNgLXJqd/Screenshot-from-2026-04-17-19-49-22.png" border="0" alt="[Image: Screenshot-from-2026-04-17-19-49-22.png]" /></a><br />
<br />
IK On:<br />
<a href="https://postimg.cc/4n32Ptqk" target="_blank"><img src="https://i.postimg.cc/xjGr03PC/Screenshot-from-2026-04-17-16-02-10.png" border="0" alt="[Image: Screenshot-from-2026-04-17-16-02-10.png]" /></a><br />
<br />
IK Off:<br />
<a href="https://postimg.cc/XXv1LFmM" target="_blank"><img src="https://i.postimg.cc/y6hqVyTN/Screenshot-from-2026-04-17-16-02-24.png" border="0" alt="[Image: Screenshot-from-2026-04-17-16-02-24.png]" /></a><br />
<br />
In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
See the README.md for more info.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Frame profiler and Input Action System]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11811</link>
			<pubDate>Fri, 17 Apr 2026 01:51:30 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11811</guid>
			<description><![CDATA[In-Engine Frame Profiler<br />
A lightweight CPU scope profiler with an on-screen stats overlay, built into the engine at Engine/H/Misc/Profiler.h + Engine/Source/Misc/Profiler.cpp. Works on all backends (DX11, DX12, Vulkan, GL). Zero overhead when disabled (~1 ns per PROFILE_SCOPE site -- one predictable branch on a cached bool).<br />
<br />
Tutorial_04_FrameProfiler (Tutorials/Source/04 - Graphics/Frame Profiler.cpp<br />
<br />
<a href="https://postimg.cc/216yymw6" target="_blank"><img src="https://i.postimg.cc/y8hZf83F/Screenshot-from-2026-04-15-12-49-24.png" border="0" alt="[Image: Screenshot-from-2026-04-15-12-49-24.png]" /></a><br />
<br />
<a href="https://postimg.cc/RWFhh9R0" target="_blank"><img src="https://i.postimg.cc/rwGrhwR5/Screenshot-from-2026-04-15-12-49-34.png" border="0" alt="[Image: Screenshot-from-2026-04-15-12-49-34.png]" /></a><br />
<br />
<br />
Input Action System (rebindable, savable, stacked)<br />
An Unreal/Unity-style action abstraction over Esenthel's raw polling API, built into the engine at Engine/H/Input/Input Action.h + Engine/Source/Input/Input Action.cpp. 100% additive -- no changes to Kb/Ms/Joypads/Touches. Works on all backends and platforms.<br />
<br />
Tutorial_05_InputRebinding (Tutorials/Source/05 - Gui/20 - Input Rebinding.cpp)<br />
<br />
<a href="https://postimg.cc/xXC88YRf" target="_blank"><img src="https://i.postimg.cc/K87M983M/Screenshot-from-2026-04-16-21-41-02.png" border="0" alt="[Image: Screenshot-from-2026-04-16-21-41-02.png]" /></a><br />
<br />
In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></description>
			<content:encoded><![CDATA[In-Engine Frame Profiler<br />
A lightweight CPU scope profiler with an on-screen stats overlay, built into the engine at Engine/H/Misc/Profiler.h + Engine/Source/Misc/Profiler.cpp. Works on all backends (DX11, DX12, Vulkan, GL). Zero overhead when disabled (~1 ns per PROFILE_SCOPE site -- one predictable branch on a cached bool).<br />
<br />
Tutorial_04_FrameProfiler (Tutorials/Source/04 - Graphics/Frame Profiler.cpp<br />
<br />
<a href="https://postimg.cc/216yymw6" target="_blank"><img src="https://i.postimg.cc/y8hZf83F/Screenshot-from-2026-04-15-12-49-24.png" border="0" alt="[Image: Screenshot-from-2026-04-15-12-49-24.png]" /></a><br />
<br />
<a href="https://postimg.cc/RWFhh9R0" target="_blank"><img src="https://i.postimg.cc/rwGrhwR5/Screenshot-from-2026-04-15-12-49-34.png" border="0" alt="[Image: Screenshot-from-2026-04-15-12-49-34.png]" /></a><br />
<br />
<br />
Input Action System (rebindable, savable, stacked)<br />
An Unreal/Unity-style action abstraction over Esenthel's raw polling API, built into the engine at Engine/H/Input/Input Action.h + Engine/Source/Input/Input Action.cpp. 100% additive -- no changes to Kb/Ms/Joypads/Touches. Works on all backends and platforms.<br />
<br />
Tutorial_05_InputRebinding (Tutorials/Source/05 - Gui/20 - Input Rebinding.cpp)<br />
<br />
<a href="https://postimg.cc/xXC88YRf" target="_blank"><img src="https://i.postimg.cc/K87M983M/Screenshot-from-2026-04-16-21-41-02.png" border="0" alt="[Image: Screenshot-from-2026-04-16-21-41-02.png]" /></a><br />
<br />
In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[DirectX 12 Ultimate rendering backend]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11810</link>
			<pubDate>Tue, 14 Apr 2026 12:12:36 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11810</guid>
			<description><![CDATA[In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
<br />
DX12 seems working nicely on Windows 11 nVidia A2000 GPU.<br />
<br />
Lines added (by Claude Code) 11,566<br />
<br />
<a href="https://postimages.org/" target="_blank"><img src="https://i.postimg.cc/jSgX3ZwX/dx12meshshaders.png" border="0" alt="[Image: dx12meshshaders.png]" /></a><br />
<br />
See README.md for info on the added DX12 specific tutorials.]]></description>
			<content:encoded><![CDATA[In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
<br />
DX12 seems working nicely on Windows 11 nVidia A2000 GPU.<br />
<br />
Lines added (by Claude Code) 11,566<br />
<br />
<a href="https://postimages.org/" target="_blank"><img src="https://i.postimg.cc/jSgX3ZwX/dx12meshshaders.png" border="0" alt="[Image: dx12meshshaders.png]" /></a><br />
<br />
See README.md for info on the added DX12 specific tutorials.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Vulkan rendering backend with hardware ray tracing]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11809</link>
			<pubDate>Thu, 09 Apr 2026 22:18:40 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11809</guid>
			<description><![CDATA[In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
<br />
Claude Code did ~97% of the work. Added some Vulkan specific tutorials:<br />
<br />
<br />
<a href="https://postimg.cc/jwvsQt8K" target="_blank"><img src="https://i.postimg.cc/PJnPHqYL/raytracing.png" border="0" alt="[Image: raytracing.png]" /></a><br />
<br />
Tutorial_17_RayTracing — Hardware ray tracing<br />
The marquee Vulkan-vs-OpenGL feature. Renders a small scene (ground plane + 3 colored boxes) entirely via vkCmdTraceRaysKHR running on the GPU's RT cores — no rasterization at all. Includes:<br />
<br />
BLAS (bottom-level acceleration structure) built from raw vertex/index buffers<br />
TLAS (top-level) with one instance of the BLAS<br />
4-shader raytracing pipeline: raygen + primary miss + shadow miss + closest hit<br />
Shader binding table built per Vulkan spec alignment rules<br />
Hardware ray-traced primary rays + hardware ray-traced shadow rays from a directional light, with Lambert shading using flat geometric normals computed in the closest-hit shader via SSBO lookups into the vertex/index buffers<br />
OpenGL has no hardware RT path at all — the legacy GL RT extensions are NV-only and dead. The OpenGL build of this tutorial compiles cleanly but only renders the "Ray tracing: N/A on OpenGL" HUD line.<br />
<br />
GPU requirements: any GPU exposing VK_KHR_ray_tracing_pipeline, VK_KHR_acceleration_structure, and VK_KHR_deferred_host_operations. Verified working on AMD Radeon RX 6650 XT (RDNA2) under Mesa RADV; should work on any RTX 20-series+ NVIDIA, RDNA2+ AMD, or Arc Alchemist+ Intel GPU.<br />
<br />
<br />
<br />
<a href="https://postimg.cc/G8zcJdMb" target="_blank"><img src="https://i.postimg.cc/3NQdhwm0/async-compute.png" border="0" alt="[Image: async-compute.png]" /></a><br />
<br />
Tutorial_17_AsyncCompute — Particle sim on a dedicated compute queue<br />
Demonstrates Vulkan's async-compute feature: a 4096-particle gravity simulation runs on a separate compute queue (when the GPU exposes a queue family with VK_QUEUE_COMPUTE_BIT but NOT VK_QUEUE_GRAPHICS_BIT) while the engine's frame rendering happens in parallel on the graphics queue. OpenGL has no equivalent — it is strictly single-queue.<br />
<br />
The HUD reports whether the GPU has a dedicated compute family. The compute shader is hand-written GLSL (particle_update.comp), pre-compiled once to SPIR-V via glslangValidator and checked in as a C header (particle_update_spv.h) so the build needs no shader tooling installed.<br />
<br />
<br />
<br />
<br />
<a href="https://postimg.cc/yg71ZkT1" target="_blank"><img src="https://i.postimg.cc/1tqfbFnq/gputimer.png" border="0" alt="[Image: gputimer.png]" /></a><br />
<br />
Tutorial_17_Benchmark — Renderer throughput benchmark<br />
Renders 50 animated characters in a 10×5 grid with vsync disabled, an atmospheric sky, the FPS counter, and the active API name. On Vulkan it also shows a per-phase GPU time breakdown (Prepare / GBuffer / Light / Sky / Blend / Post) measured via real vkCmdWriteTimestamp query pools. Useful for measuring relative renderer throughput between OpenGL and Vulkan builds on the same hardware.<br />
<br />
The GPU timer API (Engine/H/Graphics/GpuTimer.h: GpuTimerEnable, GpuTimerMs, GPU_TIMER_PHASE) is exposed engine-wide so any tutorial can read per-phase GPU times. On DX11/GL it compiles to stubs that return 0.<br />
<br />
<br />
See README.md for more info.]]></description>
			<content:encoded><![CDATA[In this repo: <a href="https://github.com/DrewGilpin/EsenthelEngine" target="_blank">https://github.com/DrewGilpin/EsenthelEngine</a><br />
<br />
Claude Code did ~97% of the work. Added some Vulkan specific tutorials:<br />
<br />
<br />
<a href="https://postimg.cc/jwvsQt8K" target="_blank"><img src="https://i.postimg.cc/PJnPHqYL/raytracing.png" border="0" alt="[Image: raytracing.png]" /></a><br />
<br />
Tutorial_17_RayTracing — Hardware ray tracing<br />
The marquee Vulkan-vs-OpenGL feature. Renders a small scene (ground plane + 3 colored boxes) entirely via vkCmdTraceRaysKHR running on the GPU's RT cores — no rasterization at all. Includes:<br />
<br />
BLAS (bottom-level acceleration structure) built from raw vertex/index buffers<br />
TLAS (top-level) with one instance of the BLAS<br />
4-shader raytracing pipeline: raygen + primary miss + shadow miss + closest hit<br />
Shader binding table built per Vulkan spec alignment rules<br />
Hardware ray-traced primary rays + hardware ray-traced shadow rays from a directional light, with Lambert shading using flat geometric normals computed in the closest-hit shader via SSBO lookups into the vertex/index buffers<br />
OpenGL has no hardware RT path at all — the legacy GL RT extensions are NV-only and dead. The OpenGL build of this tutorial compiles cleanly but only renders the "Ray tracing: N/A on OpenGL" HUD line.<br />
<br />
GPU requirements: any GPU exposing VK_KHR_ray_tracing_pipeline, VK_KHR_acceleration_structure, and VK_KHR_deferred_host_operations. Verified working on AMD Radeon RX 6650 XT (RDNA2) under Mesa RADV; should work on any RTX 20-series+ NVIDIA, RDNA2+ AMD, or Arc Alchemist+ Intel GPU.<br />
<br />
<br />
<br />
<a href="https://postimg.cc/G8zcJdMb" target="_blank"><img src="https://i.postimg.cc/3NQdhwm0/async-compute.png" border="0" alt="[Image: async-compute.png]" /></a><br />
<br />
Tutorial_17_AsyncCompute — Particle sim on a dedicated compute queue<br />
Demonstrates Vulkan's async-compute feature: a 4096-particle gravity simulation runs on a separate compute queue (when the GPU exposes a queue family with VK_QUEUE_COMPUTE_BIT but NOT VK_QUEUE_GRAPHICS_BIT) while the engine's frame rendering happens in parallel on the graphics queue. OpenGL has no equivalent — it is strictly single-queue.<br />
<br />
The HUD reports whether the GPU has a dedicated compute family. The compute shader is hand-written GLSL (particle_update.comp), pre-compiled once to SPIR-V via glslangValidator and checked in as a C header (particle_update_spv.h) so the build needs no shader tooling installed.<br />
<br />
<br />
<br />
<br />
<a href="https://postimg.cc/yg71ZkT1" target="_blank"><img src="https://i.postimg.cc/1tqfbFnq/gputimer.png" border="0" alt="[Image: gputimer.png]" /></a><br />
<br />
Tutorial_17_Benchmark — Renderer throughput benchmark<br />
Renders 50 animated characters in a 10×5 grid with vsync disabled, an atmospheric sky, the FPS counter, and the active API name. On Vulkan it also shows a per-phase GPU time breakdown (Prepare / GBuffer / Light / Sky / Blend / Post) measured via real vkCmdWriteTimestamp query pools. Useful for measuring relative renderer throughput between OpenGL and Vulkan builds on the same hardware.<br />
<br />
The GPU timer API (Engine/H/Graphics/GpuTimer.h: GpuTimerEnable, GpuTimerMs, GPU_TIMER_PHASE) is exposed engine-wide so any tutorial can read per-phase GPU times. On DX11/GL it compiles to stubs that return 0.<br />
<br />
<br />
See README.md for more info.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Wheel controller/collider]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11808</link>
			<pubDate>Mon, 06 Apr 2026 03:16:14 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11808</guid>
			<description><![CDATA[What kind of wheel controller/collider does Titan Engine use? Thanks.]]></description>
			<content:encoded><![CDATA[What kind of wheel controller/collider does Titan Engine use? Thanks.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[April 2026]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11806</link>
			<pubDate>Fri, 03 Apr 2026 04:20:49 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11806</guid>
			<description><![CDATA[Updated Esenthel Source:<br />
-reduced stack memory usage for World Editor heightmap building (to fix crashes on Linux)<br />
-replaced ThreadMayUseGPUData / ThreadFinishedUsingGPUData with a helper class GPUDataUse for auto lock and release]]></description>
			<content:encoded><![CDATA[Updated Esenthel Source:<br />
-reduced stack memory usage for World Editor heightmap building (to fix crashes on Linux)<br />
-replaced ThreadMayUseGPUData / ThreadFinishedUsingGPUData with a helper class GPUDataUse for auto lock and release]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[March 2026]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11805</link>
			<pubDate>Sat, 07 Mar 2026 06:37:33 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11805</guid>
			<description><![CDATA[Updated Esenthel Source:<br />
-tweaked vignette to automatically apply perceptual gamma adjustment based on vignette color brightness (now dark colors are stronger)<br />
-'Input/Inputs' now supports all button states<br />
-added translation languages: chinese traditional (CT), turkish (TR), czech (CS), ukrainian (UA), hungarian (HU), indonesian (IND)<br />
-improved performance / reduced memory usage of translation functions<br />
-improved outline shader to draw outlines on the outside (and not on inside which made the objects seem thinner)<br />
-fixed issues when using multi-sampling (motion vector shaders - TAA+MotionBlur, SSR, outline)<br />
-increased default 'SoundMaxConcurrent' to 32<br />
-renamed SP-&gt;ES, JP-&gt;JA, PO-&gt;PT<br />
-improved 'LanguageSpecific' function<br />
-'OSLanguage' now supports detection of Chinese Traditional (CT)]]></description>
			<content:encoded><![CDATA[Updated Esenthel Source:<br />
-tweaked vignette to automatically apply perceptual gamma adjustment based on vignette color brightness (now dark colors are stronger)<br />
-'Input/Inputs' now supports all button states<br />
-added translation languages: chinese traditional (CT), turkish (TR), czech (CS), ukrainian (UA), hungarian (HU), indonesian (IND)<br />
-improved performance / reduced memory usage of translation functions<br />
-improved outline shader to draw outlines on the outside (and not on inside which made the objects seem thinner)<br />
-fixed issues when using multi-sampling (motion vector shaders - TAA+MotionBlur, SSR, outline)<br />
-increased default 'SoundMaxConcurrent' to 32<br />
-renamed SP-&gt;ES, JP-&gt;JA, PO-&gt;PT<br />
-improved 'LanguageSpecific' function<br />
-'OSLanguage' now supports detection of Chinese Traditional (CT)]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[New animations]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11803</link>
			<pubDate>Sat, 21 Feb 2026 13:21:05 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11803</guid>
			<description><![CDATA[Hi Guys, Some stuff ive been working<br />
<br />
Back 2 the future<br />
<br />
<a href="https://www.youtube.com/watch?v=s3nwI2pvhKM" target="_blank">https://www.youtube.com/watch?v=s3nwI2pvhKM</a><br />
<br />
Johnny Bravo<br />
<br />
<a href="https://www.youtube.com/watch?v=U5WlK9YZrYw" target="_blank">https://www.youtube.com/watch?v=U5WlK9YZrYw</a><br />
<br />
Will Smith Rap<br />
<br />
<a href="https://www.youtube.com/watch?v=c-WEwwbNS6w" target="_blank">https://www.youtube.com/watch?v=c-WEwwbNS6w</a><br />
<br />
<br />
Superman<br />
<br />
<a href="https://www.youtube.com/watch?v=pHv1r0SlwwE" target="_blank">https://www.youtube.com/watch?v=pHv1r0SlwwE</a><br />
<br />
<br />
Batman<br />
<br />
<a href="https://www.youtube.com/watch?v=WImSZBtwQ9E" target="_blank">https://www.youtube.com/watch?v=WImSZBtwQ9E</a>]]></description>
			<content:encoded><![CDATA[Hi Guys, Some stuff ive been working<br />
<br />
Back 2 the future<br />
<br />
<a href="https://www.youtube.com/watch?v=s3nwI2pvhKM" target="_blank">https://www.youtube.com/watch?v=s3nwI2pvhKM</a><br />
<br />
Johnny Bravo<br />
<br />
<a href="https://www.youtube.com/watch?v=U5WlK9YZrYw" target="_blank">https://www.youtube.com/watch?v=U5WlK9YZrYw</a><br />
<br />
Will Smith Rap<br />
<br />
<a href="https://www.youtube.com/watch?v=c-WEwwbNS6w" target="_blank">https://www.youtube.com/watch?v=c-WEwwbNS6w</a><br />
<br />
<br />
Superman<br />
<br />
<a href="https://www.youtube.com/watch?v=pHv1r0SlwwE" target="_blank">https://www.youtube.com/watch?v=pHv1r0SlwwE</a><br />
<br />
<br />
Batman<br />
<br />
<a href="https://www.youtube.com/watch?v=WImSZBtwQ9E" target="_blank">https://www.youtube.com/watch?v=WImSZBtwQ9E</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[February 2026]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11802</link>
			<pubDate>Wed, 11 Feb 2026 06:13:29 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11802</guid>
			<description><![CDATA[Updated Esenthel Source:<br />
-Gui Skin Editor can now edit TextBox Panel<br />
-Gui Window title bar now additionally includes StrEx allowing to include images and text color<br />
-added a new kind of light 'LightPointEx' which is more customizable]]></description>
			<content:encoded><![CDATA[Updated Esenthel Source:<br />
-Gui Skin Editor can now edit TextBox Panel<br />
-Gui Window title bar now additionally includes StrEx allowing to include images and text color<br />
-added a new kind of light 'LightPointEx' which is more customizable]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[January 2026]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11801</link>
			<pubDate>Sun, 01 Feb 2026 15:11:44 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11801</guid>
			<description><![CDATA[Updated Esenthel Source:<br />
-IMPORTANT: slightly tweaked bloom formula, please adjust your Editor Settings \ Video Options \ Advanced \ Bloom Scale = 0.6, if you're using custom 'D.bloomScale' in your game codes you might need to adjust it<br />
-Joypad DPAD is now also included in the button functions b, bp, br, bd<br />
-configured engine to use Windows::Gaming::Input first, and if unavailable then fall back to XInput, for improved compatibility when running on Linux<br />
-Gui Objects now additionally support "StrEx descEx"<br />
-added a workaround for old game controllers to have axis centered initially<br />
-other various improvements]]></description>
			<content:encoded><![CDATA[Updated Esenthel Source:<br />
-IMPORTANT: slightly tweaked bloom formula, please adjust your Editor Settings \ Video Options \ Advanced \ Bloom Scale = 0.6, if you're using custom 'D.bloomScale' in your game codes you might need to adjust it<br />
-Joypad DPAD is now also included in the button functions b, bp, br, bd<br />
-configured engine to use Windows::Gaming::Input first, and if unavailable then fall back to XInput, for improved compatibility when running on Linux<br />
-Gui Objects now additionally support "StrEx descEx"<br />
-added a workaround for old game controllers to have axis centered initially<br />
-other various improvements]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Merry Christmas]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11800</link>
			<pubDate>Wed, 24 Dec 2025 11:00:45 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11800</guid>
			<description><![CDATA[Merry Christmas!<br />
<!-- start: postbit_attachments_attachment -->
<br /><img src="images/attachtypes/image.gif" border="0" alt=".jpg" />&nbsp;&nbsp;<a href="attachment.php?aid=3331" target="_blank">676b654d3dfde-merry-christmas-wishes-255207535-16x9.jpg</a> (Size: 110.48 KB / Downloads: 11)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[Merry Christmas!<br />
<!-- start: postbit_attachments_attachment -->
<br /><img src="images/attachtypes/image.gif" border="0" alt=".jpg" />&nbsp;&nbsp;<a href="attachment.php?aid=3331" target="_blank">676b654d3dfde-merry-christmas-wishes-255207535-16x9.jpg</a> (Size: 110.48 KB / Downloads: 11)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Sound Qestion]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11799</link>
			<pubDate>Wed, 24 Dec 2025 09:25:07 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11799</guid>
			<description><![CDATA[Hi everybody,<br />
Out of curiosity, what kind of headphones/speakers do you use?<br />
Can you hear quality difference between music/sound files encoded in the engine as 96 vs 128 kbps Opus? (from full quality WAV/FLAC)]]></description>
			<content:encoded><![CDATA[Hi everybody,<br />
Out of curiosity, what kind of headphones/speakers do you use?<br />
Can you hear quality difference between music/sound files encoded in the engine as 96 vs 128 kbps Opus? (from full quality WAV/FLAC)]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Johnny "incel" Bravo]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11798</link>
			<pubDate>Fri, 05 Dec 2025 19:16:58 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11798</guid>
			<description><![CDATA[Just finished this, enjoy<br />
<br />
<a href="https://www.youtube.com/watch?v=U5WlK9YZrYw" target="_blank">https://www.youtube.com/watch?v=U5WlK9YZrYw</a>]]></description>
			<content:encoded><![CDATA[Just finished this, enjoy<br />
<br />
<a href="https://www.youtube.com/watch?v=U5WlK9YZrYw" target="_blank">https://www.youtube.com/watch?v=U5WlK9YZrYw</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[December 2025]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11797</link>
			<pubDate>Mon, 01 Dec 2025 07:55:10 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11797</guid>
			<description><![CDATA[Released Esenthel for Windows:<br />
-added support for Visual Studio 2026<br />
-disabled Windows UWP project setting, now you don't need to install it along with VS]]></description>
			<content:encoded><![CDATA[Released Esenthel for Windows:<br />
-added support for Visual Studio 2026<br />
-disabled Windows UWP project setting, now you don't need to install it along with VS]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Roadmap]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11793</link>
			<pubDate>Wed, 24 Sep 2025 20:30:05 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11793</guid>
			<description><![CDATA[Is there a roadmap of the engine's development?<br />
Or are the features added as they are needed for World of Esenthel?]]></description>
			<content:encoded><![CDATA[Is there a roadmap of the engine's development?<br />
Or are the features added as they are needed for World of Esenthel?]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[September 2025]]></title>
			<link>https://esenthel.com/forum/showthread.php?tid=11792</link>
			<pubDate>Fri, 05 Sep 2025 11:55:19 +0000</pubDate>
			<guid isPermaLink="false">https://esenthel.com/forum/showthread.php?tid=11792</guid>
			<description><![CDATA[Updated Esenthel Source:<br />
-IMPORTANT: changed memory layout of Ball, BallM, BallD, now 'pos' is at the start, followed by 'r' radius. If you're saving/loading ball using File f; f&lt;&lt;ball; please replace code with f&lt;&lt;ball.r&lt;&lt;ball.pos; for compatibility<br />
-unified codes for Sweep, CutsSweep functions and added a few more variations of them<br />
-optimized DistPointLine, DistLineLine functions<br />
-added new Dist2LineLine functions<br />
-optimized Dist2 Edge Tri functions<br />
-renamed "fromMul*"-&gt; "setMul*",  "fromDiv*"-&gt; "setDiv*"<br />
-optimized Vec Edge Ball Capsule * / Matrix operators<br />
-optimized Frustum edge tests<br />
-optimized animating characters<br />
-optimized matrix multiplication and division<br />
-Plane classes can now be transformed by matrixes<br />
-added a lot of Cuts Dist2 Dist function variations]]></description>
			<content:encoded><![CDATA[Updated Esenthel Source:<br />
-IMPORTANT: changed memory layout of Ball, BallM, BallD, now 'pos' is at the start, followed by 'r' radius. If you're saving/loading ball using File f; f&lt;&lt;ball; please replace code with f&lt;&lt;ball.r&lt;&lt;ball.pos; for compatibility<br />
-unified codes for Sweep, CutsSweep functions and added a few more variations of them<br />
-optimized DistPointLine, DistLineLine functions<br />
-added new Dist2LineLine functions<br />
-optimized Dist2 Edge Tri functions<br />
-renamed "fromMul*"-&gt; "setMul*",  "fromDiv*"-&gt; "setDiv*"<br />
-optimized Vec Edge Ball Capsule * / Matrix operators<br />
-optimized Frustum edge tests<br />
-optimized animating characters<br />
-optimized matrix multiplication and division<br />
-Plane classes can now be transformed by matrixes<br />
-added a lot of Cuts Dist2 Dist function variations]]></content:encoded>
		</item>
	</channel>
</rss>