Archive for 2009

Windows Seven 64-bit

TaskMan

Po trzech tygodniach wreszcie odzyskałem notebooka, w którym jak się okazało, popsuła się płyta główna (brak możliwości restartu, ponieważ po wyłączeniu systemu operacyjnego, komputer się przez pół godziny nie dawał uruchomić). Po powrocie z serwisu niestety kilka programów zaczęło szwankować, ale jest to konsekwencją wymiany płyty głównej. Przynajmniej dostałem w końcu dobry pretekst, żeby usunąć z dysku ostatni 32-bitowy system a zarazem Vistę :).

Przechodząc do meritum, po próbach zainstalowania Windows XP 64-bit, Windows 2k8 Server x64, dopiero Windows Seven 64-bit okazał się być odpowiednim systemem, ponieważ jako jedyny posiada sterowniki do mojej, jakże wydajnej karty “GeForce 6100 Go!”. Z innymi sterownikami też nie było problemu, gdyż na stronie ASUS mimo tego, że sterowniki oznaczone są jako 32-bitowe, zawierają również wersje 64-bit.
Po sterownikach przyszedł czas na oprogramowanie. Starałem się instalować programy x64, jeżeli były one dostępne. Wśród nich są między innymi Mozilla Firefox (Minefield) oraz Mozilla Thunderbird (Shredder) dostępne na tej stronie. Niestety największą bolączką przeglądarki 64-bitowej jest brak programu Adobe Flash Player w tej samej wersji (istnieje wersja testowa, ale przeznaczona tylko na systemy Linux).
Oprócz przeglądarki, ważnym programem (przynajmniej dla mnie) jest Visual Studio. Samo IDE jest aplikacją opartą o .NET więc jest 32-bitowa, ale kompilator w nim zawarty ma już wsparcie dla 64-bitów. W ustawieniach projektu jest możliwość wyboru docelowej platformy x86/x64/IA-64 (pozostaję póki co przy x86). Niestety przy instalacji DirectX SDK pojawił się drobny dylemat. W Visualu, w opcjach (dla ułatwienia oczywiście) można podać ścieżkę do katalogu z bibliotekami statycznymi, jednak w przypadku gdybym chciał kompilować program w dwóch wersjach, dochodzi problem żonglowania ze ścieżkami do wersji x64 i x86. Lepiej by było, gdyby Visual sam wybierał ścieżki na podstawie tego, na jaką platformę kompilujemy dany projekt.
sbdksohtaOstatnią ciekawostką jest brak możliwości zainstalowania NVIDIA SDK 9, którego instalator wyrzuca komunikat o braku wsparcia dla x64.

Podsumowując muszę stwierdzić, że przejście na platformę 64-bitową jest dla mnie krokiem naprzód, ponieważ jakby nie patrzeć mogę wykorzystać posiadany sprzęt w 100% (2x więcej rejetrów xmm :)), a dodatkowo dobrze się bawić poznając dokładniej tę architekturę. Ogólnie programów 64-bitowych nie ma jeszcze za wiele, ale myślę, że ich ilość stale wzrasta. Mam również nadzieję, że system Windows Seven będzie ostatnim systemem, który jest dostępny w wersji 32-bitowej, dzięki temu producenci nie będą mieli problemy z wyborem platformy docelowej dla sterowników.

Systemy operacyjne

Już od jakiegoś czasu zanoszę się z wrzuceniem programów, które musiałem przygotowywać na zajęcia laboratoryjne z Systemów operacyjnych. Oczywiście wiadomo że “chęci największe, gdy możliwości najmniejsze”, więc wrzucam je dopiero teraz, gdy mam dostęp do komputera. Wszystko przez firmę ASUS, która zapewniła mi odwyk od komputera dzięki długoterminowej naprawie notebooka (niech będą przeklęci! :P).

Wracając do tematu, programy które należało przygotowywać miały symulować działania algorytmów takich jak:
– planowanie dostępu do procesora
– planowanie dostępu do dysku twardego
– pamięć wirtualna

