简体   繁体   English

如何控制CAD程序等3D应用程序视点?

[英]How to control 3D application viewpoint like CAD programs?

I'm writing a 3D application using OpenGL and the SDL library. 我正在使用OpenGL和SDL库编写3D应用程序。 How can I implement camera controls similar to CAD programs like AutoCAD, FreeCAD, or OpenSCAD? 如何实现类似于AutoCAD,FreeCAD或OpenSCAD等CAD程序的相机控制? Specifically, I am interested in unconstrained rotation of the camera around some point controlled by clicking and dragging the mouse in the viewport, as well as panning and zooming that behaves as expected. 具体来说,我感兴趣的是通过在视口中单击并拖动鼠标来控制某个点周围的相机的无约束旋转,以及按预期行为的平移和缩放。

Years ago I encountered an article on this topic that described an elegant approach. 几年前,我遇到一篇关于这个主题的文章描述了一种优雅的方法。 The article suggested projecting the mouse position onto a hemisphere inscribed in the viewing volume, then applying a rotation equal to the angle formed by the previous projected location, the origin, and the current projected location. 该文章建议将鼠标位置投影到内接在观察体积中的半球上,然后施加等于由先前投射位置,原点和当前投射位置形成的角度的旋转。

I cannot remember more details of the article nor can I find it using google. 我不记得有关该文章的更多细节,也无法使用谷歌找到它。

Also, this is not a question about OpenGL basics or keyboard/mouse input. 此外,这不是关于OpenGL基础知识或键盘/鼠标输入的问题。 I am currently using an FPS or flight sim inspired camera controller. 我目前正在使用FPS或飞行SIM卡启发的相机控制器。

The article suggested projecting the mouse position onto a hemisphere inscribed in the viewing volume, then applying a rotation equal to the angle formed by the previous projected location, the origin, and the current projected location. 该文章建议将鼠标位置投影到内接在观察体积中的半球上,然后施加等于由先前投射位置,原点和当前投射位置形成的角度的旋转。

Commonly known as an arcball control, from Ken Shoemake's article in Graphics Gems IV . 通常被称为弧球控制,来自Ken Shoemake 在Graphics Gems IV中文章

This is an old post but I have code that does what you are asking for. 这是一篇旧帖子,但我的代码可以满足您的要求。 I spent some time figuring this out long ago.. I just rewrote it for a new project in C++. 我花了一些时间来解决这个问题。我刚刚用C ++重新编写了一个新项目。 It helps to draw a grid in the XZ plane to test this code. 它有助于在XZ平面中绘制网格以测试此代码。

Basically, you need to add some code to the keyDown and Keyup events so that you can switch from arcing around the center (L mouse button) and zooming (R mouse button) and movement (Shift key), You can change this to what ever key code you would like. 基本上,你需要在keyDown和Keyup事件中添加一些代码,这样你就可以从围绕中心的弧形切换(L鼠标按钮)和缩放(R鼠标按钮)和移动(Shift键),你可以将它改为任何一个你想要的关键代码。 You will also need to add code for the mouse events Down, Move and UP. 您还需要为鼠标事件Down,Move和UP添加代码。 In these events you set bool values that the routine uses. 在这些事件中,您可以设置例程使用的bool值。 I use the old gluLookAt to set my view matrix. 我使用旧的gluLookAt来设置我的视图矩阵。 If you dont want to use it, you'll need to write one. 如果你不想使用它,你需要写一个。 Here is the code for setting the view matrix: 以下是设置视图矩阵的代码:

    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.
    }

Here is the code that does the math.. To be able to move the look at point in the direction you move the mouse regardless of what angle you are looking at in Y, you have to do some 2D rotation math on the shift value. 下面是进行数学运算的代码。为了能够在移动鼠标的方向上移动外观,无论您在Y中查看的是什么角度,都必须对移位值进行一些2D旋转数学运算。 The math isn't complex but understanding my code might be. 数学并不复杂,但理解我的代码可能是。

//=================================================
// 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;
}

Note: "mouse_p" needs to be set when the left or right mouse button is pressed. 注意:按下鼠标左键或右键时需要设置“mouse_p”。 Its used to get the delta distances from where the mouse is from where it used to be. 它用于获取鼠标所在位置的三角形距离。

"point" is the mouse location from the mouse move event. “point”是鼠标移动事件的鼠标位置。 "mouse_p is set to match "point" after the math is done. Otherwise the movement starts scaling out of control!. “在完成数学运算后,mouse_p被设置为匹配”point“。否则,运动开始缩小失控!

On KEYUP... Set the bools to false (xz_translation_flag and y_move_flag). 在KEYUP上...将bools设置为false(xz_translation_flag和y_move_flag)。 ON KEYDOWN... Set xz_translation_flag = TRUE when the shift key is pressed. ON KEYDOWN ...按下shift键时设置xz_translation_flag = TRUE。 y_move_flag has no function tied to it directly yet. y_move_flag还没有直接绑定它的函数。 Its going to be used to move the u_look_point_Y var to change altitude of the look at point. 它将用于移动u_look_point_Y var以改变观察点的高度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从Viewpoint Feature Histogram进行3D对象识别的训练和匹配? - How to do a training and matching for 3D object recognition from Viewpoint Feature Histogram? 从给定视点将3D点云射线投射到2D图像 - Raycast a 3D pointcloud to an 2D image from a given viewpoint 如何知道点是否与SolidWorks生成的CAD文件中的3D对象相交? - How to know if a point intersects a 3D object in a SolidWorks generated CAD file? 如何在 Qt 3D 中控制帧率? - How can I control frame rate in Qt 3D? 如何在 DirectX 中为 3D object 之类的 Cylinder 实现几何着色器? - How to implement Geometry Shader in DirectX for a 3D object like Cylinder? 3D应用程序中的OpenGL 2D hud - OpenGL 2D hud in 3D application 带有 3d 阵列接口的八叉树 - Octree with 3d array like interface 子弹3d中的滚动摩擦不像演示一样 - rollingfriction in bullet 3d not behaving like demo 应用程序图标未显示在控制面板的添加/删除程序中 - Application icon is not getting displayed in control panel add/remove programs 如何像在MATLAB中一样将JPEG图像像素作为3D数组访问? - How can I access the JPEG image pixels as a 3D array like we do in MATLAB?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM