Skocz do zawartości

[Delphi][OpenGL]Hierarchia obiektów


Brainer

Polecane posty

Witam. :)

 

Ponownie uderzam do was z pytaniem, bo mam parę wątpliwości, a wy z pewnością zdołacie je rozwiać. :) A więc zamierzam zabrać się za napisanie własnego silnika 3D. Nie ma być to nic skomplikowanego, ale prosty i wydajny framework, dzięki któremu później będę mógł łatwiej tworzyć sobie inne projekty. Moim problemem jest teraz utworzenie wydajnej hierarchii obiektów na scenie. Mam taki kodzik:

 

CODEtype

TTransformationType = (ttMove, ttRotate, ttScale);

TTransformations = set of TTransformationType;

 

{ .: TBaseSceneObject :. }

TBaseSceneObject = class(TPersistent)

public

{ Public declarations }

procedure LoadFromFile(const FileName: String); virtual; abstract;

procedure SaveToFile(const FileName: String); virtual; abstract;

procedure LoadFromStream(const S: TStream); virtual; abstract;

procedure SaveToStream(S: TStream); virtual; abstract;

 

procedure Think(const AElapsedTime: Integer); virtual;

procedure Render(); virtual;

end;

 

{ .: TSceneObject :. }

TSceneObject = class(TBaseSceneObject)

private

{ Private declarations }

FDir: TVector4f;

FTrans: TTransformations;

FScale: TAffineVector;

FPos: TAffineVector;

FVisible: Boolean;

FMatIdx: Integer;

procedure SetDir(const Value: TVector4f);

procedure SetPos(const Value: TAffineVector);

procedure SetScale(const Value: TAffineVector);

public

{ Public declarations }

constructor Create();

 

procedure Assign(Source: TPersistent); override;

procedure Render(); override;

 

property Visible: Boolean read FVisible write FVisible;

 

property MaterialIdx: Integer read FMatIdx write FMatIdx;

 

property Transformations: TTransformations read FTrans write FTrans;

property Position: TAffineVector read FPos write SetPos;

property Direction: TVector4f read FDir write SetDir;

property Scale: TAffineVector read FScale write SetScale;

end;

Te dwie klasy służą mi do reprezentacji pojedynczego obiektu na scenie. I tutaj pojawia się pierwszy mój problem. Chodzi o wykonywanie przekształceń. Używam takiego kodu:

 
procedure TSceneObject.Render;
begin
 if not FVisible then
   exit;

 // Transformations
 if ttMove in FTrans then
   glTranslatef(FPos[0], FPos[1], FPos[2]);
 if ttRotate in FTrans then
   glRotatef(FDir[3], FDir[0], FDir[1], FDir[2]);
 if ttScale in FTrans then
   glScalef(FScale[0], FScale[1], FScale[2]);
end;

Obawiam się jednak, że ten kod nie jest najlepszym rozwiązaniem. ;)

 

Drugim problemem jest stworzenie systemu hierarchii obiektów. Chodzi mi o to, żeby był jeden obiekt-korzeń, a każdy obiekt pochodny mógł mieć nieskończenie wiele obiektów pochodnych. Kojarzycie o co mi biega? :)

 

Byłbym wdzięczny za wszelkie informacje. Jeżeli coś nie jest jasne odnośnie mojego problemu, proszę dać znać.

 

Z góry dziękuję. :)

Link do komentarza
Udostępnij na innych stronach

Do przekształceń możesz przechowywać macierz przekształceń a potem mnożyć przez nią będziesz w niej przechowywał wszystkie przekształcenia poskładane a dodatkowo osobno w wektorach możesz jeszcze trzymać te przekształcenia pojedynczo.

 

Drugim problemem jest stworzenie systemu hierarchii obiektów. Chodzi mi o to, żeby był jeden obiekt-korzeń, a każdy obiekt pochodny mógł mieć nieskończenie wiele obiektów pochodnych. Kojarzycie o co mi biega?
Kojarze ale po co nieskończenie wiele xD ? Zbyt "dalekie" dziedziczenie czy też nadmierne nie jest najlepszym nawykiem. Ale pewne klasy bazowe dla reszty obiektów to wiadomo wyjście dobre.

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link do komentarza
Udostępnij na innych stronach

CYTAT(5corpio @ nie, 16 gru 2007 - 17:39)

Do przekształceń możesz przechowywać macierz przekształceń a potem mnożyć przez nią będziesz w niej przechowywał wszystkie przekształcenia poskładane a dodatkowo osobno w wektorach możesz jeszcze trzymać te przekształcenia pojedynczo.

 

