Skip to content

External weapon model cleanup: consolidate duplicated logic, fix several longstanding bugs#7588

Open
Goober5000 wants to merge 6 commits into
scp-fs2open:masterfrom
Goober5000:cleanup/external_weapons
Open

External weapon model cleanup: consolidate duplicated logic, fix several longstanding bugs#7588
Goober5000 wants to merge 6 commits into
scp-fs2open:masterfrom
Goober5000:cleanup/external_weapons

Conversation

@Goober5000

Copy link
Copy Markdown
Contributor

This is a cleanup pass over the external weapon model code ($Show Primary/Secondary Models:), consolidating duplicated logic and fixing the bugs that were hiding in the copies.

Consolidations

  • Weapon page-inweapons_page_in(), weapons_page_in_cheats(), and weapon_page_in() each duplicated the same ~60-line per-weapon block (model load, thruster flag, external model load, shockwaves, graphics paging). Extracted into weapon_page_in_one(); the cheats variant passes load_graphics = false to preserve its model-only behavior.
  • Firing point selection — the "offset the shot by the external model's firing points" logic was copied in ship_fire_primary(), ship_fire_secondary(), and the AI line-of-sight check. Now shared as ship_get_external_model_fp_offset().
  • Render slot placement — the per-slot math (angle-offset yaw, missile reload slide-back) was duplicated four times across ship_render_weapon_models() and HudGaugeHardpoints::render(). Now shared as ship_get_weapon_model_slot_transform().

Bug fixes

  • "chain external model fps" never chained. All three copies of the firing point logic reset the chain counter against n_guns (the number of gun banks) instead of the number of firing points in bank 0. For the typical one-bank model the counter reset after every shot, so chaining never advanced; models with several banks could read past the end of the point array.
  • The AI line-of-sight check advanced the chain counter, even though it's only a query, desyncing it from the shots actually fired.
  • Gatling barrel-spin instances went stale after a mid-mission weapon swap. The lazily created model instance was guarded by a once-per-bank flag that nothing reset, so after set-primary-weapon, a script swap, or a rearm-precedence swap, the old weapon's instance leaked and the new weapon's barrels never spun. The instance is now keyed to the weapon it was created for and recreated on change (ship_get_external_weapon_model_instance()), which self-heals after every swap path.
  • $Weapon Model Draw Distance: has been dead for years — nothing set MR_ATTACHED_MODEL since 1ef7688. The distance is now carried in model_render_params and enforced again. The old check also computed depth as if the weapon model's ship-local position were in world space; depth is now measured from the eye position in the parent's frame, which additionally fixes attached weapon models choosing their LOD from a garbage distance. MR_ATTACHED_MODEL is removed (nothing else referenced it).
  • Hardpoints gauge misplaced launcher models — the external model launcher branch computed the view-rotated position but rendered at the untransformed local point.
  • Never-firing assertion — the invalid-render-type assertion in the page-in code was a tautology inside its default case; it now asserts render_type == WRT_NONE.
  • Empty-bank hardening — the render paths and ship_stop_fire_primary_bank() indexed Weapon_info[] without checking for an empty bank (-1), unlike other bank-iterating code.

Notes for modders

  • Weapons with "chain external model fps" will now actually cycle through the external model's firing points; previously every shot came from point 0.
  • $Weapon Model Draw Distance: (default 200) is enforced again, and attached weapon models now LOD correctly with distance — missions far from the world origin previously got worst-case LODs regardless of camera distance.

🤖 Generated with Claude Code

@Goober5000 Goober5000 added cleanup A modification or rewrite of code to make it more understandable or easier to maintain. fix A fix for bugs, not-a-bugs, and/or regressions. models Issues or features having to do with model data (like animations or geometry) Requested by Active Mod A feature request that has been requested by a mod that is actively in development. labels Jul 9, 2026
@Goober5000
Goober5000 force-pushed the cleanup/external_weapons branch 3 times, most recently from 7cdf52b to 24df058 Compare July 9, 2026 02:16
Goober5000 and others added 6 commits July 13, 2026 13:19
weapons_page_in(), weapons_page_in_cheats(), and weapon_page_in() each
duplicated the same ~60-line block for loading a weapon's models and
graphics.  Extract it into a weapon_page_in_one() helper; the cheats
variant passes load_graphics = false to preserve its behavior of
loading only model geometry.

