Skip to content

External weapon model refactor: convert legacy gun rotation - #7751

Open
Goober5000 wants to merge 3 commits into
scp-fs2open:masterfrom
Goober5000:refactor/gun_rotation_migration
Open

External weapon model refactor: convert legacy gun rotation#7751
Goober5000 wants to merge 3 commits into
scp-fs2open:masterfrom
Goober5000:refactor/gun_rotation_migration

Conversation

@Goober5000

Copy link
Copy Markdown
Contributor

Follow-up to #7634, migrating gatling gun rotation into the model animation system.

  • Add the weapon-warmup animation type. Weapon-owned $Animations: sets (which already existed for on-spawn and scripted triggers on the in-flight model) gain an engine trigger that targets the weapon's external display model: the animation starts when the bank tries to fire, the bank holds fire until it is fully started, and it winds down when the bank stops firing. Following the review of External weapon model refactor: consolidate per-bank state, fix design issues #7634, the animation update runs in ship_process_pre, so the firing gate sees the current frame's state for both the player and the AI.

  • Trigger per-shot animations on the external model. Weapon-owned primary-fired / secondary-fired animations now fire on the bank's external model with each shot, enabling recoil animations. Secondary banks get model instances for this too.

  • Convert old-style gun rotation into a parse-time shim. $Submodel Rotation Speed: / $Submodel Rotation Acceleration: plus the Gun_rotation submodel flag now synthesize a seamless warmup animation at weapon page-in (unless the modder supplied one), reproducing the old behavior including the spin-up firing gate — and the hand-rolled spin machinery is deleted. One deliberate change: a weapon tabled with rotation velocity but zero acceleration could never fire under the old system; it now spins up instantly.

  • Add the engine pieces the above needs. A "seamless forward shutdown" animation flag (the shutdown of a seamless looping animation continues forward while decelerating, instead of retracing backwards); a $Spin Up: segment that accelerates to a target angular velocity and ends at speed, unlike $Rotation:, which always brakes to hit its angle; and AnimationList::isFullyStarted() / startShutdown() as the firing-gate and trigger-release primitives.

  • Fix animations on object-less model instances in multiplayer. ModelAnimation::start() no longer takes the multiplayer early-return path for model instances that belong to no object (external weapon models, the skybox, cockpits); such animations cannot be object-synced and now simply run locally on each machine. Previously multiplayer clients never started them at all.

@Goober5000
Goober5000 force-pushed the refactor/gun_rotation_migration branch from f5e7e54 to 11e8cde Compare August 28, 2026 20:47
…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>
Goober5000 and others added 2 commits August 28, 2026 18:46
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>
@Goober5000
Goober5000 force-pushed the refactor/gun_rotation_migration branch from bbfa5c1 to 90aea96 Compare August 28, 2026 22:46
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