External weapon model refactor: external models on turrets - #7752
Draft
Goober5000 wants to merge 6 commits into
Draft
External weapon model refactor: external models on turrets#7752Goober5000 wants to merge 6 commits into
Goober5000 wants to merge 6 commits into
Conversation
…up queries Three engine additions in preparation for migrating external weapon model gun rotation to the animation system: - "seamless forward shutdown" animation flag: while a "seamless with startup" animation plays its shutdown (or is otherwise reversed), its motion is mirrored about the pose where the reversal began, so the motion continues forward while winding down (a decelerating forward spin for gun barrels) instead of retracing backwards. The mirror pivot is captured whenever an animation enters reverse; the mirrored pose is this animation's delta reflected about its pivot delta, composed onto the apply buffer in place of the normal calculation. - $Spin Up: segment: rotates a submodel in PBH with constant acceleration up to a target angular velocity and ends while still moving at that velocity, unlike $Rotation:, which always brakes to hit its target angle. Intended as the startup portion of a seamless looping animation, followed by a constant-velocity $Rotation: covering one full cycle, so pose and velocity are continuous at the loop seam. - AnimationList::isFullyStarted() (true once every animation is "up to speed": playing forward within the seamless loop portion, or completed for non-looping animations) and AnimationList::startShutdown() (looping animations finish their current loop and stop -- entering the seamless shutdown -- while others play in reverse). These are the firing-gate and trigger-release primitives for weapon warmup animations. Also, an explicit forward start now cancels a pending or in-progress seamless shutdown, so a re-triggered animation spins back up from its current state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the "weapon-warmup" animation trigger type for weapon-owned animation sets ($Animations: in weapons.tbl, which already existed for the in-flight model's on-spawn and scripted triggers). Warmup animations target the weapon's external display model: they start when the bank tries to fire, the bank holds fire until they are fully started, and they wind down when the bank stops firing. Weapon-owned primary-fired and secondary-fired animations now also trigger on the bank's external model with each shot, enabling recoil animations. Wiring: - ship_get_external_weapon_model_instance() is generalized to any bank (secondaries can now have instances) and creates an instance whenever the weapon has animations, not just for Gun_rotation submodels. It also stops running animations before deleting a stale instance, as does ship_delete(). - update_external_weapon_animations() (formerly ..._spin) ensures the bank instance exists even for ships that are never rendered, starts or shuts down warmup animations on firing-state transitions, and steps each bank instance's animations -- these instances belong to no object, so the object-loop stepping never reaches them. - ship_fire_primary() gates firing on AnimationList::isFullyStarted() for the weapon's warmup animations, parallel to the legacy submodel-rotation gate. Also, ModelAnimation::start() no longer takes the multiplayer early-return path for model instances that belong to no object (external weapon models, skybox, cockpit): such animations cannot be synced by object and now simply run locally on each machine; previously multiplayer clients would never start them at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
$Submodel Rotation Speed: / $Submodel Rotation Acceleration: plus the Gun_rotation submodel flag are now a parse-time shim: at weapon page-in the tabled values are converted into a synthesized weapon-warmup animation on the weapon's display model, unless the modder already supplied one. Each Gun_rotation submodel gets a $Spin Up: ramp to the tabled velocity about its bank axis followed by one full revolution at speed, looping seamlessly, with the forward-shutdown flag so barrels decelerate forward on release -- reproducing the old hardcoded behavior, including the firing gate. If the display model has no Gun_rotation submodels, the old system still delayed firing by the spin-up time, so a segmentless wait animation preserves that gate. The hand-rolled spin machinery is deleted: the per-bank rotate rate/angle state, the integration in update_external_weapon_spin, the firing gate on rotate_rate, and the render-time bashing of canonical_orient on Gun_rotation submodels are all replaced by the warmup animation path added in the previous commit. One deliberate behavior change: a weapon tabled with rotation velocity but zero acceleration could never fire under the old system (its spin rate never increased); it now spins up instantly and fires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New subsystem flag "show external weapon model": the turret's weapon model (first weapon with a displayable model, secondaries preferred; external model with fallback to the weapon's own model) is rendered at each of the turret's firing points, transformed through the turret's submodel chain so it tracks base rotation and barrel elevation. This makes a missile visible in its launcher while a turret-firing (trap door) animation plays, per-firing-point: - When the turret fires, the fired point's model is hidden exactly as the live missile spawns at the same transformed position, making the swap seamless (also orientation-seamless for "fire down normals" turrets). - The model stays hidden until the turret's firing and post-firing (turret-fired) animations have fully completed, tracked with the new AnimationList::anyActive() query rather than timestamps -- which also gives multiplayer clients exact timing, since those animations arrive via the existing animation sync. - The new "weapon-reload" animation type then plays if the ship defines one; the weapon model reappears when it completes, since the animation may show the round being moved into place. Without one, the model simply reappears. The weapon-reload type is dual-keyed: +Triggered By: takes either the name of a turret subsystem, or an integer, reserved for a secondary bank index for reload animations on ship weapon banks. Setting the subsystem flag implies the ship-level Draw_weapon_models flag so the render path activates. Destroyed turrets show nothing. Beam turrets never empty their points (no projectile to display). Multiplayer: the server empties firing points in turret_fire_weapon and turret_swarm_fire_from_turret; clients do the same in the turret- and flak-fired packet handlers, cycling their own firing points -- visually consistent even if the cycle drifts from the server's. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A weapon-reload animation keyed by secondary bank index (+Triggered By: <bank>) now replaces the hardcoded slide-back reload visual for that bank's external weapon models: when a point fires, the animation starts and the point's missile stays hidden until it completes, then appears at its full position. Banks without a reload animation keep the classic slide-back. Applies to both the in-mission render and the HUD hardpoints gauge. The animation starts immediately on firing (banks have no door or post-firing phase to wait for, unlike turrets). If it is longer than the bank's fire wait, a fast-firing bank can fire from a still-hidden point -- coordinating those times is up to the ship designer, matching the stance taken for weapon-warmup. Since a bank's points share one animation, simultaneously reloading points restart it; staggered single fire looks correct. Multiplayer works without extra plumbing: ship_fire_secondary runs on every machine via the existing secondary-fired packets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
For an auto-reversing reload animation (e.g. a loader arm that extends with the round and retracts empty), the external weapon model should reappear at the moment the round is placed -- mid-animation -- rather than at the end. The optional +Model Spawn Time: key (seconds, after $Flags:) lets the designer specify that moment; it is stored in the animation's flag data alongside loopsFrom. AnimationList::getModelSpawnTime() honors the key, falling back to the animation's duration when unset, and both reload consumers (turrets and secondary banks) now use it for their reappear stamps. The spawn time is deliberately not capped to the duration, since an auto-reversing animation's return leg plays past the one-way duration that getTime() reports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Goober5000
force-pushed
the
feature/external_turret_model
branch
from
August 28, 2026 23:32
157b3a9 to
64261d8
Compare
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.
Follow-up to #7751. This adds the feature that motivated the whole series: external model support for turrets, complete with firing animations.
Add external weapon models on turrets. With the new
"show external weapon model"subsystem flag, the turret's weapon model is rendered at each of the turret's firing points, transformed through the turret's submodel chain so it tracks base rotation and barrel elevation. When the turret fires, the fired point's model is hidden exactly as the live missile spawns at the same transformed position, making the swap seamless. The model stays hidden until the turret'sturret-firingandturret-firedanimations have fully completed. Destroyed turrets show nothing, and beam turrets never empty their points.Add the
weapon-reloadanimation type. After the post-firing animations complete, a reload animation plays if the ship defines one, and the weapon model reappears when it completes, since the animation may show the round being moved into place; without one, the model simply reappears. The type is dual-keyed:+Triggered By:takes the name of a turret subsystem, or a secondary bank index. The optional+Model Spawn Time:key lets the designer specify the exact moment of reappearance instead — for example the apex of an auto-reversing loader arm, or any point during its return leg.Support
weapon-reloadon ship secondary banks. A reload animation keyed by bank index replaces the hardcoded slide-back reload visual for that bank: when a point fires, the animation starts and the point's missile stays hidden until it completes, then appears at its full position. Banks without one keep the classic slide-back. Applies to both the in-mission render and the HUD hardpoints gauge.Multiplayer is supported. The server empties firing points when turrets fire (including the swarm path); clients do the same in the turret- and flak-fired packet handlers, cycling their own firing points. Reappear timing is exact on clients too, because it is derived from the turret's animation state (via a new
AnimationList::anyActive()query rather than timestamps), and those animations already arrive via the existing animation sync. Banks need no extra plumbing, sinceship_fire_secondaryruns on every machine.In draft until #7751 is merged.