Programy te nie są zbyt wyszukane, ale spełniają założenia. Oczywiście na przekór temu, czego używamy na wydziale (Java), swoje programy pisałem w C++. Programy są “prawie” multiplatformowe. Aby ruszyły pod linuksem wystarczy zamienić kilka funkcji i nagłówków na ich odpowiedniki.

Zadanie 1
Zadanie 2
Zadanie 3
Zadanie 4

Spotkanie warsztatowe we Wrocławiu

Dzisiaj odbyło się kolejne wrocławskie spotkanie Warsztatu. Niestety znów frekwencja była niezbyt duża. Na początku spotkaliśmy się z Revo pod umówionym barem, który okazał się restauracją, gdzie lekko nie pasowaliśmy. Potem przenieśliśmy się do pizzerii, gdzie dołączył do na DJ_cool_. No cóż mam nadzieje, że następnym razem będzie lepiej.

Odnośnie ostatniego dnia IT Days – z powodu braku ciekawych tematów zrezygnowałem z nich :).

IT Days 2009 na Politechnice Wrocławskiej

Od wczoraj, czyli od wtorku (22.04.2009), na Politechnice Wrocławskiej trwają Dni Technologii Informatycznych (IT Days). Celem konferencji, organizowanej przez AIESEC Polska, jest prezentacja działalności firm biorących w niej udział, a także możliwość zapoznania się z ofertami pracodawców na Targach Pracy, które odbędą się jutro, czyli w ostatni dzień konferencji.

Jak do tej pory udało mi się uczestniczyć tylko na na dwóch wykładach. Pierwszym jest, składający się z dwóch części, wykład firmy AMD, na którym w pierwszej (dłuższej) części, została zaprezentowana nowa platforma do gier PC o nazwie “Dragon”, natomiast druga część traktowała o technologiach oszczędzania energii wykorzystywanych w procesorach AMD.
Drugi wykład został zaprezentowany przez przedstawicieli firmy Nokia Siemens Networks, a jego tematem były technologie UMTS, HSDPA, HSUPA oraz kilka innych. Niestety nie jest to tematyka, o której mógłbym cokolwiek powiedzieć, ale mimo wszystko wykład był bardzo ciekawy.

O czwartku postaram się napisać w najbliższym czasie :>.

IGK 2009 – podsumowanie

W tym roku wreszcie mogłem w pełni uczestniczyć na konferencji Inżynieria Gier komputerowych, która odbywała się w Siedlcach (W zeszłym roku byłem niestety tylko na jeden nocleg). Jest to jedyne w roku wydarzenie, w którym tak duża ilość ludzi z Warsztatu spotyka się w jednym miejscu.

Standardowym składnikiem konferencji są referaty, które są wygłaszane przez ludzi zarówno z Akademii Podlaskiej jak i ludzi z zewnątrz przez pierwsze dwa dni. Bardzo podobał mi się referat Jakuba Jastrzębskiego, który bardzo fajnie przedstawił swój referat, w którym zawarł informacje o rodzajach silników fizyki, przykładowej architekturze, implementacji własnych rozwiązań.
Również kolejnym ciekawy referat przedstawił Hubert Rutkowski, który poruszył w nim głównie problemy niezwiązane bezpośrednio z programowaniem, lecz z pracą przy komputerze, czyli problem bolących nadgarstków oraz szybkiego pisania. W swojej pracy polecił kilka ciekawych wtyczek do środowiska Microsoft Visual Studio.
Referat Karola Gasińskiego był także bardzo ciekawy. Omawiał on w swojej pracy wykorzystanie megatexturingu w renderingu planety z dynamicznym poziomem dokładności.
Ostatnim, a zarazem najlepszym referatem okazała się praca Wojciecha Tomana omawiająca jego dotychczasowy dorobek przy pracy nad silnikiem nGene. W swojej prezentacji zaprezentował sposób renderowania realistycznej wody jako efekt post-process, w której poszczególne składowe światła zanikają wraz ze wzrostem głębokości. Za swoją pracę otrzymał nagrodę za najlepszy referat ufundowaną przez firmę Seventhtear.

