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