.[ ČeskéHry.cz ].
Pohyb myši a WinAPI
Jdi na stránku 1, 2  Další
 
odeslat nové téma   Odpovědět na téma    Obsah fóra České-Hry.cz -> C / C++
Zobrazit předchozí téma :: Zobrazit následující téma  
Autor Zpráva
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 4. březen 2015, 16:19:55    Předmět: Pohyb myši a WinAPI Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
mar



Založen: 16. 06. 2012
Příspěvky: 602

PříspěvekZaslal: 4. březen 2015, 16:52:24    Předmět: Re: Pohyb myši a WinAPI Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 4. březen 2015, 17:04:24    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
mar



Založen: 16. 06. 2012
Příspěvky: 602

PříspěvekZaslal: 4. březen 2015, 21:05:46    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 4. březen 2015, 22:36:33    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
mar



Založen: 16. 06. 2012
Příspěvky: 602

PříspěvekZaslal: 4. březen 2015, 22:53:12    Předmět: Odpovědět s citátem

Č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ý Smile.
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
Zobrazit informace o autorovi Odeslat soukromou zprávu
Tringi



Založen: 28. 07. 2007
Příspěvky: 289

PříspěvekZaslal: 4. březen 2015, 23:55:55    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 5. březen 2015, 08:59:43    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
mar



Založen: 16. 06. 2012
Příspěvky: 602

PříspěvekZaslal: 5. březen 2015, 10:30:34    Předmět: Odpovědět s citátem

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 Smile
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
Zobrazit informace o autorovi Odeslat soukromou zprávu
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 5. březen 2015, 11:33:57    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
mar



Založen: 16. 06. 2012
Příspěvky: 602

PříspěvekZaslal: 5. březen 2015, 13:25:18    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 5. březen 2015, 13:53:11    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
nou



Založen: 28. 07. 2007
Příspěvky: 1047

PříspěvekZaslal: 5. březen 2015, 21:35:21    Předmět: Odpovědět s citátem

tipol by som ze treba vypnut akceleraciu mysi
_________________
Najjednoduchšie chyby sa najtažšie hľadajú.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
perry



Založen: 28. 07. 2009
Příspěvky: 879

PříspěvekZaslal: 6. březen 2015, 08:06:46    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu Zobrazit autorovi WWW stránky
Ladis



Založen: 18. 09. 2007
Příspěvky: 1533
Bydliště: u Prahy

PříspěvekZaslal: 6. březen 2015, 11:05:45    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
Zobrazit příspěvky z předchozích:   
odeslat nové téma   Odpovědět na téma    Obsah fóra České-Hry.cz -> C / C++ Časy uváděny v GMT + 1 hodina
Jdi na stránku 1, 2  Další
Strana 1 z 2

 
Přejdi na:  
Nemůžete odesílat nové téma do tohoto fóra
Nemůžete odpovídat na témata v tomto fóru
Nemůžete upravovat své příspěvky v tomto fóru
Nemůžete mazat své příspěvky v tomto fóru
Nemůžete hlasovat v tomto fóru


Powered by phpBB © 2001, 2005 phpBB Group


Vzhled udelal powermac
Styl "vykraden" z phpBB stylu MonkiDream - upraveno by rezna