Zobrazit předchozí téma :: Zobrazit následující téma |
Autor |
Zpráva |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 4. březen 2015, 16:19:55 Předmět: Pohyb myši a WinAPI |
|
|
V enginu mám pro pohyb myši tento kód (volán v každé otáčce logické smyčky enginu - tzn. ideálně s konstantním dt).
kód: |
POINT pt;
GetCursorPos(&pt);
if (this->windowHandle != NULL)
{
ScreenToClient(this->windowHandle, &pt);
}
this->lastPosX = this->posX;
this->lastPosY = this->posY;
this->posX = pt.x;
this->posY = pt.y;
this->changeX = this->posX - this->lastPosX;
this->changeY = this->posY - this->lastPosY; |
Tohle celkem funguje korektně. Problém je, když ale myš dorazí na konec obrazovky, pak to logicky přestane fungovat (posX a posY se přestanou měnit).
Zkoušel jsem to nahradit voláním GetRawInputData z WinAPI a pak číst
kód: |
raw->data.mouse.lLastX
raw->data.mouse.lLastY |
Což sice vrací dx, dy... ale jejich rychlost není odpovídající pohybu myši. Tzn. když to pak použiju např. na dragging objektů ve scéně, tak mi objekt nesleduje myš, ale prostě ujede (nebo se naopak za myší spozdí).
Zkoušel jsem i kombinaci obojího - když je myš v obrazovce, počítej souřadnice a změnu, else "WinAPI", ale to pak mělo za následek zpomalení myši, když se přešlo na to "WinAPI" *
* technicky je WinAPI i to ScreenToClient, ale jen jsem to chtěl rozlišit tady
Vypisoval jsem si i ladící výpisy, kolik je změna z mého kódu a z RawInputu a znatelně se liší. rawInput se nejspíše aktualizuje na pozadí častěji, než ho já čtu.. nebo nějak nevím a dokumentace k tomu je dost vágní. Jak to řešíte? (Přechod na SDL apod. nechci, protože všechno ostatní funguje jak má - tzn. klávesnice a dotykové ovládání).
Díky _________________ Perry.cz |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. březen 2015, 16:52:24 Předmět: Re: Pohyb myši a WinAPI |
|
|
Já taky používám RawInput a funguje to perfektně...
Tady je můj init kód s flagy, co používám (grab v "exkluzivním režimu"):
kód: |
RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01;
rid.usUsage = 0x02;
rid.dwFlags = RIDEV_NOLEGACY | RIDEV_CAPTUREMOUSE;
rid.hwndTarget = hwnd
if ( RegisterRawInputDevices( &rid, 1, sizeof(RAWINPUTDEVICE) ) == FALSE ) {
// ... fail ...
}
|
Release:
kód: |
RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01;
rid.usUsage = 0x02;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = 0;
RegisterRawInputDevices( &rid, 1, sizeof(RAWINPUTDEVICE) );
|
Pak ve window proc u WM_INPUT:
kód: |
GetRawInputData( ....)
if ( raw->header.dwType == RIM_TYPEMOUSE ) {
if ( raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE ) {
// data.mouse.lLastX, lLastY = delta
}
}
.
.
.
return 0;
|
Toto mám obalené SetCapture(hwnd) a ReleaseCapture, aby trackoval i kurzor mimo okno (což ve fullscreenu je jedno), ale myslím že to u RawInputu nepodstatné.
Řekl bych celkem standard, třeba ti to pomůže... |
|
Návrat nahoru |
|
 |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 4. březen 2015, 17:04:24 Předmět: |
|
|
To mám víceméně stejné. Ono to funguje na pohyb v herním světě OK.. ale když mám přes to tahání nějakých objektů, tak prostě ta myš nedrží na místě kam jsem kliknul a ujíždí pryč nebo se zpomaluje, což nedělá (nebo minimálně) u toho ručního počítání přes pos.X - lastPos.X _________________ Perry.cz |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. březen 2015, 21:05:46 Předmět: |
|
|
Tomu nerozumím - pokud to máš tedy stejně a dává ti to přesný delta pohyb myši, tak přece nic víc nepotřebuješ...
Absolutní pozici si z toho spočítáš ručně a nepotřebuješ ani hw kurzor - prostě si ho nakreslíš sám (i když v 60hz mode to bude opticky trochu lagovat).
To, co popisuješ vypadá spíš na nějaký bug... |
|
Návrat nahoru |
|
 |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 4. březen 2015, 22:36:33 Předmět: |