Niestety najsłabsze okazały referaty przedstawicieli Akademii Podlaskiej. Pierwszy, dotyczący implementacji trójwymiarowej gry zręcznościowej na telefon komórkowy, miał zbyt dużą ilość tekstu w prezentacji, a wygłaszający po prostu go czytał, jednak mimo tego gratuluje napisania działającej gry.
Natomiast drugi temat pt. “Rola języków skryptowych w technologii gier” owszem posiadał w miarę poprawną formę prezentacji, ale sprawiał wrażenie projektu na uczelnię, ponieważ prowadzący nie byli w stanie odpowiedzieć na pytanie zadane na końcu prezentacji.

W tym roku gościem specjalnym konferencji IGK, był przedstawiciel firmy The Farm 51, który przedstawił prezentację na temat ewolucji renderera i mechaniki ich najnowszej gry FPS Necrovision. Prezentacja była świetna, a prowadzący demonstrował wszystko w edytorze gry bądź na filmikach.

Tak jak w zeszłym roku i tym razem odbył się Quiz nt. IT, którego pomysłodawcą i prowadzącym jest Regedit. W tym roku jednak za zajęcie pierwszego miejsca były przyznawane nagrody rzeczowe. Najlepszy okazał się Krzysiek K., gratulacje :).

Ostatnim oficjalnym składnikiem konferencji był konkurs Compo, w którym należało napisać grę na temat: “Gra psychodeliczna”. W konkursie wzięło udział 9 drużyn, a tegorocznym zwycięzcą okazał się zespół Rzeźnicy Inc., w którego skład wchodzą: Krzysiek K., Regedit, gemGreg, Ayufan. Drugie i trzecie miejsce zajęły odpowiednio zespoły Kryzys oraz Vertex.

Oczywiście nie obyło się bez wieczorowych integracji, które miały miejsce w piątek i niedziele wieczorem, w pokoju grupy Vexillium. W trakcie tych integracji można było zagrać w takie gry jak Starcraft, Quake 3 Demo, Urban Terror oraz World of Goo :D. W niedziele wieczorem odbył się turniej Quake’a, w którym zająłem zaszczytne ostatnie miejsce :P, a oczywiście wygrał Gynvael, zresztą więcej na jego stronce.
Aktualnie zastanawia mnie tylko jedna sprawa, czy w przyszłym roku również będzie nas gościć hotel Arche (uczestnicy wiedzą ocb :)).

Ogólnie podsumowując, mam nadzieję że w przyszłym roku frajda z uczestnictwa będzie minimum taka jak w tym. Mam nadzieję, że jednak więcej osób zgłosi się z referatami, żeby było czego posłuchać :).

AutomaticHandle ciąg dalszy

Jak się okazuje nic nie jest na początku idealne. Dzięki uwagom Revo udało mi się usprawnić szablon automatycznych uchwytów. Revo poradził mi dodanie dodatkowego wskaźnika na licznik odwołań do danego zasobu. Dzięki temu możliwe było usunięcie jednej funkcji wirtualnej, a druga została sprowadzona do roli sprzątaczki, która zostaje wywołana tylko w momencie, gdy licznik osiąga wartość 0. Dodatkowo dodałem sprawdzenie czy uchwyt nie jest zerowy przed odwołaniem się do wskaźników.

Poniżej znajduję się kod poprawionej klasy szablonowej AutomaticHandle:

typedef unsigned int Dword;
 
