Невозможно вращать объект вокруг своего центра при щелчке мышью OpenGL - PullRequest
0 голосов
/ 30 октября 2019

Мне удалось повернуть мой объект, но мне нужно щелкнуть правой кнопкой мыши по объекту, чтобы остановить его вращение. Я не знаю, как заставить объект вращаться вокруг его центра, а затем остановиться. Я прикреплю код, чтобы точно увидеть, что происходит с этой формой. Проблема, я думаю, заключается в функции spinDisplay() ... дело в том, что мне нужно вращать вокруг ее центра при щелчке левой кнопкой мыши, а при щелчке правой кнопкой мыши я должен изменить цвет объекта ....

#include <stdlib.h>
#include <math.h>
#include "dependente\freeglut\freeglut.h"
#include "dependente\glfw\glfw3.h"
#include <stdio.h> //incluziuni librarii

float ORG[3] = { 0,0,0 };
static GLfloat spin = 0.0;
GLfloat viewangle = 0, tippangle = 0, traj[120][3]; //variabila pentru unghi camera

GLfloat d[3] = { 0.1, 0.1, 0.1 }; //vector directie

GLfloat  xAngle = 0.0, yAngle = 0.0, zAngle = 0.0;
bool draw_triangle = false; //variabila desenat figuri 
bool draw_square = false;
bool draw_decagon = false;


void Triangle(void) //draw the triangle shape
{
    glBegin(GL_TRIANGLE_FAN);//triangles have a common vertex, which is the central vertex
    glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(0.0f, 1.0f, 0.0f);   //V0(red)
    glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);   //V1(green)
    glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);   //V2(blue)
    glEnd();
}
void Square(void) {
    glBegin(GL_QUADS);
    glVertex2f(-1.0f, 1.0f); // top left
    glVertex2f(1.0f, 1.0f); // top right 
    glVertex2f(1.0f, -1.0f); // bottom right
    glVertex2f(-1.0f, -1.0f); // bottom left
    glEnd();
}
void Decagon(void) //draw the decagon shape
{
    glBegin(GL_POLYGON);
                glColor3f(0.0f, 0.0f, 1.0f);
                glVertex3f(0.72f,0.8f, 0.0f); //a1
                glVertex3f(0.52f,   0.8f,0.0f);  //z
                glVertex3f(0.35f,   0.64f, 0.0f); //b1
                glVertex3f(0.3f,   0.48f, 0.0f); //d1
                glVertex3f(0.35f,   0.3f, 0.0f); //e1
                glVertex3f(0.52f, 0.16f, 0.0f); //l1
                glVertex3f(0.72f, 0.16f, 0.0f); //m1
                glVertex3f(0.9f, 0.3f, 0.0f); //o1
                glVertex3f(0.95f, 0.48f, 0.0f); //p1
                glVertex3f(0.9f, 0.64f, 0.0f); //c1
                glScalef(10, 10, 10);
                glTranslatef(1, 2, 3);
    glEnd();
}

void Keyboard(unsigned char key, int x, int y) //press a key to perform actions
{
    switch (key) {

    case 'd': d[0] += 0.1;  break; //camera right
    case 'a': d[0] -= 0.1;  break; //camera left
    case 'w': d[1] += 0.1;  break; //camera up 
    case 's': d[1] -= 0.1;  break; //camera down 
    case 'm': d[2] += 0.1;  break; //magnify
    case 'n': d[2] -= 0.1;  break; //minify
    case 't': draw_triangle = true; draw_decagon = false;   break; //draw pyramid when key is pressed
    case 'q': draw_square = true; draw_decagon = false; draw_triangle = false; break; //draw cube when key is pressed
    case 'l': draw_decagon = true; draw_triangle = false; draw_square = false; break; //draw prism when key is pressed

    case 'x': xAngle += 5;  break; //modify x axis angle
    case 'y': yAngle += 5;  break; //modify y axis angle
    case 'z': zAngle += 5;  break;  //modify z axis angle

    default: printf("   Keyboard %c == %d", key, key); //see what key it's pressed
    }

    glutPostRedisplay();
}

