Skip to content
Open
38 changes: 38 additions & 0 deletions code/missioneditor/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,41 @@ void rotate_jump_nodes(const SCP_vector<int>& slots, int from_pos, int to_pos)
Jump_nodes[slots[j]] = std::move(Jump_nodes[slots[j + step]]);
Jump_nodes[slots[to_pos]] = std::move(moving);
}

bool get_object_bounds(bool marked_only, vec3d* center, float* radius)
{
vec3d mins = ZERO_VECTOR, maxs = ZERO_VECTOR;
bool found = false;

for (auto* objp : list_range(&obj_used_list)) {
if (marked_only && !objp->flags[Object::Object_Flags::Marked]) {
continue;
}
if (!found) {
mins = maxs = objp->pos;
found = true;
continue;
}
mins.xyz.x = std::min(mins.xyz.x, objp->pos.xyz.x);
mins.xyz.y = std::min(mins.xyz.y, objp->pos.xyz.y);
mins.xyz.z = std::min(mins.xyz.z, objp->pos.xyz.z);
maxs.xyz.x = std::max(maxs.xyz.x, objp->pos.xyz.x);
maxs.xyz.y = std::max(maxs.xyz.y, objp->pos.xyz.y);
maxs.xyz.z = std::max(maxs.xyz.z, objp->pos.xyz.z);
}

if (!found) {
return false;
}

vm_vec_avg(center, &mins, &maxs);
*radius = 0.0f;
for (auto* objp : list_range(&obj_used_list)) {
if (marked_only && !objp->flags[Object::Object_Flags::Marked]) {
continue;
}
*radius = std::max(*radius, vm_vec_dist(center, &objp->pos));
}

return true;
}
3 changes: 3 additions & 0 deletions code/missioneditor/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ SCP_string check_name_conflict(const char *entity_type, const char *name, int ex
// system is still up, since models own GPU buffers and cursors own bitmaps.
void editor_free_engine_data();

// Bounding-box midpoint of all (or only marked) objects and the farthest distance from it; false if there are none.
bool get_object_bounds(bool marked_only, vec3d* center, float* radius);

// Resolve a mission filename to its index in Campaign.missions[], lazy-loading the mission's goal/event name lists from disk if the
// FRED_LOAD_PENDING flag is still set. Returns -1 if the mission isn't in the campaign.
int load_and_find_campaign_mission(const char *mission_filename);
Expand Down
2 changes: 2 additions & 0 deletions fred2/fred.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ BOOL CFREDApp::InitInstance() {
Draw_outlines_on_selected_ships = GetProfileInt("Preferences", "Draw outlines on selected ships", 1) != 0;
Point_using_uvec = GetProfileInt("Preferences", "Point using uvec", Point_using_uvec);
Draw_outline_at_warpin_position = GetProfileInt("Preferences", "Draw outline at warpin position", 0) != 0;
Orbit_selection_midpoint = GetProfileInt("Preferences", "Orbit selection midpoint", 0) != 0;
Outline_lod = GetProfileInt("Preferences", "Outline LOD", 1);
Fred_label_font_scale = GetProfileInt("Preferences", "Label font scale percent", 100) / 100.0f;
Always_save_display_names = GetProfileInt("Preferences", "Always save display names", 0) != 0;
Expand Down Expand Up @@ -539,6 +540,7 @@ void CFREDApp::write_ini_file(int degree) {
WriteProfileInt("Preferences", "Draw outlines on selected ships", Draw_outlines_on_selected_ships ? 1 : 0);
WriteProfileInt("Preferences", "Point using uvec", Point_using_uvec);
WriteProfileInt("Preferences", "Draw outline at warpin position", Draw_outline_at_warpin_position ? 1 : 0);
WriteProfileInt("Preferences", "Orbit selection midpoint", Orbit_selection_midpoint ? 1 : 0);
WriteProfileInt("Preferences", "Outline LOD", Outline_lod);
WriteProfileInt("Preferences", "Label font scale percent", fl2ir(Fred_label_font_scale * 100.0f));
WriteProfileInt("Preferences", "Always save display names", Always_save_display_names ? 1 : 0);
Expand Down
4 changes: 4 additions & 0 deletions fred2/fred.rc
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ BEGIN
MENUITEM "Adjust Grid", 33032
MENUITEM "Point Ships Using UVec", 33101
MENUITEM "Move Ships When Undocking", 33098, CHECKED
MENUITEM "Rotate Around Selection Midpoint", ID_MISC_ROTATE_SELECTION_MIDPOINT
MENUITEM SEPARATOR
MENUITEM "Always save Display Names", ID_ALWAYS_SAVE_DISPLAY_NAMES
MENUITEM "Error checker checks for potential issues", ID_ERROR_CHECKER_CHECKS_POTENTIAL_ISSUES, CHECKED
Expand Down Expand Up @@ -664,6 +665,7 @@ BEGIN
MENUITEM "Calculate Relative Coordinates", 33030
MENUITEM "Point Ships Using UVec", 33101
MENUITEM "Move Ships When Undocking", 33098, CHECKED
MENUITEM "Rotate Around Selection Midpoint", ID_MISC_ROTATE_SELECTION_MIDPOINT
MENUITEM "Mission Statistics\tCtrl+Shift+D", 33067
MENUITEM "Music Player", ID_MUSIC_PLAYER
MENUITEM SEPARATOR
Expand Down Expand Up @@ -3891,6 +3893,8 @@ STRINGTABLE
BEGIN
ID_MISC_MOVESHIPSWHENUNDOCKING
"When setting initially docked ships to initially undocked, move the smaller ship away from the dockpoint by one ship length"
ID_MISC_ROTATE_SELECTION_MIDPOINT
"When several objects are marked, rotate the view around the midpoint of the selection instead of the current object"
ID_EDIT_CLONEMARKEDOBJECTS
"Clone every marked object in-place, like Ctrl-drag without the dragging"
ID_EDIT_LOCK_MARKED_OBJECTS
Expand Down
25 changes: 19 additions & 6 deletions fred2/fredrender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "model/model.h"
#include "model/model.h"
#include "mod_table/mod_table.h"
#include "missioneditor/common.h"
#include "object/object.h"
#include "physics/physics.h"
#include "prop/prop.h"
Expand Down Expand Up @@ -115,6 +116,7 @@ int Show_horizon = 0;
int Show_outlines = 0;
bool Draw_outlines_on_selected_ships = true;
bool Draw_outline_at_warpin_position = false;
bool Orbit_selection_midpoint = false;
int Outline_lod = 1;
bool Always_save_display_names = false;
bool Error_checker_checks_potential_issues = true;
Expand Down Expand Up @@ -1055,10 +1057,9 @@ void game_do_frame() {
}

if (Lookat_mode && query_valid_object()) {
float dist;

dist = vm_vec_dist(&view_pos, &Objects[cur_object_index].pos);
vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.vec.fvec, -dist);
vec3d center = selection_midpoint();
float dist = vm_vec_dist(&view_pos, &center);
vm_vec_scale_add(&view_pos, &center, &view_orient.vec.fvec, -dist);
}

switch (viewpoint) {
Expand Down Expand Up @@ -1288,15 +1289,27 @@ void move_mouse(int btn, int mdx, int mdy) {
}
}

vec3d selection_midpoint()
{
vec3d center;
float radius;

if (Orbit_selection_midpoint && Marked > 1 && get_object_bounds(true, &center, &radius)) {
return center;
}

return Objects[cur_object_index].pos;
}

// ---------- Orbit camera functions ----------

vec3d orbit_camera_get_pivot()
{
vec3d pivot;

if (query_valid_object()) {
// Pivot on current object
pivot = Objects[cur_object_index].pos;
// Pivot on the selection
pivot = selection_midpoint();
} else if (!The_grid) {
// Pivot on the origin, if no grid
pivot = ZERO_VECTOR;
Expand Down
3 changes: 3 additions & 0 deletions fred2/fredrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern int Show_coordinates; //!< Bool. If nonzero, draw the coordinates
extern int Show_outlines; //!< Bool. If nonzero, draw each object's mesh. If models are shown, highlight them in white.
extern bool Draw_outlines_on_selected_ships; // If a ship is selected, draw mesh lines
extern bool Draw_outline_at_warpin_position; // Project an outline at the place where the ship will arrive after warping in
extern bool Orbit_selection_midpoint;
extern int Outline_lod; // The LOD to use for wireframe outlines (0 = highest detail, default 1)
extern float Fred_label_font_scale; // Multiplier applied to viewport text labels
extern bool Always_save_display_names; // When saving a mission, always write display names to the mission file even if the display name is not set.
Expand Down Expand Up @@ -105,6 +106,8 @@ void verticalize_controlled();
*/
int select_object(int cx, int cy);

vec3d selection_midpoint();

// Orbit camera functions
vec3d orbit_camera_get_pivot();
void orbit_camera_init_from_current_view(const vec3d *pivot, const matrix *grid_orient);
Expand Down
74 changes: 21 additions & 53 deletions fred2/fredview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ BEGIN_MESSAGE_MAP(CFREDView, CView)
ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINES_ON_SELECTED, OnUpdateViewOutlinesOnSelected)
ON_COMMAND(ID_VIEW_OUTLINE_AT_WARPIN, OnViewOutlineAtWarpin)
ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINE_AT_WARPIN, OnUpdateViewOutlineAtWarpin)
ON_COMMAND(ID_MISC_ROTATE_SELECTION_MIDPOINT, OnMiscRotateSelectionMidpoint)
ON_UPDATE_COMMAND_UI(ID_MISC_ROTATE_SELECTION_MIDPOINT, OnUpdateMiscRotateSelectionMidpoint)
ON_COMMAND(ID_ALWAYS_SAVE_DISPLAY_NAMES, OnAlwaysSaveDisplayNames)
ON_UPDATE_COMMAND_UI(ID_ALWAYS_SAVE_DISPLAY_NAMES, OnUpdateAlwaysSaveDisplayNames)
ON_COMMAND(ID_ERROR_CHECKER_CHECKS_POTENTIAL_ISSUES, OnErrorCheckerChecksPotentialIssues)
Expand Down Expand Up @@ -2305,62 +2307,15 @@ void CFREDView::OnSelectList()
// position camera to view all objects on the screen at once. Doesn't change orientation.
void view_universe(int just_marked)
{
int i, max = 0, obj_flags[MAX_OBJECTS];
float dist, largest = 20.0f;
vec3d center, p1, p2; // center of all the objects collectively
float dist, radius;
vec3d center;
vertex v;
object *ptr;

for (i=0; i<MAX_OBJECTS; i++)
obj_flags[i] = 0;

if (just_marked)
ptr = &Objects[cur_object_index];
else
ptr = GET_FIRST(&obj_used_list);

p1.xyz.x = p2.xyz.x = ptr->pos.xyz.x;
p1.xyz.y = p2.xyz.y = ptr->pos.xyz.y;
p1.xyz.z = p2.xyz.z = ptr->pos.xyz.z;

ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (!just_marked || (ptr->flags[Object::Object_Flags::Marked])) {
center = ptr->pos;
if (center.xyz.x < p1.xyz.x)
p1.xyz.x = center.xyz.x;
if (center.xyz.x > p2.xyz.x)
p2.xyz.x = center.xyz.x;
if (center.xyz.y < p1.xyz.y)
p1.xyz.y = center.xyz.y;
if (center.xyz.y > p2.xyz.y)
p2.xyz.y = center.xyz.y;
if (center.xyz.z < p1.xyz.z)
p1.xyz.z = center.xyz.z;
if (center.xyz.z > p2.xyz.z)
p2.xyz.z = center.xyz.z;
}

ptr = GET_NEXT(ptr);
}

vm_vec_avg(&center, &p1, &p2);
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (!just_marked || (ptr->flags[Object::Object_Flags::Marked])) {
dist = vm_vec_dist_squared(&center, &ptr->pos);
if (dist > largest)
largest = dist;

obj_flags[OBJ_INDEX(ptr)] = 1; // flag object as needing on-screen check
if (OBJ_INDEX(ptr) > max)
max = OBJ_INDEX(ptr);
}

ptr = GET_NEXT(ptr);
}
if (!get_object_bounds(just_marked != 0, &center, &radius))
return;

dist = fl_sqrt(largest) + 1.0f;
dist = std::max(radius, fl_sqrt(20.0f)) + 1.0f;
vm_vec_scale_add(&view_pos, &center, &view_orient.vec.fvec, -dist);
g3_set_view_matrix(&view_pos, &view_orient, 0.5f);

Expand Down Expand Up @@ -4109,6 +4064,19 @@ void CFREDView::OnUpdateViewOutlineAtWarpin(CCmdUI* pCmdUI)
pCmdUI->SetCheck(Draw_outline_at_warpin_position);
}