template< typename TAG>
class AutomaticHandle
{
public:
	// Podstawowy konstruktor
	AutomaticHandle(NLib::Dword handle, MHandleMgr* manager, NLib::Dword* resCounter) : m_handle(handle), m_manager(manager), m_resCounter(resCounter)
	{ if(m_handle) ++(*m_resCounter); }
	// Konstruktor kopiujący
	AutomaticHandle(const AutomaticHandle& src) : m_manager(src.m_manager), m_handle(src.m_handle), m_resCounter(src.m_resCounter)
	{ if(m_handle) ++(*m_resCounter); }
	// Destruktor
	~AutomaticHandle()
	{
		if(m_handle)
		{ if(!(--(*m_resCounter))) m_manager->ReleaseResource(m_handle); }
	}
	// Operator przypisania
	const AutomaticHandle& operator =(const AutomaticHandle& src)
	{
		if(m_handle)
		{ if(!(--(*m_resCounter))) m_manager->ReleaseResource(m_handle); }
		m_handle = src.m_handle;
		if(m_handle) ++(*(m_resCounter = src.m_resCounter));
		return src;
	}
	// Operator równości
	bool operator ==(const AutomaticHandle& src)
	{ return m_handle == src.m_handle; }
	// Operator różności
	bool operator !=(const AutomaticHandle& src)
	{ return m_handle != src.m_handle; }
	// Operator Dword
	operator NLib::Dword() const
	{ return m_handle; }
	// Operator bool
	operator bool() const
	{ return !!m_handle; }
 
private:
	// Uchwyt
	NLib::Dword m_handle;
	// Wskaźnik do managera
	MHandleMgr* m_manager;
	// Wskaźnik do licznika zasobu
	NLib::Dword* m_resCounter;
};

Kolejną klasą jest AutoHandleMgr, czyli klasa implementująca obsługę automatycznych uchwytów. Aktualnie tylko trzy funkcje są dostępne klasie pochodnej, w tym jedna dla uchwytów. Klasa ta posiada jedną funkcję abstrakcyjną, w której po zdefiniowaniu w klasie pochodnej należy usuwać odpowiednie zasoby zgodnie z przekazanym indeksem.

Oto jej definicja:

typedef unsigned int Dword;
 
class AutoHandleMgr
{
	// Unia do wyciągania informacji z uchwytu
	enum
	{
		// Rozmiary pól
		MAX_BITS_INDEX = sizeof(NLib::Dword) * 4,
		MAX_BITS_MAGIC = sizeof(NLib::Dword) * 4,
		// Maksymalne wartości pól
		MAX_INDEX = (1 << MAX_BITS_INDEX) - 1,
		MAX_MAGIC = (1 << MAX_BITS_MAGIC) - 1
	};
 
public:
	// Funkcja do zwracania uchwytu
	void ReleaseResource(NLib::Dword handle)
	{
		NAssert(GetIndex(handle) < m_magicValues.size(), "Wrong Handle");
		NAssert(CheckMagic(handle), "Wrong handle");
		DeleteHandleAt(GetIndex(handle));
		ReleaseResourceByIndex(GetIndex(handle));
	}
 
protected:
	// Funkcja tworzy nowy uchwyt i go zwraca
	// Index zawarty w uchwycie odpowiada indeksowi w tablicy z danymi
	// w odziedziczonej klasie
	NLib::Dword CreateNewHandle()
	{
		NLib::Dword handle;
		static NLib::Dword s_autoMagic = 0;
		if(++s_autoMagic > MAX_MAGIC) s_autoMagic = 1;	// 0 oznacza pusty uchwyt
		// Jeżeli nie ma wolnych miejsc to tworze nowy index
		if(m_freeSlots.empty())
		{
			handle = m_magicValues.size() << (MAX_BITS_INDEX - 1);
			m_magicValues.push_back(s_autoMagic);
		}
		else
		{
			handle = m_freeSlots.back() << (MAX_BITS_INDEX - 1);
			m_magicValues[m_freeSlots.back()] = s_autoMagic;
			m_freeSlots.pop_back();
		}
		handle |= s_autoMagic;
		return handle;
	}
	// Funkcja zwraca index
	NLib::Dword GetIndex(NLib::Dword handle)
	{ return (handle >> (MAX_BITS_INDEX - 1)) & MAX_INDEX; }
 
private:
	// Funkcja kasuje wskazany uchwyt
	void DeleteHandleAt(NLib::Dword index)
	{	m_freeSlots.push_back(index);
		m_magicValues[index] = 0; }
	// Funkcja zwraca część magiczną
	NLib::Dword GetMagic(NLib::Dword handle)
	{ return handle & MAX_MAGIC; }
	// Funkcja sprawdzająca wartośc magic
	bool CheckMagic(NLib::Dword handle)
	{	NAssert(GetIndex(handle) < m_magicValues.size(), "Wrong Handle");
		return GetMagic(handle) == m_magicValues[GetIndex(handle)]; }
 
private:
	// Funkcja dekrementująca licznik w zasobach
	virtual void ReleaseResourceByIndex(NLib::Dword index) = 0;
 
private:
	// Vector zawierający magiczne wartości
	std::vector m_magicValues;
	// Vector zawierający indeksy z wolnymi miejscami
	std::vector m_freeSlots;
};