Właśnie tak myślałem o tych macierzach, ale nie za bardzo rozumiem, jak to stosować. ;) Chodzi Ci na przykład o coś takiego? :mellow:

 
procedure TSceneObject.Render;
begin
 if not FVisible then
   exit;

 glPushMatrix();
   // Transformations
   if ttMove in FTrans then
     glTranslatef(FPos[0], FPos[1], FPos[2]);
   if ttRotate in FTrans then
     glRotatef(FDir[3], FDir[0], FDir[1], FDir[2]);
   if ttScale in FTrans then
     glScalef(FScale[0], FScale[1], FScale[2]);
 glPopMatrix();
end;

 

A jeśli chodzi o te listy, to po poprostu niech każdy obiekt ma listę TList dzieci. I w czasie renderowanie już jak się wyrenderuje obiekt wywoływać Render dla dzieci.

Dobry pomysł! :D A co myślisz o TThreadList? Czy wpłynie to jakoś na wydajność wykonywanego kodu?

Link do komentarza
Udostępnij na innych stronach

Źle zrozumiałeś 5corpio. Chodzi o to, że masz macierze dla obrotu, skalowania i przesunięcia. I nie musisz po kolej każdą "użyć" (dziwna nazwa:P) to możesz je przez siebie przemnożyć (kolejność ważna) i ją "użyć". Nie wiem po co TThreadList, to nawet nie logiczne, masz mieć listę obiektów

Baza tysięcy lotnisk: http://airportsbase.com

Link do komentarza
Udostępnij na innych stronach

CYTAT(Force @ nie, 16 gru 2007 - 20:58)

Źle zrozumiałeś 5corpio. Chodzi o to, że masz macierze dla obrotu, skalowania i przesunięcia. I nie musisz po kolej każdą "użyć" (dziwna nazwa:P) to możesz je przez siebie przemnożyć (kolejność ważna) i ją "użyć".

 

Czyli chodzi o coś takiego?

 
public
 { Public declarations }
 // ... 
 property TransformationMatrix: TMatrix read FTransMat write FTransMat;
 property RotationMatrix: TMatrix read FRotMat write FRotMat;
 property ScaleMatrix: TMatrix read FScaleMat write FScaleMat;

I jak potem "używać" tych macierzy? :blink:

 

Nie wiem po co TThreadList, to nawet nie logiczne, masz mieć listę obiektów

No przecież wspomniałeś, żeby każdy obiekt miał listę TList dzieci. Zatem, czy można to zastąpić TThreadList i czy zyskam jakiś wzrost wydajności?

Link do komentarza
Udostępnij na innych stronach

http://www.cs.rutgers.edu/~decarlo/428/gl_...multmatrix.html

i ten:

 

http://www.cosc.brocku.ca/Offerings/3P98/c...s/2d_3d_xforms/

 

w tym drugim masz pokazane, które elementy macierzy odpowiadają za jakie przekształcenia.

 

 

Chociaż używanie zwykłego glRotate etc. sprowadza się do tego samego co masz przy używaniu macierzy ale wykonywane to jest już nie "przez Ciebie" więc wsumie możesz pozostawić swoje rozwiązanie, które poniekąd też jest dobre.

 

Edit: A jednak nie zrozumiałem chyba twojego drugiego pytania po co Ci obiekt "mega" nadrzędny, który w liście ma ileś tam dzieci, które powiedzmy "latają" po scenie? Nie lepiej zrobić liste obiektów do siebie ew. dolinkowanych, które jakoś tam na siebie wpływają ?

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link do komentarza
Udostępnij na innych stronach

Chodzi o to, że kolejność składania przekształceń w innej kolejności może dać inny wynik niż złożenei tych samych przekształceń w odwrotnej.

 

Obrót->przesunięcie nie będzie tak samo jako przesunięcie->obrót etc.

 

No chcę zrobić tak, jak to jest w GLScene. Tam jest jeden obiekt nadrzędny, a reszta jest obiektami-dziećmi.
Hmm a co to daje bo szczerze powiedziawszy nie wiem ? To zależy co potrzebujesz uzyskać i co chcesz mieć ?

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link do komentarza
Udostępnij na innych stronach

Chodzi o to, że kolejność składania przekształceń w innej kolejności może dać inny wynik niż złożenei tych samych przekształceń w odwrotnej.

 

Obrót->przesunięcie nie będzie tak samo jako przesunięcie->obrót etc.

Czyli jak mniemam, najpierw ma być przesunięcie, potem obrót, a na koniec skalowanie. Dobrze kminię?

EDIT

No chcę sobie zrobić drzewo obiektów, gdzie będzie tylko jeden obiekt nadrzędny (scena), a reszta będzie niżej w hierarchii.

Link do komentarza
Udostępnij na innych stronach

