diff --git a/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html b/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html
index 5efc835ad20..a462c688520 100644
--- a/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html
+++ b/qtfred/help-src/doc/dialogs/ShipTextureReplacementDialog.html
@@ -24,11 +24,13 @@
Other Maps
- Replace - check to enable a replacement for this map
type.
- - Inherit - automatically derives the replacement name by
- appending the map type suffix to the main texture replacement name (e.g.
- replacing
fighter01 with myfighter and inheriting
- the normal map produces myfighter-normal). Uncheck to enter the
- name manually.
+ - Inherit - when checked (the default), automatically derives
+ the replacement name by appending the map type suffix to the main texture
+ replacement name (e.g. replacing
fighter01 with
+ myfighter and inheriting the normal map produces
+ myfighter-normal). Uncheck to enter a custom replacement name
+ for just this map type; the name field becomes active when Inherit is
+ unchecked.
Models that only define a base diffuse texture will show no controls in this
section.
diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp
index 92a36b7814b..6514ac483a1 100644
--- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp
+++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.cpp
@@ -3,6 +3,8 @@
#include "mission/object.h"
#include "model/model.h"
+#include
+
namespace {
const SCP_vector& get_replaceable_texture_types()
{
@@ -23,621 +25,495 @@ bool is_known_subtexture_type(const SCP_string& type)
get_replaceable_texture_types().end(),
[&type](const SCP_string& knownType) { return lcase_equal(type, knownType); });
}
+
+void remove_ship_entries(const char* ship_name)
+{
+ Fred_texture_replacements.erase(
+ std::remove_if(Fred_texture_replacements.begin(), Fred_texture_replacements.end(),
+ [ship_name](const texture_replace& tr) {
+ return !tr.from_table && !stricmp(tr.ship_name, ship_name);
+ }),
+ Fred_texture_replacements.end());
+}
}
-namespace fso {
- namespace fred {
- namespace dialogs {
- ShipTextureReplacementDialogModel::ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi) :
- AbstractDialogModel(parent, viewport)
- {
- initialiseData(multi);
- }
+namespace fso::fred::dialogs {
- void ShipTextureReplacementDialogModel::initialiseData(bool multi)
- {
- m_multi = multi;
- char texture_file[MAX_FILENAME_LEN];
- char* p = nullptr;
- int duplicate;
- polymodel* pm = model_get(Ship_info[Ships[_editor->cur_ship].ship_info_index].model_num);
- defaultTextures.clear();
- defaultTextures.resize(pm->n_textures);
- currentTextures.clear();
- currentTextures.resize(pm->n_textures);
- subTypesAvailable.clear();
- subTypesAvailable.resize(pm->n_textures);
- replaceMap.clear();
- replaceMap.resize(pm->n_textures);
- inheritMap.clear();
- inheritMap.resize(pm->n_textures);
-
- // look for textures to populate the list
- for (int i = 0; i < pm->n_textures; i++) {
- // get texture file name
- bm_get_filename(pm->maps[i].textures[0].GetOriginalTexture(), texture_file);
-
- // skip blank textures
- if (!strlen(texture_file))
- continue;
+ShipTextureReplacementDialogModel::ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi)
+ : AbstractDialogModel(parent, viewport)
+{
+ initializeData(multi);
+}
- // get rid of file extension
- p = strchr(texture_file, '.');
- if (p)
- {
- //mprintf(( "ignoring extension on file '%s'\n", texture_file ));
- *p = 0;
- }
+void ShipTextureReplacementDialogModel::initializeData(bool multi)
+{
+ _multi = multi;
+ char texture_file[MAX_FILENAME_LEN];
+ char* p = nullptr;
+ int duplicate;
+ polymodel* pm = model_get(Ship_info[Ships[_editor->cur_ship].ship_info_index].model_num);
+ _defaultTextures.clear();
+ _defaultTextures.resize(pm->n_textures);
+ _currentTextures.clear();
+ _currentTextures.resize(pm->n_textures);
+ _subTypesAvailable.clear();
+ _subTypesAvailable.resize(pm->n_textures);
+ _replaceMap.clear();
+ _replaceMap.resize(pm->n_textures);
+ _inheritMap.clear();
+ _inheritMap.resize(pm->n_textures);
+
+ // look for textures to populate the list
+ for (int i = 0; i < pm->n_textures; i++) {
+ // get texture file name
+ bm_get_filename(pm->maps[i].textures[0].GetOriginalTexture(), texture_file);
+
+ // skip blank textures
+ if (!strlen(texture_file))
+ continue;
+
+ // get rid of file extension
+ p = strchr(texture_file, '.');
+ if (p)
+ {
+ *p = 0;
+ }
- // check for duplicate textures in list
- duplicate = -1;
- for (size_t k = 0; k < defaultTextures.size(); k++)
- {
- if (!stricmp(defaultTextures[k].c_str(), texture_file))
- {
- duplicate = static_cast(k);
- break;
- }
- }
+ // check for duplicate textures in list
+ duplicate = -1;
+ for (size_t k = 0; k < _defaultTextures.size(); k++)
+ {
+ if (!stricmp(_defaultTextures[k].c_str(), texture_file))
+ {
+ duplicate = static_cast(k);
+ break;
+ }
+ }
- if (duplicate >= 0)
- continue;
+ if (duplicate >= 0)
+ continue;
- // make old texture lowercase
- strlwr(texture_file);
+ // make old texture lowercase
+ strlwr(texture_file);
- // add it to the field
- defaultTextures[i] = texture_file;
- currentTextures[i].insert(std::pair("main", ""));
- //Get all Available SubTypes
- initSubTypes(pm, i);
+ // add it to the field
+ _defaultTextures[i] = texture_file;
+ _currentTextures[i].insert(std::pair("main", ""));
+ //Get all Available SubTypes
+ initSubTypes(pm, i);
+ }
+ if (!_multi) {
+ for (auto& Fred_texture_replacement : Fred_texture_replacements)
+ {
+ if (!stricmp(Ships[_editor->cur_ship].ship_name, Fred_texture_replacement.ship_name) && !(Fred_texture_replacement.from_table))
+ {
+ // old_texture is stored as the bare base name by this dialog (no type suffix).
+ // However, entries loaded from old mission files may have a type suffix
+ // (e.g. "fenris-body-misc"), so fall back to stripping if no direct match.
+ SCP_string pureName = Fred_texture_replacement.old_texture;
+
+ // Find the matching default texture slot.
+ // Try direct match first; fall back to stripping the last '-' segment
+ // for old mission-file entries that stored old_texture with a type suffix.
+ size_t matchIdx = _defaultTextures.size();
+ for (size_t i = 0; i < _defaultTextures.size(); i++) {
+ if (lcase_equal(_defaultTextures[i], pureName)) {
+ matchIdx = i;
+ break;
+ }
+ }
+ if (matchIdx == _defaultTextures.size()) {
+ auto stripPos = pureName.find_last_of('-');
+ if (stripPos != SCP_string::npos) {
+ SCP_string stripped = pureName.substr(0, stripPos);
+ for (size_t i = 0; i < _defaultTextures.size(); i++) {
+ if (lcase_equal(_defaultTextures[i], stripped)) {
+ matchIdx = i;
+ break;
+ }
+ }
+ }
}
- if (!m_multi) {
- for (auto& Fred_texture_replacement : Fred_texture_replacements)
+ if (matchIdx < _defaultTextures.size())
+ {
+ size_t i = matchIdx;
{
- if (!stricmp(Ships[_editor->cur_ship].ship_name, Fred_texture_replacement.ship_name) && !(Fred_texture_replacement.from_table))
+ SCP_string newText = Fred_texture_replacement.new_texture;
+ SCP_string type;
{
- // old_texture is stored as the bare base name by this dialog (no type suffix).
- // However, entries loaded from old mission files may have a type suffix
- // (e.g. "fenris-body-misc"), so fall back to stripping if no direct match.
- SCP_string pureName = Fred_texture_replacement.old_texture;
-
- // Find the matching default texture slot.
- // Try direct match first; fall back to stripping the last '-' segment
- // for old mission-file entries that stored old_texture with a type suffix.
- size_t matchIdx = defaultTextures.size();
- for (size_t i = 0; i < defaultTextures.size(); i++) {
- if (lcase_equal(defaultTextures[i], pureName)) {
- matchIdx = i;
- break;
- }
- }
- if (matchIdx == defaultTextures.size()) {
- auto stripPos = pureName.find_last_of('-');
- if (stripPos != SCP_string::npos) {
- SCP_string stripped = pureName.substr(0, stripPos);
- for (size_t i = 0; i < defaultTextures.size(); i++) {
- if (lcase_equal(defaultTextures[i], stripped)) {
- matchIdx = i;
- break;
- }
- }
- }
- }
-
- if (matchIdx < defaultTextures.size())
- {
- size_t i = matchIdx;
- {
- SCP_string newText = Fred_texture_replacement.new_texture;
- SCP_string type;
- {
- auto npos = newText.find_last_of('-');
- if (npos != SCP_string::npos) {
- SCP_string possibleType = newText.substr(npos + 1);
- // Only treat the suffix as a type if it's a known sub-texture type.
- // Texture names themselves can contain hyphens (e.g. "fighter01-01a"),
- // so we must not blindly strip the last segment.
- for (const auto& kt : get_replaceable_texture_types()) {
- if (lcase_equal(possibleType, kt)) {
- type = possibleType;
- newText = newText.substr(0, npos);
- break;
- }
- }
- }
- }
- if (!type.empty()) {
- if (type == "misc") {
- currentTextures[i]["misc"] = newText;
- replaceMap[i]["misc"] = true;
- inheritMap[i]["misc"] = (newText == pureName);
- }
- if (type == "shine") {
- currentTextures[i]["shine"] = newText;
- replaceMap[i]["shine"] = true;
- inheritMap[i]["shine"] = (newText == pureName);
-
- }
- if (type == "glow") {
- currentTextures[i]["glow"] = newText;
- replaceMap[i]["glow"] = true;
- inheritMap[i]["glow"] = (newText == pureName);
-
- }
- if (type == "normal") {
- currentTextures[i]["normal"] = newText;
- replaceMap[i]["normal"] = true;
- inheritMap[i]["normal"] = (newText == pureName);
-
- }
- if (type == "height") {
- currentTextures[i]["height"] = newText;
- replaceMap[i]["height"] = true;
- inheritMap[i]["height"] = (newText == pureName);
-
- }
- if (type == "ao") {
- currentTextures[i]["ao"] = newText;
- replaceMap[i]["ao"] = true;
- inheritMap[i]["ao"] = (newText == pureName);
-
- }
- if (type == "reflect") {
- currentTextures[i]["reflect"] = newText;
- replaceMap[i]["reflect"] = true;
- inheritMap[i]["reflect"] = (newText == pureName);
-
- }
- }
- else {
- currentTextures[i]["main"] = newText;
- }
-
+ auto npos = newText.find_last_of('-');
+ if (npos != SCP_string::npos) {
+ SCP_string possibleType = newText.substr(npos + 1);
+ // Only treat the suffix as a type if it's a known sub-texture type.
+ // Texture names themselves can contain hyphens (e.g. "fighter01-01a"),
+ // so we must not blindly strip the last segment.
+ if (is_known_subtexture_type(possibleType)) {
+ type = possibleType;
+ newText = newText.substr(0, npos);
}
}
}
- }
- }
- modelChanged();
- _modified = false;
- }
- void ShipTextureReplacementDialogModel::initSubTypes(polymodel* model, int MapNum)
- {
- for (const auto& type : get_replaceable_texture_types()) {
- subTypesAvailable[MapNum].insert(std::pair(type, false));
- currentTextures[MapNum].insert(std::pair(type, ""));
- replaceMap[MapNum].insert(std::pair(type, false));
- inheritMap[MapNum].insert(std::pair(type, true));
- }
- char subMap[MAX_FILENAME_LEN];
- //init saftly, probly not necessary
- for (int j = 1; j < TM_NUM_TYPES; j++) {
- bm_get_filename(model->maps[MapNum].textures[j].GetOriginalTexture(), subMap);
- char* p = strchr(subMap, '.');
- if (p)
- {
- //mprintf(( "ignoring extension on file '%s'\n", texture_file ));
- *p = 0;
- }
- SCP_string subMapClean = subMap;
- SCP_tolower(subMapClean);
- SCP_string type;
- auto npos = subMapClean.find_last_of('-');
- if (npos != SCP_string::npos) {
- type = subMapClean.substr(npos + 1);
- }
- else {
- continue;
- }
- if (!type.empty()) {
- if (lcase_equal(type, MODEL_TEXTURE_SUFFIX_TRANS.substr(1))) {
- // transparency map, not a replaceable subtype
- } else {
+ if (!type.empty()) {
if (is_known_subtexture_type(type)) {
- subTypesAvailable[MapNum][type] = true;
- } else {
- error_display(1, "Invalid Map type %s. Check your model's texture names or get a programmer", type.c_str());
+ _currentTextures[i][type] = newText;
+ _replaceMap[i][type] = true;
+ _inheritMap[i][type] = !_currentTextures[i]["main"].empty() &&
+ lcase_equal(newText, _currentTextures[i]["main"]);
}
}
+ else {
+ _currentTextures[i]["main"] = newText;
+ }
}
}
}
+ }
+ }
+ modelChanged();
+ _modified = false;
+}
- bool ShipTextureReplacementDialogModel::apply()
- {
- if (query_modified()) {
- for (size_t i = 0; i < getSize(); i++) {
- if ((!currentTextures[i]["main"].empty()) && (currentTextures[i]["main"] != defaultTextures[i])) {
- mainChanged = true;
- SCP_string name = currentTextures[i]["main"];
- if (testTexture(name)) {
- SCP_vector::iterator ii, end;
- end = Fred_texture_replacements.end();
- if (!m_multi) {
- end = Fred_texture_replacements.end();
- for (ii = Fred_texture_replacements.begin(); ii != end; ++ii)
- {
- if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name))
- {
- do {
- end--;
- } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name));
- if (end == ii)
- break;
- texture_set(&(*ii), &(*end));
- }
- }
-
- if (end != Fred_texture_replacements.end())
- Fred_texture_replacements.erase(end, Fred_texture_replacements.end());
-
- // now put the new entries on the end of the list
- texture_replace tr;
- strcpy_s(tr.old_texture, defaultTextures[i].c_str());
- strcpy_s(tr.new_texture, name.c_str());
- strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name);
- tr.new_texture_id = -1;
- tr.from_table = false;
-
- // assign to global FRED array
- Fred_texture_replacements.push_back(tr);
- _editor->missionChanged();
- }
- else {
- object* objp = nullptr;
- objp = GET_FIRST(&obj_used_list);
- while (objp != END_OF_LIST(&obj_used_list)) {
- if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
- (objp->flags[Object::Object_Flags::Marked])) {
- Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START));
- auto shipp = &Ships[get_ship_from_obj(objp)];
- end = Fred_texture_replacements.end();
- for (ii = Fred_texture_replacements.begin(); ii != end; ++ii)
- {
- if (!stricmp(ii->ship_name, shipp->ship_name))
- {
- do {
- end--;
- } while (end != ii && !stricmp(end->ship_name, shipp->ship_name));
- if (end == ii)
- break;
- texture_set(&(*ii), &(*end));
- }
- }
- if (end != Fred_texture_replacements.end())
- Fred_texture_replacements.erase(end, Fred_texture_replacements.end());
-
- // now put the new entries on the end of the list
- texture_replace tr;
- strcpy_s(tr.old_texture, defaultTextures[i].c_str());
- strcpy_s(tr.new_texture, name.c_str());
- strcpy_s(tr.ship_name, shipp->ship_name);
- tr.new_texture_id = -1;
- tr.from_table = false;
-
- // assign to global FRED array
- Fred_texture_replacements.push_back(tr);
- _editor->missionChanged();
- }
-
- objp = GET_NEXT(objp);
- }
- }
- }
- else {
- auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find Main Texture %s \n Aborting at this point",
- { DialogButton::Ok });
- if (button == DialogButton::Ok) {
- return false;
- }
- }
- }
- saveSubMap(i, "misc");
- saveSubMap(i, "shine");
- saveSubMap(i, "glow");
- saveSubMap(i, "normal");
- saveSubMap(i, "height");
- saveSubMap(i, "ao");
- saveSubMap(i, "reflect");
- _editor->missionChanged();
- }
- _editor->missionChanged();
- return true;
- }
- else {
- return true;
+void ShipTextureReplacementDialogModel::initSubTypes(polymodel* model, int mapNum)
+{
+ for (const auto& type : get_replaceable_texture_types()) {
+ _subTypesAvailable[mapNum].insert({type, false});
+ _currentTextures[mapNum].insert({type, ""});
+ _replaceMap[mapNum].insert({type, false});
+ _inheritMap[mapNum].insert({type, true});
+ }
+
+ char subMap[MAX_FILENAME_LEN];
+ for (int j = 1; j < TM_NUM_TYPES; j++) {
+ bm_get_filename(model->maps[mapNum].textures[j].GetOriginalTexture(), subMap);
+ char* p = strchr(subMap, '.');
+ if (p)
+ {
+ *p = 0;
+ }
+ SCP_string subMapClean = subMap;
+ SCP_tolower(subMapClean);
+ SCP_string type;
+ auto npos = subMapClean.find_last_of('-');
+ if (npos != SCP_string::npos) {
+ type = subMapClean.substr(npos + 1);
+ }
+ else {
+ continue;
+ }
+ if (!type.empty()) {
+ if (type == MODEL_TEXTURE_SUFFIX_TRANS.substr(1)) {
+ // transparency map, not a replaceable subtype
+ } else {
+ if (is_known_subtexture_type(type)) {
+ _subTypesAvailable[mapNum][type] = true;
+ } else {
+ error_display(1, "Invalid Map type %s. Check your model's texture names or get a programmer", type.c_str());
}
}
- void ShipTextureReplacementDialogModel::reject()
- {
- }
- texture_replace* ShipTextureReplacementDialogModel::texture_set(texture_replace* dest, const texture_replace* src)
- {
- dest->new_texture_id = src->new_texture_id;
- strcpy_s(dest->ship_name, src->ship_name);
- strcpy_s(dest->old_texture, src->old_texture);
- strcpy_s(dest->new_texture, src->new_texture);
- dest->from_table = src->from_table;
+ }
+ }
+}
- return dest;
+bool ShipTextureReplacementDialogModel::apply()
+{
+ if (query_modified()) {
+ _mainChanged = false;
+
+ // Remove all existing non-table entries for affected ships before writing new ones.
+ // This ensures that each save operation is a simple append rather than a remove-and-add,
+ // which was causing each type's save to clobber the previous type's entry.
+ if (!_multi) {
+ remove_ship_entries(Ships[_editor->cur_ship].ship_name);
+ } else {
+ object* objp = GET_FIRST(&obj_used_list);
+ while (objp != END_OF_LIST(&obj_used_list)) {
+ if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
+ (objp->flags[Object::Object_Flags::Marked])) {
+ remove_ship_entries(Ships[get_ship_from_obj(objp)].ship_name);
+ }
+ objp = GET_NEXT(objp);
}
+ }
- void ShipTextureReplacementDialogModel::saveSubMap(const size_t index, const SCP_string& type) {
- SCP_string fullName;
- if (replaceMap[index][type]) {
- if (inheritMap[index][type]) {
- if (mainChanged) {
- if (!currentTextures[index]["main"].empty()) {
- if (currentTextures[index]["main"] != "invisible") {
- fullName = currentTextures[index]["main"] + "-" + type;
- }
- else {
- fullName = currentTextures[index]["main"];
- }
- if (testTexture(fullName)) {
- SCP_vector::iterator ii, end;
- end = Fred_texture_replacements.end();
- if (!m_multi) {
- end = Fred_texture_replacements.end();
- for (ii = Fred_texture_replacements.begin(); ii != end; ++ii)
- {
- if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name))
- {
- do {
- end--;
- } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name));
- if (end == ii)
- break;
- texture_set(&(*ii), &(*end));
- }
- }
-
- if (end != Fred_texture_replacements.end())
- Fred_texture_replacements.erase(end, Fred_texture_replacements.end());
-
- // now put the new entries on the end of the list
- texture_replace tr;
- strcpy_s(tr.old_texture, defaultTextures[index].c_str());
- strcpy_s(tr.new_texture, fullName.c_str());
- strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name);
- tr.new_texture_id = -1;
- tr.from_table = false;
-
- // assign to global FRED array
- Fred_texture_replacements.push_back(tr);
- _editor->missionChanged();
- }
- else {
- object* objp = nullptr;
- objp = GET_FIRST(&obj_used_list);
- while (objp != END_OF_LIST(&obj_used_list)) {
- if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
- (objp->flags[Object::Object_Flags::Marked])) {
- Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START));
- auto shipp = &Ships[get_ship_from_obj(objp)];
- end = Fred_texture_replacements.end();
- for (ii = Fred_texture_replacements.begin(); ii != end; ++ii)
- {
- if (!stricmp(ii->ship_name, shipp->ship_name))
- {
- do {
- end--;
- } while (end != ii && !stricmp(end->ship_name, shipp->ship_name));
- if (end == ii)
- break;
- texture_set(&(*ii), &(*end));
- }
- }
-
- if (end != Fred_texture_replacements.end())
- Fred_texture_replacements.erase(end, Fred_texture_replacements.end());
-
- // now put the new entries on the end of the list
- texture_replace tr;
- strcpy_s(tr.old_texture, defaultTextures[index].c_str());
- strcpy_s(tr.new_texture, fullName.c_str());
- strcpy_s(tr.ship_name, shipp->ship_name);
- tr.new_texture_id = -1;
- tr.from_table = false;
-
- // assign to global FRED array
- Fred_texture_replacements.push_back(tr);
- _editor->missionChanged();
- }
-
- objp = GET_NEXT(objp);
- }
- }
- }
- else {
- auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping",
- { DialogButton::Ok });
- if (button == DialogButton::Ok) {
- return;
- }
- }
-
- }
- }
- else {
- error_display(0, "Cannot use inherited data without changing the main texture name. Ignoring %s map change.", type.c_str());
- }
+ for (size_t i = 0; i < getSize(); i++) {
+ if ((!_currentTextures[i]["main"].empty()) && (_currentTextures[i]["main"] != _defaultTextures[i])) {
+ _mainChanged = true;
+ SCP_string name = _currentTextures[i]["main"];
+ if (testTexture(name)) {
+ if (!_multi) {
+ texture_replace tr;
+ strcpy_s(tr.old_texture, _defaultTextures[i].c_str());
+ strcpy_s(tr.new_texture, name.c_str());
+ strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name);
+ tr.new_texture_id = -1;
+ tr.from_table = false;
+ Fred_texture_replacements.push_back(tr);
+ _editor->missionChanged();
}
else {
- if (!currentTextures[index][type].empty()) {
- if (currentTextures[index][type] != "invisible") {
- fullName = currentTextures[index][type] + "-" + type;
- }
- else {
- fullName = currentTextures[index][type];
- }
- if (testTexture(fullName)) {
- SCP_vector::iterator ii, end;
- end = Fred_texture_replacements.end();
- if (!m_multi) {
- end = Fred_texture_replacements.end();
- for (ii = Fred_texture_replacements.begin(); ii != end; ++ii)
- {
- if (!stricmp(ii->ship_name, Ships[_editor->cur_ship].ship_name))
- {
- do {
- end--;
- } while (end != ii && !stricmp(end->ship_name, Ships[_editor->cur_ship].ship_name));
- if (end == ii)
- break;
- texture_set(&(*ii), &(*end));
- }
- }
-
- if (end != Fred_texture_replacements.end())
- Fred_texture_replacements.erase(end, Fred_texture_replacements.end());
-
- // now put the new entries on the end of the list
- texture_replace tr;
- strcpy_s(tr.old_texture, defaultTextures[index].c_str());
- strcpy_s(tr.new_texture, fullName.c_str());
- strcpy_s(tr.ship_name, Ships[_editor->cur_ship].ship_name);
- tr.new_texture_id = -1;
- tr.from_table = false;
-
- // assign to global FRED array
- Fred_texture_replacements.push_back(tr);
- _editor->missionChanged();
- }
- else {
- object* objp = nullptr;
- objp = GET_FIRST(&obj_used_list);
- while (objp != END_OF_LIST(&obj_used_list)) {
- if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
- (objp->flags[Object::Object_Flags::Marked])) {
- Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START));
- auto shipp = &Ships[get_ship_from_obj(objp)];
- end = Fred_texture_replacements.end();
- for (ii = Fred_texture_replacements.begin(); ii != end; ++ii)
- {
- if (!stricmp(ii->ship_name, shipp->ship_name))
- {
- do {
- end--;
- } while (end != ii && !stricmp(end->ship_name, shipp->ship_name));
- if (end == ii)
- break;
- texture_set(&(*ii), &(*end));
- }
- }
-
- if (end != Fred_texture_replacements.end())
- Fred_texture_replacements.erase(end, Fred_texture_replacements.end());
-
- // now put the new entries on the end of the list
- texture_replace tr;
- strcpy_s(tr.old_texture, defaultTextures[index].c_str());
- strcpy_s(tr.new_texture, fullName.c_str());
- strcpy_s(tr.ship_name, shipp->ship_name);
- tr.new_texture_id = -1;
- tr.from_table = false;
-
- // assign to global FRED array
- Fred_texture_replacements.push_back(tr);
- _editor->missionChanged();
- }
-
- objp = GET_NEXT(objp);
- }
- }
+ object* objp = GET_FIRST(&obj_used_list);
+ while (objp != END_OF_LIST(&obj_used_list)) {
+ if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
+ (objp->flags[Object::Object_Flags::Marked])) {
+ Assertion((objp->type == OBJ_SHIP) || (objp->type == OBJ_START), "Expected ship or start object"); // NOLINT(readability-simplify-boolean-expr)
+ auto shipp = &Ships[get_ship_from_obj(objp)];
+ texture_replace tr;
+ strcpy_s(tr.old_texture, _defaultTextures[i].c_str());
+ strcpy_s(tr.new_texture, name.c_str());
+ strcpy_s(tr.ship_name, shipp->ship_name);
+ tr.new_texture_id = -1;
+ tr.from_table = false;
+ Fred_texture_replacements.push_back(tr);
+ _editor->missionChanged();
}
- else {
- auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find %s \n Skipping",
- { DialogButton::Ok });
- if (button == DialogButton::Ok) {
- return;
- }
- }
-
+ objp = GET_NEXT(objp);
}
}
}
- }
-
- bool ShipTextureReplacementDialogModel::testTexture(const SCP_string& fullName)
- {
- int temp_bmp, temp_frames, temp_fps;
- if (fullName == "invisible") {
- return true;
- }
else {
- // try loading the texture (bmpman should take care of eventually unloading it)
- temp_bmp = bm_load(fullName);
- if (temp_bmp < 0)
- {
- temp_bmp = bm_load_animation(fullName.c_str(), &temp_frames, &temp_fps, nullptr, nullptr, false, true);
+ auto button = _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture", "FRED was unable to find Main Texture %s \n Aborting at this point",
+ { DialogButton::Ok });
+ if (button == DialogButton::Ok) {
+ return false;
}
- return temp_bmp >= 0;
}
}
-
- size_t ShipTextureReplacementDialogModel::getSize() const
- {
- return defaultTextures.size();
+ for (const auto& type : get_replaceable_texture_types()) {
+ saveSubMap(i, type);
}
- SCP_string ShipTextureReplacementDialogModel::getDefaultName(const size_t index) const
- {
- Assert(index <= defaultTextures.size());
- return defaultTextures[index];
+ _editor->missionChanged();
+ }
+ _editor->missionChanged();
+
+ // Update each affected ship's pmi so the viewport reflects the new textures immediately.
+ // We cannot use ship::apply_replacement_textures here because all our Fred_texture_replacements
+ // entries share old_texture = base name, so FindTexture always resolves to TM_BASE_TYPE and
+ // sub-type entries would overwrite the base slot. Instead, write slots directly using the
+ // TM_* indices from MODEL_TEXTURE_SUFFIXES.
+ auto load_tex = [](const SCP_string& name) -> int {
+ int id = bm_load(name);
+ if (id < 0) {
+ int nf, fps;
+ id = bm_load_animation(name.c_str(), &nf, &fps, nullptr, nullptr, false, true);
}
- void ShipTextureReplacementDialogModel::setMap(const size_t index, const SCP_string& type, const SCP_string& newName)
- {
- Assert(index < currentTextures.size());
- auto pos = currentTextures[index].find(type);
- if (pos == currentTextures[index].end()) {
- //handle the error
- error_display(1, "Tried to set non existant map type %s. Get a programmer", type.c_str());
+ return id;
+ };
+
+ auto apply_to_ship = [&](int ship_index) {
+ auto& shipp = Ships[ship_index];
+ auto* pmi = model_get_instance(shipp.model_instance_num);
+ auto* pm = model_get(Ship_info[shipp.ship_info_index].model_num);
+
+ pmi->texture_replace = std::make_shared();
+
+ for (size_t i = 0; i < getSize(); i++) {
+ if (_defaultTextures[i].empty())
+ continue;
+
+ // Find which texture group owns this base texture.
+ int groupIdx = -1;
+ for (int j = 0; j < pm->n_textures; j++) {
+ if (pm->maps[j].FindTexture(_defaultTextures[i].c_str()) >= 0) {
+ groupIdx = j;
+ break;
+ }
}
- else {
- modify(currentTextures[index][type], newName);
+ if (groupIdx < 0)
+ continue;
+
+ // Main (base) slot.
+ const SCP_string& mainName = _currentTextures[i].at("main");
+ if (!mainName.empty() && !lcase_equal(mainName, _defaultTextures[i])) {
+ int id = load_tex(mainName);
+ if (id >= 0)
+ (*pmi->texture_replace)[groupIdx * TM_NUM_TYPES + TM_BASE_TYPE] = id;
}
- }
- SCP_string ShipTextureReplacementDialogModel::getMap(const size_t index, const SCP_string& type) const {
- Assert(index < currentTextures.size());
- auto pos = currentTextures[index].find(type);
- if (pos == currentTextures[index].end()) {
- error_display(1, "Asked for non existant map type %s. Get a programmer", type.c_str());
- return nullptr;
- }
- else {
- return pos->second;
+ // Sub-type slots.
+ for (const auto& [tmType, suffix] : MODEL_TEXTURE_SUFFIXES) {
+ const SCP_string typeKey = suffix.substr(1);
+ if (!_replaceMap[i].at(typeKey))
+ continue;
+
+ SCP_string fullName;
+ if (_inheritMap[i].at(typeKey)) {
+ if (mainName.empty() || lcase_equal(mainName, _defaultTextures[i]))
+ continue;
+ fullName = (mainName != "invisible") ? mainName + suffix : mainName;
+ } else {
+ const SCP_string& typeName = _currentTextures[i].at(typeKey);
+ if (typeName.empty())
+ continue;
+ fullName = (typeName != "invisible") ? typeName + suffix : typeName;
+ }
+
+ int id = load_tex(fullName);
+ if (id >= 0)
+ (*pmi->texture_replace)[groupIdx * TM_NUM_TYPES + tmType] = id;
}
}
- SCP_map ShipTextureReplacementDialogModel::getSubtypesForMap(const size_t index) const
- {
- Assert(index < currentTextures.size());
- return subTypesAvailable[index];
- }
- SCP_map ShipTextureReplacementDialogModel::getReplace(const size_t index) const
- {
- Assert(index < currentTextures.size());
- return replaceMap[index];
- }
- SCP_map ShipTextureReplacementDialogModel::getInherit(const size_t index) const
- {
- Assert(index < currentTextures.size());
- return inheritMap[index];
+ };
+
+ if (!_multi) {
+ apply_to_ship(_editor->cur_ship);
+ } else {
+ object* objp = GET_FIRST(&obj_used_list);
+ while (objp != END_OF_LIST(&obj_used_list)) {
+ if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
+ objp->flags[Object::Object_Flags::Marked])
+ apply_to_ship(get_ship_from_obj(objp));
+ objp = GET_NEXT(objp);
}
+ }
- void ShipTextureReplacementDialogModel::setReplace(const size_t index, const SCP_string& type, const bool state)
- {
- Assert(index < currentTextures.size());
- modify(replaceMap[index][type], state);
- }
- void ShipTextureReplacementDialogModel::setInherit(const size_t index, const SCP_string& type, const bool state)
- {
- Assert(index < currentTextures.size());
- modify(inheritMap[index][type], state);
+ return true;
+ }
+ else {
+ return true;
+ }
+}
+
+void ShipTextureReplacementDialogModel::reject() {}
+
+void ShipTextureReplacementDialogModel::saveSubMap(size_t index, const SCP_string& type)
+{
+ if (!_replaceMap[index][type])
+ return;
+
+ SCP_string fullName;
+ if (_inheritMap[index][type]) {
+ if (!_mainChanged)
+ return;
+ if (_currentTextures[index]["main"].empty())
+ return;
+ fullName = (_currentTextures[index]["main"] != "invisible")
+ ? _currentTextures[index]["main"] + "-" + type
+ : _currentTextures[index]["main"];
+ } else {
+ if (_currentTextures[index][type].empty())
+ return;
+ fullName = (_currentTextures[index][type] != "invisible")
+ ? _currentTextures[index][type] + "-" + type
+ : _currentTextures[index][type];
+ }
+
+ if (!testTexture(fullName)) {
+ _viewport->dialogProvider->showButtonDialog(DialogType::Error, "Missing Texture",
+ "FRED was unable to find %s \n Skipping", { DialogButton::Ok });
+ return;
+ }
+
+ // Existing entries for this ship were already removed at the start of apply(),
+ // so just append the new entry for each affected ship.
+ auto push_entry = [&](const char* ship_name) {
+ texture_replace tr;
+ strcpy_s(tr.old_texture, (_defaultTextures[index] + "-" + type).c_str());
+ strcpy_s(tr.new_texture, fullName.c_str());
+ strcpy_s(tr.ship_name, ship_name);
+ tr.new_texture_id = -1;
+ tr.from_table = false;
+ Fred_texture_replacements.push_back(tr);
+ _editor->missionChanged();
+ };
+
+ if (!_multi) {
+ push_entry(Ships[_editor->cur_ship].ship_name);
+ } else {
+ object* objp = GET_FIRST(&obj_used_list);
+ while (objp != END_OF_LIST(&obj_used_list)) {
+ if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) &&
+ (objp->flags[Object::Object_Flags::Marked])) {
+ Assertion((objp->type == OBJ_SHIP) || (objp->type == OBJ_START), "Expected ship or start object"); // NOLINT(readability-simplify-boolean-expr)
+ push_entry(Ships[get_ship_from_obj(objp)].ship_name);
}
+ objp = GET_NEXT(objp);
}
}
-}
\ No newline at end of file
+}
+
+bool ShipTextureReplacementDialogModel::testTexture(const SCP_string& fullName)
+{
+ int temp_bmp, temp_frames, temp_fps;
+ if (fullName == "invisible") {
+ return true;
+ }
+ else {
+ // try loading the texture (bmpman should take care of eventually unloading it)
+ temp_bmp = bm_load(fullName);
+ if (temp_bmp < 0)
+ {
+ temp_bmp = bm_load_animation(fullName.c_str(), &temp_frames, &temp_fps, nullptr, nullptr, false, true);
+ }
+ return temp_bmp >= 0;
+ }
+}
+
+size_t ShipTextureReplacementDialogModel::getSize() const
+{
+ return _defaultTextures.size();
+}
+
+SCP_string ShipTextureReplacementDialogModel::getDefaultName(size_t index) const
+{
+ Assert(index <= _defaultTextures.size());
+ return _defaultTextures[index];
+}
+
+void ShipTextureReplacementDialogModel::setMap(size_t index, const SCP_string& type, const SCP_string& newName)
+{
+ Assert(index < _currentTextures.size());
+ auto pos = _currentTextures[index].find(type);
+ if (pos == _currentTextures[index].end()) {
+ error_display(1, "Tried to set non existant map type %s. Get a programmer", type.c_str());
+ }
+ else {
+ modify(_currentTextures[index][type], newName);
+ }
+}
+
+SCP_string ShipTextureReplacementDialogModel::getMap(size_t index, const SCP_string& type) const
+{
+ Assert(index < _currentTextures.size());
+ auto pos = _currentTextures[index].find(type);
+ if (pos == _currentTextures[index].end()) {
+ error_display(1, "Asked for non existant map type %s. Get a programmer", type.c_str());
+ return nullptr;
+ }
+ else {
+ return pos->second;
+ }
+}
+
+SCP_map ShipTextureReplacementDialogModel::getSubtypesForMap(size_t index) const
+{
+ Assert(index < _currentTextures.size());
+ return _subTypesAvailable[index];
+}
+
+SCP_map ShipTextureReplacementDialogModel::getReplace(size_t index) const
+{
+ Assert(index < _currentTextures.size());
+ return _replaceMap[index];
+}
+
+SCP_map ShipTextureReplacementDialogModel::getInherit(size_t index) const
+{
+ Assert(index < _currentTextures.size());
+ return _inheritMap[index];
+}
+
+void ShipTextureReplacementDialogModel::setReplace(size_t index, const SCP_string& type, bool state)
+{
+ Assert(index < _currentTextures.size());
+ modify(_replaceMap[index][type], state);
+}
+
+void ShipTextureReplacementDialogModel::setInherit(size_t index, const SCP_string& type, bool state)
+{
+ Assert(index < _currentTextures.size());
+ modify(_inheritMap[index][type], state);
+}
+
+} // namespace fso::fred::dialogs
diff --git a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h
index d8a4ca4cf45..518a6fdc9fa 100644
--- a/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h
+++ b/qtfred/src/mission/dialogs/ShipEditor/ShipTextureReplacementDialogModel.h
@@ -2,51 +2,41 @@
#include "../AbstractDialogModel.h"
-namespace fso {
- namespace fred {
- namespace dialogs {
- constexpr auto NUM__SUBTEXTURE_TYPES = 7;
- class ShipTextureReplacementDialogModel : public AbstractDialogModel {
- private:
- void initSubTypes(polymodel* model, int);
-
- bool m_multi;
- //Used to dermeine what type of texutre a map has.
- SCP_vector> subTypesAvailable;
-
- //Wether to replace textures.
- SCP_vector> replaceMap;
- //Wether to inherit name from base texture.
- SCP_vector> inheritMap;
-
-
- SCP_vector defaultTextures;
- SCP_vector> currentTextures;
- bool mainChanged = false;
- void saveSubMap(const size_t index, const SCP_string& type);
- static bool testTexture(const SCP_string&);
- static texture_replace* texture_set(texture_replace* dest, const texture_replace* src);
- public:
- ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi);
- void initialiseData(bool);
-
- bool apply() override;
- void reject() override;
-
- size_t getSize() const;
- SCP_string getDefaultName(const size_t) const;
-
- void setMap(const size_t index, const SCP_string& type, const SCP_string& newName);
- SCP_string getMap(const size_t index, const SCP_string& type) const;
-
- SCP_map getSubtypesForMap(const size_t index) const;
- SCP_map getReplace(const size_t index) const;
- SCP_map getInherit(const size_t index) const;
- void setReplace(const size_t index, const SCP_string& type, const bool state);
- void setInherit(const size_t index, const SCP_string& type, const bool state);
-
-
- };
- }
- }
-}
\ No newline at end of file
+namespace fso::fred::dialogs {
+
+class ShipTextureReplacementDialogModel : public AbstractDialogModel {
+ Q_OBJECT
+ public:
+ ShipTextureReplacementDialogModel(QObject* parent, EditorViewport* viewport, bool multi);
+
+ bool apply() override;
+ void reject() override;
+
+ size_t getSize() const;
+ SCP_string getDefaultName(size_t index) const;
+
+ void setMap(size_t index, const SCP_string& type, const SCP_string& newName);
+ SCP_string getMap(size_t index, const SCP_string& type) const;
+
+ SCP_map getSubtypesForMap(size_t index) const;
+ SCP_map getReplace(size_t index) const;
+ SCP_map getInherit(size_t index) const;
+ void setReplace(size_t index, const SCP_string& type, bool state);
+ void setInherit(size_t index, const SCP_string& type, bool state);
+
+ private: // NOLINT(readability-redundant-access-specifiers)
+ void initializeData(bool multi);
+ void initSubTypes(polymodel* model, int mapNum);
+ void saveSubMap(size_t index, const SCP_string& type);
+ static bool testTexture(const SCP_string& name);
+
+ bool _multi;
+ SCP_vector> _subTypesAvailable;
+ SCP_vector> _replaceMap;
+ SCP_vector> _inheritMap;
+ SCP_vector _defaultTextures;
+ SCP_vector> _currentTextures;
+ bool _mainChanged = false;
+};
+
+} // namespace fso::fred::dialogs
diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp
index cf38f032a75..b44494bdab6 100644
--- a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp
+++ b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.cpp
@@ -4,9 +4,11 @@
#include
#include
+#include
#include
#include
+#include
namespace fso::fred::dialogs {
// Model for list view, Should really have its own file but its not big enught for me to bother.
@@ -41,22 +43,51 @@ ShipTextureReplacementDialog::ShipTextureReplacementDialog(QDialog* parent, Edit
ui->setupUi(this);
ui->newTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->AmbiantTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->MiscTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->ShineTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->HeightTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->GlowTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->NormalTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- ui->ReflectTextureLineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
- listmodel = new MapModel(_model.get(), this);
- ui->TexturesList->setModel(listmodel);
+ auto* subLayout = qobject_cast(ui->subTextureBox->layout());
+ int gridRow = 0;
+ for (const auto& [tmType, suffix] : MODEL_TEXTURE_SUFFIXES) {
+ SCP_string typeKey = suffix.substr(1);
+ SCP_string displayStr = typeKey;
+ if (!displayStr.empty())
+ displayStr[0] = static_cast(std::toupper(static_cast(displayStr[0])));
+
+ auto* label = new QLabel(displayStr.c_str(), this);
+ auto* useBox = new QCheckBox(tr("Replace"), this);
+ auto* inheritBox = new QCheckBox(tr("Inherit"), this);
+ auto* lineEdit = new QLineEdit(this);
+ lineEdit->setMaxLength(MAX_FILENAME_LEN - 1);
+
+ subLayout->addWidget(label, gridRow, 0);
+ subLayout->addWidget(useBox, gridRow, 1);
+ subLayout->addWidget(inheritBox, gridRow, 2);
+ subLayout->addWidget(lineEdit, gridRow, 3);
+
+ connect(lineEdit, &QLineEdit::editingFinished, this, [this, typeKey, lineEdit]() {
+ SCP_string newText;
+ if (!lineEdit->text().isEmpty())
+ newText = lineEdit->text().toUtf8().constData();
+ _model->setMap(_selectedRow, typeKey, newText);
+ });
+ connect(useBox, &QCheckBox::toggled, this, [this, typeKey](bool state) {
+ _model->setReplace(_selectedRow, typeKey, state);
+ });
+ connect(inheritBox, &QCheckBox::toggled, this, [this, typeKey](bool state) {
+ _model->setInherit(_selectedRow, typeKey, state);
+ });
+
+ _textureRows[typeKey] = { label, useBox, inheritBox, lineEdit };
+ ++gridRow;
+ }
+
+ _listModel = new MapModel(_model.get(), this);
+ ui->TexturesList->setModel(_listModel);
QItemSelectionModel* selectionModel = ui->TexturesList->selectionModel();
- connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ShipTextureReplacementDialog::updateUIFull);
- QModelIndex index = listmodel->index(0);
+ connect(selectionModel, &QItemSelectionModel::selectionChanged, this, &ShipTextureReplacementDialog::updateUiFull);
+ QModelIndex index = _listModel->index(0);
ui->TexturesList->setCurrentIndex(index);
- connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipTextureReplacementDialog::updateUI);
+ connect(_model.get(), &AbstractDialogModel::modelChanged, this, &ShipTextureReplacementDialog::updateUi);
// Resize the dialog to the minimum size
resize(QDialog::sizeHint());
@@ -100,282 +131,45 @@ void ShipTextureReplacementDialog::on_newTextureLineEdit_editingFinished()
if (!ui->newTextureLineEdit->text().isEmpty()) {
newText = ui->newTextureLineEdit->text().toUtf8().constData();
}
- _model->setMap(row, "main", newText);
+ _model->setMap(_selectedRow, "main", newText);
}
-void ShipTextureReplacementDialog::on_MiscTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->MiscTextureLineEdit->text().isEmpty()) {
- newText = ui->MiscTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "misc", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_GlowTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->GlowTextureLineEdit->text().isEmpty()) {
- newText = ui->GlowTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "glow", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_ShineTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->ShineTextureLineEdit->text().isEmpty()) {
- newText = ui->ShineTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "shine", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_NormalTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->NormalTextureLineEdit->text().isEmpty()) {
- newText = ui->NormalTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "normal", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_HeightTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->HeightTextureLineEdit->text().isEmpty()) {
- newText = ui->HeightTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "height", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_AmbiantTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->AmbiantTextureLineEdit->text().isEmpty()) {
- newText = ui->AmbiantTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "ao", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_ReflectTextureLineEdit_editingFinished()
-{
- SCP_string newText;
- if (!ui->ReflectTextureLineEdit->text().isEmpty()) {
- newText = ui->ReflectTextureLineEdit->text().toUtf8().constData();
- _model->setMap(row, "reflect", newText);
- }
-}
-
-void ShipTextureReplacementDialog::on_useMiscTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "misc", state);
-}
-
-void ShipTextureReplacementDialog::on_useGlowTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "glow", state);
-}
-
-void ShipTextureReplacementDialog::on_useShineTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "shine", state);
-}
-
-void ShipTextureReplacementDialog::on_useNormalTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "normal", state);
-}
-
-void ShipTextureReplacementDialog::on_useHeightTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "height", state);
-}
-
-void ShipTextureReplacementDialog::on_useAmbiantTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "ao", state);
-}
-
-void ShipTextureReplacementDialog::on_useReflectTexturecheckbox_toggled(bool state)
-{
- _model->setReplace(row, "reflect", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritMiscTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "misc", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritGlowTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "glow", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritShineTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "shine", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritNormalTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "normal", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritHeightTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "height", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritAmbiantTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "ao", state);
-}
-
-void ShipTextureReplacementDialog::on_inheritReflectTexturecheckbox_toggled(bool state)
-{
- _model->setInherit(row, "reflect", state);
-}
-
-void ShipTextureReplacementDialog::updateUI()
+void ShipTextureReplacementDialog::updateUi()
{
util::SignalBlockers blockers(this);
- ui->newTextureLineEdit->setText(_model->getMap(row, "main").c_str());
- bool replace;
- bool inherit;
- ui->useMiscTexturecheckbox->setChecked(replace = _model->getReplace(row)["misc"]);
- if (replace) {
- ui->inheritMiscTextureCheckbox->setChecked(inherit = _model->getInherit(row)["misc"]);
- ui->inheritMiscTextureCheckbox->setEnabled(replace);
- ui->MiscTextureLineEdit->setEnabled(!inherit);
- if (!inherit) {
- ui->MiscTextureLineEdit->setText(_model->getMap(row, "misc").c_str());
- }
- } else {
- ui->inheritMiscTextureCheckbox->setDisabled(true);
- ui->MiscTextureLineEdit->setDisabled(true);
- }
-
- ui->useShineTexturecheckbox->setChecked(replace = _model->getReplace(row)["shine"]);
- if (replace) {
- ui->inheritShineTextureCheckbox->setChecked(inherit = _model->getInherit(row)["shine"]);
- ui->inheritShineTextureCheckbox->setEnabled(replace);
-
- ui->ShineTextureLineEdit->setEnabled(!inherit);
- if (!inherit) {
- ui->ShineTextureLineEdit->setText(_model->getMap(row, "shine").c_str());
- }
- } else {
- ui->inheritShineTextureCheckbox->setDisabled(true);
- ui->ShineTextureLineEdit->setDisabled(true);
- }
-
- ui->useGlowTexturecheckbox->setChecked(replace = _model->getReplace(row)["glow"]);
- if (replace) {
- ui->inheritGlowTextureCheckbox->setEnabled(replace);
- ui->inheritGlowTextureCheckbox->setChecked(inherit = _model->getInherit(row)["glow"]);
- ui->GlowTextureLineEdit->setEnabled(!inherit);
- if (!inherit) {
- ui->GlowTextureLineEdit->setText(_model->getMap(row, "glow").c_str());
- }
- } else {
- ui->inheritGlowTextureCheckbox->setDisabled(true);
- ui->GlowTextureLineEdit->setDisabled(true);
- }
-
- ui->useNormalTexturecheckbox->setChecked(replace = _model->getReplace(row)["normal"]);
- if (replace) {
- ui->inheritNormalTextureCheckbox->setChecked(inherit = _model->getInherit(row)["normal"]);
- ui->inheritNormalTextureCheckbox->setEnabled(replace);
- ui->NormalTextureLineEdit->setEnabled(!inherit);
- if (!inherit) {
- ui->NormalTextureLineEdit->setText(_model->getMap(row, "normal").c_str());
- }
- } else {
- ui->inheritNormalTextureCheckbox->setDisabled(true);
- ui->NormalTextureLineEdit->setDisabled(true);
- }
-
- ui->useHeightTexturecheckbox->setChecked(replace = _model->getReplace(row)["height"]);
- if (replace) {
- ui->inheritHeightTextureCheckbox->setChecked(inherit = _model->getInherit(row)["height"]);
- ui->inheritHeightTextureCheckbox->setEnabled(replace);
- ui->HeightTextureLineEdit->setEnabled(!inherit);
- if (!inherit) {
- ui->HeightTextureLineEdit->setText(_model->getMap(row, "height").c_str());
- }
- } else {
- ui->inheritHeightTextureCheckbox->setDisabled(true);
- ui->HeightTextureLineEdit->setDisabled(true);
- }
-
- ui->useAmbiantTexturecheckbox->setChecked(replace = _model->getReplace(row)["ao"]);
- if (replace) {
- ui->inheritAmbiantTextureCheckbox->setChecked(inherit = _model->getInherit(row)["ao"]);
- ui->AmbiantTextureLineEdit->setEnabled(!inherit);
- ui->inheritAmbiantTextureCheckbox->setEnabled(replace);
- if (!inherit) {
- ui->AmbiantTextureLineEdit->setText(_model->getMap(row, "ao").c_str());
- }
- } else {
- ui->inheritAmbiantTextureCheckbox->setDisabled(true);
- ui->AmbiantTextureLineEdit->setDisabled(true);
- }
-
- ui->useReflectTexturecheckbox->setChecked(replace = _model->getReplace(row)["reflect"]);
- if (replace) {
- ui->inheritReflectTextureCheckbox->setChecked(inherit = _model->getInherit(row)["reflect"]);
- ui->ReflectTextureLineEdit->setEnabled(!inherit);
- ui->inheritReflectTextureCheckbox->setEnabled(replace);
- if (!inherit) {
- ui->ReflectTextureLineEdit->setText(_model->getMap(row, "reflect").c_str());
+ ui->newTextureLineEdit->setText(_model->getMap(_selectedRow, "main").c_str());
+ for (auto& [type, widgets] : _textureRows) {
+ bool replace = _model->getReplace(_selectedRow)[type];
+ widgets.useCheckbox->setChecked(replace);
+ if (replace) {
+ bool inherit = _model->getInherit(_selectedRow)[type];
+ widgets.inheritCheckbox->setEnabled(true);
+ widgets.inheritCheckbox->setChecked(inherit);
+ widgets.lineEdit->setEnabled(!inherit);
+ if (inherit)
+ widgets.lineEdit->setText(_model->getMap(_selectedRow, "main").c_str());
+ else
+ widgets.lineEdit->setText(_model->getMap(_selectedRow, type).c_str());
+ } else {
+ widgets.inheritCheckbox->setDisabled(true);
+ widgets.lineEdit->setDisabled(true);
}
- } else {
- ui->inheritReflectTextureCheckbox->setDisabled(true);
- ui->ReflectTextureLineEdit->setDisabled(true);
}
}
-void ShipTextureReplacementDialog::updateUIFull()
+void ShipTextureReplacementDialog::updateUiFull()
{
util::SignalBlockers blockers(this);
const QModelIndex index = ui->TexturesList->selectionModel()->currentIndex();
- row = index.row();
- SCP_map subtypes = _model->getSubtypesForMap(row);
- bool hide = !(subtypes)["misc"];
- ui->inheritMiscTextureCheckbox->setHidden(hide);
- ui->MiscTextureLabel->setHidden(hide);
- ui->MiscTextureLineEdit->setHidden(hide);
- ui->useMiscTexturecheckbox->setHidden(hide);
- hide = !(subtypes)["shine"];
- ui->inheritShineTextureCheckbox->setHidden(hide);
- ui->ShineTextureLabel->setHidden(hide);
- ui->ShineTextureLineEdit->setHidden(hide);
- ui->useShineTexturecheckbox->setHidden(hide);
- hide = !(subtypes)["glow"];
- ui->inheritGlowTextureCheckbox->setHidden(hide);
- ui->GlowTextureLabel->setHidden(hide);
- ui->GlowTextureLineEdit->setHidden(hide);
- ui->useGlowTexturecheckbox->setHidden(hide);
- hide = !(subtypes)["normal"];
- ui->inheritNormalTextureCheckbox->setHidden(hide);
- ui->NormalTextureLabel->setHidden(hide);
- ui->NormalTextureLineEdit->setHidden(hide);
- ui->useNormalTexturecheckbox->setHidden(hide);
- hide = !(subtypes)["height"];
- ui->inheritHeightTextureCheckbox->setHidden(hide);
- ui->HeightTextureLabel->setHidden(hide);
- ui->HeightTextureLineEdit->setHidden(hide);
- ui->useHeightTexturecheckbox->setHidden(hide);
- hide = !(subtypes)["ao"];
- ui->inheritAmbiantTextureCheckbox->setHidden(hide);
- ui->AmbiantTextureLabel->setHidden(hide);
- ui->AmbiantTextureLineEdit->setHidden(hide);
- ui->useAmbiantTexturecheckbox->setHidden(hide);
- hide = !(subtypes)["reflect"];
- ui->inheritReflectTextureCheckbox->setHidden(hide);
- ui->ReflectTextureLabel->setHidden(hide);
- ui->ReflectTextureLineEdit->setHidden(hide);
- ui->useReflectTexturecheckbox->setHidden(hide);
+ _selectedRow = index.row();
+ SCP_map subtypes = _model->getSubtypesForMap(_selectedRow);
+ for (auto& [type, widgets] : _textureRows) {
+ bool hide = !subtypes[type];
+ widgets.label->setHidden(hide);
+ widgets.useCheckbox->setHidden(hide);
+ widgets.inheritCheckbox->setHidden(hide);
+ widgets.lineEdit->setHidden(hide);
+ }
resize(QDialog::sizeHint());
- updateUI();
+ updateUi();
}
} // namespace fso::fred::dialogs
diff --git a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h
index 7bf8dc541f5..5fc056ad874 100644
--- a/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h
+++ b/qtfred/src/ui/dialogs/ShipEditor/ShipTextureReplacementDialog.h
@@ -3,7 +3,10 @@
#include
#include
+#include
#include
+#include
+#include
#include
namespace fso::fred::dialogs {
@@ -22,7 +25,6 @@ class MapModel : public QAbstractListModel {
};
class ShipTextureReplacementDialog : public QDialog {
-
Q_OBJECT
public:
@@ -37,39 +39,23 @@ class ShipTextureReplacementDialog : public QDialog {
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
-
void on_newTextureLineEdit_editingFinished();
- void on_MiscTextureLineEdit_editingFinished();
- void on_GlowTextureLineEdit_editingFinished();
- void on_ShineTextureLineEdit_editingFinished();
- void on_NormalTextureLineEdit_editingFinished();
- void on_HeightTextureLineEdit_editingFinished();
- void on_AmbiantTextureLineEdit_editingFinished();
- void on_ReflectTextureLineEdit_editingFinished();
-
- void on_useMiscTexturecheckbox_toggled(bool);
- void on_useGlowTexturecheckbox_toggled(bool);
- void on_useShineTexturecheckbox_toggled(bool);
- void on_useNormalTexturecheckbox_toggled(bool);
- void on_useHeightTexturecheckbox_toggled(bool);
- void on_useAmbiantTexturecheckbox_toggled(bool);
- void on_useReflectTexturecheckbox_toggled(bool);
- void on_inheritMiscTexturecheckbox_toggled(bool);
- void on_inheritGlowTexturecheckbox_toggled(bool);
- void on_inheritShineTexturecheckbox_toggled(bool);
- void on_inheritNormalTexturecheckbox_toggled(bool);
- void on_inheritHeightTexturecheckbox_toggled(bool);
- void on_inheritAmbiantTexturecheckbox_toggled(bool);
- void on_inheritReflectTexturecheckbox_toggled(bool);
+ private: // NOLINT(readability-redundant-access-specifiers)
+ struct TextureTypeRow {
+ QLabel* label;
+ QCheckBox* useCheckbox;
+ QCheckBox* inheritCheckbox;
+ QLineEdit* lineEdit;
+ };
- private:// NOLINT(readability-redundant-access-specifiers)
std::unique_ptr ui;
std::unique_ptr _model;
EditorViewport* _viewport;
- int row = 0;
- MapModel* listmodel;
- void updateUI();
- void updateUIFull();
+ int _selectedRow = 0;
+ MapModel* _listModel;
+ SCP_map _textureRows;
+ void updateUi();
+ void updateUiFull();
};
} // namespace fso::fred::dialogs
\ No newline at end of file
diff --git a/qtfred/ui/ShipTextureReplacementDialog.ui b/qtfred/ui/ShipTextureReplacementDialog.ui
index cf4ba715e19..50abad213be 100644
--- a/qtfred/ui/ShipTextureReplacementDialog.ui
+++ b/qtfred/ui/ShipTextureReplacementDialog.ui
@@ -50,176 +50,7 @@
Other Maps
-
- -
-
-
- Replace
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
- -
-
-
- Shine
-
-
-
- -
-
-
- Height
-
-
-
- -
-
-
- Misc
-
-
-
- -
-
-
- Replace
-
-
-
- -
-
-
- Replace
-
-
-
- -
-
-
- Glow
-
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
- Replace
-
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
- Replace
-
-
-
- -
-
-
- Normal
-
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
- -
-
-
- Replace
-
-
-
- -
-
-
- AO
-
-
-
- -
-
-
- Reflect
-
-
-
- -
-
-
- Replace
-
-
-
- -
-
-
- Inherit
-
-
-
- -
-
-
-
+
-