W przypadku tego menadżera uchwyty muszą być usunięte przed nim, inaczej pojawią się błędy z dostępem do pamięci.

Refactor silnika + automatyczne uchwyty

Właśnie mija tydzień od zakończenia dwutygodniowego pobytu w domu w czasie końcówki sesji i przerwy egzaminacyjnej. W tym czasie rozpocząłem przepisywania mojego “silnika” od nowa, tym razem w oparciu o interfejsy. Są one bardzo poręczne, gdy chce się ukryć implementację klas wewnątrz statycznych, bądź dynamicznych bibliotek. Głównym założeniem przy projektowaniu drugiej wersji NIne było właśnie ukrycie implementacji wewnątrz statycznej biblioteki .lib. Dzięki takiemu postępowaniu biblioteka wygląda ładnie i pozwala ładnie rozszerzać własną funkcjonalność.

Drugim zagadnieniem poruszanym w tej notce są uchwyty, które same zarządzają licznikiem danego zasobu.
Z początku implementację uchwytów opierałem na artykule znajdującym się w książce “Game Programming Gems”, jednak problemem okazało się zmuszenie tychże uchwytów do sprzątania po sobie zasobów. Na szczęście rozwiązanie okazało się proste, ponieważ wystarczyło umieścić w klasie uchwytu wskaźnik do menadżera zasobów.

Poniżej znajduje się moja implementacja takiego uchwytu:

template< typename TAG>
class AutomaticHandle
{
public:
	// Podstawowy konstruktor
	AutomaticHandle(Dword handle, MHandleMgr* manager) : m_handle(handle), m_manager(manager)
	{}
	// Konstruktor kopiujący
	AutomaticHandle(const AutomaticHandle& src) : m_manager(src.m_manager), m_handle(src.m_handle)
	{ m_manager->Copy(src.m_handle); }
	// Destruktor
	~AutomaticHandle()
	{ m_manager->Release(m_handle); }
	// Operator przypisania
	const AutomaticHandle& operator =(const ManagedHandle& src)
	{ m_manager->Release(m_handle);
	  m_handle = src.m_handle;
	  m_manager->Copy(src.m_handle);
	  return src; }
	// Operator Dword
	operator Dword() const
	{ return m_handle; }
	/*
	...
	*/
 
private:
	// Uchwyt
	Dword m_handle;
	// Wskaźnik do managera
	MHandleMgr* m_manager;
};

Jak widać klasa zawiera dodatkową zmienną – wskaźnik na klasę menadżera ustawiany przy konstrukcji. Klasa menadżera musi zawierać używane metody, które wewnątrz zarządzają licznikiem odwołań na dany zasób, a w momencie jego spadku do 0, likwidować dany zasób.

Interfejs IMesh, klasa Mesh i loadery.

Nawiązując do poprzedniej notki chcę tylko zamieścić jak wyglądają klasy, które napisałem i czemu są takie fajne:

// Interfejs
class IMesh
{
public:
	/*
	...
	*/
	// Funkcja aktualizująca mesha
	virtual void SetMesh(LPD3DXMESH mesh) = 0;
	// Funkcja dodająca materiał do wektora
	virtual void AddMaterial(const Material&amp; mat) = 0;
	// Funkcja zwraca materiał o podanym indeksie
	virtual Material GetMaterial(Dword index) = 0;
	/*
	...
	*/
};
 
