Autor Wątek: [3D]Obrót i poprawny ruch do przodu [solved]  (Przeczytany 1202 razy)

Offline SaDman

  • Użytkownik

# Czerwiec 01, 2011, 20:51:15
Mój problem przedstawia się następująco:

Próbuję oprogramować pocisk sterowany przez gracza. Gracz może pociskiem obracać wokół osi X i Z, a sam pocisk stoi wzdłuż osi Y, w początku układu współrzędnych mając swój najniżej położony punkt (np. silnik odrzutowy).
Wykonujemy obroty, a następnie wciskamy przycisk odpowiedzialny za "gaz", tak by pocisk leciał do przodu. No właśnie, do przodu... nie w bok, czy w dół, ale przed siebie.
Dane mam kąty angX, angZ oraz długość wektora ciągu r. Wiem, że teraz korzystając z trygonometrii muszę wyliczyć przesunięcie dx, dy, dz dla pocisku.

Pomyślałem sobie:
Okej, mam 2 kąty obrotu, narysuje sobie 2 ładne rysuneczki (jak w załączniku) i policzę nowe współrzędne pocisku z trygonometrii.

Biorąc pod uwagę rysunek z obrotem wokół osi Z mam:

dx = r * sin(angZ);
dy = r * cos(angZ);

A z drugiego rysunku:

dz = r * sin(angX);
dy = r * cos(angX);

Wszystko działa super do momentu, w którym nie zacznę składać obu obrotów razem.
Intuicyjnie wymnożyłem cosinusy dla dy i zaimplementowałem takie równania:

dx = r * sin(angZ);
dy = r * cos(angZ) * cos(angX);
dz = r * sin(angX);

Ciągle jednak to nie jest to. Ruch psuje się po przekroczeniu pewnego kąta. Nie chcę sprawdzać doświadczalnie jaki to kąt i potem dochodzić do tego, dlaczego tak się dzieje. Mam nadzieję, że znajdzie się tu ktoś, kto potrafiłby mi na chłopski rozum wytłumaczyć w czym tkwi mój problem i jak go rozwiązać.
Próbowałem też implementować równania z wikipedii (nt. współrzędnych sferycznych), no ale nadal nie działają one tak ładnie. Może dlatego, że jest ograniczenie dla kątów, a ja chciałbym swobodnie móc obracać obiektem.
« Ostatnia zmiana: Czerwiec 05, 2011, 11:47:38 wysłana przez SaDman »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Czerwiec 02, 2011, 11:53:30
Brakuje tego, co poniżej:

dx = r * sin(angZ) * cos(angX);
dy = r * cos(angZ) * cos(angX);
dz = r * sin(angX);

Offline SaDman

  • Użytkownik

# Czerwiec 02, 2011, 12:31:11
A skąd bierze się ten cosinus? Próbowałem wrzucić Twoje równania, ale też coś nie pasuje :/
Dokładniej - zależy mi na tym, żeby ten układ współrzędnych w załączniku zawsze leciał w kierunku wskazywanym przez zieloną oś (Y).
Obracając wokół Z i X osobno nie ma żadnego problemu, ale jak obrócę trochę po X, a potem trochę po Z, to "lata jak chce".

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Czerwiec 02, 2011, 13:19:59
Może źle wrzuciłeś te równanie. Dopisałem cosinus, bo u siebie korzystam z bardzo podobnego, tyle że u siebie stosuję być może inny układ współrzędnych (Z-up).

Można to wytłumaczyć bardziej ogólnie.
Latając po płaszczyźnie mamy:
dfront = cos(heading)
dright = sin(heading)

Dochodzi nam teraz "pitch", czyli odchylenie kierunku od płaszczyzny:
dfront = cos(heading)*cos(pitch)
dright = sin(heading)*cos(pitch)
dup = sin(pitch)

Wytłumaczenie: jeżeli pitch=0, to latamy po płaszczyźnie (cos=1, sin=0). W miarę jak pitch odchyla się od płaszczyzny, zwiększa się ruch w pionie (dup=sin), ale zmniejsza się ruch w poziomie (dfront/dright*=cos) do momentu, gdy patrzymy pionowo w górę/dół (cos=0, sin=+1/-1).

Ten wzór to typowy wzór na położenie punktu na sferze, mając dane kąty (długość/szerokość, jak w geografii).

Offline hashedone

  • Użytkownik

# Czerwiec 02, 2011, 13:42:37
Więcej na temat wyprowadzenia na temat tych wzorów jest choćby na Wikipedii;) Po prostu opisujesz swój pocisk jako współrzędne sferyczne - dwa kąty opisujące odchylenie i odległość od środka. Matematycy wymyślili to bardzo dawno i generalnie nie ma specjalnej potrzeby od nowa wynajdywać tych wzorów, chyba że chodzi o zrozumienie tematu.

