Skip to content

Commit 3af8fa8

Browse files
authored
QtFred Render volumetric (scp-fs2open#7467)
* render volumetric in qtfred * warn for bad model files
1 parent fddedb3 commit 3af8fa8

3 files changed

Lines changed: 82 additions & 1 deletion

File tree

code/mission/missionparse.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7101,7 +7101,10 @@ bool post_process_mission(mission *pm)
71017101
}
71027102
Last_file_checksum = Current_file_checksum;
71037103

7104-
if (pm->volumetrics)
7104+
// Skip the volumetric noise bake under FRED. Only the gameplay deferred
7105+
// pass and the Lab consume the baked 3D bitmap, and the bake can take
7106+
// minutes in a debug build for higher quality nebulae.
7107+
if (pm->volumetrics && !Fred_running)
71057108
pm->volumetrics->renderVolumeBitmap();
71067109

71077110
apply_default_custom_data(pm);

qtfred/src/mission/FredRenderer.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <math/fvi.h>
2727
#include <graphics/light.h>
2828
#include <mod_table/mod_table.h>
29+
#include <cfile/cfile.h>
2930

3031
#include "mission/object.h"
3132
#include "prop/prop.h"
@@ -263,12 +264,24 @@ FredRenderer::FredRenderer(os::Viewport* targetView) : _targetView(targetView) {
263264
init_fred_colors();
264265
}
265266
FredRenderer::~FredRenderer() {
267+
freeVolumetricModel();
266268
}
267269
void FredRenderer::setViewport(EditorViewport* viewport) {
268270
Assertion(_viewport == nullptr, "Resetting viewport is not supported");
269271
Assertion(viewport != nullptr, "Invalid viewport specified!");
270272

271273
_viewport = viewport;
274+
275+
connect(_viewport->editor, &Editor::missionLoaded, this,
276+
[this](const std::string&) { freeVolumetricModel(); });
277+
}
278+
279+
void FredRenderer::freeVolumetricModel() {
280+
if (_volumetric_model_num >= 0) {
281+
model_unload(_volumetric_model_num);
282+
_volumetric_model_num = -1;
283+
}
284+
_volumetric_cached_pof.clear();
272285
}
273286

274287
void FredRenderer::render_grid(grid* gridp) {
@@ -867,6 +880,65 @@ void FredRenderer::render_one_model_htl(object* objp,
867880
rendering_order.push_back(OBJ_INDEX(objp));
868881
}
869882

883+
void FredRenderer::render_volumetric_overlay() {
884+
if (!The_mission.volumetrics) {
885+
return;
886+
}
887+
888+
constexpr float alpha = 0.35f;
889+
890+
const volumetric_nebula& neb = *The_mission.volumetrics;
891+
if (!neb.get_enabled() || neb.getHullPof().empty()) {
892+
return;
893+
}
894+
895+
const SCP_string& pof = neb.getHullPof();
896+
if (pof != _volumetric_cached_pof) {
897+
// Stamp the cache before model_load so a re-entrant paint (e.g. from
898+
// an Error() dialog pumping events on a missing POF) sees the load
899+
// as already attempted and bails out instead of re-loading.
900+
freeVolumetricModel();
901+
_volumetric_cached_pof = pof;
902+
if (cf_exists_full(pof.c_str(), CF_TYPE_MODELS)) {
903+
_volumetric_model_num = model_load(pof.c_str());
904+
} else {
905+
mprintf(("Volumetric nebula hull POF '%s' not found; skipping editor overlay.\n", pof.c_str()));
906+
if (_viewport->dialogProvider != nullptr) {
907+
SCP_string msg = "Volumetric nebula hull POF '";
908+
msg += pof;
909+
msg += "' was not found. The nebula will render without an editor overlay until a valid POF is set in the Volumetric Nebula dialog.";
910+
_viewport->dialogProvider->showButtonDialog(DialogType::Warning,
911+
"Volumetric Nebula POF Missing",
912+
msg,
913+
{ DialogButton::Ok });
914+
}
915+
}
916+
}
917+
918+
if (_volumetric_model_num < 0) {
919+
return;
920+
}
921+
922+
const auto& col = neb.getNebulaColor();
923+
// Premultiply by alpha. MR_NO_TEXTURING + MR_ALL_XPARENT lands on
924+
// ALPHA_BLEND_ADDITIVE (glBlendFunc(GL_ONE, GL_ONE)) which ignores
925+
// src.alpha, so scaling RGB here is what actually controls intensity.
926+
const int r = static_cast<int>(std::get<0>(col) * alpha * 255.0f);
927+
const int g = static_cast<int>(std::get<1>(col) * alpha * 255.0f);
928+
const int b = static_cast<int>(std::get<2>(col) * alpha * 255.0f);
929+
const vec3d pos = neb.getPos();
930+
931+
enable_htl();
932+
933+
model_render_params fill;
934+
fill.set_color(r, g, b);
935+
fill.set_alpha(1.0f);
936+
fill.set_flags(MR_NO_LIGHTING | MR_NO_TEXTURING | MR_NO_BATCH | MR_ALL_XPARENT);
937+
model_render_immediate(&fill, _volumetric_model_num, &vmd_identity_matrix, &pos);
938+
939+
disable_htl();
940+
}
941+
870942
void FredRenderer::render_models(int cur_object_index) {
871943
gr_set_color_fast(&colour_white);
872944

@@ -945,6 +1017,7 @@ void FredRenderer::render_frame(int cur_object_index,
9451017

9461018
gr_set_color(0, 0, 64);
9471019
render_models(cur_object_index);
1020+
render_volumetric_overlay();
9481021

9491022
if (view().Show_distances) {
9501023
display_distances();

qtfred/src/mission/FredRenderer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class FredRenderer: public QObject {
3636
EditorViewport* _viewport = nullptr;
3737
os::Viewport* _targetView = nullptr;
3838

39+
int _volumetric_model_num = -1;
40+
SCP_string _volumetric_cached_pof;
41+
3942
FredRenderer(const FredRenderer& other) = delete;
4043
FredRenderer& operator=(const FredRenderer& other) = delete;
4144

@@ -61,6 +64,8 @@ class FredRenderer: public QObject {
6164
void render_compass();
6265
void render_one_model_htl(object* objp, int cur_object_index);
6366
void render_models(int cur_object_index);
67+
void render_volumetric_overlay();
68+
void freeVolumetricModel();
6469
void render_frame(int cur_object_index,
6570
subsys_to_render& Render_subsys,
6671
bool box_marking,

0 commit comments

Comments
 (0)