Egy effect ami arról szól, hogy egy végtelen textúrázott síklap mozog a szemlélő alatt. Vagy a szemlélő a sík felett? Ilyen mély filozófiai kérdésekbe ne menjünk bele. Maga az elv igen egyszerű és rövid úton következik a Wallpaper texture maping-ből. Ez utóbbiról pedig már volt szó. Sőt volt egy másik ugyanezen az elven alapuló progi, ami próbálta kicsit térbe levinni a dolgot. Ez akkor köztünk szólva elég nyomott lett. De majd most…

Egy kis visszatekintés:

A Wallpaper Texture maping:

Megközelítésében különbözik más texture mapingektől, nem a textúrát feszítjük valahova, hanem a képernyőt illesztjük a textúrára, ami természetesen végtelenítve van. A végtelenítéssel különösebb baj nincs is, hiszen ha a textúra 256*256-os, a kirajzolás során körbefordulnak a regiszterek (255 után 0 jön)

Probléma csak a képernyő textúrára való ráillesztésénél jelentkezhet. Hogy ez sikerüljön, csak pár dolgot kell tudnunk. Először is a paramétereket. A nézőpontot a képernyőn (ami körül később forgatjuk), és a hozzá tartozó a textúrapontot. Nos a nézőpont praktikusan a képernyő közepe, vagy esetleg a bal alsó sarka szokott lenni. A hozzá tartozó textúrapont pedig már ízlés dolga. Ezzel csak eltolást adunk meg, így nem csak forgatni tudjuk, hanem mozgatni is.

A paramétereken kívül az elvet sem árt érteni. Egy ábra:

Szóval ez az ábra azt szeretné megmutatni, hogy mi történik, ha forgatni akarjuk a textúrán a képernyőt. A képernyőt vízszintes vonalanként (Scanline) rajzoljuk ki. Egy ilyen a piros vonal. Ahhoz, hogy egy Scanline-t rajzoljunk, kell tudnunk, hogy a textura mely pontjait takarja, és ezeket a pontokat kell a képernyőre rakni. A textúra minden egyes pontját kis szürke négyzet jelképezi. (Ezért is van tele az egész háttér kis szürke négyzetekkel) 

Akkor tehát egyszerűen azokat a kis szürke négyzeteket kell a képernyőre varázsolni, amiket takar a piros vonal 

Mi sem egyszerűbb. Ugye egy pontot tudunk, ami rajta van a scanline-on ez a (Cx,Cy). Innen lépésközöket adogatva ezekhez a koordinátákhoz, mindig megkapjuk a következőt textúrapontot. Ezek a lépésközök az ábrán Xstep és Ystep. Ezek Pedig az ábráról leolvashatóan:

Xstep=M*Cos(a)
Ystep=-M*Sin(a)

Ahol az M a nagyítás (tetszőleges), "a" az elforgatás szöge. Itt a nagyítás tulajdonképpen csak akkor jelent nagyítást, ha egynél kisebb, mert csak így csökkenti a lépésköz értékét. Ellenkező esetben növeli a lépésköz értékét, és ezzel kicsinyít.

Így a következő takart pont:

X=X+Xstep
Y=Y+Ystep

Ezzel már egy Scanline-t ki lehet rajzolni, ha tudjuk az elejét a textúrán.

Mi a Scanline közepét tudjuk. Az lenne az optimális, ha az elejét tudnánk, de most szándékosan csak a közepét tudjuk. Így is ki lehet rajzolni, csak két lépésből. Kirajzoljuk jobbra, a lépésközöket hozzáadogatva, aztán balra, a lépésközöket levonogatva. 

A következő Scanline közepét ki kell számolni. Ezeket a pontokat a zöld vonal jelképezi. Amint az látszik az ábrán, merőleges a pirossal. Azaz, ami a scanline-on belül volt lépésköz, az itt is jó lesz, csak az X és Y összetevőjét fel kell cserélni.

Az előjelekkel persze értelem szerint kell bánni.

Az Ystep azért volt negatív, mert a textúrán lefelé mentünk.

Cxstep=M*sin(a)
Cystep=M*Cos(a)