void spinDisplay() //here it's the problematic function
{
    spin = spin + 0.1;
    if (spin > 360.0)
    {
        spin = 0.0;
    }
    glutPostRedisplay();
}
void mouse(int buton, int state, int x, int y)
{
    switch (buton) {
    case GLUT_LEFT_BUTTON:
        if (state == GLUT_DOWN)
            glutIdleFunc(spinDisplay);
        break;
    case GLUT_RIGHT_BUTTON: //here I don't know how to change the color of the shape
        glutIdleFunc(NULL);
    default:glutIdleFunc(NULL);

        break;
    }
}
void redraw(void)
{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);

    glLoadIdentity();

    glTranslatef(0, 0, -3);
    glRotatef(tippangle, 1, 0, 0);  // Up and down arrow keys 'tip' view.
    glRotatef(viewangle, 0, 1, 0);  // Right/left arrow keys 'turn' view.

    glDisable(GL_LIGHTING);


    glPushMatrix();
    glTranslatef(d[0], d[1], d[2]);    // Move box down X axis.
    glScalef(0.7f, 0.7f, 0.7f); //increase the object size
    glRotatef(zAngle, 0, 0, 1);
    glRotatef(yAngle, 0, 1, 0);
    glRotatef(xAngle, 1, 0, 0);
    glRotatef(spin, 0.0, 0.0, 1.0);

    if (draw_triangle)
        Triangle();

    if (draw_decagon)
        Decagon();
    if (draw_square)
        Square();

    glPopMatrix();

    glutSwapBuffers();
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitWindowSize(900, 600);
    glutInitWindowPosition(300, 300);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE);

    glutCreateWindow("Figure Rotation");
    glutDisplayFunc(redraw);
    glutKeyboardFunc(Keyboard);
    glutMouseFunc(mouse);
    glClearColor(0.1, 0.0, 0.1, 1.0);

    glMatrixMode(GL_PROJECTION);//specify which matrix is the current matrix, matrix that represents your camera's lens (aperture, far-field, near-field, etc).
    gluPerspective(60, 1.5, 1, 10); //set up a perspective projection matrix
    glMatrixMode(GL_MODELVIEW); //specify which matrix is the current matrix,matrix that represents your camera (position, pointing, and up vector).
    glutMainLoop();

    return 1;
}

1 Ответ

1 голос
/ 30 октября 2019

Основываясь исключительно на вашем коде, основная проблема в Decagon() для определения вершин формы. Поскольку такие вершины определены не в центре самой фигуры, а в направлении к верхнему правому углу, она не будет вращаться вокруг себя, а, кажется, будет вращаться вокруг центра, хотя ваша последовательность умножений матриц работает нормально.

Для простоты я бы визуализировал центрирование его в 0,0 вдоль плоскости xy, затем определял правую половину его формы, а затем отражал ее обратно в левую. Вы можете воспользоваться знаком - минус. Неявно используйте преимущество определения формы в NDC (Normalizd Device Coordinate) пространстве .

Примечание : не совсем такое же соотношение, как в вашем первоначальном определении, но чтобы получить васидея. Вы можете попробовать поменять следующее на свое, тогда оно должно вращаться вокруг себя.

    glBegin(GL_POLYGON);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(0.25f, 0.5f, 0.0f);
        glVertex3f(0.45f, 0.30f, 0.0f);
        glVertex3f(0.55f, 0.0f, 0.0f);
        glVertex3f(0.45f, -0.30f, 0.0f);
        glVertex3f(0.25f, -0.5f, 0.0f);
        glVertex3f(-0.25f, -0.5f, 0.0f);
        glVertex3f(-0.45f, -0.30f, 0.0f);
        glVertex3f(-0.55f, 0.0f, 0.0f);
        glVertex3f(-0.45f, 0.30f, 0.0f);
        glVertex3f(-0.25f, 0.5f, 0.0f);
        //glScalef(10, 10, 10);  // this won't have any effect on result
        //glTranslatef(1, 2, -3);// the same
    glEnd();