// Główna klasa Mesh
class Mesh : public IMesh
{
public:
	// Konstruktor
	Mesh();
	// Destruktor
	~Mesh();
 
public:
	/* Funkcje używane do rysowania modelu
	...
	*/
 
protected:
	/*
	...
	*/
	// Funkcja aktualizująca mesha
	virtual void SetMesh(LPD3DXMESH mesh) = 0;
	// Funkcja dodająca materiał do wektora
	virtual void AddMaterial(const Material&amp; mat) = 0;
	// Funkcja zwraca materiał o podanym indeksie
	virtual Material GetMaterial(Dword index) = 0;
	/*
	...
	*/
};
 
// Przykład loadera
class X_file
{
public:
	// Konstruktor
	X_file(IMesh* mesh) { m_mesh = mesh; }
	// Funkcja ładująca
	RESULT LoadMesh(String filename)
	{
		LPD3DXMESH mesh;
		/*
		... cuda-niewidy...
		*/
		m_mesh->AddMesh(mesh); // Wywołanie funkcji interfejsu
	}
 
private:
	// Obiekt na którym działam
	IMesh* m_mesh;
};

Nie lubię się rozpisywać. Jak można zauważyć dzięki interfejsowi IMesh można załadować dowolny model do klasy Mesh, ponieważ są publiczne wszystkie funkcje umożliwiające to zadanie, natomiast klasa Mesh posiada publiczne funkcje służące tylko i wyłącznie do rysowania modelu. Dzięki takiemu rozwiązaniu kod jest przejrzysty.

Programowania ciąg dalszy

Minęło trochę czasu od mojej ostatniej notki, ale co poradzić, studia są trochę wymagające, więc trzeba się czasem pouczyć, zwłaszcza, że jest to styczeń czyli ostatni miesiąc przed sesją. W momencie, gdy piszę tę notkę, upływa właśnie pierwszy tydzień tej najbardziej nielubianej przez studentów pory roku :). Wracając jednak do tematyki tego devBloga należałoby powiedzieć czym ciekawym zajmowałem się przez ten czas, przecież nie samą nauką człowiek żyje.

Postanowiłem dodać do mojego silnika możliwość wczytywania różnego rodzaju plików z meshami. Na początek napisałem prostą klasę opakowującą interfejs ID3DXMesh, w której umieściłem strukturę przechowującą materiały danego mesha. Następnie napisałem funkcje automatyzujące odczyt i zapis plików w formacie .x. Schody zaczęły się, gdy chciałem dodać obsługę dodatkowego formatu – zdecydowałem, że będą to pliki .obj (Object files). OBJ są to pliki tekstowe, przechowujące informacje o całym meshu (fajna specyfikacja tego formatu znajduje się tutaj), z którymi w parze są jeszcze pliki .mtl, gdzie trzymane są informacje o materiałach dla danego subsetu (więcej info tutaj). Najważniejszą sprawą w całym tym przedsięwzięciu było napisanie parsera dla tego formatu. Na szczęście z pomocą przychodzi Microsoft DirectX SDK, w którym znajduje się przykładowy program (“MeshFromOBJ”) wczytujący pliki OBJ.

Pisząc klasy obsługujące różne modele chciałem, aby istniała możliwość konwersji między tymi formatami. Powstała więc koncepcja dwóch rodzajów klas – klasa Mesh oraz klasy obsługujące formaty. Klasy te mają być od siebie w jak największym stopniu niezależne, czyli ma istnieć możliwość dodawania funkcjonalności do klasy Mesh bez konfliktu z klasami wczytującym oraz musi istnieć możliwość pisania dodatkowych klas obsługujących różne formaty. Szczerze mówiąc, pierwszy raz zetknąłem się z takim problemem, ale na szczęście udało mi się znaleźć proste i eleganckie rozwiązanie. Mianowicie zastosowałem prosty interfejs, po którym dziedziczy klasa Mesh, z tym wyjątkiem, że klasa interfejsu posiada funkcje abstrakcyjne public, a klasa Mesh definiuje te funkcje jako protected, bądź private. Dzięki takiemu podejściu klasy korzystające z interfejsu, mogą operować na tych funkcjach, a klasa Mesh ma te funkcje ukryte :).