Itt az M ugyanaz a nagyítás, és a szög is ugyanaz kell hogy legyen

Ez ilyen egyszerű. Az egész képernyő kirajzolása alatt ugyanaz a nagyítás és a szögelfordulás. Így a lépésközöket csak egyszer kel kiszámolni a kirajzolás előtt.

A Wallpaper maping röviden ennyi lenne.

Most akkor a Floor effekt.

Itt a képernyőt egy szintén végtelen, de döntött síkra illesztjük rá.

Abban különbözik az eddigiektől, hogy a nagyítás (M) értéke a kirajzolás alatt nem állandó. Ahogy feljebb megyünk a képernyőn egyre nagyobb. Ezzel tulajdonképpen nem nagyít, hanem kicsinyít. Így míg a képernyő alján normális a méret, ahogy feljebb megyünk egyre jobban összenyomódik a textúra. Persze mivel végtelenítve van, ugyanúgy kitölti a képernyőt. Ezzel válik térbelivé az ábra. Ami messzebb van az kisebb. És persze halványabb. Ezt egy kis árnyékolással szintén meg lehet oldani. 

Ami még különbözik, hogy van horizont. Mivel egy padló, még ha végtelen is, nem töltheti be az egész látóteret, csak a horizontig látszik. Gyakorlatilag annyit jelent, hogy csak a képernyő feléig rajzolunk, vagy még addig se.

Akkor nézzük az egyetlen sarkallatos pontot, a nagyítást. Kiszámolásához ki kell lépni a síkból, el kell képzelni térben a dolgot.

Egy Scanline-nak végig ugyanaz a mélysége. A Nagyítás pedig később a mélységtől fog függeni. De nem lineárisan hanem fordítottan arányos. Mivel mi a leképezés utánni álapotot nézzük. Elég egy tömb Distances[], amiben minden scanline-ra eltároljuk a nagyítást. A lépésközöket minden sor pixel kirajzolásakor (Scanline) újra kell számolni. Ez nem nagy ügy, össze kell szorozni a nagyítással. Persze a szögfüggvényeket előre ki kell számolni.

A nagyítások:

for i:=1 to Lines do begin   Distances[i]:=round(P*Lines/(i+20)-1);        {0-tol p-1 -ig} end;

Tehát:
Xstep=Distances[Y]*Cos(a)
Ystep=- Distances[Y]*Sin(a)

A középpontoknál már nem tudunk lépésközöket számolni, hiszen minden egyes scanline-nál újraértékelődik a helyzet, a nagyítás miatt. Itt az eddigi képletekkel újraszámoljuk a középpontot.

Cx= Distances[Y]*Sin(a)
Cy= Distances[Y]*Cos(a)

Ebből:

Xstep=Cy
Ystep=-Cx

Maga a kirajzolás ugyanúgy megy, ahogy azt a Walpaper texture mapingnél próbáltam elmagyarázni. 

Csak itt árnyékolni is kell. Hasonlóan a nagyításhoz, egy scanline-on belül mindig ugyanaz az árnyékolási fázis. Az Y-tól függ. Azaz, hogy hányadik sorban vagyunk. Ennek megfelelően elkészíthető egy olyan tömb, ami minden egyes színhez tartalmazza az öszses sor beli árnyalatát. Ez egy mátrix lesz, 255 széles, és olyan magas, ahány színt tartalmaz a textúra. Elvileg 100-as szélesség is elég lenne, hiszen 100 sorhoz elég 100 árnyalat, de így egyszerűbb lesz a címszámítás assemblyben.

Szóval a rutinnak a következő dolgok fognak kelleni:

Külső értékek:

  • A textúra. Legegyszerűbb a szegmensét átadni
  • A mélység tömb
  • Az árnyékolási tábla szegmense
  • Sin és Cos táblázatok
  • és természetesen a képernyőbuffer szegmense
És a praméterei:
  • x, és y eltolás
  • elforgatási szög

Ezekután már csak a rutint kell megírni. Természetesen assembly-ben.
Ezen a részen szoktam fölényesen átsiklani, mintha nem is lenne fontos. Pedighát ez lenne a lényeg.