Ja mam skalowanie>obrot>przesuniecie i dziala dobrze i raczej jak to pisalem nie przejmowalem sie kolejnoscia(moze po prostu mi sie udalo :) )

Okej, czyli tak to zrobię. :)

Teraz mam małą prośbę. Mógłbym mi ktoś pokazać, jak się używa tej funkcji glMultMatrix? Będę bardzo, bardzo wdzięczny...

Link do komentarza
Udostępnij na innych stronach

Ta funkcja mnoży aktualnie używaną macierz (GL_MODELVIEW_MATRIX, GL_PROJECTION_MATRIX albo GL_TEXTURE_MATRIX) przez macierz, którą przekażesz jako parametr tej funkcji i tyle.

 

A jak wiadomo aktualnie używaną macierz ustawia się w ogl-u przez: glMatrixMode xD

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link do komentarza
Udostępnij na innych stronach

Ta funkcja mnoży aktualnie używaną macierz (GL_MODELVIEW_MATRIX, GL_PROJECTION_MATRIX albo GL_TEXTURE_MATRIX) przez macierz, którą przekażesz jako parametr tej funkcji i tyle.

 

A jak wiadomo aktualnie używaną macierz ustawia się w ogl-u przez: glMatrixMode xD

Aha! :D Czyli teraz pozostało tylko wyliczyć macierz skalowania, obrotu i przesunięcia i użyć ich z tą funkcją, tak?

Link do komentarza
Udostępnij na innych stronach

5corpio

 

Hmm a co to daje bo szczerze powiedziawszy nie wiem ? To zależy co potrzebujesz uzyskać i co chcesz mieć ?

Takie podejście ma wiele zalet jest bardzo przydatne w pisaniu animacji kości (odwrotna kinematyka), do ustawienia pozycji względnej obieku np efektor systemu cząsteczek zawsze przyczepiony w danym punkcie obiektu...

 

Co do rotacji warto też używać kwaternionów nie tylko macierzy w tym przypadku jeśli na hierarchii byśmy chcieli robić interpolacje to macierze mogą nam przysporzyć sporo kłopotów...

 

Jeśli chodzi o mnożenie kolejnych macierzy obiektu i ich kolejność to odpowiem lepiej niż poprzednicy wcale nie trzeba ich mnożyć przed wysłaniem do OpenGL tu jest pewna optymalizacja zamiast mnożyć macierze możemy je normalnie uzupełniać. Macierz do wysłania openglowi wygląda mniej więcej tak bez skalowania:

X.x X.y X.z 0

Y.x Y.y Y.z 0

Z.x Z.y Z.z 0

T.x T.y T.z 1

 

X, Y, Z - obrót na osiach (baza przestrzeni tzw. Trójścian Freneta)

T Przesuniecie (przesunięcie bazy)

Jest jeszcze skalowanie które znajduje się na przekątnej tej macierzy jest rzadko zrywane więc można je pomnożyć w szczególnych przypacdkach.

Przy okazji przypominam by zwrócić uwagę na to czy układ jest lewo czy prawoskrętny bo mnożenie macierzy może źle działać.

 

Dla ciekawostki dodam ze jak byśmy chcieli użyć przekształcenia odwrotnego wystarczy że zrobimy transpozycje macierzy obrotu i przeciwność wektora przesunięcia. jest to spore uproszczenie bo

liczenie odwrotności macierzy 4x4 to niezły hard core :D

 

Pozdrawiam i życzę powodzenia w pisaniu hieracrchi.

www.spider.dathox.com :)

Link do komentarza
Udostępnij na innych stronach

  • 2 weeks later...

Hej. :)

 

Sorry za odgrzebywanie tego tematu, ale nie chcę zakładać nowego. :P

Otóż doszedłem do czegoś takiego:

CODEunit UBEObjects;

 

interface

 

uses

Classes,

// -- Headers --

dglOpenGL,

// -- Base Units --

VectorGeometry, VectorTypes;

 

type

{ .: TBaseObject :. }

TBaseObject = class(TObject)

public

{ Public declarations }

procedure LoadFromStream(const S: TStream); virtual; abstract;

procedure LoadFromFile(const FileName: String); virtual; abstract;

procedure SaveToStream(S: TStream); virtual; abstract;

procedure SaveToFile(const FileName: String); virtual; abstract;

end;

 

{ .: TSceneObject :. }

TSceneObject = class(TBaseObject)

private

{ Private declarations }

FPos: TAffineVector;

FRot: TVector4f;

FScale: TAffineVector;

procedure SetPos(const Value: TAffineVector);

procedure SetRot(const Value: TVector4f);

procedure SetScale(const Value: TAffineVector);

public

{ Public declarations }

Children: TList;

 

constructor Create();

destructor Destroy(); override;

 

procedure TransformationChanged();

 

