www.pudn.com > 3dterrain.zip > Camera.cpp
#include "camera.h"
void Camera::Normalise(XYZ *p)
{
float length;
length = (float) sqrt(p->x * p->x + p->y * p->y + p->z * p->z);
if (length != 0) {
p->x /= length;
p->y /= length;
p->z /= length;
} else {
p->x = 0;
p->y = 0;
p->z = 0;
}
}
void Camera::Normalise(float *x, float *y, float *z)
{
float length;
length = (float) sqrt( (*x)*(*x) + (*y)*(*y) + (*z)*(*z));
if (length != 0)
{
*x /= length; // *x = hodnota z adresy x, x - smernik, adresa
*y /= length;
*z /= length;
}
else
{
*x = 0;
*y = 0;
*z = 0;
}
}
void Camera::DerivateMatrix(void)
{
glLoadIdentity();
gluLookAt(vp.x, vp.y, vp.z, vd.x+vp.x, vd.y+vp.y, vd.z+vp.z, vu.x, vu.y, vu.z);
}
void Camera::d(void)
{
glLoadIdentity();
gluLookAt(vp.x, vp.y, vp.z, vd.x+vp.x, vd.y+vp.y, vd.z+vp.z, vu.x, vu.y, vu.z);
}
Camera::Camera(void)
{
Reset();
stack_pointer = NULL;
SetCursorPos(screen_x/2,screen_y/2); // nastavy mys do stredu obrazovky , je mozne to vypnut
zmena = 1;
}
Camera::~Camera(void)
{
if(stack_pointer==NULL)return;
{
position *pom; // pomocny smernik
int i=0,j;
pom = stack_pointer;
while(pom->next!=NULL) {pom = pom->next; i++;} // po skonceni v i bude pocet alokovanych struktur - 1
while(i>0) // v kazdom cykle uvolnime poslednu cast spojkoveho zoznamu
{
i--;
pom = stack_pointer;
for(j=i;j!=0;j--)pom = pom->next;
delete pom->next;
}
delete stack_pointer;
}
}
void Camera::Reset(void)
{
vp.x=0.0f;vp.y=0.0f;vp.z=8.0f;
vd.x=0;vd.y=0;vd.z=-1;
vu.x=0;vu.y=1;vu.z=0;
vr.x=1;vr.y=0;vr.z=0;
}
void Camera::RelativeTranslate(float x,float y,float z)
{
vp.x += x;
vp.y += y;
vp.z += z;
}
void Camera::RelativeRotate(float uhol,float x,float y,float z)
{
// rotuje suradnicovu sustavu podla vlastnych osi
/* v - normalizovany vektor v = (x, y, z) vT - transponovany vektor v
( 0 -z y) (1 0 0) (x) (xx xy xz)
S = ( z 0 -x) I = (0 1 0) vT =(y) v*vT = (yx yy yz)
(-y x 0) (0 0 1) (z) (zx zy zz)
A = v*vT + cos(uhol)*(I-v*vT) + sin(uhol)*S */
// Najprv vypocitame rotacnu maticu
float a[9];
float c,s;
Normalise(&x, &y, &z);
c = (float) cos(uhol*PI180);
s = (float) sin(uhol*PI180);
a[0] = x*x + c*(1-x*x); // ( a0 3 a6 )
a[1] = x*y + c*(0-x*y) + s*z; // A = ( a1 a4 a7 )
a[2] = x*z + c*(0-x*z) - s*y; // ( a2 a5 a8 )
a[3] = y*x + c*(0-y*x) - s*z;
a[4] = y*y + c*(1-y*y);
a[5] = y*z + c*(0-y*z) + s*x;
a[6] = z*x + c*(0-z*x) + s*y;
a[7] = z*y + c*(0-z*y) - s*x;
a[8] = z*z + c*(1-z*z);
// Rotovanie jednotlivych vektorov pomocou rotacnej matice A
// Kedze pohybujem kamerov musime nou hybat opacne ako sa bude tocit scena
// preto nebudem nasobit (x,y,z)T * A, ale naopak A * (x,y,z)T
vp.x = a[0]*vp.x + a[1]*vp.y + a[2]*vp.z;
vp.y = a[3]*vp.x + a[4]*vp.y + a[5]*vp.z;
vp.z = a[6]*vp.x + a[7]*vp.y + a[8]*vp.z;
vd.x = a[0]*vd.x + a[1]*vd.y + a[2]*vd.z;
vd.y = a[3]*vd.x + a[4]*vd.y + a[5]*vd.z;
vd.z = a[6]*vd.x + a[7]*vd.y + a[8]*vd.z;
Normalise(&vd);
vu.x = a[0]*vu.x + a[1]*vu.y + a[2]*vu.z;
vu.y = a[3]*vu.x + a[4]*vu.y + a[5]*vu.z;
vu.z = a[6]*vu.x + a[7]*vu.y + a[8]*vu.z;
Normalise(&vu);
CROSSPROD(vd,vu,vr);
Normalise(&vr);
}
void Camera::RelativeRotatePoint(float uhol,float x,float y,float z, float px, float py, float pz)
{
vp.x -= px; // aby sme rotovali okolo bodu (px,py,pz)
vp.y -= py; // najprv polohu posunieme do opacneho bodu
vp.z -= pz;
RelativeRotate(uhol, x, y, z);
vp.x += px; // po rotacii polohu vratime naspat
vp.y += py;
vp.z += pz;
}
void Camera::AbsoluteRotate(float x, float y, float z)
{
float dd;
// velkost vp
dd = (float) sqrt(vp.x*vp.x + vp.y*vp.y + vp.z*vp.z);
vp.x += dd * x * vu.x + dd * y * vr.x;
vp.y += dd * x * vu.y + dd * y * vr.y;
vp.z += dd * x * vu.z + dd * y * vr.z;
Normalise(&vp);
vp.x *= dd;
vp.y *= dd;
vp.z *= dd;
vr.x = vp.x + vr.x; // vypocet noveho praveho vektora
vr.y = vp.y + vr.y;
vr.z = vp.z + vr.z;
Normalise(&vr);
vd.x = - vp.x; // pozerame na bod 0,0,0
vd.y = - vp.y;
vd.z = - vp.z;
Normalise(&vd);
CROSSPROD(vr,vd,vu); // vypocet noveho up vektora
Normalise(&vu);
CROSSPROD(vd,vu,vr);
Normalise(&vr);
// otocenie kamery okolo osi
if (z != 0) {
vu.x += z * vr.x;
vu.y += z * vr.y;
vu.z += z * vr.z;
Normalise(&vu);
CROSSPROD(vd,vu,vr);
Normalise(&vr);
}
}
void Camera::AbsoluteRotatePoint(float x, float y, float z, float px, float py, float pz)
{
XYZ pr,d;
float dd;
pr.x = px;
pr.y = py;
pr.z = pz;
// vypocet vzdialenosti k bodu okolo ktoreho sa bude rotovat
d.x = vp.x - pr.x;
d.y = vp.y - pr.y;
d.z = vp.z - pr.z;
dd = (float) sqrt(d.x*d.x + d.y*d.y + d.z*d.z);
// vypocet noveho bodu z ktoreho sa pozerame
vp.x += dd * x * vu.x + dd * y * vr.x - pr.x;
vp.y += dd * x * vu.y + dd * y * vr.y - pr.y;
vp.z += dd * x * vu.z + dd * y * vr.z - pr.z;
Normalise(&vp);
vp.x = pr.x + dd * vp.x;
vp.y = pr.y + dd * vp.y;
vp.z = pr.z + dd * vp.z;
// vypocet smeroveho vektora
vd.x = pr.x - vp.x; // pozerame sa na bod okolo ktoreha sa rotuje
vd.y = pr.y - vp.y;
vd.z = pr.z - vp.z;
Normalise(&vd);
// vypocet noveho praveho vektora
vr.x = vp.x - pr.x + vr.x;
vr.y = vp.y - pr.y + vr.y;
vr.z = vp.z - pr.z + vr.z;
Normalise(&vr);
vr.x = pr.x + dd * vr.x - vp.x;
vr.y = pr.y + dd * vr.y - vp.y;
vr.z = pr.z + dd * vr.z - vp.z;
// vypocet up vektora
CROSSPROD(vr,vd,vu); // vektorovy sucin
Normalise(&vu);
CROSSPROD(vd,vu,vr);
Normalise(&vr);
// otocenie kamery okolo osi
if (z != 0) {
vu.x += z * vr.x;
vu.y += z * vr.y;
vu.z += z * vr.z;
Normalise(&vu);
CROSSPROD(vd,vu,vr);
Normalise(&vr);
}
}
void Camera::AbsoluteRotateDistance(float x, float y, float z, float distance)
{
AbsoluteRotatePoint(x, y, z, distance*vd.x + vp.x, distance*vd.y + vp.y, distance*vd.z + vp.z);
}
void Camera::FlyRotate(float x, float y, float z)
{
// FlyRotate - rotacia, ktora zabezpecuje volny pohyb (Fly - lietanie), surad. sustava (myslim tym osi x,y,z vykreslene po tejto transformacii)
// sa rotuje tak, ze sa otaca vzdy oko osi horizontalne (x) a vertikalnej (y)
// v pripade ze mame vp = {0,0,5}, vd = {0,0,-1} a vp = {0,1,0} tak pre
// x > 0 sa suradnicova sustava posunie dolava t.j. kamera sa otoci doprava
// x < 0 doprava dolava
// y > 0 sa suradnicova sustava posunie dole t.j. kamera sa otoci hore
// y < 0 hore dole
// z > 0 sa suradnicova sustava otoci proti smeru hodinovych ruciciek
// z < 0 v smere
/* zmenime poluhu smeroveho (direction) vektora tak,
ze k nemu pripocitame cast vektora kolmeho na nho v danom smere */
vd.x += x*vr.x + y*vu.x; // vr - vektor smerujuci vpravo - v smere osi x
vd.y += x*vr.y + y*vu.y; // vu - (up) hore smerujuci vektor - v smere osi y
vd.z += x*vr.z + y*vu.z; //
Normalise(&vd); // normalizacia vektora, aby (velkost) ||vd||=1
CROSSPROD(vr,vd,vu); // vypocitame novy vu vektor a to pomocu vekoroveho sucinu
Normalise(&vu); // vektora smerom vpravo a dopredu
CROSSPROD(vd,vu,vr);
Normalise(&vr);
if(z!=0) // v pripade ze chceme kameru rotovat v smere osi
{
vu.x += z*vr.x; // k vektoru smerujucemu hore pripocitame cast vektora
vu.y += z*vr.y; // smerujuceho vpravo, tym sa vektor vu otoci vpravo a
vu.z += z*vr.z; // suradnicovy system sa otoci proti smeru hodinovych ruciciek
Normalise(&vu); // normalizacia vektora
CROSSPROD(vd,vu,vr);
Normalise(&vr);
}
}
/*
void Camera::WalkRotate(float x, float y, float z)
{
// WalkRotate - rotacia, ktora sa vyuziva pri chodeni (walk) v bludisku
// pri tejto rotacii sa rotuje vzdy okolu osi y t.j. v pripade ze sa pozerame prave v smere
// osi y (hore, alebo dole) tak sa scena otaca okolo vlastnej osi (neposuva sa vpravo alebo vlavo)
// v pripade ze sa otacanim dole alebo hore vektory vd a vu velmi priblizia moze vypocitana
// transformacna matica byt nulova, co sposoby ze na obrazovke nic neuvidime, treba zabranit otacaniu ked su vd a vu velmi blizko
// v pripade ze mame vp = {0,0,5}, vd = {0,0,-1} a vp = {0,1,0} tak pre
// x > 0 sa suradnicova sustava posunie dolava t.j. kamera sa otoci doprava
// x < 0 doprava dolava
// y > 0 sa suradnicova sustava posunie dole t.j. kamera sa otoci hore
// y < 0 hore dole
// z > 0 sa suradnicova sustava otoci proti smeru hodinovych ruciciek
// z < 0 v smere
XYZ vr; // vr - vector right - vektor smerujuci v pravo
CROSSPROD(vd,vu,vr); // vektorovy sucin vd x vu = vr - vektor smerujuci vpravo
Normalise(&vr); // normalizacia vektora
// zmenime poluhu smeroveho (direction) vektora tak,
// ze k nemu pripocitame cast vektora kolmeho na nho v danom smere
vd.x += x*vr.x + y*vu.x; // vr - vektor smerujuci vpravo - v smere osi x
vd.y += x*vr.y + y*vu.y; // vu - (up) hore smerujuci vektor - v smere osi y
vd.z += x*vr.z + y*vu.z; //
Normalise(&vd); // normalizacia vektora, aby (velkost) ||vd||=1
if(z!=0) // v pripade ze chceme kameru rotovat v smere osi
{
vu.x += z*vr.x; // k vektoru smerujucemu hore pripocitame cast vektora
vu.y += z*vr.y; // smerujuceho vpravo, tym sa vektor vu otoci vpravo a
vu.z += z*vr.z; // suradnicovy system sa otoci proti smeru hodinovych ruciciek
Normalise(&vu); // normalizacia vektora
}
}*/
void Camera::WalkRotate(float x, float y, float z)
{
// WalkRotate - rotacia, ktora sa vyuziva pri chodeni (walk) v bludisku
// pri tejto rotacii sa rotuje vzdy okolu osi y t.j. v pripade ze sa pozerame prave v smere
// osi y (hore, alebo dole) tak sa scena otaca okolo vlastnej osi (neposuva sa vpravo alebo vlavo)
// v pripade ze sa otacanim dole alebo hore vektory vd a vu velmi priblizia moze vypocitana
// transformacna matica byt nulova, co sposoby ze na obrazovke nic neuvidime, treba zabranit otacaniu ked su vd a vu velmi blizko
// v pripade ze mame vp = {0,0,5}, vd = {0,0,-1} a vp = {0,1,0} tak pre
// x > 0 sa suradnicova sustava posunie dolava t.j. kamera sa otoci doprava
// x < 0 doprava dolava
// y > 0 sa suradnicova sustava posunie dole t.j. kamera sa otoci hore
// y < 0 hore dole
// z > 0 sa suradnicova sustava otoci proti smeru hodinovych ruciciek
// z < 0 v smere
float angle; // uhol
float vel0,vel1;
XYZ vd_,vr_,p;
vd_ = vd;
angle = (float) atan(-x); // uhol o ktory mame otocit
//angle = (float) asin(-x); // uhol o ktory mame otocit
// otocime okolo osi y, vpravo a vlavo vd
vd.x = vd_.z*(float)sin(angle) + vd_.x*(float)cos(angle);
// vd.y = vd_.y;
vd.z = vd_.z*(float)cos(angle) - vd_.x*(float)sin(angle);
Normalise(&vd); // normalizacia vektora, aby (velkost) ||vd||=1
vr_ = vr;
// otocime okolo osi y, vpravo a vlavo vr_
vr.x = vr_.z*(float)sin(angle) + vr_.x*(float)cos(angle);
// vr.y = vr_.y;
vr.z = vr_.z*(float)cos(angle) - vr_.x*(float)sin(angle);
Normalise(&vr); // normalizacia vektora, aby (velkost) ||vd||=1
CROSSPROD(vr,vd,vu); // vektorovy sucin vd x vu = vr - vektor smerujuci vpravo
Normalise(&vu); // normalizacia vektora
vd_ = vd;
p.x = y*vu.x; // vr - vektor smerujuci vpravo - v smere osi x
p.y = 0; // vu - (up) hore smerujuci vektor - v smere osi y
p.z = y*vu.z; //
vel0 = (float) sqrt(p.x * p.x + p.y * p.y + p.z * p.z); // o kolko sa otoci
p = vd;
p.y = 0;
vel1 = (float) sqrt(p.x * p.x + p.y * p.y + p.z * p.z); // kolko chyba k osi
vd.x += y*vu.x; // vr - vektor smerujuci vpravo - v smere osi x
vd.y += y*vu.y; // vu - (up) hore smerujuci vektor - v smere osi y
vd.z += y*vu.z; //
Normalise(&vd); // normalizacia vektora, aby (velkost) ||vd||=1
if(vel0>=vel1) // prekroci y
{
if(vd_.y>0 && y>0)
{
vd.x = 0;
vd.y = 1;
vd.z = 0;
}
if(vd_.y<0 && y<0) // smeruje k osi y
{
vd.x = 0;
vd.y =-1;
vd.z = 0;
}
}
if(vd_.x==0 && vd_.z==0 && (vd_.y==1 || vd_.y==-1) )
{
if(y>0 && vd_.y==1)
{
vd.x = 0;
vd.y = 1;
vd.z = 0;
}
else
if(y<0 && vd_.y==-1)
{
vd.x = 0;
vd.y =-1;
vd.z = 0;
}
}
CROSSPROD(vr,vd,vu); // vektorovy sucin vd x vu = vr - vektor smerujuci vpravo
Normalise(&vu); // normalizacia vektora
if(z!=0) // v pripade ze chceme kameru rotovat v smere osi
{
vu.x += z*vr_.x; // k vektoru smerujucemu hore pripocitame cast vektora
vu.y += z*vr_.y; // smerujuceho vpravo, tym sa vektor vu otoci vpravo a
vu.z += z*vr_.z; // suradnicovy system sa otoci proti smeru hodinovych ruciciek
Normalise(&vu); // normalizacia vektora
CROSSPROD(vd,vu,vr);
Normalise(&vr);
}
}
void Camera::Translate(float x,float y,float z)
{
// funkcia posunie suradnicovy sustavu tak, ze ak
// x > 0 posun do lava, y > 0 posun dole, z > 0 posun do obrazovky (v smere vd)
vp.x += x*vr.x + y*vu.x + z*vd.x;
vp.y += x*vr.y + y*vu.y + z*vd.y;
vp.z += x*vr.z + y*vu.z + z*vd.z;
}
void Camera::MoveWorld(float fps,Mode mod)
{
POINT MouseBod;
float mys_x,mys_y,step;
if(keys[VK_SHIFT])step=1.0f/fps;else step=5.0f/fps;
if(keys['R']){ Reset(); zmena = 1; keys['R']=0; }
if (keys[VK_UP] || keys[VK_LBUTTON]) Translate(0,0,step);
if (keys[VK_DOWN] || keys[VK_RBUTTON])Translate(0,0,-step);
if (keys[VK_HOME]) Translate(0,step,0);
if (keys[VK_END])Translate(0,-step,0);
if (keys[VK_INSERT]) Translate(step,0,0);
if (keys[VK_DELETE])Translate(-step,0,0);
GetCursorPos(&MouseBod);
SetCursorPos(screen_x/2,screen_y/2);
mys_x = ((float)MouseBod.x - (float)screen_x/2)/200.0f;
mys_y = ((float)MouseBod.y - (float)screen_y/2)/200.0f;
if (keys[VK_NEXT]) mys_y +=-0.3f * step; //Page Down
else if(keys[VK_PRIOR]) mys_y += 0.3f * step; //Page Up
if (keys[VK_RIGHT])mys_x += 0.3f * step;
else if(keys[VK_LEFT]) mys_x +=-0.3f * step;
if( keys[VK_UP] || keys[VK_LBUTTON] || keys[VK_DOWN] || keys[VK_RBUTTON] || \
keys[VK_HOME] || keys[VK_END] || keys[VK_INSERT] || keys[VK_DELETE] || \
mys_x!=0 || mys_y!=0 ) zmena = 1;
if(mod==Walk || mod==walk)WalkRotate( mys_x, -mys_y, 0);
else FlyRotate( mys_x, -mys_y, 0);
DerivateMatrix();
}
void Camera::Push(void)
{
if(stack_pointer==NULL)
{
stack_pointer = new position;
stack_pointer->vp = vp;
stack_pointer->vd = vd;
stack_pointer->vu = vu;
stack_pointer->next = NULL;
}
else
{
position *pom;
pom = stack_pointer;
while(pom->next!=NULL) pom = pom->next;
pom->next = new position;
if(pom->next!=NULL)
{
pom = pom->next;
pom->vp = vp;
pom->vd = vd;
pom->vu = vu;
pom->next = NULL;
}
}
}
void Camera::Pop(void)
{
if(stack_pointer==NULL)return;
{
position *pom;
int i=0;
pom = stack_pointer;
while(pom->next!=NULL) {pom = pom->next; i++;}
vp = pom->vp;
vd = pom->vd;
vu = pom->vu;
delete pom;
if(i==0)stack_pointer = NULL;
else
{
pom = stack_pointer;
i--;
while(i!=0){pom = pom->next; i--;}
pom->next = NULL;
}
}
}
void Camera::PrintVector(Font* font,int stlpec,int riadok) // vypise vektory na poziciu riadok, stlpec
{
char text[30];
font->glPrint(stlpec,riadok,"pozicia vp:",0);
gcvt( (double)vp.x, 2, text);
font->glPrint(stlpec+12,riadok, text, 0);
gcvt( (double)vp.y, 2, text);
font->glPrint(stlpec+22,riadok, text, 0);
gcvt( (double)vp.z, 2, text);
font->glPrint(stlpec+32,riadok, text, 0);
gcvt( (double)sqrt(vp.x*vp.x + vp.y*vp.y + vp.z*vp.z), 3, text);
font->glPrint(stlpec+39,riadok,"velkost:",0);
font->glPrint(stlpec+49,riadok,text,0);
font->glPrint(stlpec,riadok+1,"smerov. vd:",0);
gcvt( (double)vd.x, 2, text);
font->glPrint(stlpec+12,riadok+1, text, 0);
gcvt( (double)vd.y, 2, text);
font->glPrint(stlpec+22,riadok+1, text, 0);
gcvt( (double)vd.z, 2, text);
font->glPrint(stlpec+32,riadok+1, text, 0);
font->glPrint(stlpec,riadok+2,"up vek. vu:",0);
gcvt( (double)vu.x, 2, text);
font->glPrint(stlpec+12,riadok+2, text, 0);
gcvt( (double)vu.y, 2, text);
font->glPrint(stlpec+22,riadok+2, text, 0);
gcvt( (double)vu.z, 2, text);
font->glPrint(stlpec+32,riadok+2, text, 0);
}
float Camera::Distance()
{
return (float) sqrt(vp.x*vp.x + vp.y*vp.y + vp.z*vp.z);
}