Compare commits

...
Author SHA1 Message Date
SirKnightly 8b88858f63 Automated build commit 2026-11-12 14:01:15 +00:00
Goober5000 185b2fbae5 Merge pull request #7989 from AdmiralTolwyn/vulkan-hud-mask-8bit
Vulkan: keep AABITMAP HUD masks 8-bit in preload and animation frames
2026-11-11 23:21:54 -05:00
Goober5000 19fd33fc47 Merge pull request #7862 from Goober5000/feature/flashScreenAlpha
Add gr.flashScreenAlpha() for a blended screen flash
2026-11-11 23:09:22 -05:00
Goober5000 436b520e65 Merge pull request #7845 from Goober5000/enhancement/mission_list_iterators
Add getBeamList, getWeaponList, getFireballList, and getPropList
2026-11-11 23:08:29 -05:00
Mike Nelson c64365d108 QtFRED help preferences (#7928)
* remove the unregistered duplicate qtFRED Preferences help page

Two Preferences pages exist, both added in #7380:
general/PreferencesDialog.html, which qtfred.qhp registers as its section,
keyword and file, and dialogs/PreferencesDialog.html, which nothing registers.

An unregistered page is not compiled into qtfred_help.qch, so the second page has
never been reachable in the built help. It is also out of date: its General table
lists "Move ships with arrow keys", "Check for errors on save", "Show SEXP help
tooltips" and a Dark mode checkbox, none of which are on that tab, and its
Keybindings heading describes what the dialog calls Controls. The registered page
documents the dialog as it is, including autosave, the grid and remapping
controls.

Nothing links to the duplicate, so removing it changes no link and leaves the
built help identical.

* fix the Mission Specs link on the qtFRED Music Player help page

The link pointed at MissionSpecDialog.html; the page is MissionSpecsDialog.html,
so the link went nowhere in the built help.

* drop the help index entry for a Form Wing page that does not exist

index.html listed Form Wing under Objects and linked dialogs/FormWingDialog.html,
which is not in the tree and is not registered in qtfred.qhp, so the entry was a
dead link. The dialog is a single text box for the wing's name, and forming a
wing is already covered in the quickstart, the fundamentals page and the toolbar
reference, so no page is needed.

* register the Custom Data concept help page

concepts/custom-data.html was in the tree and linked from the Mission Specs
dialog page, but was not listed in qtfred.qhp, so it was not compiled into
qtfred_help.qch and the link to it went nowhere. Adds it to the Concepts section
of the table of contents, the keyword index and the file list.
2026-11-11 09:02:40 -05:00
Saga portandClaude Opus 5.5 a08a7d5094 Vulkan: fix the AABITMAP preload build and correct the sibling-frame comment
Name vulkan_preload's is_aabitmap parameter. The previous commit reads it
but left the name commented out, so Vulkan builds failed.

The sibling-frame change matters for EFF animations of 8-bit PCX frames,
not ANI: ANI and APNG decode every frame in the triggering lock. Say so,
and name why other 8bpp sources are safe to treat as AABITMAP.

Log the chosen format for AABITMAP preloads and let them bypass the log
cap, so a playtest can confirm R8 for HUD handles.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 10:48:47 +01:00
Saga port e598d11b52 Vulkan: keep AABITMAP HUD masks 8-bit in preload and animation frames
vulkan_preload ignored is_aabitmap and locked uncompressed HUD art at
32 bpp with BMP_TEX_XPARENT. bm_lock uploads what it locks, so that RGBA
image satisfied every later bind and the 8-bit BMP_AABITMAP lock in
bindMaterialTextures never ran: the HUD shader read palette colour as
coverage. Preload now locks AABITMAPs at 8 bpp, like OpenGL's preload.

uploadAnimationFrames locked the remaining ANI frames with bm->flags,
which never retain BMP_AABITMAP. An 8-bpp decoded frame can only come
from an AABITMAP lock, so recover the policy from the frame depth.
2026-11-03 19:19:37 +01:00
Goober5000andClaude Opus 5.5 31dd7a9545 Add gr.flashScreenAlpha() for a blended screen flash
gr.flashScreen() adds its color to every pixel after tonemapping, so
during a strong flash any bright effect on screen clips to white.  The
MediaVPs blinding effect does this for several seconds after big
explosions, flattening impact flashes into featureless white discs.

flashScreenAlpha() blends the screen toward the color instead, using the
existing gr_flash_alpha() path.  Over a black background the result is
the same as flashScreen, but bright pixels keep their detail.  Also
describe what flashScreen actually does in its help text.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 02:19:16 -04:00
Goober5000andClaude Fable 5.1 30bf2981cc Add getBeamList, getWeaponList, getFireballList, and getPropList
The Beams, Weapons, Fireballs, and Props mission sub-libraries index
by rescanning their backing array on every lookup, so iterating them
by index is quadratic.  Give each one an iterator like the existing
getShipList and getDebrisList.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-11 22:00:55 -04:00
9 changed files with 180 additions and 56 deletions

No files matched your search

+8 -5
View File
@@ -676,12 +676,15 @@ bool VulkanTextureManager::uploadAnimationFrames(int handle, bitmap* bm, int com
// Build per-layer copy regions and upload each frame's data
SCP_vector<vk::BufferImageCopy> copyRegions;
// Use the same lock parameters that were used for the triggering frame.
// bm->flags contains the lock flags (BMP_AABITMAP, BMP_TEX_OTHER, BMP_TEX_DXT*, etc.)
// bm->bpp contains the requested bpp. Using these ensures all frames are locked
// consistently (e.g., 8bpp for aabitmaps, 32bpp for RGBA textures).
// BMPMAN's bitmap flags describe converted pixel data and do not retain the
// BMP_AABITMAP lock flag. EFF animations load each frame separately, so an
// 8-bit PCX sibling locked with flags 0 would trip the bpp assert in
// bm_convert_format. ANI and APNG decode every frame in the triggering lock,
// so their sibling locks load nothing. Other 8bpp sources are safe to treat
// the same way: the standalone server never runs Vulkan, user bitmaps are
// never multi-frame, and the PNG and DDS loaders ignore lock flags.
auto lockBpp = bm->bpp;
auto lockFlags = bm->flags;
auto lockFlags = (lockBpp == 8) ? BMP_AABITMAP : bm->flags;
// Set guard flag to make recursive bm_data calls no-ops
m_uploadingAnimation = true;
+15 -5
View File
@@ -17,7 +17,7 @@ namespace graphics::vulkan {
// ========== gr_screen function pointer implementations ==========
int vulkan_preload(int bitmap_num, int /*is_aabitmap*/)
int vulkan_preload(int bitmap_num, int is_aabitmap)
{
auto* texManager = getTextureManager();
@@ -76,8 +76,14 @@ int vulkan_preload(int bitmap_num, int /*is_aabitmap*/)
lockFlags = BMP_TEX_ETC2_RGBA1;
break;
default:
// Uncompressed — use 32bpp decompressed
// AABITMAP art is a palette-index mask. Keep the indices as R8 so
// the HUD shader can use them as coverage instead of RGB color data,
// matching the draw-time AABITMAP lock in bindMaterialTextures.
compType = 0;
if (is_aabitmap) {
lockBpp = 8;
lockFlags = BMP_AABITMAP;
}
break;
}
@@ -99,9 +105,13 @@ int vulkan_preload(int bitmap_num, int /*is_aabitmap*/)
if (success) {
static int successCount = 0;
if (successCount < 10) {
nprintf(("vulkan", "vulkan_preload: Successfully uploaded texture %d (compressed=%d)\n",
bitmap_num, compType));
// AABITMAP uploads bypass the cap so a playtest can confirm R8 for HUD handles,
// which page in long after the first few textures.
if (successCount < 10 || is_aabitmap) {
auto* uploaded = texManager->getTextureSlot(bitmap_num);
nprintf(("vulkan", "vulkan_preload: Successfully uploaded texture %d (compressed=%d, aabitmap=%d, format=%s)\n",
bitmap_num, compType, is_aabitmap,
uploaded ? vk::to_string(uploaded->format).c_str() : "none"));
successCount++;
}
}
+39 -1
View File
@@ -2002,7 +2002,8 @@ ADE_FUNC(getImageHeight, l_Graphics, "string name", "Gets image height", "number
ADE_FUNC(flashScreen,
l_Graphics,
"number|color /* red value or color object */, number Green, number Blue",
"Flashes the screen",
"Flashes the screen by adding the given color (0-255 per channel) to every pixel. "
"Bright parts of the scene can clip to white; see flashScreenAlpha for a blended flash.",
nullptr,
nullptr)
{
@@ -2033,6 +2034,43 @@ ADE_FUNC(flashScreen,
return ADE_RETURN_NIL;
}
ADE_FUNC(flashScreenAlpha,
l_Graphics,
"number|color /* red value or color object */, number Green, number Blue, number Alpha",
"Flashes the screen by blending every pixel toward the given color by Alpha (0-255), "
"so bright parts of the scene keep their detail instead of clipping as with flashScreen. "
"When a color object is passed, its alpha is used.",
nullptr,
nullptr)
{
if(!Gr_inited)
return ADE_RETURN_NIL;
int r, g, b, a;
if (lua_isnumber(L, 1)) {
if (!ade_get_args(L, "iiii", &r, &g, &b, &a))
return ADE_RETURN_NIL;
} else {
color col;
gr_init_alphacolor(&col, 0, 0, 0, 255);
if (!ade_get_args(L, "o", l_Color.Get(&col)))
return ADE_RETURN_NIL;
r = col.red;
g = col.green;
b = col.blue;
a = col.alpha;
}
gr_flash_alpha(r, g, b, a);
return ADE_RETURN_NIL;
}
ADE_FUNC(loadModel, l_Graphics, "string Filename", "Loads the model - will not setup subsystem data, DO NOT USE FOR LOADING SHIP MODELS", "model", "Handle to a model")
{
const char* s;
+112
View File
@@ -3144,6 +3144,62 @@ ADE_FUNC(getMissileList,
}));
}
ADE_FUNC(getWeaponList,
l_Mission,
nullptr,
"Get an iterator to the list of weapons in this mission",
"iterator<weapon>",
"An iterator across all weapons in the mission, including lasers, missiles, and flak. Can be used in a for .. in loop. Is not valid for more than one frame.")
{
int idx = 0;
return ade_set_args(L, "u", luacpp::LuaFunction::createFromStdFunction(L, [idx](lua_State* LInner, const luacpp::LuaValueList& /*params*/) mutable -> luacpp::LuaValueList {
// iterate through the Weapons array to find the next live weapon
while (idx < MAX_WEAPONS) {
const weapon& wp = Weapons[idx];
idx++;
// unused slots have objnum -1
if (wp.objnum >= 0) {
if (!Objects[wp.objnum].flags[Object::Object_Flags::Should_be_dead]) {
return luacpp::LuaValueList{ luacpp::LuaValue::createValue(LInner, l_Weapon.Set(object_h(wp.objnum))) };
}
}
}
// end of list or no more live weapons found
return luacpp::LuaValueList{ luacpp::LuaValue::createNil(LInner) };
}));
}
ADE_FUNC(getBeamList,
l_Mission,
nullptr,
"Get an iterator to the list of beams in this mission",
"iterator<beam>",
"An iterator across all beams in the mission. Can be used in a for .. in loop. Is not valid for more than one frame.")
{
size_t idx = 0;
return ade_set_args(L, "u", luacpp::LuaFunction::createFromStdFunction(L, [idx](lua_State* LInner, const luacpp::LuaValueList& /*params*/) mutable -> luacpp::LuaValueList {
// iterate through the Beams array to find the next active beam
while (idx < Beams.size()) {
const beam& b = Beams[idx];
idx++;
// inactive and freed beams both have objnum -1
if (b.objnum >= 0) {
if (!Objects[b.objnum].flags[Object::Object_Flags::Should_be_dead]) {
return luacpp::LuaValueList{ luacpp::LuaValue::createValue(LInner, l_Beam.Set(object_h(b.objnum))) };
}
}
}
// end of list or no more active beams found
return luacpp::LuaValueList{ luacpp::LuaValue::createNil(LInner) };
}));
}
ADE_FUNC(getAsteroidList,
l_Mission,
nullptr,
@@ -3204,6 +3260,62 @@ ADE_FUNC(getDebrisList,
}));
}
ADE_FUNC(getFireballList,
l_Mission,
nullptr,
"Get an iterator to the list of fireballs in this mission",
"iterator<fireball>",
"An iterator across all fireballs in the mission. Can be used in a for .. in loop. Is not valid for more than one frame.")
{
size_t idx = 0;
return ade_set_args(L, "u", luacpp::LuaFunction::createFromStdFunction(L, [idx](lua_State* LInner, const luacpp::LuaValueList& /*params*/) mutable -> luacpp::LuaValueList {
// iterate through the Fireballs vector to find the next live fireball
while (idx < Fireballs.size()) {
const fireball& fb = Fireballs[idx];
idx++;
// deleted fireballs keep their slot with objnum -1
if (fb.objnum >= 0) {
if (!Objects[fb.objnum].flags[Object::Object_Flags::Should_be_dead]) {
return luacpp::LuaValueList{ luacpp::LuaValue::createValue(LInner, l_Fireball.Set(object_h(fb.objnum))) };
}
}
}
// end of list or no more live fireballs found
return luacpp::LuaValueList{ luacpp::LuaValue::createNil(LInner) };
}));
}
ADE_FUNC(getPropList,
l_Mission,
nullptr,
"Get an iterator to the list of props in this mission",
"iterator<prop>",
"An iterator across all props in the mission. Can be used in a for .. in loop. Is not valid for more than one frame.")
{
size_t idx = 0;
return ade_set_args(L, "u", luacpp::LuaFunction::createFromStdFunction(L, [idx](lua_State* LInner, const luacpp::LuaValueList& /*params*/) mutable -> luacpp::LuaValueList {
// iterate through the Props vector to find the next live prop
while (idx < Props.size()) {
const auto& opt_prop = Props[idx];
idx++;
// deleted props leave an empty optional behind
if (opt_prop.has_value() && opt_prop->objnum >= 0) {
if (!Objects[opt_prop->objnum].flags[Object::Object_Flags::Should_be_dead]) {
return luacpp::LuaValueList{ luacpp::LuaValue::createValue(LInner, l_Prop.Set(object_h(opt_prop->objnum))) };
}
}
}
// end of list or no more live props found
return luacpp::LuaValueList{ luacpp::LuaValue::createNil(LInner) };
}));
}
ADE_FUNC(waitAsync,
l_Mission,
"number seconds",
@@ -11,7 +11,7 @@
<p>Opens via <strong>View &rsaquo; Music Player</strong>.</p>
<p>Plays back music files from the game's music folder while you work, so you can
audition tracks without launching FreeSpace Open. Useful when choosing tracks for
<a href="MissionSpecDialog.html">Mission Specs</a>,
<a href="MissionSpecsDialog.html">Mission Specs</a>,
<a href="BriefingEditorDialog.html">Briefing</a>,
<a href="DebriefingDialog.html">Debriefing</a>, or
<a href="CommandBriefingDialog.html">Command Briefing</a>.</p>
@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Preferences - QtFRED Help</title>
<link rel="stylesheet" type="text/css" href="../css/help.css"/>
</head>
<body>
<nav class="breadcrumb"><a href="../index.html">QtFRED Help</a> &rsaquo; Tools &rsaquo; Preferences</nav>
<h1>Preferences</h1>
<p>Opens via <strong>File &rsaquo; Preferences</strong>.</p>
<p>Controls QtFRED editor settings. Changes take effect immediately; there is no
Apply button.</p>
<h2>General</h2>
<table>
<tr><th>Setting</th><th>Description</th></tr>
<tr><td>Move ships with arrow keys</td><td>When enabled, the arrow keys nudge
selected objects in the viewport instead of scrolling the view.</td></tr>
<tr><td>Check for errors on save</td><td>Runs the built-in mission error checker
automatically each time the mission is saved.</td></tr>
<tr><td>Show SEXP help tooltips</td><td>Displays documentation tooltips when
hovering over SEXP nodes in the condition and action trees.</td></tr>
<tr><td>Dark mode</td><td>Switches the editor UI to a dark color
theme.</td></tr>
</table>
<h2>Grid</h2>
<table>
<tr><th>Setting</th><th>Description</th></tr>
<tr><td>Grid plane</td><td>Which plane the editor grid lies on:
<kbd>XZ</kbd> (horizontal), <kbd>XY</kbd>, or <kbd>YZ</kbd>.</td></tr>
<tr><td>Grid center</td><td>The world-space coordinates of the grid's center
point.</td></tr>
<tr><td>Reset grid</td><td>Resets the grid plane and center to defaults.</td></tr>
</table>
<h2>Keybindings</h2>
<p>Lists every editor action alongside its current keyboard shortcut. Click a shortcut
field and press a new key combination to rebind it. Conflicts are highlighted.
Bindings are saved per-user and persist across sessions.</p>
</body>
</html>
-1
View File
@@ -68,7 +68,6 @@ covers the basics of mission creation, the editor dialogs, and advanced topics.<
<h3>Objects</h3>
<ul>
<li><a href="dialogs/FormWingDialog.html">Form Wing</a></li>
<li><a href="dialogs/JumpNodeEditorDialog.html">Jump Node Editor</a></li>
<li><a href="dialogs/ObjectOrientEditorDialog.html">Object Orientation</a></li>
<li><a href="dialogs/PropEditorDialog.html">Prop Editor</a></li>
+3
View File
@@ -10,6 +10,7 @@
<section title="Concepts">
<section title="AI" ref="concepts/ai.html"/>
<section title="Campaign Flow" ref="concepts/campaign-flow.html"/>
<section title="Custom Data" ref="concepts/custom-data.html"/>
<section title="IFF &amp; Teams" ref="concepts/iff.html"/>
<section title="Messages" ref="concepts/messages.html"/>
<section title="SEXPs" ref="concepts/sexps.html"/>
@@ -93,6 +94,7 @@
<keyword name="AI" id="concept.ai" ref="concepts/ai.html"/>
<keyword name="Messages" id="concept.messages" ref="concepts/messages.html"/>
<keyword name="Campaign Flow" id="concept.campaignflow" ref="concepts/campaign-flow.html"/>
<keyword name="Custom Data Concepts" id="concept.customdata" ref="concepts/custom-data.html"/>
<keyword name="Viewport" id="concept.viewport" ref="general/Viewport.html"/>
<keyword name="Toolbars" id="concept.toolbar" ref="general/Toolbars.html"/>
<keyword name="Custom Data" id="CustomDataDialog" ref="dialogs/CustomDataDialog.html"/>
@@ -158,6 +160,7 @@
<file>concepts/ai.html</file>
<file>concepts/messages.html</file>
<file>concepts/campaign-flow.html</file>
<file>concepts/custom-data.html</file>
<file>dialogs/CustomDataDialog.html</file>
<file>css/help.css</file>
<file>dialogs/AsteroidEditorDialog.html</file>
+2
View File
@@ -0,0 +1,2 @@
set(FSO_VERSION_REVISION 20261112)
set(FSO_VERSION_REVISION_STR 20261112_185b2fbae)