Offline SaDman

  • Użytkownik

# Czerwiec 02, 2011, 15:58:47
dfront = cos(heading)*cos(pitch)
dright = sin(heading)*cos(pitch)
dup = sin(pitch)

Wykorzystując Twoje wytłumaczenie, przerobiłem to na swój układ współrzędnych (do przodu po Y, na prawo po X, do góry po Z).
No i sytuacja się powtarza (pewnie nawet już wcześniej kiedyś implementowałem te wzory, ale ciągle źle działało). Być może problem jest w samych rotacjach? Mam wrażenie, że jeden obrót wykonuje się prawidłowo, a drugi już zapomina, jak skierowane są osie po pierwszym obrocie.

Ja chcę tylko zrobić ładne sterowanie do jak najbardziej prymitywnego pseudo-samolocika, startującego pionowo, jak rakieta.
Zamieszczam kod, może on coś więcej powie.


#include "rocket.h"

CRocket::CRocket()
{
this->angX = 0.0f;
this->angY = 0.0f;
this->angZ = 0.0f;
this->posX = 640.0f;
this->posY = 0.0f;
this->posZ = -640.0f;
}

CRocket::~CRocket()
{
}

void CRocket::draw()
{
glPushAttrib(GL_ENABLE_BIT);
glPushMatrix();
if(!this->model)
this->model = glmReadOBJ("Objects//rocket.obj");

/*Ide do miejsca, gdzie pocisk ma byc narysowany*/
glTranslatef(posX,posY,posZ);

/*Układ wspolrzednych przed rotacjami*/
glBegin(GL_LINES);
glColor3f(0.5f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(10.0f, 0.0f, 0.0f);
glColor3f(0.0f, 0.5f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 10.0f, 0.0f);
glColor3f(0.0f, 0.0f, 0.5f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 10.0f);
glEnd();

/*Wykonuje rotacje*/
glRotatef(angX, 1.0f, 0.0f, 0.0f);
glRotatef(angY, 0.0f, 1.0f, 0.0f);
glRotatef(angZ, 0.0f, 0.0f, 1.0f);


/*Uklad wspolrzednych po rotacjach*/
glBegin(GL_LINES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(10.0f, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 10.0f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 10.0f);
glEnd();



//glmDraw(this->model, GLM_SMOOTH|GLM_TEXTURE);

glPopMatrix();
glPopAttrib();

}

void CRocket::RotateX(bool ccw)
{
if(ccw)
this->angX -= 1.0f;
else
this->angX += 1.0f;

}

void CRocket::RotateZ(bool ccw)
{
if(ccw)
this->angZ += 1.0f;
else
this->angZ -= 1.0f;
}

void CRocket::MoveForward(float amount)
{
/*w prawo*/ this->posX -= amount * sin(deg2rad(angZ)) * cos(deg2rad(angX));
/*do przodu*/ this->posY += amount * cos(deg2rad(angZ)) * cos(deg2rad(angX));
/*do gory*/ this->posZ += amount * sin(deg2rad(angX));
}

Innymi słowy: Wydaje mi się, że pierwszy obrót wykonuje się dobrze, względem nowego lokalnego układu współrzędnych (po rotacji), a następny obrót obraca się już względem starego lokalnego układu (przed rotacją). Ktoś doradzał skorzystanie z kątów eulera, ale nie jestem co do tego pozytywnie nastawiony (Mam kiepską wyobraźnie przestrzenną, a z matmą jestem na poziomie 2 liceum może)
« Ostatnia zmiana: Czerwiec 02, 2011, 17:52:48 wysłana przez SaDman »

Offline counterClockWise

  • Użytkownik

# Czerwiec 02, 2011, 18:11:43
to może kwaternion?

Offline SaDman

  • Użytkownik

# Czerwiec 02, 2011, 20:24:18
Wrzucę jeszcze execa, bo nie daje mi to spokoju.
Równania mam takie jak wyżej. Chcę, żeby zawsze leciało w kierunku wskazywanym przez zieloną oś Y.

Sterowanie:
W,S - obrót wokół osi X
A,D - obrót wokół Z
Q - do przodu

Kamera:
Przytrzymac PPM i zmieniać widok + rolka - odleglosc

Chciałbym wiedzieć, dlaczego tak się dzieje ;/
« Ostatnia zmiana: Czerwiec 02, 2011, 20:25:51 wysłana przez SaDman »

Offline SaDman

  • Użytkownik

# Czerwiec 05, 2011, 11:47:20
Problem rozwiązany. Kolejność obrotów przed rysowaniem była nieprawidłowa.
"Ja pierdziele, ale siara" jak to mawia moja siostrzyczka.
Nie mniej jednak dziękuję, Krzysiek za fajne wytłumaczenie tych równań :-)