void CFREDView::OnMiscRotateSelectionMidpoint()
{
Orbit_selection_midpoint = !Orbit_selection_midpoint;
Orbit_active = false;
theApp.write_ini_file();
Update_window = 1;
}

void CFREDView::OnUpdateMiscRotateSelectionMidpoint(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Orbit_selection_midpoint);
}

void CFREDView::OnAlwaysSaveDisplayNames()
{
Always_save_display_names = !Always_save_display_names;
Expand Down Expand Up @@ -4502,7 +4470,7 @@ void CFREDView::OnLookatObj()
vec3d v, loc;
matrix m;

loc = Objects[cur_object_index].pos;
loc = selection_midpoint();
vm_vec_sub(&v, &loc, &view_pos);

if (v.xyz.x || v.xyz.y || v.xyz.z) {
Expand Down
2 changes: 2 additions & 0 deletions fred2/fredview.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ class CFREDView : public CView
afx_msg void OnUpdateViewOutlinesOnSelected(CCmdUI* pCmdUI);
afx_msg void OnViewOutlineAtWarpin();
afx_msg void OnUpdateViewOutlineAtWarpin(CCmdUI* pCmdUI);
afx_msg void OnMiscRotateSelectionMidpoint();
afx_msg void OnUpdateMiscRotateSelectionMidpoint(CCmdUI* pCmdUI);
afx_msg void OnAlwaysSaveDisplayNames();
afx_msg void OnUpdateAlwaysSaveDisplayNames(CCmdUI* pCmdUI);
afx_msg void OnErrorCheckerChecksPotentialIssues();
Expand Down
3 changes: 2 additions & 1 deletion fred2/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -1626,6 +1626,7 @@
#define ID_LABEL_FONT_SCALE_300 33121
#define ID_LABEL_FONT_SCALE_350 33122
#define ID_LABEL_FONT_SCALE_400 33123
#define ID_MISC_ROTATE_SELECTION_MIDPOINT 33124
#define ID_INDICATOR_MODE 59142
#define ID_INDICATOR_LEFT 59143
#define ID_INDICATOR_RIGHT 59144
Expand All @@ -1639,7 +1640,7 @@
#define _APS_3D_CONTROLS 1
#define _APS_NEXT_RESOURCE_VALUE 340
#define _APS_NEXT_CONTROL_VALUE 1747
#define _APS_NEXT_COMMAND_VALUE 33124
#define _APS_NEXT_COMMAND_VALUE 33125
#define _APS_NEXT_SYMED_VALUE 105
#endif
#endif
8 changes: 8 additions & 0 deletions qtfred/help-src/doc/general/PreferencesDialog.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,13 @@ <h2>Controls</h2>
viewport navigation. Hover over each entry for a description of what it does. The
default keyboard bindings are listed on the
<a href="Viewport.html">Viewport</a> page.</p>
<ul>
<li><strong>Mouse Orbit Camera</strong> - invert the horizontal or vertical
direction of mouse orbit rotation.</li>
<li><strong>Rotate Around Selection Midpoint</strong> - when several objects are
marked, look at and orbit around the midpoint of the selection's bounding
box instead of the current object. Off by default, which keeps the camera
on the current object as before.</li>
</ul>
</body>
</html>
7 changes: 5 additions & 2 deletions qtfred/help-src/doc/general/Viewport.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ <h2>Context menu</h2>
empty space.</p>

<h2>Camera controls</h2>
<p>The camera orbits around a pivot point in the scene. Mouse controls move the
camera without changing any mission data:</p>
<p>The camera orbits around a pivot point in the scene. With several objects
marked, the pivot is the current object unless <strong>Rotate Around Selection
Midpoint</strong> is enabled in Preferences &rsaquo; Controls, which uses the
midpoint of the selection's bounding box instead. Mouse controls move the camera
without changing any mission data:</p>
<ul>
<li><strong>Middle-button drag</strong> - orbit (rotate) the camera around the
pivot.</li>
Expand Down
10 changes: 10 additions & 0 deletions qtfred/src/mission/CameraController.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class CameraController {
// Default to inverted, matching the engine's F3 lab and POF Tools.
bool _invertOrbitX = true;
bool _invertOrbitY = true;
bool _orbitSelectionMidpoint = false;

void orbitCameraApply();

Expand Down Expand Up @@ -81,6 +82,15 @@ class CameraController {
bool getInvertOrbitY() const { return _invertOrbitY; }
void setInvertOrbitY(bool invert) { _invertOrbitY = invert; }

bool getOrbitSelectionMidpoint() const { return _orbitSelectionMidpoint; }
void setOrbitSelectionMidpoint(bool enabled)
{
if (_orbitSelectionMidpoint != enabled) {
_orbitSelectionMidpoint = enabled;
_orbitActive = false;
}
}

void orbitCameraInitFromCurrentView(const vec3d* pivot, const matrix* grid_orient);
void orbitCameraRotate(int dx, int dy);
void orbitCameraPan(int dx, int dy);
Expand Down
Loading
Loading