poniedziałek, 9 sierpnia 2010

[WPF 3D] Czworościan, sześcian ... itd. z poziomu C# [PL]

Promuj
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 ;).
Promuj

7 komentarzy:

  1. Przypominają mi się stare czasy i zabawy z OpenGl-em ....

    OdpowiedzUsuń
  2. mam nadzieję, że wspomnienia masz dobre ;)

    OdpowiedzUsuń
  3. 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.

    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...

    OdpowiedzUsuń
  4. Jestem początkujący. Kod mi zadziałał dopiero jak dodałem poniższą linijkę:

    using System.Windows.Media.Media3D;

    Bez tego nie było widoczne ModelVisual3D

    OdpowiedzUsuń
  5. 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.

    W każdym razie dzięki za komentarz.

    OdpowiedzUsuń
  6. 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ń

Posty powiązane / Related posts