Télécharger la version PDF
Transcription
Télécharger la version PDF
U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E Gestion des périphériques Leçon N°3 : curseur et picking Année universitaire 2004-2005 Pascal Mignot [email protected] Curseur de la souris U N IVERSIT É D E R EIMS Généralité C H AMP AGN E-A RD EN N E Par défaut, en mode fenêtré: niveau de coopération non exclusif le curseur est celui de Windows. exclusif pas de curseur. en mode plein écran: pas de curseur. Redéfinition du curseur: – mode non exclusif: fonctions spécifiques à utiliser. – mode exclusif: à gérer à la main. Curseur de la souris U N IVERSIT É D E R EIMS mode non exclusif : principe C H AMP AGN E-A RD EN N E La mise en place requière un ensemble d’étape: 1. Préparation: créer un curseur (texture) au format DDS (RGBA). 2. Utilisation: a) Initialisation: – charger la texture. – fixer les propriétés du curseur. b) Rendu: rien. c) Boucle de traitement des messages: réagir à l’événement WM_SETCURSOR (message envoyé à l’application quand elle a l’opportunité de changer son curseur). Remarque: le changement de curseur est automatiquement assuré lorsqu’il passe dans l’application. Curseur de la souris U N IVERSIT É D E R EIMS mode non exclusif : création du curseur C H AMP AGN E-A RD EN N E 1. Utiliser le DirectX Texture Tool (DXTool.exe): File/New Texture pour créer la texture avec les attributs suivants: texture standard taille désirée 1 niveau de MipMap format A8R8G8B8 Save : sauvegarder la texture au format DDS. 2. Utiliser un éditeur d’image supportant le format DDS pour dessiner le curseur. • • Dessiner le curseur sur les plans RGB. Utiliser le plan Alpha pour régler la transparence des pixels du curseur (rappel: 0 = transparent, 1 = opaque). Exemple: Photoshop avec le plug-in proposé par Nvidia pour lire et écrire ce format. Curseur de la souris U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E mode non exclusif : utilisation du curseur Deux méthodes du Device permettent de gérer le curseur: SetCursorProperties(x,y,pSurf) fixe les propriétés du curseur avec: x,y : position du hotspot du curseur relativement au coin supérieur droit de la texture. pSurf : pointeur sur l’interface de la surface contenant le curseur. SetCursorPosition(x,y,flag) change la position du curseur. x,y : position du curseur (coordonnées globales). On utilisera MapWindowPoints pour passer des coordonnées locales (celles souhaitées) au coordonnées globales (celles acceptées par la fonction). flag: 0 ou D3DCURSOR_IMMEDIATE_UPDATE pour mettre à jour le curseur à la fréquence du taux de rafraîchissement. ShowCursor(bool) bool : vrai pour afficher le curseur, false pour l’occulter. Curseur de la souris U N IVERSIT É D E R EIMS mode non exclusif : utilisation du curseur (exemple) C H AMP AGN E-A RD EN N E LPDIRECT3DSURFACE9 pCUR = NULL; // surface contenant la surface HRESULT InitCursor(void) { // Chargement et activation du curseur HRESULT s; LPDIRECT3DTEXTURE9 pTEX = NULL; // texture intermediaire D3DSURFACE_DESC dsc; // lecture de la texture contenant le pointeur de la souris s = D3DXCreateTextureFromFile( pDEV, "pointeur_cross.dds", &pTEX); if FAILED(s) { MessageBox(NULL, "Could not find pointeur.dds", "curseur.exe", MB_OK); return E_FAIL; } // recupere un pointeur sur la surface pTEX->GetSurfaceLevel(0,&pCUR); SAFE_RELEASE(pTEX); // pointeur sur la texture plus necessaire // fixe comme point chaud du pointeur le centre de celui-ci pCUR->GetDesc(&dsc); pDEV->SetCursorProperties(dsc.Width/2,dsc.Height/2,pCUR); return S_OK; } // (lsx,lsy) = taille de la fenêtre, // repositionne le curseur au centre de la fenêtre POINT WinPos={Isx/2,Isy/2}; MapWindowPoints(hWnd,NULL,&WinPos,1); pDEV->SetCursorPosition(WinPos.x,WinPos.y,0); // penser à libérer la surface au moment de la // libération des surfaces pCUR->Release(); // dans la boucle de traitement des messages … case WM_SETCURSOR: SetCursor( NULL ); // Turn off window cursor. pDEV->ShowCursor( TRUE ); return TRUE; break; … Curseur de la souris U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E mode exclusif : principe Dans ce cas, le curseur de la souris n’est plus gérée par Windows. Il faut le gérer à la main: 1. créer la géométrie (et éventuellement la texture) nécessaire. 2. récupérer les informations en provenance de la souris (Messages Windows ou DirectInput). 3. dans la fonction de rendu, effectuer le rendu du curseur Remarques: • utiliser les coordonnées non transformées XYZRHW (coordonnées directes sur l’écran). • ne pas oublier d’activer le alpha pour les curseurs définis par une texture lors du rendu. Types de curseur: Défini par des points Défini par des lignes Défini par une texture RGB A Géométrie (2 facettes) Picking U N IVERSIT É D E R EIMS Principes C H AMP AGN E-A RD EN N E Picking = utiliser le curseur pour désigner un objet particulier apparaissant dans la fenêtre. Problème posé par le picking: • on connaît les coordonnées du pixel sur lequel on a cliqué. • on recherche l’objet de la scène qui est dessiné dans ce pixel (i.e. le plus proche de l’observateur). Solution: utiliser le lancé de rayon. • construire un rayon (un point d’origine et un vecteur de direction) partant de l’observateur et passant par le centre du pixel considéré. • calculer les intersections entre le rayon et les objets de la scène. • objet vu = celui dont son intersection avec le rayon et la plus proche de l’observateur. Pixel sur lequel on a cliqué Observateur Ecran Picking U N IVERSIT É D E R EIMS Chaîne des transformations C H AMP AGN E-A RD EN N E Dans la chaîne de traitement ci-dessous, il apparaît clairement que le problème provient de l’absence d’inversibilité de la transformation projective (perte du z). Les calculs d’intersection se mènent généralement: • soit dans le repère local en général, le cas pour les objets facettisés en mouvement. • soit dans le repère global en général, le cas pour l’environnement (décors) ou les boites englobantes. Dans tous les cas, il faut que l’objet et le rayon soient tous les deux dans le même repère. Repère de l'écran (en pixel) Repère de l'écran normalisé Calibration Ecran Repère observateur Projection Transform View Transform Repère Global Repère local de l'objet World Transform Picking U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E Construction du rayon dans le repère de l’observateur Se rappeler que dans le repère de l’observateur (après mise-en-perspective), on est dans le cube [-1;+1 ] x [-1;+1] x [0;1]. En partant de la position (Cx,Cy) sur lequel on a cliqué sur la fenêtre de résolution Sx x Sy, Px = 2.Cx/Sx - 1 Py = 2.Cy/Sy – 1 Les coordonnées du point (Px,Py) obtenu est dans [-1;+1] x [-1;+1]. La matrice de projection MProj générée par D3DXMatrixPerspectiveFovLH contient une mise à l’échelle des axes (aspects ratio) + une inversion de l’axe. Par conséquent, le point associé sur le plan de projection a pour coordonnées: Px’ = + Px / MProj[0][0] Py’ = - Py / MProj[1][1] Dans le repère de l’observateur, Origine du rayon : Ω = [0,0,0] Direction du rayon : V = [ Px’ , Py’, +1 ] Rappel: Matrice de projection Rx 0 0 0 0 Ry 0 0 0 0 zf/(zf-zn) 1 0 0 -zn*zf/(zf-zn) 0 Picking U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E Calcul d’intersections 1. Déterminer dans quel repère le calcul est fait: objet dans le repère global : M = MView il y a une matrice M pour toute la scène. objet dans le repère local : M = MWorld x MView (notation DX) il y a une matrice M par objet. M-1= 2. Passer le rayon dans le repère approprié: A 0 B 1 Si on note M-1 la matrice inverse de M, en utilisant les notations ci-contre, on a alors: Ω’ = Ω.A + B = B V’ = V.A 3. Calculer l’intersection entre le rayon et l’objet: on note P0 le point d’intersection et d la distance de l’origine du rayon à l’objet. s’il n’y a pas d’intersection, l’objet n’est pas visible dans ce pixel. 4. Trouver l’objet le plus proche de l’observateur: • dans le repère global: pas de problème, l’objet le plus proche est celui qui a la plus petite distance à l’observateur. • dans le repère local: deux cas: • si M est une transformation rigide: comme pour le repère global. • sinon: la distance entre deux points dans le repère local n’est pas la même que dans le repère global, et distance dans le repère global = distance(Ω , P0.M) Picking U N IVERSIT É D E R EIMS Calcul d’intersections en pratique C H AMP AGN E-A RD EN N E On note: ROri, RDir : origine et direction du rayon (D3DXVECTOR3). Intersection rayon-facette ou rayon-mesh paramètres communs au retour des fonctions: u,v [out] : position de l’intersection sur la facette en coordonnées barycentriques = P0 + U(P1 – P0) + V(P2 – P0) d [out] : distance. • calcul d’intersection rayon-facette BOOL D3DXIntersectTri(&P0, &P1, &P2 ,&ROri,&RDir, &u, &v, &d) P0, P1, P2 : sommets de la facette. retourne TRUE s’il y a intersection, FALSE sinon. • calcul de l’intersection entre un rayon et un ensemble de facettes. HRESULT D3DXIntersect(pMSH, &ROri, &RDir, &IsHit, &iFace, &u, &v, &d, &AllHits, &nHits) pMSH : mesh avec lequel il faut calculer l’intersection. IsHit : [out] TRUE s’il y a une intersection, FALSE sinon. iFace : [out] indice de la face intersectée. nHits : [in] NULL ou [out] nombre total de facettes intersectées. AllHits : [in] NULL ou [out] pointeur sur un tableau de { iFace, u, v, d } (D3DXINTERSECTINFO) de toutes les facettes intersectées. voir aussi D3DXIntersectSubset (idem mais sur les subsets du mesh). Intersection rayon-sphère BOOL D3DXSphereBoundProbe(&c,r,&ROri,&RDir) c,r : centre et rayon de la sphère. retourne TRUE s’il y a intersection, FALSE sinon. Algorithmes utilisés: cf le cours de synthèse (voir aussi ce cours pour les autres cas d’intersection). Picking U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E // nObj = nombre d’objets struct Object { D3DXVECTOR3 P; float s; D3DXMATRIX MWorld; }; Exemple: picking sur des objets définis par leur bounding box // centre de la sphère // rayon de la sphère // transf. locale void PickSphere(POINT Pos, bool &Hit, UINT &HitObj) { float dmin = FLT_MAX, d; Hit = false; for(UINT i=0;i<nObj;i++) { // rayon D3DXVECTOR3 Rori, Rdir; // Transformation inversion D3DXMATRIXA16 m; m = ObjLst[i].MWorld * MView; D3DXMatrixInverse(&m, NULL, &m); // direction et origine du rayon ComputeRay(Pos, MProj, Minv, Rori, Rdir); // calcul d'intersection // repere local: l'objet est une sphere unite D3DXVECTOR3 Pview=D3DXVECTOR3(0.f,0.f,0.f); BOOL H = D3DXSphereBoundProbe(&Pview,1.f, &ROri,&RDir); if (H) { D3DXVECTOR3 Vs; Hit=true; D3DXVec3Subtract(&Vs,&ObjLst[i].P,&Peye); d = D3DXVec3Length(&Vs); if (d<dmin) { HitObj=i; dmin = d; } } } // Rsx, Rsy = taille de la render target. void ComputeRay( POINT &Pos, // position du clic D3DXMATRIXA16 &Proj, // mat. projection D3DXMATRIXA16 &m // matrice inverse D3DXVECTOR3 &ROri, D3DXVECTOR3 &RDir; ) { // passage repere de l’observateur D3DXVECTOR3 v; v.x = +( Pos.x * 2.f / Rsx - 1 )/ Proj._11; v.y = -( Pos.y * 2.f / Rsy - 1 )/ Proj._22; v.z = 1.f; // direction et origine du rayon RDir.x = v.x*m._11 + v.y*m._21 + v.z*m._31; RDir.y = v.x*m._12 + v.y*m._22 + v.z*m._32; RDir.z = v.x*m._13 + v.y*m._23 + v.z*m._33; ROri.x = m._41; ROri.y = m._42; ROri.z = m._43; } case WM_LBUTTONDOWN: { POINT Pos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; PickSphere(Pos); } break; Picking U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E Exemple: picking d’une facette sur un mesh void Render( void ) { // Fonction de rendu ctime = timeGetTime(); pDEV->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, white, 1.0f, 0 ); pDEV->BeginScene(); // tranformation float biais; SetTransform(); biais=1e-3f; pDEV->SetRenderState(D3DRS_DEPTHBIAS, *((DWORD*)&biais)); pDEV->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); pDEV->SetMaterial(&mat[0]); pMSH->DrawSubset(0); pDEV->SetMaterial(&mat[1]); pMSH->DrawSubset(1); biais=0.f; pDEV->SetRenderState(D3DRS_DEPTHBIAS, *((DWORD*)&biais)); pDEV->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME); pDEV->SetMaterial(&mat[2]); pMSH->DrawSubset(0); // affichage du FPS drawFPS(0,black); drawText(1,"F1: Show/Hide mesh",black); pDEV->EndScene(); pDEV->Present( NULL, NULL, NULL, NULL ); } void PickFacette(POINT Pos) { D3DXVECTOR3 ROri, RDir; // Transformation inversion D3DXMATRIXA16 m; m = MWorld * MView; D3DXMatrixInverse(&m, NULL, &m); // direction et origine du rayon ComputeRay(Pos, MProj, Minv, Rori, Rdir); // calcul d'intersection BOOL bHit; DWORD iFace; FLOAT u, v, d; D3DXIntersect(pMSH, &ROri, &RDir, &bHit, &iFace, &u, &v, &d, NULL, NULL); // changement de l'attribut de la facette sur // laquelle on a cliqué if (bHit) { DWORD *Ind; pMSH->LockAttributeBuffer(0,&Ind); Ind[dwFace] = 1 - Ind[dwFace]; pMSH->UnlockAttributeBuffer(); } } Picking U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E Calcul d’intersections : optimisations Limiter au maximum ce calcul d’intersection en utilisant les techniques classiques d’optimisation des intersections: • utiliser des boundings box avant de « détailler » le contenu de l’objet (facette par facette). on utilise un objet englobant avec lequel le calcul d’intersection avec un rayon est simple (exemple: sphère, slab, ... ). • utiliser les partitions d’espaces pour limiter les calculs d’intersection aux portions que traverse le rayon. – L’espace est divisée en régions. – Pour chaque région, on connaît l’ensemble des objets contenus dans cette région. – Lors du calcul d’intersection, on calcule l’intersection avec les objets contenu dans les régions traversées par le rayon (dans l’ordre dans lequel elles sont traversées). Exemple de partition: BSP tree. Lectures et codes U N IVERSIT É D E R EIMS C H AMP AGN E-A RD EN N E Lectures: Cours de synthèse: • géométrie. • lancé de rayons (section sur le calcul d’intersection). Codes: exemples MS des SDKs précédents (cf site) site: Curseur de la souris en mode non exclusif Picking Objets Picking Facettes