Skip to content

ARE ambient-animation detection (memory-primary, disk fallback) - #21

Draft
TheForgotten69 wants to merge 20 commits into
mainfrom
feature/are-animation-detection
Draft

TheForgotten69 wants to merge 20 commits into
mainfrom
feature/are-animation-detection

Conversation

@TheForgotten69

Copy link
Copy Markdown
Owner

Detection data path for the graphics roadmap §10.3 classification channel.

  • In-memory primary: walk CGameObjectArray for the area's live CGameStatic objects (raw authored ARE record retained at +0x60). Resolution via EEex's version-independent GetShare pattern + RIP-operand decode; any ambiguity fails closed.
  • Disk fallback: override/ then chitin.key/BIFF V1 reader, cached by resref.
  • Conservative fire/smoke/fountain/light classifier; unclassified resrefs are logged per area to grow the table from real data.
  • Offline evidence: pattern unique at RVA 0x276490 on 2.6.6.0 with operands decoding to 0x68D434/0x68D450; parser validated against real BG2EE data (AR0406/AR0700). Full trail in docs/are-animation-detection.md.
  • Remaining: in-game validation on 2.6.6.x; 2.7.3.x GetShare reference RVA pending (runtime unique-match gates it; disk path covers failure).

🤖 Generated with Claude Code

@TheForgotten69
TheForgotten69 force-pushed the feature/are-animation-detection branch 8 times, most recently from dc44bcf to 5606827 Compare July 17, 2026 21:14
…cale heuristic

At the LoadArea boundary, walk CGameObjectArray for the active area's live
CGameStatic objects (the raw authored ARE record is retained at +0x60) and
classify authored point sources into an AppContext snapshot (graphics roadmap
§10.3 classification channel).

Resolution rides EEex's version-independent GetShare pattern with RIP-operand
decoding of the m_maxArrayIndex/entry-table globals; any ambiguity fails
closed and disables the scan. Offline-verified on both supported builds
(2.6.6.0: RVA 0x276490, globals 0x68D434/0x68D450; 2.7.3.0: RVA 0x276700,
globals 0x68F8F4/0x68F910) and validated in-game on 2.7.3. Unclassified
resrefs are logged per area to grow the classification table. Water bodies
stay on the WED overlay path.

Classification table calibrated against the full BG1EE (96%+) and BG2EE ARE
exports plus visual frame inspection of the ambiguous BAMs: FLM*/FLSM*/FIM*/
YSFL* flame families, candle-named flames as light, steam as smoke, bubbles
as water, new water/lava/wildlife kinds, and a built-in exact-resref table
for the identified per-area AM* overlays. AR<area>W[DN]* overlays are
day/night shadow scenery, not lit windows.

First consumer: the fpSEAM override renders point effects from the snapshot —
warm flickering fire glow with heat shimmer above flames, drifting chimney-
smoke haze columns, and steady candle/glow halos. Shown fire/light/smoke
points are packed into a 32-slot uIeePoints[] uniform array (fire prioritized
under capacity pressure) and fed through the uniform bridge under its own
revision. Gated by [Shaders] EnablePointEffects and the F10 master toggle;
background layer only, with vanilla output untouched when off.

Also removes the tile-scale UV/texture-id heuristic tier: raw UV magnitudes
are atlas-origin noise and GL texture ids grow with session allocations, so
the tier produced false 4x detections on vanilla areas (observed in-game on
2.7.3). Detection is now TIS header -> PVR-table grid GCD -> fail closed to
standard 1x delegation via the existing sampling path.
@TheForgotten69
TheForgotten69 force-pushed the feature/are-animation-detection branch from 5606827 to b42c659 Compare July 17, 2026 21:34
…kers

The struct-returning helper was the remaining suspect for the effects not
rendering despite a verified uniform feed (locations resolved, count fed) —
old GLSL frontends miscompile struct returns. The loop now uses flat locals
inside main. ALIGN mode (F10 x2) draws a dot and 40px ring at every fed
point (orange fire, grey smoke, yellow light), so point placement is
verifiable in-game independently of the effect math.
… metadata-free tilesets

