Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions fred2/fredrender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ int Last_cursor_over = -1;
int last_x = 0;
int last_y = 0;
int Lookat_mode = 0;

// Orbit camera state
vec3d Orbit_pivot = ZERO_VECTOR;
float Orbit_distance = 200.0f;
float Orbit_phi = 1.24f;
float Orbit_theta = 2.25f;
bool Orbit_active = false;
int rendering_order[MAX_SHIPS];
int render_count = 0;
int Show_asteroid_field = 1;
Expand Down Expand Up @@ -1311,6 +1318,124 @@ void move_mouse(int btn, int mdx, int mdy) {
}
}

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

vec3d orbit_camera_get_pivot()
{
vec3d pivot;

if (query_valid_object()) {
pivot = Objects[cur_object_index].pos;
} else if (!The_grid) {
pivot = ZERO_VECTOR;
} else {
// Intersect camera forward ray with the grid plane
vec3d *grid_normal = &The_grid->gmatrix.vec.uvec;
float denom = vm_vec_dot(grid_normal, &view_orient.vec.fvec);

if (fl_abs(denom) > 0.0001f) {
// t = -(dot(normal, view_pos) + planeD) / dot(normal, fvec)
float t = -(vm_vec_dot(grid_normal, &view_pos) + The_grid->planeD) / denom;
if (t > 0.0f) {
vm_vec_scale_add(&pivot, &view_pos, &view_orient.vec.fvec, t);
} else {
pivot = The_grid->center;
}
} else {
// Camera is parallel to grid plane; fall back to grid center
pivot = The_grid->center;
}
}
return pivot;
}

void orbit_camera_init_from_current_view(const vec3d *pivot)
{
vec3d offset;
vm_vec_sub(&offset, &view_pos, pivot);

Orbit_distance = vm_vec_mag(&offset);
if (Orbit_distance < 1.0f)
Orbit_distance = 100.0f;

// Transform offset into grid-local frame for angle decomposition
// In local frame, Y is always "up" (the grid plane normal)
const matrix &grid_mat = The_grid ? The_grid->gmatrix : vmd_identity_matrix;
vec3d local_offset;
vm_vec_rotate(&local_offset, &offset, &grid_mat);

Orbit_phi = acosf(std::clamp(local_offset.xyz.y / Orbit_distance, -1.0f, 1.0f));
Orbit_theta = atan2f(local_offset.xyz.z, local_offset.xyz.x);
Orbit_pivot = *pivot;
Orbit_active = true;
}

void orbit_camera_apply()
{
float sp = sinf(Orbit_phi);

// Build offset in grid-local coordinates (Y = up/normal)
vec3d local_pos;
local_pos.xyz.x = sp * cosf(Orbit_theta);
local_pos.xyz.y = cosf(Orbit_phi);
local_pos.xyz.z = sp * sinf(Orbit_theta);

// Transform back to world space
const matrix &grid_mat = The_grid ? The_grid->gmatrix : vmd_identity_matrix;
vec3d world_offset;
vm_vec_unrotate(&world_offset, &local_pos, &grid_mat);

vm_vec_scale(&world_offset, Orbit_distance);
vm_vec_add(&view_pos, &Orbit_pivot, &world_offset);

// Point camera at pivot, using grid's up vector
vec3d look_dir;
vm_vec_sub(&look_dir, &Orbit_pivot, &view_pos);
if (vm_vec_mag(&look_dir) > 0.001f) {
vec3d grid_up = grid_mat.vec.uvec;
vm_vector_2_matrix(&view_orient, &look_dir, &grid_up, nullptr);
}

Update_window = 1;
}

void orbit_camera_rotate(int dx, int dy)
{
float rot_scale = physics_rot / 300.0f;
Orbit_theta += dx / 100.0f * rot_scale;
Orbit_phi += dy / 100.0f * rot_scale;

CLAMP(Orbit_phi, 0.01f, PI - 0.01f);

orbit_camera_apply();
}

void orbit_camera_pan(int dx, int dy)
{
float speed_factor = 1.0f + (physics_speed - 1) / 499.0f * 9.0f;
float pan_scale = Orbit_distance * 0.002f * speed_factor;

vec3d pan_delta;
vm_vec_copy_scale(&pan_delta, &view_orient.vec.rvec, -(float)dx * pan_scale);
vm_vec_scale_add2(&pan_delta, &view_orient.vec.uvec, (float)dy * pan_scale);

vm_vec_add2(&Orbit_pivot, &pan_delta);

orbit_camera_apply();
}

