|
|
|
cignoni@iei.pi.cnr.it |
|
http://vcg.iei.pi.cnr.it/~cignoni |
|
|
|
|
|
|
|
Come può un utente specificare una rotazione
tramite un interfaccia? |
|
Due modalità: |
|
Diretta: specifica valori numerici esatti |
|
Interattiva: tramite movimenti del mouse |
|
Come rappresento una rotazione? |
|
Euler Angle |
|
Axis/angle |
|
Quaternions |
|
|
|
|
|
|
|
Una rotazione viene espressa come una serie di
tre rotazioni sui tre assi. |
|
Deriva dal modo con cui si descrive
l’orientamento di un aereo |
|
Yaw |
|
Pitch |
|
Roll |
|
Intuitivo per piccoli valori di pitch e roll |
|
|
|
|
|
|
|
Problema ordine rotazione |
|
Il risultato dipende dall’ordine in cui faccio
le tre rotazioni |
|
Problema Gimbal Lock |
|
In alcune situazioni le rotazioni fatte su un
asse possono coprire quelle su un altro asse |
|
Se il pitch è a 90
gradi yaw e roll si
possono annullare
a vicenda. |
|
|
|
|
|
|
|
|
|
|
|
Capita ad esempio quando cerco di far
specificare gli euler angle interattivamente all’utente: |
|
Up/down:
rot asse x |
|
Left/right:
rot asse y |
|
Pgup/pgdn: rot asse z |
|
Si incarta. |
|
|
|
|
|
|
Approccio OpenGL |
|
Si specifica un’asse di rotazione e un angolo di
rotazione |
|
Molto generico |
|
Poco intuitivo |
|
Qual’e l’asse di rotazione per girare
la testa in modo da guardare in
basso a destra? |
|
|
|
|
Cos’è un quaternione? |
|
Un estensione dei numeri complessi, |
|
|
|
|
|
Spesso rappresentato come una coppia
scalare-vettore: |
|
|
|
|
|
|
Magnitudine |
|
|
|
|
|
Normalizzazione a quaternione unitario |
|
|
|
|
|
|
|
Dati due quaternioni |
|
|
|
|
|
|
|
Si definisce |
|
|
|
|
|
|
|
|
|
Identità |
|
somma |
|
prodotto |
|
|
|
|
|
|
Ogni quaternione unitario corrisponde ad una
rotazione in 3d |
|
La moltiplicazione tra due quaternioni
corrisponde alla composizione delle due rotazioni |
|
|
|
|
Da quaternione a matrice |
|
|
|
|
|
|
|
|
|
Da quaternione ad axis/angle |
|
|
|
|
|
|
Da axis angle a quaternioni |
|
|
|
|
|
|
|
|
|
Da euler angle a quaternion |
|
|
|
|
|
|
|
Euler angle è molto intuitivo per piccole
rotazioni: se ruoto di angoli piccoli quello che ottengo è esattamente
quello che mi aspetto |
|
Soluzione: |
|
Tenere la rotazione come un quaternione |
|
Ad ogni pressione di tasto generare un
quaternione corrispondente al piccolo euler angle |
|
Ad es. se premo left genero un quaternione |
|
|
|
Comporre il risultato con moltiplicazione tra
quaternioni e tenere il risultato come base; |
|
|
|
|
|
Come si mappa il movimento del mouse in una
rotazione? |
|
Si immagina una sfera circoscritta all’oggetto
con cui si vuole interagire |
|
Ogni drag del mouse definisce due punti p1 e
p2(inizio e fine del drag) sulla sfera |
|
Si considera la rotazione che descrive l’arco di
cerchio sulla superficie sferica delimitato da p1 e p2 |
|
|
|
|
|
|
La rotazione così calcolata viene trasformata in
un quaternione e composta con la trasf corrente |
|
Se una volta rilasciato il mouse, si continua
comporre con l’ultimo quaternione calcolato, si ottiene l’effetto di
spinning. |
|
|
|
|
|
|
|
|
|
|
|
Classe che implementa una trackball |
|
Di solito è un oggetto della glview |
|
Interfaccia fondamentale nella glview |
|
Si deve gestire Mousedown/mouseup/Mousemove |
|
Avere un membro dove tenere la mat di rotazione
corrente |
|
Comunicare Resize alla trackball |
|
|
|
|
|
|
class CMBGLView : public CView |
|
{ |
|
… |
|
// Gestione trackball |
|
CITrackball
m_tb; |
|
Matrix44f
m_matRot; |
|
bool
m_bCaptured; |
|
… |
|
}; |
|
|
|
void CMBGLView::OnInitialUpdate() |
|
{ |
|
… |
|
CRect rc; |
|
GetClientRect(&rc); |
|
m_tb.Init(rc.right,rc.bottom); |
|
m_matRot.SetIdentity(); |
|
} |
|
void CMBGLView::OnSize(UINT nType, int cx, int
cy) |
|
{ |
|
… |
|
m_tb.Resize( cx,cy ); |
|
… |
|
} |
|
|
|
|
|
|
|
|
void CMBGLView::OnLButtonDown(UINT nFlags,
CPoint point) |
|
{ |
|
if(!m_bCaptured) { |
|
m_tb.MouseDown( point.x, point.y, 0 ); |
|
SetCapture(); |
|
m_bCaptured = TRUE; |
|
} |
|
CView::OnLButtonDown(nFlags, point); |
|
} |
|
void CMBGLView::OnLButtonUp(UINT nFlags, CPoint
point) |
|
{ |
|
if(m_bCaptured) { |
|
m_tb.MouseUp(point.x,point.y); |
|
ReleaseCapture(); |
|
m_bCaptured = FALSE; |
|
} |
|
CView::OnLButtonUp(nFlags, point); |
|
} |
|
void CMBGLView::OnMouseMove(UINT nFlags, CPoint
point) |
|
{ |
|
if(m_bCaptured) { |
|
m_tb.CalcRotMatrix( m_matRot, point.x,
point.y ); |
|
Invalidate(); |
|
} |
|
CView::OnMouseMove(nFlags, point); |
|
} |
|
|
|
|
|
|
void CMBGLView::OnDraw(CDC* pDC) |
|
{ |
|
… |
|
// World To Camera Transformation |
|
gluLookAt(0,0,10,0,0,0,0,1,0); |
|
|
|
// Apply the trackball |
|
glMultMatrix(m_matRot); |
|
|
|
glRotatef( -90,0,1,0); |
|
glRotatef( 90,1,0,0); |
|
|
|
// moto di precessione: una rotazione il cui
asse ruota intorno all'asse z |
|
glRotated(
10,cos(ToRad(-45+CurAngleDeg*.5)),sin(ToRad(-45+CurAngleDeg*.5)),0); |
|
… |
|
if(pd->m.Empty()) pd->m.Generate(); |
|
pd->m.Ring.Draw<true,true>(); |
|
SwapBuffers(m_pDC->GetSafeHdc()); |
|
} |
|
|
|
|
|
|
Resa piu’ generale |
|
Non piu width/ height ma innerradius e
filletratio |
|
La Generate è ora divisa in due parti: |
|
Prima si genera una sezione |
|
Poi si estrude |
|
Gestione CreaseAngle tramite duplicazione
vertici |
|
Introduzione Matrix44 |
|
Gestione normali corretta |
|
Generazione coordinate texture |
|
Draw templated |
|
|
|
|
|
“the art of programming programs that read,
transform or write other programs” |
|
Risultati |
|
Compile-time computations |
|
Compile-time control structure |
|
Performance |
|
Metaprogramming è una gran disciplina, questo è
sono un minimo assaggio. |
|
|
|
|
Normalmente il passaggio di parametri è fatto
attraverso variabili/oggetti |
|
double square(x) {return x*x;} |
|
Valori specifici vengono passati a run time
quando la funzione viene invocata |
|
…square(-3)… |
|
In c++ si puo’ fornire placeholder per risolvere
tutto a runtime |
|
template<type t> inline square(T x) {
return x*x;} |
|
|
|
|
Ma si può templatare anche in base a valori |
|
template <unsigned n> |
|
inline double pow(double x) { |
|
double ans=1.0; |
|
for(unsigned k=0;k<n;++k) |
|
ans*=k; |
|
return ans; |
|
} |
|
Oppure ancora meglio |
|
template <unsigned n> |
|
inline double pow(double x) { |
|
return pow<n%2>(x) * pow<n/2> (x*x); |
|
} |
|
template<> pow<1u>(double x) {return
x;} |
|
|
|
|
In questo modo si puo far fare al compilatore
parte del processing |
|
template <bool NormFlag, bool TexFlag>
Draw() |
|
{ |
|
glBegin(GL_TRIANGLES); |
|
vector<CIFace>::const_iterator fi; |
|
for(fi=face.begin();fi!=face.end();++fi) |
|
{ |
|
if(NormFlag) glNormal((*fi).v[0]->n); |
|
if(TexFlag)
glTexCoord2f((*fi).v[0]->s,(*fi).v[0]->t); |
|
glVertex((*fi).v[0]->p); |
|
… |
|
if(NormFlag)
glNormal((*fi).v[2]->n); |
|
if(TexFlag) glTexCoord2f((*fi).v[2]->s,(*fi).v[2]->t); |
|
glVertex((*fi).v[2]->p); |
|
} |
|
glEnd(); |
|
} |
|
|
|
|
|
|
Stiamo ancora sperimentando |
|
Mancano gli oggetti che si muovono sull’anello |
|
Dobbiamo decidere meglio le specifiche dell’app. |
|
L’animazione ora è gestita interamente dentro la
view. Non portabile… |
|
Butteremo via ancora varie volta quasi tutto… |
|