Ten wpis jest kontynuacją tematyki związanej z grafiką trójwymiarową opartej o WPF. Zostanie w nim pokazane jak przy pomocy kodu napisanego w C# narysować proste wielościany.
Niniejszy wpis poza prezentacją różnych i niezależnych od konkretnego wykorzystania treści stanowi również opis pewnych zagadnień związanych z projektem Mesh Diagram 3D.Informacje dotyczące projektu oznaczone są etykietą MeshDiagram3D.
Poprzedni wypis został zakończony na prezentacji najprostszego obiektu w scenie – czyli trójkąta. Trójkąt został dodany do sceny z poziomu kodu napisanego w XAML. Zobaczmy teraz jak można to wykonać z poziomu kodu napisanego w C#. W tym celu do kontrolki ViewPort3D musimy dodać obiekt ModelVisual3D, którego zawartością będzie odpowiednia grupa (typu Model3DGroup). Można to zrealizować poprzez prosty kod (w C#):
ModelVisual3D mv3d = new ModelVisual3D(); mv3d.Content = CreateTriangleModel (new Point3D(0,0,0),new Point3D(0,2,0),new Point3D(0,0,2)); this.mainViewport.Children.Add( mv3d );
Oczywiście ważna jest jeszcze funkcja
CreateTriangleModel, która ma u mnie postać:
static private Model3DGroup CreateTriangleModel( Point3D p0, Point3D p1, Point3D p2 ) { MeshGeometry3D mesh = new MeshGeometry3D(); mesh.Positions.Add( p0 ); mesh.Positions.Add( p1 ); mesh.Positions.Add( p2 ); mesh.TriangleIndices.Add( 0 ); mesh.TriangleIndices.Add( 1 ); mesh.TriangleIndices.Add( 2 ); Material material = new DiffuseMaterial( new SolidColorBrush( Colors.DarkKhaki ) ); GeometryModel3D model = new GeometryModel3D( mesh, material ); Model3DGroup group = new Model3DGroup(); group.Children.Add( model ); return group; }
Do funkcji przekazujemy trzy punkty (typ Point3D,
określający trójkę współrzędnych). Wewnątrz funkcji tworzony
jest obiekt typu MeshGeometry3D,
do którego dodawane są punkty (jako Positions),
na których bazie tworzone są trójkąty (poprzez ustawianie
wskaźników TriangleIndices).
Wewnątrz funkcji przygotowany jest również materiał, który ma
pokryć nasz trójkąt (Material).
W tym przypadku materiałem jest DiffuseMaterial
pozwalający na wykorzystanie tradycyjnego (dla grafiki 2D) pędzla
jak w tym przypadku SolidColorBrush
w kolorze ciemnego khaki. Na bazie siatki (mesh) i materiału
(material) tworzony jest model typu GeometryModel3D,
który jest dodawany do wytworzonego obiektu Model3DGroup,
a ten jest ostatecznie zwracany z funkcji. W ten sposób na ekranie
powinniśmy otrzymać trójkąt, podobny do tego na rysunku:
Nadal jest to jednak figura dwu-wymiarowa.
Spróbujmy więc narysować sześcian lub prostopadłościan. W tym
celu wystarczy wykorzystać kod napisany na początku, lecz zamiast
funkcji CreateTriangleModel należy wykorzystać funkcję
CreateCubeModel3DGroup, której kod może wyglądać następująco:
static private Model3DGroup CreateCubeModel3DGroup ( double X, double Y, double Z, double sizeX, double sizeY, double sizeZ ) { Model3DGroup cube = new Model3DGroup(); Point3D p0 = new Point3D( X - sizeX / 2, Y - sizeY / 2, Z - sizeZ / 2 ); Point3D p1 = new Point3D( X + sizeX / 2, Y - sizeY / 2, Z - sizeZ / 2 ); Point3D p2 = new Point3D( X + sizeX / 2, Y - sizeY / 2, Z + sizeZ / 2 ); Point3D p3 = new Point3D( X - sizeX / 2, Y - sizeY / 2, Z + sizeZ / 2 ); Point3D p4 = new Point3D( X - sizeX / 2, Y + sizeY / 2, Z - sizeZ / 2 ); Point3D p5 = new Point3D( X + sizeX / 2, Y + sizeY / 2, Z - sizeZ / 2 ); Point3D p6 = new Point3D( X + sizeX / 2, Y + sizeY / 2, Z + sizeZ / 2 ); Point3D p7 = new Point3D( X - sizeX / 2, Y + sizeY / 2, Z + sizeZ / 2 ); //front side triangles cube.Children.Add( CreateTriangleModel( p3, p2, p6 ) ); cube.Children.Add( CreateTriangleModel( p3, p6, p7 ) ); //right side triangles cube.Children.Add( CreateTriangleModel( p2, p1, p5 ) ); cube.Children.Add( CreateTriangleModel( p2, p5, p6 ) ); //back side triangles cube.Children.Add( CreateTriangleModel( p1, p0, p4 ) ); cube.Children.Add( CreateTriangleModel( p1, p4, p5 ) ); //left side triangles cube.Children.Add( CreateTriangleModel( p0, p3, p7 ) ); cube.Children.Add( CreateTriangleModel( p0, p7, p4 ) ); //top side triangles cube.Children.Add( CreateTriangleModel( p7, p6, p5 ) ); cube.Children.Add( CreateTriangleModel( p7, p5, p4 ) ); //bottom side triangles cube.Children.Add( CreateTriangleModel( p2, p3, p0 ) ); cube.Children.Add( CreateTriangleModel( p2, p0, p1 ) ); return cube; }
Przekazywane są do niej współrzędne środka prostopadłościanu
oraz rozmiar każdego boku. W efekcie możemy otrzymać:
W tytule obiecywałem jeszcze czworościan. W tym przypadku
współrzędne, jakie należy wybrać dla jego wierzchołków, mogą
nie być takie oczywiste, jak w przypadku prostopadłościanu. Możemy
jednak wpisać ten czworościan w prostopadłościan, np. tak jak na
rysunku (źródło Wikipedia):
Otrzymamy więc następującą funkcję rysującą
czworościan (na podstawie wcześniejszego prostopadłościanu):
static private Model3DGroup CreateTetraederModel3DGroup ( double X, double Y, double Z, double sizeX, double sizeY, double sizeZ ) { Model3DGroup tetraeder = new Model3DGroup(); Point3D p0 = new Point3D( X - sizeX / 2, Y - sizeY / 2, Z - sizeZ / 2 ); Point3D p2 = new Point3D( X + sizeX / 2, Y - sizeY / 2, Z + sizeZ / 2 ); Point3D p5 = new Point3D( X + sizeX / 2, Y + sizeY / 2, Z - sizeZ / 2 ); Point3D p7 = new Point3D( X - sizeX / 2, Y + sizeY / 2, Z + sizeZ / 2 ); tetraeder.Children.Add( CreateTriangleModel( p0, p5, p7 ) ); tetraeder.Children.Add( CreateTriangleModel( p0, p2, p5 ) ); tetraeder.Children.Add( CreateTriangleModel( p0, p7, p2 ) ); tetraeder.Children.Add( CreateTriangleModel( p5, p2, p7 ) ); return tetraeder; }
Co da w efekcie:
Proste prawda?? w następnym wpisie przejdziemy do czegoś trochę
trudniejszego... do rysowania linii ;).
Przypominają mi się stare czasy i zabawy z OpenGl-em ....
OdpowiedzUsuńmam nadzieję, że wspomnienia masz dobre ;)
OdpowiedzUsuńhey, nie jestem zwolennikiem komentowania czegokolwiek i dużo po blogach nie chodzę, ale jestem znudzony, zmęczony, śpiący i pewnie trochę wstawiony w związku z czym pewnie napisze coś głupiego ale i tak macie tutaj mało komentarzy więc ten wam raczej nie zaszkodzi.
OdpowiedzUsuńWszytko to powyżej jakieś takie zagmatwane się wydaje, nie łatwiej by było wsadzić wszystkie vertexy w odpowiedniej kolejności w vertex buffer jakiś i to wszystko potem po prostu kazać odrysować? A może to zboczenie z DX'a.. pewnie koniec końców było by więcej bazgrania pipeline'a... .Ale całe to rysowanie trójkąta po trójkącie jeszcze z jakimiś childrenami.. :/
Jutro rano wstanę i będę się zastanawiał jak usunąć ten komentarz...
Jestem początkujący. Kod mi zadziałał dopiero jak dodałem poniższą linijkę:
OdpowiedzUsuńusing System.Windows.Media.Media3D;
Bez tego nie było widoczne ModelVisual3D
jak najbardziej się zgadza, w każdym z przykładów z tego cyklu trzeba dopisać różne "using", ale sam VS podpowiada jakie ;), a pisząc wpisy na blogu czasem uważam że dopisywanie wspomnianych "using" tylko zaciemniło by istotne rzeczy.
OdpowiedzUsuńW każdym razie dzięki za komentarz.
No właśnie, wspomniałeś, że VS sam podpowiada jakie "using" wykorzystać, lecz jakoś mój VS nie widzi nawet ja wpiszę linijkę podaną przez Animowego. Pojawia się błąd, że tej przestrzeni nazw nie mam. Czy muszę doinstalować jakieś dodatkowe biblioteki?
OdpowiedzUsuńjuż wiem :-)
Usuń