Akkor talán nézzük is. 
 

Floor proc PascalArg x : WORD, y : WORD, Angle : WORD Local Lines : WORD, CosValue : WORD, SinValue : WORD Local xstep : WORD, ystep : WORD

Ez eddig a paraméterek, és lokális változók megadása. Nagyon fontos dolog, hogy ha lokális változókat használunk, a bp-t nem szabad változtatni, mert az assembler ezzel címzi őket

push es mov fs,TexSeg mov gs,ShadingTabSeg mov es,ScreenBufSeg mov di, 32160 ;kezdocim a kepernyo kozepe mov Lines,100 mov si,offset Distances mov bx,Angle add bx,bx  ;a kétszerese kell, mert word-os tomb mov ax,[offset SinTab+bx] mov SinValue,ax mov ax,[offset CosTab+bx] mov CosValue,ax;Eddig csak átvettük a szükséges adatokat és paramétereket @Yloop: mov bx,[si] ;bx=distance mov ax,bx imul SinValue mov cx,dx ;cx=Cx=distance*Sin(Angle) mov ystep,cx ;ystep=Cx mov ax,bx imul CosValue ;dx=Cy=distance*Cos(angle) xstep=-Cy mov xstep,dx ;(cx,dx) kezdopont a texturan neg ystep ;xstep,ystep lepeskoz shl cx,8 ;felszorzas 256-al (az egész rész lesz ch-ban, a
tört cl-ben)
shl dx,8 ;felszorzas 256-al (egész rész dh-ban, tört
dl-ben)
add ch,byte ptr x ;eltolas add dh,byte ptr y

És most jönnek az u.n. Innerloop-ok. Először jobbra, aztán balra. Ebben tesszük ki a pixeleket. Gyakori trükk, hogy mivel nem maradt regiszter, amivel számolhatnánk a ciklust, (pontosan 80 szor kell lefutnia), kiküszöbölik a ciklust. A rept makróval. Ekkor a fordító annyiszor egymás után leírja a ciklusmagot, ahányszor le kell futnia. Nem ugrálunk mindig vissza az elejére. A módszer hátránya, hogy a kódrészlet megnagyobbodik. Esetünkben 80-szor nagyobb lesz, mintha valódi ciklus lenne. Előnye viszont, hogy nem kell külön regiszter a számoláshoz, és még gyorsabb is lesz, hiszen megspóroltunk egy rakás jump-ot.

További trükk az is, hogy egyszerre két pixelt teszünk ki. Egyben kirakjuk az ax-et a képernyőmemóriába. Ezzel is gyorsul.

offs=0 rept 80 mov bl,ch mov bh,dh ;bx=256*Ty+Tx mov bh,fs:[bx]

Most bh-ban van a textura keresett eleme. Hiszen bh=textura[256*Ty+Tx], tehát bh=color

mov bl,byte ptr Lines mov al,gs:[bx] ;első pixel

Ez volt az árnyékolás, al=ShadingTab[256*color+Lines], al-ben van az árnyékolt szín

add cx,xstep add dx,ystep ;tovabbleptetes;Ugyanaz megegyszer: mov bl,ch mov bh,dh mov bh,fs:[bx] mov bl,byte ptr Lines mov ah,gs:[bx] ;masodik pixel add cx,xstep add dx,ystep mov es:[di+offs],ax ;kirakas offs=offs+2 endm pop dx cx offs=0Ugyanaz balra: rept 80 sub cx,xstep sub dx,ystep mov bl,ch mov bh,dh mov bh,fs:[bx] mov bl,byte ptr Lines mov ah,gs:[bx] sub cx,xstep sub dx,ystep mov bl,ch mov bh,dh mov bh,fs:[bx] mov bl,byte ptr Lines mov al,gs:[bx] offs=offs-2 mov es:[di+offs],ax endm add di,320 ;egy sorral lejjebb add si,2 dec Lines jnz @Yloop pop es ret Floor endp
Nos, minden megvan.

A megjelnítéshez a szokásos asm rutinokat használja a progi, azokon nincs sokat magyarázni.