In-game ALIGN markers confirmed exact point placement, but the first-pass
constants were too timid, especially at night where multiplicative glow
cannot lift dark pixels. Fire glow gains a night-adaptive additive cast-light
term, a wider radius (140px) with a hot core, and an up-biased center onto
the visible flame; smoke columns are taller, denser, and read as moonlit
haze over dark roofs; candle glow strengthened.

Also skips the 10-draw sampling window when a tileset is resident and
readable but simply has no deterministic scale metadata (classic paletted
tilesets): more draws cannot change that verdict, so it latches standard 1x
immediately. Sampling remains for resources still streaming in.
…nder suppression

Radial-gradient halos read as artificial airbrush blobs (and painted the
unexplored void). This pass makes fire and smoke real replacements, the same
approach as the water pillar:

- New tileable FBM asset iee_effects_noise.rgba (generated in-repo, IRGB
  blob, unit 6/uIeeNoiseMap) riding the existing texture registry with the
  same DDS-override path.
- fpSEAM draws shaped flame bodies eroded by rising noise with a blackbody
  ramp (per-resref scale: sconces small, fire pits full), night-adaptive
  cast light that never paints the void, and noise-billowed smoke plumes
  that wander and dissipate with altitude.
- CGameStatic::Render hook (prologue pattern offline-verified unique on both
  builds: 0x1F2570 / 0x1F27D0) skips the engine's authored fire/smoke BAM
  draws while the effect is active; WBM/PVRZ setpieces and all other kinds
  stay vanilla, and failure to resolve keeps engine draws.
…e; fix haze mottling

Calibrated against the exported BAM frame dimensions: FIRE* torch flames are
12x45 px, FLAM* sconces ~10x30, YSFL* small flames 7x14 — the v2 flame body
was 42x76 for everything, hence the traffic-cone look. Flame strength now
derives from authored height per family with the authored ~1:3.5 aspect,
noise sampling scales with flame size so small flames keep structure, and
the hot-white core is confined to flicker peaks. FLAMBLU*-family flames keep
their authored blue (palette id in the kind fraction drives both ramp and
cast-light color).

Smoke plumes are narrower, wispier, and capped at 62% opacity so the roof
stays readable, and the per-texel luma gate is removed from the haze mix —
it mottled dark stonework; the gate remains only on the fire cast-light
additive term, which is what protects the unexplored void.

Also archives the owner-observed water follow-up (some cells render more
transparent than the opaque water body) in the phase-2 water plan backlog.
…engine draw

Two field-observed defects, both from ignoring the BAM frame tables:

- Suppression scope: AM*/AR* per-area overlay images (AM5204C lit-hearth art
  in AR2616) were suppressed like standalone flames, blacking out the hearth
  while a synthetic flame burned on the floor anchor. Replacement is now
  limited to standalone flame/smoke BAM families
  (should_replace_animation_draw); overlay fires keep the engine draw and
  contribute cast light only (palette id .2), overlay smoke packs no point.

- Placement/size: points now carry the exact authored draw box, extracted
  from the game BAM frame tables (frame w/h and center offsets; e.g.
  FLAMBLU2 draws down-right of its anchor at 8x15). Each point uses two
  vec4 slots (base-center position + height, half-width); flame bodies,
  plumes, and glows derive from authored pixels instead of guessed scales.
…appled cast light

The blue-flame palette rode the kind's fractional digit through the uniform
feed; field evidence says it never survived to the shader on the target
driver. The palette is now a plain float in the point's second slot (0 warm,
1 blue, 2 glow-only) — no fract decoding anywhere — and every packed point
is logged at DEBUG (kind/palette/pos/geometry) so the feed is verifiable
from the game log alone.

Cast light no longer reads as an airbrushed disc: the footprint is a 1.9:1
ellipse (isometric floor projection), modulated by drifting noise dapple,
with reduced multiplicative and additive weights.