procedure Render(); virtual; abstract;

procedure Think(const ElapsedTime: Integer); virtual; abstract;

 

property Position: TAffineVector read FPos write SetPos;

property Rotation: TVector4f read FRot write SetRot;

property Scale: TAffineVector read FScale write SetScale;

end;

 

{ .: TCube :. }

TCube = class(TSceneObject)

public

{ Public declarations }

procedure Render(); override;

end;

 

implementation

 

{ TSceneObject }

 

constructor TSceneObject.Create;

begin

inherited Create();

 

Children := TList.Create();

FPos := AffineVectorMake(0.0, 0.0, 0.0);

FRot := VectorMake(0.0, 1.0, 0.0, 0.0);

FScale := AffineVectorMake(1.0, 1.0, 1.0);

end;

 

destructor TSceneObject.Destroy;

begin

Children.Free();

 

inherited Destroy();

end;

 

procedure TSceneObject.SetPos(const Value: TAffineVector);

begin

FPos := Value;

end;

 

procedure TSceneObject.SetRot(const Value: TVector4f);

begin

FRot := Value;

end;

 

procedure TSceneObject.SetScale(const Value: TAffineVector);

begin

FScale := Value;

end;

 

procedure TSceneObject.TransformationChanged;

var

RotM, TransM, ScaleM: TMatrix;

begin

ScaleM := CreateScaleMatrix(FScale);

TransM := CreateTranslationMatrix(FPos);

RotM := CreateRotationMatrix(FRot, FRot[3]);

 

glMultMatrixf(@TransM);

glMultMatrixf(@RotM);

glMultMatrixf(@ScaleM);

end;

 

{ TCube }

procedure TCube.Render;

var

I: Integer;

begin

glPushMatrix();

TransformationChanged();

 

glBegin(GL_QUADS);

// Front Face

glNormal3f(0.0, 0.0, 1.0);

glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);

glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 1.0);

glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 1.0);

glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);

 

// Back Face

glNormal3f(0.0, 0.0, -1.0);

glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);

glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);

glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, -1.0);

glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, -1.0);

 

// Top Face

glNormal3f(0.0, 1.0, 0.0);

glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);

glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0);

glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 1.0, 1.0);

glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0);

 

// Bottom Face

glNormal3f(0.0, -1.0, 0.0);

glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);

glTexCoord2f(0.0, 1.0); glVertex3f(1.0, -1.0, -1.0);

glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0);

glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);

 

// Right Face

glNormal3f(1.0, 0.0, 0.0);

glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, -1.0);

glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, -1.0);

glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 1.0);

glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 1.0);

 

// Left Face

glNormal3f(-1.0, 0.0, 0.0);

glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);

glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);

glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);

glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);

glEnd();

for I := 0 to Children.Count -1 do

TSceneObject(Children).Render();

glPopMatrix();

end;

 

end.

 

Jednak dalej nie spełnia to zadania. Znaczy, transformacje działają elegancko dla obiektu nadrzędnego, ale gdy renderuję obiekty z listy Children, to działają źle (dla potomków). Nie implementowałem jeszcze żadnej hierarchii, ale zrobiłem tak, jak mi doradził Force z użyciem obiektu TList. Co mam zrobić, żeby to działało? Albo inne pytanie: co robię źle?

 

Z góry dziękuję za wszelką pomoc.

Link do komentarza
Udostępnij na innych stronach

Źle czyli co się dzieje? Mi się wydaje, że TransformationChanged masz zepsute, znaczy nie podoba mi się ta funkcja, Bo pieewszo powinieneś ustawić macierz na jednostkową, a potem przez nią przemnażać.

Tutaj jest link do EXEka: http://www.sendspace.com/file/htf6ag

A gdy próbuję najpierw ustawić macierz na jednostkową, a dopiero w niej grzebać, to drugi sześcian się nie rusza. :(

Link do komentarza
Udostępnij na innych stronach

Mogę się mylić ale wg. mnie jest jednak źle bo on ma chyba tak, że dzieci mają swoje przekształcenia nie względem "rodzica" tylko hmm "względem świata" ciężko mi się wyrazić a jak odłoży na stos macierz zrobi przekształcenia rodzica jeszcze jej nie zdejmie i znów wykona przekształcenia dziecka to zostaną one wykonane przecież już względem tego nowego układu rodzica dlatego efekty są inne od oczekiwanych tak ?

Ot taka mini-strona moja po godzinach :)http://www.wnetrzekuchni.pl

Link do komentarza
Udostępnij na innych stronach

Zarchiwizowany

Ten temat jest archiwizowany i nie można dodawać nowych odpowiedzi.

×
×
  • Utwórz nowe...