我正在使用OpenGL和SDL库编写3D应用程序。如何实现类似于AutoCAD,FreeCAD或OpenSCAD等CAD程序的相机控制?具体来说,我感兴趣的是通过在视口中单击并拖动鼠标来控制某些点周围的相机的无约束旋转,以及按预期行为的平移和缩放。
多年前,我遇到一篇关于这个主题的文章描述了一种优雅的方法。文章建议将鼠标位置投射到观察体积内的半球上,然后施加等于前一个投射位置,原点和当前投射位置形成的角度的旋转。
我不记得该文章的更多细节,也无法使用谷歌找到它。
此外,这不是关于OpenGL基础知识或键盘/鼠标输入的问题。我目前正在使用FPS或飞行SIM卡启发的相机控制器。
答案 0 :(得分:3)
文章建议将鼠标位置投射到观察体积内的半球上,然后应用等于前一个投射位置,原点和当前投射位置形成的角度的旋转。
通常称为arcball控制,来自Ken Shoemake的article in Graphics Gems IV。
答案 1 :(得分:0)
这是一篇旧帖子,但我的代码可以满足您的要求。 我花了一些时间来解决这个问题。我刚刚用C ++重新编写了一个新项目。它有助于在XZ平面中绘制网格以测试此代码。
基本上,你需要在keyDown和Keyup事件中添加一些代码,以便你可以从围绕中心的弧形切换(L鼠标按钮)和缩放(R鼠标按钮)和移动(Shift键),你可以改变它什么是你想要的关键代码。 您还需要为鼠标事件Down,Move和UP添加代码。在这些事件中,您可以设置例程使用的bool值。 我使用旧的gluLookAt来设置我的视图矩阵。如果你不想使用它,你需要写一个。 以下是设置视图矩阵的代码:
void set_Eyes(void)
{
float sin_x, sin_y, cos_x, cos_y;
sin_x = sin(z_rotation + angle_offset); // angle_offset is not needed...
cos_x = cos(z_rotation + angle_offset); // It's a special use variable.
cos_y = cos(x_rotation);
sin_y = sin(x_rotation);
cam_y = sin(x_rotation) * view_radius; // view_radius is always a negitive number.
cam_x = (sin_x - (1.0f - cos_y) * sin_x) * view_radius;
cam_z = (cos_x - (1.0f - cos_y) * cos_x) * view_radius;
gluLookAt(
cam_x + u_look_point_X, //eye positions
cam_y + u_look_point_Y,
cam_z + u_look_point_Z,
u_look_point_X, // where we are looking
u_look_point_Y,
u_look_point_Z,
0.0F, 1.0F, 0.0F); // up vector... Y is up
eyeX = cam_x + u_look_point_X; // where the eye is in 3D space...
eyeY = cam_y + u_look_point_Y; // needed for some shaders
eyeZ = cam_z + u_look_point_Z; // u_look_points is the point we are looking at.
}
这是进行数学运算的代码.. 为了能够在移动鼠标的方向上移动外观,无论您在Y中查看的是什么角度,都必须对移位值进行一些2D旋转数学运算。数学并不复杂,但理解我的代码可能是。
//=================================================
// Mouse Movement Control
// "ArcBall" Style of viewing and movement.
//
//
//=================================================
#include "stdafx.h"
#include "mouse_control.h"
using namespace std;
// external variables
extern float PI;// 3.141592654
extern bool Right_Mouse_Down;
extern bool Left_Mouse_Down;
//==================================================
float m_speed = 2.0; // control mouse movement speed
float m_speed_global = 2.0; // Move to external setting
//==================================================
// Keep z_rotaion in -PI*2 to PI*2 range
float check_overflow(float v)
{
if (v > 0.0f) if (v > (PI * 2)) v -= (PI * 2);
if (v < 0.0f) if (v < (-PI * 2)) v += (PI * 2);
return v;
}
// Keep x_rotaion in 0.0 to -PI/2.0 range
float check_overflow_x(float v)
{
const float Half_PI = PI / 2.0f;
if (v < 0.0f)
if (v < -Half_PI+0.001f) // need the +0.001f to avoide fail at set_eyes function
v = -Half_PI+0.001f;
if (v > 0.0f) v = 0.0f;
return v;
}
// handle mouse rotation
void handle_mouse_eye_rotaion(CPoint point)
{
CPoint delta = mouse_p - point;
int deadzone = 0;
m_speed = m_speed_global * -view_radius * 0.1f;
if (xz_translation_flag || Left_Mouse_Down)
{
// about z
if (delta.x < deadzone)
{
rotate_left(delta.x);
}
if (delta.x > deadzone)
{
rotate_right(delta.x);
}
// about x
if (delta.y < deadzone)
{
rotate_down(delta.y);
}
if (delta.y > deadzone)
{
rotate_up(delta.y);
}
mouse_p = point;
}
else
{
if (Right_Mouse_Down && !y_move_flag)
{
zoom_radius(delta); // change zoom
mouse_p = point;
}
}
}
// rotate view clockwise
void rotate_left(int x)
{
if (x > 100) x = 100;
if (x < -100) x = -100;
float t = (float(x) / (100.0f * PI));
if (!xz_translation_flag)
{
z_rotation += t * m_speed_global;
z_rotation = check_overflow(z_rotation);
return;
}
u_look_point_X -= (t * cosf(z_rotation)*2.0f * m_speed);
u_look_point_Z += (t * sinf(z_rotation)*2.0f * m_speed);
}
// rotate view counter clockwise
void rotate_right(int x)
{
if (x > 100) x = 100;
if (x < -100) x = -100;
float t = (float(x) / (100.0f * PI));
if (!xz_translation_flag)
{
z_rotation += t * m_speed_global;
z_rotation = check_overflow(z_rotation);
return;
}
u_look_point_X -= (t * cosf(z_rotation)*2.0f * m_speed);
u_look_point_Z += (t * sinf(z_rotation)*2.0f * m_speed);
}
//======================== Y
// rotate view up
void rotate_up(int x)
{
if (x > 100) x = 100;
if (x < -100) x = -100;
float t = (float(x) / (100.0f * PI));
if (!xz_translation_flag)
{
x_rotation += t * m_speed_global;
x_rotation = check_overflow_x(x_rotation);
return;
}
u_look_point_Z -= (t * cosf(z_rotation)*2.0f * m_speed);
u_look_point_X -= (t * sinf(z_rotation)*2.0f * m_speed);
}
// rotate view down
void rotate_down(int x)
{
if (x > 100) x = 100;
if (x < -100) x = -100;
float t = (float(x) / (100.0f * PI ));
if (!xz_translation_flag)
{
x_rotation += t * m_speed_global;
x_rotation = check_overflow_x(x_rotation);
return;
}
u_look_point_Z -= (t * cosf(z_rotation)*2.0f * m_speed);
u_look_point_X -= (t * sinf(z_rotation)*2.0f * m_speed);
}
// used to change zoom
void zoom_radius(CPoint delta)
{
if (delta.y > 0)
{
if (delta.y > 100) delta.y = 100;
}
if (delta.y < 0)
{
if (delta.x < -100) delta.x = -100;
}
float y = float(delta.y) / 100.0f;
view_radius += y* 2.0f * m_speed;
// Adjust these to change max zoom in and out values.
// view_radius MOST STAY A NEGATIVE NUMBER!
// OpenGL ALWAYS LOOKS IN THE NEGATIVE Z SCREEN DIRECTION!
if (view_radius > -0.5f) view_radius = -0.5f;
if (view_radius < -1000.0f) view_radius = -1000.0f;
}
// debug junk
//string s;
//s = "mouse_p";
//outStr1(s, mouse_p.x, mouse_p.y);
//s = "delta";
//outStr1(s, delta.x, delta.y);
// debug code
void outStr1(string s, int a, int b)
{
char buf[100];
sprintf_s(buf, "%s : A= %i : B= %i\n", s.c_str(), a, b);
OutputDebugStringA(buf);
return;
}
注意:按下鼠标左键或右键时需要设置“mouse_p”。 它用于获取鼠标距离以前的三角形距离。
“point”是鼠标移动事件的鼠标位置。 “在完成数学运算后,mouse_p被设置为匹配”point“。否则,运动开始缩小失控!。
On KEYUP ...将bools设置为false(xz_translation_flag和y_move_flag)。 ON KEYDOWN ...按下shift键时设置xz_translation_flag = TRUE。 y_move_flag还没有直接绑定它的函数。它将用于移动u_look_point_Y var以改变外观的高度。