Sync upstream into main - #4
Open
github-actions[bot] wants to merge 171 commits into
Open
Conversation
A handful of SolLua bindings still referenced sol::nil / sol::type::nil while the rest of the same files already use sol::lua_nil. sol::nil is a thin alias for lua_nil (see lib/sol2/sol.hpp) and collides with the `nil` macro that Objective-C headers define, so lua_nil is the portable spelling. This brings the remaining spots in line with the prevailing convention. Extracted from cc3cad9 in beyond-all-reason#2991; cross-platform, no behavior change.
float3.h included lib/streflop/streflop_cond.h directly, ahead of FastMath.h. FastMath.h defines MATH_SQRT_OVERRIDE before it includes streflop_cond.h (so streflop does not define its own math::sqrt(float) -- FastMath provides a faster one). The direct include meant streflop_cond.h could be processed before that override was set. Drop the direct include; FastMath.h pulls in streflop_cond.h transitively with the override in place. creg_cond.h keeps its original position -- it does not pull in streflop, so it does not need to move. Surfaced by the macOS port (beyond-all-reason#2991, commit cc3cad9). Co-authored-by: Mark Kropf <markkropf@gmail.com>
…son#3025) `SafeUtil.h` uses `<memory>` for `std::addressof`, and `<type_traits>` for `std::is_trivially_copyable` / `std::is_trivially_constructible_v`, but pulled them in only transitively. Include them directly so the header is self-contained and does not rely on include order elsewhere. Also use `std::is_trivially_default_constructible` for the default-construction. Extracted from cc3cad9 in beyond-all-reason#2991; cross-platform, no behavior change. Co-authored-by: Mark Kropf <markkropf@gmail.com> Co-authored-by: sprunk <spr.ng@o2.pl>
LuaTextures::Create returned an empty string on glTexImage failure with no diagnostics. Log target/size/format/dataFormat/dataType/glError so texture-creation failures can be diagnosed. Extracted from 1e75080 in beyond-all-reason#2991; cross-platform, no behavior change beyond the added log.
Engine crash thread: https://discordapp.com/channels/549281623154229250/1516994684591931535 Bugged Scenario: A reclaimer A is guarding another reclaimer B, while both reclaiming the same wreck. A can be notified of wreck death BEFORE the guarded unit B is notified, leading to trying to reclaim the same (actively-deleting) wreck because B's reference hasn't been cleaned up yet. This leads to an eventual segfault. So: the short term/easy fix is to skip reclaiming of a dead/dying target.
util_fileSelector was declared with a non-const struct dirent* on __APPLE__ and const elsewhere. macOS scandir() expects the selector argument as int(*)(const struct dirent*), so the non-const Apple variant failed to compile under GCC: Util.c:500: error: passing argument 3 of 'scandir' from incompatible pointer type Both branches were otherwise identical, so drop the __APPLE__ split and use const struct dirent* unconditionally (matches POSIX scandir). No effect on Linux/Windows. Assisted by Claude Code; verified by compiling the macOS headless build.
The legacy build did find_package(X11 REQUIRED) under a plain if(UNIX) guard. macOS is UNIX in CMake but does not use X11 (it uses Cocoa), so configuring the engine on macOS failed at find_package(X11). An if(APPLE) block already follows for Foundation, so exclude Apple from the X11 branch: if(UNIX AND NOT APPLE). Surfaced configuring the spring-headless target on macOS (which reuses the legacy Game target). No effect on Linux/Windows. Assisted by Claude Code; verified by configuring the macOS build.
Modern 7-Zip ships its CLI as '7zz' (Homebrew's 'sevenzip' formula installs /opt/homebrew/bin/7zz; recent Linux distros likewise package '7zz'). FindSevenZip only searched for '7z'/'7za', so configure failed with 'Could NOT find SevenZip (missing: SEVENZIP_BIN)' on such systems. Add '7zz' to the searched NAMES. No effect where 7z/7za already exist.
rts/System/Platform/Mac/SDLMain.m and SDLMain.h are the classic SDL 1.2 Cocoa main wrapper (the Darrell Walisser / Max Horn template). They are: - not listed in any CMakeLists (never compiled) - not #included by any source file - built on Carbon (<Carbon/Carbon.h>), which is 32-bit-only and unavailable on modern macOS / Apple Silicon SDL2 provides its own SDL_main, so this wrapper is obsolete. Remove the dead files so the macOS platform layer reflects what is actually built.
Updated the link for the SplinterFaction card to include the 'https://' prefix. This has been broken for a very long time. I've asked Skyrbunny to fix it multiple times, but it has never gotten fixed.
beyond-all-reason#3052) setMinLevel() only searched for an existing entry on the "set back to default" (erase) path. Setting a section to a *non-default* level appended a new row unconditionally, so repeatedly changing one section's level (e.g. via Spring.SetLogSectionFilterLevel) accumulated duplicate entries and eventually filled the fixed 64-slot sectionMinLevels table -- after which every section-level change silently failed with "too many section-levels". Fix: Look the section up before appending and, if it already has an entry, update it in place. This bounds the table at one entry per section. This likely never happens in practice but this was found while writing other tests Co-authored-by: Bruno Da Silva <Bruno-DaSilva@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The original code relied on implicit null-termination which is not guaranteed.
Aligns forward declarations with definitions to silence MSVC warnings.
SpringApp.cpp included <X11/Xlib.h> for every non-Windows platform, but macOS has no X11 headers by default, breaking the native build. The only user of it, XInitThreads(), is already excluded on __APPLE__ at its call site, so guard the include the same way.
glad_glx.c includes X11/X.h to provide the GLX windowing-system bindings,
but macOS has no X11/GLX. The Glad CMakeLists added glad_glx.c for every
UNIX-and-not-MinGW platform, so building any GL-enabled target (e.g.
engine-legacy) on macOS failed:
fatal error: X11/X.h: No such file or directory
macOS resolves GL entry points without GLX (the engine's glxHandler is
already #ifdef'd out on __APPLE__), so exclude glad_glx.c on Apple and
build only the core glad.c there.
…eyond-all-reason#2919) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These three headers use std:: algorithms but rely on <algorithm> being pulled in transitively. libstdc++ does so; libc++ (clang/macOS) does not, so they can fail to compile under libc++ depending on the version. Symbols that require the include: - rts/System/Matrix44f.h -> std::copy - rts/System/SpringHashMap.hpp -> std::fill_n - rts/System/SpringHashSet.hpp -> std::fill_n Adding the include is "include what you use" correctness and is a no-op on toolchains that already provide it transitively. No functional change. Cherry-picked from ExaDev/RecoilEngine (0ed29d5, 0b4bd10, 3adabd0). AI assistance: changes identified and applied with Claude (Anthropic); verified by a human (compiled under clang/libc++, no regression).
DemoTool compiles engine FileSystem sources (FileHandler, FileSystem) that include nowide/fstream.hpp and fmt/printf.h, but its target_link_libraries omitted the nowide and fmt targets. The engine builds obtain those include directories transitively through the nowide::nowide and fmt::fmt INTERFACE targets; DemoTool linked neither, so the build failed when the headers were not on a default search path (observed building the demotool target on macOS). Link nowide::nowide and fmt::fmt so their INTERFACE include directories propagate to the demotool target. Co-authored-by: Robert Burnham <burnhamrobertp@gmail.com>
* avoid parenthesised aggregate init and name the type explicitly (not supported everywhere) * avoid narrowing conversions.
* adds `Spring.TraceRayBetweenPositions(xA, yA, zA, xB, yB, zB, type)`
* adds `Spring.TraceRayInDirection(x, y, z, dx, dy, dz, length, type)`
* type is a string, "unit", "feature", or "both"
* both return an array of `{distance, objID, objType}` sorted by increasing distance
* these no longer did anything and just polluted the interface. * also removed example gadgetry using them from basecontent.
…#3274) Spring.ReplaceMouseCursor with a name whose files load no frames silently turns a working cursor invisible, which reads as a rendering bug and cost a debugging session on macOS before the cause surfaced. Warn rather than refuse: content may rely on a frameless replacement to hide the cursor deliberately.
Fixes beyond-all-reason#3179 (earlier tests solved via c1a5eb0 and fa50380 )
…am[, fullView]]) (beyond-all-reason#3206) * Added gl.DrawMiniMapIcons(left, top, right, bottom, iconSize[, allyTeam[, fullView]]) add `gl.DrawMiniMapIcons(left, top, right, bottom, iconSize[, allyTeam[, fullView]])`, which draws all unit icons visible to the given perspective (team colors, radar dots, LOS rules, ghost dimming — exactly as on the minimap) for a map-space rectangle, mapped onto the unit square under the current transform. Composes with `gl.RenderToTexture`/FBOs, so widgets (e.g. picture-in-picture views) can render icon layers for any camera rect and zoom without maintaining their own icon pipeline. - `allyTeam` and `fullView` default to the local view; other perspectives (e.g. for casting) require a full-read Lua handle. * addressed sprungs comments 1. Magic 8 → named + documented. It's now constexpr float MAX_ICON_SCALE_MULT = 8.0f with a comment explaining what it bounds: the per-icon scale multiplier the drawer applies (icontypes.lua size, times radius/30 for radiusAdjust icons), and the failure mode when exceeded (an oversized icon can pop at the cull-rect edge; callers that care can pad their rectangle). Reply to sprunk: "Yes — an upper bound on the per-icon scale multiplier (icontypes size × radiusAdjust factor); now named and documented, including the pop-at-edge consequence for icons beyond it." 2. Rect validation → strict ordering with epsilon. The check is now !((right - left) > float3::cmp_eps()) || !((bottom - top) > float3::cmp_eps()) — which rejects flipped, degenerate, near-degenerate, and NaN rects (the !(a > b) form catches NaN where a < b wouldn't), instead of only exact equality. I went with your "also fail if left > right" option rather than renaming to x1/x2: mirroring costs the caller nothing since they control the transform the unit square lands in, and a strict contract keeps the docs and the cull setup simpler (the std::min/max there is gone). Docstring updated to say so. 3. fullView gate → sprunk's condition adopted, plus one addition. They were right, and it cut both ways: my symmetric fullView != gu->spectatingFullView check errored on a restricted handle requesting less visibility than the global state (harmless), and — worse — permitted a restricted handle to render godmode whenever the global state happened to be fullview, leaking past the handle's read scope. Now: (allyTeam != gu->myAllyTeam || fullView) && !fullRead errors — requesting fullView always demands fullRead, restricting never does. The addition worth mentioning in your reply: the default for the parameter is now gu->spectatingFullView && GetHandleFullRead(L) — without that clamp, sprunk's stricter check would have made a bare 5-argument call error from a restricted handle during fullview spectating, since the old default blindly inherited the global fullview state. * Update rts/Lua/LuaOpenGL.cpp Co-authored-by: sprunk <spr.ng@o2.pl> * reverted changelog -> moved into doc/pr-changelogs\3206.md * added addition param: @PARAM highlightSelected boolean? (Default: true) draw the local player's selected units white. Other players' selections are unknown to the engine — when rendering another perspective, pass false and overlay that viewer's selection yourself added param highlightSelected (Default: true) draw the local player's selected units white. --------- Co-authored-by: sprunk <spr.ng@o2.pl>
> warning: space between quotes and suffix is deprecated in C++23
Fixes beyond-all-reason#3177. Seems a missed side effect of beyond-all-reason#1509.
…for unittype based draw order (beyond-all-reason#3204) * Added configbool "UnitIconsSorted" (default disabled) to allow for unittype based draw order * sortUnitIcons -> sortUnitIconsByDepth removed `sortUnitIcons`, Games that define no `drawOrder` values skip sorting entirely added `UnitIconsSortedByDepth` config bool (default false). When enabled, overlapping icons are additionally ordered back-to-front by view depth within equal `drawOrder`; this makes overlap stacking change as units and the camera move, hence optional. * changed comment, applied static_assert "icon sort records cannot index all units" * moved changelog to: doc\pr-changelogs\3204.md
* Fix local demo waiting for replay players --------- Co-authored-by: sprunk <spr.ng@o2.pl>
* Add mod rule to allow game to override map gravity value.
* ProjectileDrawer: reuse alpha particle geometry across the water passes
When water is visible the main view draws alpha particles twice per
frame: a below-water pass, then an above-water pass after the water
surface. Both passes contain the same particles, viewed from the same
camera at the same interpolation time; only the clip-plane uniform
differs. Each pass nevertheless refiltered, re-sorted, re-ran every
particle's Draw() to regenerate its quads, and re-uploaded all of it.
Fill and upload the geometry once in the below-water pass, capture the
pending index range before submitting (new TypedRenderBuffer::
GetPendingElemsRange), and have the above-water pass of the same draw
frame re-issue that range through the new DrawElementsRange, which
draws an already-uploaded range without touching the consume/rewind
bookkeeping. The reflection and refraction passes that run in between
fill and consume their own ranges, so the saved range stays valid;
stream buffer chunks only swap at end of frame. The reuse pass still
fires DrawWorldPreParticles and flushes anything appended during it,
so nothing can leak into a later submit under a different shader.
The reuse can be disabled at runtime with the new config
ProjectileDrawReuseWaterPasses (default 1, safemode 0), which forces
the previous full-refill-per-pass behavior.
The above-water particle pass drops from ~1.16 ms to ~65 us mean at
~6k visible alpha particles (deterministic map-wide effect-spawner
benchmark at 3x battle-level intensity on a water map, measured with
Tracy). The resubmitted geometry is byte-identical to what a refill
would produce, and maps without visible water (single combined pass)
are unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: faster sorting and threaded quad generation
Three further reductions of the alpha pass' CPU cost, measured with the
same deterministic effect-spawner benchmark as the previous commit
(~6k visible alpha particles, water map, Tracy):
1. Sort-key snapshot. The sorting predicates dereferenced two
CProjectile pointers per comparison (drawOrder + sortDist), so
sorting a few thousand particles was mostly cache misses (~200 us
per fill). DrawAlpha now snapshots {drawOrder, sortDist, proj} into
a contiguous array while filtering and sorts that instead. Ordering
semantics are unchanged (drawOrder asc, distance desc, pointer
tiebreak).
2. Threaded quad generation, config ProjectileDrawThreadedFill
(default 1, safemode 0). The per-particle Draw() loops split the
draw list into contiguous chunks; each chunk generates its quads
into a per-chunk scratch buffer through a thread_local redirect of
the buffer returned by CExpGenSpawnable::GetPrimaryRenderBuffer,
and the scratch buffers are merged into the primary buffer in chunk
order. The submitted geometry is therefore byte-identical to a
serial fill, including back-to-front order. Each chunk opens a
ProjectileDrawer::DrawAlpha(MTChunk) Tracy zone so the distribution
is visible on the worker tracks; the ordered merge is zoned as
DrawAlpha(MTMerge).
All Draw() overrides reachable from the alpha pass were audited for
shared-state writes: none call RNG, write statics, or hit lazily
initialized caches (CColorMap::GetColor and the pre-resolved
AtlasedTexture pointers are pure reads); they only write their own
members and the buffer. The two exceptions carry the new
CProjectile::mtDrawSafe = false and are drawn serially at their
exact position in the sorted order: CTracerProjectile issues
immediate GL calls on its own VA_TYPE_TC buffer inside Draw(), and
ShieldSegmentProjectile mutates the shared per-shield
ShieldSegmentCollection latch and can fire the DrawShield Lua
callin. Segments shorter than 96 particles per chunk stay serial,
so small fills (reflection pass, quiet frames) skip the for_mt
dispatch overhead. Scratch buffers are only grown on the main
thread since RenderBuffer registration is not thread-safe.
On sync safety (particles that deal damage or spawn other
particles): damage dealing and particle spawning live exclusively
in synced simulation paths. Every guRNG call and every
projMemPool.alloc chained spawn (CWreckProjectile smoke,
CExpGenSpawner, FireBallProjectile sparks) sits in a ctor, Init()
or Update(); gsRNG is not referenced by any particle code; Draw()
bodies write only render-side members that sim code never reads.
The draw path therefore cannot reach synced state, and neither
reordering, deduplicating nor threading it can diverge the
simulation.
3. ProjectileReflectionMinRadius (default 0 = off): optionally skip
non-model alpha particles below a given draw radius in the water
reflection pass, decided in UpdateDrawFlags before the reflection
camera InView test. Small particles are barely visible in a wavy
reflection, while the reflection pass pays the full fill/sort/
quad-generation cost for them.
Also hoists the loop-invariant camera direction reads out of
CSimpleParticleSystem's non-directional draw loop.
With threading on, DrawAlpha(DS) drops ~390 us -> ~130 us and the
below-water particle pass ~1.15 ms -> ~660 us. Benchmark frame
average: 5.82 ms -> 5.22 ms; combined with the previous commit,
7.51 ms -> 5.22 ms (-30% frame time, 133 -> 192 fps) at 3x
battle-level effect load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: radix-sort the alpha particles
The back-to-front ordering used std::sort with a comparison predicate;
at battle-level counts (~20k sortable alpha particles) this cost
~184 us mean and up to ~470 us per fill, with the input-dependent
variance inherent to comparison sorting.
drawOrder and view distance are now packed into a single 64-bit key at
filter time (drawOrder as a biased int in the high half, distance
mapped to an order-preserving inverted integer in the low half; an
ascending sort yields drawOrder asc, distance desc as before), sorted
with a stable LSD radix sort: 8-bit digits, passes whose digit is
constant across all keys are skipped (with drawOrder unused the whole
high half is), and fills below 1024 elements use a plain
single-integer-compare std::sort, which wins at that size. Cost is
linear and input-independent, so the worst-case spikes disappear:
measured 31 us mean / ~75 us max (down from 184/470), with the key
packing moving a fixed ~50 us into the filter pass - a net ~100 us
saved per fill.
Two deliberate semantic notes: exactly-coincident particles (equal
drawOrder and distance, e.g. multi-part effects spawned at the same
point) are now tiebroken by stable fill order instead of pointer
address, which is frame-coherent; and NaN distances can no longer feed
a comparison predicate that violates strict weak ordering (undefined
behavior for std::sort) - the radix path handles any bit pattern.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: reuse geometry for refraction, balance fill chunks
Two refinements adopted from bruno-dasilva's draft beyond-all-reason#3037, which
independently arrived at the same overall design as this branch (pass
geometry reuse, threaded quad fill with the same two serial-exception
classes, radix sort):
- The water refraction pass views the same particles from the same
player camera as the main view, so it now re-submits the geometry
saved by the below-water pass with its own clip plane instead of
doing a filter/sort/fill of the underwater-flagged subset. Visually
equivalent: the clip plane discards everything above the surface.
Water maps now build alpha geometry twice per frame (main view +
mirrored reflection camera) instead of four times. Gated by the
existing ProjectileDrawReuseWaterPasses config.
- Fill chunks are oversubscribed 4x relative to the worker count.
Per-particle draw cost varies wildly (a smoke trail emits dozens of
quads, a small flash one), and with one chunk per worker a single
heavy chunk gated the whole dispatch. MTChunk mean dropped from
~68 us to ~26 us and benchmark 1%-low fps improved 111 -> 125.
Benchmark frame average 5.18 ms -> 4.98 ms (193 -> 201 fps); total
against the pre-series baseline 7.51 ms -> 4.98 ms (-34% frame time)
at 3x battle-level effect load on a water map.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* System: extract the radix sort into a reusable utility
Move the stable LSD radix sort out of ProjectileDrawer into
System/RadixSort.h, templated on element type and key projection, as
suggested by review on beyond-all-reason#3037 for the equivalent code there. No
functional change: ProjectileDrawer sorts by the packed 64-bit
particle key exactly as before, and the caller-owned scratch buffer
keeps its capacity across frames.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: thread the shadow-transparent particle fill
DrawShadowTransparent runs whenever shadows are enabled at all (any
shadowConfig > 0 sets SHADOWGEN_BIT_PROJ), and filled shadow-camera
billboards for every shadow-casting particle serially on the render
thread. Route it through the same FillParticleGeometry helper as the
alpha pass: the multiplicative shadow blend is order-independent, so
the list needs no sorting and chunk merge order does not matter; the
mtDrawSafe exceptions are handled by the shared helper as usual.
Gated by the existing ProjectileDrawThreadedFill config; the fill is
Tracy-zoned as DrawShadowTransparent(Fill).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: hoist per-frame invariants out of UpdateDrawFlags
The per-particle camera loop re-evaluated per-frame constants for
every projectile: IWater::GetWater()->CanDrawReflectionPass() (a
virtual call through a unique_ptr, once per particle per frame), the
projectile shadow-gen bit, the camera pointer lookups, and repeated
GetDrawRadius() calls. Hoist them ahead of the for_mt and unroll the
three-camera loop into straight-line per-camera blocks.
Flag results and sort distances are unchanged; the reflection
min-radius guard is re-expressed equivalently (hasModel || radius >=
min instead of skip-if !hasModel && radius < min).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ProjectileDrawer: declare the on/off toggles as CONFIG(bool)
ProjectileDrawThreadedFill and ProjectileDrawReuseWaterPasses are pure
on/off switches; declare them as bool configs (read via GetBool)
instead of int. Stored 1/0 values from existing configs parse the
same, so nothing user-facing changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…all-reason#2797) So we successfully update with the f-1 value, not the f value; and OwnerMoved() can be called with the proper p(f) - p(f-1) vector
Via a dummy function so that people trying to add it in the future are likely to actually stumble upon it.
…2878) Obsoleted by resource packs. No logic change, internals only. Co-authored-by: TarnishedKnight <lostsquirrel43@gmail.com>
* Updated EnTT to v3.16.0
Affects the the Lua Platform value.
* Add KeyBindingsChanged callin * Emit KeyBindingsChanged from keybinding commands
* Register key names for the keys that had none * Correct the key listing in the ui-keys reference * Deprecate KEYSYMS in favour of Spring.GetKeyCode
…ond-all-reason#3132) * fix issue where ships could get incorrect pathmap blocking zones with underwater structures near coast lines. * rename isSubmersible to hasUnderwaterCollision
* Stop unbind from removing longer chains that share the last key * Match unbind keychains exactly instead of by fit() --------- Co-authored-by: TarnishedKnight <lostsquirrel43@gmail.com>
…ed) (beyond-all-reason#3191) * Nano Particles: add NanoParticleUpdate engine callin Batched, unsynced lifecycle events for nano particles, so deferred-lighting widgets can light them without polling. Nothing emits these yet; the standalone nano particle effect added in the following commit owns the batching and the sampling that decides which particles are reported. Events are passed as one flat numeric array of 13-entry records rather than a table per event, because a per-event table would dominate the cost of the callin at the rates involved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Nano Particles: add standalone nano particle effect Adds an optional nano particle effect that renders build spray as shader-generated 3D shapes with an additive halo, behind NanoParticlesGL4 (default off). Ported from BAR's gfx_nano_particles_gl4 gadget. The effect is not a projectile. Its particles have no projectile id, take no part in collision or quadfield work, are never handed to Lua as projectiles, and are not serialised; motion is analytic, so the shader reconstructs position from start/velocity/frame and the CPU only touches a particle when it homes or has to clear terrain. All of it lives in rts/Rendering/Env/NanoParticles: NanoParticleConfig every tunable, in one place NanoParticleDefs the PODs the other three share NanoParticleSystem the particle store, homing, ground clamp, LuaUI batching NanoParticleEmitter how much spray a builder produces, and reclaim bursts NanoParticleRenderer shaders, buffers, culling, draw Legacy nano spray is untouched. NanoProjectile, ProjectileDrawer and NanoPieceCache have no diff at all; when the effect is off, or no shader path is usable, emission falls through to CNanoProjectile exactly as before. The simulation cannot tell the difference either way: a work tick still polls QueryNanoPiece once and draws one synced RNG value, and everything the effect adds runs off the unsynced RNG, as nano spray already did. Beyond the shader look the effect also carries, each behind its own setting: * emission proportional to buildSpeed * buildPower rather than one particle per work tick, so a builder's spray tracks the work it is doing instead of its nano piece count, spread round-robin over its pieces * NanoParticlesHoming, particles following moving targets * NanoParticlesGroundClamp, particles routed over intervening terrain * NanoParticlesReclaimBurst, a burst when reclaiming a unit finishes, sized by metal cost and split across the builders that contributed * NanoParticlesUpdateLuaUI, the batched callin added in the previous commit The per-unit emission accumulators and the reclaim contributor tracking are owned by the emitter rather than by CBuilder/CUnit: they are unsynced presentation state, they must not be serialised, and no part of the sim needs to know they exist. No sim class gains a member. The renderer is heap-allocated behind a pointer, as the other GL-owning drawers are. A VBO's constructor calls VBO::IsSupported(), which latches the GLAD extension flags into function-local statics on its first call; constructing one before GLAD has loaded latches them all to false and silently turns every VBO in the process into a no-op. Particles show on the minimap as the legacy ones do, reusing the vertex arrays the world pass already filtered so it costs one walk and no visibility work, and filling the shared projectile minimap buffer rather than adding a draw of its own. The spawn gate is the effect's own rather than the legacy proportional one, which throttles from the first particle and so makes emission approach the budget asymptotically instead of scaling with NanoParticlesRate. Tunables are named and documented in NanoParticleConfig.h instead of being literals spread through the sources. The visual subset reaches both shader paths as uniforms, so the geometry and instanced renderers cannot drift apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Nano Particles: changelog for the standalone effect Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * added `Engine.FeatureSupport.nanoParticlesGL4` boolean, so games can detect that the engine has the standalone nano particle effect and retire their own Lua implementation of it. * moved changelog to: doc\pr-changelogs\3191.md * Nano Particles: added configbool: NanoParticlesTargetLostFade "Nano particles fade out and shrink when the unit they were aimed at is destroyed, cancelled, or finished, instead of flying on into nothing" (ported over this feature from the gadget as well) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n modifiers (beyond-all-reason#3336) * apply terrain speed mod to max speed for drag calculations
* Sim: avoid UB in float-to-short heading casts * Move the cast into a TAAngleToShort helper * Cast up front through FloatToHeading, assert the range
…ll-reason#3268) The early block check added in beyond-all-reason#2557 runs on a position snapped to 8 elmos, while the build itself uses Pos2BuildPos, which snaps to 16 and adds half a square for an odd footprint. For an odd footprint the check therefore evaluates a footprint 8 elmos away from the one that would be placed, and can drop a build order the build itself would accept. Pos2BuildPos is idempotent, so the later call at MoveInBuildRange is left alone rather than moved, keeping the change to the check itself.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated upstream sync hit conflicts. Merge
master(now level with beyond-all-reason/RecoilEngine) intomainand resolve here.