void orbit_camera_zoom(float delta)
{
float zoom_speed = 1.0f + (physics_speed - 1) / 499.0f * 4.0f;
Orbit_distance *= powf(2.0f, delta * zoom_speed);

CLAMP(Orbit_distance, 1.0f, 10000000.0f);

orbit_camera_apply();
}

// ---------- End orbit camera functions ----------

int object_check_collision(object *objp, vec3d *p0, vec3d *p1, vec3d *hitpos) {
mc_info mc;

Expand Down Expand Up @@ -1425,6 +1550,9 @@ void process_controls(vec3d *pos, matrix *orient, float frametime, int key, int
if (rotangs.h && Universal_heading)
vm_transpose(orient);
}

// Invalidate orbit camera state so it re-initializes on next mouse drag
Orbit_active = false;
}

void process_movement_keys(int key, vec3d *mvec, angles *angs) {
Expand Down
15 changes: 15 additions & 0 deletions fred2/fredrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ extern int Flying_controls_mode; //!< Bool. Unknown.
extern int Group_rotate; //!< Bool. If nonzero, each object rotates around the leader. If zero. rotate
extern int Show_horizon; //!< Bool. If nonzero, draw a line representing the horizon (XZ plane)
extern int Lookat_mode; //!< Bool. Unknown.

// Orbit camera state
extern vec3d Orbit_pivot;
extern float Orbit_distance;
extern float Orbit_phi;
extern float Orbit_theta;
extern bool Orbit_active;
extern int True_rw; //!< Unsigned. The width of the render area
extern int True_rh; //!< Unsigned. The height of the render area
extern int Fixed_briefing_size; //!< Bool. If nonzero then expand the briefing preview as much as we can, maintaining the aspect ratio.
Expand Down Expand Up @@ -93,3 +100,11 @@ void verticalize_controlled();
* @return -1 if no object found
*/
int select_object(int cx, int cy);

// Orbit camera functions
vec3d orbit_camera_get_pivot();
void orbit_camera_init_from_current_view(const vec3d *pivot);
void orbit_camera_apply();
void orbit_camera_rotate(int dx, int dy);
void orbit_camera_pan(int dx, int dy);
void orbit_camera_zoom(float delta);
114 changes: 111 additions & 3 deletions fred2/fredview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ BEGIN_MESSAGE_MAP(CFREDView, CView)
ON_WM_SIZE()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_MBUTTONDOWN()
ON_WM_MBUTTONUP()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_MOUSEWHEEL()
ON_COMMAND(ID_MISCSTUFF_SHOWSHIPSASICONS, OnMiscstuffShowshipsasicons)
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnEditPopupShowShipIcons)
Expand Down Expand Up @@ -1038,7 +1043,7 @@ void CFREDView::OnLButtonDown(UINT nFlags, CPoint point)
CView::OnLButtonDown(nFlags, point);
}

