glutPassiveMotionFunc и glutWarpMousePointer - PullRequest
8 голосов
/ 08 апреля 2009

Я хочу реализовать свой собственный курсор в окне OpenGL / GLUT. Обычный способ сделать это - заморозить курсор (чтобы он не ударялся о края экрана) и самостоятельно отслеживать его положение. Я могу сделать экранный курсор невидимым, используя

glutSetCursor(GLUT_CURSOR_NONE);

и затем внутри моего обратного вызова glutPassiveMotionFunc переместите указатель в середину окна, используя

int centerX = (float)kWindowWidth / 2.0;
int centerY = (float)kWindowHeight / 2.0;

int deltaX = (x - centerX);
int deltaY = (y - centerY);

mouseX += deltaX / (float)kWindowWidth;
mouseY -= deltaY / (float)kWindowHeight;

glutWarpPointer( centerX, centerY );

Это работает в том смысле, что держит указатель на середине окна. Проблема заключается в том, что когда я рисую мышь «OpenGL» (внутри функции обратного вызова glutDisplayFunc ()), она становится очень резкой.

Я посмотрел в Интернете и обнаружил, что может быть проблема, из-за которой glutWarpPointer () вызывает повторный вызов glutPassiveMotionFunc, что приводит к циклу, но здесь этого не происходит.

Я нахожусь на Mac OS X, и я нашел пост, говорящий, что CGDisplayMoveCursorToPoint лучше подходит для этого. Вызов CGDisplayMoveCursorToPoint работает, но движение все еще очень резкое (и я, кажется, получаю много событий, где x и y оба равны 0). В любом случае, я бы хотел, чтобы это работало и в Linux, поэтому решение, предназначенное только для Mac, не является идеальным (но у меня все в порядке, когда приходится работать в разных системах по-разному).

Я сократил это до теста.

#include <stdio.h>
#include <OpenGL/OpenGL.h>
#include <GLUT/GLUT.h>

int curX = 0;
int curY = 0;

void display() {
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glClear( GL_COLOR_BUFFER_BIT );

    float vx = (float)curX / 300.0 + 0.5;
    float vy = (float)curY / 300.0 + 0.5;

    glColor3f( 1.0, 0.0, 0.0 );
    glBegin( GL_POINTS );
        glVertex3f( vx, vy, 0.0 );
    glEnd();

    glutSwapBuffers();

}

void passivemotion( int x, int y ) {
    int centerX = 150;
    int centerY = 150;

    int deltaX = x - centerX;
    int deltaY = y - centerY;
    curX += deltaX;
    curY -= deltaY;

    glutWarpPointer( centerX, centerY );
}

void timer( int val ) {
    glutTimerFunc( 16, &timer,  0);
    glutPostRedisplay();
}

int main (int argc, char * argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB);
    glutInitWindowSize(300,300);
    glutCreateWindow("FPS Mouse Sample");
    glutDisplayFunc(&display);
    glutPassiveMotionFunc(&passivemotion);
    glutSetCursor( GLUT_CURSOR_NONE );
    glutTimerFunc( 16, &timer, 0 );
    glutMainLoop();
    return 0;
}

Ответы [ 6 ]

6 голосов
/ 08 апреля 2009

Спасибо, аиб за советы. Вы заставили меня посмотреть на разборку glutWarpPointer, и стало очевидно, что происходит. Вызов glutWarpPointer CGPostMouseEvent, который приводит к куче бессмысленных событий (и нет никакого способа их пропустить, поскольку вы получаете события мыши только один раз за кадр, новые «настоящие» события будут запаздывать). Решение, которое я нашел, состоит в том, чтобы деформироваться только тогда, когда указатель находится на краю экрана (в конце концов, цель - притворяться, будто точка никогда не достигнет края экрана). В любом случае вот код.

int lastX = 150;
int lastY = 150;
void passivemotion( int x, int y ) {    
    int deltaX = x - lastX;
    int deltaY = y - lastY;

    lastX = x;
    lastY = y;

    if( deltaX == 0 && deltaY == 0 ) return;

    int windowX     = glutGet( GLUT_WINDOW_X );
    int windowY     = glutGet( GLUT_WINDOW_Y );
    int screenWidth     = glutGet( GLUT_SCREEN_WIDTH );
    int screenHeight    = glutGet( GLUT_SCREEN_HEIGHT );

    int screenLeft = -windowX;
    int screenTop = -windowY;
    int screenRight = screenWidth - windowX;
    int screenBottom = screenHeight - windowY;

    if( x <= screenLeft+10 || (y) <= screenTop+10 || x >= screenRight-10 || y >= screenBottom - 10) {
        lastX = 150;
        lastY = 150;
        glutWarpPointer( lastX, lastY );
        //  If on Mac OS X, the following will also work (and CGwarpMouseCursorPosition seems faster than glutWarpPointer).
        //  CGPoint centerPos = CGPointMake( windowX + lastX, windowY + lastY );
        //  CGWarpMouseCursorPosition( centerPos );
        // Have to re-hide if the user touched any UI element with the invisible pointer, like the Dock.
        //  CGDisplayHideCursor(kCGDirectMainDisplay);
    }

    curX += deltaX;
    curY -= deltaY;
}
5 голосов
/ 17 марта 2011

Я нашел лучший подход. Что происходит, так это то, что ОС подавляет события примерно на 0,25 секунды после деформации мыши. Так что вместо этого просто позвоните:

#ifdef __APPLE__
CGSetLocalEventsSuppressionInterval(0.0);
#endif

Тогда все пройдет гладко, без заиканий.

Возможно, вам потребуется:

#include <ApplicationServices/ApplicationServices.h>

и добавьте этот фреймворк в ваш проект или в опции компилятора.

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

0 голосов
/ 08 апреля 2009

Может ли это быть из-за того, что вы меняете буферы в окне с двойной буферизацией?

Ваш пример не работает в моей системе Win32, если я не добавлю GLUT_DOUBLE в glutInitDisplayMode ().

Edit:

Вы правы. Вызов glutWarpPointer () из функции движения, похоже, вызывает цикл в моей системе [win32]. Таймер даже не может выстрелить, если я не нажму кнопку или еще что-нибудь. Держу пари, что очередь сообщений наводнена событиями движения.

Вызов функции display () справа от функции движения тоже не работает - на этот раз не удается зарегистрировать любое движение.

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

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

0 голосов
/ 08 апреля 2009

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

0 голосов
/ 08 апреля 2009

Я предполагаю здесь, но я подозреваю, что движение прерывистое, потому что курсор рисуется в функции рисования вашего приложения (display ()), а не обрабатывается ОС.

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

Когда вы рисуете это сами, вы подчиняетесь обычному механизму планирования вашей ОС и проходите обычную очистку и перерисовываете все окно. В этом случае это быстро, но не так быстро, как мы привыкли с указателем мыши из-за вышеизложенного.

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

Удачи!

0 голосов
/ 08 апреля 2009

У меня не слишком много опыта с переизбытком, за исключением примеров из Красной книги, но не слишком ли это из-за того, что вы рисуете для курсора, или как часто вы его рисуете? Если вы просто нарисуете точку, в которой курсор должен использовать вызовы OpenGL, это все равно будет рывком? Может ли быть проблема с вашим временным кодом?

Какой код вы вызываете для обновления указателя каждый тик? Я предполагаю, что это не код, указанный в списке, поскольку вы будете вычислять центральную точку каждый раз, а не при изменении размера.

Мои извинения за слепой ответ здесь (то есть с ограниченным опытом перенасыщения).

...