У вас есть 2 варианта здесь

  1. Полностью изменить определение вершин (только с Decagonбыть похожим на вышеуказанное относительно происхождения). Другие формы уже хороши, они определены относительно начала координат.
  2. Тщательно определите источник формы независимо от того, как вершины вашей определенной формы. Используйте такое положение, чтобы сначала перевести форму назад как часть операции матрицы перед всеми другими операциями (пожалуйста, прочтите, чтобы узнать почему).

Концепция вращения вокруг себя

Концепция вращения вокруг себя заключается в том, что нам нужно выполнить следующие операции в следующем порядке:

  1. Масштаб (в данном случае у нас нет)
  2. Вращение
  3. Перевод

Масштабирование, хотя у нас его нет в этом случае, должно быть последним, иначе это может повлиять на другие две операции.

Если мы переведем сначала в произвольную позицию,тогда вращение произойдет вокруг такой точки. На самом деле вращение работает относительно начала координат 0,0, поэтому нам просто нужно каким-либо образом сначала поместить объект обратно в начало координат, прежде чем мы продолжим, затем мы можем вращать, переводить в желаемое положение, в котором он должен быть, и масштабировать.

Давайте посмотрим ваш порядок умножения матриц

    glScalef(0.7f, 0.7f, 0.7f); //increase the object size
    glTranslatef(d[0], d[1], d[2]);    // Move box down X axis.
    glRotatef(zAngle, 0, 0, 1);
    glRotatef(yAngle, 0, 1, 0);
    glRotatef(xAngle, 1, 0, 0);
    glRotatef(spin, 0.0, 0.0, 1.0);

Это означает, что мы делаем следующее в следующем порядке:

  • вращаемся вокруг оси z с spin angle
  • вращается вокруг оси x с xAngle углом
  • вращается вокруг оси y с yAngle углом
  • вращается вокруг оси z с zAngle углом

Хотя мы могли бы, возможно, объединить первое и последнее вместе, но в любом случае это нормально.

Также вы можете захотеть дополнительно взглянуть на Углы Эйлера , когда мы вращаемся вокруг3 кардинальных оси, подобные этой, могут привести к проблеме блокировки карданного подвеса , но ее можно решить путем ограничения углов, которые пользователь может вращать вокруг определенной оси.

Порядок правильный. Это переведено в математические термины как S * T * Rz * Ry * Rx * Rspin, в котором вы можете видеть, что в коде он обратен порядку. Rspin происходит сначала, затем Rx, а затем и так далее.

И что же произойдет, если форма Decagon определена не относительно начала координат, но определена так, как онапереведено вправо.

Возьмите мое определение вершин, но + 0.55f для всех позиций x, у нас будет

    glBegin(GL_POLYGON);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(0.80f, 0.5f, 0.0f);
        glVertex3f(1.0f, 0.30f, 0.0f);
        glVertex3f(1.10f, 0.0f, 0.0f);
        glVertex3f(1.0f, -0.30f, 0.0f);
        glVertex3f(0.80f, -0.5f, 0.0f);
        glVertex3f(0.30f, -0.5f, 0.0f);
        glVertex3f(0.10f, -0.30f, 0.0f);
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(0.1f, 0.30f, 0.0f);
        glVertex3f(0.30f, 0.5f, 0.0f);
    glEnd();

Если вы поменяете код выше на определение вершин, то оно выиграетбольше не вращайся вокруг себя. Но мы знаем, что для возврата формы в исходное положение требуется -0.55f по оси x, поэтому, если мы добавим glTranslatef(-0.55f, 0.0f, 0.0f) в качестве первой выполняемой операции, она будет работать так же.

Мы 'd have

    glScalef(0.7f, 0.7f, 0.7f);
    glTranslatef(d[0], d[1], d[2]);
    glRotatef(zAngle, 0, 0, 1);
    glRotatef(yAngle, 0, 1, 0);
    glRotatef(xAngle, 1, 0, 0);
    glRotatef(spin, 0.0, 0.0, 1.0);
    glTranslatef(-0.55f, 0.0f, 0.0f);  // <------ add this

