Úkolem je implementovat jednoduchý 3D simulátor Rubikovy kostky. Uživatel bude manipulovat s virtuální kostkou na obrazovce, může si ji prohlížet pomocí systému "Trackball" a při stisku druhého tlačítka myši na kostičku se provede otočení dané vrstvy o 90 stupňů.
Jako základ poslouží projekt 096puzzle z repository grcis. Je připravena aplikace, která umí v reálném čase vykreslovat 3D scénu (ovládání systémem "Trackball"), mezi vykreslováním se volá simulace kostky (Puzzle.Simulate()). Pro řízení Vaší simulace jsou připravena tlačítka Start / stop, Reset sim a Update sim. Při inicializaci/update simulace je možné použít uživatelem zadaný řetězec Param.
Postačí implementovat jednoduché otáčení konstantní úhlovou rychlostí, podle zájmu můžete implementovat i nějaká vylepšení (rozjezd a zpomalování), turbo režim apod.
Ovládání: levé tlačítko myši se používá pro otáčení celou scénou (Trackball). Pravé tlačítko byste měli použít k otáčení vrstvami kostky ("tahy"). Návrh konkrétních detailů ovládání je zcela na Vás, např.: klikneme-li na malý čtvereček, vyvolá se tah (otočení vrstvy). Bude třeba použít nějaká jednoduchá gesta ("cuknutí") pro rozlišení nejednoznačných příkazů na rohových kostičkách. Při kliknutí na středové čtverečky se nemusí dělat nic..
Hlavolam je implementován třídou Cube (potomek DefaultRenderObject), celá simulace je uzavřena ve třídě Puzzle.
Simulace se počítá v diskrétních časových okamžicích, jednou v každém zobrazovacím cyklu. Všechny simulované objekty (v pilotním řešení pouze Cube) mají pro tento účel deklarovanou funkci Simulate ( double time ). Objekty se musí přepočítat tak, aby jejich stav odpovídal požadovanému času time v sekundách.
Framework má vestavěné některé mechanismy pro ladění/podrobné zkoumání: tlačítka Start / stop, Reset sim a Update sim. Dále je zde checkbox Slow umožňující zpomalit simulovaný čas zadaným faktorem (implicitní nastavení: slow=0.25).
Univerzální systém (interface IRenderObject) se používá k vykreslování všech komponent
simulovaného světa. Jednotlivé objekty mohou definovat body (GL_POINTS), úsečky
(GL_LINES) a trojúhelníky (GL_TRIANGLES), pomocí kterých budou zobrazeny.
V našem případě je systém zjednodušen, používají se pouze trojúhelníky.
Je implementováno obecné dvoufázové plnění datových bufferů:
1. poprvé se zavolá metoda TriangleVertices() (TriangleIndices(),
LineVertices(), LineIndices() nebo PointVertices())
bez zadání datového pointeru, aby se jen zjistila velikost vertex-bufferu (index-bufferu)
v bytech
2. podruhé se již příslušné metody používají k vlastnímu naplnění dat, VBO buffer z grafické
karty se přitom namapuje do operační paměti aplikace metodou GL.MapBuffer()
Globální simulační objekt Puzzle implementuje vykreslování za pomoci trojúhelníků (FillTriangleData()). Stačí, když všechny Vaše simulační objekty budou korektně implementovat interface IRenderObject a nebudete muset do vykreslování více zasahovat. Pro jednoduchost projektu se nepoužívají shadery, ale lze přepínat mezi mapováním textury a barvou jednotlivých stěn (checkbox Tex).
Při ukazování myší do zobrazované 3D scény se používá klasický koncept UnProject (původně gluUnProject()). 2D souřadnice z obrazovky (vracené v událostech obsluhy myši MouseEventArgs) se doplní Z-ovou složkou a funkce Geometry.UnProject() spočítá odpovídající bod ve světovém 3D souřadnicovém systému. Z-složka může nabývat hodnot od 0.0 (bod na blízké ořezávací rovině near) do 1.0 (bod na vzdálené ořezávací rovině far nebo v nekonečnu).
Aplikace musí sama rozhodnout, na který objekt uživatel ukázal. Typicky se počítá nejbližší objekt v simulované 3D scéně, na který narazí paprek vedený obrazovkou v místě polohy myši (takový paprsek dostaneme, když spojíme "near" a "far" bod). Naše aplikace obsahuje ukázkový kód: v režimu Debug se po stisku pravého tlačítka myši spočítá takový testovací paprsek a spočte se jeho průsečík s kostkou. Krajní body úsečky jsou barevné, bod průsečíku se kreslí bílou barvou. Pro názornost je možné stiskem klávesy F (Frustum) vygenerovat 3D zobrazení zorného pole, které platilo v daný okamžik (pro jeho prohlédnutí musíme samozřejmě změnit úhel pohledu a nejlépe i zoom). Ukázka implementace této funkcionality: metody screenToWorld(), glControl1_MouseDown() a kód na konci Application_Idle(). Pilotní implementace kostky obsahuje funkci Cube.Intersect() procházející všechny trojúhelníky z modelu (cachované polohy + transformace aktuální modelovací maticí objectMatrix) a hledající nejbližší průsečík.
Obdobnou funkci jsem přidal i do projektu 086shader, kde si můžete vyzkoušet ukazování do scén načtených z disku.
Navrhuji Vám zde systém, kterým se můžete řídit při implementaci logiky simulátoru Rubikovy kostky. Nebudu se zabývat interaktivním ovládáním, tam stačí detekovat, na kterou kostičku uživatel klepne a kterým směrem ji potáhne. Doporučuji přijímat GUI vstup jenom tehdy, když se neprovádí žádná animace, bude to jednodušší.
Pro zajímavost: lze do toho zapojit i systém hlavolamu Rubikovy
kostky. Tj. složená kostka znamená, že všechny její kostičky mají
shodnou transformační matici (rotaci), triviálně třeba
jednotkovou matici.
Rozházení (zamíchání) kostky znamená konzistentní nastavení
transformací jednotlivým kostičkám. Podmínka konzistence =
"konfigurace musí být dosažitelná z iniciálníého stavu
posloupností regulérních tahů".
Proto je nejjednodušší kostku "míchat"
tak, že se vygeneruje nějaká dlouhá posloupnost
náhodných tahů a ta se virtuálně provede – bez
zobrazení a animace.
Pak můžete nechat uživatele skládat kostku a snadno po každém tahu zkontrolujete, zda je složená: transformace všech kostiček by musely být stejné! (nemusí to být jednotková matice, na natočení složené kostky v prostoru nezáleží).
Odevzdat do: 28. 2. 2022
Základ: 11 bodů (jednoduchá ale funkční simulace, možnost zadat interaktivně všechny tahy),
dalších až 9 bodů: bonus za zajímavá rozšíření (pěkný vzhled kostky /i uvnitř vypadá stejně jako originál/,
bohatší animace, míchání kostky, automatické skládání, obecnější kostka, apod.)
Visual Studio projekt: 096puzzle
Modifikujte a odevzdejte soubor: Puzzle.cs
V příslušném parametru funkce Form1.InitParams() vraťte své celé jméno.
Copyright (C) 2016-2022 J.Pelikán, last change: 2022-02-02 01:03:41 +0100 (Wed, 02 Feb 2022)