void CFREDView::OnMouseMove(UINT nFlags, CPoint point)
void CFREDView::OnMouseMove(UINT nFlags, CPoint point)
{
// RT point

Expand All @@ -1048,6 +1053,26 @@ void CFREDView::OnMouseMove(UINT nFlags, CPoint point)
last_mouse_y = marking_box.y2 = point.y;
Cursor_over = select_object(point.x, point.y);

// Orbit camera: middle button drag
if (m_orbit_dragging && (nFlags & MK_MBUTTON)) {
handle_orbit_drag(point, nFlags);
CView::OnMouseMove(nFlags, point);
return;
}

// Orbit camera: right button drag
if (m_rbutton_down && (nFlags & MK_RBUTTON) && viewpoint == 0 && Control_mode == 0) {
if (!m_rbutton_moved) {
if (abs(point.x - m_rbutton_down_point.x) > 2 || abs(point.y - m_rbutton_down_point.y) > 2)
m_rbutton_moved = true;
}
if (m_rbutton_moved) {
handle_orbit_drag(point, nFlags);
CView::OnMouseMove(nFlags, point);
return;
}
}

if (!(nFlags & MK_LBUTTON))
button_down = 0;

Expand All @@ -1056,7 +1081,7 @@ void CFREDView::OnMouseMove(UINT nFlags, CPoint point)
if (button_down && GetCapture() != this)
cancel_drag();

if (!button_down && GetCapture() == this)
if (!button_down && !m_orbit_dragging && !m_rbutton_down && GetCapture() == this)
ReleaseCapture();

if (button_down) {
Expand Down Expand Up @@ -1089,7 +1114,7 @@ void CFREDView::OnLButtonUp(UINT nFlags, CPoint point)
if (button_down && GetCapture() != this)
cancel_drag();

if (GetCapture() == this)
if (!m_orbit_dragging && !m_rbutton_down && GetCapture() == this)
ReleaseCapture();

if (button_down) {
Expand Down Expand Up @@ -1174,6 +1199,89 @@ void CFREDView::OnLButtonUp(UINT nFlags, CPoint point)
CView::OnLButtonUp(nFlags, point);
}

// ---------- Orbit camera mouse handlers ----------

void CFREDView::handle_orbit_drag(CPoint point, UINT nFlags)
{
int dx = point.x - m_orbit_last_mouse.x;
int dy = point.y - m_orbit_last_mouse.y;
m_orbit_last_mouse = point;

if (nFlags & MK_SHIFT)
orbit_camera_pan(dx, dy);
else
orbit_camera_rotate(dx, dy);
}

void CFREDView::OnMButtonDown(UINT nFlags, CPoint point)
{
if (viewpoint != 0 || Control_mode != 0)
return;

vec3d pivot = orbit_camera_get_pivot();
orbit_camera_init_from_current_view(&pivot);

m_orbit_dragging = true;
m_orbit_last_mouse = point;
SetCapture();
}

void CFREDView::OnMButtonUp(UINT nFlags, CPoint point)
{
if (m_orbit_dragging) {
m_orbit_dragging = false;
if (GetCapture() == this && !m_rbutton_down)
ReleaseCapture();
}
}

void CFREDView::OnRButtonDown(UINT nFlags, CPoint point)
{
m_rbutton_down = true;
m_rbutton_moved = false;
m_rbutton_down_point = point;
m_orbit_last_mouse = point;

if (viewpoint == 0 && Control_mode == 0) {
vec3d pivot = orbit_camera_get_pivot();
orbit_camera_init_from_current_view(&pivot);
SetCapture();
}
}

void CFREDView::OnRButtonUp(UINT nFlags, CPoint point)
{
bool was_dragging = m_rbutton_moved;
m_rbutton_down = false;
m_rbutton_moved = false;

if (GetCapture() == this && !m_orbit_dragging)
ReleaseCapture();

if (!was_dragging) {
// No drag occurred — show context menu as normal
CPoint screen_point = point;
ClientToScreen(&screen_point);
OnContextMenu(this, screen_point);
}
}

BOOL CFREDView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
if (viewpoint != 0 || Control_mode != 0)
return CView::OnMouseWheel(nFlags, zDelta, pt);

if (!Orbit_active) {
vec3d pivot = orbit_camera_get_pivot();
orbit_camera_init_from_current_view(&pivot);
}

orbit_camera_zoom(zDelta / -200.0f);
return TRUE;
}

// ---------- End orbit camera mouse handlers ----------

// This function never gets called because nothing causes
// the WM_GOODBYE event to occur.
// False! When you close the Ship Dialog, this function is called! --MK, 8/30/96
Expand Down
13 changes: 13 additions & 0 deletions fred2/fredview.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ class CFREDView : public CView
CGrid* m_pGDlg;
int global_error_check_player_wings(int multi);

// Orbit camera drag state
bool m_orbit_dragging = false;
bool m_rbutton_down = false;
bool m_rbutton_moved = false;
CPoint m_orbit_last_mouse;
CPoint m_rbutton_down_point;
void handle_orbit_drag(CPoint point, UINT nFlags);

protected: // create from serialization only
CFREDView();
DECLARE_DYNCREATE(CFREDView)
Expand Down Expand Up @@ -331,6 +339,11 @@ class CFREDView : public CView
afx_msg void OnUpdateViewLighting(CCmdUI* pCmdUI);
afx_msg void OnViewFullDetail();
afx_msg void OnUpdateViewFullDetail(CCmdUI *pCmdUI);
afx_msg void OnMButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMButtonUp(UINT nFlags, CPoint point);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
//}}AFX_MSG
afx_msg void OnGroup(UINT nID);
afx_msg void OnSetGroup(UINT nID);
Expand Down
Loading
Loading