Короче говоря, прежде чем вращать (вокруг себя), переведите целевой объект в исходное положение, затем выполните правильную последовательность действий.

Если вы хотите иметьтакой объект должен быть расположен в том месте, где вы определили вершины фигуры, т.е. он находится справа +0.55f вдоль оси x и все еще вращается вокруг себя. Тогда вместо этого вы используете glTranslatef(d[0] + 0.55f, d[1], d[2]).

Дополнительные примечания

  1. Последние две операции gl glScalef() и glTranslatef() не будут иметь никакого эффекта, так как вы уже нарисовали фигуру. Эти две операции отбрасываются каждый кадр, когда вы вызываете glLoadIdentity().
  2. . Просто обратите внимание, что исходный код все еще основан на конвейере фиксированных функций OpenGL. Возможно, вы захотите взглянуть на современный программируемый конвейер. Это даст вам больше гибкости в управлении виртуальной камерой, поэтому операции с матрицами будут более четкими и отделенными от самого объекта всякий раз, когда нам нужно перемещаться. Таким образом, это облегчит понимание и понимание матричных операций.

Редактировать Для дополнительного контроля и удовлетворения требований приложения, как указано ниже

  • Щелкните левой кнопкой мыши, чтобы вращаться бесконечно, затем снова щелкните левой кнопкой мыши, чтобы остановить
  • Щелкните правой кнопкой мыши, чтобы пролистать цвет для визуализированной формы

У нас должны быть контрольные флаги и информация для насизменить в любое время кадра следующим образом.

bool isSpinning = false;
#define NUM_COLOR 4
int sCurrColor = 0;
GLfloat sColor[NUM_COLOR][3] = {
    {1.0f, 1.0f, 1.0f},
    {1.0f, 0.0f, 0.0f},
    {0.0f, 1.0f, 0.0f},
    {0.0f, 0.0f, 1.0f}
};

Так что для цветов у нас есть белый, красный, синий и зеленый. Всего в 4 цветах, каждый цвет имеет 3 значения компонента для RGB. Мы начинаем с белого цвета, как видно из sCurrColor для нашего индекса.

Теперь ваша mouse() функция будет выглядеть следующим образом

void mouse(int buton, int state, int x, int y)
{
    switch (buton) {
    case GLUT_LEFT_BUTTON:
        if (state == GLUT_DOWN && !isSpinning)
            isSpinning = true;
        else if (state == GLUT_DOWN && isSpinning)
            isSpinning = false;
        break;
    case GLUT_RIGHT_BUTTON: //here I don't know how to change the color of the shape
        if (state == GLUT_DOWN)
            sCurrColor = (sCurrColor + 1) % NUM_COLOR;
        break;
    default:glutIdleFunc(NULL);

        break;
    }
}

Мы оптимизировали перемещение glutIdleFunc(spinDisplay); для вызова внутриФункция main() как раз перед glutMainLoop(). В соответствии с вашими требованиями, нам не нужно менять его каждый кадр.

Таким образом, spinDisplay() теперь изменяется на

void spinDisplay() //here it's the problematic function
{
    if (isSpinning)
    {
        spin = spin + 3.0;
        if (spin > 360.0)
        {
            spin = 0.0;
        }
    }
    glutPostRedisplay();
}

Возможно, лучше изменить имяфункционировать в нечто вроде display(), так как это более общее, чтобы не путать, что мы должны вращаться каждый раз. Во всяком случае, я не изменил это для краткости и последовательности вашего кода.

Теперь последняя часть - подключить sColor для использования всеми этими фигурами при рендеринге. Например, для Decagon вы можете сделать это

glColor3f(sColor[sCurrColor][0], sColor[sCurrColor][1], sColor[sCurrColor][2]);

Это будет то же самое для других фигур, если вы хотите получить тот же эффект, щелкнув правой кнопкой мыши для циклического изменения цвета.

...