|
|
No delta pohyb to dává přesně ve smyslu směru, to jo.. ale už mi přijde že to neodpovídá rychlosti pohybu myši. Když s ní trhnu rychle, tak ta delta je skoro stejná jako při pomalém pohybu. Když to počítám ručně, tak se tam projeví změna. Spíš to vidím možná na bug v nějaké synchronizaci, kdy ta myš se v tom WinAPI refreshuje častěji než moje smyčka a pak tam ten pohyb "mizí".. ale přesně netuším _________________ Perry.cz |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 4. březen 2015, 22:53:12 Předmět: |
|
|
Čekal bych, že to čtení vrací deltu od posledního volání (nebo že to nějak bufferuje), takže by to mělo být přece v pořádku (pokud to kromě tebe ještě nečte někdo jiný .
Takže ty to natvrdo polluješ každý frame? Registruješ si ten device?
Možná kdybys postnul kus mainloopu, co tam přesně děláš... |
|
Návrat nahoru |
|
 |
Tringi

Založen: 28. 07. 2007 Příspěvky: 290
|
Zaslal: 4. březen 2015, 23:55:55 Předmět: |
|
|
Standardní řešení (v době kdy jsem dělal tyhle věci) bylo, po každém GetCursorPos vrátit kurzor pomocí SetCursorPos doprostřed obrazovky. _________________ WWW | GitHub | TW |
|
Návrat nahoru |
|
 |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 5. březen 2015, 08:59:43 Předmět: |
|
|
citace: |
Čekal bych, že to čtení vrací deltu od posledního volání (nebo že to nějak bufferuje), takže by to mělo být přece v pořádku (pokud to kromě tebe ještě nečte někdo jiný |
No asi jsem to dotrackoval k tomu, že WM_INPUT prostě chodí pořád při změně myši. Je asynchronní, takže přijde kdykoliv a updatuje mi stav klidně vícekrát, než ho já terpve přečtu ve smyčce enginu. Např. mi přijde 2x update myši a pak teprve běží smyčka enginu.
Tringi > Jo, to jde... ale pak bych musel mít vlastní kurzor, což se mi moc nechce (protože GUI apod) _________________ Perry.cz |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 5. březen 2015, 10:30:34 Předmět: |
|
|
Tringi napsal: |
Standardní řešení (v době kdy jsem dělal tyhle věci) bylo, po každém GetCursorPos vrátit kurzor pomocí SetCursorPos doprostřed obrazovky. |
Ano, tohle jsem používal původně, teď jenom jako fallback.
RI je z mé zkušenosti plynulejší a nepřijde mi jako hack.
V X11 bohužel používám pořád totéž (nepřišel jsem na lepší způsob), Cocoa to má vyřešené o poznání líp - dá se odpojit kurzor or myši (jednoduchý flag) a v eventech chodí i delta, což je fajn.
Perry: co ti brání to číst tehdy, když ti do WindowProc přijde WM_INPUT? Pořád tomu asi nerozumím
EDIT: můžeš si přece udělat jednoduchý akumulátor a ten si pak číst před updatem.
Mimochodem není to tak, že se ti víc těch WM_INPUT zpráv nabufferuje do thread message queue? Nebo máš update enginu v jiném threadu? |
|
Návrat nahoru |
|
 |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 5. březen 2015, 11:33:57 Předmět: |
|
|
citace: |
Perry: co ti brání to číst tehdy, když ti do WindowProc přijde WM_INPUT? Pořád tomu asi nerozumím |
kód: |
int Form::Run(int cmd)
{
this->mainWindowHandle = CreateWindow(this->name.c_str(), //name of registered class
this->caption.c_str(), //caption
this->style, //style flags
this->positionX, //x pos
this->positionY, //y pos
this->width,
this->height,
NULL, //parent window
NULL, //menu
this->instance,
this); //pointer to window creation data
if (this->mainWindowHandle == 0)
{
MessageBox(0, L"Create window failed", 0, 0);
return false;
}
ShowWindow(this->mainWindowHandle, cmd);
UpdateWindow(this->mainWindowHandle);
this->UpdateSettings();
this->Initialize();
MSG msg = {0};
while ( msg.message != WM_QUIT )
{
if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
engineCore->MainLoop(); //ENGINE LOOP
}
}
return (int)msg.wParam;
} |
A pak mám
kód: |
LRESULT CALLBACK Form::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch ( msg )
{
case WM_INPUT:
{
engineCore->UpdateInput(lParam);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
} |
V UpdateInput je pak čtení ctení RAW inputu
A ještě init okna
kód: |
//called from window ctor
bool Form::InitWnd(HINSTANCE instance)
{
WNDCLASS window_class;
window_class.style = CS_OWNDC;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = instance;
window_class.hIcon = LoadIcon(NULL,IDI_APPLICATION);
window_class.hCursor = LoadCursor(NULL,IDC_ARROW);
window_class.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
window_class.lpszMenuName = NULL;
window_class.lpszClassName = this->name.c_str();
window_class.lpfnWndProc = &Form::MainWndProc; //An external function to handle window messages
if (!RegisterClass(&window_class))
{
MessageBox(0, L"Register class failed", 0, 0);
return false;
}
return true;
}
LRESULT CALLBACK Form::MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static Form* me = 0;
switch ( msg )
{
case WM_CREATE:
{
CREATESTRUCT* createStruct = (CREATESTRUCT*)lParam;
me = (Form*)createStruct->lpCreateParams;
return 0;
}
}
if ( me )
{
return me->WndProc(hWnd, msg, wParam, lParam);
}
else
{
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
|
Je pravda, že tohle jsme psal už dávno a nijak na to od tý doby nešahal, ale přijde mi to logicky OK. Přijde mi, že RawInput tam chodí asynchroně přes to WndProc
Spuštěné je to z mainu celé aplikace pak takhle
kód: |
MyWindow::IceProjectWindow ff = MyWindow::IceProjectWindow(hInstance);
return ff.Run(nShowCmd);
|
Jinak jsem tam dodal bufferování dx/dy a to naakumuluju a vyprázdním v EngineLoop na začátku smyčky, ale moc to nepomohlo. Objekt mi pořád jede rychleji, než pohyb myši. Zkusím ještě projít ten dragging kód, jestli tam není něco blbě. _________________ Perry.cz |
|
Návrat nahoru |
|
 |
mar
Založen: 16. 06. 2012 Příspěvky: 610
|
Zaslal: 5. březen 2015, 13:25:18 Předmět: |
|
|
perry napsal: |
Je pravda, že tohle jsme psal už dávno a nijak na to od tý doby nešahal, ale přijde mi to logicky OK. |
Taky mi to přijde v pohodě... |
|
Návrat nahoru |
|
 |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 5. březen 2015, 13:53:11 Předmět: |
|
|
Jako mě právě taky... očekával bych, že když to počítám přes actPos - lastPos ručně, že to vyjde stejně jako přes ty RAW inputy dx, dy (nebo max. +- něco malého). Pro pomalý pohyb to celkem platí, ale jakmile pohnu myší hodně rychle (trhnutý pohyb), tak je to totálně jinak a tím pádem se mi pohyb podělá, protože ty RAW inputy jsou daleko menší než ty ručně spočtený.
Zkusil jsem to přepsat teď na GetRawInputBuffer (üdajně lepší pro high speed devices) a nečíst to ve WM_INPUT ale až když to potřebuju.. nicméně to vrací pořád nulový počet událostí. _________________ Perry.cz |
|
Návrat nahoru |
|
 |
nou

Založen: 28. 07. 2007 Příspěvky: 1050
|
Zaslal: 5. březen 2015, 21:35:21 Předmět: |
|
|
tipol by som ze treba vypnut akceleraciu mysi _________________ Najjednoduchšie chyby sa najtažšie hľadajú. |
|
Návrat nahoru |
|
 |
perry

Založen: 28. 07. 2009 Příspěvky: 879
|
Zaslal: 6. březen 2015, 08:06:46 Předmět: |
|
|
Tak při vypnutí akcelerace se to zdá OK. Nicméně, asi není úplně OK aby uživatel musel vypínat akceleraci v nastavení.
Zkoušel jsem to přes tenhle kód:
kód: |
int mouseParams[3];
// Get the current values.
SystemParametersInfo(SPI_GETMOUSE, 0, mouseParams, 0);
// Modify the acceleration value as directed.
mouseParams[2] = mouseAccel;
// Update the system setting.
SystemParametersInfo(SPI_SETMOUSE, 0, mouseParams, SPIF_SENDCHANGE);
|
To akceleraci vypne, ALE pak to shazuje OpenGL driver. Plus pokud program spadne dřív než má, tak se to nevrátí zpátky. A vypnutí jen pro aktuální okno / session jsem nenašel. _________________ Perry.cz |
|
Návrat nahoru |
|
 |
Ladis

Založen: 18. 09. 2007 Příspěvky: 1537 Bydliště: u Prahy
|
Zaslal: 6. březen 2015, 11:05:45 Předmět: |
|
|
Tak to je chyba tvého OpenGL driveru, že ti padá (alternativy: Direct3D, např. skrz OpenGL ES->Direct3D, pokud chceš zůstat u OpenGL). Jinak vracet bys to neměl jen při ukončení, ale i při deaktivaci okna aplikace. A taky to vracej zpátky i v případě spadnutí tvé aplikace (výjimky: zachycení někde nahoře, vrácení zpátky a poslání výjimky dál; crash: atexit() apod.). |
|
Návrat nahoru |
|
 |
|