9

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;
}
4

6 に答える 6

7

ヒントをありがとう。あなたは私に glutWarpPointer の逆アセンブルを調べさせ、何が起こっているのか明らかになりました。glutWarpPointer CGPostMouseEvent を呼び出すと、意味のないイベントが多数発生します (マウス イベントはフレームごとに 1 回しか取得できないため、それらをスキップする方法はありません。新しい「実際の」イベントは遅れます)。私が見つけた解決策は、ポインターが画面の端にあるときにのみワープすることです (結局、ポイントは、ポイントが画面の端に決して到達できないようにふりをすることです)。いずれにせよ、ここにコードがあります。

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;
}
于 2009-04-08T15:55:08.663 に答える
5

より良いアプローチを見つけました。何が起こっているかというと、マウスをワープした後、OS は約 0.25 秒間イベントを抑制します。したがって、代わりに次のように呼び出します。

#ifdef __APPLE__
CGSetLocalEventsSuppressionInterval(0.0);
#endif

その後、すべてが途切れることなくスムーズに進みます。

以下を含める必要がある場合があります。

#include <ApplicationServices/ApplicationServices.h>

このフレームワークをプロジェクトまたはコンパイラ オプションに追加します。

マウスが画面の中央に移動するとイベントが発生する可能性があるため、画面の中央にある場合はイベントを無視します。

于 2011-03-16T21:13:03.077 に答える
0

ここで推測していますが、カーソルがOSで処理されるのではなく、アプリケーションの描画関数(display())で描画されるため、モーションがぎくしゃくしているのではないかと思います。

通常のマウスポインタは、カーソル画像をフレームバッファの内容とXORすることによってドライバレベルで処理されます。つまり、非常に高速であり、割り込みサービスルーチンでOSによって非常に高い優先度で処理されます(応答性の錯覚を維持するため)。

自分で描画すると、OSの通常のスケジューリングメカニズムが適用され、通常のクリアを実行してウィンドウ全体のリガマロールを再描画します。この場合、高速ですが、上記の理由により、マウスポインタで慣れているほど高速ではありません。

要するに、期待したほど速くなるかどうかはわかりません(特に、表示機能とアプリのロジックがより複雑になるにつれて)。

幸運を!

于 2009-04-08T02:21:41.083 に答える
0

ダブルバッファリングされていないウィンドウでバッファを交換している可能性がありますか?

あなたの例は、GLUT_DOUBLE を glutInitDisplayMode() に追加しない限り、私の Win32 システムでは機能しません。

編集:

あなたは正しいです。モーション関数内から glutWarpPointer() を呼び出すと、[win32] システムでループが発生するようです。ボタンか何かをクリックしない限り、タイマーが起動する機会すらありません。メッセージ キューがモーション イベントであふれているに違いありません。

モーション関数から直接 display() を呼び出しても機能しないようです。今回は、どのような種類のモーションも登録できません。

あなたの例を機能させる唯一の方法は、パッシブモーション コールバックをアクティブモーション コールバックに変更し、その関数から直接 display() を呼び出すことでした。これが当初の意図とはかけ離れていることは承知していますが、少なくともこの方法でスムーズな動きが得られました。

glutIdleFunc() を使用してディスプレイの更新をトリガーしてみましたか? あふれたメッセージ キューではまだ機能しない可能性がありますが、試してみる価値はあります。すべての動きでカーソルをウィンドウの中央に手動でラップする代わりに、API 呼び出しを使用してマウスをキャプチャすることも検討できます。

于 2009-04-08T12:57:37.310 に答える
0

数フレームにわたるマウスの動きを平均化していますか? 仕事中のため、以前のプロジェクトのコードが見つかりません。しかし、数フレームにわたってマウスの動きを平均化したと思いますが、それを行う前は、動きが非常にぎくしゃくしていました。

于 2009-04-08T03:08:24.950 に答える
0

レッドブックの例を除いて、過剰摂取の経験はあまりありませんが、カーソルのために何を描いているのか、またはどれくらいの頻度で描いているのかが原因でぎくしゃくしていますか? カーソルが OpenGL 呼び出しを使用するはずのポイントを描画しただけでも、まだぎくしゃくしていますか? タイミングコードが問題になる可能性はありますか?

ティックごとにポインタを更新するためにどのコードを呼び出していますか? サイズ変更イベントではなく、毎回中心点を計算するため、リストされているコードではないと思います。

ここでやみくもに答えてしまったことをお詫びします(つまり、過剰摂取の経験が限られています)。

于 2009-04-08T01:29:04.780 に答える