From fce46a87ad180550f0f667fca3e56975ccdcbb79 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Tue, 29 Apr 2025 16:47:08 -0500 Subject: [PATCH 1/7] Activate Lab Weapon viewer --- code/lab/dialogs/lab_ui.cpp | 44 ++++++++-- code/lab/dialogs/lab_ui.h | 3 +- code/lab/dialogs/lab_ui_helpers.cpp | 126 ++++++++++++++++++++++++++++ code/lab/dialogs/lab_ui_helpers.h | 2 + code/lab/manager/lab_manager.cpp | 59 ++++++++++--- code/lab/manager/lab_manager.h | 6 ++ code/lab/renderer/lab_cameras.cpp | 45 +++++++++- code/lab/renderer/lab_cameras.h | 13 ++- code/lab/renderer/lab_renderer.cpp | 23 ++++- code/weapon/beam.cpp | 3 +- 10 files changed, 300 insertions(+), 24 deletions(-) diff --git a/code/lab/dialogs/lab_ui.cpp b/code/lab/dialogs/lab_ui.cpp index 2cad3936d22..467fe207a04 100644 --- a/code/lab/dialogs/lab_ui.cpp +++ b/code/lab/dialogs/lab_ui.cpp @@ -95,10 +95,10 @@ void LabUi::build_weapon_list() const { //weapon display needs to be rethought - //with_TreeNode("Weapon Classes") - //{ - // build_weapon_subtype_list(); - //} + with_TreeNode("Weapon Classes") + { + build_weapon_subtype_list(); + } } void LabUi::build_background_list() const @@ -523,7 +523,7 @@ void LabUi::do_triggered_anim(animation::ModelAnimationTriggerType type, TextUnformatted(colB); \ -void LabUi::build_table_info_txtbox(ship_info* sip) const +void LabUi::build_ship_table_info_txtbox(ship_info* sip) const { with_TreeNode("Table information") { @@ -543,6 +543,26 @@ void LabUi::build_table_info_txtbox(ship_info* sip) const } } +void LabUi::build_weapon_table_info_txtbox(weapon_info* wip) const +{ + with_TreeNode("Table information") + { + // Cache result across frames for performance + static SCP_string table_text; + static int old_class = getLabManager()->CurrentClass; + + if (table_text.length() == 0 || old_class != getLabManager()->CurrentClass) { + table_text = get_weapon_table_text(wip); + } + + InputTextMultiline("##weapon_table_text", + const_cast(table_text.c_str()), + table_text.length(), + ImVec2(-FLT_MIN, GetTextLineHeight() * 16), + ImGuiInputTextFlags_ReadOnly); + } +} + void LabUi::build_model_info_box_actual(ship_info* sip, polymodel* pm) const { ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg; @@ -1127,7 +1147,7 @@ void LabUi::show_object_options() const with_CollapsingHeader(sip->name) { - build_table_info_txtbox(sip); + build_ship_table_info_txtbox(sip); build_model_info_box(sip, pm); @@ -1154,6 +1174,18 @@ void LabUi::show_object_options() const { build_weapon_options(shipp); } + } else if (getLabManager()->CurrentMode == LabMode::Weapon && getLabManager()->isSafeForWeapons()) { + auto wip = &Weapon_info[getLabManager()->CurrentClass]; + + with_CollapsingHeader("Weapon Info") + { + build_weapon_table_info_txtbox(wip); + } + + with_CollapsingHeader("Object Actions") + { + Checkbox("Allow weapon to reach end of life", &getLabManager()->AllowWeaponDestruction); + } } } } diff --git a/code/lab/dialogs/lab_ui.h b/code/lab/dialogs/lab_ui.h index 3e2cd666b82..10554d11654 100644 --- a/code/lab/dialogs/lab_ui.h +++ b/code/lab/dialogs/lab_ui.h @@ -31,7 +31,8 @@ class LabUi { void build_texture_quality_combobox(); void build_antialiasing_combobox(); void build_tone_mapper_combobox(); - void build_table_info_txtbox(ship_info* sip) const; + void build_ship_table_info_txtbox(ship_info* sip) const; + void build_weapon_table_info_txtbox(weapon_info* wip) const; void build_model_info_box(ship_info* sip, polymodel* pm) const; void build_subsystem_list(object* objp, ship* shipp) const; void build_subsystem_list_entry(SCP_string& subsys_name, diff --git a/code/lab/dialogs/lab_ui_helpers.cpp b/code/lab/dialogs/lab_ui_helpers.cpp index ad4f3f4f2b1..972701f7b46 100644 --- a/code/lab/dialogs/lab_ui_helpers.cpp +++ b/code/lab/dialogs/lab_ui_helpers.cpp @@ -131,6 +131,132 @@ SCP_string get_ship_table_text(ship_info* sip) return result; } +SCP_string get_weapon_table_text(weapon_info* wip) +{ + char line[256], line2[256], file_text[82]; + int i, j, n, found = 0, comment = 0, num_files = 0; + SCP_vector tbl_file_names; + SCP_string result; + + auto fp = cfopen("weapons.tbl", "r"); + Assert(fp); + + while (cfgets(line, 255, fp)) { + while (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = 0; + + for (i = j = 0; line[i]; i++) { + if (line[i] == '/' && line[i + 1] == '/') + break; + + if (line[i] == '/' && line[i + 1] == '*') { + comment = 1; + i++; + continue; + } + + if (line[i] == '*' && line[i + 1] == '/') { + comment = 0; + i++; + continue; + } + + if (!comment) + line2[j++] = line[i]; + } + + line2[j] = 0; + if (!strnicmp(line2, "$Name:", 6)) { + drop_trailing_white_space(line2); + found = 0; + i = 6; + + while (line2[i] == ' ' || line2[i] == '\t' || line2[i] == '@') + i++; + + if (!stricmp(line2 + i, wip->name)) { + result += "-- weapons.tbl -------------------------------\r\n"; + found = 1; + } + } + + if (found) { + result += line; + result += "\r\n"; + } + } + + cfclose(fp); + + num_files = cf_get_file_list(tbl_file_names, CF_TYPE_TABLES, NOX("*-wep.tbm"), CF_SORT_REVERSE); + + for (n = 0; n < num_files; n++) { + tbl_file_names[n] += ".tbm"; + + fp = cfopen(tbl_file_names[n].c_str(), "r"); + Assert(fp); + + memset(line, 0, sizeof(line)); + memset(line2, 0, sizeof(line2)); + found = 0; + comment = 0; + + while (cfgets(line, 255, fp)) { + while (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = 0; + + for (i = j = 0; line[i]; i++) { + if (line[i] == '/' && line[i + 1] == '/') + break; + + if (line[i] == '/' && line[i + 1] == '*') { + comment = 1; + i++; + continue; + } + + if (line[i] == '*' && line[i + 1] == '/') { + comment = 0; + i++; + continue; + } + + if (!comment) + line2[j++] = line[i]; + } + + line2[j] = 0; + if (!strnicmp(line2, "$Name:", 6)) { + drop_trailing_white_space(line2); + found = 0; + i = 6; + + while (line2[i] == ' ' || line2[i] == '\t' || line2[i] == '@') + i++; + + if (!stricmp(line2 + i, wip->name)) { + memset(file_text, 0, sizeof(file_text)); + snprintf(file_text, + sizeof(file_text) - 1, + "-- %s -------------------------------\r\n", + tbl_file_names[n].c_str()); + result += file_text; + found = 1; + } + } + + if (found) { + result += line; + result += "\r\n"; + } + } + + cfclose(fp); + } + + return result; +} + SCP_string get_directory_or_vp(const char* path) { SCP_string result(path); diff --git a/code/lab/dialogs/lab_ui_helpers.h b/code/lab/dialogs/lab_ui_helpers.h index e0982a5b20e..818c4bd208a 100644 --- a/code/lab/dialogs/lab_ui_helpers.h +++ b/code/lab/dialogs/lab_ui_helpers.h @@ -4,6 +4,8 @@ SCP_string get_ship_table_text(ship_info* sip); +SCP_string get_weapon_table_text(weapon_info* wip); + SCP_string get_directory_or_vp(const char* path); bool graphics_options_changed(); diff --git a/code/lab/manager/lab_manager.cpp b/code/lab/manager/lab_manager.cpp index 3d21a17cd48..bc742738872 100644 --- a/code/lab/manager/lab_manager.cpp +++ b/code/lab/manager/lab_manager.cpp @@ -20,6 +20,7 @@ //Turret firing forward declarations void ai_turret_execute_behavior(const ship* shipp, ship_subsys* ss); +extern void beam_delete(beam* b); void lab_exit() { @@ -359,20 +360,31 @@ void LabManager::onFrame(float frametime) { gr_flip(); } -void LabManager::changeDisplayedObject(LabMode mode, int info_index) { - if (mode == CurrentMode && info_index == CurrentClass) - return; - - CurrentMode = mode; - CurrentClass = info_index; - +void LabManager::cleanup() { if (CurrentObject != -1) { + // Stop any firing turrets FireTurrets.clear(); + // Surprisingly obj_delete_all() does not delete beams so we have to vanish it manually + object* obj = &Objects[getLabManager()->CurrentObject]; + if (obj->type == OBJ_BEAM && Beam_count > 0) { + beam* b = &Beams[Objects[CurrentObject].instance]; + beam_delete(b); + } obj_delete_all(); CurrentObject = -1; } +} + +void LabManager::changeDisplayedObject(LabMode mode, int info_index) { + if (mode == CurrentMode && info_index == CurrentClass) + return; + + CurrentMode = mode; + CurrentClass = info_index; + + cleanup(); switch (CurrentMode) { case LabMode::Ship: @@ -380,9 +392,36 @@ void LabManager::changeDisplayedObject(LabMode mode, int info_index) { changeShipInternal(); break; case LabMode::Weapon: - CurrentObject = weapon_create(&CurrentPosition, &CurrentOrientation, CurrentClass, -1); - if (Weapon_info[CurrentClass].model_num != -1) { - ModelFilename = model_get(Weapon_info[CurrentClass].model_num)->filename; + if (Weapon_info[CurrentClass].wi_flags[Weapon::Info_Flags::Beam]) { + beam_fire_info fire_info; + memset(&fire_info, 0, sizeof(beam_fire_info)); + fire_info.accuracy = 0.000001f; // this will guarantee a hit + fire_info.bfi_flags |= BFIF_FLOATING_BEAM; + fire_info.turret = nullptr; // A free-floating beam isn't fired from a subsystem. + fire_info.burst_index = 0; + fire_info.beam_info_index = CurrentClass; + fire_info.shooter = nullptr; + fire_info.team = 0; + fire_info.starting_pos = CurrentPosition; + fire_info.target = nullptr; + fire_info.target_subsys = nullptr; + fire_info.bfi_flags |= BFIF_TARGETING_COORDS; + fire_info.fire_method = BFM_SEXP_FLOATING_FIRED; + + // Fire beam straight ahead from spawn origin + vec3d origin = CurrentPosition; + vec3d endpoint; + vm_vec_scale_add(&endpoint, &origin, &vmd_z_vector, 1500.0f); // Fire forward along +Z + + fire_info.target_pos1 = endpoint; + fire_info.target_pos2 = endpoint; + + CurrentObject = beam_fire(&fire_info); + } else { + CurrentObject = weapon_create(&CurrentPosition, &CurrentOrientation, CurrentClass, -1); + if (Weapon_info[CurrentClass].model_num != -1) { + ModelFilename = model_get(Weapon_info[CurrentClass].model_num)->filename; + } } break; default: diff --git a/code/lab/manager/lab_manager.h b/code/lab/manager/lab_manager.h index 5ef4f37ce71..0ea0ffc7b09 100644 --- a/code/lab/manager/lab_manager.h +++ b/code/lab/manager/lab_manager.h @@ -26,6 +26,9 @@ class LabManager { // Do rendering and handle keyboard/mouse events void onFrame(float frametime); + + // Cleanup all objects + void cleanup(); // Creates a new object of the passed type, using the respective class definition found at info_index and replaces the currently // displayed object @@ -34,6 +37,8 @@ class LabManager { void close() { animation::ModelAnimationSet::stopAnimations(); + cleanup(); + LabRenderer::close(); Game_mode &= ~GM_LAB; @@ -83,6 +88,7 @@ class LabManager { LabRotationMode RotationMode = LabRotationMode::Both; float RotationSpeedDivisor = 100.0f; + bool AllowWeaponDestruction = false; flagset Flags; diff --git a/code/lab/renderer/lab_cameras.cpp b/code/lab/renderer/lab_cameras.cpp index 64df91cdb95..cf6057d3b2d 100644 --- a/code/lab/renderer/lab_cameras.cpp +++ b/code/lab/renderer/lab_cameras.cpp @@ -29,8 +29,30 @@ void OrbitCamera::handleInput(int dx, int dy, bool, bool rmbDown, int modifierKe } void OrbitCamera::displayedObjectChanged() { + float distance_multiplier = 1.6f; + if (getLabManager()->CurrentObject != -1) { - distance = Objects[getLabManager()->CurrentObject].radius * 1.6f; + object* obj = &Objects[getLabManager()->CurrentObject]; + + // Ships and Missiles use the object radius to get a camera distance + distance = obj->radius * distance_multiplier; + + if (obj->type == OBJ_WEAPON || obj->type == OBJ_BEAM) { + int wep_index; + // Beams use the muzzle radius + if (obj->type == OBJ_BEAM) { + wep_index = Beams[obj->instance].weapon_info_index; + weapon_info* wip = &Weapon_info[wep_index]; + distance = wip->b_info.beam_muzzle_radius * distance_multiplier; + // Lasers use the laser length + } else { + wep_index = Weapons[obj->instance].weapon_info_index; + weapon_info* wip = &Weapon_info[wep_index]; + if (wip != nullptr && wip->render_type == WRT_LASER) { + distance = wip->laser_length * distance_multiplier; + } + } + } } updateCamera(); @@ -45,6 +67,25 @@ void OrbitCamera::updateCamera() { vm_vec_scale(&new_position, distance); + object* obj = &Objects[getLabManager()->CurrentObject]; + vec3d target = obj->pos; + + if (obj->type == OBJ_WEAPON) { + weapon_info* wip = &Weapon_info[Weapons[obj->instance].weapon_info_index]; + if (wip != nullptr && wip->render_type == WRT_LASER) { + // Offset target by half the laser length forward along the facing + vec3d forward; + vm_vec_copy_normalize(&forward, &obj->orient.vec.fvec); + vm_vec_scale_add2(&target, &forward, wip->laser_length * 0.5f); + } + + vm_vec_add2(&new_position, &target); + } + cam->set_position(&new_position); - cam->set_rotation_facing(&getLabManager()->CurrentPosition); + + // If these are the same then that's not great so do nothing and use the last facing value + if (!vm_vec_same(&new_position, &target)) { + cam->set_rotation_facing(&target); + } } diff --git a/code/lab/renderer/lab_cameras.h b/code/lab/renderer/lab_cameras.h index 3a2e6a467a3..4aec57f697e 100644 --- a/code/lab/renderer/lab_cameras.h +++ b/code/lab/renderer/lab_cameras.h @@ -44,6 +44,12 @@ class LabCamera { /// /// virtual bool handlesObjectPlacement() = 0; + + /// + /// Forces the camera to update its position based on input or object position + /// + /// + virtual void updateCamera() = 0; }; class OrbitCamera : public LabCamera { @@ -68,10 +74,11 @@ class OrbitCamera : public LabCamera { void displayedObjectChanged() override; bool handlesObjectPlacement() override { return false; } -private: + + void updateCamera() override; + + private: float distance = 100.0f; float phi = 1.24f; float theta = 2.25f; - - void updateCamera(); }; \ No newline at end of file diff --git a/code/lab/renderer/lab_renderer.cpp b/code/lab/renderer/lab_renderer.cpp index cf083a2a842..7b7151f9f21 100644 --- a/code/lab/renderer/lab_renderer.cpp +++ b/code/lab/renderer/lab_renderer.cpp @@ -74,7 +74,10 @@ void LabRenderer::renderModel(float frametime) { object* obj = &Objects[getLabManager()->CurrentObject]; - obj->pos = getLabManager()->CurrentPosition; + // Ships stay put. Weapons are allowed to move so particle effects look correct + if (obj->type == OBJ_SHIP) { + obj->pos = getLabManager()->CurrentPosition; + } obj->orient = getLabManager()->CurrentOrientation; Envmap_override = renderFlags[LabRenderFlag::NoEnvMap]; @@ -131,8 +134,26 @@ void LabRenderer::renderModel(float frametime) { Ships[obj->instance].flags.set(Ship::Ship_Flags::No_thrusters); } + // Prevent weapons from destroying themselves + if (!getLabManager()->AllowWeaponDestruction) { + if (obj->type == OBJ_WEAPON) { + weapon* wep = &Weapons[obj->instance]; + weapon_info* wip = &Weapon_info[wep->weapon_info_index]; + + if (wip != nullptr && !wip->wi_flags[Weapon::Info_Flags::Beam]) { + wep->lifeleft = wip->lifetime; + } + } else if (obj->type == OBJ_BEAM) { + beam* b = &Beams[obj->instance]; + b->life_left = b->life_total; + } + } + obj_move_all(frametime); + // Force the camera to follow our current object + labCamera->updateCamera(); + particle::move_all(frametime); particle::ParticleManager::get()->doFrame(frametime); shockwave_move_all(frametime); diff --git a/code/weapon/beam.cpp b/code/weapon/beam.cpp index 7d991c14217..1d8d14a06ca 100644 --- a/code/weapon/beam.cpp +++ b/code/weapon/beam.cpp @@ -1662,7 +1662,8 @@ void beam_render_muzzle_glow(beam *b) // don't show the muzzle glow for players in the cockpit unless show_ship_model is on, provided Render_player_mflash isn't on bool in_cockpit_view = (Viewer_mode & (VM_EXTERNAL | VM_CHASE | VM_OTHER_SHIP | VM_WARP_CHASE)) == 0; bool player_show_ship_model = ( - b->objp == Player_obj + b->objp != nullptr // Can validly be nullptr for floating beams! + && b->objp == Player_obj && Ship_info[Ships[b->objp->instance].ship_info_index].flags[Ship::Info_Flags::Show_ship_model] && (!Show_ship_only_if_cockpits_enabled || Cockpit_active)); if ((b->flags & BF_IS_FIGHTER_BEAM) && (b->objp == Player_obj && !Render_player_mflash && in_cockpit_view && !player_show_ship_model)) { From 9eeb9756c1e4540467faa62c2ebe204db76747bd Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Wed, 30 Apr 2025 11:47:23 -0500 Subject: [PATCH 2/7] Allow displaying weapon tech models --- code/graphics/shadows.cpp | 1 + code/lab/dialogs/lab_ui.cpp | 6 +++ code/lab/manager/lab_manager.cpp | 14 +++++-- code/lab/manager/lab_manager.h | 1 + code/lab/renderer/lab_cameras.cpp | 23 +++++------ code/nebula/neb.cpp | 3 +- code/object/object.cpp | 68 +++++++++++++++++++++++++++++++ code/object/object.h | 50 +++++++++++++++-------- code/object/objectsort.cpp | 7 +++- 9 files changed, 138 insertions(+), 35 deletions(-) diff --git a/code/graphics/shadows.cpp b/code/graphics/shadows.cpp index 3b390409918..f9db7329300 100644 --- a/code/graphics/shadows.cpp +++ b/code/graphics/shadows.cpp @@ -508,6 +508,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos) switch(objp->type) { + case OBJ_RAW_POF: case OBJ_SHIP: { obj_queue_render(objp, &scene); diff --git a/code/lab/dialogs/lab_ui.cpp b/code/lab/dialogs/lab_ui.cpp index 467fe207a04..3476fb4c72f 100644 --- a/code/lab/dialogs/lab_ui.cpp +++ b/code/lab/dialogs/lab_ui.cpp @@ -1185,6 +1185,12 @@ void LabUi::show_object_options() const with_CollapsingHeader("Object Actions") { Checkbox("Allow weapon to reach end of life", &getLabManager()->AllowWeaponDestruction); + + if (VALID_FNAME(wip->tech_model)) { + if (Checkbox("Show Tech Model", &getLabManager()->ShowingTechModel)) { + getLabManager()->changeDisplayedObject(LabMode::Weapon, getLabManager()->CurrentClass); + } + } } } } diff --git a/code/lab/manager/lab_manager.cpp b/code/lab/manager/lab_manager.cpp index bc742738872..8ea4699a6fa 100644 --- a/code/lab/manager/lab_manager.cpp +++ b/code/lab/manager/lab_manager.cpp @@ -5,6 +5,7 @@ #include "asteroid/asteroid.h" #include "math/staticrand.h" #include "missionui/missionscreencommon.h" +#include "object/object.h" #include "debris/debris.h" #include "ship/ship.h" #include "ship/shipfx.h" @@ -378,8 +379,12 @@ void LabManager::cleanup() { } void LabManager::changeDisplayedObject(LabMode mode, int info_index) { - if (mode == CurrentMode && info_index == CurrentClass) - return; + // Removing this allows reseting by clicking on the object again, + // making it easier to respawn destroyed objects + // If this is re-enabled then it will need to be modified so that + // ShowingTechModel bool toggles are also accounted for + //if (mode == CurrentMode && info_index == CurrentClass) + //return; CurrentMode = mode; CurrentClass = info_index; @@ -392,7 +397,10 @@ void LabManager::changeDisplayedObject(LabMode mode, int info_index) { changeShipInternal(); break; case LabMode::Weapon: - if (Weapon_info[CurrentClass].wi_flags[Weapon::Info_Flags::Beam]) { + if (ShowingTechModel) { + ModelFilename = Weapon_info[CurrentClass].tech_model; + CurrentObject = obj_raw_pof_create(ModelFilename.c_str(), &CurrentOrientation, &CurrentPosition); + }else if (Weapon_info[CurrentClass].wi_flags[Weapon::Info_Flags::Beam]) { beam_fire_info fire_info; memset(&fire_info, 0, sizeof(beam_fire_info)); fire_info.accuracy = 0.000001f; // this will guarantee a hit diff --git a/code/lab/manager/lab_manager.h b/code/lab/manager/lab_manager.h index 0ea0ffc7b09..1790d2ff93d 100644 --- a/code/lab/manager/lab_manager.h +++ b/code/lab/manager/lab_manager.h @@ -56,6 +56,7 @@ class LabManager { vec3d CurrentPosition = vmd_zero_vector; matrix CurrentOrientation = vmd_identity_matrix; SCP_string ModelFilename = ""; + bool ShowingTechModel = false; bool isSafeForShips() { return CurrentMode == LabMode::Ship && CurrentObject != -1; diff --git a/code/lab/renderer/lab_cameras.cpp b/code/lab/renderer/lab_cameras.cpp index cf6057d3b2d..52ab6aaf285 100644 --- a/code/lab/renderer/lab_cameras.cpp +++ b/code/lab/renderer/lab_cameras.cpp @@ -37,20 +37,17 @@ void OrbitCamera::displayedObjectChanged() { // Ships and Missiles use the object radius to get a camera distance distance = obj->radius * distance_multiplier; - if (obj->type == OBJ_WEAPON || obj->type == OBJ_BEAM) { - int wep_index; - // Beams use the muzzle radius - if (obj->type == OBJ_BEAM) { - wep_index = Beams[obj->instance].weapon_info_index; - weapon_info* wip = &Weapon_info[wep_index]; + // Beams use the muzzle radius + if (obj->type == OBJ_BEAM) { + weapon_info* wip = &Weapon_info[Beams[obj->instance].weapon_info_index]; + if (wip != nullptr) { distance = wip->b_info.beam_muzzle_radius * distance_multiplier; - // Lasers use the laser length - } else { - wep_index = Weapons[obj->instance].weapon_info_index; - weapon_info* wip = &Weapon_info[wep_index]; - if (wip != nullptr && wip->render_type == WRT_LASER) { - distance = wip->laser_length * distance_multiplier; - } + } + // Lasers use the laser length + } else if (obj->type == OBJ_WEAPON) { + weapon_info* wip = &Weapon_info[Weapons[obj->instance].weapon_info_index]; + if (wip != nullptr && wip->render_type == WRT_LASER) { + distance = wip->laser_length * distance_multiplier; } } } diff --git a/code/nebula/neb.cpp b/code/nebula/neb.cpp index e13b4456ce6..86cdd219e69 100644 --- a/code/nebula/neb.cpp +++ b/code/nebula/neb.cpp @@ -671,7 +671,8 @@ int neb2_skip_render(object *objp, float z_depth) } break; - // any ship less than 3% visible at their closest point + // any ship or raw pof less than 3% visible at their closest point + case OBJ_RAW_POF: case OBJ_SHIP: if (fog < 0.03f) return 1; diff --git a/code/object/object.cpp b/code/object/object.cpp index 88233c0d4aa..0d2e9a56e4a 100644 --- a/code/object/object.cpp +++ b/code/object/object.cpp @@ -70,6 +70,7 @@ object *Viewer_obj = NULL; //Data for objects object Objects[MAX_OBJECTS]; +SCP_map Pof_objects; #ifdef OBJECT_CHECK checkobject CheckObjects[MAX_OBJECTS]; @@ -140,6 +141,7 @@ const char *Object_type_names[MAX_OBJECT_TYPES] = { "Asteroid", "Jump Node", "Beam", + "Raw Pof" //XSTR:ON }; @@ -276,6 +278,7 @@ int free_object_slots(int target_num_used) case OBJ_ASTEROID: case OBJ_JUMP_NODE: case OBJ_BEAM: + case OBJ_RAW_POF: break; default: Int3(); // Hey, what kind of object is this? Unknown! @@ -564,6 +567,49 @@ void obj_free(int objnum) } } +/** + * Create a raw pof item + * @return the objnum of this raw POF + */ +int obj_raw_pof_create(const char* pof_filename, matrix* orient, vec3d* pos) +{ + static int next_raw_pof_id = 0; + + // Unlikely this would ever be hit.. but just in case + if (next_raw_pof_id >= INT_MAX) { + Error(LOCATION, "Too many RAW_POF objects created!"); + return -1; + } + + if (!VALID_FNAME(pof_filename)) { + Warning(LOCATION, "Invalid tech model POF: %s", pof_filename); + return -1; + } + + int model_num = model_load(pof_filename); + if (model_num < 0) { + Warning(LOCATION, "Failed to load tech model: %s", pof_filename); + return -1; + } + + float radius = model_get_radius(model_num); + + raw_pof_obj pof_obj = {-1, -1}; + pof_obj.model_num = model_num; + + int id = next_raw_pof_id++; + Pof_objects[id] = pof_obj; + + flagset flags; + flags.set(Object::Object_Flags::Renders); + + int objnum = obj_create(OBJ_RAW_POF, -1, id, orient, pos, radius, flags); + + Pof_objects[id].model_instance = model_create_instance(objnum, model_num); + + return objnum; +} + /** * Initialize a new object. Adds to the list for the given segment. * @@ -715,6 +761,10 @@ void obj_delete(int objnum) break; case OBJ_BEAM: break; + case OBJ_RAW_POF: + model_delete_instance(Pof_objects[objp->instance].model_instance); + Pof_objects.erase(objp->instance); + break; case OBJ_NONE: Int3(); break; @@ -1222,6 +1272,8 @@ void obj_move_all_pre(object *objp, float frametime) break; case OBJ_BEAM: break; + case OBJ_RAW_POF: + break; case OBJ_NONE: Int3(); break; @@ -1485,6 +1537,9 @@ void obj_move_all_post(object *objp, float frametime) case OBJ_BEAM: break; + case OBJ_RAW_POF: + break; + case OBJ_NONE: Int3(); break; @@ -1842,6 +1897,14 @@ void obj_queue_render(object* obj, model_draw_list* scene) break; case OBJ_BEAM: break; + case OBJ_RAW_POF: + { + model_render_params render_info; + render_info.set_object_number(OBJ_INDEX(obj)); + + model_render_queue(&render_info, scene, Pof_objects[obj->instance].model_num, &obj->orient, &obj->pos); + } + break; default: Error( LOCATION, "Unhandled obj type %d in obj_render", obj->type ); } @@ -1947,6 +2010,7 @@ int obj_team(object *objp) case OBJ_GHOST: case OBJ_SHOCKWAVE: case OBJ_BEAM: + case OBJ_RAW_POF: team = -1; break; @@ -2094,6 +2158,8 @@ int object_get_model_num(const object *objp) weapon *wp = &Weapons[objp->instance]; return Weapon_info[wp->weapon_info_index].model_num; } + case OBJ_RAW_POF: + return Pof_objects[objp->instance].model_num; default: break; } @@ -2146,6 +2212,8 @@ int object_get_model_instance_num(const object *objp) Assertion(jnp != nullptr, "Could not find jump node!"); return jnp->GetPolymodelInstanceNum(); } + case OBJ_RAW_POF: + return Pof_objects[objp->instance].model_instance; default: break; } diff --git a/code/object/object.h b/code/object/object.h index e3114851c8b..2153a211d55 100644 --- a/code/object/object.h +++ b/code/object/object.h @@ -34,25 +34,26 @@ #endif //Object types -#define OBJ_NONE 0 //unused object -#define OBJ_SHIP 1 //a ship -#define OBJ_WEAPON 2 //a laser, missile, etc -#define OBJ_FIREBALL 3 //an explosion -#define OBJ_START 4 //a starting point marker (player start, etc) -#define OBJ_WAYPOINT 5 //a waypoint object, maybe only ever used by Fred -#define OBJ_DEBRIS 6 //a flying piece of ship debris -//#define OBJ_CMEASURE 7 //a countermeasure, such as chaff -#define OBJ_GHOST 8 //so far, just a placeholder for when a player dies. -#define OBJ_POINT 9 //generic object type to display a point in Fred. -#define OBJ_SHOCKWAVE 10 // a shockwave -#define OBJ_WING 11 // not really a type used anywhere, but I need it for Fred. -#define OBJ_OBSERVER 12 // used for multiplayer observers (possibly single player later) -#define OBJ_ASTEROID 13 // An asteroid, you know, a big rock, like debris, sort of. -#define OBJ_JUMP_NODE 14 // A jump node object, used only in Fred. -#define OBJ_BEAM 15 // beam weapons. we have to roll them into the object system to get the benefits of the collision pairs +#define OBJ_NONE 0 //unused object +#define OBJ_SHIP 1 //a ship +#define OBJ_WEAPON 2 //a laser, missile, etc +#define OBJ_FIREBALL 3 //an explosion +#define OBJ_START 4 //a starting point marker (player start, etc) +#define OBJ_WAYPOINT 5 //a waypoint object, maybe only ever used by Fred +#define OBJ_DEBRIS 6 //a flying piece of ship debris +//#define OBJ_CMEASURE 7 //a countermeasure, such as chaff +#define OBJ_GHOST 8 //so far, just a placeholder for when a player dies. +#define OBJ_POINT 9 //generic object type to display a point in Fred. +#define OBJ_SHOCKWAVE 10 // a shockwave +#define OBJ_WING 11 // not really a type used anywhere, but I need it for Fred. +#define OBJ_OBSERVER 12 // used for multiplayer observers (possibly single player later) +#define OBJ_ASTEROID 13 // An asteroid, you know, a big rock, like debris, sort of. +#define OBJ_JUMP_NODE 14 // A jump node object, used only in Fred. +#define OBJ_BEAM 15 // beam weapons. we have to roll them into the object system to get the benefits of the collision pairs +#define OBJ_RAW_POF 16 // A raw pof file. has no physics, ai or anything. Currently only used in the Lab to render tech models //Make sure to change Object_type_names in Object.c when adding another type! -#define MAX_OBJECT_TYPES 16 +#define MAX_OBJECT_TYPES 17 #define UNUSED_OBJNUM (-MAX_OBJECTS*2) // Newer systems use this instead of -1 for invalid object. @@ -124,6 +125,14 @@ class model_draw_list; class polymodel; struct polymodel_instance; +typedef struct raw_pof_obj +{ + int model_num; // The model number of the loaded POF + int model_instance; // The model instance +} raw_pof_obj; + +extern SCP_map Pof_objects; + class object { public: @@ -414,5 +423,12 @@ void physics_populate_snapshot(physics_snapshot& snapshot, const object* objp); */ void physics_apply_pstate_to_object(object* objp, const physics_snapshot& source); +/** + *@brief create a raw pof instance + * + * @author Mike Nelson + */ +int obj_raw_pof_create(const char* pof_filename, matrix* orient, vec3d* pos); + #endif diff --git a/code/object/objectsort.cpp b/code/object/objectsort.cpp index e9c43476373..723c93668a4 100644 --- a/code/object/objectsort.cpp +++ b/code/object/objectsort.cpp @@ -71,6 +71,8 @@ inline bool sorted_obj::operator < (const sorted_obj &other) const asp = &Asteroids[obj->instance]; model_num_a = Asteroid_info[asp->asteroid_type].subtypes[asp->asteroid_subtype].model_number; + } else if (obj->type == OBJ_RAW_POF) { + model_num_a = Pof_objects[obj->instance].model_num; } if ( other.obj->type == OBJ_SHIP ) { @@ -95,6 +97,8 @@ inline bool sorted_obj::operator < (const sorted_obj &other) const asp = &Asteroids[other.obj->instance]; model_num_b = Asteroid_info[asp->asteroid_type].subtypes[asp->asteroid_subtype].model_number; + } else if (other.obj->type == OBJ_RAW_POF) { + model_num_b = Pof_objects[other.obj->instance].model_num; } if ( model_num_a == model_num_b ) { @@ -164,7 +168,8 @@ inline bool obj_render_is_model(object *obj) && Weapon_info[Weapons[obj->instance].weapon_info_index].render_type == WRT_POF) || obj->type == OBJ_ASTEROID || obj->type == OBJ_DEBRIS - || obj->type == OBJ_JUMP_NODE; + || obj->type == OBJ_JUMP_NODE + || obj->type == OBJ_RAW_POF; } // Are there reasons to hide objects base on distance? From 5d057b58a184f24dea69332e9478c2e2d4045ce8 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Wed, 30 Apr 2025 12:06:50 -0500 Subject: [PATCH 3/7] check for a valid tech model when switching --- code/lab/manager/lab_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/lab/manager/lab_manager.cpp b/code/lab/manager/lab_manager.cpp index 8ea4699a6fa..f12e7d1fa60 100644 --- a/code/lab/manager/lab_manager.cpp +++ b/code/lab/manager/lab_manager.cpp @@ -397,7 +397,7 @@ void LabManager::changeDisplayedObject(LabMode mode, int info_index) { changeShipInternal(); break; case LabMode::Weapon: - if (ShowingTechModel) { + if (ShowingTechModel && VALID_FNAME(Weapon_info[CurrentClass].tech_model)) { ModelFilename = Weapon_info[CurrentClass].tech_model; CurrentObject = obj_raw_pof_create(ModelFilename.c_str(), &CurrentOrientation, &CurrentPosition); }else if (Weapon_info[CurrentClass].wi_flags[Weapon::Info_Flags::Beam]) { From 531f36a213dd39e72a613ba8bdb1a0160a7fe5a3 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Wed, 30 Apr 2025 15:47:16 -0500 Subject: [PATCH 4/7] enable render options for weapons and tech models --- code/lab/dialogs/lab_ui.cpp | 16 +++++--- code/lab/dialogs/lab_ui.h | 5 ++- code/lab/manager/lab_manager.cpp | 10 +++++ code/lab/renderer/lab_renderer.cpp | 41 +++++++++++++++++-- code/lab/renderer/lab_renderer.h | 1 + code/object/object.cpp | 64 +++++++++++++++++++++++++++--- code/object/object.h | 8 ++-- code/object/object_flags.h | 15 +++++++ code/weapon/weapon_flags.h | 13 ++++++ code/weapon/weapons.cpp | 48 ++++++++++++++++++++-- 10 files changed, 198 insertions(+), 23 deletions(-) diff --git a/code/lab/dialogs/lab_ui.cpp b/code/lab/dialogs/lab_ui.cpp index 3476fb4c72f..9d632afbbce 100644 --- a/code/lab/dialogs/lab_ui.cpp +++ b/code/lab/dialogs/lab_ui.cpp @@ -342,13 +342,17 @@ void LabUi::show_render_options() with_CollapsingHeader("Model features") { - Checkbox("Rotate/Translate Subsystems", &animate_subsystems); + if (getLabManager()->CurrentMode == LabMode::Ship) { + Checkbox("Rotate/Translate Subsystems", &animate_subsystems); + } Checkbox("Show full detail", &show_full_detail); Checkbox("Show thrusters", &show_thrusters); - Checkbox("Show afterburners", &show_afterburners); - Checkbox("Show weapons", &show_weapons); - Checkbox("Show Insignia", &show_insignia); - Checkbox("Show damage lightning", &show_damage_lightning); + if (getLabManager()->CurrentMode == LabMode::Ship) { + Checkbox("Show afterburners", &show_afterburners); + Checkbox("Show weapons", &show_weapons); + Checkbox("Show Insignia", &show_insignia); + Checkbox("Show damage lightning", &show_damage_lightning); + } Checkbox("No glowpoints", &no_glowpoints); } @@ -372,6 +376,7 @@ void LabUi::show_render_options() with_CollapsingHeader("Scene rendering options") { Checkbox("Hide Post Processing", &hide_post_processing); + Checkbox("Hide particles", &no_particles); Checkbox("Render as wireframe", &use_wireframe_rendering); Checkbox("Render without light", &no_lighting); Checkbox("Render with emissive lighting", &show_emissive_lighting); @@ -487,6 +492,7 @@ void LabUi::show_render_options() getLabManager()->Renderer->setRenderFlag(LabRenderFlag::ShowWeapons, show_weapons); getLabManager()->Renderer->setRenderFlag(LabRenderFlag::ShowEmissiveLighting, show_emissive_lighting); getLabManager()->Renderer->setRenderFlag(LabRenderFlag::MoveSubsystems, animate_subsystems); + getLabManager()->Renderer->setRenderFlag(LabRenderFlag::NoParticles, no_particles); getLabManager()->Renderer->setEmissiveFactor(emissive_factor); getLabManager()->Renderer->setAmbientFactor(ambient_factor); getLabManager()->Renderer->setLightFactor(light_factor); diff --git a/code/lab/dialogs/lab_ui.h b/code/lab/dialogs/lab_ui.h index 10554d11654..bb0ecabb969 100644 --- a/code/lab/dialogs/lab_ui.h +++ b/code/lab/dialogs/lab_ui.h @@ -12,6 +12,8 @@ enum class LabTurretAimType { class LabUi { public: + bool show_thrusters = false; // So that it can be toggled on/off based on the lab mode being changed + void create_ui(); void object_changed(); void closeUi(); @@ -86,6 +88,7 @@ class LabUi { bool show_damage_lightning = false; bool animate_subsystems = false; bool hide_post_processing = false; + bool no_particles = false; bool diffuse_map = true; bool glow_map = true; bool spec_map = true; @@ -99,10 +102,10 @@ class LabUi { bool use_wireframe_rendering = false; bool no_lighting = false; bool show_full_detail = false; - bool show_thrusters = false; bool show_afterburners = false; bool show_weapons = false; bool show_emissive_lighting = false; + bool show_particles = true; std::optional volumetrics_pos_backup = std::nullopt; }; \ No newline at end of file diff --git a/code/lab/manager/lab_manager.cpp b/code/lab/manager/lab_manager.cpp index f12e7d1fa60..c19b96718de 100644 --- a/code/lab/manager/lab_manager.cpp +++ b/code/lab/manager/lab_manager.cpp @@ -386,6 +386,16 @@ void LabManager::changeDisplayedObject(LabMode mode, int info_index) { //if (mode == CurrentMode && info_index == CurrentClass) //return; + // Toggle the show thrusters default when we change modes + if (mode != CurrentMode) { + if (mode == LabMode::Ship) { + labUi.show_thrusters = false; + } + if (mode == LabMode::Weapon) { + labUi.show_thrusters = true; + } + } + CurrentMode = mode; CurrentClass = info_index; diff --git a/code/lab/renderer/lab_renderer.cpp b/code/lab/renderer/lab_renderer.cpp index 7b7151f9f21..853a5e06013 100644 --- a/code/lab/renderer/lab_renderer.cpp +++ b/code/lab/renderer/lab_renderer.cpp @@ -111,6 +111,34 @@ void LabRenderer::renderModel(float frametime) { } } + if (obj->type == OBJ_WEAPON) { + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_light, + renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); + } + + if (obj->type == OBJ_RAW_POF) { + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_light, + renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]); + Pof_objects[obj->instance].flags.set(Object::Raw_Pof_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]); + } + if (renderFlags[LabRenderFlag::ShowWireframe]) model_render_set_wireframe_color(&Color_white); @@ -118,6 +146,8 @@ void LabRenderer::renderModel(float frametime) { obj->phys_info.linear_thrust.xyz.z = 1.0f; if (obj->type == OBJ_SHIP) { Ships[obj->instance].flags.remove(Ship::Ship_Flags::No_thrusters); + } else if (obj->type == OBJ_WEAPON) { + Weapons[obj->instance].weapon_flags.remove(Weapon::Weapon_Flags::No_thruster); } if (renderFlags[LabRenderFlag::ShowAfterburners]) { obj->phys_info.flags |= PF_AFTERBURNER_ON; @@ -130,8 +160,11 @@ void LabRenderer::renderModel(float frametime) { else { obj->phys_info.linear_thrust.xyz.z = 0.0f; - if (obj->type == OBJ_SHIP) + if (obj->type == OBJ_SHIP) { Ships[obj->instance].flags.set(Ship::Ship_Flags::No_thrusters); + } else if (obj->type == OBJ_WEAPON) { + Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::No_thruster); + } } // Prevent weapons from destroying themselves @@ -154,8 +187,10 @@ void LabRenderer::renderModel(float frametime) { // Force the camera to follow our current object labCamera->updateCamera(); - particle::move_all(frametime); - particle::ParticleManager::get()->doFrame(frametime); + if (!renderFlags[LabRenderFlag::NoParticles]) { + particle::move_all(frametime); + particle::ParticleManager::get()->doFrame(frametime); + } shockwave_move_all(frametime); Trail_render_override = true; diff --git a/code/lab/renderer/lab_renderer.h b/code/lab/renderer/lab_renderer.h index 032f73cbb7e..55ce25f960c 100644 --- a/code/lab/renderer/lab_renderer.h +++ b/code/lab/renderer/lab_renderer.h @@ -36,6 +36,7 @@ FLAG_LIST(LabRenderFlag) { ShowEmissiveLighting, ShowAfterburners, TimeStopped, + NoParticles, NUM_VALUES }; diff --git a/code/object/object.cpp b/code/object/object.cpp index 0d2e9a56e4a..c98abeda52b 100644 --- a/code/object/object.cpp +++ b/code/object/object.cpp @@ -1828,6 +1828,63 @@ void obj_render(object *obj) gr_reset_lighting(); } +void raw_pof_render(object* obj, model_draw_list* scene) { + model_render_params render_info; + + auto pof_obj = Pof_objects[obj->instance]; + + uint64_t render_flags = MR_NORMAL | MR_IS_MISSILE | MR_NO_BATCH; + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_light]) + render_flags |= MR_NO_LIGHTING; + + if (pof_obj.flags[Object::Raw_Pof_Flags::Glowmaps_disabled]) { + render_flags |= MR_NO_GLOWMAPS; + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Draw_as_wireframe]) { + render_flags |= MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_TEXTURING; + render_info.set_color(Wireframe_color); + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_full_detail]) { + render_flags |= MR_FULL_DETAIL; + } + + uint debug_flags = render_info.get_debug_flags(); + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_diffuse]) { + debug_flags |= MR_DEBUG_NO_DIFFUSE; + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_glowmap]) { + debug_flags |= MR_DEBUG_NO_GLOW; + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_normalmap]) { + debug_flags |= MR_DEBUG_NO_NORMAL; + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_ambientmap]) { + debug_flags |= MR_DEBUG_NO_AMBIENT; + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_specmap]) { + debug_flags |= MR_DEBUG_NO_SPEC; + } + + if (pof_obj.flags[Object::Raw_Pof_Flags::Render_without_reflectmap]) { + debug_flags |= MR_DEBUG_NO_REFLECT; + } + + render_info.set_object_number(OBJ_INDEX(obj)); + + render_info.set_flags(render_flags); + render_info.set_debug_flags(debug_flags); + + model_render_queue(&render_info, scene, Pof_objects[obj->instance].model_num, &obj->orient, &obj->pos); +} + void obj_queue_render(object* obj, model_draw_list* scene) { TRACE_SCOPE(tracing::QueueRender); @@ -1898,12 +1955,7 @@ void obj_queue_render(object* obj, model_draw_list* scene) case OBJ_BEAM: break; case OBJ_RAW_POF: - { - model_render_params render_info; - render_info.set_object_number(OBJ_INDEX(obj)); - - model_render_queue(&render_info, scene, Pof_objects[obj->instance].model_num, &obj->orient, &obj->pos); - } + raw_pof_render(obj, scene); break; default: Error( LOCATION, "Unhandled obj type %d in obj_render", obj->type ); diff --git a/code/object/object.h b/code/object/object.h index 2153a211d55..542b795944a 100644 --- a/code/object/object.h +++ b/code/object/object.h @@ -125,10 +125,10 @@ class model_draw_list; class polymodel; struct polymodel_instance; -typedef struct raw_pof_obj -{ - int model_num; // The model number of the loaded POF - int model_instance; // The model instance +typedef struct raw_pof_obj { + int model_num; // The model number of the loaded POF + int model_instance; // The model instance + flagset flags; // Render flags } raw_pof_obj; extern SCP_map Pof_objects; diff --git a/code/object/object_flags.h b/code/object/object_flags.h index 668c61a0553..6b2f3966d21 100644 --- a/code/object/object_flags.h +++ b/code/object/object_flags.h @@ -44,6 +44,21 @@ namespace Object { Autoaim_convergence, // has autoaim with convergence Convergence_offset, // marks that convergence has offset valuem, only used for ships not weapons + NUM_VALUES + }; + + FLAG_LIST(Raw_Pof_Flags){Glowmaps_disabled, // No glowmaps for this weapon instance + Draw_as_wireframe, // Render wireframe for this weapon instance + Render_full_detail, // Render full detail for this weapon instance + Render_without_light, // Render without light for this weapon instance + Render_without_diffuse, // Render without diffuse for this weapon instance + Render_without_glowmap, // Render without glowmap for this weapon instance + Render_without_normalmap, // Render without normal map for this weapon instance + Render_without_heightmap, // Render without height map for this weapon instance + Render_without_ambientmap, // Render without ambient for this weapon instance + Render_without_specmap, // Render without spec for this weapon instance + Render_without_reflectmap, // Render without reflect for this weapon instance + NUM_VALUES}; } diff --git a/code/weapon/weapon_flags.h b/code/weapon/weapon_flags.h index 5aa6aa5c644..5083ca4ab35 100644 --- a/code/weapon/weapon_flags.h +++ b/code/weapon/weapon_flags.h @@ -114,6 +114,19 @@ namespace Weapon { Multi_homing_update_needed, // this is a newly spawned homing weapon which needs to update client machines Multi_Update_Sent, // Marks this missile as already being updated once by the server Begun_detonation, // The engine has set this weapon to detonate momentarily + No_thruster, // Disable rendering thrusters for this weapon instance + Glowmaps_disabled, // No glowmaps for this weapon instance + Draw_as_wireframe, // Render wireframe for this weapon instance + Render_full_detail, // Render full detail for this weapon instance + Render_without_light, // Render without light for this weapon instance + Render_without_diffuse, // Render without diffuse for this weapon instance + Render_without_glowmap, // Render without glowmap for this weapon instance + Render_without_normalmap, // Render without normal map for this weapon instance + Render_without_heightmap, // Render without height map for this weapon instance + Render_without_ambientmap, // Render without ambient for this weapon instance + Render_without_specmap, // Render without spec for this weapon instance + Render_without_reflectmap, // Render without reflect for this weapon instance + NUM_VALUES }; diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index b59111aecfe..02be208299e 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -6208,7 +6208,7 @@ void weapon_process_post(object * obj, float frame_time) } } - if ( wip->wi_flags[Weapon::Info_Flags::Thruster] ) { + if (wip->wi_flags[Weapon::Info_Flags::Thruster] && !wp->weapon_flags[Weapon::Weapon_Flags::No_thruster]) { ship_do_weapon_thruster_frame( wp, obj, flFrametime ); } @@ -6221,7 +6221,7 @@ void weapon_process_post(object * obj, float frame_time) weapon_maybe_play_flyby_sound(obj, wp); #endif - if(wip->wi_flags[Weapon::Info_Flags::Particle_spew] && wp->lssm_stage != 3 ){ + if (wip->wi_flags[Weapon::Info_Flags::Particle_spew] && wp->lssm_stage != 3) { weapon_maybe_spew_particle(obj); } @@ -9432,7 +9432,7 @@ void weapon_render(object* obj, model_draw_list *scene) uint64_t render_flags = MR_NORMAL|MR_IS_MISSILE|MR_NO_BATCH; - if (wip->wi_flags[Weapon::Info_Flags::Mr_no_lighting]) + if (wip->wi_flags[Weapon::Info_Flags::Mr_no_lighting] || wp->weapon_flags[Weapon::Weapon_Flags::Render_without_light]) render_flags |= MR_NO_LIGHTING; if (wip->wi_flags[Weapon::Info_Flags::Transparent]) { @@ -9440,11 +9440,50 @@ void weapon_render(object* obj, model_draw_list *scene) render_flags |= MR_ALL_XPARENT; } + if (wp->weapon_flags[Weapon::Weapon_Flags::Glowmaps_disabled]) { + render_flags |= MR_NO_GLOWMAPS; + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Draw_as_wireframe]) { + render_flags |= MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_TEXTURING; + render_info.set_color(Wireframe_color); + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_full_detail]) { + render_flags |= MR_FULL_DETAIL; + } + + uint debug_flags = render_info.get_debug_flags(); + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_without_diffuse]) { + debug_flags |= MR_DEBUG_NO_DIFFUSE; + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_without_glowmap]) { + debug_flags |= MR_DEBUG_NO_GLOW; + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_without_normalmap]) { + debug_flags |= MR_DEBUG_NO_NORMAL; + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_without_ambientmap]) { + debug_flags |= MR_DEBUG_NO_AMBIENT; + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_without_specmap]) { + debug_flags |= MR_DEBUG_NO_SPEC; + } + + if (wp->weapon_flags[Weapon::Weapon_Flags::Render_without_reflectmap]) { + debug_flags |= MR_DEBUG_NO_REFLECT; + } + model_clear_instance(wip->model_num); render_info.set_object_number(wp->objnum); - if ( (wip->wi_flags[Weapon::Info_Flags::Thruster]) && ((wp->thruster_bitmap > -1) || (wp->thruster_glow_bitmap > -1)) ) { + if ( (wip->wi_flags[Weapon::Info_Flags::Thruster]) && !wp->weapon_flags[Weapon::Weapon_Flags::No_thruster] && ((wp->thruster_bitmap > -1) || (wp->thruster_glow_bitmap > -1)) ) { float ft; mst_info mst; @@ -9481,6 +9520,7 @@ void weapon_render(object* obj, model_draw_list *scene) } render_info.set_flags(render_flags); + render_info.set_debug_flags(debug_flags); if (wp->model_instance_num >= 0) render_info.set_replacement_textures(model_get_instance(wp->model_instance_num)->texture_replace); From 235221d78972bb6c0021aad8a348758a240dafba Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 1 May 2025 12:30:38 -0500 Subject: [PATCH 5/7] Some cleanup --- code/lab/dialogs/lab_ui.cpp | 16 +------- code/lab/manager/lab_manager.cpp | 33 ++++++++++----- code/lab/manager/lab_manager.h | 11 +++-- code/lab/renderer/lab_renderer.cpp | 7 +++- code/object/object.cpp | 9 +---- code/weapon/beam.cpp | 65 ++++++++++++++++++++++-------- code/weapon/beam.h | 3 ++ 7 files changed, 88 insertions(+), 56 deletions(-) diff --git a/code/lab/dialogs/lab_ui.cpp b/code/lab/dialogs/lab_ui.cpp index 9d632afbbce..4b11eeb823b 100644 --- a/code/lab/dialogs/lab_ui.cpp +++ b/code/lab/dialogs/lab_ui.cpp @@ -777,15 +777,9 @@ void LabUi::build_weapon_options(ship* shipp) const { build_primary_weapon_combobox(text, wip, primary_slot); SameLine(); - static bool should_fire[MAX_SHIP_PRIMARY_BANKS] = {false, false, false}; SCP_string cb_text; sprintf(cb_text, "Fire bank %i", bank); - Checkbox(cb_text.c_str(), &should_fire[bank]); - if (should_fire[bank]) { - getLabManager()->FirePrimaries |= 1 << bank; - } else { - getLabManager()->FirePrimaries &= ~(1 << bank); - } + Checkbox(cb_text.c_str(), &getLabManager()->FirePrimaries[bank]); bank++; } @@ -803,15 +797,9 @@ void LabUi::build_weapon_options(ship* shipp) const { sprintf(text, "##Secondary bank %i", bank); build_secondary_weapon_combobox(text, wip, secondary_slot); SameLine(); - static bool should_fire[MAX_SHIP_SECONDARY_BANKS] = {false, false, false, false}; SCP_string cb_text; sprintf(cb_text, "Fire bank %i##secondary", bank); - Checkbox(cb_text.c_str(), &should_fire[bank]); - if (should_fire[bank]) { - getLabManager()->FireSecondaries |= 1 << bank; - } else { - getLabManager()->FireSecondaries &= ~(1 << bank); - } + Checkbox(cb_text.c_str(), &getLabManager()->FireSecondaries[bank]); bank++; } diff --git a/code/lab/manager/lab_manager.cpp b/code/lab/manager/lab_manager.cpp index c19b96718de..cbbce94fa63 100644 --- a/code/lab/manager/lab_manager.cpp +++ b/code/lab/manager/lab_manager.cpp @@ -235,7 +235,7 @@ void LabManager::onFrame(float frametime) { auto obj = &Objects[CurrentObject]; bool weapons_firing = false; for (auto i = 0; i < Ships[obj->instance].weapons.num_primary_banks; ++i) { - if (FirePrimaries & (1 << i)) { + if (FirePrimaries[i]) { weapons_firing = true; Ships[obj->instance].weapons.current_primary_bank = i; @@ -248,7 +248,7 @@ void LabManager::onFrame(float frametime) { Ships[obj->instance].flags.set(Ship::Ship_Flags::Trigger_down, weapons_firing); for (auto i = 0; i < Ships[obj->instance].weapons.num_secondary_banks; ++i) { - if (FireSecondaries & (1 << i)) { + if (FireSecondaries[i]) { Ships[obj->instance].weapons.current_secondary_bank = i; ship_fire_secondary(obj); @@ -361,20 +361,31 @@ void LabManager::onFrame(float frametime) { gr_flip(); } +//Cleans the scene and resets object actions. Stops any firing weapons. void LabManager::cleanup() { if (CurrentObject != -1) { - // Stop any firing turrets + // Stop any firing weapons FireTurrets.clear(); + FirePrimaries.fill(false); + FireSecondaries.fill(false); - // Surprisingly obj_delete_all() does not delete beams so we have to vanish it manually - object* obj = &Objects[getLabManager()->CurrentObject]; - if (obj->type == OBJ_BEAM && Beam_count > 0) { - beam* b = &Beams[Objects[CurrentObject].instance]; - beam_delete(b); - } + // Remove all beams + beam_delete_all(); + + // Remove all objects obj_delete_all(); + + // Clean up the particles + particle::kill_all(); + + // Reset lab variables + CurrentMode = LabMode::None; CurrentObject = -1; + CurrentClass = -1; + CurrentPosition = vmd_zero_vector; + CurrentOrientation = vmd_identity_matrix; + ModelFilename = ""; } } @@ -396,11 +407,11 @@ void LabManager::changeDisplayedObject(LabMode mode, int info_index) { } } + cleanup(); + CurrentMode = mode; CurrentClass = info_index; - cleanup(); - switch (CurrentMode) { case LabMode::Ship: CurrentObject = ship_create(&CurrentOrientation, &CurrentPosition, CurrentClass); diff --git a/code/lab/manager/lab_manager.h b/code/lab/manager/lab_manager.h index 1790d2ff93d..23984da6d4e 100644 --- a/code/lab/manager/lab_manager.h +++ b/code/lab/manager/lab_manager.h @@ -27,7 +27,7 @@ class LabManager { // Do rendering and handle keyboard/mouse events void onFrame(float frametime); - // Cleanup all objects + // Cleanup all objects and stop firing any weapons void cleanup(); // Creates a new object of the passed type, using the respective class definition found at info_index and replaces the currently @@ -55,8 +55,7 @@ class LabManager { int CurrentClass = -1; vec3d CurrentPosition = vmd_zero_vector; matrix CurrentOrientation = vmd_identity_matrix; - SCP_string ModelFilename = ""; - bool ShowingTechModel = false; + SCP_string ModelFilename = ""; bool isSafeForShips() { return CurrentMode == LabMode::Ship && CurrentObject != -1; @@ -81,15 +80,15 @@ class LabManager { } void resetGraphicsSettings(); - - int FirePrimaries = 0; - int FireSecondaries = 0; + std::array FirePrimaries = {}; + std::array FireSecondaries = {}; SCP_vector> FireTurrets; LabRotationMode RotationMode = LabRotationMode::Both; float RotationSpeedDivisor = 100.0f; bool AllowWeaponDestruction = false; + bool ShowingTechModel = false; flagset Flags; diff --git a/code/lab/renderer/lab_renderer.cpp b/code/lab/renderer/lab_renderer.cpp index 853a5e06013..e6b355c35e4 100644 --- a/code/lab/renderer/lab_renderer.cpp +++ b/code/lab/renderer/lab_renderer.cpp @@ -178,7 +178,12 @@ void LabRenderer::renderModel(float frametime) { } } else if (obj->type == OBJ_BEAM) { beam* b = &Beams[obj->instance]; - b->life_left = b->life_total; + //Little hack to keep targeting beams going + if (b->type == BeamType::TARGETING) { + b->life_left = 0.1f; + } else { + b->life_left = b->life_total; + } } } diff --git a/code/object/object.cpp b/code/object/object.cpp index c98abeda52b..10e4eef2a30 100644 --- a/code/object/object.cpp +++ b/code/object/object.cpp @@ -592,18 +592,13 @@ int obj_raw_pof_create(const char* pof_filename, matrix* orient, vec3d* pos) return -1; } - float radius = model_get_radius(model_num); - - raw_pof_obj pof_obj = {-1, -1}; - pof_obj.model_num = model_num; - int id = next_raw_pof_id++; - Pof_objects[id] = pof_obj; + Pof_objects[id] = {model_num, -1, {}}; flagset flags; flags.set(Object::Object_Flags::Renders); - int objnum = obj_create(OBJ_RAW_POF, -1, id, orient, pos, radius, flags); + int objnum = obj_create(OBJ_RAW_POF, -1, id, orient, pos, model_get_radius(model_num), flags); Pof_objects[id].model_instance = model_create_instance(objnum, model_num); diff --git a/code/weapon/beam.cpp b/code/weapon/beam.cpp index 1d8d14a06ca..c613a712891 100644 --- a/code/weapon/beam.cpp +++ b/code/weapon/beam.cpp @@ -921,19 +921,31 @@ void beam_type_slashing_move(beam *b) // targeting type beams functions void beam_type_targeting_move(beam *b) { - vec3d temp; + // If floating and targeting, synthesize orientation from known points + // because the object could be validly nullptr as when fired in the lab + if ((b->flags & BF_FLOATING_BEAM) && (b->flags & BF_TARGETING_COORDS)) { + // Compute forward vector from start to target + vec3d fvec; + vm_vec_sub(&fvec, &b->target_pos1, &b->last_start); + vm_vec_normalize_safe(&fvec); + + // Create synthetic orientation matrix + matrix beam_orient; + vm_vector_2_matrix(&beam_orient, &fvec, nullptr, nullptr); + + // Final beam endpoint + vm_vec_scale_add(&b->last_shot, &b->last_start, &beam_orient.vec.fvec, b->range); + } else { + Assertion(b->objp != nullptr, "Targeting beam does not have a valid parent object!"); + Assertion(b->objp->instance >= 0, "Targeting beam parent object instance is invalid!"); - // ugh - if ( (b->objp == NULL) || (b->objp->instance < 0) ) { - Int3(); - return; + // Standard case: beam fired from a real object + // start point + vm_vec_unrotate(&b->last_start, &b->local_fire_postion, &b->objp->orient); + vm_vec_add2(&b->last_start, &b->objp->pos); + // end point + vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.vec.fvec, b->range); } - - // targeting type beams only last one frame so we never have to "move" them. - temp = b->local_fire_postion; - vm_vec_unrotate(&b->last_start, &temp, &b->objp->orient); - vm_vec_add2(&b->last_start, &b->objp->pos); - vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.vec.fvec, b->range); } // antifighter type beam functions @@ -1004,7 +1016,19 @@ void beam_type_normal_move(beam *b) { vec3d turret_norm; - if (b->subsys == NULL) { // If we're a free-floating beam, there's nothing to calculate here. + // If we're a floating beam and targeting coords then we don't have a subsystem to get a normal from + // So we'll calculate it. + if (b->subsys == nullptr && (b->flags & BF_FLOATING_BEAM) && (b->flags & BF_TARGETING_COORDS)) { + // Build fvec from target_pos1 and last_start + vec3d fvec; + vm_vec_sub(&fvec, &b->target_pos1, &b->last_start); + vm_vec_normalize_safe(&fvec); + + vm_vec_scale_add(&b->last_shot, &b->last_start, &fvec, b->range); + return; + } + + if (b->subsys == nullptr) { return; } @@ -1892,6 +1916,17 @@ void beam_render_all() } } +// Delete all active beams +void beam_delete_all() +{ + beam* b = GET_FIRST(&Beam_used_list); + while (b != END_OF_LIST(&Beam_used_list)) { + beam* next = GET_NEXT(b); + beam_delete(b); + b = next; + } +} + // output top and bottom vectors // fvec == forward vector (eye viewpoint basically. in world coords) // pos == world coordinate of the point we're calculating "around" @@ -2952,11 +2987,7 @@ void beam_aim(beam *b) break; case BeamType::TARGETING: - // start point - vm_vec_unrotate(&b->last_start, &b->local_fire_postion, &b->objp->orient); - vm_vec_add2(&b->last_start, &b->objp->pos); - // end point - vm_vec_scale_add(&b->last_shot, &b->last_start, &b->objp->orient.vec.fvec, b->range); + beam_type_targeting_move(b); break; case BeamType::NORMAL_FIRE: diff --git a/code/weapon/beam.h b/code/weapon/beam.h index 19d91330e29..1eeb9a509f7 100644 --- a/code/weapon/beam.h +++ b/code/weapon/beam.h @@ -289,6 +289,9 @@ void beam_move_all_post(); // render all beam weapons void beam_render_all(); +// delete all active beams +void beam_delete_all(); + // early-out function for when adding object collision pairs, return 1 if the pair should be ignored int beam_collide_early_out(object *a, object *b); From 483d8efc0ea75fd759308a68b4c6dab9b9915264 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Thu, 1 May 2025 13:21:04 -0500 Subject: [PATCH 6/7] Clang --- code/lab/dialogs/lab_ui.cpp | 4 ++-- code/lab/dialogs/lab_ui.h | 2 -- code/lab/manager/lab_manager.h | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/code/lab/dialogs/lab_ui.cpp b/code/lab/dialogs/lab_ui.cpp index 4b11eeb823b..6ce1d30d442 100644 --- a/code/lab/dialogs/lab_ui.cpp +++ b/code/lab/dialogs/lab_ui.cpp @@ -529,7 +529,7 @@ void LabUi::do_triggered_anim(animation::ModelAnimationTriggerType type, TextUnformatted(colB); \ -void LabUi::build_ship_table_info_txtbox(ship_info* sip) const +static void build_ship_table_info_txtbox(ship_info* sip) { with_TreeNode("Table information") { @@ -549,7 +549,7 @@ void LabUi::build_ship_table_info_txtbox(ship_info* sip) const } } -void LabUi::build_weapon_table_info_txtbox(weapon_info* wip) const +static void build_weapon_table_info_txtbox(weapon_info* wip) { with_TreeNode("Table information") { diff --git a/code/lab/dialogs/lab_ui.h b/code/lab/dialogs/lab_ui.h index bb0ecabb969..74d6b9b157e 100644 --- a/code/lab/dialogs/lab_ui.h +++ b/code/lab/dialogs/lab_ui.h @@ -33,8 +33,6 @@ class LabUi { void build_texture_quality_combobox(); void build_antialiasing_combobox(); void build_tone_mapper_combobox(); - void build_ship_table_info_txtbox(ship_info* sip) const; - void build_weapon_table_info_txtbox(weapon_info* wip) const; void build_model_info_box(ship_info* sip, polymodel* pm) const; void build_subsystem_list(object* objp, ship* shipp) const; void build_subsystem_list_entry(SCP_string& subsys_name, diff --git a/code/lab/manager/lab_manager.h b/code/lab/manager/lab_manager.h index 23984da6d4e..71af11e899a 100644 --- a/code/lab/manager/lab_manager.h +++ b/code/lab/manager/lab_manager.h @@ -55,7 +55,7 @@ class LabManager { int CurrentClass = -1; vec3d CurrentPosition = vmd_zero_vector; matrix CurrentOrientation = vmd_identity_matrix; - SCP_string ModelFilename = ""; + SCP_string ModelFilename; bool isSafeForShips() { return CurrentMode == LabMode::Ship && CurrentObject != -1; From e495c3ed92543ce357c02e3c8e349af2231e8939 Mon Sep 17 00:00:00 2001 From: Mike Nelson Date: Sun, 25 May 2025 11:55:53 -0500 Subject: [PATCH 7/7] address feedback --- code/lab/dialogs/lab_ui.cpp | 2 +- code/object/object.cpp | 4 ++-- code/object/object.h | 2 +- code/weapon/beam.cpp | 37 +++++++++++++++---------------------- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/code/lab/dialogs/lab_ui.cpp b/code/lab/dialogs/lab_ui.cpp index 6ce1d30d442..52e28109882 100644 --- a/code/lab/dialogs/lab_ui.cpp +++ b/code/lab/dialogs/lab_ui.cpp @@ -1178,7 +1178,7 @@ void LabUi::show_object_options() const with_CollapsingHeader("Object Actions") { - Checkbox("Allow weapon to reach end of life", &getLabManager()->AllowWeaponDestruction); + Checkbox("Progress weapon lifetime", &getLabManager()->AllowWeaponDestruction); if (VALID_FNAME(wip->tech_model)) { if (Checkbox("Show Tech Model", &getLabManager()->ShowingTechModel)) { diff --git a/code/object/object.cpp b/code/object/object.cpp index 10e4eef2a30..0fd43bf692b 100644 --- a/code/object/object.cpp +++ b/code/object/object.cpp @@ -571,12 +571,12 @@ void obj_free(int objnum) * Create a raw pof item * @return the objnum of this raw POF */ -int obj_raw_pof_create(const char* pof_filename, matrix* orient, vec3d* pos) +int obj_raw_pof_create(const char* pof_filename, const matrix* orient, const vec3d* pos) { static int next_raw_pof_id = 0; // Unlikely this would ever be hit.. but just in case - if (next_raw_pof_id >= INT_MAX) { + if (next_raw_pof_id >= INT_MAX || next_raw_pof_id < 0) { Error(LOCATION, "Too many RAW_POF objects created!"); return -1; } diff --git a/code/object/object.h b/code/object/object.h index 542b795944a..21fa9b27c98 100644 --- a/code/object/object.h +++ b/code/object/object.h @@ -428,7 +428,7 @@ void physics_apply_pstate_to_object(object* objp, const physics_snapshot& source * * @author Mike Nelson */ -int obj_raw_pof_create(const char* pof_filename, matrix* orient, vec3d* pos); +int obj_raw_pof_create(const char* pof_filename, const matrix* orient, const vec3d* pos); #endif diff --git a/code/weapon/beam.cpp b/code/weapon/beam.cpp index c613a712891..0acbd321cae 100644 --- a/code/weapon/beam.cpp +++ b/code/weapon/beam.cpp @@ -929,12 +929,8 @@ void beam_type_targeting_move(beam *b) vm_vec_sub(&fvec, &b->target_pos1, &b->last_start); vm_vec_normalize_safe(&fvec); - // Create synthetic orientation matrix - matrix beam_orient; - vm_vector_2_matrix(&beam_orient, &fvec, nullptr, nullptr); - // Final beam endpoint - vm_vec_scale_add(&b->last_shot, &b->last_start, &beam_orient.vec.fvec, b->range); + vm_vec_scale_add(&b->last_shot, &b->last_start, &fvec, b->range); } else { Assertion(b->objp != nullptr, "Targeting beam does not have a valid parent object!"); Assertion(b->objp->instance >= 0, "Targeting beam parent object instance is invalid!"); @@ -1016,26 +1012,23 @@ void beam_type_normal_move(beam *b) { vec3d turret_norm; - // If we're a floating beam and targeting coords then we don't have a subsystem to get a normal from - // So we'll calculate it. - if (b->subsys == nullptr && (b->flags & BF_FLOATING_BEAM) && (b->flags & BF_TARGETING_COORDS)) { - // Build fvec from target_pos1 and last_start - vec3d fvec; - vm_vec_sub(&fvec, &b->target_pos1, &b->last_start); - vm_vec_normalize_safe(&fvec); - - vm_vec_scale_add(&b->last_shot, &b->last_start, &fvec, b->range); - return; - } - if (b->subsys == nullptr) { - return; + // If we're a floating beam and targeting coords then we don't have a subsystem to get a normal from, so we'll calculate it. + if ((b->flags & BF_FLOATING_BEAM) && (b->flags & BF_TARGETING_COORDS)) { + // Build normal from target_pos1 and last_start + vm_vec_sub(&turret_norm, &b->target_pos1, &b->last_start); + vm_vec_normalize_safe(&turret_norm); + } + // Otherwise, there's nothing to calculate here. + else { + return; + } + } else { + // LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN + // get the "originating point" of the beam for this frame. essentially bashes last_start + beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, false, &turret_norm, true, nullptr, (b->flags & BF_IS_FIGHTER_BEAM) != 0); } - // LEAVE THIS HERE OTHERWISE MUZZLE GLOWS DRAW INCORRECTLY WHEN WARMING UP OR DOWN - // get the "originating point" of the beam for this frame. essentially bashes last_start - beam_get_global_turret_gun_info(b->objp, b->subsys, &b->last_start, false, &turret_norm, true, nullptr, (b->flags & BF_IS_FIGHTER_BEAM) != 0); - // if the "warming up" timestamp has not expired if((b->warmup_stamp != -1) || (b->warmdown_stamp != -1)){ return;