Also archives the liquid-type sweep findings in the water backlog: lava's
opaque art never engages the alpha-hole mask (root cause for "lava not
working"), washed-out sewage channels, and the saturated harbor sea — all
scoped to the dedicated water pass.
…centered on the art

Field log confirmed the blue-flame bug: live resrefs keep their authored
casing and this install stores "flamblu2" lowercase — the exact-match
geometry table and the "BLU" palette check both missed, falling back to
default geometry with a warm palette (classification survived because it
uppercases internally). Both lookups now run on the uppercased resref, with
a lowercase regression test.

Glow-only overlay points (hearths) anchor at the art's floor edge; their
cast light is now centered well up onto the coals instead of pooling on the
floor in front.
…rtion

Placement evidence across rounds (ALIGN markers land on the sconce bowls;
frame-box-derived offsets rendered flames below them) says the ARE anchor is
already the flame's base point — the per-family dx/dy shifts are gone and
only the authored footprint varies.

The heat shimmer offset the ATLAS texture coordinate; crossing a tile
boundary sampled unrelated atlas content, which showed as a vertical
smear-seam ("compressed map"). Shimmer is removed from the tile pass —
world-space distortion belongs to a future post pass.
…-box offsets

CGameStatic::RenderBam ground truth (decompilation): the engine draws the
current frame's top-left at (CGameObject::m_pos - frameCenter) — the BAM's
embedded per-frame centers ARE applied, and the render base is the live
object position, not the ARE header coordinates (which can diverge; a
chimney showed header y 163px off its authored value). The walk now reads
m_pos, packing places the flame at the frame box's bottom-center relative to
it, and the per-animation debug line logs both positions so any divergence
stays visible in the field log.
The per-resref geometry table is now a fallback only. The walk mirrors the
engine's own cached current-frame entry (CVidCell::m_pFrame at CGameStatic
+0xB0+0x128, the exact frameTableEntry RenderBam draws with), so any flame
BAM — including mods — places and sizes itself from the same numbers the
engine uses: frame top-left at (m_pos - center), flame at the box's
bottom-center. The fallback covers only the first refresh before an object
has rendered once and the disk-parse path. The per-animation debug line now
prints the live frame geometry, and CVidCell/CGameStatic layouts carry
static asserts for the new fields.
The ARE object position is already the flame's on-screen base — ALIGN
markers at raw objPos land on the sconce bowls, so every per-family dx/dy
offset was the source of the persistent shift. Placement now uses objPos
directly; only the authored size varies per family.

Also records the finding that CVidCell::m_pFrame stays null for replaced
flames: the Render-suppression hook returns before RenderBam populates the
frame cache (chicken-and-egg), so the size table is the normal path and the
live-frame read is an opportunistic upgrade only.
CGameStatic::RenderBam draws at y = m_pos.y - m_posZ; mounted flames (wall
sconces) carry authored Z, so anchoring at raw m_pos.y left them hanging
below the art by exactly z pixels. The walk now reads the live m_posZ
(+0x14, already modeled), the disk mirror loads the ARE height field, and
point packing applies the same subtraction. objZ joins the per-animation
debug log so field logs show the elevation directly.
@TheForgotten69 TheForgotten69 self-assigned this Aug 26, 2026
Enables building InfinityEngine-Enhancer.dll from Linux via a mingw-w64
toolchain, for local Proton-based iteration without a Windows host.
Fixes three MSVC-only assumptions the cross build exposed:

- release_bundle unconditionally copied $<TARGET_PDB_FILE:...>, which
  hard-errors at CMake generate time for non-MSVC linkers (mingw has
  no PDB). Gated behind if(MSVC); the MSVC/CI path is unaffected.
- opengl_types.cpp implicitly converted FARPROC to void*, an MSVC
  extension GCC rejects under -fpermissive-off. Made explicit.
- shader_probe.cpp called _ReturnAddress(), which mingw-w64 declares
  for source compatibility but never defines, causing an unresolved
  symbol at link time. Added a caller_return_address() wrapper that
  uses __builtin_return_address(0) on GCC/Clang.

Adds cmake/toolchains/mingw-w64.cmake for the cross build itself.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant