No dobra, nie pierwsze tylko trzecie w moim wypadku, ale poprzednie się nie liczą, bo skończyło się na utworzeniu najprostszego okna
Czym jest OpenTK? Jest to binding OpenGL, OpenAL i OpenCL pod .NET. Aktualnie jest w wersji 1.0 RC1 i obsługuję Otwarty GL w wersji 3.2, OpenAL
1.1 i OpenCL 1.0. Do tego posiada dość rozbudowaną bibliotekę matematyczną(która mam nadzieję wystarczy mi) obsługującą wektory, macierze, kwaterniony i struktury opisujące krzywe Béziera.
Posiada kilka klas-kontrolek, które ułatwiają integracje z już istniejącymi programami, np. GLControl dla Windows Forms, GLWidget dla GTK# i, podobno, kontrolkę dla WPF(lecz ja jej nie znalazłem).
Dla gier stworzona jest specjalna klasa GameWindow – całe okno jest przeznaczone tylko dla OpenGL, daje to nam najlepszą wydajność i usuwa zbędne rzeczy ze „zwykłych” form. To właśnie jej będę używał i to ją szerzej opiszę w dzisiejszym poście.
Najzwyczajniej w świecie jest to klasa opakowująca(może niedokładnie ona, ale dziedziczy z niej) wywołania WinAPI do tworzenia okna i WGL do tworzenia kontekstu OpenGL. Do tego udostępnia stosunkowo przyjazny interfejs do tworzenia obiektu gry. Nie jest to może dokładnie to, czego bym chciał(dlatego ja opakowałem to jeszcze raz
), ale tragedii nie ma.
Aby je stworzyć wystarczy utworzyć obiekt tej klasy i… wywołać metodę Run! Dzięki temu otrzymamy puste okno, na którym nie będziemy mogli nic narysować.
static class Program
{
static void Main()
{
using (var game = new OpenTK.GameWindow())
{
game.Run();
}
}
}
By to zmienić trzeba odziedziczyć tą klasę, przeładować metodę OnRenderFrame, w której to rysujemy wszystko, co mamy do narysowania i przeładować metodę OnUpdateFrame, która służy do aktualizacji tego, co mamy. Równie dobrze wszystko można wpakować do jednej metody, ale przez to zagmatwamy sobie kod i stracimy możliwość ustawiania stałej ilości klatek i uaktualnień/sekunda(udostępnia to metoda Run).
class Game
: OpenTK.GameWindow
{
public Game()
//Tworzymy okno 800x600
: base(800, 600, OpenTK.Graphics.GraphicsMode.Default, "Moje pierwsze okno w OpenTK!")
{ }
protected override void OnUpdateFrame(OpenTK.FrameEventArgs e)
{
//Aktualizacja!
}
protected override void OnRenderFrame(OpenTK.FrameEventArgs e)
{
//Renderowanie!
}
static void Main()
{
using (var game = new Game())
{
game.Run();
}
}
}
Klasa GameWindow udostępnia trzy przeładowania metody Run:
Może to być przydatne, gdy np. nie chcemy uaktualniać gry niewiadomo ile razy na sekundę, bo np. i tak tego w grze nie odczujemy.
Nie bardzo pasowała mi ilość danych udostępniana przez GameWindow, dlatego postawiłem opakować to jeszcze raz, gdyż nie lubię, jeśli w klasie jest multum właściwości, których nigdy nie użyję(chyba, że nie mam wyjścia). Stworzyłem sobie interfejs, któryudostępnia funkcjonalność taką, jaką chcę i nic więcej. Zabrawszy się do implementacji pierwsze, co zrobiłem to… odziedziczyłem z GameWindow! Dzięki temu nadal miałem „wszystko w jednej klasie”. Całe szczęście zorientowałem się w porę i z GameWindow odziedziczyłem w wewnętrznej klasie(choć w sumie nie musiałem – wystarczyło dodać odpowiednie zdarzenia). Dzięki temu otrzymałem klasę sensownych rozmiarów udostępniającą wszystko to, czego chciałem. Jej kod możecie zobaczyć tutaj.