2009-04-08 8 views
8

Voglio implementare il mio cursore in una finestra OpenGL/GLUT. Il solito modo per farlo è congelare il cursore (in modo che non possa colpire i bordi dello schermo) e tenere traccia della sua posizione da solo. Posso fare il cursore sullo schermo invisibile utilizzandoglutPassiveMotionFunc e glutWarpMousePointer

glutSetCursor(GLUT_CURSOR_NONE); 

e poi all'interno della mia richiamata glutPassiveMotionFunc spostare il puntatore al centro della finestra utilizzando

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); 

Questo funziona in quanto mantiene il puntatore attaccato al al centro della finestra. Il problema è che quando disegno il mouse "OpenGL" (all'interno del callback glutDisplayFunc()) è estremamente a scatti.

Ho cercato online e ho scoperto che può esserci un problema in cui glutWarpPointer() provoca il richiamo del callback glutPassiveMotionFunc, risultante in un ciclo, ma questo non sembra accadere qui.

Sono su Mac OS X e ho trovato un post che diceva che CGDisplayMoveCursorToPoint si adattava meglio a questo. Chiamare CGDisplayMoveCursorToPoint funziona ma il movimento è ancora molto a scatti (e mi sembra di ottenere molti eventi in cui xey sono entrambi 0). In ogni caso, mi piacerebbe che funzionasse anche su Linux, quindi una soluzione solo per Mac non è l'ideale (ma sto bene dovendo fare cose diverse sui diversi sistemi).

Ho ridotto questo a un banco di prova.

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

risposta

6

Grazie aib per i suggerimenti. Mi hai fatto vedere lo smontaggio di glutWarpPointer ed è diventato chiaro cosa stava succedendo. Chiamando glutWarpPointer CGPostMouseEvent che si traduce in una serie di eventi senza senso (e non c'è modo di ignorarli poiché ottieni solo eventi del mouse una volta per fotogramma, i nuovi eventi "reali" saranno in ritardo). La soluzione che ho trovato è di deformare solo quando il puntatore si trova sul bordo dello schermo (il punto, dopo tutto, è fingere che il punto non possa mai raggiungere il bordo dello schermo). In ogni caso, ecco il codice.

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; 
} 
+1

Felice che tu abbia funzionato. +1 per pubblicare la tua soluzione e una buona spiegazione. Sembra che non ero nemmeno vicino! :) –

+1

L'ho risolto girando un po 'prima di chiamare glutWarpPointer e ignoing gli eventi del mouse fino alla riga successiva. –

0

non ho troppa esperienza con sovrabbondanza, salvo per gli esempi rosso-book, ma è a scatti a causa di ciò che si sta disegnando per il cursore, o quanto spesso si sta disegnando? Se si disegna un punto in cui si suppone che il cursore utilizzi le chiamate OpenGL, è ancora a scatti? Il tuo codice di temporizzazione potrebbe essere un problema?

Quale codice si sta chiamando per aggiornare il puntatore ogni segno di spunta? Presumo che non sia il codice elencato come si calcoli il punto centrale ogni volta, invece che su un evento di ridimensionamento.

Le mie scuse per la risposta cieca qui (cioè con esperienza di glut limitato).

+0

Ho modificato per fornire l'origine di un caso ridotto. –

0

Sto indovinando qui, ma ho il sospetto che il movimento sia a scatti perché il cursore è disegnato nella funzione di disegno dell'applicazione (display()), piuttosto che gestita dal sistema operativo.

Un normale puntatore del mouse viene gestito a livello di driver XORando l'immagine del cursore con il contenuto del buffer del fotogramma - quindi fulmineo e gestito con priorità molto alta dal sistema operativo in una routine di interrupt (per mantenere l'illusione di reattività).

Quando lo si disegna da solo, si è soggetti al normale meccanismo di pianificazione del proprio sistema operativo & passare attraverso il normale chiaro & ridisegnare l'intera finestra rigamarole. In questo caso, è veloce ma non così veloce come siamo abituati con un puntatore del mouse a causa di quanto sopra.

In breve, non sono sicuro che riuscirai mai ad essere veloce come ci si aspetta che sia (in particolare quando la logica di app della funzione di visualizzazione & diventa più complessa).

Buona fortuna!

+0

Questa è una buona idea, ma rimuovendo la chiamata a glutWarpPointer() si ottiene l'uniformità prevista (ma ovviamente si perde l'azione del puntatore warp). –

0

Stai facendo la media del movimento del mouse su alcuni fotogrammi? Non riesco a trovare il mio codice per il mio progetto precedente perché sono al lavoro. Ma penso di aver fatto una media dei movimenti del mouse su alcuni fotogrammi, prima di ho fatto che il movimento era molto a scatti.

0

Potrebbe essere perché si stanno scambiando i buffer su una finestra non a doppio buffer?

L'esempio non funziona sul mio sistema Win32, a meno che non aggiungo GLUT_DOUBLE a glutInitDisplayMode().

Modifica:

Sei corretto. Chiamare glutWarpPointer() dall'interno della funzione motion sembra causare un loop sul mio sistema [win32]. Il timer non ha nemmeno la possibilità di sparare a meno che non clicchi su un pulsante o qualcosa del genere. Sto scommettendo che la coda dei messaggi è inondata di eventi di movimento.

Chiamare display() direttamente dalla funzione di movimento non sembra funzionare, anche - questa volta non riesce a registrare alcun tipo di movimento.

L'unico modo ho potuto ottenere il vostro esempio a lavoro è stato modificando il movimento di callback passivo ad un movimento di callback attiva e chiamando display() direttamente da tale funzione. So che questo è lontano da ciò che intendevi in ​​origine, ma almeno ho avuto un movimento fluido in questo modo.

Hai provato a utilizzare un glutIdleFunc() per attivare gli aggiornamenti di visualizzazione per te? Potrebbe non funzionare ancora con una coda di messaggi inondata, ma potrebbe valere la pena provare. È anche possibile esaminare catturando il mouse usando una chiamata API invece di spostare manualmente il cursore al centro della finestra ad ogni movimento.

5

Ho trovato un approccio migliore. Quello che succede è che il sistema operativo sopprime gli eventi per circa 0,25 secondi dopo aver deformato il mouse. Quindi, basta chiamare:

#ifdef __APPLE__ 
CGSetLocalEventsSuppressionInterval(0.0); 
#endif 

Quindi tutto andrà liscio senza alcuna balbuzie.

Potrebbe essere necessario includere:

#include <ApplicationServices/ApplicationServices.h> 

e aggiungere questo quadro al progetto o alle opzioni del compilatore.

Nota che potresti ottenere un evento in cui il mouse si sposta al centro dello schermo, quindi ignoro un evento solo se si trova al centro dello schermo.

+0

Cheeez !! Grazie mille per questa risposta !!!Ho pensato che stesse succedendo qualcosa, e questo fa andare via il mio jitter! L'unico inconveniente è che la funzione è deprecata dal 10.6 :( – v01pe

+0

c'è un bel trucco (non deprecato) per questo: http://stackoverflow.com/a/17547015/1050264 – v01pe

+1

Bello sapere, grazie per averlo indicato. –