Also fix the tautological Assertion in the default render_type case:
the old condition was always true inside that branch, so it could never
fire.  It now asserts render_type == WRT_NONE, which is the only other
legitimate value (invisible weapons).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The logic for offsetting a shot by the external weapon model's firing
points was duplicated in ship_fire_primary(), ship_fire_secondary(),
and the AI's line-of-sight check.  Extract it into
ship_get_external_model_fp_offset().

This fixes two bugs that were present in all three copies:

1. The chain counter for "chain external model fps" weapons was reset
   when it reached weapon_model->n_guns (the number of gun banks)
   rather than the number of firing points in the bank being indexed.
   For the typical model with one gun bank the counter reset after
   every shot, so chaining never advanced past the first firing point;
   for a model with several gun banks the counter could index past the
   end of the first bank's point array.

2. The AI line-of-sight check advanced the chain counter even though it
   is only a query, desyncing the counter from the shots actually
   fired.  It now reads the current firing point without advancing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sition

The per-slot placement math for rendered external weapon models (the
angle-offset yaw plus the reload slide-back for missiles) was
duplicated four times across ship_render_weapon_models() and
HudGaugeHardpoints::render().  Extract it into
ship_get_weapon_model_slot_transform() so that future changes (such as
submodel-attached weapon banks) only need to touch one place.

This fixes a bug in the hardpoints gauge: the launcher-style secondary
branch computed the view-rotated world position but then rendered the
model at the untransformed local point, misplacing launcher models in
the rotated top/front views.

Also: hoist per-bank invariants out of the slot loops, drop the
redundant render_flags parameter of ship_render_weapon_models() (it
always equaled the flags already set on the render params), remove
long-dead commented-out code, and read the reload percentage once per
slot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The model instance used to spin Gun_rotation submodels (gatling
barrels) was created lazily on first render, guarded by a
once-per-bank bool that nothing ever reset.  If a primary bank's
weapon changed mid-mission (set-primary-weapon SEXP, scripting, rearm
precedence swap, multiplayer sync), the old weapon's instance leaked
until the ship was deleted and was silently discarded at render time
by the model/instance mismatch guard, while the new weapon never got
an instance, so its barrels never spun.

Replace the bool with a record of which weapon the instance was
created for, and move the logic into
ship_get_external_weapon_model_instance(), which recreates the
instance whenever the bank's weapon differs.  This self-heals after
every swap path without needing hooks in each of them.  The HUD
hardpoints gauge now goes through the same function instead of reading
the possibly-stale instance array directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The draw-distance cull for attached external weapon models checked
MR_ATTACHED_MODEL, but nothing has set that flag since commit
1ef7688, so $Weapon Model Draw Distance: has been dead for years.
The check also needed the ship resolved from the render params' object
number, which is deliberately not set while weapon models render
(setting it would make model_render_queue clobber the explicitly
passed gatling model instance), and it compared against a depth
computed as if the weapon model's ship-local position were in world
space.

Carry the distance in model_render_params instead
(set_attached_model_draw_distance, negative = not an attached model),
set by ship_render_weapon_models from the ship's tabled value.  When
set, model_render_queue measures depth from the eye position expressed
in the parent's frame (model_draw_list::get_view_position), which is
correct under the pushed parent transform, culls against the tabled
distance, and uses that same corrected depth for detail level
selection -- previously attached weapon models picked their LOD from a
garbage depth (roughly the eye's distance from the world origin).

MR_ATTACHED_MODEL is removed; nothing else referenced it. The unused
flags are also removed and the remaining flags are consolidated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ship_render_weapon_models(), HudGaugeHardpoints::render(), and
ship_stop_fire_primary_bank() indexed Weapon_info[] with the bank's
weapon index without checking for an empty bank (-1).  Banks within
num_*_banks are normally populated, but other code that iterates banks
treats them as possibly empty and guards accordingly (e.g.
ship_do_cargo_hidden, ship_page_in), so these paths now do too.  In
the hardpoints gauge an empty primary bank still draws its firing
point circles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Goober5000
Goober5000 force-pushed the cleanup/external_weapons branch from 24df058 to 009529b Compare July 13, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup A modification or rewrite of code to make it more understandable or easier to maintain. fix A fix for bugs, not-a-bugs, and/or regressions. models Issues or features having to do with model data (like animations or geometry) Requested by Active Mod A feature request that has been requested by a mod that is actively in development.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant