Foundations of Videogame Programming(2020)[Sanders[9798657300802]
 9798657300802

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

Foundations of Videogame Programming Code Repository Benjamin Sanders, M. S. July 30, 2020

2

3

Other Books by Benjamin Sanders

The Bible in Original Tongues In My Heart, On My Mind: Forty Days of Biblical Prayer Four Year Mission: My Prayer Journal The Psalms in Verse: A Guitarist’s Journal Allegory and Illustration in the Book of Genesis LARP Gameworld Design: Cartographer’s Journal Role-playing Games in Physics Write Your Own Adventure The Sanders Family Cookbook

All content herein Copyright (C) 2020 Benjamin Sanders. All rights reserved.

4

Contents I

Text-based Videogames

7

1 Adventure

9

2 Game of Life

47

3 Networked Chess

53

4 Threaded Network

67

II

73

2D Videogames

5 Asteroids

75

6 Zelda

III

109

2.5-D Videogames

151

7 Image Transformations

153

8 Formula One

159

IV

3D Videogames

181

9 Sauerbraten

183

5

6

Part I Text-based Videogames

7

Chapter 1 Adventure

License Copyright (c) 1991, 1993 The Regents of the University of California.

All rights reserved.

The game adventure was originally written in Fortran by Will Crowther and Don Woods. It was later translated to C and enhanced by Jim Gillogly. This code is derived from software contributed to Berkeley by Jim Gillogly at The Rand Corporation. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ‘‘AS IS’’ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 9

10

Foundations of Videogame Programming Code Repository

SUCH DAMAGE. ADVENTURE -- Jim Gillogly, Jul 1977 This program is a re-write of ADVENT, written in FORTRAN mostly by Don Woods of SAIL. In most places it is as nearly identical to the original as possible given the language and word-size differences. A few places, such as the message arrays and travel arrays were changed to reflect the smaller core size and word size. The labels of the original are reflected in this version, so that the comments of the fortran are still applicable here. The data file distributed with the fortran source is assumed to be called "glorkz" in the directory where the program is first run. The original FORTRAN version can be found at .

Makefile # #

$NetBSD : Makefile , v 1.14 2013/02/16 16:30:28 j m c n e i l l Exp $ @( # ) Makefile 8.1 ( Berkeley ) 6/12/93

PROG= adventure SRCS= main . c i n i t . c done . c save . c subr . c vocab . c wizard . c i o . c data . c crc .c MAN= adventure .6 HIDEGAME=hidegame CLEANFILES+=mkdata setup . l o data . c data . c : glorkz mkdata ${ MKTARGET CREATE} ./mkdata $ { .CURDIR}/ glorkz > data . c setup . l o : hdr . h mkdata : setup . l o ${ MKTARGET LINK} ${HOST LINK . c} −o $ { .TARGET} $ { .ALLSRC} . include

main.c /∗

$NetBSD : main . c , v 1.21 2009/08/25 06:56:52 dholland Exp $

#include #ifndef l i n t COPYRIGHT ( ”@( # ) Copyright ( c ) 1991, 1993\ The Regents o f the University o f C a l i f o r n i a . #endif /∗ not l i n t ∗/

∗/

A l l r i g h t s reserved . ” ) ;

#ifndef l i n t #if 0 s t a t i c char sccsid [ ] = ”@( # ) main . c 8.1 ( Berkeley ) 6/2/93” ; #e l s e RCSID ( ”$NetBSD : main . c , v 1.21 2009/08/25 06:56:52 dholland Exp $” ) ; #endif #endif /∗ not l i n t ∗/ /∗

Re−coding o f advent in C: main program ∗/

main.c

#include #include #include #include #include #include #include #include





” hdr . h” ” extern . h”

int main ( i n t argc , char ∗∗argv ) { int i; int rval , l l ; struct t e x t ∗kk ; /∗ revoke s e t g i d p r i v i l e g e s from dm ∗/ setgid ( getgid ( ) ) ; init ( ) ; /∗ I n i t i a l i z e everything ∗/ s i g n a l ( SIGINT , trapdel ) ; i f ( argc > 1) { /∗ Restore f i l e s p e c i f i e d ∗/ /∗ Restart i s l a b e l 8305 ( Fortran ) ∗/ i = r e s t o r e ( argv [ 1 ] ) ; /∗ See what we ’ ve got ∗/ switch ( i ) { case 0: /∗ The r e s t o r e worked f i n e ∗/ yea = Start ( ) ; k = null ; unlink ( argv [ 1 ] ) ; /∗ Don ’ t re−use the save ∗/ goto l8 ; /∗ Get where we ’ re going ∗/ case 1: /∗ Couldn ’ t open i t ∗/ errx ( 1 , ” can ’ t open f i l e ” ) ; /∗ So g i v e up ∗/ case 2: /∗ Oops −− f i l e was a l t e r e d ∗/ rspeak (202) ; /∗ You d i s s o l v e ∗/ exit ( 1 ) ; /∗ F i l e could be non−adventure ∗/ } /∗ So don ’ t unlink i t . ∗/ } startup ( ) ; /∗ prepare f o r a user ∗/ for ( ; ; ) { /∗ main command loop ( l a b e l 2) ∗/ i f ( newloc < 9 && newloc ! = 0 && i s c l o s i n g ) { rspeak (130) ; /∗ i f c l o s i n g leave only by ∗/ newloc = l o c ; /∗ main o f f i c e ∗/ i f ( ! panic ) clock2 = 15; panic = TRUE; } r v a l = fdwarf ( ) ; /∗ dwarf s t u f f ∗/ i f ( r v a l == 99) die ( 9 9 ) ; l2000 :

i f ( l o c == 0) die ( 9 9 ) ; /∗ l a b e l 2000 ∗/ kk = &s t e x t [ l o c ] ; i f ( ( abb [ l o c ] % abbnum) == 0 || kk−>seekadr == 0) kk = &l t e x t [ l o c ] ; i f ( ! forced ( l o c ) && dark ( ) ) { i f ( wasdark && pct ( 3 5 ) ) { die ( 9 0 ) ; goto l2000 ; } kk = &r t e x t [ 1 6 ] ; }

#if 0 l2001 : #endif i f ( t o t i n g ( bear ) ) rspeak (141) ; /∗ 2001 ∗/ speak ( kk ) ; k = 1; i f ( forced ( l o c ) ) goto l8 ; i f ( l o c == 33 && pct ( 2 5 ) && ! i s c l o s i n g ) rspeak ( 8 ) ; i f ( ! dark ( ) ) { abb [ l o c ] + + ; f o r ( i = a t l o c [ l o c ] ; i ! = 0; i = l i n k s [ i ] ) { /∗ 2004 ∗/ obj = i ; i f ( obj > 100) obj −= 100; i f ( obj == steps && t o t i n g ( nugget ) ) continue ; i f ( prop [ obj ] < 0) { i f ( closed ) continue ; prop [ obj ] = 0; i f ( obj == rug || obj == chain ) prop [ obj ] = 1; t a l l y −−; i f ( t a l l y == t a l l y 2 && t a l l y ! = 0) i f ( l i m i t > 35) l i m i t = 35;

} l l = prop [ obj ] ; /∗ 2006 ∗/ i f ( obj == steps && l o c == f i x e d [ steps ] ) l l = 1; pspeak ( obj , l l ) ; } /∗ 2008 ∗/ goto l2012 ; l2009 : k = 54; /∗ 2009 ∗/ l2010 : spk = k ; l2011 : rspeak ( spk ) ; } l2012 : verb = 0; /∗ 2012 ∗/ obj = 0; l2600 : checkhints ( ) ; /∗ to 2600−2602 ∗/ i f ( closed ) { i f ( prop [ oyster ] < 0 && t o t i n g ( oyster ) ) pspeak ( oyster , 1) ; f o r ( i = 1; i < 100; i ++) i f ( t o t i n g ( i ) && prop [ i ] < 0) /∗ 2604 ∗/ prop [ i ] = −1 − prop [ i ] ; } wasdark = dark ( ) ; /∗ 2605 ∗/ i f ( knfloc > 0 && knfloc ! = l o c ) knfloc = 1; g e t i n (&wd1, &wd2) ; i f ( delhit ) { /∗ user typed a DEL ∗/ d e l h i t = 0; /∗ r e s e t counter ∗/ copystr ( ” quit ” , wd1) ; /∗ pretend he ’ s q u i t t i n g ∗/ ∗wd2 = 0; } l2608 : i f ( ( foobar = −foobar ) > 0) foobar = 0; /∗ 2608 ∗/ /∗ should check here f o r ” magic mode” ∗/ turns ++; i f ( demo && turns >= SHORT) done ( 1 ) ; /∗ to 13000 ∗/ i f ( verb == say && ∗wd2 ! = 0) verb = 0; i f ( verb == say ) goto l4090 ; i f ( t a l l y == 0 && l o c >= 15 && l o c ! = 33) clock1−−; i f ( clock1 == 0) { closing ( ) ; /∗ to 10000 ∗/ goto l19999 ; } i f ( clock1 < 0) clock2−−; i f ( clock2 == 0) { caveclose ( ) ; /∗ to 11000 ∗/ continue ; /∗ back to 2 ∗/ } i f ( prop [ lamp ] == 1) l i m i t−−; i f ( l i m i t 0; j−−) i f ( f i x d [ j ] > 0) { drop ( j + 100, f i x d [ j ] ) ; drop ( j , plac [ j ] ) ; } f o r ( j = 100; j > 0; j−−) { fixed [ j ] = fixd [ j ] ; i f ( plac [ j ] ! = 0 && f i x d [ j ] c r c v a l >> 24 ˆ udata [ pos + + ] ) & 0 x f f ; i f ( x == 0) { x = c−>step ++; i f ( c−>step >= arraycount ( crctab ) ) { c−>step = 0; } } c−>c r c v a l = ( c−>c r c v a l c r c v a l ; }

io.c /∗

$NetBSD : i o . c , v 1.22 2009/08/25 06:56:52 dholland Exp $

∗/

#include #ifndef l i n t #if 0 s t a t i c char sccsid [ ] = ”@( # ) i o . c 8.1 ( Berkeley ) 5/31/93” ; #e l s e RCSID ( ”$NetBSD : i o . c , v 1.22 2009/08/25 06:56:52 dholland Exp $” ) ; #endif #endif /∗ not l i n t ∗/ /∗

Re−coding o f advent in C: f i l e i /o and user i /o ∗/

#include #include #include #include #include



” hdr . h”

0x18b74777 , 0x8f659eff , 0xd70dd2ee , 0x4969474d , 0x37d83bf0 , 0xbdbdf21c , 0xcdd70693 , 0x5d681b02 , 0x2d02ef8d

/∗ ∗ crc −− ∗ Compute a POSIX.2 checksum . This routine modified by Jim Gillogly ∗ to work on sequential data rather than on a f i l e . Initial call to ∗ c r c s t a r t i n i t i a l i z e s the sum, and subsequent c a l l s to crc update ∗ it . ∗/

#include ” extern . h” s t a t i c const uint32 t crctab [256] = { 0x7fffffff , 0x77073096 , 0xee0e612c , 0x990951ba , 0xe963a535 , 0x9e6495a3 , 0x0edb8832 , 0x97d2d988 , 0x09b64c2b , 0x7eb17cbd , 0x1db71064 , 0x6ab020f2 , 0xf3b97148 , 0x6ddde4eb , 0xf4d4b551 , 0x83d385c7 , 0xfd62f97a , 0x8a65c9ec , 0x14015c4f , 0x8d080df5 , 0x3b6e20c8 , 0x4c69105e , 0x3c03e4d1 , 0x4b04d447 , 0xd20d85fd , 0x42b2986c , 0xdbbbc9d6 , 0xacbcf940 , 0xdcd60dcf , 0xabd13d59 , 0x26d930ac , 0xbfd06116 , 0x21b4f4b5 , 0x56b3c423 , 0x2802b89e , 0x5f058808 , 0xc60cd9b2 , 0x58684c11 , 0xc1611dab , 0xb6662d3d , 0x98d220bc , 0xefd5102a , 0x71b18589 , 0xe8b8d433 , 0x7807c9a2 , 0x0f00f934 , 0x7f6a0dbb , 0x086d3d2d , 0x91646c97 , 0x1c6c6162 , 0x856530d8 , 0xf262004e , 0x8208f4c1 , 0xf50fc457 , 0x65b0d9c6 , 0xfcb9887c , 0x62dd1ddf , 0x15da2d49 , 0x4db26158 , 0x3ab551ce , 0xa3bc0074 , 0x3dd895d7 , 0xa4d1c46d , 0xd3d6f4fb , 0xad678846 , 0xda60b8d0 , 0x44042d73 , 0xdd0d7cc9 , 0x5005713c , 0x270241aa , 0x5768b525 , 0x206f85b3 , 0xb966d409 , 0x29d9c998 , 0xb0d09822 , 0xc7d7a8b4 , 0xb7bd5c3b , 0xc0ba6cad , 0xedb88320 , 0x74b1d29a , 0xead54739 , 0x9dd277af , 0xe3630b12 , 0x94643b84 , 0x0d6d6a3e , 0x9309ff9d , 0x0a00ae27 , 0x7d079eb1 , 0x1e01f268 , 0x6906c2fe , 0xf762575d , 0x6e6b06e7 , 0xfed41b76 , 0x89d32be0 , 0xf9b9df6f , 0x8ebeeff9 , 0x17b7be43 , 0xa1d1937e , 0x38d8c2c4 , 0x4fdff252 , 0x3fb506dd , 0x48b2364b , 0xd80d2bda , 0x41047a60 , 0xdf60efc3 , 0xa867df55 , 0xcb61b38c , 0xbc66831a , 0x256fd2a0 , 0xbb0b4703 , 0x220216b9 , 0x5505262f , 0x2bb45a92 , 0x5cb36a04 , 0xc2d7ffa7 , 0x5bdeae1d , 0x9b64c2b0 , 0xec63f226 , 0x9c0906a9 , 0xeb0e363f , 0x72076785 , 0xe2b87a14 , 0x7bb12bae , 0x0cb61b38 , 0x7cdcefb7 , 0x0bdbdf21 , 0x86d3d2d4 ,

0x1fda836e , 0x88085ae6 , 0xf862ae69 , 0x4e048354 , 0x3e6e77db , 0xa9bcae53 , 0xcabac28a , 0x54de5729 , 0x2a6f2b94 ,

#include ” extern . h” static static static static static static static static static

i n t next ( void ) ; void rdesc ( i n t ) ; void r d e f a u l t ( void ) ; void rhints ( void ) ; void r l i q ( void ) ; void r l o c s ( void ) ; i n t rnum( void ) ; void r t r a v ( void ) ; void rvoc ( void ) ;

/∗ get command from user ∗/ /∗ no prompt, usually ∗/ void g e t i n ( char ∗∗wrd1 , char ∗∗wrd2 ) { char ∗s ; s t a t i c char wd1buf [MAXSTR] , wd2buf [MAXSTR] ;

18 int

Foundations of Videogame Programming Code Repository f i r s t , numch, c ;

∗wrd1 = wd1buf ; /∗ return ptr to i n t e r n a l s t r ∗/ ∗wrd2 = wd2buf ; wd2buf [ 0 ] = 0; /∗ in case i t isn ’ t set here ∗/ f o r ( s = wd1buf , f i r s t = 1 , numch = 0 ; ; ) { c = getchar ( ) ; i f ( ( ∗ s = ( char ) c ) >= ’ A ’ && ∗s = MAXSTR) { /∗ s t r i n g too long ∗/ printf ( ” Give me a break ! ! \n” ) ; wd1buf [ 0 ] = wd2buf [ 0 ] = 0; FLUSHLINE; return ; } s ++; } }

mspeak ( z ) ; return ( r e s u l t ) ; } /∗ FILE ∗inbuf ,∗outbuf ; ∗/ s t a t i c char ∗i n p t r ;

/∗ Pointer i n t o v i r t u a l disk

s t a t i c i n t outsw = 0; s t a t i c const char ; s t a t i c const char

∗/ ∗/

/∗ putting s t u f f to data f i l e ?

iotape [ ] = ”Ax3F’\003tt$8h\315qer∗h\017nGKrX\207:! l ” ∗tape = iotape ;

/∗ next v i r t u a l char , bump adr static int next ( void ) { int ch ;

∗/

/∗ pointer to encryption tape

∗/

ch = (∗ i n p t r ˆ random ( ) ) & 0xFF ; /∗ Decrypt input data ∗/ i f ( outsw ) { /∗ putting data in tmp f i l e ∗/ i f (∗ tape == 0) tape = iotape ; /∗ rewind encryption tape ∗/ ∗i n p t r = ch ˆ ∗tape ++; /∗ re−encrypt and replace value ∗/ } i n p t r ++; return ( ch ) ; } s t a t i c char breakch ;

/∗ t e l l which char ended rnum

∗/

/∗ ” read ” data from v i r t u a l f i l e ∗/ void rdata ( void ) { int sect ; char ch ; inptr = d a t a f i l e ; srandom (SEED) ;

/∗ Pointer to v i r t u a l data f i l e ∗/ /∗ which i s l i g h t l y encrypted . ∗/

} /∗ confirm with rspeak ∗/ int yes ( i n t x , i n t y , i n t z ) { int r e s u l t = TRUE; /∗ p a c i f y gcc ∗/ int ch ; for ( ; ; ) { rspeak ( x ) ; /∗ t e l l him what we want ∗/ i f ( ( ch = getchar ( ) ) == ’ y ’ ) r e s u l t = TRUE; else i f ( ch == ’ n ’ ) r e s u l t = FALSE; else i f ( ch == EOF) { printf ( ” user closed input stream , q u i t t i n g . . . \ n” ) ; exit ( 0 ) ; } FLUSHLINE; i f ( ch == ’ y ’ || ch == ’ n ’ ) break ; printf ( ” Please answer the question .\n” ) ; } i f ( r e s u l t == TRUE) rspeak ( y ) ; i f ( r e s u l t == FALSE ) rspeak ( z ) ; return ( r e s u l t ) ; } /∗ confirm with mspeak ∗/ int yesm ( i n t x , i n t y , i n t z ) { int r e s u l t = TRUE; /∗ p a c i f y gcc ∗/ int ch ; for ( ; ; ) { mspeak ( x ) ; /∗ t e l l him what we want ∗/ i f ( ( ch = getchar ( ) ) == ’ y ’ ) r e s u l t = TRUE; else i f ( ch == ’ n ’ ) r e s u l t = FALSE; else i f ( ch == EOF) { printf ( ” user closed input stream , q u i t t i n g . . . \ n” ) ; exit ( 0 ) ; } FLUSHLINE; i f ( ch == ’ y ’ || ch == ’ n ’ ) break ; printf ( ” Please answer the question .\n” ) ; } i f ( r e s u l t == TRUE) mspeak ( y ) ; i f ( r e s u l t == FALSE )

classes = 1; for ( ; ; ) { /∗ read data sections ∗/ sect = next ( ) − ’ 0 ’ ; /∗ 1s t d i g i t o f section number ∗/ # i f d e f VERBOSE printf ( ” Section %c ” , sect + ’ 0 ’ ) ; #endif i f ( ( ch = next ( ) ) ! = LF ) { /∗ i s there a second d i g i t ? FLUSHLF; # i f d e f VERBOSE putchar ( ch ) ; #endif sect = 10 ∗ sect + ch − ’ 0 ’ ; } # i f d e f VERBOSE putchar ( ’ \n ’ ) ; #endif switch ( sect ) { case 0: /∗ f i n i s h e d reading database ∗/ return ; case 1: /∗ long form descriptions ∗/ rdesc ( 1 ) ; break ; case 2: /∗ short form descriptions ∗/ rdesc ( 2 ) ; break ; case 3: /∗ t r a v e l t a b l e ∗/ rtrav ( ) ; break ; case 4: /∗ vocabulary ∗/ rvoc ( ) ; break ; case 5: /∗ o b j e c t descriptions ∗/ rdesc ( 5 ) ; break ; case 6: /∗ a r b i t r a r y messages ∗/ rdesc ( 6 ) ; break ; case 7: /∗ o b j e c t l o c a t i o n s ∗/ rlocs ( ) ; break ; case 8: /∗ action defaults ∗/ rdefault ( ) ; break ; case 9: /∗ l i q u i d assets ∗/ rliq () ; break ; case 10: /∗ class messages ∗/ rdesc ( 1 0 ) ; break ; case 11: /∗ hints ∗/ rhints ( ) ; break ; case 12: /∗ magic messages ∗/

∗/

io.c rdesc ( 1 2 ) ; break ; default : printf ( ” I n v a l i d data section number : %d\n” , sect ) ; for ( ; ; ) putchar ( next ( ) ) ; } i f ( breakch ! = LF ) /∗ routines return a f t e r ”−1” ∗/ FLUSHLF; }

19

/∗ read t r a v e l t a b l e ∗/ s t a t i c void r t r a v ( void ) { int locc ; struct t r a v l i s t ∗t = NULL; char ∗s ; char buf [ 1 2 ] ; int len , m, n , e n t r i e s = 0;

} s t a t i c char nbf [ 1 2 ] ; /∗ read i n i t i a l l o c a t i o n num ∗/ static int rnum( void ) { char ∗s ; tape = iotape ; /∗ r e s t a r t encryption tape ∗/ f o r ( s = nbf , ∗s = 0 ; ; s ++) i f ( ( ∗ s = next ( ) ) == TAB || ∗s == ’\n ’ || ∗s == LF ) break ; breakch = ∗s ; /∗ save char f o r r t r a v ( ) ∗/ ∗s = 0; /∗ got the number as a s c i i ∗/ i f ( nbf [ 0 ] == ’ − ’) return (−1) ; /∗ end o f data ∗/ return ( a t o i ( nbf ) ) ; /∗ convert i t to i n t e g e r ∗/ } s t a t i c char ∗seekhere ; /∗ read description−format msgs ∗/ s t a t i c void rdesc ( i n t sect ) { int locc ; char ∗seekstart , ∗maystart ; seekhere = i n p t r ; /∗ Where are we in v i r t u a l f i l e ? ∗/ outsw = 1; /∗ these msgs go i n t o tmp f i l e ∗/ f o r ( o l d l o c = −1, seekstart = seekhere ; ; ) { maystart = i n p t r ; /∗ maybe s t a r t i n g new entry ∗/ i f ( ( l o c c = rnum ( ) ) ! = o l d l o c && o l d l o c >= 0 /∗ f i n i s h e d msg ∗/ /∗ unless sect 5 ∗/ && ! ( sect == 5 && ( l o c c == 0 || l o c c >= 100) ) ) { switch ( sect ) { /∗ now put i t i n t o r i g h t t a b l e ∗/ case 1:/∗ long descriptions ∗/ l t e x t [ o l d l o c ] . seekadr = seekhere ; l t e x t [ o l d l o c ] . t x t l e n = maystart − seekstart ; break ; case 2:/∗ short descriptions ∗/ s t e x t [ o l d l o c ] . seekadr = seekhere ; s t e x t [ o l d l o c ] . t x t l e n = maystart − seekstart ; break ; case 5:/∗ o b j e c t descriptions ∗/ ptext [ o l d l o c ] . seekadr = seekhere ; ptext [ o l d l o c ] . t x t l e n = maystart − seekstart ; break ; case 6:/∗ random messages ∗/ i f ( o l d l o c >= RTXSIZE ) errx ( 1 , ” Too many random msgs” ) ; r t e x t [ o l d l o c ] . seekadr = seekhere ; r t e x t [ o l d l o c ] . t x t l e n = maystart − seekstart ; break ; case 10: /∗ class messages ∗/ c t e x t [ classes ] . seekadr = seekhere ; c t e x t [ classes ] . t x t l e n = maystart − seekstart ; cval [ classes ++] = o l d l o c ; break ; case 12: /∗ magic messages ∗/ i f ( o l d l o c >= MAGSIZE) errx ( 1 , ” Too many magic msgs” ) ; mtext [ o l d l o c ] . seekadr = seekhere ; mtext [ o l d l o c ] . t x t l e n = maystart − seekstart ; break ; default : errx ( 1 , ” rdesc c a l l e d with bad section ” ) ; } seekhere += maystart − seekstart ; } i f ( l o c c < 0) { outsw = 0; /∗ turn o f f output ∗/ seekhere += 3; /∗ −1 ∗/ return ; } i f ( sect ! = 5 || ( lo c c > 0 && l o c c < 100) ) { i f ( oldloc != locc ) /∗ s t a r t i n g a new message ∗/ seekstart = maystart ; oldloc = locc ; } FLUSHLF; /∗ scan the l i n e ∗/ } }

f o r ( o l d l o c = −1;;) { /∗ get another l i n e ∗/ /∗ end o f entry ∗/ i f ( ( l o c c = rnum ( ) ) ! = o l d l o c && o l d l o c >= 0 && t ) { t−>next = 0; /∗ terminate the old entry ∗/ /∗ printf ( ”%d:%d e n t r i e s\n” , oldloc , e n t r i e s ) ; ∗/ /∗ t w r i t e ( o l d l o c ) ; ∗/ } i f ( l o c c == −1) return ; i f ( locc != oldloc ) { /∗ g e t t i n g a new entry ∗/ t = t r a v e l [ l o c c ] = c a l l o c ( 1 , s i z e o f (∗ t ) ) ; i f ( t == NULL) e r r ( 1 , NULL) ; /∗ printf ( ”New t r a v e l l i s t f o r %d\n” , l o c c ) ; ∗/ e n t r i e s = 0; oldloc = locc ; } f o r ( s = buf ; ; s ++) /∗ get the newloc number /ASCII ∗/ i f ( ( ∗ s = next ( ) ) == TAB || ∗s == LF ) break ; ∗s = 0; len = length ( buf ) − 1; /∗ quad long number handling ∗/ /∗ printf ( ” Newloc : %s (%d chars )\n” , buf , len ) ; ∗/ i f ( len < 4) { /∗ no ”m” conditions ∗/ m = 0; n = a t o i ( buf ) ; /∗ newloc mod 1000 = newloc ∗/ } else { /∗ a long i n t e g e r ∗/ n = a t o i ( buf + len − 3) ; buf [ len − 3] = 0; /∗ terminate newloc/1000 ∗/ m = a t o i ( buf ) ; } while ( breakch ! = LF ) { /∗ only do one l i n e at a time ∗/ i f ( t == NULL) abort ( ) ; i f ( e n t r i e s ++) { t−>next = c a l l o c ( 1 , s i z e o f (∗ t ) ) ; i f ( t−>next == NULL) e r r ( 1 , NULL) ; t = t−>next ; } t−>tverb = rnum ( ) ; /∗ get verb from the f i l e ∗/ t−>t l o c = n ; /∗ t a b l e entry mod 1000 ∗/ t−>conditions = m; /∗ t a b l e entry / 1000 ∗/ /∗ printf ( ” entry %d f o r %d\n” , entries , l o c c ) ; ∗/ } } } # i f d e f DEBUG /∗ t r a v e l options from t h i s l o c ∗/ void t w r i t e ( i n t loq ) { struct t r a v l i s t ∗t ; printf ( ” I f ” ) ; speak(& l t e x t [ loq ] ) ; printf ( ” then\n” ) ; f o r ( t = t r a v e l [ loq ] ; t ! = 0; t = t−>next ) { printf ( ” verb %d takes you to ” , t−>tverb ) ; i f ( t−>t l o c t l o c ] ) ; else i f ( t−>t l o c t l o c − 300) ; else rspeak ( t−>t l o c − 500) ; printf ( ” under conditions %d\n” , t−>conditions ) ; } } #endif /∗ DEBUG ∗/ /∗ read the vocabulary ∗/ s t a t i c void rvoc ( void ) { char ∗s ; int idx ; char buf [ 6 ] ; for ( ; ; ) { idx = rnum ( ) ; i f ( idx < 0) break ; f o r ( s = buf , ∗s = 0 ; ; s ++)

/∗ get the word

∗/

20

Foundations of Videogame Programming Code Repository i f ( ( ∗ s = next ( ) ) == TAB || ∗s == ’\n ’ || ∗s == LF || ∗s == ’ ’ ) break ; /∗ terminate word with newline , LF , tab , blank ∗/ i f (∗s ! = ’\n ’ && ∗s ! = LF ) FLUSHLF;/∗ can be comments ∗/ ∗s = 0; /∗ printf ( ”\”%s\”=%d\n” , buf , idx ) ; ∗/ vocab ( buf , −2, idx ) ;

void mspeak ( i n t msg ) { i f ( msg ! = 0) speak(&mtext [ msg ] ) ; }

/∗ read , decrypt , and print a message ( not ptext ) ∗/ /∗ msg i s a pointer to seek address and length o f mess ∗/ void speak ( const struct t e x t ∗msg ) { char ∗s , n o n f i r s t ;

} /∗ }

prht ( ) ;

∗/

/∗ i n i t i a l o b j e c t l o c a t i o n s ∗/ s t a t i c void r l o c s ( void ) { for ( ; ; ) { i f ( ( obj = rnum ( ) ) < 0) break ; plac [ obj ] = rnum ( ) ; /∗ i n i t i a l l o c f o r t h i s obj i f ( breakch == TAB) /∗ there ’ s another entry f i x d [ obj ] = rnum ( ) ; else f i x d [ obj ] = 0; } } /∗ d e f a u l t verb messages s t a t i c void r d e f a u l t ( void ) { for ( ; ; ) { i f ( ( verb = rnum ( ) ) < 0) break ; actspeak [ verb ] = rnum ( ) ; } }

s = msg−>seekadr ; n o n f i r s t = 0; while ( s − msg−>seekadr < msg−>t x t l e n ) { /∗ read a l i n e at a time ∗/ tape = iotape ; /∗ r e s t a r t decryption tape ∗/ while ( ( ∗ s++ ˆ ∗tape ++) ! = TAB) ; /∗ read past l o c num ∗/ /∗ assume tape i s longer than l o c a t i o n number ∗/ /∗ plus the lookahead put together ∗/ i f ( ( ∗ s ˆ ∗tape ) == ’>’ && ( ∗ ( s + 1) ˆ ∗( tape + 1) ) == ’ $ ’ && ( ∗ ( s + 2) ˆ ∗( tape + 2) ) == ’ < ’) break ; i f ( b l k l i n && ! n o n f i r s t ++) putchar ( ’ \n ’ ) ; do { i f (∗ tape == 0) tape = iotape ; /∗ rewind decryp tape ∗/ putchar(∗s ˆ ∗tape ) ; } while ( ( ∗ s++ ˆ ∗tape ++) ! = LF ) ; /∗ b e t t e r end with LF ∗/ }

∗/ ∗/

∗/

/∗ l i q u i d assets &c : cond b i t s ∗/ s t a t i c void r l i q ( void ) { int bitnum ; for ( ; ; ) { /∗ read new b i t l i s t i f ( ( bitnum = rnum ( ) ) < 0) break ; for ( ; ; ) { /∗ read l o c s f o r b i t s i n t n = rnum ( ) ; i f ( n < 0) break ; cond [ n ] |= s e t b i t [ bitnum ] ; i f ( breakch == LF ) break ; } } }

}

∗/

/∗ read , decrypt and print a ptext message ∗/ /∗ msg i s the number o f a l l the p msgs f o r t h i s place ∗/ /∗ assumes o b j e c t 1 doesn ’ t have prop 1 , obj 2 no prop 2 &c ∗/ void pspeak ( i n t m, i n t skip ) { char ∗s , n o n f i r s t ; char ∗numst ; struct t e x t ∗msg; char ∗tbuf ;

∗/

msg = &ptext [m] ; i f ( ( tbuf = ( char ∗) malloc ( msg−>t x t l e n + 1) ) == NULL) e r r ( 1 , NULL) ; memcpy( tbuf , msg−>seekadr , msg−>t x t l e n + 1) ; /∗ Room to null ∗/ s = tbuf ; n o n f i r s t = 0; while ( s − tbuf < msg−>t x t l e n ) { /∗ read l i n e at a time ∗/ tape = iotape ; /∗ r e s t a r t decryption tape ∗/ f o r ( numst = s ; (∗s ˆ= ∗tape ++) ! = TAB; s ++) ; /∗ get number /∗ Temporarily trash the s t r i n g ( cringe ) ∗/ ∗s++ = 0; /∗ decrypting number within the s t r i n g

s t a t i c void rhints ( void ) { int hintnum , i ; hintmax = 0; for ( ; ; ) { i f ( ( hintnum = rnum ( ) ) < 0) break ; f o r ( i = 1; i < 5; i ++) hints [ hintnum ] [ i ] = rnum ( ) ; i f ( hintnum > hintmax ) hintmax = hintnum ; } }

∗/

i f ( a t o i ( numst ) ! = 100 ∗ skip && skip >= 0) { while ( ( ∗ s++ ˆ ∗tape ++) ! = LF ) /∗ flush the l i n e ∗/ i f (∗ tape == 0) tape = iotape ; continue ; } i f ( ( ∗ s ˆ ∗tape ) == ’>’ && ( ∗ ( s + 1) ˆ ∗( tape + 1) ) == ’ $ ’ && ( ∗ ( s + 2) ˆ ∗( tape + 2) ) == ’ < ’) break ; i f ( b l k l i n && ! n o n f i r s t ++) putchar ( ’ \n ’ ) ; do { i f (∗ tape == 0) tape = iotape ; putchar(∗s ˆ ∗tape ) ; } while ( ( ∗ s++ ˆ ∗tape ++) ! = LF ) ; /∗ b e t t e r end with LF ∗/ i f ( skip < 0) break ;

void rspeak ( i n t msg ) { i f ( msg ! = 0) speak(& r t e x t [ msg ] ) ; }

} f r e e ( tbuf ) ; }

save.c

∗/

save.c /∗

$NetBSD : save . c , v 1.14 2014/03/22 22:04:40 dholland Exp $

∗/

”Hmm. The name \”%s\” appears to be magically blocked .\n” , name) ; return NULL;

#include #ifndef l i n t #if 0 s t a t i c char sccsid [ ] = ”@( # ) save . c 8.1 ( Berkeley ) 5/31/93” ; #e l s e RCSID ( ”$NetBSD : save . c , v 1.14 2014/03/22 22:04:40 dholland Exp $” ) ; #endif #endif /∗ not l i n t ∗/ #include #include #include #include #include #include #include





#include ” hdr . h” #include ” extern . h” struct s a v e f i l e { FILE ∗f ; const char ∗name; bool warned ; s i z e t bintextpos ; uint32 t key ; struct c r c s t a t e crc ; unsigned char pad [ 8 ] ; unsigned padpos ; }; #define BINTEXT WIDTH 60 #define FORMAT VERSION 2 #define FORMAT VERSION NOSUM 1 s t a t i c const char header [ ] = ” Adventure save f i l e \n” ;

} sf−>name = name; sf−>warned = f a l s e ; sf−>bintextpos = 0; sf−>key = 0; c r c s t a r t (& sf−>crc ) ; memset ( sf−>pad , 0 , s i z e o f ( sf−>pad ) ) ; sf−>padpos = 0; return s f ; } /∗ ∗ Raw read . ∗/ static int s a v e f i l e r a w r e a d ( struct s a v e f i l e ∗sf , void ∗data , s i z e t len ) { s i z e t result ; r e s u l t = fread ( data , 1 , len , sf−>f ) ; i f ( r e s u l t ! = len || f e r r o r ( sf−>f ) ) { f p r i n t f ( stderr , ”Oops : e r r o r reading %s.\n” , sf−>name) ; sf−>warned = true ; return 1; } return 0; } /∗ ∗ Raw write . ∗/ static int s a v e f i l e r a w w r i t e ( struct s a v e f i l e ∗sf , const void ∗data , s i z e t len ) { s i z e t result ;

//////////////////////////////////////////////////////////// // base16 output encoding

r e s u l t = f w r i t e ( data , 1 , len , sf−>f ) ; i f ( r e s u l t ! = len || f e r r o r ( sf−>f ) ) { f p r i n t f ( stderr , ”Oops : e r r o r w r i t i n g %s.\n” , sf−>name) ; sf−>warned = true ; return 1; } return 0;

/∗ ∗ Map 16 plain values i n t o 90 coded values and back . ∗/ s t a t i c const char coding [ 9 0 ] = ”Db.GOyT]7 a6zpF ( c∗5H9oK˜ 0 [WVAg&kR ) ml, 2 ˆ q−1Y3v+ ” ”X/=JirZL$C> N?:}B{dfnsxUbintextpos > 0) { s a v e f i l e r a w w r i t e ( sf , ”\n” , 1) ; } r e t = 0; i f ( f c l o s e ( sf−>f ) ) { i f ( ! sf−>warned ) { f p r i n t f ( stderr , ”Oops : e r r o r on %s.\n” , sf−>name) ; } r e t = 1; } free ( sf ) ; return r e t ;

} s t a t i c char w r i t e l e t t e r ( unsigned char nibble ) { unsigned code ; assert ( nibble < 16) ; do { code = (16 ∗ ( random ( ) % 6) ) + nibble ; } while ( code >= 90) ; return coding [ code ] ; } //////////////////////////////////////////////////////////// // s a v e f i l e /∗ ∗ Open a s a v e f i l e . ∗/ s t a t i c struct s a v e f i l e ∗ s a v e f i l e o p e n ( const char ∗name, bool f o r w r i t e ) { struct s a v e f i l e ∗s f ; s f = malloc ( s i z e o f (∗ s f ) ) ; i f ( s f == NULL) { return NULL; } sf−>f = fopen ( name, f o r w r i t e ? ”w” : ” r ” ) ; i f ( sf−>f == NULL) { free ( sf ) ; f p r i n t f ( stderr ,

21

} /∗ ∗ Read encoded binary data , discarding any whitespace that appears . ∗/ static int s a v e f i l e b i n t e x t r e a d ( struct s a v e f i l e ∗sf , void ∗data , s i z e t len ) { s i z e t pos ; unsigned char ∗udata ; i n t ch ; udata pos = while ch if

= data ; 0; ( pos < len ) { = f g e t c ( sf−>f ) ; ( ch == EOF || f e r r o r ( sf−>f ) ) { f p r i n t f ( stderr , ”Oops : e r r o r reading %s.\n” , sf−>name) ; sf−>warned = true ; return 1;

} i f ( ch == ’ ’ || ch == ’\ t ’ || ch == ’\ r ’ || ch == ’\n ’ ) { continue ; } udata [ pos ++] = ch ;

22

Foundations of Videogame Programming Code Repository

} return 0;

return 1; } return 0;

} } /∗ ∗ Read binary data , decoding from t e x t using r e a d l e t t e r ( ) . ∗/ static int s a v e f i l e b i n r e a d ( struct s a v e f i l e ∗sf , void ∗data , s i z e t len ) { unsigned char buf [ 6 4 ] ; unsigned char ∗udata ; unsigned char val1 , val2 ; s i z e t pos , amt , i ; udata = data ; pos = 0; while ( pos < len ) { amt = len − pos ; i f ( amt > s i z e o f ( buf ) / 2) { amt = s i z e o f ( buf ) / 2; } i f ( s a v e f i l e b i n t e x t r e a d ( sf , buf , amt∗2) ) { return 1; } f o r ( i =0; ibintextpos ; i f ( amt > len − pos ) { amt = len − pos ; } i f ( s a v e f i l e r a w w r i t e ( sf , udata + pos , amt ) ) { return 1; } pos += amt ; sf−>bintextpos += amt ; i f ( sf−>bintextpos >= BINTEXT WIDTH ) { s a v e f i l e r a w w r i t e ( sf , ”\n” , 1) ; sf−>bintextpos = 0; } } return 0;

udata = data ; pos = 0; bpos = 0; while ( pos < len ) { byte = udata [ pos + + ] ; buf [ bpos++] = w r i t e l e t t e r ( byte >> 4) ; buf [ bpos++] = w r i t e l e t t e r ( byte & 0x f ) ; i f ( bpos >= s i z e o f ( buf ) ) { i f ( s a v e f i l e b i n t e x t w r i t e ( sf , buf , bpos ) ) { return 1; } bpos = 0; } } i f ( s a v e f i l e b i n t e x t w r i t e ( sf , buf , bpos ) ) {

∗uval ;

uval = ( unsigned char ∗)&v a l ; valpos = 0; f o r ( i =0; i= s i z e o f ( v a l ) ) { valpos = 0; } } } /∗ ∗ Set the ” encryption ” key . ∗/ s t a t i c void s a v e f i l e k e y ( struct s a v e f i l e ∗sf , uint32 t key ) { sf−>key = 0; c r c s t a r t (& sf−>crc ) ; hash(& sf−>key , s i z e o f ( sf−>key ) , sf−>pad , s i z e o f ( sf−>pad ) ) ; sf−>padpos = 0; } /∗ ∗ Get an ” encryption ” pad byte . This forms a stream ” cipher ” that we ∗ xor with the p l a i n t e x t save data . ∗/ s t a t i c unsigned char s a v e f i l e g e t p a d ( struct s a v e f i l e ∗s f ) { unsigned char r e t ; r e t = sf−>pad [ sf−>padpos + + ] ; i f ( sf−>padpos >= s i z e o f ( sf−>pad ) ) { hash ( sf−>pad , s i z e o f ( sf−>pad ) , sf−>pad , s i z e o f ( sf−>pad ) ) ; sf−>padpos = 0; } return r e t ;

} /∗ ∗ Write binary data , encoding as t e x t using w r i t e l e t t e r ( ) . ∗/ static int s a v e f i l e b i n w r i t e ( struct s a v e f i l e ∗sf , const void ∗data , s i z e t len ) { unsigned char buf [ 6 4 ] ; const unsigned char ∗udata ; s i z e t pos , bpos ; unsigned char byte ;

s i z e t datalen , unsigned char ∗out , s i z e t outlen ) ∗udata ;

udata = data ; v a l = 0; f o r ( i =0; i 60) ; v a l += udata [ i ] ˆ 0xbeefU ; }

} /∗ ∗ Write encoded binary data , i n s e r t i n g newlines to get a neatly ∗ formatted block . ∗/ static int s a v e f i l e b i n t e x t w r i t e ( struct s a v e f i l e ∗sf , const void ∗data , s i z e t len ) { s i z e t pos , amt ; const unsigned char ∗udata ;

buf [ 0 . . buflen ] . Note : buf and outhash may overlap .

} /∗ ∗ Read ” encrypted ” data . ∗/ static int s a v e f i l e c r e a d ( struct s a v e f i l e ∗sf , void ∗data , s i z e t len ) { char buf [ 6 4 ] ; unsigned char ∗udata ; s i z e t pos , amt , i ; unsigned char ch ; udata = data ; pos = 0; while ( pos < len ) { amt = len − pos ; i f ( amt > s i z e o f ( buf ) ) { amt = s i z e o f ( buf ) ; } i f ( s a v e f i l e b i n r e a d ( sf , buf , amt ) ) { return 1; } f o r ( i =0; icrc , data , len ) ; return 0; } /∗ ∗ Write ” encrypted ” data . ∗/ static int s a v e f i l e c w r i t e ( struct s a v e f i l e ∗sf , const void ∗data , s i z e t len ) { char buf [ 6 4 ] ; const unsigned char ∗udata ; s i z e t pos , amt , i ; unsigned char ch ;

{NULL, 0} };

udata = data ; pos = 0; while ( pos < len ) { amt = len − pos ; i f ( amt > s i z e o f ( buf ) ) { amt = s i z e o f ( buf ) ; } f o r ( i =0; icrc , data , len ) ; return 0;

static int compat restore ( const char ∗ i n f i l e ) { FILE ∗in ; const struct compat saveinfo ∗p ; char ∗s ; long sum, cksum = 0; size t i ; struct c r c s t a t e crc ; i f ( ( in = fopen ( i n f i l e , ” rb ” ) ) == NULL) { f p r i n t f ( stderr , ”Hmm. The f i l e \”%s\” appears to be magically blocked .\n” , infile ) ; return 1; } fread (&sum, s i z e o f (sum) , 1 , in ) ; /∗ Get the seed ∗/ srandom ( ( i n t ) sum) ; f o r ( p = compat savearray ; p−>address ! = NULL; p++) { fread ( p−>address , p−>width , 1 , in ) ; f o r ( s = p−>address , i = 0; i < p−>width ; i ++ , s ++) ∗s = (∗s ˆ random ( ) ) & 0xFF ; /∗ L i g h t l y decrypt ∗/ } f c l o s e ( in ) ;

} //////////////////////////////////////////////////////////// // compat f o r old save f i l e s struct compat saveinfo { void ∗address ; s i z e t width ; }; s t a t i c const struct compat saveinfo compat savearray [ ] = { {&abbnum, s i z e o f (abbnum) }, {&attack , s i z e o f ( attack ) }, {&b l k l i n , s i z e o f ( b l k l i n ) }, {&bonus , s i z e o f ( bonus ) }, {&chloc , s i z e o f ( chloc ) }, {&chloc2 , s i z e o f ( chloc2 ) }, {&clock1 , s i z e o f ( clock1 ) }, {&clock2 , s i z e o f ( clock2 ) }, {&closed , s i z e o f ( closed ) }, {&i s c l o s i n g , s i z e o f ( i s c l o s i n g ) }, {&d a l t l o c , s i z e o f ( d a l t l o c ) }, {&demo, s i z e o f ( demo ) }, {&d e t a i l , s i z e o f ( d e t a i l ) }, {&dflag , s i z e o f ( d f l a g ) }, {&d k i l l , s i z e o f ( d k i l l ) }, {&dtotal , s i z e o f ( d t o t a l ) }, {&foobar , s i z e o f ( foobar ) }, {&gaveup , s i z e o f ( gaveup ) }, {&holding , s i z e o f ( holding ) }, {&iwest , s i z e o f ( iwest ) }, {&k , s i z e o f ( k ) }, {&k2 , s i z e o f ( k2 ) }, {&knfloc , s i z e o f ( knfloc ) }, {&kq , s i z e o f ( kq ) }, {&latency , s i z e o f ( latency ) }, {&l i m i t , s i z e o f ( l i m i t ) }, {&lmwarn , s i z e o f ( lmwarn ) }, {&loc , s i z e o f ( l o c ) }, {&maxdie , s i z e o f ( maxdie ) }, {&maxscore , s i z e o f ( maxscore ) }, {&newloc , s i z e o f ( newloc ) }, {&numdie , s i z e o f ( numdie ) }, {&obj , s i z e o f ( obj ) }, {&oldloc2 , s i z e o f ( oldloc2 ) }, {&oldloc , s i z e o f ( o l d l o c ) }, {&panic , s i z e o f ( panic ) }, {&saveday , s i z e o f ( saveday ) }, {&savet , s i z e o f ( savet ) }, {&scoring , s i z e o f ( scoring ) }, {&spk , s i z e o f ( spk ) }, {&stick , s i z e o f ( s t i c k ) }, {&t a l l y , s i z e o f ( t a l l y ) }, {&t a l l y 2 , s i z e o f ( t a l l y 2 ) },

c r c s t a r t (& crc ) ; /∗ See i f she cheated ∗/ f o r ( p = compat savearray ; p−>address ! = NULL; p++) crc add (&crc , p−>address , p−>width ) ; cksum = c r c g e t (& crc ) ; i f (sum ! = cksum ) /∗ Tsk tsk ∗/ return 2; /∗ Altered the f i l e ∗/ /∗ We successfully restored , so t h i s r e a l l y was a save f i l e ∗/ /∗ ∗ The above code loads these from disk even though they ’ re ∗ pointers . Null them out and hope we don ’ t crash on them ∗ l a t e r ; that ’ s b e t t e r than having them be garbage . ∗/ tkk = NULL; wd1 = NULL; wd2 = NULL; return 0; } //////////////////////////////////////////////////////////// // save + r e s t o r e s t a t i c i n t ∗const s a v e i n t s [ ] = { &abbnum, &attack , &b l k l i n , &bonus , &chloc , &chloc2 , &clock1 , &clock2 , &closed , &i s c l o s i n g , &d a l t l o c , &demo, &d e t a i l , &dflag , &d k i l l , &dtotal , &foobar , &gaveup , &holding , &iwest , &k , &k2 , &knfloc , &kq ,

24

Foundations of Videogame Programming Code Repository

&latency , &l i m i t , &lmwarn , &loc , &maxdie , &maxscore , &newloc , &numdie , &obj , &oldloc2 , &oldloc , &panic , &saveday , &savet , &scoring , &spk , &stick , &t a l l y , &t a l l y 2 , &turns , &verb , &wasdark , &yea ,

return 1; } /∗ other parts o f the code may depend on us doing t h i s here ∗/ srandom ( key ) ; s a v e f i l e k e y ( sf , key ) ; /∗ ∗ Integers ∗/ f o r ( i =0; i= 15) || ( dloc [ i ] == l o c || odloc [ i ] == l o c ) ; i f ( ! dseen [ i ] ) continue ; /∗ i . e . goto 6030 ∗/ dloc [ i ] = l o c ; i f ( i == 6) { /∗ pirate ’ s spotted him ∗/ i f ( l o c == chloc || prop [ chest ] >= 0) continue ; k = 0; f o r ( j = 50; j tverb ; /∗ k back to verb tkk = t r a v e l [ l o c ] ; return ( 9 ) ; } i f ( l l t l o c ) tk2 = tkk ; }

∗/

∗/

} tkk = tk2 ; /∗ 23 i f ( tkk ! = 0) { k = tkk−>tverb ; tkk = t r a v e l [ l o c ] ; return ( 9 ) ; } rspeak (140) ; return ( 2 ) ;

∗/

∗/

∗/

} /∗ 30000 ∗/ static int s p e c i a l s ( void ) { switch ( newloc −= 300) { case 1: /∗ 30100 ∗/ newloc = 99 + 100 − l o c ; i f ( holding == 0 || ( holding == 1 && t o t i n g ( emerald ) ) ) return ( 2 ) ; newloc = l o c ; rspeak (117) ; return ( 2 ) ; case 2: /∗ 30200 ∗/ drop ( emerald , l o c ) ; return ( 1 2 ) ; case 3: /∗ to 30300 ∗/ return ( t r b r i d g e ( ) ) ; default : bug ( 2 9 ) ; } }

l9 : f o r ( ; tkk ! = 0; tkk = tkk−>next ) i f ( tkk−>tverb == 1 || tkk−>tverb == k ) break ; i f ( tkk == 0) { badmove ( ) ; return ( 2 ) ; } l11 : l l 1 = tkk−>conditions ; /∗ 11 ∗/ l l 2 = tkk−>t l o c ; newloc = l l 1 ; /∗ newloc=conditions ∗/ k = newloc % 100; /∗ k used f o r prob ∗/ i f ( newloc conditions ! = l l 1 ) break ; i f ( tkk == 0) bug ( 2 5 ) ; goto l11 ; } /∗ 20 ∗/ static int mback ( void ) { struct t r a v l i s t ∗tk2 , ∗j ; int ll ; i f ( forced ( k = o l d l o c ) ) k = oldloc2 ; /∗ k= l o c a t i o n oldloc2 = o l d l o c ; oldloc = loc ; tk2 = 0; i f ( k == l o c ) { rspeak ( 9 1 ) ; return ( 2 ) ; } f o r ( ; tkk ! = 0; tkk = tkk−>next ) { l l = tkk−>t l o c ;

27

/∗ 30300 ∗/ static int t r b r i d g e ( void ) { i f ( prop [ t r o l l ] == 1) { pspeak ( t r o l l , 1) ; prop [ t r o l l ] = 0; move ( t r o l l 2 , 0) ; move ( t r o l l 2 + 100, 0) ; move ( t r o l l , plac [ t r o l l ] ) ; move ( t r o l l + 100, f i x d [ t r o l l ] ) ; j u g g l e ( chasm ) ; newloc = l o c ; return ( 2 ) ; } newloc = plac [ t r o l l ] + f i x d [ t r o l l ] − l o c ; i f ( prop [ t r o l l ] == 0) prop [ t r o l l ] = 1; i f ( ! t o t i n g ( bear ) ) return ( 2 ) ; rspeak (162) ; prop [ chasm ] = 1; prop [ t r o l l ] = 2; drop ( bear , newloc ) ; f i x e d [ bear ] = −1; prop [ bear ] = 3; i f ( prop [ spices ] < 0) t a l l y 2 ++; oldloc2 = newloc ; return ( 9 9 ) ; }

∗/

/∗ 21

∗/

/∗ 20 ∗/ s t a t i c void badmove ( void ) { spk = 12; i f ( k >= 43 && k 100) return (8000) ; i f ( obj == 0) { i f ( here ( bird ) && verb ! = throw ) obj = bird ; i f ( here ( clam ) || here ( oyster ) ) obj = 100 ∗ obj + clam ; i f ( obj > 100) return (8000) ; } } i f ( obj == bird ) { /∗ 9124 ∗/ spk = 137; i f ( closed ) return (2011) ; destroy ( bird ) ; prop [ bird ] = 0; i f ( place [ snake ] == plac [ snake ] ) t a l l y 2 ++; spk = 45; } i f ( obj == 0) spk = 44; /∗ 9125 ∗/ i f ( obj == clam || obj == oyster ) spk = 150; i f ( obj == snake ) spk = 46; i f ( obj == dwarf ) spk = 49; i f ( obj == dwarf && closed ) return (19000) ; i f ( obj == dragon ) spk = 147; i f ( obj == t r o l l ) spk = 157; i f ( obj == bear ) spk = 165 + ( prop [ bear ] + 1) / 2; i f ( obj ! = dragon || prop [ dragon ] ! = 0) return (2011) ; rspeak ( 4 9 ) ; verb = 0; obj = 0; g e t i n (&wd1, &wd2) ; i f ( ! weq ( wd1, ” y ” ) && ! weq ( wd1, ” yes ” ) ) return (2608) ; pspeak ( dragon , 1) ; prop [ dragon ] = 2; prop [ rug ] = 0; k = ( plac [ dragon ] + f i x d [ dragon ] ) / 2; move ( dragon + 100, −1) ; move ( rug + 100, 0) ; move ( dragon , k ) ; move ( rug , k ) ; f o r ( obj = 1; obj = 50 && obj atab ; ∗s ; ) ∗t ++ = ∗s++ ˆ ’ = ’ ; ∗t = 0 ˆ ’ = ’ ; /∗ encrypt s l i g h t l y to thwart core reader ∗/ /∗ printf ( ” Stored \”%s\” (%d ch ) as entry %d\n” , ∗/ /∗ word , length ( word ) , adr ) ; ∗/ return ( 0 ) ; /∗ entry unused ∗/ case −1: /∗ looking up user word ∗/ i f ( h−>v a l == 0) return (−1) ; /∗ not found ∗/ f o r ( s = word , t = h−>atab ; ∗t ˆ ’ = ’ ; ) i f ( ( ∗ s++ ˆ ’ = ’ ) ! = ∗t ++) goto exitloop2 ; i f ( ( ∗ s ˆ ’ = ’ ) ! = ∗t && s − word < 5) goto exitloop2 ; /∗ the word matched o . k . ∗/ return ( h−>v a l ) ; default : /∗ looking up known word ∗/ i f ( h−>v a l == 0) errx ( 1 , ” Unable to f i n d %s in vocab ” , word ) ; f o r ( s = word , t = h−>atab ; ∗t ˆ ’ = ’ ; ) i f ( ( ∗ s++ ˆ ’ = ’ ) ! = ∗t ++) goto exitloop2 ; /∗ the word matched o . k . ∗/ i f ( h−>v a l / 1000 ! = type ) continue ; return ( h−>v a l % 1000) ; } exitloop2 : /∗ hashed entry does not match i f ( adr + 1 == hash || hash == 0) errx ( 1 , ”Hash t a b l e overflow ” ) ; } } /∗ print hash t a b l e ( f o r debugging ) static unused void prht ( void ) { int i, j, l; char ∗c ; struct hashtab ∗h ; f o r ( i = 0; i < HTSIZE / 10 + 1; i ++) { printf ( ”%4d” , i ∗ 10) ; f o r ( j = 0; j < 10; j ++) { i f ( i ∗ 10 + j >= HTSIZE ) break ; h = &voc [ i ∗ 10 + j ] ; putchar ( ’ ’ ) ; i f ( h−>v a l == 0) { printf ( ”−−−−−” ) ; continue ;

∗/

∗/

∗/

32

Foundations of Videogame Programming Code Repository } f o r ( l = 0 , c = h−>atab ; l < 5; l ++) i f ((∗ c ˆ ’ = ’ ) ) putchar(∗c++ ˆ ’ = ’ ) ; else

putchar ( ’ } putchar ( ’ \n ’ ) ;

’) ;

} }

wizard.c /∗

$NetBSD : wizard . c , v 1.16 2012/10/12 15:41:10 dholland Exp $

∗/

delay , delay == 1 ? ” ” : ” s ” ) ; i f ( delay tm yday + 365 ∗ ( tptr−>tm year − 77) + ( tptr−>tm year − 77) / 4 − ( tptr−>tm year − 1) / 100 + ( tptr−>tm year + 299) / 400) ; /∗ bug : t h i s w i l l overflow in the year 2066 AD ( with 16 b i t i n t ) ∗/ /∗ i t w i l l be a t t r i b u t e d to Wm the C’ s m i l l e n i a l c e l e b r a t i o n ∗/ /∗ and minutes since midnite ∗/ ∗t = tptr−>tm hour ∗ 60 + tptr−>tm min ; /∗ p r e t t y painless ∗/

} /∗ not as complex as advent/10 ( f o r now ) static int wizard ( void ) { char ∗word , ∗x ; i f ( ! yesm(16 , 0 , 7) ) return ( FALSE ) ; mspeak( 1 7 ) ; g e t i n (&word , &x ) ; i f ( ! weq ( word , magic ) ) { mspeak( 2 0 ) ; return ( FALSE ) ; } mspeak( 1 9 ) ; return (TRUE) ; } void ciao ( void ) { char fname [ 8 0 ] ; s i z e t pos ; printf ( ”What would you l i k e to c a l l the saved version?\n” ) ; /∗ XXX − should use f g e t l n to avoid a r b i t r a r y l i m i t ∗/ f o r ( pos = 0; pos < s i z e o f ( fname ) − 1; pos ++) { i n t ch ; ch = getchar ( ) ; i f ( ch == ’\n ’ || ch == EOF) break ; fname [ pos ] = ch ; } fname [ pos ] = ’ \ 0 ’ ; i f ( save ( fname ) ! = 0) return ; /∗ Save f a i l e d ∗/ printf ( ” To resume , say \” adventure %s\” .\n” , fname ) ; printf ( ”\” With these rooms I might now have been f a m i l i a r l y ” ) ; p r i n t f ( ” acquainted .\ ”\n” ) ; exit ( 0 ) ;

s t a t i c char magic [ 6 ] ; void poof ( void ) { strcpy ( magic , DECR( ’ d ’ , latency = 45; }

’w’ ,

’a ’ ,

’r ’ ,

’f ’) ) ;

int Start ( void ) { int d , t , delay ; datime(&d , &t ) ; delay = ( d − saveday ) ∗ 1440 + ( t − savet ) ; ∗ month ∗/

}

/∗ good f o r about a

i f ( delay >= latency ) { saved = −1; return ( FALSE ) ; } printf ( ” This adventure was suspended a mere %d minute%s ago . ” ,

done.c

∗/

int ran ( i n t range ) { long i; i = rand ( ) % range ; return ( i ) ; }

hdr.h /∗

$NetBSD : done . c , v 1.10 2009/08/25 06:56:52 dholland Exp $

∗/

#include #ifndef l i n t #if 0 s t a t i c char sccsid [ ] = ”@( # ) done . c 8.1 ( Berkeley ) 5/31/93” ; #e l s e RCSID ( ”$NetBSD : done . c , v 1.10 2009/08/25 06:56:52 dholland Exp $” ) ; #endif #endif /∗ not l i n t ∗/ /∗

Re−coding o f advent in C: termination routines ∗/

#include #include #include #include

” hdr . h” ” extern . h”

int score ( void ) { /∗ s o r t o f l i k e 20000 ∗/ int myscore , i ; maxscore = myscore = 0; f o r ( i = 50; i chest ) k = 16; i f ( prop [ i ] >= 0) myscore += 2; i f ( place [ i ] == 3 && prop [ i ] == 0) myscore += k − 2; maxscore += k ; } myscore += ( maxdie − numdie ) ∗ 10; maxscore += maxdie ∗ 10; i f ( ! ( scoring || gaveup ) ) myscore += 4; maxscore += 4; i f ( d f l a g ! = 0) myscore += 25; maxscore += 25; if ( isclosing ) myscore += 25; maxscore += 25; i f ( closed ) { i f ( bonus == 0) myscore += 10; i f ( bonus == 135) myscore += 25; i f ( bonus == 134) myscore += 30; i f ( bonus == 133) myscore += 45; } maxscore += 45; i f ( place [ magazine ] == 108) myscore++; maxscore++; myscore += 2; maxscore += 2; f o r ( i = 1; i = 1; i−−) { i f ( ! toting ( i ) ) continue ; k = oldloc2 ; i f ( i == lamp ) k = 1; drop ( i , k ) ; } l o c = 3; oldloc = loc ; }

hdr.h /∗

$NetBSD : hdr . h , v 1.13 2009/08/25 06:56:52 dholland Exp $

∗/

/∗ hdr . h : included by c advent f i l e s ∗/ #include extern v o l a t i l e s i g a t o m i c t d e l h i t ; extern i n t yea ; extern char d a t a f i l e [ ] ; /∗ V i r t u a l data f i l e ∗/ #define TAB 011 #define LF 012 #define FLUSHLINE do { i n t f l u s h l i n e c h ; while ( ( f l u s h l i n e c h = getchar ( ) ) != EOF && f l u s h l i n e c h != ’\n ’ ) ; } while ( 0 )

#define FLUSHLF extern extern extern extern extern

while ( next ( ) !=LF )

int loc , newloc , oldloc , oldloc2 , wasdark , gaveup , kq , k , k2 ; char ∗wd1, ∗wd2; /∗ the complete words ∗/ int verb , obj , spk ; int blklin ; int saveday , savet , maxscore , latency ;

#define SHORT 50 #define MAXSTR

/∗ How short i s a demo game? ∗/

20

#define HTSIZE 512 extern struct hashtab {

/∗ max length o f user ’ s words ∗/ /∗ max number o f vocab words ∗/ /∗ hash t a b l e f o r vocabulary ∗/

34

Foundations of Videogame Programming Code Repository

int val ; /∗ word type &index ( ktab ) ∗/ char ∗atab ; /∗ pointer to actual s t r i n g ∗/ } voc [ HTSIZE ] ; #define SEED 1815622 /∗ ” Encryption ” seed ∗/

int int

extern extern extern

int int int

hintmax ; hints [ 2 0 ] [ 5 ] ; /∗ i n f o on hints ∗/ hinted [ 2 0 ] , h i n t l c [ 2 0 ] ;

extern extern

int int

place [ 1 0 1 ] , prop [ 1 0 1 ] , l i n k s [ 2 0 1 ] ; abb [ LOCSIZE ] ;

/∗ random t e x t messages ∗/

extern

int

/∗ magic messages ∗/

#define FALSE #define TRUE

struct t e x t { char ∗seekadr;/∗ Msg s t a r t in v i r t u a l disk ∗/ int txtlen ; /∗ length o f msg s t a r t i n g here ∗/ }; #define RTXSIZE 205 extern struct t e x t r t e x t [ RTXSIZE ] ; #define MAGSIZE 35 extern struct t e x t mtext [MAGSIZE ] ;

extern

extern int classes ; #define CLSMAX 12 extern struct t e x t c t e x t [CLSMAX] ; extern int cval [CLSMAX] ; extern

/∗ classes o f adventurer ∗/

/∗ o b j e c t descriptions ∗/

struct t e x t ptext [ 1 0 1 ] ;

/∗ various condition b i t s ∗/

extern extern

#define LOCSIZE 141 /∗ number o f l o c a t i o n s ∗/ extern struct t e x t l t e x t [ LOCSIZE ] ; /∗ long l o c description ∗/ extern struct t e x t s t e x t [ LOCSIZE ] ; /∗ short l o c descriptions ∗/ extern struct t r a v l i s t { /∗ direcs & conditions o f t r a v e l ∗/ struct t r a v l i s t ∗next ; /∗ ptr to next l i s t entry ∗/ int conditions ; /∗ m in writeup ( newloc / 1000) ∗/ int tloc ; /∗ n in writeup ( newloc % 1000) ∗/ int tverb ; /∗ the verb that takes you there ∗/ } ∗t r a v e l [ LOCSIZE ] , ∗tkk ; /∗ t r a v e l i s c l o s e r to keys ( . . . ) ∗/ extern

int

a t l o c [ LOCSIZE ] ;

extern extern

int int

plac [ 1 0 1 ] ; /∗ i n i t i a l o b j e c t placement ∗/ fixd [101] , fixed [101]; /∗ l o c a t i o n f i x e d ? ∗/

extern

int

actspeak [ 3 5 ] ;

extern

extern extern

cond [ LOCSIZE ] ; setbit [16];

/∗ b i t defn masks 1 , 2 , 4 , . . . ∗/

maxtrs , t a l l y , t a l l y 2 ;

/∗ treasure values ∗/

0 1

int keys , lamp , grate , cage , rod , rod2 , steps , /∗ mnemonics ∗/ bird , door , pillow , snake , f i s s u r e , t a b l e t , clam , oyster , magazine , dwarf , knife , food , b o t t l e , water , o i l , plant , plant2 , axe , mirror , dragon , chasm, t r o l l , t r o l l 2 , bear , message , vend , batter , nugget , coins , chest , eggs , t r i d e n t , vase , emerald , pyramid , pearl , rug , chain , spices , back , look , cave , null , entrance , depression , /∗enter , stream , pour,∗/ say , lock , throw , find , invent ; int chloc , chloc2 , dseen [ 7 ] , dloc [ 7 ] , odloc [ 7 ] , dflag , d a l t l o c ; int int and

/∗ dwarf s t u f f ∗/

tk [ 2 1 ] , stick , dtotal , attack ; turns , lmwarn , iwest , knfloc , d e t a i l ,

/∗ various f l a g s

∗ counters ∗/ abbnum, maxdie , numdie , holding , d k i l l , foobar , bonus , clock1 , clock2 , saved , i s c l o s i n g , panic , closed , scoring ; extern

int

demo, l i m i t ;

/∗ r t e x t msg f o r verb ∗/ #define DECR( a , b , c , d , e ) decr ( a+ ’ + ’ , b+’−’, c + ’ # ’ ,d+ ’& ’ , e + ’% ’)

extern.h /∗

$NetBSD : extern . h , v 1.16 2012/01/08 18:17:41 dholland Exp $

#include #include /∗ crc . c ∗/ struct c r c s t a t e { uint32 t c r c v a l ; unsigned step ; }; void c r c s t a r t ( struct c r c s t a t e ∗) ; void crc add ( struct c r c s t a t e ∗, const void ∗, s i z e t ) ; uint32 t c r c g e t ( struct c r c s t a t e ∗) ; /∗ done . c ∗/ i n t score ( void ) ; void done ( i n t ) dead ; void die ( i n t ) ; /∗ i n i t . c ∗/ void i n i t ( void ) ; char ∗decr ( int , int , int , int , i n t ) ; void trapdel ( i n t ) ; void startup ( void ) ; /∗ i o . c ∗/ void g e t i n ( char ∗∗, char ∗∗) ; i n t yes ( int , int , i n t ) ; i n t yesm ( int , int , i n t ) ; void rdata ( void ) ; # i f d e f DEBUG void t w r i t e ( i n t ) ; #endif void rspeak ( i n t ) ; void mspeak ( i n t ) ; struct t e x t ; void speak ( const struct t e x t ∗) ; void pspeak ( int , i n t ) ; /∗ save . c ∗/ i n t save ( const char ∗) ; i n t r e s t o r e ( const char ∗) ;

∗/

/∗ subr . c ∗/ int toting ( int ) ; i n t here ( i n t ) ; i n t at ( i n t ) ; i n t l i q ( void ) ; int liqloc ( int ) ; i n t forced ( i n t ) ; i n t dark ( void ) ; i n t pct ( i n t ) ; i n t fdwarf ( void ) ; i n t march ( void ) ; dead ; void bug ( i n t ) void checkhints ( void ) ; i n t trsay ( void ) ; i n t trtake ( void ) ; i n t trdrop ( void ) ; i n t tropen ( void ) ; i n t t r k i l l ( void ) ; i n t t r t o s s ( void ) ; i n t t r f e e d ( void ) ; i n t t r f i l l ( void ) ; void c l o s i n g ( void ) ; void caveclose ( void ) ; /∗ vocab . c ∗/ void destroy ( i n t ) ; void j u g g l e ( i n t ) ; void move ( int , i n t ) ; i n t put ( int , int , i n t ) ; void carry ( int , i n t ) ; void drop ( int , i n t ) ; i n t vocab ( const char ∗, int , i n t ) ; /∗ These three used to be functions in vocab . c ∗/ #define copystr ( src , dest ) strcpy ( ( dest ) , ( src ) ) #define weq ( str1 , s t r 2 ) ( ! strncmp ( ( s t r 1 ) , ( s t r 2 ) , 5 ) ) #define length ( s t r ) ( strlen ( ( str ) ) + 1) /∗ wizard . c ∗/ void datime ( i n t ∗, i n t ∗) ; void poof ( void ) ; i n t Start ( void ) ; void ciao ( void ) ; i n t ran ( i n t ) ;

adventure.6

35

adventure.6 .\ ” .\ ” .\ ” .\ ” .Dd . Dt . Os . Sh .Nm .Nd . Sh .Nm

$NetBSD : adventure . 6 , v 1.4 2003/08/07 09:36:50 agc Exp $ @( # ) adventure .6 8.1 ( Berkeley ) 5/31/93 May 31, 1993 ADVENTURE 6 NAME adventure an exploration game SYNOPSIS

.Op saved−f i l e . Sh DESCRIPTION The o b j e c t o f the game i s to l o c a t e and explore Colossal Cave , f i n d the treasures hidden there , and bring them back to the building with you . The program i s s e l f−d e s c r i p t i v e to a point , but part o f the game i s to discover i t s rules . . Pp To terminate a game, enter .Dq quit ; to save a game f o r l a t e r resumption , enter .Dq suspend .

glorkz 1 1 1 1 2 2 3 4 4 5 6 7 7 8 8 8 9 9 10 10 11 11 11 11 12 13 13 13 14 14 15 15 15 15 15 16 17 17 18 18 19 19 20

You are standing at the end o f a road before a small brick building . Around you i s a f o r e s t . A small stream flows out o f the building and down a g u l l y . You have walked up a h i l l , s t i l l in the f o r e s t . The road slopes back down the other side o f the h i l l . There i s a building in the distance . You are inside a building , a w e l l house f o r a l a r g e spring . You are in a v a l l e y in the f o r e s t beside a stream tumbling along a rocky bed . You are in open f o r e s t , with a deep v a l l e y to one side . You are in open f o r e s t near both a v a l l e y and a road . At your f e e t a l l the water o f the stream splashes i n t o a 2−inch slit in the rock . Downstream the streambed i s bare rock . You are in a 20−f o o t depression f l o o r e d with bare d i r t . Set i n t o the d i r t i s a strong s t e e l grate mounted in concrete . A dry streambed leads i n t o the depression . You are in a small chamber beneath a 3x3 s t e e l grate to the surface . A low crawl over cobbles leads inward to the west . You are crawling over cobbles in a low passage . There i s a dim light at the east end o f the passage . You are in a debris room f i l l e d with s t u f f washed in from the surface . A low wide passage with cobbles becomes plugged with mud and debris here , but an awkward canyon leads upward and west . A note on the wall says ” Magic word XYZZY” . You are in an awkward sloping east/west canyon . You are in a splendid chamber t h i r t y f e e t high . The walls are frozen r i v e r s o f orange stone . An awkward canyon and a good passage exit from east and west sides o f the chamber . At your f e e t i s a small p i t breathing traces o f white mist . An east passage ends here except f o r a small crack leading on . You are at one end o f a vast h a l l stretching forward out o f s i g h t to the west . There are openings to e i t h e r side . Nearby , a wide stone s t a i r c a s e leads downward . The h a l l i s f i l l e d with wisps o f white mist swaying to and f r o almost as i f a l i v e . A cold wind blows up the s t a i r c a s e . There i s a passage at the top o f a dome behind you . The crack i s f a r too small f o r you to f o l l o w . You are on the east bank o f a f i s s u r e s l i c i n g c l e a r across the hall . The mist i s quite thick here , and the f i s s u r e i s too wide to jump . This i s a low room with a crude note on the wall . The note says , ”You won ’ t get i t up the steps ” . You are in the Hall o f the Mountain King , with passages o f f in all directions . You are at the bottom o f the p i t with a broken neck .

21 22 23 23 24 24 25 25 26 27 28 28 29 30 30 31 32 33 33 33 34 35 35 35 35 35 35 35 36 36 37 37 38 38 39 39 40 40 41 41 41 42 43 44 45 46

You didn ’ t make i t . The dome i s unclimbable . You are at the west end o f the Twopit Room. There i s a l a r g e hole in the wall above the p i t at t h i s end o f the room . You are at the bottom o f the eastern p i t in the Twopit Room. There i s a small pool o f o i l in one corner o f the p i t . You are at the bottom o f the western p i t in the Twopit Room. There i s a l a r g e hole in the wall about 25 f e e t above you . You clamber up the plant and scurry through the hole at the top . You are on the west side o f the f i s s u r e in the Hall o f Mists . You are in a low N/S passage at a hole in the f l o o r . The hole goes down to an E/W passage . You are in the south side chamber . You are in the west side chamber o f the Hall o f the Mountain King . A passage continues west and up here . >$< You can ’ t get by the snake . You are in a l a r g e room , with a passage to the south , a passage to the west , and a wall o f broken rock to the east . There i s a l a r g e ” Y2” on a rock in the room ’ s center . You are in a jumble o f rock , with cracks everywhere . You ’ re at a low window overlooking a huge p i t , which extends up out o f s i g h t . A f l o o r i s i n d i s t i n c t l y v i s i b l e over 50 f e e t below . Traces o f white mist cover the f l o o r o f the p i t , becoming thicker to the right . Marks in the dust around the window would seem to i n d i c a t e that someone has been here r e c e n t l y . D i r e c t l y across the p i t from you and 25 f e e t away there i s a s i m i l a r window looking i n t o a l i g h t e d room . A shadowy f i g u r e can be seen there peering back at you . You are in a d i r t y broken passage . To the east i s a crawl . To the west i s a l a r g e passage . Above you i s a hole to another passage . You are on the brink o f a small clean climbable p i t . A crawl leads west . You are in the bottom o f a small p i t with a l i t t l e stream , which enters and e x i t s through t i n y s l i t s . You are in a l a r g e room f u l l o f dusty rocks . There i s a big hole in the f l o o r . There are cracks everywhere , and a passage leading east . You have crawled through a very low wide passage p a r a l l e l to and north o f the Hall o f Mists . You are at the west end o f Hall o f Mists . A low wide crawl continues west and another goes north . To the south i s a l i t t l e passage 6 feet o f f the f l o o r . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . Dead end

36 47 48 49 50 51 52 53 54 55 56 57 57 57 58 59 59 60 60 60 61 61 62 63 64 64 64 65 65 66 66 66 67 67 67 67 67 68 68 68 68 68 69 70 71 71 71 72 73 74 74 74 75 76 77 77 78 79 79 80 81 82 83 84 85 86 87 88 88 88 89 90

Foundations of Videogame Programming Code Repository Dead end Dead end You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . Dead end You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . Dead end You are on the brink o f a t h i r t y f o o t p i t with a massive orange column down one wall . You could climb down here but you could not get back up . The maze continues at t h i s l e v e l . Dead end You have crawled through a very low wide passage p a r a l l e l to and north o f the Hall o f Mists . You are at the east end o f a very long h a l l apparently without side chambers . To the east a low wide crawl slants up . To the north a round two f o o t hole slants down. You are at the west end o f a very long f e a t u r e l e s s h a l l . The hall j o i n s up with a narrow north/south passage . You are at a crossover o f a high N/S passage and a low E/W one . Dead end You are at a complex junction . A low hands and knees passage from the north j o i n s a higher crawl from the east to make a walking passage going west . There i s also a l a r g e room above . The a i r i s damp here . You are in Bedquilt , a long east/west passage with holes everywhere . To explore at random s e l e c t north , south , up, or down. You are in a room whose walls resemble Swiss cheese . Obvious passages go west , east , NE, and NW. Part o f the room i s occupied by a large bedrock block . You are at the east end o f the Twopit Room. The f l o o r here i s l i t t e r e d with thin rock slabs , which make i t easy to descend the pits . There i s a path here bypassing the p i t s to connect passages from east and west . There are holes a l l over , but the only big one i s on the wall d i r e c t l y over the west p i t where you can ’ t get to i t . You are in a l a r g e low c i r c u l a r chamber whose f l o o r i s an immense slab f a l l e n from the c e i l i n g ( Slab Room) . East and west there once were l a r g e passages , but they are now f i l l e d with boulders . Low small passages go north and south , and the south one quickly bends west around the boulders . You are in a s e c r e t N/S canyon above a l a r g e room . You are in a s e c r e t N/S canyon above a s i z a b l e passage . You are in a s e c r e t canyon at a junction o f three canyons , bearing north , south , and SE. The north one i s as t a l l as the other two combined . You are in a l a r g e low room . Crawls lead north , SE, and SW. Dead end crawl . You are in a s e c r e t canyon which here runs E/W. I t crosses over a very t i g h t canyon 15 f e e t below . I f you go down you may not be able to get back up . You are at a wide place in a very t i g h t N/S canyon . The canyon here becomes too t i g h t to go further south . You are in a t a l l E/W canyon . A low t i g h t crawl goes 3 f e e t north and seems to open up . The canyon runs i n t o a mass o f boulders −− dead end . The stream flows out through a pair o f 1 f o o t diameter sewer pipes . I t would be advisable to use the exit . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . Dead end Dead end You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . Dead end Dead end You are in a maze o f t w i s t y l i t t l e passages , a l l a l i k e . You are in a long , narrow c o r r i d o r stretching out o f s i g h t to the west . At the eastern end i s a hole through which you can see a profusion o f leaves . There i s nothing here to climb . Use ”up” or ” out ” to leave the pit . You have climbed up the plant and out o f the p i t .

91 91 91 92 92 92 93 94 95 95 95 96 96 97 97 97 98 98 98 98 99 99 99 100 100 101 102 102 103 103 103 103 104 105 106 106 106 106 107 108 109 109 109 109 109 109 109 110 110 110 110 110 110 110 111 111

You are at the top o f a steep i n c l i n e above a l a r g e room . You could climb down here , but you would not be able to climb up . There i s a passage leading back to the north . You are in the Giant Room. The c e i l i n g here i s too high up f o r your lamp to show i t . Cavernous passages lead east , north , and south . On the west wall i s scrawled the i n s c r i p t i o n , ” Fee f i e f o e foo ” [ s i c ]. The passage here i s blocked by a recent cave−in . You are at one end o f an immense north/south passage . You are in a magnificent cavern with a rushing stream , which cascades over a sparkling w a t e r f a l l i n t o a roaring whirlpool which disappears through a hole in the f l o o r . Passages exit to the south and west . You are in the Soft Room. The walls are covered with heavy curtains , the f l o o r with a thick p i l e carpet . Moss covers the c e i l i n g . This i s the Oriental Room. Ancient o r i e n t a l cave drawings cover the walls . A gently sloping passage leads upward to the north , another passage leads SE, and a hands and knees crawl leads west . You are f o l l o w i n g a wide path around the outer edge o f a l a r g e cavern . Far below , through a heavy white mist , strange splashing noises can be heard . The mist r i s e s up through a f i s s u r e in the c e i l i n g . The path e x i t s to the south and west . You are in an alcove . A small NW path seems to widen a f t e r a short distance . An extremely t i g h t tunnel leads east . I t looks l i k e a very t i g h t squeeze . An e e r i e l i g h t can be seen at the other end . You ’ re in a small chamber l i t by an e e r i e green l i g h t . An extremely narrow tunnel e x i t s to the west . A dark c o r r i d o r leads NE. You ’ re in the Dark−Room. A c o r r i d o r leading south i s the only exit . You are in an arched h a l l . A c o r a l passage once continued up and east from here , but i s now blocked by debris . The a i r smells o f sea water . You ’ re in a l a r g e room carved out o f sedimentary rock . The f l o o r and walls are l i t t e r e d with b i t s o f s h e l l s embedded in the stone . A shallow passage proceeds downward, and a somewhat steeper one leads up . A low hands and knees passage enters from the south . You are in a long sloping c o r r i d o r with ragged sharp walls . You are in a cul−de−sac about e i g h t f e e t across . You are in an anteroom leading to a l a r g e passage to the east . Small passages go west and up . The remnants o f recent digging are evident . A sign in midair here says ” Cave under construction beyond t h i s point . Proceed at own r i s k . [ Witt construction company ] ” You are in a maze o f t w i s t y l i t t l e passages , a l l d i f f e r e n t . You are at Witt ’ s End. Passages lead o f f in ∗a l l∗ d i r e c t i o n s . You are in a north/south canyon about 25 f e e t across . The f l o o r is covered by white mist seeping in from the north . The walls extend upward f o r w e l l over 100 f e e t . Suspended from some unseen point far above you , an enormous two−sided mirror i s hanging p a r a l l e l to and midway between the canyon walls . ( The mirror i s obviously provided f o r the use o f the dwarves , who as you know, are extremely vain . ) A small window can be seen in e i t h e r wall , some f i f t y f e e t up . You ’ re at a low window overlooking a huge p i t , which extends up out o f s i g h t . A f l o o r i s i n d i s t i n c t l y v i s i b l e over 50 f e e t below . Traces o f white mist cover the f l o o r o f the p i t , becoming thicker to the left . Marks in the dust around the window would seem to i n d i c a t e that someone has been here r e c e n t l y . D i r e c t l y across the p i t from you and 25 f e e t away there i s a s i m i l a r window looking i n t o a l i g h t e d room . A shadowy f i g u r e can be seen there peering back at you . A l a r g e s t a l a c t i t e extends from the r o o f and almost reaches the floor below . You could climb down i t , and jump from i t to the f l o o r , but

glorkz 111 112 113 113 113 113 113 114 115 115 115 115 115 115 115 115 115 115 115 116 116 116 116 116 116 116 116 117 117 117 118 118 119 120 121 122 122 123 123 124 124 124 125 125 125 126 126 126 126 126 126 126 126 126 126 126 126 126 126

having done so you would be unable to reach i t to climb back up . You are in a l i t t l e maze o f t w i s t i n g passages , a l l d i f f e r e n t . You are at the edge o f a l a r g e underground r e s e r v o i r . An opaque cloud o f white mist f i l l s the room and r i s e s r a p i d l y upward . The lake is fed by a stream , which tumbles out o f a hole in the wall about 10 feet overhead and splashes n o i s i l y i n t o the water somewhere within the mist . The only passage goes back toward the south . Dead end You are at the northeast end o f an immense room , even l a r g e r than the Giant Room. I t appears to be a r e p o s i t o r y f o r the ” Adventure ” program . Massive torches f a r overhead bathe the room with smoky yellow l i g h t . Scattered about you can be seen a p i l e o f b o t t l e s ( all o f them empty ) , a nursery o f young beanstalks murmuring q u i e t l y , a bed o f oysters , a bundle o f black rods with rusty stars on t h e i r ends , and a c o l l e c t i o n o f brass lanterns . Off to one side a great many dwarves are sleeping on the f l o o r , snoring loudly . A sign nearby reads : ”Do not disturb the dwarves ! ” An immense mirror i s hanging against one wall , and stretches to the other end o f the room , where various other sundry objects can be glimpsed dimly in the distance . You are at the southwest end o f the Repository . To one side i s a pit f u l l o f f i e r c e green snakes . On the other side i s a row o f small wicker cages , each o f which contains a l i t t l e sulking bird . In one corner i s a bundle o f black rods with rusty marks on t h e i r ends . A l a r g e number o f v e l v e t p i l l o w s are scattered about on the f l o o r . A vast mirror stretches o f f to the northeast . At your f e e t i s a large s t e e l grate , next to which i s a sign which reads , ” Treasure Vault . Keys in Main O f f i c e . ” You are on one side o f a large , deep chasm . A heavy white mist rising up from below obscures a l l view o f the f a r side . A SW path leads away from the chasm i n t o a winding c o r r i d o r . You are in a long winding c o r r i d o r sloping out o f s i g h t in both directions . You are in a s e c r e t canyon which e x i t s to the north and east . You are in a s e c r e t canyon which e x i t s to the north and east . You are in a s e c r e t canyon which e x i t s to the north and east . You are on the f a r side o f the chasm . A NE path leads away from the chasm on t h i s side . You ’ re in a long east/west c o r r i d o r . A f a i n t rumbling noise can be heard in the distance . The path forks here . The l e f t fork leads northeast . A d u l l rumbling seems to get louder in that d i r e c t i o n . The r i g h t fork leads southeast down a gentle slope . The main c o r r i d o r enters from the west . The walls are quite warm here . From the north can be heard a steady roar , so loud that the e n t i r e cave seems to be trembling . Another passage leads south , and a low crawl goes east . You are on the edge o f a breath−taking view . Far below you i s an a c t i v e volcano , from which great gouts o f molten lava come surging out , cascading back down i n t o the depths . The glowing rock f i l l s the f a r t h e s t reaches o f the cavern with a blood−red glare , g i v i n g every− thing an e e r i e , macabre appearance . The a i r i s f i l l e d with flickering sparks o f ash and a heavy smell o f brimstone . The walls are hot to the touch , and the thundering o f the volcano drowns out a l l other sounds . Embedded in the jagged r o o f f a r overhead are myriad twisted formations composed o f pure white alabaster , which s c a t t e r the murky l i g h t i n t o s i n i s t e r apparitions upon the walls . To one side i s a deep gorge , f i l l e d with a b i z a r r e chaos o f tortured rock which seems to have been c r a f t e d by the d e v i l himself . An immense r i v e r o f f i r e crashes out from the depths o f the volcano , burns i t s way through the gorge , and plummets i n t o a bottomless p i t f a r o f f to your l e f t .

126 126 126 126 126 127 127 127 127 128 128 129 129 130 130 130 131 132 133 134 135 136 137 138 139 140 −1 2 1 2 3 4 5 6 7 8 9 10 11 13 14 15 17 18 19 23 24 25 33 35 36 39 41 57 60 61 64 66 67 68 71 74 88 91 92 95 96 97 98 99 100 101 102 103 106 108 109 110 111 113 115 116 117 118

37 To the right , an immense geyser o f b l i s t e r i n g steam erupts continuously from a barren island in the center o f a sulfurous lake , which bubbles ominously . The f a r r i g h t wall i s aflame with an incandescence o f its own, which lends an a d d i t i o n a l i n f e r n a l splendor to the already scary scene . A dark , foreboding passage e x i t s to the south . You are in a small chamber f i l l e d with l a r g e boulders . The walls are very warm, causing the a i r in the room to be almost s t i f l i n g from the heat . The only exit i s a crawl heading west , through which i s coming a low rumbling . You are walking along a gently sloping north/south passage l i n e d with oddly shaped limestone formations . You are standing at the entrance to a large , barren room . A sign posted above the entrance reads : ” Caution ! Bear in room ! ” You are inside a barren room . The center o f the room i s completely empty except f o r some dust . Marks in the dust lead away toward the f a r end o f the room . The only exit i s the way you came in . You are in a maze o f t w i s t i n g l i t t l e passages , a l l d i f f e r e n t . You are in a l i t t l e maze o f t w i s t y passages , a l l d i f f e r e n t . You are in a t w i s t i n g maze o f l i t t l e passages , a l l d i f f e r e n t . You are in a t w i s t i n g l i t t l e maze o f passages , a l l d i f f e r e n t . You are in a t w i s t y l i t t l e maze o f passages , a l l d i f f e r e n t . You are in a t w i s t y maze o f l i t t l e passages , a l l d i f f e r e n t . You are in a l i t t l e t w i s t y maze o f passages , a l l d i f f e r e n t . You are in a maze o f l i t t l e t w i s t i n g passages , a l l d i f f e r e n t . You are in a maze o f l i t t l e t w i s t y passages , a l l d i f f e r e n t . Dead end End You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re

at end o f road again . at h i l l in road . inside building . in v a l l e y . in f o r e s t . in f o r e s t . at s l i t in streambed . outside grate . below the grate . in Cobble Crawl . in Debris Room. in Bird Chamber . at top o f small p i t . in Hall o f Mists . on east bank o f f i s s u r e . in Nugget o f Gold Room. in Hall o f Mt King . at west end o f Twopit Room. in east p i t . in west p i t . at ”Y2” . at window on p i t . in d i r t y passage . in Dusty Rock room . at west end o f Hall o f Mists . at brink o f p i t . at east end o f Long Hall . at west end o f Long Hall . at Complex Junction . in Swiss Cheese room . at east end o f Twopit Room. in Slab Room. at junction o f three s e c r e t canyons . in s e c r e t E/W canyon above t i g h t canyon . in narrow c o r r i d o r . at steep i n c l i n e above l a r g e room . in Giant Room. in cavern with w a t e r f a l l . in Soft Room. in Oriental Room. in Misty Cavern . in Alcove . in Plover Room. in Dark−Room. in Arched Hall . in Shell Room. in Anteroom . at Witt ’ s End. in Mirror Canyon . at window on p i t . at top o f s t a l a c t i t e . at Reservoir . at NE end . at SW end . on SW side o f chasm . in sloping c o r r i d o r .

38 122 123 124 125 126 127 128 129 130 −1 3 1 1 1 1 1 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 10 10 10 11 11 11 11 11 11 12 12 12 12 12 13 13 13 13 13 14 14 14 14 14 14 14 15 15 15 15 15 15 16 17 17 17 17 17 18 19 19 19 19 19 19 19

Foundations of Videogame Programming Code Repository You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re You ’ re

2 3 4 5 8 1 5 1 11 33 79 1 5 7 8 4 50005 6 5 1 4 5 1 4 5 8 595 5 1 7 303009 593 303008 593 10 14 11 9 11 14 303008 9 10 12 3 14 303008 9 11 13 14 303008 9 11 12 14 303008 9 11 13 150020 15 16 18 17 19 150022 14 34 14 15 312596 412021 412597 27 15 15 311028 311029 311030 32 35074 211032

on in at at at in in in in

NE side o f chasm . corridor . fork in path . junction with warm walls . Breath−taking View . Chamber o f Boulders . limestone passage . f r o n t o f barren room . Barren Room.

2 3 5 6 63 2 6 3 62 65 5 4 6 5 63 9 6 6 44 2 9 6 12 4 6 5 60 6 12 4 3 3 11 11 17 31 51 11 19 31 63 64 17 25 62 31 63 64 30 19 31 63 64 51 25 23 63 64 51 23 30 30 33 36 7 10 29 29 55 1 38 39 7 41 41 38 10 45 46 44 45 49 49

44 12 13 45

29 19 14 43

12 45 11

7 46 32

14 12 43 46

45 44 30

43 7

30 45

46 45 43 46

44

45 43 15 14 43

44 16 30 44

13 19

45 30

43 46

30

43

45

30

44

29

30

46 46

29 18

20 22

19

21 44

44

43 51

18 19

23 29

24 44

43 29

51 44

43 31

44

43 31

34

44 46 38 30 31

44 45 34

35

42

44

69

11 29 36 37 7

45 43

43

43

23

43

19 20 21 22 23 23 23 23 24 25 25 25 26 27 27 27 27 27 27 28 28 28 29 30 30 31 31 32 33 33 33 33 33 33 34 34 35 35 36 36 36 36 37 37 38 38 39 39 39 40 41 41 41 41 42 42 42 42 42 43 43 43 44 44 44 44 45 45 45 45 45 46 47 48 49 49 50 50 50 50 51 51 51 51 52 52 52 52 52 52 53 53 53 54

74 0 0 15 67 68 25 648 67 23 724031 26 88 312596 412021 412597 17 40 41 19 33 36 19 19 62 524089 90 19 3 28 34 35 159302 100 33 15 33 20 37 28 39 65 36 38 37 595 36 64 65 41 42 27 59 60 41 42 43 45 80 42 44 45 43 48 50 82 42 43 46 47 87 45 45 44 50 51 44 49 51 52 49 50 52 53 50 51 52 53 55 86 51 52 54 53

66 1 1 1 43 44 30 52 29 29 56 56 1 39 7 41 41 45 44 38 45 30 38 38 44 1 1 1 65 46 43 44 71 71 30 29 43 39 43 29 44 70 44 30 56 60 43 30 70 1 46 43 45 44 29 45 43 46 44 44 46 43 43 30 46 45 44 45 43 46 29 44 43 29 43 44 43 44 30 46 44 29 43 46 44 43 46 29 45 30 44 45 46 44

42 61 31 11 11

42

43

11 55 52 11 11 29

46

53

54

69

45 43

55 55 17 52

17 31 29 14 23 52

29

17

30 11 11 11

11

56 11 30

4

58

23

56

5

glorkz 55 55 55 55 56 57 57 57 57 57 58 59 60 60 60 61 61 61 62 62 62 62 63 64 64 64 64 65 65 65 65 65 65 65 65 65 65 65 65 66 66 66 66 66 66 66 67 67 67 68 68 68 69 69 69 69 69 70 70 70 71 71 71 72 72 72 72 73 74 74 74 74 75 75 76 77 77 77 78 79 80 80 80 80 81 82 83 83 83 84 84 84 85 86

52 55 56 57 55 13 55 58 83 84 57 27 41 61 62 60 62 100107 60 63 30 61 62 39 65 103 106 64 66 80556 68 80556 50070 39 60556 75072 71 80556 106 65 67 80556 77 96 50556 97 66 23 24 23 69 65 68 331120 119 109 113 71 65 111 65 70 110 65 118 73 97 72 19 331120 121 75 76 77 75 75 78 66 77 3 42 80 80 81 80 44 57 84 85 57 83 114 83 52

44 45 30 43 29 30 44 46 45 43 43 1 43 44 45 43 45 46 44 45 43 46 46 29 44 45 43 43 44 46 61 29 29 29 45 45 45 30 30 47 44 46 25 43 50 72 43 44 30 46 29 45 30 46 46 45 75 45 30 46 48 46 45 70 49 45 48 46 43 44 44 30 46 45 45 43 44 45 46 1 45 44 46 43 44 46 46 43 44 45 44 50 43 29

11 56

11 29

17

30

52

11 56 70 74

59

42 31 56 61

23

72 17

17

11 11

11 11

11

87 88 88 88 89 90 91 91 92 92 92 93 94 94 94 95 95 95 96 97 97 97 98 98 99 99 99 100 100 100 100 100 101 102 103 103 103 103 103 104 104 105 105 106 106 106 107 107 107 107 107 107 107 107 107 107 108 108 108 109 109 110 110 111 111 111 111 112 112 112 112 112 112 112 112 112 112 113 114 115 116 116 117 117 117 117 117 117 118 118 119 119 120

39 45 29 25 30 20 39 92 44 25 1 23 1 95 45 72 30 88 46 93 43 94 45 92 46 92 46 309095 45 611 45 94 46 92 27 91 44 66 44 66 48 72 44 98 29 97 46 99 44 98 50 301 43 100 43 301 44 99 44 159302 71 33 71 101 47 100 46 103 30 102 29 104 30 114618 46 115619 46 64 46 103 29 105 30 104 29 103 74 64 29 65 44 108 43 131 46 132 49 133 47 134 48 135 29 136 50 137 43 138 44 139 45 61 30 95556 43 29 30 106 43 626 44 69 46 113 45 71 44 20 39 70 45 40050 30 50053 30 45 30 131 49 132 45 133 43 134 50 135 48 136 47 137 44 138 30 139 29 140 46 109 46 84 48 116 49 115 47 593 30 118 49 233660 41 332661 41 303 41 332021 39 596 39 72 30 117 29 69 45 653 43 69 45

30 56

43

27

73 56

23

27 27 3

11 23 73

11

11 17 45 72

73

73 23 23

11

22 71 74 38

11 11

74 11

45

46

47

75

39

56

11

109

42

69

11 7

47

48

49

50

40 120 121 121 122 122 122 122 122 122 122 123 123 123 123 124 124 124 124 124 125 125 125 126 126 126 127 127 127 128 128 128 129 129 129 129 130 130 130 131 131 131 131 131 131 131 131 131 131 132 132 132 132 132 132 132 132 132 132 133 133 133 133 133 133 133 133 133 133 134 134 134 134 134 134 134 134 134 134 135 135 135 135 135 135 135 135 135 135 136 136 136 136 136 136

Foundations of Videogame Programming Code Repository 74 74 653 123 233660 303 596 124 126 129 122 124 126 129 123 125 128 126 129 124 126 127 125 124 610 125 124 126 124 129 126 128 124 130 126 129 124 126 107 132 133 134 135 136 137 138 139 112 107 131 133 134 135 136 137 138 139 112 107 131 132 134 135 136 137 138 139 112 107 131 132 133 135 136 137 138 139 112 107 131 132 133 134 136 137 138 139 112 107 131 132 133 134 135

43 43 45 47 41 41 39 77 28 40 44 43 28 40 44 47 48 28 40 46 45 43 46 77 30 44 77 28 45 46 28 44 77 43 28 44 77 28 44 48 50 49 47 29 30 45 46 43 50 29 45 46 44 49 47 43 30 48 29 30 44 47 49 43 45 50 48 46 47 45 50 48 43 30 46 29 44 49 45 48 30 46 43 44 49 47 50 29 43 44 29 49 30 46

11 7 42

69

49

77

36 37

30

77 28 17 23

11

39 11

17

29 30

77 40

29 19 11

40

3

136 136 136 136 137 137 137 137 137 137 137 137 137 137 138 138 138 138 138 138 138 138 138 138 139 139 139 139 139 139 139 139 139 139 140 −1 4 2 2 3 4 5 6 7 7 7 8 8 8 9 10 11 11 11 11 12 12 13 14 15 16 17 18 19 19 19 20 21 21 22 23 23 24 25 26 27 28 29 29 29 29 29 30 30 30 30 31 32 33 34 35 36 37 38

137 138 139 112 107 131 132 133 134 135 136 138 139 112 107 131 132 133 134 135 136 137 139 112 107 131 132 133 134 135 136 137 138 112 112

road hill enter upstr downs fores forwa conti onwar back retur retre valle stair out outsi exit leave build house gully strea rock bed crawl cobbl inwar insid in surfa null nowhe dark passa tunne low canyo awkwa giant view upwar up u above ascen d downw down desce pit outdo crack steps dome left right hall

50 48 47 45 48 47 46 30 29 50 45 49 43 44 30 43 47 29 44 45 46 48 49 50 49 50 43 44 45 30 48 29 46 47 45

11

glorkz 39 40 41 42 43 43 44 44 45 45 46 46 47 48 49 50 51 52 53 54 55 56 57 57 57 57 58 59 60 61 61 62 63 64 65 66 67 69 70 71 72 73 74 75 76 76 77 1001 1001 1002 1002 1002 1003 1004 1005 1005 1006 1006 1007 1008 1009 1010 1010 1011 1012 1013 1014 1015 1016 1016 1016 1016 1017 1017 1018 1018 1019 1019 1020 1020 1021 1021 1022 1023 1024 1024 1025 1026 1027 1027 1028 1029 1030 1031

jump barre over acros east e west w north n south s ne se sw nw debri hole wall broke y2 climb look exami touch descr floor room slit slab slabr xyzzy depre entra plugh secre cave cross bedqu plove orien caver shell reser main offic fork keys key lamp headl lante grate cage wand rod wand rod steps bird door pillo velve snake fissu table clam oyste magaz issue spelu ” spel dwarf dwarv knife knive food ratio bottl jar water h2o oil mirro plant beans plant stala shado figur axe drawi pirat drago

( must be next o b j e c t a f t e r ” r e a l ” rod )

( must be next o b j e c t a f t e r ” r e a l ” plant )

1032 1033 1034 1035 1036 1037 1037 1038 1038 1039 1040 1040 1050 1050 1051 1052 1052 1053 1054 1055 1055 1055 1056 1056 1056 1057 1058 1058 1058 1058 1059 1060 1060 1061 1062 1062 1063 1064 2001 2001 2001 2001 2001 2001 2001 2001 2002 2002 2002 2002 2002 2003 2003 2003 2003 2003 2004 2004 2005 2006 2006 2007 2007 2008 2008 2009 2009 2009 2010 2010 2010 2011 2011 2011 2011 2011 2011 2011 2011 2011 2011 2012 2012 2012 2012 2012 2012 2013 2014 2014 2015 2016 2017 2017

41 chasm troll troll bear messa volca geyse machi vendi batte carpe moss gold nugge diamo silve bars jewel coins chest box treas eggs egg nest tride vase ming shard potte emera plati pyram pearl rug persi spice chain carry take keep catch steal captu get tote drop relea free disca dump say chant sing utter mumbl unloc open nothi lock close light on extin off wave shake swing calm placa tame walk run trave go proce conti explo goto follo turn attac kill slay fight hit strik pour eat devou drink rub throw toss

( must be next o b j e c t a f t e r ” r e a l ” t r o l l )

( same as volcano )

42 2018 2019 2019 2020 2020 2021 2022 2023 2023 2023 2023 2024 2025 2025 2025 2025 2025 2026 2027 2027 2028 2028 2028 2029 2029 2030 2030 2030 2031 3001 3002 3003 3004 3005 3050 3050 3050 3050 3050 3050 3050 3051 3051 3064 3064 3066 3066 3068 3069 3079 3139 3142 3142 3147 −1 5 1 000 2 000 100 3 000 100 4 000 5 000 6 000 7 000 100 8 000 100 9 000 100 10 000 11 000 100 12 000 100 200 13 000 000 14 000 15

Foundations of Videogame Programming Code Repository quit f in d where inven inv feed fill blast deton ignit blowu score fee fie foe foo fum brief read perus break shatt smash wake distu suspe pause save hours fee fie foe foo fum sesam opens abra abrac shaza hocus pocus help ? tree trees dig excav lost mist fudge stop info infor swim

000 100 100 16 000 19 000 20 000 100 200 21 22 23 000 24 000 100 200 200 300 400 500 25 000 100 200 26 000 27 000 28 000 100 29 000 30 000 31 000 100 100 200 32 000 000 100

Set o f keys There are some keys on the ground here . Brass lantern There i s a shiny brass lamp nearby . There i s a lamp shining nearby . ∗Grate The grate i s locked . The grate i s open . Wicker cage There i s a small wicker cage discarded nearby . Black rod A three f o o t black rod with a rusty star on an end l i e s nearby . Black rod A three f o o t black rod with a rusty mark on an end l i e s nearby . ∗Steps Rough stone steps lead down the p i t . Rough stone steps lead up the dome. L i t t l e bird in cage A cheerful l i t t l e bird i s s i t t i n g here singing . There i s a l i t t l e bird in the cage . ∗Rusty door The way north i s barred by a massive , rusty , iron door . The way north leads through a massive , rusty , iron door . Velvet p i l l o w A small v e l v e t p i l l o w l i e s on the f l o o r . ∗Snake A huge green f i e r c e snake bars the way ! >$< ( Chased away ) ∗Fissure >$< A c r y s t a l bridge now spans the f i s s u r e . The c r y s t a l bridge has vanished ! ∗Stone t a b l e t A massive stone t a b l e t embedded in the wall reads : ” Congratulations on bringing l i g h t i n t o the Dark−Room! ” Giant clam >grunt!< There i s an enormous clam here with i t s s h e l l t i g h t l y closed . Giant oyster >groan!
$< ∗Plant There i s a t i n y l i t t l e plant in the p i t , murmuring ” Water , water , ... ” The plant spurts i n t o furious growth f o r a few seconds . There i s a 12−foot−t a l l beanstalk stretching up out o f the p i t , bellowing ”WATER! ! WATER! ! ” The plant grows e x p l o s i v e l y , almost f i l l i n g the bottom o f the p i t . There i s a g i g a n t i c beanstalk stretching a l l the way up to the hole . You ’ ve over−watered the plant ! I t ’ s s h r i v e l i n g up ! I t ’ s , i t ’ s ... ∗Phony plant ( seen in twopit room only when t a l l enough ) >$< The top o f a 12−foot−t a l l beanstalk i s poking out o f the west p i t . There i s a huge beanstalk growing out o f the west p i t up to the hole . ∗S t a l a c t i t e >$< ∗Shadowy f i g u r e The shadowy f i g u r e seems to be t r y i n g to a t t r a c t your attention . Dwarf ’ s axe There i s a l i t t l e axe here . There i s a l i t t l e axe l y i n g beside the bear . ∗Cave drawings >$< ∗P i r a t e >$< ∗Dragon A huge green f i e r c e dragon bars the way ! Congratulations ! You have j u s t vanquished a dragon with your bare hands ! ( Unbelievable , isn ’ t i t ? ) The body o f a huge green dead dragon i s l y i n g o f f to one side . ∗Chasm A r i c k e t y wooden bridge extends across the chasm, vanishing i n t o the mist . A sign posted on the bridge reads , ” Stop ! Pay t r o l l ! ” The wreckage o f a bridge ( and a dead bear ) can be seen at the bottom o f the chasm . ∗T r o l l A burly t r o l l stands by the bridge and i n s i s t s you throw him a treasure before you may cross . The t r o l l steps out from beneath the bridge and blocks your way . >$< ( Chased away ) ∗Phony t r o l l The t r o l l i s nowhere to be seen . >$< ( Bear uses r t e x t 141) There i s a ferocious cave bear eying you from the f a r end o f the room ! There i s a gentle cave bear s i t t i n g p l a c i d l y in one corner . There i s a contented−looking bear wandering about nearby . >$< ( Dead ) ∗Message in second maze There i s a message scrawled in the dust in a flowery s c r i p t , reading : ” This i s not the maze where the p i r a t e leaves his treasure chest . ” ∗Volcano and/or geyser >$< ∗Vending machine There i s a massive vending machine here . The instructions on i t read : ”Drop coins here to r e c e i v e fresh b a t t e r i e s . ” Batteries There are fresh b a t t e r i e s here . Some worn−out b a t t e r i e s have been discarded nearby . ∗Carpet and/or moss >$< Large gold nugget There i s a l a r g e sparkling nugget o f gold here ! Several diamonds There are diamonds here ! Bars o f s i l v e r There are bars o f s i l v e r here ! Precious j e w e l r y

glorkz 000 54 000 55 000 56 000 100 200 57 000 58 000 100 200 300 59 000 60 000 61 000 62 000 100 63 000 64 000 100 200 −1 6 1 1 1 1 1 1 1 1 1 1 1 1 2 3 3 4 5 6 7 8 9 10 11 11 12 13 14 15 15 16 17 18 19 19 20 21 21 21 22 23 24 25 26 26 27 28

There i s precious j e w e l r y here ! Rare coins There are many coins here ! Treasure chest The pirate ’ s treasure chest i s here ! Golden eggs There i s a l a r g e nest here , f u l l o f golden eggs ! The nest o f golden eggs has vanished ! Done ! Jeweled t r i d e n t There i s a jewel−encrusted t r i d e n t here ! Ming vase There i s a d e l i c a t e , precious , Ming vase here ! The vase i s now resting , d e l i c a t e l y , on a v e l v e t p i l l o w . The f l o o r i s l i t t e r e d with worthless shards o f pottery . The Ming vase drops with a d e l i c a t e crash . Egg−sized emerald There i s an emerald here the s i z e o f a plover ’ s egg ! Platinum pyramid There i s a platinum pyramid here , 8 inches on a side ! Glistening pearl Off to one side l i e s a g l i s t e n i n g pearl ! Persian rug There i s a Persian rug spread out on the f l o o r ! The dragon i s sprawled out on a Persian rug ! ! Rare spices There are rare spices here ! Golden chain There i s a golden chain l y i n g in a heap on the f l o o r ! The bear i s locked to the wall with a golden chain ! There i s a golden chain locked to the wall !

29 30 30 31 32 33 34 35 36 37 38 39 40 41 41 42 43 44 45 46 47 48 49 50 51 51 51 51

Somewhere nearby i s Colossal Cave , where others have found fortunes in treasure and gold , though i t i s rumored that some who enter are never seen again . Magic i s said to work in the cave . I w i l l be your eyes and hands . Direct me with commands o f 1 or 2 words . I should warn you that I look at only the f i r s t f i v e l e t t e r s o f each word , so you ’ l l have to enter ” northeast ” as ” ne ” to distinguish i t from ” north ” . ( Should you get stuck , type ” help ” f o r some general hints . For information on how to end your adventure , etc . , type ” i n f o ” . ) −−− This program was o r i g i n a l l y developed by W i l l Crowther . Most o f the features o f the current program were added by Don Woods . Address complaints about the UNIX version to Jim G i l l o g l y ( jim@rand . org ) . A l i t t l e dwarf with a big knife blocks your way . A l i t t l e dwarf j u s t walked around a corner , saw you , threw a little axe at you ( which missed ) , cursed , and ran away . There i s a threatening l i t t l e dwarf in the room with you ! One sharp nasty knife i s thrown at you ! None o f them h i t you ! One o f them gets you ! A hollow voice says ” Plugh ” . There i s no way to go that d i r e c t i o n . I am unsure how you are facing . Use compass points or nearby objects . I don ’ t know in from out here . Use compass points or name something in the general d i r e c t i o n you want to go . I don ’ t know how to apply that word here . I don ’ t understand that ! I ’m game . Would you care to explain how? Sorry , but I am not allowed to g i v e more d e t a i l . I w i l l repeat the long description o f your l o c a t i o n . I t i s now pitch dark . I f you proceed you w i l l l i k e l y f a l l i n t o a pit . I f you prefer , simply type w rather than west . Are you t r y i n g to catch the bird ? The bird i s frightened r i g h t now and you cannot catch i t no matter what you t r y . Perhaps you might t r y l a t e r . Are you t r y i n g to somehow deal with the snake? You can ’ t k i l l the snake , or d r i v e i t away , or avoid i t , or anything l i k e that . There i s a way to get by , but you don ’ t have the necessary resources r i g h t now. Do you r e a l l y want to quit now? You f e l l i n t o a p i t and broke every bone in your body ! You are already carrying i t ! You can ’ t be serious ! The bird was unafraid when you entered , but as you approach i t becomes disturbed and you cannot catch i t . You can catch the bird , but you cannot carry i t . There i s nothing here with a lock !

51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 52 53 54 55 56 56 57 57 58 59 59 60 61 62 63 63 63 64 64 64 64 64 64 65 66 66 67

43 You aren ’ t carrying i t ! The l i t t l e bird attacks the green snake , and in an astounding flurry drives the snake away . You have no keys ! I t has no lock . I don ’ t know how to lock or unlock such a thing . I t was already locked . The grate i s now locked . The grate i s now unlocked . I t was already unlocked . You have no source o f l i g h t . Your lamp i s now on . Your lamp i s now o f f . There i s no way to get past the bear to unlock the chain , which is probably j u s t as w e l l . Nothing happens . Where? There i s nothing here to attack . The l i t t l e bird i s now dead . I t s body disappears . Attacking the snake both doesn ’ t work and i s very dangerous . You k i l l e d a l i t t l e dwarf . You attack a l i t t l e dwarf , but he dodges out o f the way . With what? Your bare hands? Good try , but that i s an old worn−out magic word . I know o f places , actions , and things . Most o f my vocabulary describes places and i s used to move you there . To move, t r y words l i k e f o r e s t , building , downstream , enter , east , west , north , south , up, or down. I know about a few s p e c i a l objects , l i k e a black rod hidden in the cave . These objects can be manipulated using some of the action words that I know. Usually you w i l l need to g i v e both the o b j e c t and action words ( in e i t h e r order ) , but sometimes I can infer the o b j e c t from the verb alone . Some objects also imply verbs ; in particular , ” inventory ” implies ” take inventory ” , which causes me to g i v e you a l i s t o f what you ’ re carrying . The objects have side e f f e c t s ; f o r instance , the rod scares the bird . Usually people having trouble moving j u s t need to t r y a few more words . Usually people t r y i n g unsuccessfully to manipulate an o b j e c t are attempting something beyond t h e i r ( or my ! ) c a p a b i l i t i e s and should t r y a completely d i f f e r e n t tack . To speed the game you can sometimes move long distances with a s i n g l e word . For example , ” building ” usually gets you to the building from anywhere above ground except when l o s t in the f o r e s t . Also , note that cave passages turn a l o t , and that leaving a room to the north does not guarantee entering the next from the south . Good luck ! I t misses ! I t gets you ! OK You can ’ t unlock the keys . You have crawled around in some l i t t l e holes and wound up back in the main passage . I don ’ t know where the cave is , but hereabouts no stream can run on the surface f o r long . I would t r y the stream . I need more d e t a i l e d instructions to do that . I can only t e l l you what you see as you move about and manipulate things . I cannot t e l l you where remote things are . I don ’ t know that word . What? Are you t r y i n g to get i n t o the cave? The grate i s very s o l i d and has a hardened s t e e l lock . You cannot enter without a key , and there are no keys nearby . I would recommend looking elsewhere f o r the keys . The t r e e s o f the f o r e s t are l a r g e hardwood oak and maple , with an occasional grove o f pine or spruce . There i s quite a b i t o f under− growth , l a r g e l y birch and ash saplings plus nondescript bushes o f various sorts . This time o f year v i s i b i l i t y i s quite r e s t r i c t e d by a l l the leaves , but t r a v e l i s quite easy i f you detour around the spruce and berry bushes . Welcome to Adventure ! ! Would you l i k e instructions ? Digging without a shovel i s quite impractical . Even with a shovel progress i s unlikely . Blasting requires dynamite .

44 68 69 69 69 70 71 72 73 73 74 75 75 76 77 78 79 80 81 81 81 82 82 82 82 83 83 84 84 85 85 86 90 91 92 93 94 95 96 97 98 99 100 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 114 115 116 117 117 118 119 120 121 122 123 124 124 124 125 125 126 126 127 128 128

Foundations of Videogame Programming Code Repository I ’m as confused as you are . Mist i s a white vapor , usually water , seen from time to time in caverns . I t can be found anywhere but i s frequently a sign o f a deep p i t leading down to water . Your f e e t are now wet . I think I j u s t l o s t my appetite . Thank you , i t was d e l i c i o u s ! You have taken a drink from the stream . The water t a s t e s strongly o f minerals , but i s not unpleasant . I t i s extremely cold . The b o t t l e o f water i s now empty . Rubbing the e l e c t r i c lamp i s not p a r t i c u l a r l y rewarding . Anyway, nothing e x c i t i n g happens . Peculiar . Nothing unexpected happens . Your b o t t l e i s empty and the ground i s wet . You can ’ t pour that . Watch i t ! Which way? Oh dear , you seem to have gotten y o u r s e l f k i l l e d . I might be able to help you out , but I ’ ve never r e a l l y done t h i s before . Do you want me to t r y to reincarnate you? A l l r i g h t . But don ’ t blame me i f something goes wr . . . . . . −−− Poof ! ! −−− You are engulfed in a cloud o f orange smoke . Coughing and gasping , you emerge from the smoke and f i n d . . . . You clumsy oaf , you ’ ve done i t again ! I don ’ t know how long I can keep t h i s up . Do you want me to t r y reincarnating you again? Okay, now where did I put my orange smoke ? . . . . >poof!< Everything disappears in a dense cloud o f orange smoke . Now you ’ ve r e a l l y done i t ! I ’m out o f orange smoke ! You don ’ t expect me to do a decent reincarnation without any orange smoke, do you? Okay, i f you ’ re so smart , do i t y o u r s e l f ! I ’m leaving ! >>> Messages 81 thru 90 are reserved f o r ” o b i t u a r i e s ” . wrench!< You don ’ t have anything strong enough to open the clam . You don ’ t have anything strong enough to open the oyster . A g l i s t e n i n g pearl f a l l s out o f the clam and r o l l s away . Goodness , t h i s must r e a l l y be an oyster . ( I never was very good at identifying b i v a l v e s . ) Whatever i t is , i t has now snapped shut again . The oyster creaks open , r e v e a l i n g nothing but oyster inside . It promptly snaps shut again . You have crawled around in some l i t t l e holes and found your way blocked by a recent cave−in . You are now back in the main passage . There are f a i n t r u s t l i n g noises from the darkness behind you . Out from the shadows behind you pounces a bearded p i r a t e ! ”Har , har , ” he chortles , ” I ’ l l j u s t take a l l t h i s booty and hide i t away with

128 128 129 129 130 130 131 131 132 132 132 133 133 133 133 134 134 134 135 135 136 136 136 137 138 139 140 141 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 143 144 145 146 147 148 149 149 150 151 152

me chest deep in the maze ! ” He snatches your treasure and vanishes into the gloom . A sepulchral voice reverberating through the cave , says , ” Cave closing soon . A l l adventurers exit immediately through Main O f f i c e . ” A mysterious recorded voice groans i n t o l i f e and announces : ” This exit i s closed . Please leave v i a Main O f f i c e . ” I t looks as though you ’ re dead . Well , seeing as how i t ’ s so close to c l o s i n g time anyway , I think we ’ l l j u s t c a l l i t a day . The sepulchral voice intones , ” The cave i s now closed . ” As the echoes fade , there i s a blinding f l a s h o f l i g h t ( and a small puff o f orange smoke ) . . . . As your eyes refocus , you look around and f i n d ... There i s a loud explosion , and a twenty−f o o t hole appears in the far wall , burying the dwarves in the rubble . You march through the hole and f i n d y o u r s e l f in the Main O f f i c e , where a cheering band o f f r i e n d l y e l v e s carry the conquering adventurer o f f i n t o the sunset . There i s a loud explosion , and a twenty−f o o t hole appears in the far wall , burying the snakes in the rubble . A r i v e r o f molten lava pours in through the hole , destroying everything in i t s path , including you ! There i s a loud explosion , and you are suddenly splashed across the walls o f the room . The r e s u l t i n g ruckus has awakened the dwarves . There are now several threatening l i t t l e dwarves in the room with you ! Most o f them throw knives at you ! A l l o f them get you ! Oh, leave the poor unhappy bird alone . I dare say whatever you want i s around here somewhere . I don ’ t know the word ” stop ” . Use ” quit ” i f you want to g i v e up . You can ’ t get there from here . You are being followed by a very large , tame bear . I f you want to end your adventure early , say ” quit ” . To suspend your adventure such that you can continue l a t e r , say ” suspend ” ( or ” pause ” or ” save ” ) . To see what hours the cave i s normally open , say ” hours ” . To see how w e l l you ’ re doing , say ” score ” . To get f u l l c r e d i t for a treasure , you must have l e f t i t s a f e l y in the building , though you get p a r t i a l c r e d i t j u s t f o r l o c a t i n g i t . You l o s e points f o r g e t t i n g k i l l e d , or f o r quitting , though the former costs you more . There are also points based on how much ( i f any ) o f the cave you ’ ve managed to explore ; in particular , there i s a l a r g e bonus j u s t f o r g e t t i n g in ( to distinguish the beginners from the r e s t o f the pack ) , and there are other ways to determine whether you ’ ve been through some o f the more harrowing sections . I f you think you ’ ve found a l l the treasures , just keep exploring f o r a while . I f nothing i n t e r e s t i n g happens , you haven ’ t found them a l l yet . I f something i n t e r e s t i n g ∗does∗ happen , i t means you ’ re g e t t i n g a bonus and have an opportunity to garner many more points in the Master ’ s Section . I may occasionally o f f e r hints i f you seem to be having trouble . I f I do , I ’ l l warn you in advance how much i t w i l l a f f e c t your score to accept the hints . Finally , to save paper , you may s p e c i f y ” b r i e f ” , which t e l l s me never to repeat the f u l l description o f a place unless you e x p l i c i t l y ask me to . Do you indeed wish to quit now? There i s nothing here with which to f i l l the vase . The sudden change in temperature has d e l i c a t e l y shattered the vase . I t i s beyond your power to do that . I don ’ t know how. I t i s too f a r up f o r you to reach . You k i l l e d a l i t t l e dwarf . The body vanishes in a cloud o f greasy black smoke . The s h e l l i s very strong and i s impervious to attack . What ’ s the matter , can ’ t you read? Now you ’ d best s t a r t over . The axe bounces harmlessly o f f the dragon ’ s thick scales .

glorkz 153 154 154 155 156 156 157 157 158 158 159 160 161 162 162 162 162 163 163 164 165 166 167 168 168 169 170 171 172 173 174 175 176 177 178 179 179 179 180 181 182 183 183 183 184 185 185 186 186 186 186 186 187 188 188 189 189 190 191 192 192 193 193 194 195 196 197 197 198 199

The dragon looks rather nasty . You ’ d best not t r y to get by . The l i t t l e bird attacks the green dragon , and in an astounding flurry gets burnt to a cinder . The ashes blow away . On what? Okay, from now on I ’ l l only describe a place in f u l l the f i r s t time you come to i t . To get the f u l l description , say ” look ” . T r o l l s are close r e l a t i v e s with the rocks and have skin as tough as that o f a rhinoceros . The t r o l l fends o f f your blows effortlessly . The t r o l l d e f t l y catches the axe , examines i t c a r e f u l l y , and tosses i t back , declaring , ”Good workmanship , but i t ’ s not valuable enough . ” The t r o l l catches your treasure and scurries away out o f s i g h t . The t r o l l refuses to l e t you cross . There i s no longer any way across the chasm . Just as you reach the other side , the bridge buckles beneath the weight o f the bear , which was s t i l l f o l l o w i n g you around . You scrabble desperately f o r support , but as the bridge collapses you stumble back and f a l l i n t o the chasm . The bear lumbers toward the t r o l l , who l e t s out a s t a r t l e d shriek and scurries away . The bear soon g i v e s up the pursuit and wanders back . The axe misses and lands near the bear where you can ’ t get at i t . With what? Your bare hands? Against ∗his∗ bear hands?? The bear i s confused ; he only wants to be your f r i e n d . For crying out loud , the poor thing i s already dead ! The bear eagerly wolfs down your food , a f t e r which he seems to calm down considerably and even becomes rather f r i e n d l y . The bear i s s t i l l chained to the wall . The chain i s s t i l l locked . The chain i s now unlocked . The chain i s now locked . There i s nothing here to which the chain can be locked . There i s nothing here to eat . Do you want the hint ? Do you need help g e t t i n g out o f the maze? You can make the passages look l e s s a l i k e by dropping things . Are you t r y i n g to explore beyond the Plover Room? There i s a way to explore that region without having to worry about f a l l i n g i n t o a p i t . None o f the objects a v a i l a b l e i s immediately useful in discovering the s e c r e t . Do you need help g e t t i n g out o f here? Don ’ t go west . Gluttony i s not one o f the t r o l l ’ s v i c e s . Avarice , however , i s . Your lamp i s g e t t i n g dim . You ’ d best s t a r t wrapping t h i s up, unless You can f i n d some fresh b a t t e r i e s . I seem to r e c a l l there ’ s a vending machine in the maze . Bring some coins with you . Your lamp has run out o f power . There ’ s not much point in wandering around out here , and you can ’ t explore the cave without a lamp . So l e t ’ s j u s t c a l l i t a day . There are f a i n t r u s t l i n g noises from the darkness behind you . As you turn toward them , the beam o f your lamp f a l l s across a bearded pirate . He i s carrying a l a r g e chest . ” Shiver me timbers ! ” he c r i e s , ” I ’ ve been spotted ! I ’ d best hie meself o f f to the maze to hide me chest ! ” With that , he vanishes i n t o the gloom . Your lamp i s g e t t i n g dim . You ’ d best go back f o r those b a t t e r i e s . Your lamp i s g e t t i n g dim . I ’m taking the l i b e r t y o f replacing the batteries . Your lamp i s g e t t i n g dim , and you ’ re out o f spare b a t t e r i e s . You ’d best s t a r t wrapping t h i s up . I ’m a f r a i d the magazine i s written in Dwarvish . ” This i s not the maze where the p i r a t e leaves his treasure chest . ” Hmmm, t h i s looks l i k e a clue , which means i t ’ l l cost you 10 points to read i t . Should I go ahead and read i t anyway? I t says , ” there i s something strange about t h i s place , such that one o f the words I ’ ve always known now has a new e f f e c t . ” I t says the same thing i t did before . I ’m a f r a i d I don ’ t understand . ” Congratulations on bringing l i g h t i n t o the Dark−Room! ” You s t r i k e the mirror a resounding blow , whereupon i t shatters into a myriad t i n y fragments . You have taken the vase and hurled i t d e l i c a t e l y to the ground . You prod the nearest dwarf , who wakes up grumpily , takes one look

199 200 201 202 202 −1 7 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 −1 8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

45 at you , curses , and grabs f o r his axe . I s t h i s acceptable ? There ’ s no point in suspending a demonstration game . You awaken only to discover your b i t s have been d i s s o l v i n g while you s l e p t . You disappear in a cloud o f greasy black smoke .

3 3 8 10 11 0 14 13 94 96 19 17 101 103 0 106 0 0 3 3 0 0 109 25 23 111 35 0 97 0 119 117 117 0 130 0 126 140 0 96 18 27 28 29 30 0 92 95 97 100 101 0 119 127 130

24 29 0 33 0 33 38 38 42 14 43 110 29 110 73 75 29 13 59 59 174 109 67 13 147 155 195 146

9

15 −1 −1 27 −1

−1

−1 −1 67 −1 110 −1 −1 121 122 122 0 −1 −1 −1 −1 −1

121 −1

46 29 30 31 −1 9 0

Foundations of Videogame Programming Code Repository 110 13 13

1 1 1

1

2

3

4

5

6

7

8

115 3

116 4

126 7

38

95

113

24

9

10 0 2 1 3 3 4 5 6 7 7 8 9 −1 10 35 100 130 200 250 300 330 349 9999 −1 11 2 3 4 5 6 7 8 9 −1 12 1

100 1 24 46 86 122 130 8 13 19 42 50 52 86 99 108

47

48

54

56

58

82

85

123

124

125

126

127

128

129

44

45

46

47

48

49

54

55

56

80

81

82

43 51 53 87 100

101

You are obviously a rank amateur . Better luck next time . Your score q u a l i f i e s you as a Novice class Adventurer . You have achieved the r a t i n g : ” Experienced Adventurer ” . You may now consider y o u r s e l f a ” Seasoned Adventurer ” . You have reached ” Junior Master ” status . Your score puts you in Master Adventurer Class C. Your score puts you in Master Adventurer Class B. Your score puts you in Master Adventurer Class A . A l l o f Adventuredom g i v e s t r i b u t e to you , Adventurer Grandmaster !

9999 9999 4 5 8 75 25 20

10 5 2 2 2 4 5 3

0 0 62 18 20 176 178 180

0 0 63 19 21 177 179 181

A l a r g e cloud o f green smoke appears in f r o n t o f you . away

I t clears

1 2 3 4 5 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 21 22 23 24 25 26 27 28 29 30 31 32 −1 0

to r e v e a l a t a l l wizard , clothed in grey . He f i x e s you with a steely g l a r e and declares , ” This adventure has lasted too long . ” With that he makes a s i n g l e pass over you with his hands , and everything around you fades away i n t o a grey nothingness . Even wizards have to wait longer than that ! I ’m t e r r i b l y sorry , but Colossal Cave i s closed . Our hours are : Only wizards are permitted within the cave r i g h t now. We do allow v i s i t o r s to make short explorations during our o f f hours . Would you l i k e to do that ? Colossal Cave i s open to regular adventurers at the f o l l o w i n g hours : Very w e l l . Only a wizard may continue an adventure t h i s soon . I suggest you resume your adventure at a l a t e r time . Do you wish to see the hours? Do you wish to change the hours? New magic word ( null to leave unchanged ) : New magic number ( null to leave unchanged ) : Do you wish to change the message o f the day? Okay . You can save t h i s version now. Are you a wizard? Prove i t ! Say the magic word ! That i s not what I thought i t was . Do you know what I thought i t was? Oh dear , you r e a l l y ∗are∗ a wizard ! Sorry to have bothered you . . . Foo , you are nothing but a charlatan ! New hours s p e c i f i e d by defining ” prime time” . Give only the hour (E. g . 14, not 14:00 or 2pm) . Enter a negative number a f t e r l a s t pair . New hours f o r Colossal Cave : Limit l i n e s to 70 chars . End with null l i n e . Line too long , retype : Not enough room f o r another l i n e . Ending message here . Do you wish to ( re ) schedule the next holiday ? To begin how many days from today? To l a s t how many days ( zero i f no holiday ) ? To be c a l l e d what ( up to 20 characters ) ? Too small ! Assuming minimum value (45 minutes ) . Break out o f t h i s and save your core−image . Be sure to save your core−image . . .

glorkz

8.1

93/05/31

Chapter 2 Game of Life

// John Conway ’ s Game o f L i f e // Implementation Copyright ( C ) 2020 Ben Sanders import java . u t i l . Vector ; public class GameOfLife { public static Vector< Vector< I n t e g e r > > loadGame ( String t e x t f i l e ) { Vector< Vector< I n t e g e r > > r e t = new Vector< Vector< I n t e g e r > >() ; i f ( t e x t f i l e . equals ( ” d e f a u l t ” ) ) { for ( int i = 0; i < 8; i ++ ) { Vector< I n t e g e r > tempRow = new Vector< I n t e g e r >() ; for ( int j = 0; j < 12; j ++ ) { tempRow . add ( 0 ) ; } r e t . add ( tempRow ) ; } r e t . elementAt ( 0 ) . set ( 0 , r e t . elementAt ( 0 ) . set ( 1 , r e t . elementAt ( 0 ) . set ( 2 , r e t . elementAt ( 1 ) . set ( 2 , r e t . elementAt ( 2 ) . set ( 1 ,

1) ; 1) ; 1) ; 1) ; 1) ;

} return r e t ; }

47

48

Foundations of Videogame Programming Code Repository public static I n t e g e r checkNbrs ( Vector< Vector< I n t e g e r > > gameBoard , I n t e g e r row , I n t e g e r c o l ) { I n t e g e r r e t = 0; I n t e g e r l e f t = col −1; I n t e g e r r i g h t = c o l +1; I n t e g e r top = row−1; I n t e g e r bot = row+1; if ( left < 0 ) { l e f t = l e f t + gameBoard . elementAt ( 0 ) . s i z e ( ) ; } i f ( r i g h t >= gameBoard . elementAt ( 0 ) . s i z e ( ) ) { r i g h t = r i g h t − gameBoard . elementAt ( 0 ) . s i z e ( ) ; } i f ( top < 0 ) { top = top + gameBoard . s i z e ( ) ; } i f ( bot >= gameBoard . s i z e ( ) ) { bot = bot − gameBoard . s i z e ( ) ; } i f ( gameBoard . elementAt ( top ) . elementAt ( l e f t ) > 0 ) { r e t = r e t + 1; } i f ( gameBoard . elementAt ( top ) . elementAt ( c o l ) > 0 ) { r e t = r e t + 1; } i f ( gameBoard . elementAt ( top ) . elementAt ( r i g h t ) > 0 ) { r e t = r e t + 1; } i f ( gameBoard . elementAt ( row ) . elementAt ( l e f t ) > 0 ) { r e t = r e t + 1; } i f ( gameBoard . elementAt ( row ) . elementAt ( r i g h t ) > 0 ) { r e t = r e t + 1; } i f ( gameBoard . elementAt ( bot ) . elementAt ( l e f t ) > 0 )

Game of Life

49

{ r e t = r e t + 1; } i f ( gameBoard . elementAt ( bot ) . elementAt ( c o l ) > 0 ) { r e t = r e t + 1; } i f ( gameBoard . elementAt ( bot ) . elementAt ( r i g h t ) > 0 ) { r e t = r e t + 1; } return r e t ; } public static Vector< Vector< In t e g e r > > updateGame ( Vector< Vector< I n t e g er > > gameBoard ) { Vector< Vector< I n t e g e r > > r e t = new Vector< Vector< I n t e g e r > >() ; // i n i t r e t for ( int i = 0; i < gameBoard . s i z e ( ) ; i ++ ) { Vector< I n t e g e r > tempRow = new Vector< I n t e g e r >() ; for ( int j = 0; j < gameBoard . elementAt ( i ) . s i z e ( ) ; j ++ ) { tempRow . add ( 0 ) ; } r e t . add ( tempRow ) ; } // populate r e t based on 4 Game o f L i f e r u l e s . // 1. Any l i v e c e l l with fewer than two l i v e neighbours dies , as i f by underpopulation . // 2. Any l i v e c e l l with two or three l i v e neighbours l i v e s on t o the next generation . // 3. Any l i v e c e l l with more than three l i v e neighbours dies , as i f by overpopulation . // 4. Any dead c e l l with e x a c t l y three l i v e neighbours becomes a l i v e c e l l , as i f by reproduction . for ( int i = 0; i < gameBoard . s i z e ( ) ; i ++ ) { for ( int j = 0; j < gameBoard . elementAt ( i ) . s i z e ( ) ; j ++ ) { int nbrCnt = checkNbrs ( gameBoard , i , j ) ; i f ( gameBoard . elementAt ( i ) . elementAt ( j ) > 0 ) // l i v e c e l l {

50

Foundations of Videogame Programming Code Repository i f ( nbrCnt < 2 ) { r e t . elementAt ( i ) . set ( j , 0) ; } else i f ( nbrCnt == 2 | | nbrCnt == 3 ) { r e t . elementAt ( i ) . set ( j , 1) ; } else i f ( nbrCnt > 3 ) { r e t . elementAt ( i ) . set ( j , 0) ; } } else // dead c e l l { i f ( nbrCnt == 3 ) { r e t . elementAt ( i ) . set ( j , 1) ; } } } } return r e t ; } public static void printGame ( Vector< Vector< I n t e g e r > > gameBoard ) { System . out . p r i n t ( ” \ t ” ) ; for ( int i = 0; i < gameBoard . elementAt ( 0 ) . s i z e ( ) ; i ++) { System . out . p r i n t ( i + ” \ t ” ) ; } System . out . p r i n t l n ( ) ; System . out . p r i n t l n ( ) ; System . out . p r i n t l n ( ) ; for ( int i = 0; i < gameBoard . s i z e ( ) ; i ++) { System . out . p r i n t ( i ) ; String tempRow = new String ( ) ; tempRow += ” \ t ” ; for ( int j = 0; j < gameBoard . elementAt ( 0 ) . s i z e ( ) ; j ++) { i f ( gameBoard . elementAt ( i ) . elementAt ( j ) == 0 ) {

Game of Life

51

tempRow += ” \ t ” ; //gameBoard . elementAt ( i ) . elementAt ( j ) + ”\ t ”; } else { tempRow += ” ∗∗∗∗∗∗∗∗ ” ; } } System . out . p r i n t l n ( tempRow ) ; System . out . p r i n t l n ( tempRow ) ; System . out . p r i n t l n ( tempRow ) ; } } public static void main ( String [ ] args ) { // TODO: load t e x t f i l e input Vector< Vector< I n t e g e r > > gameBoard = loadGame ( ” d e f a u l t ” ) ; while ( true ) { printGame ( gameBoard ) ; gameBoard = updateGame ( gameBoard ) ; try { Thread . sleep (1000) ; } catch ( InterruptedException i e ) { i e . printStackTrace ( ) ; } } } }

52

Foundations of Videogame Programming Code Repository

Chapter 3 Networked Chess

Client // TCPChessThreadClient . java Copyright ( C ) 2020 Ben Sanders import java . net . ∗ ; import java . i o . ∗ ; public class TCPChessThreadClient { public static void printNewBoard ( ) { System . out . p r i n t l n ( ” \ t a\ t b\ t c\ t d\ t e\ t f \ t g\ t h” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” 8\ t |wCa\ t |wKn\ t | wBi\ t | wKi\ t |wQu\ t | wBi\ t |wKn \ t |wCa\ t | ” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” 7\ t | wPa\ t | wPa\ t | wPa\ t | wPa\ t | wPa\ t | wPa\ t | wPa \ t | wPa\ t | ” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” 6\ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ 53

54

Foundations of Videogame Programming Code Repository ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” 5\ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; 4\ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; 3\ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; 2\ t | bPa\ t | bPa\ t | bPa\ t | bPa\ t | bPa\ t | bPa\ t | bPa System . out . p r i n t l n ( ” \ t | bPa\ t | ” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); System . out . p r i n t l n ( ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | ” ) ; System . out . p r i n t l n ( ” 1\ t | bCa\ t | bKn\ t | bBi\ t | bKi\ t |bQu\ t | bBi\ t | bKn \ t | bCa\ t | ” ) ; System . out . p r i n t l n ( ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+ ” ); } public static void main ( String [ ] args ) throws IOException { i f ( ( args . length < 2) | | ( args . length > 3) ) { throw new IllegalArgumentException ( ” Parameter ( s ) : [] ” ) ; } //printNewBoard ( ) ; String server = args [ 0 ] ; // Server name or IP address String input = args [ 1 ] ; byte [ ] byteBuffer = input . getBytes ( ) ; int b u f f e r S i z e = 1195; int servPort = ( args . length == 3) ? I n t e g e r . parseInt ( args [ 2 ] ) : 7; int oddTurn = 0;

Client

55

boolean f i r s t T u r n = true ; while ( ! input . substring ( 0 , 4 ) . equals ( ” quit ” ) ) { Socket socket = new Socket ( server , servPort ) ; //System . out . p r i n t l n ( ” Connected t o server . . . sending echo s t r i n g ” ) ; InputStream in = socket . getInputStream ( ) ; OutputStream out = socket . getOutputStream ( ) ; //System . out . p r i n t ( ” To Send : ” ) ; i f ( oddTurn == 1 ) { input = ” wait ” ; oddTurn = 0; } else { i f ( firstTurn ) { input = ” a8 a8 ” ; firstTurn = false ; } else { input = System . console ( ) . readLine ( ) ; } oddTurn = 1; } byteBuffer = input . getBytes ( ) ; while ( byteBuffer . length < b u f f e r S i z e ) { input += ” ” ; byteBuffer = input . getBytes ( ) ; } out . write ( byteBuffer ) ; int totalBytesRcvd = 0; int bytesRcvd ; while ( totalBytesRcvd < b u f f e r S i z e ) { i f ( ( bytesRcvd = in . read ( byteBuffer , totalBytesRcvd , b u f f e r S i z e − totalBytesRcvd ) ) == −1 ) { throw new SocketException ( ” Connection closed prematurely ” ) ;

56

Foundations of Videogame Programming Code Repository } totalBytesRcvd += bytesRcvd ; } i f ( oddTurn == 0 ) System . out . p r i n t l n ( new String ( byteBuffer ) ) ; else System . out . p r i n t l n ( ” Waiting on opponent . ” ) ; socket . close ( ) ; } }

}

Server // TCPChessThreadServer . java Copyright ( C ) 2020 Ben Sanders import java . net . ∗ ; import java . i o . ∗ ; import java . u t i l . Vector ; public class TCPChessThreadServer { private static f i n a l int BUFSIZE = 32; public static Vector< Vector< String > > initChess ( ) { Vector< Vector< String > > r e t = new Vector< Vector< String > >() ; for ( int i = 0; i < 8; i ++ ) { Vector< String > tempRow = new Vector< String >() ; for ( int j = 0; j < 8; j ++ ) { tempRow . addElement ( new String ( ” ” ) ) ; } r e t . addElement ( tempRow ) ; } r e t . elementAt ( 0 ) . set ( 0 , r e t . elementAt ( 0 ) . set ( 1 , r e t . elementAt ( 0 ) . set ( 2 , r e t . elementAt ( 0 ) . set ( 3 , r e t . elementAt ( 0 ) . set ( 4 , r e t . elementAt ( 0 ) . set ( 5 , r e t . elementAt ( 0 ) . set ( 6 ,

”wCa” ”wKn” ”wBi” ” wKi ” ”wQu” ”wBi” ”wKn”

); ); ); ); ); ); );

Server

57

r e t . elementAt ( 0 ) . set ( 7 , ”wCa” ) ; for ( int i = 0; i < r e t . elementAt ( 1 ) . s i z e ( ) ; i ++ ) { r e t . elementAt ( 1 ) . set ( i , ”wPa” ) ; } for ( int i = 0; i < r e t . elementAt ( 2 ) . s i z e ( ) ; i ++ ) { r e t . elementAt ( 2 ) . set ( i , blank ) ; r e t . elementAt ( 3 ) . set ( i , blank ) ; r e t . elementAt ( 4 ) . set ( i , blank ) ; r e t . elementAt ( 5 ) . set ( i , blank ) ; } for ( int i = 0; i < r e t . elementAt ( 6 ) . s i z e ( ) ; i ++ ) { r e t . elementAt ( 6 ) . set ( i , ”bPa” ) ; } r e t . elementAt ( 7 ) . set ( 0 , r e t . elementAt ( 7 ) . set ( 1 , r e t . elementAt ( 7 ) . set ( 2 , r e t . elementAt ( 7 ) . set ( 3 , r e t . elementAt ( 7 ) . set ( 4 , r e t . elementAt ( 7 ) . set ( 5 , r e t . elementAt ( 7 ) . set ( 6 , r e t . elementAt ( 7 ) . set ( 7 ,

”bCa” ”bKn” ” bBi ” ” bKi ” ”bQu” ” bBi ” ”bKn” ”bCa”

); ); ); ); ); ); ); );

return r e t ; } public static String formatBoard ( Vector< Vector< String > > inputBoard , int turn ) { String r e t = ” ” ; r e t += ” \ t a\ t b\ t c\ t d\ t e\ t f \ t g\ t h\n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; r e t += ” 8\ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 0 ) + ” \ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard .

58

Foundations of Videogame Programming Code Repository elementAt ( 0 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 0 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; 7\ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 0 ) + ” \ t | ” + r e t += ” inputBoard . elementAt ( 1 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 1 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; 6\ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 0 ) + ” \ t | ” + r e t += ” inputBoard . elementAt ( 2 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 2 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; r e t += ” 5\ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 0 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 3 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; 4\ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 0 ) + ” \ t | ” + r e t += ” inputBoard . elementAt ( 4 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 4 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t

Server

59

+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; 3\ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 0 ) + ” \ t | ” + r e t += ” inputBoard . elementAt ( 5 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 5 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; 2\ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 0 ) + ” \ t | ” + r e t += ” inputBoard . elementAt ( 6 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 6 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n” ; r e t += ” \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ t | \ n” ; r e t += ” 1\ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 0 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 1 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 2 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 3 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 4 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 5 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 6 ) + ” \ t | ” + inputBoard . elementAt ( 7 ) . elementAt ( 7 ) + ” \ t | \ n” ; r e t += ” \ t +−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+−−−−−−−+\ n\n” ; i f ( turn % 2 == 0 ) { r e t += ” white ’ s turn . \n” ; } else { r e t += ” black ’ s turn . \n” ; } byte [ ] tempBuffer = r e t . getBytes ( ) ; /∗

60

Foundations of Videogame Programming Code Repository while ( tempBuffer . length < b u f f e r S i z e ) { r e t += ” ” ; } ∗/ return r e t ; } public static Boolean validCommand ( String input ) { Boolean r e t = f a l s e ; try { i f ( ( input . substring ( 0 , 1) . equals ( ” a ” ) | | input . substring ( 0 , 1) . equals ( ”b” ) | | input . substring ( 0 , 1) . equals ( ” c ” ) | | input . substring ( 0 , 1) . equals ( ”d” ) | | input . substring ( 0 , 1) . equals ( ” e ” ) | | input . substring ( 0 , 1) . equals ( ” f ” ) | | input . substring ( 0 , 1) . equals ( ” g ” ) | | input . substring ( 0 , 1) . equals ( ”h” ) | | input . substring ( 0 , 1) . equals ( ”A” ) | | input . substring ( 0 , 1) . equals ( ”B” ) | | input . substring ( 0 , 1) . equals ( ”C” ) | | input . substring ( 0 , 1) . equals ( ”D” ) | | input . substring ( 0 , 1) . equals ( ”E” ) | | input . substring ( 0 , 1) . equals ( ”F” ) | | input . substring ( 0 , 1) . equals ( ”G” ) | | input . substring ( 0 , 1) . equals ( ”H” ) ) && ( input . substring ( 3 , 4) . equals ( ” a ” ) | | input . substring ( 3 , 4) . equals ( ”b” ) | | input . substring ( 3 , 4) . equals ( ” c ” ) | | input . substring ( 3 , 4) . equals ( ”d” ) | | input . substring ( 3 , 4) . equals ( ” e ” ) | | input . substring ( 3 , 4) . equals ( ” f ” ) | | input . substring ( 3 , 4) . equals ( ” g ” ) | | input . substring ( 3 , 4) . equals ( ”h” ) | | input . substring ( 3 , 4) . equals ( ”A” ) | | input . substring ( 3 , 4) . equals ( ”B” ) | | input . substring ( 3 , 4) . equals ( ”C” ) | | input . substring ( 3 , 4) . equals ( ”D” ) | | input . substring ( 3 , 4) . equals ( ”E” ) | | input . substring ( 3 , 4) . equals ( ”F” ) | | input . substring ( 3 , 4) . equals ( ”G” ) | | input . substring ( 3 , 4) . equals ( ”H” ) ) && ( input . substring ( 1 , 2) . equals ( ”8” ) | |

Server input . substring ( 1 , 2) . equals ( ”7” ) | | input . substring ( 1 , 2) . equals ( ”6” ) | | input . substring ( 1 , 2) . equals ( ”5” ) | | input . substring ( 1 , 2) . equals ( ”4” ) | | input . substring ( 1 , 2) . equals ( ”3” ) | | input . substring ( 1 , 2) . equals ( ”2” ) | | input . substring ( 1 , 2) . equals ( ”1” ) ) && ( input . substring ( 4 , 5) . equals ( ”8” ) | | input . substring ( 4 , 5) . equals ( ”7” ) | | input . substring ( 4 , 5) . equals ( ”6” ) | | input . substring ( 4 , 5) . equals ( ”5” ) | | input . substring ( 4 , 5) . equals ( ”4” ) | | input . substring ( 4 , 5) . equals ( ”3” ) | | input . substring ( 4 , 5) . equals ( ”2” ) | | input . substring ( 4 , 5) . equals ( ”1” ) ) ) { //System . out . p r i n t l n ( ” v a l i d ” ) ; r e t = true ; } else { //System . out . p r i n t l n ( ” example input : \ nb8 c6 ” ) ; ret = false ; } } catch ( Exception e ) { e . printStackTrace ( ) ; } return r e t ; } public { int if ( if ( if ( if ( if ( if ( if ( if ( if ( if ( if (

static int parseIndex ( String input ) r e t = 0; input . equals ( ”A” ) input . equals ( ”B” ) input . equals ( ”C” ) input . equals ( ”D” ) input . equals ( ”E” ) input . equals ( ”F” ) input . equals ( ”G” ) input . equals ( ”H” ) input . equals ( ” a ” ) input . equals ( ”b” ) input . equals ( ” c ” )

) ) ) ) ) ) ) ) ) ) )

ret ret ret ret ret ret ret ret ret ret ret

= = = = = = = = = = =

0; 1; 2; 3; 4; 5; 6; 7; 0; 1; 2;

61

62

Foundations of Videogame Programming Code Repository if if if if if if if if if if if if if

( ( ( ( ( ( ( ( ( ( ( ( (

input . equals ( ”d” ) input . equals ( ” e ” ) input . equals ( ” f ” ) input . equals ( ” g ” ) input . equals ( ”h” ) input . equals ( ”8” ) input . equals ( ”7” ) input . equals ( ”6” ) input . equals ( ”5” ) input . equals ( ”4” ) input . equals ( ”3” ) input . equals ( ”2” ) input . equals ( ”1” )

) ) ) ) ) ) ) ) ) ) ) ) )

ret ret ret ret ret ret ret ret ret ret ret ret ret

= = = = = = = = = = = = =

3; 4; 5; 6; 7; 0; 1; 2; 3; 4; 5; 6; 7;

return r e t ; } public static Vector< Vector< String > > parseCommand ( String cmd, Vector< Vector< String > > board ) { Vector< Vector< String > > r e t = new Vector< Vector< String > >() ; for ( int i = 0; i < board . s i z e ( ) ; i ++ ) { Vector< String > tempRow = new Vector< String >() ; for ( int j = 0; j < board . elementAt ( i ) . s i z e ( ) ; j ++ ) { tempRow . addElement ( board . elementAt ( i ) . elementAt ( j ) ) ; } r e t . addElement ( tempRow ) ; } i f ( validCommand ( cmd ) ) { // set up the new board c o n f i g u r a t i o n int fromx , fromy ; int tox , toy ; fromx fromy tox = toy =

= parseIndex ( cmd. substring ( 0 , 1 ) ) ; = parseIndex ( cmd. substring ( 1 , 2 ) ) ; parseIndex ( cmd. substring ( 3 , 4 ) ) ; parseIndex ( cmd. substring ( 4 , 5 ) ) ;

i f ( ! board . elementAt ( fromy ) . elementAt ( fromx ) . substring ( 0 , 3) . equals ( blank ) && ! ( fromx == tox && fromy == toy ) ) {

Server

63

r e t . elementAt ( toy ) . set ( tox , board . elementAt ( fromy ) . elementAt ( fromx ) ) ; r e t . elementAt ( fromy ) . set ( fromx , blank ) ; } } return r e t ; } private static class Server1 implements Runnable { public void run ( ) { while ( ! ( new String ( byteBuffer1 ) . substring ( 0 , 4 ) . equals ( ” quit ” ) ) ) { chessBoard = parseCommand ( new String ( byteBuffer1 ) , chessBoard ); i f ( validCommand ( new String ( byteBuffer1 ) ) ) { turnCount = turnCount + 1; b u f f e r S i z e = byteBuffer1 . length ; } while ( turnCount % 2 == 1 ) { System . out . p r i n t ( ” ” ) ; System . out . p r i n t ( ” \b” ) ; } sendBoard ( ) ; } } public void sendBoard ( ) { try { Socket clntSock = servSock1 . accept ( ) ; /∗System . out . p r i n t l n ( ” Handling c l i e n t at ” + clntSock . getInetAddress ( ) . getHostAddress ( ) + ” on p o r t ” + clntSock . g e t P o r t ( ) ) ; ∗/ InputStream in = clntSock . getInputStream ( ) ; OutputStream out = clntSock . getOutputStream ( ) ; byteBuffer1 = formatBoard ( chessBoard , turnCount ) . getBytes ( ) ;

64

Foundations of Videogame Programming Code Repository while ( ( recvMsgSize1 = in . read ( byteBuffer1 ) ) ! = −1 ) { out . write ( formatBoard ( chessBoard , turnCount ) . getBytes ( ) , 0 , recvMsgSize1 ) ; } clntSock . close ( ) ; } catch ( Exception e ) { e . printStackTrace ( ) ; } } } private static class Server2 implements Runnable { public void run ( ) { while ( ! ( new String ( byteBuffer2 ) . substring ( 0 , 4 ) . equals ( ” quit ” ) ) ) { chessBoard = parseCommand ( new String ( byteBuffer2 ) , chessBoard ); i f ( validCommand ( new String ( byteBuffer2 ) ) ) { turnCount = turnCount + 1; b u f f e r S i z e = byteBuffer2 . length ; } //sendBoard ( ) ; while ( turnCount % 2 == 0 ) { System . out . p r i n t ( ” ” ) ; System . out . p r i n t ( ” \b” ) ; } sendBoard ( ) ; } } public void sendBoard ( ) { try { Socket clntSock = servSock2 . accept ( ) ; /∗System . out . p r i n t l n ( ” Handling c l i e n t at ” + clntSock . getInetAddress ( ) . getHostAddress ( ) +

Server

65

” on p o r t ” + clntSock . g e t P o r t ( ) ) ; ∗/ InputStream in = clntSock . getInputStream ( ) ; OutputStream out = clntSock . getOutputStream ( ) ; byteBuffer2 = formatBoard ( chessBoard , turnCount ) . getBytes ( ) ; while ( ( recvMsgSize2 = in . read ( byteBuffer2 ) ) ! = −1 ) { //out . write ( formatBoard ( chessBoard , turnCount ) . getBytes ( ) , 0 , boardSize ) ; out . write ( formatBoard ( chessBoard , turnCount ) . getBytes ( ) , 0 , recvMsgSize2 ) ; //out . write ( byteBuffer , 0 , recvMsgSize ) ; } clntSock . close ( ) ; } catch ( Exception e ) { e . printStackTrace ( ) ; } } }

public static void main ( String [ ] args ) throws IOException { i f ( args . length ! = 2 ) { throw new IllegalArgumentException ( ” Parameter ( s ) : ” ) ; } int servPort1 = I n t e g e r . parseInt ( args [ 0 ] ) ; int servPort2 = I n t e g e r . parseInt ( args [ 1 ] ) ; servSock1 = new ServerSocket ( servPort1 ) ; servSock2 = new ServerSocket ( servPort2 ) ; byteBuffer1 = new byte [ BUFSIZE ] ; byteBuffer2 = new byte [ BUFSIZE ] ; blank = ”

”;

chessBoard = initChess ( ) ; turnCount = 0; // S t a r t threads here . . . Thread t1 = new Thread ( new Server1 ( ) ) ;

66

Foundations of Videogame Programming Code Repository Thread t2 = new Thread ( new Server2 ( ) ) ; t1 . s t a r t ( ) ; t2 . s t a r t ( ) ; } private private private private private private private private private private

}

static static static static static static static static static static

ServerSocket servSock1 ; ServerSocket servSock2 ; Vector< Vector< String > > chessBoard ; int turnCount ; byte [ ] byteBuffer1 ; byte [ ] byteBuffer2 ; int recvMsgSize1 ; int recvMsgSize2 ; int b u f f e r S i z e ; String blank ;

Chapter 4 Threaded Network

Client // TCPWriteClient . java Copyright ( C ) 2020 Ben Sanders import java . net . ∗ ; import java . i o . ∗ ; public class TCPWriteClient { public static void main ( String [ ] args ) throws IOException { i f ( args . length ! = 2 ) { throw new IllegalArgumentException ( ” Parameters : ” ) ; } String server = args [ 0 ] ; int port = I n t e g e r . parseInt ( args [ 1 ] ) ; String input = ”00000000” ; byte [ ] byteBuffer ; while ( ! input . substring ( 0 , 4 ) . equals ( ” quit ” ) ) { Socket mySocket = new Socket ( server , port ) ; OutputStream out = mySocket . getOutputStream ( ) ; System . out . p r i n t l n ( ” User Input : ” ) ; input = System . console ( ) . readLine ( ) ; 67

68

Foundations of Videogame Programming Code Repository byteBuffer = input . getBytes ( ) ; out . write ( byteBuffer ) ; mySocket . close ( ) ; } }

}

Server // TCPWriteDisplayServer . java Copyright ( C ) 2020 Ben Sanders import java . net . ∗ ; import java . i o . ∗ ; public class TCPWriteDisplayServer { private static f i n a l int BUFSIZE = 32; private static class WriteServer1 implements Runnable { public void run ( ) { while ( ! ( msg. substring ( 0 , 4 ) . equals ( ” quit ” ) ) ) { try { Socket writeClntSock1 = writeSocket1 . accept ( ) ; InputStream in = writeClntSock1 . getInputStream ( ) ; while ( ( writeMsgSize1 = in . read ( writeBuffer1 ) ) ! = −1 ) { } msg = new String ( writeBuffer1 ) ; System . out . p r i n t l n ( ”msg in Server1 : ” + msg ) ; writeClntSock1 . close ( ) ; } catch ( Exception e ) { e . printStackTrace ( ) ; } } } }

Server

69

private static class WriteServer2 implements Runnable { public void run ( ) { while ( ! ( msg. substring ( 0 , 4 ) . equals ( ” quit ” ) ) ) { try { Socket writeClntSock2 = writeSocket2 . accept ( ) ; InputStream in = writeClntSock2 . getInputStream ( ) ; while ( ( writeMsgSize2 = in . read ( writeBuffer2 ) ) ! = −1 ) { } msg = new String ( writeBuffer2 ) ; System . out . p r i n t l n ( ”msg in Server2 : ” + msg ) ; writeClntSock2 . close ( ) ; } catch ( Exception e ) { e . printStackTrace ( ) ; } } } } public static class DisplayServer1 implements Runnable { public void run ( ) { try { while ( ! ( msg. substring ( 0 , 4 ) . equals ( ” quit ” ) ) ) { Socket displayClntSock1 = displaySocket1 . accept ( ) ; InputStream in = displayClntSock1 . getInputStream ( ) ; OutputStream out = displayClntSock1 . getOutputStream ( ) ; displayBuffer1 = msg. getBytes ( ) ; while ( ( displayMsgSize1 = in . read ( displayBuffer1 ) ) ! = −1 ) { out . write ( msg. getBytes ( ) , 0 , displayMsgSize1 ) ; } displayClntSock1 . close ( ) ;

70

Foundations of Videogame Programming Code Repository } } catch ( Exception e ) { e . printStackTrace ( ) ; } } } public static class DisplayServer2 implements Runnable { public void run ( ) { try { while ( ! ( msg. substring ( 0 , 4 ) . equals ( ” quit ” ) ) ) { Socket displayClntSock2 = displaySocket2 . accept ( ) ; InputStream in = displayClntSock2 . getInputStream ( ) ; OutputStream out = displayClntSock2 . getOutputStream ( ) ; displayBuffer2 = msg. getBytes ( ) ; while ( ( displayMsgSize2 = in . read ( displayBuffer2 ) ) ! = −1 ) { out . write ( msg. getBytes ( ) , 0 , displayMsgSize2 ) ; } displayClntSock2 . close ( ) ; } } catch ( Exception e ) { e . printStackTrace ( ) ; } } } public static void main ( String [ ] args ) throws IOException { i f ( args . length ! = 4 ) { throw new IllegalArgumentException ( ” Parameters : < DisplayPort> ” ) ; } int writePort1 = I n t e g e r . parseInt ( args [ 0 ] ) ; int displayPort1 = I n t e g e r . parseInt ( args [ 1 ] ) ; int writePort2 = I n t e g e r . parseInt ( args [ 2 ] ) ;

Server int displayPort2 = I n t e g e r . parseInt ( args [ 3 ] ) ; writeSocket1 = displaySocket1 writeSocket2 = displaySocket2

new ServerSocket ( writePort1 ) ; = new ServerSocket ( displayPort1 ) ; new ServerSocket ( writePort2 ) ; = new ServerSocket ( displayPort2 ) ;

writeBuffer1 = displayBuffer1 writeBuffer2 = displayBuffer2

new byte [ BUFSIZE ] ; = new byte [ BUFSIZE ] ; new byte [ BUFSIZE ] ; = new byte [ BUFSIZE ] ;

msg = ”00000000” ; Thread Thread Thread Thread

t1 t2 t3 t4

t1 . s t a r t t2 . s t a r t t3 . s t a r t t4 . s t a r t

() () () ()

= = = =

new new new new

Thread ( Thread ( Thread ( Thread (

new new new new

WriteServer1 ( ) ) ; DisplayServer1 ( ) ) ; WriteServer2 ( ) ) ; DisplayServer2 ( ) ) ;

; ; ; ;

} private private private private

static static static static

ServerSocket ServerSocket ServerSocket ServerSocket

private private private private

static static static static

byte [ ] byte [ ] byte [ ] byte [ ]

private private private private

static static static static

int int int int

writeBuffer1 ; displayBuffer1 ; writeBuffer2 ; displayBuffer2 ;

writeMsgSize1 ; displayMsgSize1 ; writeMsgSize2 ; displayMsgSize2 ;

private static String msg; }

writeSocket1 ; displaySocket1 ; writeSocket2 ; displaySocket2 ;

71

72

Foundations of Videogame Programming Code Repository

Part II 2D Videogames

73

Chapter 5 Asteroids

// Asteroids . java Copyright ( C ) 2019 Ben Sanders // TODO make l e v e l s that flow , one t o the next . have a score and l i v e s ! import java . u t i l . Vector ; import java . u t i l .Random; import java . time . LocalTime ; import java . time . temporal . ChronoUnit ; import import import import import import import

javax . swing . JFrame ; javax . swing . JPanel ; javax . swing . JButton ; javax . swing . JComponent ; javax . swing . KeyStroke ; javax . swing . AbstractAction ; javax . swing .JComboBox;

import javax . imageio . ImageIO ; import java . awt . event . ActionEvent ; import java . awt . event . ActionListener ; import java . awt . image . BufferedImage ; import java . i o . F i l e ; import java . i o . IOException ; import java . awt . event . ActionEvent ; import java . awt . event . ActionListener ; import java . awt . Graphics ; import java . awt . Graphics2D ;

75

76

Foundations of Videogame Programming Code Repository

import java . awt . geom . AffineTransform ; import java . awt . image . AffineTransformOp ; public class Asteroids { public Asteroids ( ) { setup ( ) ; } public static void setup ( ) { appFrame = new JFrame ( ” Asteroids ” ) ; XOFFSET = 0; YOFFSET = 40; WINWIDTH = 500; WINHEIGHT = 500; pi = 3.14159265358979; twoPi = 2.0 ∗ 3.14159265358979; endgame = f a l s e ; p1width = 25; //18.5; p1height = 25; //25; p1originalX = ( double )XOFFSET + ( ( double )WINWIDTH / 2 . 0 ) − ( p1width / 2.0) ; p1originalY = ( double )YOFFSET + ( ( double )WINHEIGHT / 2 . 0 ) − ( p1height / 2.0) ; playerBullets = new Vector< ImageObject >() ; playerBulletsTimes = new Vector< Long >() ; bulletWidth = 5; p l a y e r b u l l e t l i f e t i m e = new Long(1600) ; //0.75; enemybulletlifetime = new Long(1600) ; e x p l o s i o n l i f e t i m e = new Long(800) ; playerbulletgap = 1; flamecount = 1; flamewidth = 12.0; expcount = 1; l e v e l = 3; asteroids = new Vector< ImageObject >() ; asteroidsTypes = new Vector< I n t e g e r >() ; ast1width = 32; ast2width = 21; ast3width = 26; try { background = ImageIO . read ( new F i l e ( ” space . png” ) ) ; player = ImageIO . read ( new F i l e ( ” player . png” ) ) ; flame1 = ImageIO . read ( new F i l e ( ” f l a m e l e f t . png” ) ) ;

Asteroids flame2 = ImageIO . read ( new F i l e ( ” flamecenter . png” ) ) ; flame3 = ImageIO . read ( new F i l e ( ” flameright . png” ) ) ; flame4 = ImageIO . read ( new F i l e ( ” b l u e f l a m e l e f t . png” ) ) ; flame5 = ImageIO . read ( new F i l e ( ” blueflamecenter . png” ) ) ; flame6 = ImageIO . read ( new F i l e ( ” blueflameright . png” ) ) ; ast1 = ImageIO . read ( new F i l e ( ” ast1 . png” ) ) ; ast2 = ImageIO . read ( new F i l e ( ” ast2 . png” ) ) ; ast3 = ImageIO . read ( new F i l e ( ” ast3 . png” ) ) ; playerBullet = ImageIO . read ( new F i l e ( ” p l a y e r b u l l e t . png” ) ) ; enemyShip = ImageIO . read ( new F i l e ( ”enemy . png” ) ) ; enemyBullet = ImageIO . read ( new F i l e ( ” enemybullet . png” ) ) ; exp1 = ImageIO . read ( new F i l e ( ” explosion1 . png” ) ) ; exp2 = ImageIO . read ( new F i l e ( ” explosion2 . png” ) ) ; } catch ( IOException i o e ) { } } private static class Animate implements Runnable { public void run ( ) { while ( endgame == f a l s e ) { backgroundDraw ( ) ; asteroidsDraw ( ) ; explosionsDraw ( ) ; enemyBulletsDraw ( ) ; enemyDraw ( ) ; playerBulletsDraw ( ) ; playerDraw ( ) ; flameDraw ( ) ; try { Thread . sleep ( 3 2 ) ; } catch ( InterruptedException e ) { } } } }

77

78

Foundations of Videogame Programming Code Repository private static void i n s e r t P l a y e r B u l l e t ( ) { ImageObject b u l l e t = new ImageObject ( 0 , 0 , bulletWidth , bulletWidth , p1 . getAngle ( ) ) ; lockrotateObjAroundObjtop ( b u l l e t , p1 , p1width / 2.0 ) ; playerBullets . addElement ( b u l l e t ) ; playerBulletsTimes . addElement ( System . currentTimeMillis ( ) ) ; } private static void insertEnemyBullet ( ) { try { // randomize angle here Random randomNumbers = new Random( LocalTime .now ( ) . getNano ( ) ) ; ImageObject b u l l e t = new ImageObject ( enemy . getX ( ) + enemy . getWidth ( ) /2.0 , enemy . getY ( ) + enemy . getHeight ( ) /2.0 , bulletWidth , bulletWidth , randomNumbers . nextInt (360) ) ; //lockrotateObjAroundObjbottom ( b u l l e t , enemy, enemy . getWidth ( ) / 2.0 ) ; enemyBullets . addElement ( b u l l e t ) ; enemyBulletsTimes . addElement ( System . currentTimeMillis ( ) ) ; } catch ( java . lang . NullPointerException j l n p e ) { } } private static class PlayerMover implements Runnable { public PlayerMover ( ) { v e l o c i t y s t e p = 0.01; r o t a t e s t e p = 0.01; } public void run ( ) { while ( endgame == f a l s e ) { try { Thread . sleep ( 10 ) ; } catch ( InterruptedException e ) {

Asteroids

79

} i f ( upPressed == true ) { p1velocity = p1velocity + velocitystep ; } i f ( downPressed == true ) { p1velocity = p1velocity − velocitystep ; } i f ( l e f t P r e s s e d == true ) { i f ( p1velocity < 0 ) { p1 . r o t a t e ( −r o t a t e s t e p ) ; } else { p1 . r o t a t e ( r o t a t e s t e p ) ; } } i f ( rightPressed == true ) { i f ( p1velocity < 0 ) { p1 . r o t a t e ( r o t a t e s t e p ) ; } else { p1 . r o t a t e ( −r o t a t e s t e p ) ; } } i f ( f i r e P r e s s e d == true ) { try { i f ( playerBullets . s i z e ( ) == 0 ) { insertPlayerBullet ( ) ; } else i f ( System . currentTimeMillis ( ) − playerBulletsTimes . elementAt ( playerBulletsTimes . s i z e ( ) − 1 ) > p l a y e r b u l l e t l i f e t i m e / 4.0 ) { insertPlayerBullet ( ) ; }

80

Foundations of Videogame Programming Code Repository } catch ( java . lang . ArrayIndexOutOfBoundsException aioobe ) { } } p1 . move ( −p 1 v e l o c i t y ∗ Math . cos ( p1 . getAngle ( ) − pi / 2.0 ) , p 1 v e l o c i t y ∗ Math . sin ( p1 . getAngle ( ) − pi / 2.0 ) ) ; p1 . screenWrap ( XOFFSET, XOFFSET + WINWIDTH, YOFFSET, YOFFSET + WINHEIGHT ) ; } } private double v e l o c i t y s t e p ; private double r o t a t e s t e p ; } private static class FlameMover implements Runnable { public FlameMover ( ) { gap = 7 . 0 ; } public void run ( ) { while ( endgame == f a l s e ) { lockrotateObjAroundObjbottom ( flames , p1 , gap ) ; } } private double gap ; } private static class AsteroidsMover implements Runnable { public AsteroidsMover ( ) { velocity = 0.1; spinstep = 0.01; s p i n d i r e c t i o n = new Vector ( ) ; } public void run ( ) { Random randomNumbers = new Random( LocalTime .now ( ) . getNano ( ) ) ; for ( int i = 0; i < asteroids . s i z e ( ) ; i ++ ) {

Asteroids s p i n d i r e c t i o n . addElement ( randomNumbers . nextInt ( 2 ) ) ; } while ( endgame == f a l s e ) { try { Thread . sleep ( 1 ) ; } catch ( InterruptedException e ) { } try { for ( int i = 0; i < asteroids . s i z e ( ) ; i ++ ) { i f ( s p i n d i r e c t i o n . elementAt ( i ) < 1 ) { asteroids . elementAt ( i ) . spin ( −spinstep ) ; } else { asteroids . elementAt ( i ) . spin ( spinstep ) ; } asteroids . elementAt ( i ) . move ( −v e l o c i t y ∗ Math . cos ( asteroids . elementAt ( i ) . getAngle ( ) − pi / 2.0 ) , v e l o c i t y ∗ Math . sin ( asteroids . elementAt ( i ) . getAngle ( ) − pi / 2.0 ) ) ; asteroids . elementAt ( i ) . screenWrap ( XOFFSET, XOFFSET + WINWIDTH, YOFFSET, YOFFSET + WINHEIGHT ) ; } } catch ( java . lang . ArrayIndexOutOfBoundsException j l a i o o b e ) { } } } private double v e l o c i t y ; private double spinstep ; private Vector< I n t e g e r > s p i n d i r e c t i o n ; } private static class PlayerBulletsMover implements Runnable

81

82

Foundations of Videogame Programming Code Repository { public PlayerBulletsMover ( ) { velocity = 1.0; } public void run ( ) { while ( endgame == f a l s e ) { try { // c o n t r o l s b u l l e t speed Thread . sleep ( 4 ) ; } catch ( InterruptedException e ) { } try { for ( int i = 0; i < playerBullets . s i z e ( ) ; i ++ ) { playerBullets . elementAt ( i ) . move ( −v e l o c i t y ∗ Math . cos ( playerBullets . elementAt ( i ) . getAngle ( ) − pi / 2.0 ) , v e l o c i t y ∗ Math . sin ( playerBullets . elementAt ( i ) . getAngle ( ) − pi / 2.0 ) ) ; playerBullets . elementAt ( i ) . screenWrap ( XOFFSET, XOFFSET + WINWIDTH, YOFFSET, YOFFSET + WINHEIGHT ) ; i f ( System . currentTimeMillis ( ) − playerBulletsTimes . elementAt ( i ) > p l a y e r b u l l e t l i f e t i m e ) { playerBullets . remove ( i ) ; playerBulletsTimes . remove ( i ) ; } } } catch ( java . lang . ArrayIndexOutOfBoundsException a i e ) { playerBullets . c l e a r ( ) ; playerBulletsTimes . c l e a r ( ) ; } } } private double v e l o c i t y ; }

Asteroids

83

private static class EnemyShipMover implements Runnable { public EnemyShipMover ( ) { velocity = 1.0; } public void run ( ) { while ( endgame == f a l s e && enemyAlive == true ) { try { Thread . sleep ( 10 ) ; } catch ( InterruptedException e ) { } try { enemy . move ( −v e l o c i t y ∗ Math . cos ( enemy . getAngle ( ) − pi / 2.0 ) , v e l o c i t y ∗ Math . sin ( enemy . getAngle ( ) − pi / 2.0 ) ); enemy . screenWrap ( XOFFSET, XOFFSET + WINWIDTH, YOFFSET, YOFFSET + WINHEIGHT ) ; } catch ( java . lang . NullPointerException j l n p e ) { } try { i f ( enemyAlive == true ) { i f ( enemyBullets . s i z e ( ) == 0 ) { insertEnemyBullet ( ) ; } else i f ( System . currentTimeMillis ( ) − enemyBulletsTimes . elementAt ( enemyBulletsTimes . s i z e ( ) − 1 ) > enemybulletlifetime / 4.0 ) { insertEnemyBullet ( ) ; }

84

Foundations of Videogame Programming Code Repository } } catch ( java . lang . ArrayIndexOutOfBoundsException aioobe ) { } } } private double v e l o c i t y ; } private static class EnemyBulletsMover implements Runnable { public EnemyBulletsMover ( ) { velocity = 1.2; } public void run ( ) { while ( endgame == f a l s e && enemyAlive == true ) { try { // c o n t r o l s b u l l e t speed Thread . sleep ( 4 ) ; } catch ( InterruptedException e ) { } try { for ( int i = 0; i < enemyBullets . s i z e ( ) ; i ++ ) { enemyBullets . elementAt ( i ) . move ( −v e l o c i t y ∗ Math . cos ( enemyBullets . elementAt ( i ) . getAngle ( ) − pi / 2.0 ) , v e l o c i t y ∗ Math . sin ( enemyBullets . elementAt ( i ) . getAngle ( ) − pi / 2.0 ) ) ; enemyBullets . elementAt ( i ) . screenWrap ( XOFFSET, XOFFSET + WINWIDTH, YOFFSET, YOFFSET + WINHEIGHT ) ; i f ( System . currentTimeMillis ( ) − enemyBulletsTimes . elementAt ( i ) > enemybulletlifetime ) { enemyBullets . remove ( i ) ;

Asteroids

85

enemyBulletsTimes . remove ( i ) ; } } } catch ( java . lang . ArrayIndexOutOfBoundsException a i e ) { enemyBullets . c l e a r ( ) ; enemyBulletsTimes . c l e a r ( ) ; } } } private double v e l o c i t y ; } private static class CollisionChecker implements Runnable { public void run ( ) { Random randomNumbers = new Random( LocalTime .now ( ) . getNano ( ) ) ; while ( endgame == f a l s e ) { try { // TODO compare a l l asteroids t o a l l player b u l l e t s for ( int i = 0; i < asteroids . s i z e ( ) ; i ++ ) { for ( int j = 0; j < playerBullets . s i z e ( ) ; j ++ ) { i f ( collisionOccurs ( asteroids . elementAt ( i ) , playerBullets . elementAt ( j ) ) == true ) { // d e l e t e asteroid , show explosion animation , replace old a s t e r o i d with two new, smaller asteroids at same place , random d i r e c t i o n s . double posX = asteroids . elementAt ( i ) . getX ( ) ; double posY = asteroids . elementAt ( i ) . getY ( ) ; // create explosion ! explosions . addElement ( new ImageObject ( posX , posY , 27, 24, 0.0 ) ) ; explosionsTimes . addElement ( System . currentTimeMillis ( ) ) ; // create two new asteroids o f type 2 i f ( asteroidsTypes . elementAt ( i ) == 1 )

86

Foundations of Videogame Programming Code Repository { asteroids . addElement ( new ImageObject ( posX , posY , ast2width , ast2width , ( double ) ( randomNumbers . nextInt (360) ) ) ) ; asteroidsTypes . addElement ( 2 ) ; asteroids . remove ( i ) ; asteroidsTypes . remove ( i ) ; playerBullets . remove ( j ) ; playerBulletsTimes . remove ( j ) ; } // create two new asteroids o f type 3 i f ( asteroidsTypes . elementAt ( i ) == 2 ) { asteroids . addElement ( new ImageObject ( posX , posY , ast3width , ast3width , ( double ) ( randomNumbers . nextInt (360) ) ) ) ; asteroidsTypes . addElement ( 3 ) ; asteroids . remove ( i ) ; asteroidsTypes . remove ( i ) ; playerBullets . remove ( j ) ; playerBulletsTimes . remove ( j ) ; } // d e l e t e asteroids . i f ( asteroidsTypes . elementAt ( i ) == 3 ) { asteroids . remove ( i ) ; asteroidsTypes . remove ( i ) ; playerBullets . remove ( j ) ; playerBulletsTimes . remove ( j ) ; } } } } // compare a l l asteroids t o player for ( int i = 0; i < asteroids . s i z e ( ) ; i ++ ) { i f ( collisionOccurs ( asteroids . elementAt ( i ) , p1 ) == true ) { endgame = true ; System . out . p r i n t l n ( ”Game Over . You Lose ! ” ) ; } }

Asteroids

87

try { // compare a l l player b u l l e t s t o enemy ship for ( int i = 0; i < playerBullets . s i z e ( ) ; i ++ ) { i f ( collisionOccurs ( playerBullets . elementAt ( i ) , enemy ) == true ) { double posX = enemy . getX ( ) ; double posY = enemy . getY ( ) ; // create explosion ! explosions . addElement ( new ImageObject ( posX , posY , 27, 24, 0.0 ) ) ; explosionsTimes . addElement ( System . currentTimeMillis ( ) ) ; playerBullets . remove ( i ) ; playerBulletsTimes . remove ( i ) ; enemyAlive = f a l s e ; enemy = null ; enemyBullets . c l e a r ( ) ; enemyBulletsTimes . c l e a r ( ) ; } } // compare enemy ship t o player i f ( collisionOccurs ( enemy, p1 ) == true ) { endgame = true ; System . out . p r i n t l n ( ”Game Over . You Lose ! ” ) ; } // TODO compare a l l enemy b u l l e t s t o player for ( int i = 0; i < enemyBullets . s i z e ( ) ; i ++ ) { i f ( collisionOccurs ( enemyBullets . elementAt ( i ) , p1 ) == true ) { endgame = true ; System . out . p r i n t l n ( ”Game Over . You Lose ! ” ) ; } } } catch ( java . lang . NullPointerException j l n p e ) {

88

Foundations of Videogame Programming Code Repository } } catch ( java . lang . ArrayIndexOutOfBoundsException j l a i o o b e ) { } } } } private static class WinChecker implements Runnable { public void run ( ) { while ( endgame == f a l s e ) { i f ( asteroids . s i z e ( ) == 0 ) { endgame = true ; System . out . p r i n t l n ( ”Game Over . You Win ! ” ) ; } } } } private static void generateAsteroids ( ) { asteroids = new Vector< ImageObject >() ; asteroidsTypes = new Vector< I n t e g e r >() ; Random randomNumbers = new Random( LocalTime .now ( ) . getNano ( ) ) ; for ( int i = 0; i < l e v e l ; i ++ ) { asteroids . addElement ( new ImageObject ( XOFFSET + ( double ) ( randomNumbers . nextInt (WINWIDTH) ) , YOFFSET + ( double ) ( randomNumbers . nextInt (WINHEIGHT) ) , ast1width , ast1width , ( double ) ( randomNumbers . nextInt (360) ) ) ) ; asteroidsTypes . addElement ( 1 ) ; } } private static void generateEnemy ( ) { try { Random randomNumbers = new Random( LocalTime .now ( ) . getNano ( ) ) ;

Asteroids

89

enemy = new ImageObject ( XOFFSET + ( double ) ( randomNumbers . nextInt ( WINWIDTH) ) , YOFFSET + ( double ) ( randomNumbers . nextInt (WINHEIGHT) ) , 29.0 , 16.0 , ( double ) ( randomNumbers . nextInt (360) ) ) ; } catch ( java . lang . IllegalArgumentException j l i a e ) { } } // TODO make one l o c k r o t a t e f u n c t i o n which takes as input objInner , objOuter , and p o i n t r e l a t i v e t o objInner ’ s x , y that objOuter must r o t a t e around . // d i s t i s a distance between the two o b j e c t s at the bottom o f objInner . private static void lockrotateObjAroundObjbottom ( ImageObject objOuter , ImageObject objInner , double d i s t ) { objOuter . moveto ( objInner . getX ( ) + ( d i s t + objInner . getWidth ( ) / 2 . 0 ) ∗ Math . cos(− objInner . getAngle ( ) + pi /2.0) + objOuter . getWidth ( ) / 2.0 , objInner . getY ( ) + ( d i s t + objInner . getHeight ( ) / 2 . 0 ) ∗ Math . sin (− objInner . getAngle ( ) + pi /2.0) + objOuter . getHeight ( ) / 2.0) ; objOuter . setAngle ( objInner . getAngle ( ) ) ; } // d i s t i s a distance between the two o b j e c t s at the top o f the inner object . private static void lockrotateObjAroundObjtop ( ImageObject objOuter , ImageObject objInner , double d i s t ) { objOuter . moveto ( objInner . getX ( ) + objOuter . getWidth ( ) + ( objInner . getWidth ( ) / 2.0 + ( d i s t + objInner . getWidth ( ) / 2.0 ) ∗ Math . cos ( objInner . getAngle ( ) + pi /2.0 ) ) / 2.0 , objInner . getY ( ) − objOuter . getHeight ( ) + ( d i s t + objInner . getHeight ( ) / 2.0 ) ∗ Math . sin ( objInner . getAngle ( ) / 2.0 ) ) ; objOuter . setAngle ( objInner . getAngle ( ) ) ; } private static AffineTransformOp rotateImageObject ( ImageObject obj ) { AffineTransform at = AffineTransform . getRotateInstance ( −obj . getAngle ( ) , obj . getWidth ( ) /2.0 , obj . getHeight ( ) /2.0 ) ; AffineTransformOp atop = new AffineTransformOp ( at , AffineTransformOp . TYPE BILINEAR ) ;

90

Foundations of Videogame Programming Code Repository return atop ; } private static AffineTransformOp spinImageObject ( ImageObject obj ) { AffineTransform at = AffineTransform . getRotateInstance ( −obj . getInternalAngle ( ) , obj . getWidth ( ) /2.0 , obj . getHeight ( ) /2.0 ) ; AffineTransformOp atop = new AffineTransformOp ( at , AffineTransformOp . TYPE BILINEAR ) ; return atop ; } private static void backgroundDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; g2D . drawImage ( background , XOFFSET, YOFFSET, null ) ; } private static void enemyBulletsDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; for ( int i = 0; i < enemyBullets . s i z e ( ) ; i ++ ) { g2D . drawImage ( enemyBullet , ( int ) ( enemyBullets . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( enemyBullets . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } } private static void enemyDraw ( ) { i f ( enemyAlive == true ) { try { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; g2D . drawImage ( enemyShip , ( int ) ( enemy . getX ( ) + 0 . 5 ) , ( int ) ( enemy . getY ( ) + 0 . 5 ) , null ) ; } catch ( java . lang . NullPointerException j l n p e ) { }

Asteroids

91

} } private static void playerBulletsDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; try { for ( int i = 0; i < playerBullets . s i z e ( ) ; i ++ ) { g2D . drawImage ( rotateImageObject ( playerBullets . elementAt ( i ) ) . f i l t e r ( playerBullet , null ) , ( int ) ( playerBullets . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( playerBullets . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } } catch ( java . lang . ArrayIndexOutOfBoundsException aioobe ) { playerBullets . c l e a r ( ) ; playerBulletsTimes . c l e a r ( ) ; } } private static void playerDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( player , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) ( p1 . getY ( ) + 0 . 5 ) , null ) ; } private static void flameDraw ( ) { i f ( upPressed == true ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; i f ( flamecount == 1 ) { g2D . drawImage ( rotateImageObject ( flames ) . f i l t e r ( flame1 , null ) , ( int ) ( flames . getX ( ) + 0 . 5 ) , ( int ) ( flames . getY ( ) + 0 . 5 ) , null ) ; flamecount = 1 + ( ( flamecount + 1) % 3) ; } else i f ( flamecount == 2 ) {

92

Foundations of Videogame Programming Code Repository g2D . drawImage ( rotateImageObject ( flames ) . f i l t e r ( flame2 , null ) , ( int ) ( flames . getX ( ) + 0 . 5 ) , ( int ) ( flames . getY ( ) + 0 . 5 ) , null ) ; flamecount = 1 + ( ( flamecount + 1) % 3) ; } else i f ( flamecount == 3 ) { g2D . drawImage ( rotateImageObject ( flames ) . f i l t e r ( flame3 , null ) , ( int ) ( flames . getX ( ) + 0 . 5 ) , ( int ) ( flames . getY ( ) + 0 . 5 ) , null ) ; flamecount = 1 + ( ( flamecount + 1) % 3) ; } } i f ( downPressed == true ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; i f ( flamecount == 1 ) { g2D . drawImage ( rotateImageObject ( flames ) , ( int ) ( flames . getX ( ) + 0 . 5 ) , ( int ) null ) ; flamecount = 1 + ( ( flamecount + 1) % 3) ; } else i f ( flamecount == 2 ) { g2D . drawImage ( rotateImageObject ( flames ) , ( int ) ( flames . getX ( ) + 0 . 5 ) , ( int ) null ) ; flamecount = 1 + ( ( flamecount + 1) % 3) ; } else i f ( flamecount == 3 ) { g2D . drawImage ( rotateImageObject ( flames ) , ( int ) ( flames . getX ( ) + 0 . 5 ) , ( int ) null ) ; flamecount = 1 + ( ( flamecount + 1) % 3) ; } } } private static void asteroidsDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; for ( int i = 0; i < asteroids . s i z e ( ) ; i ++ ) {

) . f i l t e r ( flame4 , null ( flames . getY ( ) + 0 . 5 ) ,

) . f i l t e r ( flame5 , null ( flames . getY ( ) + 0 . 5 ) ,

) . f i l t e r ( flame6 , null ( flames . getY ( ) + 0 . 5 ) ,

Asteroids i f ( asteroidsTypes . elementAt ( i ) == 1 ) { g2D . drawImage ( spinImageObject ( asteroids . elementAt ( i ) ) . ( ast1 , null ) , ( int ) ( asteroids . elementAt ( i ) . getX ( ) + ( int ) ( asteroids . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } i f ( asteroidsTypes . elementAt ( i ) == 2 ) { g2D . drawImage ( spinImageObject ( asteroids . elementAt ( i ) ) . ( ast2 , null ) , ( int ) ( asteroids . elementAt ( i ) . getX ( ) + ( int ) ( asteroids . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } i f ( asteroidsTypes . elementAt ( i ) == 3 ) { g2D . drawImage ( spinImageObject ( asteroids . elementAt ( i ) ) . ( ast3 , null ) , ( int ) ( asteroids . elementAt ( i ) . getX ( ) + ( int ) ( asteroids . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; }

93

filter 0.5) ,

filter 0.5) ,

filter 0.5) ,

} } private static void explosionsDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; for ( int i = 0; i < explosions . s i z e ( ) ; i ++ ) { i f ( System . currentTimeMillis ( ) − explosionsTimes . elementAt ( i ) > explosionlifetime ) { try { explosions . remove ( i ) ; explosionsTimes . remove ( i ) ; } catch ( java . lang . NullPointerException j l n p e ) { explosions . c l e a r ( ) ; explosionsTimes . c l e a r ( ) ; } } else { i f ( expcount == 1 ) { g2D . drawImage ( exp1 , ( int ) ( explosions . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( explosions . elementAt ( i ) . getY ( ) + 0 . 5 ) , null

94

Foundations of Videogame Programming Code Repository ); expcount = 2; } else i f ( expcount == 2 ) { g2D . drawImage ( exp2 , ( int ) ( explosions . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( explosions . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ); expcount = 1; } } } } private static class KeyPressed extends AbstractAction { public KeyPressed ( ) { action = ” ” ; } public KeyPressed ( String input ) { action = input ; } public void actionPerformed ( ActionEvent e ) { i f ( action . equals ( ”UP” ) ) { upPressed = true ; } i f ( action . equals ( ”DOWN” ) ) { downPressed = true ; } i f ( action . equals ( ”LEFT” ) ) { l e f t P r e s s e d = true ; } i f ( action . equals ( ”RIGHT” ) ) { rightPressed = true ; } i f ( action . equals ( ”F” ) ) { f i r e P r e s s e d = true ; }

Asteroids } private String action ; } private static class KeyReleased extends AbstractAction { public KeyReleased ( ) { action = ” ” ; } public KeyReleased ( String input ) { action = input ; } public void actionPerformed ( ActionEvent e ) { i f ( action . equals ( ”UP” ) ) { upPressed = f a l s e ; } i f ( action . equals ( ”DOWN” ) ) { downPressed = f a l s e ; } i f ( action . equals ( ”LEFT” ) ) { leftPressed = false ; } i f ( action . equals ( ”RIGHT” ) ) { rightPressed = f a l s e ; } i f ( action . equals ( ”F” ) ) { firePressed = false ; } } private String action ; } private static class QuitGame implements ActionListener { public void actionPerformed ( ActionEvent e )

95

96

Foundations of Videogame Programming Code Repository { endgame = true ; } } private static class StartGame implements ActionListener { public void actionPerformed ( ActionEvent e ) { endgame = true ; enemyAlive = true ; upPressed = f a l s e ; downPressed = f a l s e ; leftPressed = false ; rightPressed = f a l s e ; firePressed = false ; p1 = new ImageObject ( p1originalX , p1originalY , p1width , p1height , 0.0 ) ; p1velocity = 0.0; generateEnemy ( ) ; flames = new ImageObject ( p1originalX + p1width / 2.0 , p1originalY + p1height , flamewidth , flamewidth , 0.0 ) ; flamecount = 1; expcount = 1; try { Thread . sleep ( 5 0 ) ; } catch ( InterruptedException i e ) { } playerBullets = new Vector ( ) ; playerBulletsTimes = new Vector ( ) ; enemyBullets = new Vector ( ) ; enemyBulletsTimes = new Vector ( ) ; explosions = new Vector ( ) ; explosionsTimes = new Vector ( ) ; generateAsteroids ( ) ; endgame = f a l s e ; Thread t1 = new Thread ( new Animate ( ) ) ; Thread t2 = new Thread ( new PlayerMover ( ) ) ; Thread t3 = new Thread ( new FlameMover ( ) ) ; Thread t4 = new Thread ( new AsteroidsMover ( ) ) ; Thread t5 = new Thread ( new PlayerBulletsMover ( ) ) ; Thread t6 = new Thread ( new EnemyShipMover ( ) ) ; Thread t7 = new Thread ( new EnemyBulletsMover ( ) ) ;

Asteroids Thread t8 = new Thread ( new CollisionChecker ( ) ) ; Thread t9 = new Thread ( new WinChecker ( ) ) ; t1 . s t a r t ( ) ; t2 . s t a r t ( ) ; t3 . s t a r t ( ) ; t4 . s t a r t ( ) ; t5 . s t a r t ( ) ; t6 . s t a r t ( ) ; t7 . s t a r t ( ) ; t8 . s t a r t ( ) ; t9 . s t a r t ( ) ; } } private static class GameLevel implements ActionListener { public int decodeLevel ( String input ) { int r e t = 3; i f ( input . equals ( ”One” ) ) { r e t = 1; } else i f ( input . equals ( ”Two” ) ) { r e t = 2; } else i f ( input . equals ( ” Three ” ) ) { r e t = 3; } else i f ( input . equals ( ” Four ” ) ) { r e t = 4; } else i f ( input . equals ( ” Five ” ) ) { r e t = 5; } else i f ( input . equals ( ” Six ” ) ) { r e t = 6; } else i f ( input . equals ( ” Seven ” ) ) { r e t = 7; }

97

98

Foundations of Videogame Programming Code Repository else i f ( { ret = } else i f ( { ret = } else i f ( { ret = }

input . equals ( ” Eight ” ) ) 8; input . equals ( ” Nine ” ) ) 9; input . equals ( ” Ten ” ) ) 10;

return r e t ; } public void actionPerformed ( ActionEvent e ) { JComboBox cb = ( JComboBox ) e . getSource ( ) ; String t e x t L e v e l = ( String ) cb . getSelectedItem ( ) ; l e v e l = decodeLevel ( t e x t L e v e l ) ; } } private static Boolean i s I n s i d e ( double p1x , double p1y , double p2x1 , double p2y1 , double p2x2 , double p2y2 ) { Boolean r e t = f a l s e ; i f ( p1x > p2x1 && p1x < p2x2 ) { i f ( p1y > p2y1 && p1y < p2y2 ) { r e t = true ; } i f ( p1y > p2y2 && p1y < p2y1 ) { r e t = true ; } } i f ( p1x > p2x2 && p1x < p2x1 ) { i f ( p1y > p2y1 && p1y < p2y2 ) { r e t = true ; } i f ( p1y > p2y2 && p1y < p2y1 ) { r e t = true ;

Asteroids } } return r e t ; }

private static Boolean collisionOccursCoordinates ( double p1x1 , double p1y1 , double p1x2 , double p1y2 , double p2x1 , double p2y1 , double p2x2 , double p2y2 ) { Boolean r e t = f a l s e ; i f ( i s I n s i d e ( p1x1 , p1y1 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p1x1 , p1y2 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p1x2 , p1y1 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p1x2 , p1y2 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x1 , p2y1 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x1 , p2y2 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x2 , p2y1 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x2 , p2y2 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } return r e t ; }

99

100

Foundations of Videogame Programming Code Repository

private static Boolean collisionOccurs ( ImageObject obj1 , ImageObject obj2 ) { Boolean r e t = f a l s e ; i f ( collisionOccursCoordinates ( obj1 . getX ( ) , obj1 . getY ( ) , obj1 . getX ( ) + obj1 . getWidth ( ) , obj1 . getY ( ) + obj1 . getHeight ( ) , obj2 . getX ( ) , obj2 . getY ( ) , obj2 . getX ( ) + obj2 . getWidth ( ) , obj2 . getY ( ) + obj2 . getHeight ( ) ) == true ) { r e t = true ; } return r e t ; } private static class ImageObject { public ImageObject ( ) { } public ImageObject ( double xinput , double yinput , double xwidthinput , double yheightinput , double angleinput ) { x = xinput ; y = yinput ; xwidth = xwidthinput ; yheight = yheightinput ; angle = angleinput ; internalangle = 0 . 0 ; coords = new Vector ( ) ; } public double getX ( ) { return x ; } public double getY ( ) { return y ; } public double getWidth ( ) { return xwidth ; }

Asteroids

public double getHeight ( ) { return yheight ; } public double getAngle ( ) { return angle ; } public double getInternalAngle ( ) { return internalangle ; } public void setAngle ( double angleinput ) { angle = angleinput ; } public void setInternalAngle ( double internalangleinput ) { internalangle = internalangleinput ; } public Vector getCoords ( ) { return coords ; } public void setCoords ( Vector coordsinput ) { coords = coordsinput ; generateTriangles ( ) ; // p r i n t T r i a n g l e s ( ) ; } public void generateTriangles ( ) { t r i a n g l e s = new Vector ( ) ; // format : ( 0 , 1 ) , ( 2 , 3 ) , ( 4 , 5 ) i s the ( x , y ) coords o f a triangle . // get center p o i n t o f a l l coordinates . comX = getComX ( ) ; comY = getComY ( ) ;

101

102

Foundations of Videogame Programming Code Repository

for ( int i = 0; i < coords . s i z e ( ) ; i = i + 2 ) { t r i a n g l e s . addElement ( coords . elementAt ( i ) ) ; t r i a n g l e s . addElement ( coords . elementAt ( i +1) ) ; t r i a n g l e s . addElement ( coords . elementAt ( ( i +2) % coords . s i z e ( ) ) ); t r i a n g l e s . addElement ( coords . elementAt ( ( i +3) % coords . s i z e ( ) ) ); t r i a n g l e s . addElement (comX) ; t r i a n g l e s . addElement (comY) ; } } public void p r i n t T r i a n g l e s ( ) { for ( int i = 0; i < t r i a n g l e s . s i z e ( ) ; i = i + 6 ) { System . out . p r i n t ( ” p0x : ” + t r i a n g l e s . elementAt ( i ) + ” , p0y : ” + t r i a n g l e s . elementAt ( i +1) ) ; System . out . p r i n t ( ” p1x : ” + t r i a n g l e s . elementAt ( i +2) + ” , p1y : ” + t r i a n g l e s . elementAt ( i +3) ) ; System . out . p r i n t l n ( ” p2x : ” + t r i a n g l e s . elementAt ( i +4) + ” , p2y : ” + t r i a n g l e s . elementAt ( i +5) ) ; } } public double getComX ( ) { double r e t = 0; i f ( coords . s i z e ( ) > 0 ) { for ( int i = 0; i < coords . s i z e ( ) ; i = i + 2 ) { r e t = r e t + coords . elementAt ( i ) ; } r e t = r e t / ( coords . s i z e ( ) / 2 . 0 ) ; } return r e t ; } public double getComY ( ) { double r e t = 0; i f ( coords . s i z e ( ) > 0 )

Asteroids { for ( int i = 1; i < coords . s i z e ( ) ; i = i + 2 ) { r e t = r e t + coords . elementAt ( i ) ; } r e t = r e t / ( coords . s i z e ( ) / 2 . 0 ) ; } return r e t ; } public void move ( double xinput , double yinput ) { x = x + xinput ; y = y + yinput ; } public void moveto ( double xinput , double yinput ) { x = xinput ; y = yinput ; } public void screenWrap ( double leftEdge , double rightEdge , double topEdge , double bottomEdge ) { i f ( x > rightEdge ) { moveto ( leftEdge , getY ( ) ) ; } i f ( x < leftEdge ) { moveto ( rightEdge , getY ( ) ) ; } i f ( y > bottomEdge ) { moveto ( getX ( ) , topEdge ) ; } i f ( y < topEdge ) { moveto ( getX ( ) , bottomEdge ) ; } } public void r o t a t e ( double angleinput ) { angle = angle + angleinput ; while ( angle > twoPi )

103

104

Foundations of Videogame Programming Code Repository { angle = angle − twoPi ; } while ( angle < 0 ) { angle = angle + twoPi ; } } public void spin ( double internalangleinput ) { internalangle = internalangle + internalangleinput ; while ( internalangle > twoPi ) { internalangle = internalangle − twoPi ; } while ( internalangle < 0 ) { internalangle = internalangle + twoPi ; } } private private private private private private private private private private

double x ; double y ; double xwidth ; double yheight ; double angle ; // i n Radians double internalangle ; // i n Radians Vector coords ; Vector t r i a n g l e s ; double comX; double comY;

} private static void bindKey ( JPanel myPanel , String input ) { myPanel . getInputMap ( IFW ) . put ( KeyStroke . getKeyStroke ( ” pressed ” + input ) , input + ” pressed ” ) ; myPanel . getActionMap ( ) . put ( input + ” pressed ” , new KeyPressed ( input ) ); myPanel . getInputMap ( IFW ) . put ( KeyStroke . getKeyStroke ( ” released ” + input ) , input + ” released ” ) ; myPanel . getActionMap ( ) . put ( input + ” released ” , new KeyReleased ( input ) ) ;

Asteroids

105

}

public static void main ( String [ ] args ) { setup ( ) ; appFrame . setDefaultCloseOperation ( JFrame . EXIT ON CLOSE ) ; appFrame . s e t S i z e (501 ,585) ; JPanel myPanel = new JPanel ( ) ; String [ ] l e v e l s = { ”One” , ”Two” , ” Three ” , ” Four ” , ” Five ” , ” Six ” , ” Seven ” , ” Eight ” , ” Nine ” , ” Ten ” } ; JComboBox levelMenu = new JComboBox( l e v e l s ) ; levelMenu . setSelectedIndex ( 2 ) ; levelMenu . addActionListener ( new GameLevel ( ) ) ; myPanel . add ( levelMenu ) ; JButton newGameButton = new JButton ( ”New Game” ) ; newGameButton . addActionListener ( new StartGame ( ) ) ; myPanel . add ( newGameButton ) ; JButton quitButton = new JButton ( ” Quit Game” ) ; quitButton . addActionListener ( new QuitGame ( ) ) ; myPanel . add ( quitButton ) ; bindKey ( bindKey ( bindKey ( bindKey ( bindKey (

myPanel , myPanel , myPanel , myPanel , myPanel ,

”UP” ) ; ”DOWN” ) ; ”LEFT” ) ; ”RIGHT” ) ; ”F” ) ;

appFrame . getContentPane ( ) . add ( myPanel , ” South ” ) ; appFrame . s e t V i s i b l e ( true ) ; } private private private private

static static static static

Boolean endgame ; Boolean enemyAlive ; BufferedImage background ; BufferedImage player ;

private private private private private

static static static static static

Boolean Boolean Boolean Boolean Boolean

upPressed ; downPressed ; leftPressed ; rightPressed ; firePressed ;

106

Foundations of Videogame Programming Code Repository private private private private private private

static static static static static static

ImageObject p1 ; double p1width ; double p1height ; double p1originalX ; double p1originalY ; double p 1 v e l o c i t y ;

private private private private private private

static static static static static static

ImageObject enemy ; BufferedImage enemyShip ; BufferedImage enemyBullet ; Vector enemyBullets ; Vector enemyBulletsTimes ; Long enemybulletlifetime ;

private private private private private private

static static static static static static

Vector playerBullets ; Vector playerBulletsTimes ; double bulletWidth ; BufferedImage playerBullet ; Long p l a y e r b u l l e t l i f e t i m e ; double playerbulletgap ;

private private private private private private private private private

static static static static static static static static static

ImageObject flames ; BufferedImage flame1 ; BufferedImage flame2 ; BufferedImage flame3 ; BufferedImage flame4 ; BufferedImage flame5 ; BufferedImage flame6 ; int flamecount ; double flamewidth ;

private static int l e v e l ; private private private private private private private private

static static static static static static static static

Vector< ImageObject > asteroids ; Vector< I n t e g e r > asteroidsTypes ; BufferedImage ast1 ; BufferedImage ast2 ; BufferedImage ast3 ; double ast1width ; double ast2width ; double ast3width ;

private private private private private

static static static static static

Vector< ImageObject > explosions ; Vector< Long > explosionsTimes ; Long e x p l o s i o n l i f e t i m e ; BufferedImage exp1 ; BufferedImage exp2 ;

Asteroids private static int expcount ; private private private private

static static static static

int int int int

XOFFSET; YOFFSET; WINWIDTH; WINHEIGHT;

private static double pi ; private static double twoPi ; private static JFrame appFrame ; private static f i n a l int IFW = JComponent .WHEN IN FOCUSED WINDOW; }

107

108

Foundations of Videogame Programming Code Repository

Chapter 6 Zelda

// Zelda . java Copyright ( C ) 2020 Ben Sanders // TODO: Add weapons and enemy l i v e s . import java . u t i l . Vector ; import java . u t i l .Random; import java . time . LocalTime ; import java . time . temporal . ChronoUnit ; import import import import import import import

javax . swing . JFrame ; javax . swing . JPanel ; javax . swing . JButton ; javax . swing . JComponent ; javax . swing . KeyStroke ; javax . swing . AbstractAction ; javax . swing .JComboBox;

import javax . imageio . ImageIO ; import java . awt . image . BufferedImage ; import java . i o . F i l e ; import java . i o . IOException ; import java . awt . event . ActionEvent ; import java . awt . event . ActionListener ; import java . awt . Graphics ; import java . awt . Graphics2D ; import java . awt . geom . AffineTransform ; import java . awt . image . AffineTransformOp ; import javax . sound . sampled . AudioInputStream ; 109

110

Foundations of Videogame Programming Code Repository

import javax . sound . sampled . AudioSystem ; import javax . sound . sampled . Clip ; public class Zelda { public Zelda ( ) { setup ( ) ; } public static void setup ( ) { appFrame = new JFrame ( ” The Legend o f Zelda : Link ’ s Awakening ” ) ; XOFFSET = 0; YOFFSET = 40; WINWIDTH = 338; WINHEIGHT = 271; pi = 3.14159265358979; quarterPi = 0.25 ∗ pi ; h a l f P i = 0.5 ∗ pi ; threequartersPi = 0.75 ∗ pi ; f i v e q u a r t e r s P i = 1.25 ∗ pi ; threehalvesPi = 1.5 ∗ pi ; sevenquartersPi = 1.75 ∗ pi ; twoPi = 2.0 ∗ pi ; endgame = f a l s e ; p1width = 20; //18.5; p1height = 20; //25; p1originalX = ( double )XOFFSET + ( ( double )WINWIDTH / 2 . 0 ) − ( p1width / 2.0) ; p1originalY = ( double )YOFFSET + ( ( double )WINHEIGHT / 2 . 0 ) − ( p1height / 2.0) ; l e v e l = 3; a u d i o l i f e t i m e = new Long(78000) ; // 78 seconds f o r KI .wav d r o p L i f e L i f e t i m e = new Long(1000) ; // 1 second try { // s e t t i n g up the Koholint Island images xdimKI = 16; ydimKI = 16; backgroundKI = new Vector< Vector< BufferedImage > >() ; for ( int i = 0; i < ydimKI ; i ++ ) { Vector< BufferedImage > temp = new Vector< BufferedImage >() ; for ( int j = 0; j < xdimKI ; j ++ ) {

Zelda

111

BufferedImage tempImg = ImageIO . read ( new F i l e ( ” blank . png” ) ); temp . addElement ( tempImg ) ; } backgroundKI . addElement ( temp ) ; } for ( int i = 0; i < backgroundKI . s i z e ( ) ; i ++ ) { for ( int j = 0; j < backgroundKI . elementAt ( i ) . s i z e ( ) ; j ++ ) { i f ( ( j == 5 && i == 10 ) | | ( j == 5 && i == 11 ) | | ( j == 6 && i == 10 ) | | ( j == 6 && i == 11 ) | | ( j == 7 && i == 10 ) | | ( j == 7 && i == 11 ) | | ( j == 8 && i == 9 ) | | ( j == 8 && i == 10 ) ) // TODO swap j and i { String filename = ” KI ” ; i f ( j < 10 ) { filename = filename + ”0” ; } filename = filename + j ; i f ( i < 10 ) { filename = filename + ”0” ; } filename = filename + i + ” . png” ; //System . out . p r i n t l n ( filename ) ; backgroundKI . elementAt ( i ) . set ( j , ImageIO . read ( new F i l e ( filename ) ) ) ; } } } // s e t t i n g up the Koholint Island walls wallsKI = new Vector< Vector< Vector< ImageObject > > >() ; for ( int i = 0; i < ydimKI ; i ++ ) { Vector< Vector< ImageObject > > temp = new Vector< Vector< ImageObject > >() ; for ( int j = 0; j < xdimKI ; j ++ ) { Vector< ImageObject > tempWalls = new Vector< ImageObject >() ; temp . addElement ( tempWalls ) ; }

112

Foundations of Videogame Programming Code Repository wallsKI . add ( temp ) ; } for ( int i = 0; i < wallsKI . s i z e ( ) ; i ++ ) { for ( int j = 0; j < wallsKI . elementAt ( i ) . s i z e ( ) ; j ++ ) { i f ( i == 5 && j == 10 ) { wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 270, 35, 68, 70, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 100, 100, 200, 35, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 100, 135, 35, 35, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 0 , 165, 35, 135, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 100, 200, 35, 100, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 135, 270, 200, 35, 0.0 ) ) ; } i f ( i == 8 && j == 9 ) { wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 0 , 35, 135, 35, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 100, 70, 35, 140, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 35, 135, 35, 100, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 0 , 170, 35, 70, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 0 , 235, 35, 70, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 0 , 270, 135, 35, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 170, 270, 135, 35, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 300, 35, 35, 270, 0.0 ) ) ; wallsKI . elementAt ( i ) . elementAt ( j ) . addElement ( new ImageObject ( 235, 35, 70, 35, 0.0 ) ) ; } } } // s e t t i n g up the T a i l Cave images

Zelda

113

xdimTC = 9;//7; // TODO: need t o be able t o j u s t use 7 and 6 , not 9 and 8. ydimTC = 8;//6; backgroundTC = new Vector< Vector< BufferedImage > >() ; for ( int i = 0; i < ydimTC ; i ++ ) { Vector< BufferedImage > temp = new Vector< BufferedImage >() ; for ( int j = 0; j < xdimTC ; j ++ ) { BufferedImage tempImg = ImageIO . read ( new F i l e ( ” blank . png” ) ); temp . addElement ( tempImg ) ; } backgroundTC . addElement ( temp ) ; } for ( int i = 0; i < backgroundTC . s i z e ( ) ; i ++ ) { for ( int j = 0; j < backgroundTC . elementAt ( i ) . s i z e ( ) ; j ++ ) { i f ( ( j == 0 && i == 2) | | ( j == 0 && i == 3) | | ( j == 0 && i == 4) | | ( j == 1 && i == 1) | | ( j == 1 && i == 3) | | ( j == 1 && i == 5) | | ( j == 2 && i == 1) | | ( j == 2 && i == 2) | | ( j == 2 && i == 3) | | ( j == 2 && i == 4) | | ( j == 2 && i == 5) | | ( j == 2 && i == 6) | | ( j == 3 && i == 1) | | ( j == 3 && i == 2) | | ( j == 3 && i == 3) | | ( j == 3 && i == 4) | | ( j == 3 && i == 5) | | ( j == 4 && i == 2) | | ( j == 4 && i == 3) | | ( j == 4 && i == 4) | | ( j == 5 && i == 2) | | ( j == 5 && i == 3) | | ( j == 6 && i == 0) | | ( j == 6 && i == 1) | | ( j == 6 && i == 2) | | ( j == 6 && i == 3) ) { String filename = ”TC” ; i f ( j < 10 ) { filename = filename + ”0” ; } filename = filename + j ; i f ( i < 10 ) { filename = filename + ”0” ; } filename = filename + i + ” . png” ; //System . out . p r i n t l n ( filename ) ;

114

Foundations of Videogame Programming Code Repository backgroundTC . elementAt ( i ) . set ( j , ImageIO . read ( new F i l e ( filename ) ) ) ; } } } // s e t t i n g up the T a i l Cave walls wallsTC = new Vector< Vector< Vector< ImageObject > > >() ; for ( int i = 0; i < ydimTC ; i ++ ) { Vector< Vector< ImageObject > > temp = new Vector< Vector< ImageObject > >() ; for ( int j = 0; j < xdimTC ; j ++ ) { Vector< ImageObject > tempWalls = new Vector< ImageObject >() ; temp . addElement ( tempWalls ) ; } wallsTC . add ( temp ) ; } player = ImageIO . read ( new F i l e ( ” link00 . png” ) ) ; // Link ’ s images l i n k = new Vector< BufferedImage >() ; for ( int i = 0; i < 72; i ++ ) { i f ( i < 10 ) { String filename = ” link0 ” + i + ” . png” ; l i n k . addElement ( ImageIO . read ( new F i l e ( filename ) ) ) ; } else { String filename = ” l i n k ” + i + ” . png” ; l i n k . addElement ( ImageIO . read ( new F i l e ( filename ) ) ) ; } } // BluePig Enemy ’ s images bluepigEnemies = new Vector< ImageObject >() ; bluepigEnemy = new Vector< BufferedImage >() ; bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( bluepigEnemy . addElement ( ImageIO . read ( new F i l e (

”BPB1. png” ”BPB2. png” ”BPF1. png” ”BPF2. png” ”BPL1 . png”

) ) ) ) )

) ) ) ) )

); ); ); ); );

Zelda

115

bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( ”BPL2 . png” ) ) ) ; bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( ”BPR1. png” ) ) ) ; bluepigEnemy . addElement ( ImageIO . read ( new F i l e ( ”BPR2. png” ) ) ) ; // BubbleBoss Enemies bubblebossEnemies = new Vector< ImageObject >() ; // Health images l e f t H e a r t O u t l i n e = ImageIO . read ( new F i l e ( ” heartOutlineLeft . png” ) ); rightHeartOutline = ImageIO . read ( new F i l e ( ” heartOutlineRight . png ” ) ); l e f t H e a r t = ImageIO . read ( new F i l e ( ” h e a r t L e f t . png” ) ) ; rightHeart = ImageIO . read ( new F i l e ( ” heartRight . png” ) ) ; } catch ( IOException i o e ) { } } private static class Animate implements Runnable { public void run ( ) { while ( endgame == f a l s e ) { backgroundDraw ( ) ; enemiesDraw ( ) ; playerDraw ( ) ; healthDraw ( ) ; try { Thread . sleep ( 3 2 ) ; } catch ( InterruptedException e ) { } } } } private static class AudioLooper implements Runnable {

116

Foundations of Videogame Programming Code Repository public void run ( ) { while ( endgame == f a l s e ) { Long curTime = new Long ( System . currentTimeMillis ( ) ) ; i f ( curTime − lastAudioStart > a u d i o l i f e t i m e ) { playAudio ( backgroundState ) ; } } }

} private static void playAudio ( String backgroundState ) { try { c l i p . stop ( ) ; } catch ( Exception e ) { // NOP } try { i f ( backgroundState . substring ( 0 , 2 ) . equals ( ” KI ” ) ) { AudioInputStream a i s = AudioSystem . getAudioInputStream (new F i l e ( ” KI . wav” ) . getAbsoluteFile ( ) ) ; c l i p = AudioSystem . g e t C l i p ( ) ; c l i p . open ( a i s ) ; clip . start ( ) ; lastAudioStart = System . currentTimeMillis ( ) ; a u d i o l i f e t i m e = new Long(78000) ; } else i f ( backgroundState . substring ( 0 , 2 ) . equals ( ”TC” ) ) { AudioInputStream a i s = AudioSystem . getAudioInputStream (new F i l e ( ”TC. wav” ) . getAbsoluteFile ( ) ) ; c l i p = AudioSystem . g e t C l i p ( ) ; c l i p . open ( a i s ) ; clip . start ( ) ; lastAudioStart = System . currentTimeMillis ( ) ; a u d i o l i f e t i m e = new Long(191000) ; } } catch ( Exception e )

Zelda { // NOP } } private static String bgWrap ( String input , int wrap ) { String r e t = input ; i f ( wrap == 0 ) { // NOP } else i f ( wrap == 1 ) // r i g h t { int xcoord = I n t e g e r . parseInt ( input . substring ( 2 , 4 ) ) ; int ycoord = I n t e g e r . parseInt ( input . substring ( 4 , 6 ) ) ; xcoord = xcoord + 1; i f ( xcoord < 10 ) { r e t = input . substring ( 0 , 2) + ”0” + xcoord ; } else { r e t = input . substring ( 0 , 2) + xcoord ; } i f ( ycoord < 10 ) { r e t = r e t + ”0” + ycoord ; } else { r e t = r e t + ycoord ; } } else i f ( wrap == 2 ) // l e f t { int xcoord = I n t e g e r . parseInt ( input . substring ( 2 , 4 ) ) ; int ycoord = I n t e g e r . parseInt ( input . substring ( 4 , 6 ) ) ; xcoord = xcoord − 1; i f ( xcoord < 10 ) { r e t = input . substring ( 0 , 2) + ”0” + xcoord ; }

117

118

Foundations of Videogame Programming Code Repository else { r e t = input . substring ( 0 , 2) + xcoord ; } i f ( ycoord < 10 ) { r e t = r e t + ”0” + ycoord ; } else { r e t = r e t + ycoord ; } } else i f ( wrap == 3 ) // down { int xcoord = I n t e g e r . parseInt ( input . substring ( 2 , 4 ) ) ; int ycoord = I n t e g e r . parseInt ( input . substring ( 4 , 6 ) ) ; ycoord = ycoord + 1; i f ( xcoord < 10 ) { r e t = input . substring ( 0 , 2) + ”0” + xcoord ; } else { r e t = input . substring ( 0 , 2) + xcoord ; } i f ( ycoord < 10 ) { r e t = r e t + ”0” + ycoord ; } else { r e t = r e t + ycoord ; } } else i f ( wrap == 4 ) // up { int xcoord = I n t e g e r . parseInt ( input . substring ( 2 , 4 ) ) ; int ycoord = I n t e g e r . parseInt ( input . substring ( 4 , 6 ) ) ; ycoord = ycoord − 1; i f ( xcoord < 10 ) { r e t = input . substring ( 0 , 2) + ”0” + xcoord ;

Zelda

119

} else { r e t = input . substring ( 0 , 2) + xcoord ; } i f ( ycoord < 10 ) { r e t = r e t + ”0” + ycoord ; } else { r e t = r e t + ycoord ; } } return r e t ; } private static class PlayerMover implements Runnable { public PlayerMover ( ) { v e l o c i t y s t e p = 3; } public void run ( ) { while ( endgame == f a l s e ) { try { Thread . sleep ( 10 ) ; } catch ( InterruptedException e ) { } i f ( upPressed | | downPressed | | l e f t P r e s s e d | | rightPressed ) { p1velocity = velocitystep ; i f ( upPressed ) { i f ( leftPressed ) { p1 . setInternalAngle ( f i v e q u a r t e r s P i ) ; } else i f ( rightPressed )

120

Foundations of Videogame Programming Code Repository { p1 . setInternalAngle (5.49779) ; } else { p1 . setInternalAngle ( threehalvesPi ) ; } } i f ( downPressed ) { i f ( leftPressed ) { p1 . setInternalAngle (2.35619) ; } else i f ( rightPressed ) { p1 . setInternalAngle ( quarterPi ) ; } else { p1 . setInternalAngle ( h a l f P i ) ; } } i f ( leftPressed ) { i f ( upPressed ) { p1 . setInternalAngle ( f i v e q u a r t e r s P i ) ; } else i f ( downPressed ) { p1 . setInternalAngle ( threequartersPi ) ; } else { p1 . setInternalAngle ( pi ) ; } } i f ( rightPressed ) { i f ( upPressed ) { p1 . setInternalAngle (5.49779) ; } else i f ( downPressed ) { p1 . setInternalAngle ( quarterPi ) ;

Zelda

121

} else { p1 . setInternalAngle ( 0 . 0 ) ; } } } else { p1velocity = 0.0; p1 . setInternalAngle ( threehalvesPi ) ; } p1 . updateBounce ( ) ; p1 . move ( p 1 v e l o c i t y ∗ Math . cos ( p1 . getInternalAngle ( ) ) , p 1 v e l o c i t y ∗ Math . sin ( p1 . getInternalAngle ( ) ) ) ; int wrap = p1 . screenWrap ( XOFFSET, XOFFSET + WINWIDTH, YOFFSET, YOFFSET + WINHEIGHT ) ; backgroundState = bgWrap ( backgroundState , wrap ) ; i f ( wrap ! = 0 ) { clearEnemies ( ) ; generateEnemies ( backgroundState ) ; } } } private double v e l o c i t y s t e p ; } private static void clearEnemies ( ) { bluepigEnemies . c l e a r ( ) ; bubblebossEnemies . c l e a r ( ) ; } private static void generateEnemies ( { i f ( backgroundState . substring ( 0 , { bluepigEnemies . addElement ( new bluepigEnemies . addElement ( new ); }

String backgroundState ) 6 ) . equals ( ” KI0809 ” ) ) ImageObject (20 , 90, 33, 33, 0 . 0 ) ) ; ImageObject (250 , 230, 33, 33, 0 . 0 )

for ( int i = 0; i < bluepigEnemies . s i z e ( ) ; i ++ ) { bluepigEnemies . elementAt ( i ) . setMaxFrames ( 2 5 ) ;

122

Foundations of Videogame Programming Code Repository }

} private static class EnemyMover implements Runnable { public EnemyMover ( ) { b l u e p i g v e l o c i t y s t e p = 2; } public void run ( ) { Random randomNumbers = new Random( LocalTime .now ( ) . getNano ( ) ) ; while ( endgame == f a l s e ) { try { Thread . sleep ( 10 ) ; } catch ( InterruptedException e ) { // NOP; } // TODO try { for ( int i = 0; i < bluepigEnemies . s i z e ( ) ; i ++ ) { int s t a t e = randomNumbers . nextInt ( 1000 ) ; i f ( state < 5 ) { bluepigvelocity = bluepigvelocitystep ; bluepigEnemies . elementAt ( i ) . setInternalAngle ( 0 ) ; } else i f ( s t a t e < 10 ) { bluepigvelocity = bluepigvelocitystep ; bluepigEnemies . elementAt ( i ) . setInternalAngle ( h a l f P i ) ; } else i f ( s t a t e < 15 ) { bluepigvelocity = bluepigvelocitystep ; bluepigEnemies . elementAt ( i ) . setInternalAngle ( pi ) ; } else i f ( s t a t e < 20 ) { bluepigvelocity = bluepigvelocitystep ;

Zelda

123

bluepigEnemies . elementAt ( i ) . setInternalAngle ( threehalvesPi ) ; } else i f ( s t a t e < 250 ) { bluepigvelocity = bluepigvelocitystep ; } else { b l u e p i g v e l o c i t y = 0; } bluepigEnemies . elementAt ( i ) . updateBounce ( ) ; bluepigEnemies . elementAt ( i ) . move ( b l u e p i g v e l o c i t y ∗ Math . cos ( bluepigEnemies . elementAt ( i ) . getInternalAngle ( ) ) , b l u e p i g v e l o c i t y ∗ Math . sin ( bluepigEnemies . elementAt ( i ) . getInternalAngle ( ) ) ) ; } for ( int i = 0; i < bubblebossEnemies . s i z e ( ) ; i ++ ) { } } catch ( java . lang . NullPointerException j l n p e ) { // NOP } } } private double b l u e p i g v e l o c i t y s t e p ; private double b l u e p i g v e l o c i t y ; } private static class HealthTracker implements Runnable { public void run ( ) { while ( endgame == f a l s e ) { Long curTime = new Long ( System . currentTimeMillis ( ) ) ; i f ( availableToDropLife && p1 . getDropLife ( ) > 0 ) { int newLife = p1 . g e t L i f e ( ) − p1 . getDropLife ( ) ; p1 . setDropLife ( 0 ) ; availableToDropLife = f a l s e ; lastDropLife = System . currentTimeMillis ( ) ;

124

Foundations of Videogame Programming Code Repository p1 . s e t L i f e ( newLife ) ; try { AudioInputStream a is = AudioSystem . getAudioInputStream ( new F i l e ( ” hurt . wav” ) . getAbsoluteFile ( ) ) ; Clip h u r t c l i p = AudioSystem . g e t C l i p ( ) ; h u r t c l i p . open ( a i s ) ; hurtclip . start ( ) ; } catch ( Exception e ) { } } else { i f ( curTime − lastDropLife > d r o p L i f e L i f e t i m e ) { availableToDropLife = true ; } } } }

} private static class CollisionChecker implements Runnable { public void run ( ) { //Random randomNumbers = new Random ( LocalTime .now ( ) . getNano ( ) ) ; while ( endgame == f a l s e ) { // check player against doors i n given scenes i f ( backgroundState . substring ( 0 , 6 ) . equals ( ” KI0511 ” ) ) { i f ( collisionOccurs ( p1 , doorKItoTC ) ) { p1 . moveto ( p1originalX , p1originalY ) ; backgroundState = ”TC0305” ; c l i p . stop ( ) ; playAudio ( backgroundState ) ; } } else i f ( backgroundState . substring ( 0 , 6 ) . equals ( ”TC0305” ) ) { i f ( collisionOccurs ( p1 , doorTCtoKI ) )

Zelda

125

{ p1 . moveto ( p1originalX , p1originalY ) ; backgroundState = ” KI0511 ” ; c l i p . stop ( ) ; playAudio ( backgroundState ) ; } } // check player and enemies against walls i f ( backgroundState . substring ( 0 , 6 ) . equals ( ” KI0510 ” ) ) { checkMoversAgainstWalls ( wallsKI . elementAt ( 5 ) . elementAt ( 1 0 ) ); } i f ( backgroundState . substring ( 0 , 6 ) . equals ( ” KI0809 ” ) ) { checkMoversAgainstWalls ( wallsKI . elementAt ( 8 ) . elementAt ( 9 ) ) ; } // check player against enemies for ( int i = 0; i < bluepigEnemies . s i z e ( ) ; i ++ ) { i f ( collisionOccurs ( p1 , bluepigEnemies . elementAt ( i ) ) ) { //System . out . p r i n t l n ( ” S t i l l C o l l i d i n g : ” + i + ” , ” + System . c u r r e n t T i m e M i l l i s ( ) ) ; p1 . setBounce ( true ) ; bluepigEnemies . elementAt ( i ) . setBounce ( true ) ; i f ( availableToDropLife ) { p1 . setDropLife ( 1 ) ; } } }

// TODO: check enemies against walls // TODO: check player against deep water or p i t s

// TODO: check player against enemy arrows // TODO: check enemies against player weapons }

126

Foundations of Videogame Programming Code Repository } private static void checkMoversAgainstWalls ( Vector< ImageObject > wallsInput ) { for ( int i = 0; i < wallsInput . s i z e ( ) ; i ++ ) { i f ( collisionOccurs ( p1 , wallsInput . elementAt ( i ) ) ) { p1 . setBounce ( true ) ; } for ( int j = 0; j < bluepigEnemies . s i z e ( ) ; j ++ ) { i f ( collisionOccurs ( bluepigEnemies . elementAt ( j ) , wallsInput . elementAt ( i ) ) ) { bluepigEnemies . elementAt ( j ) . setBounce ( true ) ; } } } }

} // TODO make one l o c k r o t a t e f u n c t i o n which takes as input objInner , objOuter , and p o i n t r e l a t i v e t o objInner ’ s x , y that objOuter must r o t a t e around . // d i s t i s a distance between the two o b j e c t s at the bottom o f objInner . private static void lockrotateObjAroundObjbottom ( ImageObject objOuter , ImageObject objInner , double d i s t ) { objOuter . moveto ( objInner . getX ( ) + ( d i s t + objInner . getWidth ( ) / 2 . 0 ) ∗ Math . cos(− objInner . getAngle ( ) + pi /2.0) + objOuter . getWidth ( ) / 2.0 , objInner . getY ( ) + ( d i s t + objInner . getHeight ( ) / 2 . 0 ) ∗ Math . sin (− objInner . getAngle ( ) + pi /2.0) + objOuter . getHeight ( ) / 2.0) ; objOuter . setAngle ( objInner . getAngle ( ) ) ; } // d i s t i s a distance between the two o b j e c t s at the top o f the inner object . private static void lockrotateObjAroundObjtop ( ImageObject objOuter , ImageObject objInner , double d i s t ) { objOuter . moveto ( objInner . getX ( ) + objOuter . getWidth ( ) + ( objInner . getWidth ( ) / 2.0 + ( d i s t + objInner . getWidth ( ) / 2.0 ) ∗ Math . cos ( objInner . getAngle ( ) + pi /2.0 ) ) / 2.0 , objInner . getY ( ) −

Zelda

127

objOuter . getHeight ( ) + ( d i s t + objInner . getHeight ( ) / 2.0 ) ∗ Math . sin ( objInner . getAngle ( ) / 2.0 ) ) ; objOuter . setAngle ( objInner . getAngle ( ) ) ; } private static AffineTransformOp rotateImageObject ( ImageObject obj ) { AffineTransform at = AffineTransform . getRotateInstance ( −obj . getAngle ( ) , obj . getWidth ( ) /2.0 , obj . getHeight ( ) /2.0 ) ; AffineTransformOp atop = new AffineTransformOp ( at , AffineTransformOp . TYPE BILINEAR ) ; return atop ; } private static AffineTransformOp spinImageObject ( ImageObject obj ) { AffineTransform at = AffineTransform . getRotateInstance ( −obj . getInternalAngle ( ) , obj . getWidth ( ) /2.0 , obj . getHeight ( ) /2.0 ) ; AffineTransformOp atop = new AffineTransformOp ( at , AffineTransformOp . TYPE BILINEAR ) ; return atop ; } private static void backgroundDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; i f ( backgroundState . substring ( 0 , 2 ) . equals ( ” KI ” ) ) { int i = I n t e g e r . parseInt ( backgroundState . substring ( 4 , 6 ) ) ; int j = I n t e g e r . parseInt ( backgroundState . substring ( 2 , 4 ) ) ; i f ( i < backgroundKI . s i z e ( ) ) { i f ( j < backgroundKI . elementAt ( i ) . s i z e ( ) ) { g2D . drawImage ( backgroundKI . elementAt ( i ) . elementAt ( j ) , XOFFSET, YOFFSET, null ) ; } } } i f ( backgroundState . substring ( 0 , 2 ) . equals ( ”TC” ) ) { int i = I n t e g e r . parseInt ( backgroundState . substring ( 4 , 6 ) ) ; int j = I n t e g e r . parseInt ( backgroundState . substring ( 2 , 4 ) ) ; i f ( i < backgroundTC . s i z e ( ) )

128

Foundations of Videogame Programming Code Repository { i f ( j < backgroundTC . elementAt ( i ) . s i z e ( ) ) { g2D . drawImage ( backgroundTC . elementAt ( i ) . elementAt ( j ) , XOFFSET, YOFFSET, null ) ; } } }

} private static void playerDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; i f ( upPressed | | downPressed | | l e f t P r e s s e d | | rightPressed ) { i f ( upPressed == true ) { i f ( p1 . getCurrentFrame ( ) == 0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 4 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } else i f ( p1 . getCurrentFrame ( ) == 1 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 5 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } p1 . updateCurrentFrame ( ) ; } i f ( downPressed == true ) { i f ( p1 . getCurrentFrame ( ) == 0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 2 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } else i f ( p1 . getCurrentFrame ( ) == 1 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 3 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; }

( p1 .

( p1 .

( p1 .

( p1 .

Zelda p1 . updateCurrentFrame ( ) ; } i f ( l e f t P r e s s e d == true ) { i f ( p1 . getCurrentFrame ( ) == 0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 0 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } else i f ( p1 . getCurrentFrame ( ) == 1 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 1 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } p1 . updateCurrentFrame ( ) ; } i f ( rightPressed == true ) { i f ( p1 . getCurrentFrame ( ) == 0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 6 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } else i f ( p1 . getCurrentFrame ( ) == 1 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 7 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) getY ( ) + 0 . 5 ) , null ) ; } p1 . updateCurrentFrame ( ) ; }

129

( p1 .

( p1 .

( p1 .

( p1 .

} else { i f ( Math . abs ( lastPressed − 90.0 ) < 1.0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 4 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) ( p1 . getY ( ) + 0 . 5 ) , null ) ; } i f ( Math . abs ( lastPressed − 270.0 ) < 1.0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 2 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) ( p1 . getY ( ) + 0 . 5 )

130

Foundations of Videogame Programming Code Repository , null ) ; } i f ( Math . abs ( lastPressed − 0.0 ) < 1.0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 6 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) ( p1 . getY ( ) + 0 . 5 ) , null ) ; } i f ( Math . abs ( lastPressed − 180.0 ) < 1.0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l i n k . elementAt ( 0 ) , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) ( p1 . getY ( ) + 0 . 5 ) , null ) ; } } //g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( player , n u l l ) , ( i n t ) ( p1 . getX ( ) + 0 . 5 ) , ( i n t ) ( p1 . getY ( ) + 0 . 5 ) , n u l l ) ;

} private static void healthDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; int l e f t s c a l e = 10; int l e f t o f f s e t = 10; int r i g h t o f f s e t = 9; int i n t e r i o r o f f s e t = 2; int h a l f i n t e r i o r o f f s e t = 1; for ( int i = 0; i < p1 . getMaxLife ( ) ; i ++ ) { i f ( i % 2 == 0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( leftHeartOutline , null ) , l e f t s c a l e ∗ i + l e f t o f f s e t + XOFFSET, YOFFSET, null ) ; } else { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( rightHeartOutline , null ) , l e f t s c a l e ∗ i + r i g h t o f f s e t + XOFFSET, YOFFSET, null ) ; } } for ( int i = 0; i < p1 . g e t L i f e ( ) ; i ++ )

Zelda

131

{ i f ( i % 2 == 0 ) { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( l e f t H e a r t , null ) , l e f t s c a l e ∗ i + l e f t o f f s e t + i n t e r i o r o f f s e t + XOFFSET, i n t e r i o r o f f s e t + YOFFSET, null ) ; } else { g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( rightHeart , null ) , leftscale ∗ i + leftoffset − halfinterioroffset + XOFFSET, i n t e r i o r o f f s e t + YOFFSET, null ) ; } } } private static void enemiesDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; for ( int i = 0; i < bluepigEnemies . s i z e ( ) ; i ++ ) { i f ( Math . abs ( bluepigEnemies . elementAt ( i ) . getInternalAngle ( ) − 0.0 ) < 1.0 ) { i f ( bluepigEnemies . elementAt ( i ) . getCurrentFrame ( ) < bluepigEnemies . elementAt ( i ) . getMaxFrames ( ) / 2 ) { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 6 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } else { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 7 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } bluepigEnemies . elementAt ( i ) . updateCurrentFrame ( ) ; } i f ( Math . abs ( bluepigEnemies . elementAt ( i ) . getInternalAngle ( ) − pi ) < 1.0 ) {

132

Foundations of Videogame Programming Code Repository i f ( bluepigEnemies . elementAt ( i ) . getCurrentFrame ( ) < bluepigEnemies . elementAt ( i ) . getMaxFrames ( ) / 2 ) { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 4 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } else { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 5 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } bluepigEnemies . elementAt ( i ) . updateCurrentFrame ( ) ; } i f ( Math . abs ( bluepigEnemies . elementAt ( i ) . getInternalAngle ( ) − h a l f P i ) < 1.0 ) { i f ( bluepigEnemies . elementAt ( i ) . getCurrentFrame ( ) < bluepigEnemies . elementAt ( i ) . getMaxFrames ( ) / 2 ) { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 2 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } else { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 3 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } bluepigEnemies . elementAt ( i ) . updateCurrentFrame ( ) ; } i f ( Math . abs ( bluepigEnemies . elementAt ( i ) . getInternalAngle ( ) − threehalvesPi ) < 1.0 ) { i f ( bluepigEnemies . elementAt ( i ) . getCurrentFrame ( ) < bluepigEnemies . elementAt ( i ) . getMaxFrames ( ) / 2 ) { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 0 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ;

Zelda

133

} else { g2D . drawImage ( rotateImageObject ( bluepigEnemies . elementAt ( i ) ) . f i l t e r ( bluepigEnemy . elementAt ( 1 ) , null ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getX ( ) + 0 . 5 ) , ( int ) ( bluepigEnemies . elementAt ( i ) . getY ( ) + 0 . 5 ) , null ) ; } bluepigEnemies . elementAt ( i ) . updateCurrentFrame ( ) ; } } } private static class KeyPressed extends AbstractAction { public KeyPressed ( ) { action = ” ” ; } public KeyPressed ( String input ) { action = input ; } public void actionPerformed ( ActionEvent e ) { i f ( action . equals ( ”UP” ) ) { upPressed = true ; lastPressed = 90.0; } i f ( action . equals ( ”DOWN” ) ) { downPressed = true ; lastPressed = 270.0; } i f ( action . equals ( ”LEFT” ) ) { l e f t P r e s s e d = true ; lastPressed = 180.0; } i f ( action . equals ( ”RIGHT” ) ) { rightPressed = true ; lastPressed = 0 . 0 ; } i f ( action . equals ( ”A” ) )

134

Foundations of Videogame Programming Code Repository { aPressed = true ; } i f ( action . equals ( ”X” ) ) { xPressed = true ; } } private String action ;

} private static class KeyReleased extends AbstractAction { public KeyReleased ( ) { action = ” ” ; } public KeyReleased ( String input ) { action = input ; } public void actionPerformed ( ActionEvent e ) { i f ( action . equals ( ”UP” ) ) { upPressed = f a l s e ; } i f ( action . equals ( ”DOWN” ) ) { downPressed = f a l s e ; } i f ( action . equals ( ”LEFT” ) ) { leftPressed = false ; } i f ( action . equals ( ”RIGHT” ) ) { rightPressed = f a l s e ; } i f ( action . equals ( ”A” ) ) { aPressed = f a l s e ; } i f ( action . equals ( ”X” ) )

Zelda

135

{ xPressed = f a l s e ; } } private String action ; } private static class QuitGame implements ActionListener { public void actionPerformed ( ActionEvent e ) { endgame = true ; } } private static class StartGame implements ActionListener { public void actionPerformed ( ActionEvent e ) { endgame = true ; upPressed = f a l s e ; downPressed = f a l s e ; leftPressed = false ; rightPressed = f a l s e ; aPressed = f a l s e ; xPressed = f a l s e ; lastPressed = 90.0; backgroundState = ” KI0809 ” ; availableToDropLife = true ; try { clearEnemies ( ) ; generateEnemies ( backgroundState ) ; } catch ( java . lang . NullPointerException j l n p e ) { } p1 = new ImageObject ( p1originalX , p1originalY , p1width , p1height , 0.0 ) ; p1velocity = 0.0; p1 . setInternalAngle ( threehalvesPi ) ; // 270 degrees , i n radians p1 . setMaxFrames ( 2 ) ; p1 . setlastposx ( p1originalX ) ; p1 . setlastposy ( p1originalY ) ; p1 . s e t L i f e ( 6 ) ;

136

Foundations of Videogame Programming Code Repository p1 . setMaxLife ( 6 ) ; doorKItoTC = new ImageObject ( 200, 55, 35, 35, 0.0 ) ; doorTCtoKI = new ImageObject ( 150, 270, 35, 35, 0.0 ) ; try { Thread . sleep ( 5 0 ) ; } catch ( InterruptedException i e ) { } lastAudioStart = System . currentTimeMillis ( ) ; playAudio ( backgroundState ) ; endgame = f a l s e ; lastDropLife = System . currentTimeMillis ( ) ; Thread t1 = new Thread ( new Animate ( ) ) ; Thread t2 = new Thread ( new PlayerMover ( ) ) ; Thread t3 = new Thread ( new CollisionChecker ( ) ) ; Thread t4 = new Thread ( new AudioLooper ( ) ) ; Thread t5 = new Thread ( new EnemyMover ( ) ) ; Thread t6 = new Thread ( new HealthTracker ( ) ) ; t1 . s t a r t ( ) ; t2 . s t a r t ( ) ; t3 . s t a r t ( ) ; t4 . s t a r t ( ) ; t5 . s t a r t ( ) ; t6 . s t a r t ( ) ; }

} private static class GameLevel implements ActionListener { public int decodeLevel ( String input ) { int r e t = 3; i f ( input . equals ( ”One” ) ) { r e t = 1; } else i f ( input . equals ( ”Two” ) ) { r e t = 2; } else i f ( input . equals ( ” Three ” ) ) { r e t = 3; }

Zelda else i f ( { ret = } else i f ( { ret = } else i f ( { ret = } else i f ( { ret = } else i f ( { ret = } else i f ( { ret = } else i f ( { ret = }

input . equals ( ” Four ” ) ) 4; input . equals ( ” Five ” ) ) 5; input . equals ( ” Six ” ) ) 6; input . equals ( ” Seven ” ) ) 7; input . equals ( ” Eight ” ) ) 8; input . equals ( ” Nine ” ) ) 9; input . equals ( ” Ten ” ) ) 10;

return r e t ; } public void actionPerformed ( ActionEvent e ) { JComboBox cb = ( JComboBox ) e . getSource ( ) ; String t e x t L e v e l = ( String ) cb . getSelectedItem ( ) ; l e v e l = decodeLevel ( t e x t L e v e l ) ; } } private static Boolean i s I n s i d e ( double p1x , double p1y , double p2x1 , double p2y1 , double p2x2 , double p2y2 ) { Boolean r e t = f a l s e ; i f ( p1x > p2x1 && p1x < p2x2 ) { i f ( p1y > p2y1 && p1y < p2y2 ) {

137

138

Foundations of Videogame Programming Code Repository r e t = true ; } i f ( p1y > p2y2 && p1y < p2y1 ) { r e t = true ; } } i f ( p1x > p2x2 && p1x < p2x1 ) { i f ( p1y > p2y1 && p1y < p2y2 ) { r e t = true ; } i f ( p1y > p2y2 && p1y < p2y1 ) { r e t = true ; } } return r e t ;

}

private static Boolean collisionOccursCoordinates ( double p1x1 , double p1y1 , double p1x2 , double p1y2 , double p2x1 , double p2y1 , double p2x2 , double p2y2 ) { Boolean r e t = f a l s e ; i f ( i s I n s i d e ( p1x1 , p1y1 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p1x1 , p1y2 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p1x2 , p1y1 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p1x2 , p1y2 , p2x1 , p2y1 , p2x2 , p2y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x1 , p2y1 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; }

Zelda

139

i f ( i s I n s i d e ( p2x1 , p2y2 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x2 , p2y1 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } i f ( i s I n s i d e ( p2x2 , p2y2 , p1x1 , p1y1 , p1x2 , p1y2 ) == true ) { r e t = true ; } return r e t ; } private static Boolean collisionOccurs ( ImageObject obj1 , ImageObject obj2 ) { Boolean r e t = f a l s e ; i f ( collisionOccursCoordinates ( obj1 . getX ( ) , obj1 . getY ( ) , obj1 . getX ( ) + obj1 . getWidth ( ) , obj1 . getY ( ) + obj1 . getHeight ( ) , obj2 . getX ( ) , obj2 . getY ( ) , obj2 . getX ( ) + obj2 . getWidth ( ) , obj2 . getY ( ) + obj2 . getHeight ( ) ) == true ) { r e t = true ; } return r e t ; } private static class ImageObject { public ImageObject ( ) { maxFrames = 1; currentFrame = 0; bounce = f a l s e ; l i f e = 1; maxLife = 1; dropLife = 0; } public ImageObject ( double xinput , double yinput , double xwidthinput , double yheightinput , double angleinput ) { this ( ) ; x = xinput ;

140

Foundations of Videogame Programming Code Repository y = yinput ; lastposx = x ; lastposy = y ; xwidth = xwidthinput ; yheight = yheightinput ; angle = angleinput ; internalangle = 0 . 0 ; coords = new Vector ( ) ; } public double getX ( ) { return x ; } public double getY ( ) { return y ; } public double getlastposx ( ) { return lastposx ; } public double getlastposy ( ) { return lastposy ; } public void setlastposx ( double input ) { lastposx = input ; } public void setlastposy ( double input ) { lastposy = input ; } public double getWidth ( ) { return xwidth ; } public double getHeight ( ) {

Zelda return yheight ; } public double getAngle ( ) { return angle ; } public double getInternalAngle ( ) { return internalangle ; } public void setAngle ( double angleinput ) { angle = angleinput ; } public void setInternalAngle ( double internalangleinput ) { internalangle = internalangleinput ; } public Vector getCoords ( ) { return coords ; } public void setCoords ( Vector coordsinput ) { coords = coordsinput ; generateTriangles ( ) ; // p r i n t T r i a n g l e s ( ) ; } public int getMaxFrames ( ) { return maxFrames ; } public void setMaxFrames ( int input ) { maxFrames = input ; } public int getCurrentFrame ( ) {

141

142

Foundations of Videogame Programming Code Repository return currentFrame ; } public void setCurrentFrame ( int input ) { currentFrame = input ; } public Boolean getBounce ( ) { return bounce ; } public void setBounce ( Boolean input ) { bounce = input ; } public int g e t L i f e ( ) { return l i f e ; } public void s e t L i f e ( int input ) { l i f e = input ; } public int getMaxLife ( ) { return maxLife ; } public void setMaxLife ( int input ) { maxLife = input ; } public int getDropLife ( ) { return dropLife ; } public void setDropLife ( int input ) { dropLife = input ; }

Zelda

143

public void updateBounce ( ) { i f ( getBounce ( ) ) { moveto ( getlastposx ( ) , getlastposy ( ) ) ; } else { setlastposx ( getX ( ) ) ; setlastposy ( getY ( ) ) ; } setBounce ( f a l s e ) ; } public void updateCurrentFrame ( ) { currentFrame = ( currentFrame + 1) % maxFrames ; } public void generateTriangles ( ) { t r i a n g l e s = new Vector ( ) ; // format : ( 0 , 1 ) , ( 2 , 3 ) , ( 4 , 5 ) i s the ( x , y ) coords o f a triangle . // get center p o i n t o f a l l coordinates . comX = getComX ( ) ; comY = getComY ( ) ; for ( int i = 0; i < coords . s i z e ( ) ; i = i + 2 ) { t r i a n g l e s . addElement ( coords . elementAt ( i ) ) ; t r i a n g l e s . addElement ( coords . elementAt ( i +1) ) ; t r i a n g l e s . addElement ( coords . elementAt ( ( i +2) % coords . s i z e ( ) ) ); t r i a n g l e s . addElement ( coords . elementAt ( ( i +3) % coords . s i z e ( ) ) ); t r i a n g l e s . addElement (comX) ; t r i a n g l e s . addElement (comY) ; } } public void p r i n t T r i a n g l e s ( ) {

144

Foundations of Videogame Programming Code Repository for ( int i = 0; i < t r i a n g l e s . s i z e ( ) ; i = i + 6 ) { System . out . p r i n t ( ” p0x : ” + t r i a n g l e s . elementAt ( i ) + ” , p0y : ” + t r i a n g l e s . elementAt ( i +1) ) ; System . out . p r i n t ( ” p1x : ” + t r i a n g l e s . elementAt ( i +2) + ” , p1y : ” + t r i a n g l e s . elementAt ( i +3) ) ; System . out . p r i n t l n ( ” p2x : ” + t r i a n g l e s . elementAt ( i +4) + ” , p2y : ” + t r i a n g l e s . elementAt ( i +5) ) ; } } public double getComX ( ) { double r e t = 0; i f ( coords . s i z e ( ) > 0 ) { for ( int i = 0; i < coords . s i z e ( ) ; i = i + 2 ) { r e t = r e t + coords . elementAt ( i ) ; } r e t = r e t / ( coords . s i z e ( ) / 2 . 0 ) ; } return r e t ; } public double getComY ( ) { double r e t = 0; i f ( coords . s i z e ( ) > 0 ) { for ( int i = 1; i < coords . s i z e ( ) ; i = i + 2 ) { r e t = r e t + coords . elementAt ( i ) ; } r e t = r e t / ( coords . s i z e ( ) / 2 . 0 ) ; } return r e t ; } public void move ( double xinput , double yinput ) { x = x + xinput ; y = y + yinput ; } public void moveto ( double xinput , double yinput ) {

Zelda x = xinput ; y = yinput ; } public int screenWrap ( double leftEdge , double rightEdge , double topEdge , double bottomEdge ) { int r e t = 0; i f ( x > rightEdge ) { moveto ( leftEdge , getY ( ) ) ; r e t = 1; } i f ( x < leftEdge ) { moveto ( rightEdge , getY ( ) ) ; r e t = 2; } i f ( y > bottomEdge ) { moveto ( getX ( ) , topEdge ) ; r e t = 3; } i f ( y < topEdge ) { moveto ( getX ( ) , bottomEdge ) ; r e t = 4; } return r e t ; } public void r o t a t e ( double angleinput ) { angle = angle + angleinput ; while ( angle > twoPi ) { angle = angle − twoPi ; } while ( angle < 0 ) { angle = angle + twoPi ; } } public void spin ( double internalangleinput ) {

145

146

Foundations of Videogame Programming Code Repository internalangle = internalangle + internalangleinput ; while ( internalangle > twoPi ) { internalangle = internalangle − twoPi ; } while ( internalangle < 0 ) { internalangle = internalangle + twoPi ; } } private private private private private private private private private private private private

double x ; double y ; double lastposx ; double lastposy ; double xwidth ; double yheight ; double angle ; // i n Radians double internalangle ; // i n Radians Vector coords ; Vector t r i a n g l e s ; double comX; double comY;

private int maxFrames ; private int currentFrame ; private int l i f e ; private int maxLife ; private int dropLife ; private Boolean bounce ; } private static void bindKey ( JPanel myPanel , String input ) { myPanel . getInputMap ( IFW ) . put ( KeyStroke . getKeyStroke ( ” pressed ” + input ) , input + ” pressed ” ) ; myPanel . getActionMap ( ) . put ( input + ” pressed ” , new KeyPressed ( input ) ); myPanel . getInputMap ( IFW ) . put ( KeyStroke . getKeyStroke ( ” released ” + input ) , input + ” released ” ) ; myPanel . getActionMap ( ) . put ( input + ” released ” , new KeyReleased ( input ) ) ; }

Zelda

147

public static void main ( String [ ] args ) { setup ( ) ; appFrame . setDefaultCloseOperation ( JFrame . EXIT ON CLOSE ) ; appFrame . s e t S i z e (WINWIDTH+1 ,WINHEIGHT+85) ; JPanel myPanel = new JPanel ( ) ; /∗ S t r i n g [ ] l e v e l s = { ”One” , ”Two” , ” Three ” , ” Four ” , ” Five ” , ” Six ” , ” Seven ” , ” Eight ” , ” Nine ” , ”Ten” } ; JComboBox levelMenu = new JComboBox( l e v e l s ) ; levelMenu . setSelectedIndex ( 2 ) ; levelMenu . addActionListener ( new GameLevel ( ) ) ; myPanel . add ( levelMenu ) ; ∗/ JButton quitButton = new JButton ( ” S e l e c t ” ) ; quitButton . addActionListener ( new QuitGame ( ) ) ; myPanel . add ( quitButton ) ; JButton newGameButton = new JButton ( ” Start ” ) ; newGameButton . addActionListener ( new StartGame ( ) ) ; myPanel . add ( newGameButton ) ; bindKey ( bindKey ( bindKey ( bindKey ( bindKey (

myPanel , myPanel , myPanel , myPanel , myPanel ,

”UP” ) ; ”DOWN” ) ; ”LEFT” ) ; ”RIGHT” ) ; ”F” ) ;

appFrame . getContentPane ( ) . add ( myPanel , ” South ” ) ; appFrame . s e t V i s i b l e ( true ) ; } private static Boolean endgame ; private static Vector< Vector< BufferedImage > > backgroundKI ; private static Vector< Vector< BufferedImage > > backgroundTC ; private static Vector< Vector< Vector< ImageObject > > > wallsKI ; private static Vector< Vector< Vector< ImageObject > > > wallsTC ; private static int xdimKI ; private static int ydimKI ;

148

Foundations of Videogame Programming Code Repository private static int xdimTC ; private static int ydimTC ; private private private private private private private private private

static static static static static static static static static

BufferedImage player ; Vector< BufferedImage > l i n k ; BufferedImage l e f t H e a r t O u t l i n e ; BufferedImage rightHeartOutline ; BufferedImage l e f t H e a r t ; BufferedImage rightHeart ; Vector< BufferedImage > bluepigEnemy ; Vector< ImageObject > bluepigEnemies ; Vector< ImageObject > bubblebossEnemies ;

private static ImageObject doorKItoTC ; private static ImageObject doorTCtoKI ; private private private private private private private

static static static static static static static

Boolean upPressed ; Boolean downPressed ; Boolean l e f t P r e s s e d ; Boolean rightPressed ; Boolean aPressed ; Boolean xPressed ; double lastPressed ;

private private private private private private

static static static static static static

ImageObject p1 ; double p1width ; double p1height ; double p1originalX ; double p1originalY ; double p 1 v e l o c i t y ;

private static int l e v e l ; private static Long a u d i o l i f e t i m e ; private static Long lastAudioStart ; private static Clip c l i p ; private static Long d r o p L i f e L i f e t i m e ; private static Long lastDropLife ; private private private private

static static static static

int int int int

XOFFSET; YOFFSET; WINWIDTH; WINHEIGHT;

private static double pi ; private static double quarterPi ;

Zelda private private private private private private

static static static static static static

double double double double double double

halfPi ; threequartersPi ; fivequartersPi ; threehalvesPi ; sevenquartersPi ; twoPi ;

private static JFrame appFrame ; private static String backgroundState ; private static Boolean availableToDropLife ; private static f i n a l int IFW = JComponent .WHEN IN FOCUSED WINDOW; }

149

150

Foundations of Videogame Programming Code Repository

Part III 2.5-D Videogames

151

Chapter 7 Image Transformations

import import import import import

ij ij ij ij ij

.∗; . process . ∗ ; . gui . ∗ ; . plugin . ∗ ; . plugin . frame . ∗ ;

import java . u t i l . Vector ; import java . awt . Color ; public class Transforms CSC313 implements PlugIn { private static int decFromHex ( int r , int g , int b ) { int r e t = 0; r e t = ( r > s p l i t C o l o r s ( ImagePlus img ) { Vector< Vector< Vector< I n t e g e r > > > c o l o r s = new Vector< Vector< Vector< I n t e g e r > > >() ; ColorProcessor cp = new ColorProcessor ( img . getBufferedImage ( ) ) ; Color myColor = new Color ( 0 , 0 , 0) ; for ( int i = 0; i < img . getHeight ( ) ; i ++ ) { 153

154

Foundations of Videogame Programming Code Repository Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > >() ; for ( int j = 0; j < img . getWidth ( ) ; j ++ ) { myColor = cp . getColor ( j , i ) ; Vector< I n t e g e r > tempColor = new Vector ( ) ; tempColor . addElement ( myColor . getRed ( ) ) ; tempColor . addElement ( myColor . getGreen ( ) ) ; tempColor . addElement ( myColor . getBlue ( ) ) ; tempRow . addElement ( tempColor ) ; } c o l o r s . addElement ( tempRow ) ; } return c o l o r s ;

}

private static void writeToImage ( Vector< Vector< Vector< I n t e g e r > > > inputImage , ImagePlus img , ImageProcessor imp ) { for ( int i = 0; i < inputImage . s i z e ( ) ; i ++ ) { for ( int j = 0; j < inputImage . elementAt ( i ) . s i z e ( ) ; j ++ ) { int c o l o r = decFromHex ( inputImage . elementAt ( i ) . elementAt ( j ) . elementAt ( 0 ) , inputImage . elementAt ( i ) . elementAt ( j ) . elementAt ( 1 ) , inputImage . elementAt ( i ) . elementAt ( j ) . elementAt ( 2 ) ) ; imp . putPixel ( j , i , c o l o r ) ; } } img . updateAndDraw ( ) ; } private static Vector< Vector< Vector< I n t e g e r > > > transform ( Vector< Vector< Vector< I n t e g e r > > > inputImage , String transformName , int Tx , int Ty , int Sx , int Sy , double angle ) { Vector< Vector< Vector< I n t e g e r > > > r e t = new Vector< Vector< Vector< I n t e g e r > > >() ; for ( int i = 0; i < inputImage . s i z e ( ) ; i ++ ) {

Image Transformations

155

Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > > () ; for ( int j = 0; j < inputImage . elementAt ( 0 ) . s i z e ( ) ; j ++ ) { Vector< I n t e g e r > tempRGB = new Vector< I n t e g e r >() ; tempRGB. addElement ( 0 ) ; tempRGB. addElement ( 0 ) ; tempRGB. addElement ( 0 ) ; tempRow . addElement ( tempRGB ) ; } r e t . addElement ( tempRow ) ; } for ( int i = 0; i < inputImage . s i z e ( ) ; i ++ ) { for ( int j = 0; j < inputImage . elementAt ( i ) . s i z e ( ) ; j ++ ) { int newI = 0; int newJ = 0; i f ( transformName . substring ( 0 , 5 ) . equals ( ” Trans ” ) ) { newI = i + Ty ; newJ = j + Tx ; } i f ( transformName . substring ( 0 , 5 ) . equals ( ” Rotat ” ) ) { newI = ( int ) ( 0 . 5 + ( double ) j ∗ Math . sin ( angle ) + ( double ) i ∗ Math . cos ( angle ) ) ; newJ = ( int ) ( 0 . 5 + ( double ) j ∗ Math . cos ( angle ) − ( double ) i ∗ Math . sin ( angle ) ) ; } i f ( transformName . substring ( 0 , 5 ) . equals ( ”Compo” ) ) { //System . out . p r i n t l n ( ”Compo” ) ; newI = ( int ) ( 0 . 5 + ( ( double ) j − ( double ) Tx ) ∗ Math . sin ( angle ) + ( ( double ) i − ( double ) Ty ) ∗Math . cos ( angle ) + ( double ) Ty ) ; newJ = ( int ) ( 0 . 5 + ( ( double ) j − ( double ) Tx ) ∗ Math . cos ( angle ) + ( ( double ) i − ( double ) Ty ) ∗ −1.0 ∗ Math . sin ( angle ) + ( double ) Tx ) ; } i f ( transformName . substring ( 0 , 5 ) . equals ( ” Scale ” ) ) { newI = ( int ) ( 0 . 5 + ( double ) Sy ∗ ( double ) i ) ; newJ = ( int ) ( 0 . 5 + ( double ) Sx ∗ ( double ) j ) ;

156

Foundations of Videogame Programming Code Repository } i f ( transformName . substring ( 0 , 5 ) . equals ( ”SheaX” ) ) { newI = i ; newJ = j + Sx ∗ i ; } i f ( newI >= 0 && newI < inputImage . s i z e ( ) ) { i f ( newJ >= 0 && newJ < inputImage . elementAt ( 0 ) . s i z e ( ) ) { r e t . elementAt ( i ) . elementAt ( j ) . set ( 0 , inputImage . elementAt ( newI ) . elementAt ( newJ ) . elementAt ( 0 ) ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 1 , inputImage . elementAt ( newI ) . elementAt ( newJ ) . elementAt ( 1 ) ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 2 , inputImage . elementAt ( newI ) . elementAt ( newJ ) . elementAt ( 2 ) ) ; } } } } return r e t ;

}

public void run ( String arg ) { ImagePlus img = IJ . getImage ( ) ; ImageProcessor imp = img . getProcessor ( ) ; Vector< Vector< Vector< I n t e g e r > > > c o l o r s = s p l i t C o l o r s ( img ) ; String transformName = ”SheaX” ; int Tx = c o l o r s . elementAt ( 0 ) . s i z e ( ) / 2;//125; int Ty = c o l o r s . s i z e ( ) / 2;//230; int Sx = 10; int Sy = 150; double angle = 1 . 2 ; for ( int i = 0; i < 1000; i ++ ) { Vector< Vector< Vector< I n t e g e r > > > transformedImage = transform ( colors , transformName , Tx , Ty , ( int ) ( 0 . 5 + ( double ) Sx ∗ ( double ) i / 1000.0) , ( int ) ( 0 . 5 + ( double ) Sy ∗ ( double ) i /1000.0) , angle ∗ ( double ) i / 1000.0 ) ; writeToImage ( transformedImage , img , imp ) ;

Image Transformations try { Thread . sleep ( 32 ) ; } catch ( Exception e ) { } } } }

157

158

Foundations of Videogame Programming Code Repository

Chapter 8 Formula One

// FormulaOne . java Copyright ( C ) 2020 Ben Sanders import java . u t i l . Vector ; import java . u t i l .Random; import java . time . LocalTime ; import java . time . temporal . ChronoUnit ; import import import import import import import

javax . swing . JFrame ; javax . swing . JPanel ; javax . swing . JButton ; javax . swing . JComponent ; javax . swing . KeyStroke ; javax . swing . AbstractAction ; javax . swing .JComboBox;

import javax . imageio . ImageIO ; import java . awt . event . ActionEvent ; import java . awt . event . ActionListener ; import java . awt . image . BufferedImage ; import java . i o . F i l e ; import java . i o . IOException ; import java . awt . Graphics ; import java . awt . Graphics2D ; import import import import

java . awt . geom . AffineTransform ; java . awt . image . AffineTransformOp ; java . awt . Polygon ; java . awt . Color ;

159

160

Foundations of Videogame Programming Code Repository

public class FormulaOne { public FormulaOne ( ) { setup ( ) ; } public static void setup ( ) { appFrame = new JFrame ( ” Formula 1” ) ; XOFFSET = 0; YOFFSET = 40; WINWIDTH = 500; WINHEIGHT = 500; pi = 3.14159265358979; quarterPi = 0.25 ∗ pi ; h a l f P i = 0.5 ∗ pi ; threequartersPi = 0.75 ∗ pi ; f i v e q u a r t e r s P i = 1.25 ∗ pi ; threehalvesPi = 1.5 ∗ pi ; sevenquartersPi = 1.75 ∗ pi ; twoPi = 2.0 ∗ pi ; endgame = f a l s e ; p1width = 228; p1height = 228; c o c k p i t S h i f t = 350; p1originalX = ( double )XOFFSET + ( ( double )WINWIDTH / 2 . 0 ) − ( p1width / 2.0) ; p1originalY = ( double )YOFFSET + ( double ) c o c k p i t S h i f t ; trackMatrix = new Vector< Vector< Vector< I n t e g e r > > >() ; try { background = ImageIO . read ( new F i l e ( ” MiamiTransparent . png” ) ) ; // ” DresdenSmall . png” ) ) ; player = ImageIO . read ( new F i l e ( ” steeringWheelSmall . png” ) ) ; cockpit = ImageIO . read ( new F i l e ( ” cockpit . png” ) ) ; track = ImageIO . read ( new F i l e ( ” trackchessboard . png” ) ) ; perspectiveTrack = convertToARGB ( ImageIO . read ( new F i l e ( ” perspectiveTrack . png” ) ) ) ; } catch ( IOException i o e ) {

Formula One

} } private static BufferedImage convertToARGB ( BufferedImage input ) { BufferedImage r e t = new BufferedImage ( input . getWidth ( ) , input . getHeight ( ) , BufferedImage . TYPE INT ARGB ) ; Graphics2D g = r e t . createGraphics ( ) ; g . drawImage ( input , 0 , 0 , null ) ; g . dispose ( ) ; return r e t ; } private static class Animate implements Runnable { public void run ( ) { while ( endgame == f a l s e ) { backgroundDraw ( ) ; trackDraw ( ) ; playerDraw ( ) ; try { Thread . sleep ( 3 2 ) ; } catch ( InterruptedException e ) { } } } } private static class PlayerMover implements Runnable { public PlayerMover ( ) { v e l o c i t y s t e p = 0.01; r o t a t e s t e p = 0.02; } public void run ( ) { while ( endgame == f a l s e )

161

162

Foundations of Videogame Programming Code Repository { try { Thread . sleep ( 10 ) ; } catch ( InterruptedException e ) { } i f ( upPressed == true ) { p1velocity = p1velocity + velocitystep ; } i f ( downPressed == true ) { p1velocity = p1velocity − velocitystep ; } i f ( l e f t P r e s s e d == true ) { i f ( p1velocity < 0 ) { p1 . r o t a t e ( −r o t a t e s t e p ) ; } else { p1 . r o t a t e ( r o t a t e s t e p ) ; } } i f ( rightPressed == true ) { i f ( p1velocity < 0 ) { p1 . r o t a t e ( r o t a t e s t e p ) ; } else { p1 . r o t a t e ( −r o t a t e s t e p ) ; } } } } private double v e l o c i t y s t e p ; private double r o t a t e s t e p ;

}

Formula One

163

private static int constrainToCap ( int position , int d i f f e r e n t i a l , int cap ) { int r e t = d i f f e r e n t i a l ; while ( p o s i t i o n + r e t < 0 ) { r e t = r e t + cap ; } while ( p o s i t i o n + r e t >= cap ) { r e t = r e t − cap ; } // r e t = ( p o s i t i o n + r e t ) % cap ; return r e t ; } private static class CameraMover implements Runnable { public CameraMover ( ) { } public void run ( ) { while ( endgame == f a l s e ) { try { Thread . sleep ( 10 ) ; } catch ( Exception e ) { } // TODO: why does the v e l o c i t y not impact the movement u n t i l about +1.5? int sumx 2.0 ) int sumy 2.0 )

= + = +

( int ) ( −p 1 v e l o c i t y ∗ Math . cos ( p1 . getAngle ( ) − pi / 0.5 ) ; ( int ) ( p 1 v e l o c i t y ∗ Math . sin ( p1 . getAngle ( ) − pi / 0.5) ;

camerax = camerax + constrainToCap ( camerax , sumx, trackMatrix . elementAt ( 0 ) . s i z e ( ) ) ;

164

Foundations of Videogame Programming Code Repository cameray = cameray + constrainToCap ( cameray , sumy, trackMatrix . size ( ) ) ; } }

} private static Vector< Vector< Vector< I n t e g e r > > > s p l i t C o l o r s ( BufferedImage input ) { Vector< Vector< Vector< I n t e g e r > > > r e t = new Vector< Vector< Vector< I n t e g e r > > >() ; for ( int i = 0; i < input . getWidth ( ) ; i ++ ) { Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > >() ; for ( int j = 0; j < input . getHeight ( ) ; j ++ ) { Vector temp = new Vector ( ) ; int rgb = input . getRGB ( i , j ) ; int r = ( rgb >> 16) & 0x000000FF ; int g = ( rgb >> 8) & 0x000000FF ; int b = rgb & 0x000000FF ; temp . addElement ( r temp . addElement ( g temp . addElement ( b tempRow . addElement (

); ); ); temp ) ;

} r e t . addElement ( tempRow ) ; } return r e t ; } private static void setupTrack ( ) { trackMatrix = s p l i t C o l o r s ( track ) ; } private static AffineTransformOp rotateImageObject ( ImageObject obj ) { AffineTransform at = AffineTransform . getRotateInstance ( −obj . getAngle ( ) , obj . getWidth ( ) /2.0 , obj . getHeight ( ) /2.0 ) ; AffineTransformOp atop = new AffineTransformOp ( at , AffineTransformOp . TYPE BILINEAR ) ; return atop ;

Formula One

165

} private static AffineTransformOp spinImageObject ( ImageObject obj ) { AffineTransform at = AffineTransform . getRotateInstance ( −obj . getInternalAngle ( ) , obj . getWidth ( ) /2.0 , obj . getHeight ( ) /2.0 ) ; AffineTransformOp atop = new AffineTransformOp ( at , AffineTransformOp . TYPE BILINEAR ) ; return atop ; } private static void backgroundDraw ( ) { Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; int x s h i f t = XOFFSET + ( int ) ( ( p1 . getAngle ( ) / twoPi ) ∗ new Double ( background . getWidth ( ) ) + 0.5 ) ; g2D . drawImage ( background , x s h i f t , YOFFSET, null ) ; g2D . drawImage ( background , x s h i f t − background . getWidth ( ) , YOFFSET, null ) ; g2D . drawImage ( cockpit , XOFFSET, cockpitShift , null ) ; g2D . drawImage ( rotateImageObject ( p1 ) . f i l t e r ( player , null ) , ( int ) ( p1 . getX ( ) + 0 . 5 ) , ( int ) ( p1 . getY ( ) + 0 . 5 ) , null ) ; } private static Vector< Vector< Vector< I n t e g e r > > > perspectiveFromRectangle ( Vector< Vector< Vector< I n t e g e r > > > inputGrid , int base ) { Vector< Vector < Vector< I n t e g e r > > > r e t = new Vector< Vector< Vector< I n t e g e r > > > ( ) ; // a l l o c a t e space f o r r e t for ( int i = 0; i < inputGrid . s i z e ( ) ; i ++ ) { Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > >() ; for ( int j = 0; j < inputGrid . elementAt ( i ) . s i z e ( ) ; j ++ ) { Vector< I n t e g e r > tempRGB = new Vector< I n t e g e r >() ; tempRGB. addElement ( 0 ) ; tempRGB. addElement ( 0 ) ; tempRGB. addElement ( 0 ) ; tempRow . addElement ( tempRGB ) ; }

166

Foundations of Videogame Programming Code Repository r e t . addElement ( tempRow ) ; } // c o l l a p s e rows from inputGrid i n t o r e t for ( int i = 0; i < inputGrid . s i z e ( ) ; i ++ ) { for ( int j = 0; j < inputGrid . elementAt ( i ) . s i z e ( ) ; j ++ ) { double xdim = ( double ) inputGrid . elementAt ( i ) . s i z e ( ) ; double ydim = ( double ) inputGrid . s i z e ( ) ; double width = xdim − ( ( double ) i / ( ydim − 1 . 0 ) ) ∗ ( xdim − ( double ) base ) ; double s t e p s i z e = width / xdim ; double o f f s e t = ( xdim − width ) /2.0; int indexi = i ; int indexj = ( int ) ( 0 . 5 + o f f s e t + ( double ) j ∗ s t e p s i z e ) ; //System . out . p r i n t l n ( ” i : ” + i n d e x i + ” , j : ” + i n d e x j ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 0 , inputGrid . elementAt ( indexi ) . elementAt ( indexj ) . elementAt ( 0 ) ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 1 , inputGrid . elementAt ( indexi ) . elementAt ( indexj ) . elementAt ( 1 ) ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 2 , inputGrid . elementAt ( indexi ) . elementAt ( indexj ) . elementAt ( 2 ) ) ; } } return r e t ;

} private static Vector< Vector< Vector< I n t e g e r > > > rotateImage ( Vector < Vector< Vector< I n t e g e r > > > inputImg , double angle , double xpos , double ypos , boolean repeatImg ) { Vector< Vector< Vector< I n t e g e r > > > r e t = new Vector< Vector< Vector< I n t e g e r > > >() ; for ( int i = 0; i < inputImg . s i z e ( ) ; i ++ ) { Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > >() ; for ( int j = 0; j < inputImg . elementAt ( i ) . s i z e ( ) ; j ++ ) { Vector< I n t e g e r > tempPixel = new Vector< I n t e g e r >() ; for ( int k = 0; k < inputImg . elementAt ( i ) . elementAt ( j ) . s i z e ( ) ; k++ ) {

Formula One tempPixel . addElement ( 0 ) ; } tempRow . addElement ( tempPixel ) ; } r e t . addElement ( tempRow ) ; } for ( int i = 0; i < inputImg . s i z e ( ) ; i ++ ) { for ( int j = 0; j < inputImg . elementAt ( i ) . s i z e ( ) ; j ++ ) { int newj = ( int ) ( 0 . 5 + xpos + ( ( double ) j − xpos ) ∗ Math . cos ( angle ) − ( ( double ) i − ypos ) ∗ Math . sin ( angle ) ) ; int newi = ( int ) ( 0 . 5 + ypos + ( ( double ) j − xpos ) ∗ Math . sin ( angle ) + ( ( double ) i − ypos ) ∗ Math . cos ( angle ) ) ; i f ( repeatImg ) { while ( newj >= r e t . elementAt ( 0 ) . s i z e ( ) ) { newj = newj − r e t . elementAt ( 0 ) . s i z e ( ) ; } while ( newj < 0 ) { newj = newj + r e t . elementAt ( 0 ) . s i z e ( ) ; } while ( newi >= r e t . s i z e ( ) ) { newi = newi − r e t . s i z e ( ) ; } while ( newi < 0 ) { newi = newi + r e t . s i z e ( ) ; } } i f ( newj < r e t . elementAt ( 0 ) . s i z e ( ) && newj >= 0 ) { i f ( newi < r e t . s i z e ( ) && newi >= 0 ) { r e t . elementAt ( newi ) . elementAt ( newj ) . set ( 0 , inputImg . elementAt ( i ) . elementAt ( j ) . elementAt ( 0 ) ) ; r e t . elementAt ( newi ) . elementAt ( newj ) . set ( 1 , inputImg . elementAt ( i ) . elementAt ( j ) . elementAt ( 1 ) ) ; r e t . elementAt ( newi ) . elementAt ( newj ) . set ( 2 , inputImg . elementAt ( i ) . elementAt ( j ) . elementAt ( 2 ) ) ; } }

167

168

Foundations of Videogame Programming Code Repository } } return r e t ;

} private static Vector< Vector< Vector< I n t e g e r > > > duplicate3x3 ( Vector< Vector< Vector< I n t e g e r > > > inputImg ) { Vector< Vector< Vector< I n t e g e r > > > r e t = new Vector< Vector< Vector< I n t e g e r > > >() ; for ( int i = 0; i < inputImg . s i z e ( ) ∗ 3; i ++ ) { Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > >() ; for ( int j = 0; j < inputImg . elementAt ( 0 ) . s i z e ( ) ∗ 3; j ++ ) { Vector< I n t e g e r > tempPixel = new Vector< I n t e g e r >() ; tempPixel . addElement ( 0 ) ; tempPixel . addElement ( 0 ) ; tempPixel . addElement ( 0 ) ; tempRow . addElement ( tempPixel ) ; } r e t . addElement ( tempRow ) ; } for ( int i = 0; i < r e t . s i z e ( ) ; i ++ ) { for ( int j = 0; j < r e t . elementAt ( i ) . s i z e ( ) ; j ++ ) { r e t . elementAt ( i ) . elementAt ( j ) . set ( 0 , inputImg . elementAt ( i % inputImg . s i z e ( ) ) . elementAt ( j % inputImg . elementAt ( 0 ) . s i z e ( ) ) . elementAt ( 0 ) ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 1 , inputImg . elementAt ( i % inputImg . s i z e ( ) ) . elementAt ( j % inputImg . elementAt ( 0 ) . s i z e ( ) ) . elementAt ( 1 ) ) ; r e t . elementAt ( i ) . elementAt ( j ) . set ( 2 , inputImg . elementAt ( i % inputImg . s i z e ( ) ) . elementAt ( j % inputImg . elementAt ( 0 ) . s i z e ( ) ) . elementAt ( 2 ) ) ; } } return r e t ; } private static void trackDraw ( ) {

Formula One

169

// use camera ’ s p o s i t i o n , p1 ’ s r o t a t i o n , and trapezoid mapper . int int int int int int

rectWidth = 500;//500; rectHeight = 175;//200; base = 150;//250; x o f f s e t = 0; y o f f s e t = 232; scaledown = 5;

Vector< Vector< Vector< I n t e g e r > > > cameraView = new Vector< Vector < Vector< I n t e g e r > > >() ; for ( int i = 0; i < rectHeight ; i ++ ) { Vector< Vector< I n t e g e r > > tempRow = new Vector< Vector< I n t e g e r > >() ; for ( int j = 0; j < rectWidth ; j ++ ) { Vector< I n t e g e r > tempRGB = new Vector< I n t e g e r >() ; int indexi = cameray − ( rectHeight − i ) ; // % trackMatrix . s i z e () ; int indexj = camerax − ( rectWidth − j + ( int ) ( 0 . 5 + ( ( double ) rectWidth / 2 . 0 ) ) ) ; // % trackMatrix . elementAt ( 0 ) . s i z e ( ) ; while ( indexi < 0 ) { indexi = indexi + trackMatrix . s i z e ( ) ; } while ( trackMatrix . s i z e ( ) > > userView = perspectiveFromRectangle ( cameraView , base ) ; Graphics g = appFrame . getGraphics ( ) ; Graphics2D g2D = ( Graphics2D ) g ; for ( int i = 0; i < rectHeight ; i ++ ) { for ( int j = 0; j < rectWidth ; j ++ ) { int alpha = 255; int red = userView . elementAt ( i ) . elementAt ( j ) . elementAt ( 0 ) ; int green = userView . elementAt ( i ) . elementAt ( j ) . elementAt ( 1 ) ; int blue = userView . elementAt ( i ) . elementAt ( j ) . elementAt ( 2 ) ; while ( red < 0 ) { red = red + 256; } while ( 256 rightEdge ) { moveto ( leftEdge , getY ( ) ) ; } i f ( x < leftEdge ) { moveto ( rightEdge , getY ( ) ) ; } i f ( y > bottomEdge ) { moveto ( getX ( ) , topEdge ) ; } i f ( y < topEdge ) { moveto ( getX ( ) , bottomEdge ) ; } } public void r o t a t e ( double angleinput ) { angle = angle + angleinput ; while ( angle > twoPi ) { angle = angle − twoPi ; } while ( angle < 0 ) { angle = angle + twoPi ; } } public void spin ( double internalangleinput ) { internalangle = internalangle + internalangleinput ; while ( internalangle > twoPi ) { internalangle = internalangle − twoPi ; } while ( internalangle < 0 ) { internalangle = internalangle + twoPi ; } }

177

178

Foundations of Videogame Programming Code Repository private private private private private private private private private private

double x ; double y ; double xwidth ; double yheight ; double angle ; // i n Radians double internalangle ; // i n Radians Vector coords ; Vector t r i a n g l e s ; double comX; double comY;

} private static void bindKey ( JPanel myPanel , String input ) { myPanel . getInputMap ( IFW ) . put ( KeyStroke . getKeyStroke ( ” pressed ” + input ) , input + ” pressed ” ) ; myPanel . getActionMap ( ) . put ( input + ” pressed ” , new KeyPressed ( input ) ); myPanel . getInputMap ( IFW ) . put ( KeyStroke . getKeyStroke ( ” released ” + input ) , input + ” released ” ) ; myPanel . getActionMap ( ) . put ( input + ” released ” , new KeyReleased ( input ) ) ; }

public static void main ( String [ ] args ) { setup ( ) ; appFrame . setDefaultCloseOperation ( JFrame . EXIT ON CLOSE ) ; appFrame . s e t S i z e (501 ,585) ; JPanel myPanel = new JPanel ( ) ; JButton newGameButton = new JButton ( ”New Game” ) ; newGameButton . addActionListener ( new StartGame ( ) ) ; myPanel . add ( newGameButton ) ; JButton quitButton = new JButton ( ” Quit Game” ) ; quitButton . addActionListener ( new QuitGame ( ) ) ; myPanel . add ( quitButton ) ; bindKey ( bindKey ( bindKey ( bindKey ( bindKey (

myPanel , myPanel , myPanel , myPanel , myPanel ,

”UP” ) ; ”DOWN” ) ; ”LEFT” ) ; ”RIGHT” ) ; ”F” ) ;

Formula One

appFrame . getContentPane ( ) . add ( myPanel , ” South ” ) ; appFrame . s e t V i s i b l e ( true ) ; } private private private private private private private

static static static static static static static

Boolean endgame ; BufferedImage background ; BufferedImage player ; BufferedImage cockpit ; BufferedImage track ; BufferedImage perspectiveTrack ; Vector trackMatrix ;

private static int camerax ; private static int cameray ; private static int c o c k p i t S h i f t ; private private private private

static static static static

Boolean Boolean Boolean Boolean

private private private private private private

static static static static static static

ImageObject p1 ; double p1width ; double p1height ; double p1originalX ; double p1originalY ; double p 1 v e l o c i t y ;

private private private private

static static static static

int int int int

private private private private private private private private

static static static static static static static static

double double double double double double double double

upPressed ; downPressed ; leftPressed ; rightPressed ;

XOFFSET; YOFFSET; WINWIDTH; WINHEIGHT; pi ; quarterPi ; halfPi ; threequartersPi ; fivequartersPi ; threehalvesPi ; sevenquartersPi ; twoPi ;

private static JFrame appFrame ; private static f i n a l int IFW = JComponent .WHEN IN FOCUSED WINDOW;

179

180 }

Foundations of Videogame Programming Code Repository

Part IV 3D Videogames

181

Chapter 9 Sauerbraten

Not Included • Makefile • enet/* • fpsgame/* • include/* • lib/* • lib64/* • rpggame/* • shared/* • vcpp/* • xcode/*

readme source Sauerbraten source code license, usage, and documentation. You may use the Sauerbraten source code if you abide by the ZLIB license http://www.opensource.org/licenses/zlib-license.php (very similar to the BSD license): 183

184

Foundations of Videogame Programming Code Repository

License Sauerbraten game engine source code, any release. Copyright (C) 2001-2012 Wouter van Oortmerssen, Lee Salzman, Mike Dysart, Robert Pointon, and Quinton Reeves This software is provided ’as-is’, without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.

License Notes The license covers the source code found in the ”src” directory of this archive as well as the .cfg files under the ”data” directory. The included ENet network library which Sauerbraten uses is covered by an MIT-style license, which is however compatible with the above license for all practical purposes. Game media included in the game (maps, textures, sounds, models etc.) are NOT covered by this license, and may have individual copyrights and distribution restrictions (see individual readmes).

Usage Compiling the sources should be straight forward. Unix users need to make sure to have the development version of all libs installed (OpenGL, SDL, SDL mixer, SDL image, zlib). The included Makefile can be used to build. Windows users can use the included Visual Studio project files in the vcpp directory, which references the lib/include directories for the external libraries and should thus be self contained. Release mode builds will place executables in the bin dir ready for testing and distribution. An alternative to Visual Studio for Windows is MinGW/MSYS, which can be compiled using the provided Makefile. Another alternative for Windows is to compile under Code::Blocks with the provided vcpp/sauerbraten.cbp project file. The Sauerbraten sources are very small, compact, and non-redundant, so anyone wishing to modify the source code should be able to gain an overview of Sauerbraten’s inner workings by simply reading through the source code in its entirety. Small amounts of comments should guide you through the more tricky sections. When reading the source code and trying to understand Sauerbaten’s internal design, keep in mind the goal of Cube: minimalism. I wanted to create a very complete game / game engine with absolutely minimal means, and made a sport out of it keeping the implementation

engine

185

small and simple. Sauerbraten is not a commercial product, it is merely the author’s idea of a fun little programming project.

Authors • Wouter “Aardappel” van Oortmerssen http://strlen.com • Lee “eihrul” Salzman http://lee.fov120.com • Mike “Gilt” Dysart • Robert “baby-rabbit” Pointon http://www.fernlightning.com • Quinton “Quin” Reeves http://www.redeclipse.net For additional authors/contributors, see the Sauerbraten binary distribution readme.

engine engine/3dgui.cpp // creates multiple gui windows that f l o a t inside the 3d world // s p e c i a l feature i s that i t s mostly ∗modeless∗: you can use t h i s menu while playing , without turning menus on or o f f // implementationwise , i t i s ∗s t a t e l e s s ∗: i t keeps no i n t e r n a l gui structure , h i t t e s t s are instant , usage & implementation i s greatly simplified

{ struct l i s t { i n t parent , w, h , springs , curspring , column ; }; int f i r s t l i s t , nextlist ; i n t columns [MAXCOLUMNS] ;

#include ” engine . h” #include ” t e x t e d i t . h” s t a t i c bool layoutpass , actionon = f a l s e ; s t a t i c i n t mousebuttons = 0; s t a t i c struct gui ∗windowhit = NULL; static float firstx , firsty ;

static static static static

vector l i s t s ; f l o a t hitx , h i t y ; i n t curdepth , c u r l i s t , xsize , ysize , curx , cury ; bool shouldmergehits , shouldautotab ;

s t a t i c void r e s e t ( ) { l i s t s . setsize (0) ; }

enum {FIELDCOMMIT, FIELDABORT, FIELDEDIT , FIELDSHOW, FIELDKEY}; s t a t i c i n t fieldmode = FIELDSHOW; s t a t i c bool f i e l d s a c t i v e = f a l s e ; s t a t i c bool hascursor ; s t a t i c f l o a t cursorx = 0.5 f , cursory = 0.5 f ; #define #define #define #define #define #define #define

SHADOW 4 ICON SIZE (FONTH−SHADOW) SKIN W 256 SKIN H 128 SKIN SCALE 4 INSERT (3∗SKIN SCALE ) MAXCOLUMNS 16

s t a t i c i n t ty , tx , tpos , ∗tcurrent , t c o l o r ; //tracking tab s i z e and p o s i t i o n since uses d i f f e r e n t layout method . . . void allowautotab ( bool on ) { shouldautotab = on ; }

VARP( guiautotab , 6 , 16, 40) ; VARP( guiclicktab , 0 , 0 , 1) ; VARP( guifadein , 0 , 1 , 1) ;

void autotab ( ) { i f ( tcurrent ) { i f ( layoutpass && ! tpos ) tcurrent = NULL; //disable tabs because you didn ’ t s t a r t with one i f ( shouldautotab && ! curdepth && ( layoutpass ? 0 : cury ) + y s i z e > guiautotab∗FONTH) tab (NULL, t c o l o r ) ; } }

struct gui : g3d gui

bool shouldtab ( )

186

Foundations of Videogame Programming Code Repository

{ i f ( tcurrent && shouldautotab ) { i f ( layoutpass ) { i n t space = guiautotab∗FONTH − y s i z e ; i f ( space < 0) return true ; i n t l = l i s t s [ c u r l i s t ] . parent ; while ( l >= 0) { space −= l i s t s [ l ] . h ; i f ( space < 0) return true ; l = l i s t s [ l ] . parent ; } } else { i n t space = guiautotab∗FONTH − cury ; i f ( y s i z e > space ) return true ; i n t l = l i s t s [ c u r l i s t ] . parent ; while ( l >= 0) { i f ( l i s t s [ l ] . h > space ) return true ; l = l i s t s [ l ] . parent ; } } } return f a l s e ; } bool v i s i b l e ( ) { return ( ! tcurrent || tpos==∗tcurrent ) && ! layoutpass ; } //tab i s always at top o f page void tab ( const char ∗name, i n t c o l o r ) { i f ( curdepth ! = 0) return ; i f ( color ) tcolor = color ; tpos ++; i f ( ! name) name = i n t s t r ( tpos ) ; i n t w = max( text width (name) − 2∗INSERT, 0) ; i f ( layoutpass ) { ty = max( ty , y s i z e ) ; y s i z e = 0; } else { cury = −y s i z e ; i n t h = FONTH−2∗INSERT, x1 = curx + tx , x2 = x1 + w + ( ( skinx[3]−skinx [ 2 ] ) + ( skinx[5]−skinx [ 4 ] ) )∗ SKIN SCALE , y1 = cury − ( ( skiny[6]−skiny [ 1 ] )−(skiny[3]−skiny [ 2 ] ) )∗ SKIN SCALE−h, y2 = cury ; bool h i t = tcurrent && windowhit== t h i s && hitx>=x1 && hity>=y1 && hitx= l i s t s . length ( ) ) // should never get here unless

s c r i p t code doesn ’ t use same amount o f l i s t s in layout and render passes { l i s t &l = l i s t s . add ( ) ; l . parent = c u r l i s t ; l . springs = 0; l . column = −1; l .w = l . h = 0; } l i s t &l = l i s t s [ c u r l i s t ] ; l . curspring = 0; i f ( l . springs > 0) { i f ( i s h o r i z o n t a l ( ) ) x s i z e = l .w; e l s e y s i z e = l . h ; } else { x s i z e = l .w; ysize = l .h; } } curdepth ++; } void p o p l i s t ( ) { i f ( ! l i s t s . inrange ( c u r l i s t ) ) return ; l i s t &l = l i s t s [ c u r l i s t ] ; i f ( layoutpass ) { l .w = x s i z e ; l .h = ysize ; i f ( l . column >= 0) columns [ l . column ] = max( columns [ l . column ] , ishorizontal ( ) ? ysize : xsize ) ; } c u r l i s t = l . parent ; curdepth−−; i f ( l i s t s . inrange ( c u r l i s t ) ) { i n t w = xsize , h = y s i z e ; i f ( i s h o r i z o n t a l ( ) ) cury −= h ; e l s e curx −= w; l i s t &p = l i s t s [ c u r l i s t ] ; x s i z e = p .w; ysize = p.h; i f ( ! layoutpass && p . springs > 0) { l i s t &s = l i s t s [ p . parent ] ; i f ( i s h o r i z o n t a l ( ) ) x s i z e = s .w; e l s e y s i z e = s . h ; } layout (w, h ) ; } } i n t t e x t ( const char return button i n t button ( const char return button i n t t i t l e ( const char return button

∗text , ( text , ∗text , ( text , ∗text , ( text ,

i n t color , const char ∗icon ) { autotab ( ) ; color , icon , f a l s e , f a l s e ) ; } i n t color , const char ∗icon ) { autotab ( ) ; color , icon , true , f a l s e ) ; } i n t color , const char ∗icon ) { autotab ( ) ; color , icon , f a l s e , true ) ; }

void separator ( ) { autotab ( ) ; l i n e (FONTH/3) ; } void progress ( f l o a t percent ) { autotab ( ) ; l i n e ( ( FONTH∗4)/5 , percent ) ; } //use to set min s i z e ( useful when you have progress bars ) void s t r u t ( f l o a t s i z e ) { layout ( i s v e r t i c a l ( ) ? i n t ( s i z e∗FONTW) : 0 , i s v e r t i c a l ( ) ? 0 : i n t ( s i z e∗FONTH) ) ; } //add space between l i s t items void space ( f l o a t s i z e ) { layout ( i s v e r t i c a l ( ) ? 0 : i n t ( s i z e∗FONTW) , i s v e r t i c a l ( ) ? i n t ( s i z e∗FONTH) : 0) ; } void spring ( i n t weight ) { i f ( c u r l i s t < 0) return ; l i s t &l = l i s t s [ c u r l i s t ] ; i f ( layoutpass ) { i f ( l . parent >= 0) l . springs += weight ; return ; } i n t nextspring = min ( l . curspring + weight , l . springs ) ; i f ( nextspring = MAXCOLUMNS)

engine/3dgui.cpp return ; l i s t &l = l i s t s [ c u r l i s t ] ; l . column = c o l ;

glDisable ( GL SCISSOR TEST ) ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ; i f ( overlaid ) { i f ( hit ) { glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; glBlendFunc ( GL ZERO, GL SRC COLOR) ; g l C o l o r 3 f ( 1 , 0.5 f , 0.5 f ) ; r e c t ( xi , yi , xs , ys , −1) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } i f ( ! overlaytex ) overlaytex = textureload ( ” data/guioverlay . png ” , 3) ; glColor3fv ( l i g h t . v ) ; glBindTexture ( GL TEXTURE 2D, overlaytex−>id ) ; r e c t ( xi , yi , xs , ys , 0) ; }

} i n t layout ( i n t w, i n t h ) { i f ( layoutpass ) { i f ( ishorizontal ( ) ) { x s i z e += w; y s i z e = max( ysize , h ) ; } else { x s i z e = max( xsize , w) ; y s i z e += h ; } return 0; } else { bool h i t = i s h i t (w, h ) ; i f ( i s h o r i z o n t a l ( ) ) curx += w; e l s e cury += h ; return ( h i t && v i s i b l e ( ) ) ? mousebuttons|G3D ROLLOVER : 0; } }

} return layout ( s i z e +SHADOW, s i z e +SHADOW) ; } i n t modelpreview ( const char ∗name, i n t anim , f l o a t s i z e s c a l e , bool overlaid ) { autotab ( ) ; i f ( s i z e s c a l e ==0) s i z e s c a l e = 1; i n t s i z e = ( i n t ) ( s i z e s c a l e∗2∗FONTH)−SHADOW; i f ( visible ( ) ) { bool h i t = i s h i t ( s i z e +SHADOW, s i z e +SHADOW) ; f l o a t xs = size , ys = size , x i = curx , y i = cury ; i f ( o v e r l a i d && h i t && actionon ) { glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; g l C o l o r 4 f ( 0 , 0 , 0 , 0.75 f ) ; r e c t ( x i +SHADOW, y i +SHADOW, xs , ys , −1) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } i n t x1 = i n t ( f l o o r ( screen−>w∗( x i∗scale . x+ o r i g i n . x ) ) ) , y1 = i n t ( f l o o r ( screen−>h∗(1 − ( ( y i +ys )∗scale . y+ o r i g i n . y ) ) ) ) , x2 = i n t ( c e i l ( screen−>w∗ ( ( x i +xs )∗scale . x+ o r i g i n . x ) ) ) , y2 = i n t ( c e i l ( screen−>h∗(1 − ( y i∗scale . y+ o r i g i n . y ) ) ) ) ; glViewport ( x1 , y1 , x2−x1 , y2−y1 ) ; g l S c i s s o r ( x1 , y1 , x2−x1 , y2−y1 ) ; glEnable ( GL SCISSOR TEST ) ; glDisable (GL BLEND) ; modelpreview : : s t a r t ( o v e r l a i d ) ; model ∗m = loadmodel (name) ; i f (m) { entitylight light ; l i g h t . c o l o r = vec ( 1 , 1 , 1) ; l i g h t . d i r = vec ( 0 , −1, 2) . normalize ( ) ; vec center , radius ; m−>boundbox ( 0 , center , radius ) ; f l o a t d i s t = 2.0 f∗max( radius . magnitude2 ( ) , 1.1 f∗radius . z ) , yaw = fmod ( l a s t m i l l i s /10000.0 f ∗360.0 f , 360.0 f ) ; vec o(−center . x , d i s t − center . y , −0.1f∗d i s t − center . z ) ; rendermodel(& l i g h t , name, anim , o , yaw , 0 , 0 , NULL, NULL, 0) ; } modelpreview : : end ( ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glEnable (GL BLEND) ; glDisable ( GL SCISSOR TEST ) ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ; i f ( overlaid ) { i f ( hit ) { glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; glBlendFunc ( GL ZERO, GL SRC COLOR) ; g l C o l o r 3 f ( 1 , 0.5 f , 0.5 f ) ; r e c t ( xi , yi , xs , ys , −1) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } i f ( ! overlaytex ) overlaytex = textureload ( ” data/guioverlay . png ” , 3) ; glColor3fv ( l i g h t . v ) ; glBindTexture ( GL TEXTURE 2D, overlaytex−>id ) ; r e c t ( xi , yi , xs , ys , 0) ; } } return layout ( s i z e +SHADOW, s i z e +SHADOW) ;

void mergehits ( bool on ) { shouldmergehits = on ; } bool i s h i t ( i n t w, i n t h , i n t x = curx , i n t y = cury ) { i f ( shouldmergehits ) return windowhit== t h i s && ( i s h o r i z o n t a l ( ) ? hitx>=x && hitx=y && hity=x && hity>=y && hitxset ( ) ; } i n t x1 = i n t ( f l o o r ( screen−>w∗( x i∗scale . x+ o r i g i n . x ) ) ) , y1 f l o o r ( screen−>h∗(1 − ( ( y i +ys )∗scale . y+ o r i g i n . y ) ) ) ) x2 = i n t ( c e i l ( screen−>w∗ ( ( x i +xs )∗scale . x+ o r i g i n . x ) ) ) , i n t ( c e i l ( screen−>h∗(1 − ( y i∗scale . y+ o r i g i n . y ) ) ) glViewport ( x1 , y1 , x2−x1 , y2−y1 ) ; g l S c i s s o r ( x1 , y1 , x2−x1 , y2−y1 ) ; glEnable ( GL SCISSOR TEST ) ; glDisable (GL BLEND) ; modelpreview : : s t a r t ( o v e r l a i d ) ; game : : renderplayerpreview ( model , team , weap ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glEnable (GL BLEND) ; modelpreview : : end ( ) ;

187

= int ( , y2 = );

}

188

Foundations of Videogame Programming Code Repository

void s l i d e r ( i n t &val , i n t vmin , i n t vmax, i n t color , const char ∗l a b e l )

e−>rendered = true ;

{

bool h i t = i s h i t (w, h ) ; i f ( hit ) { i f ( mousebuttons&G3D DOWN) //mouse request focus { i f ( f i e l d t y p e ==FIELDKEY ) e−>c l e a r ( ) ; useeditor ( name, initmode , true ) ; e−>mark ( f a l s e ) ; fieldmode = f i e l d t y p e ; } } bool e d i t i n g = ( fieldmode ! = FIELDSHOW) && ( e==currentfocus ( ) ) ; i f ( h i t && e d i t i n g && ( mousebuttons&G3D PRESSED) !=0 && f i e l d t y p e ==FIELDEDIT ) e−>h i t ( i n t ( f l o o r ( hitx −(curx+FONTW/2) ) ) , i n t ( f l o o r ( hity−cury ) ) , ( mousebuttons&G3D DRAGGED) ! = 0 ) ; // mouse request p o s i t i o n i f ( e d i t i n g && ( ( fieldmode==FIELDCOMMIT) || ( fieldmode== FIELDABORT) || ! h i t ) ) // commit f i e l d i f user pressed enter or wandered out o f focus { i f ( fieldmode==FIELDCOMMIT || ( fieldmode ! =FIELDABORT && ! h i t ) ) r e s u l t = e−>currentline ( ) . t e x t ; e−>a c t i v e = ( e−>mode! =EDITORFOCUSED) ; fieldmode = FIELDSHOW; } e l s e f i e l d s a c t i v e = true ;

autotab ( ) ; i n t x = curx ; i n t y = cury ; l i n e ( ( FONTH∗2) /3) ; i f ( visible ( ) ) { i f ( ! label ) label = i n t s t r ( val ) ; i n t w = text width ( l a b e l ) ; bool h i t ; i n t px , py ; i f ( ishorizontal ( ) ) { h i t = i s h i t (FONTH, ysize , x , y ) ; px = x + (FONTH−w) /2; py = y + ( ysize−FONTH) − ( ( ysize−FONTH) ∗( val−vmin ) ) / ( ( vmax== vmin ) ? 1 : ( vmax−vmin ) ) ; //vmin at bottom } else { h i t = i s h i t ( xsize , FONTH, x , y ) ; px = x + FONTH/2 − w/2 + ( ( xsize−w) ∗( val−vmin ) ) / ( ( vmax==vmin ) ? 1 : ( vmax−vmin ) ) ; //vmin at l e f t py = y ; } i f ( h i t ) c o l o r = 0xFF0000 ; t e x t ( label , px , py , color , h i t && actionon , h i t ) ; i f ( h i t && actionon ) { i n t vnew = ( vmin < vmax ? 1 : −1)+vmax−vmin ; i f ( i s h o r i z o n t a l ( ) ) vnew = i n t ( ( vnew∗(y+ysize−FONTH/2−h i t y ) ) / ( ysize−FONTH) ) ; e l s e vnew = i n t ( ( vnew∗( hitx−x−FONTH/2) ) / ( xsize−w) ) ; vnew += vmin ; vnew = vmin < vmax ? clamp ( vnew , vmin , vmax ) : clamp ( vnew , vmax, vmin ) ; i f ( vnew ! = v a l ) v a l = vnew ; }

e−>draw ( curx+FONTW/2 , cury , color , h i t && e d i t i n g ) ; lineshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ; glDisable (GL BLEND) ; i f ( e d i t i n g ) g l C o l o r 3 f ( 1 , 0 , 0) ; e l s e glColor3ub ( color>>16, ( color>>8)&0xFF , c o l o r&0xFF ) ; r e c t ( curx , cury , w, h , −1, true ) ; glEnable ( GL TEXTURE 2D ) ; glEnable (GL BLEND) ; defaultshader−>set ( ) ; } layout (w, h ) ;

} }

i f ( e−>maxy ! = 1) { i n t s l i n e s = e−>l i m i t s c r o l l y ( ) ; i f ( s l i n e s > 0) { i n t pos = e−>s c r o l l y ; s l i d e r ( e−>s c r o l l y , slines , 0 , color , NULL) ; i f ( pos ! = e−>s c r o l l y ) e−>cy = e−>s c r o l l y ; } i f ( wasvertical ) p o p l i s t ( ) ; }

char ∗f i e l d ( const char ∗name, i n t color , i n t length , i n t height , const char ∗i n i t v a l , i n t initmode ) { return f i e l d ( name, color , length , height , i n i t v a l , initmode , FIELDEDIT ) ; } char ∗k e y f i e l d ( const char ∗name, i n t color , i n t length , i n t height , const char ∗i n i t v a l , i n t initmode ) { return f i e l d ( name, color , length , height , i n i t v a l , initmode , FIELDKEY ) ;

return r e s u l t ; }

} char ∗ f i e l d ( const char ∗name, i n t color , i n t length , i n t height , const char ∗i n i t v a l , i n t initmode , i n t f i e l d t y p e = FIELDEDIT ) { e d i t o r ∗e = useeditor ( name, initmode , f a l s e , i n i t v a l ) ; // generate a new e d i t o r i f necessary i f ( layoutpass ) { i f ( i n i t v a l && e−>mode==EDITORFOCUSED && ( e ! = currentfocus ( ) || fieldmode == FIELDSHOW) ) { i f ( strcmp ( e−>l i n e s [ 0 ] . text , i n i t v a l ) ) e−>c l e a r ( i n i t v a l ) ; } e−>linewrap = ( lengthmaxx = ( e−>linewrap ) ? −1 : length ; e−>maxy = ( heightpixelwidth = abs ( length )∗FONTW; i f ( e−>linewrap && e−>maxy==1) { i n t temp ; text bounds ( e−>l i n e s [ 0 ] . text , temp , e−>pixelheight , e−> pixelwidth ) ; //only s i n g l e l i n e e d i t o r s can have v a r i a b l e height } else e−>p i x e l h e i g h t = FONTH∗max( height , 1) ; } i n t h = e−>p i x e l h e i g h t ; i n t w = e−>pixelwidth + FONTW;

void r e c t ( f l o a t x , f l o a t y , f l o a t w, f l o a t h , i n t usetc = −1, bool lines = false ) { glBegin ( l i n e s ? GL LINE LOOP : GL TRIANGLE STRIP ) ; s t a t i c const GLfloat t c [ 4 ] [ 2 ] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; i f ( usetc>=0) glTexCoord2fv ( t c [ usetc ] ) ; glVertex2f ( x , y ) ; i f ( usetc>=0) glTexCoord2fv ( t c [ ( usetc +1) %4]) ; g l V e r t e x 2 f ( x + w, y ) ; i f ( lines ) { i f ( usetc>=0) glTexCoord2fv ( t c [ ( usetc +2) %4]) ; g l V e r t e x 2 f ( x + w, y + h ) ; } i f ( usetc>=0) glTexCoord2fv ( t c [ ( usetc +3) %4]) ; glVertex2f ( x , y + h) ; i f ( ! lines ) { i f ( usetc>=0) glTexCoord2fv ( t c [ ( usetc +2) %4]) ; g l V e r t e x 2 f ( x + w, y + h ) ; } glEnd ( ) ; x t r a v e r t s += 4; } void t e x t ( const char ∗text , i n t x , i n t y , i n t color , bool shadow , bool f o r c e = f a l s e ) { i f ( shadow ) draw text ( text , x+SHADOW, y+SHADOW, 0x00 , 0x00 , 0x00 , −0 xC0 ) ; draw text ( text , x , y , color>>16, ( color>>8)&0xFF , c o l o r&0xFF , f o r c e ? −0xFF : 0xFF ) ;

bool wasvertical = i s v e r t i c a l ( ) ; i f ( wasvertical && e−>maxy ! = 1) pushlist ( ) ; char ∗r e s u l t = NULL; i f ( v i s i b l e ( ) && ! layoutpass ) {

} void background ( i n t color , i n t inheritw , i n t inherith )

engine/3dgui.cpp {

} SETSHADER( rgbonly ) ; const vec &c o l o r = h i t ? vec ( 1 , 0.5 f , 0.5 f ) : ( o v e r l a i d ? vec ( 1 , 1 , 1) : l i g h t ) ; f l o a t t c [ 4 ] [ 2 ] = { { 0 , 0 }, { 1 , 0 }, { 1 , 1 }, { 0 , 1 } }; int xoff = vslot . xoffset , yoff = vslot . yoffset ; i f ( vslot . rotation ) { i f ( ( v s l o t . r o t a t i o n &5) == 1) { swap ( x o f f , y o f f ) ; loopk ( 4 ) swap ( t c [ k ] [ 0 ] , tc [ k ] [ 1 ] ) ; } i f ( v s l o t . r o t a t i o n >= 2 && v s l o t . r o t a t i o n ys ; } i f ( s l o t . loaded ) g l C o l o r 3 f ( c o l o r . x∗v s l o t . c o l o r s c a l e . x , c o l o r . y∗v s l o t . c o l o r s c a l e . y , c o l o r . z∗v s l o t . c o l o r s c a l e . z ) ; else glColor3fv ( color . v ) ; glBindTexture ( GL TEXTURE 2D, t−>id ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2fv ( t c [ 0 ] ) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2fv ( t c [ 1 ] ) ; g l V e r t e x 2 f ( x+xs , y ) ; glTexCoord2fv ( t c [ 3 ] ) ; g l V e r t e x 2 f ( x , y+ys ) ; glTexCoord2fv ( t c [ 2 ] ) ; g l V e r t e x 2 f ( x+xs , y+ys ) ; glEnd ( ) ; i f ( glowtex ) { glBlendFunc ( GL SRC ALPHA, GL ONE) ; glBindTexture ( GL TEXTURE 2D, glowtex−>id ) ; i f ( h i t || o v e r l a i d ) g l C o l o r 3 f ( c o l o r . x∗v s l o t . glowcolor . x , c o l o r . y ∗v s l o t . glowcolor . y , c o l o r . z∗v s l o t . glowcolor . z ) ; e l s e g l C o l o r 3 f v ( v s l o t . glowcolor . v ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2fv ( t c [ 0 ] ) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2fv ( t c [ 1 ] ) ; g l V e r t e x 2 f ( x+xs , y ) ; glTexCoord2fv ( t c [ 3 ] ) ; g l V e r t e x 2 f ( x , y+ys ) ; glTexCoord2fv ( t c [ 2 ] ) ; g l V e r t e x 2 f ( x+xs , y+ys ) ; glEnd ( ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } i f ( layertex ) { glBindTexture ( GL TEXTURE 2D, layertex−>id ) ; g l C o l o r 3 f ( c o l o r . x∗layer−>c o l o r s c a l e . x , c o l o r . y∗layer−>c o l o r s c a l e . y , c o l o r . z∗layer−>c o l o r s c a l e . z ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2fv ( t c [ 0 ] ) ; g l V e r t e x 2 f ( x+xs/2 , y+ys /2) ; glTexCoord2fv ( t c [ 1 ] ) ; g l V e r t e x 2 f ( x+xs , y+ys /2) ; glTexCoord2fv ( t c [ 3 ] ) ; g l V e r t e x 2 f ( x+xs/2 , y+ys ) ; glTexCoord2fv ( t c [ 2 ] ) ; g l V e r t e x 2 f ( x+xs , y+ys ) ; glEnd ( ) ; }

i f ( layoutpass ) return ; glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; glColor4ub ( color>>16, ( color>>8)&0xFF , c o l o r&0xFF , 0x80 ) ; i n t w = xsize , h = y s i z e ; i f ( inheritw >0) { i n t parentw = c u r l i s t , parentdepth = 0; f o r ( ; parentdepth < inheritw && l i s t s [ parentw ] . parent>=0; parentdepth ++) parentw = l i s t s [ parentw ] . parent ; l i s t &p = l i s t s [ parentw ] ; w = p . springs > 0 && ( curdepth−parentdepth )&1 ? l i s t s [ p . parent ] . w : p .w; } i f ( inherith >0) { i n t parenth = c u r l i s t , parentdepth = 0; f o r ( ; parentdepth < inherith && l i s t s [ parenth ] . parent>=0; parentdepth ++) parenth = l i s t s [ parenth ] . parent ; l i s t &p = l i s t s [ parenth ] ; h = p . springs > 0 && ! ( ( curdepth−parentdepth ) &1) ? l i s t s [ p . parent ] . h : p . h ; } r e c t ( curx , cury , w, h ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } void i c o n ( Texture ∗t , bool overlaid , i n t x , i n t y , i n t size , bool h i t ) { f l o a t scale = f l o a t ( s i z e ) /max( t−>xs , t−>ys ) ; //scale and preserve aspect r a t i o f l o a t xs = t−>xs∗scale , ys = t−>ys∗scale ; x += i n t ( ( size−xs ) /2) ; y += i n t ( ( size−ys ) /2) ; const vec &c o l o r = h i t ? vec ( 1 , 0.5 f , 0.5 f ) : ( o v e r l a i d ? vec ( 1 , 1 , 1) : l i g h t ) ; glBindTexture ( GL TEXTURE 2D, t−>id ) ; i f ( h i t && actionon ) { g l C o l o r 4 f ( 0 , 0 , 0 , 0.75 f ) ; r e c t ( x+SHADOW, y+SHADOW, xs , ys , 0) ; } glColor3fv ( color . v ) ; r e c t ( x , y , xs , ys , 0) ; i f ( overlaid ) { i f ( ! overlaytex ) overlaytex = textureload ( ” data/guioverlay . png ” , 3) ; glBindTexture ( GL TEXTURE 2D, overlaytex−>id ) ; glColor3fv ( l i g h t . v ) ; r e c t ( x , y , xs , ys , 0) ; }

defaultshader−>set ( ) ; i f ( overlaid ) { i f ( ! overlaytex ) overlaytex = textureload ( ” data/guioverlay . png ” , 3) ; glBindTexture ( GL TEXTURE 2D, overlaytex−>id ) ; glColor3fv ( l i g h t . v ) ; r e c t ( x , y , xs , ys , 0) ; }

} void previewslot ( VSlot &v s l o t , bool overlaid , i n t x , i n t y , i n t size , bool h i t ) { S l o t &s l o t = ∗v s l o t . s l o t ; i f ( s l o t . sts . empty ( ) ) return ; VSlot ∗l a y e r = NULL; Texture ∗t = NULL, ∗glowtex = NULL, ∗l a y e r t e x = NULL; i f ( s l o t . loaded ) { t = s l o t . sts [ 0 ] . t ; i f ( t == notexture ) return ; S l o t &s l o t = ∗v s l o t . s l o t ; i f ( s l o t . texmask&(1sts . empty ( ) ) l a y e r t e x = layer−>s l o t−>sts [ 0 ] . t; } } e l s e i f ( s l o t . thumbnail && s l o t . thumbnail ! = notexture ) t = s l o t . thumbnail ; e l s e return ; f l o a t xt = min( 1 . 0 f , t−>xs / ( f l o a t ) t−>ys ) , yt = min( 1 . 0 f , t−>ys / ( f l o a t ) t−>xs ) , xs = size , ys = s i z e ; i f ( h i t && actionon ) { glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; g l C o l o r 4 f ( 0 , 0 , 0 , 0.75 f ) ; r e c t ( x+SHADOW, y+SHADOW, xs , ys ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ;

189

} void l i n e ( i n t size , f l o a t percent = 1.0 f ) { i f ( visible ( ) ) { i f ( ! s l i d e r t e x ) s l i d e r t e x = textureload ( ” data/ g u i s l i d e r . png ” , 3) ; glBindTexture ( GL TEXTURE 2D, s l i d e r t e x−>id ) ; i f ( percent < 0.99 f ) { g l C o l o r 4 f ( l i g h t . x , l i g h t . y , l i g h t . z , 0.375 f ) ; i f ( ishorizontal ( ) ) r e c t ( curx + FONTH/2 − s i z e /2 , cury , size , ysize , 0) ; else r e c t ( curx , cury + FONTH/2 − s i z e /2 , xsize , size , 1) ; } glColor3fv ( l i g h t . v ) ; i f ( ishorizontal ( ) ) r e c t ( curx + FONTH/2 − s i z e /2 , cury + y s i z e∗(1−percent ) , size , y s i z e∗percent , 0) ; else r e c t ( curx , cury + FONTH/2 − s i z e /2 , x s i z e∗percent , size , 1) ; } layout ( i s h o r i z o n t a l ( ) ? FONTH : 0 , i s h o r i z o n t a l ( ) ? 0 : FONTH) ; } void textbox ( const char ∗text , i n t width , i n t height , i n t c o l o r ) { width ∗= FONTW; height ∗= FONTH;

190

Foundations of Videogame Programming Code Repository bottom += gaph − ( gapy2−gapy1 ) ;

i n t w, h ; text bounds ( text , w, h , width ) ; i f ( h > height ) height = h ; i f ( v i s i b l e ( ) ) draw text ( text , curx , cury , color>>16, ( color>>8)&0 xFF , c o l o r&0xFF , 0xFF , −1, width ) ; layout ( width , height ) ;

} //multiple t i l e d quads i f necessary rather than a s i n g l e stretched one i n t ystep = bottom−top ; i n t yo = y+top ; while ( ystep > 0) { i f ( p . f l a g s&0x10 && yo+ystep−(y+top ) > gaph ) { ystep = gaph+y+top−yo ; tbottom = ttop+ystep∗hscale ; } i n t xstep = right−l e f t ; i n t xo = x+ l e f t ; f l o a t tright2 = tright ; while ( xstep > 0) { i f ( p . f l a g s&0x01 && xo+xstep−(x+ l e f t ) > gapw ) { xstep = gapw+x+ l e f t−xo ; t r i g h t = t l e f t +xstep∗wscale ; } i f ( ! quads ) { quads = true ; glBegin (GL QUADS) ; } glTexCoord2f ( t l e f t , ttop ) ; g l V e r t e x 2 f ( xo , yo ) ; glTexCoord2f ( t r i g h t , ttop ) ; g l V e r t e x 2 f ( xo+xstep , yo ) ; glTexCoord2f ( t r i g h t , tbottom ) ; g l V e r t e x 2 f ( xo+xstep , yo+ ystep ) ; glTexCoord2f ( t l e f t , tbottom ) ; g l V e r t e x 2 f ( xo , yo+ ystep ) ; x t r a v e r t s += 4; i f ( ! ( p . f l a g s&0x01 ) ) break ; xo += xstep ; } tright = tright2 ; i f ( ! ( p . f l a g s&0x10 ) ) break ; yo += ystep ; }

} i n t button ( const char ∗text , i n t color , const char ∗icon , bool c l i c k a b l e , bool center ) { const i n t padding = 10; i n t w = 0; i f ( icon ) w += ICON SIZE ; i f ( icon && t e x t ) w += padding ; i f ( t e x t ) w += text width ( t e x t ) ; i f ( visible ( ) ) { bool h i t = i s h i t (w, FONTH) ; i f ( h i t && c l i c k a b l e ) c o l o r = 0xFF0000 ; i n t x = curx ; i f ( i s v e r t i c a l ( ) && center ) x += ( xsize−w) /2; i f ( icon ) { i f ( icon [ 0 ] ! = ’ ’ ) { const char ∗ext = s t r r c h r ( icon , ’ . ’ ) ; defformatstring ( tname ) ( ” packages/icons/%s%s ” , icon , ext ? ” ” : ” . jpg ” ) ; i c o n ( textureload ( tname , 3) , f a l s e , x , cury , ICON SIZE , c l i c k a b l e && h i t ) ; } x += ICON SIZE ; } i f ( icon && t e x t ) x += padding ; i f ( t e x t ) t e x t ( text , x , cury , color , center || ( h i t && c l i c k a b l e && actionon ) , h i t && c l i c k a b l e ) ;

} i f ( quads ) glEnd ( ) ; e l s e break ; // i f i t didn ’ t happen on the f i r s t pass , i t won ’ t happen on the second . .

} return layout (w, FONTH) ; } s t a t i c Texture ∗skintex , ∗overlaytex , ∗s l i d e r t e x ; s t a t i c const i n t skinx [ ] , skiny [ ] ; s t a t i c const struct patch { ushort l e f t , right , top , bottom ; uchar f l a g s ; } patches [ ] ; s t a t i c void drawskin ( i n t x , i n t y , i n t gapw , i n t gaph , i n t s t a r t , i n t n , i n t passes = 1 , const vec &l i g h t = vec ( 1 , 1 , 1) , f l o a t alpha = 0.80 f ) // i n t v l e f t , i n t vright , i n t vtop , i n t vbottom , i n t start , int n) { i f ( ! skintex ) skintex = textureload ( ” data/guiskin . png ” , 3) ; glBindTexture ( GL TEXTURE 2D, skintex−>id ) ; i n t gapx1 = INT MAX , gapy1 = INT MAX , gapx2 = INT MAX , gapy2 = INT MAX ; f l o a t wscale = 1.0 f / (SKIN W∗SKIN SCALE ) , hscale = 1.0 f / (SKIN H∗ SKIN SCALE ) ; l o o p j ( passes ) { bool quads = f a l s e ; i f ( passes>1) glDepthFunc ( j ? GL LEQUAL : GL GREATER) ; g l C o l o r 4 f ( j ? l i g h t . x : 1.0 f , j ? l i g h t . y : 1.0 f , j ? l i g h t . z : 1.0 f , passes= gapx2 ) { l e f t += gapw − ( gapx2−gapx1 ) ; r i g h t += gapw − ( gapx2−gapx1 ) ; } i f ( p . f l a g s&0x10 ) { gapy1 = top ; gapy2 = bottom ; } e l s e i f ( top >= gapy2 ) { top += gaph − ( gapy2−gapy1 ) ;

} i f ( passes>1) glDepthFunc (GL ALWAYS) ; } vec o r i g i n , scale , ∗savedorigin ; float dist ; g3d callback ∗cb ; bool gui2d ; static static static static

f l o a t basescale , maxscale ; bool passthrough ; f l o a t alpha ; vec l i g h t ;

void adjustscale ( ) { i n t w = x s i z e + ( skinx[2]−skinx [ 1 ] ) ∗SKIN SCALE + ( skinx[10]−skinx [ 9 ] ) ∗SKIN SCALE , h = y s i z e + ( skiny[9]−skiny [ 7 ] ) ∗SKIN SCALE ; i f ( tcurrent ) h += ( ( skiny[5]−skiny [ 1 ] )−(skiny[3]−skiny [ 2 ] ) )∗ SKIN SCALE + FONTH−2∗INSERT ; e l s e h += ( skiny[6]−skiny [ 3 ] ) ∗SKIN SCALE ; f l o a t aspect = forceaspect ? 1.0 f /forceaspect : f l o a t ( screen−>h ) / f l o a t ( screen−>w) , f i t = 1.0 f ; i f (w∗aspect∗basescale>1.0f ) f i t = 1.0 f / (w∗aspect∗basescale ) ; i f ( h∗basescale∗f i t>maxscale ) f i t ∗= maxscale / (h∗basescale∗ f i t ) ; o r i g i n = vec ( 0 . 5 f −((w−x s i z e ) /2 − ( skinx[2]−skinx [ 1 ] ) ∗SKIN SCALE )∗ aspect∗scale . x∗f i t , 0.5 f + ( 0 . 5 f∗h−(skiny[9]−skiny [ 7 ] ) ∗ SKIN SCALE )∗scale . y∗f i t , 0) ; scale = vec ( aspect∗scale . x∗f i t , scale . y∗f i t , 1) ; } void s t a r t ( i n t starttime , f l o a t i n i t s c a l e , i n t ∗tab , bool allowinput ) { i f ( gui2d ) { i n i t s c a l e ∗= 0.025 f ; i f ( allowinput ) hascursor = true ; } basescale = i n i t s c a l e ; i f ( layoutpass ) scale . x = scale . y = scale . z = guifadein ? basescale∗ min ( ( t o t a l m i l l i s−starttime ) /300.0 f , 1.0 f ) : basescale ; alpha = allowinput ? 0.80 f : 0.60 f ; passthrough = scale . xo ) . set ( 2 , 0) . normalize ( ) , origin ) ; i f ( p . r a y i n t e r s e c t ( camera1−>o , camdir , d i s t ) && dist >=0) { vec hitpos ( camdir ) ; hitpos . mul ( d i s t ) . add ( camera1−>o ) . sub ( o r i g i n ) ; h i t x = vec(−p . y , p . x , 0) . dot ( hitpos ) /scale . x ; h i t y = −hitpos . z/scale . y ; } } i f ( ( mousebuttons & G3D PRESSED) && ( fabs ( hitx−f i r s t x ) > 2 || fabs ( h i t y − f i r s t y ) > 2) ) mousebuttons |= G3D DRAGGED; i f ( dist>=0 && hitx>=−x s i z e/2 && hitx=−ysize −(FONTH−2∗ INSERT ) −((skiny[6]−skiny [ 1 ] )−(skiny[3]−skiny [ 2 ] ) )∗ SKIN SCALE && hitxo . y , o r i g i n . x−camera1−>o .x) ; glTranslatef ( origin . x , origin . y , origin . z ) ; g l R o t a t e f ( yaw/RAD−90, 0 , 0 , 1) ; g l R o t a t e f (−90, 1 , 0 , 0) ; g l S c a l e f(−scale . x , scale . y , scale . z ) ; vec d i r ; l i g h t r e a c h i n g ( o r i g i n , l i g h t , dir , f a l s e , 0 , 0.5 f ) ; f l o a t i n t e n s i t y = vec ( yaw , 0.0 f ) . dot ( d i r ) ; l i g h t . mul( 1 . 0 f + max( i n t e n s i t y , 0.0 f ) ) ; } drawskin ( curx−skinx [2]∗SKIN SCALE , cury−skiny [6]∗SKIN SCALE , xsize , ysize , 0 , 9 , gui2d ? 1 : 2 , l i g h t , alpha ) ; i f ( ! tcurrent ) drawskin ( curx−skinx [5]∗SKIN SCALE , cury−skiny [6]∗ SKIN SCALE , xsize , 0 , 9 , 1 , gui2d ? 1 : 2 , l i g h t , alpha ) ;

} else { i f ( tcurrent && tx= 0; d ˆ= 1) { l i s t &p = l i s t s [ i ] ; i f ( d&1) { dh = h − p . h ; i f ( dh = 0; d ˆ= 1) { l i s t &p = l i s t s [ i ] ; i f ( d&1) { p .w += dw; w = p .w; } e l s e { dw = w − p .w; i f (dw = 0) columns [ l . column ] = max( columns [ l . column ] , ishorizontal ( ) ? ysize : xsize ) ; } i n t parent = −1, depth = 0; f o r ( i n t i = f i r s t l i s t ; i < l i s t s . length ( ) ; i ++) { l i s t &l = l i s t s [ i ] ; i f ( l . parent > parent ) { parent = l . parent ; depth ++; } e l s e i f ( l . parent < parent ) { parent = l . parent ; depth−−; } i f ( l . column >= 0) { i f ( depth&1) adjusthorizontalcolumn ( l . column , i ) ; e l s e adjustverticalcolumn ( l . column , i ) ; } } } void end ( ) { i f ( layoutpass ) { adjustcolumns ( ) ;

191

} void draw ( ) { cb−>gui (∗ this , layoutpass ) ; } }; Texture ∗gui : : skintex = NULL, ∗gui : : overlaytex = NULL, ∗gui : : s l i d e r t e x = NULL; //chop skin i n t o a g r i d const i n t gui : : skiny [ ] = {0, 7 , 21, 34, 43, 48, 56, 104, 111, 117, 128}, gui : : skinx [ ] = {0, 11, 23, 37, 105, 119, 137, 151, 215, 229, 246, 256}; //Note : skinx[3]−skinx [ 2 ] = skinx[7]−skinx [ 6 ] // skinx[5]−skinx [ 4 ] = skinx[9]−skinx [ 8 ] const gui : : patch gui : : patches [ ] = { //arguably t h i s data can be compressed − i t depends on what e l s e needs to be skinned in the future {1 ,2 ,3 ,6 , 0}, // body {2 ,9 ,5 ,6 , 0x01}, {9 ,10 ,3 ,6 , 0}, {1 ,2 ,6 ,7 , 0x10}, {2 ,9 ,6 ,7 , 0x11}, {9 ,10 ,6 ,7 , 0x10}, {1 ,2 ,7 ,9 , 0}, {2 ,9 ,7 ,9 , 0x01}, {9 ,10 ,7 ,9 , 0}, {5 ,6 ,3 ,5 , 0x01}, // top {2 ,3 ,1 ,2 , {3 ,4 ,1 ,2 , {4 ,5 ,1 ,2 , {2 ,3 ,2 ,3 , {3 ,4 ,2 ,3 , {4 ,5 ,2 ,3 , {2 ,3 ,3 ,5 , {3 ,4 ,3 ,5 , {4 ,5 ,3 ,5 ,

0}, // s e l e c t e d tab 0x01}, 0}, 0x10}, 0x11}, 0x10}, 0}, 0x01}, 0},

{6 ,7 ,1 ,2 , {7 ,8 ,1 ,2 , {8 ,9 ,1 ,2 , {6 ,7 ,2 ,3 , {7 ,8 ,2 ,3 ,

0}, // deselected tab 0x01}, 0}, 0x10}, 0x11},

192 {8 ,9 ,2 ,3 , {6 ,7 ,3 ,5 , {7 ,8 ,3 ,5 , {8 ,9 ,3 ,5 ,

Foundations of Videogame Programming Code Repository 0x10}, 0}, 0x01}, 0},

i f ( isdown ) fieldmode = FIELDABORT; return true ; case SDLK RETURN: case SDLK TAB: i f ( cooked && ( e−>maxy ! = 1) ) break ; case SDLK KP ENTER : i f ( isdown ) fieldmode = FIELDCOMMIT; // s i g n a l f i e l d commit ( handled when drawing f i e l d ) return true ; case SDLK HOME: case SDLK END: case SDLK PAGEUP: case SDLK PAGEDOWN: case SDLK DELETE: case SDLK BACKSPACE: case SDLK UP: case SDLK DOWN: case SDLK LEFT : case SDLK RIGHT : case SDLK LSHIFT : case SDLK RSHIFT : case −4: case −5: break ; default : i f ( ! cooked || ( codel i n e s . length ( ) !=1 || ! e−>l i n e s [ 0 ] . empty ( ) ) e−>i n s e r t ( ” ” ) ; e−>i n s e r t ( keyname ) ; } return true ; } i f ( code==−1 && g3d windowhit ( isdown , true ) ) return true ; e l s e i f ( code==−3 && g3d windowhit ( isdown , f a l s e ) ) return true ; i f ( fieldmode == FIELDSHOW || ! e ) { i f ( windowhit ) switch ( code ) { case −4: // window ”management” i f ( isdown ) { i f ( windowhit−>gui2d ) { vec o r i g i n = ∗guis2d . l a s t ( ) . savedorigin ; i n t i = windowhit − &guis2d [ 0 ] ; f o r ( i n t j = guis2d . length ( ) −1; j > i ; j−−) ∗guis2d [ j ] . savedorigin = ∗guis2d [ j −1]. savedorigin ; ∗windowhit−>savedorigin = o r i g i n ; i f ( guis2d . length ( ) > 1) { i f ( camera1−>o . d i s t (∗windowhit−>savedorigin ) o . d i s t (∗ guis2d . l a s t ( ) . savedorigin ) ) windowhit−>savedorigin−>add ( camdir ) ; } } e l s e windowhit−>savedorigin−>add ( vec ( camdir ) . mul ( guipushdist ) ) ; } return true ; case −5: i f ( isdown ) { i f ( windowhit−>gui2d ) { vec o r i g i n = ∗guis2d [ 0 ] . savedorigin ; l o o p j ( guis2d . length ( ) −1) ∗guis2d [ j ] . savedorigin = ∗ guis2d [ j + 1 ] . savedorigin ; ∗guis2d . l a s t ( ) . savedorigin = o r i g i n ; i f ( guis2d . length ( ) > 1) { i f ( camera1−>o . d i s t (∗ guis2d . l a s t ( ) . savedorigin ) >= camera1−>o . d i s t (∗ guis2d [ 0 ] . savedorigin ) ) guis2d . l a s t ( ) . savedorigin−>sub ( camdir ) ; } } e l s e windowhit−>savedorigin−>sub ( vec ( camdir ) . mul ( guipushdist ) ) ; } return true ; } return f a l s e ; } switch ( code ) { case SDLK ESCAPE: //cancel e d i t i n g without commit

} i f ( ! isdown ) return true ; e−>key ( code , cooked ) ; return true ; } void g3d cursorpos ( f l o a t &x , f l o a t &y ) { i f ( guis2d . length ( ) ) { x = cursorx ; y = cursory ; } e l s e x = y = 0.5 f ; } void g3d resetcursor ( ) { cursorx = cursory = 0.5 f ; } FVARP( guisens , 1e−3f , 1 , 1e3f ) ; bool g3d movecursor ( i n t dx , i n t dy ) { i f ( ! guis2d . length ( ) || ! hascursor ) return f a l s e ; const f l o a t CURSORSCALE = 500.0 f ; cursorx = max( 0 . 0 f , min( 1 . 0 f , cursorx+guisens∗dx∗( screen−>h/ ( screen−>w ∗CURSORSCALE) ) ) ) ; cursory = max( 0 . 0 f , min( 1 . 0 f , cursory+guisens∗dy/CURSORSCALE) ) ; return true ; } VARNP( guifollow , useguifollow , 0 , 1 , 1) ; VARNP( gui2d , usegui2d , 0 , 1 , 1) ; void g3d addgui ( g3d callback ∗cb , vec &o r i g i n , i n t f l a g s ) { bool gui2d = f l a g s&GUI FORCE 2D || ( f l a g s&GUI 2D && usegui2d ) || mainmenu; i f ( ! gui2d && f l a g s&GUI FOLLOW && useguifollow ) o r i g i n . z = player−>o . z −(player−>eyeheight−1); gui &g = ( gui2d ? guis2d : guis3d ) . add ( ) ; g . cb = cb ; g . origin = origin ; g . savedorigin = &o r i g i n ; g . d i s t = f l a g s&GUI BOTTOM && gui2d ? 1e16f : camera1−>o . d i s t ( g . o r i g i n ) ; g . gui2d = gui2d ; } void g 3 d l i m i t s c a l e ( f l o a t scale ) { gui : : maxscale = scale ; } s t a t i c i n l i n e bool g3d sort ( const gui &a , const gui &b ) { return a . d i s t < b. dist ; } bool g3d windowhit ( bool on , bool act ) { extern i n t c l e a r g u i ( i n t n ) ; i f ( act ) { i f ( actionon || windowhit ) { i f ( on ) { f i r s t x = gui : : h i t x ; f i r s t y = gui : : h i t y ; } mousebuttons |= ( actionon=on ) ? G3D DOWN : G3D UP; } } e l s e i f ( ! on && windowhit ) c l e a r g u i ( 1 ) ; return ( guis2d . length ( ) && hascursor ) || ( windowhit && ! windowhit−>

engine/animmodel.h gui2d ) ;

193

glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glOrtho ( 0 , 1 , 1 , 0 , −1, 1) ;

} void g3d render ( ) { windowhit = NULL; i f ( actionon ) mousebuttons |= G3D PRESSED;

glMatrixMode (GL MODELVIEW) ; glPushMatrix ( ) ; glLoadIdentity ( ) ;

gui : : r e s e t ( ) ; guis2d . shrink ( 0 ) ; guis3d . shrink ( 0 ) ;

loopvrev ( guis2d ) guis2d [ i ] . draw ( ) ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ;

// c a l l a l l places in the engine that may want to render a gui from here , they c a l l g3d addgui ( ) extern void g3d texturemenu ( ) ; } i f ( ! mainmenu) g3d texturemenu ( ) ; g3d mainmenu ( ) ; i f ( ! mainmenu) game : : g3d gamemenus ( ) ;

i f ( guis2d . length ( ) || guis3d . length ( ) ) { glDisable (GL BLEND) ; }

guis2d . s o r t ( g3d sort ) ; guis3d . s o r t ( g3d sort ) ;

flusheditors ( ) ; i f ( ! f i e l d s a c t i v e ) fieldmode = FIELDSHOW; //didn ’ t draw any f i e l d s , so loose focus − mainly f o r menu closed i f ( ( fieldmode ! =FIELDSHOW) ! = wasfocused ) { SDL EnableUNICODE ( fieldmode ! =FIELDSHOW) ; keyrepeat ( fieldmode ! =FIELDSHOW || editmode ) ; }

readyeditors ( ) ; bool wasfocused = ( fieldmode ! =FIELDSHOW) ; fieldsactive = false ; hascursor = f a l s e ; layoutpass = true ; loopv ( guis2d ) guis2d [ i ] . draw ( ) ; loopv ( guis3d ) guis3d [ i ] . draw ( ) ; layoutpass = f a l s e ; i f ( guis2d . length ( ) || guis3d . length ( ) ) { glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } i f ( guis3d . length ( ) ) { glEnable ( GL DEPTH TEST ) ; glDepthFunc (GL ALWAYS) ; glDepthMask ( GL FALSE ) ; loopvrev ( guis3d ) guis3d [ i ] . draw ( ) ; glDepthFunc ( GL LESS ) ; glDepthMask ( GL TRUE ) ; glDisable ( GL DEPTH TEST ) ; } i f ( guis2d . length ( ) ) {

mousebuttons = 0; } void consolebox ( i n t x1 , i n t y1 , i n t x2 , i n t y2 ) { glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glPushMatrix ( ) ; g l T r a n s l a t e f ( x1 , y1 , 0) ; f l o a t bw = x2 − x1 , bh = y2 − y1 , aspect = bw/bh , sh = bh , sw = sh∗ aspect ; bw ∗= f l o a t (4∗FONTH) / (SKIN H∗SKIN SCALE ) ; bh ∗= f l o a t (4∗FONTH) / (SKIN H∗SKIN SCALE ) ; sw /= bw + ( gui : : skinx[2]−gui : : skinx [ 1 ] + gui : : skinx[10]−gui : : skinx [ 9 ] ) ∗SKIN SCALE ; sh /= bh + ( gui : : skiny[9]−gui : : skiny [ 7 ] + gui : : skiny[6]−gui : : skiny [ 4 ] ) ∗SKIN SCALE ; g l S c a l e f ( sw, sh , 1) ; gui : : drawskin(−gui : : skinx [1]∗SKIN SCALE , −gui : : skiny [4]∗SKIN SCALE , i n t (bw) , i n t ( bh ) , 0 , 9 , 1 , vec ( 1 , 1 , 1) , 0.60 f ) ; gui : : drawskin((−gui : : skinx [ 1 ] + gui : : skinx [ 2 ] − gui : : skinx [ 5 ] ) ∗ SKIN SCALE , −gui : : skiny [4]∗SKIN SCALE , i n t (bw) , 0 , 9 , 1 , 1 , vec ( 1 , 1 , 1) , 0.60 f ) ; glPopMatrix ( ) ; }

engine/animmodel.h VARFP( lightmodels , 0 , 1 , 1 , preloadmodelshaders ( ) ) ; VARFP( envmapmodels , 0 , 1 , 1 , preloadmodelshaders ( ) ) ; VARFP( glowmodels , 0 , 1 , 1 , preloadmodelshaders ( ) ) ; VARFP( bumpmodels, 0 , 1 , 1 , preloadmodelshaders ( ) ) ; VARP( fullbrightmodels , 0 , 0 , 200) ;

f r 1 = ( i n t ) ( time/ i n f o . speed ) ; // round to f u l l frames t = ( time−f r 1∗i n f o . speed ) / i n f o . speed ; // progress o f the frame , value from 0.0 f to 1.0 f } i f ( i n f o . anim&ANIM LOOP ) { f r 1 = f r 1%i n f o . range+ i n f o . frame ; f r 2 = f r 1 +1; i f ( fr2>=i n f o . frame+ i n f o . range ) f r 2 = i n f o . frame ; } else { f r 1 = min ( fr1 , i n f o . range−1)+ i n f o . frame ; f r 2 = min ( f r 1 +1 , i n f o . frame+ i n f o . range−1); } i f ( i n f o . anim&ANIM REVERSE) { f r 1 = ( i n f o . frame+ i n f o . range−1)−(fr1−i n f o . frame ) ; f r 2 = ( i n f o . frame+ i n f o . range−1)−(fr2−i n f o . frame ) ; }

struct animmodel : model { struct animspec { i n t frame , range ; f l o a t speed ; int priority ; }; struct animpos { i n t anim , fr1 , f r 2 ; float t ; void setframes ( const animinfo &i n f o ) { anim = i n f o . anim ; i f ( i n f o . range=1); cur ! = a . cur || ( : a . interp 0 && envmapmodels && ( renderpath ! =R FIXEDFUNCTION || maxtmus >= 3) ; } bool bumpmapped ( ) { return renderpath ! =R FIXEDFUNCTION && normalmap && bumpmodels ; } bool normals ( ) { return renderpath ! =R FIXEDFUNCTION || ( lightmodels && ! f u l l b r i g h t ) || envmapped ( ) || bumpmapped ( ) ; } bool tangents ( ) { return bumpmapped ( ) ; } void setuptmus ( const animstate ∗as , bool masked ) { i f ( fullbright ) { i f ( e n a b l e l i g h t i n g ) { glDisable ( GL LIGHTING ) ; e n a b l e l i g h t i n g = false ; } } e l s e i f ( lightmodels && ! e n a b l e l i g h t i n g ) { glEnable ( GL LIGHTING ) ; e n a b l e l i g h t i n g = true ; i f ( ! enablerescale ) { glEnable ( hasRN ? GL RESCALE NORMAL EXT : GL NORMALIZE) ; enablerescale = true ; } } i f ( masked! = enableglow ) l a s t t e x = lastmasks = NULL; f l o a t mincolor = as−>cur . anim&ANIM FULLBRIGHT ? fullbrightmodels /100.0 f : 0.0 f ; vec c o l o r = vec ( l i g h t c o l o r ) .max( mincolor ) , matcolor ( 1 , 1 , 1) ; i f ( masked ) { bool needenvmap = envmaptmu>=0 && envmapmax>0; i f ( enableoverbright ) disableoverbright ( ) ; i f ( ! enableglow || enableenvmap ! =needenvmap ) setuptmu ( 0 , ”1 , C @ T ” , needenvmap ? ”= Ta ” : ”= Ca ” ) ; i n t glowscale = glow>2 ? 4 : ( glow>1 || mincolor>1 ? 2 : 1) ; matcolor . div ( glowscale ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; i f ( ! enableglow || enableenvmap ! =needenvmap ) { i f ( ! enableglow ) glEnable ( GL TEXTURE 2D ) ; setuptmu ( 1 , ”P ∗ T ” , needenvmap ? ”= Pa ” : ” Pa ∗ Ta ” ) ; } scaletmu ( 1 , glowscale ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; enableglow = true ; } else { i f ( enableglow ) disableglow ( ) ; i f ( mincolor>1 && maxtmus>=1) { matcolor . div ( 2 ) ; i f ( ! enableoverbright ) { setuptmu ( 0 , ”C ∗ T x 2 ” ) ; enableoverbright = true ; } } e l s e i f ( enableoverbright ) disableoverbright ( ) ; } i f ( f u l l b r i g h t ) g l C o l o r 4 f ( matcolor . x∗f u l l b r i g h t , matcolor . y∗ f u l l b r i g h t , matcolor . z∗f u l l b r i g h t , transparent ) ; e l s e i f ( lightmodels ) { GLfloat material [ 4 ] = { matcolor . x , matcolor . y , matcolor . z ,

} void setshaderparams ( mesh ∗m, const animstate ∗as , bool masked ) { i f ( fullbright ) { g l C o l o r 4 f ( f u l l b r i g h t /2 , f u l l b r i g h t /2 , f u l l b r i g h t /2 , transparent ) ; setenvparamf ( ” l i g h t s c a l e ” , SHPARAM VERTEX, 2 , 0 , 0 , 2) ; setenvparamf ( ” l i g h t s c a l e ” , SHPARAM PIXEL, 2 , 0 , 0 , 2) ; } else { f l o a t mincolor = as−>cur . anim&ANIM FULLBRIGHT ? fullbrightmodels /100.0 f : 0.0 f , bias = max( mincolor−1.0f , 0.2 f ) , scale = 0.5 f∗max( 0 . 8 f− bias , 0.0 f ) , minshade = scale∗max( ambient , mincolor ) ; vec c o l o r = vec ( l i g h t c o l o r ) .max( mincolor ) ; g l C o l o r 4 f ( c o l o r . x , c o l o r . y , c o l o r . z , transparent ) ; setenvparamf ( ” l i g h t s c a l e ” , SHPARAM VERTEX, 2 , scale − minshade , scale , minshade + bias ) ; setenvparamf ( ” l i g h t s c a l e ” , SHPARAM PIXEL, 2 , scale − minshade , scale , minshade + bias ) ; } f l o a t curglow = glow ; i f ( glowpulse > 0) { f l o a t curpulse = l a s t m i l l i s∗glowpulse ; curpulse −= f l o o r ( curpulse ) ; curglow += glowdelta∗2∗fabs ( curpulse − 0.5 f ) ; } setenvparamf ( ” maskscale ” , SHPARAM PIXEL, 4 , 0.5 f∗spec∗ lightmodels , 0.5 f∗curglow∗glowmodels , 16∗specglare , 4∗ glowglare ) ; setenvparamf ( ” t e x s c r o l l ” , SHPARAM VERTEX, 5 , l a s t m i l l i s /1000.0 f , s c r o l l u∗l a s t m i l l i s /1000.0 f , s c r o l l v∗l a s t m i l l i s /1000.0 f ) ; i f ( envmaptmu>=0 && envmapmax>0) setenvparamf ( ” envmapscale ” , bumpmapped ( ) ? SHPARAM PIXEL : SHPARAM VERTEX, 3 , envmapmin−envmapmax, envmapmax) ; } Shader ∗loadshader ( bool shouldenvmap , bool masked ) { #define DOMODELSHADER( name, body ) \ do { \ s t a t i c Shader ∗name##shader = NULL; \ i f ( ! name##shader ) name##shader = useshaderbyname(#name) ; \ body ; \ } while ( 0 ) #define LOADMODELSHADER(name) DOMODELSHADER( name, return name## shader ) #define SETMODELSHADER(m, name) DOMODELSHADER( name, (m)−> setshader (name##shader ) ) i f ( shader ) return shader ; e l s e i f (bumpmapped ( ) ) { i f ( shouldenvmap ) { i f ( lightmodels && ! f u l l b r i g h t && ( masked || spec>=0.01 f ) ) LOADMODELSHADER( bumpenvmapmodel ) ; e l s e LOADMODELSHADER( bumpenvmapnospecmodel ) ; } e l s e i f ( masked && lightmodels && ! f u l l b r i g h t ) LOADMODELSHADER ( bumpmasksmodel ) ; e l s e i f ( masked && glowmodels ) LOADMODELSHADER( bumpmasksnospecmodel ) ; e l s e i f ( spec>=0.01 f && lightmodels && ! f u l l b r i g h t ) LOADMODELSHADER( bumpmodel ) ; e l s e LOADMODELSHADER( bumpnospecmodel ) ;

engine/animmodel.h } e l s e i f ( shouldenvmap ) { i f ( lightmodels && ! f u l l b r i g h t && ( masked || spec>=0.01 f ) ) LOADMODELSHADER( envmapmodel ) ; e l s e LOADMODELSHADER( envmapnospecmodel ) ; } e l s e i f ( masked && lightmodels && ! f u l l b r i g h t ) LOADMODELSHADER( masksmodel ) ; e l s e i f ( masked && glowmodels ) LOADMODELSHADER( masksnospecmodel ) ; e l s e i f ( spec>=0.01 f && lightmodels && ! f u l l b r i g h t ) LOADMODELSHADER( stdmodel ) ; e l s e LOADMODELSHADER( nospecmodel ) ;

glBindTexture ( GL TEXTURE 2D, n−>id ) ; lastnormalmap = n ; } i f ( s−>type&Texture : : ALPHA) { i f ( alphablend ) { i f ( ! enablealphablend && ! r e f l e c t i n g && ! r e f r a c t i n g ) { glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; enablealphablend = true ; } } e l s e i f ( enablealphablend ) { glDisable (GL BLEND) ; enablealphablend = f a l s e ; } i f ( alphatest >0) { i f ( ! enablealphatest ) { glEnable ( GL ALPHA TEST ) ; enablealphatest = true ; } i f ( l a s t a l p h a t e s t ! = alphatest ) { glAlphaFunc (GL GREATER, alphatest ) ; l a s t a l p h a t e s t = alphatest ; } } e l s e i f ( enablealphatest ) { glDisable ( GL ALPHA TEST ) ; enablealphatest = f a l s e ; } } else { i f ( enablealphatest ) { glDisable ( GL ALPHA TEST ) ; enablealphatest = f a l s e ; } i f ( enablealphablend && transparent>=1) { glDisable (GL BLEND) ; enablealphablend = f a l s e ; } } i f (m! = lastmasks && m! = notexture ) { i f ( ! enableglow ) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; activetmu = 1; } e l s e i f ( activetmu ! = 0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; activetmu = 0; } glBindTexture ( GL TEXTURE 2D, m−>id ) ; lastmasks = m; } i f ( ( renderpath ! =R FIXEDFUNCTION || m! = notexture ) && envmaptmu>=0 && envmapmax>0) { GLuint emtex = envmap ? envmap−>id : closestenvmaptex ; i f ( ! enableenvmap || lastenvmaptex ! = emtex ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+envmaptmu ) ; activetmu = envmaptmu; i f ( ! enableenvmap ) { glEnable (GL TEXTURE CUBE MAP ARB) ; i f ( ! lastenvmaptex && renderpath==R FIXEDFUNCTION ) { glTexGeni ( GL S , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glTexGeni ( GL T , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glTexGeni ( GL R , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glEnable ( GL TEXTURE GEN S ) ; glEnable ( GL TEXTURE GEN T ) ; glEnable ( GL TEXTURE GEN R ) ; } enableenvmap = true ; i f ( ! enablerescale ) { glEnable ( hasRN ? GL RESCALE NORMAL EXT : GL NORMALIZE) ; enablerescale = true ; } } i f ( lastenvmaptex ! = emtex ) { glBindTexture ( GL TEXTURE CUBE MAP ARB, emtex ) ; lastenvmaptex = emtex ; } } } e l s e i f ( enableenvmap ) { disableenvmap ( ) ; activetmu = 0; } i f ( activetmu ! = 0) g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ;

} void preloadBIH ( ) { i f ( tex && tex−>type&Texture : : ALPHA && ! tex−>alphamask ) loadalphamask ( tex ) ; } void preloadshader ( ) { bool shouldenvmap = envmapped ( ) ; i f ( masks−>type&Texture : : STUB && ! strncmp ( masks−>name, ””, 6) ) { f l o a t glowscale = glow / ( glow>2 ? 4 : ( glow>1 ? 2 : 1) ) , envscale = envmapmax > 0 ? 0.2 f∗envmapmax + 0.8 f∗ envmapmin : 0; defformatstring ( ffmask ) (””, f l o o r ( glowscale ∗16 + 0.5 f ) /16, f l o o r ( envscale∗16 + 0.5 f ) /16) ; masks = textureload ( makerelpath (NULL, masks−>name + 6 , NULL, ffmask ) , 0 , true , f a l s e ) ; } loadshader ( shouldenvmap , masks! = notexture && ! ( masks−>type& Texture : : STUB) && ( lightmodels || glowmodels || shouldenvmap ) ) ; } void setshader ( mesh ∗m, const animstate ∗as , bool masked ) { m−>setshader ( loadshader ( envmaptmu>=0 && envmapmax>0, masked ) ) ; } void bind ( mesh ∗b , const animstate ∗as ) { i f ( ! c u l l f a c e && enablecullface ) { glDisable ( GL CULL FACE ) ; enablecullface = f a l s e ; } e l s e i f ( c u l l f a c e && ! enablecullface ) { glEnable ( GL CULL FACE ) ; enablecullface = true ; } i f ( as−>cur . anim&ANIM NOSKIN ) { i f ( enablealphatest ) { glDisable ( GL ALPHA TEST ) ; enablealphatest = f a l s e ; } i f ( enablealphablend ) { glDisable (GL BLEND) ; enablealphablend = false ; } i f ( enableglow ) disableglow ( ) ; i f ( enableenvmap ) disableenvmap ( ) ; i f ( e n a b l e l i g h t i n g ) { glDisable ( GL LIGHTING ) ; e n a b l e l i g h t i n g = false ; } i f ( enablerescale ) { glDisable ( hasRN ? GL RESCALE NORMAL EXT : GL NORMALIZE) ; enablerescale = f a l s e ; } i f ( shadowmapping ) SETMODELSHADER( b , shadowmapcaster ) ; e l s e /∗ i f ( as−>cur . anim&ANIM SHADOW) ∗/ SETMODELSHADER( b , notexturemodel ) ; return ; } Texture ∗s = bumpmapped ( ) && u n l i t t e x ? u n l i t t e x : tex , ∗m = masks−>type&Texture : : STUB ? notexture : masks, ∗n = bumpmapped ( ) ? normalmap : NULL; i f ( ( renderpath==R FIXEDFUNCTION || ! lightmodels ) && ! glowmodels && ( ! envmapmodels || envmaptmus e t v a r i a n t ( 0 , 2) ; e l s e s−>set ( ) ; } template void smoothnorms ( V ∗verts , i n t numverts , T ∗t r i s , i n t numtris , f l o a t l i m i t , bool areaweight ) { hashtable share ; i n t ∗next = new i n t [ numverts ] ; memset ( next , −1, numverts∗s i z e o f ( i n t ) ) ; l o o p i ( numverts ) { V &v = v e r t s [ i ] ; v . norm = vec ( 0 , 0 , 0) ; i n t idx = share . access ( v . pos , i ) ; i f ( idx ! = i ) { next [ i ] = next [ idx ] ; next [ idx ] = i ; } } l o o p i ( numtris ) { T &t = t r i s [ i ] ; V &v1 = v e r t s [ t . v e r t [ 0 ] ] , &v2 = v e r t s [ t . v e r t [ 1 ] ] , &v3 = v e r t s [ t . vert [ 2 ] ] ; vec norm ; norm . cross ( vec ( v2 . pos ) . sub ( v1 . pos ) , vec ( v3 . pos ) . sub ( v1 . pos ) ) ; i f ( ! areaweight ) norm . normalize ( ) ; v1 . norm . add ( norm ) ; v2 . norm . add ( norm ) ; v3 . norm . add ( norm ) ; } vec ∗norms = new vec [ numverts ] ; memset ( norms , 0 , numverts∗s i z e o f ( vec ) ) ; l o o p i ( numverts ) { V &v = v e r t s [ i ] ; norms [ i ] . add ( v . norm ) ; i f ( next [ i ] >= 0) { f l o a t v l i m i t = l i m i t∗v . norm . magnitude ( ) ; f o r ( i n t j = next [ i ] ; j >= 0; j = next [ j ] ) { V &o = v e r t s [ j ] ; i f ( v . norm . dot ( o . norm ) >= v l i m i t∗o . norm . magnitude ( ) ) { norms [ i ] . add ( o . norm ) ; norms [ j ] . add ( v . norm ) ; } } } } l o o p i ( numverts ) v e r t s [ i ] . norm = norms [ i ] . normalize ( ) ; d e l e t e [ ] next ; d e l e t e [ ] norms ; }

i f ( vec ( ) . cross ( e2 , e1 ) . dot ( vec ( ) . cross ( v , u ) ) >= 0) { u . neg ( ) ; v . neg ( ) ; } i f ( ! areaweight ) { u . normalize ( ) ; v . normalize ( ) ; } loopj ( 3 ) { tangent [ t . v e r t [ j ] ] . sub ( u ) ; bitangent [ t . v e r t [ j ] ] . add ( v ) ; } } l o o p i ( numverts ) { const vec &n = v e r t s [ i ] . norm, &t = tangent [ i ] , &bt = bitangent [ i ] ; B &bv = bumpverts [ i ] ; ( bv . tangent = t ) . sub ( vec ( n ) . mul ( n . dot ( t ) ) ) . normalize ( ) ; bv . bitangent = vec ( ) . cross ( n , t ) . dot ( bt ) < 0 ? −1 : 1; } d e l e t e [ ] tangent ; } }; struct meshgroup { meshgroup ∗next ; i n t shared ; char ∗name; vector meshes ; meshgroup ( ) : next (NULL) , shared ( 0 ) , name(NULL) { } v i r t u a l ˜meshgroup ( ) { DELETEA(name) ; meshes . deletecontents ( ) ; DELETEP( next ) ; } v i r t u a l i n t f i n d t a g ( const char ∗name) { return −1; } v i r t u a l void concattagtransform ( part ∗p , i n t frame , i n t i , const matrix3x4 &m, matrix3x4 &n ) {}

template void buildnorms ( V ∗verts , i n t numverts , T ∗t r i s , i n t numtris , bool areaweight ) { l o o p i ( numverts ) v e r t s [ i ] . norm = vec ( 0 , 0 , 0) ; l o o p i ( numtris ) { T &t = t r i s [ i ] ; V &v1 = v e r t s [ t . v e r t [ 0 ] ] , &v2 = v e r t s [ t . v e r t [ 1 ] ] , &v3 = v e r t s [ t . vert [ 2 ] ] ; vec norm ; norm . cross ( vec ( v2 . pos ) . sub ( v1 . pos ) , vec ( v3 . pos ) . sub ( v1 . pos ) ) ; i f ( ! areaweight ) norm . normalize ( ) ; v1 . norm . add ( norm ) ; v2 . norm . add ( norm ) ; v3 . norm . add ( norm ) ; } l o o p i ( numverts ) v e r t s [ i ] . norm . normalize ( ) ; } template void calctangents (B ∗ bumpverts , V ∗verts , TC ∗t c v e r t s , i n t numverts , T ∗t r i s , i n t numtris , bool areaweight ) { vec ∗tangent = new vec[2∗numverts ] , ∗bitangent = tangent+ numverts ; memset ( tangent , 0 , 2∗numverts∗s i z e o f ( vec ) ) ; l o o p i ( numtris )

void calcbb ( i n t frame , vec &bbmin , vec &bbmax, const matrix3x4 &m) { loopv ( meshes ) meshes [ i]−>calcbb ( frame , bbmin , bbmax, m) ; } void g e n t r i s ( i n t frame , vector &skins , vector ∗t r i s , const matrix3x4 &m) { loopv ( meshes ) meshes [ i]−>g e n t r i s ( frame , skins [ i ] . tex && skins [ i ] . tex−>type&Texture : : ALPHA ? skins [ i ] . tex : NULL, t r i s , m ); } v i r t u a l i n t totalframes ( ) const { return 1; bool hasframe ( i n t i ) const { return i>=0 && bool hasframes ( i n t i , i n t n ) const { return totalframes ( ) ; } i n t clipframes ( i n t i , i n t n ) const { return i); } v i r t u a l void v i r t u a l void v i r t u a l void axis , };

} i=0 && i +nl i n k ( p , tag , translate , anim , basetime , pos ) ) return true ; return f a l s e ;

v i r t u a l meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { return NULL; } meshgroup ∗sharemeshes ( const char ∗name, . . . ) { s t a t i c hashtable meshgroups ; i f ( ! meshgroups . access (name) ) { v a l i s t args ; v a s t a r t ( args , name) ; meshgroup ∗group = loadmeshes ( name, args ) ; va end ( args ) ; i f ( ! group ) return NULL; meshgroups [ group−>name] = group ; } return meshgroups [name ] ; } struct linkedpart { part ∗p ; i n t tag , anim , basetime ; vec t r a n s l a t e ; vec ∗pos ; g l m a t r i x f matrix ;

} linkedpart &l = l i n k s . add ( ) ; l .p = p; l . tag = i ; l . anim = anim ; l . basetime = basetime ; l . translate = translate ; l . pos = pos ; return true ; } bool unlink ( part ∗p ) { loopvrev ( l i n k s ) i f ( l i n k s [ i ] . p==p ) { l i n k s . remove ( i , 1) ; return true ; } loopv ( l i n k s ) i f ( l i n k s [ i ] . p && l i n k s [ i ] . p−>unlink ( p ) ) return true ; return f a l s e ; } void i n i t s k i n s ( Texture ∗tex = notexture , Texture ∗masks = notexture , i n t l i m i t = 0) { i f ( ! limit ) { i f ( ! meshes ) return ; l i m i t = meshes−>meshes . length ( ) ; } while ( skins . length ( ) < l i m i t ) { skin &s = skins . add ( ) ; s . owner = t h i s ; s . tex = tex ; s . masks = masks ; }

linkedpart ( ) : p (NULL) , tag(−1) , anim(−1) , basetime ( 0 ) , t r a n s l a t e ( 0 , 0 , 0) , pos (NULL) {} }; struct part { animmodel ∗model ; i n t index ; meshgroup ∗meshes ; vector l i n k s ; vector skins ; vector ∗anims [MAXANIMPARTS ] ; i n t numanimparts ; f l o a t pitchscale , p i t c h o f f s e t , pitchmin , pitchmax ; vec t r a n s l a t e ; part ( ) : meshes (NULL) , numanimparts ( 1 ) , pitchscale ( 1 ) , p i t c h o f f s e t ( 0 ) , pitchmin ( 0 ) , pitchmax ( 0 ) , t r a n s l a t e ( 0 , 0 , 0) { loopk (MAXANIMPARTS) anims [ k ] = NULL; } v i r t u a l ˜ part ( ) { loopk (MAXANIMPARTS) DELETEA( anims [ k ] ) ; } v i r t u a l void cleanup ( ) { i f ( meshes ) meshes−>cleanup ( ) ; }

197

} void preloadBIH ( ) { loopv ( skins ) skins [ i ] . preloadBIH ( ) ; } void preloadshaders ( ) { loopv ( skins ) skins [ i ] . preloadshader ( ) ; } void preloadmeshes ( ) { i f ( meshes ) meshes−>preload ( t h i s ) ; } v i r t u a l void getdefaultanim ( animinfo &info , i n t anim , uint varseed , dynent ∗d ) {

void calcbb ( i n t frame , vec &bbmin , vec &bbmax, const matrix3x4 &m) { matrix3x4 t = m; t . translate ( translate ) ; t . scale ( model−>scale ) ; meshes−>calcbb ( frame , bbmin , bbmax, t ) ; loopv ( l i n k s ) { matrix3x4 n ; meshes−>concattagtransform ( this , frame , l i n k s [ i ] . tag , m, n ) ; n . transformedtranslate ( l i n k s [ i ] . translate , model−>scale ) ; l i n k s [ i ] . p−>calcbb ( frame , bbmin , bbmax, n ) ; } } void g e n t r i s ( i n t frame , vector ∗t r i s , const matrix3x4 &m) { matrix3x4 t = m; t . translate ( translate ) ; t . scale ( model−>scale ) ; meshes−>g e n t r i s ( frame , skins , t r i s , t ) ; loopv ( l i n k s ) { matrix3x4 n ; meshes−>concattagtransform ( this , frame , l i n k s [ i ] . tag , m, n ) ; n . transformedtranslate ( l i n k s [ i ] . translate , model−>scale ) ; l i n k s [ i ] . p−>g e n t r i s ( frame , t r i s , n ) ; } } bool l i n k ( part ∗p , const char ∗tag , const vec &t r a n s l a t e = vec ( 0 , 0 , 0) , i n t anim = −1, i n t basetime = 0 , vec ∗pos = NULL) { i n t i = meshes ? meshes−>f i n d t a g ( tag ) : −1; i f ( i totalframes ( ) ; } else { animspec ∗spec = NULL; i f ( anims [ animpart ] ) { vector &primary = anims [ animpart ] [ anim& ANIM INDEX ] ; i f (&primary < &anims [ animpart ] [NUMANIMS] && primary . length ( ) ) spec = &primary [ uint ( varseed + basetime )% primary . length ( ) ] ; i f ( ( anim>>ANIM SECONDARY) &(ANIM INDEX|ANIM DIR ) ) { vector &secondary = anims [ animpart ] [ ( anim>> ANIM SECONDARY)&ANIM INDEX ] ; i f (&secondary < &anims [ animpart ] [NUMANIMS] && secondary . length ( ) ) { animspec &spec2 = secondary [ uint ( varseed + basetime2 )%secondary . length ( ) ] ;

198

Foundations of Videogame Programming Code Repository i f ( ! spec || spec2 . p r i o r i t y > spec−>p r i o r i t y ) { spec = &spec2 ; i n f o . anim >>= ANIM SECONDARY; i n f o . basetime = basetime2 ; } } } } i f ( spec ) { i n f o . frame = spec−>frame ; i n f o . range = spec−>range ; i f ( spec−>speed>0) i n f o . speed = 1000.0 f /spec−>speed ; } e l s e getdefaultanim ( info , anim , uint ( varseed + i n f o . basetime ) , d) ; } i n f o . anim &= (1hasframe ( i n f o . frame ) ) return f a l s e ; i n f o . range = meshes−>clipframes ( i n f o . frame , i n f o . range ) ; } i f ( d && interp >=0) { animinterpinfo &a i = d−>animinterp [ interp ] ; i f ( ( i n f o . anim&ANIM CLAMP) ==ANIM CLAMP) aitime = min ( aitime , i n t ( i n f o . range∗i n f o . speed∗0.5e−3f ) ) ; i f ( d−>r a g d o l l && ! ( anim&ANIM RAGDOLL) ) { a i . prev . range = a i . cur . range = 0; a i . lastswitch = −1; } e l s e i f ( a i . lastmodel ! = t h i s || a i . lastswitchlastrendered>aitime ) { a i . prev = a i . cur = i n f o ; a i . lastswitch = l a s t m i l l i s−aitime ∗2; } e l s e i f ( a i . cur ! = i n f o ) { i f ( l a s t m i l l i s−a i . lastswitch>aitime /2) a i . prev = a i . cur ; a i . cur = i n f o ; a i . lastswitch = l a s t m i l l i s ; } e l s e i f ( i n f o . anim&ANIM SETTIME ) a i . cur . basetime = i n f o . basetime ; a i . lastmodel = t h i s ; } return true ;

} void render ( i n t anim , i n t basetime , i n t basetime2 , f l o a t pitch , const vec &axis , const vec &forward , dynent ∗d ) { animstate as [MAXANIMPARTS ] ; render ( anim , basetime , basetime2 , pitch , axis , forward , d , as ) ; } void render ( i n t anim , i n t basetime , i n t basetime2 , f l o a t pitch , const vec &axis , const vec &forward , dynent ∗d , animstate ∗ as ) { i f ( ! ( anim&ANIM REUSE) ) l o o p i ( numanimparts ) { animinfo i n f o ; i n t interp = d && index+numanimparts=0 && d−>animinterp [ interp ] . prev . range>0)

{ i n t d i f f = l a s t m i l l i s−d−>animinterp [ interp ] . lastswitch ; i f ( d i f faniminterp [ interp ] . prev ) ; p . interp = d i f f / f l o a t ( aitime ) ; } } } vec oaxis , oforward ; matrixstack [ matrixpos ] . transposedtransformnormal ( axis , oaxis ) ; f l o a t pitchamount = pitchscale∗pitch + p i t c h o f f s e t ; i f ( pitchmin || pitchmax ) pitchamount = clamp ( pitchamount , pitchmin , pitchmax ) ; i f ( as−>cur . anim&ANIM NOPITCH || ( as−>interp < 1 && as−>prev . anim &ANIM NOPITCH ) ) pitchamount ∗= ( as−>cur . anim&ANIM NOPITCH ? 0 : as−>interp ) + ( as−>interp < 1 && as−>prev . anim&ANIM NOPITCH ? 0 : 1−as−>interp ) ; i f ( pitchamount ) { ++matrixpos ; matrixstack [ matrixpos ] = matrixstack [ matrixpos −1]; matrixstack [ matrixpos ] . r o t a t e ( pitchamount∗RAD, oaxis ) ; } matrixstack [ matrixpos ] . transposedtransformnormal ( forward , oforward ) ; i f ( ! ( anim&ANIM NORENDER) ) { glPushMatrix ( ) ; glMultMatrixf ( matrixstack [ matrixpos ] . v ) ; i f ( model−>scale ! = 1 ) g l S c a l e f ( model−>scale , model−>scale , model−>scale ) ; i f ( ! translate . iszero ( ) ) glTranslatef ( translate . x , translate . y , translate . z ) ; i f ( renderpath ! =R FIXEDFUNCTION && envmaptmu>=0) { glMatrixMode (GL TEXTURE) ; glLoadMatrixf ( matrixstack [ matrixpos ] . v ) ; glMatrixMode (GL MODELVIEW) ; } } i f ( ! ( anim&(ANIM NOSKIN|ANIM NORENDER) ) ) { i f ( renderpath ! =R FIXEDFUNCTION ) { vec odir , ocampos ; matrixstack [ matrixpos ] . transposedtransformnormal ( l i g h t d i r , odir ) ; setenvparamf ( ” l i g h t d i r ” , SHPARAM VERTEX, 0 , odir . x , odir . y , odir . z ) ; setenvparamf ( ” l i g h t d i r ” , SHPARAM PIXEL, 0 , odir . x , odir . y , odir . z ) ; matrixstack [ matrixpos ] . transposedtransform ( camera1−>o , ocampos ) ; ocampos . div ( model−>scale ) . sub ( t r a n s l a t e ) ; setenvparamf ( ” camera ” , SHPARAM VERTEX, 1 , ocampos . x , ocampos . y , ocampos . z , 1) ; } } meshes−>render ( as , pitch , oaxis , oforward , d , t h i s ) ; i f ( ! ( anim&ANIM NORENDER) ) { glPopMatrix ( ) ; } i f ( ! ( anim&ANIM REUSE) ) { loopv ( l i n k s ) { linkedpart &l i n k = l i n k s [ i ] ; l i n k . matrix . transformedtranslate ( l i n k s [ i ] . translate , model −>scale ) ; matrixpos ++; matrixstack [ matrixpos ] . mul ( matrixstack [ matrixpos −1], l i n k . matrix ) ; i f ( l i n k . pos ) ∗l i n k . pos = matrixstack [ matrixpos ] . gettranslation ( ) ; i f ( ! link .p ) { matrixpos−−; continue ; } i n t nanim = anim , nbasetime = basetime , nbasetime2 = basetime2 ;

engine/animmodel.h i f ( l i n k . anim>=0) { nanim = l i n k . anim | ( anim&ANIM FLAGS ) ; nbasetime = l i n k . basetime ; nbasetime2 = 0; } l i n k . p−>render ( nanim , nbasetime , nbasetime2 , pitch , axis , forward , d ) ;

199

i f ( a [ i ] . pos ) unlink (NULL) ; continue ; } part ∗p = m−>parts [ 0 ] ; switch ( linktype (m) ) { case LINK TAG : i f ( p−>index >= 0) unlink ( p ) ; p−>index = 0; break ;

matrixpos−−; } }

case LINK COOP : p−>render ( anim , basetime , basetime2 , pitch , axis , forward , d) ; p−>index = 0; break ;

i f ( pitchamount ) matrixpos−−; } void setanim ( i n t animpart , i n t num, i n t frame , i n t range , f l o a t speed , i n t p r i o r i t y = 0)

case LINK REUSE : p−>render ( anim | ANIM REUSE, basetime , basetime2 , pitch , axis , forward , d , as ) ; break ;

{ i f ( animpart=MAXANIMPARTS) return ; i f ( frameloadname ) ; return ; } i f ( ! anims [ animpart ] ) anims [ animpart ] = new vector[ NUMANIMS] ; animspec &spec = anims [ animpart ] [num] . add ( ) ; spec . frame = frame ; spec . range = range ; spec . speed = speed ; spec . p r i o r i t y = p r i o r i t y ; }

} } } void render ( i n t anim , i n t basetime , i n t basetime2 , const vec &o , f l o a t yaw , f l o a t pitch , dynent ∗d , modelattach ∗a , const vec &color , const vec &dir , f l o a t trans ) { i f ( ! loaded ) return ; yaw += spinyaw∗l a s t m i l l i s /1000.0 f ; pitch += o f f s e t p i t c h + spinpitch∗l a s t m i l l i s /1000.0 f ; vec axis ( 0 , −1, 0) , forward ( 1 , 0 , 0) ;

}; enum { LINK TAG = 0 , LINK COOP, LINK REUSE }; v i r t u a l i n t linktype ( animmodel ∗m) const { return LINK TAG ; } void render ( i n t anim , i n t basetime , i n t basetime2 , f l o a t pitch , const vec &axis , const vec &forward , dynent ∗d , modelattach ∗a ) { i f ( ! loaded ) return ; i n t numtags = 0; if (a) { i n t index = parts . l a s t ( )−>index + parts . l a s t ( )−>numanimparts ; f o r ( i n t i = 0; a [ i ] . tag ; i ++) { numtags++; animmodel ∗m = ( animmodel ∗)a [ i ] .m; i f ( !m || !m−>loaded ) { i f ( a [ i ] . pos ) l i n k (NULL, a [ i ] . tag , vec ( 0 , 0 , 0) , 0 , 0 , a [ i ] . pos ) ; continue ; } part ∗p = m−>parts [ 0 ] ; switch ( linktype (m) ) { case LINK TAG : p−>index = l i n k ( p , a [ i ] . tag , vec ( 0 , 0 , 0) , a [ i ] . anim , a [ i ] . basetime , a [ i ] . pos ) ? index : −1; break ; case LINK COOP : p−>index = index ; break ; default : continue ; } index += p−>numanimparts ; }

matrixpos = 0; matrixstack [ 0 ] . i d e n t i t y ( ) ; i f ( ! d || ! d−>r a g d o l l || anim&ANIM RAGDOLL) { matrixstack [ 0 ] . t r a n s l a t e ( o ) ; matrixstack [ 0 ] . rotate around z ( yaw∗RAD) ; matrixstack [ 0 ] . transformnormal ( vec ( axis ) , axis ) ; matrixstack [ 0 ] . transformnormal ( vec ( forward ) , forward ) ; i f ( offsetyaw ) matrixstack [ 0 ] . rotate around z ( offsetyaw∗RAD) ; } e l s e pitch = 0; i f ( anim&ANIM NORENDER) { render ( anim , basetime , basetime2 , pitch , axis , forward , d , a ) ; i f ( d ) d−>lastrendered = l a s t m i l l i s ; return ; } i f ( ! ( anim&ANIM NOSKIN ) ) { i f ( renderpath==R FIXEDFUNCTION && lightmodels ) { GLfloat pos [ 4 ] = { d i r . x∗1000, d i r . y∗1000, d i r . z∗1000, 0 }; g l L i g h t f v ( GL LIGHT0 , GL POSITION , pos ) ; } transparent = trans ; lightdir = dir ; lightcolor = color ; i f ( envmapped ( ) ) envmaptmu = 2; e l s e i f ( a ) f o r ( i n t i = 0; a [ i ] . tag ; i ++) i f ( a [ i ] .m && a [ i ] .m−> envmapped ( ) ) { envmaptmu = 2; break ; } i f ( envmaptmu>=0) closestenvmaptex = lookupenvmap ( closestenvmap ( o )); } i f ( depthoffset && ! enabledepthoffset ) { enablepolygonoffset ( GL POLYGON OFFSET FILL ) ; enabledepthoffset = true ; }

} animstate as [MAXANIMPARTS ] ; parts[0]−>render ( anim , basetime , basetime2 , pitch , axis , forward , d , as ) ; i f ( a ) f o r ( i n t i = numtags−1; i >= 0; i−−) { animmodel ∗m = ( animmodel ∗)a [ i ] .m; i f ( !m || !m−>loaded ) {

i f ( envmaptmu>=0) { i f ( renderpath==R FIXEDFUNCTION ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+envmaptmu ) ; setuptmu ( envmaptmu, ”T , P @ Pa ” , hasTEX || hasTE4 ? ”Ca ∗ $1a” : ”= Ca ” ) ; glMatrixMode (GL TEXTURE) ; glLoadMatrixf ( envmatrix . v ) ;

200

Foundations of Videogame Programming Code Repository glMatrixMode (GL MODELVIEW) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; } else { setenvparamf ( ” l i g h t d i r w o r l d ” , SHPARAM PIXEL, 1 , d i r . x , d i r . y , dir . z ) ; }

} i f ( transparentpreloadBIH ( ) ; } BIH ∗setBIH ( ) { i f ( bih ) return bih ; vector t r i s [ 2 ] ; gentris (0 , t r i s ) ; bih = new BIH ( t r i s ) ; return bih ; } bool l i n k ( part ∗p , const char ∗tag , const vec &t r a n s l a t e = vec ( 0 , 0 , 0) , i n t anim = −1, i n t basetime = 0 , vec ∗pos = NULL) { i f ( parts . empty ( ) ) return f a l s e ; return parts[0]−>l i n k ( p , tag , translate , anim , basetime , pos ) ; } bool unlink ( part ∗p ) { i f ( parts . empty ( ) ) return f a l s e ; return parts[0]−>unlink ( p ) ; } bool envmapped ( ) { loopv ( parts ) l o o p v j ( parts [ i]−>skins ) i f ( parts [ i]−>skins [ j ] . envmapped ( ) ) return true ; return f a l s e ; }

render ( anim , basetime , basetime2 , pitch , axis , forward , d , a ) ; i f ( envmaptmu>=0) { i f ( renderpath==R FIXEDFUNCTION ) g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB +envmaptmu ) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; i f ( renderpath==R FIXEDFUNCTION ) g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ); } i f ( transparentlastrendered = l a s t m i l l i s ; } bool loaded ; char ∗loadname ; vector parts ; animmodel ( const char ∗name) : loaded ( f a l s e ) { loadname = newstring (name) ; } v i r t u a l ˜animmodel ( ) { d e l e t e [ ] loadname ; parts . deletecontents ( ) ; }

v i r t u a l bool loaddefaultparts ( ) { return true ; } void preloadshaders ( ) { loopv ( parts ) parts [ i]−>preloadshaders ( ) ; } void preloadmeshes ( ) { loopv ( parts ) parts [ i]−>preloadmeshes ( ) ; } void setshader ( Shader ∗shader ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . shader = shader ; } void setenvmap ( f l o a t envmapmin, f l o a t envmapmax, Texture ∗envmap ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) { skin &s = parts [ i]−>skins [ j ] ; i f (envmapmax) { s . envmapmin = envmapmin ; s .envmapmax = envmapmax; } i f ( envmap ) s . envmap = envmap ; } }

const char ∗name ( ) const { return loadname ; } void cleanup ( ) { loopv ( parts ) parts [ i]−>cleanup ( ) ; enablelight0 = f a l s e ; }

void setspec ( f l o a t spec ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . spec = spec ; }

void i n i t m a t r i x ( matrix3x4 &m) { m. i d e n t i t y ( ) ; i f ( offsetyaw ) m. rotate around z ( offsetyaw∗RAD) ; i f ( o f f s e t p i t c h ) m. rotate around y(−o f f s e t p i t c h∗RAD) ; }

void setambient ( f l o a t ambient ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . ambient = ambient ; }

void g e n t r i s ( i n t frame , vector ∗t r i s ) { i f ( parts . empty ( ) ) return ; matrix3x4 m; i n i t m a t r i x (m) ; parts[0]−>g e n t r i s ( frame , t r i s , m) ;

void setglow ( f l o a t glow , f l o a t delta , f l o a t pulse ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) { skin &s = parts [ i]−>skins [ j ] ;

engine/animmodel.h s . glow = glow ; s . glowdelta = d e l t a ; s . glowpulse = pulse ; }

201

g l M a t e r i a l f v ( GL FRONT, GL EMISSION, zero ) ; enablelight0 = true ; } }

} void s e t g l a r e ( f l o a t specglare , f l o a t glowglare ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) { skin &s = parts [ i]−>skins [ j ] ; s . specglare = specglare ; s . glowglare = glowglare ; } } void setalphatest ( f l o a t alphatest ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . alphatest = alphatest ; } void setalphablend ( bool alphablend ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . alphablend = alphablend ; } void s e t f u l l b r i g h t ( f l o a t f u l l b r i g h t ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . f u l l b r i g h t = fullbright ; } void s e t c u l l f a c e ( bool c u l l f a c e ) { i f ( parts . empty ( ) ) loaddefaultparts ( ) ; loopv ( parts ) l o o p v j ( parts [ i]−>skins ) parts [ i]−>skins [ j ] . c u l l f a c e = cullface ; } void calcbb ( i n t frame , vec ¢er , vec &radius ) { i f ( parts . empty ( ) ) return ; vec bbmin(1 e16f , 1e16f , 1e16f ) , bbmax(−1e16f , −1e16f , −1e16f ) ; matrix3x4 m; i n i t m a t r i x (m) ; parts[0]−>calcbb ( frame , bbmin , bbmax, m) ; radius = bbmax; radius . sub ( bbmin ) ; radius . mul( 0 . 5 f ) ; center = bbmin ; center . add ( radius ) ; } s t a t i c bool enabletc , enablemtc , enablealphatest , enablealphablend , enableenvmap , enableglow , enableoverbright , enablelighting , enablelight0 , enablecullface , enablenormals , enabletangents , enablebones , enablerescale , enabledepthoffset ; s t a t i c vec l i g h t d i r , l i g h t c o l o r ; s t a t i c f l o a t transparent , l a s t a l p h a t e s t ; s t a t i c void ∗lastvbuf , ∗l a s t t c b u f , ∗lastmtcbuf , ∗lastnbuf , ∗lastxbuf , ∗lastbbuf , ∗lastsdata , ∗lastbdata ; s t a t i c GLuint lastebuf , lastenvmaptex , closestenvmaptex ; s t a t i c Texture ∗l a s t t e x , ∗lastmasks , ∗lastnormalmap ; s t a t i c i n t envmaptmu, matrixpos ; s t a t i c g l m a t r i x f matrixstack [ 6 4 ] ; void startrender ( ) { enabletc = enablemtc = enablealphatest = enablealphablend = enableenvmap = enableglow = enableoverbright = e n a b l e l i g h t i n g = enablenormals = enabletangents = enablebones = enablerescale = enabledepthoffset = f a l s e ; enablecullface = true ; l a s t a l p h a t e s t = −1; lastvbuf = l a s t t c b u f = lastmtcbuf = lastxbuf = lastnbuf = lastbbuf = lastsdata = lastbdata = NULL; lastebuf = lastenvmaptex = closestenvmaptex = 0; l a s t t e x = lastmasks = lastnormalmap = NULL; envmaptmu = −1; transparent = 1; i f ( renderpath==R FIXEDFUNCTION && lightmodels && ! enablelight0 ) { glEnable ( GL LIGHT0 ) ; s t a t i c const GLfloat zero [ 4 ] = { 0 , 0 , 0 , 0 }; glLightModelfv ( GL LIGHT MODEL AMBIENT, zero ) ; g l L i g h t f v ( GL LIGHT0 , GL SPECULAR, zero ) ; g l M a t e r i a l f v ( GL FRONT, GL SPECULAR, zero ) ;

s t a t i c void disablebones ( ) { glDisableVertexAttribArray ( 6 ) ; glDisableVertexAttribArray ( 7 ) ; enablebones = f a l s e ; } s t a t i c void disabletangents ( ) { glDisableVertexAttribArray ( 1 ) ; enabletangents = f a l s e ; } s t a t i c void disablemtc ( ) { g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; enablemtc = f a l s e ; } s t a t i c void d i s a b l e t c ( ) { g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; i f ( enablemtc ) disablemtc ( ) ; enabletc = f a l s e ; } s t a t i c void disablenormals ( ) { g l D i s a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ; enablenormals = f a l s e ; } s t a t i c void disablevbo ( ) { i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; i f ( enabletc ) d i s a b l e t c ( ) ; i f ( enablenormals ) disablenormals ( ) ; i f ( enabletangents ) disabletangents ( ) ; i f ( enablebones ) disablebones ( ) ; lastvbuf = l a s t t c b u f = lastmtcbuf = lastxbuf = lastnbuf = lastbbuf = NULL; lastebuf = 0; } s t a t i c void disableoverbright ( ) { resettmu ( 0 ) ; enableoverbright = f a l s e ; } s t a t i c void disableglow ( ) { resettmu ( 0 ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; resettmu ( 1 ) ; glDisable ( GL TEXTURE 2D ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; l a s t t e x = lastmasks = NULL; enableglow = f a l s e ; } s t a t i c void disableenvmap ( bool cleanup = f a l s e ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+envmaptmu ) ; i f ( enableenvmap ) glDisable (GL TEXTURE CUBE MAP ARB) ; i f ( cleanup && renderpath==R FIXEDFUNCTION ) { resettmu ( envmaptmu ) ; glDisable ( GL TEXTURE GEN S ) ; glDisable ( GL TEXTURE GEN T ) ; glDisable ( GL TEXTURE GEN R ) ; } g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; enableenvmap = f a l s e ; } void endrender ( ) { i f ( lastvbuf || lastebuf ) disablevbo ( ) ; i f ( enablealphatest ) glDisable ( GL ALPHA TEST ) ; i f ( enablealphablend ) glDisable (GL BLEND) ; i f ( enableglow ) disableglow ( ) ;

202

Foundations of Videogame Programming Code Repository if if if if

( enableoverbright ) disableoverbright ( ) ; ( e n a b l e l i g h t i n g ) glDisable ( GL LIGHTING ) ; ( lastenvmaptex ) disableenvmap ( true ) ; ( enablerescale ) glDisable ( hasRN ? GL RESCALE NORMAL EXT : GL NORMALIZE) ; i f ( ! enablecullface ) glEnable ( GL CULL FACE ) ; i f ( enabledepthoffset ) d i s a b l e p o l y g o n o f f s e t ( GL POLYGON OFFSET FILL ) ; } }; bool animmodel : : enabletc = f a l s e , animmodel : : enablemtc = f a l s e , animmodel : : enablealphatest = f a l s e , animmodel : : enablealphablend = f a l s e , animmodel : : enableenvmap = f a l s e , animmodel : : enableglow = f a l s e , animmodel : : enableoverbright = f a l s e , animmodel : : e n a b l e l i g h t i n g = f a l s e , animmodel : : enablelight0 = f a l s e , animmodel : : enablecullface = true , animmodel : : enablenormals = f a l s e , animmodel : : enabletangents = f a l s e , animmodel : : enablebones = f a l s e , animmodel : : enablerescale = f a l s e , animmodel : : enabledepthoffset = f a l s e ; vec animmodel : : l i g h t d i r ( 0 , 0 , 1) , animmodel : : l i g h t c o l o r ( 1 , 1 , 1) ; f l o a t animmodel : : transparent = 1 , animmodel : : l a s t a l p h a t e s t = −1; void ∗animmodel : : lastvbuf = NULL, ∗animmodel : : l a s t t c b u f = NULL, ∗ animmodel : : lastmtcbuf = NULL, ∗animmodel : : lastnbuf = NULL, ∗ animmodel : : lastxbuf = NULL, ∗animmodel : : lastbbuf = NULL, ∗ animmodel : : lastsdata = NULL, ∗animmodel : : lastbdata = NULL; GLuint animmodel : : lastebuf = 0 , animmodel : : lastenvmaptex = 0 , animmodel : : closestenvmaptex = 0; Texture ∗animmodel : : l a s t t e x = NULL, ∗animmodel : : lastmasks = NULL, ∗ animmodel : : lastnormalmap = NULL; i n t animmodel : : envmaptmu = −1, animmodel : : matrixpos = 0; g l m a t r i x f animmodel : : matrixstack [ 6 4 ] ; template struct modelloader { s t a t i c MDL ∗loading ; static string dir ; s t a t i c bool animated ( ) { return true ; } s t a t i c bool multiparted ( ) { return true ; } s t a t i c bool multimeshed ( ) { return true ; } }; template MDL ∗modelloader::loading = NULL; template s t r i n g modelloader:: d i r = { ’\0 ’}; // crashes clang i f ” ” i s used here template struct modelcommands { typedef struct MDL: : part part ; typedef struct MDL: : skin skin ; s t a t i c void s e t d i r ( char ∗name) { i f ( !MDL: : loading ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } formatstring (MDL: : d i r ) ( ” packages/models/%s ” , name) ; } #define loopmeshes (meshname, m, body ) \ i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } \ part &mdl = ∗MDL: : loading−>parts . l a s t ( ) ; \ i f ( ! mdl . meshes ) return ; \ loopv ( mdl . meshes−>meshes ) \ { \ MESH &m = ∗(MESH ∗)mdl . meshes−>meshes [ i ] ; \ i f ( ! strcmp (meshname, ” ∗ ” ) || (m.name && ! strcmp (m.name, meshname ))) \ { \ body ; \ } \ } #define loopskins (meshname, s , body ) loopmeshes (meshname, m, { skin &s = mdl . skins [ i ] ; body ; }) s t a t i c void setskin ( char ∗meshname, char ∗tex , char ∗masks, f l o a t ∗ envmapmax, f l o a t ∗envmapmin ) { loopskins (meshname, s , s . tex = textureload ( makerelpath (MDL: : dir , tex ) , 0 , true , f a l s e ) ; i f (∗masks ) { s . masks = textureload ( makerelpath (MDL: : dir , masks, ””) , 0 , true , f a l s e ) ; s .envmapmax = ∗envmapmax; s . envmapmin = ∗envmapmin ; } ); } s t a t i c void setspec ( char ∗meshname, i n t ∗percent ) { f l o a t spec = 1.0 f ;

i f (∗ percent>0) spec = ∗percent/100.0 f ; e l s e i f (∗ percent0) ambient = ∗percent/100.0 f ; e l s e i f (∗ percent 0 ? ∗pulse/1000.0 f : 0; i f (∗ percent>0) glow = ∗percent/100.0 f ; e l s e i f (∗ percent”) , 0 , true , f a l s e ) ; i f ( s k i n f i l e [ 0 ] ) skintex = textureload ( makerelpath (MDL: : dir , s k i n f i l e , ””) , 0 , true , f a l s e ) ; loopskins (meshname, s , { s . u n l i t t e x = skintex ; s . normalmap = normalmaptex ; m. calctangents ( ) ; }) ; } s t a t i c void s e t f u l l b r i g h t ( char ∗meshname, f l o a t ∗f u l l b r i g h t ) { loopskins (meshname, s , s . f u l l b r i g h t = ∗f u l l b r i g h t ) ; } s t a t i c void setshader ( char ∗meshname, char ∗shader ) { loopskins (meshname, s , s . shader = lookupshaderbyname ( shader ) ) ; } s t a t i c void s e t s c r o l l ( char ∗meshname, f l o a t ∗scrollu , f l o a t ∗s c r o l l v ) { loopskins (meshname, s , { s . s c r o l l u = ∗s c r o l l u ; s . s c r o l l v = ∗s c r o l l v ; }) ; } s t a t i c void s e t n o c l i p ( char ∗meshname, i n t ∗noclip ) { loopmeshes (meshname, m, m. noclip = ∗noclip ! = 0 ) ; } s t a t i c void s e t l i n k ( i n t ∗parent , i n t ∗child , char ∗tagname , f l o a t ∗x , f l o a t ∗y , f l o a t ∗z ) { i f ( !MDL: : loading ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } i f ( !MDL: : loading−>parts . inrange (∗ parent ) || !MDL: : loading−>parts . inrange (∗ c h i l d ) ) { conoutf ( ” no models loaded to l i n k ” ) ;

engine/bih.cpp return ; } i f ( !MDL: : loading−>parts [∗ parent]−>l i n k (MDL: : loading−>parts [∗ c h i l d ] , tagname , vec (∗x , ∗y , ∗z ) ) ) conoutf ( ” could not l i n k model %s ” , MDL: : loading−>loadname ) ;

modelcommand ( setskin , ” skin ” , ” s s s f f ” ) ; modelcommand ( setspec , ” spec ” , ” s i ” ) ; modelcommand ( setambient , ” ambient ” , ” s i ” ) ; modelcommand ( setglow , ” glow ” , ” s i i f ” ) ; modelcommand ( s e t g l a r e , ” g l a r e ” , ” s f f ” ) ; modelcommand ( setalphatest , ” alphatest ” , ” s f ” ) ; modelcommand ( setalphablend , ” alphablend ” , ” s i ” ) ; modelcommand ( s e t c u l l f a c e , ” c u l l f a c e ” , ” s i ” ) ; modelcommand ( setenvmap , ”envmap” , ” ss ” ) ; modelcommand ( setbumpmap, ”bumpmap” , ” sss ” ) ; modelcommand ( s e t f u l l b r i g h t , ” f u l l b r i g h t ” , ” s f ” ) ; modelcommand ( setshader , ” shader ” , ” ss ” ) ; modelcommand ( s e t s c r o l l , ” s c r o l l ” , ” s f f ” ) ; modelcommand ( setnoclip , ” noclip ” , ” s i ” ) ;

} template void modelcommand ( F ∗fun , const char ∗s u f f i x , const char ∗args ) { defformatstring (name) (”%s%s ” , MDL: : formatname ( ) , s u f f i x ) ; addcommand( newstring (name) , ( void ( ∗ ) ( ) ) fun , args ) ; } modelcommands ( ) { modelcommand ( s e t d i r , ” d i r ” , ” s ” ) ; i f (MDL: : multimeshed ( ) ) {

203

} i f (MDL: : multiparted ( ) ) modelcommand ( s e t l i n k , ” l i n k ” , ” i i s f f f ” ) ; } };

engine/bih.cpp #include ” engine . h”

e l s e i f ( curnode−>i s l e a f ( nearidx ) ) { i f ( t r i i n t e r s e c t ( t r i s [ curnode−>childindex ( nearidx ) ] , o , ray , maxdist , dist , mode, noclip ) ) return true ; i f ( f a r s p l i t < tmax ) { i f ( ! curnode−>i s l e a f ( f a r i d x ) ) { curnode = &nodes [ curnode−>childindex ( f a r i d x ) ] ; tmin = max( tmin , f a r s p l i t ) ; continue ; } e l s e i f ( t r i i n t e r s e c t ( t r i s [ curnode−>childindex ( f a r i d x ) ] , o , ray , maxdist , dist , mode, noclip ) ) return true ; } } else { i f ( f a r s p l i t < tmax ) { i f ( ! curnode−>i s l e a f ( f a r i d x ) ) { i f ( stacksize < i n t ( s i z e o f ( stack ) / s i z e o f ( stack [ 0 ] ) ) ) { BIHStack &save = stack [ stacksize + + ] ; save . node = &nodes [ curnode−>childindex ( f a r i d x ) ] ; save . tmin = max( tmin , f a r s p l i t ) ; save . tmax = tmax ; } else { i f ( traverse ( o , ray , invray , maxdist , dist , mode, &nodes [ curnode−>childindex ( nearidx ) ] , tmin , min ( tmax , n e a r s p l i t ) ) ) return true ; curnode = &nodes [ curnode−>childindex ( f a r i d x ) ] ; tmin = max( tmin , f a r s p l i t ) ; continue ; } } e l s e i f ( t r i i n t e r s e c t ( t r i s [ curnode−>childindex ( f a r i d x ) ] , o , ray , maxdist , dist , mode, noclip ) ) return true ; } curnode = &nodes [ curnode−>childindex ( nearidx ) ] ; tmax = min ( tmax , n e a r s p l i t ) ; continue ; } i f ( stacksize 1) return f a l s e ; vec q ; q . cross ( r , t . b ) ; f l o a t v = ray . dot ( q ) / det ; i f ( v < 0 || u + v > 1) return f a l s e ; f l o a t f = t . c . dot ( q ) / det ; i f ( f < 0 || f > maxdist ) return f a l s e ; i f ( ! ( mode&RAY SHADOW) && &t >= noclip ) return f a l s e ; i f ( t . tex && ( mode&RAY ALPHAPOLY ) ==RAY ALPHAPOLY && ( t . tex−>alphamask || ( lightmapping alphamask ) ) ) ) { i n t s i = clamp ( i n t ( t . tex−>xs ∗ ( t . t c [ 0 ] + u∗( t . t c [ 2 ] − t . t c [ 0 ] ) + v ∗( t . t c [ 4 ] − t . t c [ 0 ] ) ) ) , 0 , t . tex−>xs−1) , t i = clamp ( i n t ( t . tex−>ys ∗ ( t . t c [ 1 ] + u∗( t . t c [ 3 ] − t . t c [ 1 ] ) + v ∗( t . t c [ 5 ] − t . t c [ 1 ] ) ) ) , 0 , t . tex−>ys−1); i f ( ! ( t . tex−>alphamask [ t i ∗ ( ( t . tex−>xs +7) /8) + s i /8] & (10 ? 0 : 1 , ray . z>0 ? 0 : 1) ; for ( ; ; ) { i n t axis = curnode−>axis ( ) ; i n t nearidx = order [ axis ] , f a r i d x = nearidx ˆ 1 ; f l o a t n e a r s p l i t = ( curnode−>s p l i t [ nearidx ] − o [ axis ] ) ∗invray [ axis ] , f a r s p l i t = ( curnode−>s p l i t [ f a r i d x ] − o [ axis ] ) ∗invray [ axis ] ; i f ( n e a r s p l i t i s l e a f ( f a r i d x ) ) { curnode = &nodes [ curnode−>childindex ( f a r i d x ) ] ; tmin = max( tmin , f a r s p l i t ) ; continue ; } e l s e i f ( t r i i n t e r s e c t ( t r i s [ curnode−>childindex ( f a r i d x ) ] , o , ray , maxdist , dist , mode, noclip ) ) return true ; } }

} } i n l i n e bool BIH : : traverse ( const vec &o , const vec &ray , f l o a t maxdist , f l o a t &dist , i n t mode ) { i f ( ! numnodes ) return f a l s e ; vec invray ( ray . x ? 1/ray . x : 1e16f , ray . y ? 1/ray . y : 1e16f , ray . z ? 1/ray . z : 1e16f ) ; f l o a t tmin , tmax ; f l o a t t1 = ( bbmin . x − o . x )∗invray . x , t2 = (bbmax. x − o . x )∗invray . x ; i f ( invray . x > 0) { tmin = t1 ; tmax = t2 ; } e l s e { tmin = t2 ; tmax = t1 ; } t1 = ( bbmin . y − o . y )∗invray . y ; t2 = (bbmax. y − o . y )∗invray . y ; i f ( invray . y > 0) { tmin = max( tmin , t1 ) ; tmax = min ( tmax , t2 ) ; } e l s e

204

Foundations of Videogame Programming Code Repository

{ tmin = max( tmin , t2 ) ; tmax = min ( tmax , t1 ) ; } t1 = ( bbmin . z − o . z )∗invray . z ; t2 = (bbmax. z − o . z )∗invray . z ; i f ( invray . z > 0) { tmin = max( tmin , t1 ) ; tmax = min ( tmax , t2 ) ; } e l s e { tmin = max( tmin , t2 ) ; tmax = min ( tmax , t1 ) ; } i f ( tmin >= maxdist || tmin>=tmax ) return f a l s e ; tmax = min ( tmax , maxdist ) ;

i f ( numindices−r i g h t ==1) buildnodes [ node ] . c h i l d [ 1 ] = (1bih && ( lightmapping > 1 || !m−>setBIH ( ) ) ) return f a l s e ; vec mo = vec ( o ) . sub ( e . o ) , mray ( ray ) ; f l o a t v = mo. dot ( mray ) , inside = m−>bih−>radius − mo. squaredlen ( ) ; i f ( ( inside < 0 && v > 0) || inside + v∗v < 0) return f a l s e ; i n t yaw = e . a t t r 1 ; i f ( yaw ! = 0) { i f ( yaw < 0) yaw = 360 + yaw%360; e l s e i f ( yaw >= 360) yaw %= 360; const vec2 &r o t = sincos360 [ yaw ] ; mo. rotate around z ( r o t . x , −r o t . y ) ; mray . rotate around z ( r o t . x , −r o t . y ) ; } return m−>bih−>traverse (mo, mray , maxdist ? maxdist : 1e16f , dist , mode ) ;

i n t node = buildnodes . length ( ) ; buildnodes . add ( ) ; buildnodes [ node ] . s p l i t [ 0 ] = short ( c e i l ( s p l i t l e f t ) ) ; buildnodes [ node ] . s p l i t [ 1 ] = short ( f l o o r ( s p l i t r i g h t ) ) ; i f ( l e f t ==1) buildnodes [ node ] . c h i l d [ 0 ] = ( axisnode = blendmap ; cache−>scale = worldscale−BM SCALE; cache−>o r i g i n = i v e c ( 0 , 0 , 0) ; return cache−>node . s o l i d !=&bmsolids [0xFF ] ; }

i f ( type==BM BRANCH) { s i z e /= 2; i f ( y1 < s i z e ) { i f ( x1 < s i z e ) fillblendmap ( node . branch−>type [ 0 ] , node . branch−> children [ 0 ] , size , val , x1 , y1 , min ( x2 , s i z e ) , min ( y2 , s i z e ) ) ; i f ( x2 > s i z e ) fillblendmap ( node . branch−>type [ 1 ] , node . branch−> children [ 1 ] , size , val , max( x1−size , 0) , y1 , x2−size , min ( y2 , s i z e ) ); } i f ( y2 > s i z e ) { i f ( x1 < s i z e ) fillblendmap ( node . branch−>type [ 2 ] , node . branch−> children [ 2 ] , size , val , x1 , max( y1−size , 0) , min ( x2 , s i z e ) , y2−s i z e ); i f ( x2 > s i z e ) fillblendmap ( node . branch−>type [ 3 ] , node . branch−> children [ 3 ] , size , val , max( x1−size , 0) , max( y1−size , 0) , x2−size , y2−s i z e ) ; } l o o p i ( 4 ) i f ( node . branch−>type [ i ] ! = BM SOLID || node . branch−>children [ i ] . s o l i d−>v a l ! = v a l ) return ; node . cleanup ( type ) ; type = BM SOLID; node . s o l i d = &bmsolids [ v a l ] ; return ; } e l s e i f ( type==BM SOLID ) { uchar o l d v a l = node . s o l i d−>v a l ; i f ( o l d v a l == v a l ) return ;

BlendMapBranch ∗bm = blendmap . branch ; i n t bmscale = worldscale−BM SCALE, bmsize = 1BM SCALE, y = o . y>>BM SCALE, x1 = max( x−1, 0) , y1 = max( y−1, 0) , x2 = min ( ( ( o . x + s i z e + (1BM SCALE) + 1 , bmsize ) , y2 = min ( ( ( o . y + s i z e + (1BM SCALE) + 1 , bmsize ) , d i f f = ( x1ˆx2 ) | ( y1 ˆ y2 ) ; i f ( d i f f < bmsize ) while ( ! ( d i f f &(1bmscale ) &1)bmscale ) &1) ; i f (bm−>type [ n ] ! =BM BRANCH) { cache−>node = BlendMapRoot (bm−>type [ n ] , bm−>children [ n ] ) ; cache−>scale = bmscale ; cache−>o r i g i n = i v e c ( x1&(˜0Unode . branch = bm; cache−>scale = bmscale ; cache−>o r i g i n = i v e c ( x1&(˜0Udata ) ) ;

{ for ( ; ; ) { bmscale−−; i n t n = ( ( ( y>>bmscale ) &1)bmscale ) &1) ; switch (bm−>type [ n ] ) { case BM SOLID: return bm−>children [ n ] . s o l i d−>v a l ; case BM IMAGE: return bm−>children [ n ] . image−>data [ ( y&((1node . s o l i d−>v a l ; uchar vals [ 4 ] , ∗v a l = vals ; f l o a t bx = pos . x/(1node . branch , cache−>

} uchar ∗dst = &node . image−>data [ y1∗BM IMAGE SIZE + x1 ] ; l o o p i ( y2−y1 ) { memset ( dst , val , x2−x1 ) ; dst += BM IMAGE SIZE ; } } void fillblendmap ( i n t x , i n t y , i n t w, i n t h , uchar v a l ) { i n t bmsize = worldsize>>BM SCALE, x1 = clamp ( x , 0 , bmsize ) , y1 = clamp ( y , 0 , bmsize ) , x2 = clamp ( x+w, 0 , bmsize ) , y2 = clamp ( y+h , 0 , bmsize ) ; i f (max( x1 , y1 ) >= bmsize || min ( x2 , y2 ) =x2 || y1>=y2 ) return ; fillblendmap ( blendmap . type , blendmap , bmsize , val , x1 , y1 , x2 , y2 ) ; } s t a t i c void invertblendmap ( uchar &type , BlendMapNode &node , i n t size , i n t x1 , i n t y1 , i n t x2 , i n t y2 ) { i f ( type==BM BRANCH) { s i z e /= 2; i f ( y1 < s i z e ) {

engine/blend.cpp i f ( x1 < s i z e ) invertblendmap ( node . branch−>type [ 0 ] , node . branch−> children [ 0 ] , size , x1 , y1 , min ( x2 , s i z e ) , min ( y2 , s i z e ) ) ; i f ( x2 > s i z e ) invertblendmap ( node . branch−>type [ 1 ] , node . branch−> children [ 1 ] , size , max( x1−size , 0) , y1 , x2−size , min ( y2 , s i z e ) );

207

smode ) { i f ( type==BM BRANCH) { bmsize /= 2; i f ( sy < bmy + bmsize ) { i f ( sx < bmx + bmsize ) blitblendmap ( node . branch−>type [ 0 ] , node . branch−>children [ 0 ] , bmx, bmy, bmsize , src , sx , sy , sw, sh , smode ) ; i f ( sx + sw > bmx + bmsize ) blitblendmap ( node . branch−>type [ 1 ] , node . branch−>children [ 1 ] , bmx+bmsize , bmy, bmsize , src , sx , sy , sw, sh , smode ) ; } i f ( sy + sh > bmy + bmsize ) { i f ( sx < bmx + bmsize ) blitblendmap ( node . branch−>type [ 2 ] , node . branch−>children [ 2 ] , bmx, bmy+bmsize , bmsize , src , sx , sy , sw, sh , smode ) ; i f ( sx + sw > bmx + bmsize ) blitblendmap ( node . branch−>type [ 3 ] , node . branch−>children [ 3 ] , bmx+bmsize , bmy+bmsize , bmsize , src , sx , sy , sw, sh , smode ) ; } return ; } i f ( type==BM SOLID ) { uchar v a l = node . s o l i d−>v a l ; i f ( bmsize > BM IMAGE SIZE ) { node . s p l i t s o l i d ( type , v a l ) ; blitblendmap ( type , node , bmx, bmy, bmsize , src , sx , sy , sw, sh , smode ) ; return ; }

} i f ( y2 > s i z e ) { i f ( x1 < s i z e ) invertblendmap ( node . branch−>type [ 2 ] , node . branch−> children [ 2 ] , size , x1 , max( y1−size , 0) , min ( x2 , s i z e ) , y2−s i z e ); i f ( x2 > s i z e ) invertblendmap ( node . branch−>type [ 3 ] , node . branch−> children [ 3 ] , size , max( x1−size , 0) , max( y1−size , 0) , x2−size , y2−s i z e ) ; } return ; } e l s e i f ( type==BM SOLID ) { fillblendmap ( type , node , size , 255−node . s o l i d−>val , x1 , y1 , x2 , y2 ) ; } e l s e i f ( type==BM IMAGE) { uchar ∗dst = &node . image−>data [ y1∗BM IMAGE SIZE + x1 ] ; l o o p i ( y2−y1 ) { l o o p j ( x2−x1 ) dst [ j ] = 255−dst [ j ] ; dst += BM IMAGE SIZE ; } } }

type = BM IMAGE; node . image = new BlendMapImage ; memset ( node . image−>data , val , s i z e o f ( node . image−>data ) ) ;

void invertblendmap ( i n t x , i n t y , i n t w, i n t h ) { i n t bmsize = worldsize>>BM SCALE, x1 = clamp ( x , 0 , bmsize ) , y1 = clamp ( y , 0 , bmsize ) , x2 = clamp ( x+w, 0 , bmsize ) , y2 = clamp ( y+h , 0 , bmsize ) ; i f (max( x1 , y1 ) >= bmsize || min ( x2 , y2 ) =x2 || y1>=y2 ) return ; invertblendmap ( blendmap . type , blendmap , bmsize , x1 , y1 , x2 , y2 ) ; } s t a t i c void optimizeblendmap ( uchar &type , BlendMapNode &node ) { switch ( type ) { case BM IMAGE: { uint v a l = node . image−>data [ 0 ] ; v a l |= val children [ i ] ) ; i f ( node . branch−>type [ 3 ] ! = BM SOLID ) return ; uint v a l = node . branch−>children [ 3 ] . s o l i d−>v a l ; l o o p i ( 3 ) i f ( node . branch−>type [ i ] ! = BM SOLID || node . branch−> children [ i ] . s o l i d−>v a l ! = v a l ) return ; node . cleanup ( type ) ; type = BM SOLID; node . s o l i d = &bmsolids [ v a l ] ; break ; } } } void optimizeblendmap ( ) { optimizeblendmap ( blendmap . type , blendmap ) ; } VARF( blendpaintmode , 0 , 0 , 5 , { i f ( ! blendpaintmode ) stoppaintblendmap ( ) ; }) ; s t a t i c void blitblendmap ( uchar &type , BlendMapNode &node , i n t bmx, i n t bmy, i n t bmsize , uchar ∗src , i n t sx , i n t sy , i n t sw, i n t sh , i n t

} i n t x1 = clamp ( sx − bmx, 0 , bmsize ) , y1 = clamp ( sy − bmy, 0 , bmsize ) , x2 = clamp ( sx+sw − bmx, 0 , bmsize ) , y2 = clamp ( sy+sh − bmy, 0 , bmsize ) ; uchar ∗dst = &node . image−>data [ y1∗BM IMAGE SIZE + x1 ] ; src += max(bmy − sy , 0)∗sw + max(bmx − sx , 0) ; l o o p i ( y2−y1 ) { switch ( smode ) { case 1: memcpy( dst , src , x2 − x1 ) ; break ; case 2: l o o p i ( x2 − x1 ) dst [ i ] = min ( dst [ i ] , src [ i ] ) ; break ; case 3: l o o p i ( x2 − x1 ) dst [ i ] = max( dst [ i ] , src [ i ] ) ; break ; case 4: l o o p i ( x2 − x1 ) dst [ i ] = min ( dst [ i ] , uchar (0xFF − src [ i ] ) ) ; break ; case 5: l o o p i ( x2 − x1 ) dst [ i ] = max( dst [ i ] , uchar (0xFF − src [ i ] ) ) ; break ; } dst += BM IMAGE SIZE ; src += sw ; } } void blitblendmap ( uchar ∗src , i n t sx , i n t sy , i n t sw, i n t sh , i n t smode ) { i n t bmsize = worldsize>>BM SCALE; i f (max( sx , sy ) >= bmsize || min ( sx+sw, sy+sh ) children [ 0 ] = blendmap ; loopi ( 3 ) { branch−>type [ i +1] = BM SOLID; branch−>children [ i + 1 ] . s o l i d = &bmsolids [0xFF ] ; } blendmap . type = BM BRANCH; blendmap . branch = branch ;

glTexParameterfv ( GL TEXTURE 2D, GL TEXTURE BORDER COLOR, border ) ; d e l e t e [ ] buf ; } void r e o r i e n t ( bool f l i p x , bool f l i p y , bool swapxy ) { uchar ∗rdata = new uchar [w∗h ] ; i n t s t r i d e x = 1 , s t r i d e y = 1; i f ( swapxy ) s t r i d e x ∗= h ; e l s e s t r i d e y ∗= w; uchar ∗src = data , ∗dst = rdata ; i f ( f l i p x ) { dst += (w−1)∗s t r i d e x ; s t r i d e x = −s t r i d e x ; } i f ( f l i p y ) { dst += ( h−1)∗s t r i d e y ; s t r i d e y = −s t r i d e y ; } loopi (h) { uchar ∗curdst = dst ; l o o p j (w) { ∗curdst = ∗src ++; curdst += s t r i d e x ; } dst += s t r i d e y ; } i f ( swapxy ) swap (w, h ) ; d e l e t e [ ] data ; data = rdata ; i f ( tex ) gentex ( ) ; }

} void shrinkblendmap ( i n t octant ) { blendmap . shrink ( octant&3) ; } void moveblendmap ( uchar type , BlendMapNode &node , i n t size , i n t x , i n t y , i n t dx , i n t dy ) { i f ( type == BM BRANCH) { s i z e /= 2; moveblendmap ( node . branch−>type [ 0 ] , node . branch−>children [ 0 ] , x , y , dx , dy ) ; moveblendmap ( node . branch−>type [ 1 ] , node . branch−>children [ 1 ] , x + size , y , dx , dy ) ; moveblendmap ( node . branch−>type [ 2 ] , node . branch−>children [ 2 ] , x , y + size , dx , dy ) ; moveblendmap ( node . branch−>type [ 3 ] , node . branch−>children [ 3 ] , x + size , y + size , dx , dy ) ; return ; } e l s e i f ( type == BM SOLID ) { fillblendmap ( x+dx , y+dy , size , size , node . s o l i d−>v a l ) ; } e l s e i f ( type == BM IMAGE) { blitblendmap ( node . image−>data , x+dx , y+dy , size , size , 1) ; }

size , size , size ,

};

size ,

s t a t i c vector brushes ; s t a t i c i n t curbrush = −1;

} void moveblendmap ( i n t dx , i n t dy ) { BlendMapRoot old = blendmap ; blendmap . type = BM SOLID; blendmap . s o l i d = &bmsolids [0xFF ] ; moveblendmap ( old . type , old , worldsize>>BM SCALE, 0 , 0 , dx , dy ) ; old . cleanup ( ) ; } struct BlendBrush { char ∗name; i n t w, h ; uchar ∗data ; GLuint tex ;

void cleanupblendmap ( ) { loopv ( brushes ) brushes [ i]−>cleanup ( ) ; } void clearblendbrushes ( ) { while ( brushes . length ( ) ) d e l e t e brushes . pop ( ) ; curbrush = −1; } void delblendbrush ( const char ∗name) { loopv ( brushes ) i f ( ! strcmp ( brushes [ i]−>name, name) ) { d e l e t e brushes [ i ] ; brushes . remove ( i−−); } curbrush = brushes . empty ( ) ? −1 : clamp ( curbrush , 0 , brushes . length ( ) −1) ; } void addblendbrush ( const char ∗name, const char ∗imgname ) { delblendbrush (name) ; ImageData s ; i f ( ! loadimage ( imgname, s ) ) { conoutf (CON ERROR, ” could not load blend brush image %s ” , imgname ) ; return ; } i f (max( s .w, s . h ) > (1BM SCALE, y1 = s e l . o . y>>BM SCALE, x2 = ( s e l . o . x+ s e l . s . x∗s e l . g r i d+(1BM SCALE, y2 = ( s e l . o . y+ s e l . s . y∗s e l . g r i d+(1BM SCALE; fillblendmap ( x1 , y1 , x2−x1 , y2−y1 , 0xFF ) ; previewblends ( i v e c ( x1type [ i ] = BM SOLID; node . branch−> children [ i ] . s o l i d = &bmsolids [0xFF ] ; } l o o p i ( 4 ) i f ( ! loadblendmap ( f , node . branch−>type [ i ] , node . branch−> children [ i ] ) ) return f a l s e ; break ; default : type = BM SOLID; node . s o l i d = &bmsolids [0xFF ] ; return f a l s e ;

case BM IMAGE: f−>write ( node . image−>data , s i z e o f ( node . image−>data ) ) ; break ; case BM BRANCH: l o o p i ( 4 ) saveblendmap ( f , node . branch−>type [ i ] , node . branch−> children [ i ] ) ; break ; } } void saveblendmap ( stream ∗f ) { saveblendmap ( f , blendmap . type , blendmap ) ; } uchar shouldsaveblendmap ( ) { return blendmap . s o l i d !=&bmsolids [0xFF ] ? 1 : 0; }

engine/blob.cpp #include ” engine . h” VARNP( blobs , showblobs , 0 , 1 , 1) ; VARFP( blobintensity , 0 , 60, 100, resetblobs ( ) ) ; VARFP( blobheight , 1 , 32, 128, resetblobs ( ) ) ; VARFP( blobfadelow , 1 , 8 , 32, resetblobs ( ) ) ; VARFP( blobfadehigh , 1 , 8 , 32, resetblobs ( ) ) ; VARFP( blobmargin , 0 , 1 , 16, resetblobs ( ) ) ;

blobrenderer ( const char ∗texname ) : texname ( texname ) , tex (NULL) , cache (NULL) , cachesize ( 0 ) , blobs (NULL) , maxblobs ( 0 ) , s t a r t b l o b ( 0 ) , endblob ( 0 ) , v e r t s (NULL) , maxverts ( 0 ) , s t a r t v e r t ( 0 ) , endvert ( 0 ) , a v a i l v e r t s ( 0 ) , indexes (NULL) , maxindexes ( 0 ) , startindex ( 0 ) , endindex ( 0 ) , availindexes ( 0 ) , l a s t b l o b (NULL) {}

VAR( dbgblob , 0 , 0 , 1) ; struct blobinfo { vec o ; f l o a t radius ; int millis ; uint startindex , endindex ; ushort s t a r t v e r t , endvert ; }; struct blobvert { vec pos ; f l o a t u, v ; bvec c o l o r ; uchar alpha ; }; struct blobrenderer { const char ∗texname ; Texture ∗tex ; blobinfo ∗∗cache ; i n t cachesize ; blobinfo ∗blobs ; i n t maxblobs , startblob , endblob ; blobvert ∗v e r t s ; i n t maxverts , s t a r t v e r t , endvert , a v a i l v e r t s ; ushort ∗indexes ; i n t maxindexes , startindex , endindex , availindexes ; blobinfo ∗lastblob , ∗flushblob ; vec blobmin , blobmax ; i v e c bborigin , bbsize ; f l o a t blobalphalow , blobalphahigh ; uchar blobalpha ;

void i n i t ( i n t t r i s ) { i f ( cache ) { DELETEA( cache ) ; cachesize = 0; } i f ( blobs ) { DELETEA( blobs ) ; maxblobs = s t a r t b l o b = endblob = 0; } i f ( verts ) { DELETEA( v e r t s ) ; maxverts = s t a r t v e r t = endvert = a v a i l v e r t s = 0; } i f ( indexes ) { DELETEA( indexes ) ; maxindexes = startindex = endindex = availindexes = 0; } i f ( ! t r i s ) return ; tex = textureload ( texname , 3) ; cachesize = t r i s /2; cache = new blobinfo ∗[ cachesize ] ; memset ( cache , 0 , cachesize ∗ s i z e o f ( blobinfo ∗) ) ; maxblobs = t r i s /2; blobs = new blobinfo [ maxblobs ] ; memset ( blobs , 0 , maxblobs ∗ s i z e o f ( blobinfo ) ) ; maxindexes = t r i s ∗3 + 3; availindexes = maxindexes − 3; indexes = new ushort [ maxindexes ] ; maxverts = min ( t r i s ∗3/2 + 1 , (1 below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( c > above ) { i f ( pc < below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; i f ( pc < above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; } else { i f ( pc < below ) { i f ( c > below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( pc > above && c < above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; out [ numout++] = v ; } p = &v ; pc = c ; } return numout;

bool f r e e b l o b ( ) { blobinfo &b = blobs [ s t a r t b l o b ] ; i f (&b == l a s t b l o b ) return f a l s e ; s t a r t b l o b ++; i f ( s t a r t b l o b >= maxblobs ) s t a r t b l o b = 0; s t a r t v e r t = b . endvert ; i f ( s t a r t v e r t>=maxverts ) s t a r t v e r t = 0; a v a i l v e r t s += b . endvert − b . s t a r t v e r t ; startindex = b . endindex ; i f ( startindex>=maxindexes ) startindex = 0; availindexes += b . endindex − b . startindex ; b . m i l l i s = 0; return true ; } blobinfo &newblob ( const vec &o , f l o a t radius ) { blobinfo &b = blobs [ endblob ] ; i n t next = endblob + 1; i f ( next>=maxblobs ) next = 0; i f ( next== s t a r t b l o b ) { l a s t b l o b = &b ; freeblob ( ) ; } endblob = next ; b.o = o; b . radius = radius ; b. millis = totalmillis ; b . startindex = b . endindex = endindex ; b . s t a r t v e r t = b . endvert = endvert ; l a s t b l o b = &b ; return b ; } void clearblobs ( ) { s t a r t b l o b = endblob = 0; s t a r t v e r t = endvert = 0; a v a i l v e r t s = maxverts − 1; startindex = endindex = 0; availindexes = maxindexes − 3; } template s t a t i c i n t s p l i t ( const vec ∗in , i n t numin, f l o a t below , f l o a t above , vec ∗out ) { i n t numout = 0; const vec ∗p = &in [ numin−1]; f l o a t pc = (∗p ) [C ] ; l o o p i ( numin ) { const vec &v = in [ i ] ; f l o a t c = v [C ] ; i f ( c < below ) { i f ( pc > above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; i f ( pc > below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( c > above ) { i f ( pc < below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; i f ( pc < above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( pc < below ) { i f ( c > below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( pc > above && c < above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; out [ numout++] = v ; p = &v ; pc = c ; } return numout; } template s t a t i c i n t c l i p ( const vec ∗in , i n t numin, f l o a t below , f l o a t above , vec ∗out ) { i n t numout = 0;

211

} void dupblob ( ) { i f ( lastblob−>s t a r t v e r t >= lastblob−>endvert ) { lastblob−>startindex = lastblob−>endindex = endindex ; lastblob−>s t a r t v e r t = lastblob−>endvert = endvert ; return ; } blobinfo &b = newblob ( lastblob−>o , lastblob−>radius ) ; b . m i l l i s = −1; } i n l i n e i n t addvert ( const vec &pos ) { blobvert &v = v e r t s [ endvert ] ; v . pos = pos ; v . u = ( pos . x − blobmin . x ) / ( blobmax . x − blobmin . x ) ; v . v = ( pos . y − blobmin . y ) / ( blobmax . y − blobmin . y ) ; v . c o l o r = bvec (255 , 255, 255) ; i f ( pos . z < blobmin . z + blobfadelow ) v . alpha = uchar ( blobalphalow ∗ ( pos . z − blobmin . z ) ) ; e l s e i f ( pos . z > blobmax . z − blobfadehigh ) v . alpha = uchar ( blobalphahigh ∗ ( blobmax . z − pos . z ) ) ; e l s e v . alpha = blobalpha ; return endvert ++; } void addtris ( const vec ∗v , i n t numv) { i f ( endvert ! = i n t ( lastblob−>endvert ) || endindex ! = i n t ( lastblob−> endindex ) ) dupblob ( ) ; f o r ( const vec ∗cur = &v [ 2 ] , ∗end = &v [numv ] ; ; ) { i n t l i m i t = maxverts − endvert − 2; i f ( l i m i t endvert = maxverts ; endvert = 0; dupblob ( ) ; l i m i t = maxverts − 2; } l i m i t = min ( i n t ( end − cur ) , min ( l i m i t , ( maxindexes − endindex ) /3) ) ; while ( a v a i l v e r t s < l i m i t +2) i f ( ! f r e e b l o b ( ) ) return ; while ( availindexes < l i m i t ∗3) i f ( ! f r e e b l o b ( ) ) return ; i n t i1 = addvert ( v [ 0 ] ) , i2 = addvert ( cur[ −1]) ; loopk ( l i m i t ) { indexes [ endindex ++] = i1 ; indexes [ endindex ++] = i2 ; i2 = addvert (∗cur ++) ; indexes [ endindex ++] = i2 ; }

212

Foundations of Videogame Programming Code Repository a v a i l v e r t s −= endvert − lastblob−>endvert ; availindexes −= endindex − lastblob−>endindex ; lastblob−>endvert = endvert ; lastblob−>endindex = endindex ; i f ( endvert >= maxverts ) endvert = 0; i f ( endindex >= maxindexes ) endindex = 0;

#define CLIPSIDE ( c l i p , below , above ) \ { \ vec ∗in = v ; \ v = in==v1 ? v2 : v1 ; \ numv = c l i p ( in , numv, below , above , v ) ; \ i f (numv < 3) continue ; \ } i f ( f l a t ! = 0 ) CLIPSIDE ( c l i p , blobmin . x , blobmax . x ) ; i f ( f l a t ! = 1 ) CLIPSIDE ( c l i p , blobmin . y , blobmax . y ) ; i f ( f l a t !=2) { CLIPSIDE ( c l i p , blobmin . z , blobmax . z ) ; CLIPSIDE ( s p l i t , blobmin . z + blobfadelow , blobmax . z − blobfadehigh ) ; } addtris ( v , numv) ;

i f ( cur >= end ) break ; dupblob ( ) ; } } void g e n t r i s ( cube &cu , i n t orient , const i v e c &o , i n t size , materialsurface ∗mat = NULL, i n t vismask = 0) { vec pos [MAXFACEVERTS+ 8 ] ; i n t dim = dimension ( o r i e n t ) , numverts = 0 , numplanes = 1 , f l a t = −1; i f ( mat ) { switch ( o r i e n t ) { #define GENFACEORIENT( orient , v0 , v1 , v2 , v3 ) \ case o r i e n t : v0 v1 v2 v3 break ; #define GENFACEVERT( orient , vert , x , y , z , xv , yv , zv ) \ pos [ numverts++] = vec ( x xv , y yv , z zv ) ; GENFACEVERTS( o . x , o . x , o . y , o . y , o . z , o . z , , + mat−>csize , , + mat−>r s i z e , + 0.1 f , − 0.1 f ) ; #undef GENFACEORIENT #undef GENFACEVERT } f l a t = dim ; } e l s e i f ( cu . texture [ o r i e n t ] == DEFAULT SKY) return ; e l s e i f ( cu . ext && ( numverts = cu . ext−>surfaces [ o r i e n t ] . numverts& MAXFACEVERTS) ) { v e r t i n f o ∗v e r t s = cu . ext−>v e r t s ( ) + cu . ext−>surfaces [ o r i e n t ] . verts ; i v e c vo = i v e c ( o ) .mask( ˜ 0xFFF ) . shl ( 3 ) ; l o o p j ( numverts ) pos [ j ] = v e r t s [ j ] . getxyz ( ) . add ( vo ) . tovec ( ) . mul (1/8.0 f ) ; i f ( numverts >= 4 && ! ( cu . merged&(1 blobmax . y || vmax . z < blobmin . z || vmin . z > blobmax . z ) return ; vec v1 [MAXFACEVERTS+6+4] , v2 [MAXFACEVERTS+6+4]; l o o p l ( numplanes ) { vec ∗v = pos ; i n t numv = numverts ; i f ( numplanes >= 2) { i f ( l ) { pos [ 1 ] = pos [ 2 ] ; pos [ 2 ] = pos [ 3 ] ; } numv = 3; } i f ( vec ( ) . cross ( v [ 0 ] , v [ 1 ] , v [ 2 ] ) . z matbuf ; i n t matsurfs = va−>matsurfs ; l o o p i ( matsurfs ) { materialsurface &m = matbuf [ i ] ; i f ( ! i s c l i p p e d (m. material&MATF VOLUME) || m. o r i e n t == O BOTTOM) { i += m. skip ; continue ; } i n t dim = dimension (m. o r i e n t ) , c = C[ dim ] , r = R[ dim ] ; for ( ; ; ) { materialsurface &m = matbuf [ i ] ; i f (m. o [ dim ] >= blobmin [ dim ] && m. o [ dim ] = blobmin [ c ] && m. o [ c ] = blobmin [ r ] && m. o [ r ] = matsurfs ) break ; materialsurface &n = matbuf [ i + 1 ] ; i f ( n . material ! = m. material || n . o r i e n t ! = m. o r i e n t ) break ; i ++; } } } void findescaped ( cube ∗cu , const i v e c &o , i n t size , i n t escaped ) { loopi ( 8 ) { i f ( escaped&(11, cu [ i ] . escaped ) ; else { i n t vismask = cu [ i ] . merged ; i f ( vismask ) l o o p j ( 6 ) i f ( vismask&(1va ) ; i f ( cu [ i ] . children ) g e n t r i s ( cu [ i ] . children , co , size>>1, cu [ i ] . escaped ) ; else { i n t vismask = cu [ i ] . v i s i b l e ; i f ( vismask&0xC0 ) { i f ( vismask&0x80 ) l o o p j ( 6 ) g e n t r i s ( cu [ i ] , j , co , size , NULL, vismask ) ; e l s e l o o p j ( 6 ) i f ( vismask&(1= 0 ? &b : NULL; }

setuprenderstate ( ) ; } glVertexPointer ( 3 , GL FLOAT, s i z e o f ( blobvert ) , &verts−>pos ) ; glTexCoordPointer ( 2 , GL FLOAT, s i z e o f ( blobvert ) , &verts−>u ) ; glColorPointer ( 4 , GL UNSIGNED BYTE, s i z e o f ( blobvert ) , &verts−> color ) ; i f ( ! lastrender || lastrender−>tex ! = tex ) glBindTexture ( GL TEXTURE 2D, tex−>id ) ; lastrender = t h i s ; } union { i n t i ; f l o a t f ; } ox , oy ; ox . f = o . x ; oy . f = o . y ; uint hash = uint ( ox . i ˆ ˜ oy . i ˆ ( INT MAX−oy . i ) ˆ uint ( radius ) ) ; hash %= cachesize ; blobinfo ∗b = cache [ hash ] ; i f ( ! b || b−>m i l l i s o ! = o || b−>radius ! = radius ) { b = addblob ( o , radius , fade ) ; cache [ hash ] = b ; i f ( ! b ) return ; } e l s e i f ( fade < 1 && b−>m i l l i s < t o t a l m i l l i s ) fadeblob ( b , fade ) ; do { i f ( b−>endvert − b−>s t a r t v e r t >= 3) { i f (hasDRE) glDrawRangeElements ( GL TRIANGLES, b−>s t a r t v e r t , b −>endvert−1, b−>endindex − b−>startindex , GL UNSIGNED SHORT, &indexes [ b−>startindex ] ) ; e l s e glDrawElements ( GL TRIANGLES, b−>endindex − b−>startindex , GL UNSIGNED SHORT, &indexes [ b−>startindex ] ) ; xtravertsva += b−>endvert − b−>s t a r t v e r t ; } i n t o f f s e t = b − &blobs [ 0 ] + 1; i f ( o f f s e t >= maxblobs ) o f f s e t = 0; i f ( o f f s e t < endblob ? o f f s e t > s t a r t b l o b || s t a r t b l o b > endblob : o f f s e t > s t a r t b l o b ) b = &blobs [ o f f s e t ] ; e l s e break ; } while ( b−>m i l l i s < 0) ;

s t a t i c void setuprenderstate ( ) { foggedshader−>set ( ) ; enablepolygonoffset ( GL POLYGON OFFSET FILL ) ; glDepthMask ( GL FALSE ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; i f ( ! dbgblob ) glEnable (GL BLEND) ; glEnableClientState ( GL VERTEX ARRAY ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; glEnableClientState (GL COLOR ARRAY) ; } s t a t i c void cleanuprenderstate ( ) { g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL COLOR ARRAY) ;

} };

glDepthMask ( GL TRUE ) ; glDisable (GL BLEND) ;

i n t blobrenderer : : l a s t r e s e t = 0; blobrenderer ∗blobrenderer : : lastrender = NULL;

d i s a b l e p o l y g o n o f f s e t ( GL POLYGON OFFSET FILL ) ; }

VARFP( b l o b s t a t t r i s , 128, 4096, 1endvert − b−>s t a r t v e r t >= 3) f o r ( blobvert ∗v = &v e r t s [ b−> s t a r t v e r t ] , ∗end = &v e r t s [ b−>endvert ] ; v < end ; v ++) { f l o a t z = v−>pos . z ; i f ( z < minz + blobfadelow ) v−>alpha = uchar ( scalelow ∗ ( z − minz ) ) ; e l s e i f ( z > maxz − blobfadehigh ) v−>alpha = uchar ( scalehigh ∗ ( maxz − z ) ) ; e l s e v−>alpha = alpha ; } i n t o f f s e t = b − &blobs [ 0 ] + 1; i f ( o f f s e t >= maxblobs ) o f f s e t = 0; i f ( o f f s e t < endblob ? o f f s e t > s t a r t b l o b || s t a r t b l o b > endblob : o f f s e t > s t a r t b l o b ) b = &blobs [ o f f s e t ] ; e l s e break ;

s t a t i c blobrenderer blobs [ ] = { blobrenderer(”packages/ p a r t i c l e s /blob . png ” ) , blobrenderer(”packages/ p a r t i c l e s /blob . png ” ) }; void i n i t b l o b s ( i n t type ) { i f ( type < 0 || ( type==BLOB STATIC && blobs [ BLOB STATIC ] . blobs ) ) blobs [ BLOB STATIC ] . i n i t ( showblobs ? b l o b s t a t t r i s : 0) ; i f ( type < 0 || ( type==BLOB DYNAMIC && blobs [BLOB DYNAMIC ] . blobs ) ) blobs [BLOB DYNAMIC ] . i n i t ( showblobs ? blobdyntris : 0) ; } void resetblobs ( ) { blobrenderer : : l a s t r e s e t = t o t a l m i l l i s ; } void renderblob ( i n t type , const vec &o , f l o a t radius , f l o a t fade ) { i f ( ! showblobs ) return ; i f ( r e f r a c t i n g < 0 && o . z − blobheight − blobfadelow >= r e f l e c t z ) return ; blobs [ type ] . renderblob ( o , radius + blobmargin , fade ) ; } void flushblobs ( ) { i f ( blobrenderer : : lastrender ) blobrenderer : : cleanuprenderstate ( ) ; blobrenderer : : lastrender = NULL; }

214

Foundations of Videogame Programming Code Repository

engine/client.cpp // c l i e n t . cpp , mostly network r e l a t e d c l i e n t game code ENetAddress address ; address . port = serverport ;

#include ” engine . h” ENetHost ∗c l i e n t h o s t = NULL; ENetPeer ∗curpeer = NULL, ∗connpeer = NULL; i n t connmillis = 0 , connattempts = 0 , d i s c m i l l i s = 0;

void t h r o t t l e ( ) ;

i f ( servername ) { i f ( strcmp ( servername , connectname ) ) setsvar ( ” connectname ” , servername ) ; i f ( serverport ! = connectport ) setvar ( ” connectport ” , serverport ) ; addserver ( servername , serverport , serverpassword && serverpassword [ 0 ] ? serverpassword : NULL) ; conoutf ( ” attempting to connect to %s:%d ” , servername , serverport ) ; i f ( ! r e s o l v e r w a i t ( servername , &address ) ) { conoutf (”\ f3could not r e s o l v e server %s ” , servername ) ; return ; } } else { setsvar ( ” connectname ” , ” ” ) ; setvar ( ” connectport ” , 0) ; conoutf ( ” attempting to connect over LAN ” ) ; address . host = ENET HOST BROADCAST; }

VARF( t h r o t t l e i n t e r v a l , 0 , 5 , 30, t h r o t t l e ( ) ) ; VARF( t h r o t t l e a c c e l , 0 , 2 , 32, t h r o t t l e ( ) ) ; VARF( t h r o t t l e d e c e l , 0 , 2 , 32, t h r o t t l e ( ) ) ;

i f ( ! clienthost ) c l i e n t h o s t = e n e t h o s t c r e a t e (NULL, 2 , server : : numchannels ( ) , rate ∗1024, rate ∗1024) ;

void t h r o t t l e ( ) { i f ( ! curpeer ) return ; ASSERT( ENET PEER PACKET THROTTLE SCALE==32) ; e n e t p e e r t h r o t t l e c o n f i g u r e ( curpeer , t h r o t t l e i n t e r v a l ∗1000, throttle accel , throttle decel ) ; }

i f ( clienthost ) { connpeer = enet host connect ( c l i e n t h o s t , &address , server : : numchannels ( ) , 0) ; enet host flush ( clienthost ) ; connmillis = t o t a l m i l l i s ; connattempts = 0;

bool isconnected ( bool attempt , bool l o c a l ) { return curpeer || ( attempt && connpeer ) || ( l o c a l && h a s l o c a l c l i e n t s ( ) ); }

game : : connectattempt ( servername ? servername : ” ” , serverpassword ? serverpassword : ” ” , address ) ; } e l s e conoutf (”\ f3could not connect to server ” ) ;

bool multiplayer ( bool msg ) { bool v a l = curpeer || hasnonlocalclients ( ) ; i f ( v a l && msg ) conoutf (CON ERROR, ” operation not a v a i l a b l e in multiplayer ” ) ; return v a l ; } void s e t r a t e ( i n t rate ) { i f ( ! curpeer ) return ; enet host bandwidth limit ( c li e n t h o s t , rate ∗1024, rate ∗1024) ; } VARF( rate , 0 , 0 , 1024, s e t r a t e ( rate ) ) ;

ICOMMAND( isconnected , ”bb ” , ( i n t ∗attempt , i n t ∗l o c a l ) , i n t r e t ( isconnected (∗ attempt > 0 , ∗l o c a l ! = 0) ? 1 : 0) ) ; const ENetAddress ∗connectedpeer ( ) { return curpeer ? &curpeer−>address : NULL; } ICOMMAND( connectedip , ” ” , ( ) , { const ENetAddress ∗address = connectedpeer ( ) ; s t r i n g hostname ; r e s u l t ( address && e n e t a d d r e s s g e t h o s t i p ( address , hostname , s i z e o f ( hostname ) ) >= 0 ? hostname : ” ” ) ; }) ; ICOMMAND( connectedport , ” ” , ( ) , { const ENetAddress ∗address = connectedpeer ( ) ; i n t r e t ( address ? address−>port : −1) ; }) ; void abortconnect ( ) { i f ( ! connpeer ) return ; game : : c o n n e c t f a i l ( ) ; i f ( connpeer−>s t a t e ! =ENET PEER STATE DISCONNECTED) e n e t p e e r r e s e t ( connpeer ) ; connpeer = NULL; i f ( curpeer ) return ; enet host destroy ( c l i e n t h o s t ) ; c l i e n t h o s t = NULL; } SVARP( connectname , ” ” ) ; VARP( connectport , 0 , 0 , 0xFFFF ) ; void connectserv ( const char ∗servername , i n t serverport , const char ∗ serverpassword ) { i f ( connpeer ) { conoutf ( ” aborting connection attempt ” ) ; abortconnect ( ) ; } i f ( serverport 3) { conoutf (”\ f3could not connect to server ” ) ; abortconnect ( ) ; return ; }

conoutf ( ” attempting to disconnect . . . ” ) ; disconnect ( ! d i s c m i l l i s ) ; } e l s e i f ( l o c a l && h a s l o c a l c l i e n t s ( ) ) localdisconnect ( ) ; e l s e conoutf ( ” not connected ” ) ; }

} while ( c l i e n t h o s t && e n e t h o s t s e r v i c e ( c l i e n t h o s t , &event , 0)>0) switch ( event . type ) { case ENET EVENT TYPE CONNECT: disconnect ( f a l s e , f a l s e ) ; localdisconnect ( f a l s e ) ; curpeer = connpeer ; connpeer = NULL; conoutf ( ” connected to server ” ) ; throttle ( ) ; i f ( rate ) s e t r a t e ( rate ) ; game : : gameconnect ( true ) ; break ;

ICOMMAND( connect , ” s i s ” , ( char ∗name, i n t ∗port , char ∗pw) , connectserv ( name, ∗port , pw) ) ; ICOMMAND( lanconnect , ” i s ” , ( i n t ∗port , char ∗pw) , connectserv (NULL, ∗port , pw) ) ; COMMAND( reconnect , ” s ” ) ; ICOMMAND( disconnect , ”b ” , ( i n t ∗l o c a l ) , trydisconnect (∗ l o c a l ! = 0) ) ; ICOMMAND( localconnect , ” ” , ( ) , { i f ( ! isconnected ( ) ) localconnect ( ) ; }) ; ICOMMAND( localdisconnect , ” ” , ( ) , { i f ( h a s l o c a l c l i e n t s ( ) ) localdisconnect ( ) ; }) ; void sendclientpacket ( ENetPacket ∗packet , i n t chan ) { i f ( curpeer ) enet peer send ( curpeer , chan , packet ) ; e l s e l o c a l c l i e n t t o s e r v e r ( chan , packet ) ; }

case ENET EVENT TYPE RECEIVE : i f ( d i s c m i l l i s ) conoutf ( ” attempting to disconnect . . . ” ) ; e l s e l o c a l s e r v e r t o c l i e n t ( event . channelID , event . packet ) ; enet packet destroy ( event . packet ) ; break ;

void f l u s h c l i e n t ( ) { i f ( clienthost ) enet host flush ( clienthost ) ; }

case ENET EVENT TYPE DISCONNECT: i f ( event . data>=DISC NUM) event . data = DISC NONE; i f ( event . peer==connpeer ) { conoutf (”\ f3could not connect to server ” ) ; abortconnect ( ) ; } else { i f ( ! d i s c m i l l i s || event . data ) { const char ∗msg = disconnectreason ( event . data ) ; i f ( msg ) conoutf (”\ f 3 s e r v e r network error , disconnecting (% s ) . . . ” , msg ) ; e l s e conoutf (”\ f 3 s e r v e r network error , disconnecting . . . ” ) ; } disconnect ( ) ; } return ;

void neterr ( const char ∗s , bool disc ) { conoutf (CON ERROR, ”\ f 3 i l l e g a l network message (%s ) ” , s ) ; i f ( disc ) disconnect ( ) ; } void l o c a l s e r v e r t o c l i e n t ( i n t chan , ENetPacket ∗packet ) updates from the server

// processes any

{ packetbuf p ( packet ) ; game : : parsepacketclient ( chan , p ) ; } void c l i e n t k e e p a l i v e ( ) { i f ( c l i e n t h o s t ) e n e t h o s t s e r v i c e ( c l i e n t h o s t , NULL, 0) ; } void gets2c ( ) // get updates from the server { ENetEvent event ; i f ( ! c l i e n t h o s t ) return ; i f ( connpeer && t o t a l m i l l i s /3000 > connmillis /3000) { conoutf ( ” attempting to connect . . . ” ) ; connmillis = t o t a l m i l l i s ;

default : break ; } }

engine/command.cpp // command. cpp : implements the parsing and execution o f a t i n y s c r i p t language which // i s l a r g e l y backwards compatible with the quake console language . #include ” engine . h” hashset idents ; // contains ALL vars/commands/ a l i a s e s vector identmap ; ident ∗dummyident = NULL; i n t i d e n t f l a g s = 0; s t a t i c const i n t MAXARGS = 25; VARN( numargs ,

numargs , MAXARGS, 0 , 0) ;

s t a t i c i n l i n e void f r e e a r g ( t a g v a l &v ) { switch ( v . type ) { case VAL STR : d e l e t e [ ] v . s ; break ; case VAL CODE: i f ( v . code[−1] == CODE START) d e l e t e [ ] ( uchar ∗)&v . code[ −1]; break ; } } s t a t i c i n l i n e void f o r c e n u l l ( t a g v a l &v ) { switch ( v . type ) { case VAL NULL : return ; } freearg ( v ) ; v . setnull ( ) ;

215

} s t a t i c i n l i n e f l o a t f o r c e f l o a t ( t a g v a l &v ) { f l o a t f = 0.0 f ; switch ( v . type ) { case VAL INT : f = v . i ; break ; case VAL STR : f = p a r s e f l o a t ( v . s ) ; break ; case VAL MACRO: f = p a r s e f l o a t ( v . s ) ; break ; case VAL FLOAT : return v . f ; } freearg ( v ) ; v . setfloat ( f ) ; return f ; } s t a t i c i n l i n e i n t f o r c e i n t ( t a g v a l &v ) { i n t i = 0; switch ( v . type ) { case VAL FLOAT : i = v . f ; break ; case VAL STR : i = parseint ( v . s ) ; break ; case VAL MACRO: i = parseint ( v . s ) ; break ; case VAL INT : return v . i ; } freearg ( v ) ; v . setint ( i ) ; return i ; } s t a t i c i n l i n e const char ∗f o r c e s t r ( t a g v a l &v ) {

216

Foundations of Videogame Programming Code Repository

const char ∗s = ” ” ; switch ( v . type ) { case VAL FLOAT : s = f l o a t s t r ( v . f ) ; break ; case VAL INT : s = i n t s t r ( v . i ) ; break ; case VAL STR : case VAL MACRO: return v . s ; } freearg ( v ) ; v . s e t s t r ( newstring ( s ) ) ; return s ;

i f ( ! i . v a l . s [ 0 ] ) break ; delete [ ] i . val . s ; } cleancode ( i ) ; i . valtype = VAL STR ; i . v a l . s = newstring ( ” ” ) ; break ; case ID VAR : ∗i . storage . i = i . o v e r r i d e v a l . i ; i . changed ( ) ; break ; case ID FVAR : ∗i . storage . f = i . o v e r r i d e v a l . f ; i . changed ( ) ; break ; case ID SVAR : d e l e t e [ ] ∗i . storage . s ; ∗i . storage . s = i . o v e r r i d e v a l . s ; i . changed ( ) ; break ;

} s t a t i c i n l i n e void forcearg ( t a g v a l &v , i n t type ) { switch ( type ) { case RET STR : i f ( v . type ! = VAL STR ) f o r c e s t r ( v ) ; break ; case RET INT : i f ( v . type ! = VAL INT ) f o r c e i n t ( v ) ; break ; case RET FLOAT : i f ( v . type ! = VAL FLOAT ) f o r c e f l o a t ( v ) ; break ; } } s t a t i c i n l i n e ident ∗f o r c e i d e n t ( t a g v a l &v ) { switch ( v . type ) { case VAL IDENT : return v . id ; case VAL MACRO: { ident ∗id = newident ( v . s , IDF UNKNOWN) ; v . s e t i d e n t ( id ) ; return id ; } case VAL STR : { ident ∗id = newident ( v . s , IDF UNKNOWN) ; delete [ ] v . s ; v . s e t i d e n t ( id ) ; return id ; } } freearg ( v ) ; v . s e t i d e n t ( dummyident ) ; return dummyident ; } void t a g v a l : : cleanup ( ) { f r e e a r g (∗ t h i s ) ; } s t a t i c i n l i n e void f r e e a r g s ( t a g v a l ∗args , i n t &oldnum , i n t newnum) { f o r ( i n t i = newnum; i < oldnum ; i ++) f r e e a r g ( args [ i ] ) ; oldnum = newnum; } s t a t i c i n l i n e void cleancode ( ident &id ) { i f ( id . code ) { id . code [ 0 ] −= 0x100 ; i f ( i n t ( id . code [ 0 ] ) < 0x100 ) d e l e t e [ ] id . code ; id . code = NULL; } } struct n u l l v a l : t a g v a l { nullval ( ) { setnull ( ) ; } } nullval ; t a g v a l noret = nullval , ∗commandret = &noret ; void clear command ( ) { enumerate ( idents , ident , i , { i f ( i . type==ID ALIAS ) { DELETEA( i .name) ; i . forcenull ( ) ; DELETEA( i . code ) ; } }) ; } void c l e a r o v e r r i d e ( ident &i ) { i f ( ! ( i . f l a g s&IDF OVERRIDDEN) ) return ; switch ( i . type ) { case ID ALIAS : i f ( i . valtype==VAL STR ) {

} i . f l a g s &= ˜IDF OVERRIDDEN; } void c l e a r o v e r r i d e s ( ) { enumerate ( idents , ident , i , c l e a r o v e r r i d e ( i ) ) ; } s t a t i c bool i n i t e d i d e n t s = f a l s e ; s t a t i c vector ∗i d e n t i n i t s = NULL; s t a t i c i n l i n e ident ∗addident ( const ident &id ) { i f ( ! initedidents ) { i f ( ! i d e n t i n i t s ) i d e n t i n i t s = new vector; i d e n t i n i t s−>add ( id ) ; return NULL; } ident &def = idents . access ( id .name, id ) ; def . index = identmap . length ( ) ; return identmap . add(& def ) ; } s t a t i c bool i n i t i d e n t s ( ) { i n i t e d i d e n t s = true ; f o r ( i n t i = 0; i < MAXARGS; i ++) { defformatstring ( argname ) ( ” arg%d ” , i +1) ; newident ( argname , IDF ARG ) ; } dummyident = newident ( ” / /dummy” , IDF UNKNOWN) ; i f ( identinits ) { loopv (∗ i d e n t i n i t s ) addident ( ( ∗ i d e n t i n i t s ) [ i ] ) ; DELETEP( i d e n t i n i t s ) ; } return true ; } s t a t i c bool f o r c e i n i t i d e n t s = i n i t i d e n t s ( ) ; s t a t i c const char ∗s o u r c e f i l e = NULL, ∗sourcestr = NULL; s t a t i c const char ∗debugline ( const char ∗p , const char ∗fmt ) { i f ( ! sourcestr ) return fmt ; i n t num = 1; const char ∗l i n e = sourcestr ; for ( ; ; ) { const char ∗end = strchr ( l i n e , ’\n ’ ) ; i f ( ! end ) end = l i n e + s t r l e n ( l i n e ) ; i f ( p >= l i n e && p id ; ++depth ; i f ( depth < dbgalias ) conoutf (CON ERROR, ” %d ) %s ” , t o t a l−depth+1 , id−>name) ; e l s e i f ( l−>next == &noalias ) conoutf (CON ERROR, depth == dbgalias ? ” %d ) %s ” : ” ..%d ) %s ” , t o t a l−depth+1 , id−>name) ; } } s t a t i c i n t nodebug = 0; s t a t i c void debugcode ( const char ∗fmt , . . . ) PRINTFARGS( 1 , 2) ; s t a t i c void debugcode ( const char ∗fmt , . . . ) { i f ( nodebug ) return ; v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv (CON ERROR, fmt , args ) ; va end ( args ) ; debugalias ( ) ; } s t a t i c void debugcodeline ( const char ∗p , const char ∗fmt , . . . ) PRINTFARGS ( 2 , 3) ; s t a t i c void debugcodeline ( const char ∗p , const char ∗fmt , . . . ) { i f ( nodebug ) return ; v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv (CON ERROR, debugline ( p , fmt ) , args ) ; va end ( args ) ; debugalias ( ) ;

s t a t i c i n l i n e void popalias ( ident &id ) { i f ( id . type == ID ALIAS && id . index >= MAXARGS) poparg ( id ) ; } KEYWORD( l o c a l , ID LOCAL ) ; s t a t i c i n l i n e bool checknumber ( const char ∗s ) { i f ( i s d i g i t ( s [ 0 ] ) ) return true ; e l s e switch ( s [ 0 ] ) { case ’ + ’ : case ’ − ’: return i s d i g i t ( s [ 1 ] ) || ( s [ 1 ] == ’ . ’ && i s d i g i t (s[2]) ) ; case ’ . ’ : return i s d i g i t ( s [ 1 ] ) ! = 0; d e f a u l t : return f a l s e ; } } ident ∗newident ( const char ∗name, i n t f l a g s ) { ident ∗id = idents . access (name) ; i f ( ! id ) { i f ( checknumber (name) ) { debugcode ( ” number %s i s not a v a l i d i d e n t i f i e r name” , name) ; return dummyident ; } id = addident ( ident ( ID ALIAS , newstring (name) , f l a g s ) ) ; } return id ; } ident ∗w r i t e i d e n t ( const char ∗name, i n t f l a g s ) { ident ∗id = newident ( name, f l a g s ) ; i f ( id−>index < MAXARGS && ! ( aliasstack−>usedargs&(1argstack [ id−>index ] ) ; aliasstack−>usedargs |= 1index < MAXARGS && ! ( aliasstack−>usedargs&(1f l a g s&IDF READONLY ) debugcode ( ” v a r i a b l e %s i s read−only ” , id−> name) ; e l s e c l e a r o v e r r i d e (∗ id ) ; } COMMAND( resetvar , ” s ” ) ;

s t a t i c i n l i n e void poparg ( ident &id ) { i f ( ! id . stack ) return ; identstack ∗stack = id . stack ; i f ( id . valtype == VAL STR ) d e l e t e [ ] id . v a l . s ; id . s e t v a l (∗ stack ) ; cleancode ( id ) ; id . stack = stack−>next ; } ICOMMAND( push , ” r t e ” , ( ident ∗id , t a g v a l ∗v , uint ∗code ) , { i f ( id−>type ! = ID ALIAS || id−>index < MAXARGS) return ; identstack stack ; pusharg(∗ id , ∗v , stack ) ; v−>type = VAL NULL ; id−>f l a g s &= ˜IDF UNKNOWN; executeret ( code , ∗commandret ) ; poparg(∗ id ) ; }) ; s t a t i c i n l i n e void pushalias ( ident &id , identstack &stack ) { i f ( id . type == ID ALIAS && id . index >= MAXARGS)

s t a t i c i n l i n e void setarg ( ident &id , t a g v a l &v ) { i f ( aliasstack−>usedargs&(1usedargs |= 1index < MAXARGS) setarg (∗ id , v ) ; e l s e s e t a l i a s (∗ id , v ) ; } else { debugcode ( ” cannot r e d e f i n e b u i l t i n %s with an a l i a s ” , id−>name) ; freearg ( v ) ; } } e l s e i f ( checknumber (name) ) { debugcode ( ” cannot a l i a s number %s ” , name) ; freearg ( v ) ; } else { addident ( ident ( ID ALIAS , newstring (name) , v , i d e n t f l a g s ) ) ; } } void a l i a s ( const char ∗name, const char ∗s t r ) { tagval v ; v . s e t s t r ( newstring ( s t r ) ) ; s e t a l i a s ( name, v ) ; } void a l i a s ( const char ∗name, t a g v a l &v ) { s e t a l i a s ( name, v ) ; } ICOMMAND( a l i a s , ” s t ” , ( const char ∗name, t a g v a l ∗v ) , { s e t a l i a s ( name, ∗v ) ; v−>type = VAL NULL ; }) ; // variable ’ s and commands are r e g i s t e r e d through globals , see cube . h i n t v a r i a b l e ( const char ∗name, i n t min , i n t cur , i n t max, i n t ∗storage , identfun fun , i n t f l a g s ) { addident ( ident ( ID VAR , name, min , max, storage , ( void ∗)fun , f l a g s ) ) ; return cur ; } f l o a t f v a r i a b l e ( const char ∗name, f l o a t min , f l o a t cur , f l o a t max, f l o a t ∗storage , identfun fun , i n t f l a g s ) { addident ( ident ( ID FVAR , name, min , max, storage , ( void ∗)fun , f l a g s ) ) ; return cur ; } char ∗s v a r i a b l e ( const char ∗name, const char ∗cur , char ∗∗storage , identfun fun , i n t f l a g s ) { addident ( ident ( ID SVAR , name, storage , ( void ∗)fun , f l a g s ) ) ; return newstring ( cur ) ; } #define GETVAR ( id , vartype , name, r e t v a l ) \ ident ∗id = idents . access (name) ; \ i f ( ! id || id−>type ! = vartype ) return r e t v a l ; #define GETVAR( id , name, r e t v a l ) GETVAR ( id , ID VAR , name, r e t v a l ) #define OVERRIDEVAR( e r r o r v a l , saveval , r e s e t v a l , c l e a r v a l ) \ i f ( i d e n t f l a g s&IDF OVERRIDDEN || id−>f l a g s&IDF OVERRIDE ) \ { \ i f ( id−>f l a g s&IDF PERSIST ) \ { \ debugcode ( ” cannot override p e r s i s t e n t v a r i a b l e %s ” , id−>name) ; \ errorval ; \ } \ i f ( ! ( id−>f l a g s&IDF OVERRIDDEN) ) { saveval ; id−>f l a g s |= IDF OVERRIDDEN; } \ else { clearval ; } \ } \ else \ { \ i f ( id−>f l a g s&IDF OVERRIDDEN) { r e s e t v a l ; id−>f l a g s &= ˜ IDF OVERRIDDEN; } \ clearval ; \ } void setvar ( const char ∗name, i n t i , bool dofunc , bool doclamp ) { GETVAR( id , name, ) ; OVERRIDEVAR( return , id−>o v e r r i d e v a l . i = ∗id−>storage . i , , )

i f ( doclamp ) ∗id−>storage . i = clamp ( i , id−>minval , id−>maxval ) ; e l s e ∗id−>storage . i = i ; i f ( dofunc ) id−>changed ( ) ; } void s e t f v a r ( const char ∗name, f l o a t f , bool dofunc , bool doclamp ) { GETVAR ( id , ID FVAR , name, ) ; OVERRIDEVAR( return , id−>o v e r r i d e v a l . f = ∗id−>storage . f , , ) ; i f ( doclamp ) ∗id−>storage . f = clamp ( f , id−>minvalf , id−>maxvalf ) ; e l s e ∗id−>storage . f = f ; i f ( dofunc ) id−>changed ( ) ; } void setsvar ( const char ∗name, const char ∗str , bool dofunc ) { GETVAR ( id , ID SVAR , name, ) ; OVERRIDEVAR( return , id−>o v e r r i d e v a l . s = ∗id−>storage . s , d e l e t e [ ] id−> o v e r r i d e v a l . s , d e l e t e [ ] ∗id−>storage . s ) ; ∗id−>storage . s = newstring ( s t r ) ; i f ( dofunc ) id−>changed ( ) ; } i n t getvar ( const char ∗name) { GETVAR( id , name, 0) ; return ∗id−>storage . i ; } i n t getvarmin ( const char ∗name) { GETVAR( id , name, 0) ; return id−>minval ; } i n t getvarmax ( const char ∗name) { GETVAR( id , name, 0) ; return id−>maxval ; } f l o a t getfvarmin ( const char ∗name) { GETVAR ( id , ID FVAR , name, 0) ; return id−>minvalf ; } f l o a t getfvarmax ( const char ∗name) { GETVAR ( id , ID FVAR , name, 0) ; return id−>maxvalf ; } bool i d e n t e x i s t s ( const char ∗name) { return idents . access (name) ! =NULL; } ident ∗getident ( const char ∗name) { return idents . access (name) ; } void touchvar ( const char ∗name) { ident ∗id = idents . access (name) ; i f ( id ) switch ( id−>type ) { case ID VAR : case ID FVAR : case ID SVAR : id−>changed ( ) ; break ; } } const char ∗g e t a l i a s ( const char ∗name) { ident ∗i = idents . access (name) ; return i && i−>type==ID ALIAS && ( i−>index >= MAXARGS || aliasstack−> usedargs&(1g e t s t r ( ) : ” ” ; } i n t clampvar ( ident ∗id , i n t val , i n t minval , i n t maxval ) { i f ( v a l < minval ) v a l = minval ; e l s e i f ( v a l > maxval ) v a l = maxval ; e l s e return v a l ; debugcode ( id−>f l a g s&IDF HEX ? ( minval name, minval , maxval ) ; return v a l ; } void setvarchecked ( ident ∗id , i n t v a l ) { i f ( id−>f l a g s&IDF READONLY ) debugcode ( ” v a r i a b l e %s i s read−only ” , id−> name) ; # i f n d e f STANDALONE e l s e i f ( ! ( id−>f l a g s&IDF OVERRIDE ) || i d e n t f l a g s&IDF OVERRIDDEN || game : : allowedittoggle ( ) ) #e l s e else #endif { OVERRIDEVAR( return , id−>o v e r r i d e v a l . i = ∗id−>storage . i , , ) i f ( v a l < id−>minval || v a l > id−>maxval ) v a l = clampvar ( id , val , id

engine/command.cpp −>minval , id−>maxval ) ; ∗id−>storage . i = v a l ; id−>changed ( ) ; // c a l l t r i g g e r function i f a v a i l a b l e # i f n d e f STANDALONE i f ( id−>f l a g s&IDF OVERRIDE && ! ( i d e n t f l a g s&IDF OVERRIDDEN) ) game : : v a r t r i g g e r ( id ) ; #endif } } f l o a t clampfvar ( ident ∗id , f l o a t val , f l o a t minval , f l o a t maxval ) { i f ( v a l < minval ) v a l = minval ; e l s e i f ( v a l > maxval ) v a l = maxval ; e l s e return v a l ; debugcode ( ” v a l i d range f o r %s i s %s ..% s ” , id−>name, f l o a t s t r ( minval ) , f l o a t s t r ( maxval ) ) ; return v a l ; } void setfvarchecked ( ident ∗id , f l o a t v a l ) { i f ( id−>f l a g s&IDF READONLY ) debugcode ( ” v a r i a b l e %s i s read−only ” , id−> name) ; # i f n d e f STANDALONE e l s e i f ( ! ( id−>f l a g s&IDF OVERRIDE ) || i d e n t f l a g s&IDF OVERRIDDEN || game : : allowedittoggle ( ) ) #e l s e else #endif { OVERRIDEVAR( return , id−>o v e r r i d e v a l . f = ∗id−>storage . f , , ) ; i f ( v a l < id−>minvalf || v a l > id−>maxvalf ) v a l = clampfvar ( id , val , id−>minvalf , id−>maxvalf ) ; ∗id−>storage . f = v a l ; id−>changed ( ) ; # i f n d e f STANDALONE i f ( id−>f l a g s&IDF OVERRIDE && ! ( i d e n t f l a g s&IDF OVERRIDDEN) ) game : : v a r t r i g g e r ( id ) ; #endif } }

const char ∗parsestring ( const char ∗p ) { f o r ( ; ∗p ; p++) switch (∗p ) { case ’\ r ’ : case ’\n ’ : case ’ \ ” ’ : return p ; case ’ ˆ ’ : i f (∗++p ) break ; return p ; } return p ; } i n t unescapestring ( char ∗dst , const char ∗src , const char ∗end ) { char ∗s t a r t = dst ; while ( src < end ) { i n t c = ∗src ++; i f ( c == ’ ˆ ’ ) { i f ( src >= end ) break ; i n t e = ∗src ++; switch ( e ) { case ’ n ’ : ∗dst++ = ’\n ’ ; break ; case ’ t ’ : ∗dst++ = ’\ t ’ ; break ; case ’ f ’ : ∗dst++ = ’\ f ’ ; break ; d e f a u l t : ∗dst++ = e ; break ; } } e l s e ∗dst++ = c ; } return dst − s t a r t ; } s t a t i c char ∗conc ( vector &buf , t a g v a l ∗v , i n t n , bool space , const char ∗p r e f i x = NULL, i n t p r e f i x l e n = 0) { i f ( prefix ) { buf . put ( p r e f i x , p r e f i x l e n ) ; i f ( space && n ) buf . add ( ’ ’ ) ; } loopi (n) { const char ∗s = ” ” ; i n t len = 0; switch ( v [ i ] . type ) { case VAL INT : s = i n t s t r ( v [ i ] . i ) ; break ; case VAL FLOAT : s = f l o a t s t r ( v [ i ] . f ) ; break ; case VAL STR : s = v [ i ] . s ; break ; case VAL MACRO: s = v [ i ] . s ; len = v [ i ] . code[−1]>>8; goto haslen ; } len = i n t ( s t r l e n ( s ) ) ; haslen : buf . put ( s , len ) ; i f ( i == n−1) break ; i f ( space ) buf . add ( ’ ’ ) ; } buf . add ( ’ \ 0 ’ ) ; return buf . getbuf ( ) ;

void setsvarchecked ( ident ∗id , const char ∗v a l ) { i f ( id−>f l a g s&IDF READONLY ) debugcode ( ” v a r i a b l e %s i s read−only ” , id−> name) ; # i f n d e f STANDALONE e l s e i f ( ! ( id−>f l a g s&IDF OVERRIDE ) || i d e n t f l a g s&IDF OVERRIDDEN || game : : allowedittoggle ( ) ) #e l s e else #endif { OVERRIDEVAR( return , id−>o v e r r i d e v a l . s = ∗id−>storage . s , d e l e t e [ ] id −>o v e r r i d e v a l . s , d e l e t e [ ] ∗id−>storage . s ) ; ∗id−>storage . s = newstring ( v a l ) ; id−>changed ( ) ; # i f n d e f STANDALONE i f ( id−>f l a g s&IDF OVERRIDE && ! ( i d e n t f l a g s&IDF OVERRIDDEN) ) game : : v a r t r i g g e r ( id ) ; #endif } } bool addcommand( const char ∗name, identfun fun , const char ∗args ) { uint argmask = 0; i n t numargs = 0; bool l i m i t = true ; f o r ( const char ∗fmt = args ; ∗fmt ; fmt ++) switch (∗fmt ) { case ’ i ’ : case ’ b ’ : case ’ f ’ : case ’ t ’ : case ’N ’ : case ’D’ : i f ( numargs < MAXARGS) numargs++; break ; case ’ s ’ : case ’ e ’ : case ’ r ’ : case ’ $ ’ : i f ( numargs < MAXARGS) { argmask |= 1>8; break ; case VAL STR : len += i n t ( s t r l e n ( v [ i ] . s ) ) ; break ; default : { vector buf ; buf . reserve ( len ) ; conc ( buf , v , n , space , p r e f i x , p r e f i x l e n ) ; return newstring ( buf . getbuf ( ) , buf . length ( ) −1) ; } } char ∗buf = newstring ( p r e f i x ? p r e f i x : ” ” , len ) ; i f ( p r e f i x && space && n ) { buf [ p r e f i x l e n ] = ’ ’ ; buf [ p r e f i x l e n ] = ’\0 ’; } loopi (n) { s t r c a t ( buf , v [ i ] . s ) ; i f ( i ==n−1) break ; i f ( space ) s t r c a t ( buf , ” ” ) ; } return buf ;

220

Foundations of Videogame Programming Code Repository

}

code . add ( i ) ; }

s t a t i c i n l i n e void skipcomments ( const char ∗&p ) { for ( ; ; ) { p += strspn ( p , ” \t\r ” ) ; i f ( p [ 0 ] ! = ’ / ’ || p [ 1 ] ! = ’ / ’ ) break ; p += strcspn ( p , ”\n\0”) ; } } s t a t i c i n l i n e char ∗cutstring ( const char ∗&p , i n t &len ) { p++; const char ∗end = parsestring ( p ) ; char ∗s = newstring ( end − p ) ; len = unescapestring ( s , p , end ) ; s [ len ] = ’ \ 0 ’ ; p = end ; i f (∗p = = ’ \ ” ’ ) p++; return s ; } s t a t i c i n l i n e const char ∗parseword ( const char ∗p ) { const i n t maxbrak = 100; s t a t i c char brakstack [ maxbrak ] ; i n t brakdepth = 0; f o r ( ; ; p++) { p += strcspn ( p , ” \ ” / ; ( ) [ ] \t\r\n\0”) ; switch ( p [ 0 ] ) { case ’ ” ’ : case ’ ; ’ : case ’ ’ : case ’\ t ’ : case ’\ r ’ : case ’\n ’ : case ’ \ 0 ’ : return p ; case ’ / ’ : i f ( p [ 1 ] == ’ / ’ ) return p ; break ; case ’ [ ’ : case ’ ( ’ : i f ( brakdepth >= maxbrak ) return p ; brakstack [ brakdepth ++] = p [ 0 ] ; break ; case ’ ] ’ : i f ( brakdepth 8]); continue ; case CODE IDENTARG: { ident ∗id = identmap [ op>>8]; i f ( ! ( aliasstack−>usedargs&(1argstack [ id−>index ] ) ; aliasstack−>usedargs |= 1index < MAXARGS && ! ( aliasstack−>usedargs&(1argstack [ id−>index ] ) ; aliasstack−>usedargs |= 1type ) \ { \ case ID ALIAS : \ i f ( id−>f l a g s&IDF UNKNOWN) break ; \ f r e e a r g ( arg ) ; \ i f ( id−>index < MAXARGS && ! ( aliasstack−>usedargs &(1g e t s t r ( ) ) ) , arg . s e t s t r ( newstring (∗ id−>storage . s ) ) , arg . s e t s t r ( newstring ( i n t s t r (∗ id−>storage . i ) ) ) , arg . s e t s t r ( newstring ( f l o a t s t r (∗ id−>storage . f ) ) ) , arg . s e t s t r ( newstring ( ” ” ) ) ) ; case CODE LOOKUP|RET STR : #define LOOKUP( aval ) { \ id = identmap [ op>>8]; \ i f ( id−>f l a g s&IDF UNKNOWN) debugcode ( ” unknown a l i a s lookup : %s ” , id−>name) ; \ aval ; \ continue ; \ } LOOKUP( args [ numargs+ + ] . s e t s t r ( newstring ( id−>g e t s t r ( ) ) ) ) ; case CODE LOOKUPARG|RET STR : #define LOOKUPARG( aval , nval ) { \ id = identmap [ op>>8]; \ i f ( ! ( aliasstack−>usedargs&(1g e t s t r ( ) ) ) , args [ numargs+ + ] . s e t s t r ( newstring ( ” ” ) ) ) ; case CODE LOOKUPU|RET INT :

226

Foundations of Videogame Programming Code Repository LOOKUPU( arg . s e t i n t ( id−>g e t i n t ( ) ) , arg . s e t i n t ( parseint (∗ id−>storage . s ) ) , arg . s e t i n t (∗ id−>storage . i ) , arg . s e t i n t ( i n t (∗ id−>storage . f ) ) , arg . s e t i n t ( 0 ) ) ; case CODE LOOKUP|RET INT : LOOKUP( args [ numargs+ + ] . s e t i n t ( id−>g e t i n t ( ) ) ) ; case CODE LOOKUPARG|RET INT : LOOKUPARG( args [ numargs+ + ] . s e t i n t ( id−>g e t i n t ( ) ) , args [ numargs ++]. setint (0) ) ; case CODE LOOKUPU|RET FLOAT : LOOKUPU( arg . s e t f l o a t ( id−>g e t f l o a t ( ) ) , arg . s e t f l o a t ( p a r s e f l o a t (∗ id−>storage . s ) ) , arg . s e t f l o a t ( f l o a t (∗ id−>storage . i ) ) , arg . s e t f l o a t (∗ id−>storage . f ) , arg . s e t f l o a t ( 0 . 0 f ) ) ; case CODE LOOKUP|RET FLOAT : LOOKUP( args [ numargs+ + ] . s e t f l o a t ( id−>g e t f l o a t ( ) ) ) ; case CODE LOOKUPARG|RET FLOAT : LOOKUPARG( args [ numargs+ + ] . s e t f l o a t ( id−>g e t f l o a t ( ) ) , args [ numargs+ + ] . s e t f l o a t ( 0 . 0 f ) ) ; case CODE LOOKUPU|RET NULL : LOOKUPU( id−>g e t v a l ( arg ) , arg . s e t s t r ( newstring (∗ id−>storage . s ) ) , arg . s e t i n t (∗ id−>storage . i ) , arg . s e t f l o a t (∗ id−>storage . f ) , arg . s e t n u l l ( ) ) ; case CODE LOOKUP|RET NULL : LOOKUP( id−>g e t v a l ( args [ numargs + + ] ) ) ; case CODE LOOKUPARG|RET NULL : LOOKUPARG( id−>g e t v a l ( args [ numargs + + ] ) , args [ numargs+ + ] . setnull ( ) ) ; case CODE SVAR|RET STR : case CODE SVAR|RET NULL : args [ numargs + + ] . s e t s t r ( newstring (∗identmap [ op>>8]−>storage . s ) ) ; continue ; case CODE SVAR|RET INT : args [ numargs+ + ] . s e t i n t ( parseint (∗ identmap [ op>>8]−>storage . s ) ) ; continue ; case CODE SVAR|RET FLOAT : args [ numargs+ + ] . s e t f l o a t ( p a r s e f l o a t (∗ identmap [ op>>8]−>storage . s ) ) ; continue ; case CODE SVAR1: setsvarchecked ( identmap [ op>>8], args [ 0 ] . s ) ; f r e e a r g s ( args , numargs , 0) ; continue ; case CODE IVAR|RET INT : case CODE IVAR|RET NULL : args [ numargs + + ] . s e t i n t (∗identmap [ op>>8]−>storage . i ) ; continue ; case CODE IVAR|RET STR : args [ numargs+ + ] . s e t s t r ( newstring ( i n t s t r (∗identmap [ op>>8]−>storage . i ) ) ) ; continue ; case CODE IVAR|RET FLOAT : args [ numargs+ + ] . s e t f l o a t ( f l o a t (∗ identmap [ op>>8]−>storage . i ) ) ; continue ; case CODE IVAR1: setvarchecked ( identmap [ op>>8], args [ 0 ] . i ) ; numargs = 0; continue ; case CODE IVAR2: setvarchecked ( identmap [ op>>8], ( args [ 0 ] . istorage . f ) ) ) ; continue ; case CODE FVAR|RET INT : args [ numargs+ + ] . s e t i n t ( i n t (∗identmap [ op >>8]−>storage . f ) ) ; continue ; case CODE FVAR1: setfvarchecked ( identmap [ op>>8], args [ 0 ] . f ) ; numargs = 0; continue ;

case CODE COM|RET NULL : case CODE COM|RET STR : case CODE COM| RET FLOAT : case CODE COM|RET INT : id = identmap [ op>>8]; # i f n d e f STANDALONE callcom : #endif forcenull ( result ) ; CALLCOM( numargs ) forceresult : f r e e a r g s ( args , numargs , 0) ; forcearg ( result , op&CODE RET MASK) ; continue ; # i f n d e f STANDALONE case CODE COMD|RET NULL : case CODE COMD|RET STR : case CODE COMD| RET FLOAT : case CODE COMD|RET INT : id = identmap [ op>>8]; args [ numargs ] . s e t i n t ( addreleaseaction ( conc ( args , numargs , true , id−>name) ) ? 1 : 0) ; numargs++; goto callcom ; #endif case CODE COMV|RET NULL : case CODE COMV|RET STR : case CODE COMV| RET FLOAT : case CODE COMV|RET INT : id = identmap [ op>>8]; forcenull ( result ) ; ( ( comfunv ) id−>fun ) ( args , numargs ) ; goto f o r c e r e s u l t ; case CODE COMC|RET NULL : case CODE COMC|RET STR : case CODE COMC| RET FLOAT : case CODE COMC|RET INT :

id = identmap [ op>>8]; forcenull ( result ) ; { vector buf ; ( ( comfun1 ) id−>fun ) ( conc ( buf , args , numargs , true ) ) ; } goto f o r c e r e s u l t ; case CODE CONC|RET NULL : case CODE CONC|RET STR : case CODE CONC| RET FLOAT : case CODE CONC|RET INT : case CODE CONCW|RET NULL : case CODE CONCW|RET STR : case CODE CONCW|RET FLOAT : case CODE CONCW|RET INT : { i n t numconc = op>>8; char ∗s = conc(&args [ numargs−numconc ] , numconc, ( op& CODE OP MASK) ==CODE CONC) ; f r e e a r g s ( args , numargs , numargs−numconc ) ; args [ numargs+ + ] . s e t s t r ( s ) ; forcearg ( args [ numargs−1], op&CODE RET MASK) ; continue ; } case CODE CONCM|RET NULL : case CODE CONCM|RET STR : case CODE CONCM|RET FLOAT : case CODE CONCM|RET INT : { i n t numconc = op>>8; char ∗s = conc(&args [ numargs−numconc ] , numconc, f a l s e ) ; f r e e a r g s ( args , numargs , numargs−numconc ) ; result . setstr ( s ) ; forcearg ( result , op&CODE RET MASK) ; continue ; } case CODE ALIAS : s e t a l i a s (∗identmap [ op>>8], args[−−numargs ] ) ; f r e e a r g s ( args , numargs , 0) ; continue ; case CODE ALIASARG: setarg (∗identmap [ op>>8], args[−−numargs ] ) ; f r e e a r g s ( args , numargs , 0) ; continue ; case CODE ALIASU: f o r c e s t r ( args [ 0 ] ) ; s e t a l i a s ( args [ 0 ] . s , args[−−numargs ] ) ; f r e e a r g s ( args , numargs , 0) ; continue ; case CODE CALL|RET NULL : case CODE CALL|RET STR : case CODE CALL| RET FLOAT : case CODE CALL|RET INT : #define CALLALIAS ( o f f s e t ) { \ identstack argstack [MAXARGS] ; \ f o r ( i n t i = 0; i < numargs−o f f s e t ; i ++) \ pusharg(∗identmap [ i ] , args [ i + o f f s e t ] , argstack [ i ] ) ; \ i n t oldargs = numargs , newargs = numargs−o f f s e t ; \ numargs = newargs ; \ int oldflags = identflags ; \ i d e n t f l a g s |= id−>f l a g s&IDF OVERRIDDEN; \ i d e n t l i n k a l i a s l i n k = { id , aliasstack , (1code = compilecode ( id−>g e t s t r ( ) ) ; \ uint ∗code = id−>code ; \ code [ 0 ] += 0x100 ; \ runcode ( code+1 , r e s u l t ) ; \ code [ 0 ] −= 0x100 ; \ i f ( i n t ( code [ 0 ] ) < 0x100 ) d e l e t e [ ] code ; \ a l i a s s t a c k = a l i a s l i n k . next ; \ identflags = oldflags ; \ f o r ( i n t i = 0; i < newargs ; i ++) \ poparg(∗identmap [ i ] ) ; \ f o r ( i n t argmask = a l i a s l i n k . usedargs&(˜08]; i f ( ! ( aliasstack−>usedargs&(1type ) { case ID COMMAND: f r e e a r g ( args [ 0 ] ) ; callcommand ( id , args +1 , numargs−1); forcearg ( result , op&CODE RET MASK) ; numargs = 0; continue ; case ID LOCAL : { identstack l o c a l s [MAXARGS] ; f r e e a r g ( args [ 0 ] ) ; l o o p j ( numargs−1) pushalias (∗ f o r c e i d e n t ( args [ j + 1 ] ) , locals [ j ] ) ; code = runcode ( code , r e s u l t ) ; l o o p j ( numargs−1) popalias (∗ args [ j + 1 ] . id ) ; goto e x i t ; } case ID VAR : i f ( numargs f l a g s&IDF HEX && numargs > 2) { v a l = ( v a l = 0x100 ) code . disown ( ) ; int i = result . getint ( ) ; freearg ( result ) ; return i ; } s t a t i c i n l i n e bool getbool ( const char ∗s ) { switch ( s [ 0 ] ) { case ’ + ’ : case ’ − ’: switch ( s [ 1 ] ) { case ’ 0 ’ : break ; case ’ . ’ : return ! i s d i g i t ( s [ 2 ] ) || p a r s e f l o a t ( s ) ! = 0; d e f a u l t : return true ; } // f a l l through case ’ 0 ’ : { char ∗end ; i n t v a l = s t r t o l ( ( char ∗)s , &end , 0) ; i f ( v a l ) return true ; switch (∗end ) { case ’ e ’ : case ’ . ’ : return p a r s e f l o a t ( s ) ! = 0; d e f a u l t : return f a l s e ; } } case ’ . ’ : return ! i s d i g i t ( s [ 1 ] ) || p a r s e f l o a t ( s ) ! = 0; case ’ \ 0 ’ : return f a l s e ; d e f a u l t : return true ; } } s t a t i c i n l i n e bool getbool ( const t a g v a l &v ) { switch ( v . type ) { case VAL FLOAT : return v . f ! = 0 ; case VAL INT : return v . i ! = 0 ; case VAL STR : case VAL MACRO: return getbool ( v . s ) ; d e f a u l t : return f a l s e ; } } bool executebool ( const uint ∗code ) { tagval result ; runcode ( code , r e s u l t ) ; bool b = getbool ( r e s u l t ) ; freearg ( result ) ; return b ; } bool executebool ( const char ∗p ) { tagval result ; executeret ( p , r e s u l t ) ; bool b = getbool ( r e s u l t ) ; freearg ( result ) ; return b ; } bool e x e c f i l e ( const char ∗c f g f i l e , bool msg ) { string s ; copystring ( s , c f g f i l e ) ; char ∗buf = l o a d f i l e ( path ( s ) , NULL) ;

227

228

Foundations of Videogame Programming Code Repository

i f ( ! buf ) { i f ( msg ) conoutf (CON ERROR, ” could not read \”%s \”” , c f g f i l e ) ; return f a l s e ; } const char ∗o l d s o u r c e f i l e = s o u r c e f i l e , ∗oldsourcestr = sourcestr ; sourcefile = c f g f i l e ; sourcestr = buf ; execute ( buf ) ; sourcefile = oldsourcefile ; sourcestr = oldsourcestr ; d e l e t e [ ] buf ; return true ;

id . storage . s ) ) ; break ; } } f−>p r i n t f (”\n ” ) ; writebinds ( f ) ; f−>p r i n t f (”\n ” ) ; loopv ( ids ) { ident &id = ∗ids [ i ] ; i f ( id . type==ID ALIAS && id . f l a g s&IDF PERSIST && ! ( id . f l a g s& IDF OVERRIDDEN) ) switch ( id . valtype ) { case VAL STR : i f ( ! id . v a l . s [ 0 ] ) break ; i f ( ! validateblock ( id . v a l . s ) ) { f−>p r i n t f (”%s = %s\n” , escapeid ( id ) , escapestring ( id . v a l . s ) ) ; break ; } case VAL FLOAT : case VAL INT : f−>p r i n t f (”%s = [%s]\n” , escapeid ( id ) , id . g e t s t r ( ) ) ; break ; } } f−>p r i n t f (”\n ” ) ; writecompletions ( f ) ; delete f ;

} const char ∗escapestring ( const char ∗s ) { s t a t i c vector strbuf [ 3 ] ; s t a t i c i n t s t r i d x = 0; s t r i d x = ( s t r i d x + 1)%3; vector &buf = strbuf [ s t r i d x ] ; buf . s e t s i z e ( 0 ) ; buf . add ( ’ ” ’ ) ; f o r ( ; ∗s ; s ++) switch (∗s ) { case ’\n ’ : buf . put ( ” ˆ n” , 2) ; break ; case ’\ t ’ : buf . put ( ” ˆ t ” , 2) ; break ; case ’\ f ’ : buf . put ( ” ˆ f ” , 2) ; break ; case ’ ” ’ : buf . put ( ” ˆ \ ” ” , 2) ; break ; case ’ ˆ ’ : buf . put ( ” ˆ ˆ ” , 2) ; break ; d e f a u l t : buf . add(∗s ) ; break ; } buf . put (”\”\0” , 2) ; return buf . getbuf ( ) ; } const char ∗escapeid ( const char ∗s ) { const char ∗end = s + strcspn ( s , ” \ ” / ; ( ) [ ]@ \f\t\r\n\0”) ; return ∗end ? escapestring ( s ) : s ; } bool validateblock ( const char ∗s ) { const i n t maxbrak = 100; s t a t i c char brakstack [ maxbrak ] ; i n t brakdepth = 0; f o r ( ; ∗s ; s ++) switch (∗s ) { case ’ [ ’ : case ’ ( ’ : i f ( brakdepth >= maxbrak ) return f a l s e ; brakstack [ brakdepth ++] = ∗s ; break ; case ’ ] ’ : i f ( brakdepth s e t f l o a t ( v ) ; } #undef ICOMMANDNAME #define ICOMMANDNAME(name)

stdcmd

# i f n d e f STANDALONE s t a t i c i n l i n e bool s o r t i d e n t s ( ident ∗x , ident ∗y ) { return strcmp ( x−>name, y−>name) < 0; }

ICOMMAND( do , ” e ” , ( uint ∗body ) , executeret ( body , ∗commandret ) ) ; ICOMMAND( i f , ” tee ” , ( t a g v a l ∗cond , uint ∗t , uint ∗f ) , executeret ( getbool (∗cond ) ? t : f , ∗commandret ) ) ; ICOMMAND( ? , ” t t t ” , ( t a g v a l ∗cond , t a g v a l ∗t , t a g v a l ∗f ) , r e s u l t ( ∗ ( getbool (∗cond ) ? t : f ) ) ) ;

void w r i t e c f g ( const char ∗name) { stream ∗f = o p e n u t f 8 f i l e ( path (name && name[ 0 ] ? name : game : : savedconfig ( ) , true ) , ”w” ) ; i f ( ! f ) return ; f−>p r i n t f ( ” / / automatically written on e x i t , DO NOT MODIFY\n// d e l e t e t h i s f i l e to have %s overwrite these s e t t i n g s\n// modify s e t t i n g s in game, or put s e t t i n g s in %s to override anything\n\ n” , game : : d e f a u l t c o n f i g ( ) , game : : autoexec ( ) ) ; game : : w r i t e c l i e n t i n f o ( f ) ; f−>p r i n t f (”\n ” ) ; writecrosshairs ( f ) ; vector ids ; enumerate ( idents , ident , id , ids . add(& id ) ) ; ids . s o r t ( s o r t i d e n t s ) ; loopv ( ids ) { ident &id = ∗ids [ i ] ; i f ( id . f l a g s&IDF PERSIST ) switch ( id . type ) { case ID VAR : f−>p r i n t f (”%s %d\n” , escapeid ( id ) , ∗id . storage . i ) ; break ; case ID FVAR : f−>p r i n t f (”%s %s\n” , escapeid ( id ) , f l o a t s t r (∗ id . storage . f ) ) ; break ; case ID SVAR : f−>p r i n t f (”%s %s\n” , escapeid ( id ) , escapestring (∗

s t a t i c i n l i n e void s e t i t e r ( ident &id , i n t i , identstack &stack ) { if ( i ) { i f ( id . valtype ! = VAL INT ) { i f ( id . valtype == VAL STR ) d e l e t e [ ] id . v a l . s ; cleancode ( id ) ; id . valtype = VAL INT ; } id . v a l . i = i ; } else { t a g v a l zero ; zero . s e t i n t ( 0 ) ; pusharg ( id , zero , stack ) ; id . f l a g s &= ˜IDF UNKNOWN; } } ICOMMAND( loop , ” r i e ” , ( ident ∗id , i n t ∗n , uint ∗body ) , { i f (∗n type ! = ID ALIAS ) return ; identstack stack ; l o o p i (∗n )

engine/command.cpp {

} e l s e s . add ( i ) ; } e l s e s . add ( c ) ;

s e t i t e r (∗ id , i , stack ) ; execute ( body ) ; } poparg(∗ id ) ; }) ; ICOMMAND( loopwhile , ” r i e e ” , ( ident ∗id , i n t ∗n , uint ∗cond , uint ∗body ) , { i f (∗n type ! = ID ALIAS ) return ; identstack stack ; l o o p i (∗n ) { s e t i t e r (∗ id , i , stack ) ; i f ( ! executebool ( cond ) ) break ; execute ( body ) ; } poparg(∗ id ) ; }) ; ICOMMAND( while , ” ee ” , ( uint ∗cond , uint ∗body ) , while ( executebool ( cond ) ) execute ( body ) ) ; char ∗loopconc ( ident ∗id , i n t n , uint ∗body , bool space ) { identstack stack ; vector s ; loopi (n) { s e t i t e r (∗ id , i , stack ) ; tagval v ; executeret ( body , v ) ; const char ∗v s t r = v . g e t s t r ( ) ; i n t len = s t r l e n ( v s t r ) ; i f ( space && i ) s . add ( ’ ’ ) ; s . put ( vstr , len ) ; freearg ( v ) ; } poparg(∗ id ) ; s . add ( ’ \ 0 ’ ) ; return newstring ( s . getbuf ( ) , s . length ( ) −1) ; } ICOMMAND( loopconcat , ” r i e ” , ( ident ∗id , i n t ∗n , uint ∗body ) , { i f (∗n > 0 && id−>type==ID ALIAS ) commandret−>s e t s t r ( loopconc ( id , ∗n , body , true ) ) ; }) ; ICOMMAND( loopconcatword , ” r i e ” , ( ident ∗id , i n t ∗n , uint ∗body ) , { i f (∗n > 0 && id−>type==ID ALIAS ) commandret−>s e t s t r ( loopconc ( id , ∗n , body , f a l s e ) ) ; }) ; void concat ( t a g v a l ∗v , i n t n ) { commandret−>s e t s t r ( conc ( v , n , true ) ) ; } void concatword ( t a g v a l ∗v , i n t n ) { commandret−>s e t s t r ( conc ( v , n , f a l s e ) ) ; } void r e s u l t ( t a g v a l &v ) { ∗commandret = v ; v . type = VAL NULL ; } void s t r i n g r e t ( char ∗s ) { commandret−>s e t s t r ( s ) ; } void r e s u l t ( const char ∗s ) { commandret−>s e t s t r ( newstring ( s ) ) ; } void format ( t a g v a l ∗args , i n t numargs ) { vector s ; const char ∗f = args [ 0 ] . g e t s t r ( ) ; while (∗ f ) { i n t c = ∗f ++; i f ( c == ’ % ’ ) { i n t i = ∗f ++; i f ( i >= ’ 1 ’ && i s e t s t r ( newstring ( s t a r t , end−s t a r t ) ) ; } void substr ( char ∗s , i n t ∗s t a r t , i n t ∗count , i n t ∗numargs ) { i n t len = s t r l e n ( s ) , o f f s e t = clamp(∗ s t a r t , 0 , len ) ; commandret−>s e t s t r ( newstring(&s [ o f f s e t ] , ∗numargs >= 3 ? clamp(∗count , 0 , len − o f f s e t ) : len − o f f s e t ) ) ; } void s u b l i s t ( const char ∗s , i n t ∗skip , i n t ∗count , i n t ∗numargs ) { i n t o f f s e t = max(∗ skip , 0) , len = ∗numargs >= 3 ? max(∗count , 0) : −1; l o o p i ( o f f s e t ) i f ( ! p a r s e l i s t ( s ) ) break ; i f ( len < 0) { i f ( o f f s e t > 0) s k i p l i s t ( s ) ; commandret−>s e t s t r ( newstring ( s ) ) ; return ; } const char ∗ l i s t = s , ∗s t a r t , ∗end , ∗qstart , ∗qend = s ; i f ( len > 0 && p a r s e l i s t ( s , s t a r t , end , l i s t , qend ) ) while(−−len > 0 && p a r s e l i s t ( s , s t a r t , end , qstart , qend ) ) ; commandret−>s e t s t r ( newstring ( l i s t , qend − l i s t ) ) ; } void g e t a l i a s ( char ∗s ) { result ( getalias ( s ) ) ; } ICOMMAND( exec , ” s ” , ( char ∗ f i l e ) , e x e c f i l e ( f i l e ) ) ; ICOMMAND( result , ” t ” , ( t a g v a l ∗v ) , { ∗commandret = ∗v ; v−>type = VAL NULL ; }) ; COMMAND( concat , ”V ” ) ; COMMAND( concatword , ”V ” ) ; COMMAND( format , ”V ” ) ; COMMAND( at , ” si1V ” ) ; ICOMMAND( escape , ” s ” , ( char ∗s ) , r e s u l t ( escapestring ( s ) ) ) ; ICOMMAND( unescape , ” s ” , ( char ∗s ) , { i n t len = s t r l e n ( s ) ; char ∗d = newstring ( len ) ; d [ unescapestring ( d , s , &s [ len ] ) ] = ’ \ 0 ’ ; stringret (d) ; }) ; ICOMMAND( s t r i p c o l o r s , ” s ” , ( char ∗s ) , { i n t len = s t r l e n ( s ) ; char ∗d = newstring ( len ) ; f i l t e r t e x t ( d , s , true , len ) ; stringret (d) ; }) ; COMMAND( substr , ” s i i N ” ) ; COMMAND( sublist , ” s i i N ” ) ; ICOMMAND( l i s t l e n , ” s ” , ( char ∗s ) , i n t r e t ( l i s t l e n ( s ) ) ) ; COMMANDN( g e t a l i a s , g e t a l i a s , ” s ” ) ; ICOMMAND( getvarmin , ” s ” , ( char ∗s ) , i n t r e t ( getvarmin ( s ) ) ) ; ICOMMAND( getvarmax , ” s ” , ( char ∗s ) , i n t r e t ( getvarmax ( s ) ) ) ; ICOMMAND( getfvarmin , ” s ” , ( char ∗s ) , f l o a t r e t ( getfvarmin ( s ) ) ) ; ICOMMAND( getfvarmax , ” s ” , ( char ∗s ) , f l o a t r e t ( getfvarmax ( s ) ) ) ; void l o o p l i s t ( ident ∗id , const char ∗l i s t , const uint ∗body , bool search ) { i f ( id−>type ! = ID ALIAS ) { i f ( search ) i n t r e t (−1) ; return ; } identstack stack ; i n t n = 0; f o r ( const char ∗s = l i s t , ∗s t a r t , ∗end ; p a r s e l i s t ( s , s t a r t , end ) ; ) { char ∗v a l = newstring ( s t a r t , end−s t a r t ) ; i f ( n++) { i f ( id−>valtype == VAL STR ) d e l e t e [ ] id−>v a l . s ; e l s e id−>valtype = VAL STR ; cleancode (∗ id ) ; id−>v a l . s = v a l ; } else { tagval t ; t . setstr ( val ) ; pusharg(∗ id , t , stack ) ; id−>f l a g s &= ˜IDF UNKNOWN; } i f ( executebool ( body ) && search ) { i n t r e t ( n−1); search = f a l s e ; break ; } } i f ( search ) i n t r e t (−1) ; i f ( n ) poparg(∗ id ) ; }

void p r e t t y l i s t ( const char ∗s , const char ∗conj ) { vector p ; const char ∗s t a r t , ∗end ; f o r ( i n t len = l i s t l e n ( s ) , n = 0; p a r s e l i s t ( s , s t a r t , end ) ; n++) { p . put ( s t a r t , end − s t a r t ) ; i f ( n+1 < len ) { i f ( len > 2 || ! conj [ 0 ] ) p . add ( ’ , ’ ) ; i f ( n+2 == len && conj [ 0 ] ) { p . add ( ’ ’ ) ; p . put ( conj , s t r l e n ( conj ) ) ; } p . add ( ’ ’ ) ; } } p . add ( ’ \ 0 ’ ) ; r e s u l t ( p . getbuf ( ) ) ; } COMMAND( p r e t t y l i s t , ” ss ” ) ; i n t l i s t i n c l u d e s ( const char ∗l i s t , const char ∗needle , i n t needlelen ) { i n t o f f s e t = 0; f o r ( const char ∗s = l i s t , ∗s t a r t , ∗end ; p a r s e l i s t ( s , s t a r t , end ) ; ) { i n t len = end − s t a r t ; i f ( needlelen == len && ! strncmp ( needle , s t a r t , len ) ) return o f f s e t ; o f f s e t ++; } return −1; } char ∗l i s t d e l ( const char ∗s , const char ∗del ) { vector p ; f o r ( const char ∗s t a r t , ∗end , ∗qstart , ∗qend ; p a r s e l i s t ( s , s t a r t , end , qstart , qend ) ; ) { i f ( l i s t i n c l u d e s ( del , s t a r t , end−s t a r t ) < 0) { i f ( ! p . empty ( ) ) p . add ( ’ ’ ) ; p . put ( qstart , qend−q s t a r t ) ; } } p . add ( ’ \ 0 ’ ) ; return newstring ( p . getbuf ( ) , p . length ( ) −1) ; } void l i s t s p l i c e ( const char ∗s , const char ∗vals , i n t ∗skip , i n t ∗count , i n t ∗numargs ) { i n t o f f s e t = max(∗ skip , 0) , len = ∗numargs >= 4 ? max(∗count , 0) : −1; const char ∗ l i s t = s , ∗s t a r t , ∗end , ∗qstart , ∗qend = s ; l o o p i ( o f f s e t ) i f ( ! p a r s e l i s t ( s , s t a r t , end , qstart , qend ) ) break ; vector p ; i f ( qend > l i s t ) p . put ( l i s t , qend−l i s t ) ; i f (∗ vals ) { i f ( ! p . empty ( ) ) p . add ( ’ ’ ) ; p . put ( vals , s t r l e n ( vals ) ) ; } while ( len−−> 0) i f ( ! p a r s e l i s t ( s ) ) break ; skiplist ( s ) ; switch (∗s ) { case ’ \ 0 ’ : case ’ ) ’ : case ’ ] ’ : break ; default : i f ( ! p . empty ( ) ) p . add ( ’ ’ ) ; p . put ( s , s t r l e n ( s ) ) ; break ; } p . add ( ’ \ 0 ’ ) ; commandret−>s e t s t r ( newstring ( p . getbuf ( ) , p . length ( ) −1) ) ; } COMMAND( l i s t s p l i c e , ” s s i i N ” ) ; ICOMMAND( l i s t d e l , ” ss ” , ( char ∗l i s t , char ∗del ) , commandret−>s e t s t r ( l i s t d e l ( l i s t , del ) ) ) ; ICOMMAND( indexof , ” ss ” , ( char ∗l i s t , char ∗elem ) , i n t r e t ( l i s t i n c l u d e s ( l i s t , elem , s t r l e n ( elem ) ) ) ) ; ICOMMAND( l i s t f i n d , ” rse ” , ( ident ∗id , char ∗l i s t , uint ∗body ) , l o o p l i s t ( id , l i s t , body , true ) ) ; ICOMMAND( l o o p l i s t , ” rse ” , ( ident ∗id , char ∗l i s t , uint ∗body ) , l o o p l i s t ( id , l i s t , body , f a l s e ) ) ; ICOMMAND( l o o p f i l e s , ” rsse ” , ( ident ∗id , char ∗dir , char ∗ext , uint ∗body ) , { i f ( id−>type ! = ID ALIAS ) return ; identstack stack ; vector f i l e s ; l i s t f i l e s ( dir , ext [ 0 ] ? ext : NULL, f i l e s ) ;

engine/command.cpp loopv ( f i l e s ) { char ∗ f i l e = f i l e s [ i ] ; bool redundant = f a l s e ; l o o p j ( i ) i f ( ! strcmp ( f i l e s [ j ] , f i l e ) ) { redundant = true ; break ; } i f ( redundant ) { d e l e t e [ ] f i l e ; continue ; } if ( i ) { i f ( id−>valtype == VAL STR ) d e l e t e [ ] id−>v a l . s ; e l s e id−>valtype = VAL STR ; id−>v a l . s = f i l e ; } else { tagval t ; t . setstr ( f i l e ) ; pusharg(∗ id , t , stack ) ; id−>f l a g s &= ˜IDF UNKNOWN; } execute ( body ) ; } i f ( f i l e s . length ( ) ) poparg(∗ id ) ; }) ; struct sortitem { const char ∗str , ∗quotestart , ∗quoteend ; }; struct sortfun { ident ∗x , ∗y ; uint ∗body ; bool operator ( ) ( const sortitem &xval , const sortitem &yval ) { i f ( x−>valtype ! = VAL MACRO) x−>valtype = VAL MACRO; cleancode (∗x ) ; x−>v a l . code = ( const uint ∗) xval . s t r ; i f ( y−>valtype ! = VAL MACRO) y−>valtype = VAL MACRO; cleancode (∗y ) ; y−>v a l . code = ( const uint ∗) yval . s t r ; return executebool ( body ) ; } }; void s o r t l i s t ( char ∗l i s t , ident ∗x , ident ∗y , uint ∗body ) { i f ( x == y || x−>type != ID ALIAS || y−>type ! = ID ALIAS ) return ; vector items ; i n t macrolen = s t r l e n ( l i s t ) , t o t a l = 0; char ∗macros = newstring ( l i s t , macrolen ) ; const char ∗c u r l i s t = l i s t , ∗s t a r t , ∗end , ∗quotestart , ∗quoteend ; while ( p a r s e l i s t ( c u r l i s t , s t a r t , end , quotestart , quoteend ) ) { macros [ end − l i s t ] = ’ \ 0 ’ ; sortitem item = { ¯os [ s t a r t − l i s t ] , quotestart , quoteend }; items . add ( item ) ; t o t a l += i n t ( quoteend − quotestart ) ; } identstack xstack , ystack ; pusharg(∗x , nullval , xstack ) ; x−>f l a g s &= ˜IDF UNKNOWN; pusharg(∗y , nullval , ystack ) ; y−>f l a g s &= ˜IDF UNKNOWN; sortfun f = { x , y , body }; items . s o r t ( f ) ; poparg(∗x ) ; poparg(∗y ) ; char ∗sorted = macros ; i n t sortedlen = t o t a l + max( items . length ( ) − 1 , 0) ; i f ( macrolen < sortedlen ) { d e l e t e [ ] macros ; sorted = newstring ( sortedlen ) ; } i n t o f f s e t = 0; loopv ( items ) { sortitem &item = items [ i ] ; i n t len = i n t ( item . quoteend − item . quotestart ) ; i f ( i ) sorted [ o f f s e t ++] = ’ ’ ; memcpy(& sorted [ o f f s e t ] , item . quotestart , len ) ; o f f s e t += len ; } sorted [ o f f s e t ] = ’ \ 0 ’ ; commandret−>s e t s t r ( sorted ) ; } COMMAND( s o r t l i s t , ” srre ” ) ;

231

ICOMMAND( + , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a + ∗b ) ) ; ICOMMAND(∗ , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a ∗ ∗b ) ) ; ICOMMAND(−, ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a − ∗b ) ) ; ICOMMAND( + f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , f l o a t r e t (∗a + ∗b ) ) ; ICOMMAND(∗ f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , f l o a t r e t (∗a ∗ ∗b ) ) ; ICOMMAND(−f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , f l o a t r e t (∗a − ∗b ) ) ; ICOMMAND( = , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t ( ( i n t ) (∗a == ∗b ) ) ) ; ICOMMAND( ! = , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t ( ( i n t ) (∗a ! = ∗b ) ) ) ; ICOMMAND(, ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t ( ( i n t ) (∗a > ∗b ) ) ) ; ICOMMAND(= ∗b ) ) ) ; ICOMMAND( = f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , i n t r e t ( ( i n t ) (∗a == ∗b ) ) ) ; ICOMMAND( ! = f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , i n t r e t ( ( i n t ) (∗a ! = ∗b ) ) ) ; ICOMMAND(f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , i n t r e t ( ( i n t ) (∗a > ∗b ) ) ) ; ICOMMAND(= ∗b ) ) ) ; ICOMMAND( ˆ , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a ˆ ∗b ) ) ; ICOMMAND( ! , ” t ” , ( t a g v a l ∗a ) , i n t r e t ( ! getbool (∗a ) ) ) ; ICOMMAND(& , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a & ∗b ) ) ; ICOMMAND( | , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a | ∗b ) ) ; ICOMMAND( ˜ , ” i ” , ( i n t ∗a ) , i n t r e t (˜∗a ) ) ; ICOMMAND( ˆ ˜ , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a ˆ ˜∗b ) ) ; ICOMMAND(&˜ , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a & ˜∗b ) ) ; ICOMMAND( | ˜ , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a | ˜∗b ) ) ; ICOMMAND(> ∗b ) ) ; ICOMMAND(&&, ”e1V ” , ( t a g v a l ∗args , i n t numargs ) , { i f ( ! numargs ) i n t r e t ( 1 ) ; e l s e l o o p i ( numargs ) { i f ( i ) f r e e a r g (∗commandret ) ; executeret ( args [ i ] . code , ∗commandret ) ; i f ( ! getbool (∗commandret ) ) break ; } }) ; ICOMMAND( | | , ”e1V ” , ( t a g v a l ∗args , i n t numargs ) , { i f ( ! numargs ) i n t r e t ( 0 ) ; e l s e l o o p i ( numargs ) { i f ( i ) f r e e a r g (∗commandret ) ; executeret ( args [ i ] . code , ∗commandret ) ; i f ( getbool (∗commandret ) ) break ; } }) ; ICOMMAND( div , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗b ? ∗a / ∗b : 0) ) ; ICOMMAND(mod, ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗b ? ∗a % ∗b : 0) ) ; ICOMMAND( d i v f , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , f l o a t r e t (∗b ? ∗a / ∗b : 0) ) ; ICOMMAND( modf , ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , f l o a t r e t (∗b ? fmod(∗a , ∗b ) : 0) ) ; ICOMMAND( sin , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( sin (∗a∗RAD) ) ) ; ICOMMAND( cos , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( cos (∗a∗RAD) ) ) ; ICOMMAND( tan , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( tan (∗a∗RAD) ) ) ; ICOMMAND( asin , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( asin (∗a ) /RAD) ) ; ICOMMAND( acos , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( acos(∗a ) /RAD) ) ; ICOMMAND( atan , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( atan(∗a ) /RAD) ) ; ICOMMAND( sqrt , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( sqrt (∗a ) ) ) ; ICOMMAND( pow, ” f f ” , ( f l o a t ∗a , f l o a t ∗b ) , f l o a t r e t ( pow(∗a , ∗b ) ) ) ; ICOMMAND( loge , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( l o g (∗a ) ) ) ; ICOMMAND( log2 , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( l o g (∗a ) /M LN2 ) ) ; ICOMMAND( log10 , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( log10 (∗a ) ) ) ; ICOMMAND( exp , ” f ” , ( f l o a t ∗a ) , f l o a t r e t ( exp(∗a ) ) ) ; ICOMMAND( min , ”V” , ( t a g v a l ∗args , i n t numargs ) , { i n t v a l = numargs > 0 ? args [ numargs − 1 ] . g e t i n t ( ) : 0; l o o p i ( numargs − 1) v a l = min ( val , args [ i ] . g e t i n t ( ) ) ; i n t r e t ( val ) ; }) ; ICOMMAND(max, ”V” , ( t a g v a l ∗args , i n t numargs ) , { i n t v a l = numargs > 0 ? args [ numargs − 1 ] . g e t i n t ( ) : 0; l o o p i ( numargs − 1) v a l = max( val , args [ i ] . g e t i n t ( ) ) ; i n t r e t ( val ) ; }) ; ICOMMAND( minf , ”V” , ( t a g v a l ∗args , i n t numargs ) , { f l o a t v a l = numargs > 0 ? args [ numargs − 1 ] . g e t f l o a t ( ) : 0.0 f ; l o o p i ( numargs − 1) v a l = min ( val , args [ i ] . g e t f l o a t ( ) ) ; f l o a t r e t ( val ) ; }) ; ICOMMAND( maxf , ”V” , ( t a g v a l ∗args , i n t numargs ) , { f l o a t v a l = numargs > 0 ? args [ numargs − 1 ] . g e t f l o a t ( ) : 0.0 f ; l o o p i ( numargs − 1) v a l = max( val , args [ i ] . g e t f l o a t ( ) ) ; f l o a t r e t ( val ) ; }) ; ICOMMAND( abs , ” i ” , ( i n t ∗n ) , i n t r e t ( abs(∗n ) ) ) ; ICOMMAND( absf , ” f ” , ( f l o a t ∗n ) , f l o a t r e t ( fabs (∗n ) ) ) ;

232

Foundations of Videogame Programming Code Repository

ICOMMAND( cond , ” ee2V ” , ( t a g v a l ∗args , i n t numargs ) , { f o r ( i n t i = 0; i < numargs ; i += 2) { i f ( i +1 < numargs ) { i f ( executebool ( args [ i ] . code ) ) { executeret ( args [ i + 1 ] . code , ∗commandret ) ; break ; } } else { executeret ( args [ i ] . code , ∗commandret ) ; break ; } } }) ; #define CASECOMMAND( name, fmt , type , acc , compare ) \ ICOMMAND( name, fmt ” te2V ” , ( t a g v a l ∗args , i n t numargs ) , \ { \ type v a l = acc ; \ int i ; \ f o r ( i = 1; i +1 < numargs ; i += 2) \ { \ i f ( compare ) \ { \ executeret ( args [ i + 1 ] . code , ∗commandret ) ; \ return ; \ } \ } \ }) CASECOMMAND( case , ” i ” , int , args [ 0 ] . g e t i n t ( ) , args [ i ] . type == VAL NULL || args [ i ] . g e t i n t ( ) == v a l ) ; CASECOMMAND( casef , ” f ” , f l o a t , args [ 0 ] . g e t f l o a t ( ) , args [ i ] . type == VAL NULL || args [ i ] . g e t f l o a t ( ) == v a l ) ; CASECOMMAND( cases , ” s ” , const char ∗, args [ 0 ] . g e t s t r ( ) , args [ i ] . type == VAL NULL || ! strcmp ( args [ i ] . g e t s t r ( ) , v a l ) ) ; ICOMMAND( rnd , ” i i ” , ( i n t ∗a , i n t ∗b ) , i n t r e t (∗a − ∗b > 0 ? rnd(∗a − ∗b ) + ∗b : ∗b ) ) ; ICOMMAND( strcmp , ” ss ” , ( char ∗a , char ∗b ) , i n t r e t ( strcmp ( a , b ) ==0) ) ; ICOMMAND( = s , ” ss ” , ( char ∗a , char ∗b ) , i n t r e t ( strcmp ( a , b ) ==0) ) ; ICOMMAND( ! = s , ” ss ” , ( char ∗a , char ∗b ) , i n t r e t ( strcmp ( a , b ) ! = 0 ) ) ; ICOMMAND(0) ) ; ICOMMAND(=0) ) ; ICOMMAND( echo , ”C” , ( char ∗s ) , conoutf (”\ f1%s ” , s ) ) ; ICOMMAND( error , ”C” , ( char ∗s ) , conoutf (CON ERROR, ”%s ” , s ) ) ; ICOMMAND( s t r s t r , ” ss ” , ( char ∗a , char ∗b ) , { char ∗s = s t r s t r ( a , b ) ; i n t r e t ( s ? s−a : −1) ; }) ; ICOMMAND( strlen , ” s ” , ( char ∗s ) , i n t r e t ( s t r l e n ( s ) ) ) ; char ∗s t r r e p l a c e ( const char ∗s , const char ∗oldval , const char ∗newval ) { vector buf ; i n t oldlen = s t r l e n ( o l d v a l ) ; i f ( ! oldlen ) return newstring ( s ) ; for ( ; ; ) { const char ∗found = s t r s t r ( s , o l d v a l ) ; i f ( found ) { while ( s < found ) buf . add(∗s ++) ; f o r ( const char ∗n = newval ; ∗n ; n++) buf . add(∗n ) ; s = found + oldlen ; } else {

while (∗s ) buf . add(∗s ++) ; buf . add ( ’ \ 0 ’ ) ; return newstring ( buf . getbuf ( ) , buf . length ( ) ) ; } } } ICOMMAND( strreplace , ” sss ” , ( char ∗s , char ∗o , char ∗n ) , commandret−> setstr ( strreplace ( s , o , n) ) ) ; # i f n d e f STANDALONE ICOMMAND( g e t m i l l i s , ” i ” , ( i n t ∗t o t a l ) , i n t r e t (∗ t o t a l ? t o t a l m i l l i s : lastmillis ) ) ; struct sleepcmd { i n t delay , m i l l i s , f l a g s ; char ∗command; }; vector sleepcmds ; void addsleep ( i n t ∗msec , char ∗cmd) { sleepcmd &s = sleepcmds . add ( ) ; s . delay = max(∗msec , 1) ; s . millis = lastmillis ; s .command = newstring (cmd) ; s . flags = identflags ; } COMMANDN( sleep , addsleep , ” i s ” ) ; void checksleep ( i n t m i l l i s ) { loopv ( sleepcmds ) { sleepcmd &s = sleepcmds [ i ] ; i f ( m i l l i s − s . m i l l i s >= s . delay ) { char ∗cmd = s .command; // execute might create more sleep commands s .command = NULL; int oldflags = identflags ; identflags = s . flags ; execute (cmd) ; identflags = oldflags ; d e l e t e [ ] cmd; i f ( sleepcmds . inrange ( i ) && ! sleepcmds [ i ] . command) sleepcmds . remove ( i−−); } } } void c l e a r s l e e p ( bool c l e a r o v e r r i d e s ) { i n t len = 0; loopv ( sleepcmds ) i f ( sleepcmds [ i ] . command) { i f ( c l e a r o v e r r i d e s && ! ( sleepcmds [ i ] . f l a g s&IDF OVERRIDDEN) ) sleepcmds [ len ++] = sleepcmds [ i ] ; e l s e d e l e t e [ ] sleepcmds [ i ] . command; } sleepcmds . shrink ( len ) ; } void c l e a r s l e e p ( i n t ∗c l e a r o v e r r i d e s ) { c l e a r s l e e p (∗ c l e a r o v e r r i d e s !=0 || i d e n t f l a g s&IDF OVERRIDDEN) ; } COMMANDN( clearsleep , c l e a r s l e e p , ” i ” ) ; #endif

engine/console.cpp // console . cpp : the console buffer , i t s display , and command l i n e control #define CONSTRLEN 512 #include ” engine . h” struct c l i n e { char ∗l i n e ; i n t type , outtime ; }; vector conlines ;

void conline ( i n t type , const char ∗s f ) buffer

cline cl ; c l . l i n e = conlines . length ( )>maxcon ? conlines . pop ( ) . l i n e : newstring ( ” ” , CONSTRLEN−1); // constrain the b u f f e r s i z e c l . type = type ; c l . outtime = t o t a l m i l l i s ; // f o r how long to keep l i n e on screen conlines . i n s e r t ( 0 , c l ) ; copystring ( c l . l i n e , sf , CONSTRLEN) ;

i n t commandmillis = −1; s t r i n g commandbuf ; char ∗commandaction = NULL, ∗commandprompt = NULL; enum { CF COMPLETE = 1 0 ? numl−i−1 : i ) ; i f ( ! ( conlines [ idx ] . type&f i l t e r ) ) continue ; char ∗l i n e = conlines [ idx ] . l i n e ; i n t width , height ; text bounds ( l i n e , width , height , conwidth ) ; i f ( d i r 0) y += height ; } return y+conoff ;

void conoutf ( const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv ( CON INFO, fmt , args ) ; va end ( args ) ; } void conoutf ( i n t type , const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv ( type , fmt , args ) ; va end ( args ) ; } VAR( f u l l c o n s o l e , 0 , 0 , 1) ; ICOMMAND( toggleconsole , ” ” , ( ) , { f u l l c o n s o l e ˆ= 1; }) ; } i n t rendercommand ( i n t x , i n t y , i n t w) { i f ( commandmillis < 0) return 0;

i n t renderconsole ( i n t w, i n t h , i n t abovehud ) b u f f e r taking i n t o account time & s c r o l l i n g

// render

{ defformatstring ( s ) (”%s %s ” , commandprompt ? commandprompt : ”>”, commandbuf ) ; i n t width , height ; text bounds ( s , width , height , w) ; y −= height ; draw text ( s , x , y , 0xFF , 0xFF , 0xFF , 0xFF , ( commandpos>=0) ? ( commandpos+1+(commandprompt? s t r l e n ( commandprompt ) : 1 ) ) : s t r l e n ( s ) , w) ; return height ;

i n t conpad = f u l l c o n s o l e ? 0 : FONTH/4 , conoff = f u l l c o n s o l e ? FONTH : FONTH/3 , conheight = min ( f u l l c o n s o l e ? ( ( h∗f u l l c o n s i z e /100)/FONTH)∗FONTH : FONTH∗consize , h − 2∗(conpad + conoff ) ) , conwidth = w − 2∗(conpad + conoff ) − ( f u l l c o n s o l e ? 0 : game : : c l i p c o n s o l e (w, h ) ) ; extern void consolebox ( i n t x1 , i n t y1 , i n t x2 , i n t y2 ) ; i f ( f u l l c o n s o l e ) consolebox ( conpad , conpad , conwidth+conpad+2∗conoff , conheight+conpad+2∗conoff ) ;

}

i n t y = drawconlines ( conskip , f u l l c o n s o l e ? 0 : confade , conwidth , conheight , conpad+conoff , f u l l c o n s o l e ? f u l l c o n f i l t e r : confilter ) ; i f ( ! f u l l c o n s o l e && ( miniconsize && miniconwidth ) ) drawconlines ( miniconskip , miniconfade , ( miniconwidth∗(w − 2∗(conpad + conoff ) ) ) /100, min (FONTH∗miniconsize , abovehud − y ) , conpad+conoff , m i n i c o n f i l t e r , abovehud , −1) ; return f u l l c o n s o l e ? conheight + 2∗(conpad + conoff ) : y ;

VARP( consize , 0 , 5 , 100) ; VARP( miniconsize , 0 , 5 , 100) ; VARP( miniconwidth , 0 , 40, 100) ; VARP( confade , 0 , 30, 60) ; VARP( miniconfade , 0 , 30, 60) ; VARP( f u l l c o n s i z e , 0 , 75, 100) ; HVARP( c o n f i l t e r , 0 , 0x7FFFFFF, 0x7FFFFFF ) ; HVARP( f u l l c o n f i l t e r , 0 , 0x7FFFFFF, 0x7FFFFFF ) ; HVARP( m i n i c o n f i l t e r , 0 , 0 , 0x7FFFFFF ) ;

}

i n t conskip = 0 , miniconskip = 0;

// keymap i s defined e x t e r n a l l y in keymap . c f g

void setconskip ( i n t &skip , i n t f i l t e r , i n t n ) { i n t o f f s e t = abs ( n ) , d i r = n < 0 ? −1 : 1; skip = clamp ( skip , 0 , conlines . length ( ) −1) ; while ( o f f s e t ) { skip += d i r ; i f ( ! conlines . inrange ( skip ) ) { skip = clamp ( skip , 0 , conlines . length ( ) −1) ; return ; } i f ( conlines [ skip ] . type&f i l t e r ) −−o f f s e t ; } }

struct keym { enum { ACTION DEFAULT = 0 , ACTION SPECTATOR, ACTION EDITING , NUMACTIONS };

ICOMMAND( conskip , ” i ” , ( i n t ∗n ) , setconskip ( conskip , f u l l c o n s o l e ? f u l l c o n f i l t e r : c o n f i l t e r , ∗n ) ) ; ICOMMAND( miniconskip , ” i ” , ( i n t ∗n ) , setconskip ( miniconskip , m i n i c o n f i l t e r , ∗n ) ) ;

i n t code ; char ∗name; char ∗actions [NUMACTIONS ] ; bool pressed ; keym ( ) : code(−1) , name(NULL) , pressed ( f a l s e ) { l o o p i (NUMACTIONS) actions [ i ] = newstring ( ” ” ) ; } ˜keym ( ) { DELETEA(name) ; l o o p i (NUMACTIONS) DELETEA( actions [ i ] ) ; } }; hashtable keyms(128) ;

ICOMMAND( clearconsole , ” ” , ( ) , { while ( conlines . length ( ) ) d e l e t e [ ] conlines . pop ( ) . l i n e ; }) ; i n t drawconlines ( i n t conskip , i n t confade , i n t conwidth , i n t conheight , i n t conoff , i n t f i l t e r , i n t y = 0 , i n t d i r = 1) { i n t numl = conlines . length ( ) , o f f s e t = min ( conskip , numl ) ; i f ( confade ) { i f ( ! conskip ) { numl = 0; loopvrev ( conlines ) i f ( t o t a l m i l l i s−conlines [ i ] . outtime < confade ∗1000) { numl = i +1; break ; } } e l s e o f f s e t −−;

void keymap ( i n t ∗code , char ∗key ) { i f ( i d e n t f l a g s&IDF OVERRIDDEN) { conoutf (CON ERROR, ” cannot override keymap %d ” , ∗code ) ; return ; } keym &km = keyms[∗code ] ; km. code = ∗code ; DELETEA(km.name) ; km.name = newstring ( key ) ; } COMMAND( keymap, ” i s ” ) ; keym ∗keypressed = NULL; char ∗keyaction = NULL; const char ∗getkeyname ( i n t code )

234

Foundations of Videogame Programming Code Repository

{ keym ∗km = keyms . access ( code ) ; return km ? km−>name : NULL; } void searchbinds ( char ∗action , i n t type ) { vector names ; enumerate ( keyms , keym, km, { i f ( ! strcmp (km. actions [ type ] , action ) ) { i f ( names . length ( ) ) names . add ( ’ ’ ) ; names . put (km.name, s t r l e n (km.name) ) ; } }) ; names . add ( ’ \ 0 ’ ) ; r e s u l t ( names . getbuf ( ) ) ; } keym ∗findbind ( char ∗key ) { enumerate ( keyms , keym, km, { i f ( ! strcasecmp (km.name, key ) ) return &km; }) ; return NULL; } void getbind ( char ∗key , i n t type ) { keym ∗km = findbind ( key ) ; r e s u l t (km ? km−>actions [ type ] : ” ” ) ; } void bindkey ( char ∗key , char ∗action , i n t state , const char ∗cmd) { i f ( i d e n t f l a g s&IDF OVERRIDDEN) { conoutf (CON ERROR, ” cannot override %s \”%s \”” , cmd, key ) ; return ; } keym ∗km = findbind ( key ) ; i f ( !km) { conoutf (CON ERROR, ”unknown key \”%s \”” , key ) ; return ; } char ∗&binding = km−>actions [ s t a t e ] ; i f ( ! keypressed || keyaction != binding ) d e l e t e [ ] binding ; // trim white−space to make searchbinds more r e l i a b l e while ( iscubespace (∗ action ) ) action ++; i n t len = s t r l e n ( action ) ; while ( len>0 && iscubespace ( action [ len −1]) ) len−−; binding = newstring ( action , len ) ; } ICOMMAND( bind , ” ss ” , ( char ∗key , char ∗action ) , bindkey ( key , action , keym : : ACTION DEFAULT, ” bind ” ) ) ; ICOMMAND( specbind , ” ss ” , ( char ∗key , char ∗action ) , bindkey ( key , action , keym : : ACTION SPECTATOR, ” specbind ” ) ) ; ICOMMAND( editbind , ” ss ” , ( char ∗key , char ∗action ) , bindkey ( key , action , keym : : ACTION EDITING , ” editbind ” ) ) ; ICOMMAND( getbind , ” s ” , ( char ∗key ) , getbind ( key , keym : : ACTION DEFAULT ) ); ICOMMAND( getspecbind , ” s ” , ( char ∗key ) , getbind ( key , keym : : ACTION SPECTATOR ) ) ; ICOMMAND( geteditbind , ” s ” , ( char ∗key ) , getbind ( key , keym : : ACTION EDITING )); ICOMMAND( searchbinds , ” s ” , ( char ∗action ) , searchbinds ( action , keym : : ACTION DEFAULT ) ) ; ICOMMAND( searchspecbinds , ” s ” , ( char ∗action ) , searchbinds ( action , keym : : ACTION SPECTATOR ) ) ; ICOMMAND( searcheditbinds , ” s ” , ( char ∗action ) , searchbinds ( action , keym : : ACTION EDITING ) ) ; void inputcommand ( char ∗i n i t , char ∗action = NULL, char ∗prompt = NULL, char ∗f l a g s = NULL) // turns input to the command l i n e on or o f f { commandmillis = i n i t ? t o t a l m i l l i s : −1; SDL EnableUNICODE ( commandmillis >= 0 ? 1 : 0) ; i f ( ! editmode ) keyrepeat ( commandmillis >= 0) ; copystring ( commandbuf, i n i t ? i n i t : ” ” ) ; DELETEA( commandaction ) ; DELETEA( commandprompt ) ; commandpos = −1; i f ( action && action [ 0 ] ) commandaction = newstring ( action ) ; i f ( prompt && prompt [ 0 ] ) commandprompt = newstring ( prompt ) ; commandflags = 0; i f ( f l a g s ) while (∗ f l a g s ) switch (∗ f l a g s ++) { case ’ c ’ : commandflags |= CF COMPLETE; break ; case ’ x ’ : commandflags |= CF EXECUTE; break ; case ’ s ’ : commandflags |= CF COMPLETE|CF EXECUTE; break ; } e l s e i f ( i n i t ) commandflags |= CF COMPLETE|CF EXECUTE;

# i f ! defined ( WIN32 ) && ! defined ( #include #include #endif

APPLE

)

void pasteconsole ( ) { # i f d e f WIN32 UINT fmt = CF UNICODETEXT; i f ( ! IsClipboardFormatAvailable ( fmt ) ) { fmt = CF TEXT ; i f ( ! IsClipboardFormatAvailable ( fmt ) ) return ; } i f ( ! OpenClipboard (NULL) ) return ; HANDLE h = GetClipboardData ( fmt ) ; s i z e t commandlen = s t r l e n ( commandbuf ) ; i n t cblen = i n t ( GlobalSize ( h ) ) , decoded = 0; ushort ∗cb = ( ushort ∗) GlobalLock ( h ) ; switch ( fmt ) { case CF UNICODETEXT: decoded = min ( i n t ( s i z e o f ( commandbuf )−1−commandlen ) , cblen /2) ; l o o p i ( decoded ) commandbuf [ commandlen++] = uni2cube ( cb [ i ] ) ; break ; case CF TEXT : decoded = min ( i n t ( s i z e o f ( commandbuf )−1−commandlen ) , cblen ) ; memcpy(&commandbuf [ commandlen ] , cb , decoded ) ; break ; } commandbuf [ commandlen + decoded ] = ’ \ 0 ’ ; GlobalUnlock ( cb ) ; CloseClipboard ( ) ; # e l i f defined ( APPLE ) extern char ∗mac pasteconsole ( i n t ∗cblen ) ; i n t cblen = 0; uchar ∗cb = ( uchar ∗) mac pasteconsole(&cblen ) ; i f ( ! cb ) return ; s i z e t commandlen = s t r l e n ( commandbuf ) ; i n t decoded = decodeutf8 ( ( uchar ∗)&commandbuf [ commandlen ] , i n t ( s i z e o f ( commandbuf )−1−commandlen ) , cb , cblen ) ; commandbuf [ commandlen + decoded ] = ’ \ 0 ’ ; f r e e ( cb ) ; #e l s e SDL SysWMinfo wminfo ; SDL VERSION(&wminfo . version ) ; wminfo . subsystem = SDL SYSWM X11; i f ( ! SDL GetWMInfo(&wminfo ) ) return ; i n t cbsize ; uchar ∗cb = ( uchar ∗)XFetchBytes ( wminfo . i n f o . x11 . display , &cbsize ) ; i f ( ! cb || ! cbsize ) return ; s i z e t commandlen = s t r l e n ( commandbuf ) ; f o r ( uchar ∗cbline = cb , ∗cbend ; commandlen + 1 < s i z e o f ( commandbuf ) && cbline < &cb [ cbsize ] ; cbline = cbend + 1) { cbend = ( uchar ∗)memchr( cbline , ’\0 ’ , &cb [ cbsize ] − cbline ) ; i f ( ! cbend ) cbend = &cb [ cbsize ] ; i n t cblen = i n t ( cbend−cbline ) , commandmax = i n t ( s i z e o f ( commandbuf ) −1−commandlen ) ; l o o p i ( cblen ) i f ( ( cbline [ i ]&0xC0 ) == 0x80 ) { commandlen += decodeutf8 ( ( uchar ∗)&commandbuf [ commandlen ] , commandmax, cbline , cblen ) ; goto n e x t l i n e ; } cblen = min ( cblen , commandmax) ; l o o p i ( cblen ) commandbuf [ commandlen++] = uni2cube(∗ cbline ++) ; nextline : commandbuf [ commandlen ] = ’\n ’ ; i f ( commandlen + 1 < s i z e o f ( commandbuf ) && cbend < &cb [ cbsize ] ) ++ commandlen ; commandbuf [ commandlen ] = ’ \ 0 ’ ; } XFree ( cb ) ; #endif } struct hline { char ∗buf , ∗action , ∗prompt ; int flags ; hline ( ) : buf (NULL) , action (NULL) , prompt (NULL) , f l a g s ( 0 ) {} ˜ hline ( ) { DELETEA( buf ) ; DELETEA( action ) ; DELETEA( prompt ) ; }

} ICOMMAND( saycommand, ”C” , ( char ∗i n i t ) , inputcommand ( i n i t ) ) ; COMMAND( inputcommand , ” ssss ” ) ;

void r e s t o r e ( ) { copystring ( commandbuf, buf ) ; i f ( commandpos >= ( i n t ) s t r l e n ( commandbuf ) ) commandpos = −1;

engine/console.cpp DELETEA( commandaction ) ; DELETEA( commandprompt ) ; i f ( action ) commandaction = newstring ( action ) ; i f ( prompt ) commandprompt = newstring ( prompt ) ; commandflags = f l a g s ;

i f ( editmode ) s t a t e = keym : : ACTION EDITING ; e l s e i f ( player−>s t a t e ==CS SPECTATOR) s t a t e = keym : : ACTION SPECTATOR; } char ∗&action = k . actions [ s t a t e ] [ 0 ] ? k . actions [ s t a t e ] : k . actions [ keym : : ACTION DEFAULT ] ; keyaction = action ; keypressed = &k ; execute ( keyaction ) ; keypressed = NULL; i f ( keyaction ! = action ) d e l e t e [ ] keyaction ;

} bool shouldsave ( ) { return strcmp ( commandbuf, buf ) || ( commandaction ? ! action || strcmp ( commandaction , action ) : action ! =NULL) || ( commandprompt ? ! prompt || strcmp ( commandprompt, prompt ) : prompt ! =NULL) || commandflags ! = f l a g s ; } void save ( ) { buf = newstring ( commandbuf ) ; i f ( commandaction ) action = newstring ( commandaction ) ; i f ( commandprompt ) prompt = newstring ( commandprompt ) ; f l a g s = commandflags ; } void run ( ) { i f ( f l a g s&CF EXECUTE && buf [ 0 ] = = ’ / ’ ) execute ( buf +1) ; e l s e i f ( action ) { a l i a s ( ” commandbuf” , buf ) ; execute ( action ) ; } e l s e game : : t o s e r v e r ( buf ) ; }

235

} k . pressed = isdown ; } void consolekey ( i n t code , bool isdown , i n t cooked ) { #ifdef APPLE #define MOD KEYS (KMOD LMETA|KMOD RMETA) #e l s e #define MOD KEYS (KMOD LCTRL|KMOD RCTRL) #endif i f ( isdown ) { switch ( code ) { case SDLK RETURN: case SDLK KP ENTER : break ; case SDLK HOME: i f ( s t r l e n ( commandbuf ) ) commandpos = 0; break ;

}; vector h i s t o r y ; i n t histpos = 0;

case SDLK END: commandpos = −1; break ;

VARP( maxhistory , 0 , 1000, 10000) ;

case SDLK DELETE: { i n t len = ( i n t ) s t r l e n ( commandbuf ) ; i f ( commandpos=len−1) commandpos = −1; break ; }

void h i s t o r y ( i n t ∗n ) { s t a t i c bool i n h i s t o r y = f a l s e ; i f ( ! i n h i s t o r y && h i s t o r y . inrange (∗n ) ) { i n h i s t o r y = true ; h i s t o r y [ h i s t o r y . length ( )−∗n−1]−>run ( ) ; inhistory = false ; } } COMMANDN( history , h i s t o r y , ” i ” ) ; struct r e l e a s e a c t i o n { keym ∗key ; char ∗action ; }; vector releaseactions ; const char ∗addreleaseaction ( char ∗s ) { i f ( ! keypressed ) return NULL; r e l e a s e a c t i o n &ra = releaseactions . add ( ) ; ra . key = keypressed ; ra . action = s ; return keypressed−>name; } void onrelease ( const char ∗s ) { addreleaseaction ( newstring ( s ) ) ; } COMMAND( onrelease , ” s ” ) ; void execbind ( keym &k , bool isdown ) { loopv ( releaseactions ) { r e l e a s e a c t i o n &ra = releaseactions [ i ] ; i f ( ra . key==&k ) { i f ( ! isdown ) execute ( ra . action ) ; d e l e t e [ ] ra . action ; releaseactions . remove ( i−−); } } i f ( isdown ) { i n t s t a t e = keym : : ACTION DEFAULT; i f ( ! mainmenu) {

case SDLK BACKSPACE: { i n t len = ( i n t ) s t r l e n ( commandbuf ) , i = commandpos>=0 ? commandpos : len ; i f ( i 0) commandpos−−; e l s e i f ( ! commandpos && len0) commandpos−−; e l s e i f ( commandpos=0 && ++commandpos>=(i n t ) s t r l e n ( commandbuf ) ) commandpos = −1; break ; case SDLK UP: i f ( histpos > h i s t o r y . length ( ) ) histpos = h i s t o r y . length ( ) ; i f ( histpos > 0) h i s t o r y[−−histpos]−>r e s t o r e ( ) ; break ; case SDLK DOWN: i f ( histpos + 1 < h i s t o r y . length ( ) ) h i s t o r y [++ histpos]−> restore ( ) ; break ; case SDLK TAB: i f ( commandflags&CF COMPLETE) { complete ( commandbuf, commandflags&CF EXECUTE ? ” / ” : NULL) ; i f ( commandpos>=0 && commandpos>=(i n t ) s t r l e n ( commandbuf ) ) commandpos = −1; } break ; case SDLK v : i f ( SDL GetModState ( ) &MOD KEYS) { pasteconsole ( ) ; return ; }

236

Foundations of Videogame Programming Code Repository }

// f a l l through } default : resetcomplete ( ) ; i f ( cooked ) { s i z e t len = ( i n t ) s t r l e n ( commandbuf ) ; i f ( len+1= maxhistory ) { l o o p i ( h i s t o r y . length ( )−maxhistory +1) d e l e t e h i s t o r y [ i ] ; h i s t o r y . remove ( 0 , h i s t o r y . length ( )−maxhistory +1) ; } h i s t o r y . add ( h = new hline )−>save ( ) ; } else h = history . last ( ) ; } histpos = h i s t o r y . length ( ) ; inputcommand (NULL) ; i f ( h ) h−>run ( ) ; } e l s e i f ( code==SDLK ESCAPE) { histpos = h i s t o r y . length ( ) ; inputcommand (NULL) ; } } } extern bool menukey ( i n t code , bool isdown , i n t cooked ) ; void keypress ( i n t code , bool isdown , i n t cooked ) { keym ∗haskey = keyms . access ( code ) ; i f ( haskey && haskey−>pressed ) execbind (∗haskey , isdown ) ; // allow pressed keys to r e l e a s e e l s e i f ( ! menukey ( code , isdown , cooked ) ) // 3D GUI mouse button intercept { i f ( commandmillis >= 0) consolekey ( code , isdown , cooked ) ; e l s e i f ( haskey ) execbind (∗haskey , isdown ) ; } } void c l e a r c o n s o l e ( ) { keyms . c l e a r ( ) ; } s t a t i c i n l i n e bool sortbinds ( keym ∗x , keym ∗y ) { return strcmp ( x−>name, y−>name) < 0; } void writebinds ( stream ∗f ) { s t a t i c const char ∗cmds [ 3 ] = { ” bind ” , ” specbind ” , ” editbind ” }; vector binds ; enumerate ( keyms , keym, km, binds . add(&km) ) ; binds . s o r t ( sortbinds ) ; loopj ( 3 ) { loopv ( binds ) { keym &km = ∗binds [ i ] ; i f (∗km. actions [ j ] ) { i f ( validateblock (km. actions [ j ] ) ) f−>p r i n t f (”%s %s [%s]\n” , cmds [ j ] , escapestring (km.name) , km. actions [ j ] ) ; e l s e f−>p r i n t f (”%s %s %s\n” , cmds [ j ] , escapestring (km.name) , escapestring (km. actions [ j ] ) ) ;

} } // tab−completion o f a l l idents and base maps enum { FILES DIR = 0 , FILES LIST }; struct f i l e s k e y { i n t type ; const char ∗dir , ∗ext ; f i l e s k e y ( ) {} f i l e s k e y ( i n t type , const char ∗dir , const char ∗ext ) : type ( type ) , d i r ( d i r ) , ext ( ext ) {} }; struct f i l e s v a l { i n t type ; char ∗dir , ∗ext ; vector f i l e s ; int millis ; f i l e s v a l ( i n t type , const char ∗dir , const char ∗ext ) : type ( type ) , d i r ( newstring ( d i r ) ) , ext ( ext && ext [ 0 ] ? newstring ( ext ) : NULL) , m i l l i s (−1) {} ˜ f i l e s v a l ( ) { DELETEA( d i r ) ; DELETEA( ext ) ; f i l e s . deletearrays ( ) ; } s t a t i c bool comparefiles ( const char ∗x , const char ∗y ) { return strcmp ( x , y ) < 0; } void update ( ) { i f ( type ! = FILES DIR || m i l l i s >= commandmillis ) return ; f i l e s . deletearrays ( ) ; l i s t f i l e s ( dir , ext , f i l e s ) ; f i l e s . s o r t ( comparefiles ) ; loopv ( f i l e s ) i f ( i && ! strcmp ( f i l e s [ i ] , f i l e s [ i −1]) ) d e l e t e [ ] f i l e s . remove ( i−−); millis = totalmillis ; } }; s t a t i c i n l i n e bool htcmp ( const f i l e s k e y &x , const f i l e s k e y &y ) { return x . type==y . type && ! strcmp ( x . dir , y . d i r ) && ( x . ext == y . ext || ( x . ext && y . ext && ! strcmp ( x . ext , y . ext ) ) ) ; } s t a t i c i n l i n e uint hthash ( const f i l e s k e y &k ) { return hthash ( k . d i r ) ; } s t a t i c hashtable c o m p l e t e f i l e s ; s t a t i c hashtable completions ; i n t completesize = 0; s t r i n g lastcomplete ; void resetcomplete ( ) { completesize = 0; } void addcomplete ( char ∗command, i n t type , char ∗dir , char ∗ext ) { i f ( i d e n t f l a g s&IDF OVERRIDDEN) { conoutf (CON ERROR, ” cannot override complete %s ” , command) ; return ; } i f ( ! dir [ 0 ] ) { f i l e s v a l ∗∗h a s f i l e s = completions . access (command) ; i f ( h a s f i l e s ) ∗h a s f i l e s = NULL; return ; } i f ( type==FILES DIR ) { int dirlen = ( int ) strlen ( dir ) ; while ( d i r l e n > 0 && ( d i r [ dirlen −1] == ’ / ’ || d i r [ dirlen −1] == ’\\ ’) ) d i r[−−d i r l e n ] = ’ \ 0 ’ ; i f ( ext ) { i f ( strchr ( ext , ’ ∗ ’ ) ) ext [ 0 ] = ’ \ 0 ’ ; i f ( ! ext [ 0 ] ) ext = NULL; } } f i l e s k e y key ( type , dir , ext ) ; f i l e s v a l ∗∗v a l = c o m p l e t e f i l e s . access ( key ) ; i f ( ! val ) {

engine/cubeloader.cpp f i l e s v a l ∗f = new f i l e s v a l ( type , dir , ext ) ; i f ( type==FILES LIST ) e x p l o d e l i s t ( dir , f−>f i l e s ) ; v a l = &c o m p l e t e f i l e s [ f i l e s k e y ( type , f−>dir , f−>ext ) ] ; ∗v a l = f ;

i n t commandsize = strchr (&s [ cmdlen ] , ’ ’ )+1−s ; copystring ( p r e f i x , s , min ( s i z e t ( commandsize+1) , s i z e o f ( p r e f i x ) ) ) ; f−>update ( ) ; loopv ( f−>f i l e s ) { i f ( strncmp ( f−>f i l e s [ i ] , &s [ commandsize ] , completesize+cmdlen− commandsize ) ==0 && strcmp ( f−>f i l e s [ i ] , lastcomplete ) > 0 && ( ! nextcomplete || strcmp ( f−>f i l e s [ i ] , nextcomplete ) < 0) ) nextcomplete = f−>f i l e s [ i ] ; }

} f i l e s v a l ∗∗h a s f i l e s = completions . access (command) ; i f ( h a s f i l e s ) ∗h a s f i l e s = ∗v a l ; e l s e completions [ newstring (command) ] = ∗v a l ; } void addfilecomplete ( char ∗command, char ∗dir , char ∗ext ) { addcomplete (command, FILES DIR , dir , ext ) ; }

} e l s e // complete using command names { i f ( cmdprefix ) copystring ( p r e f i x , cmdprefix ) ; e l s e p r e f i x [ 0 ] = ’ \ 0 ’ ; enumerate ( idents , ident , id , i f ( strncmp ( id .name, &s [ cmdlen ] , completesize ) ==0 && strcmp ( id .name, lastcomplete ) > 0 && ( ! nextcomplete || strcmp ( id .name, nextcomplete ) < 0) ) nextcomplete = id .name; ); } i f ( nextcomplete ) { formatstring ( s ) (”%s%s ” , p r e f i x , nextcomplete ) ; copystring ( lastcomplete , nextcomplete ) ; } e l s e lastcomplete [ 0 ] = ’ \ 0 ’ ;

void addlistcomplete ( char ∗command, char ∗ l i s t ) { addcomplete (command, FILES LIST , l i s t , NULL) ; } COMMANDN( complete , addfilecomplete , ” sss ” ) ; COMMANDN( listcomplete , addlistcomplete , ” ss ” ) ; void complete ( char ∗s , const char ∗cmdprefix ) { i n t cmdlen = 0; i f ( cmdprefix ) { cmdlen = s t r l e n ( cmdprefix ) ; i f ( strncmp ( s , cmdprefix , cmdlen ) ) { defformatstring (cmd) (”%s%s ” , cmdprefix , s ) ; copystring ( s , cmd) ; } } i f ( ! s [ cmdlen ] ) return ; i f ( ! completesize ) { completesize = ( i n t ) s t r l e n (&s [ cmdlen ] ) ; lastcomplete [ 0 ] = ’ \ 0 ’ ; } f i l e s v a l ∗f = NULL; i f ( completesize ) { char ∗end = strchr (&s [ cmdlen ] , ’ ’ ) ; i f ( end ) { s t r i n g command; copystring (command, &s [ cmdlen ] , min ( s i z e t ( end−&s [ cmdlen ] + 1 ) , s i z e o f (command) ) ) ; f i l e s v a l ∗∗h a s f i l e s = completions . access (command) ; i f ( h a s f i l e s ) f = ∗h a s f i l e s ; } } const char ∗nextcomplete = NULL; string prefix ; i f ( f ) // complete using filenames {

237

} s t a t i c i n l i n e bool sortcompletions ( const char ∗x , const char ∗y ) { return strcmp ( x , y ) < 0; } void writecompletions ( stream ∗f ) { vector cmds ; enumeratekt ( completions , char ∗, k , f i l e s v a l ∗, v , { i f ( v ) cmds . add ( k ) ; }) ; cmds . s o r t ( sortcompletions ) ; loopv ( cmds ) { char ∗k = cmds [ i ] ; f i l e s v a l ∗v = completions [ k ] ; i f ( v−>type==FILES LIST ) { i f ( validateblock ( v−>d i r ) ) f−>p r i n t f ( ” l i s t c o m p l e t e %s [%s]\n” , escapeid ( k ) , v−>d i r ) ; e l s e f−>p r i n t f ( ” l i s t c o m p l e t e %s %s\n” , escapeid ( k ) , escapestring ( v−>d i r ) ) ; } e l s e f−>p r i n t f ( ” complete %s %s %s\n” , escapeid ( k ) , escapestring ( v−> d i r ) , escapestring ( v−>ext ? v−>ext : ” ∗ ” ) ) ; } }

engine/cubeloader.cpp #include ” engine . h”

char f l o o r , c e i l ; // height , in cubes uchar wtex , f t e x , ctex ; // wall/ f l o o r / c e i l texture ids uchar vdelta ; // vertex delta , used f o r h e i g h t f i e l d cubes uchar utex ; // upper wall tex id

VAR( importcuberemip , 0 , 1024, 2048) ; struct cubeloader { enum { DEFAULT LIQUID = 1 , DEFAULT WALL, DEFAULT FLOOR, DEFAULT CEIL }; enum { C SOLID = 0 , wtex ] C CORNER, C FHF, values C CHF, C SPACE, C SEMISOLID, C MAXTYPE }; struct c sqr { uchar type ;

}; struct c { short short uchar uchar };

persistent entity

// map e n t i t y

x, y, z; // cube aligned p o s i t i o n attr1 ; type ; // type i s one o f the above attr2 , attr3 , a t t r 4 ;

// block types , order matters ! // e n t i r e l y s o l i d cube [ only s p e c i f i e s // h a l f f u l l corner o f a wall // f l o o r h e i g h t f i e l d using neighbour vdelta // idem c e i l i n g // e n t i r e l y empty cube // generated by mipmapping

// one o f the above

struct c header // map f i l e format header { char head [ 4 ] ; // ”CUBE” i n t version ; // any >8b i t quantity i s l i t t l e endian i n t headersize ; // s i z e o f ( header ) int sfactor ; // in b i t s i n t numents ; char maptitle [ 1 2 8 ] ; uchar t e x l i s t s [ 3 ] [ 2 5 6 ] ; int waterlevel ; i n t reserved [ 1 5 ] ; }; c sqr ∗world ; int ssize ;

238

Foundations of Videogame Programming Code Repository

i n t x0 , x1 , y0 , y1 , z0 , z1 ; c sqr ∗o [ 4 ] ; i n t lastremip ; i n t progress ; void c r e a t e e n t ( c p e r s i s t e n t e n t i t y &ce ) { i f ( ce . type>=7) ce . type ++; // grenade ammo i f ( ce . type>=8) ce . type ++; // p i s t o l ammo i f ( ce . type ==16) ce . type = ET MAPMODEL; e l s e i f ( ce . type>=ET MAPMODEL && ce . type=ET ENVMAP) ce . type ++; i f ( ce . type>=ET PARTICLES ) ce . type ++; i f ( ce . type>=ET SOUND) ce . type ++; i f ( ce . type>=ET SPOTLIGHT ) ce . type ++; e x t e n t i t y &e = ∗e n t i t i e s : : newentity ( ) ; e n t i t i e s : : getents ( ) . add(&e ) ; e . type = ce . type ; e . spawned = f a l s e ; e . inoctanode = f a l s e ; e . o = vec ( ce . x∗4+worldsize /4 , ce . y∗4+worldsize /4 , ce . z∗4+worldsize /2) ; e . l i g h t . c o l o r = vec ( 1 , 1 , 1) ; e . l i g h t . d i r = vec ( 0 , 0 , 1) ; e . a t t r 1 = ce . a t t r 1 ; e . a t t r 2 = ce . a t t r 2 ; switch ( e . type ) { case ET MAPMODEL: e . o . z += ce . a t t r 3 ∗4; e . a t t r 3 = e . a t t r 4 = 0; break ; case ET LIGHT : e . a t t r 1 ∗= 4; i f ( ! ce . a t t r 3 && ! ce . a t t r 4 ) { e . a t t r 3 = e . a t t r 4 = e . a t t r 2 ; break ; } // f a l l through default : e . a t t r 3 = ce . a t t r 3 ; e . a t t r 4 = ce . a t t r 4 ; break ; } switch ( e . type ) { case ET PLAYERSTART : case ET MAPMODEL: case ET GAMESPECIFIC+12: // t e l e p o r t case ET GAMESPECIFIC+13: // monster e . a t t r 1 = ( i n t ( e . a t t r 1 ) +180)%360; break ; } e . a t t r 5 = 0; } cube &getcube ( i n t x , i n t y , i n t z ) { return lookupcube ( x∗4+worldsize /4 , y∗4+worldsize /4 , z∗4+worldsize /2 , 4) ; } i n t neighbours ( c sqr &t ) { o [ 0 ] = &t ; o [ 1 ] = &t +1; o [ 2 ] = &t + s s i z e ; o [ 3 ] = &t + s s i z e +1; i n t best = 0xFFFF ; l o o p i ( 4 ) i f ( o [ i]−>vdeltavdelta ; return best ; } // p u l l up h e i g h f i e l d s to where they don ’ t void preprocess cubes ( ) cross cube boundaries { for ( ; ; ) { bool changed = f a l s e ; loop ( x , s s i z e ) { loop ( y , s s i z e ) { c sqr &t = world [ x+y∗s s i z e ] ; i f ( t . type==C FHF || t . type==C CHF ) { i n t bottom = ( neighbours ( t ) &(˜3) ) +4; l o o p j ( 4 ) i f ( o [ j]−>vdelta>bottom ) { o [ j]−>vdelta = bottom ; changed = true ; } } } } i f ( ! changed ) break ; } }

i n t g e t f l o o r c e i l ( c sqr &s , i n t &f l o o r , i n t &c e i l ) { floor = s . floor ; ceil = s. ceil ; i n t cap = 0; switch ( s . type ) { case C SOLID : f l o o r = c e i l ; break ; case C FHF : f l o o r −= ( cap = neighbours ( s ) &(˜3) ) /4; break ; case C CHF: c e i l += ( cap = neighbours ( s ) &(˜3) ) /4; break ; } return cap ; } void boundingbox ( ) { x0 = y0 = s s i z e ; x1 = y1 = 0; z0 = 128; z1 = −128; loop ( x , s s i z e ) loop ( y , s s i z e ) { c sqr &t = world [ x+y∗s s i z e ] ; i f ( t . type ! =C SOLID ) { i f ( xy1 ) y1 = y ; int floor , c e i l ; getfloorceil ( t , floor , ceil ) ; i f ( f l o o rz1 ) z1 = c e i l ; } } } void hf ( i n t x , i n t y , i n t z , i n t side , i n t dir , i n t cap ) { cube &c = getcube ( x , y , z ) ; l o o p i ( 2 ) l o o p j ( 2 ) edgeset ( cubeedge ( c , 2 , i , j ) , side , d i r ∗(o [ ( jtype==C SOLID || z f l o o r || z>=s−>c e i l ; } void createcorner ( cube &c , i n t l s t a r t , i n t lend , i n t r s t a r t , i n t rend ) { i n t ledge = edgemake ( l s t a r t , lend ) ; i n t redge = edgemake ( r s t a r t , rend ) ; cubeedge ( c , 1 , 0 , 0) = ledge ; cubeedge ( c , 1 , 1 , 0) = ledge ; cubeedge ( c , 1 , 0 , 1) = redge ; cubeedge ( c , 1 , 1 , 1) = redge ; } void create cubes ( ) { preprocess cubes ( ) ; boundingbox ( ) ; lastremip = allocnodes ; progress = 0; f o r ( i n t x = x0−1; xwtex = f−>getchar ( ) ; s−>vdelta = f−>getchar ( ) ; i f ( hdr . version getchar ( ) ; f−>getchar ( ) ; } s−>f t e x = DEFAULT FLOOR; s−>ctex = DEFAULT CEIL ; s−>utex = s−>wtex ; s−>f l o o r = 0; s−>c e i l = 16; break ; } default : { i f ( type=C MAXTYPE) { defformatstring ( t ) (”%d @ %d ” , type , k ) ; f a t a l ( ” while reading map: type out o f range : %s ” , t ) ; } s−>type = type ; s−>f l o o r = f−>getchar ( ) ; s−>c e i l = f−>getchar ( ) ; i f ( s−>f l o o r>=s−>c e i l ) s−>f l o o r = s−>c e i l −1; // f o r pre 12 13 s−>wtex = f−>getchar ( ) ; s−>f t e x = f−>getchar ( ) ; s−>ctex = f−>getchar ( ) ; i f ( hdr . version getchar ( ) ; f−>getchar ( ) ; } s−>vdelta = f−>getchar ( ) ; s−>utex = ( hdr . version >=2) ? f−>getchar ( ) : s−>wtex ; i f ( hdr . version >=5) f−>getchar ( ) ; s−>type = type ; } } t = s; } delete f ;

// f i x texture on ground o f a corner

( ts−>f l o o r −1==z && bs−>f l o o r −1!=z ) { c . texture [ O TOP ] = ts−>f t e x ; } e l s e i f ( ts−>f l o o r −1!=z && bs−>f l o o r −1==z ) { c . texture [ O TOP ] = bs−>f t e x ; } if ( ts−>c e i l ==z && bs−>c e i l ! = z ) { c . texture [ O BOTTOM] = ts−>ctex ; } e l s e i f ( ts−>c e i l ! = z && bs−>c e i l ==z ) { c . texture [ O BOTTOM] = bs−>ctex ; }

} } } switch ( s . type ) { case C FHF : hf ( x , y , f l o o r −1, 1 , −1, cap ) ; break ; case C CHF: hf ( x , y , c e i l , 0 , 1 , cap ) ; break ; } i f ( importcuberemip && ( allocnodes − lastremip ) ∗ 8 > importcuberemip ∗ 1024) { mpremip ( true ) ; lastremip = allocnodes ; } i f ( ( progress++&0x7F ) ==0) { f l o a t bar = f l o a t ( ( y1−y0+2) ∗(x−x0+1) + y−y0+1) / f l o a t ( ( y1−y0 +2) ∗(x1−x0+2) ) ; defformatstring ( t e x t ) ( ” creating cubes . . . %d%%”, i n t ( bar∗100) ) ; renderprogress ( bar , t e x t ) ; } } } void load cube world ( char ∗mname) { i n t l o a d i n g s t a r t = SDL GetTicks ( ) ; s t r i n g pakname, cgzname ; formatstring ( pakname ) ( ” cube/%s ” , mname) ; formatstring ( cgzname ) ( ” packages/%s . cgz ” , pakname ) ; stream ∗f = o p e n g z f i l e ( path ( cgzname ) , ” rb ” ) ; i f ( ! f ) { conoutf (CON ERROR, ” could not read cube map %s ” , cgzname ) ; return ; } c header hdr ; f−>read(&hdr , s i z e o f ( c header )−s i z e o f ( i n t ) ∗16) ; l i l s w a p (&hdr . version , 4) ; bool mod = f a l s e ; i f ( strncmp ( hdr . head , ”CUBE” , 4) ) { i f ( ! strncmp ( hdr . head , ”ACMP” , 4) ) mod = true ; else { conoutf (CON ERROR, ”map %s has malformatted header ” , cgzname ) ; delete f ; return ; } } e l s e i f ( hdr . version >5) mod = true ; i f ( hdr . version>5 && !mod) { conoutf (CON ERROR, ”map %s requires a newer version o f the Cube 1 importer ” , cgzname ) ; d e l e t e f ; return ; } i f ( ! h a s l o c a l c l i e n t s ( ) ) game : : f o r c e e d i t ( ” ” ) ; emptymap(12 , true , NULL) ; f r e e o c t a ( worldroot ) ; worldroot = newcubes ( F SOLID ) ; defformatstring ( cs ) ( ” importing %s ” , cgzname ) ; renderbackground ( cs ) ; i f ( hdr . version >=4) { f−>read(&hdr . waterlevel , s i z e o f ( i n t ) ∗16) ; l i l s w a p (&hdr . waterlevel , 16) ; } else { hdr . w a t e r l e v e l = −100000; } i f (mod) f−>seek ( hdr . numents∗s i z e o f ( c p e r s i s t e n t e n t i t y ) , SEEK CUR) ; e l s e l o o p i ( hdr . numents ) { c persistent entity e ; f−>read(&e , s i z e o f ( c p e r s i s t e n t e n t i t y ) ) ; l i l s w a p (&e . x , 4) ; i f ( i < MAXENTS) c r e a t e e n t ( e ) ; } s s i z e = 1= fadeintime ) return ; fadedecal (∗d , ( fade=0 ? t i m e t o l i v e : decalfade ) + fadeouttime − lastmillis ; while ( d < end ) { i n t fade = d−>m i l l i s + o f f s e t ; i f ( fade >= fadeouttime ) return ; fadedecal (∗d , ( fade= fadeouttime ) return ; fadedecal (∗d , ( fadepos ) ; glTexCoordPointer ( 2 , GL FLOAT, s i z e o f ( d e c a l v e r t ) , &verts−>u ) ; glColorPointer ( 4 , GL UNSIGNED BYTE, s i z e o f ( d e c a l v e r t ) , &verts−> color ) ; i n t count = endvert < s t a r t v e r t ? maxverts − s t a r t v e r t : endvert − startvert ; glDrawArrays ( GL TRIANGLES, s t a r t v e r t , count ) ; i f ( endvert < s t a r t v e r t ) { count += endvert ; glDrawArrays ( GL TRIANGLES, 0 , endvert ) ; } xtravertsva += count ; i f ( f l a g s &(DF ADD|DF INVMOD|DF OVERBRIGHT) ) glFogfv (GL FOG COLOR, oldfogc ) ; i f ( f l a g s &(DF OVERBRIGHT|DF SATURATE) && renderpath==R FIXEDFUNCTION && hasTE ) resettmu ( 0 ) ; extern i n t i n t e l v e r t e x a r r a y b u g ; i f ( i n t e l v e r t e x a r r a y b u g ) glFlush ( ) ; } d e c a l i n f o &newdecal ( ) { d e c a l i n f o &d = decals [ enddecal ] ; i n t next = enddecal + 1; i f ( next>=maxdecals ) next = 0; i f ( next== s t a r t d e c a l ) f r e e d e c a l ( ) ; enddecal = next ; return d ; } i v e c bborigin , bbsize ; vec decalcenter , decalnormal , decaltangent , decalbitangent ; f l o a t decalradius , decalu , decalv ; bvec decalcolor ; void adddecal ( const vec ¢er , const vec &dir , f l o a t radius , const bvec &color , i n t i n f o ) { i n t i s z = i n t ( c e i l ( radius ) ) ; bborigin = i v e c ( center ) . sub ( i s z ) ; bbsize = i v e c ( i s z ∗2, i s z ∗2, i s z ∗2) ;

glDepthMask ( GL TRUE ) ; glDisable (GL BLEND) ; d i s a b l e p o l y g o n o f f s e t ( GL POLYGON OFFSET FILL ) ; } void render ( ) { i f ( s t a r t v e r t ==endvert ) return ;

decalcolor = c o l o r ; decalcenter = center ; decalradius = radius ; decalnormal = d i r ; #if 0 decaltangent . orthogonal ( d i r ) ;

f l o a t oldfogc [ 4 ] ; i f ( f l a g s &(DF ADD|DF INVMOD|DF OVERBRIGHT) ) { glGetFloatv (GL FOG COLOR, oldfogc ) ; s t a t i c f l o a t zerofog [ 4 ] = { 0 , 0 , 0 , 1 }, grayfog [ 4 ] = { 0.5 f , 0.5 f , 0.5 f , 1 }; glFogfv (GL FOG COLOR, f l a g s&DF OVERBRIGHT && ( renderpath ! = R FIXEDFUNCTION || hasTE ) ? grayfog : zerofog ) ; } i f ( f l a g s&DF OVERBRIGHT) { i f ( renderpath ! =R FIXEDFUNCTION ) { glBlendFunc ( GL DST COLOR, GL SRC COLOR) ; SETSHADER( overbrightdecal ) ; } e l s e i f ( hasTE ) { glBlendFunc ( GL DST COLOR, GL SRC COLOR) ; setuptmu ( 0 , ”T , C @ Ca ” ) ; } e l s e glBlendFunc ( GL DST COLOR, GL ONE MINUS SRC ALPHA ) ; } else { i f ( f l a g s&DF INVMOD) glBlendFunc ( GL ZERO, GL ONE MINUS SRC COLOR ) ; e l s e i f ( f l a g s&DF ADD) glBlendFunc (GL ONE, GL ONE MINUS SRC COLOR ); e l s e glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; i f ( f l a g s&DF SATURATE) { i f ( renderpath ! =R FIXEDFUNCTION ) SETSHADER( saturatedecal ) ; e l s e i f ( hasTE ) setuptmu ( 0 , ”C ∗ T x 2 ” ) ; }

#e l s e decaltangent = vec ( d i r . z , −d i r . x , d i r . y ) ; decaltangent . sub ( vec ( d i r ) . mul ( decaltangent . dot ( d i r ) ) ) ; #endif i f ( f l a g s&DF ROTATE) decaltangent . r o t a t e ( rnd (360)∗RAD, d i r ) ; decaltangent . normalize ( ) ; decalbitangent . cross ( decaltangent , d i r ) ; i f ( f l a g s&DF RND4) { decalu = 0.5 f ∗( i n f o &1) ; decalv = 0.5 f ∗ ( ( info>>1)&1) ; } ushort dstart = endvert ; g e n t r i s ( worldroot , i v e c ( 0 , 0 , 0) , worldsize>>1); i f ( dbgdec ) { i n t nverts = endvert < dstart ? endvert + maxverts − dstart : endvert − dstart ; conoutf (CON DEBUG, ” t r i s = %d , v e r t s = %d , t o t a l t r i s = %d ” , nverts /3 , nverts , ( maxverts − 3 − a v a i l v e r t s ) /3) ; } i f ( endvert==dstart ) return ; d e c a l i n f o &d = newdecal ( ) ; d. color = color ; d. millis = lastmillis ; d . s t a r t v e r t = dstart ; d . endvert = endvert ; } s t a t i c i n t c l i p ( const vec ∗in , i n t numin, const vec &dir , f l o a t below , f l o a t above , vec ∗out ) { i n t numout = 0; const vec ∗p = &in [ numin−1]; f l o a t pc = d i r . dot (∗p ) ;

242

Foundations of Videogame Programming Code Repository

l o o p i ( numin ) { const vec &v = in [ i ] ; f l o a t c = d i r . dot ( v ) ; i f ( c < below ) { i f ( pc > above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; i f ( pc > below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( c > above ) { i f ( pc < below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; i f ( pc < above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; } else { i f ( pc < below ) { i f ( c > below ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( below − c ) / ( pc − c ) ) . add ( v ) ; } e l s e i f ( pc > above && c < above ) out [ numout++] = vec (∗p ) . sub ( v ) . mul ( ( above − c ) / ( pc − c ) ) . add ( v ) ; out [ numout++] = v ; } p = &v ; pc = c ; } return numout;

i f ( convex ) { planes [ 1 ] . cross ( pos [ 0 ] , pos [ 2 ] , pos [ 3 ] ) . normalize ( ) ; numplanes++; } } e l s e return ; l o o p l ( numplanes ) { const vec &n = planes [ l ] ; f l o a t facing = n . dot ( decalnormal ) ; i f ( facing decalradius ) continue ; vec pcenter = vec ( decalnormal ) . mul ( d i s t ) . add ( decalcenter ) ; #e l s e // t r a v e l back along plane normal from the decal center f l o a t d i s t = n . dot ( p ) ; i f ( fabs ( d i s t ) > decalradius ) continue ; vec pcenter = vec ( n ) . mul ( d i s t ) . add ( decalcenter ) ; #endif vec f t , fb ; f t . orthogonal ( n ) ; f t . normalize ( ) ; fb . cross ( f t , n ) ; vec pt = vec ( f t ) . mul ( f t . dot ( decaltangent ) ) . add ( vec ( fb ) . mul ( fb . dot ( decaltangent ) ) ) . normalize ( ) , pb = vec ( f t ) . mul ( f t . dot ( decalbitangent ) ) . add ( vec ( fb ) . mul ( fb . dot ( decalbitangent ) ) ) . normalize ( ) ; // orthonormalize projected bitangent to prevent streaking pb . sub ( vec ( pt ) . mul ( pt . dot ( pb ) ) ) . normalize ( ) ; vec v1 [MAXFACEVERTS+4] , v2 [MAXFACEVERTS+ 4 ] ; f l o a t ptc = pt . dot ( pcenter ) , pbc = pb . dot ( pcenter ) ; i n t numv; i f ( numplanes >= 2) { i f ( l ) { pos [ 1 ] = pos [ 2 ] ; pos [ 2 ] = pos [ 3 ] ; } numv = c l i p ( pos , 3 , pt , ptc − decalradius , ptc + decalradius , v1 ) ; i f (numvcsize , , + mat−>r s i z e , + 0.1 f , − 0.1 f ) ; #undef GENFACEORIENT #undef GENFACEVERT } } e l s e i f ( cu . texture [ o r i e n t ] == DEFAULT SKY) return ; e l s e i f ( cu . ext && ( numverts = cu . ext−>surfaces [ o r i e n t ] . numverts& MAXFACEVERTS) ) { v e r t i n f o ∗v e r t s = cu . ext−>v e r t s ( ) + cu . ext−>surfaces [ o r i e n t ] . verts ; i v e c vo = i v e c ( o ) .mask( ˜ 0xFFF ) . shl ( 3 ) ; l o o p j ( numverts ) pos [ j ] = v e r t s [ j ] . getxyz ( ) . add ( vo ) . tovec ( ) . mul (1/8.0 f ) ; planes [ 0 ] . cross ( pos [ 0 ] , pos [ 1 ] , pos [ 2 ] ) . normalize ( ) ; i f ( numverts >= 4 && ! ( cu . merged&(1>1, cu [ i ] . escaped ) ; else { i n t vismask = cu [ i ] . v i s i b l e ; i f ( vismask&0xC0 ) { i f ( vismask&0x80 ) l o o p j ( 6 ) g e n t r i s ( cu [ i ] , j , co , size , NULL, vismask ) ; e l s e l o o p j ( 6 ) i f ( vismask&(1>= DYNLIGHTBITS, index ++) { dynlight &d = ∗closedynlights [ ( mask&DYNLIGHTMASK) −1];

} #endif c o l o r . add ( dyncolor ) ; } void calcdynlightmask ( vtxarray ∗va ) { uint mask = 0; i n t o f f s e t = 0; loopv ( closedynlights ) { dynlight &d = ∗closedynlights [ i ] ; i f ( d . o . d i s t t o b b ( va−>geommin, va−>geommax) >= d . curradius ) continue ;

f l o a t scale = 1.0 f /d . curradius ; vec o r i g i n = vec ( d . o ) . mul(−scale ) ; setenvparamf ( posparams [ index ] , SHPARAM VERTEX, 10+index , o r i g i n . x , o r i g i n . y , o r i g i n . z , scale ) ; i f ( indexdynlightmask ) return 0;

} return index ; }

engine/engine.h #ifndef #define

ENGINE H ENGINE H

#include ” cube . h” #include ” world . h” # i f n d e f STANDALONE #include #include #include #include #include

” octa . h” ” lightmap . h” ” bih . h” ” texture . h” ” model . h”

// GL ARB multitexture extern PFNGLACTIVETEXTUREARBPROC glActiveTexture ; extern PFNGLCLIENTACTIVETEXTUREARBPROC g l C l i e n t A c t i v e T e x t u r e ; extern PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2f ; extern PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3f ; extern PFNGLMULTITEXCOORD4FARBPROC glMultiTexCoord4f ; // GL extern extern extern extern extern extern extern extern

ARB vertex buffer object PFNGLGENBUFFERSARBPROC glGenBuffers ; PFNGLBINDBUFFERARBPROC glBindBuffer ; PFNGLMAPBUFFERARBPROC glMapBuffer ; PFNGLUNMAPBUFFERARBPROC glUnmapBuffer ; PFNGLBUFFERDATAARBPROC glBufferData ; PFNGLBUFFERSUBDATAARBPROC glBufferSubData ; PFNGLDELETEBUFFERSARBPROC glDeleteBuffers ; PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubData ;

// GL ARB occlusion query extern PFNGLGENQUERIESARBPROC glGenQueries ; extern PFNGLDELETEQUERIESARBPROC glDeleteQueries ; extern PFNGLBEGINQUERYARBPROC glBeginQuery ; extern PFNGLENDQUERYARBPROC glEndQuery ; extern PFNGLGETQUERYIVARBPROC glGetQueryiv ; extern PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectiv ; extern PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuiv ; // GL extern extern extern extern extern extern extern extern extern

EXT framebuffer object PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbuffer ; PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffers ; PFNGLGENFRAMEBUFFERSEXTPROC glGenRenderbuffers ; PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorage ; PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatus ; PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebuffer ; PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffers ; PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffers ; PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2D ;

extern PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbuffer ; extern PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmap ; // GL EXT framebuffer blit # i f n d e f GL EXT framebuffer blit #define GL READ FRAMEBUFFER EXT 0x8CA8 0x8CA9 #define GL DRAW FRAMEBUFFER EXT #define GL DRAW FRAMEBUFFER BINDING EXT 0x8CA6 #define GL READ FRAMEBUFFER BINDING EXT 0x8CAA typedef void ( APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) ( GLint srcX0 , GLint srcY0 , GLint srcX1 , GLint srcY1 , GLint dstX0 , GLint dstY0 , GLint dstX1 , GLint dstY1 , G L b i t f i e l d mask, GLenum f i l t e r ) ; #endif extern PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebuffer ; // GL EXT draw range elements extern PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElements ; // GL EXT blend minmax extern PFNGLBLENDEQUATIONEXTPROC glBlendEquation ; // GL EXT blend color extern PFNGLBLENDCOLOREXTPROC glBlendColor ; // GL EXT multi draw arrays extern PFNGLMULTIDRAWARRAYSEXTPROC glMultiDrawArrays ; extern PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElements ; // GL EXT # i f n d e f GL #define GL #endif # i f n d e f GL #define GL #endif

packed depth stencil DEPTH STENCIL EXT DEPTH STENCIL EXT 0x84F9 DEPTH24 STENCIL8 EXT DEPTH24 STENCIL8 EXT 0x88F0

// GL ARB texture compression extern PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glCompressedTexImage3D ; extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2D ; extern PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glCompressedTexImage1D ; extern PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glCompressedTexSubImage3D ; extern PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2D ; extern PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1D ; extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImage ; // GL EXT fog coord extern PFNGLFOGCOORDPOINTEREXTPROC glFogCoordPointer ; // GL ARB map buffer range # i f n d e f GL ARB map buffer range

engine/engine.h #define GL MAP READ BIT 0x0001 #define GL MAP WRITE BIT 0x0002 #define GL MAP INVALIDATE RANGE BIT 0x0004 #define GL MAP INVALIDATE BUFFER BIT 0x0008 #define GL MAP FLUSH EXPLICIT BIT 0x0010 #define GL MAP UNSYNCHRONIZED BIT 0x0020 typedef GLvoid∗ ( APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target , GLintptr o f f s e t , GLsizeiptr length , G L b i t f i e l d access ) ; typedef void ( APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target , GLintptr o f f s e t , GLsizeiptr length ) ; #endif extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange ; extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange ; #include ” varray . h” extern dynent ∗player ; extern physent ∗camera1 ; // s p e c i a l ent that acts as camera , same o b j e c t as player1 in FPS mode extern extern extern extern extern extern extern extern extern extern extern

i n t worldscale , worldsize ; i n t mapversion ; char ∗maptitle ; vector texmru ; i n t xtraverts , xtravertsva ; const i v e c cubecoords [ 8 ] ; const i v e c facecoords [ 6 ] [ 4 ] ; const uchar f v [ 6 ] [ 4 ] ; const uchar fvmasks [ 6 4 ] ; const uchar faceedgesidx [ 6 ] [ 4 ] ; bool inbetweenframes , renderedframe ;

extern SDL Surface ∗screen ; extern i n t zpass , glowpass ;

extern extern extern extern extern extern extern

247

void setuptexcompress ( ) ; void c l e a r s l o t s ( ) ; void compacteditvslots ( ) ; void compactmruvslots ( ) ; void compactvslots ( cube ∗c , i n t n = 8) ; void compactvslot ( i n t &index ) ; i n t compactvslots ( ) ;

// shadowmap extern i n t shadowmap, shadowmapcasters ; extern bool shadowmapping ; extern extern extern extern extern extern extern extern

bool bool bool void void void void void

isshadowmapcaster ( const vec &o , f l o a t rad ) ; addshadowmapcaster ( const vec &o , f l o a t xyrad , f l o a t zrad ) ; isshadowmapreceiver ( vtxarray ∗va ) ; rendershadowmap ( ) ; pushshadowmap ( ) ; popshadowmap ( ) ; rendershadowmapreceivers ( ) ; guessshadowdir ( ) ;

// pvs extern extern extern extern extern extern extern

void clearpvs ( ) ; bool pvsoccluded ( const i v e c &bborigin , const i v e c &bbsize ) ; bool waterpvsoccluded ( i n t height ) ; void s e t v i e w c e l l ( const vec &p ) ; void savepvs ( stream ∗f ) ; void loadpvs ( stream ∗f , i n t numpvs ) ; i n t getnumviewcells ( ) ;

s t a t i c i n l i n e bool pvsoccluded ( const i v e c &bborigin , i n t s i z e ) { return pvsoccluded ( bborigin , i v e c ( size , size , s i z e ) ) ; }

extern vector entgroup ; // rendertext struct font { struct charinfo { short x , y , w, h , o f f s e t x , o f f s e t y , advance , tex ; }; char ∗name; vector texs ; vector chars ; i n t charoffset , defaultw , defaulth , scale ; font ( ) : name(NULL) {} ˜ font ( ) { DELETEA(name) ; }

// rendergl extern bool hasVBO, hasDRE, hasOQ, hasTR , hasFBO, hasDS, hasTF , hasBE, hasBC, hasCM, hasNP2 , hasTC , hasS3TC, hasFXT1 , hasTE , hasMT, hasD3 , hasAF , hasVP2 , hasVP3 , hasPP , hasMDA, hasTE3 , hasTE4 , hasVP , hasFP , hasGLSL, hasGM, hasNVFB, hasSGIDT , hasSGISH, hasDT, hasSH, hasNVPCF, hasRN, hasPBO, hasFBB, hasUBO, hasBUE, hasMBR, hasFC , hasTEX ; extern i n t hasstencil ; extern i n t glversion , g l s l v e r s i o n ; extern extern extern extern

f l o a t curfov , fovy , aspect , forceaspect ; bool envmapping , minimapping , renderedgame , modelpreviewing ; const g l m a t r i x f viewmatrix ; g l m a t r i x f mvmatrix , projmatrix , mvpmatrix , invmvmatrix , invmvpmatrix , fogmatrix , invfogmatrix , envmatrix ; extern bvec f o g c o l o r ;

}; #define #define #define #define

FONTH ( curfont−>scale ) FONTW (FONTH/2) MINRESW 640 MINRESH 480

extern font ∗curfont ; // texture extern i n t hwtexsize , hwcubetexsize , hwmaxaniso , maxtexsize ; extern Texture ∗textureload ( const char ∗name, i n t clamp = 0 , bool mipit = true , bool msg = true ) ; extern i n t t e x a l i g n ( void ∗data , i n t w, i n t bpp ) ; extern void cleanuptexture ( Texture ∗t ) ; extern void loadalphamask ( Texture ∗t ) ; extern void loadlayermasks ( ) ; extern Texture ∗cubemapload ( const char ∗name, bool mipit = true , bool msg = true , bool transient = f a l s e ) ; extern void drawcubemap ( i n t size , const vec &o , f l o a t yaw , f l o a t pitch , const cubemapside &side ) ; extern void loadshaders ( ) ; extern void setuptexparameters ( i n t tnum, void ∗pixels , i n t clamp , i n t f i l t e r , GLenum format = GL RGB, GLenum t a r g e t = GL TEXTURE 2D ) ; extern void createtexture ( i n t tnum, i n t w, i n t h , void ∗pixels , i n t clamp , i n t f i l t e r , GLenum component = GL RGB, GLenum t a r g e t = GL TEXTURE 2D, i n t pw = 0 , i n t ph = 0 , i n t pitch = 0 , bool r e s i z e = true , GLenum format = GL FALSE ) ; extern void blurtexture ( i n t n , i n t bpp , i n t w, i n t h , uchar ∗dst , const uchar ∗src , i n t margin = 0) ; extern void blurnormals ( i n t n , i n t w, i n t h , bvec ∗dst , const bvec ∗src , i n t margin = 0) ; extern void renderpostfx ( ) ; extern void initenvmaps ( ) ; extern void genenvmaps ( ) ; extern ushort closestenvmap ( const vec &o ) ; extern ushort closestenvmap ( i n t orient , i n t x , i n t y , i n t z , i n t s i z e ) ; extern GLuint lookupenvmap ( ushort emid ) ; extern GLuint lookupenvmap ( S l o t &s l o t ) ; extern bool reloadtexture ( Texture &tex ) ; extern bool reloadtexture ( const char ∗name) ;

extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern

void gl checkextensions ( ) ; void g l i n i t ( i n t w, i n t h , i n t bpp , i n t depth , i n t fsaa ) ; void cleangl ( ) ; void rendergame ( bool mainpass = f a l s e ) ; void i n v a l i d a t e p o s t f x ( ) ; void gl drawframe ( i n t w, i n t h ) ; void gl drawmainmenu ( i n t w, i n t h ) ; void drawminimap ( ) ; void drawtextures ( ) ; void enablepolygonoffset (GLenum type ) ; void d i s a b l e p o l y g o n o f f s e t (GLenum type ) ; void calcspherescissor ( const vec ¢er , f l o a t size , f l o a t &sx1 , f l o a t &sy1 , f l o a t &sx2 , f l o a t &sy2 ) ; i n t pushscissor ( f l o a t sx1 , f l o a t sy1 , f l o a t sx2 , f l o a t sy2 ) ; void popscissor ( ) ; void recomputecamera ( ) ; void f i n d o r i e n t a t i o n ( ) ; void writecrosshairs ( stream ∗f ) ;

namespace modelpreview { extern void s t a r t ( bool background = true ) ; extern void end ( ) ; } // renderextras extern void render3dbox ( vec &o , f l o a t t o f l o o r , f l o a t t o c e i l , f l o a t xradius , f l o a t yradius = 0) ; // octa extern cube ∗newcubes ( uint face = F EMPTY, i n t mat = MAT AIR ) ; extern cubeext ∗growcubeext ( cubeext ∗ext , i n t maxverts ) ; extern void setcubeext ( cube &c , cubeext ∗ext ) ; extern cubeext ∗newcubeext ( cube &c , i n t maxverts = 0 , bool i n i t = true ) ; extern void getcubevector ( cube &c , i n t d , i n t x , i n t y , i n t z , i v e c &p ) ; extern void setcubevector ( cube &c , i n t d , i n t x , i n t y , i n t z , const i v e c &p ) ; extern i n t f a m i l y s i z e ( const cube &c ) ; extern void f r e e o c t a ( cube ∗c ) ; extern void discardchildren ( cube &c , bool f i x t e x = f a l s e , i n t depth = 0) ; extern void o p t i f a c e ( uchar ∗p , cube &c ) ;

248 extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern

extern extern extern extern extern extern extern

Foundations of Videogame Programming Code Repository void v a l i d a t e c ( cube ∗c , i n t s i z e = 0) ; bool isvalidcube ( const cube &c ) ; i v e c lu ; int lusize ; cube &lookupcube ( i n t tx , i n t ty , i n t tz , i n t t s i z e = 0 , i v e c &ro = lu , i n t &r s i z e = l u s i z e ) ; const cube ∗neighbourstack [ 3 2 ] ; i n t neighbourdepth ; const cube &neighbourcube ( const cube &c , i n t orient , i n t x , i n t y , i n t z , i n t size , i v e c &ro = lu , i n t &r s i z e = l u s i z e ) ; void r e s e t c l i p p l a n e s ( ) ; i n t getmippedtexture ( const cube &p , i n t o r i e n t ) ; void forcemip ( cube &c , bool f i x t e x = true ) ; bool subdividecube ( cube &c , bool fullcheck=true , bool brighten= true ) ; void edgespan2vectorcube ( cube &c ) ; i n t faceconvexity ( const i v e c v [ 4 ] ) ; i n t faceconvexity ( const i v e c v [ 4 ] , i n t &v i s ) ; i n t faceconvexity ( const v e r t i n f o ∗verts , i n t numverts , i n t s i z e ) ; i n t faceconvexity ( const cube &c , i n t o r i e n t ) ; void c a l c v e r t ( const cube &c , i n t x , i n t y , i n t z , i n t size , i v e c & vert , i n t i , bool s o l i d = f a l s e ) ; void c a l c v e r t ( const cube &c , i n t x , i n t y , i n t z , i n t size , vec & vert , i n t i , bool s o l i d = f a l s e ) ; uint faceedges ( const cube &c , i n t o r i e n t ) ; bool collapsedface ( const cube &c , i n t o r i e n t ) ; bool touchingface ( const cube &c , i n t o r i e n t ) ; bool f l a t a x i s f a c e ( const cube &c , i n t o r i e n t ) ; bool c o l l i d e f a c e ( const cube &c , i n t o r i e n t ) ; i n t genclipplane ( const cube &c , i n t i , vec ∗v , plane ∗c l i p ) ; void genclipplanes ( const cube &c , i n t x , i n t y , i n t z , i n t size , clipplanes &p , bool c o l l i d e = true ) ; bool v i s i b l e f a c e ( const cube &c , i n t orient , i n t x , i n t y , i n t z , i n t size , ushort mat = MAT AIR , ushort nmat = MAT AIR , ushort matmask = MATF VOLUME) ; i n t c l a s s i f y f a c e ( const cube &c , i n t orient , i n t x , i n t y , i n t z , int size ) ; i n t v i s i b l e t r i s ( const cube &c , i n t orient , i n t x , i n t y , i n t z , i n t size , ushort nmat = MAT ALPHA, ushort matmask = MAT ALPHA) ; i n t v i s i b l e o r i e n t ( const cube &c , i n t o r i e n t ) ; void genfaceverts ( const cube &c , i n t orient , i v e c v [ 4 ] ) ; i n t calcmergedsize ( i n t orient , const i v e c &co , i n t size , const v e r t i n f o ∗verts , i n t numverts ) ; void invalidatemerges ( cube &c , const i v e c &co , i n t size , bool msg ) ; void calcmerges ( ) ;

extern i n t mergefaces ( i n t orient , facebounds ∗m, i n t sz ) ; extern void mincubeface ( const cube &cu , i n t orient , const i v e c &o , i n t size , const facebounds &orig , facebounds &cf , ushort nmat = MAT AIR , ushort matmask = MATF VOLUME) ;

extern extern extern extern extern extern extern extern

void octarender ( ) ; void allchanged ( bool load = f a l s e ) ; void clearvas ( cube ∗c ) ; vtxarray ∗newva ( i n t x , i n t y , i n t z , i n t s i z e ) ; void destroyva ( vtxarray ∗va , bool reparent = true ) ; bool readva ( vtxarray ∗va , ushort ∗&edata , uchar ∗&vdata ) ; void updatevabb ( vtxarray ∗va , bool f o r c e = f a l s e ) ; void updatevabbs ( bool f o r c e = f a l s e ) ;

// renderva extern void visiblecubes ( bool c u l l = true ) ; extern void s e t v f c P ( f l o a t z = −1, const vec &bbmin = vec(−1, −1, −1) , const vec &bbmax = vec ( 1 , 1 , 1) ) ; extern void savevfcP ( ) ; extern void r e s t o r e v f c P ( ) ; extern void rendergeom ( f l o a t causticspass = 0 , bool fogpass = f a l s e ) ; extern void renderalphageom ( bool fogpass = f a l s e ) ; extern void rendermapmodels ( ) ; extern void renderreflectedgeom ( bool causticspass = f a l s e , bool fogpass = false ) ; extern void renderreflectedmapmodels ( ) ; extern void renderoutline ( ) ; extern bool rendersky ( bool e x p l i c i t o n l y = f a l s e ) ; extern extern extern extern extern extern extern extern

bool isfoggedsphere ( f l o a t rad , const vec &cv ) ; i n t i s v i s i b l e s p h e r e ( f l o a t rad , const vec &cv ) ; bool bboccluded ( const i v e c &bo , const i v e c &br ) ; occludequery ∗newquery ( void ∗owner ) ; bool checkquery ( occludequery ∗query , bool nowait = f a l s e ) ; void resetqueries ( ) ; i n t getnumqueries ( ) ; void drawbb ( const i v e c &bo , const i v e c &br , const vec &camera = camera1−>o ) ;

#define startquery ( query ) { glBeginQuery ( GL SAMPLES PASSED ARB, ( ( occludequery ∗) ( query ) )−>id ) ; } #define endquery ( query ) \ { \ glEndQuery ( GL SAMPLES PASSED ARB ) ; \ extern i n t ati oq bug ; \ i f ( ati oq bug ) glFlush ( ) ; \ } // dynlight extern extern extern extern extern

void updatedynlights ( ) ; i n t finddynlights ( ) ; void calcdynlightmask ( vtxarray ∗va ) ; i n t setdynlights ( vtxarray ∗va ) ; bool getdynlight ( i n t n , vec &o , f l o a t &radius , vec &c o l o r ) ;

// material s t a t i c i n l i n e uchar octantrectangleoverlap ( const i v e c &c , i n t size , const i v e c &o , const i v e c &s ) { uchar p = 0xFF ; // bitmask o f possible c o l l i s i o n s with octants . 0 b i t = 0 octant , etc ivec v ( c ) ; v . add ( s i z e ) ; i f ( v . z = o . z+s . z ) p &= 0x0F ; // not in a +ve Z octant i f ( v . y = o . y+s . y ) p &= 0x33 ; // etc . . i f ( v . x = o . x+s . x ) p &= 0x55 ; return p ; } s t a t i c i n l i n e cubeext &ext ( cube &c ) { return ∗(c . ext ? c . ext : newcubeext ( c ) ) ; } // ents extern char ∗entname ( e n t i t y &e ) ; extern bool haveselent ( ) ; extern undoblock ∗copyundoents ( undoblock ∗u ) ; extern void pasteundoents ( undoblock ∗u ) ; // o c t a e d i t extern void extern void extern void extern void extern void extern void

cancelsel ( ) ; rendertexturepanel ( i n t w, i n t h ) ; addundo ( undoblock ∗u ) ; commitchanges ( bool f o r c e = f a l s e ) ; rendereditcursor ( ) ; tryedit ( ) ;

// octarender extern vector t j o i n t s ; extern extern extern extern

ushort encodenormal ( const vec &n ) ; vec decodenormal ( ushort norm ) ; void reduceslope ( i v e c &n ) ; void f i n d t j o i n t s ( ) ;

extern i n t showmat ; extern extern extern extern extern extern extern extern extern extern

i n t findmaterial ( const char ∗name) ; const char ∗findmaterialname ( i n t mat ) ; const char ∗getmaterialdesc ( i n t mat , const char ∗p r e f i x = ” ” ) ; void genmatsurfs ( const cube &c , i n t cx , i n t cy , i n t cz , i n t size , vector &matsurfs ) ; void rendermatsurfs ( materialsurface ∗matbuf , i n t matsurfs ) ; void rendermatgrid ( materialsurface ∗matbuf , i n t matsurfs ) ; i n t optimizematsurfs ( materialsurface ∗matbuf , i n t matsurfs ) ; void setupmaterials ( i n t s t a r t = 0 , i n t len = 0) ; void rendermaterials ( ) ; i n t v i s i b l e m a t e r i a l ( const cube &c , i n t orient , i n t x , i n t y , i n t z , i n t size , ushort matmask = MATF VOLUME) ;

// water extern i n t r e f r a c t i n g , r e f r a c t f o g ; extern bvec r e f r a c t c o l o r ; extern bool r e f l e c t i n g , fading , fogging ; extern f l o a t r e f l e c t z ; extern i n t r e f l e c t d i s t , vertwater , waterrefract , w a t e r r e f l e c t , waterfade , caustics , w a t e r f a l l r e f r a c t ; #define GETMATIDXVAR( name, var , type ) \ type get##name##var ( i n t mat ) \ { \ switch ( mat&MATF INDEX ) \ { \ d e f a u l t : case 0: return name##var ; \ case 1: return name##2##var ; \ case 2: return name##3##var ; \ case 3: return name##4##var ; \ } \ } extern extern extern extern extern extern

const bvec &getwatercolor ( i n t mat ) ; const bvec &g e t w a t e r f a l l c o l o r ( i n t mat ) ; i n t getwaterfog ( i n t mat ) ; const bvec &g e t l a v a c o l o r ( i n t mat ) ; i n t g e t l a v a f o g ( i n t mat ) ; const bvec &g e t g l a s s c o l o r ( i n t mat ) ;

engine/engine.h

extern extern extern extern extern

void c l e a n r e f l e c t i o n s ( ) ; void q u e r y r e f l e c t i o n s ( ) ; void drawreflections ( ) ; void renderwater ( ) ; void renderlava ( const materialsurface &m, Texture ∗tex , f l o a t scale ) ; extern void loadcaustics ( bool f o r c e = f a l s e ) ; extern void preloadwatershaders ( bool f o r c e = f a l s e ) ; // g l a r e extern bool g l a r i n g ; extern void drawglaretex ( ) ; extern void addglare ( ) ; // depthfx extern bool depthfxing ; extern void drawdepthfxtex ( ) ; // server extern vector gameargs ; extern extern extern extern

void void void void

i n i t s e r v e r ( bool l i s t e n , bool dedicated ) ; cleanupserver ( ) ; s e r v e r s l i c e ( bool dedicated , uint timeout ) ; updatetime ( ) ;

extern extern extern extern

ENetSocket connectmaster ( ) ; void l o c a l c l i e n t t o s e r v e r ( i n t chan , ENetPacket ∗) ; void localconnect ( ) ; bool serveroption ( char ∗opt ) ;

249

extern f l o a t loadprogress ; extern void renderbackground ( const char ∗caption = NULL, Texture ∗mapshot = NULL, const char ∗mapname = NULL, const char ∗mapinfo = NULL, bool r e s t o r e = f a l s e , bool f o r c e = f a l s e ) ; extern void renderprogress ( f l o a t bar , const char ∗text , GLuint tex = 0 , bool background = f a l s e ) ; extern void g e t f p s ( i n t &fps , i n t &b e s t d i f f , i n t &w o r s t d i f f ) ; extern void swapbuffers ( ) ; extern i n t g e t c l o c k m i l l i s ( ) ; // menu extern void menuprocess ( ) ; extern void addchange ( const char ∗desc , i n t type ) ; extern void clearchanges ( i n t type ) ; // physics extern void mousemove ( i n t dx , i n t dy ) ; extern bool pointincube ( const clipplanes &p , const vec &v ) ; extern bool overlapsdynent ( const vec &o , f l o a t radius ) ; extern void rotatebb ( vec ¢er , vec &radius , i n t yaw ) ; extern f l o a t shadowray ( const vec &o , const vec &ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t = NULL) ; struct ShadowRayCache ; extern ShadowRayCache ∗newshadowraycache ( ) ; extern void freeshadowraycache ( ShadowRayCache ∗&cache ) ; extern void resetshadowraycache ( ShadowRayCache ∗cache ) ; extern f l o a t shadowray ( ShadowRayCache ∗cache , const vec &o , const vec & ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t = NULL) ; // world extern vector outsideents ;

// serverbrowser extern bool r e s o l v e r w a i t ( const char ∗name, ENetAddress ∗address ) ; extern i n t connectwithtimeout ( ENetSocket sock , const char ∗hostname , const ENetAddress &address ) ; extern void addserver ( const char ∗name, i n t port = 0 , const char ∗ password = NULL, bool keep = f a l s e ) ; extern void w r i t e s e r v e r c f g ( ) ; // c l i e n t extern void localdisconnect ( bool cleanup = true ) ; extern void l o c a l s e r v e r t o c l i e n t ( i n t chan , ENetPacket ∗packet ) ; extern void connectserv ( const char ∗servername , i n t port , const char ∗ serverpassword ) ; extern void abortconnect ( ) ; extern void c l i e n t k e e p a l i v e ( ) ; // command extern hashset idents ; extern i n t i d e n t f l a g s ; extern void c l e a r o v e r r i d e s ( ) ; extern void w r i t e c f g ( const char ∗name = NULL) ; extern void checksleep ( i n t m i l l i s ) ; extern void c l e a r s l e e p ( bool c l e a r o v e r r i d e s = true ) ; // console extern void keypress ( i n t code , bool isdown , i n t cooked ) ; extern i n t rendercommand ( i n t x , i n t y , i n t w) ; extern i n t renderconsole ( i n t w, i n t h , i n t abovehud ) ; extern void conoutf ( const char ∗s , . . . ) PRINTFARGS( 1 , 2) ; extern void conoutf ( i n t type , const char ∗s , . . . ) PRINTFARGS( 2 , 3) ; extern void resetcomplete ( ) ; extern void complete ( char ∗s , const char ∗cmdprefix ) ; const char ∗getkeyname ( i n t code ) ; extern const char ∗addreleaseaction ( char ∗s ) ; extern void writebinds ( stream ∗f ) ; extern void writecompletions ( stream ∗f ) ; // main enum { NOT INITING = 0 , INIT LOAD , INIT RESET }; extern i n t i n i t i n g , numcpus; enum { CHANGE GFX = 1pos ) ; glTexCoordPointer ( 2 , GL FLOAT, s i z e o f ( spherevert ) , &sphereverts−>s ) ; }

252

Foundations of Videogame Programming Code Repository

}

{ l o o p i ( 2 ) i f ( expmodtex [ i ] ) { glDeleteTextures ( 1 , &expmodtex [ i ] ) ; expmodtex [ i ] = 0; } i f ( hemivbuf ) { g l D e l e t e B u f f e r s ( 1 , &hemivbuf ) ; hemivbuf = 0; } i f ( hemiebuf ) { g l D e l e t e B u f f e r s ( 1 , &hemiebuf ) ; hemiebuf = 0; } DELETEA( hemiverts ) ; DELETEA( hemiindices ) ; i f ( expvbuf ) { g l D e l e t e B u f f e r s ( 1 , &expvbuf ) ; expvbuf = 0; } DELETEA( expverts ) ; i f ( spherevbuf ) { g l D e l e t e B u f f e r s ( 1 , &spherevbuf ) ; spherevbuf = 0; } i f ( sphereebuf ) { g l D e l e t e B u f f e r s ( 1 , &sphereebuf ) ; sphereebuf = 0; } DELETEA( sphereverts ) ; DELETEA( sphereindices ) ;

s t a t i c void drawexpverts ( i n t numverts , i n t numindices , GLushort ∗indices ) { i f (hasDRE) glDrawRangeElements ( GL TRIANGLES, 0 , numverts−1, numindices , GL UNSIGNED SHORT, indices ) ; e l s e glDrawElements ( GL TRIANGLES, numindices , GL UNSIGNED SHORT, indices ) ; x t r a v e r t s += numindices ; glde ++; } s t a t i c void drawexplosion ( bool inside , uchar r , uchar g , uchar b , uchar a )

}

{ i f ( ( renderpath ! =R FIXEDFUNCTION || maxtmus>=2) && lastexpmodtex ! = expmodtex [ inside ? 1 : 0 ] ) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; lastexpmodtex = expmodtex [ inside ? 1 : 0 ] ; glBindTexture ( GL TEXTURE 2D, lastexpmodtex ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; } i n t passes = ! r e f l e c t i n g && ! r e f r a c t i n g && inside ? 2 : 1; i f ( renderpath ! =R FIXEDFUNCTION && ! explosion2d ) { i f ( inside ) g l S c a l e f ( 1 , 1 , −1) ; l o o p i ( passes ) { glColor4ub ( r , g , b , i ? a/2 : a ) ; i f ( i ) glDepthFunc (GL GEQUAL) ; drawexpverts ( spherenumverts , spherenumindices , sphereindices ) ; i f ( i ) glDepthFunc ( GL LESS ) ; } return ; } l o o p i ( passes ) { glColor4ub ( r , g , b , i ? a/2 : a ) ; if ( i ) { g l S c a l e f ( 1 , 1 , −1) ; glDepthFunc (GL GEQUAL) ; } i f ( inside ) { i f ( passes >= 2) { glCullFace (GL FRONT) ; drawexpverts ( heminumverts , heminumindices , hemiindices ) ; glCullFace (GL BACK) ; } g l S c a l e f ( 1 , 1 , −1) ; } drawexpverts ( heminumverts , heminumindices , hemiindices ) ; i f ( i ) glDepthFunc ( GL LESS ) ; } } s t a t i c void cleanupexplosion ( ) { g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; i f ( renderpath==R FIXEDFUNCTION ) { g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; i f ( maxtmus>=2) { resettmu ( 0 ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glDisable ( GL TEXTURE 2D ) ; resettmu ( 1 ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; } } else { i f ( ! explosion2d ) g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; } i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } } s t a t i c void deleteexplosions ( )

s t a t i c const f l o a t WOBBLE = 1.25 f ; struct f i r e b a l l r e n d e r e r : l i s t r e n d e r e r { f i r e b a l l r e n d e r e r ( const char ∗texname ) : l i s t r e n d e r e r ( texname , 0 , PT FIREBALL|PT GLARE ) {} void startrender ( ) { setupexplosion ( ) ; } void endrender ( ) { cleanupexplosion ( ) ; particleshader−>set ( ) ; } void cleanup ( ) { deleteexplosions ( ) ; } i n t finddepthfxranges ( void ∗∗owners , f l o a t ∗ranges , i n t numranges , i n t maxranges , vec &bbmin , vec &bbmax) { s t a t i c struct f i r e b a l l e n t : physent { fireballent ( ) { type = ENT CAMERA; c o l l i d e t y p e = COLLIDE AABB; } } e; f o r ( l i s t p a r t i c l e ∗p = l i s t ; p ; p = p−>next ) { i n t t s = p−>fade m i l l i s ; f l o a t pmax = p−>val , s i z e = p−>fade ? f l o a t ( t s ) /p−>fade : 1 , psize = ( p−>s i z e + pmax ∗ s i z e )∗WOBBLE; i f (2∗(p−>s i z e + pmax)∗WOBBLE < depthfxblend || ( ! depthfxtex . highprecision ( ) && ! depthfxtex . emulatehighprecision ( ) && psize > depthfxscale − depthfxbias ) || isfoggedsphere ( psize , p−>o ) ) continue ; e . o = p−>o ; e . radius = e . xradius = e . yradius = e . eyeheight = e . aboveeye = psize ; i f ( : : c o l l i d e (&e , vec ( 0 , 0 , 0) , 0 , f a l s e ) ) continue ; i f ( depthfxscissor==2 && ! depthfxtex . addscissorbox ( p−>o , psize ) ) continue ; vec d i r = camera1−>o ; d i r . sub ( p−>o ) ; f l o a t d i s t = d i r . magnitude ( ) ; d i r . mul ( psize/ d i s t ) . add ( p−>o ) ; f l o a t depth = depthfxtex . eyedepth ( d i r ) ; loopk ( 3 ) { bbmin [ k ] = min ( bbmin [ k ] , p−>o [ k ] − psize ) ; bbmax[ k ] = max(bbmax[ k ] , p−>o [ k ] + psize ) ; } i n t pos = numranges ; l o o p i ( numranges ) i f ( depth < ranges [ i ] ) { pos = i ; break ; } i f ( pos >= maxranges ) continue ; i f ( numranges > pos ) { i n t moved = min ( numranges−pos , maxranges−(pos+1) ) ; memmove(&ranges [ pos +1] , &ranges [ pos ] , moved∗s i z e o f ( f l o a t ) ) ; memmove(&owners [ pos +1] , &owners [ pos ] , moved∗s i z e o f ( void ∗) ) ; } i f ( numranges < maxranges ) numranges++;

engine/glare.cpp g l R o t a t e f ( pitch , 1 , 0 , 0) ; r o t d i r = vec ( 0 , 0 , 1) ; } else { vec s ( 1 , 0 , 0) , t ( 0 , 1 , 0) ; s . r o t a t e ( pitch∗RAD, vec(−1, s . r o t a t e ( yaw∗RAD, vec ( 0 , 0 , t . r o t a t e ( pitch∗RAD, vec(−1, t . r o t a t e ( yaw∗RAD, vec ( 0 , 0 ,

ranges [ pos ] = depth ; owners [ pos ] = p ; } return numranges ; } void seedemitter ( p a r t i c l e e m i t t e r &pe , const vec &o , const vec &d , i n t fade , f l o a t size , i n t g r a v i t y ) { pe . maxfade = max( pe . maxfade , fade ) ; pe . extendbb ( o , ( s i z e +1+pe . ent−>a t t r 2 )∗WOBBLE) ; }

0 , 0) ) ; −1) ) ; 0 , 0) ) ; −1) ) ;

r o t d i r = vec(−1, 1 , −1) . normalize ( ) ; s . r o t a t e(−l a s t m i l l i s /7.0 f∗RAD, r o t d i r ) ; t . r o t a t e(−l a s t m i l l i s /7.0 f∗RAD, r o t d i r ) ;

void renderpart ( l i s t p a r t i c l e ∗p , const vec &o , const vec &d , i n t blend , i n t ts , uchar ∗c o l o r )

setlocalparamf ( ” texgenS ” , SHPARAM VERTEX, 2 , 0.5 f∗s . x , 0.5 f∗s . y , 0.5 f∗s . z , 0.5 f ) ; setlocalparamf ( ” texgenT ” , SHPARAM VERTEX, 3 , 0.5 f∗t . x , 0.5 f∗t . y , 0.5 f∗t . z , 0.5 f ) ;

{ f l o a t pmax = p−>val , s i z e = p−>fade ? f l o a t ( t s ) /p−>fade : 1 , psize = p−>s i z e + pmax ∗ s i z e ;

} i f ( renderpath ! =R FIXEDFUNCTION ) { setlocalparamf ( ” center ” , SHPARAM VERTEX, 0 , o . x , o . y , o . z ) ; setlocalparamf ( ” animstate ” , SHPARAM VERTEX, 1 , size , psize , pmax , float ( lastmillis ) ) ; binddepthfxparams ( depthfxblend , inside ? blend/(2∗255.0 f ) : 0 , 2∗(p−>s i z e + pmax)∗WOBBLE >= depthfxblend , p ) ; }

i f ( isfoggedsphere ( psize∗WOBBLE, p−>o ) ) return ; glPushMatrix ( ) ; glTranslatef ( o . x , o . y , o . z ) ; bool inside = o . d i s t ( camera1−>o ) o ) ; i f ( r e f l e c t i n g ) oc . z = o . z − r e f l e c t z ; f l o a t yaw = inside ? camera1−>yaw : atan2 ( oc . y , oc . x ) /RAD − 90, pitch = ( inside ? camera1−>pitch : asin ( oc . z/oc . magnitude ( ) ) /RAD) − 90; vec r o t d i r ; i f ( renderpath==R FIXEDFUNCTION || explosion2d ) { g l R o t a t e f ( yaw , 0 , 0 , 1) ;

253

g l R o t a t e f ( l a s t m i l l i s /7.0 f , −r o t d i r . x , r o t d i r . y , −r o t d i r . z ) ; g l S c a l e f(−psize , psize , −psize ) ; drawexplosion ( inside , c o l o r [ 0 ] , c o l o r [ 1 ] , c o l o r [ 2 ] , blend ) ; glPopMatrix ( ) ; } }; s t a t i c f i r e b a l l r e n d e r e r f i r e b a l l s ( ” packages/ p a r t i c l e s /explosion . png ” ) , b l u e f i r e b a l l s ( ” packages/ p a r t i c l e s /plasma . png ” ) ;

engine/glare.cpp #include ” engine . h” #include ” rendertarget . h” s t a t i c struct g l a r e t e x t u r e : rendertarget { bool dorender ( ) { extern void drawglare ( ) ; drawglare ( ) ; return true ; } } glaretex ;

void drawglaretex ( ) { i f ( ! g l a r e || renderpath==R FIXEDFUNCTION ) return ; g l a r e t e x . render(18)&0xFF , grasscolour&0xFF ) ; }) ; FVARR( grassalpha , 0 , 1 , 1) ; s t a t i c void gengrassquads ( grassgroup ∗&group , const grasswedge &w, const g r a s s t r i &g , Texture ∗tex ) { f l o a t t = camera1−>o . dot (w. d i r ) ; i n t tstep = i n t ( c e i l ( t /grassstep ) ) ; f l o a t t s t a r t = tstep∗grassstep , t0 = w. d i r . dot ( g . v [ 0 ] ) , t1 = w. d i r . dot ( g . v [ 1 ] ) , t2 = w. d i r . dot ( g . v [ 2 ] ) , t3 = w. d i r . dot ( g . v [ 3 ] ) , tmin = min ( min ( t0 , t1 ) , min ( t2 , t3 ) ) , tmax = max(max( t0 , t1 ) , max( t2 , t3 ) ) ; i f ( tmax < t s t a r t || tmin > t + grassdist ) return ; i n t minstep = max( i n t ( c e i l ( tmin/grassstep ) ) − tstep , 1) , maxstep = i n t ( f l o o r ( min ( tmax , t + grassdist ) /grassstep ) ) − tstep , numsteps = maxstep − minstep + 1; f l o a t t e x s c a l e = ( grassscale∗tex−>ys ) / f l o a t ( grassheight∗tex−>xs ) , animscale = grassheight∗t e x s c a l e ; vec t c ; t c . cross ( g . surface , w. d i r ) . mul ( t e x s c a l e ) ; i n t c o l o r = tstep + maxstep ; i f ( c o l o r < 0) c o l o r = NUMGRASSOFFSETS − (−c o l o r )%NUMGRASSOFFSETS; c o l o r += numsteps + NUMGRASSOFFSETS − numsteps%NUMGRASSOFFSETS; float const i f ( t1 i f ( t2 i f ( t3 float const

l e f t d i s t = t0 ; vec ∗l e f t v = &g . v [ 0 ] ; > l e f t d i s t ) { l e f t v = &g . v [ 1 ] ; l e f t d i s t = t1 ; } > l e f t d i s t ) { l e f t v = &g . v [ 2 ] ; l e f t d i s t = t2 ; } > l e f t d i s t ) { l e f t v = &g . v [ 3 ] ; l e f t d i s t = t3 ; } rightdist = leftdist ; vec ∗r i g h t v = l e f t v ;

vec across (w. across . x , w. across . y , g . surface . z d e l t a (w. across ) ) , l e f t d i r ( 0 , 0 , 0) , r i g h t d i r ( 0 , 0 , 0) , l e f t p = ∗l e f t v , rightp = ∗ rightv ;

f l o a t t a p e r d i s t = grassdist∗grasstaper , taperscale = 1.0 f / ( grassdist − t a p e r d i s t ) , d i s t = maxstep∗grassstep + t s t a r t , l e f t b = 0 , rightb = 0 , l e f t d b = 0 , rightdb = 0; f o r ( i n t i = maxstep ; i >= minstep ; i−−, color−−, l e f t p . add ( l e f t d i r ) , rightp . add ( r i g h t d i r ) , l e f t b += l e f t d b , rightb += rightdb , d i s t −= grassstep ) { i f ( d i s t dot (w. d i r ) ; i f ( d i s t dot (w. d i r ) ; } l e f t d i r = vec (∗ l e f t v ) . sub(∗ prev ) ; l e f t d i r . mul ( grassstep/−w. d i r . dot ( l e f t d i r ) ) ; l e f t p = vec ( l e f t d i r ) . mul ( ( prevdist − d i s t ) /grassstep ) . add(∗ prev ) ; l e f t b = w. bound1 . d i s t ( l e f t p ) ; l e f t d b = w. bound1 . dot ( l e f t d i r ) ; } i f ( d i s t = &g . v [ g .numv ] ) r i g h t v = g . v ; r i g h t d i s t = rightv−>dot (w. d i r ) ; i f ( d i s t = &g . v [ g .numv ] ) r i g h t v = g . v ; r i g h t d i s t = rightv−>dot (w. d i r ) ; } r i g h t d i r = vec (∗ r i g h t v ) . sub(∗ prev ) ; r i g h t d i r . mul ( grassstep/−w. d i r . dot ( r i g h t d i r ) ) ; rightp = vec ( r i g h t d i r ) . mul ( ( prevdist − d i s t ) /grassstep ) . add(∗ prev ) ; rightb = w. bound2 . d i s t ( rightp ) ; rightdb = w. bound2 . dot ( r i g h t d i r ) ; } vec p1 = l e f t p , p2 = rightp ; i f ( l e f t b > 0) { i f (w. bound1 . d i s t ( p2 ) >= 0) continue ; p1 . add ( vec ( across ) . mul ( l e f t b ) ) ; } i f ( rightb > 0) { i f (w. bound2 . d i s t ( p1 ) >= 0) continue ; p2 . sub ( vec ( across ) . mul ( rightb ) ) ; } i f ( ! group ) { group = &grassgroups . add ( ) ; group−>t r i = &g ; group−>tex = tex−>id ; extern bool brightengeom ; extern i n t f u l l b r i g h t ; i n t lmid = brightengeom && ( g . lmid < LMID RESERVED || ( f u l l b r i g h t && editmode ) ) ? LMID BRIGHT : g . lmid ; group−>lmtex = lightmaptexs . inrange ( lmid ) ? lightmaptexs [ lmid ] . id : notexture−>id ; group−>o f f s e t = grassverts . length ( ) ; group−>numquads = 0; i f ( lastgrassanim ! = l a s t m i l l i s ) animategrass ( ) ; } group−>numquads++; f l o a t o f f s e t = g r a s s o f f s e t s [ c o l o r%NUMGRASSOFFSETS] , animoffset = animscale∗grassanimoffsets [ c o l o r%NUMGRASSOFFSETS ], tc1 = t c . dot ( p1 ) + o f f s e t , tc2 = t c . dot ( p2 ) + o f f s e t , lm1u = g . tcu . dot ( p1 ) , lm1v = g . tcv . dot ( p1 ) , lm2u = g . tcu . dot ( p2 ) , lm2v = g . tcv . dot ( p2 ) , fade = d i s t − t > t a p e r d i s t ? ( grassdist − ( d i s t − t ) )∗ taperscale : 1 , height = grassheight ∗ fade ; uchar c o l o r [ 4 ] = { grasscolor . x , grasscolor . y , grasscolor . z , uchar ( fade∗grassalpha∗255) }; #define GRASSVERT( n , tcv , modify ) { \ grassvert &gv = grassverts . add ( ) ; \ gv . pos = p##n ; \ memcpy( gv . color , color , s i z e o f ( c o l o r ) ) ; \

engine/grass.cpp gv . u = t c##n ; gv . v = tcv ; \ gv . lmu = lm##n##u ; gv . lmv = lm##n##v ; \ modify ; \

glEnable (GL BLEND) ; glBlendFunc ( renderpath==R FIXEDFUNCTION ? GL SRC ALPHA : GL ONE, GL ONE MINUS SRC ALPHA ) ; glDepthMask ( GL FALSE ) ;

} GRASSVERT( 2 , GRASSVERT( 1 , GRASSVERT( 1 , GRASSVERT( 2 ,

0, 0, 1, 1,

255

{ gv . pos . z += height ; gv . u += animoffset ; }) ; { gv . pos . z += height ; gv . u += animoffset ; }) ; ); );

SETSHADER( grass ) ; glEnableClientState ( GL VERTEX ARRAY ) ; glVertexPointer ( 3 , GL FLOAT, s i z e o f ( grassvert ) , grassverts [ 0 ] . pos . v ) ;

} }

glEnableClientState (GL COLOR ARRAY) ; glColorPointer ( 4 , GL UNSIGNED BYTE, s i z e o f ( grassvert ) , grassverts [ 0 ] . color ) ;

s t a t i c void gengrassquads ( vtxarray ∗va ) { loopv ( va−>g r a s s t r i s ) { g r a s s t r i &g = va−>g r a s s t r i s [ i ] ; i f ( isfoggedsphere ( g . radius , g . center ) ) continue ; f l o a t d i s t = g . center . d i s t ( camera1−>o ) ; i f ( d i s t − g . radius > grassdist ) continue ;

glEnableClientState (GL TEXTURE COORD ARRAY) ; glTexCoordPointer ( 2 , GL FLOAT, s i z e o f ( grassvert ) , &grassverts [ 0 ] . u ) ; i f ( renderpath ! =R FIXEDFUNCTION || maxtmus>=2) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glEnable ( GL TEXTURE 2D ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; glTexCoordPointer ( 2 , GL FLOAT, s i z e o f ( grassvert ) , &grassverts [ 0 ] . lmu ) ; i f ( renderpath==R FIXEDFUNCTION ) setuptmu ( 1 , ”P ∗ T x 2 ” ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; }

S l o t &s = ∗lookupvslot ( g . texture , f a l s e ) . s l o t ; i f ( ! s . grasstex ) { i f ( ! s . autograss ) continue ; s . grasstex = textureload ( s . autograss , 2) ; } grassgroup ∗group = NULL; l o o p i (NUMGRASSWEDGES) { grasswedge &w = grasswedges [ i ] ; i f (w. bound1 . d i s t ( g . center ) > g . radius || w. bound2 . d i s t ( g . center ) > g . radius ) continue ; gengrassquads ( group , w, g , s . grasstex ) ; } i f ( group ) group−>d i s t = d i s t ;

i n t t e x i d = −1, lmtexid = −1; loopv ( grassgroups ) { grassgroup &g = grassgroups [ i ] ; i f ( r e f l e c t i n g || r e f r a c t i n g ) { i f ( r e f r a c t i n g < 0 ? g . t r i−>minz > r e f l e c t z : g . t r i−>maxz + grassheight < r e f l e c t z ) continue ; i f ( isfoggedsphere ( g . t r i−>radius , g . t r i−>center ) ) continue ; }

} } s t a t i c i n l i n e bool comparegrassgroups ( const grassgroup &x , const grassgroup &y ) {

i f ( t e x i d ! = g . tex ) { glBindTexture ( GL TEXTURE 2D, g . tex ) ; t e x i d = g . tex ; } i f ( lmtexid ! = g . lmtex ) { i f ( renderpath ! =R FIXEDFUNCTION || maxtmus>=2) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glBindTexture ( GL TEXTURE 2D, g . lmtex ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; } lmtexid = g . lmtex ; }

return x . d i s t > y . d i s t ; } void generategrass ( ) { i f ( ! grass || ! grassdist ) return ; grassgroups . s e t s i z e ( 0 ) ; grassverts . s e t s i z e ( 0 ) ; i f ( g r a s s o f f s e t s [ 0 ] < 0) l o o p i (NUMGRASSOFFSETS) g r a s s o f f s e t s [ i ] = rnd (0 x1000000 ) / f l o a t (0x1000000 ) ; l o o p i (NUMGRASSWEDGES) { grasswedge &w = grasswedges [ i ] ; w. bound1 . o f f s e t = −camera1−>o . dot (w. bound1 ) ; w. bound2 . o f f s e t = −camera1−>o . dot (w. bound2 ) ; }

glDrawArrays (GL QUADS, g . o f f s e t , 4∗g .numquads) ; xtravertsva += 4∗g .numquads; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; g l D i s a b l e C l i e n t S t a t e (GL COLOR ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ;

extern vtxarray ∗v i s i b l e v a ; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( va−>g r a s s t r i s . empty ( ) || va−>occluded >= OCCLUDE GEOM) continue ; i f ( va−>distance > grassdist ) continue ; i f ( r e f l e c t i n g || r e f r a c t i n g>0 ? va−>o . z+va−>sizeo . z >=r e f l e c t z ) continue ; gengrassquads ( va ) ; }

i f ( renderpath ! =R FIXEDFUNCTION || maxtmus>=2) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; i f ( renderpath==R FIXEDFUNCTION ) resettmu ( 1 ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; glDisable ( GL TEXTURE 2D ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; }

grassgroups . s o r t ( comparegrassgroups ) ; } void rendergrass ( ) { i f ( ! grass || ! grassdist || grassgroups . empty ( ) || dbggrass ) return ; glDisable ( GL CULL FACE ) ;

engine/iqm.h

glDisable (GL BLEND) ; glDepthMask ( GL TRUE ) ; glEnable ( GL CULL FACE ) ; }

256

Foundations of Videogame Programming Code Repository

struct iqm ;

};

struct iqmheader { char magic [ 1 6 ] ; uint version ; uint f i l e s i z e ; uint f l a g s ; uint num text , o f s t e x t ; uint num meshes, ofs meshes ; uint num vertexarrays , num vertexes , o f s v e r t e x a r r a y s ; uint num triangles , o f s t r i a n g l e s , ofs adjacency ; uint num joints , o f s j o i n t s ; uint num poses , ofs poses ; uint num anims, ofs anims ; uint num frames , num framechannels , ofs frames , ofs bounds ; uint num comment, ofs comment ; uint num extensions , ofs extensions ; };

struct iqm : skelmodel , skelloader { iqm ( const char ∗name) : skelmodel (name) {}

struct iqmmesh { uint name; uint material ; uint f i r s t v e r t e x , num vertexes ; uint f i r s t t r i a n g l e , num triangles ; }; enum { IQM POSITION = 0, IQM TEXCOORD = 1, IQM NORMAL = 2, IQM TANGENT = 3, IQM BLENDINDEXES = 4 , IQM BLENDWEIGHTS = 5 , = 6, IQM COLOR IQM CUSTOM = 0x10 }; enum { IQM BYTE = 0, IQM UBYTE = 1 , IQM SHORT = 2 , IQM USHORT = 3 , IQM INT = 4, IQM UINT = 5, = 6, IQM HALF IQM FLOAT = 7 , IQM DOUBLE = 8 , }; struct iqmtriangle { uint vertex [ 3 ] ; }; struct iqmjoint { uint name; i n t parent ; vec pos ; quat o r i e n t ; vec s i z e ; }; struct iqmpose { i n t parent ; uint mask; vec o f f s e t p o s ; vec4 o f f s e t o r i e n t ; vec o f f s e t s i z e ; vec scalepos ; vec4 s c a l e o r i e n t ; vec s c a l e s i z e ; }; struct iqmanim { uint name; uint f i r s t f r a m e , num frames ; f l o a t framerate ; uint f l a g s ; }; struct iqmvertexarray { uint type ; uint f l a g s ; uint format ; uint s i z e ; uint o f f s e t ;

s t a t i c const char ∗formatname ( ) { return ”iqm ” ; } i n t type ( ) const { return MDL IQM; } struct iqmmeshgroup : skelmeshgroup { iqmmeshgroup ( ) { } bool loadiqmmeshes ( const char ∗filename , const iqmheader &hdr , uchar ∗buf ) { l i l s w a p ( ( uint ∗)&buf [ hdr . o f s v e r t e x a r r a y s ] , hdr . num vertexarrays ∗s i z e o f ( iqmvertexarray ) / s i z e o f ( uint ) ) ; l i l s w a p ( ( uint ∗)&buf [ hdr . o f s t r i a n g l e s ] , hdr . num triangles∗ s i z e o f ( iqmtriangle ) / s i z e o f ( uint ) ) ; l i l s w a p ( ( uint ∗)&buf [ hdr . ofs meshes ] , hdr . num meshes∗s i z e o f ( iqmmesh ) / s i z e o f ( uint ) ) ; l i l s w a p ( ( uint ∗)&buf [ hdr . o f s j o i n t s ] , hdr . num joints∗s i z e o f ( iqmjoint ) / s i z e o f ( uint ) ) ; const char ∗s t r = hdr . o f s t e x t ? ( char ∗)&buf [ hdr . o f s t e x t ] : ””; f l o a t ∗vpos = NULL, ∗vnorm = NULL, ∗vtan = NULL, ∗vtc = NULL; uchar ∗vindex = NULL, ∗vweight = NULL; iqmvertexarray ∗vas = ( iqmvertexarray ∗)&buf [ hdr . ofs vertexarrays ] ; l o o p i ( hdr . num vertexarrays ) { iqmvertexarray &va = vas [ i ] ; switch ( va . type ) { case IQM POSITION : i f ( va . format ! = IQM FLOAT || va . s i z e ! = 3) return f a l s e ; vpos = ( f l o a t ∗)&buf [ va . o f f s e t ] ; l i l s w a p ( vpos , 3∗hdr . num vertexes ) ; break ; case IQM NORMAL: i f ( va . format ! = IQM FLOAT || va . s i z e ! = 3) return f a l s e ; vnorm = ( f l o a t ∗)&buf [ va . o f f s e t ] ; l i l s w a p ( vnorm , 3∗hdr . num vertexes ) ; break ; case IQM TANGENT: i f ( va . format ! = IQM FLOAT || va . s i z e ! = 4) return f a l s e ; vtan = ( f l o a t ∗)&buf [ va . o f f s e t ] ; l i l s w a p ( vtan , 4∗hdr . num vertexes ) ; break ; case IQM TEXCOORD: i f ( va . format ! = IQM FLOAT || va . s i z e ! = 2) return f a l s e ; vtc = ( f l o a t ∗)&buf [ va . o f f s e t ] ; l i l s w a p ( vtc , 2∗hdr . num vertexes ) ; break ; case IQM BLENDINDEXES: i f ( va . format ! = IQM UBYTE || va . s i z e ! = 4) return f a l s e ; vindex = ( uchar ∗)&buf [ va . o f f s e t ] ; break ; case IQM BLENDWEIGHTS: i f ( va . format ! = IQM UBYTE || va . s i z e ! = 4) return f a l s e ; vweight = ( uchar ∗)&buf [ va . o f f s e t ] ; break ; } } iqmtriangle ∗t r i s = ( iqmtriangle ∗)&buf [ hdr . o f s t r i a n g l e s ] ; iqmmesh ∗imeshes = ( iqmmesh ∗)&buf [ hdr . ofs meshes ] ; iqmjoint ∗j o i n t s = ( iqmjoint ∗)&buf [ hdr . o f s j o i n t s ] ; i f ( hdr . num joints ) { i f ( skel−>numbones numbones = hdr . num joints ; skel−>bones = new boneinfo [ skel−>numbones ] ; l o o p i ( hdr . num joints ) { iqmjoint &j = j o i n t s [ i ] ; boneinfo &b = skel−>bones [ i ] ; i f ( ! b .name) b .name = newstring(& s t r [ j .name ] ) ; b . parent = j . parent ; i f ( skel−>shared = 0) b . base . mul ( skel−>bones [ b . parent ] . base , dualquat ( b . base ) ) ; ( b . invbase = b . base ) . i n v e r t ( ) ; } } } i f ( skel−>shared l i n k c h i l d r e n ( ) ; } l o o p i ( hdr . num meshes ) {

engine/iqm.h iqmmesh &im = imeshes [ i ] ; skelmesh ∗m = new skelmesh ; m−>group = t h i s ; meshes . add (m) ; m−>name = newstring(& s t r [ im .name ] ) ; m−>numverts = im . num vertexes ; i f (m−>numverts ) { m−>v e r t s = new v e r t [m−>numverts ] ; i f ( vtan ) m−>bumpverts = new bumpvert [m−>numverts ] ; } l o o p j ( im . num vertexes ) { i n t f j = j + im . f i r s t v e r t e x ; v e r t &v = m−>v e r t s [ j ] ; loopk ( 3 ) v . pos [ k ] = vpos[3∗ f j + k ] ; v . pos . y = −v . pos . y ; v . u = vtc [2∗ f j + 0 ] ; v . v = vtc [2∗ f j + 1 ] ; i f ( vnorm ) { loopk ( 3 ) v . norm [ k ] = vnorm[3∗ f j + k ] ; v . norm . y = −v . norm . y ; i f ( vtan ) { bumpvert &bv = m−>bumpverts [ j ] ; loopk ( 3 ) bv . tangent [ k ] = vtan[4∗ f j + k ] ; bv . tangent . y = −bv . tangent . y ; bv . bitangent = vtan[4∗ f j + 3 ] ; } } blendcombo c ; i n t sorted = 0; i f ( vindex && vweight ) loopk ( 4 ) sorted = c . addweight ( sorted , vweight[4∗ f j + k ] , vindex[4∗ f j + k ] ) ; c . f i n a l i z e ( sorted ) ; v . blend = m−>addblendcombo ( c ) ; } m−>numtris = im . num triangles ; i f (m−>numtris ) m−>t r i s = new t r i [m−>numtris ] ; l o o p j ( im . num triangles ) { i n t f j = j + im . f i r s t t r i a n g l e ; loopk ( 3 ) m−>t r i s [ j ] . v e r t [ k ] = t r i s [ f j ] . vertex [ k ] − im . first vertex ; } i f ( !m−>numtris || !m−>numverts ) { conoutf ( ” empty mesh in %s ” , filename ) ; meshes . removeobj (m) ; d e l e t e m; }

257 skel−>framebones = animbones ; animbones += skel−>numframes∗skel−>numbones ; skel−>numframes += a . num frames ; ushort ∗animdata = &frames [ a . f i r s t f r a m e∗hdr . num framechannels ] ; l o o p j ( a . num frames ) { dualquat ∗frame = &animbones [ j∗skel−>numbones ] ; loopk ( skel−>numbones ) { iqmpose &p = poses [ k ] ; vec pos ; quat o r i e n t ; pos . x = p . o f f s e t p o s . x ; i f ( p .mask&0x01 ) pos . x += ∗ animdata++ ∗ p . scalepos . x ; pos . y = −p . o f f s e t p o s . y ; i f ( p .mask&0x02 ) pos . y −= ∗ animdata++ ∗ p . scalepos . y ; pos . z = p . o f f s e t p o s . z ; i f ( p .mask&0x04 ) pos . z += ∗ animdata++ ∗ p . scalepos . z ; o r i e n t . x = −p . o f f s e t o r i e n t . x ; i f ( p .mask&0x08 ) o r i e n t . x −= ∗animdata++ ∗ p . s c a l e o r i e n t . x ; o r i e n t . y = p . o f f s e t o r i e n t . y ; i f ( p .mask&0x10 ) o r i e n t . y += ∗animdata++ ∗ p . s c a l e o r i e n t . y ; o r i e n t . z = −p . o f f s e t o r i e n t . z ; i f ( p .mask&0x20 ) o r i e n t . z −= ∗animdata++ ∗ p . s c a l e o r i e n t . z ; o r i e n t .w = p . o f f s e t o r i e n t .w; i f ( p .mask&0x40 ) o r i e n t .w += ∗animdata++ ∗ p . s c a l e o r i e n t .w; o r i e n t . normalize ( ) ; i f ( p .mask&0x380 ) { i f ( p .mask&0x80 ) animdata++; i f ( p .mask&0x100 ) animdata++; i f ( p .mask&0x200 ) animdata++; } frame [ k ] = dualquat ( orient , pos ) ; i f ( adjustments . inrange ( k ) ) adjustments [ k ] . adjust ( frame [ k]) ; boneinfo &b = skel−>bones [ k ] ; frame [ k ] . mul ( b . invbase ) ; i f ( b . parent >= 0) frame [ k ] . mul ( skel−>bones [ b . parent ] . base , dualquat ( frame [ k ] ) ) ; frame [ k ] . f i x a n t i p o d a l ( skel−>framebones [ k ] ) ; } } } return true ;

} bool loadiqm ( const char ∗filename , bool doloadmesh , bool doloadanim ) { stream ∗f = o p e n f i l e ( filename , ” rb ” ) ; i f ( ! f ) return f a l s e ;

} sortblendcombos ( ) ;

uchar ∗buf = NULL; iqmheader hdr ; i f ( f−>read(&hdr , s i z e o f ( hdr ) ) ! = s i z e o f ( hdr ) || memcmp( hdr . magic , ”INTERQUAKEMODEL” , s i z e o f ( hdr . magic ) ) ) goto e r r o r ; l i l s w a p (&hdr . version , ( s i z e o f ( hdr ) − s i z e o f ( hdr . magic ) ) / s i z e o f ( uint ) ) ; i f ( hdr . version ! = 2) goto e r r o r ; i f ( hdr . f i l e s i z e > (16findskelanim (name) ; i f ( sa ) continue ; sa = &skel−>addskelanim (name) ; sa−>frame = skel−>numframes ; sa−>range = a . num frames ; dualquat ∗animbones = new dualquat [ ( skel−>numframes+a . num frames )∗skel−>numbones ] ; i f ( skel−>bones ) { memcpy( animbones , skel−>framebones , skel−>numframes∗skel−> numbones∗s i z e o f ( dualquat ) ) ; d e l e t e [ ] skel−>framebones ; }

i f ( doloadmesh && ! loadiqmmeshes ( filename , hdr , buf ) ) goto e r r o r ; i f ( doloadanim && ! loadiqmanims ( filename , hdr , buf ) ) goto e r r o r ; d e l e t e [ ] buf ; delete f ; return true ; error : i f ( buf ) d e l e t e [ ] buf ; delete f ; return f a l s e ; } bool loadmesh ( const char ∗filename ) { name = newstring ( filename ) ; return loadiqm ( filename , true , f a l s e ) ; } skelanimspec ∗loadanim ( const char ∗animname ) { const char ∗sep = strchr ( animname, ’ : ’ ) ; skelanimspec ∗sa = skel−>findskelanim ( animname, sep ? ’\0 ’ : ’: ’) ; i f ( ! sa )

258

Foundations of Videogame Programming Code Repository { s t r i n g filename ; copystring ( filename , animname ) ; i f ( sep ) filename [ sep − animname ] = ’ \ 0 ’ ; i f ( loadiqm ( filename , f a l s e , true ) ) sa = skel−>findskelanim ( animname, sep ? ’\0 ’ :

bool load ( ) { i f ( loaded ) return true ; formatstring ( d i r ) ( ” packages/models/%s ” , loadname ) ; defformatstring ( cfgname ) ( ” packages/models/%s/iqm . c f g ” , loadname ) ;

’: ’) ;

} return sa ;

loading = t h i s ; i d e n t f l a g s &= ˜IDF PERSIST ; i f ( e x e c f i l e ( cfgname , f a l s e ) && parts . length ( ) ) // configured iqm , w i l l c a l l the iqm∗ commands below { i d e n t f l a g s |= IDF PERSIST ; loading = NULL; loopv ( parts ) i f ( ! parts [ i]−>meshes ) return f a l s e ; } e l s e // iqm without configuration , t r y d e f a u l t t r i s and skin { i d e n t f l a g s |= IDF PERSIST ; i f ( ! loaddefaultparts ( ) ) { loading = NULL; return f a l s e ; } loading = NULL; } scale /= 4; parts[0]−>t r a n s l a t e = t r a n s l a t e ; loopv ( parts ) { skelpart ∗p = ( skelpart ∗) parts [ i ] ; p−>endanimparts ( ) ; p−>meshes−>shared ++; } return loaded = true ;

} }; meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { iqmmeshgroup ∗group = new iqmmeshgroup ; group−>shareskeleton ( va arg ( args , char ∗) ) ; i f ( ! group−>loadmesh (name) ) { d e l e t e group ; return NULL; } return group ; } bool loaddefaultparts ( ) { skelpart &mdl = ∗new skelpart ; parts . add(&mdl ) ; mdl . model = t h i s ; mdl . index = 0; mdl . pitchscale = mdl . p i t c h o f f s e t = mdl . pitchmin = mdl . pitchmax = 0; adjustments . s e t s i z e ( 0 ) ; const char ∗fname = loadname + s t r l e n ( loadname ) ; do −−fname ; while ( fname >= loadname && ∗fname ! = ’ / ’ && ∗fname ! = ’ \ \ ’ ) ; fname++; defformatstring (meshname) ( ” packages/models/%s/%s . iqm ” , loadname , fname ) ; mdl . meshes = sharemeshes ( path (meshname) , NULL) ; i f ( ! mdl . meshes ) return f a l s e ; mdl . initanimparts ( ) ; mdl . i n i t s k i n s ( ) ; return true ; }

} }; skelcommands iqmcommands;

engine/lensflare.h s t a t i c struct f l a r e t y p e { i n t type ; /∗ f l a r e t e x index , 0 . . 5 , −1 f o r 6+random shine ∗/ f l o a t loc ; /∗ postion on axis ∗/ f l o a t scale ; /∗ texture scaling ∗/ uchar alpha ; /∗ c o l o r alpha ∗/ } flaretypes [ ] = { {2, 1.30 f , 0.04 f , 153}, // f l a r e s {3, 1.00 f , 0.10 f , 102}, {1, 0.50 f , 0.20 f , 77}, {3, 0.20 f , 0.05 f , 77}, {0, 0.00 f , 0.04 f , 77}, {5, −0.25f , 0.07 f , 127}, {5, −0.40f , 0.02 f , 153}, {5, −0.60f , 0.04 f , 102}, {5, −1.00f , 0.03 f , 51}, {−1, 1.00 f , 0.30 f , 255}, //shine − red , green , blue {−2, 1.00 f , 0.20 f , 255}, {−3, 1.00 f , 0.25 f , 255} }; struct f l a r e { vec o , center ; float size ; uchar c o l o r [ 3 ] ; bool sparkle ; };

numflares = 0; } void newflare ( vec &o , const vec ¢er , uchar r , uchar g , uchar b , f l o a t mod, f l o a t size , bool sun , bool sparkle ) { i f ( numflares >= maxflares ) return ; vec t a r g e t ; //occlusion check ( neccessary as depth t e s t i n g i s turned o f f ) i f ( ! raycubelos ( o , camera1−>o , t a r g e t ) ) return ; f l a r e &f = f l a r e s [ numflares + + ] ; f .o = o; f . center = center ; f . size = size ; f . c o l o r [ 0 ] = uchar ( r∗mod) ; f . c o l o r [ 1 ] = uchar ( g∗mod) ; f . c o l o r [ 2 ] = uchar ( b∗mod) ; f . sparkle = sparkle ; } void addflare ( vec &o , uchar r , uchar g , uchar b , bool sun , bool sparkle ) { //frustrum + fog check i f ( i s v i s i b l e s p h e r e ( 0 . 0 f , o ) > ( sun?VFC FOGGED: VFC FULL VISIBLE ) ) return ; // f i n d c l o s e s t point between camera l i n e o f s i g h t and f l a r e pos vec f l a r e d i r = vec ( o ) . sub ( camera1−>o ) ; vec center = vec ( camdir ) . mul ( f l a r e d i r . dot ( camdir ) ) . add ( camera1−>o ) ; f l o a t mod, s i z e ; i f ( sun ) // f i x e d s i z e { mod = 1 . 0 ; s i z e = f l a r e d i r . magnitude ( ) ∗ f l a r e s i z e / 100.0 f ; } else { mod = ( f l a r e c u t o f f−vec ( o ) . sub ( center ) . squaredlen ( ) ) / f l a r e c u t o f f ; i f (mod < 0.0 f ) return ; s i z e = f l a r e s i z e / 5.0 f ; } newflare ( o , center , r , g , b , mod, size , sun , sparkle ) ;

VAR( f l a r e l i g h t s , 0 , 0 , 1) ; VARP( f l a r e c u t o f f , 0 , 1000, 10000) ; VARP( f l a r e s i z e , 20, 100, 500) ; struct f l a r e r e n d e r e r : partrenderer { i n t maxflares , numflares ; unsigned i n t shinetime ; f l a r e ∗f l a r e s ; f l a r e r e n d e r e r ( const char ∗texname , i n t maxflares ) : partrenderer ( texname , 3 , PT FLARE ) , maxflares ( maxflares ) , shinetime ( 0 ) { f l a r e s = new f l a r e [ maxflares ] ; } void r e s e t ( ) {

} void makelightflares ( ) { numflares = 0; //regenerate f l a r e l i s t each frame shinetime = l a s t m i l l i s /10;

engine/lightmap.cpp

glBegin (GL QUADS) ; l o o p i ( numflares ) { f l a r e ∗f = f l a r e s + i ; vec center = f−>center ; vec axis = vec ( f−>o ) . sub ( center ) ; uchar c o l o r [ 4 ] = {f−>c o l o r [ 0 ] , f−>c o l o r [ 1 ] , f−>c o l o r [ 2 ] , 255}; l o o p j ( f−>sparkle ?12:9) { const f l a r e t y p e &f t = f l a r e t y p e s [ j ] ; vec o = vec ( axis ) . mul ( f t . l o c ) . add ( center ) ; f l o a t sz = f t . scale ∗ f−>s i z e ; i n t tex = f t . type ; i f ( f t . type < 0) //sparkles − always done l a s t { shinetime = ( shinetime + 1) % 10; tex = 6+shinetime ; c o l o r [ 0 ] = 0; c o l o r [ 1 ] = 0; c o l o r [ 2 ] = 0; c o l o r[− f t . type−1] = f−>c o l o r[− f t . type −1]; //only want a s i n g l e channel } c o l o r [ 3 ] = f t . alpha ; glColor4ubv ( c o l o r ) ; const f l o a t t s z = 0.25; // f l a r e s are aranged in 4x4 g r i d f l o a t tx = t s z ∗( tex&0x03 ) ; f l o a t ty = t s z ∗ ( ( tex>>2)&0x03 ) ; glTexCoord2f ( tx , ty+ t s z ) ; g l V e r t e x 3 f ( o . x+(−camright . x+ camup. x )∗sz , o . y+(−camright . y+camup. y )∗sz , o . z+(− camright . z+camup. z )∗sz ) ; glTexCoord2f ( tx+tsz , ty+ t s z ) ; g l V e r t e x 3 f ( o . x + ( camright . x+ camup. x )∗sz , o . y + ( camright . y+camup. y )∗sz , o . z + ( camright . z+camup. z )∗sz ) ; glTexCoord2f ( tx+tsz , ty ) ; g l V e r t e x 3 f ( o . x + ( camright . x− camup. x )∗sz , o . y + ( camright . y−camup. y )∗sz , o . z + ( camright . z−camup. z )∗sz ) ; glTexCoord2f ( tx , ty ) ; g l V e r t e x 3 f ( o . x+(−camright . x−camup . x )∗sz , o . y+(−camright . y−camup. y )∗sz , o . z+(−camright . z −camup. z )∗sz ) ; } } glEnd ( ) ; glEnable ( GL DEPTH TEST ) ; glEnable (GL FOG) ;

i f ( editmode || ! f l a r e l i g h t s ) return ; const vector &ents = e n t i t i e s : : getents ( ) ; extern const vector &checklightcache ( i n t x , i n t y ) ; const vector &l i g h t s = checklightcache ( i n t ( camera1−>o . x ) , i n t ( camera1−>o . y ) ) ; loopv ( l i g h t s ) { e n t i t y &e = ∗ents [ l i g h t s [ i ] ] ; i f ( e . type ! = ET LIGHT ) continue ; bool sun = ( e . a t t r 1 ==0) ; f l o a t radius = f l o a t ( e . a t t r 1 ) ; vec f l a r e d i r = vec ( e . o ) . sub ( camera1−>o ) ; f l o a t len = f l a r e d i r . magnitude ( ) ; i f ( ! sun && ( len > radius ) ) continue ; i f ( i s v i s i b l e s p h e r e ( 0 . 0 f , e . o ) > ( sun?VFC FOGGED: VFC FULL VISIBLE ) ) continue ; vec center = vec ( camdir ) . mul ( f l a r e d i r . dot ( camdir ) ) . add ( camera1−> o) ; f l o a t mod, s i z e ; i f ( sun ) // f i x e d s i z e { mod = 1 . 0 ; s i z e = len ∗ f l a r e s i z e / 100.0 f ; } else { mod = ( radius−len ) /radius ; s i z e = f l a r e s i z e / 5.0 f ; } newflare ( e . o , center , e . attr2 , e . attr3 , e . attr4 , mod, size , sun , sun ) ; } } i n t count ( ) { return numflares ; } bool haswork ( ) { return ( numflares != 0) && ! g l a r i n g && ! r e f l e c t i n g }

259

&& ! r e f r a c t i n g ; }

void render ( ) { glDisable (GL FOG) ; defaultshader−>set ( ) ; glDisable ( GL DEPTH TEST ) ; i f ( ! tex ) tex = textureload ( texname ) ; glBindTexture ( GL TEXTURE 2D, tex−>id ) ;

//square per round hole − use addflare ( . . ) instead p a r t i c l e ∗addpart ( const vec &o , const vec &d , i n t fade , i n t color , f l o a t size , i n t g r a v i t y = 0) { return NULL; } }; s t a t i c f l a r e r e n d e r e r f l a r e s (”packages/ p a r t i c l e s / l e n s f l a r e s . png ” , 64) ;

engine/lightmap.cpp #include ” engine . h” #define MAXLIGHTMAPTASKS 4096 #define LIGHTMAPBUFSIZE (2∗1024∗1024) struct lightmapinfo ; struct lightmaptask ; struct lightmapworker { uchar ∗buf ; i n t bufstart , bufused ; lightmapinfo ∗f i r s t l i g h t m a p , ∗lastlightmap , ∗curlightmaps ; cube ∗c ; cubeext ∗ext ; uchar ∗colorbuf ; bvec ∗raybuf ; uchar ∗ambient , ∗blur ; vec ∗colordata , ∗raydata ; i n t type , bpp , w, h , orient , r o t a t e ; VSlot ∗v s l o t ; S l o t ∗s l o t ; vector l i g h t s ; ShadowRayCache ∗shadowraycache ; BlendMapCache ∗blendmapcache ; bool needspace , doneworking ; SDL cond ∗spacecond ; SDL Thread ∗thread ; lightmapworker ( ) ; ˜ lightmapworker ( ) ; void r e s e t ( ) ; bool setupthread ( ) ;

void cleanupthread ( ) ; s t a t i c i n t work ( void ∗data ) ; }; struct lightmapinfo { lightmapinfo ∗next ; cube ∗c ; uchar ∗colorbuf ; bvec ∗raybuf ; bool packed ; i n t type , w, h , bpp , bufsize , surface , l a y e r s ; }; struct lightmaptask { ivec o ; i n t size , usefaces , progress ; cube ∗c ; cubeext ∗ext ; lightmapinfo ∗lightmaps ; lightmapworker ∗worker ; }; struct lightmapext { cube ∗c ; cubeext ∗ext ; }; s t a t i c vector lightmapworkers ; s t a t i c vector lightmaptasks [ 2 ] ; s t a t i c vector lightmapexts ;

260

Foundations of Videogame Programming Code Repository

s t a t i c i n t packidx = 0 , a l l o c i d x = 0; s t a t i c SDL mutex ∗l i g h t l o c k = NULL, ∗tasklock = NULL; s t a t i c SDL cond ∗fullcond = NULL, ∗emptycond = NULL;

{ i n t d s t o f f s e t = 0; i f ( ! c . ext ) newcubeext ( c , numsrcverts , true ) ; else { i n t numbefore = 0 , b e f o r e o f f s e t = 0; loopi ( orient ) { surfaceinfo &surf = c . ext−>surfaces [ i ] ; i n t numverts = surf . t o t a l v e r t s ( ) ; i f ( ! numverts ) continue ; numbefore += numverts ; b e f o r e o f f s e t = surf . v e r t s + numverts ; } i n t numafter = 0 , a f t e r o f f s e t = c . ext−>maxverts ; f o r ( i n t i = 5; i > o r i e n t ; i−−) { surfaceinfo &surf = c . ext−>surfaces [ i ] ; i n t numverts = surf . t o t a l v e r t s ( ) ; i f ( ! numverts ) continue ; numafter += numverts ; a f t e r o f f s e t = surf . v e r t s ; } i f ( a f t e r o f f s e t − b e f o r e o f f s e t >= numsrcverts ) d s t o f f s e t = beforeoffset ; else { cubeext ∗ext = c . ext ; i f ( numbefore + numsrcverts + numafter > c . ext−>maxverts ) { ext = growcubeext ( c . ext , numbefore + numsrcverts + numafter ) ; memcpy( ext−>surfaces , c . ext−>surfaces , s i z e o f ( ext−>surfaces ) ) ; } i n t o f f s e t = 0; i f ( numbefore == b e f o r e o f f s e t ) { i f ( numbefore && c . ext ! = ext ) memcpy( ext−>v e r t s ( ) , c . ext−> v e r t s ( ) , numbefore∗s i z e o f ( v e r t i n f o ) ) ; o f f s e t = numbefore ; } else loopi ( orient ) { surfaceinfo &surf = ext−>surfaces [ i ] ; i n t numverts = surf . t o t a l v e r t s ( ) ; i f ( ! numverts ) continue ; memmove( ext−>v e r t s ( ) + o f f s e t , c . ext−>v e r t s ( ) + surf . verts , numverts∗s i z e o f ( v e r t i n f o ) ) ; surf . v e r t s = o f f s e t ; o f f s e t += numverts ; } dstoffset = offset ; o f f s e t += numsrcverts ; i f ( numafter && o f f s e t > a f t e r o f f s e t ) { o f f s e t += numafter ; f o r ( i n t i = 5; i > o r i e n t ; i−−) { surfaceinfo &surf = ext−>surfaces [ i ] ; i n t numverts = surf . t o t a l v e r t s ( ) ; i f ( ! numverts ) continue ; o f f s e t −= numverts ; memmove( ext−>v e r t s ( ) + o f f s e t , c . ext−>v e r t s ( ) + surf . verts , numverts∗s i z e o f ( v e r t i n f o ) ) ; surf . v e r t s = o f f s e t ; } } i f ( c . ext ! = ext ) setcubeext ( c , ext ) ; } } surfaceinfo &dst = c . ext−>surfaces [ o r i e n t ] ; dst = src ; dst . v e r t s = d s t o f f s e t ; i f ( s r c v e r t s ) memcpy( c . ext−>v e r t s ( ) + d s t o f f s e t , srcverts , numsrcverts∗ sizeof ( vertinfo ) ) ;

i n t lightmapping = 0; vector lightmaps ; VARR( l i g h t p r e c i s i o n , 1 , 32, 1024) ; VARR( l i g h t e r r o r , 1 , 8 , 16) ; VARR( bumperror , 1 , 3 , 16) ; VARR( l i g h t l o d , 0 , 0 , 10) ; bvec ambientcolor (0x19 , 0x19 , 0x19 ) , s k y l i g h t c o l o r ( 0 , 0 , 0) ; HVARFR( ambient , 1 , 0x191919 , 0xFFFFFF, { i f ( ambient surfaces , surfs , s i z e o f ( c . ext−>surfaces ) ) ; memcpy( c . ext−>v e r t s ( ) , verts , numverts∗s i z e o f ( v e r t i n f o ) ) ; } void setsurface ( cube &c , i n t orient , const surfaceinfo &src , const v e r t i n f o ∗srcverts , i n t numsrcverts )

bool c a l c l i g h t c a n c e l e d = f a l s e ; v o l a t i l e bool c h e c k c a l c l i g h t p r o g r e s s = f a l s e ; void c h e c k c a l c l i g h t c a n c e l e d ( ) { i f ( interceptkey (SDLK ESCAPE) ) {

engine/lightmap.cpp

261

src += bpp ∗ tw ; } ++lightmaps ; lumels += tw ∗ th ;

c a l c l i g h t c a n c e l e d = true ; loopv ( lightmapworkers ) lightmapworkers [ i]−>doneworking = true ; } i f ( ! calclight canceled ) check calclight progress = false ; }

}

void show calclight progress ( ) { f l o a t bar1 = f l o a t ( progress ) / f l o a t ( allocnodes ) ; defformatstring ( text1 ) (”%d%% using %d textures ” , i n t ( bar1 ∗ 100) , lightmaps . length ( ) ) ;

s t a t i c void i n s e r t u n l i t ( i n t i ) { LightMap &l = lightmaps [ i ] ; i f ( ( l . type&LM TYPE ) == LM BUMPMAP1) { l . u n l i t x = l . u n l i t y = −1; return ; } ushort x , y ; uchar u n l i t [ 4 ] = { ambientcolor [ 0 ] , ambientcolor [ 1 ] , ambientcolor [ 2 ] , 255 }; i f ( l . i n s e r t ( x , y , unlit , 1 , 1) ) { i f ( ( l . type&LM TYPE ) == LM BUMPMAP0) { bvec f r o n t (128 , 128, 255) ; ASSERT( lightmaps [ i + 1 ] . i n s e r t ( x , y , f r o n t . v , 1 , 1) ) ; } l . unlitx = x ; l . unlity = y ; } }

i f (LM PACKW = 0 && ! ( progresstexticks++ % 4) ) { i f ( tasklock ) SDL LockMutex ( tasklock ) ; LightMap &lm = lightmaps [ progresslightmap ] ; uchar ∗data = lm . data ; i n t bpp = lm . bpp ; i f ( tasklock ) SDL UnlockMutex ( tasklock ) ; glBindTexture ( GL TEXTURE 2D, progresstex ) ; g l P i x e l S t o r e i (GL UNPACK ALIGNMENT, t e x a l i g n ( data , LM PACKW, bpp ) ) ; glTexSubImage2D ( GL TEXTURE 2D, 0 , 0 , 0 , LM PACKW, LM PACKH, bpp > 3 ? GL RGBA : GL RGB, GL UNSIGNED BYTE, data ) ; } renderprogress ( bar1 , text1 , progresstexticks ? progresstex : 0) ; } #define CHECK PROGRESS LOCKED( e x i t , before , a f t e r ) CHECK CALCLIGHT PROGRESS LOCKED( e x i t , show calclight progress , before , a f t e r ) #define CHECK PROGRESS( e x i t ) CHECK PROGRESS LOCKED( e x i t , , ) bool PackNode : : i n s e r t ( ushort &tx , ushort { i f ( ( a v a i l a b l e < tw && a v a i l a b l e < th ) return f a l s e ; i f ( child1 ) { bool inserted = child1−>i n s e r t ( tx , child2−>i n s e r t ( tx , ty , a v a i l a b l e = max( child1−>a v a i l a b l e , i f ( ! available ) clear ( ) ; return inserted ; } i f (w == tw && h == th ) { a v a i l a b l e = 0; tx = x ; ty = y ; return true ; } i f (w − tw > { child1 = child2 = } else { child1 = child2 = }

&ty , ushort tw , ushort th ) || w < tw || h < th )

ty , tw , th ) || tw , th ) ; child2−>a v a i l a b l e ) ;

struct l a y o u t i n f o { ushort x , y , lmid ; uchar w, h ; }; s t a t i c void insertlightmap ( lightmapinfo &l i , l a y o u t i n f o &s i ) { loopv ( lightmaps ) { i f ( lightmaps [ i ] . type == l i . type && lightmaps [ i ] . i n s e r t ( s i . x , s i . y , l i . colorbuf , s i .w, s i . h ) ) { s i . lmid = i + LMID RESERVED; i f ( ( l i . type&LM TYPE ) == LM BUMPMAP0) ASSERT( lightmaps [ i + 1 ] . i n s e r t ( s i . x , s i . y , ( uchar ∗) l i . raybuf , s i .w, s i . h ) ) ; return ; } } progresslightmap = lightmaps . length ( ) ; s i . lmid = lightmaps . length ( ) + LMID RESERVED; LightMap &l = lightmaps . add ( ) ; l . type = l i . type ; l . bpp = l i . bpp ; l . data = new uchar [ l i . bpp∗LM PACKW∗LM PACKH ] ; memset ( l . data , 0 , l i . bpp∗LM PACKW∗LM PACKH) ; ASSERT( l . i n s e r t ( s i . x , s i . y , l i . colorbuf , s i .w, s i . h ) ) ; i f ( ( l i . type&LM TYPE ) == LM BUMPMAP0) { LightMap &r = lightmaps . add ( ) ; r . type = LM BUMPMAP1 | ( l i . type&˜LM TYPE ) ; r . bpp = 3; r . data = new uchar[3∗LM PACKW∗LM PACKH ] ; memset ( r . data , 0 , 3∗LM PACKW∗LM PACKH) ; ASSERT( r . i n s e r t ( s i . x , s i . y , ( uchar ∗) l i . raybuf , s i .w, s i . h ) ) ; }

h − th ) new PackNode ( x , y , tw , h ) ; new PackNode ( x + tw , y , w − tw , h ) ;

new PackNode ( x , y , w, th ) ; new PackNode ( x , y + th , w, h − th ) ;

bool inserted = child1−>i n s e r t ( tx , ty , tw , th ) ; a v a i l a b l e = max( child1−>a v a i l a b l e , child2−>a v a i l a b l e ) ; return inserted ; } bool LightMap : : i n s e r t ( ushort &tx , ushort &ty , uchar ∗src , ushort tw , ushort th )

} s t a t i c void copylightmap ( lightmapinfo &l i , l a y o u t i n f o &s i ) { lightmaps [ s i . lmid−LMID RESERVED ] . copy ( s i . x , s i . y , l i . colorbuf , s i .w, si .h) ; i f ( ( l i . type&LM TYPE ) ==LM BUMPMAP0 && lightmaps . inrange ( s i . lmid+1− LMID RESERVED) ) lightmaps [ s i . lmid+1−LMID RESERVED ] . copy ( s i . x , s i . y , ( uchar ∗) l i . raybuf , s i .w, s i . h ) ; }

{ i f ( ( type&LM TYPE ) ! = LM BUMPMAP1 && ! packroot . i n s e r t ( tx , ty , tw , th ) ) return f a l s e ; copy ( tx , ty , src , tw , th ) ; return true ; } void LightMap : : copy ( ushort tx , ushort ty , uchar ∗src , ushort tw , ushort th ) { uchar ∗dst = data + bpp ∗ tx + ty ∗ bpp ∗ LM PACKW; l o o p i ( th ) { memcpy( dst , src , bpp ∗ tw ) ; dst += bpp ∗ LM PACKW;

s t a t i c i n l i n e bool htcmp ( const lightmapinfo &k , const l a y o u t i n f o &v ) { i n t kw = k .w, kh = k . h ; i f (kw ! = v .w || kh ! = v . h ) return f a l s e ; LightMap &vlm = lightmaps [ v . lmid − LMID RESERVED ] ; i n t ktype = k . type ; i f ( ktype ! = vlm . type ) return f a l s e ; i n t kbpp = k . bpp ; const uchar ∗kcolor = k . colorbuf , ∗v c o l o r = vlm . data + kbpp∗(v . x + v . y ∗LM PACKW) ; l o o p i ( kh ) { i f (memcmp( kcolor , vcolor , kbpp∗kw) ) return f a l s e ; kcolor += kbpp∗kw; v c o l o r += kbpp∗LM PACKW;

262

Foundations of Videogame Programming Code Repository

} i f ( ( ktype&LM TYPE ) ! = LM BUMPMAP0) return true ; const bvec ∗kdir = k . raybuf , ∗v d i r = ( const bvec ∗) lightmaps [ v . lmid+1 − LMID RESERVED ] . data ; l o o p i ( kh ) { i f (memcmp( kdir , vdir , kw∗s i z e o f ( bvec ) ) ) return f a l s e ; kdir += kw; v d i r += LM PACKW; } return true ;

glTexSubImage2D ( GL TEXTURE 2D, 0 , lm . o f f s e t x + surface . x , lm . o f f s e t y + surface . y , surface .w, surface . h , lm . type&LM ALPHA ? GL RGBA : GL RGB, GL UNSIGNED BYTE, &lm . data [ ( surface . y∗LM PACKW + surface . x )∗lm . bpp ] ) ; i f ( renderpath ! =R FIXEDFUNCTION && ( lm . type&LM TYPE ) ==LM BUMPMAP0 && lightmaps . inrange ( surface . lmid+1−LMID RESERVED) ) { LightMap &lm2 = lightmaps [ surface . lmid+1−LMID RESERVED ] ; glBindTexture ( GL TEXTURE 2D, lightmaptexs [ lm2 . tex ] . id ) ; glTexSubImage2D ( GL TEXTURE 2D, 0 , lm2 . o f f s e t x + surface . x , lm2 . o f f s e t y + surface . y , surface .w, surface . h , GL RGB, GL UNSIGNED BYTE, &lm2 . data [ ( surface . y∗LM PACKW + surface . x ) ∗3]) ; }

} s t a t i c i n l i n e uint hthash ( const lightmapinfo &k ) { i n t kw = k .w, kh = k . h , kbpp = k . bpp ; uint hash = kw + ( kh 1 ? RAY ALPHAPOLY : 0) | ( s k y t e x t u r e l i g h t ? RAY SKIPSKY : 0) ) > 1e15f ) ) { float intensity ; switch (w−>type&LM TYPE ) { case LM BUMPMAP0: i n t e n s i t y = 1; avgray . add ( s u n l i g h t d i r ) ; break ; default : i n t e n s i t y = angle ;

engine/lightmap.cpp {

break ; } r += i n t e n s i t y ∗ ( sunlightcolor . x∗sunlightscale ) ; g += i n t e n s i t y ∗ ( sunlightcolor . y∗sunlightscale ) ; b += i n t e n s i t y ∗ ( sunlightcolor . z∗sunlightscale ) ; } } switch (w−>type&LM TYPE ) { case LM BUMPMAP0: i f ( avgray . i s z e r o ( ) ) break ; // transform to tangent space extern vec orientation tangent [ 6 ] [ 3 ] ; extern vec orientation binormal [ 6 ] [ 3 ] ; vec S ( orientation tangent [w−>r o t a t e ] [ dimension (w−>o r i e n t ) ] ) , T ( orientation binormal [w−>r o t a t e ] [ dimension (w−>o r i e n t ) ] ) ; normal . orthonormalize ( S , T ) ; avgray . normalize ( ) ; w−>raydata [ y∗w−>w+x ] . add ( vec ( S . dot ( avgray ) /S . magnitude ( ) , T . dot ( avgray ) /T . magnitude ( ) , normal . dot ( avgray ) ) ) ; break ; } sample . x = min(255.0 f , max( r , f l o a t ( ambientcolor [ 0 ] ) ) ) ; sample . y = min(255.0 f , max( g , f l o a t ( ambientcolor [ 1 ] ) ) ) ; sample . z = min(255.0 f , max( b , f l o a t ( ambientcolor [ 2 ] ) ) ) ; return lightused ;

263

i f ( normal . dot ( rays [ i ] )>=0 && shadowray (w−>shadowraycache , vec ( rays [ i ] ) . mul ( tolerance ) . add ( o ) , rays [ i ] , 1e16f , f l a g s , t )>1e15f ) h i t ++; } else loopi (17) { i f ( normal . dot ( rays [ i ] )>=0 && shadowray ( vec ( rays [ i ] ) . mul ( tolerance ) . add ( o ) , rays [ i ] , 1e16f , f l a g s , t )>1e15f ) h i t ++; } loopk ( 3 ) s k y l i g h t [ k ] = uchar ( ambientcolor [ k ] + (max( s k y l i g h t c o l o r [ k ] , ambientcolor [ k ] ) − ambientcolor [ k ] ) ∗h i t /17.0 f ) ; } s t a t i c i n l i n e bool hasskylight ( ) { return s k y l i g h t c o l o r [0]>ambientcolor [ 0 ] || s k y l i g h t c o l o r [1]> ambientcolor [ 1 ] || s k y l i g h t c o l o r [2]>ambientcolor [ 2 ] ; } VARR( blurlms , 0 , 0 , 2) ; VARR( blurskylight , 0 , 0 , 2) ; s t a t i c i n l i n e void generatealpha ( lightmapworker ∗w, f l o a t tolerance , const vec &pos , uchar &alpha ) {

}

alpha = lookupblendmap (w−>blendmapcache , pos ) ; i f (w−>s l o t−>layermask ) { s t a t i c const i n t sdim [ ] = { 1 , 0 , 0 }, tdim [ ] = { 2 , 2 , 1 }; i n t dim = dimension (w−>o r i e n t ) ; f l o a t k = 8.0 f /w−>v s l o t−>scale , s = ( pos [ sdim [ dim ] ] ∗ k − w−>v s l o t−>x o f f s e t ) / w−>s l o t−> layermaskscale , t = ( pos [ tdim [ dim ] ] ∗ ( dim v s l o t−>y o f f s e t ) / w−>s l o t−>layermaskscale ; i f ( ( w−>r o t a t e &5)==1) swap ( s , t ) ; i f (w−>rotate>=2 && w−>rotate rotate>=1 && w−>rotate r o t a t e ==5) t = −t ; const ImageData &mask = ∗w−>s l o t−>layermask ; i n t mx = i n t ( f l o o r ( s ) )%mask.w, my = i n t ( f l o o r ( t ) )%mask. h ; i f (mx < 0) mx += mask.w; i f (my < 0) my += mask. h ; uchar maskval = mask. data [mask. bpp∗(mx + 1) − 1 + mask. pitch∗my ] ; switch (w−>s l o t−>layermaskmode ) { case 2: alpha = min ( alpha , maskval ) ; break ; case 3: alpha = max( alpha , maskval ) ; break ; case 4: alpha = min ( alpha , uchar (0xFF − maskval ) ) ; break ; case 5: alpha = max( alpha , uchar (0xFF − maskval ) ) ; break ; d e f a u l t : alpha = maskval ; break ; } }

s t a t i c bool lumelsample ( const vec &sample , i n t aasample , i n t s t r i d e ) { i f ( sample . x >= i n t ( ambientcolor [ 0 ] ) +1 || sample . y >= i n t ( ambientcolor [ 1 ] ) +1 || sample . z >= i n t ( ambientcolor [ 2 ] ) +1) return true ; #define NCHECK( n ) \ i f ( ( n ) . x >= i n t ( ambientcolor [ 0 ] ) +1 || ( n ) . y >= i n t ( ambientcolor [ 1 ] ) +1 || ( n ) . z >= i n t ( ambientcolor [ 2 ] ) +1) \ return true ; const vec ∗n = &sample − s t r i d e − aasample ; NCHECK( n [ 0 ] ) ; NCHECK( n [ aasample ] ) ; NCHECK( n[2∗aasample ] ) ; n += s t r i d e ; NCHECK( n [ 0 ] ) ; NCHECK( n[2∗aasample ] ) ; n += s t r i d e ; NCHECK( n [ 0 ] ) ; NCHECK( n [ aasample ] ) ; NCHECK( n[2∗aasample ] ) ; return f a l s e ; } s t a t i c void c a l c s k y l i g h t ( lightmapworker ∗w, const vec &o , const vec & normal , f l o a t tolerance , uchar ∗skylight , i n t f l a g s = RAY ALPHAPOLY, e x t e n t i t y ∗t = NULL) { s t a t i c const vec rays [ 1 7 ] = { vec ( cosf (21∗RAD)∗cosf (50∗RAD) , s i n f (21∗RAD)∗cosf (50∗RAD) , s i n f (50∗ RAD) ) , vec ( cosf (111∗RAD)∗cosf (50∗RAD) , s i n f (111∗RAD)∗cosf (50∗RAD) , s i n f (50∗RAD) ) , vec ( cosf (201∗RAD)∗cosf (50∗RAD) , s i n f (201∗RAD)∗cosf (50∗RAD) , s i n f (50∗RAD) ) , vec ( cosf (291∗RAD)∗cosf (50∗RAD) , s i n f (291∗RAD)∗cosf (50∗RAD) , s i n f (50∗RAD) ) , vec ( cosf (66∗RAD)∗cosf (70∗RAD) , s i n f (66∗RAD)∗cosf (70∗RAD) , s i n f (70∗ RAD) ) , vec ( cosf (156∗RAD)∗cosf (70∗RAD) , s i n f (156∗RAD)∗cosf (70∗RAD) , s i n f (70∗RAD) ) , vec ( cosf (246∗RAD)∗cosf (70∗RAD) , s i n f (246∗RAD)∗cosf (70∗RAD) , s i n f (70∗RAD) ) , vec ( cosf (336∗RAD)∗cosf (70∗RAD) , s i n f (336∗RAD)∗cosf (70∗RAD) , s i n f (70∗RAD) ) , vec ( 0 , 0 , 1) , vec ( cosf (43∗RAD)∗cosf (60∗RAD) , s i n f (43∗RAD)∗cosf (60∗RAD) , s i n f (60∗ RAD) ) , vec ( cosf (133∗RAD)∗cosf (60∗RAD) , s i n f (133∗RAD)∗cosf (60∗RAD) , s i n f (60∗RAD) ) , vec ( cosf (223∗RAD)∗cosf (60∗RAD) , s i n f (223∗RAD)∗cosf (60∗RAD) , s i n f (60∗RAD) ) , vec ( cosf (313∗RAD)∗cosf (60∗RAD) , s i n f (313∗RAD)∗cosf (60∗RAD) , s i n f (60∗RAD) ) , vec ( cosf (88∗RAD)∗cosf (80∗RAD) , s i n f (88∗RAD)∗cosf (80∗RAD) , s i n f (80∗ RAD) ) , vec ( cosf (178∗RAD)∗cosf (80∗RAD) , s i n f (178∗RAD)∗cosf (80∗RAD) , s i n f (80∗RAD) ) , vec ( cosf (268∗RAD)∗cosf (80∗RAD) , s i n f (268∗RAD)∗cosf (80∗RAD) , s i n f (80∗RAD) ) , vec ( cosf (358∗RAD)∗cosf (80∗RAD) , s i n f (358∗RAD)∗cosf (80∗RAD) , s i n f (80∗RAD) ) , }; f l a g s |= RAY SHADOW; i f ( s k y t e x t u r e l i g h t ) f l a g s |= RAY SKIPSKY ; i n t h i t = 0; i f (w) l o o p i ( 1 7 )

} VAR( edgetolerance , 1 , 4 , 64) ; VAR( adaptivesample , 0 , 2 , 2) ; enum { NO SURFACE = 0 , SURFACE AMBIENT BOTTOM, SURFACE AMBIENT TOP, SURFACE LIGHTMAP BOTTOM, SURFACE LIGHTMAP TOP, SURFACE LIGHTMAP BLEND }; #define SURFACE AMBIENT SURFACE AMBIENT BOTTOM #define SURFACE LIGHTMAP SURFACE LIGHTMAP BOTTOM s t a t i c bool generatelightmap ( lightmapworker ∗w, f l o a t lpu , const l e r p v e r t ∗lv , i n t numv, vec origin1 , const vec &xstep1 , const vec &ystep1 , vec origin2 , const vec &xstep2 , const vec &ystep2 , f l o a t side0 , f l o a t sidestep ) { s t a t i c const f l o a t aacoords [ 8 ] [ 2 ] = { {0.0 f , 0.0 f }, {−0.5f , −0.5f }, {0.0 f , −0.5f }, {−0.5f , 0.0 f }, {0.3 f , −0.6f }, {0.6 f , 0.3 f }, {−0.3f , 0.6 f }, {−0.6f , −0.3f }, }; f l o a t tolerance = 0.5 / lpu ; uint lightmask = 0 , lightused = 0; vec o f f s e t s 1 [ 8 ] , o f f s e t s 2 [ 8 ] ; loopi ( 8 ) {

264

Foundations of Videogame Programming Code Repository

o f f s e t s 1 [ i ] = vec ( xstep1 ) . mul ( aacoords [ i ] [ 0 ] ) . add ( vec ( ystep1 ) . mul ( aacoords [ i ] [ 1 ] ) ) ; o f f s e t s 2 [ i ] = vec ( xstep2 ) . mul ( aacoords [ i ] [ 0 ] ) . add ( vec ( ystep2 ) . mul ( aacoords [ i ] [ 1 ] ) ) ;

center . div ( 5 ) ; } } } i f ( aasample > 1) { vec u = w−>w < sidex ? vec ( xstep1 ) . mul (w−>w) . add ( vec ( ystep1 ) . mul ( y ) ) . add ( origin1 ) : vec ( xstep2 ) . mul (w−>w) . add ( vec ( ystep2 ) . mul ( y ) ) . add ( origin2 ) ; const vec ∗o f f s e t s = w−>w < sidex ? o f f s e t s 1 : o f f s e t s 2 ; vec n = vec ( normal ) . normalize ( ) ; generatelumel (w, edgetolerance ∗ tolerance , lightmask , w−>l i g h t s , vec ( u ) . add ( o f f s e t s [ 1 ] ) , n , sample [ 1 ] , w−>w−1, y ) ; i f ( aasample > 2) generatelumel (w, edgetolerance ∗ tolerance , lightmask , w−> l i g h t s , vec ( u ) . add ( o f f s e t s [ 3 ] ) , n , sample [ 3 ] , w−>w−1, y) ; } sample += aasample ;

} i f ( ( w−>type&LM TYPE ) == LM BUMPMAP0) memset (w−>raydata , 0 , (LM MAXW + 4) ∗(LM MAXH + 4)∗s i z e o f ( vec ) ) ; origin1 . sub ( vec ( ystep1 ) . add ( xstep1 ) . mul ( blurlms ) ) ; origin2 . sub ( vec ( ystep2 ) . add ( xstep2 ) . mul ( blurlms ) ) ; i n t aasample = min(1 w+1) ; vec ∗sample = w−>colordata ; uchar ∗s k y l i g h t = w−>ambient ; lerpbounds s t a r t , end ; initlerpbounds(−blurlms , −blurlms , lv , numv, s t a r t , end ) ; f l o a t sidex = side0 + blurlms∗sidestep ; f o r ( i n t y = 0; y < w−>h ; ++y , sidex += sidestep ) { vec normal , nstep ; lerpnormal(−blurlms , y − blurlms , lv , numv, s t a r t , end , normal , nstep ) ; f o r ( i n t x = 0; x < w−>w; ++x , normal . add ( nstep ) , s k y l i g h t += w−>bpp ) { #define EDGE TOLERANCE( x , y ) \ ( x < blurlms \ || x+1 > w−>w − blurlms \ || y < blurlms \ || y+1 > w−>h − blurlms \ ? edgetolerance : 1) f l o a t t = EDGE TOLERANCE( x , y ) ∗ tolerance ; vec u = x < sidex ? vec ( xstep1 ) . mul ( x ) . add ( vec ( ystep1 ) . mul ( y ) ) . add ( origin1 ) : vec ( xstep2 ) . mul ( x ) . add ( vec ( ystep2 ) . mul ( y ) ) . add ( origin2 ) ; lightused |= generatelumel (w, t , 0 , w−>l i g h t s , u, vec ( normal ) . normalize ( ) , ∗sample , x , y ) ; i f ( hasskylight ( ) ) { i f ( ( w−>type&LM TYPE ) ==LM BUMPMAP0 || ! adaptivesample || sample−>xyz 1 ? RAY ALPHAPOLY : 0) ; e l s e loopk ( 3 ) s k y l i g h t [ k ] = max( s k y l i g h t c o l o r [ k ] , ambientcolor [ k ] ) ; } e l s e loopk ( 3 ) s k y l i g h t [ k ] = ambientcolor [ k ] ; i f (w−>type&LM ALPHA) generatealpha (w, t , u, s k y l i g h t [ 3 ] ) ; sample += aasample ; } sample += aasample ; } i f ( adaptivesample > 1 && min (w−>w, w−>h ) >= 2) lightmask = ˜ lightused ; sample = w−>colordata ; initlerpbounds(−blurlms , −blurlms , lv , numv, s t a r t , end ) ; sidex = side0 + blurlms∗sidestep ; f o r ( i n t y = 0; y < w−>h ; ++y , sidex += sidestep ) { vec normal , nstep ; lerpnormal(−blurlms , y − blurlms , lv , numv, s t a r t , end , normal , nstep ) ; f o r ( i n t x = 0; x < w−>w; ++x , normal . add ( nstep ) ) { vec ¢er = ∗sample++; i f ( adaptivesample && x > 0 && x+1 < w−>w && y > 0 && y+1 < w−>h && ! lumelsample ( center , aasample , s t r i d e ) ) l o o p i ( aasample−1) ∗sample++ = center ; else { #define AA EDGE TOLERANCE( x , y , i ) EDGE TOLERANCE( x + aacoords [ i ] [ 0 ] , y + aacoords [ i ] [ 1 ] ) vec u = x < sidex ? vec ( xstep1 ) . mul ( x ) . add ( vec ( ystep1 ) . mul ( y ) ) . add ( origin1 ) : vec ( xstep2 ) . mul ( x ) . add ( vec ( ystep2 ) . mul ( y ) ) . add ( origin2 ) ; const vec ∗o f f s e t s = x < sidex ? o f f s e t s 1 : o f f s e t s 2 ; vec n = vec ( normal ) . normalize ( ) ; l o o p i ( aasample−1) generatelumel (w, AA EDGE TOLERANCE( x , y , i +1) ∗ tolerance , lightmask , w−>l i g h t s , vec ( u ) . add ( o f f s e t s [ i + 1 ] ) , n , ∗sample++ , x , y ) ; i f ( lmaa == 3) { loopi ( 4 ) { vec s ; generatelumel (w, AA EDGE TOLERANCE( x , y , i +4) ∗ tolerance , lightmask , w−>l i g h t s , vec ( u ) . add ( o f f s e t s [ i +4]) , n, s , x , y ) ; center . add ( s ) ; }

} i f ( aasample > 1) { vec normal , nstep ; lerpnormal(−blurlms , w−>h − blurlms , lv , numv, s t a r t , end , normal , nstep ) ; f o r ( i n t x = 0; x w; ++x , normal . add ( nstep ) ) { vec u = x < sidex ? vec ( xstep1 ) . mul ( x ) . add ( vec ( ystep1 ) . mul (w−>h ) ) . add ( origin1 ) : vec ( xstep2 ) . mul ( x ) . add ( vec ( ystep2 ) . mul (w −>h ) ) . add ( origin2 ) ; const vec ∗o f f s e t s = x < sidex ? o f f s e t s 1 : o f f s e t s 2 ; vec n = vec ( normal ) . normalize ( ) ; generatelumel (w, edgetolerance ∗ tolerance , lightmask , w−>l i g h t s , vec ( u ) . add ( o f f s e t s [ 1 ] ) , n , sample [ 1 ] , min ( x , w−>w−1) , w −>h−1); i f ( aasample > 2) generatelumel (w, edgetolerance ∗ tolerance , lightmask , w−> l i g h t s , vec ( u ) . add ( o f f s e t s [ 2 ] ) , n , sample [ 2 ] , min ( x , w −>w−1) , w−>h−1); sample += aasample ; } } return true ; } s t a t i c i n t finishlightmap ( lightmapworker ∗w) { i f ( hasskylight ( ) && b l u r s k y l i g h t && (w−>w>1 || w−>h>1)) { blurtexture ( blurskylight , w−>bpp , w−>w, w−>h , w−>blur , w−>ambient ) ; swap (w−>blur , w−>ambient ) ; } vec ∗sample = w−>colordata ; i n t aasample = min(1 w+1) ; f l o a t weight = 1.0 f / ( 1 . 0 f + 4.0 f∗lmaa ) , cweight = weight ∗ ( lmaa == 3 ? 5.0 f : 1.0 f ) ; uchar ∗s k y l i g h t = w−>ambient ; vec ∗ray = w−>raydata ; uchar ∗d s t c o l o r = blurlms && (w−>w > 1 || w−>h > 1) ? w−>blur : w−> colorbuf ; uchar mincolor [ 4 ] = { 255, 255, 255, 255 }, maxcolor [ 4 ] = { 0 , 0 , 0 , 0 }; bvec ∗dstray = blurlms && (w−>w > 1 || w−>h > 1) ? ( bvec ∗)w−>raydata : w−>raybuf ; bvec minray(255 , 255, 255) , maxray ( 0 , 0 , 0) ; loop ( y , w−>h ) { loop ( x , w−>w) { vec l ( 0 , 0 , 0) ; const vec ¢er = ∗sample++; l o o p i ( aasample−1) l . add(∗sample++) ; i f ( aasample > 1) { l . add ( sample [ 1 ] ) ; i f ( aasample > 2) l . add ( sample [ 3 ] ) ; } vec ∗next = sample + s t r i d e − aasample ; i f ( aasample > 1) { l . add ( next [ 1 ] ) ; i f ( aasample > 2) l . add ( next [ 2 ] ) ; l . add ( next [ aasample + 1 ] ) ; } i n t r = i n t ( center . x∗cweight + l . x∗weight ) , g = i n t ( center . y∗cweight + l . y∗weight ) , b = i n t ( center . z∗cweight + l . z∗weight ) , ar = s k y l i g h t [ 0 ] , ag = s k y l i g h t [ 1 ] , ab = s k y l i g h t [ 2 ] ; d s t c o l o r [ 0 ] = max( ar , r ) ; d s t c o l o r [ 1 ] = max( ag , g ) ; d s t c o l o r [ 2 ] = max( ab , b ) ;

engine/lightmap.cpp loopk ( 3 ) { mincolor [ k ] = min ( mincolor [ k ] , d s t c o l o r [ k ] ) ; maxcolor [ k ] = max( maxcolor [ k ] , d s t c o l o r [ k ] ) ; } i f (w−>type&LM ALPHA) { dstcolor [ 3 ] = skylight [ 3 ] ; mincolor [ 3 ] = min ( mincolor [ 3 ] , d s t c o l o r [ 3 ] ) ; maxcolor [ 3 ] = max( maxcolor [ 3 ] , d s t c o l o r [ 3 ] ) ; } i f ( ( w−>type&LM TYPE ) == LM BUMPMAP0) { i f ( ray−>i s z e r o ( ) ) dstray [ 0 ] = bvec (128 , 128, 255) ; else { // bias the normals towards the amount o f ambient/ s k y l i g h t in the lumel // t h i s i s necessary to prevent the l i g h t values in shaders from dropping too f a r below the s k y l i g h t ( to the ambient ) i f N. L i s small ray−>normalize ( ) ; i n t l = max( r , max( g , b ) ) , a = max( ar , max( ag , ab ) ) ; ray−>mul (max( l−a , 0) ) ; ray−>z += a ; dstray [ 0 ] = bvec ( ray−>normalize ( ) ) ; } loopk ( 3 ) { minray [ k ] = min ( minray [ k ] , dstray [ 0 ] [ k ] ) ; maxray [ k ] = max( maxray [ k ] , dstray [ 0 ] [ k ] ) ; } ray ++; dstray ++; } d s t c o l o r += w−>bpp ; s k y l i g h t += w−>bpp ; } sample += aasample ; } i f ( i n t ( maxcolor [ 0 ] ) − i n t ( mincolor [ 0 ] ) w = w−>w = 1; w−>lastlightmap−>h = w−>h = 1; } } i f ( blurlms && (w−>w>1 || w−>h>1)) { blurtexture ( blurlms , w−>bpp , w−>w, w−>h , w−>colorbuf , w−>blur , blurlms ) ; i f ( ( w−>type&LM TYPE ) == LM BUMPMAP0) blurnormals ( blurlms , w−>w, w−> h , w−>raybuf , ( const bvec ∗)w−>raydata , blurlms ) ; w−>lastlightmap−>w = (w−>w −= 2∗blurlms ) ; w−>lastlightmap−>h = (w−>h −= 2∗blurlms ) ; } i f ( mincolor [3]==255) return SURFACE LIGHTMAP TOP; e l s e i f ( maxcolor [ 3 ] = = 0 ) return SURFACE LIGHTMAP BOTTOM; e l s e return SURFACE LIGHTMAP BLEND; } s t a t i c i n t previewlightmapalpha ( lightmapworker ∗w, f l o a t lpu , const vec & origin1 , const vec &xstep1 , const vec &ystep1 , const vec &origin2 , const vec &xstep2 , const vec &ystep2 , f l o a t side0 , f l o a t sidestep ) { extern i n t f u l l b r i g h t l e v e l ; f l o a t tolerance = 0.5 / lpu ; uchar ∗dst = w−>colorbuf ; uchar minalpha = 255, maxalpha = 0; f l o a t sidex = side0 ;

265

f o r ( i n t y = 0; y < w−>h ; ++y , sidex += sidestep ) { f o r ( i n t x = 0; x < w−>w; ++x , dst += 4) { vec u = x < sidex ? vec ( xstep1 ) . mul ( x ) . add ( vec ( ystep1 ) . mul ( y ) ) . add ( origin1 ) : vec ( xstep2 ) . mul ( x ) . add ( vec ( ystep2 ) . mul ( y ) ) . add ( origin2 ) ; loopk ( 3 ) dst [ k ] = f u l l b r i g h t l e v e l ; generatealpha (w, tolerance , u, dst [ 3 ] ) ; minalpha = min ( minalpha , dst [ 3 ] ) ; maxalpha = max( maxalpha , dst [ 3 ] ) ; } } i f ( minalpha==255) return SURFACE AMBIENT TOP; i f ( maxalpha==0) return SURFACE AMBIENT BOTTOM; i f ( minalpha==maxalpha ) w−>w = w−>h = 1; i f ( ( w−>type&LM TYPE ) == LM BUMPMAP0) l o o p i (w−>w∗w−>h ) w−>raybuf [ i ] = bvec (128 , 128, 255) ; return SURFACE LIGHTMAP BLEND; } s t a t i c void clearsurfaces ( cube ∗c ) { loopi ( 8 ) { i f ( c [ i ] . ext ) { loopj ( 6 ) { surfaceinfo &surf = c [ i ] . ext−>surfaces [ j ] ; i f ( ! surf . used ( ) ) continue ; surf . c l e a r ( ) ; i n t numverts = surf . numverts&MAXFACEVERTS; i f ( numverts ) { i f ( ! ( c [ i ] . merged&(1= 0) { const e x t e n t i t y &l i g h t = ∗e n t i t i e s : : getents ( ) [ id ] ; i n t radius = l i g h t . a t t r 1 ; i f ( radius ) { f o r ( i n t x = i n t (max( l i g h t . o . x−radius , 0.0 f ) )>>l i g h t c a c h e s i z e , ex = i n t ( min ( l i g h t . o . x+radius , worldsize −1.0f ) )>> l i g h t c a c h e s i z e ; x >l i g h t c a c h e s i z e , ey = i n t ( min ( l i g h t . o . y+radius , worldsize −1.0f ) )>> l i g h t c a c h e s i z e ; y x = −1;

266

Foundations of Videogame Programming Code Repository const e x t e n t i t y &l i g h t = ∗ents [ i ] ; switch ( l i g h t . type ) { case ET LIGHT : addlight (w, l i g h t , cx , cy , cz , size , v , n , numv) ; break ; }

lce−>l i g h t s . s e t s i z e ( 0 ) ; } } const vector &checklightcache ( i n t x , i n t y ) { x >>= l i g h t c a c h e s i z e ; y >>= l i g h t c a c h e s i z e ; lightcacheentry &l c e = lightcache [LIGHTCACHEHASH( x , y ) ] ; i f ( l c e . x == x && l c e . y == y ) return l c e . l i g h t s ;

} i f ( v s l o t . l a y e r && ( setblendmaporigin (w−>blendmapcache , i v e c ( cx , cy , cz ) , s i z e ) || s l o t . layermask ) ) return true ; return w−>l i g h t s . length ( ) || hasskylight ( ) || sunlight ; }

lce . lights . setsize (0) ; i n t c s i z e = 1 cx + s i z e || l i g h t . o . y + radius < cy || l i g h t . o . y − radius > cy + s i z e || l i g h t . o . z + radius < cz || l i g h t . o . z − radius > cz + s i z e ) return ; } loopi ( 4 ) { vec p ( l i g h t . o ) ; p . sub ( v [ i ] ) ; f l o a t d i s t = p . dot ( n [ i ] ) ; i f ( d i s t >= 0 && ( ! radius || d i s t < radius ) ) { w−>l i g h t s . add(& l i g h t ) ; break ; } } } s t a t i c bool f i n d l i g h t s ( lightmapworker ∗w, i n t cx , i n t cy , i n t cz , i n t size , const vec ∗v , const vec ∗n , i n t numv, const S l o t &s l o t , const VSlot &v s l o t ) { w−>l i g h t s . s e t s i z e ( 0 ) ; const vector &ents = e n t i t i e s : : getents ( ) ; s t a t i c v o l a t i l e bool usinglightcache = f a l s e ; i f ( s i z e next ) { l−>packed = true ; space += l−>bufsize ; i f ( l−>surface < 0 || ! t . ext ) continue ; surfaceinfo &surf = t . ext−>surfaces [ l−>surface ] ; l a y o u t i n f o layout ; packlightmap(∗ l , layout ) ; i n t numverts = surf . numverts&MAXFACEVERTS; v e r t i n f o ∗v e r t s = t . ext−>v e r t s ( ) + surf . v e r t s ; i f ( l−>l a y e r s&LAYER DUP) { i f ( l−>type&LM ALPHA) surf . lmid [ 0 ] = layout . lmid ; e l s e { surf . lmid [ 1 ] = layout . lmid ; v e r t s += numverts ; } } else { i f ( l−>l a y e r s&LAYER TOP ) surf . lmid [ 0 ] = layout . lmid ; i f ( l−>l a y e r s&LAYER BOTTOM) surf . lmid [ 1 ] = layout . lmid ; } ushort o f f s e t x = layout . x ∗ ( (USHRT MAX+1)/LM PACKW) , o f f s e t y = layout . y ∗ ( (USHRT MAX+1)/LM PACKH) ; loopk ( numverts ) { v e r t i n f o &v = v e r t s [ k ] ; v . u += o f f s e t x ; v . v += o f f s e t y ; } } i f ( t . worker == w) { w−>bufused −= space ; w−>bufstart = (w−>bufstart + space )%LIGHTMAPBUFSIZE; w−>f i r s t l i g h t m a p = l ; if (! l ) { w−>lastlightmap = NULL; w−>bufstart = w−>bufused = 0; } } i f ( t . worker−>needspace ) SDL CondSignal ( t . worker−>spacecond ) ; } return numpacked ; } s t a t i c lightmapinfo ∗alloclightmap ( lightmapworker ∗w) { i n t needspace1 = s i z e o f ( lightmapinfo ) + w−>w∗w−>h∗w−>bpp , needspace2 = (w−>type&LM TYPE ) == LM BUMPMAP0 ? w−>w∗w−>h∗3 : 0 , needspace = needspace1 + needspace2 , bufend = (w−>bufstart + w−>bufused )%LIGHTMAPBUFSIZE, availspace = LIGHTMAPBUFSIZE − w−>bufused , availspace1 = min ( availspace , LIGHTMAPBUFSIZE − bufend ) , availspace2 = min ( availspace , w−>bufstart ) ; i f ( availspace < needspace || (max( availspace1 , availspace2 ) < needspace && ( availspace1 < needspace1 || availspace2 < needspace2 ) ) ) { i f ( tasklock ) SDL LockMutex ( tasklock ) ; while ( ! w−>doneworking ) { lightmapinfo ∗l = w−>f i r s t l i g h t m a p ; f o r ( ; l && l−>packed ; l = l−>next ) { w−>bufused −= l−>bufsize ;

engine/lightmap.cpp w−>bufstart = (w−>bufstart + l−>bufsize )%LIGHTMAPBUFSIZE; } w−>f i r s t l i g h t m a p = l ; if (! l ) { w−>lastlightmap = NULL; w−>bufstart = w−>bufused = 0; } bufend = (w−>bufstart + w−>bufused )%LIGHTMAPBUFSIZE; availspace = LIGHTMAPBUFSIZE − w−>bufused ; availspace1 = min ( availspace , LIGHTMAPBUFSIZE − bufend ) ; availspace2 = min ( availspace , w−>bufstart ) ; i f ( availspace >= needspace && (max( availspace1 , availspace2 ) >= needspace || ( availspace1 >= needspace1 && availspace2 >= needspace2 ) ) ) break ; i f ( packlightmaps (w) ) continue ; i f ( ! w−>spacecond || ! tasklock ) break ; w−>needspace = true ; SDL CondWait (w−>spacecond , tasklock ) ; w−>needspace = f a l s e ; } i f ( tasklock ) SDL UnlockMutex ( tasklock ) ; } i n t usedspace = needspace ; lightmapinfo ∗l = NULL; i f ( availspace1 >= needspace1 ) { l = ( lightmapinfo ∗)&w−>buf [ bufend ] ; w−>colorbuf = ( uchar ∗) ( l + 1) ; i f ( ( w−>type&LM TYPE ) ! = LM BUMPMAP0) w−>raybuf = NULL; e l s e i f ( availspace1 >= needspace ) w−>raybuf = ( bvec ∗)&w−>buf [ bufend + needspace1 ] ; else { w−>raybuf = ( bvec ∗)w−>buf ; usedspace += availspace1 − needspace1 ; } } e l s e i f ( availspace2 >= needspace ) { usedspace += availspace1 ; l = ( lightmapinfo ∗)w−>buf ; w−>colorbuf = ( uchar ∗) ( l + 1) ; w−>raybuf = (w−>type&LM TYPE ) == LM BUMPMAP0 ? ( bvec ∗)&w−>buf [ needspace1 ] : NULL; } e l s e return NULL; w−>bufused += usedspace ; l−>next = NULL; l−>c = w−>c ; l−>type = w−>type ; l−>w = w−>w; l−>h = w−>h ; l−>bpp = w−>bpp ; l−>colorbuf = w−>colorbuf ; l−>raybuf = w−>raybuf ; l−>packed = f a l s e ; l−>bufsize = usedspace ; l−>surface = −1; l−>l a y e r s = 0; i f ( ! w−>f i r s t l i g h t m a p ) w−>f i r s t l i g h t m a p = l ; i f (w−>lastlightmap ) w−>lastlightmap−>next = l ; w−>lastlightmap = l ; i f ( ! w−>curlightmaps ) w−>curlightmaps = l ; return l ; } s t a t i c void freelightmap ( lightmapworker ∗w) { lightmapinfo ∗l = w−>lastlightmap ; i f ( ! l || l−>surface >= 0) return ; i f (w−>f i r s t l i g h t m a p == w−>lastlightmap ) { w−>f i r s t l i g h t m a p = w−>lastlightmap = w−>curlightmaps = NULL; w−>bufstart = w−>bufused = 0; } else { w−>bufused −= l−>bufsize − s i z e o f ( lightmapinfo ) ; l−>bufsize = s i z e o f ( lightmapinfo ) ; l−>packed = true ; } i f (w−>curlightmaps == l ) w−>curlightmaps = NULL; } s t a t i c i n t setupsurface ( lightmapworker ∗w, plane planes [ 2 ] , i n t numplanes , const vec ∗p , const vec ∗n , i n t numverts , v e r t i n f o ∗l i t v e r t s , bool preview = f a l s e ) { vec u, v , t ; vec2 c [MAXFACEVERTS] ; u = vec ( p [ 2 ] ) . sub ( p [ 0 ] ) . normalize ( ) ; v . cross ( planes [ 0 ] , u ) ;

267

c [ 0 ] = vec2 ( 0 , 0) ; i f ( numplanes >= 2) t . cross ( planes [ 1 ] , u ) ; e l s e t = v ; vec r1 = vec ( p [ 1 ] ) . sub ( p [ 0 ] ) ; c [ 1 ] = vec2 ( r1 . dot ( u ) , min ( r1 . dot ( v ) , 0.0 f ) ) ; c [ 2 ] = vec2 ( vec ( p [ 2 ] ) . sub ( p [ 0 ] ) . dot ( u ) , 0) ; f o r ( i n t i = 3; i < numverts ; i ++) { vec r = vec ( p [ i ] ) . sub ( p [ 0 ] ) ; c [ i ] = vec2 ( r . dot ( u ) , max( r . dot ( t ) , 0.0 f ) ) ; } f l o a t carea = 1e16f ; vec2 cx ( 0 , 0) , cy ( 0 , 0) , co ( 0 , 0) , cmin ( 0 , 0) , cmax( 0 , 0) ; l o o p i ( numverts ) { vec2 px = vec2 ( c [ i +1 < numverts ? i +1 : 0 ] ) . sub ( c [ i ] ) ; f l o a t len = px . squaredlen ( ) ; i f ( ! len ) continue ; px . mul(1/ s q r t f ( len ) ) ; vec2 py(−px . y , px . x ) , pmin( 0 , 0) , pmax( 0 , 0) ; i f ( numplanes >= 2 && ( i == 0 || i >= 3) ) px . neg ( ) ; l o o p j ( numverts ) { vec2 r j = vec2 ( c [ j ] ) . sub ( c [ i ] ) , pj ( r j . dot ( px ) , r j . dot ( py ) ) ; pmin . x = min ( pmin . x , pj . x ) ; pmin . y = min ( pmin . y , pj . y ) ; pmax. x = max(pmax. x , pj . x ) ; pmax. y = max(pmax. y , pj . y ) ; } f l o a t area = (pmax. x−pmin . x ) ∗(pmax. y−pmin . y ) ; i f ( area < carea ) { carea = area ; cx = px ; cy = py ; co = c [ i ] ; cmin = pmin ; cmax = pmax; } } i n t scale = i n t ( min ( cmax . x − cmin . x , cmax . y − cmin . y ) ) ; f l o a t lpu = 16.0 f / f l o a t ( l i g h t l o d && scale < (1 w = lw ; w−>h = lh ; i f ( ! preview ) { w−>w += 2∗blurlms ; w−>h += 2∗blurlms ; } i f ( ! alloclightmap (w) ) return NO SURFACE; vec2 cscale = vec2 ( cmax ) . sub ( cmin ) . div ( vec2 ( lw−1, lh−1)) , comin = vec2 ( cx ) . mul ( cmin . x ) . add ( vec2 ( cy ) . mul ( cmin . y ) ) . add ( co ) ; l o o p i ( numverts ) { vec2 r i = vec2 ( c [ i ] ) . sub ( comin ) ; c [ i ] = vec2 ( r i . dot ( cx ) /cscale . x , r i . dot ( cy ) /cscale . y ) ; } vec xstep1 = vec ( v ) . mul ( cx . y ) . add ( vec ( u ) . mul ( cx . x ) ) . mul ( cscale . x ) , ystep1 = vec ( v ) . mul ( cy . y ) . add ( vec ( u ) . mul ( cy . x ) ) . mul ( cscale . y ) , origin1 = vec ( v ) . mul ( comin . y ) . add ( vec ( u ) . mul ( comin . x ) ) . add ( p [ 0 ] ) , xstep2 = xstep1 , ystep2 = ystep1 , origin2 = origin1 ; f l o a t side0 = LM MAXW + 1 , sidestep = 0; i f ( numplanes >= 2) { xstep2 = vec ( t ) . mul ( cx . y ) . add ( vec ( u ) . mul ( cx . x ) ) . mul ( cscale . x ) ; ystep2 = vec ( t ) . mul ( cy . y ) . add ( vec ( u ) . mul ( cy . x ) ) . mul ( cscale . y ) ; origin2 = vec ( t ) . mul ( comin . y ) . add ( vec ( u ) . mul ( comin . x ) ) . add ( p [ 0 ] ) ; i f ( cx . y ) { side0 = comin . y/−(cx . y∗cscale . x ) ; sidestep = cy . y∗cscale . y/−(cx . y∗cscale . x ) ; } e l s e i f ( cy . y ) { side0 = c e i l ( comin . y/−(cy . y∗cscale . y ) ) ∗(LM MAXW + 1) ; sidestep = −(LM MAXW + 1) ; i f ( cy . y < 0) { side0 = ( LM MAXW + 1) − side0 ; sidestep = −sidestep ; } } e l s e side0 = comin . y w − 1) / ( lw − 1) ; i f ( lh ! = w−>h ) t e x s c a l e . y ∗= f l o a t (w−>h − 1) / ( lh − 1) ; loopk ( numverts ) { l i t v e r t s [ k ] . u = ushort ( f l o o r ( clamp ( c [ k ] . x∗t e x s c a l e . x , 0.0 f , f l o a t ( USHRT MAX) ) ) ) ; l i t v e r t s [ k ] . v = ushort ( f l o o r ( clamp ( c [ k ] . y∗t e x s c a l e . y , 0.0 f , f l o a t ( USHRT MAX) ) ) ) ; } return surftype ; } s t a t i c void removelmalpha ( lightmapworker ∗w) { i f ( ! ( w−>type&LM ALPHA) ) return ; f o r ( uchar ∗dst = w−>colorbuf , ∗src = w−>colorbuf , ∗end = &src [w−>w∗w−> h∗4]; src < end ; dst += 3 , src += 4) { dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 1 ] ; dst [ 2 ] = src [ 2 ] ; } w−>type &= ˜LM ALPHA; w−>bpp = 3; w−>lastlightmap−>type = w−>type ; w−>lastlightmap−>bpp = w−>bpp ; } s t a t i c lightmapinfo ∗setupsurfaces ( lightmapworker ∗w, lightmaptask &task ) { cube &c = ∗task . c ; const i v e c &co = task . o ; i n t s i z e = task . size , usefacemask = task . usefaces ; w−>curlightmaps = NULL; w−>c = &c ; surfaceinfo surfaces [ 6 ] ; v e r t i n f o l i t v e r t s [6∗2∗MAXFACEVERTS] ; i n t numlitverts = 0; memset ( surfaces , 0 , s i z e o f ( surfaces ) ) ; loopi ( 6 ) { i n t usefaces = usefacemask&0xF ; usefacemask >>= 4; i f ( ! usefaces ) { i f ( ! c . ext ) continue ; surfaceinfo &surf = surfaces [ i ] ; surf = c . ext−>surfaces [ i ] ; i n t numverts = surf . t o t a l v e r t s ( ) ; i f ( numverts ) { memcpy(& l i t v e r t s [ numlitverts ] , c . ext−>v e r t s ( ) + surf . verts , numverts∗s i z e o f ( v e r t i n f o ) ) ; surf . v e r t s = numlitverts ; numlitverts += numverts ; } continue ; } VSlot &v s l o t = lookupvslot ( c . texture [ i ] , f a l s e ) , ∗l a y e r = v s l o t . l a y e r && ! ( c . material&MAT ALPHA) ? &lookupvslot ( v s l o t . layer , f a l s e ) : NULL; Shader ∗shader = v s l o t . s l o t−>shader ; i n t shadertype = shader−>type ; i f ( l a y e r ) shadertype |= layer−>s l o t−>shader−>type ; surfaceinfo &surf = surfaces [ i ] ; v e r t i n f o ∗c u r l i t v e r t s = &l i t v e r t s [ numlitverts ] ; i n t numverts = c . ext ? c . ext−>surfaces [ i ] . numverts&MAXFACEVERTS : 0; i v e c mo( co ) ; i n t msz = size , convex = 0; i f ( numverts ) { v e r t i n f o ∗v e r t s = c . ext−>v e r t s ( ) + c . ext−>surfaces [ i ] . v e r t s ; l o o p j ( numverts ) c u r l i t v e r t s [ j ] . set ( v e r t s [ j ] . getxyz ( ) ) ; i f ( c . merged&(1type&SHADER NORMALSLMS ? LM BUMPMAP0 : LM DIFFUSE ; i f ( l a y e r ) w−>type |= LM ALPHA; w−>bpp = w−>type&LM ALPHA ? 4 : 3; w−>o r i e n t = i ; w−>r o t a t e = v s l o t . r o t a t i o n ; i n t surftype = setupsurface (w, planes , numplanes , pos , n , numverts , curlitverts ) ; switch ( surftype ) { case SURFACE LIGHTMAP BOTTOM: i f ( ( shader−>type ˆ layer−>s l o t−>shader−>type )&SHADER NORMALSLMS || ( shader−>type&SHADER NORMALSLMS && v s l o t . r o t a t i o n ! = layer−> rotation ) ) { freelightmap (w) ; break ; } // f a l l through case SURFACE LIGHTMAP BLEND: case SURFACE LIGHTMAP TOP: { i f ( ! ( surf . numverts&MAXFACEVERTS) ) { surf . v e r t s = numlitverts ; surf . numverts |= numverts ; numlitverts += numverts ; } w−>lastlightmap−>surface = i ; w−>lastlightmap−>l a y e r s = ( surftype==SURFACE LIGHTMAP BOTTOM ? LAYER BOTTOM : LAYER TOP ) ; i f ( surftype==SURFACE LIGHTMAP BLEND) {

engine/lightmap.cpp surf . numverts |= LAYER BLEND; w−>lastlightmap−>l a y e r s = LAYER TOP ; i f ( ( shader−>type ˆ layer−>s l o t−>shader−>type )& SHADER NORMALSLMS || ( shader−>type&SHADER NORMALSLMS && v s l o t . r o t a t i o n ! = layer−>r o t a t i o n ) ) break ; w−>lastlightmap−>l a y e r s |= LAYER BOTTOM; } else { i f ( surftype==SURFACE LIGHTMAP BOTTOM) { surf . numverts |= LAYER BOTTOM; w−>lastlightmap−>l a y e r s = LAYER BOTTOM; } else { surf . numverts |= LAYER TOP ; w−>lastlightmap−>l a y e r s = LAYER TOP ; } i f (w−>type&LM ALPHA) removelmalpha (w) ; } continue ; } case SURFACE AMBIENT BOTTOM: freelightmap (w) ; surf . numverts |= l a y e r ? LAYER BOTTOM : LAYER TOP ; continue ; case SURFACE AMBIENT TOP: freelightmap (w) ; surf . numverts |= LAYER TOP ; continue ; default : freelightmap (w) ; continue ; } w−>s l o t = layer−>s l o t ; w−>v s l o t = l a y e r ; w−>type = layer−>s l o t−>shader−>type&SHADER NORMALSLMS ? LM BUMPMAP0 : LM DIFFUSE; w−>bpp = 3; w−>r o t a t e = layer−>r o t a t i o n ; v e r t i n f o ∗blendverts = surf . numverts&MAXFACEVERTS ? &c u r l i t v e r t s [ numverts ] : c u r l i t v e r t s ; switch ( setupsurface (w, planes , numplanes , pos , n , numverts , blendverts ) ) { case SURFACE LIGHTMAP TOP: { i f ( ! ( surf . numverts&MAXFACEVERTS) ) { surf . v e r t s = numlitverts ; surf . numverts |= numverts ; numlitverts += numverts ; } e l s e i f ( ! ( surf . numverts&LAYER DUP) ) { surf . numverts |= LAYER DUP; w−>lastlightmap−>l a y e r s |= LAYER DUP; loopk ( numverts ) { v e r t i n f o &src = c u r l i t v e r t s [ k ] ; v e r t i n f o &dst = blendverts [ k ] ; dst . setxyz ( src . getxyz ( ) ) ; dst . norm = src . norm ; } numlitverts += numverts ; } surf . numverts |= LAYER BOTTOM; w−>lastlightmap−>l a y e r s |= LAYER BOTTOM; w−>lastlightmap−>surface = i ; break ;

269

{ cubeext ∗ext = c . ext && c . ext−>maxverts >= numlitverts ? c . ext : growcubeext ( c . ext , numlitverts ) ; memcpy( ext−>surfaces , surfaces , s i z e o f ( ext−>surfaces ) ) ; memcpy( ext−>v e r t s ( ) , l i t v e r t s , numlitverts∗s i z e o f ( v e r t i n f o ) ) ; task . ext = ext ; break ; } } return w−>curlightmaps ? w−>curlightmaps : ( lightmapinfo ∗)−1; } i n t lightmapworker : : work ( void ∗data ) { lightmapworker ∗w = ( lightmapworker ∗) data ; SDL LockMutex ( tasklock ) ; while ( ! w−>doneworking ) { i f ( a l l o c i d x < lightmaptasks [ 0 ] . length ( ) ) { lightmaptask &t = lightmaptasks [ 0 ] [ a l l o c i d x + + ] ; t . worker = w; SDL UnlockMutex ( tasklock ) ; lightmapinfo ∗l = setupsurfaces (w, t ) ; SDL LockMutex ( tasklock ) ; t . lightmaps = l ; packlightmaps (w) ; } else { i f ( packidx >= lightmaptasks [ 0 ] . length ( ) ) SDL CondSignal ( emptycond ) ; SDL CondWait ( fullcond , tasklock ) ; } } SDL UnlockMutex ( tasklock ) ; return 0; } s t a t i c bool processtasks ( bool f i n i s h = f a l s e ) { i f ( tasklock ) SDL LockMutex ( tasklock ) ; while ( f i n i s h || lightmaptasks [ 1 ] . length ( ) ) { i f ( packidx >= lightmaptasks [ 0 ] . length ( ) ) { i f ( lightmaptasks [ 1 ] . empty ( ) ) break ; lightmaptasks [ 0 ] . s e t s i z e ( 0 ) ; lightmaptasks [ 0 ] . move ( lightmaptasks [ 1 ] ) ; packidx = a l l o c i d x = 0; i f ( fullcond ) SDL CondBroadcast ( fullcond ) ; } e l s e i f ( lightmapping > 1) { SDL CondWaitTimeout ( emptycond , tasklock , 250) ; CHECK PROGRESS LOCKED({ SDL UnlockMutex ( tasklock ) ; return f a l s e ; }, SDL UnlockMutex ( tasklock ) , SDL LockMutex ( tasklock ) ) ; } else { while ( a l l o c i d x < lightmaptasks [ 0 ] . length ( ) ) { lightmaptask &t = lightmaptasks [ 0 ] [ a l l o c i d x + + ] ; t . worker = lightmapworkers [ 0 ] ; t . lightmaps = setupsurfaces ( lightmapworkers [ 0 ] , t ) ; packlightmaps ( lightmapworkers [ 0 ] ) ; CHECK PROGRESS( return f a l s e ) ; } } } i f ( tasklock ) SDL UnlockMutex ( tasklock ) ; return true ; } s t a t i c void generatelightmaps ( cube ∗c , i n t cx , i n t cy , i n t cz , i n t s i z e ) { CHECK PROGRESS( return ) ;

}

taskprogress ++;

case SURFACE AMBIENT TOP: { freelightmap (w) ; surf . numverts |= LAYER BOTTOM; break ; }

loopi ( 8 ) { i v e c o ( i , cx , cy , cz , s i z e ) ; i f ( c [ i ] . children ) generatelightmaps ( c [ i ] . children , o . x , o . y , o . z , s i z e >> 1) ; e l s e i f ( ! isempty ( c [ i ] ) ) { i f ( c [ i ] . ext ) { loopj ( 6 ) { surfaceinfo &surf = c [ i ] . ext−>surfaces [ j ] ; i f ( surf . lmid [ 0 ] >= LMID RESERVED || surf . lmid [ 1 ] >= LMID RESERVED) goto nextcube ;

d e f a u l t : freelightmap (w) ; break ; } } loopk ( 6 ) { surfaceinfo &surf = surfaces [ k ] ; i f ( surf . used ( ) )

270

Foundations of Videogame Programming Code Repository surf . c l e a r ( ) ; } } i n t usefacemask = 0; l o o p j ( 6 ) i f ( c [ i ] . texture [ j ] ! = DEFAULT SKY && ( ! ( c [ i ] . merged &(1v s l o t = &v s l o t ; w−>type = shadertype&SHADER NORMALSLMS ? LM BUMPMAP0|LM ALPHA : LM DIFFUSE|LM ALPHA; w−>bpp = 4; w−>o r i e n t = i ; w−>r o t a t e = v s l o t . r o t a t i o n ; i n t surftype = setupsurface (w, planes , numplanes , pos , n , numverts , c u r l i t v e r t s , true ) ; switch ( surftype ) { case SURFACE AMBIENT TOP: surf = brightsurface ; continue ;

} nextcube : ; } } s t a t i c bool previewblends ( lightmapworker ∗w, cube &c , const i v e c &co , i n t size ) { i f ( isempty ( c ) || c . material&MAT ALPHA) return f a l s e ; i n t usefacemask = 0; l o o p i ( 6 ) i f ( c . texture [ i ] ! = DEFAULT SKY && lookupvslot ( c . texture [ i ] , false ) . layer ) usefacemask |= v i s i b l e t r i s ( c , i , co . x , co . y , co . z , s i z e )surfaces [ i ] . numverts&LAYER BOTTOM) { c . ext−>surfaces [ i ] . brighten ( ) ; blends = true ; } return blends ; }

case SURFACE LIGHTMAP BLEND: { i f ( surf . numverts == (LAYER BLEND|numverts ) && surf . lmid [ 0 ] == surf . lmid [ 1 ] && ( surf . numverts&MAXFACEVERTS) == numverts && !memcmp( c u r l i t v e r t s , c . ext−>v e r t s ( ) + surf . verts , numverts ∗s i z e o f ( v e r t i n f o ) ) && lightmaps . inrange ( surf . lmid[0]−LMID RESERVED) && lightmaps [ surf . lmid[0]−LMID RESERVED ] . type==w−>type ) { v e r t i n f o ∗o l d v e r t s = c . ext−>v e r t s ( ) + surf . v e r t s ; l a y o u t i n f o layout ; layout .w = w−>w; layout . h = w−>h ; layout . x = ( o l d v e r t s [ 0 ] . x − c u r l i t v e r t s [ 0 ] . x ) / ( (USHRT MAX +1)/LM PACKW) ; layout . y = ( o l d v e r t s [ 0 ] . y − c u r l i t v e r t s [ 0 ] . y ) / ( (USHRT MAX +1)/LM PACKH) ; i f (LM PACKW − layout . x >= w−>w && LM PACKH − layout . y >= w −>h ) { layout . lmid = surf . lmid [ 0 ] ; copylightmap (∗w−>lastlightmap , layout ) ; updatelightmap ( layout ) ; surf . v e r t s = numlitverts ; numlitverts += numverts ; continue ; } }

w−>f i r s t l i g h t m a p = w−>lastlightmap = w−>curlightmaps = NULL; w−>bufstart = w−>bufused = 0; w−>c = &c ; surfaceinfo surfaces [ 6 ] ; v e r t i n f o l i t v e r t s [6∗2∗MAXFACEVERTS] ; i n t numlitverts = 0; memcpy( surfaces , c . ext ? c . ext−>surfaces : brightsurfaces , s i z e o f ( surfaces ) ) ; loopi ( 6 ) { i n t usefaces = usefacemask&0xF ; usefacemask >>= 4; i f ( ! usefaces ) { surfaceinfo &surf = surfaces [ i ] ; i n t numverts = surf . t o t a l v e r t s ( ) ; i f ( numverts ) { memcpy(& l i t v e r t s [ numlitverts ] , c . ext−>v e r t s ( ) + surf . verts , numverts∗s i z e o f ( v e r t i n f o ) ) ; surf . v e r t s = numlitverts ; numlitverts += numverts ; } continue ; }

surf . v e r t s = numlitverts ; surf . numverts = LAYER BLEND|numverts ; numlitverts += numverts ; l a y o u t i n f o layout ; i f ( packlightmap(∗w−>lastlightmap , layout ) ) updatelightmap ( layout ) ; surf . lmid [ 0 ] = surf . lmid [ 1 ] = layout . lmid ; ushort o f f s e t x = layout . x ∗ ( (USHRT MAX+1)/LM PACKW) , o f f s e t y = layout . y ∗ ( (USHRT MAX+1)/LM PACKH) ; loopk ( numverts ) { v e r t i n f o &v = c u r l i t v e r t s [ k ] ; v . u += o f f s e t x ; v . v += o f f s e t y ; } continue ;

VSlot &v s l o t = lookupvslot ( c . texture [ i ] , f a l s e ) , &l a y e r = lookupvslot ( v s l o t . layer , f a l s e ) ; Shader ∗shader = v s l o t . s l o t−>shader ; i n t shadertype = shader−>type | l a y e r . s l o t−>shader−>type ; v e r t i n f o ∗c u r l i t v e r t s = &l i t v e r t s [ numlitverts ] ; i n t numverts = 0; ivec v [ 4 ] ; genfaceverts ( c , i , v ) ; i n t convex = f l a t a x i s f a c e ( c , i ) ? 0 : faceconvexity ( v ) , order = usefaces&4 || convex < 0 ? 1 : 0; i v e c vo = i v e c ( co ) .mask(0xFFF ) . shl ( 3 ) ; c u r l i t v e r t s [ numverts + + ] . set ( v [ order ] . mul ( s i z e ) . add ( vo ) ) ; i f ( usefaces&1) c u r l i t v e r t s [ numverts + + ] . set ( v [ order + 1 ] .mul ( s i z e ) . add ( vo ) ) ;

} } } setsurfaces ( c , surfaces , l i t v e r t s , numlitverts ) ; return true ;

engine/lightmap.cpp } s t a t i c bool previewblends ( lightmapworker ∗w, cube ∗c , const i v e c &co , i n t size , const i v e c &bo , const i v e c &bs ) { bool changed = f a l s e ; loopoctabox ( co , size , bo , bs ) { i v e c o ( i , co . x , co . y , co . z , s i z e ) ; cubeext ∗ext = c [ i ] . ext ; i f ( ext && ext−>va && ext−>va−>hasmerges ) { changed = true ; destroyva ( ext−>va ) ; ext−>va = NULL; invalidatemerges ( c [ i ] , co , size , true ) ; } i f ( c [ i ] . children ? previewblends (w, c [ i ] . children , o , s i z e /2 , bo , bs ) : previewblends (w, c [ i ] , o , s i z e ) ) { changed = true ; ext = c [ i ] . ext ; i f ( ext && ext−>va ) { destroyva ( ext−>va ) ; ext−>va = NULL; } } } return changed ; } void previewblends ( const i v e c &bo , const i v e c &bs ) { loadlayermasks ( ) ; i f ( lightmapworkers . empty ( ) ) lightmapworkers . add ( new lightmapworker ) ; lightmapworkers[0]−>r e s e t ( ) ; i f ( previewblends ( lightmapworkers [ 0 ] , worldroot , i v e c ( 0 , 0 , 0) , worldsize /2 , bo , bs ) ) commitchanges ( true ) ; } void cleanuplightmaps ( ) { loopv ( lightmaps ) { LightMap &lm = lightmaps [ i ] ; lm . tex = lm . o f f s e t x = lm . o f f s e t y = −1; } loopv ( lightmaptexs ) glDeleteTextures ( 1 , &lightmaptexs [ i ] . id ) ; lightmaptexs . shrink ( 0 ) ; i f ( progresstex ) { glDeleteTextures ( 1 , &progresstex ) ; progresstex = 0; } } void resetlightmaps ( bool f u l l c l e a n ) { cleanuplightmaps ( ) ; lightmaps . shrink ( 0 ) ; compressed . c l e a r ( ) ; clearlightcache ( ) ; i f ( f u l l c l e a n ) while ( lightmapworkers . length ( ) ) d e l e t e lightmapworkers . pop ( ) ; } lightmapworker : : lightmapworker ( ) { buf = new uchar [LIGHTMAPBUFSIZE ] ; bufstart = bufused = 0; f i r s t l i g h t m a p = lastlightmap = curlightmaps = NULL; ambient = new uchar [4∗(LM MAXW + 4) ∗(LM MAXH + 4) ] ; blur = new uchar [4∗(LM MAXW + 4) ∗(LM MAXH + 4) ] ; colordata = new vec [4∗(LM MAXW+1 + 4) ∗(LM MAXH+1 + 4) ] ; raydata = new vec [ (LM MAXW + 4) ∗(LM MAXH + 4) ] ; shadowraycache = newshadowraycache ( ) ; blendmapcache = newblendmapcache ( ) ; needspace = doneworking = f a l s e ; spacecond = NULL; thread = NULL; } lightmapworker : : ˜ lightmapworker ( ) { cleanupthread ( ) ; d e l e t e [ ] buf ; d e l e t e [ ] ambient ; d e l e t e [ ] blur ; d e l e t e [ ] colordata ; d e l e t e [ ] raydata ; freeshadowraycache ( shadowraycache ) ; freeblendmapcache ( blendmapcache ) ; } void lightmapworker : : cleanupthread ( )

271

{ i f ( spacecond ) { SDL DestroyCond ( spacecond ) ; spacecond = NULL; } thread = NULL; } void lightmapworker : : r e s e t ( ) { bufstart = bufused = 0; f i r s t l i g h t m a p = lastlightmap = curlightmaps = NULL; needspace = doneworking = f a l s e ; resetshadowraycache ( shadowraycache ) ; } bool lightmapworker : : setupthread ( ) { i f ( ! spacecond ) spacecond = SDL CreateCond ( ) ; i f ( ! spacecond ) return f a l s e ; thread = SDL CreateThread ( work , t h i s ) ; return thread ! =NULL; } s t a t i c Uint32 c a l c l i g h t t i m e r ( Uint32 i n t e r v a l , void ∗param ) { c h e c k c a l c l i g h t p r o g r e s s = true ; return i n t e r v a l ; } bool setlightmapquality ( i n t q u a l i t y ) { switch ( q u a l i t y ) { case 1: lmshadows = 2; lmaa = 3; l e r p t j o i n t s = 1; break ; case 0: lmshadows = lmshadows ; lmaa = lmaa ; l e r p t j o i n t s = l e r p t j o i n t s ; break ; case −1: lmshadows = 1; lmaa = 0; l e r p t j o i n t s = 0; break ; d e f a u l t : return f a l s e ; } return true ; } VARP( lightthreads , 0 , 0 , 16) ; #define ALLOCLOCK( name, i n i t ) { i f ( lightmapping > 1) name = i n i t ( ) ; i f ( ! name) lightmapping = 1; } #define FREELOCK( name, destroy ) { i f (name) { destroy (name) ; name = NULL; } } s t a t i c void cleanuplocks ( ) { FREELOCK( l i g h t l o c k , SDL DestroyMutex ) ; FREELOCK( tasklock , SDL DestroyMutex ) ; FREELOCK( fullcond , SDL DestroyCond ) ; FREELOCK( emptycond , SDL DestroyCond ) ; } s t a t i c void setupthreads ( i n t numthreads ) { l o o p i ( 2 ) lightmaptasks [ i ] . s e t s i z e ( 0 ) ; lightmapexts . s e t s i z e ( 0 ) ; packidx = a l l o c i d x = 0; lightmapping = numthreads ; i f ( lightmapping > 1) { ALLOCLOCK( l i g h t l o c k , SDL CreateMutex ) ; ALLOCLOCK( tasklock , SDL CreateMutex ) ; ALLOCLOCK( fullcond , SDL CreateCond ) ; ALLOCLOCK( emptycond , SDL CreateCond ) ; } while ( lightmapworkers . length ( ) < lightmapping ) lightmapworkers . add ( new lightmapworker ) ; l o o p i ( lightmapping ) { lightmapworker ∗w = lightmapworkers [ i ] ; w−>r e s e t ( ) ; i f ( lightmapping setupthread ( ) ) continue ; w−>cleanupthread ( ) ; lightmapping = i >= 1 ? max( i , 2) : 1; break ; } i f ( lightmapping 1) { SDL LockMutex ( tasklock ) ; loopv ( lightmapworkers ) lightmapworkers [ i]−>doneworking = true ; SDL CondBroadcast ( fullcond ) ; loopv ( lightmapworkers ) { lightmapworker ∗w = lightmapworkers [ i ] ; i f (w−>needspace && w−>spacecond ) SDL CondSignal (w−>spacecond ) ;

272

Foundations of Videogame Programming Code Repository

} SDL UnlockMutex ( tasklock ) ; loopv ( lightmapworkers ) { lightmapworker ∗w = lightmapworkers [ i ] ; i f (w−>thread ) SDL WaitThread (w−>thread , NULL) ; }

{ i f ( ( lightmaps [ i ] . type&LM TYPE ) ! = LM BUMPMAP1) progresslightmap = i ; t o t a l −= lightmaps [ i ] . lightmaps ; lumels −= lightmaps [ i ] . lumels ; } calclight canceled = false ; check calclight progress = false ; SDL TimerID timer = SDL AddTimer(250 , c a l c l i g h t t i m e r , NULL) ; i f ( patchnormals ) renderprogress ( 0 , ” computing normals . . . ” ) ; Uint32 s t a r t = SDL GetTicks ( ) ; i f ( patchnormals ) calcnormals ( l e r p t j o i n t s > 0) ; show calclight progress ( ) ; setupthreads ( numthreads ) ; generatelightmaps ( worldroot , 0 , 0 , 0 , worldsize >> 1) ; cleanupthreads ( ) ; i f ( patchnormals ) clearnormals ( ) ; Uint32 end = SDL GetTicks ( ) ; i f ( timer ) SDL RemoveTimer ( timer ) ; loopv ( lightmaps ) { t o t a l += lightmaps [ i ] . lightmaps ; lumels += lightmaps [ i ] . lumels ; } initlights ( ) ; renderbackground ( ” l i g h t i n g done . . . ” ) ; allchanged ( ) ; i f ( calclight canceled ) conoutf ( ” patchlight aborted ” ) ; else conoutf ( ” patched %d lightmaps using %d%% o f %d textures (%.1 f seconds ) ” , total , lightmaps . length ( ) ? lumels ∗ 100 / ( lightmaps . length ( ) ∗ LM PACKW ∗ LM PACKH) : 0 , lightmaps . length ( ) , ( end − s t a r t ) / 1000.0 f ) ;

} loopv ( lightmapexts ) { lightmapext &e = lightmapexts [ i ] ; setcubeext (∗e . c , e . ext ) ; } loopv ( lightmapworkers ) lightmapworkers [ i]−>cleanupthread ( ) ; cleanuplocks ( ) ; lightmapping = 0; } void c a l c l i g h t ( i n t ∗q u a l i t y ) { i f ( ! setlightmapquality (∗ q u a l i t y ) ) { conoutf (CON ERROR, ” v a l i d range f o r c a l c l i g h t q u a l i t y i s −1..1”) ; return ; } renderbackground ( ” computing lightmaps . . . ( esc to abort ) ” ) ; mpremip ( true ) ; optimizeblendmap ( ) ; loadlayermasks ( ) ; i n t numthreads = l i g h t t h r e a d s > 0 ? l i g h t t h r e a d s : numcpus; i f ( numthreads > 1) preloadusedmapmodels ( f a l s e , true ) ; resetlightmaps ( f a l s e ) ; clearsurfaces ( worldroot ) ; taskprogress = progress = 0; progresstexticks = 0; progresslightmap = −1; calclight canceled = false ; check calclight progress = false ; SDL TimerID timer = SDL AddTimer(250 , c a l c l i g h t t i m e r , NULL) ; Uint32 s t a r t = SDL GetTicks ( ) ; calcnormals ( l e r p t j o i n t s > 0) ; show calclight progress ( ) ; setupthreads ( numthreads ) ; generatelightmaps ( worldroot , 0 , 0 , 0 , worldsize >> 1) ; cleanupthreads ( ) ; clearnormals ( ) ; Uint32 end = SDL GetTicks ( ) ; i f ( timer ) SDL RemoveTimer ( timer ) ; uint t o t a l = 0 , lumels = 0; loopv ( lightmaps ) { insertunlit ( i ) ; i f ( ! editmode ) lightmaps [ i ] . f i n a l i z e ( ) ; t o t a l += lightmaps [ i ] . lightmaps ; lumels += lightmaps [ i ] . lumels ; } i f ( ! editmode ) compressed . c l e a r ( ) ; initlights ( ) ; renderbackground ( ” l i g h t i n g done . . . ” ) ; allchanged ( ) ; i f ( calclight canceled ) conoutf ( ” c a l c l i g h t aborted ” ) ; else conoutf ( ” generated %d lightmaps using %d%% o f %d textures (%.1 f seconds ) ” , total , lightmaps . length ( ) ? lumels ∗ 100 / ( lightmaps . length ( ) ∗ LM PACKW ∗ LM PACKH) : 0 , lightmaps . length ( ) , ( end − s t a r t ) / 1000.0 f ) ; } COMMAND( c a l c l i g h t , ” i ” ) ;

} COMMAND( patchlight , ” i ” ) ; void clearlightmaps ( ) { i f ( noedit ( true ) ) return ; renderprogress ( 0 , ” c l e a r i n g lightmaps . . . ” ) ; resetlightmaps ( f a l s e ) ; clearsurfaces ( worldroot ) ; initlights ( ) ; allchanged ( ) ; } COMMAND( clearlightmaps , ” ” ) ; void s e t f u l l b r i g h t l e v e l ( i n t f u l l b r i g h t l e v e l ) { i f ( lightmaptexs . length ( ) > LMID BRIGHT ) { uchar bright [ 3 ] = { uchar ( f u l l b r i g h t l e v e l ) , uchar ( f u l l b r i g h t l e v e l ) , uchar ( f u l l b r i g h t l e v e l ) }; createtexture ( lightmaptexs [ LMID BRIGHT ] . id , 1 , 1 , bright , 0 , 1) ; } initlights ( ) ; } VARF( f u l l b r i g h t , 0 , 0 , 1 , i f ( lightmaptexs . length ( ) ) { i n i t l i g h t s ( ) ; l i g h t e n t s ( ) ; }) ; VARF( f u l l b r i g h t l e v e l , 0 , 128, 255, s e t f u l l b r i g h t l e v e l ( f u l l b r i g h t l e v e l ) ) ; vector lightmaptexs ; s t a t i c void rotatenormals ( LightMap &lmlv , i n t x , i n t y , i n t w, i n t h , i n t rotate ) { bool f l i p x = rotate>=2 && rotate =1 && rotate 0 ? l i g h t t h r e a d s : numcpus; i f ( numthreads > 1) preloadusedmapmodels ( f a l s e , true ) ; cleanuplightmaps ( ) ; taskprogress = progress = 0; progresstexticks = 0; progresslightmap = −1; i n t t o t a l = 0 , lumels = 0; loopv ( lightmaps )

} s t a t i c void rotatenormals ( cube ∗c ) { loopi ( 8 )

engine/lightmap.cpp {

273 verts [ 5 ] ; verts [ 5 ] = verts [ 6 ] ; }

cube &ch = c [ i ] ; i f ( ch . children ) { rotatenormals ( ch . children ) ; continue ; } e l s e i f ( ! ch . ext ) continue ; l o o p j ( 6 ) i f ( lightmaps . inrange ( ch . ext−>surfaces [ j ] . lmid[0]+1− LMID RESERVED) ) { VSlot &v s l o t = lookupvslot ( ch . texture [ j ] , f a l s e ) ; i f ( ! v s l o t . r o t a t i o n ) continue ; surfaceinfo &surface = ch . ext−>surfaces [ j ] ; i n t numverts = surface . numverts&MAXFACEVERTS; i f ( ! numverts ) continue ; LightMap &lmlv = lightmaps [ surface . lmid[0]+1−LMID RESERVED ] ; i f ( ( lmlv . type&LM TYPE ) ! =LM BUMPMAP1) continue ; ushort x1 = USHRT MAX, y1 = USHRT MAX, x2 = 0 , y2 = 0; v e r t i n f o ∗v e r t s = ch . ext−>v e r t s ( ) + surface . v e r t s ; loopk ( numverts ) { v e r t i n f o &v = v e r t s [ k ] ; x1 = min ( x1 , v . u ) ; y1 = min ( y1 , v . u ) ; x2 = max( x2 , v . u ) ; y2 = max( y2 , v . v ) ; } i f ( x1 > x2 || y1 > y2 ) continue ; x1 /= (USHRT MAX+1)/LM PACKW; y1 /= (USHRT MAX+1)/LM PACKH; x2 /= (USHRT MAX+1)/LM PACKW; y2 /= (USHRT MAX+1)/LM PACKH; rotatenormals ( lmlv , x1 , y1 , x2−x1 , y1−y1 , v s l o t . r o t a t i o n < 4 ? 4−v s l o t . r o t a t i o n : v s l o t . r o t a t i o n ) ; } } } void fixlightmapnormals ( ) { rotatenormals ( worldroot ) ; } void fixrotatedlightmaps ( cube &c , const i v e c &co , i n t s i z e ) { i f ( c . children ) { l o o p i ( 8 ) fixrotatedlightmaps ( c . children [ i ] , i v e c ( i , co . x , co . y , co . z , size>>1), size>>1); return ; } i f ( ! c . ext ) return ; loopi ( 6 ) { i f ( c . merged&(1v e r t s ( ) + surf . v e r t s ; i n t v i s = v i s i b l e t r i s ( c , i , co . x , co . y , co . z , s i z e ) ; i f ( ! v i s || v i s ==3) continue ; i f ( ( v e r t s [ 0 ] . u ! = v e r t s [ 1 ] . u || v e r t s [ 0 ] . v ! = v e r t s [ 1 ] . v ) && ( v e r t s [ 0 ] . u ! = v e r t s [ 3 ] . u || v e r t s [ 0 ] . v ! = v e r t s [ 3 ] . v ) && ( v e r t s [ 2 ] . u ! = v e r t s [ 1 ] . u || v e r t s [ 2 ] . v ! = v e r t s [ 1 ] . v ) && ( v e r t s [ 2 ] . u ! = v e r t s [ 3 ] . u || v e r t s [ 2 ] . v ! = v e r t s [ 3 ] . v ) ) continue ; i f ( v i s &4) { v e r t i n f o tmp = v e r t s [ 0 ] ; verts [ 0 ] . x = verts [ 1 ] . x ; verts [ 0 ] . y = verts [ 1 ] . y ; verts [ 0 ] . z = verts [ 1 ] . z ; verts [ 1 ] . x = verts [ 2 ] . x ; verts [ 1 ] . y = verts [ 2 ] . y ; verts [ 1 ] . z = verts [ 2 ] . z ; verts [ 2 ] . x = verts [ 3 ] . x ; verts [ 2 ] . y = verts [ 3 ] . y ; verts [ 2 ] . z = verts [ 3 ] . z ; v e r t s [ 3 ] . x = tmp . x ; v e r t s [ 3 ] . y = tmp . y ; v e r t s [ 3 ] . z = tmp . z ; i f ( surf . numverts&LAYER DUP) loopk ( 4 ) { v e r t i n f o &v = v e r t s [ k ] , &b = v e r t s [ k + 4 ] ; b.x = v .x; b. y = v . y ; b. z = v . z ; } } surf . numverts = ( surf . numverts & ˜MAXFACEVERTS) | 3; i f ( v i s &2) { verts [ 1 ] = verts [ 2 ] ; verts [ 2 ] = verts [ 3 ] ; i f ( surf . numverts&LAYER DUP) { v e r t s [ 3 ] = v e r t s [ 4 ] ; v e r t s [ 4 ] = verts [ 6 ] ; verts [ 5 ] = verts [ 7 ] ; } } e l s e i f ( surf . numverts&LAYER DUP) { v e r t s [ 3 ] = v e r t s [ 4 ] ; v e r t s [ 4 ] =

} } void fixrotatedlightmaps ( ) { l o o p i ( 8 ) fixrotatedlightmaps ( worldroot [ i ] , i v e c ( i , 0 , 0 , 0 , worldsize >>1), worldsize>>1); } s t a t i c void convertlightmap ( LightMap &lmc , LightMap &lmlv , uchar ∗dst , size t stride ) { const uchar ∗c = lmc . data ; const bvec ∗l v = ( const bvec ∗) lmlv . data ; l o o p i (LM PACKH) { uchar ∗dstrow = dst ; l o o p j (LM PACKW) { i n t z = i n t ( lv−>z )∗2 − 255, r = ( i n t ( c [ 0 ] ) ∗ z ) / 255, g = ( i n t ( c [ 1 ] ) ∗ z ) / 255, b = ( i n t ( c [ 2 ] ) ∗ z ) / 255; dstrow [ 0 ] = max( r , i n t ( ambientcolor [ 0 ] ) ) ; dstrow [ 1 ] = max( g , i n t ( ambientcolor [ 1 ] ) ) ; dstrow [ 2 ] = max( b , i n t ( ambientcolor [ 2 ] ) ) ; i f ( lmc . bpp==4) dstrow [ 3 ] = c [ 3 ] ; c += lmc . bpp ; l v ++; dstrow += lmc . bpp ; } dst += s t r i d e ; } } s t a t i c void copylightmap ( LightMap &lm , uchar ∗dst , s i z e t s t r i d e ) { const uchar ∗c = lm . data ; l o o p i (LM PACKH) { memcpy( dst , c , lm . bpp∗LM PACKW) ; c += lm . bpp∗LM PACKW; dst += s t r i d e ; } } VARF( convertlms , 0 , 1 , 1 , { cleanuplightmaps ( ) ; i n i t l i g h t s ( ) ; allchanged ( ) ; }) ; void genreservedlightmaptexs ( ) { while ( lightmaptexs . length ( ) < LMID RESERVED) { LightMapTexture &tex = lightmaptexs . add ( ) ; tex . type = renderpath ! = R FIXEDFUNCTION && lightmaptexs . length ( ) &1 ? LM DIFFUSE : LM BUMPMAP1; glGenTextures ( 1 , &tex . id ) ; } uchar u n l i t [ 3 ] = { ambientcolor [ 0 ] , ambientcolor [ 1 ] , ambientcolor [ 2 ] }; createtexture ( lightmaptexs [ LMID AMBIENT ] . id , 1 , 1 , unlit , 0 , 1) ; bvec f r o n t (128 , 128, 255) ; createtexture ( lightmaptexs [ LMID AMBIENT1 ] . id , 1 , 1 , &front , 0 , 1) ; uchar bright [ 3 ] = { uchar ( f u l l b r i g h t l e v e l ) , uchar ( f u l l b r i g h t l e v e l ) , uchar ( f u l l b r i g h t l e v e l ) }; createtexture ( lightmaptexs [ LMID BRIGHT ] . id , 1 , 1 , bright , 0 , 1) ; createtexture ( lightmaptexs [ LMID BRIGHT1 ] . id , 1 , 1 , &front , 0 , 1) ; uchar dark [ 3 ] = { 0 , 0 , 0 }; createtexture ( lightmaptexs [LMID DARK ] . id , 1 , 1 , dark , 0 , 1) ; createtexture ( lightmaptexs [LMID DARK1 ] . id , 1 , 1 , &front , 0 , 1) ; } s t a t i c void f i n d u n l i t ( i n t i ) { LightMap &lm = lightmaps [ i ] ; i f ( lm . unlitx >=0) return ; e l s e i f ( ( lm . type&LM TYPE ) ==LM BUMPMAP0) { i f ( i+1>=lightmaps . length ( ) || ( lightmaps [ i + 1 ] . type&LM TYPE ) ! = LM BUMPMAP1) return ; } e l s e i f ( ( lm . type&LM TYPE ) ! =LM DIFFUSE ) return ; uchar ∗data = lm . data ; loop ( y , 2) loop ( x , LM PACKW) { i f ( ! data [ 0 ] && ! data [ 1 ] && ! data [ 2 ] ) { memcpy( data , ambientcolor . v , 3) ; i f ( ( lm . type&LM TYPE ) ==LM BUMPMAP0) ( ( bvec ∗) lightmaps [ i + 1 ] . data ) [ y∗LM PACKW + x ] = bvec (128 , 128, 255) ; lm . u n l i t x = x ; lm . u n l i t y = y ; return ;

274

Foundations of Videogame Programming Code Repository

} i f ( data [0]== ambientcolor [ 0 ] && data [1]== ambientcolor [ 1 ] && data [2]== ambientcolor [ 2 ] ) { i f ( ( lm . type&LM TYPE ) ! =LM BUMPMAP0 || ( ( bvec ∗) lightmaps [ i + 1 ] . data ) [ y∗LM PACKW + x ] == bvec (128 , 128, 255) ) { lm . u n l i t x = x ; lm . u n l i t y = y ; return ; } } data += lm . bpp ;

{ tex . u n l i t x = o f f s e t x + lm . u n l i t x ; tex . u n l i t y = o f f s e t y + lm . u n l i t y ; } i f ( data ) { i f ( renderpath == R FIXEDFUNCTION && ( lm . type&LM TYPE ) == LM BUMPMAP0 && convertlms ) convertlightmap ( lm , lightmaps [ i +1] , &data [ bpp∗( o f f s e t y∗tex .w + o f f s e t x ) ] , bpp∗tex .w) ; e l s e copylightmap ( lm , &data [ bpp∗( o f f s e t y∗tex .w + o f f s e t x ) ] , bpp∗tex .w) ; }

} }

o f f s e t x += LM PACKW; i f ( o f f s e t x >= tex .w) { o f f s e t x = 0; o f f s e t y += LM PACKH; } i f ( o f f s e t y >= tex . h ) break ;

VARF( roundlightmaptex , 0 , 4 , 16, { cleanuplightmaps ( ) ; i n i t l i g h t s ( ) ; allchanged ( ) ; }) ; VARF( batchlightmaps , 0 , 4 , 256, { cleanuplightmaps ( ) ; i n i t l i g h t s ( ) ; allchanged ( ) ; }) ;

} glGenTextures ( 1 , &tex . id ) ; createtexture ( tex . id , tex .w, tex . h , data ? data : f i r s t l m−>data , 3 , 1 , bpp==4 ? GL RGBA : GL RGB) ; i f ( data ) d e l e t e [ ] data ;

void genlightmaptexs ( i n t flagmask , i n t f l a g v a l ) { i f ( lightmaptexs . length ( ) < LMID RESERVED) genreservedlightmaptexs ( ) ; } i n t remaining [ 3 ] = { 0 , 0 , 0 }, t o t a l = 0; loopv ( lightmaps ) { LightMap &lm = lightmaps [ i ] ; i f ( lm . tex >= 0 || ( lm . type&flagmask ) ! = f l a g v a l ) continue ; i n t type = lm . type&LM TYPE ; remaining [ type ] + + ; t o t a l ++; i f ( lm . u n l i t x < 0) f i n d u n l i t ( i ) ; } i f ( renderpath==R FIXEDFUNCTION ) { remaining [ LM DIFFUSE ] += remaining [LM BUMPMAP0] ; remaining [LM BUMPMAP0] = remaining [LM BUMPMAP1] = 0; } i n t s i z e l i m i t = ( maxtexsize ? min ( maxtexsize , hwtexsize ) : hwtexsize ) / max(LM PACKW, LM PACKH) ; s i z e l i m i t = min ( batchlightmaps , s i z e l i m i t∗s i z e l i m i t ) ; while ( t o t a l ) { i n t type = LM DIFFUSE; LightMap ∗f i r s t l m = NULL; loopv ( lightmaps ) { LightMap &lm = lightmaps [ i ] ; i f ( lm . tex >= 0 || ( lm . type&flagmask ) ! = f l a g v a l ) continue ; i f ( renderpath ! = R FIXEDFUNCTION ) type = lm . type&LM TYPE ; e l s e i f ( ( lm . type&LM TYPE ) == LM BUMPMAP1) continue ; f i r s t l m = &lm ; break ; } i f ( ! f i r s t l m ) break ; i n t used = 0 , uselimit = min ( remaining [ type ] , s i z e l i m i t ) ; do used++; while((1o ) . sub ( e . o ) . normalize ( ) ; f l o a t maxatten = sincos360 [ clamp ( i n t ( e . attached−>a t t r 1 ) , 1 , 89) ] . x , spotatten = ( ray . dot ( spot ) − maxatten ) / (1 − maxatten ) ; i f ( spotatten = f l o a t ( e . a t t r 1 ) ) continue ; i f (mag < 1e−4f ) ray = vec ( 0 , 0 , −1) ; else { ray . div (mag) ; i f ( shadowray ( e . o , ray , mag, RAY SHADOW | RAY POLY, t ) < mag) continue ; } f l o a t i n t e n s i t y = 1; i f ( e . attr1 ) i n t e n s i t y −= mag / f l o a t ( e . a t t r 1 ) ; i f ( e . attached && e . attached−>type==ET SPOTLIGHT ) { vec spot = vec ( e . attached−>o ) . sub ( e . o ) . normalize ( ) ; f l o a t maxatten = sincos360 [ clamp ( i n t ( e . attached−>a t t r 1 ) , 1 , 89) ] . x , spotatten = ( ray . dot ( spot ) − maxatten ) / (1 − maxatten ) ; i f ( spotatten o ) //{ // conoutf (CON DEBUG, ”%d − %f %f ” , i , i n t e n s i t y , mag) ; //} vec l i g h t c o l = vec ( e . attr2 , e . attr3 , e . a t t r 4 ) . mul( 1 . 0 f /255) ; c o l o r . add ( vec ( l i g h t c o l ) . mul ( i n t e n s i t y ) ) ; d i r . add ( vec ( ray ) . mul(−i n t e n s i t y∗l i g h t c o l . x∗l i g h t c o l . y∗l i g h t c o l . z ) ) ; }

engine/lightmap.h

275

i f ( ! b r i g h t e s t || i n t e n s i t y > b i n t e n s i t y ) { b r i g h t e s t = &e ; bintensity = intensity ; } } return b r i g h t e s t ; } void dumplms ( ) { loopv ( lightmaps ) { ImageData temp (LM PACKW, LM PACKH, lightmaps [ i ] . bpp , lightmaps [ i ] . data ) ; const char ∗map = game : : getclientmap ( ) , ∗name = s t r r c h r (map, ’ / ’ ) ; defformatstring ( buf ) ( ” lightmap %s %d . png ” , name ? name+1 : map, i ) ; savepng ( buf , temp , true ) ; } } COMMAND( dumplms, ” ” ) ;

276 #define #define #define #define #define #define

Foundations of Videogame Programming Code Repository LM MINW 2 LM MINH 2 LM MAXW 128 LM MAXH 128 LM PACKW 512 LM PACKH 512

i n t w, h , type ; GLuint id ; i n t unlitx , u n l i t y ; LightMapTexture ( ) : w( 0 ) , h ( 0 ) , type ( LM DIFFUSE ) , id ( 0 ) , u n l i t x (−1) , u n l i t y (−1) {}

struct PackNode { PackNode ∗child1 , ∗child2 ; ushort x , y , w, h ; int available ; PackNode ( ) : child1 ( 0 ) , child2 ( 0 ) , x ( 0 ) , y ( 0 ) , w(LM PACKW) , h (LM PACKH ) , a v a i l a b l e ( min (LM PACKW, LM PACKH) ) {} PackNode ( ushort x , ushort y , ushort w, ushort h ) : child1 ( 0 ) , child2 ( 0 ) , x ( x ) , y ( y ) , w(w) , h ( h ) , a v a i l a b l e ( min (w, h ) ) {} void c l e a r ( ) { DELETEP( child1 ) ; DELETEP( child2 ) ; } ˜PackNode ( ) { clear ( ) ; }

}; extern vector lightmaptexs ; extern bvec ambientcolor , s k y l i g h t c o l o r ; extern extern extern extern extern extern extern

void c l e a r l i g h t s ( ) ; void i n i t l i g h t s ( ) ; void l i g h t e n t s ( bool f o r c e = f a l s e ) ; void c l e a r l i g h t c a c h e ( i n t id = −1) ; void resetlightmaps ( bool f u l l c l e a n = true ) ; void brightencube ( cube &c ) ; void setsurfaces ( cube &c , const surfaceinfo ∗surfs , const v e r t i n f o ∗verts , i n t numverts ) ; extern void setsurface ( cube &c , i n t orient , const surfaceinfo &surf , const v e r t i n f o ∗verts , i n t numverts ) ; extern void previewblends ( const i v e c &bo , const i v e c &bs ) ;

struct l e r p v e r t { vec normal ; f l o a t u, v ;

bool i n s e r t ( ushort &tx , ushort &ty , ushort tw , ushort th ) ; bool operator ==( const l e r p v e r t &l ) const { return u == l . u && v == l . v ; } bool operator ! = ( const l e r p v e r t &l ) const { return u ! = l . u || v ! = l . v ; }

}; enum { LM DIFFUSE = 0 , LM BUMPMAP0, LM BUMPMAP1, LM TYPE = 0x0F ,

};

LM ALPHA = 1ys ) / ( sz∗tex−>xs ) , blend = pow ( clamp ( f l o a t ( l a s t m i l l i s − l a s t l n j i t t e r ) / l n j i t t e r m i l l i s , 0.0 f , 1.0 f ) , lnblendpower ) , j i t t e r 0 = (1−blend )∗l n j i t t e r s c a l e∗sz/ l n j i t t e r r a d i u s , j i t t e r 1 = blend∗l n j i t t e r s c a l e∗sz/ l n j i t t e r r a d i u s ; glBegin ( GL TRIANGLE STRIP ) ; l o o p j ( numsteps ) { vec next ( cur ) ; next . add ( step ) ; i f ( j +1==numsteps ) next = d ; else { i n t l j = ( j + j i t t e r o f f s e t )%MAXLIGHTNINGSTEPS; next . add ( vec ( r i g h t ) . mul ( ( j i t t e r 1∗l n j i t t e r x [ l n j i t t e r f r a m e ] [ l j ] + j i t t e r 0∗l n j i t t e r x [ l n j i t t e r f r a m e ˆ 1 ] [ l j ] ) ) ) ; next . add ( vec ( up ) . mul ( ( j i t t e r 1∗l n j i t t e r y [ l n j i t t e r f r a m e ] [ l j ] + j i t t e r 0∗l n j i t t e r y [ l n j i t t e r f r a m e ˆ 1 ] [ l j ] ) ) ) ; } vec dir1 = next , dir2 = next , across ; dir1 . sub ( cur ) ; dir2 . sub ( camera1−>o ) ; across . cross ( dir2 , dir1 ) . normalize ( ) . mul ( sz ) ; glTexCoord2f ( s c r o l l , 1) ; g l V e r t e x 3 f ( cur . x−across . x , cur . y−across . y , cur . z−across . z ) ; glTexCoord2f ( s c r o l l , 0) ; g l V e r t e x 3 f ( cur . x+across . x , cur . y+across . y , cur . z+across . z ) ;

277

} glEnd ( ) ; } struct lightningrenderer : l i s t r e n d e r e r { lightningrenderer ( ) : l i s t r e n d e r e r ( ” packages/ p a r t i c l e s / l i g h t n i n g . jpg ” , 2 , PT LIGHTNING| PT TRACK|PT GLARE ) {} void startrender ( ) { glDisable ( GL CULL FACE ) ; } void endrender ( ) { glEnable ( GL CULL FACE ) ; } void update ( ) { setuplightning ( ) ; } void seedemitter ( p a r t i c l e e m i t t e r &pe , const vec &o , const vec &d , i n t fade , f l o a t size , i n t g r a v i t y ) { pe . maxfade = max( pe . maxfade , fade ) ; pe . extendbb ( o , s i z e ) ; pe . extendbb ( d , s i z e ) ; } void renderpart ( l i s t p a r t i c l e ∗p , const vec &o , const vec &d , i n t blend , i n t ts , uchar ∗c o l o r ) { blend = min ( blend8, ( c o l o r [1]∗ blend )>>8, ( c o l o r [2]∗ blend )>>8); else glColor4ub ( c o l o r [ 0 ] , c o l o r [ 1 ] , c o l o r [ 2 ] , blend ) ; renderlightning ( tex , o , d , p−>s i z e ) ; } }; s t a t i c lightningrenderer l i g h t n i n g s ;

engine/main.cpp cleanup ( ) ; e x i t ( EXIT SUCCESS ) ;

// main . cpp : i n i t i a l i s a t i o n & main loop #include ” engine . h”

}

extern void cleargamma ( ) ;

void f a t a l ( const char ∗s , . . . ) { s t a t i c i n t errors = 0; errors ++;

void cleanup ( ) { recorder : : stop ( ) ; cleanupserver ( ) ; SDL ShowCursor ( 1 ) ; SDL WM GrabInput ( SDL GRAB OFF ) ; cleargamma ( ) ; f r e e o c t a ( worldroot ) ; extern void clear command ( ) ; clear command ( ) ; extern void c l e a r c o n s o l e ( ) ; c l e a r c o n s o l e ( ) ; extern void clear mdls ( ) ; clear mdls ( ) ; extern void clear sound ( ) ; clear sound ( ) ; closelogfile ( ) ; SDL Quit ( ) ; } void quit ( ) // normal e x i t { extern void w r i t e i n i t c f g ( ) ; writeinitcfg ( ) ; writeservercfg ( ) ; abortconnect ( ) ; disconnect ( ) ; localdisconnect ( ) ; writecfg ( ) ;

// f a i l u r e e x i t

i f ( errors p r i n t f ( ” f u l l s c r e e n %d\n” , f u l l s c r e e n ) ; f−>p r i n t f ( ” scr w %d\n” , scr w ) ; f−>p r i n t f ( ” scr h %d\n” , scr h ) ; f−>p r i n t f ( ” c o l o r b i t s %d\n” , c o l o r b i t s ) ; f−>p r i n t f ( ” depthbits %d\n” , depthbits ) ; f−>p r i n t f ( ” s t e n c i l b i t s %d\n” , s t e n c i l b i t s ) ; f−>p r i n t f ( ” fsaa %d\n” , fsaa ) ; f−>p r i n t f ( ” vsync %d\n” , vsync ) ; extern i n t useshaders , shaderprecision , f o r c e g l s l ; f−>p r i n t f ( ” shaders %d\n” , useshaders ) ; f−>p r i n t f ( ” shaderprecision %d\n” , shaderprecision ) ; f−>p r i n t f ( ” f o r c e g l s l %d\n” , f o r c e g l s l ) ; extern i n t soundchans , soundfreq , soundbufferlen ; f−>p r i n t f ( ” soundchans %d\n” , soundchans ) ; f−>p r i n t f ( ” soundfreq %d\n” , soundfreq ) ; f−>p r i n t f ( ” soundbufferlen %d\n” , soundbufferlen ) ; delete f ; } COMMAND( quit , ” ” ) ; s t a t i c void getbackgroundres ( i n t &w, i n t &h ) { f l o a t wk = 1 , hk = 1; i f (w < 1024) wk = 1024.0 f /w; i f ( h < 768) hk = 768.0 f /h ; wk = hk = max( wk, hk ) ; w = i n t ( c e i l (w∗wk) ) ; h = i n t ( c e i l ( h∗hk ) ) ; } s t r i n g backgroundcaption = ” ” ; Texture ∗backgroundmapshot = NULL; s t r i n g backgroundmapname = ” ” ; char ∗backgroundmapinfo = NULL; void restorebackground ( ) { i f ( renderedframe ) return ; renderbackground ( backgroundcaption [ 0 ] ? backgroundcaption : NULL, backgroundmapshot , backgroundmapname [ 0 ] ? backgroundmapname : NULL, backgroundmapinfo , true ) ; } void renderbackground ( const char ∗caption , Texture ∗mapshot , const char ∗ mapname, const char ∗mapinfo , bool restore , bool f o r c e ) { i f ( ! inbetweenframes && ! f o r c e ) return ; stopsounds ( ) ; // stop sounds while loading i n t w = screen−>w, h = screen−>h ; i f ( forceaspect ) w = i n t ( c e i l ( h∗forceaspect ) ) ;

defaultshader−>set ( ) ; glEnable ( GL TEXTURE 2D ) ; s t a t i c i n t lastupdate = −1, lastw = −1, lasth = −1; s t a t i c f l o a t backgroundu = 0 , backgroundv = 0 , d e t a i l u = 0 , d e t a i l v = 0; s t a t i c i n t numdecals = 0; s t a t i c struct decal { f l o a t x , y , s i z e ; i n t side ; } decals [ 1 2 ] ; i f ( ( renderedframe && !mainmenu && lastupdate ! = l a s t m i l l i s ) || lastw ! = w || lasth ! = h ) { lastupdate = l a s t m i l l i s ; lastw = w; lasth = h ; backgroundu = rndscale ( 1 ) ; backgroundv = rndscale ( 1 ) ; d e t a i l u = rndscale ( 1 ) ; d e t a i l v = rndscale ( 1 ) ; numdecals = s i z e o f ( decals ) / s i z e o f ( decals [ 0 ] ) ; numdecals = numdecals/3 + rnd ( ( numdecals∗2)/3 + 1) ; f l o a t maxsize = min (w, h ) /16.0 f ; l o o p i ( numdecals ) { decal d = { rndscale (w) , rndscale ( h ) , maxsize/2 + rndscale ( maxsize /2) , rnd ( 2 ) }; decals [ i ] = d ; } } e l s e i f ( lastupdate ! = l a s t m i l l i s ) lastupdate = l a s t m i l l i s ; l o o p i ( r e s t o r e ? 1 : 3) { g l C o l o r 3 f ( 1 , 1 , 1) ; s e t t e x t u r e ( ” data/background . png ” , 0) ; f l o a t bu = w∗0.67 f /256.0 f + backgroundu , bv = h∗0.67 f /256.0 f + backgroundv ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( 0 , 0) ; glTexCoord2f ( bu, 0) ; g l V e r t e x 2 f (w, 0) ; glTexCoord2f ( 0 , bv ) ; g l V e r t e x 2 f ( 0 , h ) ; glTexCoord2f ( bu, bv ) ; g l V e r t e x 2 f (w, h ) ; glEnd ( ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glEnable (GL BLEND) ; s e t t e x t u r e ( ” data/background detail . png ” , 0) ; f l o a t du = w∗0.8 f /512.0 f + detailu , dv = h∗0.8 f /512.0 f + d e t a i l v ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( 0 , 0) ; glTexCoord2f ( du, 0) ; g l V e r t e x 2 f (w, 0) ; glTexCoord2f ( 0 , dv ) ; g l V e r t e x 2 f ( 0 , h ) ; glTexCoord2f ( du, dv ) ; g l V e r t e x 2 f (w, h ) ; glEnd ( ) ; s e t t e x t u r e ( ” data/background decal . png ” , 3) ; glBegin (GL QUADS) ; l o o p j ( numdecals ) { f l o a t hsz = decals [ j ] . size , hx = clamp ( decals [ j ] . x , hsz , w−hsz ) , hy = clamp ( decals [ j ] . y , hsz , h−hsz ) , side = decals [ j ] . side ; glTexCoord2f ( side , 0) ; g l V e r t e x 2 f ( hx−hsz , hy−hsz ) ; glTexCoord2f(1−side , 0) ; g l V e r t e x 2 f ( hx+hsz , hy−hsz ) ; glTexCoord2f(1−side , 1) ; g l V e r t e x 2 f ( hx+hsz , hy+hsz ) ; glTexCoord2f ( side , 1) ; g l V e r t e x 2 f ( hx−hsz , hy+hsz ) ; } glEnd ( ) ; f l o a t lh = 0.5 f∗min (w, h ) , lw = lh ∗2, l x = 0.5 f ∗(w − lw ) , l y = 0.5 f ∗(h∗0.5 f − lh ) ; s e t t e x t u r e ( ( maxtexsize ? min ( maxtexsize , hwtexsize ) : hwtexsize ) >= 1024 && ( screen−>w > 1280 || screen−>h > 800) ? ” data/ logo 1024 . png” : ” data/logo . png ” , 3) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( lx , ly ) ; glTexCoord2f ( 1 , 0) ; g l V e r t e x 2 f ( l x +lw , l y ) ; glTexCoord2f ( 0 , 1) ; g l V e r t e x 2 f ( lx , l y +lh ) ; glTexCoord2f ( 1 , 1) ; g l V e r t e x 2 f ( l x +lw , l y +lh ) ; glEnd ( ) ; i f ( caption ) { i n t tw = text width ( caption ) ; f l o a t t s z = 0.04 f∗min (w, h ) /FONTH, tx = 0.5 f ∗(w − tw∗t s z ) , ty = h − 0.075 f ∗1.5 f∗min (w, h ) − 1.25 f∗FONTH∗t s z ; glPushMatrix ( ) ;

engine/main.cpp g l T r a n s l a t e f ( tx , ty , 0) ; g l S c a l e f ( tsz , tsz , 1) ; draw text ( caption , 0 , 0) ; glPopMatrix ( ) ; } i f ( mapshot || mapname) { i n t infowidth = 12∗FONTH; f l o a t sz = 0.35 f∗min (w, h ) , msz = (0.75 f∗min (w, h ) − sz ) / ( infowidth + FONTH) , x = 0.5 f ∗(w−sz ) , y = l y +lh − sz /15; i f ( mapinfo ) { i n t mw, mh; text bounds ( mapinfo , mw, mh, infowidth ) ; x −= 0.5 f ∗(mw∗msz + FONTH∗msz ) ; } i f ( mapshot && mapshot! = notexture ) { glBindTexture ( GL TEXTURE 2D, mapshot−>id ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2f ( 1 , 0) ; g l V e r t e x 2 f ( x+sz , y ) ; glTexCoord2f ( 0 , 1) ; g l V e r t e x 2 f ( x , y+sz ) ; glTexCoord2f ( 1 , 1) ; g l V e r t e x 2 f ( x+sz , y+sz ) ; glEnd ( ) ; } else { i n t qw, qh ; text bounds ( ” ? ” , qw, qh ) ; f l o a t qsz = sz∗0.5 f /max( qw, qh ) ; glPushMatrix ( ) ; g l T r a n s l a t e f ( x + 0.5 f ∗( sz − qw∗qsz ) , y + 0.5 f ∗( sz − qh∗qsz ) , 0) ; g l S c a l e f ( qsz , qsz , 1) ; draw text ( ” ? ” , 0 , 0) ; glPopMatrix ( ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } s e t t e x t u r e ( ” data/mapshot frame . png ” , 3) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2f ( 1 , 0) ; g l V e r t e x 2 f ( x+sz , y ) ; glTexCoord2f ( 0 , 1) ; g l V e r t e x 2 f ( x , y+sz ) ; glTexCoord2f ( 1 , 1) ; g l V e r t e x 2 f ( x+sz , y+sz ) ; glEnd ( ) ; i f (mapname) { i n t tw = text width (mapname) ; f l o a t t s z = sz/(8∗FONTH) , tx = 0.9 f∗sz − tw∗tsz , ty = 0.9 f∗sz − FONTH∗t s z ; i f ( tx < 0.1 f∗sz ) { t s z = 0.1 f∗sz/tw ; tx = 0.1 f ; } glPushMatrix ( ) ; g l T r a n s l a t e f ( x+tx , y+ty , 0) ; g l S c a l e f ( tsz , tsz , 1) ; draw text (mapname, 0 , 0) ; glPopMatrix ( ) ; } i f ( mapinfo ) { glPushMatrix ( ) ; g l T r a n s l a t e f ( x+sz+FONTH∗msz, y , 0) ; g l S c a l e f ( msz, msz, 1) ; draw text ( mapinfo , 0 , 0 , 0xFF , 0xFF , 0xFF , 0xFF , −1, infowidth ) ; glPopMatrix ( ) ; } } glDisable (GL BLEND) ; i f ( ! r e s t o r e ) swapbuffers ( ) ; } glDisable ( GL TEXTURE 2D ) ; i f ( ! restore ) { renderedframe = f a l s e ; copystring ( backgroundcaption , caption ? caption : ” ” ) ; backgroundmapshot = mapshot ; copystring ( backgroundmapname, mapname ? mapname : ” ” ) ; i f ( mapinfo ! = backgroundmapinfo ) { DELETEA( backgroundmapinfo ) ; i f ( mapinfo ) backgroundmapinfo = newstring ( mapinfo ) ; } }

279

clientkeepalive ( ) ; // make sure our connection doesn ’ t time out while loading maps etc . APPLE #ifdef interceptkey (SDLK UNKNOWN) ; // keep the event queue awake to avoid ’ beachball ’ cursor #endif extern i n t sdl backingstore bug ; i f ( background || sdl backingstore bug > 0) restorebackground ( ) ; i n t w = screen−>w, h = screen−>h ; i f ( forceaspect ) w = i n t ( c e i l ( h∗forceaspect ) ) ; getbackgroundres (w, h ) ; g e t t e x t r e s (w, h ) ; glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glOrtho ( 0 , w, h , 0 , −1, 1) ; glMatrixMode (GL MODELVIEW) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; g l C o l o r 3 f ( 1 , 1 , 1) ; f l o a t fh = 0.075 f∗min (w, h ) , fw = fh∗10, f x = renderedframe ? w − fw − fh/4 : 0.5 f ∗(w − fw ) , f y = renderedframe ? fh/4 : h − fh∗1.5 f , fu1 = 0/512.0 f , fu2 = 511/512.0 f , fv1 = 0/64.0 f , fv2 = 52/64.0 f ; s e t t e x t u r e ( ” data/loading frame . png ” , 3) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( fu1 , fv1 ) ; g l V e r t e x 2 f ( fx , fy ) ; glTexCoord2f ( fu2 , fv1 ) ; g l V e r t e x 2 f ( f x +fw , f y ) ; glTexCoord2f ( fu1 , fv2 ) ; g l V e r t e x 2 f ( fx , f y +fh ) ; glTexCoord2f ( fu2 , fv2 ) ; g l V e r t e x 2 f ( f x +fw , f y +fh ) ; glEnd ( ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; f l o a t bw = fw∗(511 − 2∗17)/511.0 f , bh = fh∗20/52.0f , bx = f x + fw∗17/511.0f , by = f y + fh∗16/52.0f , bv1 = 0/32.0 f , bv2 = 20/32.0 f , su1 = 0/32.0 f , su2 = 7/32.0 f , sw = fw∗7/511.0f , eu1 = 23/32.0 f , eu2 = 30/32.0 f , ew = fw∗7/511.0f , mw = bw − sw − ew, ex = bx+sw + max(mw∗bar , fw∗7/511.0 f ) ; i f ( bar > 0) { s e t t e x t u r e ( ” data/loading bar . png ” , 3) ; glBegin (GL QUADS) ; glTexCoord2f ( su1 , bv1 ) ; g l V e r t e x 2 f ( bx , by ) ; glTexCoord2f ( su2 , bv1 ) ; g l V e r t e x 2 f ( bx+sw, by ) ; glTexCoord2f ( su2 , bv2 ) ; g l V e r t e x 2 f ( bx+sw, by+bh ) ; glTexCoord2f ( su1 , bv2 ) ; g l V e r t e x 2 f ( bx , by+bh ) ; glTexCoord2f ( su2 , glTexCoord2f ( eu1 , glTexCoord2f ( eu1 , glTexCoord2f ( su2 ,

bv1 ) ; bv1 ) ; bv2 ) ; bv2 ) ;

g l V e r t e x 2 f ( bx+sw, by ) ; g l V e r t e x 2 f ( ex , by ) ; g l V e r t e x 2 f ( ex , by+bh ) ; g l V e r t e x 2 f ( bx+sw, by+bh ) ;

glTexCoord2f ( eu1 , glTexCoord2f ( eu2 , glTexCoord2f ( eu2 , glTexCoord2f ( eu1 , glEnd ( ) ;

bv1 ) ; bv1 ) ; bv2 ) ; bv2 ) ;

g l V e r t e x 2 f ( ex , by ) ; g l V e r t e x 2 f ( ex+ew, by ) ; g l V e r t e x 2 f ( ex+ew, by+bh ) ; g l V e r t e x 2 f ( ex , by+bh ) ;

} i f ( text ) { i n t tw = text width ( t e x t ) ; f l o a t t s z = bh∗0.8 f /FONTH; i f ( tw∗t s z > mw) t s z = mw/tw ; glPushMatrix ( ) ; g l T r a n s l a t e f ( bx+sw, by + ( bh − FONTH∗t s z ) /2 , 0) ; g l S c a l e f ( tsz , tsz , 1) ; draw text ( text , 0 , 0) ; glPopMatrix ( ) ; } glDisable (GL BLEND) ;

} f l o a t loadprogress = 0; void renderprogress ( f l o a t bar , const char ∗text , GLuint tex , bool background ) // also used during loading { i f ( ! inbetweenframes || envmapping ) return ;

i f ( tex ) { glBindTexture ( GL TEXTURE 2D, tex ) ; f l o a t sz = 0.35 f∗min (w, h ) , x = 0.5 f ∗(w−sz ) , y = 0.5 f∗min (w, h ) − sz /15; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2f ( 1 , 0) ; g l V e r t e x 2 f ( x+sz , y ) ;

280

Foundations of Videogame Programming Code Repository

glTexCoord2f ( 0 , 1) ; g l V e r t e x 2 f ( x , y+sz ) ; glTexCoord2f ( 1 , 1) ; g l V e r t e x 2 f ( x+sz , y+sz ) ; glEnd ( ) ;

f l o a t f = gamma/100.0 f ; i f ( SDL SetGamma ( f , f , f ) ==−1) conoutf (CON ERROR, ” Could not set gamma: % s ” , SDL GetError ( ) ) ; }) ;

glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; s e t t e x t u r e ( ” data/mapshot frame . png ” , 3) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2f ( 1 , 0) ; g l V e r t e x 2 f ( x+sz , y ) ; glTexCoord2f ( 0 , 1) ; g l V e r t e x 2 f ( x , y+sz ) ; glTexCoord2f ( 1 , 1) ; g l V e r t e x 2 f ( x+sz , y+sz ) ; glEnd ( ) ; glDisable (GL BLEND) ; } glDisable ( GL TEXTURE 2D ) ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ; swapbuffers ( ) ; } void keyrepeat ( bool on ) { SDL EnableKeyRepeat ( on ? SDL DEFAULT REPEAT DELAY : 0 , SDL DEFAULT REPEAT INTERVAL ) ; } bool grabinput = f a l s e , minimized = f a l s e ; void inputgrab ( bool on ) { # i f n d e f WIN32 i f ( ! ( screen−>f l a g s & SDL FULLSCREEN) ) SDL WM GrabInput ( SDL GRAB OFF ) ; else #endif SDL WM GrabInput ( on ? SDL GRAB ON : SDL GRAB OFF ) ; SDL ShowCursor ( on ? SDL DISABLE : SDL ENABLE) ; } void s e t f u l l s c r e e n ( bool enable ) { i f ( ! screen ) return ; # i f defined ( WIN32 ) || defined ( APPLE ) initwarning ( enable ? ” f u l l s c r e e n ” : ”windowed ” ) ; #e l s e i f ( enable == ! ( screen−>f l a g s&SDL FULLSCREEN) ) { SDL WM ToggleFullScreen ( screen ) ; inputgrab ( grabinput ) ; } #endif } # i f d e f DEBUG VARF( f u l l s c r e e n , 0 , 0 , 1 , s e t f u l l s c r e e n ( f u l l s c r e e n ! = 0 ) ) ; #e l s e VARF( f u l l s c r e e n , 0 , 1 , 1 , s e t f u l l s c r e e n ( f u l l s c r e e n ! = 0 ) ) ; #endif void screenres ( i n t ∗w, i n t ∗h ) { # i f ! defined ( WIN32 ) && ! defined ( APPLE ) i f ( i n i t i n g >= INIT RESET ) { #endif scr w = clamp(∗w, SCR MINW, SCR MAXW) ; scr h = clamp(∗h , SCR MINH, SCR MAXH) ; # i f defined ( WIN32 ) || defined ( APPLE ) initwarning ( ” screen resolution ” ) ; #e l s e return ; } SDL Surface ∗surf = SDL SetVideoMode ( clamp(∗w, SCR MINW, SCR MAXW) , clamp(∗h , SCR MINH, SCR MAXH) , 0 , SDL OPENGL| ( screen−>f l a g s& SDL FULLSCREEN ? SDL FULLSCREEN : SDL RESIZABLE ) ) ; i f ( ! surf ) return ; screen = surf ; scr w = screen−>w; scr h = screen−>h ; glViewport ( 0 , 0 , scr w , scr h ) ; #endif } COMMAND( screenres , ” i i ” ) ; s t a t i c i n t curgamma = 100; VARFP(gamma, 30, 100, 300, { i f (gamma == curgamma ) return ; curgamma = gamma;

void restoregamma ( ) { i f ( curgamma == 100) return ; f l o a t f = curgamma/100.0 f ; SDL SetGamma( 1 , 1 , 1) ; SDL SetGamma ( f , f , f ) ; } void cleargamma ( ) { i f ( curgamma ! = 100) SDL SetGamma( 1 , 1 , 1) ; } VAR( dbgmodes , 0 , 0 , 1) ; i n t desktopw = 0 , desktoph = 0; void setupscreen ( i n t &usedcolorbits , i n t &useddepthbits , i n t &usedfsaa ) { i n t f l a g s = SDL RESIZABLE ; # i f defined ( WIN32 ) || defined ( APPLE ) f l a g s = 0; #endif i f ( f u l l s c r e e n ) f l a g s = SDL FULLSCREEN; SDL Rect ∗∗modes = SDL ListModes (NULL, SDL OPENGL| f l a g s ) ; i f ( modes && modes ! = ( SDL Rect ∗∗)−1) { i n t widest = −1, best = −1; f o r ( i n t i = 0; modes [ i ] ; i ++) { i f ( dbgmodes ) conoutf (CON DEBUG, ”mode[%d ] : %d x %d ” , i , modes [ i ]−>w, modes [ i]−>h ) ; i f ( widest < 0 || modes [ i]−>w > modes [ widest]−>w || ( modes [ i]−>w == modes [ widest]−>w && modes [ i]−>h > modes [ widest]−>h ) ) widest = i ; } i f ( scr w < 0 || scr h < 0) { i n t w = scr w , h = scr h , ratiow = desktopw , ratioh = desktoph ; i f (w < 0 && h < 0) { w = SCR DEFAULTW; h = SCR DEFAULTH; } i f ( ratiow h ; } f o r ( i n t i = 0; modes [ i ] ; i ++) i f ( modes [ i]−>w∗ratioh == modes [ i ]−>h∗ratiow ) { i f (w w && h h && ( best < 0 || modes [ i]−>w < modes [ best]−>w) ) best = i ; } } i f ( best < 0) { i n t w = scr w , h = scr h ; i f (w < 0 && h < 0) { w = SCR DEFAULTW; h = SCR DEFAULTH; } e l s e i f (w < 0) w = ( h∗SCR DEFAULTW) /SCR DEFAULTH; e l s e i f ( h < 0) h = (w∗SCR DEFAULTH) /SCR DEFAULTW; f o r ( i n t i = 0; modes [ i ] ; i ++) { i f (w w && h h && ( best < 0 || modes [ i]−>w < modes [ best]−>w || ( modes [ i]−>w == modes [ best ]−>w && modes [ i]−>h < modes [ best]−>h ) ) ) best = i ; } } i f ( f l a g s&SDL FULLSCREEN) { i f ( best >= 0) { scr w = modes [ best]−>w; scr h = modes [ best]−>h ; } e l s e i f ( desktopw > 0 && desktoph > 0) { scr w = desktopw ; scr h = desktoph ; } e l s e i f ( widest >= 0) { scr w = modes [ widest]−>w; scr h = modes [ widest]−>h ; } } e l s e i f ( best < 0) { scr w = min ( scr w >= 0 ? scr w : ( scr h >= 0 ? ( scr h∗ SCR DEFAULTW) /SCR DEFAULTH : SCR DEFAULTW) , ( i n t ) modes [ widest]−>w) ; scr h = min ( scr h >= 0 ? scr h : ( scr w >= 0 ? ( scr w∗ SCR DEFAULTH) /SCR DEFAULTW : SCR DEFAULTH) , ( i n t ) modes [ widest]−>h ) ; } i f ( dbgmodes ) conoutf (CON DEBUG, ” s e l e c t e d %d x %d ” , scr w , scr h ) ; } i f ( scr w < 0 && scr h < 0) { scr w = SCR DEFAULTW; scr h = SCR DEFAULTH; } e l s e i f ( scr w < 0) scr w = ( scr h∗SCR DEFAULTW) /SCR DEFAULTH; e l s e i f ( scr h < 0) scr h = ( scr w∗SCR DEFAULTH) /SCR DEFAULTW;

engine/main.cpp bool hasbpp = true ; i f ( colorbits ) hasbpp = SDL VideoModeOK ( scr w , scr h , c o l o r b i t s , SDL OPENGL| f l a g s ) == c o l o r b i t s ; SDL GL SetAttribute (SDL GL DOUBLEBUFFER, 1) ; # i f SDL VERSION ATLEAST( 1 , 2 , 11) i f ( vsync>=0) SDL GL SetAttribute (SDL GL SWAP CONTROL, vsync ) ; #endif s t a t i c i n t configs [ ] = { 0x7 , /∗ t r y everything ∗/ 0x6 , 0x5 , 0x3 , /∗ t r y disabling one at a time ∗/ 0x4 , 0x2 , 0x1 , /∗ t r y disabling two at a time ∗/ 0 /∗ t r y disabling everything ∗/ }; i n t c o n f i g = 0; SDL GL SetAttribute ( SDL GL STENCIL SIZE , 0) ; i f ( ! depthbits ) SDL GL SetAttribute ( SDL GL DEPTH SIZE , 16) ; i f ( ! fsaa ) { SDL GL SetAttribute (SDL GL MULTISAMPLEBUFFERS, 0) ; SDL GL SetAttribute (SDL GL MULTISAMPLESAMPLES, 0) ; } l o o p i ( s i z e o f ( configs ) / s i z e o f ( configs [ 0 ] ) ) { c o n f i g = configs [ i ] ; i f ( ! depthbits && c o n f i g &1) continue ; i f ( ! s t e n c i l b i t s && c o n f i g &2) continue ; i f ( fsaa0) { SDL GL SetAttribute (SDL GL MULTISAMPLEBUFFERS, c o n f i g&4 ? 1 : 0) ; SDL GL SetAttribute (SDL GL MULTISAMPLESAMPLES, c o n f i g&4 ? fsaa : 0) ; } screen = SDL SetVideoMode ( scr w , scr h , hasbpp ? c o l o r b i t s : 0 , SDL OPENGL| f l a g s ) ; i f ( screen ) break ; } i f ( ! screen ) f a t a l ( ” Unable to create OpenGL screen : %s ” , SDL GetError ( ) ); else { i f ( ! hasbpp ) conoutf (CON WARN, ”%d b i t c o l o r b u f f e r not supported − disabling ” , c o l o r b i t s ) ; i f ( depthbits && ( c o n f i g &1)==0) conoutf (CON WARN, ”%d b i t z−b uf f e r not supported − disabling ” , depthbits ) ; i f ( s t e n c i l b i t s && ( c o n f i g &2)==0) conoutf (CON WARN, ” S t e n c i l b u f f e r not supported − disabling ” ) ; i f ( fsaa>0 && ( c o n f i g &4)==0) conoutf (CON WARN, ”%dx anti−a l i a s i n g not supported − disabling ” , fsaa ) ; } scr w = screen−>w; scr h = screen−>h ; usedcolorbits = hasbpp ? c o l o r b i t s : 0; useddepthbits = c o n f i g&1 ? depthbits : 0; usedfsaa = c o n f i g&4 ? fsaa : 0; } void r e s e t g l ( ) { clearchanges (CHANGE GFX) ; renderbackground ( ” r e s e t t i n g OpenGL ” ) ; extern void cleanupva ( ) ; extern void cleanupparticles ( ) ; extern void cleanupsky ( ) ; extern void cleanupmodels ( ) ; extern void cleanuptextures ( ) ; extern void cleanuplightmaps ( ) ; extern void cleanupblendmap ( ) ; extern void cleanshadowmap ( ) ; extern void c l e a n r e f l e c t i o n s ( ) ; extern void cleanupglare ( ) ; extern void cleanupdepthfx ( ) ; extern void cleanupshaders ( ) ; extern void cleanupgl ( ) ; recorder : : cleanup ( ) ; cleanupva ( ) ; cleanupparticles ( ) ; cleanupsky ( ) ;

281

cleanupmodels ( ) ; cleanuptextures ( ) ; cleanuplightmaps ( ) ; cleanupblendmap ( ) ; cleanshadowmap ( ) ; cleanreflections ( ) ; cleanupglare ( ) ; cleanupdepthfx ( ) ; cleanupshaders ( ) ; cleanupgl ( ) ; SDL SetVideoMode ( 0 , 0 , 0 , 0) ; i n t usedcolorbits = 0 , useddepthbits = 0 , usedfsaa = 0; setupscreen ( usedcolorbits , useddepthbits , usedfsaa ) ; g l i n i t ( scr w , scr h , usedcolorbits , useddepthbits , usedfsaa ) ; extern void reloadfonts ( ) ; extern void reloadtextures ( ) ; extern void reloadshaders ( ) ; inbetweenframes = f a l s e ; i f ( ! reloadtexture (∗ notexture ) || ! reloadtexture ( ” data/logo . png ” ) || ! reloadtexture ( ” data/logo 1024 . png ” ) || ! reloadtexture ( ” data/background . png ” ) || ! reloadtexture ( ” data/background detail . png ” ) || ! reloadtexture ( ” data/background decal . png ” ) || ! reloadtexture ( ” data/mapshot frame . png ” ) || ! reloadtexture ( ” data/loading frame . png ” ) || ! reloadtexture ( ” data/loading bar . png ” ) ) f a t a l ( ” f a i l e d to reload core texture ” ) ; reloadfonts ( ) ; inbetweenframes = true ; renderbackground ( ” i n i t i a l i z i n g . . . ” ) ; restoregamma ( ) ; reloadshaders ( ) ; reloadtextures ( ) ; initlights ( ) ; allchanged ( true ) ; } COMMAND( r e s e t g l , ” ” ) ; vector events ; void pushevent ( const SDL Event &e ) { events . add ( e ) ; } s t a t i c bool f i l t e r e v e n t ( const SDL Event &event ) { switch ( event . type ) { case SDL MOUSEMOTION: # i f n d e f WIN32 i f ( grabinput && ! ( screen−>f l a g s&SDL FULLSCREEN) ) { i f ( event . motion . x == screen−>w / 2 && event . motion . y == screen−>h / 2) return f a l s e ; // ignore any motion events generated by SDL WarpMouse #ifdef APPLE i f ( event . motion . y == 0) return f a l s e ; // l e t mac users drag windows v i a the t i t l e bar #endif } #endif break ; } return true ; } s t a t i c i n l i n e bool p o l l e v e n t ( SDL Event &event ) { while ( SDL PollEvent(& event ) ) { i f ( f i l t e r e v e n t ( event ) ) return true ; } return f a l s e ; } bool interceptkey ( i n t sym ) { s t a t i c i n t l a s t i n t e r c e p t = SDLK UNKNOWN; i n t len = l a s t i n t e r c e p t == sym ? events . length ( ) : 0; SDL Event event ; while ( p o l l e v e n t ( event ) ) { switch ( event . type ) { case SDL MOUSEMOTION: break ; d e f a u l t : pushevent ( event ) ; break ;

282

Foundations of Videogame Programming Code Repository

} } l a s t i n t e r c e p t = sym; i f ( sym ! = SDLK UNKNOWN) f o r ( i n t i = len ; i < events . length ( ) ; i ++) { i f ( events [ i ] . type == SDL KEYDOWN && events [ i ] . key . keysym .sym == sym ) { events . remove ( i ) ; return true ; } } return f a l s e ;

checkmousemotion ( dx , dy ) ; i f ( ! g3d movecursor ( dx , dy ) ) mousemove ( dx , dy ) ; mousemoved = true ; } break ; case SDL MOUSEBUTTONDOWN: case SDL MOUSEBUTTONUP: i f ( l a s t t y p e ==event . type && lastbut==event . button . button ) break ; // why?? get event twice without i t keypress(−event . button . button , event . button . s t a t e !=0 , 0) ; l a s t t y p e = event . type ; lastbut = event . button . button ; break ;

} s t a t i c void ignoremousemotion ( ) { SDL Event e ; SDL PumpEvents ( ) ; while ( SDL PeepEvents(&e , 1 , SDL GETEVENT, SDL EVENTMASK( SDL MOUSEMOTION) ) ) ; } s t a t i c void resetmousemotion ( ) { # i f n d e f WIN32 i f ( grabinput && ! ( screen−>f l a g s&SDL FULLSCREEN) ) { SDL WarpMouse ( screen−>w / 2 , screen−>h / 2) ; } #endif } s t a t i c void checkmousemotion ( i n t &dx , i n t &dy ) { loopv ( events ) { SDL Event &event = events [ i ] ; i f ( event . type ! = SDL MOUSEMOTION) { i f ( i > 0) events . remove ( 0 , i ) ; return ; } dx += event . motion . x r e l ; dy += event . motion . y r e l ; } events . s e t s i z e ( 0 ) ; SDL Event event ; while ( p o l l e v e n t ( event ) ) { i f ( event . type ! = SDL MOUSEMOTION) { events . add ( event ) ; return ; } dx += event . motion . x r e l ; dy += event . motion . y r e l ; } } void checkinput ( ) { SDL Event event ; i n t l a s t t y p e = 0 , lastbut = 0; bool mousemoved = f a l s e ; while ( events . length ( ) || p o l l e v e n t ( event ) ) { i f ( events . length ( ) ) event = events . remove ( 0 ) ; switch ( event . type ) { case SDL QUIT : quit ( ) ; return ; # i f ! defined ( WIN32 ) && ! defined ( APPLE ) case SDL VIDEORESIZE : screenres (& event . r e s i z e .w, &event . r e s i z e . h ) ; break ; #endif case SDL KEYDOWN: case SDL KEYUP: keypress ( event . key . keysym . sym, event . key . s t a t e ==SDL PRESSED, uni2cube ( event . key . keysym . unicode ) ) ; break ; case SDL ACTIVEEVENT: i f ( event . a c t i v e . s t a t e & SDL APPINPUTFOCUS ) inputgrab ( grabinput = event . a c t i v e . gain ! = 0 ) ; i f ( event . a c t i v e . s t a t e & SDL APPACTIVE ) minimized = ! event . a c t i v e . gain ; break ; case SDL MOUSEMOTION: i f ( grabinput ) { i n t dx = event . motion . xrel , dy = event . motion . y r e l ;

} } i f ( mousemoved ) resetmousemotion ( ) ; } void swapbuffers ( ) { recorder : : capture ( ) ; SDL GL SwapBuffers ( ) ; } VAR( menufps , 0 , 60, 1000) ; VARP( maxfps , 0 , 200, 1000) ; void l i m i t f p s ( i n t &m i l l i s , i n t c u r m i l l i s ) { i n t l i m i t = (mainmenu || minimized ) && menufps ? ( maxfps ? min ( maxfps , menufps ) : menufps ) : maxfps ; i f ( ! l i m i t ) return ; s t a t i c i n t f p s e r r o r = 0; i n t delay = 1000/ l i m i t − ( m i l l i s−c u r m i l l i s ) ; i f ( delay < 0) f p s e r r o r = 0; else { f p s e r r o r += 1000%l i m i t ; i f ( f p s e r r o r >= l i m i t ) { ++delay ; f p s e r r o r −= l i m i t ; } i f ( delay > 0) { SDL Delay ( delay ) ; m i l l i s += delay ; } } } # i f defined ( WIN32 ) && ! defined ( DEBUG) && ! defined ( GNUC ) void stackdumper ( unsigned i n t type , EXCEPTION POINTERS ∗ep ) { i f ( ! ep ) f a t a l ( ” unknown type ” ) ; EXCEPTION RECORD ∗er = ep−>ExceptionRecord ; CONTEXT ∗context = ep−>ContextRecord ; s t r i n g out , t ; formatstring ( out ) ( ” Cube 2: Sauerbraten Win32 Exception : 0x%x [0 x%x]\n\ n” , er−>ExceptionCode , er−>ExceptionCode== EXCEPTION ACCESS VIOLATION ? er−>ExceptionInformation [ 1 ] : −1) ; S y m I n i t i a l i z e ( GetCurrentProcess ( ) , NULL, TRUE) ; # i f d e f AMD64 STACKFRAME64 s f = {{context−>Rip , 0 , AddrModeFlat}, {}, {context −>Rbp, 0 , AddrModeFlat}, {context−>Rsp , 0 , AddrModeFlat}, 0}; while ( : : StackWalk64 ( IMAGE FILE MACHINE AMD64, GetCurrentProcess ( ) , GetCurrentThread ( ) , &sf , context , NULL, : : SymFunctionTableAccess , : : SymGetModuleBase , NULL) ) { union { IMAGEHLP SYMBOL64 sym; char symext [ s i z e o f ( IMAGEHLP SYMBOL64) + s i z e o f ( s t r i n g ) ] ; }; sym. SizeOfStruct = s i z e o f ( sym ) ; sym. MaxNameLength = s i z e o f ( symext ) − s i z e o f ( sym ) ; IMAGEHLP LINE64 l i n e ; l i n e . SizeOfStruct = s i z e o f ( l i n e ) ; DWORD64 symoff ; DWORD l i n e o f f ; i f ( SymGetSymFromAddr64 ( GetCurrentProcess ( ) , s f . AddrPC . Offset , & symoff , &sym ) && SymGetLineFromAddr64 ( GetCurrentProcess ( ) , s f . AddrPC . Offset , &l i n e o f f , &l i n e ) ) #e l s e STACKFRAME s f = {{context−>Eip , 0 , AddrModeFlat}, {}, {context−>Ebp, 0 , AddrModeFlat}, {context−>Esp , 0 , AddrModeFlat}, 0}; while ( : : StackWalk ( IMAGE FILE MACHINE I386 , GetCurrentProcess ( ) , GetCurrentThread ( ) , &sf , context , NULL, : : SymFunctionTableAccess , : : SymGetModuleBase , NULL) ) { union { IMAGEHLP SYMBOL sym; char symext [ s i z e o f ( IMAGEHLP SYMBOL) + s i z e o f ( s t r i n g ) ] ; }; sym. SizeOfStruct = s i z e o f ( sym ) ; sym. MaxNameLength = s i z e o f ( symext ) − s i z e o f ( sym ) ; IMAGEHLP LINE l i n e ;

engine/main.cpp l i n e . SizeOfStruct = s i z e o f ( l i n e ) ; DWORD symoff , l i n e o f f ; i f ( SymGetSymFromAddr ( GetCurrentProcess ( ) , s f . AddrPC . Offset , &symoff , &sym ) && SymGetLineFromAddr ( GetCurrentProcess ( ) , s f . AddrPC . Offset , &l i n e o f f , &l i n e ) ) #endif { char ∗del = s t r rc h r ( l i n e . FileName , ’\\ ’) ; formatstring ( t ) (”%s − %s [%d]\n” , sym.Name, del ? del + 1 : l i n e . FileName , l i n e . LineNumber ) ; concatstring ( out , t ) ; } } f a t a l ( out ) ; } #endif #define MAXFPSHISTORY 60 i n t fpspos = 0 , f p s h i s t o r y [MAXFPSHISTORY ] ; void r e s e t f p s h i s t o r y ( ) { l o o p i (MAXFPSHISTORY) f p s h i s t o r y [ i ] = 1; fpspos = 0; } void updatefpshistory ( i n t m i l l i s ) { f p s h i s t o r y [ fpspos ++] = max( 1 , min(1000 , m i l l i s ) ) ; i f ( fpspos>=MAXFPSHISTORY) fpspos = 0; } void g e t f p s ( i n t &fps , i n t &b e s t d i f f , i n t &w o r s t d i f f ) { i n t t o t a l = f p s h i s t o r y [MAXFPSHISTORY−1], best = t o t a l , worst = t o t a l ; l o o p i (MAXFPSHISTORY−1) { int m i l l i s = fpshistory [ i ] ; t o t a l += m i l l i s ; i f ( m i l l i s < best ) best = m i l l i s ; i f ( m i l l i s > worst ) worst = m i l l i s ; } fps = (1000∗MAXFPSHISTORY) / t o t a l ; b e s t d i f f = 1000/best−fps ; w o r s t d i f f = fps−1000/worst ; } void g e t f p s ( i n t ∗raw ) { i n t fps , b e s t d i f f , w o r s t d i f f ; i f (∗raw ) fps = 1000/f p s h i s t o r y [ ( fpspos+MAXFPSHISTORY−1)%MAXFPSHISTORY ]; e l s e g e t f p s ( fps , b e s t d i f f , w o r s t d i f f ) ; i n t r e t ( fps ) ; } COMMANDN( getfps , g e t f p s , ” i ” ) ; bool inbetweenframes = f a l s e , renderedframe = true ; s t a t i c bool findarg ( i n t argc , char ∗∗argv , const char ∗s t r ) { f o r ( i n t i = 1; icurrent h ; } i n t usedcolorbits = 0 , useddepthbits = 0 , usedfsaa = 0; setupscreen ( usedcolorbits , useddepthbits , usedfsaa ) ;

i f ( i n i t s c r i p t ) execute ( i n i t s c r i p t ) ; l o g o u t f ( ” i n i t : mainloop ” ) ; initmumble ( ) ; resetfpshistory ( ) ; inputgrab ( grabinput = true ) ; ignoremousemotion ( ) ;

l o g o u t f ( ” i n i t : video : misc ” ) ; SDL WM SetCaption ( ” Cube 2: Sauerbraten ” , NULL) ; keyrepeat ( f a l s e ) ; SDL ShowCursor ( 0 ) ;

for ( ; ; ) { s t a t i c i n t frames = 0; int millis = getclockmillis ( ) ; limitfps ( millis , totalmillis ) ; i n t elapsed = m i l l i s−t o t a l m i l l i s ; s t a t i c i n t timeerr = 0; i n t scaledtime = game : : scaletime ( elapsed ) + timeerr ; curtime = scaledtime /100; timeerr = scaledtime%100; i f ( ! multiplayer ( f a l s e ) && curtime>200) curtime = 200; i f ( game : : ispaused ( ) ) curtime = 0; l a s t m i l l i s += curtime ; totalmillis = millis ; updatetime ( ) ;

logoutf ( ” i n i t : gl ” ) ; gl checkextensions ( ) ; g l i n i t ( scr w , scr h , usedcolorbits , useddepthbits , usedfsaa ) ; notexture = textureload ( ” packages/textures/notexture . png ” ) ; i f ( ! notexture ) f a t a l ( ” could not f i n d core textures ” ) ; l o g o u t f ( ” i n i t : console ” ) ; i f ( ! e x e c f i l e ( ” data/ s t d l i b . c f g ” , f a l s e ) ) f a t a l ( ” cannot f i n d data f i l e s ( you are running from the wrong f o l d e r , t r y . bat f i l e in the main f o l d e r ) ” ) ; // t h i s i s the f i r s t f i l e we load . i f ( ! e x e c f i l e ( ” data/font . c f g ” , f a l s e ) ) f a t a l ( ” cannot f i n d font definitions ” ) ; i f ( ! s e t f o n t ( ” d e f a u l t ” ) ) f a t a l ( ” no d e f a u l t font s p e c i f i e d ” ) ;

checkinput ( ) ; menuprocess ( ) ; tryedit ( ) ;

inbetweenframes = true ; renderbackground ( ” i n i t i a l i z i n g . . . ” ) ;

i f ( l a s t m i l l i s ) game : : updateworld ( ) ; logoutf ( ” i n i t : gl : effects ” ) ; loadshaders ( ) ; particleinit ( ) ; initdecals ( ) ;

checksleep ( l a s t m i l l i s ) ; s e r v e r s l i c e ( f a l s e , 0) ; i f ( frames ) updatefpshistory ( elapsed ) ; frames ++;

l o g o u t f ( ” i n i t : world ” ) ; camera1 = player = game : : iterdynents ( 0 ) ; emptymap( 0 , true , NULL, f a l s e ) ;

// miscellaneous general game e f f e c t s recomputecamera ( ) ; updateparticles ( ) ; updatesounds ( ) ;

l o g o u t f ( ” i n i t : sound ” ) ; initsound ( ) ; logoutf ( ” i n i t : cfg ” ) ; e x e c f i l e ( ” data/keymap . c f g ” ) ; e x e c f i l e ( ” data/ s t d e d i t . c f g ” ) ; e x e c f i l e ( ” data/menus. c f g ” ) ; e x e c f i l e ( ” data/sounds . c f g ” ) ; e x e c f i l e ( ” data/brush . c f g ” ) ; e x e c f i l e ( ” mybrushes . c f g ” , f a l s e ) ; i f ( game : : savedservers ( ) ) e x e c f i l e ( game : : savedservers ( ) , f a l s e ) ;

i f ( minimized ) continue ; inbetweenframes = f a l s e ; i f (mainmenu) gl drawmainmenu ( screen−>w, screen−>h ) ; e l s e gl drawframe ( screen−>w, screen−>h ) ; swapbuffers ( ) ; renderedframe = inbetweenframes = true ; }

i d e n t f l a g s |= IDF PERSIST ; i n i t i n g = INIT LOAD ; i f ( ! e x e c f i l e ( game : : savedconfig ( ) , f a l s e ) ) { e x e c f i l e ( game : : d e f a u l t c o n f i g ( ) ) ; w r i t e c f g ( game : : r e s t o r e c o n f i g ( ) ) ; } e x e c f i l e ( game : : autoexec ( ) , f a l s e ) ; i n i t i n g = NOT INITING ;

engine/master.cpp

ASSERT( 0 ) ; return EXIT FAILURE ; # i f defined ( WIN32 ) && ! defined ( DEBUG) && ! defined ( GNUC ) } e x c e p t ( stackdumper ( 0 , GetExceptionInformation ( ) ) , EXCEPTION CONTINUE SEARCH) { return 0; } #endif }

engine/master.cpp #include ” cube . h” #include #include #define #define #define #define #define #define #define #define #define #define #define #define

INPUT LIMIT 4096 OUTPUT LIMIT (64∗1024) CLIENT TIME (3∗60∗1000) AUTH TIME (30∗1000) AUTH LIMIT 100 AUTH THROTTLE 1000 CLIENT LIMIT 8192 DUP LIMIT 16 PING TIME 3000 PING RETRY 5 KEEPALIVE TIME (65∗60∗1000) SERVER LIMIT (10∗1024)

FILE ∗ l o g f i l e = NULL; struct userinfo { char ∗name; void ∗pubkey ; }; hashtable users ; void adduser ( char ∗name, char ∗pubkey ) { name = newstring (name) ; userinfo &u = users [name ] ; u .name = name; u . pubkey = parsepubkey ( pubkey ) ; } COMMAND( adduser , ” ss ” ) ; void clearusers ( ) { enumerate ( users , userinfo , u, { d e l e t e [ ] u .name; freepubkey ( u . pubkey ) ; }) ; users . c l e a r ( ) ; } COMMAND( clearusers , ” ” ) ; struct baninfo { enet uint32 ip , mask; }; vector bans , servbans , gbans ; void clearbans ( ) { bans . shrink ( 0 ) ; servbans . shrink ( 0 ) ; gbans . shrink ( 0 ) ; } COMMAND( clearbans , ” ” ) ; void addban ( vector &bans , const char ∗name) { union { uchar b [ s i z e o f ( enet uint32 ) ] ; enet uint32 i ; } ip , mask; ip . i = 0; mask. i = 0; loopi ( 4 ) { char ∗end = NULL; i n t n = s t r t o l ( name, &end , 10) ; i f ( ! end ) break ; i f ( end > name) { ip . b [ i ] = n ; mask. b [ i ] = 0xFF ; } name = end ; while (∗name && ∗name++ ! = ’ . ’ ) ; } baninfo &ban = bans . add ( ) ; ban . ip = ip . i ; ban .mask = mask. i ; } ICOMMAND( ban , ” s ” , ( char ∗name) , addban ( bans , name) ) ; ICOMMAND( servban , ” s ” , ( char ∗name) , addban ( servbans , name) ) ; ICOMMAND( gban , ” s ” , ( char ∗name) , addban ( gbans , name) ) ; char ∗printban ( const baninfo &ban , char ∗buf ) { union { uchar b [ s i z e o f ( enet uint32 ) ] ; enet uint32 i ; } ip , mask; ip . i = ban . ip ; mask. i = ban .mask; i n t l a s t d i g i t = −1; l o o p i ( 4 ) i f (mask. b [ i ] ) { i f ( l a s t d i g i t >= 0) ∗buf++ = ’ . ’ ; l o o p j ( i − l a s t d i g i t − 1) { ∗buf++ = ’ ∗ ’ ; ∗buf++ = ’ . ’ ; } buf += s p r i n t f ( buf , ”%d ” , ip . b [ i ] ) ; lastdigit = i ; } return buf ; }

285

bool checkban ( vector &bans , enet uint32 host ) { loopv ( bans ) i f ( ( host & bans [ i ] . mask) == bans [ i ] . ip ) return true ; return f a l s e ; } struct authreq { enet uint32 reqtime ; uint id ; void ∗answer ; }; struct gameserver { ENetAddress address ; s t r i n g ip ; i n t port , numpings ; enet uint32 lastping , lastpong ; }; vector gameservers ; struct messagebuf { vector &owner ; vector buf ; int refs ; messagebuf ( vector &owner ) : owner ( owner ) , r e f s ( 0 ) {} const char ∗getbuf ( ) { return buf . getbuf ( ) ; } i n t length ( ) { return buf . length ( ) ; } void purge ( ) ; bool equals ( const messagebuf &m) const { return buf . length ( ) == m. buf . length ( ) && !memcmp( buf . getbuf ( ) , m. buf . getbuf ( ) , buf . length ( ) ) ; } bool endswith ( const messagebuf &m) const { return buf . length ( ) >= m. buf . length ( ) && !memcmp(&buf [ buf . length ( ) − m. buf . length ( ) ] , m. buf . getbuf ( ) , m. buf . length ( ) ) ; } void concat ( const messagebuf &m) { i f ( buf . length ( ) && buf . l a s t ( ) == ’ \ 0 ’ ) buf . pop ( ) ; buf . put (m. buf . getbuf ( ) , m. buf . length ( ) ) ; } }; vector gameserverlists , gbanlists ; bool u p d a t e s e r v e r l i s t = true ; struct c l i e n t { ENetAddress address ; ENetSocket socket ; char input [ INPUT LIMIT ] ; messagebuf ∗message ; vector output ; i n t inputpos , outputpos ; enet uint32 connecttime , lastinput ; i n t servport ; enet uint32 lastauth ; vector authreqs ; bool shouldpurge ; bool r e g i s t e r e d s e r v e r ; c l i e n t ( ) : message (NULL) , inputpos ( 0 ) , outputpos ( 0 ) , servport (−1) , lastauth ( 0 ) , shouldpurge ( f a l s e ) , r e g i s t e r e d s e r v e r ( f a l s e ) {} }; vector c l i e n t s ; ENetSocket serversocket = ENET SOCKET NULL; t i m e t starttime ; enet uint32 servtime = 0; void f a t a l ( const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; v f p r i n t f ( l o g f i l e , fmt , args ) ; fputc ( ’ \n ’ , l o g f i l e ) ; va end ( args ) ; e x i t ( EXIT FAILURE ) ; } void conoutfv ( i n t type , const char ∗fmt , v a l i s t args ) { v f p r i n t f ( l o g f i l e , fmt , args ) ;

286

Foundations of Videogame Programming Code Repository

fputc ( ’ \n ’ , l o g f i l e ) ;

messagebuf ∗l = new messagebuf ( gameserverlists ) ; loopv ( gameservers ) { gameserver &s = ∗gameservers [ i ] ; i f ( ! s . lastpong ) continue ; defformatstring (cmd) ( ” addserver %s %d\n” , s . ip , s . port ) ; l−>buf . put (cmd, s t r l e n (cmd) ) ; } l−>buf . add ( ’ \ 0 ’ ) ; gameserverlists . add ( l ) ; updateserverlist = false ;

} void conoutf ( const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv ( CON INFO, fmt , args ) ; va end ( args ) ; } void conoutf ( i n t type , const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv ( type , fmt , args ) ; va end ( args ) ; } void purgeclient ( i n t n ) { c l i e n t &c = ∗c l i e n t s [ n ] ; i f ( c . message ) c . message−>purge ( ) ; enet socket destroy ( c . socket ) ; delete clients [n ] ; c l i e n t s . remove ( n ) ; } void output ( c l i e n t &c , const char ∗msg, i n t len = 0) { i f ( ! len ) len = s t r l e n ( msg ) ; c . output . put ( msg, len ) ; } void outputf ( c l i e n t &c , const char ∗fmt , . . . ) { s t r i n g msg; v a l i s t args ; v a s t a r t ( args , fmt ) ; vformatstring ( msg, fmt , args ) ; va end ( args ) ; output ( c , msg ) ; } ENetSocket pingsocket = ENET SOCKET NULL; bool setuppingsocket ( ENetAddress ∗address ) { i f ( pingsocket ! = ENET SOCKET NULL) return true ; pingsocket = e n e t s o c k e t c r e a t e (ENET SOCKET TYPE DATAGRAM) ; i f ( pingsocket == ENET SOCKET NULL) return f a l s e ; i f ( address && enet socket bind ( pingsocket , address ) < 0) return f a l s e ; e n e t s o c k e t s e t o p t i o n ( pingsocket , ENET SOCKOPT NONBLOCK, 1) ; return true ; } void setupserver ( i n t port , const char ∗ip = NULL) { ENetAddress address ; address . host = ENET HOST ANY; address . port = port ; i f ( ip ) { i f ( enet address set host (&address , ip )buf . put (cmd, printban ( b , &cmd[ cmdlen ] ) − cmd) ; l−>buf . add ( ’ \n ’ ) ; } i f ( gbanlists . length ( ) && gbanlists . l a s t ( )−>equals (∗ l ) ) { delete l ; return ; } while ( gbanlists . length ( ) && gbanlists . l a s t ( )−>r e f s r e f s > 0 && !m−>endswith(∗ l ) ) m−>concat (∗ l ) ; } gbanlists . add ( l ) ; loopv ( c l i e n t s ) { c l i e n t &c = ∗c l i e n t s [ i ] ; i f ( c . servport >= 0 && ! c . message ) { c . message = l ; c . message−>r e f s ++; } } } void addgameserver ( c l i e n t &c ) { i f ( gameservers . length ( ) >= SERVER LIMIT ) return ; loopv ( gameservers ) { gameserver &s = ∗gameservers [ i ] ; i f ( s . address . host == c . address . host && s . port == c . servport ) { s . l a s t p i n g = 0; s . numpings = 0; return ; } } s t r i n g hostname ; i f ( e n e t a d d r e s s g e t h o s t i p (&c . address , hostname , s i z e o f ( hostname ) ) < 0) { outputf ( c , ” f a i l r e g f a i l e d r e s o l v i n g ip\n ” ) ; return ; } gameserver &s = ∗gameservers . add ( new gameserver ) ; s . address . host = c . address . host ; s . address . port = c . servport +1; copystring ( s . ip , hostname ) ; s . port = c . servport ; s . numpings = 0; s . l a s t p i n g = s . lastpong = 0; } c l i e n t ∗f i n d c l i e n t ( gameserver &s ) { loopv ( c l i e n t s ) { c l i e n t &c = ∗c l i e n t s [ i ] ; i f ( s . address . host == c . address . host && s . port == c . servport ) return &c ; } return NULL; } void servermessage ( gameserver &s , const char ∗msg ) { c l i e n t ∗c = f i n d c l i e n t ( s ) ; i f ( c ) outputf (∗c , msg ) ;

engine/master.cpp }

287

delete this ; }

void checkserverpongs ( ) { ENetBuffer buf ; ENetAddress addr ; s t a t i c uchar pong [MAXTRANS] ; for ( ; ; ) { buf . data = pong ; buf . dataLength = s i z e o f ( pong ) ; i n t len = e n e t s o c k e t r e c e i v e ( pingsocket , &addr , &buf , 1) ; i f ( len r e g i s t e r e d s e r v e r = true ; outputf (∗c , ” succreg\n ” ) ; i f ( ! c−>message && gbanlists . length ( ) ) { c−>message = gbanlists . l a s t ( ) ; c−>message−>r e f s ++; } } } i f ( ! s . lastpong ) u p d a t e s e r v e r l i s t = true ; s . lastpong = servtime ? servtime : 1; break ; } } } }

} void purgeauths ( c l i e n t &c ) { i n t expired = 0; loopv ( c . authreqs ) { i f ( ENET TIME DIFFERENCE ( servtime , c . authreqs [ i ] . reqtime ) >= AUTH TIME ) { outputf ( c , ” f a i l a u t h %u\n” , c . authreqs [ i ] . id ) ; f r e e c h a l l e n g e ( c . authreqs [ i ] . answer ) ; expired = i + 1; } e l s e break ; } i f ( expired > 0) c . authreqs . remove ( 0 , expired ) ; } void reqauth ( c l i e n t &c , uint id , char ∗name) { i f ( ENET TIME DIFFERENCE ( servtime , c . lastauth ) < AUTH THROTTLE) return ; c . lastauth = servtime ; purgeauths ( c ) ; t i m e t t = time (NULL) ; char ∗c t = ctime(& t ) ; i f ( ct ) { char ∗newline = strchr ( ct , ’\n ’ ) ; i f ( newline ) ∗newline = ’ \ 0 ’ ; } s t r i n g ip ; i f ( e n e t a d d r e s s g e t h o s t i p (&c . address , ip , s i z e o f ( ip ) ) < 0) copystring ( ip , ”−”) ; conoutf (”%s : attempting \”%s\” as %u from %s ” , c t ? c t : ”−”, name, id , ip ) ;

void bangameservers ( ) { loopvrev ( gameservers ) i f ( checkban ( servbans , gameservers [ i]−>address . host ) ) { d e l e t e gameservers . remove ( i ) ; u p d a t e s e r v e r l i s t = true ; } } void checkgameservers ( ) { ENetBuffer buf ; loopv ( gameservers ) { gameserver &s = ∗gameservers [ i ] ; i f ( s . l a s t p i n g && s . lastpong && ENET TIME LESS EQUAL ( s . lastping , s . lastpong ) ) { i f ( ENET TIME DIFFERENCE ( servtime , s . lastpong ) > KEEPALIVE TIME ) { d e l e t e gameservers . remove ( i−−); u p d a t e s e r v e r l i s t = true ; } } e l s e i f ( ! s . l a s t p i n g || ENET TIME DIFFERENCE ( servtime , s . l a s t p i n g ) > PING TIME ) { i f ( s . numpings >= PING RETRY ) { servermessage ( s , ” f a i l r e g f a i l e d pinging server\n ” ) ; d e l e t e gameservers . remove ( i−−); u p d a t e s e r v e r l i s t = true ; } else { s t a t i c const uchar ping [ ] = { 1 }; buf . data = ( void ∗) ping ; buf . dataLength = s i z e o f ( ping ) ; s . numpings++; s . l a s t p i n g = servtime ? servtime : 1; enet socket send ( pingsocket , &s . address , &buf , 1) ; } } } } void messagebuf : : purge ( ) { r e f s = max( r e f s − 1 , 0) ; i f ( r e f s= AUTH LIMIT ) { outputf ( c , ” f a i l a u t h %u\n” , c . authreqs [ 0 ] . id ) ; f r e e c h a l l e n g e ( c . authreqs [ 0 ] . answer ) ; c . authreqs . remove ( 0 ) ; } authreq &a = c . authreqs . add ( ) ; a . reqtime = servtime ; a . id = id ; uint seed [ 3 ] = { uint ( starttime ) , servtime , randomMT ( ) }; s t a t i c vector buf ; buf . s e t s i z e ( 0 ) ; a . answer = genchallenge ( u−>pubkey , seed , s i z e o f ( seed ) , buf ) ; outputf ( c , ” chalauth %u %s\n” , id , buf . getbuf ( ) ) ; } void confauth ( c l i e n t &c , uint id , const char ∗v a l ) { purgeauths ( c ) ; loopv ( c . authreqs ) i f ( c . authreqs [ i ] . id == id ) { s t r i n g ip ; i f ( e n e t a d d r e s s g e t h o s t i p (&c . address , ip , s i z e o f ( ip ) ) < 0) copystring ( ip , ”−”) ; i f ( checkchallenge ( val , c . authreqs [ i ] . answer ) ) { outputf ( c , ” succauth %u\n” , id ) ; conoutf ( ” succeeded %u from %s ” , id , ip ) ; } else { outputf ( c , ” f a i l a u t h %u\n” , id ) ; conoutf ( ” f a i l e d %u from %s ” , id , ip ) ; } f r e e c h a l l e n g e ( c . authreqs [ i ] . answer ) ; c . authreqs . remove ( i−−); return ; } outputf ( c , ” f a i l a u t h %u\n” , id ) ; }

288

Foundations of Videogame Programming Code Repository

bool checkclientinput ( c l i e n t &c ) { i f ( c . inputpossocket = c l i e n t s o c k e t ; c−>connecttime = servtime ; c−>lastinput = servtime ; c l i e n t s . add ( c ) ; } } loopv ( c l i e n t s ) { c l i e n t &c = ∗c l i e n t s [ i ] ; i f ( ( c . message || c . output . length ( ) ) && ENET SOCKETSET CHECK( writeset , c . socket ) ) { const char ∗data = c . output . length ( ) ? c . output . getbuf ( ) : c . message−>getbuf ( ) ; i n t len = c . output . length ( ) ? c . output . length ( ) : c . message−> length ( ) ; ENetBuffer buf ; buf . data = ( void ∗)&data [ c . outputpos ] ; buf . dataLength = len−c . outputpos ; i n t res = enet socket send ( c . socket , NULL, &buf , 1) ; i f ( res>=0) { c . outputpos += res ; i f ( c . outputpos>=len ) { i f ( c . output . length ( ) ) c . output . s e t s i z e ( 0 ) ; else { c . message−>purge ( ) ; c . message = NULL; } c . outputpos = 0; i f ( ! c . message && c . output . empty ( ) && c . shouldpurge ) { purgeclient ( i−−); continue ; } } } e l s e { purgeclient ( i−−); continue ; } } i f (ENET SOCKETSET CHECK( readset , c . socket ) ) { ENetBuffer buf ; buf . data = &c . input [ c . inputpos ] ; buf . dataLength = s i z e o f ( c . input ) − c . inputpos ; i n t res = e n e t s o c k e t r e c e i v e ( c . socket , NULL, &buf , 1) ; i f ( res>0) { c . inputpos += res ; c . input [ min ( c . inputpos , ( i n t ) s i z e o f ( c . input )−1) ] = ’ \ 0 ’ ; i f ( ! checkclientinput ( c ) ) { purgeclient ( i−−); continue ; } } e l s e { purgeclient ( i−−); continue ; } } i f ( c . output . length ( ) > OUTPUT LIMIT ) { purgeclient ( i−−); continue ; } i f ( ENET TIME DIFFERENCE ( servtime , c . lastinput ) >= ( c . r e g i s t e r e d s e r v e r ? KEEPALIVE TIME : CLIENT TIME ) ) { purgeclient ( i−−); continue ; } }

i n t port ; uint id ; s t r i n g user , v a l ; i f ( ! strncmp ( c . input , ” l i s t ” , 4) && ( ! c . input [ 4 ] || c . input [ 4 ] == ’\ n ’ || c . input [ 4 ] == ’\ r ’ ) ) { genserverlist ( ) ; i f ( gameserverlists . empty ( ) || c . message ) return f a l s e ; c . message = gameserverlists . l a s t ( ) ; c . message−>r e f s ++; c . output . s e t s i z e ( 0 ) ; c . outputpos = 0; c . shouldpurge = true ; return true ; } e l s e i f ( sscanf ( c . input , ” regserv %d ” , &port ) == 1) { i f ( checkban ( servbans , c . address . host ) ) return f a l s e ; i f ( port < 0 || port + 1 < 0 || ( c . servport >= 0 && port ! = c . servport ) ) outputf ( c , ” f a i l r e g i n v a l i d port\n ” ) ; else { c . servport = port ; addgameserver ( c ) ; } } e l s e i f ( sscanf ( c . input , ” reqauth %u %100s ” , &id , user ) == 2) { reqauth ( c , id , user ) ; } e l s e i f ( sscanf ( c . input , ” confauth %u %100s ” , &id , v a l ) == 2) { confauth ( c , id , v a l ) ; } c . inputpos = &c . input [ c . inputpos ] − end ; memmove( c . input , end , c . inputpos ) ; end = ( char ∗)memchr( c . input , ’\n ’ , c . inputpos ) ; } return c . inputposaddress . host == address . host ) { dups++; i f ( oldestconnecttime < c l i e n t s [ o l d e s t]−> connecttime ) o l d e s t = i ; } i f ( dups >= DUP LIMIT ) purgeclient ( o l d e s t ) ; c l i e n t ∗c = new c l i e n t ; c−>address = address ;

} void banclients ( ) { loopvrev ( c l i e n t s ) i f ( checkban ( bans , c l i e n t s [ i]−>address . host ) ) purgeclient ( i ) ; } v o l a t i l e bool r e l o a d c f g = true ; void reloadsignal ( i n t signum ) { r e l o a d c f g = true ; } i n t main ( i n t argc , char ∗∗argv ) { i f ( e n e t i n i t i a l i z e ( ) =2) d i r = argv [ 1 ] ; i f ( argc>=3) port = a t o i ( argv [ 2 ] ) ; i f ( argc>=4) ip = argv [ 3 ] ; defformatstring ( logname ) (”%smaster . l o g ” , d i r ) ; defformatstring ( cfgname ) (”%smaster . c f g ” , d i r ) ; path ( logname ) ; path ( cfgname ) ; l o g f i l e = fopen ( logname , ” a ” ) ; i f ( ! l o g f i l e ) l o g f i l e = stdout ; setvbuf ( l o g f i l e , NULL, IOLBF , BUFSIZ ) ;

engine/material.cpp # i f n d e f WIN32 s i g n a l ( SIGUSR1, reloadsignal ) ; #endif setupserver ( port , ip ) ; for ( ; ; ) { i f ( reloadcfg ) { conoutf ( ” reloading master . c f g ” ) ; e x e c f i l e ( cfgname ) ; bangameservers ( ) ; banclients ( ) ;

289

gengbanlist ( ) ; reloadcfg = false ; } servtime = e n e t t i m e g e t ( ) ; checkclients ( ) ; checkgameservers ( ) ; } return EXIT SUCCESS ; }

engine/material.cpp } l o o p i ( 4 ) i f ( c h i l d [ i ] ) c h i l d [ i]−>genmatsurfs ( mat , orient , f l a g s , z , matbuf ) ;

#include ” engine . h” struct QuadNode { int x , y , size ; uint f i l l e d ; QuadNode ∗c h i l d [ 4 ] ; QuadNode ( i n t x , i n t y , i n t s i z e ) : x ( x ) , y ( y ) , s i z e ( s i z e ) , f i l l e d ( 0 ) { l o o p i ( 4 ) c h i l d [ i ] = 0; }

} }; s t a t i c f l o a t wfwave , w f s c r o l l , wfxscale , wfyscale ; s t a t i c void r e n d e r w a t e r f a l l ( const materialsurface &m, f l o a t o f f s e t , const vec ∗normal = NULL) {

void c l e a r ( ) { l o o p i ( 4 ) DELETEP( c h i l d [ i ] ) ; } ˜QuadNode ( ) { clear ( ) ; } void i n s e r t ( i n t mx, i n t my, i n t msize ) { i f ( s i z e == msize ) { f i l l e d = 0xF ; return ; } i n t c s i z e = size>>1, i = 0; i f (mx >= x+ c s i z e ) i |= 1; i f (my >= y+ c s i z e ) i |= 2; i f ( c s i z e == msize ) { f i l l e d |= (1 i n s e r t (mx, my, msize ) ; loopj ( 4 ) i f ( child [ j ] ) { i f ( c h i l d [ j]−>f i l l e d == 0xF ) { DELETEP( c h i l d [ j ] ) ; f i l l e d |= (1 >1; l o o p i ( 4 ) i f ( f i l l e d & (1 &matsurfs ) { loopi ( 6 ) { s t a t i c const ushort matmasks [ ] = { MATF VOLUME|MATF INDEX, MATF CLIP , MAT DEATH, MAT ALPHA }; l o o p j ( s i z e o f ( matmasks ) / s i z e o f ( matmasks [ 0 ] ) ) { i n t matmask = matmasks [ j ] ; i n t v i s = v i s i b l e m a t e r i a l ( c , i , cx , cy , cz , size , matmask&˜ MATF INDEX ) ; i f ( v i s ! = MATSURF NOT VISIBLE ) { materialsurface m; m. material = c . material&matmask; m. o r i e n t = i ; m. f l a g s = v i s == MATSURF EDIT ONLY ? materialsurface : : F EDIT : 0; m. o = i v e c ( cx , cy , cz ) ; m. c s i z e = m. r s i z e = s i z e ; i f ( dimcoord ( i ) ) m. o [ dimension ( i ) ] += s i z e ; matsurfs . add (m) ; break ; } } } } s t a t i c i n l i n e bool mergematcmp ( const materialsurface &x , const materialsurface &y ) { i n t dim = dimension ( x . o r i e n t ) , c = C[ dim ] , r = R[ dim ] ; i f ( x . o [ r ] + x . r s i z e < y . o [ r ] + y . r s i z e ) return true ; i f ( x . o [ r ] + x . r s i z e > y . o [ r ] + y . r s i z e ) return f a l s e ; return x . o [ c ] < y . o [ c ] ; } s t a t i c i n t mergematr ( materialsurface ∗m, i n t sz , materialsurface &n ) { i n t dim = dimension ( n . o r i e n t ) , c = C[ dim ] , r = R[ dim ] ; f o r ( i n t i = sz−1; i >= 0; −−i ) { i f (m[ i ] . o [ r ] + m[ i ] . r s i z e < n . o [ r ] ) break ; i f (m[ i ] . o [ r ] + m[ i ] . r s i z e == n . o [ r ] && m[ i ] . o [ c ] == n . o [ c ] && m[ i ] . c s i z e == n . c s i z e ) { n . o [ r ] = m[ i ] . o [ r ] ; n . r s i z e += m[ i ] . r s i z e ; memmove(&m[ i ] , &m[ i +1] , ( sz − ( i +1) ) ∗ s i z e o f ( materialsurface ) ) ; return 1; } } return 0; } s t a t i c i n t mergematc ( materialsurface &m, materialsurface &n ) { i n t dim = dimension ( n . o r i e n t ) , c = C[ dim ] , r = R[ dim ] ; i f (m. o [ r ] == n . o [ r ] && m. r s i z e == n . r s i z e && m. o [ c ] + m. c s i z e == n . o [ c ]) { n . o [ c ] = m. o [ c ] ; n . c s i z e += m. c s i z e ; return 1; } return 0; } s t a t i c i n t mergemat ( materialsurface ∗m, i n t sz , materialsurface &n ) { f o r ( bool merged = f a l s e ; sz ; merged = true ) { i n t rmerged = mergematr (m, sz , n ) ; sz −= rmerged ; i f ( ! rmerged && merged ) break ; i f ( ! sz ) break ; i n t cmerged = mergematc (m[ sz −1], n ) ; sz −= cmerged ; i f ( ! cmerged ) break ; } m[ sz ++] = n ; return sz ; } s t a t i c i n t mergemats ( materialsurface ∗m, i n t sz ) { quicksort (m, sz , mergematcmp ) ; i n t nsz = 0; l o o p i ( sz ) nsz = mergemat (m, nsz , m[ i ] ) ; return nsz ; }

engine/material.cpp s t a t i c i n l i n e bool optmatcmp ( const materialsurface &x , const materialsurface &y )

wi .m = &m; vec center (m. o . x+m. r s i z e /2 , m. o . y+m. c s i z e /2 , m. o . z− WATER OFFSET) ; m. l i g h t = b r i g h t e s t l i g h t ( center , vec ( 0 , 0 , 1) ) ; f l o a t depth = raycube ( center , vec ( 0 , 0 , −1) , 10000) ; wi . depth = double ( depth )∗m. r s i z e∗m. c s i z e ; wi . area = m. r s i z e∗m. c s i z e ;

{ i f ( x . material < y . material ) return true ; i f ( x . material > y . material ) return f a l s e ; i f ( x . o r i e n t > y . o r i e n t ) return true ; i f ( x . o r i e n t < y . o r i e n t ) return f a l s e ; i n t dim = dimension ( x . o r i e n t ) ; return x . o [ dim ] < y . o [ dim ] ;

} e l s e i f ( i s l i q u i d ( matvol ) && m. o r i e n t ! =O BOTTOM && m. o r i e n t ! = O TOP ) { m. ends = 0; i n t dim = dimension (m. o r i e n t ) , coord = dimcoord (m. o r i e n t ) ; i v e c o (m. o ) ; o . z −= 1; o [ dim ] += coord ? 1 : −1; i n t minc = o [ dim ˆ 1 ] , maxc = minc + (C[ dim]==2 ? m. r s i z e : m. csize ) ; i v e c co ; int csize ; while ( o [ dim ˆ 1 ] < maxc ) { cube &c = lookupcube ( o . x , o . y , o . z , 0 , co , c s i z e ) ; i f ( i s l i q u i d ( c . material&MATF VOLUME) ) { m. ends |= 1; break ; } o [ dim ˆ 1 ] += c s i z e ; } o [ dim ˆ 1 ] = minc ; o . z += R[ dim]==2 ? m. r s i z e : m. c s i z e ; o [ dim ] −= coord ? 2 : −2; while ( o [ dim ˆ 1 ] < maxc ) { cube &c = lookupcube ( o . x , o . y , o . z , 0 , co , c s i z e ) ; i f ( v i s i b l e m a t e r i a l ( c , O TOP, co . x , co . y , co . z , c s i z e ) ) { m . ends |= 2; break ; } o [ dim ˆ 1 ] += c s i z e ; } } e l s e i f ( matvol==MAT GLASS) { i f ( ! hasCM || ( renderpath==R FIXEDFUNCTION && ! hasTE ) ) m. envmap = EMID NONE; else { i n t dim = dimension (m. o r i e n t ) ; vec center (m. o . tovec ( ) ) ; center [R[ dim ] ] += m. r s i z e /2; center [C[ dim ] ] += m. c s i z e /2; m. envmap = closestenvmap ( center ) ; } } i f ( matvol ) hasmat |= 1 o r i e n t && skip−>skip < 0xFFFF ) skip−>skip ++; else skip = &m;

} VARF( optmats , 0 , 1 , 1 , allchanged ( ) ) ; i n t optimizematsurfs ( materialsurface ∗matbuf , i n t matsurfs ) { quicksort ( matbuf , matsurfs , optmatcmp ) ; i f ( ! optmats ) return matsurfs ; materialsurface ∗cur = matbuf , ∗end = matbuf+matsurfs ; while ( cur < end ) { materialsurface ∗s t a r t = cur ++; i n t dim = dimension ( s t a r t−>o r i e n t ) ; while ( cur < end && cur−>material == s t a r t−>material && cur−>o r i e n t == s t a r t−>o r i e n t && cur−>f l a g s == s t a r t−>f l a g s && cur−>o [ dim ] == s t a r t−>o [ dim ] ) ++cur ; i f ( ! i s l i q u i d ( s t a r t−>material&MATF VOLUME) || s t a r t−>o r i e n t ! = O TOP || ! vertwater ) { i f ( s t a r t ! = matbuf ) memmove( matbuf , s t a r t , ( cur−s t a r t )∗s i z e o f ( materialsurface ) ) ; matbuf += mergemats ( matbuf , cur−s t a r t ) ; } e l s e i f ( cur−s t a r t >=4) { QuadNode vmats ( 0 , 0 , worldsize ) ; l o o p i ( cur−s t a r t ) vmats . i n s e r t ( s t a r t [ i ] . o [C[ dim ] ] , s t a r t [ i ] . o [R[ dim ] ] , s t a r t [ i ] . c s i z e ) ; vmats . genmatsurfs ( s t a r t−>material , s t a r t−>orient , s t a r t−>f l a g s , s t a r t−>o [ dim ] , matbuf ) ; } else { i f ( s t a r t ! = matbuf ) memmove( matbuf , s t a r t , ( cur−s t a r t )∗s i z e o f ( materialsurface ) ) ; matbuf += cur−s t a r t ; } } return matsurfs − ( end−matbuf ) ; } extern vector v a l i s t ; struct waterinfo { materialsurface ∗m; double depth , area ; }; void setupmaterials ( i n t s t a r t , i n t len ) { i n t hasmat = 0; vector water ; unionfind uf ; i f ( ! len ) len = v a l i s t . length ( ) ; f o r ( i n t i = s t a r t ; i < len ; i ++) { vtxarray ∗va = v a l i s t [ i ] ; materialsurface ∗skip = NULL; l o o p j ( va−>matsurfs ) { materialsurface &m = va−>matbuf [ j ] ; i n t matvol = m. material&MATF VOLUME; i f ( matvol==MAT WATER && m. o r i e n t ==O TOP ) { m. index = water . length ( ) ; loopvk ( water ) { materialsurface &n = ∗water [ k ] .m; i f (m. material ! =n . material || m. o . z ! =n . o . z ) continue ; i f ( n . o . x+n . r s i z e ==m. o . x || m. o . x+m. r s i z e ==n . o . x ) { i f ( n . o . y+n . csize>m. o . y && n . o . ym. o . x && n . o . xa t t r 1 || ! n . l i g h t || ( n . l i g h t−>a t t r 1 && m. l i g h t−>a t t r 1 > n . l i g h t−>a t t r 1 ) ) ) n . l i g h t = m. l i g h t ; water [ root ] . depth += water [ i ] . depth ; water [ root ] . area += water [ i ] . area ; } loopv ( water ) { i n t root = uf . f i n d ( i ) ; water [ i ] .m−>l i g h t = water [ root ] .m−>l i g h t ; water [ i ] .m−>depth = ( short ) ( water [ root ] . depth/water [ root ] . area ) ; } i f ( hasmat&(0xFo . z − r e f l e c t z ) ); vec d i r ; vecfromyawpitch ( camera1−>yaw , r e f l e c t i n g ? −camera1−>pitch : camera1−> pitch , 1 , 0 , d i r ) ; l o o p i ( 3 ) { d i r [ i ] = fabs ( d i r [ i ] ) ; sortdim [ i ] = i ; } i f ( d i r [ sortdim [ 2 ] ] > d i r [ sortdim [ 1 ] ] ) swap ( sortdim [ 2 ] , sortdim [ 1 ] ) ; i f ( d i r [ sortdim [ 1 ] ] > d i r [ sortdim [ 0 ] ] ) swap ( sortdim [ 1 ] , sortdim [ 0 ] ) ; i f ( d i r [ sortdim [ 2 ] ] > d i r [ sortdim [ 1 ] ] ) swap ( sortdim [ 2 ] , sortdim [ 1 ] ) ; f o r ( vtxarray ∗va = r e f l e c t i n g ? r e f l e c t e d v a : v i s i b l e v a ; va ; va = r e f l e c t i n g ? va−>rnext : va−>next ) { i f ( ! va−>matsurfs || va−>occluded >= OCCLUDE BB) continue ; i f ( r e f l e c t i n g || r e f r a c t i n g>0 ? va−>o . z+va−>s i z e o . z >= r e f l e c t z ) continue ; l o o p i ( va−>matsurfs ) { materialsurface &m = va−>matbuf [ i ] ; i f ( ! editmode || ! showmat || envmapping ) { i n t matvol = m. material&MATF VOLUME; i f ( matvol==MAT WATER && (m. o r i e n t ==O TOP || ( r e f r a c t i n gworldsize ) ) ) { i += m. skip ; continue ; } i f (m. f l a g s&materialsurface : : F EDIT ) { i += m. skip ; continue ; } i f ( g l a r i n g && matvol ! =MAT LAVA ) { i += m. skip ; continue ; } } e l s e i f ( g l a r i n g ) continue ; vismats . add(&m) ; } } s o r t e d i t = editmode && showmat && ! envmapping ; vismats . s o r t ( vismatcmp ) ; } void rendermatgrid ( vector &vismats ) { enablepolygonoffset ( GL POLYGON OFFSET LINE ) ; glPolygonMode ( GL FRONT AND BACK, GL LINE ) ; i n t lastmat = −1; loopvrev ( vismats ) { materialsurface &m = ∗vismats [ i ] ; i f (m. material ! = lastmat ) { x t r a v e r t s += varray : : end ( ) ; lastmat = m. material ;

} VARFP( waterfallenv , 0 , 1 , 1 , preloadwatershaders ( ) ) ; void rendermaterials ( ) { vector vismats ; sortmaterials ( vismats ) ; i f ( vismats . empty ( ) ) return ; glDisable ( GL CULL FACE ) ; varray : : enable ( ) ; MSlot ∗mslot = NULL; uchar wcol [ 4 ] = { 255, 255, 255, 192 }, wfcol [ 4 ] = { 255, 255, 255, 192 }; i n t l a s t o r i e n t = −1, lastmat = −1, usedwaterfall = −1; GLenum textured = GL TEXTURE 2D;

engine/material.cpp bool depth = true , blended = f a l s e , t i n t = f a l s e , overbright = f a l s e , usedcamera = f a l s e ; ushort envmapped = EMID NONE; s t a t i c const vec normals [ 6 ] = { vec(−1, 0 , 0) , vec ( 1 , 0 , 0) , vec ( 0 , −1, 0) , vec ( 0 , 1 , 0) , vec ( 0 , 0 , −1) , vec ( 0 , 0 , 1) }; s t a t i c const f l o a t zerofog [ 4 ] = { 0 , 0 , 0 , 1 }; f l o a t oldfogc [ 4 ] ; glGetFloatv (GL FOG COLOR, oldfogc ) ; i n t l a s t f o g t y p e = 1; i f ( editmode && showmat && ! envmapping ) { glBlendFunc ( GL ZERO, GL ONE MINUS SRC COLOR ) ; glEnable (GL BLEND) ; blended = true ; glDisable ( GL TEXTURE 2D ) ; textured = 0; foggednotextureshader−>set ( ) ; glFogfv (GL FOG COLOR, zerofog ) ; l a s t f o g t y p e = 0; loopv ( vismats ) { const materialsurface &m = ∗vismats [ i ] ; i f ( lastmat ! =m. material ) { x t r a v e r t s += varray : : end ( ) ; switch (m. material&˜MATF INDEX ) { case MAT WATER: glColor3ub (255 , 128, 0) ; break ; // blue case MAT CLIP : glColor3ub ( 0 , 255, 255) ; break ; // red case MAT GLASS: glColor3ub (255 , 0, 0) ; break ; // cyan case MAT NOCLIP : glColor3ub (255 , 0 , 255) ; break ; // green case MAT LAVA: glColor3ub ( 0 , 128, 255) ; break ; // orange case MAT GAMECLIP: glColor3ub ( 0 , 0 , 255) ; break ; // yellow case MAT DEATH: glColor3ub (192 , 192, 192) ; break ; // black case MAT ALPHA: glColor3ub ( 0 , 255, 0) ; break ; // pink d e f a u l t : continue ; } lastmat = m. material ; } drawmaterial (m, −0.1f ) ; } } e l s e loopv ( vismats ) { const materialsurface &m = ∗vismats [ i ] ; i n t matvol = m. material&˜MATF INDEX; i f ( lastmat ! =m. material || l a s t o r i e n t ! =m. o r i e n t || ( matvol== MAT GLASS && envmapped && m. envmap ! = envmapped ) ) { i n t fogtype = l a s t f o g t y p e ; switch ( matvol ) { case MAT WATER: i f (m. o r i e n t == O TOP ) continue ; i f ( lastmat == m. material ) break ; mslot = &lookupmaterialslot (m. material , f a l s e ) ; i f ( ! mslot−>loaded || ! mslot−>sts . inrange ( 1 ) ) continue ; else { x t r a v e r t s += varray : : end ( ) ; glBindTexture ( GL TEXTURE 2D, mslot−>sts [ 1 ] . t−>id ) ; f l o a t angle = fmod ( f l o a t ( l a s t m i l l i s / ( renderpath ! = R FIXEDFUNCTION ? 600.0 f : 300.0 f ) /(2∗M PI ) ) , 1.0 f ) , s = angle − i n t ( angle ) − 0.5 f ; s ∗= 8 − fabs ( s ) ∗16; wfwave = vertwater ? WATER AMPLITUDE∗s−WATER OFFSET : − WATER OFFSET; w f s c r o l l = 16.0 f∗l a s t m i l l i s /1000.0 f ; wfxscale = TEX SCALE/ ( mslot−>sts [ 1 ] . t−>xs∗mslot−>scale ) ; wfyscale = TEX SCALE/ ( mslot−>sts [ 1 ] . t−>ys∗mslot−>scale ) ; memcpy( wcol , getwatercolor (m. material ) . v , 3) ; memcpy( wfcol , g e t w a t e r f a l l c o l o r (m. material ) . v , 3) ; i f ( ! wfcol [ 0 ] && ! wfcol [ 1 ] && ! wfcol [ 2 ] ) memcpy( wfcol , wcol , 3) ; i n t wfog = getwaterfog (m. material ) ; i f ( overbright || t i n t ) { resettmu ( 0 ) ; overbright = t i n t = false ; }

293 i f ( ! wfog && ( renderpath==R FIXEDFUNCTION || !hasCM || ! waterfallenv ) ) { glColor3ubv ( wfcol ) ; foggednotextureshader−>set ( ) ; fogtype = 1; i f ( blended ) { glDisable (GL BLEND) ; blended = f a l s e ; } i f ( ! depth ) { glDepthMask ( GL TRUE ) ; depth = true ; } i f ( textured ) { glDisable ( textured ) ; textured = 0; } break ; } e l s e i f ( renderpath==R FIXEDFUNCTION || ( ( ! w a t e r f a l l r e f r a c t || r e f l e c t i n g || r e f r a c t i n g ) && ( ! hasCM || ! w a t e r f a l l e n v ) ) ) { glBlendFunc (GL ONE, GL ONE MINUS SRC COLOR ) ; glColor3ubv ( wfcol ) ; foggedshader−>set ( ) ; fogtype = 0; i f ( ! blended ) { glEnable (GL BLEND) ; blended = true ; } i f ( depth ) { glDepthMask ( GL FALSE ) ; depth = f a l s e ; } } else { glColor3ubv ( wfcol ) ; fogtype = 1; i f ( ! usedcamera ) { setenvparamf ( ” camera ” , SHPARAM VERTEX, 0 , camera1 −>o . x , camera1−>o . y , camera1−>o . z ) ; usedcamera = true ; } i f ( w a t e r f a l l r e f r a c t && wfog && ! r e f l e c t i n g && ! refracting ) { i f (hasCM && w a t e r f a l l e n v ) SETSHADER( waterfallenvrefract ) ; e l s e SETSHADER( w a t e r f a l l r e f r a c t ) ; i f ( blended ) { glDisable (GL BLEND) ; blended = false ; } i f ( ! depth ) { glDepthMask ( GL TRUE ) ; depth = true ; } } else { SETSHADER( w a t e r f a l l e n v ) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; i f ( wfog ) { i f ( ! blended ) { glEnable (GL BLEND) ; blended = true ; } i f ( depth ) { glDepthMask ( GL FALSE ) ; depth = false ; } } else { i f ( blended ) { glDisable (GL BLEND) ; blended = false ; } i f ( ! depth ) { glDepthMask ( GL TRUE ) ; depth = true ; } } } i f ( usedwaterfall ! = m. material ) { Texture ∗dudv = mslot−>sts . inrange ( 5 ) ? mslot−> sts [ 5 ] . t : notexture ; f l o a t scale = 8.0 f / (dudv−>ys∗mslot−>scale ) ; setlocalparamf ( ” dudvoffset ” , SHPARAM PIXEL, 1 , 0 , scale∗16∗l a s t m i l l i s /1000.0 f ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glBindTexture ( GL TEXTURE 2D, mslot−>sts . inrange ( 4 ) ? mslot−>sts [ 4 ] . t−>id : notexture−>id ) ; g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ; glBindTexture ( GL TEXTURE 2D, mslot−>sts . inrange ( 5 ) ? mslot−>sts [ 5 ] . t−>id : notexture−>id ) ; i f (hasCM && w a t e r f a l l e n v ) { g l A c t i v e T e x t u r e ( GL TEXTURE3 ARB ) ; glBindTexture (GL TEXTURE CUBE MAP ARB, lookupenvmap(∗ mslot ) ) ; } i f ( w a t e r f a l l r e f r a c t && ( ! r e f l e c t i n g || !

294

Foundations of Videogame Programming Code Repository −>o . x , camera1−>o . y , camera1−>o . z ) ; usedcamera = true ; } envmapped = m. envmap ;

r e f r a c t i n g ) && usedwaterfall < 0) { extern void s e t u p w a t e r f a l l r e f r a c t (GLenum tmu1, GLenum tmu2 ) ; s e t u p w a t e r f a l l r e f r a c t ( GL TEXTURE4 ARB, GL TEXTURE0 ARB ) ;

} } i f ( lastmat ! =m. material ) { i f ( ! blended ) { glEnable (GL BLEND) ; blended = true ; } i f ( depth ) { glDepthMask ( GL FALSE ) ; depth = f a l s e ; } const bvec &gcol = g e t g l a s s c o l o r (m. material ) ; i f (m. envmap! =EMID NONE && glassenv ) { i f ( renderpath==R FIXEDFUNCTION ) { i f ( ! t i n t ) { colortmu ( 0 , 0.8 f , 0.8 f , 0.8 f , 0.8 f ) ; setuptmu ( 0 , ”T , C @ Ka” , ”= Ca ” ) ; t i n t = true ; overbright = f a l s e ; } glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; g l C o l o r 4 f ( gcol . x/255.0 f , gcol . y/255.0 f , gcol . z /255.0 f , 0.25 f ) ; fogtype = 1; } else { i f ( overbright || t i n t ) { resettmu ( 0 ) ; overbright = tint = false ; } glBlendFunc (GL ONE, GL SRC ALPHA ) ; glColor3ubv ( gcol . v ) ; } SETSHADER( glass ) ; } else { i f ( overbright || t i n t ) { resettmu ( 0 ) ; overbright = tint = false ; } i f ( textured ) { glDisable ( textured ) ; textured = 0; } glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; g l C o l o r 4 f ( gcol . x/255.0 f , gcol . y/255.0 f , gcol . z/255.0 f , 0.15 f ) ; foggednotextureshader−>set ( ) ; fogtype = 1; } } break ;

} e l s e g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; usedwaterfall = m. material ; } } } i f ( textured ! =GL TEXTURE 2D ) { i f ( textured ) glDisable ( textured ) ; glEnable ( GL TEXTURE 2D ) ; textured = GL TEXTURE 2D; } break ; case MAT LAVA: i f ( lastmat==m. material && l a s t o r i e n t ! =O TOP && m. o r i e n t ! = O TOP ) break ; mslot = &lookupmaterialslot (m. material , f a l s e ) ; i f ( ! mslot−>loaded ) continue ; else { i n t subslot = m. o r i e n t ==O TOP ? 0 : 1; i f ( ! mslot−>sts . inrange ( subslot ) ) continue ; x t r a v e r t s += varray : : end ( ) ; glBindTexture ( GL TEXTURE 2D, mslot−>sts [ subslot ] . t−>id ) ; } i f (m. o r i e n t ! =O TOP ) { f l o a t angle = fmod ( f l o a t ( l a s t m i l l i s /2000.0 f /(2∗M PI ) ) , 1.0 f ) , s = angle − i n t ( angle ) − 0.5 f ; s ∗= 8 − fabs ( s ) ∗16; wfwave = vertwater ? WATER AMPLITUDE∗s−WATER OFFSET : − WATER OFFSET; w f s c r o l l = 16.0 f∗l a s t m i l l i s /3000.0 f ; wfxscale = TEX SCALE/ ( mslot−>sts [ 1 ] . t−>xs∗mslot−>scale ) ; wfyscale = TEX SCALE/ ( mslot−>sts [ 1 ] . t−>ys∗mslot−>scale ) ; } i f ( lastmat ! =m. material ) { i f ( ! depth ) { glDepthMask ( GL TRUE ) ; depth = true ; } i f ( blended ) { glDisable (GL BLEND) ; blended = f a l s e ; } i f ( renderpath==R FIXEDFUNCTION && ! overbright ) { setuptmu ( 0 , ”C ∗ T x 2 ” ) ; overbright = true ; tint = false ; } f l o a t t = l a s t m i l l i s /2000.0 f ; t −= f l o o r ( t ) ; t = 1.0 f − 2∗fabs ( t−0.5f ) ; extern i n t g l a r e ; i f ( renderpath ! =R FIXEDFUNCTION && g l a r e ) t = 0.625 f + 0.075 f∗t ; e l s e t = 0.5 f + 0.5 f∗t ; glColor3f ( t , t , t ) ; i f ( g l a r i n g ) SETSHADER( l a v a g l a r e ) ; e l s e SETSHADER( lava ) ; fogtype = 1; } i f ( textured ! =GL TEXTURE 2D ) { i f ( textured ) glDisable ( textured ) ; glEnable ( GL TEXTURE 2D ) ; textured = GL TEXTURE 2D; } break ; case MAT GLASS: i f ( (m. envmap==EMID NONE || ! glassenv || ( envmapped==m. envmap && textured==GL TEXTURE CUBE MAP ARB) ) && lastmat==m. material ) break ; x t r a v e r t s += varray : : end ( ) ; i f (m. envmap! =EMID NONE && glassenv ) { i f ( textured ! =GL TEXTURE CUBE MAP ARB) { i f ( textured ) glDisable ( textured ) ; glEnable (GL TEXTURE CUBE MAP ARB) ; textured = GL TEXTURE CUBE MAP ARB; } i f ( envmapped! =m. envmap ) { glBindTexture (GL TEXTURE CUBE MAP ARB, lookupenvmap ( m. envmap ) ) ; i f ( renderpath ! =R FIXEDFUNCTION && ! usedcamera ) { setenvparamf ( ” camera ” , SHPARAM VERTEX, 0 , camera1

d e f a u l t : continue ; } lastmat = m. material ; l a s t o r i e n t = m. o r i e n t ; i f ( fogtype ! = l a s t f o g t y p e ) { glFogfv (GL FOG COLOR, fogtype ? oldfogc : zerofog ) ; l a s t f o g t y p e = fogtype ; } } switch ( matvol ) { case MAT WATER: r e n d e r w a t e r f a l l (m, 0.1 f , renderpath ! =R FIXEDFUNCTION && hasCM && w a t e r f a l l e n v ? &normals [m. o r i e n t ] : NULL) ; break ; case MAT LAVA: i f (m. o r i e n t ==O TOP ) renderlava (m, mslot−>sts [ 0 ] . t , mslot−> scale ) ; e l s e r e n d e r w a t e r f a l l (m, 0.1 f ) ; break ; case MAT GLASS: i f (m. envmap! =EMID NONE && glassenv ) drawglass (m, 0.1 f , renderpath ! =R FIXEDFUNCTION ? &normals [m. o r i e n t ] : NULL) ; e l s e drawmaterial (m, 0.1 f ) ; break ; } } x t r a v e r t s += varray : : end ( ) ; if if if if if {

( ! depth ) glDepthMask ( GL TRUE ) ; ( blended ) glDisable (GL BLEND) ; ( overbright || t i n t ) resettmu ( 0 ) ; ( ! l a s t f o g t y p e ) glFogfv (GL FOG COLOR, oldfogc ) ; ( editmode && showmat && ! envmapping ) foggedlineshader−>set ( ) ; rendermatgrid ( vismats ) ;

}

engine/md2.h

295

{ varray : : disable ( ) ; glEnable ( GL CULL FACE ) ; i f ( textured ! =GL TEXTURE 2D )

i f ( textured ) glDisable ( textured ) ; glEnable ( GL TEXTURE 2D ) ; } }

engine/md2.h {

struct md2; s t a t i c const f l o a t md2normaltable [ 2 5 6 ] [ 3 ] = { { −0.525731f , 0.000000 f , 0.850651 f }, { −0.442863f , 0.238856 f , 0.864188 f }, { −0.295242f , 0.000000 f , 0.955423 f }, { −0.309017f , 0.500000 f , 0.809017 f }, { −0.162460f , 0.262866 f , 0.951056 f }, { 0.000000 f , 0.000000 f , 1.000000 f }, { 0.000000 f , 0.850651 f , 0.525731 f }, { −0.147621f , 0.716567 f , 0.681718 f }, { 0.147621 f , 0.716567 f , 0.681718 f }, { 0.000000 f , 0.525731 f , 0.850651 f }, { 0.309017 f , 0.500000 f , 0.809017 f }, { 0.525731 f , 0.000000 f , 0.850651 f }, { 0.295242 f , 0.000000 f , 0.955423 f }, { 0.442863 f , 0.238856 f , 0.864188 f }, { 0.162460 f , 0.262866 f , 0.951056 f }, { −0.681718f , 0.147621 f , 0.716567 f }, { −0.809017f , 0.309017 f , 0.500000 f }, { −0.587785f , 0.425325 f , 0.688191 f }, { −0.850651f , 0.525731 f , 0.000000 f }, { −0.864188f , 0.442863 f , 0.238856 f }, { −0.716567f , 0.681718 f , 0.147621 f }, { −0.688191f , 0.587785 f , 0.425325 f }, { −0.500000f , 0.809017 f , 0.309017 f }, { −0.238856f , 0.864188 f , 0.442863 f }, { −0.425325f , 0.688191 f , 0.587785 f }, { −0.716567f , 0.681718 f , −0.147621 f }, { −0.500000f , 0.809017 f , −0.309017 f }, { −0.525731f , 0.850651 f , 0.000000 f }, { 0.000000 f , 0.850651 f , −0.525731 f }, { −0.238856f , 0.864188 f , −0.442863 f }, { 0.000000 f , 0.955423 f , −0.295242 f }, { −0.262866f , 0.951056 f , −0.162460 f }, { 0.000000 f , 1.000000 f , 0.000000 f }, { 0.000000 f , 0.955423 f , 0.295242 f }, { −0.262866f , 0.951056 f , 0.162460 f }, { 0.238856 f , 0.864188 f , 0.442863 f }, { 0.262866 f , 0.951056 f , 0.162460 f }, { 0.500000 f , 0.809017 f , 0.309017 f }, { 0.238856 f , 0.864188 f , −0.442863 f }, { 0.262866 f , 0.951056 f , −0.162460 f }, { 0.500000 f , 0.809017 f , −0.309017 f }, { 0.850651 f , 0.525731 f , 0.000000 f }, { 0.716567 f , 0.681718 f , 0.147621 f }, { 0.716567 f , 0.681718 f , −0.147621 f }, { 0.525731 f , 0.850651 f , 0.000000 f }, { 0.425325 f , 0.688191 f , 0.587785 f }, { 0.864188 f , 0.442863 f , 0.238856 f }, { 0.688191 f , 0.587785 f , 0.425325 f }, { 0.809017 f , 0.309017 f , 0.500000 f }, { 0.681718 f , 0.147621 f , 0.716567 f }, { 0.587785 f , 0.425325 f , 0.688191 f }, { 0.955423 f , 0.295242 f , 0.000000 f }, { 1.000000 f , 0.000000 f , 0.000000 f }, { 0.951056 f , 0.162460 f , 0.262866 f }, { 0.850651 f , −0.525731f , 0.000000 f }, { 0.955423 f , −0.295242f , 0.000000 f }, { 0.864188 f , −0.442863f , 0.238856 f }, { 0.951056 f , −0.162460f , 0.262866 f }, { 0.809017 f , −0.309017f , 0.500000 f }, { 0.681718 f , −0.147621f , 0.716567 f }, { 0.850651 f , 0.000000 f , 0.525731 f }, { 0.864188 f , 0.442863 f , −0.238856 f }, { 0.809017 f , 0.309017 f , −0.500000 f }, { 0.951056 f , 0.162460 f , −0.262866 f }, { 0.525731 f , 0.000000 f , −0.850651 f }, { 0.681718 f , 0.147621 f , −0.716567 f }, { 0.681718 f , −0.147621f , −0.716567 f }, { 0.850651 f , 0.000000 f , −0.525731 f }, { 0.809017 f , −0.309017f , −0.500000 f }, { 0.864188 f , −0.442863f , −0.238856 f }, { 0.951056 f , −0.162460f , −0.262866 f }, { 0.147621 f , 0.716567 f , −0.681718 f }, { 0.309017 f , 0.500000 f , −0.809017 f }, { 0.425325 f , 0.688191 f , −0.587785 f }, { 0.442863 f , 0.238856 f , −0.864188 f }, { 0.587785 f , 0.425325 f , −0.688191 f }, { 0.688191 f , 0.587785 f , −0.425325 f }, { −0.147621f , 0.716567 f , −0.681718 f }, { −0.309017f , 0.500000 f , −0.809017 f }, { 0.000000 f , 0.525731 f , −0.850651 f }, { −0.525731f , 0.000000 f , −0.850651 f }, { −0.442863f , 0.238856 f , −0.864188 f }, { −0.295242f , 0.000000 f , −0.955423 f }, { −0.162460f , 0.262866 f , −0.951056 f }, { 0.000000 f , 0.000000 f , −1.000000 f }, { 0.295242 f , 0.000000 f , −0.955423 f }, { 0.162460 f , 0.262866 f , −0.951056 f }, { −0.442863f , −0.238856f , −0.864188 f }, { −0.309017f , −0.500000f , −0.809017 f }, { −0.162460f , −0.262866f , −0.951056 f }, { 0.000000 f , −0.850651f , −0.525731 f }, { −0.147621f , −0.716567f , −0.681718 f }, { 0.147621 f , −0.716567f , −0.681718 f }, { 0.000000 f , −0.525731f , −0.850651 f }, { 0.309017 f , −0.500000f , −0.809017 f }, { 0.442863 f , −0.238856f , −0.864188 f }, { 0.162460 f , −0.262866f , −0.951056 f }, { 0.238856 f , −0.864188f , −0.442863 f }, { 0.500000 f , −0.809017f , −0.309017 f }, { 0.425325 f , −0.688191f , −0.587785 f }, { 0.716567 f , −0.681718f , −0.147621 f }, { 0.688191 f , −0.587785f , −0.425325 f }, { 0.587785 f , −0.425325f , −0.688191 f }, { 0.000000 f , −0.955423f , −0.295242 f },

{

{

{

{

{

{

{

{

{

{

{

{

{

{

0.000000 f , −1.000000f , 0.000000 f }, { 0.262866 f , −0.951056f , −0.162460 f }, { 0.000000 f , −0.850651f , 0.525731 f }, { 0.000000 f , −0.955423f , 0.295242 f }, 0.238856 f , −0.864188f , 0.442863 f }, { 0.262866 f , −0.951056f , 0.162460 f }, { 0.500000 f , −0.809017f , 0.309017 f }, { 0.716567 f , −0.681718f , 0.147621 f }, 0.525731 f , −0.850651f , 0.000000 f }, { −0.238856f , −0.864188f , −0.442863 f }, { −0.500000f , −0.809017f , −0.309017 f }, { −0.262866f , −0.951056f , −0.162460 f }, −0.850651f , −0.525731f , 0.000000 f }, { −0.716567f , −0.681718f , −0.147621 f }, { −0.716567f , −0.681718f , 0.147621 f }, { −0.525731f , −0.850651f , 0.000000 f }, −0.500000f , −0.809017f , 0.309017 f }, { −0.238856f , −0.864188f , 0.442863 f }, { −0.262866f , −0.951056f , 0.162460 f }, { −0.864188f , −0.442863f , 0.238856 f }, −0.809017f , −0.309017f , 0.500000 f }, { −0.688191f , −0.587785f , 0.425325 f }, { −0.681718f , −0.147621f , 0.716567 f }, { −0.442863f , −0.238856f , 0.864188 f }, −0.587785f , −0.425325f , 0.688191 f }, { −0.309017f , −0.500000f , 0.809017 f }, { −0.147621f , −0.716567f , 0.681718 f }, { −0.425325f , −0.688191f , 0.587785 f }, −0.162460f , −0.262866f , 0.951056 f }, { 0.442863 f , −0.238856f , 0.864188 f }, { 0.162460 f , −0.262866f , 0.951056 f }, { 0.309017 f , −0.500000f , 0.809017 f }, 0.147621 f , −0.716567f , 0.681718 f }, { 0.000000 f , −0.525731f , 0.850651 f }, { 0.425325 f , −0.688191f , 0.587785 f }, { 0.587785 f , −0.425325f , 0.688191 f }, 0.688191 f , −0.587785f , 0.425325 f }, { −0.955423f , 0.295242 f , 0.000000 f }, { −0.951056f , 0.162460 f , 0.262866 f }, { −1.000000f , 0.000000 f , 0.000000 f }, −0.850651f , 0.000000 f , 0.525731 f }, { −0.955423f , −0.295242f , 0.000000 f }, { −0.951056f , −0.162460f , 0.262866 f }, { −0.864188f , 0.442863 f , −0.238856 f }, −0.951056f , 0.162460 f , −0.262866 f }, { −0.809017f , 0.309017 f , −0.500000 f }, { −0.864188f , −0.442863f , −0.238856 f }, { −0.951056f , −0.162460f , −0.262866 f }, −0.809017f , −0.309017f , −0.500000 f }, { −0.681718f , 0.147621 f , −0.716567 f }, { −0.681718f , −0.147621f , −0.716567 f }, { −0.850651f , 0.000000 f , −0.525731 f }, −0.688191f , 0.587785 f , −0.425325 f }, { −0.587785f , 0.425325 f , −0.688191 f }, { −0.425325f , 0.688191 f , −0.587785 f }, { −0.425325f , −0.688191f , −0.587785 f }, −0.587785f , −0.425325f , −0.688191 f }, { −0.688191f , −0.587785f , −0.425325 f }

}; struct md2 : vertmodel , vertloader { struct md2 header { i n t magic ; i n t version ; i n t skinwidth , skinheight ; i n t framesize ; i n t numskins , numvertices , numtexcoords ; i n t numtriangles , numglcommands, numframes ; int offsetskins , offsettexcoords , o f f s e t t r i a n g l e s ; i n t offsetframes , offsetglcommands , offsetend ; }; struct md2 vertex { uchar vertex [ 3 ] , normalindex ; }; struct md2 frame { float scale [ 3 ] ; float translate [ 3 ] ; char name[ 1 6 ] ; }; md2( const char ∗name) : vertmodel (name) {} s t a t i c const char ∗formatname ( ) { return ”md2” ; } s t a t i c bool multiparted ( ) { return f a l s e ; } s t a t i c bool multimeshed ( ) { return f a l s e ; } i n t type ( ) const { return MDL MD2; } i n t linktype ( animmodel ∗m) const { return LINK COOP ; } struct md2meshgroup : vertmeshgroup

296

Foundations of Videogame Programming Code Repository

{ void genverts ( i n t ∗glcommands , vector &t c v e r t s , vector< ushort> &vindexes , vector &t r i s ) { hashtable tchash ; vector idxs ; f o r ( i n t ∗command = glcommands ; (∗command) ! = 0 ; ) { i n t numvertex = ∗command++; bool i s f a n = numvertexread(&header , s i z e o f ( md2 header ) ) ; l i l s w a p (&header . magic , s i z e o f ( md2 header ) / s i z e o f ( i n t ) ) ;

md2 vertex ∗tmpverts = new md2 vertex [ header . numvertices ] ; i n t f r a m e o f f s e t = header . offsetframes ; v e r t ∗curvert = m. v e r t s ; l o o p i ( header . numframes ) { md2 frame frame ; f i l e−>seek ( f r a m e o f f s e t , SEEK SET ) ; f i l e−>read(&frame , s i z e o f ( md2 frame ) ) ; l i l s w a p ( frame . scale , 6) ; f i l e−>read ( tmpverts , header . numvertices∗s i z e o f ( md2 vertex ) ) ; l o o p j (m. numverts ) { const md2 vertex &v = tmpverts [ vgen [ j ] ] ; curvert−>pos = vec ( v . vertex [0]∗ frame . scale [ 0 ] + frame . translate [ 0 ] , −(v . vertex [1]∗ frame . scale [ 1 ] + frame . translate [ 1 ] ) , v . vertex [2]∗ frame . scale [ 2 ] + frame . t r a n s l a t e [2]) ; const f l o a t ∗norm = md2normaltable [ v . normalindex ] ; curvert−>norm = vec ( norm [ 0 ] , −norm [ 1 ] , norm [ 2 ] ) ; curvert ++; } f r a m e o f f s e t += header . framesize ; } d e l e t e [ ] tmpverts ; delete f i l e ; return true ; } }; struct md2part : part { void getdefaultanim ( animinfo &info , i n t anim , uint varseed , dynent ∗d ) { // 0 3 6 7 8 9 10 11 12 13 14 15 16 17 // D D D D D D A P I R, E J T W FO SA GS GI s t a t i c const i n t frame [ ] = { 178, 184, 190, 183, 189, 197, 46, 54, 0 , 40, 162, 67, 95, 112, 72, 84, 7 , 6 }; s t a t i c const i n t range [ ] = { 6 , 6, 8, 1, 1, 1, 8, 4 , 40, 6 , 1 , 1 , 17, 11, 12, 11, 18, 1 }; // DE DY I F B L R H1 H2 H3 H4 H5 H6 H7 A1 A2 A3 A4 A5 A6 A7 PA J SI SW ED LA T WI LO GI GS s t a t i c const i n t animfr [ ] = { 5 , 2 , 8 , 9 , 9 , 9 , 9 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 7 , 11, 8 , 9 , 10, 14, 12, 13, 15, 17, 16 }; anim &= ANIM INDEX ; i f ( ( s i z e t ) anim >= s i z e o f ( animfr ) / s i z e o f ( animfr [ 0 ] ) ) { i n f o . frame = 0; i n f o . range = 1; return ; } i n t n = animfr [ anim ] ; switch ( anim ) { case ANIM DYING : case ANIM DEAD: n −= varseed%3; break ; case ANIM FORWARD: case ANIM BACKWARD: case ANIM LEFT : case ANIM RIGHT : case ANIM SWIM: i n f o . speed = 5500.0 f /d−>maxspeed ; break ; } i n f o . frame = frame [ n ] ; i n f o . range = range [ n ] ;

i f ( header . magic!=844121161 || header . version ! = 8 ) { delete f i l e ; return f a l s e ; } name = newstring ( filename ) ; numframes = header . numframes ; vertmesh &m = ∗new vertmesh ; m. group = t h i s ; meshes . add(&m) ; i n t ∗glcommands = new i n t [ header .numglcommands ] ; f i l e−>seek ( header . offsetglcommands , SEEK SET ) ; i n t numglcommands = f i l e−>read ( glcommands , header .numglcommands∗ sizeof ( int ) ) /sizeof ( int ) ; l i l s w a p ( glcommands , numglcommands ) ; i f ( numglcommands < header .numglcommands ) memset(&glcommands [ numglcommands] , 0 , ( header .numglcommands−numglcommands )∗ sizeof ( int ) ) ; } vector tcgen ; vector vgen ; vector t r i g e n ; genverts ( glcommands , tcgen , vgen , t r i g e n ) ; d e l e t e [ ] glcommands ; m. numverts = tcgen . length ( ) ; m. t c v e r t s = new t c v e r t [m. numverts ] ; memcpy(m. t c v e r t s , tcgen . getbuf ( ) , m. numverts∗s i z e o f ( t c v e r t ) ) ; m. numtris = t r i g e n . length ( ) ; m. t r i s = new t r i [m. numtris ] ; memcpy(m. t r i s , t r i g e n . getbuf ( ) , m. numtris∗s i z e o f ( t r i ) ) ; m. v e r t s = new v e r t [m. numverts∗numframes ] ;

}; meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { md2meshgroup ∗group = new md2meshgroup ; i f ( ! group−>load (name) ) { d e l e t e group ; return NULL; } return group ; } bool load ( ) { i f ( loaded ) return true ; part &mdl = ∗new md2part ; parts . add(&mdl ) ; mdl . model = t h i s ; mdl . index = 0;

engine/md3.h const char ∗pname = parentdir ( loadname ) ; defformatstring (name1) ( ” packages/models/%s/ t r i s .md2” , loadname ) ; mdl . meshes = sharemeshes ( path (name1) ) ; i f ( ! mdl . meshes ) { defformatstring (name2) ( ” packages/models/%s/ t r i s .md2” , pname) ; // t r y md2 in parent f o l d e r ( v e r t sharing ) mdl . meshes = sharemeshes ( path (name2) ) ; i f ( ! mdl . meshes ) return f a l s e ; } Texture ∗tex , ∗masks ; loadskin ( loadname , pname, tex , masks ) ; mdl . i n i t s k i n s ( tex , masks ) ; i f ( tex==notexture ) conoutf ( ” could not load model skin f o r %s ” , name1) ; loading = t h i s ; i d e n t f l a g s &= ˜IDF PERSIST ;

297

defformatstring (name3) ( ” packages/models/%s/md2. c f g ” , loadname ) ; i f ( ! e x e c f i l e ( name3, f a l s e ) ) { formatstring (name3) ( ” packages/models/%s/md2. c f g ” , pname) ; e x e c f i l e ( name3, f a l s e ) ; } i d e n t f l a g s |= IDF PERSIST ; loading = 0; scale /= 4; t r a n s l a t e . y = −t r a n s l a t e . y ; parts[0]−>t r a n s l a t e = t r a n s l a t e ; loopv ( parts ) parts [ i]−>meshes−>shared ++; return loaded = true ; } }; vertcommands md2commands;

engine/md3.h struct md3;

}

struct md3frame { vec bbmin , bbmax, o r i g i n ; f l o a t radius ; uchar name[ 1 6 ] ; };

name = newstring ( path ) ;

struct md3tag { char name[ 6 4 ] ; vec pos ; f l o a t rotation [ 3 ] [ 3 ] ; };

numframes = header . numframes ; i n t mesh offset = header . ofs meshes ; l o o p i ( header .nummeshes) { vertmesh &m = ∗new vertmesh ; m. group = t h i s ; meshes . add(&m) ; md3meshheader mheader ; f−>seek ( mesh offset , SEEK SET ) ; f−>read(&mheader , s i z e o f ( md3meshheader ) ) ; l i l s w a p (&mheader . f l a g s , 10) ;

struct md3vertex { short vertex [ 3 ] ; short normal ; };

m.name = newstring ( mheader .name) ; m. numtris = mheader . numtriangles ; m. t r i s = new t r i [m. numtris ] ; f−>seek ( mesh offset + mheader . o f s t r i a n g l e s , SEEK SET ) ; l o o p j (m. numtris ) { md3triangle t r i ; f−>read(& t r i , s i z e o f ( md3triangle ) ) ; // read the t r i a n g l e s l i l s w a p ( t r i . vertexindices , 3) ; loopk ( 3 ) m. t r i s [ j ] . v e r t [ k ] = ( ushort ) t r i . v e r t e x i n d i c e s [ k ] ; }

struct md3triangle { int vertexindices [ 3 ] ; }; struct md3header { char id [ 4 ] ; i n t version ; char name[ 6 4 ] ; int flags ; i n t numframes , numtags , nummeshes, numskins ; i n t ofs frames , o f s t a g s , ofs meshes , o f s e o f ; // o f f s e t s };

m. numverts = mheader . numvertices ; m. t c v e r t s = new t c v e r t [m. numverts ] ; f−>seek ( mesh offset + mheader . ofs uv , SEEK SET ) ; f−>read (m. t c v e r t s , m. numverts∗2∗s i z e o f ( f l o a t ) ) ; // read the UV data l i l s w a p (&m. t c v e r t s [ 0 ] . u, 2∗m. numverts ) ;

struct md3meshheader { char id [ 4 ] ; char name[ 6 4 ] ; int flags ; i n t numframes , numshaders , numvertices , numtriangles ; i n t o f s t r i a n g l e s , ofs shaders , ofs uv , o f s v e r t i c e s , meshsize ; // offsets };

m. v e r t s = new v e r t [ numframes∗m. numverts ] ; f−>seek ( mesh offset + mheader . o f s v e r t i c e s , SEEK SET ) ; l o o p j ( numframes∗m. numverts ) { md3vertex v ; f−>read(&v , s i z e o f ( md3vertex ) ) ; // read the v e r t i c e s l i l s w a p ( v . vertex , 4) ; m. v e r t s [ j ] . pos . x = v . vertex [0]/64.0 f ; m. v e r t s [ j ] . pos . y = −v . vertex [1]/64.0 f ; m. v e r t s [ j ] . pos . z = v . vertex [2]/64.0 f ;

struct md3 : vertmodel , vertloader { md3( const char ∗name) : vertmodel (name) {}

f l o a t lng = ( v . normal&0xFF )∗PI2/255.0 f ; // decode vertex normals f l o a t l a t = ( ( v . normal>>8)&0xFF )∗PI2/255.0 f ; m. v e r t s [ j ] . norm . x = cosf ( l a t )∗s i n f ( lng ) ; m. v e r t s [ j ] . norm . y = −s i n f ( l a t )∗s i n f ( lng ) ; m. v e r t s [ j ] . norm . z = cosf ( lng ) ;

s t a t i c const char ∗formatname ( ) { return ”md3” ; } i n t type ( ) const { return MDL MD3; } struct md3meshgroup : vertmeshgroup { bool load ( const char ∗path ) { stream ∗f = o p e n f i l e ( path , ” rb ” ) ; i f ( ! f ) return f a l s e ; md3header header ; f−>read(&header , s i z e o f ( md3header ) ) ; l i l s w a p (&header . version , 1) ; l i l s w a p (&header . f l a g s , 9) ; i f ( strncmp ( header . id , ” IDP3 ” , 4) ! = 0 || header . version ! = 15) // header check { delete f ; conoutf ( ”md3: corrupted header ” ) ; return f a l s e ;

} mesh offset += mheader . meshsize ; } numtags = header . numtags ; i f ( numtags ) { tags = new tag [ numframes∗numtags ] ; f−>seek ( header . o f s t a g s , SEEK SET ) ; md3tag tag ; l o o p i ( header . numframes∗header . numtags ) {

298

Foundations of Videogame Programming Code Repository f−>read(&tag , s i z e o f ( md3tag ) ) ; l i l s w a p (& tag . pos . x , 12) ; i f ( tag .name[ 0 ] && imeshes ) return f a l s e ; } e l s e // md3 without configuration , t r y d e f a u l t t r i s and skin { i d e n t f l a g s |= IDF PERSIST ; loading = NULL; i f ( ! loaddefaultparts ( ) ) return f a l s e ; } scale /= 4; t r a n s l a t e . y = −t r a n s l a t e . y ; parts[0]−>t r a n s l a t e = t r a n s l a t e ; loopv ( parts ) parts [ i]−>meshes−>shared ++; return loaded = true ;

#endif } } delete f ; return true ; } }; meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { md3meshgroup ∗group = new md3meshgroup ; i f ( ! group−>load (name) ) { d e l e t e group ; return NULL; } return group ; } bool loaddefaultparts ( ) { const char ∗pname = parentdir ( loadname ) ; part &mdl = ∗new part ; parts . add(&mdl ) ; mdl . model = t h i s ;

} }; vertcommands md3commands;

engine/md5.h struct md5;

md5vert ∗v e r t i n f o ;

struct md5joint { vec pos ; quat o r i e n t ; };

md5mesh ( ) : weightinfo (NULL) , numweights ( 0 ) , v e r t i n f o (NULL) { }

struct md5weight { int joint ; f l o a t bias ; vec pos ; }; struct md5vert { f l o a t u, v ; ushort s t a r t , count ; }; struct md5hierarchy { s t r i n g name; i n t parent , f l a g s , s t a r t ; }; struct md5 : skelmodel , skelloader { md5( const char ∗name) : skelmodel (name) {} s t a t i c const char ∗formatname ( ) { return ”md5” ; } i n t type ( ) const { return MDL MD5; } struct md5mesh : skelmesh { md5weight ∗weightinfo ; i n t numweights ;

˜md5mesh ( ) { cleanup ( ) ; } void cleanup ( ) { DELETEA( weightinfo ) ; DELETEA( v e r t i n f o ) ; } void b u i l d v e r t s ( vector &j o i n t s ) { l o o p i ( numverts ) { md5vert &v = v e r t i n f o [ i ] ; vec pos ( 0 , 0 , 0) ; loopk ( v . count ) { md5weight &w = weightinfo [ v . s t a r t +k ] ; md5joint &j = j o i n t s [w. j o i n t ] ; vec wpos = j . o r i e n t . r o t a t e (w. pos ) ; wpos . add ( j . pos ) ; wpos . mul (w. bias ) ; pos . add ( wpos ) ; } v e r t &vv = v e r t s [ i ] ; vv . pos = pos ; vv . u = v . u ; vv . v = v . v ; blendcombo c ;

engine/md5.h i n t sorted = 0; l o o p j ( v . count ) { md5weight &w = weightinfo [ v . s t a r t + j ] ; sorted = c . addweight ( sorted , w. bias , w. j o i n t ) ; } c . f i n a l i z e ( sorted ) ; vv . blend = addblendcombo ( c ) ; } } void load ( stream ∗f , char ∗buf , s i z e t bufsize ) { md5weight w; md5vert v ; tri t ; i n t index ; while ( f−>g e t l i n e ( buf , bufsize ) && buf [ 0 ] ! = ’ } ’ ) { i f ( s t r s t r ( buf , ”// meshes : ” ) ) { char ∗s t a r t = strchr ( buf , ’ : ’ ) +1; i f (∗ s t a r t == ’ ’ ) s t a r t ++; char ∗end = s t a r t + s t r l e n ( s t a r t )−1; while ( end >= s t a r t && isspace (∗end ) ) end−−; name = newstring ( s t a r t , end+1−s t a r t ) ; } e l s e i f ( s t r s t r ( buf , ” shader ” ) ) { char ∗s t a r t = strchr ( buf , ’ ” ’ ) , ∗end = s t a r t ? strchr ( s t a r t +1 , ’ ” ’ ) : NULL; i f ( s t a r t && end ) { char ∗texname = newstring ( s t a r t +1 , end−(s t a r t +1) ) ; part ∗p = loading−>parts . l a s t ( ) ; p−>i n i t s k i n s ( notexture , notexture , group−>meshes . length () ) ; skin &s = p−>skins . l a s t ( ) ; s . tex = textureload ( makerelpath ( dir , texname ) , 0 , true , false ) ; d e l e t e [ ] texname ; } } e l s e i f ( sscanf ( buf , ” numverts %d ” , &numverts ) ==1) { numverts = max( numverts , 0) ; i f ( numverts ) { v e r t i n f o = new md5vert [ numverts ] ; v e r t s = new v e r t [ numverts ] ; } } e l s e i f ( sscanf ( buf , ” numtris %d ” , &numtris ) ==1) { numtris = max( numtris , 0) ; i f ( numtris ) t r i s = new t r i [ numtris ] ; } e l s e i f ( sscanf ( buf , ” numweights %d ” , &numweights ) ==1) { numweights = max( numweights , 0) ; i f ( numweights ) weightinfo = new md5weight [ numweights ] ; } e l s e i f ( sscanf ( buf , ” v e r t %d ( %f %f ) %hu %hu” , &index , &v . u, &v . v , &v . s t a r t , &v . count ) ==5) { i f ( index>=0 && index=0 && index=0 && indexg e t l i n e ( buf , s i z e o f ( buf ) ) ) { i n t tmp ; i f ( sscanf ( buf , ” MD5Version %d ” , &tmp ) ==1) { i f ( tmp!=10) { d e l e t e f ; return f a l s e ; } } e l s e i f ( sscanf ( buf , ” numJoints %d ” , &tmp ) ==1) { i f ( tmpnumbones>0) continue ; skel−>numbones = tmp ; skel−>bones = new boneinfo [ skel−>numbones ] ; } e l s e i f ( sscanf ( buf , ” numMeshes %d ” , &tmp ) ==1) { i f ( tmpg e t l i n e ( buf , s i z e o f ( buf ) ) && buf [ 0 ] ! = ’ } ’ ) { char ∗curbuf = buf , ∗curname = name; bool allowspace = f a l s e ; while (∗ curbuf && isspace (∗ curbuf ) ) curbuf ++; i f (∗ curbuf == ’ ” ’ ) { curbuf ++; allowspace = true ; } while (∗ curbuf && curname < &name[ s i z e o f (name) −1]) { char c = ∗curbuf ++; i f ( c == ’ ” ’ ) break ; i f ( isspace ( c ) && ! allowspace ) break ; ∗curname++ = c ; } ∗curname = ’ \ 0 ’ ; i f ( sscanf ( curbuf , ” %d ( %f %f %f ) ( %f %f %f ) ” , &parent , &j . pos . x , &j . pos . y , &j . pos . z , &j . o r i e n t . x , &j . o r i e n t . y , &j . o r i e n t . z ) ==7) { j . pos . y = −j . pos . y ; j . o r i e n t . x = −j . o r i e n t . x ; j . o r i e n t . z = −j . o r i e n t . z ; i f ( basejoints . length ( )numbones ) { i f ( ! skel−>bones [ basejoints . length ( ) ] . name) skel−>bones [ basejoints . length ( ) ] . name = newstring (name) ; skel−>bones [ basejoints . length ( ) ] . parent = parent ; } j . o r i e n t . restorew ( ) ; basejoints . add ( j ) ; } } i f ( basejoints . length ( ) ! = skel−>numbones ) { d e l e t e f ; return false ; } } e l s e i f ( s t r s t r ( buf , ”mesh {”) ) { md5mesh ∗m = new md5mesh; m−>group = t h i s ; meshes . add (m) ; m−>load ( f , buf , s i z e o f ( buf ) ) ; i f ( !m−>numtris || !m−>numverts ) { conoutf ( ” empty mesh in %s ” , filename ) ; meshes . removeobj (m) ; d e l e t e m; } } } i f ( skel−>shared l i n k c h i l d r e n ( ) ; loopv ( basejoints ) { boneinfo &b = skel−>bones [ i ] ; b . base = dualquat ( basejoints [ i ] . orient , basejoints [ i ] . pos ) ; ( b . invbase = b . base ) . i n v e r t ( ) ; } } loopv ( meshes ) { md5mesh &m = ∗(md5mesh ∗)meshes [ i ] ; m. b u i l d v e r t s ( basejoints ) ; i f ( smooth g e t l i n e ( buf , s i z e o f ( buf ) ) && buf [0]!= ’} ’;)

sortblendcombos ( ) ;

{ f o r ( char ∗src = buf , ∗next = src ; numdata < animdatalen ; numdata++ , src = next )

delete f ; return true ;

{

}

animdata [ numdata ] = s t r t o d ( src , &next ) ; i f ( next findskelanim ( filename ) ; i f ( sa ) return sa ;

} dualquat ∗frame = &animbones [ tmp∗skel−>numbones ] ; loopv ( basejoints ) { md5hierarchy &h = hierarchy [ i ] ; md5joint j = basejoints [ i ] ; i f ( h . s t a r t < animdatalen && h . f l a g s ) { f l o a t ∗jdata = &animdata [ h . s t a r t ] ; i f ( h . f l a g s &1) j . pos . x = ∗jdata ++; i f ( h . f l a g s &2) j . pos . y = −∗jdata ++; i f ( h . f l a g s &4) j . pos . z = ∗jdata ++; i f ( h . f l a g s &8) j . o r i e n t . x = −∗jdata ++; i f ( h . f l a g s &16) j . o r i e n t . y = ∗jdata ++; i f ( h . f l a g s &32) j . o r i e n t . z = −∗jdata ++; j . o r i e n t . restorew ( ) ; } frame [ i ] = dualquat ( j . orient , j . pos ) ; i f ( adjustments . inrange ( i ) ) adjustments [ i ] . adjust ( frame [ i ]) ; frame [ i ] . mul ( skel−>bones [ i ] . invbase ) ; i f ( h . parent >= 0) frame [ i ] . mul ( skel−>bones [ h . parent ] . base , dualquat ( frame [ i ] ) ) ; frame [ i ] . f i x a n t i p o d a l ( skel−>framebones [ i ] ) ; }

stream ∗f = o p e n f i l e ( filename , ” r ” ) ; i f ( ! f ) return NULL; vector hierarchy ; vector basejoints ; i n t animdatalen = 0 , animframes = 0; f l o a t ∗animdata = NULL; dualquat ∗animbones = NULL; char buf [ 5 1 2 ] ; while ( f−>g e t l i n e ( buf , s i z e o f ( buf ) ) ) { i n t tmp ; i f ( sscanf ( buf , ” MD5Version %d ” , &tmp ) ==1) { i f ( tmp!=10) { d e l e t e f ; return NULL; } } e l s e i f ( sscanf ( buf , ” numJoints %d ” , &tmp ) ==1) { i f ( tmp! = skel−>numbones ) { d e l e t e f ; return NULL; } } e l s e i f ( sscanf ( buf , ” numFrames %d ” , &animframes ) ==1) { i f ( animframes0) animdata = new f l o a t [ animdatalen ] ; } e l s e i f ( s t r s t r ( buf , ”bounds {”) ) { while ( f−>g e t l i n e ( buf , s i z e o f ( buf ) ) && buf [ 0 ] ! = ’ } ’ ) ; } e l s e i f ( s t r s t r ( buf , ” hierarchy {”) ) { while ( f−>g e t l i n e ( buf , s i z e o f ( buf ) ) && buf [ 0 ] ! = ’ } ’ ) { md5hierarchy h ; i f ( sscanf ( buf , ” %100s %d %d %d ” , h .name, &h . parent , &h . f l a g s , &h . s t a r t ) ==4) hierarchy . add ( h ) ; } } e l s e i f ( s t r s t r ( buf , ” baseframe {”) ) { while ( f−>g e t l i n e ( buf , s i z e o f ( buf ) ) && buf [ 0 ] ! = ’ } ’ ) { md5joint j ; i f ( sscanf ( buf , ” ( %f %f %f ) ( %f %f %f ) ” , &j . pos . x , &j . pos . y , &j . pos . z , &j . o r i e n t . x , &j . o r i e n t . y , &j . o r i e n t . z ) ==6) { j . pos . y = −j . pos . y ; j . o r i e n t . x = −j . o r i e n t . x ; j . o r i e n t . z = −j . o r i e n t . z ; j . o r i e n t . restorew ( ) ; basejoints . add ( j ) ; } } i f ( basejoints . length ( ) ! = skel−>numbones ) { d e l e t e f ; return NULL; } animbones = new dualquat [ ( skel−>numframes+animframes )∗skel −>numbones ] ; i f ( skel−>framebones ) { memcpy( animbones , skel−>framebones , skel−>numframes∗ skel−>numbones∗s i z e o f ( dualquat ) ) ; d e l e t e [ ] skel−>framebones ; } skel−>framebones = animbones ; animbones += skel−>numframes∗skel−>numbones ; sa = &skel−>addskelanim ( filename ) ; sa−>frame = skel−>numframes ; sa−>range = animframes ; skel−>numframes += animframes ; } e l s e i f ( sscanf ( buf , ” frame %d ” , &tmp ) ==1) {

} } DELETEA( animdata ) ; delete f ; return sa ; } bool load ( const char ∗meshfile , f l o a t smooth ) { name = newstring ( meshfile ) ; i f ( ! loadmesh ( meshfile , smooth ) ) return f a l s e ; return true ; } }; meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { md5meshgroup ∗group = new md5meshgroup ; group−>shareskeleton ( va arg ( args , char ∗) ) ; i f ( ! group−>load ( name, va arg ( args , double ) ) ) { d e l e t e group ; return NULL; } return group ; } bool loaddefaultparts ( ) { skelpart &mdl = ∗new skelpart ; parts . add(&mdl ) ; mdl . model = t h i s ; mdl . index = 0; mdl . pitchscale = mdl . p i t c h o f f s e t = mdl . pitchmin = mdl . pitchmax = 0; adjustments . s e t s i z e ( 0 ) ; const char ∗fname = loadname + s t r l e n ( loadname ) ; do −−fname ; while ( fname >= loadname && ∗fname ! = ’ / ’ && ∗fname ! = ’ \ \ ’ ) ; fname++; defformatstring (meshname) ( ” packages/models/%s/%s .md5mesh” , loadname , fname ) ; mdl . meshes = sharemeshes ( path (meshname) , NULL, 2 . 0 ) ; i f ( ! mdl . meshes ) return f a l s e ; mdl . initanimparts ( ) ; mdl . i n i t s k i n s ( ) ; defformatstring ( animname ) ( ” packages/models/%s/%s .md5anim” , loadname , fname ) ; ( ( md5meshgroup ∗)mdl . meshes )−>loadanim ( path ( animname ) ) ; return true ; } bool load ( ) { i f ( loaded ) return true ; formatstring ( d i r ) ( ” packages/models/%s ” , loadname ) ; defformatstring ( cfgname ) ( ” packages/models/%s/md5. c f g ” , loadname ) ; loading = t h i s ; i d e n t f l a g s &= ˜IDF PERSIST ;

engine/menus.cpp loading = NULL; } scale /= 4; parts[0]−>t r a n s l a t e = t r a n s l a t e ; loopv ( parts ) { skelpart ∗p = ( skelpart ∗) parts [ i ] ; p−>endanimparts ( ) ; p−>meshes−>shared ++; } return loaded = true ;

i f ( e x e c f i l e ( cfgname , f a l s e ) && parts . length ( ) ) // configured md5, w i l l c a l l the md5∗ commands below { i d e n t f l a g s |= IDF PERSIST ; loading = NULL; loopv ( parts ) i f ( ! parts [ i]−>meshes ) return f a l s e ; } e l s e // md5 without configuration , t r y d e f a u l t t r i s and skin { i d e n t f l a g s |= IDF PERSIST ; i f ( ! loaddefaultparts ( ) ) { loading = NULL; return f a l s e ; }

} }; skelcommands md5commands;

engine/menus.cpp d e f a u l t : return 0;

// menus. cpp : ingame menu system ( also used f o r scores and s e r v e r l i s t ) } #include ” engine . h”

}

#define GUI TITLE COLOR 0xFFDD88 #define GUI BUTTON COLOR 0xFFFFFF #define GUI TEXT COLOR 0xDDFFDD

f l o a t g e t f l o a t ( ) const { switch ( type ) { case INT : return f l o a t ( v a l . i ) ; case FLOAT: return v a l . f ; case STRING : return f l o a t ( p a r s e f l o a t ( v a l . s ) ) ; d e f a u l t : return 0; } }

static static static static

vec int int g3d

menupos ; menustart = 0; menutab = 1; gui ∗cgui = NULL;

struct menu : g3d callback { char ∗name, ∗header ; uint ∗contents , ∗i n i t , ∗onclear ; bool showtab ;

const char ∗g e t s t r i n g ( ) const { switch ( type ) { case INT : return i n t s t r ( v a l . i ) ; case FLOAT: return i n t s t r ( i n t ( f l o o r ( v a l . f ) ) ) ; case STRING : return v a l . s ; d e f a u l t : return ” ” ; } }

menu ( ) : name(NULL) , header (NULL) , contents (NULL) , i n i t (NULL) , onclear (NULL) , showtab ( true ) {} void gui ( g3d gui &g , bool f i r s t p a s s ) { cgui = &g ; cgui−>s t a r t ( menustart , 0.03 f , showtab ? &menutab : NULL) ; i f ( showtab ) cgui−>tab ( header ? header : name, GUI TITLE COLOR ) ; execute ( contents ) ; cgui−>end ( ) ; cgui = NULL; }

void run ( ) { i f ( type == ACTION ) { i f ( v a l . s ) execute ( v a l . s ) ; } e l s e i f ( id ) switch ( id−>type ) { case ID VAR : setvarchecked ( id , g e t i n t ( ) ) ; break ; case ID FVAR : setfvarchecked ( id , g e t f l o a t ( ) ) ; break ; case ID SVAR : setsvarchecked ( id , g e t s t r i n g ( ) ) ; break ; case ID ALIAS : a l i a s ( id−>name, g e t s t r i n g ( ) ) ; break ; } }

v i r t u a l void c l e a r ( ) { i f ( onclear ) { freecode ( onclear ) ; onclear = NULL; } } }; }; struct delayedupdate { enum { INT , FLOAT, STRING, ACTION } type ; ident ∗id ; union { int i ; float f ; char ∗s ; } val ; delayedupdate ( ) : type ( ACTION ) , id (NULL) { v a l . s = NULL; } ˜ delayedupdate ( ) { i f ( type == STRING || type == ACTION ) DELETEA( v a l . s ) ; } void schedule ( const char ∗s ) { type = ACTION; v a l . s = newstring ( s ) ; } void schedule ( ident ∗var , i n t i ) { type = INT ; id = var ; v a l . i = i ; } void schedule ( ident ∗var , f l o a t f ) { type = FLOAT; id = var ; v a l . f = f ; } void schedule ( ident ∗var , char ∗s ) { type = STRING ; id = var ; v a l . s = newstring ( s ) ; } i n t g e t i n t ( ) const { switch ( type ) { case INT : return v a l . i ; case FLOAT: return i n t ( v a l . f ) ; case STRING : return i n t ( s t r t o l ( v a l . s , NULL, 0) ) ;

static static static static

hashtable guis ; vector guistack ; vector updatelater ; bool shouldclearmenu = true , c l e a r l a t e r = f a l s e ;

VARP( menudistance , 16, 40, 256) ; VARP( menuautoclose , 32, 120, 4096) ; vec menuinfrontofplayer ( ) { vec d i r ; vecfromyawpitch ( camera1−>yaw , 0 , 1 , 0 , d i r ) ; d i r . mul ( menudistance ) . add ( camera1−>o ) ; d i r . z −= player−>eyeheight−1; return d i r ; } void popgui ( ) { menu ∗m = guistack . pop ( ) ; m−>c l e a r ( ) ; } void removegui (menu ∗m) { loopv ( guistack ) i f ( guistack [ i ]==m) { guistack . remove ( i ) ; m−>c l e a r ( ) ; return ; } }

301

302

Foundations of Videogame Programming Code Repository

void pushgui (menu ∗m, i n t pos = −1) { i f ( guistack . empty ( ) ) { menupos = menuinfrontofplayer ( ) ; g3d resetcursor ( ) ; } i f ( pos < 0) guistack . add (m) ; e l s e guistack . i n s e r t ( pos , m) ; i f ( pos < 0 || pos==guistack . length ( ) −1) { menutab = 1; menustart = t o t a l m i l l i s ; } i f (m−>i n i t ) execute (m−>i n i t ) ; } void r e s t o r e g u i ( i n t pos ) { i n t c l e a r = guistack . length ( )−pos−1; l o o p i ( c l e a r ) popgui ( ) ; menutab = 1; menustart = t o t a l m i l l i s ; }

} //@DOC name and icon are optional void guibutton ( char ∗name, char ∗action , char ∗icon ) { i f ( ! cgui ) return ; bool hideicon = ! strcmp ( icon , ” 0 ” ) ; i n t r e t = cgui−>button ( name, GUI BUTTON COLOR, hideicon ? NULL : ( icon [ 0 ] ? icon : ( s t r s t r ( action , ” showgui ” ) ? ”menu” : ” action ” ) ) ) ; i f ( r e t&G3D UP) { updatelater . add ( ) . schedule ( action [ 0 ] ? action : name) ; i f ( shouldclearmenu ) c l e a r l a t e r = true ; } e l s e i f ( r e t&G3D ROLLOVER) { a l i a s ( ” guirollovername ” , name) ; a l i a s ( ” g u i r o l l o v e r a c t i o n ” , action ) ; } } void guiimage ( char ∗path , char ∗action , f l o a t ∗scale , i n t ∗overlaid , char ∗a l t ) { i f ( ! cgui ) return ; Texture ∗t = textureload ( path , 0 , true , f a l s e ) ; i f ( t ==notexture ) { i f ( a l t [ 0 ] ) t = textureload ( a l t , 0 , true , f a l s e ) ; i f ( t ==notexture ) return ; } i n t r e t = cgui−>image ( t , ∗scale , ∗o v e r l a i d ! = 0 ) ; i f ( r e t&G3D UP) { i f (∗ action ) { updatelater . add ( ) . schedule ( action ) ; i f ( shouldclearmenu ) c l e a r l a t e r = true ; } } e l s e i f ( r e t&G3D ROLLOVER) { a l i a s ( ” guirolloverimgpath ” , path ) ; a l i a s ( ” guirolloverimgaction ” , action ) ; }

void showgui ( const char ∗name) { menu ∗m = guis . access (name) ; i f ( !m) return ; i n t pos = guistack . f i n d (m) ; i f ( pos 0 && guistack[0]−>name && ! strcmp ( guistack[0]−>name, ”main ” ) ) { clear−−; i f ( ! c l e a r ) return 1; } i f ( n>0) c l e a r = min ( clear , n ) ; l o o p i ( c l e a r ) popgui ( ) ; i f ( ! guistack . empty ( ) ) r e s t o re g u i ( guistack . length ( ) −1) ; return c l e a r ; } void c l e a r g u i s ( i n t l e v e l = −1) { i f ( l e v e l < 0) l e v e l = guistack . length ( ) ; loopvrev ( guistack ) { menu ∗m = guistack [ i ] ; i f (m−>onclear ) { uint ∗action = m−>onclear ; m−>onclear = NULL; execute ( action ) ; freecode ( action ) ; } } cleargui ( l e v e l ) ; } void guionclear ( char ∗action ) { i f ( guistack . empty ( ) ) return ; menu ∗m = guistack . l a s t ( ) ; i f (m−>onclear ) { freecode (m−>onclear ) ; m−>onclear = NULL; } i f ( action [ 0 ] ) m−>onclear = compilecode ( action ) ; } void guistayopen ( uint ∗contents ) { bool oldclearmenu = shouldclearmenu ; shouldclearmenu = f a l s e ; execute ( contents ) ; shouldclearmenu = oldclearmenu ; } void guinoautotab ( uint ∗contents ) { i f ( ! cgui ) return ; cgui−>allowautotab ( f a l s e ) ; execute ( contents ) ; cgui−>allowautotab ( true ) ;

} void g u i c o l o r ( i n t ∗c o l o r ) { i f ( cgui ) { defformatstring ( desc ) ( ” 0 x%06X” , ∗c o l o r ) ; cgui−>t e x t ( desc , ∗color , NULL) ; } } void guitextbox ( char ∗text , i n t ∗width , i n t ∗height , i n t ∗c o l o r ) { i f ( cgui && t e x t [ 0 ] ) cgui−>textbox ( text , ∗width ? ∗width : 12, ∗height ? ∗height : 1 , ∗c o l o r ? ∗c o l o r : 0xFFFFFF ) ; } void g u i t e x t ( char ∗name, char ∗icon ) { bool hideicon = ! strcmp ( icon , ” 0 ” ) ; i f ( cgui ) cgui−>t e x t ( name, ! hideicon && icon [ 0 ] ? GUI BUTTON COLOR : GUI TEXT COLOR, hideicon ? NULL : ( icon [ 0 ] ? icon : ” i n f o ” ) ) ; } void g u i t i t l e ( char ∗name) { i f ( cgui ) cgui−>t i t l e ( name, GUI TITLE COLOR ) ; } void guitab ( char ∗name) { i f ( cgui ) cgui−>tab ( name, GUI TITLE COLOR ) ; } void guibar ( ) { i f ( cgui ) cgui−>separator ( ) ; } void guistrut ( f l o a t ∗strut , i n t ∗a l t ) { i f ( cgui ) { i f (∗ a l t ) cgui−>s t r u t (∗ s t r u t ) ; e l s e cgui−>space(∗ s t r u t ) ; } } void guispring ( i n t ∗weight ) {

engine/menus.cpp

l i s t += strcspn ( l i s t , ”\n\t \0”) ; l i s t += strspn ( l i s t , ”\n\t ” ) ; } i f ( vals . empty ( ) ) return ; i n t v a l = g e t v a l ( var ) , o l d o f f s e t = vals . length ( ) −1, o f f s e t = o l d o f f s e t ; loopv ( vals ) i f ( v a l s l i d e r ( o f f s e t , 0 , vals . length ( ) −1, GUI TITLE COLOR , l a b e l ) ; i f ( o f f s e t ! = o l d o f f s e t ) updateval ( var , vals [ o f f s e t ] , onchange ) ; delete [ ] label ;

i f ( cgui ) cgui−>spring (max(∗ weight , 1) ) ; } void guicolumn ( i n t ∗c o l ) { i f ( cgui ) cgui−>column(∗ c o l ) ; } template s t a t i c void updateval ( char ∗var , T val , char ∗onchange ) { ident ∗id = w r i t e i d e n t ( var ) ; updatelater . add ( ) . schedule ( id , v a l ) ; i f ( onchange [ 0 ] ) updatelater . add ( ) . schedule ( onchange ) ; } s t a t i c i n t g e t v a l ( char ∗var ) { ident ∗id = readident ( var ) ; i f ( ! id ) return 0; switch ( id−>type ) { case ID VAR : return ∗id−>storage . i ; case ID FVAR : return i n t (∗ id−>storage . f ) ; case ID SVAR : return parseint (∗ id−>storage . s ) ; case ID ALIAS : return id−>g e t i n t ( ) ; d e f a u l t : return 0; } } s t a t i c f l o a t g e t f v a l ( char ∗var ) { ident ∗id = readident ( var ) ; i f ( ! id ) return 0; switch ( id−>type ) { case ID VAR : return ∗id−>storage . i ; case ID FVAR : return ∗id−>storage . f ; case ID SVAR : return p a r s e f l o a t (∗ id−>storage . s ) ; case ID ALIAS : return id−>g e t f l o a t ( ) ; d e f a u l t : return 0; } } s t a t i c const char ∗g e t s v a l ( char ∗var ) { ident ∗id = readident ( var ) ; i f ( ! id ) return ” ” ; switch ( id−>type ) { case ID VAR : return i n t s t r (∗ id−>storage . i ) ; case ID FVAR : return f l o a t s t r (∗ id−>storage . f ) ; case ID SVAR : return ∗id−>storage . s ; case ID ALIAS : return id−>g e t s t r ( ) ; d e f a u l t : return ” ” ; } } void g u i s l i d e r ( char ∗var , i n t ∗min , i n t ∗max, char ∗onchange ) { i f ( ! cgui ) return ; i n t o l d v a l = g e t v a l ( var ) , v a l = oldval , vmin = ∗max > INT MIN ? ∗min : getvarmin ( var ) , vmax = ∗max > INT MIN ? ∗max : getvarmax ( var ) ; cgui−>s l i d e r ( val , vmin , vmax, GUI TITLE COLOR ) ; i f ( v a l ! = o l d v a l ) updateval ( var , val , onchange ) ; } void g u i l i s t s l i d e r ( char ∗var , char ∗l i s t , char ∗onchange ) { i f ( ! cgui ) return ; vector vals ; l i s t += strspn ( l i s t , ”\n\t ” ) ; while (∗ l i s t ) { vals . add ( parseint ( l i s t ) ) ; l i s t += strcspn ( l i s t , ”\n\t \0”) ; l i s t += strspn ( l i s t , ”\n\t ” ) ; } i f ( vals . empty ( ) ) return ; i n t v a l = g e t v a l ( var ) , o l d o f f s e t = vals . length ( ) −1, o f f s e t = o l d o f f s e t ; loopv ( vals ) i f ( v a l s l i d e r ( o f f s e t , 0 , vals . length ( ) −1, GUI TITLE COLOR , i n t s t r ( v a l ) ) ; i f ( o f f s e t ! = o l d o f f s e t ) updateval ( var , vals [ o f f s e t ] , onchange ) ; } void guinameslider ( char ∗var , char ∗names, char ∗l i s t , char ∗onchange ) { i f ( ! cgui ) return ; vector vals ; l i s t += strspn ( l i s t , ”\n\t ” ) ; while (∗ l i s t ) { vals . add ( parseint ( l i s t ) ) ;

303

} void guicheckbox ( char ∗name, char ∗var , f l o a t ∗on , f l o a t ∗o f f , char ∗ onchange ) { bool enabled = g e t f v a l ( var ) !=∗ o f f ; i f ( cgui && cgui−>button ( name, GUI BUTTON COLOR, enabled ? ” checkbox on ” : ” checkbox off ” )&G3D UP) { updateval ( var , enabled ? ∗o f f : (∗on || ∗o f f ? ∗on : 1.0 f ) , onchange ) ; } } void guiradio ( char ∗name, char ∗var , f l o a t ∗n , char ∗onchange ) { bool enabled = g e t f v a l ( var ) ==∗n ; i f ( cgui && cgui−>button ( name, GUI BUTTON COLOR, enabled ? ” radio on ” : ” r a d i o o f f ” )&G3D UP) { i f ( ! enabled ) updateval ( var , ∗n , onchange ) ; } } void g u i b i t f i e l d ( char ∗name, char ∗var , i n t ∗mask, char ∗onchange ) { i n t v a l = g e t v a l ( var ) ; bool enabled = ( v a l & ∗mask) ! = 0; i f ( cgui && cgui−>button ( name, GUI BUTTON COLOR, enabled ? ” checkbox on ” : ” checkbox off ” )&G3D UP) { updateval ( var , enabled ? v a l & ˜∗mask : v a l | ∗mask, onchange ) ; } } //−ve length i n d i c a t e s a wrapped t e x t f i e l d o f any ( approx 260 chars ) length , | length | i s the f i e l d width void g u i f i e l d ( char ∗var , i n t ∗maxlength , char ∗onchange ) { i f ( ! cgui ) return ; const char ∗i n i t v a l = g e t s v a l ( var ) ; char ∗r e s u l t = cgui−>f i e l d ( var , GUI BUTTON COLOR, ∗maxlength ? ∗ maxlength : 12, 0 , i n i t v a l ) ; i f ( r e s u l t ) updateval ( var , result , onchange ) ; } //−ve maxlength i n d i c a t e s a wrapped t e x t f i e l d o f any ( approx 260 chars ) length , |maxlength| i s the f i e l d width void g u i e d i t o r ( char ∗name, i n t ∗maxlength , i n t ∗height , i n t ∗mode ) { i f ( ! cgui ) return ; cgui−>f i e l d ( name, GUI BUTTON COLOR, ∗maxlength ? ∗maxlength : 12, ∗ height , NULL, ∗modek e y f i e l d ( var , GUI BUTTON COLOR, ∗maxlength ? ∗ maxlength : −8, 0 , i n i t v a l ) ; i f ( r e s u l t ) updateval ( var , result , onchange ) ; } //use text to do more . . .

void g u i l i s t ( uint ∗contents ) { i f ( ! cgui ) return ; cgui−>pushlist ( ) ; execute ( contents ) ; cgui−>p o p l i s t ( ) ; } void g u i a l i g n ( i n t ∗align , uint ∗contents ) { i f ( ! cgui ) return ; cgui−>pushlist ( ) ;

304

Foundations of Videogame Programming Code Repository

i f (∗ a l i g n >= 0) cgui−>spring ( ) ; execute ( contents ) ; i f (∗ a l i g n == 0) cgui−>spring ( ) ; cgui−>p o p l i s t ( ) ; } void newgui ( char ∗name, char ∗contents , char ∗header , char ∗i n i t ) { menu ∗m = guis . access (name) ; i f ( !m) { name = newstring (name) ; m = &guis [name ] ; m−>name = name; } else { DELETEA(m−>header ) ; freecode (m−>contents ) ; freecode (m−>i n i t ) ; } i f ( header && header [ 0 ] ) { char ∗end = NULL; i n t v a l = s t r t o l ( header , &end , 0) ; i f ( end && !∗end ) { m−>header = NULL; m−>showtab = v a l ! = 0; } else { m−>header = newstring ( header ) ; m−>showtab = true ; } } else { m−>header = NULL; m−>showtab = true ; } m−>contents = compilecode ( contents ) ; m−>i n i t = i n i t && i n i t [ 0 ] ? compilecode ( i n i t ) : NULL; } menu ∗guiserversmenu = NULL; void guiservers ( uint ∗header , i n t ∗pagemin , i n t ∗pagemax ) { extern const char ∗showservers ( g3d gui ∗cgui , uint ∗header , i n t pagemin , i n t pagemax ) ; i f ( cgui ) { const char ∗command = showservers ( cgui , header , ∗pagemin , ∗pagemax > 0 ? ∗pagemax : INT MAX ) ; i f (command) { updatelater . add ( ) . schedule (command) ; i f ( shouldclearmenu ) c l e a r l a t e r = true ; guiserversmenu = c l e a r l a t e r || guistack . empty ( ) ? NULL : guistack . l a s t ( ) ; } } } void notifywelcome ( ) { i f ( guiserversmenu ) { i f ( guistack . length ( ) && guistack . l a s t ( ) == guiserversmenu ) clearguis ( ) ; guiserversmenu = NULL; } } COMMAND( newgui , ” ssss ” ) ; COMMAND( guibutton , ” sss ” ) ; COMMAND( guitext , ” ss ” ) ; COMMAND( guiservers , ” e i i ” ) ; ICOMMAND( cleargui , ” i ” , ( i n t ∗n ) , i n t r e t ( c l e a r g u i (∗n ) ) ) ; COMMAND( showgui , ” s ” ) ; COMMAND( hidegui , ” s ” ) ; COMMAND( guionclear , ” s ” ) ; COMMAND( guistayopen , ” e ” ) ; COMMAND( guinoautotab , ” e ” ) ; COMMAND( g u i l i s t , ” e ” ) ; COMMAND( guialign , ” i e ” ) ; COMMAND( g u i t i t l e , ” s ” ) ; COMMAND( guibar , ” ” ) ; COMMAND( guistrut , ” f i ” ) ; COMMAND( guispring , ” i ” ) ; COMMAND( guicolumn , ” i ” ) ; COMMAND( guiimage , ” s s f i s ” ) ;

COMMAND( g u i s l i d e r , ” sbbs ” ) ; COMMAND( g u i l i s t s l i d e r , ” sss ” ) ; COMMAND( guinameslider , ” ssss ” ) ; COMMAND( guiradio , ” s s f s ” ) ; COMMAND( g u i b i t f i e l d , ” s s i s ” ) ; COMMAND( guicheckbox , ” s s f f s ” ) ; COMMAND( guitab , ” s ” ) ; COMMAND( g u i f i e l d , ” s i s ” ) ; COMMAND( g u i k e y f i e l d , ” s i s ” ) ; COMMAND( guieditor , ” s i i i ” ) ; COMMAND( guicolor , ” i ” ) ; COMMAND( guitextbox , ” s i i i ” ) ; void guiplayerpreview ( i n t ∗model , i n t ∗team , i n t ∗weap , char ∗action , f l o a t ∗scale , i n t ∗o v e r l a i d ) { i f ( ! cgui ) return ; i n t r e t = cgui−>playerpreview (∗model , ∗team , ∗weap , ∗scale , ∗o v e r l a i d !=0) ; i f ( r e t&G3D UP) { i f (∗ action ) { updatelater . add ( ) . schedule ( action ) ; i f ( shouldclearmenu ) c l e a r l a t e r = true ; } } } COMMAND( guiplayerpreview , ” i i i s f i ” ) ; void guimodelpreview ( char ∗model , char ∗animspec , char ∗action , f l o a t ∗ scale , i n t ∗o v e r l a i d ) { i f ( ! cgui ) return ; i n t anim = ANIM ALL ; i f ( animspec [ 0 ] ) { i f ( i s d i g i t ( animspec [ 0 ] ) ) { anim = parseint ( animspec ) ; i f ( anim >= 0) anim %= ANIM INDEX ; e l s e anim = ANIM ALL ; } else { vector anims ; findanims ( animspec , anims ) ; i f ( anims . length ( ) ) anim = anims [ 0 ] ; } } i n t r e t = cgui−>modelpreview ( model , anim|ANIM LOOP, ∗scale , ∗o v e r l a i d !=0) ; i f ( r e t&G3D UP) { i f (∗ action ) { updatelater . add ( ) . schedule ( action ) ; i f ( shouldclearmenu ) c l e a r l a t e r = true ; } } } COMMAND( guimodelpreview , ” s s s f i ” ) ; struct change { i n t type ; const char ∗desc ; change ( ) {} change ( i n t type , const char ∗desc ) : type ( type ) , desc ( desc ) {} }; s t a t i c vector needsapply ; s t a t i c struct applymenu : menu { void gui ( g3d gui &g , bool f i r s t p a s s ) { i f ( guistack . empty ( ) ) return ; g . s t a r t ( menustart , 0.03 f ) ; g . t e x t ( ” the f o l l o w i n g s e t t i n g s have changed : ” , GUI TEXT COLOR, ” info ” ) ; loopv ( needsapply ) g . t e x t ( needsapply [ i ] . desc , GUI TEXT COLOR, ” i n f o ”) ; g . separator ( ) ; g . t e x t ( ” apply changes now? ” , GUI TEXT COLOR, ” i n f o ” ) ; i f ( g . button ( ” yes ” , GUI BUTTON COLOR, ” action ” )&G3D UP) { i n t changetypes = 0; loopv ( needsapply ) changetypes |= needsapply [ i ] . type ; i f ( changetypes&CHANGE GFX) updatelater . add ( ) . schedule ( ” r e s e t g l ” ) ; i f ( changetypes&CHANGE SOUND) updatelater . add ( ) . schedule ( ” resetsound ” ) ; c l e a r l a t e r = true ;

engine/model.h } i f ( g . button ( ” no ” , GUI BUTTON COLOR, ” action ” )&G3D UP) c l e a r l a t e r = true ; g . end ( ) ; } void c l e a r ( ) { menu : : c l e a r ( ) ; needsapply . shrink ( 0 ) ; } } applymenu ; VARP( applydialog , 0 , 1 , 1) ; s t a t i c bool processingmenu = f a l s e ; void addchange ( const char ∗desc , i n t type ) { i f ( ! applydialog ) return ; loopv ( needsapply ) i f ( ! strcmp ( needsapply [ i ] . desc , desc ) ) return ; needsapply . add ( change ( type , desc ) ) ; i f ( needsapply . length ( ) && guistack . f i n d (&applymenu ) < 0) pushgui(&applymenu , processingmenu ? max( guistack . length ( ) −1, 0) : −1) ; } void clearchanges ( i n t type ) { loopv ( needsapply ) { i f ( needsapply [ i ] . type&type ) { needsapply [ i ] . type &= ˜ type ; i f ( ! needsapply [ i ] . type ) needsapply . remove ( i−−); } } i f ( needsapply . empty ( ) ) removegui(&applymenu ) ; }

void menuprocess ( ) { processingmenu = true ; i n t wasmain = mainmenu, l e v e l = guistack . length ( ) ; loopv ( updatelater ) updatelater [ i ] . run ( ) ; updatelater . shrink ( 0 ) ; i f ( wasmain > mainmenu || c l e a r l a t e r ) { i f ( wasmain > mainmenu || l e v e l ==guistack . length ( ) ) c l e a r g u i s ( l e v e l ) ; clearlater = false ; } i f (mainmenu && ! isconnected ( true ) && guistack . empty ( ) ) showgui ( ” main ” ) ; processingmenu = f a l s e ; } VAR( mainmenu, 1 , 1 , 0) ; void clearmainmenu ( ) { i f (mainmenu && isconnected ( ) ) { mainmenu = 0; i f ( ! processingmenu ) c l e a r g u i ( ) ; } } void g3d mainmenu ( ) { i f ( ! guistack . empty ( ) ) { extern i n t usegui2d ; i f ( ! mainmenu && ! usegui2d && camera1−>o . d i s t ( menupos ) > menuautoclose ) c l e a r g u i ( ) ; e l s e g3d addgui ( guistack . l a s t ( ) , menupos, GUI 2D | GUI FOLLOW) ; } }

engine/model.h enum { MDL MD2 = 0 , MDL MD3, MDL MD5, MDL OBJ, MDL SMD, MDL IQM, NUMMODELTYPES };

v i r t u a l void preloadmeshes ( ) {} v i r t u a l void cleanup ( ) {}

struct model { f l o a t spinyaw , spinpitch , offsetyaw , o f f s e t p i t c h ; bool c o l l i d e , e l l i p s e c o l l i d e , shadow , alphadepth , depthoffset ; f l o a t scale ; vec t r a n s l a t e ; BIH ∗bih ; vec bbcenter , bbradius , bbextend ; f l o a t eyeheight , c o l l i d e r a d i u s , c o l l i d e h e i g h t ; i n t batch ;

v i r t u a l void startrender ( ) {} v i r t u a l void endrender ( ) {} void boundbox ( i n t frame , vec ¢er , vec &radius ) { i f ( frame ) calcbb ( frame , center , radius ) ; else { i f ( bbradius . i s z e r o ( ) ) calcbb ( 0 , bbcenter , bbradius ) ; center = bbcenter ; radius = bbradius ; } radius . add ( bbextend ) ; }

model ( ) : spinyaw ( 0 ) , spinpitch ( 0 ) , offsetyaw ( 0 ) , o f f s e t p i t c h ( 0 ) , c o l l i d e ( true ) , e l l i p s e c o l l i d e ( f a l s e ) , shadow ( true ) , alphadepth ( true ) , depthoffset ( f a l s e ) , scale ( 1 . 0 f ) , t r a n s l a t e ( 0 , 0 , 0) , bih ( 0 ) , bbcenter ( 0 , 0 , 0) , bbradius ( 0 , 0 , 0) , bbextend ( 0 , 0 , 0) , eyeheight ( 0 . 9 f ) , c o l l i d e r a d i u s ( 0 ) , c o l l i d e h e i g h t ( 0 ) , batch(−1) {} v i r t u a l ˜model ( ) { DELETEP( bih ) ; } v i r t u a l void calcbb ( i n t frame , vec ¢er , vec &radius ) = 0; v i r t u a l void render ( i n t anim , i n t basetime , i n t basetime2 , const vec & o , f l o a t yaw , f l o a t pitch , dynent ∗d , modelattach ∗a = NULL, const vec &c o l o r = vec ( 0 , 0 , 0) , const vec &d i r = vec ( 0 , 0 , 0) , f l o a t transparent = 1) = 0; v i r t u a l bool load ( ) = 0; v i r t u a l const char ∗name ( ) const = 0; v i r t u a l i n t type ( ) const = 0; v i r t u a l BIH ∗setBIH ( ) { return 0; } v i r t u a l bool envmapped ( ) { return f a l s e ; } v i r t u a l bool s k e l e t a l ( ) const { return f a l s e ; }

void c o l l i s i o n b o x ( i n t frame , vec ¢er , vec &radius ) { boundbox ( frame , center , radius ) ; i f ( collideradius ) { center [ 0 ] = center [ 1 ] = 0; radius [ 0 ] = radius [ 1 ] = c o l l i d e r a d i u s ; } i f ( collideheight ) { center [ 2 ] = radius [ 2 ] = c o l l i d e h e i g h t /2; } } f l o a t boundsphere ( i n t frame , vec ¢er ) { vec radius ; boundbox ( frame , center , radius ) ; return radius . magnitude ( ) ; }

v i r t u a l void setshader ( Shader ∗shader ) {} v i r t u a l void setenvmap ( f l o a t envmapmin, f l o a t envmapmax, Texture ∗ envmap ) {} v i r t u a l void setspec ( f l o a t spec ) {} v i r t u a l void setambient ( f l o a t ambient ) {} v i r t u a l void setglow ( f l o a t glow , f l o a t glowdelta , f l o a t glowpulse ) {} v i r t u a l void s e t g l a r e ( f l o a t specglare , f l o a t glowglare ) {} v i r t u a l void setalphatest ( f l o a t alpha ) {} v i r t u a l void setalphablend ( bool blend ) {} v i r t u a l void s e t f u l l b r i g h t ( f l o a t f u l l b r i g h t ) {} v i r t u a l void s e t c u l l f a c e ( bool c u l l f a c e ) {} v i r t u a l void preloadBIH ( ) { i f ( ! bih ) setBIH ( ) ; } v i r t u a l void preloadshaders ( ) {}

305

f l o a t above ( i n t frame = 0) { vec center , radius ; boundbox ( frame , center , radius ) ; return center . z+radius . z ; } };

306

Foundations of Videogame Programming Code Repository

engine/movie.cpp // Feedback on playing videos : // quicktime − ok // v l c − ok // xine − ok // mplayer − ok // totem − ok // avidemux − ok − 3Apr09−RockKeyman : had to swap UV channels as i t showed up blue // kino − ok #include ” engine . h” #include ” SDL mixer . h” VAR( dbgmovie , 0 , 0 , 1) ; struct aviindexentry { i n t frame , type , s i z e ; uint o f f s e t ; aviindexentry ( ) {} aviindexentry ( i n t frame , i n t type , i n t size , uint o f f s e t ) : frame ( frame ) , type ( type ) , s i z e ( s i z e ) , o f f s e t ( o f f s e t ) {} };

startchunk ( f c c ) ; f−>write ( l f c c , 4) ; t o t a l s i z e += 4; } void endchunk ( ) { assert ( chunkdepth >= 0) ; −−chunkdepth ; } void endlistchunk ( ) { assert ( chunkdepth >= 0) ; i n t s i z e = i n t ( t o t a l s i z e − chunkoffsets [ chunkdepth ] ) ; f−>seek(−4 − size , SEEK CUR) ; f−>p u t l i l ( s i z e ) ; f−>seek ( 0 , SEEK END) ; i f ( s i z e & 1) { f−>putchar (0x00 ) ; t o t a l s i z e ++; } endchunk ( ) ; } void writechunk ( const char ∗fcc , const void ∗data , uint len ) // s i m p l i f y startchunk ( ) /endchunk ( ) to avoid f−>seek ( ) {

struct avisegmentinfo { stream : : o f f s e t o f f s e t , v i d e o i n d e x o f f s e t , soundindexoffset ; int firstindex ; uint videoindexsize , soundindexsize , indexframes , videoframes , soundframes ; avisegmentinfo ( ) {} avisegmentinfo ( stream : : o f f s e t o f f s e t , i n t f i r s t i n d e x ) : o f f s e t ( o f f s e t ) , v i d e o i n d e x o f f s e t ( 0 ) , soundindexoffset ( 0 ) , f i r s t i n d e x ( f i r s t i n d e x ) , videoindexsize ( 0 ) , soundindexsize ( 0 ) , indexframes ( 0 ) , videoframes ( 0 ) , soundframes ( 0 ) {} }; struct a v i w r i t e r { stream ∗f ; uchar ∗yuv ; uint videoframes ; stream : : o f f s e t t o t a l s i z e ; const uint videow , videoh , videofps ; s t r i n g filename ; i n t soundfrequency , soundchannels ; Uint16 soundformat ; vector index ; vector segments ; stream : : o f f s e t f i l e f r a m e s o f f s e t , f i l e e x t f r a m e s o f f s e t , f i l e v i d e o o f f s e t , f i l e s o u n d o f f s e t , superindexvideooffset , superindexsoundoffset ; enum { MAX CHUNK DEPTH = 16, MAX SUPER INDEX = 1024 }; stream : : o f f s e t chunkoffsets [MAX CHUNK DEPTH ] ; i n t chunkdepth ; aviindexentry &addindex ( i n t frame , i n t type , i n t s i z e ) { avisegmentinfo &seg = segments . l a s t ( ) ; i n t i = index . length ( ) ; while(−−i >= seg . f i r s t i n d e x ) { aviindexentry &e = index [ i ] ; i f ( frame > e . frame || ( frame == e . frame && type write ( fcc , 4) ; f−>p u t l i l(s i z e ) ; t o t a l s i z e += 4 + 4; chunkoffsets [++chunkdepth ] = t o t a l s i z e ; t o t a l s i z e += s i z e ; } void listchunk ( const char ∗fcc , const char ∗l f c c ) {

f−>write ( fcc , 4) ; f−>p u t l i l ( len ) ; f−>write ( data , len ) ; t o t a l s i z e += 4 + 4 + len ; i f ( len & 1) { f−>putchar (0x00 ) ; t o t a l s i z e ++; } } void close ( ) { i f ( ! f ) return ; flushsegment ( ) ; uint soundindexes = 0 , videoindexes = 0 , soundframes = 0 , videoframes = 0 , indexframes = 0; loopv ( segments ) { avisegmentinfo &seg = segments [ i ] ; i f ( seg . soundindexsize ) soundindexes ++; videoindexes ++; soundframes += seg . soundframes ; videoframes += seg . videoframes ; indexframes += seg . indexframes ; } i f ( dbgmovie ) conoutf (CON DEBUG, ” f i l e f r a m e s : sound=%d , video=%d+%d ( dups )\n” , soundframes , videoframes , indexframes−videoframes ) ; f−>seek ( f i l e f r a m e s o f f s e t , SEEK SET ) ; f−>p u t l i l(segments [ 0 ] . indexframes ) ; f−>seek ( f i l e v i d e o o f f s e t , SEEK SET ) ; f−>p u t l i l(segments [ 0 ] . videoframes ) ; i f ( segments [ 0 ] . soundframes > 0) { f−>seek ( f i l e s o u n d o f f s e t , SEEK SET ) ; f−>p u t l i l(segments [ 0 ] . soundframes ) ; } f−>seek ( f i l e e x t f r a m e s o f f s e t , SEEK SET ) ; f−>p u t l i l(indexframes ) ; // t o t a l video frames f−>seek ( superindexvideooffset + 2 + 2 , SEEK SET ) ; f−>p u t l i l(videoindexes ) ; f−>seek ( superindexvideooffset + 2 + 2 + 4 + 4 + 4 + 4 + 4 , SEEK SET ); loopv ( segments ) { avisegmentinfo &seg = segments [ i ] ; f−>p u t l i l(seg . v i d e o i n d e x o f f s e t&stream : : o f f s e t (0xFFFFFFFFU ) ); f−>p u t l i l(seg . v i d e o i n d e x o f f s e t>>32); f−>p u t l i l(seg . videoindexsize ) ; f−>p u t l i l(seg . indexframes ) ; } i f ( soundindexes > 0) { f−>seek ( superindexsoundoffset + 2 + 2 , SEEK SET ) ; f−>p u t l i l(soundindexes ) ; f−>seek ( superindexsoundoffset + 2 + 2 + 4 + 4 + 4 + 4 + 4 , SEEK SET ) ; loopv ( segments ) { avisegmentinfo &seg = segments [ i ] ; i f ( ! seg . soundindexsize ) continue ; f−>p u t l i l(seg . soundindexoffset&stream : : o f f s e t (0 xFFFFFFFFU ) ) ; f−>p u t l i l(seg . soundindexoffset>>32); f−>p u t l i l(seg . soundindexsize ) ;

engine/movie.cpp f−>p u t l i l(seg . soundframes ) ; } } f−>seek ( 0 , SEEK END) ; DELETEP( f ) ; } a v i w r i t e r ( const char ∗name, uint w, uint h , uint fps , bool sound ) : f ( NULL) , yuv (NULL) , videoframes ( 0 ) , t o t a l s i z e ( 0 ) , videow (w&˜1) , videoh ( h&˜1) , videofps ( fps ) , soundfrequency ( 0 ) , soundchannels ( 0 ) , soundformat ( 0 ) { copystring ( filename , name) ; path ( filename ) ; i f ( ! s t r r c h r ( filename , ’ . ’ ) ) concatstring ( filename , ” . a v i ” ) ; extern bool nosound ; // sound . cpp i f ( sound && ! nosound ) { Mix QuerySpec(&soundfrequency , &soundformat , &soundchannels ) ; const char ∗desc ; switch ( soundformat ) { case AUDIO U8 : desc = ”u8 ” ; break ; desc = ” s8 ” ; break ; case AUDIO S8 : case AUDIO U16LSB: desc = ” u16l ” ; break ; case AUDIO U16MSB: desc = ”u16b ” ; break ; case AUDIO S16LSB : desc = ” s16l ” ; break ; case AUDIO S16MSB: desc = ” s16b ” ; break ; default : desc = ”unkn ” ; } i f ( dbgmovie ) conoutf (CON DEBUG, ” soundspec : %dhz %s x %d ” , soundfrequency , desc , soundchannels ) ; }

f−>p u t l i l(videow ) ; // width f−>p u t l i l(videoh ) ; // height f−>p u t l i l(3) ; // planes f−>p u t l i l(12) ; // bitcount f−>write ( ” I420 ” , 4) ; // compression f−>p u t l i l(videow∗videoh∗3/2) ; // imagesize f−>p u t l i l(0) ; // xres f−>p u t l i l(0) ; // yres ; f−>p u t l i l(0) ; // colorsused f−>p u t l i l(0) ; // colorsrequired endchunk ( ) ; // s t r f startchunk ( ” indx ” , 24 + 16∗MAX SUPER INDEX) ; superindexvideooffset = f−>t e l l ( ) ; f−>p u t l i l(4) ; // longs per entry f−>p u t l i l(0) ; // index o f indexes f−>p u t l i l(0) ; // e n t r i e s in use f−>write (”00 dc ” , 4) ; // chunk id f−>p u t l i l(0) ; // reserved 1 f−>p u t l i l(0) ; // reserved 2 f−>p u t l i l(0) ; // reserved 3 l o o p i (MAX SUPER INDEX) { f−>p u t l i l(0) ; // o f f s e t low f−>p u t l i l(0) ; // o f f s e t high f−>p u t l i l(0) ; // s i z e f−>p u t l i l(0) ; // duration } endchunk ( ) ; // indx

listchunk ( ” RIFF ” , ” AVI ” ) ;

startchunk ( ” vprp ” , 68) ; f−>p u t l i l(0) ; // video format token f−>p u t l i l(0) ; // video standard f−>p u t l i l(videofps ) ; // v e r t i c a l refresh rate f−>p u t l i l(videow ) ; // horizontal t o t a l f−>p u t l i l(videoh ) ; // v e r t i c a l t o t a l i n t gcd = screen−>w, rem = screen−>h ; while ( rem > 0) { gcd %= rem ; swap ( gcd , rem ) ; } f−>p u t l i l(screen−>h/gcd ) ; // aspect denominator f−>p u t l i l(screen−>w/gcd ) ; // aspect numerator f−>p u t l i l(videow ) ; // frame width f−>p u t l i l(videoh ) ; // frame height f−>p u t l i l(1) ; // f i e l d s per frame f−>p u t l i l(videoh ) ; // compressed bitmap height f−>p u t l i l(videow ) ; // compressed bitmap width f−>p u t l i l(videoh ) ; // v a l i d bitmap height f−>p u t l i l(videow ) ; // v a l i d bitmap width f−>p u t l i l(0) ; // v a l i d bitmap x o f f s e t f−>p u t l i l(0) ; // v a l i d bitmap y o f f s e t f−>p u t l i l(0) ; // video x o f f s e t f−>p u t l i l(0) ; // video y s t a r t endchunk ( ) ; // vprp

listchunk ( ” LIST ” , ” hdrl ” ) ;

endlistchunk ( ) ; // LIST s t r l

startchunk ( ” avih ” , 56) ; f−>p u t l i l(1000000 / videofps ) ; // microsecsperframe f−>p u t l i l(0) ; // maxbytespersec f−>p u t l i l(0) ; // reserved f−>p u t l i l(0x10 | 0x20 ) ; // f l a g s − hasindex |mustuseindex f i l e f r a m e s o f f s e t = f−>t e l l ( ) ; f−>p u t l i l(0) ; // totalvideoframes f−>p u t l i l(0) ; // i n i t i a l f r a m e s f−>p u t l i l(soundfrequency > 0 ? 2 : 1) ; // streams f−>p u t l i l(0) ; // b u f f e r s i z e f−>p u t l i l(videow ) ; // video width f−>p u t l i l(videoh ) ; // video height l o o p i ( 4 ) f−>p u t l i l(0) ; // reserved endchunk ( ) ; // avih

i f ( soundfrequency > 0) { const i n t bps = ( soundformat==AUDIO U8 || soundformat == AUDIO S8 ) ? 1 : 2;

} ˜ aviwriter ( ) { close ( ) ; i f ( yuv ) d e l e t e [ ] yuv ; } bool open ( ) { f = o p e n f i l e ( filename , ”wb ” ) ; i f ( ! f ) return f a l s e ; chunkdepth = −1;

listchunk ( ” LIST ” , ” s t r l ” ) ; startchunk ( ” strh ” , 56) ; f−>write ( ” vids ” , 4) ; // fcctype f−>write ( ” I420 ” , 4) ; // fcchandler f−>p u t l i l(0) ; // f l a g s f−>p u t l i l(0) ; // p r i o r i t y f−>p u t l i l(0) ; // i n i t i a l f r a m e s f−>p u t l i l(1) ; // scale f−>p u t l i l(videofps ) ; // rate f−>p u t l i l(0) ; // s t a r t f i l e v i d e o o f f s e t = f−>t e l l ( ) ; f−>p u t l i l(0) ; // length f−>p u t l i l(videow∗videoh∗3/2) ; // suggested b u f f e r s i z e f−>p u t l i l(0) ; // q u a l i t y f−>p u t l i l(0) ; // samplesize f−>p u t l i l(0) ; // frame l e f t f−>p u t l i l(0) ; // frame top f−>p u t l i l(videow ) ; // frame r i g h t f−>p u t l i l(videoh ) ; // frame bottom endchunk ( ) ; // strh startchunk ( ” s t r f ” , 40) ; f−>p u t l i l(40) ; //headersize

307

listchunk ( ” LIST ” , ” s t r l ” ) ; startchunk ( ” strh ” , 56) ; f−>write ( ” auds ” , 4) ; // fcctype f−>p u t l i l(1) ; // fcchandler − normally 4cc , but audio i s a s p e c i a l case f−>p u t l i l(0) ; // f l a g s f−>p u t l i l(0) ; // p r i o r i t y f−>p u t l i l(0) ; // i n i t i a l f r a m e s f−>p u t l i l(1) ; // scale f−>p u t l i l(soundfrequency ) ; // rate f−>p u t l i l(0) ; // s t a r t f i l e s o u n d o f f s e t = f−>t e l l ( ) ; f−>p u t l i l(0) ; // length f−>p u t l i l(soundfrequency∗bps∗soundchannels/2) ; // suggested b u f f e r s i z e ( t h i s i s a h a l f second ) f−>p u t l i l(0) ; // q u a l i t y f−>p u t l i l(bps∗soundchannels ) ; // samplesize f−>p u t l i l(0) ; // frame l e f t f−>p u t l i l(0) ; // frame top f−>p u t l i l(0) ; // frame r i g h t f−>p u t l i l(0) ; // frame bottom endchunk ( ) ; // strh startchunk ( ” s t r f ” , 18) ; f−>p u t l i l(1) ; // format ( uncompressed PCM) f−>p u t l i l(soundchannels ) ; // channels f−>p u t l i l(soundfrequency ) ; // sampleframes per second f−>p u t l i l(soundfrequency∗bps∗soundchannels ) ; // average bytes per second f−>p u t l i l(bps∗soundchannels ) ; // block a l i g n p u t l i l(bps∗8) ; // b i t s per sample f−>p u t l i l(0) ; // s i z e endchunk ( ) ; // s t r f

308

Foundations of Videogame Programming Code Repository i f ( ! yuv ) yuv = new uchar [ ( planesize ∗3) / 2 ] ; uchar ∗yplane = yuv , ∗uplane = yuv + planesize , ∗vplane = yuv + planesize + planesize /4; const i n t y s t r i d e = f l i p ∗i n t ( videow ) , uvstride = f l i p ∗i n t ( videow ) /2; i f ( f l i p < 0) { yplane −= i n t ( videoh−1)∗y s t r i d e ; uplane −= i n t ( videoh/2−1)∗uvstride ; vplane −= i n t ( videoh/2−1)∗uvstride ; }

startchunk ( ” indx ” , 24 + 16∗MAX SUPER INDEX) ; superindexsoundoffset = f−>t e l l ( ) ; f−>p u t l i l(4) ; // longs per entry f−>p u t l i l(0) ; // index o f indexes f−>p u t l i l(0) ; // e n t r i e s in use f−>write (”01wb” , 4) ; // chunk id f−>p u t l i l(0) ; // reserved 1 f−>p u t l i l(0) ; // reserved 2 f−>p u t l i l(0) ; // reserved 3 l o o p i (MAX SUPER INDEX) { f−>p u t l i l(0) ; // o f f s e t low f−>p u t l i l(0) ; // o f f s e t high f−>p u t l i l(0) ; // s i z e f−>p u t l i l(0) ; // duration } endchunk ( ) ; // indx

const uint s t r i d e = srcw12, h2 = ( y2n>>12) − y2i , y2low = ( ( y2n|(− i n t ( h2 )>>24))&0xFFFU ) + 1 − ( y&0xFFFU ) , y2high = ( y2n&0xFFFU ) + 1; y += hfrac ;

endlistchunk ( ) ; // LIST s t r l } listchunk ( ” LIST ” , ”odml ” ) ; startchunk ( ” dmlh” , 4) ; f i l e e x t f r a m e s o f f s e t = f−>t e l l ( ) ; f−>p u t l i l(0) ; endchunk ( ) ; // dmlh endlistchunk ( ) ; // LIST odml

const uchar ∗src = &p i x e l s [ y i∗s t r i d e ] , ∗src2 = &p i x e l s [ y2i∗ stride ] ; uchar ∗ydst = yplane , ∗ydst2 = yplane + ystride , ∗udst = uplane , ∗vdst = vplane ; f o r ( uint x = 0; x < dw ; ) { uint xn = x + wfrac − 1 , x i = x>>12, w = ( xn>>12) − xi , xlow = ( ( w+0xFFFU )&0x1000U ) − ( x&0xFFFU ) , xhigh = ( xn&0 xFFFU ) + 1; x += wfrac ; uint x2n = x + wfrac − 1 , x2i = x>>12, w2 = ( x2n>>12) − x2i , x2low = ( ( w2+0xFFFU )&0x1000U ) − ( x&0xFFFU ) , x2high = ( x2n&0xFFFU ) + 1; x += wfrac ;

listchunk ( ” LIST ” , ”INFO ” ) ; const char ∗software = ”Cube 2: Sauerbraten ” ; writechunk ( ” ISFT ” , software , s t r l e n ( software ) +1) ; endlistchunk ( ) ; // LIST INFO endlistchunk ( ) ; // LIST hdrl nextsegment ( ) ; return true ; } s t a t i c i n l i n e void boxsample ( const uchar ∗src , const uint s t r i d e , const uint area , const uint w, uint h , const uint xlow , const uint xhigh , const uint ylow , const uint yhigh , uint &bdst , uint &gdst , uint &rdst ) { const uchar ∗end = &src [w12)) ; gt = ylow∗( gt + ( ( src [1]∗ xlow + end[1]∗ xhigh )>>12)) ; r t = ylow∗( r t + ( ( src [2]∗ xlow + end[2]∗ xhigh )>>12)) ; i f (h) { f o r ( src += s t r i d e , end += s t r i d e ; −−h ; src += s t r i d e , end += stride ) { uint b = 0 , g = 0 , r = 0; f o r ( const uchar ∗cur = &src [ 4 ] ; cur < end ; cur += 4) { b += cur [ 0 ] ; g += cur [ 1 ] ; r += cur [ 2 ] ; } bt += ( b12)) ; } bdst = ( bt∗area )>>24; gdst = ( gt∗area )>>24; rdst = ( r t∗area )>>24; } void scaleyuv ( const uchar ∗pixels , uint srcw , uint srch ) { const i n t f l i p = −1; const uint planesize = videow ∗ videoh ;

uint b1 , g1 , r1 , b2 , g2 , r2 , b3 , g3 , r3 , b4 , g4 , r4 ; boxsample(& src [ xi p u t l i l(e . s i z e ) ; } endchunk ( ) ; // ix00 seg . videoindexsize = uint ( t o t a l s i z e − seg . v i d e o i n d e x o f f s e t ) ; i f ( soundframes ) { seg . soundframes = soundframes ; seg . soundindexoffset = t o t a l s i z e ; startchunk ( ” ix01 ” , 24 + soundframes∗8) ;

310

Foundations of Videogame Programming Code Repository f−>p u t l i l(2) ; // longs per entry f−>p u t l i l(0x0100 ) ; // index o f chunks f−>p u t l i l(soundframes ) ; // e n t r i e s in use f−>write (”01wb” , 4) ; // chunk id f−>p u t l i l(seg . o f f s e t&stream : : o f f s e t (0xFFFFFFFFU ) ) ; // o f f s e t low f−>p u t l i l(seg . o f f s e t >>32); // o f f s e t high f−>p u t l i l(0) ; // reserved 3 f o r ( i n t i = seg . f i r s t i n d e x ; i < index . length ( ) ; i ++) { aviindexentry &e = index [ i ] ; i f ( ! e . type ) continue ; f−>p u t l i l(e . o f f s e t + 4 + 4) ; f−>p u t l i l(e . s i z e ) ; } endchunk ( ) ; // ix01 seg . soundindexsize = uint ( t o t a l s i z e − seg . soundindexoffset ) ;

} endlistchunk ( ) ; // RIFF AVI/AVIX } bool nextsegment ( ) { i f ( segments . length ( ) ) { i f ( segments . length ( ) >= MAX SUPER INDEX) return f a l s e ; flushsegment ( ) ; listchunk ( ” RIFF ” , ”AVIX ” ) ; } listchunk ( ” LIST ” , ” movi ” ) ; segments . add ( avisegmentinfo ( chunkoffsets [ chunkdepth ] , index . length () ) ) ; return true ; } bool writevideoframe ( const uchar ∗pixels , uint srcw , uint srch , i n t format , uint frame )

void load ( uchar ∗stream , uint len , uint fnum ) { i f ( len > maxsize ) { DELETEA( sound ) ; sound = new uchar [ len ] ; maxsize = len ; } s i z e = len ; frame = fnum ; memcpy( sound , stream , len ) ; } void cleanup ( ) { DELETEA( sound ) ; maxsize = 0; } }; s t a t i c queue soundbuffers ; s t a t i c SDL mutex ∗soundlock = NULL; enum { MAXVIDEOBUFFERS = 2 }; // double b u f f e r struct videobuffer { uchar ∗video ; uint w, h , bpp , frame ; i n t format ; videobuffer ( ) : video (NULL){} ˜ videobuffer ( ) { cleanup ( ) ; } void i n i t ( i n t nw, i n t nh, i n t nbpp ) { DELETEA( video ) ; w = nw; h = nh ; bpp = nbpp ; video = new uchar [w∗h∗bpp ] ; format = −1; }

{ i f ( frame < videoframes ) return true ; switch ( format ) { case VID RGB : i f ( srcw ! = videow || srch ! = videoh ) scaleyuv ( pixels , srcw , srch ) ; e l s e encodeyuv ( p i x e l s ) ; break ; case VID YUV : compressyuv ( p i x e l s ) ; break ; }

void cleanup ( ) { DELETEA( video ) ; } }; s t a t i c queue videobuffers ; s t a t i c uint lastframe = ˜0U; s t a t i c GLuint s c a l e f b = 0 , s c a l e t e x [ 2 ] = { 0 , 0 }; s t a t i c uint scalew = 0 , scaleh = 0; s t a t i c GLuint encodefb = 0 , encoderb = 0; s t a t i c SDL Thread ∗thread = NULL; s t a t i c SDL mutex ∗videolock = NULL; s t a t i c SDL cond ∗shouldencode = NULL, ∗shouldread = NULL; bool isrecording ( ) { return f i l e ! = NULL; }

const uint framesize = ( videow ∗ videoh ∗ 3) / 2; i f ( t o t a l s i z e − segments . l a s t ( ) . o f f s e t + framesize > 1000∗1000∗1000 && ! nextsegment ( ) ) return f a l s e ; while ( videoframes videofps ) ; // s t r i c t l y speaking should lock to read dps − 1.0= perfect , 0.5= h a l f o f frames are beingdropped } i n t gettime ( ) { return inbetweenframes ? g e t c l o c k m i l l i s ( ) : t o t a l m i l l i s ; } i n t videoencoder ( void ∗data ) // runs on a separate thread { f o r ( i n t numvid = 0 , numsound = 0 ; ; ) { SDL LockMutex ( videolock ) ; f o r ( ; numvid > 0; numvid−−) videobuffers . remove ( ) ; SDL CondSignal ( shouldread ) ; while ( videobuffers . empty ( ) && s t a t e == REC OK) SDL CondWait ( shouldencode , videolock ) ; i f ( s t a t e ! = REC OK) { SDL UnlockMutex ( videolock ) ; break ; } videobuffer &m = videobuffers . removing ( ) ; numvid++; SDL UnlockMutex ( videolock ) ; i f ( f i l e−>soundfrequency > 0) { // chug data from lock protected b u f f e r to avoid holding lock while w r i t i n g to f i l e SDL LockMutex ( soundlock ) ; f o r ( ; numsound > 0; numsound−−) soundbuffers . remove ( ) ; f o r ( ; numsound < soundbuffers . length ( ) ; numsound++) { soundbuffer &s = soundbuffers . removing (numsound) ; i f ( s . frame > m. frame ) break ; // sync with video } SDL UnlockMutex ( soundlock ) ; l o o p i (numsound) {

engine/movie.cpp soundbuffer &s = soundbuffers . removing ( i ) ; i f ( ! f i l e−>writesound ( s . sound , s . size , s . frame ) ) s t a t e = REC FILERROR; }

311

shouldread = SDL CreateCond ( ) ; thread = SDL CreateThread ( videoencoder , NULL) ; i f ( f i l e−>soundfrequency > 0) Mix SetPostMix ( soundencoder , NULL) ; }

} i n t duplicates = m. frame − ( i n t ) f i l e−>videoframes + 1; i f ( duplicates > 0) // determine how many frames have been dropped over the sample window { dps −= s t a t s [ statsindex ] ; s t a t s [ statsindex ] = duplicates −1; dps += s t a t s [ statsindex ] ; statsindex = ( statsindex +1)%f i l e−>videofps ; } // p r i n t f ( ” frame %d− >%d (%d dps ) : sound = %d bytes\n” , f i l e−> videoframes , nextframenum , dps , m. soundlength ) ; i f ( c a l c q u a l i t y ( ) < movieminquality ) s t a t e = REC TOOSLOW; e l s e i f ( ! f i l e−>writevideoframe (m. video , m.w, m. h , m. format , m. frame ) ) s t a t e = REC FILERROR;

void cleanup ( ) { i f ( s c a l e f b ) { glDeleteFramebuffers ( 1 , &s c a l e f b ) ; s c a l e f b = 0; } i f ( s c a l e t e x [ 0 ] || s c a l e t e x [ 1 ] ) { glDeleteTextures ( 2 , s c a l e t e x ) ; memset ( scaletex , 0 , s i z e o f ( s c a l e t e x ) ) ; } scalew = scaleh = 0; i f ( encodefb ) { glDeleteFramebuffers ( 1 , &encodefb ) ; encodefb = 0; } i f ( encoderb ) { glDeleteRenderbuffers ( 1 , &encoderb ) ; encoderb = 0; } } void stop ( ) { i f ( ! f i l e ) return ; i f ( s t a t e == REC OK) s t a t e = REC USERHALT; i f ( f i l e−>soundfrequency > 0) Mix SetPostMix (NULL, NULL) ;

m. frame = ˜0U; }

SDL LockMutex ( videolock ) ; // wakeup thread enough to k i l l i t SDL CondSignal ( shouldencode ) ; SDL UnlockMutex ( videolock ) ;

return 0; }

SDL WaitThread ( thread , NULL) ; // block u n t i l thread i s f i n i s h e d void soundencoder ( void ∗udata , Uint8 ∗stream , i n t len ) // callback occurs on a separate thread { SDL LockMutex ( soundlock ) ; i f ( soundbuffers . f u l l ( ) ) { i f ( movieminquality >= 1) s t a t e = REC TOOSLOW; } e l s e i f ( s t a t e == REC OK) { uint nextframe = (max( gettime ( ) − starttime , 0)∗f i l e−>videofps ) /1000; soundbuffer &s = soundbuffers . add ( ) ; s . load ( ( uchar ∗)stream , len , nextframe ) ; } SDL UnlockMutex ( soundlock ) ; }

cleanup ( ) ; l o o p i (MAXVIDEOBUFFERS) videobuffers . data [ i ] . cleanup ( ) ; l o o p i (MAXSOUNDBUFFERS) soundbuffers . data [ i ] . cleanup ( ) ; SDL SDL SDL SDL

DestroyMutex ( soundlock ) ; DestroyMutex ( videolock ) ; DestroyCond ( shouldencode ) ; DestroyCond ( shouldread ) ;

soundlock = videolock = NULL; shouldencode = shouldread = NULL; thread = NULL; s t a t i c const char ∗mesgs [ ] = { ” ok ” , ” stopped ” , ” computer too slow ” , ” f i l e e r r o r ”}; conoutf ( ” movie recording halted : %s , %d frames ” , mesgs [ s t a t e ] , f i l e −>videoframes ) ;

void s t a r t ( const char ∗filename , i n t videofps , i n t videow , i n t videoh , bool sound ) {

DELETEP( f i l e ) ; s t a t e = REC OK;

i f ( f i l e ) return ; } useshaderbyname ( ” moviergb ” ) ; useshaderbyname ( ” movieyuv ” ) ; useshaderbyname ( ” moviey ” ) ; useshaderbyname ( ” movieu ” ) ; useshaderbyname ( ” moviev ” ) ; i n t fps , b e s t d i f f , w o r s t d i f f ; g e t f p s ( fps , b e s t d i f f , w o r s t d i f f ) ; i f ( videofps > fps ) conoutf (CON WARN, ” frame rate may be too low to capture at %d fps ” , videofps ) ; i f ( videow%2) videow += 1; i f ( videoh%2) videoh += 1; f i l e = new a v i w r i t e r ( filename , videow , videoh , videofps , sound ) ; i f ( ! f i l e−>open ( ) ) { conoutf (CON ERROR, ” unable to create f i l e %s ” , filename ) ; DELETEP( f i l e ) ; return ; } conoutf ( ” movie recording to : %s %dx%d @ %dfps%s ” , f i l e−>filename , f i l e−>videow , f i l e−>videoh , f i l e−>videofps , ( f i l e−> soundfrequency>0)?” + sound ” : ” ” ) ; starttime = gettime ( ) ; l o o p i ( f i l e−>videofps ) s t a t s [ i ] = 0; statsindex = 0; dps = 0; lastframe = ˜0U; videobuffers . c l e a r ( ) ; l o o p i (MAXVIDEOBUFFERS) { uint w = screen−>w, h = screen−>w; videobuffers . data [ i ] . i n i t (w, h , 4) ; videobuffers . data [ i ] . frame = ˜0U; } soundbuffers . c l e a r ( ) ; soundlock = SDL CreateMutex ( ) ; videolock = SDL CreateMutex ( ) ; shouldencode = SDL CreateCond ( ) ;

void drawquad ( f l o a t tw , f l o a t th , f l o a t x , f l o a t y , f l o a t w, f l o a t h ) { glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2f ( tw , 0) ; g l V e r t e x 2 f ( x+w, y ) ; glTexCoord2f ( 0 , th ) ; g l V e r t e x 2 f ( x , y+h ) ; glTexCoord2f ( tw , th ) ; g l V e r t e x 2 f ( x+w, y+h ) ; glEnd ( ) ; } void readbuffer ( videobuffer &m, uint nextframe ) { bool accelyuv = movieaccelyuv && renderpath ! =R FIXEDFUNCTION && ! (m .w%8) , usefbo = movieaccel && hasFBO && hasTR && f i l e−>videow w && f i l e−>videoh h && ( accelyuv || f i l e−>videow < ( uint ) screen−>w || f i l e−> videoh < ( uint ) screen−>h ) ; uint w = screen−>w, h = screen−>h ; i f ( usefbo ) { w = f i l e−>videow ; h = f i l e−>videoh ; } i f (w ! = m.w || h ! = m. h ) m. i n i t (w, h , 4) ; m. format = a v i w r i t e r : : VID RGB ; m. frame = nextframe ; g l P i x e l S t o r e i ( GL PACK ALIGNMENT, t e x a l i g n (m. video , m.w, 4) ) ; i f ( usefbo ) { uint tw = screen−>w, th = screen−>h ; i f ( hasFBB && m o v i e a c c e l b l i t ) { tw = max( tw/2 , m.w) ; th = max( th /2 , m. h ) ; } i f ( tw ! = scalew || th ! = scaleh ) { i f ( ! s c a l e f b ) glGenFramebuffers ( 1 , &s c a l e f b ) ; loopi ( 2 ) { i f ( ! s c a l e t e x [ i ] ) glGenTextures ( 1 , &s c a l e t e x [ i ] ) ; createtexture ( s c a l e t e x [ i ] , tw , th , NULL, 3 , 1 , GL RGB, GL TEXTURE RECTANGLE ARB) ; } scalew = tw ; scaleh = th ; } i f ( accelyuv && ( ! encodefb || ! encoderb ) )

312

Foundations of Videogame Programming Code Repository {

} else { glBindFramebuffer (GL FRAMEBUFFER EXT, s c a l e f b ) ; glReadPixels ( 0 , 0 , m.w, m. h , GL BGRA, GL UNSIGNED BYTE, m. video ) ; } glBindFramebuffer (GL FRAMEBUFFER EXT, 0) ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ;

i f ( ! encodefb ) glGenFramebuffers ( 1 , &encodefb ) ; glBindFramebuffer (GL FRAMEBUFFER EXT, encodefb ) ; i f ( ! encoderb ) glGenRenderbuffers ( 1 , &encoderb ) ; glBindRenderbuffer (GL RENDERBUFFER EXT, encoderb ) ; glRenderbufferStorage (GL RENDERBUFFER EXT, GL RGBA, (m.w∗3) /8 , m. h ) ; glFramebufferRenderbuffer (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL RENDERBUFFER EXT, encoderb ) ; glBindRenderbuffer (GL RENDERBUFFER EXT, 0) ; glBindFramebuffer (GL FRAMEBUFFER EXT, 0) ;

} e l s e glReadPixels ( 0 , 0 , m.w, m. h , GL BGRA, GL UNSIGNED BYTE, m. video ) ;

} } i f ( tw < ( uint ) screen−>w || th < ( uint ) screen−>h ) { glBindFramebuffer (GL READ FRAMEBUFFER EXT, 0) ; glBindFramebuffer (GL DRAW FRAMEBUFFER EXT, s c a l e f b ) ; glFramebufferTexture2D (GL DRAW FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE RECTANGLE ARB, s c a l e t e x [ 0 ] , 0) ; glBlitFramebuffer ( 0 , 0 , screen−>w, screen−>h , 0 , 0 , tw , th , GL COLOR BUFFER BIT, GL LINEAR ) ; glBindFramebuffer (GL DRAW FRAMEBUFFER EXT, 0) ; } else { glBindTexture (GL TEXTURE RECTANGLE ARB, s c a l e t e x [ 0 ] ) ; glCopyTexSubImage2D (GL TEXTURE RECTANGLE ARB, 0 , 0 , 0 , 0 , 0 , screen−>w, screen−>h ) ; }

bool readbuffer ( ) { i f ( ! f i l e ) return f a l s e ; i f ( s t a t e ! = REC OK) { stop ( ) ; return f a l s e ; } SDL LockMutex ( videolock ) ; i f ( moviesync && videobuffers . f u l l ( ) ) SDL CondWait ( shouldread , videolock ) ; uint nextframe = (max( gettime ( ) − starttime , 0)∗f i l e−>videofps ) /1000; i f ( ! videobuffers . f u l l ( ) && ( lastframe == ˜0U || nextframe > lastframe ) ) { videobuffer &m = videobuffers . adding ( ) ; SDL UnlockMutex ( videolock ) ; readbuffer (m, nextframe ) ; SDL LockMutex ( videolock ) ; lastframe = nextframe ; videobuffers . add ( ) ; SDL CondSignal ( shouldencode ) ; } SDL UnlockMutex ( videolock ) ; return true ; }

i f ( tw > m.w || th > m. h || ( ! accelyuv && renderpath ! = R FIXEDFUNCTION && tw >= m.w && th >= m. h ) ) { glBindFramebuffer (GL FRAMEBUFFER EXT, s c a l e f b ) ; glViewport ( 0 , 0 , tw , th ) ; g l C o l o r 3 f ( 1 , 1 , 1) ; glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; glOrtho ( 0 , tw , 0 , th , −1, 1) ; glMatrixMode (GL MODELVIEW) ; glLoadIdentity ( ) ; glEnable (GL TEXTURE RECTANGLE ARB) ; do { glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE RECTANGLE ARB, s c a l e t e x [ 1 ] , 0) ; glBindTexture (GL TEXTURE RECTANGLE ARB, s c a l e t e x [ 0 ] ) ; uint dw = max( tw/2 , m.w) , dh = max( th /2 , m. h ) ; i f (dw == m.w && dh == m. h && ! accelyuv && renderpath ! = R FIXEDFUNCTION ) { SETSHADER( movieyuv ) ; m. format = a v i w r i t e r : : VID YUV ; } e l s e SETSHADER( moviergb ) ; drawquad ( tw , th , 0 , 0 , dw, dh ) ; tw = dw; th = dh ; swap ( s c a l e t e x [ 0 ] , s c a l e t e x [ 1 ] ) ; } while ( tw > m.w || th > m. h ) ; glDisable (GL TEXTURE RECTANGLE ARB) ; } i f ( accelyuv ) { glBindFramebuffer (GL FRAMEBUFFER EXT, encodefb ) ; glViewport ( 0 , 0 , (m.w∗3)/8 , m. h ) ; g l C o l o r 3 f ( 1 , 1 , 1) ; glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; glOrtho ( 0 , (m.w∗3)/8 , m. h , 0 , −1, 1) ; glMatrixMode (GL MODELVIEW) ; glLoadIdentity ( ) ; glEnable (GL TEXTURE RECTANGLE ARB) ; glBindTexture (GL TEXTURE RECTANGLE ARB, s c a l e t e x [ 0 ] ) ; SETSHADER( moviey ) ; drawquad (m.w, m. h , 0 , 0 , m.w/4 , m. h ) ; SETSHADER( moviev ) ; drawquad (m.w, m. h , m.w/4 , 0 , m.w/8 , m. h/2) ; SETSHADER( movieu ) ; drawquad (m.w, m. h , m.w/4 , m. h/2 , m.w/8 , m. h/2) ; glDisable (GL TEXTURE RECTANGLE ARB) ; const uint planesize = m.w ∗ m. h ; g l P i x e l S t o r e i ( GL PACK ALIGNMENT, t e x a l i g n (m. video , m.w/4 , 4) ) ; glReadPixels ( 0 , 0 , m.w/4 , m. h , GL BGRA, GL UNSIGNED BYTE, m. video ) ; g l P i x e l S t o r e i ( GL PACK ALIGNMENT, t e x a l i g n (&m. video [ planesize ] , m.w/8 , 4) ) ; glReadPixels (m.w/4 , 0 , m.w/8 , m. h/2 , GL BGRA, GL UNSIGNED BYTE, &m. video [ planesize ] ) ; g l P i x e l S t o r e i ( GL PACK ALIGNMENT, t e x a l i g n (&m. video [ planesize + planesize /4] , m.w/8 , 4) ) ; glReadPixels (m.w/4 , m. h/2 , m.w/8 , m. h/2 , GL BGRA, GL UNSIGNED BYTE, &m. video [ planesize + planesize / 4 ] ) ; m. format = a v i w r i t e r : : VID YUV420 ;

void drawhud ( ) { i n t w = screen−>w, h = screen−>h ; i f ( forceaspect ) w = i n t ( c e i l ( h∗forceaspect ) ) ; g e t t e x t r e s (w, h ) ; glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; glOrtho ( 0 , w, h , 0 , −1, 1) ; glMatrixMode (GL MODELVIEW) ; glLoadIdentity ( ) ; glEnable (GL BLEND) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; glPushMatrix ( ) ; g l S c a l e f (1/3.0 f , 1/3.0 f , 1) ; double t o t a l s i z e = f i l e−>filespaceguess ( ) ; const char ∗unit = ”KB” ; i f ( t o t a l s i z e >= 1e9 ) { t o t a l s i z e /= 1e9 ; unit = ”GB” ; } e l s e i f ( t o t a l s i z e >= 1e6 ) { t o t a l s i z e /= 1e6 ; unit = ”MB” ; } e l s e t o t a l s i z e /= 1e3 ; draw textf ( ” recorded %.1 f%s %d%%”, w∗3−10∗FONTH, h∗3−FONTH−FONTH ∗3/2, t o t a l s i z e , unit , i n t ( c a l c q u a l i t y ( ) ∗100) ) ; glPopMatrix ( ) ; glDisable ( GL TEXTURE 2D ) ; glDisable (GL BLEND) ; } void capture ( ) { i f ( readbuffer ( ) ) drawhud ( ) ; } } VARP( moview , 0 , 320, 10000) ; VARP( movieh , 0 , 240, 10000) ; VARP( moviefps , 1 , 24, 1000) ; VARP( moviesound , 0 , 1 , 1) ; void movie ( char ∗name) { i f (name[ 0 ] == ’ \ 0 ’ ) recorder : : stop ( ) ; e l s e i f ( ! recorder : : isrecording ( ) ) recorder : : s t a r t ( name, moviefps , moview ? moview : screen−>w, movieh ? movieh : screen−>h , moviesound ! = 0 ) ;

engine/mpr.h }

313

ICOMMAND( movierecording , ” ” , ( ) , i n t r e t ( recorder : : isrecording ( ) ? 1 : 0) ) ;

COMMAND( movie , ” s ” ) ;

engine/mpr.h // This code i s based o f f the Minkowski P o r t a l Refinement algorithm by Gary Snethen in XenoCollide & Game Programming Gems 7. namespace mpr { struct CubePlanes { const clipplanes &p ;

}; struct EntOBB { physent ∗ent ; quat o r i e n t ; f l o a t zmargin ; EntOBB( physent ∗ent , f l o a t zmargin = 0) : ent ( ent ) , o r i e n t ( vec ( 0 , 0 , 1) , ent−>yaw∗RAD) , zmargin ( zmargin ) {}

CubePlanes ( const clipplanes &p ) : p ( p ) {}

vec center ( ) const { vec o ( ent−>o ) ; o . z += ( ent−>aboveeye − ent−> eyeheight − zmargin ) /2; return o ; }

vec center ( ) const { return p . o ; } vec supportpoint ( const vec &n ) const { i n t b e s t i = 7; f l o a t bestd = n . dot ( p . v [ 7 ] ) ; loopi ( 7 ) { f l o a t d = n . dot ( p . v [ i ] ) ; i f ( d > bestd ) { b e s t i = i ; bestd = d ; } } return p . v [ b e s t i ] ; }

vec contactface ( const vec &wn, const vec &wdir ) const { vec n = o r i e n t . i n v e r t e d r o t a t e (wn) . div ( vec ( ent−>xradius , ent−> yradius , ( ent−>aboveeye + ent−>eyeheight + zmargin ) /2) ) , d i r = o r i e n t . i n v e r t e d r o t a t e ( wdir ) , an ( fabs ( n . x ) , fabs ( n . y ) , d i r . z ? fabs ( n . z ) : 0) , fn ( 0 , 0 , 0) ; i f ( an . x > an . y ) { i f ( an . x > an . z ) fn . x = n . x∗d i r . x < 0 ? ( n . x > 0 ? 1 : −1) : 0; e l s e i f ( an . z > 0) fn . z = n . z∗d i r . z < 0 ? ( n . z > 0 ? 1 : −1) : 0; } e l s e i f ( an . y > an . z ) fn . y = n . y∗d i r . y < 0 ? ( n . y > 0 ? 1 : −1) : 0; e l s e i f ( an . z > 0) fn . z = n . z∗d i r . z < 0 ? ( n . z > 0 ? 1 : −1) : 0; return o r i e n t . r o t a t e ( fn ) ; }

}; struct SolidCube { vec o ; int size ; SolidCube ( f l o a t x , f l o a t y , f l o a t z , i n t s i z e ) : o ( x , y , z ) , s i z e ( s i z e ) {} SolidCube ( const vec &o , i n t s i z e ) : o ( o ) , s i z e ( s i z e ) {} SolidCube ( const i v e c &o , i n t s i z e ) : o ( o . tovec ( ) ) , s i z e ( s i z e ) {}

vec supportpoint ( const vec &n ) const { vec ln = o r i e n t . i n v e r t e d r o t a t e ( n ) , p ( 0 , 0 , 0) ; i f ( ln . x > 0) p . x += ent−>xradius ; e l s e p . x −= ent−>xradius ; i f ( ln . y > 0) p . y += ent−>yradius ; e l s e p . y −= ent−>yradius ; i f ( ln . z > 0) p . z += ent−>aboveeye ; e l s e p . z −= ent−>eyeheight + zmargin ; return o r i e n t . r o t a t e ( p ) . add ( ent−>o ) ; }

vec center ( ) const { return vec ( o ) . add ( s i z e /2) ; } vec supportpoint ( const vec &n ) const { vec p ( o ) ; i f ( n . x > 0) p . x += s i z e ; i f ( n . y > 0) p . y += s i z e ; i f ( n . z > 0) p . z += s i z e ; return p ; }

};

}; struct EntAABB { physent ∗ent ; EntAABB ( physent ∗ent ) : ent ( ent ) {} vec center ( ) const { vec o ( ent−>o ) ; o . z += ( ent−>aboveeye − ent−> eyeheight ) /2; return o ; } vec contactface ( const vec &n , const vec &d i r ) const { vec an ( n . x∗d i r . x < 0 ? fabs ( n . x ) /ent−>xradius : 0 , n . y∗d i r . y < 0 ? fabs ( n . y ) /ent−>yradius : 0 , n . z∗d i r . z < 0 ? fabs ( n . z ) ∗2/(ent−>aboveeye + ent−>eyeheight ) : 0) , fn ( 0 , 0 , 0) ; i f ( an . x > an . y ) { i f ( an . x > an . z ) fn . x = n . x > 0 ? 1 : −1; e l s e i f ( an . z > 0) fn . z = n . z > 0 ? 1 : −1; } e l s e i f ( an . y > an . z ) fn . y = n . y > 0 ? 1 : −1; e l s e i f ( an . z > 0) fn . z = n . z > 0 ? 1 : −1; return fn ; } vec supportpoint ( const vec &n ) const { vec p ( ent−>o ) ; i f ( n . x > 0) p . x += ent−>xradius ; e l s e p . x −= ent−>xradius ; i f ( n . y > 0) p . y += ent−>yradius ; e l s e p . y −= ent−>yradius ; i f ( n . z > 0) p . z += ent−>aboveeye ; e l s e p . z −= ent−>eyeheight ; return p ; }

struct EntCylinder { physent ∗ent ; f l o a t zmargin ; EntCylinder ( physent ∗ent , f l o a t zmargin = 0) : ent ( ent ) , zmargin ( zmargin ) {} vec center ( ) const { vec o ( ent−>o ) ; o . z += ( ent−>aboveeye − ent−> eyeheight − zmargin ) /2; return o ; } vec contactface ( const vec &n , const vec &d i r ) const { f l o a t dxy = n . dot2 ( n ) / ( ent−>radius∗ent−>radius ) , dz = n . z∗n . z ∗4/(ent−>aboveeye + ent−>eyeheight + zmargin ) ; vec fn ( 0 , 0 , 0) ; i f ( dz > dxy && d i r . z ) fn . z = n . z∗d i r . z < 0 ? ( n . z > 0 ? 1 : −1) : 0; e l s e i f ( n . dot2 ( d i r ) < 0) { fn . x = n . x ; fn . y = n . y ; fn . normalize ( ) ; } return fn ; } vec supportpoint ( const vec &n ) const { vec p ( ent−>o ) ; i f ( n . z > 0) p . z += ent−>aboveeye ; e l s e p . z −= ent−>eyeheight + zmargin ; i f ( n . x || n . y ) { f l o a t r = ent−>radius / n . magnitude2 ( ) ; p . x += n . x∗r ; p . y += n . y∗r ;

314

Foundations of Videogame Programming Code Repository } return p ;

ModelEllipse ( const vec &ent , const vec ¢er , const vec &radius , f l o a t yaw ) : o ( ent ) , radius ( radius ) , o r i e n t ( vec ( 0 , 0 , 1) , yaw∗RAD) { o . add ( o r i e n t . r o t a t e ( center ) ) ; }

} }; struct EntCapsule { physent ∗ent ;

vec center ( ) const { return o ; } EntCapsule ( physent ∗ent ) : ent ( ent ) {} vec contactface ( const vec &wn, const vec &wdir ) const { vec n = o r i e n t . i n v e r t e d r o t a t e (wn) . div ( radius ) , d i r = o r i e n t . i n v e r t e d r o t a t e ( wdir ) ; f l o a t dxy = n . dot2 ( n ) , dz = n . z∗n . z ; vec fn ( 0 , 0 , 0) ; i f ( dz > dxy && d i r . z ) fn . z = n . z∗d i r . z < 0 ? ( n . z > 0 ? 1 : −1) : 0; e l s e i f ( n . dot2 ( d i r ) < 0) { fn . x = n . x∗radius . x ; fn . y = n . y∗radius . y ; fn . normalize ( ) ; } return o r i e n t . r o t a t e ( fn ) ; }

vec center ( ) const { vec o ( ent−>o ) ; o . z += ( ent−>aboveeye − ent−> eyeheight ) /2; return o ; } vec supportpoint ( const vec &n ) const { vec p ( ent−>o ) ; i f ( n . z > 0) p . z += ent−>aboveeye − ent−>radius ; e l s e p . z −= ent−>eyeheight − ent−>radius ; p . add ( vec ( n ) . mul ( ent−>radius / n . magnitude ( ) ) ) ; return p ; } }; struct EntEllipsoid { physent ∗ent ;

vec supportpoint ( const vec &n ) const { vec ln = o r i e n t . i n v e r t e d r o t a t e ( n ) , p ( 0 , 0 , 0) ; i f ( ln . z > 0) p . z += radius . z ; e l s e p . z −= radius . z ; i f ( ln . x || ln . y ) { f l o a t r = n . magnitude2 ( ) ; p . x += ln . x∗radius . x/r ; p . y += ln . y∗radius . y/r ; } return o r i e n t . r o t a t e ( p ) . add ( o ) ; }

EntEllipsoid ( physent ∗ent ) : ent ( ent ) {} vec center ( ) const { vec o ( ent−>o ) ; o . z += ( ent−>aboveeye − ent−> eyeheight ) /2; return o ; } vec supportpoint ( const vec &d i r ) const { vec p ( ent−>o ) , n = vec ( d i r ) . normalize ( ) ; p . x += ent−>radius∗n . x ; p . y += ent−>radius∗n . y ; p . z += ( ent−>aboveeye + ent−>eyeheight ) /2∗(1 + n . z ) − ent−> eyeheight ; return p ; }

};

};

const f l o a t boundarytolerance = 1e−3f ;

struct ModelOBB { vec o , radius ; quat o r i e n t ;

template bool c o l l i d e ( const T &p1 , const U &p2 ) { // v0 = center o f Minkowski d i f f e r e n c e vec v0 = p2 . center ( ) . sub ( p1 . center ( ) ) ; i f ( v0 . i s z e r o ( ) ) return true ; // v0 and o r i g i n overlap ==> h i t

ModelOBB ( const vec &ent , const vec ¢er , const vec &radius , f l o a t yaw ) : o ( ent ) , radius ( radius ) , o r i e n t ( vec ( 0 , 0 , 1) , yaw∗RAD) { o . add ( o r i e n t . r o t a t e ( center ) ) ; }

// v1 = support in d i r e c t i o n o f o r i g i n vec n = vec ( v0 ) . neg ( ) ; vec v1 = p2 . supportpoint ( n ) . sub ( p1 . supportpoint ( vec ( n ) . neg ( ) ) ) ; i f ( v1 . dot ( n ) miss

vec center ( ) const { return o ; } vec contactface ( const vec &wn, const vec &wdir ) const { vec n = o r i e n t . i n v e r t e d r o t a t e (wn) . div ( radius ) , d i r = o r i e n t . i n v e r t e d r o t a t e ( wdir ) , an ( fabs ( n . x ) , fabs ( n . y ) , d i r . z ? fabs ( n . z ) : 0) , fn ( 0 , 0 , 0) ; i f ( an . x > an . y ) { i f ( an . x > an . z ) fn . x = n . x∗d i r . x < 0 ? ( n . x > 0 ? 1 : −1) : 0; e l s e i f ( an . z > 0) fn . z = n . z∗d i r . z < 0 ? ( n . z > 0 ? 1 : −1) : 0; } e l s e i f ( an . y > an . z ) fn . y = n . y∗d i r . y < 0 ? ( n . y > 0 ? 1 : −1) : 0; e l s e i f ( an . z > 0) fn . z = n . z∗d i r . z < 0 ? ( n . z > 0 ? 1 : −1) : 0; return o r i e n t . r o t a t e ( fn ) ; } vec supportpoint ( const vec &n ) const { vec ln = o r i e n t . i n v e r t e d r o t a t e ( n ) , p ( 0 , 0 , 0) ; i f ( ln . x > 0) p . x += radius . x ; e l s e p . x −= radius . x ; i f ( ln . y > 0) p . y += radius . y ; e l s e p . y −= radius . y ; i f ( ln . z > 0) p . z += radius . z ; e l s e p . z −= radius . z ; return o r i e n t . r o t a t e ( p ) . add ( o ) ; } }; struct ModelEllipse { vec o , radius ; quat o r i e n t ;

// v2 = support perpendicular to plane containing o r i g i n , v0 and v1 n . cross ( v1 , v0 ) ; i f ( n . i s z e r o ( ) ) return true ; // v0 , v1 and o r i g i n c o l i n e a r ( and o r i g i n inside v1 support plane ) == > h i t vec v2 = p2 . supportpoint ( n ) . sub ( p1 . supportpoint ( vec ( n ) . neg ( ) ) ) ; i f ( v2 . dot ( n ) miss // v3 = support perpendicular to plane containing v0 , v1 and v2 n . cross ( v0 , v1 , v2 ) ; // I f the o r i g i n i s on the − side o f the plane , reverse the d i r e c t i o n o f the plane i f ( n . dot ( v0 ) > 0) { swap ( v1 , v2 ) ; n . neg ( ) ; } /// // Phase One: Find a v a l i d p o r t a l l o o p i (100) { // Obtain the next support point vec v3 = p2 . supportpoint ( n ) . sub ( p1 . supportpoint ( vec ( n ) . neg ( ) ) ) ; i f ( v3 . dot ( n ) miss // I f o r i g i n i s outside ( v1 , v0 , v3 ) , then p o r t a l i s i n v a l i d −− eliminate v2 and f i n d new support outside face vec v3xv0 ; v3xv0 . cross ( v3 , v0 ) ; i f ( v1 . dot ( v3xv0 ) < 0) { v2 = v3 ; n . cross ( v0 , v1 , v3 ) ;

engine/mpr.h continue ; } // I f o r i g i n i s outside ( v3 , v0 , v2 ) , then p o r t a l i s i n v a l i d −− eliminate v1 and f i n d new support outside face i f ( v2 . dot ( v3xv0 ) > 0) { v1 = v3 ; n . cross ( v0 , v3 , v2 ) ; continue ; } /// // Phase Two : Refine the p o r t a l f o r ( i n t j = 0 ; ; j ++) { // Compute outward facing normal o f the p o r t a l n . cross ( v1 , v2 , v3 ) ; // I f the o r i g i n i s inside the portal , we have a h i t i f ( n . dot ( v1 ) >= 0) return true ; n . normalize ( ) ; // Find the support point in the d i r e c t i o n o f the portal ’ s normal vec v4 = p2 . supportpoint ( n ) . sub ( p1 . supportpoint ( vec ( n ) . neg ( ) ) ); // I f the o r i g i n i s outside the support plane or the boundary i s thin enough , we have a miss i f ( v4 . dot ( n ) 0 i f origin inside ( v1 , v4 , v0 ) // ( v2 % v4 ) ∗ v0 == v2 ∗ ( v4 % v0 ) > 0 i f origin inside ( v2 , v4 , v0 ) // ( v3 % v4 ) ∗ v0 == v3 ∗ ( v4 % v0 ) > 0 i f origin inside ( v3 , v4 , v0 ) vec v4xv0 ; v4xv0 . cross ( v4 , v0 ) ; i f ( v1 . dot ( v4xv0 ) > 0) { i f ( v2 . dot ( v4xv0 ) > 0) v1 = v4 ; // Inside v1 & inside v2 ==> eliminate v1 e l s e v3 = v4 ; // Inside v1 & outside v2 ==> eliminate v3 } else { i f ( v3 . dot ( v4xv0 ) > 0) v2 = v4 ; // Outside v1 & inside v3 ==> eliminate v2 e l s e v1 = v4 ; // Outside v1 & outside v3 ==> eliminate v1 } } } return f a l s e ; } template bool c o l l i d e ( const T &p1 , const U &p2 , vec ∗contactnormal , vec ∗ contactpoint1 , vec ∗contactpoint2 ) { // v0 = center o f Minkowski sum vec v01 = p1 . center ( ) ; vec v02 = p2 . center ( ) ; vec v0 = vec ( v02 ) . sub ( v01 ) ; // Avoid case where centers overlap −− any d i r e c t i o n i s f i n e in t h i s case i f ( v0 . i s z e r o ( ) ) v0 = vec ( 0 , 0 , 1e−5f ) ; // v1 = support in d i r e c t i o n o f o r i g i n vec n = vec ( v0 ) . neg ( ) ; vec v11 = p1 . supportpoint ( vec ( n ) . neg ( ) ) ; vec v12 = p2 . supportpoint ( n ) ; vec v1 = vec ( v12 ) . sub ( v11 ) ; i f ( v1 . dot ( n ) = 0 && ! h i t ) { i f ( contactnormal ) ∗contactnormal = n ; // Compute the barycentric coordinates o f the o r i g i n

316

Foundations of Videogame Programming Code Repository i f ( contactpoint1 || contactpoint2 ) { f l o a t b0 = v3 . s c a l a r t r i p l e ( v1 , v2 ) , b1 = v0 . s c a l a r t r i p l e ( v3 , v2 ) , b2 = v3 . s c a l a r t r i p l e ( v0 , v1 ) , b3 = v0 . s c a l a r t r i p l e ( v2 , v1 ) , sum = b0 + b1 + b2 + b3 ; i f (sum 0 i f origin inside ( v2 , v4 , v0 ) // ( v3 % v4 ) ∗ v0 == v3 ∗ ( v4 % v0 ) > 0 i f origin inside ( v3 , v4 , v0 ) vec v4xv0 ; v4xv0 . cross ( v4 , v0 ) ; i f ( v1 . dot ( v4xv0 ) > 0) // Compute the tetrahedron d i v i d i n g face d1 = ( v4 , v0 , v1 ) { i f ( v2 . dot ( v4xv0 ) > 0) // Compute the tetrahedron d i v i d i n g face d2 = ( v4 , v0 , v2 ) { // Inside d1 & inside d2 ==> eliminate v1 v1 = v4 ; v11 = v41 ; v12 = v42 ; } else { // Inside d1 & outside d2 ==> eliminate v3 v3 = v4 ; v31 = v41 ; v32 = v42 ; } } else { i f ( v3 . dot ( v4xv0 ) > 0) // Compute the tetrahedron d i v i d i n g face d3 = ( v4 , v0 , v3 ) { // Outside d1 & inside d3 ==> eliminate v2 v2 = v4 ; v21 = v41 ; v22 = v42 ; } else { // Outside d1 & outside d3 ==> eliminate v1 v1 = v4 ; v11 = v41 ; v12 = v42 ; } } //

// HIT ! ! ! h i t = true ; } // Find the support point in the d i r e c t i o n o f the wedge face vec v41 = p1 . supportpoint ( vec ( n ) . neg ( ) ) ; vec v42 = p2 . supportpoint ( n ) ; vec v4 = vec ( v42 ) . sub ( v41 ) ; // I f the boundary i s thin enough or the o r i g i n i s outside the support plane f o r the newly discovered vertex , then we can terminate i f ( v4 . dot ( n ) 0 i f origin

} } return f a l s e ; } }

engine/normal.cpp #include ” engine . h” struct normalgroup { vec pos ; i n t f l a t , normals , tnormals ; normalgroup ( ) : f l a t ( 0 ) , normals(−1) , tnormals(−1) {} normalgroup ( const vec &pos ) : pos ( pos ) , f l a t ( 0 ) , normals(−1) , tnormals (−1) {} };

s t a t i c i n t addnormal ( const vec &key , const vec &surface ) { normalgroup &g = normalgroups . access ( key , key ) ; normal &n = normals . add ( ) ; n . next = g . normals ; n . surface = surface ; return g . normals = normals . length ( ) −1; } s t a t i c void addtnormal ( const vec &key , f l o a t o f f s e t , i n t normal1 , i n t normal2 , normalgroup ∗group1 , normalgroup ∗group2 ) {

s t a t i c i n l i n e bool htcmp ( const vec &v , const normalgroup &n ) { return v == n . pos ; }

normalgroup &g = normalgroups . access ( key , key ) ; tnormal &n = tnormals . add ( ) ; n . next = g . tnormals ; n. offset = offset ; n . normals [ 0 ] = normal1 ; n . normals [ 1 ] = normal2 ; n . groups [ 0 ] = group1 ; n . groups [ 1 ] = group2 ; g . tnormals = tnormals . length ( ) −1;

struct normal { i n t next ; vec surface ; }; struct tnormal { i n t next ; float offset ; i n t normals [ 2 ] ; normalgroup ∗groups [ 2 ] ; }; hashset normalgroups(1>12)&0xF ; v . y += n ; t o t a l += n ; } e l s e i f ( surface . y >8)&0xF ; v . y −= n ; t o t a l += n ; } i f ( surface . z >= lerpthreshold ) { i n t n = ( g . f l a t >>20)&0xF ; v . z += n ; t o t a l += n ; } e l s e i f ( surface . z >16)&0xF ; v . z −= n ; t o t a l += n ; } f o r ( i n t cur = g . normals ; cur >= 0 ; ) { normal &o = normals [ cur ] ; i f ( o . surface . dot ( surface ) >= lerpthreshold ) { v . add ( o . surface ) ; t o t a l ++; } cur = o . next ; } i f ( t o t a l > 1) v . normalize ( ) ; e l s e i f ( ! t o t a l ) v = surface ;

i f ( c . texture [ i ] == DEFAULT SKY) continue ; vec planes [ 2 ] ; i n t numverts = c . ext ? c . ext−>surfaces [ i ] . numverts&MAXFACEVERTS : 0 , convex = 0 , numplanes = 0; i f ( numverts ) { v e r t i n f o ∗v e r t s = c . ext−>v e r t s ( ) + c . ext−>surfaces [ i ] . v e r t s ; vec vo = i v e c ( o ) .mask( ˜ 0xFFF ) . tovec ( ) ; l o o p j ( numverts ) { v e r t i n f o &v = v e r t s [ j ] ; pos [ j ] = vec ( v . x , v . y , v . z ) . mul( 1 . 0 f /8) . add ( vo ) ; } i f ( ! ( c . merged&(1groups [ 1 ] , surface , n2 ) ; v . l e r p ( n1 , n2 , bestnorm−>o f f s e t ) . normalize ( ) ; return true ;

i f ( ! flataxisface ( c , i ) ) { planes [ numplanes + + ] . cross ( pos [ 0 ] , pos [ 1 ] , pos [ 2 ] ) . normalize ( ) ; i f ( convex ) planes [ numplanes + + ] . cross ( pos [ 0 ] , pos [ 2 ] , pos [ 3 ] ) . normalize ( ) ; } i f ( ! numplanes ) loopk ( numverts ) norms [ k ] = addnormal ( pos [ k ] , i ) ; e l s e i f ( numplanes==1) loopk ( numverts ) norms [ k ] = addnormal ( pos [ k ] , planes [ 0 ] ) ; else { vec avg = vec ( planes [ 0 ] ) . add ( planes [ 1 ] ) . normalize ( ) ; norms [ 0 ] = addnormal ( pos [ 0 ] , avg ) ; norms [ 1 ] = addnormal ( pos [ 1 ] , planes [ 0 ] ) ; norms [ 2 ] = addnormal ( pos [ 2 ] , avg ) ; f o r ( i n t k = 3; k < numverts ; k++) norms [ k ] = addnormal ( pos [ k ] , planes [ 1 ] ) ; }

}

while ( t j >= 0 && t j o i n t s [ t j ] . edge < i ∗(MAXFACEVERTS+1) ) t j = t j o i n t s [ t j ] . next ; while ( t j >= 0 && t j o i n t s [ t j ] . edge < ( i +1) ∗(MAXFACEVERTS+1) ) { i n t edge = t j o i n t s [ t j ] . edge , e1 = edge%(MAXFACEVERTS+1) , e2 = ( e1+1)%numverts ; const vec &v1 = pos [ e1 ] , &v2 = pos [ e2 ] ; i v e c d = vec ( v2 ) . sub ( v1 ) . mul ( 8 ) ; i n t axis = abs ( d . x ) > abs ( d . y ) ? ( abs ( d . x ) > abs ( d . z ) ? 0 : 2) : ( abs ( d . y ) > abs ( d . z ) ? 1 : 2) ; i f ( d [ axis ] < 0) d . neg ( ) ; reduceslope ( d ) ; i n t o r i g i n = i n t ( min ( v1 [ axis ] , v2 [ axis ] ) ∗8)&˜0x7FFF , o f f s e t 1 = ( i n t ( v1 [ axis ]∗8) − o r i g i n ) / d [ axis ] , o f f s e t 2 = ( i n t ( v2 [ axis ]∗8) − o r i g i n ) / d [ axis ] ; vec o = vec ( v1 ) . sub ( d . tovec ( ) . mul ( o f f s e t 1 /8.0 f ) ) , n1 , n2 ; f l o a t d o f f s e t = 1.0 f / ( o f f s e t 2 − o f f s e t 1 ) ;

void findnormal ( const vec &key , const vec &surface , vec &v ) { const normalgroup ∗g = normalgroups . access ( key ) ; i f ( ! g ) v = surface ; e l s e i f ( g−>tnormals < 0 || ! findtnormal (∗g , surface , v ) ) findnormal (∗g , surface , v ) ; } VARR( lerpsubdiv , 0 , 2 , 4) ; VARR( lerpsubdivsize , 4 , 4 , 128) ; s t a t i c uint progress = 0; void show addnormals progress ( ) { f l o a t bar1 = f l o a t ( progress ) / f l o a t ( allocnodes ) ; renderprogress ( bar1 , ” computing normals . . . ” ) ; }

while ( t j >= 0) { t j o i n t &t = t j o i n t s [ t j ] ; i f ( t . edge ! = edge ) break ; float offset = ( t . offset − offset1 ) ∗ doffset ; vec tpos = d . tovec ( ) . mul ( t . o f f s e t /8.0 f ) . add ( o ) ; addtnormal ( tpos , o f f s e t , norms [ e1 ] , norms [ e2 ] , normalgroups . access ( v1 ) , normalgroups . access ( v2 ) ) ; t j = t . next ; }

void addnormals ( cube &c , const i v e c &o , i n t s i z e ) { CHECK CALCLIGHT PROGRESS( return , show addnormals progress ) ; i f ( c . children ) { progress ++; s i z e >>= 1; l o o p i ( 8 ) addnormals ( c . children [ i ] , i v e c ( i , o . x , o . y , o . z , s i z e ) , size ) ; return ; } e l s e i f ( isempty ( c ) ) return ; vec pos [MAXFACEVERTS] ; i n t norms [MAXFACEVERTS] ; i n t t j = usetnormals && c . ext ? c . ext−>t j o i n t s : −1, v i s ; loopi ( 6 ) i f ( ( vis = v i s i b l e t r i s ( c , i , o . x , o . y , o . z , size ) ) ) { CHECK CALCLIGHT PROGRESS( return , show addnormals progress ) ;

317

} } } void calcnormals ( bool l e r p t j o i n t s ) { i f ( ! lerpangle ) return ; usetnormals = l e r p t j o i n t s ; i f ( usetnormals ) f i n d t j o i n t s ( ) ; lerpthreshold = cos ( lerpangle∗RAD) − 1e−5f ; progress = 1; l o o p i ( 8 ) addnormals ( worldroot [ i ] , i v e c ( i , 0 , 0 , 0 , worldsize /2) , worldsize /2) ;

318

Foundations of Videogame Programming Code Repository } else { s t a r t . winding = end . winding = −1; s t a r t .max = ( s t a r t . min == &l v [numv−1] ? l v : s t a r t . min+1) ; end .max = ( end . min == l v ? &l v [numv−1] : end . min−1); }

} void clearnormals ( ) { normalgroups . c l e a r ( ) ; normals . s e t s i z e ( 0 ) ; tnormals . s e t s i z e ( 0 ) ; } void c a l c l e r p v e r t s ( const vec2 ∗c , const vec ∗n , l e r p v e r t ∗lv , i n t &numv) { i n t i = 0; l o o p j (numv) { if ( j ) { i f ( c [ j ] == c [ j −1] && n [ j ] == n [ j −1]) continue ; i f ( j == numv−1 && c [ j ] == c [ 0 ] && n [ j ] == n [ 0 ] ) continue ; } l v [ i ] . normal = n [ j ] ; lv [ i ] .u = c [ j ] . x; lv [ i ] . v = c [ j ] . y ; i ++; } numv = i ; } void s e t l e r p s t e p ( f l o a t v , lerpbounds &bounds ) { i f ( bounds . min−>v + 1 > bounds .max−>v ) { bounds . nstep = vec ( 0 , 0 , 0) ; bounds . normal = bounds . min−>normal ; i f ( bounds . min−>normal ! = bounds .max−>normal ) { bounds . normal . add ( bounds .max−>normal ) ; bounds . normal . normalize ( ) ; } bounds . ustep = 0; bounds . u = bounds . min−>u ; return ; } bounds . nstep = bounds .max−>normal ; bounds . nstep . sub ( bounds . min−>normal ) ; bounds . nstep . div ( bounds .max−>v−bounds . min−>v ) ;

setlerpstep ( v , start ) ; s e t l e r p s t e p ( v , end ) ; } void updatelerpbounds ( f l o a t v , const l e r p v e r t ∗lv , i n t numv, lerpbounds & s t a r t , lerpbounds &end ) { i f ( v >= s t a r t .max−>v ) { const l e r p v e r t ∗next = s t a r t . winding > 0 ? ( s t a r t .max == l v ? &l v [numv−1] : s t a r t .max−1) : ( s t a r t .max == &l v [numv−1] ? l v : s t a r t .max+1) ; i f ( next−>v > s t a r t .max−>v ) { s t a r t . min = s t a r t .max; s t a r t .max = next ; setlerpstep ( v , start ) ; } } i f ( v >= end .max−>v ) { const l e r p v e r t ∗next = end . winding > 0 ? ( end .max == &l v [numv−1] ? l v : end .max+1) : ( end .max == l v ? &l v [numv−1] : end .max−1); i f ( next−>v > end .max−>v ) { end . min = end .max; end .max = next ; s e t l e r p s t e p ( v , end ) ; } } } void lerpnormal ( f l o a t u, f l o a t v , const l e r p v e r t ∗lv , i n t numv, lerpbounds &s t a r t , lerpbounds &end , vec &normal , vec &nstep ) { updatelerpbounds ( v , lv , numv, s t a r t , end ) ; i f ( s t a r t . u + 1 > end . u ) { nstep = vec ( 0 , 0 , 0) ; normal = s t a r t . normal ; normal . add ( end . normal ) ; normal . normalize ( ) ; } else { vec nstart ( s t a r t . normal ) , nend ( end . normal ) ; nstart . normalize ( ) ; nend . normalize ( ) ;

bounds . normal = bounds . nstep ; bounds . normal . mul ( v − bounds . min−>v ) ; bounds . normal . add ( bounds . min−>normal ) ; bounds . ustep = ( bounds .max−>u−bounds . min−>u ) / ( bounds .max−>v−bounds . min−>v ) ; bounds . u = bounds . ustep ∗ ( v−bounds . min−>v ) + bounds . min−>u ; } void initlerpbounds ( f l o a t u, f l o a t v , const l e r p v e r t ∗lv , i n t numv, lerpbounds &s t a r t , lerpbounds &end ) { const l e r p v e r t ∗ f i r s t = &l v [ 0 ] , ∗second = NULL; l o o p i (numv−1) { i f ( l v [ i + 1 ] . v < f i r s t−>v ) { second = f i r s t ; f i r s t = &l v [ i + 1 ] ; } e l s e i f ( ! second || l v [ i + 1 ] . v < second−>v ) second = &l v [ i + 1 ] ; }

nstep = nend ; nstep . sub ( nstart ) ; nstep . div ( end . u−s t a r t . u ) ; normal = nstep ; normal . mul ( u−s t a r t . u ) ; normal . add ( nstart ) ; normal . normalize ( ) ;

i f ( i n t ( f i r s t−>v ) < i n t ( second−>v ) ) { s t a r t . min = end . min = f i r s t ; } e l s e i f ( f i r s t−>u > second−>u ) { s t a r t . min = second ; end . min = f i r s t ; } e l s e { s t a r t . min = f i r s t ; end . min = second ; } i f ( ( l v [ 1 ] . u − lv−>u ) ∗( l v [ 2 ] . v − lv−>v ) > ( l v [ 1 ] . v − lv−>v ) ∗( l v [ 2 ] . u − lv−>u ) ) { s t a r t . winding = end . winding = 1; s t a r t .max = ( s t a r t . min == l v ? &l v [numv−1] : s t a r t . min−1); end .max = ( end . min == &l v [numv−1] ? l v : end . min+1) ;

} s t a r t . normal . add ( s t a r t . nstep ) ; s t a r t . u += s t a r t . ustep ; end . normal . add ( end . nstep ) ; end . u += end . ustep ; }

engine/obj.h struct obj ; struct obj : vertmodel , vertloader { obj ( const char ∗name) : vertmodel (name) {} s t a t i c const char ∗formatname ( ) { return ” obj ” ; } s t a t i c bool animated ( ) { return f a l s e ; } i n t type ( ) const { return MDL OBJ; } struct objmeshgroup : vertmeshgroup

{ void parsevert ( char ∗s , vector &out ) { vec &v = out . add ( vec ( 0 , 0 , 0) ) ; while ( isalpha (∗s ) ) s ++; loopi ( 3 ) { v [ i ] = s t r t o d ( s , &s ) ; while ( isspace (∗s ) ) s ++; i f ( ! ∗ s ) break ; }

engine/obj.h

319

}

for ( ; ; ) { while ( isspace (∗c ) ) c ++; i f ( ! ∗ c ) break ; i v e c vkey(−1, −1, −1) ; loopi ( 3 ) { vkey [ i ] = s t r t o l ( c , &c , 10) ; i f ( vkey [ i ] < 0) vkey [ i ] = a t t r i b [ i ] . length ( ) + vkey [ i ] ; e l s e vkey [ i]−−; i f ( ! a t t r i b [ i ] . inrange ( vkey [ i ] ) ) vkey [ i ] = −1; i f (∗c ! = ’ / ’ ) break ; c ++; } i n t ∗index = verthash . access ( vkey ) ; i f ( ! index ) { index = &verthash [ vkey ] ; ∗index = v e r t s . length ( ) ; v e r t &v = v e r t s . add ( ) ; v . pos = vkey . x < 0 ? vec ( 0 , 0 , 0) : a t t r i b [ 0 ] [ vkey . x ] ; v . pos = vec ( v . pos . z , −v . pos . x , v . pos . y ) ; v . norm = vkey . z < 0 ? vec ( 0 , 0 , 0) : a t t r i b [ 2 ] [ vkey . z ] ; v . norm = vec ( v . norm . z , −v . norm . x , v . norm . y ) ; t c v e r t &tcv = t c v e r t s . add ( ) ; i f ( vkey . y < 0) tcv . u = tcv . v = 0; e l s e { tcv . u = a t t r i b [ 1 ] [ vkey . y ] . x ; tcv . v = 1− a t t r i b [ 1 ] [ vkey . y ] . y ; } } i f ( v0 < 0) v0 = ∗index ; e l s e i f ( v1 < 0) v1 = ∗index ; else { t r i &t = t r i s . add ( ) ; t . v e r t [ 0 ] = ushort (∗ index ) ; t . v e r t [ 1 ] = ushort ( v1 ) ; t . v e r t [ 2 ] = ushort ( v0 ) ; v1 = ∗index ; } } break ;

bool load ( const char ∗filename , f l o a t smooth ) { i n t len = s t r l e n ( filename ) ; i f ( len < 4 || strcasecmp(& filename [ len −4], ” . obj ” ) ) return f a l s e ; stream ∗ f i l e = o p e n f i l e ( filename , ” rb ” ) ; i f ( ! f i l e ) return f a l s e ; name = newstring ( filename ) ; numframes = 1; vector a t t r i b [ 3 ] ; char buf [ 5 1 2 ] ; hashtable verthash ; vector v e r t s ; vector t c v e r t s ; vector t r i s ; #define STARTMESH do { \ vertmesh &m = ∗new vertmesh ; \ m. group = t h i s ; \ m.name = meshname[ 0 ] ? newstring (meshname) : NULL; \ meshes . add(&m) ; \ curmesh = &m; \ verthash . c l e a r ( ) ; \ verts . setsize ( 0 ) ; \ tcverts . setsize (0) ; \ tris . setsize (0) ; \ } while ( 0 ) #define FLUSHMESH do { \ curmesh−>numverts = v e r t s . length ( ) ; \ i f ( v e r t s . length ( ) ) \ { \ curmesh−>v e r t s = new v e r t [ v e r t s . length ( ) ] ; \ memcpy( curmesh−>verts , v e r t s . getbuf ( ) , v e r t s . length ( ) ∗ sizeof ( vert ) ) ; \ curmesh−>t c v e r t s = new t c v e r t [ v e r t s . length ( ) ] ; \ memcpy( curmesh−>t c v e r t s , t c v e r t s . getbuf ( ) , t c v e r t s . length ( ) ∗s i z e o f ( t c v e r t ) ) ; \ } \ curmesh−>numtris = t r i s . length ( ) ; \ i f ( t r i s . length ( ) ) \ { \ curmesh−>t r i s = new t r i [ t r i s . length ( ) ] ; \ memcpy( curmesh−>t r i s , t r i s . getbuf ( ) , t r i s . length ( ) ∗s i z e o f ( tri ) ) ; \ } \ i f ( a t t r i b [ 2 ] . empty ( ) ) \ { \ i f ( smooth smoothnorms ( smooth ) ; \ e l s e curmesh−>buildnorms ( ) ; \ } \ } while ( 0 ) s t r i n g meshname = ” ” ; vertmesh ∗curmesh = NULL; while ( f i l e−>g e t l i n e ( buf , s i z e o f ( buf ) ) ) { char ∗c = buf ; while ( isspace (∗c ) ) c ++; switch (∗c ) { case ’ # ’ : continue ; case ’ v ’ : i f ( isspace ( c [ 1 ] ) ) parsevert ( c , a t t r i b [ 0 ] ) ; e l s e i f ( c [ 1 ] = = ’ t ’ ) parsevert ( c , a t t r i b [ 1 ] ) ; e l s e i f ( c [ 1 ] = = ’ n ’ ) parsevert ( c , a t t r i b [ 2 ] ) ; break ; case ’ g ’ : { while ( isalpha (∗c ) ) c ++; while ( isspace (∗c ) ) c ++; char ∗name = c ; s i z e t namelen = s t r l e n (name) ; while ( namelen > 0 && isspace (name[ namelen−1]) ) namelen −−; copystring (meshname, name, min ( namelen+1 , s i z e o f ( meshname) ) ) ; i f ( curmesh ) FLUSHMESH; curmesh = NULL; break ; } case ’ f ’ : { i f ( ! curmesh ) STARTMESH; i n t v0 = −1, v1 = −1; while ( isalpha (∗c ) ) c ++;

} } } i f ( curmesh ) FLUSHMESH; delete f i l e ; return true ; } }; meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { objmeshgroup ∗group = new objmeshgroup ; i f ( ! group−>load ( name, va arg ( args , double ) ) ) { d e l e t e group ; return NULL; } return group ; } bool loaddefaultparts ( ) { part &mdl = ∗new part ; parts . add(&mdl ) ; mdl . model = t h i s ; mdl . index = 0; const char ∗pname = parentdir ( loadname ) ; defformatstring (name1) ( ” packages/models/%s/ t r i s . obj ” , loadname ) ; mdl . meshes = sharemeshes ( path (name1) , 2 . 0 ) ; i f ( ! mdl . meshes ) { defformatstring (name2) ( ” packages/models/%s/ t r i s . obj ” , pname) ; // t r y obj in parent f o l d e r ( v e r t sharing ) mdl . meshes = sharemeshes ( path (name2) , 2 . 0 ) ; i f ( ! mdl . meshes ) return f a l s e ; } Texture ∗tex , ∗masks ; loadskin ( loadname , pname, tex , masks ) ; mdl . i n i t s k i n s ( tex , masks ) ; i f ( tex==notexture ) conoutf ( ” could not load model skin f o r %s ” , name1) ; return true ; } bool load ( ) { i f ( loaded ) return true ; formatstring ( d i r ) ( ” packages/models/%s ” , loadname ) ; defformatstring ( cfgname ) ( ” packages/models/%s/obj . c f g ” , loadname ) ;

320

Foundations of Videogame Programming Code Repository

loading = t h i s ; i d e n t f l a g s &= ˜IDF PERSIST ; i f ( e x e c f i l e ( cfgname , f a l s e ) && parts . length ( ) ) // configured obj , w i l l c a l l the obj∗ commands below { i d e n t f l a g s |= IDF PERSIST ; loading = NULL; loopv ( parts ) i f ( ! parts [ i]−>meshes ) return f a l s e ; } e l s e // obj without configuration , t r y d e f a u l t t r i s and skin { i d e n t f l a g s |= IDF PERSIST ;

loading = NULL; i f ( ! loaddefaultparts ( ) ) return f a l s e ; } scale /= 4; t r a n s l a t e . y = −t r a n s l a t e . y ; parts[0]−>t r a n s l a t e = t r a n s l a t e ; loopv ( parts ) parts [ i]−>meshes−>shared ++; return loaded = true ; } }; vertcommands objcommands ;

engine/octa.cpp // core world management routines

return s i z e ; }

#include ” engine . h” cube ∗worldroot = newcubes ( F SOLID ) ; i n t allocnodes = 0; cubeext ∗growcubeext ( cubeext ∗old , i n t maxverts ) { cubeext ∗ext = ( cubeext ∗)new uchar [ s i z e o f ( cubeext ) + maxverts∗s i z e o f ( vertinfo ) ] ; i f ( old ) { ext−>va = old−>va ; ext−>ents = old−>ents ; ext−>t j o i n t s = old−>t j o i n t s ; } else { ext−>va = NULL; ext−>ents = NULL; ext−>t j o i n t s = −1; } ext−>maxverts = maxverts ; return ext ; } void setcubeext ( cube &c , cubeext ∗ext ) { cubeext ∗old = c . ext ; i f ( old == ext ) return ; c . ext = ext ; i f ( old ) d e l e t e [ ] ( uchar ∗) old ; } cubeext ∗newcubeext ( cube &c , i n t maxverts , bool i n i t ) { i f ( c . ext && c . ext−>maxverts >= maxverts ) return c . ext ; cubeext ∗ext = growcubeext ( c . ext , maxverts ) ; if ( init ) { i f ( c . ext ) { memcpy( ext−>surfaces , c . ext−>surfaces , s i z e o f ( ext−>surfaces ) ) ; memcpy( ext−>v e r t s ( ) , c . ext−>v e r t s ( ) , c . ext−>maxverts∗s i z e o f ( vertinfo ) ) ; } e l s e memset ( ext−>surfaces , 0 , s i z e o f ( ext−>surfaces ) ) ; } setcubeext ( c , ext ) ; return ext ; } cube ∗newcubes ( uint face , i n t mat ) { cube ∗c = new cube [ 8 ] ; loopi ( 8 ) { c−>children = NULL; c−>ext = NULL; c−>v i s i b l e = 0; c−>merged = 0; s e t f a c e s (∗c , face ) ; l o o p l ( 6 ) c−>texture [ l ] = DEFAULT GEOM; c−>material = mat ; c ++; } allocnodes ++; return c−8; } i n t f a m i l y s i z e ( const cube &c ) { i n t s i z e = 1; i f ( c . children ) l o o p i ( 8 ) s i z e += f a m i l y s i z e ( c . children [ i ] ) ;

void f r e e o c t a ( cube ∗c ) { i f ( ! c ) return ; l o o p i ( 8 ) discardchildren ( c [ i ] ) ; delete [ ] c ; allocnodes−−; } void freecubeext ( cube &c ) { i f ( c . ext ) { d e l e t e [ ] ( uchar ∗)c . ext ; c . ext = NULL; } } void discardchildren ( cube &c , bool f i x t e x , i n t depth ) { c . material = MAT AIR ; c . v i s i b l e = 0; c . merged = 0; i f ( c . ext ) { i f ( c . ext−>va ) destroyva ( c . ext−>va ) ; c . ext−>va = NULL; c . ext−>t j o i n t s = −1; freeoctaentities ( c ) ; freecubeext ( c ) ; } i f ( c . children ) { uint f i l l e d = F EMPTY ; loopi ( 8 ) { discardchildren ( c . children [ i ] , f i x t e x , depth +1) ; f i l l e d |= c . children [ i ] . faces [ 0 ] ; } i f ( fixtex ) { l o o p i ( 6 ) c . texture [ i ] = getmippedtexture ( c , i ) ; i f ( depth > 0 && f i l l e d ! = F EMPTY ) c . faces [ 0 ] = F SOLID ; } DELETEA( c . children ) ; allocnodes−−; } } void getcubevector ( cube &c , i n t d , i n t x , i n t y , i n t z , i v e c &p ) { ivec v ( d, x , y , z ) ; loopi ( 3 ) p [ i ] = edgeget ( cubeedge ( c , i , v [R[ i ] ] , v [C[ i ] ] ) , v [D[ i ] ] ) ; } void setcubevector ( cube &c , i n t d , i n t x , i n t y , i n t z , const i v e c &p ) { ivec v ( d, x , y , z ) ; loopi ( 3 ) edgeset ( cubeedge ( c , i , v [R[ i ] ] , v [C[ i ] ] ) , v [D[ i ] ] , p [ i ] ) ; } static { p.x p. y p. z }

i n l i n e void getcubevector ( cube &c , i n t i , i v e c &p ) = edgeget ( cubeedge ( c , 0 , ( i>>R [ 0 ] ) &1, ( i>>C [ 0 ] ) &1) , ( i>>D[ 0 ] ) &1) ; = edgeget ( cubeedge ( c , 1 , ( i>>R [ 1 ] ) &1, ( i>>C [ 1 ] ) &1) , ( i>>D[ 1 ] ) &1) ; = edgeget ( cubeedge ( c , 2 , ( i>>R [ 2 ] ) &1, ( i>>C [ 2 ] ) &1) , ( i>>D[ 2 ] ) &1) ;

s t a t i c i n l i n e void setcubevector ( cube &c , i n t i , const i v e c &p )

engine/octa.cpp {

scale−−; c = &c−>children [ octastep ( tx , ty , tz , scale ) ] ; } while ( ! ( csize>>scale ) ) ; ro = i v e c ( tx , ty , t z ) .mask(˜0C [ 0 ] ) &1) , ( i>>D[ 0 ] ) &1, p . x ) ; edgeset ( cubeedge ( c , 1 , ( i>>R [ 1 ] ) &1, ( i>>C [ 1 ] ) &1) , ( i>>D[ 1 ] ) &1, p . y ) ; edgeset ( cubeedge ( c , 2 , ( i>>R [ 2 ] ) &1, ( i>>C [ 2 ] ) &1) , ( i>>D[ 2 ] ) &1, p . z ) ; } void o p t i f a c e ( uchar ∗p , cube &c ) { uint f = ∗( uint ∗)p ; i f ( ( ( f>>4)&0x0F0F0F0FU ) == ( f&0x0F0F0F0FU ) ) emptyfaces ( c ) ; } void printcube ( ) { cube &c = lookupcube ( lu . x , lu . y , lu . z ) ; // assume t h i s i s cube being pointed at conoutf (CON DEBUG, ”= %p = (%d , %d , %d ) @ %d ” , ( void ∗)&c , lu . x , lu . y , lu . z , l u s i z e ) ; conoutf (CON DEBUG, ” x %.8x ” , c . faces [ 0 ] ) ; conoutf (CON DEBUG, ” y %.8x ” , c . faces [ 1 ] ) ; conoutf (CON DEBUG, ” z %.8x ” , c . faces [ 2 ] ) ; } COMMAND( printcube , ” ” ) ; bool isvalidcube ( const cube &c ) { clipplanes p ; genclipplanes ( c , 0 , 0 , 0 , 256, p ) ; l o o p i ( 8 ) // t e s t that cube i s convex { vec v ; c a l c v e r t ( c , 0 , 0 , 0 , 256, v , i ) ; i f ( ! pointincube ( p , v ) ) return f a l s e ; } return true ; } void v a l i d a t e c ( cube ∗c , i n t s i z e ) { loopi ( 8 ) { i f ( c [ i ] . children ) { i f ( size >1); } e l s e i f ( s i z e > 0x1000 ) { subdividecube ( c [ i ] , true , f a l s e ) ; v a l i d a t e c ( c [ i ] . children , size>>1); } else { loopj ( 3 ) { uint f = c [ i ] . faces [ j ] , e0 = f&0x0F0F0F0FU, e1 = ( f>>4)&0 x0F0F0F0FU ; i f ( e0 == e1 || ( ( e1+0x07070707U ) | ( e1−e0 ) )&0xF0F0F0F0U ) { emptyfaces ( c [ i ] ) ; break ; } } } } } i v e c lu ; int lusize ; cube &lookupcube ( i n t tx , i n t ty , i n t tz , i n t t s i z e , i v e c &ro , i n t &r s i z e ) { tx = clamp ( tx , 0 , worldsize −1); ty = clamp ( ty , 0 , worldsize −1); t z = clamp ( tz , 0 , worldsize −1); i n t scale = worldscale−1, c s i z e = abs ( t s i z e ) ; cube ∗c = &worldroot [ octastep ( tx , ty , tz , scale ) ] ; i f ( ! ( csize>>scale ) ) do { i f ( ! c−>children ) { i f ( t s i z e > 0) do { subdividecube (∗c ) ; scale−−; c = &c−>children [ octastep ( tx , ty , tz , scale ) ] ; } while ( ! ( csize>>scale ) ) ; break ; }

321

} i n t lookupmaterial ( const vec &v ) { ivec o ( v ) ; i f ( ! insideworld ( o ) ) return MAT AIR ; i n t scale = worldscale−1; cube ∗c = &worldroot [ octastep ( o . x , o . y , o . z , scale ) ] ; while ( c−>children ) { scale−−; c = &c−>children [ octastep ( o . x , o . y , o . z , scale ) ] ; } return c−>material ; } const cube ∗neighbourstack [ 3 2 ] ; i n t neighbourdepth = −1; const cube &neighbourcube ( const cube &c , i n t orient , i n t x , i n t y , i n t z , i n t size , i v e c &ro , i n t &r s i z e ) { ivec n( x , y , z ) ; i n t dim = dimension ( o r i e n t ) ; uint d i f f = n [ dim ] ; i f ( dimcoord ( o r i e n t ) ) n [ dim ] += s i z e ; e l s e n [ dim ] −= s i z e ; d i f f ˆ= n [ dim ] ; i f ( d i f f >= uint ( worldsize ) ) { ro = n ; r s i z e = s i z e ; return c ; } i n t scale = worldscale ; const cube ∗nc = worldroot ; i f ( neighbourdepth >= 0) { scale −= neighbourdepth + 1; d i f f >>= scale ; do { scale ++; d i f f >>= 1; } while ( d i f f ) ; nc = neighbourstack [ worldscale − scale ] ; } scale−−; nc = &nc [ octastep ( n . x , n . y , n . z , scale ) ] ; i f ( ! ( size>>scale ) && nc−>children ) do { scale−−; nc = &nc−>children [ octastep ( n . x , n . y , n . z , scale ) ] ; } while ( ! ( size>>scale ) && nc−>children ) ; ro = n .mask(˜0R[ d ] ) &1, cd = ( i>>C[ d ] ) &1, dd = ( i>>D[ d ] ) &1; edgeset ( cubeedge ( ch [ i ] , d , 0 , 0) , z , clamp ( e [ rd ] [ cd ] − dd∗8, 0 , 8) ) ; edgeset ( cubeedge ( ch [ i ] , d , 1 , 0) , z , clamp ( e [1+ rd ] [ cd ] − dd∗8, 0 , 8) ) ; edgeset ( cubeedge ( ch [ i ] , d , 0 , 1) , z , clamp ( e [ rd ] [ 1 + cd ] − dd∗8, 0 , 8) ) ; edgeset ( cubeedge ( ch [ i ] , d , 1 , 1) , z , clamp ( e [1+ rd ] [ 1 + cd ] − dd∗8, 0 , 8) ) ; }

} i f ( fixtex ) loopj ( 6 ) c . texture [ j ] = getmippedtexture ( c , j ) ; } s t a t i c i n t midedge ( const i v e c &a , const i v e c &b , i n t xd , i n t yd , bool & perfect ) { i n t ax = a [ xd ] , ay = a [ yd ] , bx = b [ xd ] , by = b [ yd ] ; i f ( ay==by ) return ay ; i f ( ax==bx ) { p e r f e c t = f a l s e ; return ay ; } bool crossx = ( ax8) || ( ax>8 && bx8 && by=8) return axsurfaces , 0 , s i z e o f ( c . ext−>surfaces ) ) ; i f ( isempty ( c ) || i s e n t i r e l y s o l i d ( c ) ) { c . children = newcubes ( isempty ( c ) ? F EMPTY : F SOLID , c . material ) ; loopi ( 8 ) { l o o p l ( 6 ) c . children [ i ] . texture [ l ] = c . texture [ l ] ; i f ( brighten && ! isempty ( c ) ) brightencube ( c . children [ i ] ) ; } return true ; } cube ∗ch = c . children = newcubes ( F SOLID , c . material ) ; bool p e r f e c t = true ; ivec v [ 8 ] ; loopi ( 8 ) { getcubevector ( c , i , v [ i ] ) ; v [ i ] . mul ( 2 ) ; } loopj ( 6 ) { i n t d = dimension ( j ) , z = dimcoord ( j ) ; const i v e c &v00 = v [ octaindex ( d , 0 , 0 , z ) ] , &v10 = v [ octaindex ( d , 1 , 0 , z ) ] , &v01 = v [ octaindex ( d , 0 , 1 , z ) ] , &v11 = v [ octaindex ( d , 1 , 1 , z ) ] ; int e [ 3 ] [ 3 ] ; // corners e [ 0 ] [ 0 ] = v00 [ d ] ; e [ 0 ] [ 2 ] = v01 [ d ] ; e [ 2 ] [ 0 ] = v10 [ d ] ; e [ 2 ] [ 2 ] = v11 [ d ] ; // edges e [ 0 ] [ 1 ] = midedge ( v00 , v01 , C[ d ] , d , p e r f e c t ) ; e [ 1 ] [ 0 ] = midedge ( v00 , v10 , R[ d ] , d , p e r f e c t ) ; e [ 1 ] [ 2 ] = midedge ( v11 , v01 , R[ d ] , d , p e r f e c t ) ; e [ 2 ] [ 1 ] = midedge ( v11 , v10 , C[ d ] , d , p e r f e c t ) ; // center bool p1 = perfect , p2 = p e r f e c t ; i n t c1 = midedge ( v00 , v11 , R[ d ] , d , p1 ) ; i n t c2 = midedge ( v01 , v10 , R[ d ] , d , p2 ) ; i f ( z ? c1 > c2 : c1 < c2 ) { e [ 1 ] [ 1 ] = c1 ; p e r f e c t = p1 && ( c1 == c2 || crosscenter ( v00 , v11 , C[ d ] , R[ d ] ) ) ; } else { e [ 1 ] [ 1 ] = c2 ; p e r f e c t = p2 && ( c1 == c2 || crosscenter ( v01 , v10 , C[ d ] , R[ d ] ) ) ; }

i n t v i s i b l e o r i e n t ( const cube &c , i n t o r i e n t ) { loopi ( 2 ) { i n t a = faceedgesidx [ o r i e n t ] [ i∗2 + 0 ] ; i n t b = faceedgesidx [ o r i e n t ] [ i∗2 + 1 ] ; loopj ( 2 ) { i f ( crushededge ( c . edges [ a ] , j ) && crushededge ( c . edges [ b ] , j ) && touchingface ( c , o r i e n t ) ) return ( ( a>>2)= (84)| ( r2&0xF0 ) ) == uchar ( ( r1&0x0F ) | ( r24)| ( c2&0xF0 ) ) == uchar ( ( c1&0x0F ) | ( c2= 3 && i n s i d e f a c e ( cf , numc, of , numo) ; } s i z e >>= 1; i n t coord = dimcoord ( o r i e n t ) ; l o o p i ( 8 ) i f ( octacoord ( dim , i ) == coord ) { i f ( ! occludesface ( c . children [ i ] , orient , i v e c ( i , o . x , o . y , o . z , s i z e ) , size , vo , vsize , vmat , nmat, matmask, vf , numv) ) return false ; }

ASSERT( r o f f s e t ) return f a l s e ; bounds++; prev = cur ; } return bounds>=3;

}

325

} bool v i s i b l e f a c e ( const cube &c , i n t orient , i n t x , i n t y , i n t z , i n t size , ushort mat , ushort nmat, ushort matmask ) { i f ( mat ! = MAT AIR ) { i f ( faceedges ( c , o r i e n t ) ==F SOLID && touchingface ( c , o r i e n t ) ) return false ; } else { i f ( collapsedface ( c , o r i e n t ) ) return f a l s e ;

326

Foundations of Videogame Programming Code Repository 1; touching = 0xF&˜(1 MAXFACEVERTS) return f a l s e ; memcpy( p . verts , verts2 , numverts2∗s i z e o f ( pvert ) ) ; p . numverts = numverts2 ; return true ;

i v e c no ; i n t nsize ; const cube &nc = neighbourcube ( cu , orient , co . x , co . y , co . z , size , no , nsize ) ; facebounds mincf ; mincf . u1 = o r i g . u2 ; mincf . u2 = o r i g . u1 ; mincf . v1 = o r i g . v2 ; mincf . v2 = o r i g . v1 ; mincubeface ( nc , opposite ( o r i e n t ) , no , nsize , orig , mincf , cu . material& MAT ALPHA ? MAT AIR : MAT ALPHA, MAT ALPHA) ; bool smaller = f a l s e ; i f ( mincf . u1 > o r i g . u1 ) { o r i g . u1 = mincf . u1 ; smaller = true ; } i f ( mincf . u2 < o r i g . u2 ) { o r i g . u2 = mincf . u2 ; smaller = true ; } i f ( mincf . v1 > o r i g . v1 ) { o r i g . v1 = mincf . v1 ; smaller = true ; } i f ( mincf . v2 < o r i g . v2 ) { o r i g . v2 = mincf . v2 ; smaller = true ; } return smaller ; } VAR( maxmerge, 0 , 6 , 12) ; VAR( minface , 0 , 4 , 12) ; struct pvert { ushort x , y ; pvert ( ) {} pvert ( ushort x , ushort y ) : x ( x ) , y ( y ) {} bool operator ==( const pvert &o ) const { return x == o . x && y == o . y ; } bool operator ! = ( const pvert &o ) const { return x ! = o . x || y ! = o . y ; } }; struct pedge { pvert from , to ; pedge ( ) {} pedge ( const pvert &from , const pvert &to ) : from ( from ) , to ( to ) {} bool operator ==( const pedge &o ) const { return from == o . from && to == o . to ; } bool operator ! = ( const pedge &o ) const { return from ! = o . from || to ! = o . to ; } }; s t a t i c i n l i n e uint hthash ( const pedge &x ) { return uint ( x . from . x ) ˆ ( uint ( x . from . y )= q . numverts ) index −= q . numverts ; pvert &src = q . v e r t s [ index + + ] ; i n t cx = i n t ( src . x ) − i n t ( v e r t s [ numverts−1].x ) , cy = i n t ( src . y ) − i n t ( v e r t s [ numverts−1].y ) , d i r = px∗cy − py∗cx ; i f ( d i r > 0) return f a l s e ; i f ( ! d i r ) numverts−−; v e r t s [ numverts++] = src ; px = cx ; py = cy ; } i n t cx = i n t ( v e r t s [ 0 ] . x ) − i n t ( v e r t s [ numverts−1].x ) , cy = i n t ( v e r t s [ 0 ] . y ) − i n t ( v e r t s [ numverts−1].y ) , d i r = px∗cy − py∗cx ; i f ( d i r > 0) return f a l s e ; i f ( ! d i r ) numverts−−;

p . numverts = 0; i f ( coord ) { const i v e c &v0 = v [ order ] ; p . v e r t s [ p . numverts++] = pvert ( v0 [ c ] , v0 [ r ]) ; i f ( v i s &1) { const i v e c &v1 = v [ order + 1 ] ; p . v e r t s [ p . numverts++] = pvert ( v1 [ c ] , v1 [ r ] ) ; } const i v e c &v2 = v [ order + 2 ] ; p . v e r t s [ p . numverts++] = pvert ( v2 [ c ] , v2 [ r ] ) ; i f ( v i s &2) { const i v e c &v3 = v [ ( order +3) &3]; p . v e r t s [ p . numverts++] = pvert ( v3 [ c ] , v3 [ r ] ) ; } } else { i f ( v i s &2) { const i v e c &v3 = v [ ( order +3) &3]; p . v e r t s [ p . numverts++] = pvert ( v3 [ c ] , v3 [ r ] ) ; } const i v e c &v2 = v [ order + 2 ] ; p . v e r t s [ p . numverts++] = pvert ( v2 [ c ] , v2 [ r ] ) ; i f ( v i s &1) { const i v e c &v1 = v [ order + 1 ] ; p . v e r t s [ p . numverts++] = pvert ( v1 [ c ] , v1 [ r ] ) ; } const i v e c &v0 = v [ order ] ; p . v e r t s [ p . numverts++] = pvert ( v0 [ c ] , v0 [ r ]) ; } i f ( faceedges ( cu , o r i e n t ) ! = F SOLID ) { i n t px = i n t ( p . v e r t s [ p . numverts−2].x ) − i n t ( p . v e r t s [ p . numverts−3].x ) , py = i n t ( p . v e r t s [ p . numverts−2].y ) − i n t ( p . v e r t s [ p . numverts−3].y ) , cx = i n t ( p . v e r t s [ p . numverts−1].x ) − i n t ( p . v e r t s [ p . numverts−2].x ) , cy = i n t ( p . v e r t s [ p . numverts−1].y ) − i n t ( p . v e r t s [ p . numverts−2].y ) , d i r = px∗cy − py∗cx ; i f ( d i r > 0) return f a l s e ; i f ( ! d i r ) { i f ( p . numverts < 4) return f a l s e ; p . v e r t s [ p . numverts−2] = p . v e r t s [ p . numverts−1]; p . numverts−−; } px = cx ; py = cy ; cx = i n t ( p . v e r t s [ 0 ] . x ) − i n t ( p . v e r t s [ p . numverts−1].x ) ; cy = i n t ( p . v e r t s [ 0 ] . y ) − i n t ( p . v e r t s [ p . numverts−1].y ) ; d i r = px∗cy − py∗cx ; i f ( d i r > 0) return f a l s e ; i f ( ! d i r ) { i f ( p . numverts < 4) return f a l s e ; p . numverts−−; } px = cx ; py = cy ; cx = i n t ( p . v e r t s [ 1 ] . x ) − i n t ( p . v e r t s [ 0 ] . x ) ; cy = i n t ( p . v e r t s [ 1 ] . y ) − int ( p. verts [ 0 ] . y ) ; d i r = px∗cy − py∗cx ; i f ( d i r > 0) return f a l s e ; i f ( ! d i r ) { i f ( p . numverts < 4) return f a l s e ; p . v e r t s [ 0 ] = p . v e r t s [ p . numverts−1]; p . numverts−−; } px = cx ; py = cy ; cx = i n t ( p . v e r t s [ 2 ] . x ) − i n t ( p . v e r t s [ 1 ] . x ) ; cy = i n t ( p . v e r t s [ 2 ] . y ) − int ( p. verts [ 1 ] . y ) ; d i r = px∗cy − py∗cx ; i f ( d i r > 0) return f a l s e ; i f ( ! d i r ) { i f ( p . numverts < 4) return f a l s e ; p . v e r t s [ 1 ] = p . v e r t s [ 2 ] ; p . v e r t s [ 2 ] = p . v e r t s [ 3 ] ; p . numverts−−; } }

i f ( numverts > MAXFACEVERTS) return f a l s e ; q . merged = true ; q . numverts = 0; p . merged = true ; p . numverts = numverts ; memcpy( p . verts , verts , numverts∗s i z e o f ( pvert ) ) ; i n t prev = p . numverts−1; l o o p j ( p . numverts ) { pedge e ( p . v e r t s [ prev ] , p . v e r t s [ j ] ) ; i n t order = e . from . x > e . to . x || ( e . from . x == e . to . x && e . from . y > e . to . y ) ? 1 : 0; i f ( order ) swap ( e . from , e . to ) ; plink &l = l i n k s . access ( e , e ) ; bool shouldqueue = l . polys [ order ] < 0 && l . polys [ order ˆ 1 ] >= 0; l . polys [ order ] = owner ; i f ( shouldqueue ) queue . add(& l ) ; prev = j ; }

p . c = &cu ; p . merged = f a l s e ; i f ( minface && s i z e >= 11) {

i n t calcmergedsize ( i n t orient , const i v e c &co , i n t size , const v e r t i n f o ∗ verts , i n t numverts ) { ushort x1 = v e r t s [ 0 ] . x , y1 = v e r t s [ 0 ] . y , z1 = v e r t s [ 0 ] . z , x2 = x1 , y2 = y1 , z2 = z1 ; f o r ( i n t i = 1; i < numverts ; i ++) { const v e r t i n f o &v = v e r t s [ i ] ; x1 = min ( x1 , v . x ) ; x2 = max( x2 , v . x ) ; y1 = min ( y1 , v . y ) ; y2 = max( y2 , v . y ) ; z1 = min ( z1 , v . z ) ; z2 = max( z2 , v . z ) ; } i n t b i t s = 0; while(1o [ i ] ) / camdir [ i ] ) ; w = vec ( camdir ) . mul ( wdist−0.05f ) . add ( player−>o ) ; i f ( ! insideworld (w) ) { wdist = 0; l o o p i ( 3 ) w[ i ] = clamp ( player−>o [ i ] , 0.0 f , f l o a t ( worldsize ) ); } } cube ∗c = &lookupcube ( i n t (w. x ) , i n t (w. y ) , i n t (w. z ) ) ; i f ( gridlookup && ! dragging && ! moving && ! havesel && hmapedit !=1) gridsize = lusize ; i n t mag = l u s i z e / g r i d s i z e ; normalizelookupcube ( i n t (w. x ) , i n t (w. y ) , i n t (w. z ) ) ; i f ( s d i s t == 0 || s d i s t > wdist ) r a y r e c t i n t e r s e c t ( lu . tovec ( ) , vec ( g r i d s i z e ) , player−>o , camdir , t =0 , o r i e n t ) ; // j u s t getting orient cur = lu ; cor = vec (w) . mul ( 2 ) . div ( g r i d s i z e ) ; od = dimension ( o r i e n t ) ; d = dimension ( s e l . o r i e n t ) ;

glEnable (GL BLEND) ; glBlendFunc (GL ONE, GL ONE) ; // cursors lineshader−>set ( ) ; renderentselection ( player−>o , camdir , entmoving ! = 0 ) ; enablepolygonoffset ( GL POLYGON OFFSET LINE ) ; i f ( ! moving && ! hovering && ! hidecursor ) { i f ( hmapedit==1) glColor3ub ( 0 , hmapsel ? 255 : 40, 0) ; else glColor3ub (120 ,120 ,120) ; boxs ( orient , lu . tovec ( ) , vec ( l u s i z e ) ) ; } // s e l e c t i o n s i f ( havesel ) { d = dimension ( s e l . o r i e n t ) ; glColor3ub (50 ,50 ,50) ; // g r i d boxsgrid ( s e l . orient , s e l . o . tovec ( ) , s e l . s . tovec ( ) , s e l . g r i d ) ; glColor3ub (200 ,0 ,0) ; // 0 reference boxs3D ( s e l . o . tovec ( ) . sub ( 0 . 5 f∗min ( g r i d s i z e ∗0.25 f , 2.0 f ) ) , vec ( min ( g r i d s i z e ∗0.25 f , 2.0 f ) ) , 1) ; glColor3ub (200 ,200 ,200) ;// 2D s e l e c t i o n box vec co ( s e l . o . v ) , cs ( s e l . s . v ) ; co [R[ d ] ] += 0.5 f ∗( s e l . cx∗g r i d s i z e ) ; co [C[ d ] ] += 0.5 f ∗( s e l . cy∗g r i d s i z e ) ; cs [R[ d ] ] = 0.5 f ∗( s e l . cxs∗g r i d s i z e ) ; cs [C[ d ] ] = 0.5 f ∗( s e l . cys∗g r i d s i z e ) ; cs [D[ d ] ] ∗= g r i d s i z e ; boxs ( s e l . orient , co , cs ) ; i f ( hmapedit==1) // 3D s e l e c t i o n box glColor3ub (0 ,120 ,0) ; else glColor3ub (0 ,0 ,120) ; boxs3D ( s e l . o . tovec ( ) , s e l . s . tovec ( ) , s e l . g r i d ) ; }

i f ( hmapedit==1 && dimcoord ( horient ) == ( camdir [ dimension ( horient ) ]set ( ) ; glDisable (GL BLEND) ; } void t r y e d i t ( ) { extern i n t hidehud ; i f ( ! editmode || hidehud || mainmenu) return ; i f ( blendpaintmode ) trypaintblendmap ( ) ; }

336

Foundations of Videogame Programming Code Repository

//////////// ready changes to vertex arrays ////////////

{ ∗b = s ; cube ∗q = b−>c ( ) ; loopxyz ( s , rgrid , copycube ( c , ∗q++) ) ;

s t a t i c bool haschanged = f a l s e ; void readychanges ( block3 &b , cube ∗c , const i v e c &cor , i n t s i z e ) { loopoctabox ( cor , size , b . o , b . s ) { i v e c o ( i , cor . x , cor . y , cor . z , s i z e ) ; i f ( c [ i ] . ext ) { i f ( c [ i ] . ext−>va ) // removes va s so that octarender w i l l recreate { i n t hasmerges = c [ i ] . ext−>va−>hasmerges ; destroyva ( c [ i ] . ext−>va ) ; c [ i ] . ext−>va = NULL; i f ( hasmerges ) invalidatemerges ( c [ i ] , o , size , true ) ; } freeoctaentities ( c [ i ] ) ; c [ i ] . ext−>t j o i n t s = −1; } i f ( c [ i ] . children ) { i f ( size numents ) freeblock ( u−>block ( ) , f a l s e ) ; d e l e t e [ ] ( uchar ∗)u ; } void pasteundo ( undoblock ∗u ) { i f ( u−>numents ) pasteundoents ( u ) ; else { block3 ∗b = u−>block ( ) ; cube ∗s = b−>c ( ) ; i n t ∗g = u−>gridmap ( ) ; loopxyz (∗b , ∗g++ , pastecube(∗s++ , c ) ) ; } } s t a t i c i n l i n e i n t undosize ( undoblock ∗u ) { i f ( u−>numents ) return u−>numents∗s i z e o f ( undoent ) ; else { block3 ∗b = u−>block ( ) ; cube ∗q = b−>c ( ) ; i n t s i z e = b−>s i z e ( ) , t o t a l = s i z e∗s i z e o f ( i n t ) ; l o o p j ( s i z e ) t o t a l += f a m i l y s i z e (∗q++)∗s i z e o f ( cube ) ; return t o t a l ; } } struct undolist { undoblock ∗f i r s t , ∗l a s t ; undolist ( ) : f i r s t (NULL) , l a s t (NULL) {} bool empty ( ) { return ! f i r s t ; } void add ( undoblock ∗u ) { u−>next = NULL; u−>prev = l a s t ; i f ( ! f i r s t ) f i r s t = last = u; else { l a s t−>next = u ; last = u; } } undoblock ∗p o p f i r s t ( ) { undoblock ∗u = f i r s t ; f i r s t = f i r s t−>next ; i f ( f i r s t ) f i r s t−>prev = NULL; e l s e l a s t = NULL; return u ; } undoblock ∗poplast ( ) { undoblock ∗u = l a s t ; l a s t = l a s t−>prev ; i f ( l a s t ) l a s t−>next = NULL; e l s e f i r s t = NULL;

// generates a

engine/octaedit.cpp return u ;

337

r−>timestamp = t o t a l m i l l i s ; b . add ( r ) ;

} };

}

undolist undos , redos ; VARP( undomegs, 0 , 5 , 100) ; i n t totalundos = 0;

void pruneundos ( i n t maxremain ) // bound memory { while ( totalundos > maxremain && ! undos . empty ( ) ) { undoblock ∗u = undos . p o p f i r s t ( ) ; totalundos −= u−>s i z e ; freeundo ( u ) ; } //conoutf (CON DEBUG, ”undo : %d o f %d(%%%d ) ” , totalundos , undomegstimestamp ) { undoblock ∗u = a . poplast ( ) , ∗r ; i f ( u−>numents ) r = copyundoents ( u ) ; else { block3 ∗ub = u−>block ( ) ; l . o = ub−>o ; l . s = ub−>s ; l . g r i d = ub−>g r i d ; l . o r i e n t = ub−>o r i e n t ; r = newundocube ( l ) ; } if (r) { r−>s i z e = u−>s i z e ;

} commitchanges ( ) ; i f ( ! hmapsel ) { sel = l ; reorient ( ) ; } forcenextundo ( ) ; } void editundo ( ) { swapundo ( undos , redos , ”undo ” ) ; } void editredo ( ) { swapundo ( redos , undos , ” redo ” ) ; } // guard against subdivision #define p r o t e c t s e l ( f ) { undoblock ∗ u = newundocube ( s e l ) ; f ; i f ( u ) { pasteundo ( u ) ; freeundo ( u ) ; } } vector e d i t i n f o s ; e d i t i n f o ∗l o c a l e d i t = NULL; template s t a t i c void packcube ( cube &c , B &buf ) { i f ( c . children ) { buf . put (0xFF ) ; l o o p i ( 8 ) packcube ( c . children [ i ] , buf ) ; } else { cube data = c ; l i l s w a p ( data . texture , 6) ; buf . put ( c . material&0xFF ) ; buf . put ( c . material>>8); buf . put ( data . edges , s i z e o f ( data . edges ) ) ; buf . put ( ( uchar ∗) data . texture , s i z e o f ( data . texture ) ) ; } } template s t a t i c bool packblock ( block3 &b , B &buf ) { i f ( b . s i z e ( ) (1copy = NULL; } uchar ∗outbuf = NULL; i f ( ! uncompresseditinfo ( inbuf , inlen , outbuf , outlen ) ) return f a l s e ; ucharbuf buf ( outbuf , outlen ) ; i f ( ! e ) e = e d i t i n f o s . add ( new e d i t i n f o ) ; i f ( ! unpackblock ( e−>copy , buf ) ) { d e l e t e [ ] outbuf ; return f a l s e ; } d e l e t e [ ] outbuf ; return true ; } void f r e e e d i t i n f o ( e d i t i n f o ∗&e ) { i f ( ! e ) return ; e d i t i n f o s . removeobj ( e ) ; i f ( e−>copy ) freeblock ( e−>copy ) ; delete e ; e = NULL; } struct octabrushheader { char magic [ 4 ] ; i n t version ; }; struct octabrush : e d i t i n f o { char ∗name; octabrush ( ) : name(NULL) {} ˜ octabrush ( ) { DELETEA(name) ; i f ( copy ) freeblock ( copy ) ; } }; s t a t i c i n l i n e bool htcmp ( const char ∗key , const octabrush &b ) { return ! strcmp ( key , b .name) ; } s t a t i c hashset octabrushes ; void delbrush ( char ∗name) { i f ( octabrushes . remove (name) ) conoutf ( ” deleted brush %s ” , name) ;

} COMMAND( delbrush , ” s ” ) ; void savebrush ( char ∗name) { i f ( ! name[ 0 ] || noedit ( true ) || ( nompedit && multiplayer ( ) ) ) return ; octabrush ∗b = octabrushes . access (name) ; i f ( ! b) { b = &octabrushes [name ] ; b−>name = newstring (name) ; } i f ( b−>copy ) freeblock ( b−>copy ) ; p r o t e c t s e l ( b−>copy = blockcopy ( block3 ( s e l ) , s e l . g r i d ) ) ; changed ( s e l ) ; defformatstring ( filename ) ( strpbrk ( name, ”/\\”) ? ” packages/%s . obr ” : ” packages/brush/%s . obr ” , name) ; path ( filename ) ; stream ∗f = o p e n g z f i l e ( filename , ”wb ” ) ; i f ( ! f ) { conoutf (CON ERROR, ” could not write brush to %s ” , filename ) ; return ; } octabrushheader hdr ; memcpy( hdr . magic , ”OEBR” , 4) ; hdr . version = 0; l i l s w a p (&hdr . version , 1) ; f−>write (&hdr , s i z e o f ( hdr ) ) ; streambuf s ( f ) ; i f ( ! packblock(∗b−>copy , s ) ) { d e l e t e f ; conoutf (CON ERROR, ” could not pack brush %s ” , filename ) ; return ; } delete f ; conoutf ( ” wrote brush f i l e %s ” , filename ) ; } COMMAND( savebrush , ” s ” ) ; void pasteblock ( block3 &b , s e l i n f o &sel , bool l o c a l ) { sel . s = b. s ; int o = sel . orient ; sel . orient = b. orient ; cube ∗s = b . c ( ) ; loopselxyz ( i f ( ! isempty (∗s ) || s−>children || s−>material ! = MAT AIR ) pastecube(∗s , c ) ; s ++) ; // ’ transparent ’ . old opaque by ’ delcube ; paste ’ sel . orient = o ; } void pastebrush ( char ∗name) { i f ( ! name[ 0 ] || noedit ( ) || ( nompedit && multiplayer ( ) ) ) return ; octabrush ∗b = octabrushes . access (name) ; i f ( ! b) { defformatstring ( filename ) ( strpbrk ( name, ”/\\”) ? ” packages/%s . obr ” : ” packages/brush/%s . obr ” , name) ; path ( filename ) ; stream ∗f = o p e n g z f i l e ( filename , ” rb ” ) ; i f ( ! f ) { conoutf (CON ERROR, ” could not read brush %s ” , filename ) ; return ; } octabrushheader hdr ; i f ( f−>read(&hdr , s i z e o f ( hdr ) ) ! = s i z e o f ( octabrushheader ) || memcmp( hdr . magic , ”OEBR” , 4) ) { d e l e t e f ; conoutf (CON ERROR, ” brush %s has malformatted header ” , filename ) ; return ; } l i l s w a p (&hdr . version , 1) ; i f ( hdr . version ! = 0) { d e l e t e f ; conoutf (CON ERROR, ” brush %s uses unsupported version ” , filename ) ; return ; } streambuf s ( f ) ; block3 ∗copy = NULL; i f ( ! unpackblock ( copy , s ) ) { d e l e t e f ; conoutf (CON ERROR, ” could not unpack brush %s ” , filename ) ; return ; } delete f ; b = &octabrushes [name ] ; b−>name = newstring (name) ; b−>copy = copy ; } pasteblock (∗b−>copy , sel , true ) ; } COMMAND( pastebrush , ” s ” ) ; void mpcopy ( e d i t i n f o ∗&e , s e l i n f o &sel , bool l o c a l ) { i f ( l o c a l ) game : : e d i t t r i g g e r ( sel , EDIT COPY ) ; i f ( e==NULL) e = e d i t i n f o s . add ( new e d i t i n f o ) ; i f ( e−>copy ) freeblock ( e−>copy ) ; e−>copy = NULL; p r o t e c t s e l ( e−>copy = blockcopy ( block3 ( s e l ) , s e l . g r i d ) ) ; changed ( s e l ) ; } void mppaste ( e d i t i n f o ∗&e , s e l i n f o &sel , bool l o c a l ) { i f ( e==NULL) return ; i f ( l o c a l ) game : : e d i t t r i g g e r ( sel , EDIT PASTE ) ; i f ( e−>copy ) pasteblock (∗e−>copy , sel , l o c a l ) ; }

engine/octaedit.cpp

339

{ void copy ( ) { i f ( noedit ( true ) ) return ; mpcopy ( l o c a l e d i t , sel , true ) ; } void p a s t e h i l i t e ( ) { i f ( ! l o c a l e d i t ) return ; s e l . s = l o c a l e d i t−>copy−>s ; reorient ( ) ; havesel = true ; } void paste ( ) { i f ( noedit ( ) ) return ; mppaste ( l o c a l e d i t , sel , true ) ; } COMMAND( copy , ” ” ) ; COMMAND( p a s t e h i l i t e , ” ” ) ; COMMAND( paste , ” ” ) ; COMMANDN( undo , editundo , ” ” ) ; COMMANDN( redo , editredo , ” ” ) ; s t a t i c VSlot ∗e d i t i n g v s l o t = NULL; void compacteditvslots ( ) { i f ( e d i t i n g v s l o t && e d i t i n g v s l o t−>l a y e r ) compactvslot ( e d i t i n g v s l o t−> layer ) ; loopv ( e d i t i n f o s ) { e d i t i n f o ∗e = e d i t i n f o s [ i ] ; compactvslots ( e−>copy−>c ( ) , e−>copy−>s i z e ( ) ) ; } f o r ( undoblock ∗u = undos . f i r s t ; u ; u = u−>next ) i f ( ! u−>numents ) compactvslots ( u−>block ( )−>c ( ) , u−>block ( )−>s i z e ( ) ) ; f o r ( undoblock ∗u = redos . f i r s t ; u ; u = u−>next ) i f ( ! u−>numents ) compactvslots ( u−>block ( )−>c ( ) , u−>block ( )−>s i z e ( ) ) ; } ///////////// height maps //////////////// #define MAXBRUSH 64 #define MAXBRUSHC 63 #define MAXBRUSH2 32 i n t brush [MAXBRUSH] [MAXBRUSH] ; VAR( brushx , 0 , MAXBRUSH2, MAXBRUSH) ; VAR( brushy , 0 , MAXBRUSH2, MAXBRUSH) ; bool paintbrush = 0; i n t brushmaxx = 0 , brushminx = MAXBRUSH; i n t brushmaxy = 0 , brushminy = MAXBRUSH; void clearbrush ( ) { memset ( brush , 0 , s i z e o f brush ) ; brushmaxx = brushmaxy = 0; brushminx = brushminy = MAXBRUSH; paintbrush = f a l s e ; } void brushvert ( i n t ∗x , i n t ∗y , i n t ∗v ) { ∗x += MAXBRUSH2 − brushx + 1; // +1 f o r automatic padding ∗y += MAXBRUSH2 − brushy + 1; i f (∗x=MAXBRUSH) return ; brush[∗x ] [ ∗ y ] = clamp(∗v , 0 , 8) ; paintbrush = paintbrush || ( brush[∗x ] [ ∗ y ] > 0) ; brushmaxx = min (MAXBRUSH−1, max( brushmaxx , ∗x+1) ) ; brushmaxy = min (MAXBRUSH−1, max( brushmaxy , ∗y +1) ) ; brushminx = max( 0 , min ( brushminx , ∗x−1)) ; brushminy = max( 0 , min ( brushminy , ∗y−1)) ; } vector htextures ; COMMAND( clearbrush , ” ” ) ; COMMAND( brushvert , ” i i i ” ) ; ICOMMAND( hmapcancel , ” ” , ( ) , htextures . s e t s i z e ( 0 ) ; ) ; ICOMMAND( hmapselect , ” ” , ( ) , i n t t = lookupcube ( cur . x , cur . y , cur . z ) . texture [ o r i e n t ] ; i n t i = htextures . f i n d ( t ) ; i f ( i texture [ o ] ) ; } namespace hmap { # define PAINTED 1 # define NOTHMAP 2 # define MAPPED 16 uchar f l a g s [MAXBRUSH] [MAXBRUSH] ; cube ∗cmap[MAXBRUSHC] [MAXBRUSHC] [ 4 ] ; int mapz [MAXBRUSHC] [MAXBRUSHC] ; int map [MAXBRUSH] [MAXBRUSH] ; s e l i n f o changes ; bool s e l e c t i n g ; i n t d , dc , dr , dcr , biasup , br , hws, f g ; i n t gx , gy , gz , mx, my, mz, nx , ny , nz , bmx, bmy, bnx , bny ; uint f s ; s e l i n f o hundo ; cube ∗getcube ( i v e c t , i n t f ) { t [ d ] += dcr∗f∗g r i d s i z e ; i f ( t [ d ] > nz || t [ d ] < mz) return NULL; cube ∗c = &lookupcube ( t . x , t . y , t . z , g r i d s i z e ) ; i f ( c−>children ) forcemip (∗c , f a l s e ) ; discardchildren (∗c , true ) ; i f ( ! isheightmap ( s e l . orient , d , true , c ) ) return NULL; if ( t . x < changes . o . x ) changes . o . x = t . x ; e l s e i f ( t . x > changes . s . x ) changes . s . x = t . x ; if ( t . y < changes . o . y ) changes . o . y = t . y ; e l s e i f ( t . y > changes . s . y ) changes . s . y = t . y ; if ( t . z < changes . o . z ) changes . o . z = t . z ; e l s e i f ( t . z > changes . s . z ) changes . s . z = t . z ; return c ; } uint g e t f a c e ( cube ∗c , i n t d ) { return 0x 0 f 0 f 0 f 0 f & ( ( dc ? c−>faces [ d ] : 0x88888888 − c−>faces [ d ] ) >> f s ) ; } void pushside ( cube &c , i n t d , i n t x , i n t y , i n t z ) { ivec a ; getcubevector ( c , d , x , y , z , a ) ; a [R[ d ] ] = 8 − a [R[ d ] ] ; setcubevector ( c , d , x , y , z , a ) ; } void addpoint ( i n t x , i n t y , i n t z , i n t v ) { i f ( ! ( f l a g s [ x ] [ y ] & MAPPED) ) map[ x ] [ y ] = v + ( z∗8) ; f l a g s [ x ] [ y ] |= MAPPED; } void s e l e c t ( i n t x , i n t y , i n t z ) { i f ( (NOTHMAP & f l a g s [ x ] [ y ] ) || ( PAINTED & f l a g s [ x ] [ y ] ) ) return ; i v e c t ( d , x+gx , y+gy , dc ? z : hws−z ) ; t . shl ( gridpower ) ; // s e l e c t i o n s may damage ; must makeundo before hundo . o = t ; hundo . o [D[ d ] ] −= dcr∗g r i d s i z e ∗2; makeundoex ( hundo ) ; cube ∗∗c = cmap[ x ] [ y ] ; loopk ( 4 ) c [ k ] = NULL; c [ 1 ] = getcube ( t , 0) ; i f ( ! c [ 1 ] || ! isempty (∗c [ 1 ] ) ) { // t r y up c[2] = c [1]; c [ 1 ] = getcube ( t , 1) ; i f ( ! c [ 1 ] || isempty (∗c [ 1 ] ) ) { c [ 0 ] = c [ 1 ] ; c [ 1 ] = c [ 2 ] ; c [ 2 ] = NULL; } e l s e { z ++; t [ d]+= f g ; } } e l s e // drop down

340

Foundations of Videogame Programming Code Repository

{

pushside(∗c [ k ] , d , i , j , 0) ; pushside(∗c [ k ] , d , i , j , 1) ; } edgeset ( cubeedge(∗c [ k ] , d , i , j ) , dc , dc ? f : 8−f ) ;

z−−; t [ d]−= f g ; c[0] = c [1]; c [ 1 ] = getcube ( t , 0) ;

} } else emptyfaces (∗c [ k ] ) ;

} i f ( ! c [ 1 ] || isempty (∗c [ 1 ] ) ) { f l a g s [ x ] [ y ] |= NOTHMAP; return ; } } f l a g s [ x ] [ y ] |= PAINTED ; mapz [ x ] [ y ] = z ; i f ( ! c [ 0 ] ) c [ 0 ] = getcube ( t , 1) ; i f ( ! c [ 2 ] ) c [ 2 ] = getcube ( t , −1) ; c [ 3 ] = getcube ( t , −2) ; c [ 2 ] = ! c [ 2 ] || isempty (∗c [ 2 ] ) ? NULL : c [ 2 ] ; c [ 3 ] = ! c [ 3 ] || isempty (∗c [ 3 ] ) ? NULL : c [ 3 ] ; uint face = g e t f a c e ( c [ 1 ] , d ) ; i f ( face == 0x08080808 && ( ! c [ 0 ] || ! isempty (∗c [ 0 ] ) ) ) { f l a g s [ x ] [ y ] |= NOTHMAP; return ; } i f ( c[1]−>faces [R[ d ] ] == F SOLID ) // was s i n g l e face += 0x08080808 ; else // was pair face += c [ 2 ] ? g e t f a c e ( c [ 2 ] , d ) : 0x08080808 ; face += 0x08080808 ; // c [ 3 ] uchar ∗f = ( uchar∗)&face ; addpoint ( x , y, z, f [0]) ; addpoint ( x+1 , y , z, f [1]) ; addpoint ( x , y+1 , z , f [ 2 ] ) ; addpoint ( x+1 , y+1 , z , f [ 3 ] ) ; i f ( s e l e c t i n g ) // continue to adjacent cubes { i f ( x>bmx) s e l e c t ( x−1, y , z ) ; i f ( xbmy) s e l e c t ( x , y−1, z ) ; i f ( y, 0; } i f ( notempty ) { c [ k]−>texture [ s e l . o r i e n t ] = c[1]−>texture [ s e l . o r i e n t ] ; s o l i d f a c e s (∗c [ k ] ) ; loopi ( 2 ) loopj ( 2 ) { int f = e [ i ] [ j ] ; i f ( fmx) r i p p l e ( x−1, y , ( xmy) r i p p l e ( x , y−1, ( ymx && y>my) ) ; // do diagonals because +1 , ( x>mx && y> gridpower ) + cy − MAXBRUSH2; gz = ( cur [D[ d ] ] >> gridpower ) ; f s = dc ? 4 : 0; f g = dc ? g r i d s i z e : −g r i d s i z e ; mx = max( 0 , −gx ) ; // r i p p l e range my = max( 0 , −gy ) ; nx = min (MAXBRUSH−1, hws−gx ) − 1; ny = min (MAXBRUSH−1, hws−gy ) − 1; i f ( havesel ) { // s e l e c t i o n range bmx = mx = max(mx, ( s e l . o [R[ d]]>>gridpower )−gx ) ; bmy = my = max(my, ( s e l . o [C[ d]]>>gridpower )−gy ) ; bnx = nx = min ( nx , ( s e l . s [R[ d ] ] + ( s e l . o [R[ d]]>>gridpower ) )−gx−1); bny = ny = min ( ny , ( s e l . s [C[ d ] ] + ( s e l . o [C[ d]]>>gridpower ) )−gy−1); } i f ( havesel && mode1, dc , s e l d i r ) ; // corner command else { loop (mx, 2 ) loop (my, 2 ) // p u l l / push edges command { i f ( x==0 && mx==0 && s e l . cx ) continue ; i f ( y==0 && my==0 && s e l . cy ) continue ; i f ( x== s e l . s [R[ d]]−1 && mx==1 && ( s e l . cx+ s e l . cxs ) &1) continue ; i f ( y== s e l . s [C[ d]]−1 && my==1 && ( s e l . cy+ s e l . cys ) &1) continue ; i f ( p [mx+my∗2] ! = ( ( uchar ∗)&bak ) [mx+my∗2]) continue ;

} } void edithmap ( i n t dir , i n t mode ) { i f ( ( nompedit && multiplayer ( ) ) || ! hmapsel ) return ; hmap : : run ( dir , mode ) ; } ///////////// main cube e d i t //////////////// i n t bounded ( i n t n ) { return n8 ? 8 : n ) ; } void pushedge ( uchar &edge , i n t dir , i n t dc ) { i n t ne = bounded ( edgeget ( edge , dc ) + d i r ) ; edgeset ( edge , dc , ne ) ; i n t oe = edgeget ( edge , 1−dc ) ; i f ( ( dirne ) || ( dir>0 && dc==0 && oe0) == dc && ho [ d ] += s∗s e l . g r i d ; player−>r e s e t i n t e r p ( ) ; } } void mpdelcube ( s e l i n f o &sel , bool l o c a l ) {

342

Foundations of Videogame Programming Code Repository

i f ( l o c a l ) game : : e d i t t r i g g e r ( sel , EDIT DELCUBE) ; loopselxyz ( discardchildren ( c , true ) ; emptyfaces ( c ) ) ;

c . texture [ i ] = edit−>index ; i f ( ! findedit ) findedit = edit ;

}

} }

void delcube ( ) { i f ( noedit ( ) ) return ; mpdelcube ( sel , true ) ; } COMMAND( pushsel , ” i ” ) ; COMMAND( e d i t f a c e , ” i i ” ) ; COMMAND( delcube , ” ” ) ; /////////// texture e d i t i n g ////////////////// i n t curtexindex = −1, l a s t t e x = 0 , l a s t t e x m i l l i s = −1; i n t texpaneltimer = 0; vector texmru ; void t o f r o n t t e x ( ) // maintain most r e c e n t l y used o f the texture l i s t s when applying texture

} void edittexcube ( cube &c , i n t tex , i n t orient , bool &findrep ) { i f ( orient =0) { texmru . i n s e r t ( 0 , texmru . remove ( c ) ) ; curtexindex = −1; } } s e l i n f o repsel ; i n t reptex = −1; struct vslotmap { i n t index ; VSlot ∗v s l o t ; vslotmap ( ) {} vslotmap ( i n t index , VSlot ∗v s l o t ) : index ( index ) , v s l o t ( v s l o t ) {} }; s t a t i c vector remappedvslots ; VAR( usevdelta , 1 , 0 , 0) ; s t a t i c VSlot ∗remapvslot ( i n t index , const VSlot &ds ) { loopv ( remappedvslots ) i f ( remappedvslots [ i ] . index == index ) return remappedvslots [ i ] . v s l o t ; VSlot &vs = lookupvslot ( index , f a l s e ) ; i f ( vs . index < 0 || vs . index == DEFAULT SKY) return NULL; VSlot ∗e d i t = NULL; i f ( usevdelta ) { VSlot ms; mergevslot (ms, vs , ds ) ; e d i t = ms. changed ? e d i t v s l o t ( vs , ms) : vs . s l o t−>variants ; } e l s e e d i t = ds . changed ? e d i t v s l o t ( vs , ds ) : vs . s l o t−>variants ; i f ( ! e d i t ) e d i t = &vs ; remappedvslots . add ( vslotmap ( vs . index , e d i t ) ) ; return e d i t ; } s t a t i c void remapvslots ( cube &c , const VSlot &ds , i n t orient , bool & findrep , VSlot ∗&f i n d e d i t ) { i f ( c . children ) { l o o p i ( 8 ) remapvslots ( c . children [ i ] , ds , orient , findrep , f i n d e d i t ) ; return ; } s t a t i c VSlot ms; i f ( orient index ; i f ( ! findedit ) findedit = edit ; } } else { int i = visibleorient ( c , orient ) ; VSlot ∗e d i t = remapvslot ( c . texture [ i ] , ds ) ; i f ( edit ) { i f ( findrep ) { i f ( reptex < 0) reptex = c . texture [ i ] ; e l s e i f ( reptex ! = c . texture [ i ] ) findrep = f a l s e ; }

VAR( a l l f a c e s , 0 , 0 , 1) ; void mpeditvslot ( VSlot &ds , i n t a l l f a c e s , s e l i n f o &sel , bool l o c a l ) { i f ( local ) { i f ( ! ( l a s t s e l == s e l ) ) t o f r o n t t e x ( ) ; i f ( a l l f a c e s || ! ( repsel == s e l ) ) reptex = −1; repsel = s e l ; } bool findrep = l o c a l && ! a l l f a c e s && reptex < 0; VSlot ∗f i n d e d i t = NULL; e d i t i n g v s l o t = &ds ; loopselxyz ( remapvslots ( c , ds , a l l f a c e s ? −1 : s e l . orient , findrep , findedit ) ) ; e d i t i n g v s l o t = NULL; remappedvslots . s e t s i z e ( 0 ) ; i f ( l o c a l && f i n d e d i t ) { l a s t t e x = f i n d e d i t−>index ; lasttexmillis = totalmillis ; curtexindex = texmru . f i n d ( l a s t t e x ) ; i f ( curtexindex < 0) { curtexindex = texmru . length ( ) ; texmru . add ( l a s t t e x ) ; } } } void vdelta ( char ∗body ) { i f ( noedit ( ) || ( nompedit && multiplayer ( ) ) ) return ; usevdelta ++; execute ( body ) ; usevdelta−−; } COMMAND( vdelta , ” s ” ) ; void v r o t a t e ( i n t ∗n ) { i f ( noedit ( ) || ( nompedit && multiplayer ( ) ) ) return ; VSlot ds ; ds . changed = 1id ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2fv ( t c [ 0 ] ) ; g l V e r t e x 2 f ( x+r /2 , y+r /2) ; glTexCoord2fv ( t c [ 1 ] ) ; g l V e r t e x 2 f ( x+r , y+r /2) ; glTexCoord2fv ( t c [ 3 ] ) ; g l V e r t e x 2 f ( x+r /2 , y+r ) ; glTexCoord2fv ( t c [ 2 ] ) ; g l V e r t e x 2 f ( x+r , y+r ) ; glEnd ( ) ; x t r a v e r t s += 4; } if (! j ) { r −= 10; x += 5; y += 5; } e l s e i f ( j == 2) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } } y += s+gap ; } defaultshader−>set ( ) ; glPopMatrix ( ) ; } }

engine/octarender.cpp // octarender . cpp : f i l l vertex arrays with d i f f e r e n t cube surfaces .

GL ELEMENT ARRAY BUFFER ARB; glBindBuffer ( target , vbo ) ; glBufferData ( target , len , buf , GL STATIC DRAW ARB ) ; glBindBuffer ( target , 0) ;

#include ” engine . h”

} else { s t a t i c GLuint nextvbo = 0; i f ( ! nextvbo ) nextvbo ++; // j u s t in case i t ever wraps around vbo = nextvbo ++; data = new uchar [ len ] ; memcpy( data , buf , len ) ; } vboinfo &vbi = vbos [ vbo ] ; vbi . uses = numva; vbi . data = data ;

struct vboinfo { i n t uses ; uchar ∗data ; }; hashtable vbos ; VAR( printvbo , 0 , 0 , 1) ; VARFN( vbosize , maxvbosize , 0 , 1ebuf = vbo ; i f ( ! hasVBO) va−>edata = ( ushort ∗) ( data + ( s i z e t ) va−>edata ) ; break ; case VBO SKYBUF: va−>skybuf = vbo ; i f ( ! hasVBO) va−>skydata = ( ushort ∗) ( data + ( s i z e t ) va−> skydata ) ; break ; } }

s t a t i c vector vbodata [NUMVBO] ; s t a t i c vector vbovas [NUMVBO] ; s t a t i c i n t vbosize [NUMVBO] ; void destroyvbo ( GLuint vbo ) { vboinfo ∗e x i s t s = vbos . access ( vbo ) ; i f ( ! e x i s t s ) return ; vboinfo &vbi = ∗e x i s t s ; i f ( vbi . uses vbuf || ! va−>ebuf ) return f a l s e ; edata = new ushort[3∗va−>t r i s ] ; vdata = new uchar [ va−>v e r t s∗VTXSIZE ] ; i f (hasVBO)

engine/octarender.cpp {

vtx . pos = pos ; vtx . u = u ; vtx . v = v ; vtx . lmu = lmu ; vtx . lmv = lmv ; vtx . norm = norm ; vtx . reserved = 0; vtx . tangent = tangent ; vtx . bitangent = bitangent ; return addvert ( vtx ) ;

glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>ebuf ) ; glGetBufferSubData (GL ELEMENT ARRAY BUFFER ARB, ( s i z e t ) va−>edata , 3∗va−>t r i s∗s i z e o f ( ushort ) , edata ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glGetBufferSubData ( GL ARRAY BUFFER ARB, va−>v o f f s e t∗VTXSIZE, va−> v e r t s∗VTXSIZE, vdata ) ; glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; return true ; } else { memcpy( edata , va−>edata , 3∗va−>t r i s∗s i z e o f ( ushort ) ) ; memcpy( vdata , ( uchar ∗)va−>vdata + va−>v o f f s e t∗VTXSIZE, va−>v e r t s∗ VTXSIZE ) ; return true ; }

347

} }; enum { NO ALPHA = 0 , ALPHA BACK, ALPHA FRONT };

} void flushvbo ( i n t type = −1) { i f ( type < 0) { l o o p i (NUMVBO) flushvbo ( i ) ; return ; } vector &data = vbodata [ type ] ; i f ( data . empty ( ) ) return ; vector &vas = vbovas [ type ] ; genvbo ( type , data . getbuf ( ) , data . length ( ) , vas . getbuf ( ) , vas . length ( ) ) ; data . s e t s i z e ( 0 ) ; vas . s e t s i z e ( 0 ) ; vbosize [ type ] = 0; } uchar ∗addvbo ( vtxarray ∗va , i n t type , i n t numelems, i n t elemsize ) { vbosize [ type ] += numelems ;

struct sortkey { ushort tex , lmid , envmap ; uchar dim , layer , alpha ; sortkey ( ) {} sortkey ( ushort tex , ushort lmid , uchar dim , uchar l a y e r = LAYER TOP, ushort envmap = EMID NONE, uchar alpha = NO ALPHA) : tex ( tex ) , lmid ( lmid ) , envmap ( envmap ) , dim ( dim ) , l a y e r ( l a y e r ) , alpha ( alpha ) {} bool operator ==( const sortkey &o ) const { return tex==o . tex && lmid== o . lmid && envmap==o . envmap && dim==o . dim && l a y e r ==o . l a y e r && alpha==o . alpha ; } }; struct s o r t v a l { int unlit ; vector t r i s [ 2 ] ; s o r t v a l ( ) : u n l i t ( 0 ) {}

vector &data = vbodata [ type ] ; vector &vas = vbovas [ type ] ; vas . add ( va ) ; i n t len = numelems∗elemsize ; uchar ∗buf = data . reserve ( len ) . buf ; data . advance ( len ) ; return buf ; } struct verthash { s t a t i c const i n t SIZE = 1= USHRT MAX) return −1; v e r t s . add ( v ) ; chain . add ( t a b l e [ h ] ) ; return t a b l e [ h ] = v e r t s . length ( ) −1; } i n t addvert ( const vec &pos , f l o a t u = 0 , f l o a t v = 0 , short lmu = 0 , short lmv = 0 , const bvec &norm = bvec (128 , 128, 128) , const bvec &tangent = bvec (128 , 128, 128) , uchar bitangent = 128) { vertex vtx ;

}; s t a t i c i n l i n e bool htcmp ( const sortkey &x , const sortkey &y ) { return x == y ; } s t a t i c i n l i n e uint hthash ( const sortkey &k ) { return k . tex + k . lmid∗9741; } struct v a c o l l e c t : verthash { ivec origin ; int size ; hashtable indices ; vector texs ; vector g r a s s t r i s ; vector matsurfs ; vector mapmodels ; vector skyindices , e x p l i c i t s k y i n d i c e s ; vector skyfaces [ 6 ] ; i n t worldtris , skytris , skymask , skyclip , skyarea ; void c l e a r ( ) { clearverts ( ) ; w o r l d t r i s = s k y t r i s = 0; skymask = 0; skyclip = INT MAX ; skyarea = 0; indices . c l e a r ( ) ; skyindices . s e t s i z e ( 0 ) ; explicitskyindices . setsize (0) ; matsurfs . s e t s i z e ( 0 ) ; mapmodels . s e t s i z e ( 0 ) ; grasstris . setsize (0) ; texs . s e t s i z e ( 0 ) ; l o o p i ( 6 ) skyfaces [ i ] . s e t s i z e ( 0 ) ; } void remapunlit ( vector &remap ) { uint lastlmid [ 8 ] = { LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT }, f i r s t l m i d [ 8 ] = { LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT, LMID AMBIENT }; i n t f i r s t l i t [ 8 ] = { −1, −1, −1, −1, −1, −1, −1, −1 };

348

Foundations of Videogame Programming Code Repository

loopv ( texs ) { sortkey &k = texs [ i ] ; i f ( k . lmid>=LMID RESERVED) { LightMapTexture &lmtex = lightmaptexs [ k . lmid ] ; i n t type = lmtex . type&LM TYPE ; i f ( k . l a y e r ==LAYER BLEND) type += 2; e l s e i f ( k . alpha ) type += 4 + 2∗(k . alpha−1); lastlmid [ type ] = lmtex . unlitx>=0 ? k . lmid : LMID AMBIENT; i f ( f i r s t l m i d [ type ]==LMID AMBIENT && lastlmid [ type ] ! = LMID AMBIENT ) { f i r s t l i t [ type ] = i ; f i r s t l m i d [ type ] = lastlmid [ type ] ; } } e l s e i f ( k . lmid==LMID AMBIENT ) { Shader ∗s = lookupvslot ( k . tex , f a l s e ) . s l o t−>shader ; i n t type = s−>type&SHADER NORMALSLMS ? LM BUMPMAP0 : LM DIFFUSE; i f ( k . l a y e r ==LAYER BLEND) type += 2; e l s e i f ( k . alpha ) type += 4 + 2∗(k . alpha−1); i f ( lastlmid [ type ] ! = LMID AMBIENT ) { s o r t v a l &t = indices [ k ] ; i f ( t . unlit shader ; i n t type = o f f s e t + ( s−>type&SHADER NORMALSLMS ? LM BUMPMAP0 : LM DIFFUSE ) ; i f ( f i r s t l m i d [ type ]==LMID AMBIENT ) continue ; indices [ k ] . u n l i t = f i r s t l m i d [ type ] ; } } loopj ( 2 ) { i n t o f f s e t = 4 + 2∗j ; i f ( f i r s t l m i d [ o f f s e t ]==LMID AMBIENT && f i r s t l m i d [ o f f s e t +1]== LMID AMBIENT ) continue ; l o o p i (max( f i r s t l i t [ o f f s e t ] , f i r s t l i t [ o f f s e t + 1 ] ) ) { sortkey &k = texs [ i ] ; i f ( k . alpha ! = j +1) continue ; i f ( k . lmid ! =LMID AMBIENT ) continue ; Shader ∗s = lookupvslot ( k . tex , f a l s e ) . s l o t−>shader ; i n t type = o f f s e t + ( s−>type&SHADER NORMALSLMS ? LM BUMPMAP0 : LM DIFFUSE ) ; i f ( f i r s t l m i d [ type ]==LMID AMBIENT ) continue ; indices [ k ] . u n l i t = f i r s t l m i d [ type ] ; } } loopv ( remap ) { sortkey &k = remap [ i ] ; s o r t v a l &t = indices [ k ] ; i f ( t . unlit t r i s [ l ] . add ( t . t r i s [ l ] [ j ]) ;

} } void optimize ( ) { vector remap ; enumeratekt ( indices , sortkey , k , s o r t v a l , t , l o o p l ( 2 ) i f ( t . t r i s [ l ] . length ( ) && t . unlit =LMID RESERVED && lightmaptexs [ k . lmid ] . unlitx >=0) { sortkey ukey ( k . tex , LMID AMBIENT, k . dim , k . layer , k . envmap , k . alpha ) ; s o r t v a l ∗uval = indices . access ( ukey ) ; i f ( uval && uval−>unlit unlit u n l i t = k . lmid ; } } e l s e i f ( k . lmid==LMID AMBIENT ) { remap . add ( k ) ; t . u n l i t = −1; } texs . add ( k ) ; break ; } ); texs . s o r t ( t e x s o r t ) ; remapunlit ( remap ) ; matsurfs . shrink ( optimizematsurfs ( matsurfs . getbuf ( ) , matsurfs . length () ) ) ; } s t a t i c i n l i n e bool t e x s o r t ( const sortkey &x , const sortkey &y ) { i f ( x . alpha < y . alpha ) return true ; i f ( x . alpha > y . alpha ) return f a l s e ; i f ( x . l a y e r < y . l a y e r ) return true ; i f ( x . l a y e r > y . l a y e r ) return f a l s e ; i f ( x . tex == y . tex ) { i f ( x . lmid < y . lmid ) return true ; i f ( x . lmid > y . lmid ) return f a l s e ; i f ( x . envmap < y . envmap ) return true ; i f ( x . envmap > y . envmap ) return f a l s e ; i f ( x . dim < y . dim ) return true ; i f ( x . dim > y . dim ) return f a l s e ; return f a l s e ; } i f ( renderpath ! =R FIXEDFUNCTION ) { VSlot &xs = lookupvslot ( x . tex , f a l s e ) , &ys = lookupvslot ( y . tex , false ) ; i f ( xs . s l o t−>shader < ys . s l o t−>shader ) return true ; i f ( xs . s l o t−>shader > ys . s l o t−>shader ) return f a l s e ; i f ( xs . s l o t−>params . length ( ) < ys . s l o t−>params . length ( ) ) return true ; i f ( xs . s l o t−>params . length ( ) > ys . s l o t−>params . length ( ) ) return false ; } i f ( x . tex < y . tex ) return true ; e l s e return f a l s e ; } #define GENVERTS( type , ptr , body ) do \ { \ type ∗f = ( type ∗) ptr ; \ loopv ( v e r t s ) \ { \ const vertex &v = v e r t s [ i ] ; \ body ; \ f ++; \ } \ } while ( 0 ) #define GENVERTSPOSNORMUV( type , ptr , body ) GENVERTS( type , ptr , { f−>pos = v . pos ; f−>norm = v . norm ; f−>norm . f l i p ( ) ; f−>reserved = 0; f−>u = v . u ; f−>v = v . v ; body ; }) void genverts ( void ∗buf ) { i f ( renderpath==R FIXEDFUNCTION ) GENVERTSPOSNORMUV( v e r t e x f f , buf , { f−>lmu = v . lmu/ f l o a t (SHRT MAX ) ; f−>lmv = v . lmv/ f l o a t (SHRT MAX) ; }) ; else GENVERTS( vertex , buf , { ∗f = v ; f−>norm . f l i p ( ) ; }) ; } void setupdata ( vtxarray ∗va ) {

engine/octarender.cpp

349

i f ( k . l a y e r ==LAYER BLEND) { va−>texs−−; va−>t r i s −= e . length [ 1 ] / 3 ; va−>blends ++; va−>b l e n d t r i s += e . length [ 1 ] / 3 ; } e l s e i f ( k . alpha==ALPHA BACK) { va−>texs−−; va−>t r i s −= e . length [ 1 ] / 3 ; va−>alphaback ++; va−>alphabacktris += e . length [ 1 ] / 3 ; } e l s e i f ( k . alpha==ALPHA FRONT) { va−>texs−−; va−>t r i s −= e . length [ 1 ] / 3 ; va−>alphafront ++; va−>a l p h a f r o n t t r i s += e . length [ 1 ] / 3 ; }

va−>v e r t s = v e r t s . length ( ) ; va−>t r i s = w o r l d t r i s /3; va−>vbuf = 0; va−>vdata = 0; va−>minvert = 0; va−>maxvert = va−>verts −1; va−>v o f f s e t = 0; i f ( va−>v e r t s ) { i f ( vbosize [VBO VBUF] + v e r t s . length ( ) > maxvbosize || vbosize [VBO EBUF] + w o r l d t r i s > USHRT MAX || vbosize [VBO SKYBUF] + s k y t r i s > USHRT MAX) flushvbo ( ) ;

} } va−>texmask = 0; l o o p i ( va−>texs+va−>blends+va−>alphaback+va−>alphafront ) { S l o t &s l o t = ∗lookupvslot ( va−>e s l i s t [ i ] . texture , f a l s e ) . s l o t ; l o o p v j ( s l o t . sts ) va−>texmask |= 1= 2) ) ) va−>texmask |= 1verts , VTXSIZE ) ; genverts ( vdata ) ; va−>minvert += va−>v o f f s e t ; va−>maxvert += va−>v o f f s e t ; } va−>matbuf = NULL; va−>matsurfs = matsurfs . length ( ) ; i f ( va−>matsurfs ) { va−>matbuf = new materialsurface [ matsurfs . length ( ) ] ; memcpy( va−>matbuf , matsurfs . getbuf ( ) , matsurfs . length ( ) ∗s i z e o f ( materialsurface ) ) ; }

i f ( g r a s s t r i s . length ( ) ) { va−>g r a s s t r i s . move ( g r a s s t r i s ) ; useshaderbyname ( ” grass ” ) ; } i f ( mapmodels . length ( ) ) va−>mapmodels . put ( mapmodels . getbuf ( ) , mapmodels . length ( ) ) ; }

va−>skybuf = 0; va−>skydata = 0; va−>sky = skyindices . length ( ) ; va−>e x p l i c i t s k y = e x p l i c i t s k y i n d i c e s . length ( ) ; i f ( va−>sky + va−>e x p l i c i t s k y ) { va−>skydata += vbosize [VBO SKYBUF ] ; ushort ∗skydata = ( ushort ∗)addvbo ( va , VBO SKYBUF, va−>sky+va−> e x p l i c i t s k y , s i z e o f ( ushort ) ) ; memcpy( skydata , skyindices . getbuf ( ) , va−>sky∗s i z e o f ( ushort ) ) ; memcpy( skydata+va−>sky , e x p l i c i t s k y i n d i c e s . getbuf ( ) , va−> e x p l i c i t s k y∗s i z e o f ( ushort ) ) ; i f ( va−>v o f f s e t ) l o o p i ( va−>sky+va−>e x p l i c i t s k y ) skydata [ i ] += va −>v o f f s e t ; }

bool emptyva ( ) { return v e r t s . empty ( ) && matsurfs . empty ( ) && skyindices . empty ( ) && e x p l i c i t s k y i n d i c e s . empty ( ) && g r a s s t r i s . empty ( ) && mapmodels . empty ( ) ; } } vc ; i n t recalcprogress = 0; #define progress ( s ) i f ( ( recalcprogress++&0xFFF ) ==0) renderprogress ( recalcprogress / ( f l o a t ) allocnodes , s ) ; vector t j o i n t s ; vec shadowmapmin, shadowmapmax;

va−>e s l i s t = NULL; va−>texs = texs . length ( ) ; va−>b l e n d t r i s = 0; va−>blends = 0; va−>alphabacktris = 0; va−>alphaback = 0; va−>a l p h a f r o n t t r i s = 0; va−>alphafront = 0; va−>ebuf = 0; va−>edata = 0; i f ( va−>texs ) { va−>e s l i s t = new elementset [ va−>texs ] ; va−>edata += vbosize [VBO EBUF ] ; ushort ∗edata = ( ushort ∗)addvbo ( va , VBO EBUF, worldtris , s i z e o f ( ushort ) ) , ∗curbuf = edata ; loopv ( texs ) { const sortkey &k = texs [ i ] ; const s o r t v a l &t = indices [ k ] ; elementset &e = va−>e s l i s t [ i ] ; e . texture = k . tex ; e . lmid = t . unlit>0 ? t . u n l i t : k . lmid ; e . dim = k . dim ; e . layer = k. layer ; e . envmap = k . envmap ; ushort ∗startbuf = curbuf ; loopl ( 2 ) { e . minvert [ l ] = USHRT MAX; e . maxvert [ l ] = 0; i f ( t . t r i s [ l ] . length ( ) ) { memcpy( curbuf , t . t r i s [ l ] . getbuf ( ) , t . t r i s [ l ] . length ( ) ∗ s i z e o f ( ushort ) ) ; loopvj ( t . t r i s [ l ] ) { curbuf [ j ] += va−>v o f f s e t ; e . minvert [ l ] = min ( e . minvert [ l ] , curbuf [ j ] ) ; e . maxvert [ l ] = max( e . maxvert [ l ] , curbuf [ j ] ) ; } curbuf += t . t r i s [ l ] . length ( ) ; } e . length [ l ] = curbuf−startbuf ; }

i n t calcshadowmask ( vec ∗pos , i n t numpos) { extern vec shadowdir ; i n t mask = 0 , used = 1; vec pe = vec ( pos [ 1 ] ) . sub ( pos [ 0 ] ) ; loopk ( numpos−2) { vec e = vec ( pos [ k + 2 ] ) . sub ( pos [ 0 ] ) ; i f ( vec ( ) . cross ( pe , e ) . dot ( shadowdir )>0) { mask |= 1= 0) { for ( ctj = t j ; ; ) { i f ( c t j < 0) break ; i f ( t j o i n t s [ c t j ] . edge < cedge ) { c t j = t j o i n t s [ c t j ] . next ; continue ; } i f ( t j o i n t s [ c t j ] . edge ! = cedge ) c t j = −1; break ; } } i f ( c t j >= 0) { i n t e1 = cedge%(MAXFACEVERTS+1) , e2 = ( e1+1)%numverts ; vertex &v1 = v e r t s [ e1 ] , &v2 = v e r t s [ e2 ] ; i v e c d ( vec ( v2 . pos ) . sub ( v1 . pos ) . mul ( 8 ) ) ; i n t axis = abs ( d . x ) > abs ( d . y ) ? ( abs ( d . x ) > abs ( d . z ) ? 0 : 2) : ( abs ( d . y ) > abs ( d . z ) ? 1 : 2) ; i f ( d [ axis ] < 0) d . neg ( ) ; reduceslope ( d ) ; i n t o r i g i n = i n t ( min ( v1 . pos [ axis ] , v2 . pos [ axis ] ) ∗8)&˜0x7FFF , o f f s e t 1 = ( i n t ( v1 . pos [ axis ]∗8) − o r i g i n ) / d [ axis ] , o f f s e t 2 = ( i n t ( v2 . pos [ axis ]∗8) − o r i g i n ) / d [ axis ] ; vec o = vec ( v1 . pos ) . sub ( d . tovec ( ) . mul ( o f f s e t 1 /8.0 f ) ) ; f l o a t d o f f s e t = 1.0 f / ( o f f s e t 2 − o f f s e t 1 ) ; i f ( i1 < 0) f o r ( ; ; ) { t j o i n t &t = t j o i n t s [ c t j ] ; i f ( t . next < 0 || t j o i n t s [ t . next ] . edge ! = cedge ) break ; c t j = t . next ; } while ( c t j >= 0) { t j o i n t &t = t j o i n t s [ c t j ] ; i f ( t . edge ! = cedge ) break ; float offset = ( t . offset − offset1 ) ∗ doffset ; vertex v t ; v t . pos = d . tovec ( ) . mul ( t . o f f s e t /8.0 f ) . add ( o ) ; v t . reserved = 0; v t . u = v1 . u + ( v2 . u−v1 . u )∗o f f s e t ; v t . v = v1 . v + ( v2 . v−v1 . v )∗o f f s e t ; v t . lmu = short ( v1 . lmu + ( v2 . lmu−v1 . lmu )∗o f f s e t ) , v t . lmv = short ( v1 . lmv + ( v2 . lmv−v1 . lmv )∗o f f s e t ) ; v t . norm . l e r p ( v1 . norm, v2 . norm, o f f s e t ) ;

} } } } void addgrasstri ( i n t face , vertex ∗verts , i n t numv, ushort texture , ushort lmid ) { g r a s s t r i &g = vc . g r a s s t r i s . add ( ) ; i n t i1 , i2 , i3 , i4 ; i f (numv 3 ? face+3 : i3 ; } g . v [ 0 ] = v e r t s [ i1 ] . pos ; g . v [ 1 ] = v e r t s [ i2 ] . pos ; g . v [ 2 ] = v e r t s [ i3 ] . pos ; g . v [ 3 ] = v e r t s [ i4 ] . pos ; g .numv = numv; g . surface . toplane ( g . v [ 0 ] , g . v [ 1 ] , g . v [ 2 ] ) ; i f ( g . surface . z = fabs ( area . y ) && fabs ( area . x ) >= fabs ( area . z ) ) scale = 1/area . x , px = 1 , py = 2; e l s e i f ( fabs ( area . y ) >= fabs ( area . x ) && fabs ( area . y ) >= fabs ( area . z ) ) scale = −1/area . y , px = 0 , py = 2; else scale = 1/area . z , px = 0 , py = 1; bx . x = ( g . v [ 2 ] [ py ] − g . v [ 0 ] [ py ] ) ∗scale ; bx . y = ( g . v [ 2 ] [ px ] − g . v [ 0 ] [ px ] ) ∗scale ; bx . z = bx . x∗g . v [ 2 ] [ px ] − bx . y∗g . v [ 2 ] [ py ] ; by . x = ( g . v [ 2 ] [ py ] − g . v [ 1 ] [ py ] ) ∗scale ; by . y = ( g . v [ 2 ] [ px ] − g . v [ 1 ] [ px ] ) ∗scale ; by . z = by . x∗g . v [ 1 ] [ px ] − by . y∗g . v [ 1 ] [ py ] − 1; by . sub ( bx ) ; f l o a t tc1u = v e r t s [ i1 ] . lmu/ f l o a t (SHRT MAX) , tc1v = v e r t s [ i1 ] . lmv/ f l o a t (SHRT MAX) , tc2u = ( v e r t s [ i2 ] . lmu − v e r t s [ i1 ] . lmu ) / f l o a t (SHRT MAX) , tc2v = ( v e r t s [ i2 ] . lmv − v e r t s [ i1 ] . lmv ) / f l o a t (SHRT MAX) , tc3u = ( v e r t s [ i3 ] . lmu − v e r t s [ i1 ] . lmu ) / f l o a t (SHRT MAX) , tc3v = ( v e r t s [ i3 ] . lmv − v e r t s [ i1 ] . lmv ) / f l o a t (SHRT MAX) ; g . tcu = vec4 ( 0 , 0 , 0 , tc1u − ( bx . z∗tc2u + by . z∗tc3u ) ) ; g . tcu [ px ] = bx . x∗tc2u + by . x∗tc3u ; g . tcu [ py ] = −(bx . y∗tc2u + by . y∗tc3u ) ; g . tcv = vec4 ( 0 , 0 , 0 , tc1v − ( bx . z∗tc2v + by . z∗tc3v ) ) ; g . tcv [ px ] = bx . x∗tc2v + by . x∗tc3v ; g . tcv [ py ] = −(bx . y∗tc2v + by . y∗tc3v ) ; g . texture = texture ; g . lmid = lmid ; } s t a t i c i n l i n e void calctexgen ( VSlot &v s l o t , i n t dim , vec4 &sgen , vec4 & tgen ) { Texture ∗tex = v s l o t . s l o t−>sts . empty ( ) ? notexture : v s l o t . s l o t−>sts [0]. t ; f l o a t k = TEX SCALE/ v s l o t . scale , xs = v s l o t . rotation>=2 && v s l o t . rotationxs : tex−>xs ,

engine/octarender.cpp ys = ( v s l o t . rotation>=1 && v s l o t . rotation ys : tex−>ys , sk = k/xs , tk = k/ys , s o f f = −(( v s l o t . r o t a t i o n &5)==1 ? v s l o t . y o f f s e t : xs , t o f f = −(( v s l o t . r o t a t i o n &5)==1 ? v s l o t . x o f f s e t : ys ; s t a t i c const i n t s i [ ] = { 1 , 0 , 0 }, t i [ ] = { 2 , 2 , 1 i n t sdim = s i [ dim ] , tdim = t i [ dim ] ; sgen = vec4 ( 0 , 0 , 0 , s o f f ) ; tgen = vec4 ( 0 , 0 , 0 , t o f f ) ; i f ( ( v s l o t . r o t a t i o n &5)==1) { sgen [ tdim ] = ( dim 3); vc . skymask |= 0x3F&˜(1tex : LMID AMBIENT; sortkey key ( texture , lmid , v s l o t . s c r o l l S || v s l o t . s c r o l l T ? dim : 3 , l a y e r == LAYER BLEND ? LAYER BLEND : LAYER TOP, envmap, alpha ? ( v s l o t . alphaback ? ALPHA BACK : ( v s l o t . alphafront ? ALPHA FRONT : NO ALPHA) ) : NO ALPHA) ; addtris ( key , orient , verts , index , numverts , convex , shadowmask, t j ) ;

} i f ( grassy ) { f o r ( i n t i = 0; i < numverts−2; i += 2) { i n t faces = 0; i f ( index [ 0 ] ! = index [ i +1] && index [ i +1]!= index [ i +2] && index [ i +2]!= index [ 0 ] ) faces |= 1; i f ( i +3 < numverts && index [ 0 ] ! = index [ i +2] && index [ i +2]!= index [ i +3] && index [ i +3]!= index [ 0 ] ) faces |= 2; i f ( grassy > 1 && faces ==3) addgrasstri ( i , verts , 4 , texture , lmid ) ; else { i f ( faces &1) addgrasstri ( i , verts , 3 , texture , lmid ) ; i f ( faces &2) addgrasstri ( i +1 , verts , 3 , texture , lmid ) ; } } }

ushort encodenormal ( const vec &n ) { i f ( n . i s z e r o ( ) ) return 0; i n t yaw = i n t(−atan2 ( n . x , n . y ) /RAD) , pitch = i n t ( asin ( n . z ) /RAD) ; return ushort ( clamp ( pitch + 90, 0 , 180)∗360 + ( yaw < 0 ? yaw%360 + 360 : yaw%360) + 1) ; } vec decodenormal ( ushort norm ) { i f ( ! norm ) return vec ( 0 , 0 , 1) ; norm−−; const vec2 &yaw = sincos360 [ norm%360], &pitch = sincos360 [ norm /360+270]; return vec(−yaw . y∗pitch . x , yaw . x∗pitch . x , pitch . y ) ; } void addcubeverts ( VSlot &v s l o t , i n t orient , i n t size , vec ∗pos , i n t convex , ushort texture , ushort lmid , v e r t i n f o ∗vinfo , i n t numverts , i n t t j = −1, ushort envmap = EMID NONE, i n t grassy = 0 , bool alpha = f a l s e , i n t l a y e r = LAYER TOP ) { i n t dim = dimension ( o r i e n t ) ; i n t shadowmask = texture==DEFAULT SKY || alpha ? 0 : calcshadowmask ( pos , numverts ) ; LightMap ∗lm = NULL; LightMapTexture ∗lmtex = NULL; i f ( ! nolights && lightmaps . inrange ( lmid−LMID RESERVED) ) { lm = &lightmaps [ lmid−LMID RESERVED ] ; i f ( ( lm−>type&LM TYPE ) ==LM DIFFUSE || ( ( lm−>type&LM TYPE ) ==LM BUMPMAP0 && lightmaps . inrange ( lmid+1−LMID RESERVED) && ( lightmaps [ lmid+1−LMID RESERVED ] . type&LM TYPE ) ==LM BUMPMAP1) ) lmtex = &lightmaptexs [ lm−>tex ] ; e l s e lm = NULL; } vec4 sgen , tgen ; calctexgen ( v s l o t , dim , sgen , tgen ) ; vertex v e r t s [MAXFACEVERTS] ; i n t index [MAXFACEVERTS] ; loopk ( numverts ) { vertex &v = v e r t s [ k ] ; v . pos = pos [ k ] ; v . reserved = 0; v . u = sgen . dot ( v . pos ) ; v . v = tgen . dot ( v . pos ) ; i f ( lmtex ) { v . lmu = short ( c e i l ( ( lm−>o f f s e t x + v i n f o [ k ] . u∗( f l o a t (LM PACKW) / f l o a t (USHRT MAX+1) ) + 0.5 f ) ∗ f l o a t (SHRT MAX) /lmtex−>w) ) ; v . lmv = short ( c e i l ( ( lm−>o f f s e t y + v i n f o [ k ] . v∗( f l o a t (LM PACKH) / f l o a t (USHRT MAX+1) ) + 0.5 f ) ∗ f l o a t (SHRT MAX) /lmtex−>h ) ) ; } e l s e v . lmu = v . lmv = 0; i f ( renderpath ! =R FIXEDFUNCTION && v i n f o && v i n f o [ k ] . norm ) { vec n = decodenormal ( v i n f o [ k ] . norm ) , t = orientation tangent [ v s l o t . r o t a t i o n ] [ dim ] ; t . sub ( vec ( n ) . mul ( n . dot ( t ) ) ) . normalize ( ) ; v . norm = bvec ( n ) ; v . tangent = bvec ( t ) ; v . bitangent = vec ( ) . cross ( n , t ) . dot ( orientation binormal [ v s l o t . r o t a t i o n ] [ dim ] ) < 0 ? 0 : 255; } else { v . norm = v i n f o && v i n f o [ k ] . norm && envmap ! = EMID NONE ? bvec (

} struct edgegroup { i v e c slope , o r i g i n ; i n t axis ; }; s t a t i c uint hthash ( const edgegroup &g ) { return g . slope . x ˆ g . slope . y ˆ g . slope . z ˆ g . o r i g i n . x ˆ g . o r i g i n . y ˆ g . o r i g i n . z ; } s t a t i c bool htcmp ( const edgegroup &x , const edgegroup &y ) { return x . slope==y . slope && x . o r i g i n ==y . o r i g i n ; } enum { CE START CE END CE FLIP CE DUP };

= = = =

1= 0) { cubeedge &p = cubeedges [ cur ] ; i f ( p . f l a g s&CE DUP ? ce . o f f s e t>=p . o f f s e t && ce . o f f s e t +ce . size= p . o f f s e t ) { i f ( ce . o f f s e t == p . o f f s e t +p . s i z e ) ce . f l a g s &= ˜CE START ; prev = cur ; cur = p . next ; } e l s e break ; } i f ( insert ) { ce . next = cur ; while ( cur >= 0) { cubeedge &p = cubeedges [ cur ] ; i f ( ce . o f f s e t +ce . s i z e ==p . o f f s e t ) { ce . f l a g s &= ˜CE END; break ; } cur = p . next ; } i f ( prev>=0) cubeedges [ prev ] . next = cubeedges . length ( ) ; e l s e ∗e x i s t s = cubeedges . length ( ) ; } } e l s e edgegroups [ g ] = cubeedges . length ( ) ;

VSlot &v s l o t = lookupvslot ( c . texture [ i ] , true ) , ∗l a y e r = v s l o t . l a y e r && ! ( c . material&MAT ALPHA) ? &lookupvslot ( v s l o t . layer , true ) : NULL; ushort envmap = v s l o t . s l o t−>shader−>type&SHADER ENVMAP ? ( v s l o t . s l o t−>texmask&(1shader−>type&SHADER ENVMAP ? ( layer−>s l o t−>texmask&(1= 0 && t j o i n t s [ t j ] . edge < ( i +1) ∗(MAXFACEVERTS+1) ? t j : −1; i n t grassy = v s l o t . s l o t−>autograss && i ! =O BOTTOM ? ( v i s !=3 || convex ? 1 : 2) : 0; i f ( ! c . ext ) addcubeverts ( v s l o t , i , size , pos , convex , c . texture [ i ] , LMID AMBIENT, NULL, numverts , hastj , envmap, grassy , ( c . material&MAT ALPHA) ! = 0 ) ; else { const surfaceinfo &surf = c . ext−>surfaces [ i ] ; i f ( ! surf . numverts || surf . numverts&LAYER TOP ) addcubeverts ( v s l o t , i , size , pos , convex , c . texture [ i ] , surf . lmid [ 0 ] , verts , numverts , hastj , envmap, grassy , ( c . material&MAT ALPHA) !=0 , LAYER TOP | ( surf . numverts& LAYER BLEND) ) ; i f ( surf . numverts&LAYER BOTTOM) addcubeverts ( l a y e r ? ∗l a y e r : v s l o t , i , size , pos , convex , v s l o t . layer , surf . lmid [ 1 ] , surf . numverts&LAYER DUP ? v e r t s + numverts : verts , numverts , hastj , envmap2 ) ; } }

i f ( i n s e r t ) cubeedges . add ( ce ) ;

}

} } } void gencubeedges ( cube ∗c = worldroot , i n t x = 0 , i n t y = 0 , i n t z = 0 , i n t s i z e = worldsize>>1) { progress ( ” f i x i n g t−j o i n t s . . . ” ) ; neighbourstack [++ neighbourdepth ] = c ;

s t a t i c i n l i n e bool skyoccluded ( cube &c , i n t o r i e n t ) { return touchingface ( c , o r i e n t ) && faceedges ( c , o r i e n t ) == F SOLID ; } s t a t i c i n t dummyskyfaces [ 6 ] ; s t a t i c i n l i n e i n t hasskyfaces ( cube &c , i n t x , i n t y , i n t z , i n t size , i n t faces [ 6 ] = dummyskyfaces )

engine/octarender.cpp { i n t numfaces = 0; i f ( isempty ( c ) || c . material&MAT ALPHA) { i f ( x == 0) faces [ numfaces++] = O LEFT ; i f ( x + s i z e == worldsize ) faces [ numfaces++] = O RIGHT ; i f ( y == 0) faces [ numfaces++] = O BACK; i f ( y + s i z e == worldsize ) faces [ numfaces++] = O FRONT; i f ( z == 0) faces [ numfaces++] = O BOTTOM; i f ( z + s i z e == worldsize ) faces [ numfaces++] = O TOP ; } else i f ( ! isentirelysolid ( c ) ) { i f ( x == 0 && ! skyoccluded ( c , O LEFT ) ) faces [ numfaces++] = O LEFT ; i f ( x + s i z e == worldsize && ! skyoccluded ( c , O RIGHT ) ) faces [ numfaces++] = O RIGHT ; i f ( y == 0 && ! skyoccluded ( c , O BACK) ) faces [ numfaces++] = O BACK; i f ( y + s i z e == worldsize && ! skyoccluded ( c , O FRONT) ) faces [ numfaces++] = O FRONT; i f ( z == 0 && ! skyoccluded ( c , O BOTTOM) ) faces [ numfaces++] = O BOTTOM; i f ( z + s i z e == worldsize && ! skyoccluded ( c , O TOP ) ) faces [ numfaces ++] = O TOP ; } return numfaces ;

vc . skyindices . add ( index [ 0 ] ) ; vc . skyindices . add ( index [ 2 ] ) ; vc . skyindices . add ( index [ 3 ] ) ; nextskyface : ; } } } ////////// Vertex Arrays ////////////// i n t a l l o c v a = 0; i n t wtris = 0 , wverts = 0 , v t r i s = 0 , v v e r t s = 0 , glde = 0 , gbatches = 0; vector v a l i s t , varoot ; vtxarray ∗newva ( i n t x , i n t y , i n t z , i n t s i z e ) { vc . optimize ( ) ; vtxarray ∗va = new vtxarray ; va−>parent = NULL; va−>o = i v e c ( x , y , z ) ; va−>s i z e = s i z e ; va−>skyarea = vc . skyarea ; va−>skyfaces = vc . skymask ; va−>skyclip = vc . skyclip < INT MAX ? vc . skyclip : INT MAX ; va−>curvfc = VFC NOT VISIBLE ; va−>occluded = OCCLUDE NOTHING; va−>query = NULL; va−>bbmin = i v e c (−1, −1, −1) ; va−>bbmax = i v e c (−1, −1, −1) ; va−>hasmerges = 0; va−>mergelevel = −1;

} void minskyface ( cube &cu , i n t orient , const i v e c &co , i n t size , facebounds &o r i g ) { facebounds mincf ; mincf . u1 = o r i g . u2 ; mincf . u2 = o r i g . u1 ; mincf . v1 = o r i g . v2 ; mincf . v2 = o r i g . v1 ; mincubeface ( cu , orient , o r i g . u1 = max( mincf . u1, o r i g . u2 = min ( mincf . u2, o r i g . v1 = max( mincf . v1 , o r i g . v2 = min ( mincf . v2 ,

vc . setupdata ( va ) ; wverts += va−>v e r t s ; wtris += va−>t r i s + va−>blends + va−>alphabacktris + va−> alphafronttris ; a l l o c v a ++; v a l i s t . add ( va ) ;

co , size , orig , mincf , MAT ALPHA, MAT ALPHA) ; o r i g . u1 ) ; o r i g . u2 ) ; o r i g . v1 ) ; o r i g . v2 ) ;

} return va ; void genskyfaces ( cube &c , const i v e c &o , i n t s i z e ) { i n t faces [ 6 ] , numfaces = hasskyfaces ( c , o . x , o . y , o . z , size , faces ) ; i f ( ! numfaces ) return ; l o o p i ( numfaces ) { i n t o r i e n t = faces [ i ] , dim = dimension ( o r i e n t ) ; facebounds m; m. u1 = ( o [C[ dim]]&0xFFF ) alphafronttris ; allocva−−; v a l i s t . removeobj ( va ) ; i f ( ! va−>parent ) varoot . removeobj ( va ) ; i f ( reparent ) { i f ( va−>parent ) va−>parent−>children . removeobj ( va ) ; loopv ( va−>children ) { vtxarray ∗c h i l d = va−>children [ i ] ; child−>parent = va−>parent ; i f ( child−>parent ) child−>parent−>children . add ( c h i l d ) ; } } i f ( va−>vbuf ) destroyvbo ( va−>vbuf ) ; i f ( va−>ebuf ) destroyvbo ( va−>ebuf ) ; i f ( va−>skybuf ) destroyvbo ( va−>skybuf ) ; i f ( va−>e s l i s t ) d e l e t e [ ] va−>e s l i s t ; i f ( va−>matbuf ) d e l e t e [ ] va−>matbuf ; d e l e t e va ; } void clearvas ( cube ∗c ) { loopi ( 8 ) { i f ( c [ i ] . ext ) { i f ( c [ i ] . ext−>va ) destroyva ( c [ i ] . ext−>va , f a l s e ) ; c [ i ] . ext−>va = NULL; c [ i ] . ext−>t j o i n t s = −1; } i f ( c [ i ] . children ) clearvas ( c [ i ] . children ) ; } } void updatevabb ( vtxarray ∗va , bool f o r c e ) { i f ( ! f o r c e && va−>bbmin . x >= 0) return ; va−>bbmin = va−>geommin ; va−>bbmax = va−>geommax; va−>bbmin . min ( va−>matmin ) ; va−>bbmax.max( va−>matmax) ; loopv ( va−>children ) {

354

Foundations of Videogame Programming Code Repository

vtxarray ∗c h i l d = va−>children [ i ] ; updatevabb ( child , f o r c e ) ; va−>bbmin . min ( child−>bbmin ) ; va−>bbmax.max( child−>bbmax) ;

i f ( surf . numverts&LAYER BOTTOM) { mf . tex = v s l o t . l a y e r ; mf . envmap = envmap2 ; mf . lmid = surf . lmid [ 1 ] ; mf . numverts &= ˜LAYER TOP ; i f ( surf . numverts&LAYER DUP) mf . v e r t s += numverts ; vamerges [ l e v e l ] . add ( mf ) ; }

} loopv ( va−>mapmodels ) { o c t a e n t i t i e s ∗oe = va−>mapmodels [ i ] ; va−>bbmin . min ( oe−>bbmin ) ; va−>bbmax.max( oe−>bbmax) ; } i f ( va−>skyfaces ) { va−>skyfaces |= 0x80 ; i f ( va−>sky ) loop ( dim , 3) i f ( va−>skyfaces&(3skyfaces&(2s i z e > va−>bbmax [ dim ] ) || va−>o [ r ] < va−>bbmin [ r ] || va−>o [ r ] + va−>s i z e > va−>bbmax[ r ] || va−>o [ c ] < va−>bbmin [ c ] || va−>o [ c ] + va−>s i z e > va−>bbmax[ c ] ) { va−>skyfaces &= ˜0x80 ; break ; } } }

} } i f ( maxlevel >= 0) { vamergemax = max( vamergemax , maxlevel ) ; vahasmerges |= MERGE ORIGIN; } return maxlevel ; } i n t findmergedfaces ( cube &c , const i v e c &co , i n t size , i n t csi , i n t minlevel ) { i f ( c . ext && c . ext−>va && ! ( c . ext−>va−>hasmerges&MERGE ORIGIN) ) return c . ext−>va−>mergelevel ; e l s e i f ( c . children ) { i n t maxlevel = −1; loopi ( 8 ) { i v e c o ( i , co . x , co . y , co . z , s i z e /2) ; i n t l e v e l = findmergedfaces ( c . children [ i ] , o , s i z e /2 , csi −1, minlevel ) ; maxlevel = max( maxlevel , l e v e l ) ; } return maxlevel ; } e l s e i f ( c . ext && c . merged ) return genmergedfaces ( c , co , size , minlevel ); e l s e return −1;

} void updatevabbs ( bool f o r c e ) { loopv ( varoot ) updatevabb ( varoot [ i ] , f o r c e ) ; } struct mergedface { uchar orient , lmid , numverts ; ushort mat , tex , envmap ; v e r t i n f o ∗v e r t s ; int tjoints ; }; #define MAXMERGELEVEL 12 s t a t i c i n t vahasmerges = 0 , vamergemax = 0; s t a t i c vector vamerges [MAXMERGELEVEL+ 1 ] ; i n t genmergedfaces ( cube &c , const i v e c &co , i n t size , i n t minlevel = −1) { i f ( ! c . ext || isempty ( c ) ) return −1; i n t t j = c . ext−>t j o i n t s , maxlevel = −1; l o o p i ( 6 ) i f ( c . merged&(1v e r t s ( ) + surf . v e r t s ; mf . t j o i n t s = −1; i n t l e v e l = calcmergedsize ( i , co , size , mf . verts , mf . numverts& MAXFACEVERTS) ; i f ( l e v e l > minlevel ) { maxlevel = max( maxlevel , l e v e l ) ; while ( t j >= 0 && t j o i n t s [ t j ] . edge < i ∗(MAXFACEVERTS+1) ) t j = t j o i n t s [ t j ] . next ; i f ( t j >= 0 && t j o i n t s [ t j ] . edge < ( i +1) ∗(MAXFACEVERTS+1) ) mf . tjoints = tj ; VSlot &v s l o t = lookupvslot ( mf . tex , true ) , ∗l a y e r = v s l o t . l a y e r && ! ( c . material&MAT ALPHA) ? & lookupvslot ( v s l o t . layer , true ) : NULL; i f ( v s l o t . s l o t−>shader−>type&SHADER ENVMAP) mf . envmap = v s l o t . s l o t−>texmask&(1shader−>type& SHADER ENVMAP ? ( layer−>s l o t−>texmask&(1= c s i ) c . escaped |= 1mapmodels . length ( ) ) vc . mapmodels . add ( c . ext−>ents ) ; } return ;

i v e c bbmin , bbmax; calcgeombb ( cx , cy , cz , size , bbmin , bbmax) ; addskyverts ( i v e c ( cx , cy , cz ) , s i z e ) ;

} i f ( s i z e == min(0x1000 , worldsize /2) || ! vc . emptyva ( ) ) { vtxarray ∗va = newva ( cx , cy , cz , s i z e ) ; ext ( c ) . va = va ; va−>geommin = bbmin ; va−>geommax = bbmax; calcmatbb ( cx , cy , cz , size , va−>matmin, va−>matmax) ; va−>shadowmapmin = i v e c ( shadowmapmin. mul ( 8 ) ) . shr ( 3 ) ; va−>shadowmapmax = i v e c (shadowmapmax. mul ( 8 ) ) . add ( 7 ) . shr ( 3 ) ; va−>hasmerges = vahasmerges ; va−>mergelevel = vamergemax ; } else { l o o p i (MAXMERGELEVEL+1) vamerges [ i ] . s e t s i z e ( vamergeoffset [ i ] ) ; }

genskyfaces ( c , i v e c ( cx , cy , cz ) , s i z e ) ; i f ( ! isempty ( c ) ) { gencubeverts ( c , cx , cy , cz , size , c s i ) ; i f ( c . merged ) maxlevel = max( maxlevel , genmergedfaces ( c , i v e c ( cx , cy , cz ) , s i z e ) ) ; } i f ( c . material ! = MAT AIR ) genmatsurfs ( c , cx , cy , cz , size , vc . matsurfs ); i f ( c . ext ) { i f ( c . ext−>ents && c . ext−>ents−>mapmodels . length ( ) ) vc . mapmodels . add ( c . ext−>ents ) ; }

vc . c l e a r ( ) ; i f ( c s i childpos ) { vtxarray ∗c h i l d = varoot . pop ( ) ; c [ i ] . ext−>va−>children . add ( c h i l d ) ; child−>parent = c [ i ] . ext−>va ; } varoot . add ( c [ i ] . ext−>va ) ; i f ( vamergemax > s i z e ) { cmergemax = max( cmergemax , vamergemax ) ; chasmerges |= vahasmerges&˜MERGE USE; } continue ; } e l s e count = 0;

} } i f ( c s i +1 = 0) { t j o i n t &o = t j o i n t s [ cur ] ; i f ( t j . edge < o . edge || ( t j . edge==o . edge && ( e . f l a g s&CE FLIP ? t j . o f f s e t > o . o f f s e t : t j . o f f s e t < o . o f f s e t ) ) ) break ; prev = cur ; cur = o . next ; } t j . next = cur ; i f ( prev < 0) e . c−>ext−>t j o i n t s = t j o i n t s . length ( ) −1; e l s e t j o i n t s [ prev ] . next = t j o i n t s . length ( ) −1; } void f i n d t j o i n t s ( i n t cur , const edgegroup &g ) { i n t a c t i v e = −1; while ( cur >= 0) { cubeedge &e = cubeedges [ cur ] ; i n t prevactive = −1, curactive = a c t i v e ; while ( curactive >= 0) { cubeedge &a = cubeedges [ curactive ] ; i f ( a . o f f s e t +a . s i z e = 0) cubeedges [ prevactive ] . next = a . next ; e l s e a c t i v e = a . next ; } else { prevactive = curactive ; i f ( ! ( a . f l a g s&CE DUP) ) { i f ( e . f l a g s&CE START && e . o f f s e t > a . o f f s e t && e . o f f s e t < a . o f f s e t +a . s i z e ) addtjoint ( g , a , e . o f f s e t ) ; i f ( e . f l a g s&CE END && e . o f f s e t +e . s i z e > a . o f f s e t && e . o f f s e t +e . s i z e < a . o f f s e t +a . s i z e ) a d d t j o i n t ( g , a , e . o f f s e t +e . s i z e ) ; } i f ( ! ( e . f l a g s&CE DUP) ) { i f ( a . f l a g s&CE START && a . o f f s e t > e . o f f s e t && a . o f f s e t < e . o f f s e t +e . s i z e ) addtjoint ( g , e , a . o f f s e t ) ; i f ( a . f l a g s&CE END && a . o f f s e t +a . s i z e > e . o f f s e t && a . o f f s e t +a . s i z e < e . o f f s e t +e . s i z e ) a d d t j o i n t ( g , e , a . o f f s e t +a . s i z e ) ; }

extern vtxarray ∗v i s i b l e v a ; v i s i b l e v a = NULL; } void precachetextures ( ) { vector texs ; loopv ( v a l i s t ) { vtxarray ∗va = v a l i s t [ i ] ; l o o p j ( va−>texs + va−>blends ) i f ( texs . f i n d ( va−>e s l i s t [ j ] . texture ) < 0) texs . add ( va−>e s l i s t [ j ] . texture ) ; } loopv ( texs ) { loadprogress = f l o a t ( i +1)/texs . length ( ) ; lookupvslot ( texs [ i ] ) ; } loadprogress = 0; } void allchanged ( bool load ) { renderprogress ( 0 , ” c l e a r i n g vertex arrays . . . ” ) ; clearvas ( worldroot ) ; resetqueries ( ) ; resetclipplanes ( ) ; i f ( load ) initenvmaps ( ) ; guessshadowdir ( ) ; entitiesinoctanodes ( ) ; tjoints . setsize (0) ; i f ( f i l l t j o i n t s ) findtjoints ( ) ; octarender ( ) ; i f ( load ) precachetextures ( ) ; setupmaterials ( ) ; invalidatepostfx ( ) ; updatevabbs ( true ) ; resetblobs ( ) ; lightents ( ) ; i f ( load ) { seedparticles ( ) ; drawtextures ( ) ; } } void r e c a l c ( ) { allchanged ( true ) ; } COMMAND( recalc , ” ” ) ;

engine/pch.cpp

357

engine/pch.cpp #include ” engine . h”

engine/physics.cpp // physics . cpp : no physics books were hurt nor consulted in the construction o f t h i s code . // A l l physics computations and constants were invented on the f l y and simply tweaked u n t i l // they ” f e l t r i g h t ” , and have no basis in r e a l i t y . C o l l i s i o n detection i s s i m p l i s t i c but // very robust ( uses d i s c r e t e steps at f i x e d fps ) . #include ” engine . h” #include ”mpr. h” const i n t MAXCLIPPLANES = 1024; s t a t i c clipplanes clipcache [MAXCLIPPLANES ] ; s t a t i c i n t clipcacheversion = −2; s t a t i c i n l i n e clipplanes &g e t c l i p p l a n e s ( const cube &c , const i v e c &o , i n t size , bool c o l l i d e = true , i n t o f f s e t = 0) { clipplanes &p = clipcache [ i n t (&c − worldroot ) &(MAXCLIPPLANES−1) ] ; i f ( p . owner ! = &c || p . version ! = clipcacheversion+ o f f s e t ) { p . owner = &c ; p . version = clipcacheversion+ o f f s e t ; genclipplanes ( c , o . x , o . y , o . z , size , p , c o l l i d e ) ; } return p ;

} #define INTERSECTBOX( setentry , e x i t ) \ loop ( i , 3) \ { \ i f ( ray [ i ] ) \ { \ f l o a t prad = fabs ( p . r [ i ] ∗ invray [ i ] ) , pdist = ( p . o [ i ] − v [ i ] ) ∗ invray [ i ] , pmin = pdist − prad , pmax = pdist + prad ; \ i f ( pmin > e n t e r d i s t ) \ { \ i f ( pmin > e x i t d i s t ) e x i t ; \ e n t e r d i s t = pmin ; \ setentry ; \ } \ i f (pmax < e x i t d i s t ) \ { \ i f (pmax < e n t e r d i s t ) e x i t ; \ e x i t d i s t = pmax; \ } \ } \ e l s e i f ( v [ i ] < p . o [ i ]−p . r [ i ] || v [ i ] > p . o [ i ] +p . r [ i ] ) e x i t ; \ } vec hitsurface ;

}

s t a t i c i n l i n e bool raycubeintersect ( const clipplanes &p , const cube &c , const vec &v , const vec &ray , const vec &invray , f l o a t &d i s t )

void r e s e t c l i p p l a n e s ( ) { clipcacheversion += 2; i f ( ! clipcacheversion ) { memset ( clipcache , 0 , s i z e o f ( clipcache ) ) ; clipcacheversion = 2; } }

{

///////////////////////// ray − cube c o l l i s i o n ///////////////////////////////////////////////

}

s t a t i c i n l i n e bool pointinbox ( const vec &v , const vec &bo , const vec &br ) { return v . x = bo . x−br . x && v . y = bo . y−br . y && v . z = bo . z−br . z ; } bool pointincube ( const clipplanes &p , const vec &v ) { i f ( ! pointinbox ( v , p . o , p . r ) ) return f a l s e ; l o o p i ( p . s i z e ) i f ( p . p [ i ] . d i s t ( v )>1e−3f ) return f a l s e ; return true ; } #define INTERSECTPLANES( setentry , e x i t ) \ f l o a t e n t e r d i s t = −1e16f , e x i t d i s t = 1e16f ; \ loopi (p. size ) \ { \ f l o a t pdist = p . p [ i ] . d i s t ( v ) , facing = ray . dot ( p . p [ i ] ) ; \ i f ( facing < 0) \ { \ pdist /= −facing ; \ i f ( pdist > e n t e r d i s t ) \ { \ i f ( pdist > e x i t d i s t ) e x i t ; \ e n t e r d i s t = pdist ; \ setentry ; \ } \ } \ e l s e i f ( facing > 0) \ { \ pdist /= −facing ; \ i f ( pdist < e x i t d i s t ) \ { \ i f ( pdist < e n t e r d i s t ) e x i t ; \ e x i t d i s t = pdist ; \ } \ } \ e l s e i f ( pdist > 0) e x i t ; \

i n t entry = −1, bbentry = −1; INTERSECTPLANES( entry = i , return f a l s e ) ; INTERSECTBOX( bbentry = i , return f a l s e ) ; i f ( e x i t d i s t < 0) return f a l s e ; d i s t = max( e n t e r d i s t +0.1 f , 0.0 f ) ; i f ( bbentry>=0) { hitsurface = vec ( 0 , 0 , 0) ; hitsurface [ bbentry ] = ray [ bbentry]>0 ? −1 : 1; } e l s e hitsurface = p . p [ entry ] ; return true ;

extern void entselectionbox ( const e n t i t y &e , vec &eo , vec &es ) ; extern i n t entselradius ; float hitentdist ; i n t hitent , h i t o r i e n t ; s t a t i c f l o a t d i s t t o e n t ( o c t a e n t i t i e s ∗oc , o c t a e n t i t i e s ∗l a s t , const vec &o , const vec &ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t ) { vec eo , es ; int orient ; f l o a t d i s t = 1e16f , f = 0.0 f ; i f ( oc == l a s t ) return d i s t ; const vector &ents = e n t i t i e s : : getents ( ) ; #define e n t i n t e r s e c t ( mask, type , func ) {\ i f ( ( mode&(mask) ) ==(mask) ) \ { \ loopv ( oc−>type ) \ i f ( ! l a s t || l a s t−>type . f i n d ( oc−>type [ i ] ) type [ i ] ] ; \ i f ( ! e . inoctanode || &e== t ) continue ; \ func ; \ i f ( f0) \ { \ hitentdist = dist = f ; \ h i t e n t = oc−>type [ i ] ; \ hitorient = orient ; \ } \ } \ } \ } e n t i n t e r s e c t ( RAY POLY, mapmodels , o r i e n t = 0; // FIXME, not set i f ( ! mmintersect ( e , o , ray , radius , mode, f ) ) continue ; ); e n t i n t e r s e c t ( RAY ENTS, other , entselectionbox ( e , eo , es ) ; i f ( ! r a y r e c t i n t e r s e c t ( eo , es , o , ray , f , o r i e n t ) ) continue ; );

358

Foundations of Videogame Programming Code Repository elvl = lshift ; \ dent = min ( dent , e d i s t ) ; \ } \ o c l a s t = lc−>ext−>ents ; \ } \ i f ( lc−>children==NULL) break ; \ l c = lc−>children ; \ levels [ lshift ] = lc ; \

e n t i n t e r s e c t ( RAY ENTS, mapmodels , entselectionbox ( e , eo , es ) ; i f ( ! r a y r e c t i n t e r s e c t ( eo , es , o , ray , f , o r i e n t ) ) continue ; ); return d i s t ; } }

s t a t i c f l o a t d i s t t o o u t s i d e e n t ( const vec &o , const vec &ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t ) { vec eo , es ; int orient ; f l o a t d i s t = 1e16f , f = 0.0 f ; const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( outsideents ) { e x t e n t i t y &e = ∗ents [ outsideents [ i ] ] ; i f ( ! e . inoctanode || &e == t ) continue ; entselectionbox ( e , eo , es ) ; i f ( ! r a y r e c t i n t e r s e c t ( eo , es , o , ray , f , o r i e n t ) ) continue ; i f ( f0) { hitentdist = dist = f ; h i t e n t = outsideents [ i ] ; hitorient = orient ; } } return d i s t ; } // optimized shadow version s t a t i c f l o a t shadowent ( o c t a e n t i t i e s ∗oc , o c t a e n t i t i e s ∗l a s t , const vec &o , const vec &ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t ) { f l o a t d i s t = 1e16f , f = 0.0 f ; i f ( oc == l a s t ) return d i s t ; const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( oc−>mapmodels ) i f ( ! l a s t || l a s t−>mapmodels . f i n d ( oc−>mapmodels [ i ] ) mapmodels [ i ] ] ; i f ( ! e . inoctanode || &e== t ) continue ; i f ( ! mmintersect ( e , o , ray , radius , mode, f ) ) continue ; i f ( f>0 && f0 ? 1 : 0 , invray . z>0 ? 1 : 0) ; \

i n t l s i z e = 1= 0) { hitsurface = vec ( 0 , 0 , 0) ; hitsurface [ c l o s e s t ] = ray [ c l o s e s t]>0 ? −1 : 1; } return min ( dent , d i s t ) ; }

#define CHECKINSIDEWORLD \ i f ( ! insideworld ( o ) ) \ { \ f l o a t disttoworld = 0 , exitworld = 1e16f ; \ loopi ( 3 ) \ { \ float c = v [ i ] ; \ i f ( c=worldsize ) \ { \ f l o a t d = ( ( invray [ i ]>0?0: worldsize )−c )∗invray [ i ] ; \ i f ( d0?radius:−1) ; \ disttoworld = max( disttoworld , 0.1 f + d ) ; \ } \ f l o a t e = ( ( invray [ i ]>0?worldsize : 0 )−c )∗invray [ i ] ; \ exitworld = min ( exitworld , e ) ; \ } \ i f ( disttoworld > exitworld ) return ( radius>0?radius:−1) ; \ v . add ( vec ( ray ) . mul ( disttoworld ) ) ; \ d i s t += disttoworld ; \ }

i v e c l o ( x&(˜0ext−>ents && l s h i f t < e l v l ) \ { \ f l o a t e d i s t = d i s t t o e n t ( lc−>ext−>ents , oclast , o , ray , radius , mode, t ) ; \ i f ( e d i s t < 1e15f ) \ { \ i f ( e a r l y e x i t ) return min ( edist , d i s t ) ; \

} // optimized version f o r lightmap shadowing . . . every c y c l e here counts ! ! ! f l o a t shadowray ( const vec &o , const vec &ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t ) { INITRAYCUBE ; CHECKINSIDEWORLD; i n t side = O BOTTOM, x = i n t ( v . x ) , y = i n t ( v . y ) , z = i n t ( v . z ) ; for ( ; ; ) { DOWNOCTREE( shadowent , true ) ;

engine/physics.cpp

f l o a t d i s t = raycube ( o , ray , radius , mode, s i z e ) ; i f ( ( mode&RAY ENTS ) == RAY ENTS ) { f l o a t dent = d i s t t o o u t s i d e e n t ( o , ray , d i s t < 0 ? 1e16f : dist , mode , NULL) ; i f ( dent < 1e15f && ( d i s t < 0 || dent < d i s t ) ) d i s t = dent ; } orient = hitorient ; ent = h i t e n t d i s t == d i s t ? h i t e n t : −1; return d i s t ;

cube &c = ∗l c ; i v e c l o ( x&(˜0version = 1; } } f l o a t shadowray ( ShadowRayCache ∗cache , const vec &o , const vec &ray , f l o a t radius , i n t mode, e x t e n t i t y ∗t ) { INITRAYCUBE ; CHECKINSIDEWORLD; i n t side = O BOTTOM, x = i n t ( v . x ) , y = i n t ( v . y ) , z = i n t ( v . z ) ; for ( ; ; ) { DOWNOCTREE( shadowent , true ) ; cube &c = ∗l c ; i v e c l o ( x&(˜0=0 || above>=0) return true ; vec yo ( d−>o ) ; yo . sub ( o ) ; yo . rotate around z(−yaw∗RAD) ; yo . sub ( center ) ; f l o a t dx = clamp ( yo . x , −xr , xr ) − yo . x , dy = clamp ( yo . y , −yr , yr ) − yo .y, d i s t = s q r t f ( dx∗dx + dy∗dy ) − d−>radius ; i f ( d i s t < 0) { i n t sx = yo . x = xr ? 1 : 0) , sy = yo . y = yr ? 1 : 0) ; i f ( d i s t > ( yo . z < 0 ? below : above ) && ( sx || sy ) ) { vec y d i r ( d i r ) ; y d i r . rotate around z(−yaw∗RAD) ; i f ( sx∗yo . x − xr > sy∗yo . y − yr ) { i f ( d i r . i s z e r o ( ) || sx∗y d i r . x < −1e−6f ) { wall = vec ( sx , 0 , 0) ; wall . rotate around z ( yaw∗RAD) ; return f a l s e ; } } e l s e i f ( d i r . i s z e r o ( ) || sy∗y d i r . y < −1e−6f ) { wall = vec ( 0 , sy , 0) ; wall . rotate around z ( yaw∗RAD) ; return f a l s e ;

360

Foundations of Videogame Programming Code Repository

} } i f ( yo . z < 0) { i f ( d i r . i s z e r o ( ) || ( d i r . z > 0 && ( d−>type>=ENT INANIMATE || below >= d−>zmargin−(d−>eyeheight+d−>aboveeye ) /4.0 f ) ) ) { wall = vec ( 0 , 0 , −1) ; return f a l s e ; } } e l s e i f ( d i r . i s z e r o ( ) || ( d i r . z < 0 && ( d−>type>=ENT INANIMATE || above >= d−>zmargin−(d−>eyeheight+d−>aboveeye ) /3.0 f ) ) ) { wall = vec ( 0 , 0 , 1) ; return f a l s e ; } inside = true ; } return true ; } bool e l l i p s e c o l l i d e ( physent ∗d , const vec &dir , const vec &o , const vec & center , f l o a t yaw , f l o a t xr , f l o a t yr , f l o a t hi , f l o a t l o )

az az inside return

>= d−>zmargin−(d−>eyeheight+d−>aboveeye ) /4.0 f , >= d−>zmargin−(d−>eyeheight+d−>aboveeye ) /3.0 f ) ; = true ; true ;

} #define DYNENTCACHESIZE 1024 s t a t i c uint dynentframe = 0; s t a t i c struct dynentcacheentry { int x , y ; uint frame ; vector dynents ; } dynentcache [DYNENTCACHESIZE ] ; void cleardynentcache ( ) { dynentframe ++; i f ( ! dynentframe || dynentframe == 1) l o o p i (DYNENTCACHESIZE) dynentcache [ i ] . frame = 0; i f ( ! dynentframe ) dynentframe = 1; }

{ f l o a t below = ( o . z+center . z−l o ) − ( d−>o . z+d−>aboveeye ) , above = ( d−>o . z−d−>eyeheight ) − ( o . z+center . z+hi ) ; i f ( below>=0 || above>=0) return true ; vec yo ( center ) ; yo . rotate around z ( yaw∗RAD) ; yo . add ( o ) ; f l o a t x = yo . x − d−>o . x , y = yo . y − d−>o . y ; f l o a t angle = atan2f ( y , x ) , dangle = angle−(d−>yaw+90)∗RAD, eangle = angle−(yaw+90)∗RAD; f l o a t dx = d−>xradius∗cosf ( dangle ) , dy = d−>yradius∗s i n f ( dangle ) ; f l o a t ex = xr∗cosf ( eangle ) , ey = yr∗s i n f ( eangle ) ; f l o a t d i s t = s q r t f ( x∗x + y∗y ) − s q r t f ( dx∗dx + dy∗dy ) − s q r t f ( ex∗ex + ey∗ey ) ; i f ( d i s t < 0) { i f ( d i s t > ( d−>o . z < yo . z ? below : above ) && ( d i r . i s z e r o ( ) || x∗d i r . x + y∗d i r . y > 0) ) { wall = vec(−x , −y , 0) ; i f ( ! wall . i s z e r o ( ) ) wall . normalize ( ) ; return f a l s e ; } i f ( d−>o . z < yo . z ) { i f ( d i r . i s z e r o ( ) || ( d i r . z > 0 && ( d−>type>=ENT INANIMATE || below >= d−>zmargin−(d−>eyeheight+d−>aboveeye ) /4.0 f ) ) ) { wall = vec ( 0 , 0 , −1) ; return f a l s e ; } } e l s e i f ( d i r . i s z e r o ( ) || ( d i r . z < 0 && ( d−>type>=ENT INANIMATE || above >= d−>zmargin−(d−>eyeheight+d−>aboveeye ) /3.0 f ) ) ) { wall = vec ( 0 , 0 , 1) ; return f a l s e ; } inside = true ; } return true ; } bool r e c t c o l l i d e ( physent ∗d , const vec &dir , const vec &o , f l o a t xr , f l o a t yr , f l o a t hi , f l o a t lo , uchar v i s i b l e = 0xFF )

VARF( dynentsize , 4 , 7 , 12, cleardynentcache ( ) ) ; #define DYNENTHASH( x , y ) ( ( ( ( ( x ) ˆ ( y ) )5)) & ( DYNENTCACHESIZE − 1) ) const vector &checkdynentcache ( i n t x , i n t y ) { dynentcacheentry &dec = dynentcache [DYNENTHASH( x , y ) ] ; i f ( dec . x == x && dec . y == y && dec . frame == dynentframe ) return dec . dynents ; dec . x = x ; dec . y = y ; dec . frame = dynentframe ; dec . dynents . shrink ( 0 ) ; i n t numdyns = game : : numdynents ( ) , dsize = 1= dx+dsize || d−>o . y+d−>radius o . y−d−>radius >= dy+dsize ) continue ; dec . dynents . add ( d ) ; } return dec . dynents ; } #define loopdynentcache ( curx , cury , o , radius ) \ f o r ( i n t curx = max( i n t ( o . x−radius ) , 0)>>dynentsize , endx +radius ) , worldsize −1)>>dynentsize ; curx >dynentsize , endy +radius ) , worldsize −1)>>dynentsize ; cury o , d−>radius ) { dynentcacheentry &dec = dynentcache [DYNENTHASH( x , y ) ] ; i f ( dec . x ! = x || dec . y ! = y || dec . frame ! = dynentframe || dec . dynents . f i n d ( d ) >= 0) continue ; dec . dynents . add ( d ) ; } }

{ vec s ( d−>o ) ; s . sub ( o ) ; f l o a t dxr = d−>c o l l i d e t y p e ==COLLIDE AABB ? d−>xradius : d−>radius , dyr = d−>c o l l i d e t y p e ==COLLIDE AABB ? d−>yradius : d−>radius ; xr += dxr ; yr += dyr ; f l o a t zr = s . z>0 ? d−>eyeheight+hi : d−>aboveeye+ l o ; f l o a t ax = fabs ( s . x )−xr ; f l o a t ay = fabs ( s . y )−yr ; f l o a t az = fabs ( s . z )−zr ; i f ( ax>0 || ay>0 || az>0) return true ; wall . x = wall . y = wall . z = 0; #define TRYCOLLIDE( dim , ON, OP, N, P ) \ { \ i f ( s . dim=ENT INANIMATE || (N) ) ) ) ) { wall . dim = −1; return false ; } } \ e l s e i f ( v i s i b l e&(1ay && ax>az ) TRYCOLLIDE( x , O LEFT , O RIGHT, ax > −dxr , ax > −dxr ); i f ( ay>az ) TRYCOLLIDE( y , O BACK, O FRONT, ay > −dyr , ay > −dyr ) ; TRYCOLLIDE( z , O BOTTOM, O TOP,

bool overlapsdynent ( const vec &o , f l o a t radius ) { loopdynentcache ( x , y , o , radius ) { const vector &dynents = checkdynentcache ( x , y ) ; loopv ( dynents ) { physent ∗d = dynents [ i ] ; i f ( o . d i s t ( d−>o )−d−>radius < radius ) return true ; } } return f a l s e ; } template s t a t i c i n l i n e bool p l c o l l i d e ( physent ∗d , const vec &dir , physent ∗o ) { E entvol ( d ) ; O obvol ( o ) ; vec cp ; i f ( mpr : : c o l l i d e ( entvol , obvol , NULL, NULL, &cp ) ) { vec wn = vec ( cp ) . sub ( obvol . center ( ) ) ; wall = obvol . contactface (wn, d i r . i s z e r o ( ) ? vec (wn) . neg ( ) : d i r ) ;

engine/physics.cpp i f ( ! wall . i s z e r o ( ) ) return f a l s e ; inside = true ; } return true ;

vec center , radius ; m−>c o l l i s i o n b o x ( 0 , center , radius ) ; f l o a t yaw = e . a t t r 1 ; switch ( d−>c o l l i d e t y p e ) { case COLLIDE ELLIPSE : i f (m−>e l l i p s e c o l l i d e ) { // i f ( ! mmcollide(d , dir , e , center , radius , yaw ) ) return f a l s e ; i f ( ! e l l i p s e c o l l i d e ( d , dir , e . o , center , yaw , radius . x , radius . y , radius . z , radius . z ) ) return f a l s e ; } // e l s e i f ( ! mmcollide(d , dir , e , center , radius , yaw ) ) return f a l s e ; e l s e i f ( ! e l l i p s e r e c t c o l l i d e ( d , dir , e . o , center , yaw , radius . x , radius . y , radius . z , radius . z ) ) return f a l s e ; break ; case COLLIDE OBB: i f (m−>e l l i p s e c o l l i d e ) { i f ( ! mmcollide(d , dir , e , center , radius , yaw ) ) return f a l s e ; } e l s e i f ( ! mmcollide(d , dir , e , center , radius , yaw ) ) return f a l s e ; break ; case COLLIDE AABB: default : rotatebb ( center , radius , e . a t t r 1 ) ; i f ( ! r e c t c o l l i d e ( d , dir , center . add ( e . o ) , radius . x , radius . y , radius . z , radius . z ) ) return f a l s e ; break ; }

} bool p l c o l l i d e ( physent ∗d , const vec &d i r ) monster

// c o l l i d e with player or

{ i f ( d−>type==ENT CAMERA || d−>s t a t e ! = CS ALIVE ) return true ; loopdynentcache ( x , y , d−>o , d−>radius ) { const vector &dynents = checkdynentcache ( x , y ) ; loopv ( dynents ) { physent ∗o = dynents [ i ] ; i f ( o==d || d−>o . r e j e c t ( o−>o , d−>radius+o−>radius ) ) continue ; switch ( d−>c o l l i d e t y p e ) { case COLLIDE ELLIPSE : i f ( o−>c o l l i d e t y p e == COLLIDE ELLIPSE ) { i f ( e l l i p s e c o l l i d e ( d , dir , o−>o , vec ( 0 , 0 , 0) , o−>yaw , o −>xradius , o−>yradius , o−>aboveeye , o−>eyeheight ) ) continue ; } e l s e i f ( e l l i p s e r e c t c o l l i d e ( d , dir , o−>o , vec ( 0 , 0 , 0) , o−> yaw , o−>xradius , o−>yradius , o−>aboveeye , o−> eyeheight ) ) continue ; break ; case COLLIDE OBB: i f ( o−>c o l l i d e t y p e == COLLIDE ELLIPSE ) { i f ( p l c o l l i d e(d , dir , o ) ) continue ; } e l s e i f ( p l c o l l i d e(d , dir , o ) ) continue ; break ; case COLLIDE AABB: default : i f ( r e c t c o l l i d e ( d , dir , o−>o , o−>c o l l i d e t y p e == COLLIDE AABB ? o−>xradius : o−>radius , o−> c o l l i d e t y p e == COLLIDE AABB ? o−>yradius : o−> radius , o−>aboveeye , o−>eyeheight ) ) continue ; break ; } hitplayer = o ; game : : dynentcollide ( d , o , wall ) ; return f a l s e ; } } return true ;

} return true ; } template s t a t i c bool f u z z y c o l l i d e s o l i d ( physent ∗d , const vec &dir , f l o a t c u t o f f , const cube &c , const i v e c &co , i n t s i z e ) // c o l l i d e with s o l i d cube geometry { i n t crad = s i z e /2; i f ( fabs ( d−>o . x − co . x − crad ) > d−>radius + crad || fabs ( d−>o . y − co . y − crad ) > d−>radius + crad || d−>o . z + d−>aboveeye < co . z || d−>o . z − d−>eyeheight > co . z + s i z e ) return true ; E entvol ( d ) ; wall = vec ( 0 , 0 , 0) ; f l o a t b e s t d i s t = −1e10f ; i n t v i s i b l e = i s e n t i r e l y s o l i d ( c ) ? c . v i s i b l e : 0xFF ; l o o p i ( 6 ) i f ( v i s i b l e&(1typezmargin−(d−>eyeheight+d−>aboveeye ) / ( d i r . z < 0 ? 3.0 f : 4.0 f ) : ( ( d i r . x∗w. x < 0 || d i r . y∗w. y < 0) ? −d−>radius : 0) ) ) continue ; } wall = w; bestdist = dist ; } i f ( wall . i s z e r o ( ) ) { inside = true ; return true ; } return f a l s e ;

} void rotatebb ( vec ¢er , vec &radius , i n t yaw ) { i f ( yaw < 0) yaw = 360 + yaw%360; e l s e i f ( yaw >= 360) yaw %= 360; const vec2 &r o t = sincos360 [ yaw ] ; vec2 oldcenter ( center ) , oldradius ( radius ) ; center . x = oldcenter . x∗r o t . x − oldcenter . y∗r o t . y ; center . y = oldcenter . y∗r o t . x + oldcenter . x∗r o t . y ; radius . x = fabs ( oldradius . x∗r o t . x ) + fabs ( oldradius . y∗r o t . y ) ; radius . y = fabs ( oldradius . y∗r o t . x ) + fabs ( oldradius . x∗r o t . y ) ; } template s t a t i c i n l i n e bool mmcollide ( physent ∗d , const vec &dir , const e x t e n t i t y &e , const vec ¢er , const vec &radius , f l o a t yaw ) { E entvol ( d ) ; M mdlvol ( e . o , center , radius , yaw ) ; vec cp ; i f ( mpr : : c o l l i d e ( entvol , mdlvol , NULL, NULL, &cp ) ) { vec wn = vec ( cp ) . sub ( mdlvol . center ( ) ) ; wall = mdlvol . contactface (wn, d i r . i s z e r o ( ) ? vec (wn) . neg ( ) : d i r ) ; i f ( ! wall . i s z e r o ( ) ) return f a l s e ; inside = true ; } return true ; } bool mmcollide ( physent ∗d , const vec &dir , o c t a e n t i t i e s &oc ) // c o l l i d e with a mapmodel { const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( oc . mapmodels ) { e x t e n t i t y &e = ∗ents [ oc . mapmodels [ i ] ] ; i f ( e . f l a g s&e x t e n t i t y : : F NOCOLLIDE ) continue ; model ∗m = loadmodel (NULL, e . a t t r 2 ) ; i f ( !m || !m−>c o l l i d e ) continue ;

361

} template s t a t i c i n l i n e bool clampcollide ( const clipplanes &p , const E &entvol , const plane &w, const vec &pw) { i f (w. x && (w. y || w. z ) && fabs (pw. x − p . o . x ) > p . r . x ) { vec c = entvol . center ( ) ; f l o a t f v = pw. x < p . o . x ? p . o . x−p . r . x : p . o . x+p . r . x , f d i s t = (w. x∗ f v + w. y∗c . y + w. z∗c . z + w. o f f s e t ) / (w. y∗w. y + w. z∗w. z ) ; vec f d i r ( f v − c . x , −w. y∗f d i s t , −w. z∗f d i s t ) ; i f ( ( pw. y−c . y−f d i r . y )∗w. y + (pw. z−c . z−f d i r . z )∗w. z >= 0 && entvol .

362

Foundations of Videogame Programming Code Repository

supportpoint ( f d i r ) . squaredist ( c ) < f d i r . squaredlen ( ) ) return true ; } i f (w. y && (w. x || w. z ) && fabs (pw. y − p . o . y ) > p . r . y ) { vec c = entvol . center ( ) ; f l o a t f v = pw. y < p . o . y ? p . o . y−p . r . y : p . o . y+p . r . y , f d i s t = (w. x∗c . x + w. y∗f v + w. z∗c . z + w. o f f s e t ) / (w. x∗w. x + w. z∗w. z ) ; vec f d i r(−w. x∗f d i s t , f v − c . y , −w. z∗f d i s t ) ; i f ( ( pw. x−c . x−f d i r . x )∗w. x + (pw. z−c . z−f d i r . z )∗w. z >= 0 && entvol . supportpoint ( f d i r ) . squaredist ( c ) < f d i r . squaredlen ( ) ) return true ; } i f (w. z && (w. x || w. y ) && fabs (pw. z − p . o . z ) > p . r . z ) { vec c = entvol . center ( ) ; f l o a t f v = pw. z < p . o . z ? p . o . z−p . r . z : p . o . z+p . r . z , f d i s t = (w. x∗c . x + w. y∗c . y + w. z∗f v + w. o f f s e t ) / (w. x∗w. x + w. y∗w. y ) ; vec f d i r(−w. x∗f d i s t , −w. y∗f d i s t , f v − c . z ) ; i f ( ( pw. x−c . x−f d i r . x )∗w. x + (pw. y−c . y−f d i r . y )∗w. y >= 0 && entvol . supportpoint ( f d i r ) . squaredist ( c ) < f d i r . squaredlen ( ) ) return true ; } return f a l s e ;

} template s t a t i c bool c u b e c o l l i d e s o l i d ( physent ∗d , const vec &dir , f l o a t c u t o f f , const cube &c , const i v e c &co , i n t s i z e ) // c o l l i d e with s o l i d cube geometry { i n t crad = s i z e /2; i f ( fabs ( d−>o . x − co . x − crad ) > d−>radius + crad || fabs ( d−>o . y − co . y − crad ) > d−>radius + crad || d−>o . z + d−>aboveeye < co . z || d−>o . z − d−>eyeheight > co . z + s i z e ) return true ; E entvol ( d ) ; bool c o l l i d e d = mpr : : c o l l i d e ( mpr : : SolidCube ( co , s i z e ) , entvol ) ; i f ( ! c o l l i d e d ) return true ; wall = vec ( 0 , 0 , 0) ; f l o a t b e s t d i s t = −1e10f ; i n t v i s i b l e = i s e n t i r e l y s o l i d ( c ) ? c . v i s i b l e : 0xFF ; l o o p i ( 6 ) i f ( v i s i b l e&(1eyeheight+d−>aboveeye ) / ( d i r . z < 0 ? 3.0 f : 4.0 f ) : ( ( d i r . x∗w. x < 0 || d i r . y∗w. y < 0) ? −d−>radius : 0) ) ) continue ; } wall = w; bestdist = dist ; } i f ( wall . i s z e r o ( ) ) { inside = true ; return true ; } return f a l s e ;

} template s t a t i c bool f u z z y c o l l i d e p l a n e s ( physent ∗d , const vec &dir , f l o a t c u t o f f , const cube &c , const i v e c &co , i n t s i z e ) // c o l l i d e with deformed cube geometry { const clipplanes &p = g e t c l i p p l a n e s ( c , co , s i z e ) ; i f ( fabs ( d−>o . x − p . o . x ) > p . r . x + d−>radius || fabs ( d−>o . y − p . o . y ) > p . r . y + d−>radius || d−>o . z + d−>aboveeye < p . o . z − p . r . z || d−>o . z − d−>eyeheight > p . o . z + p. r . z ) return true ; E entvol ( d ) ; wall = vec ( 0 , 0 , 0) ; f l o a t b e s t d i s t = −1e10f ; l o o p i ( 6 ) i f ( p . v i s i b l e&(1typezmargin−(d−>eyeheight+d−>aboveeye ) / ( d i r . z < 4.0 f ) : ( ( d i r . x∗w. x < 0 || d i r . y∗w. y < 0) ? −d−>radius continue ; } wall = w; bestdist = dist ; } i n t bestplane = −1; loopi (p. size ) { const plane &w = p . p [ i ] ; vec pw = entvol . supportpoint ( vec (w) . neg ( ) ) ; f l o a t d i s t = w. d i s t (pw) ; i f ( d i s t >= 0) return true ; i f ( d i s t = −c u t o f f∗d i r . magnitude ( ) ) continue ; i f ( d−>typezmargin−(d−>eyeheight+d−>aboveeye ) / ( d i r . z < 4.0 f ) : ( ( d i r . x∗w. x < 0 || d i r . y∗w. y < 0) ? −d−>radius continue ; } i f ( clampcollide ( p , entvol , w, pw) ) continue ; bestplane = i ; } i f ( bestplane >= 0) wall = p . p [ bestplane ] ; e l s e i f ( wall . i s z e r o ( ) ) { inside = true ; return true ; } return f a l s e ;

} template s t a t i c bool cubecollideplanes ( physent ∗d , const vec &dir , f l o a t c u t o f f , const cube &c , const i v e c &co , i n t s i z e ) // c o l l i d e with deformed cube geometry { const clipplanes &p = g e t c l i p p l a n e s ( c , co , s i z e ) ; 0 ? 3.0 f : : 0) ) )

i f ( fabs ( d−>o . x − p . o . x ) > p . r . x + d−>radius || fabs ( d−>o . y − p . o . y ) > p . r . y + d−>radius || d−>o . z + d−>aboveeye < p . o . z − p . r . z || d−>o . z − d−>eyeheight > p . o . z + p. r . z ) return true ; E entvol ( d ) ; bool c o l l i d e d = mpr : : c o l l i d e ( mpr : : CubePlanes ( p ) , entvol ) ; i f ( ! c o l l i d e d ) return true ;

0 ? 3.0 f : : 0) ) )

wall = vec ( 0 , 0 , 0) ; f l o a t b e s t d i s t = −1e10f ; l o o p i ( 6 ) i f ( p . v i s i b l e&(1eyeheight+d−>aboveeye ) / ( d i r . z < 0 ? 3.0 f : 4.0 f ) : ( ( d i r . x∗w. x < 0 || d i r . y∗w. y < 0) ? −d−>radius : 0) ) ) continue ; } wall = w; bestdist = dist ; } i n t bestplane = −1; loopi (p. size ) { const plane &w = p . p [ i ] ; vec pw = entvol . supportpoint ( vec (w) . neg ( ) ) ;

engine/physics.cpp f l o a t d i s t = w. d i s t (pw) ; i f ( d i s t = −c u t o f f∗d i r . magnitude ( ) ) continue ; i f ( d−>typezmargin−(d−>eyeheight+d−>aboveeye ) / ( d i r . z < 0 ? 3.0 f : 4.0 f ) : ( ( d i r . x∗w. x < 0 || d i r . y∗w. y < 0) ? −d−>radius : 0) ) ) continue ; } i f ( clampcollide ( p , entvol , w, pw) ) continue ; bestplane = i ; } i f ( bestplane >= 0) wall = p . p [ bestplane ] ; e l s e i f ( wall . i s z e r o ( ) ) { inside = true ; return true ; } return f a l s e ;

363

i v e c o ( i , cor . x , cor . y , cor . z , s i z e ) ; i f ( c [ i ] . children ) { i f ( ! o c t a c o l l i d e ( d , dir , c u t o f f , bo , bs , c [ i ] . children , o , size >>1)) return f a l s e ; } else { bool s o l i d = f a l s e ; switch ( c [ i ] . material&MATF CLIP ) { case MAT NOCLIP : continue ; case MAT GAMECLIP: i f ( d−>type==ENT AI ) s o l i d = true ; break ; case MAT CLIP : i f ( i s c l i p p e d ( c [ i ] . material&MATF VOLUME) || d−> typec o l l i d e t y p e ) { case COLLIDE AABB: i f ( i s e n t i r e l y s o l i d ( c ) || s o l i d ) { i f ( c u t o f f ents ) i f ( ! mmcollide ( d , dir , ∗c [ i ] . ext−> ents ) ) return f a l s e ;

i n t d i f f = ( bo . x ˆ ( bo . x+bs . x ) ) | ( bo . y ˆ ( bo . y+bs . y ) ) | ( bo . z ˆ ( bo . z+bs . z ) ), scale = worldscale−1; i f ( d i f f &˜((1>1); const cube ∗c = &worldroot [ octastep ( bo . x , bo . y , bo . z , scale ) ] ; i f ( c−>ext && c−>ext−>ents && ! mmcollide ( d , dir , ∗c−>ext−>ents ) ) return false ; scale−−; while ( c−>children && ! ( d i f f&(1ext && c−>ext−>ents && ! mmcollide ( d , dir , ∗c−>ext−>ents ) ) return f a l s e ; scale−−; } i f ( c−>children ) return o c t a c o l l i d e ( d , dir , c u t o f f , bo , bs , c−>children , i v e c ( bo ) .mask(˜((2material&MATF VOLUME) || d−>type< ENT CAMERA) s o l i d = true ; break ; } i f ( ! s o l i d && isempty (∗c ) ) return true ; i n t c s i z e = 2radius ) , i n t ( d−>o . y−d−>radius ) , i n t ( d−>o . z−d−> eyeheight ) ) , bs ( i n t ( d−>radius ∗2) , i n t ( d−>radius ∗2) , i n t ( d−>eyeheight+d−> aboveeye ) ) ; bs . add ( 2 ) ; // guard space f o r rounding errors i f ( ! o c t a c o l l i d e ( d , dir , c u t o f f , bo , bs ) ) return f a l s e ;// , worldroot , i v e c ( 0 , 0 , 0) , worldsize>>1)) return f a l s e ; // c o l l i d e with world return ! p l a y e r c o l || p l c o l l i d e ( d , d i r ) ; } void r e c a l c d i r ( physent ∗d , const vec &oldvel , vec &d i r ) { f l o a t speed = o l d v e l . magnitude ( ) ; i f ( speed > 1e−6f ) { f l o a t step = d i r . magnitude ( ) ; d i r = d−>v e l ; d i r . add ( d−>f a l l i n g ) ; d i r . mul ( step/speed ) ; } } void s l i d e a g a i n s t ( physent ∗d , vec &dir , const vec &obstacle , bool foundfloor , bool s l i d e c o l l i d e ) { vec wall ( obstacle ) ;

364

Foundations of Videogame Programming Code Repository {

i f ( foundfloor ? wall . z > 0 : s l i d e c o l l i d e ) { wall . z = 0; i f ( ! wall . i s z e r o ( ) ) wall . normalize ( ) ; } vec o l d v e l ( d−>v e l ) ; o l d v e l . add ( d−>f a l l i n g ) ; d−>v e l . p r o j e c t ( wall ) ; d−>f a l l i n g . p r o j e c t ( wall ) ; r e c a l c d i r ( d , oldvel , d i r ) ;

d−>timeinair = 0; d−>f l o o r = f l o o r ; s w i t c h f l o o r ( d , dir , d−>f l o o r ) ; } d−>physstate = PHYS STEP UP ; return true ; } } }

} /∗ t r y stepping up ∗/ d−>o = old ; d−>o . z += d i r . magnitude ( ) ; i f ( c o l l i d e ( d , vec ( 0 , 0 , 1) ) ) { i f ( d−>physstate == PHYS FALL || d−>f l o o r ! = f l o o r ) { d−>timeinair = 0; d−>f l o o r = f l o o r ; s w i t c h f l o o r ( d , dir , d−>f l o o r ) ; } i f ( cansmooth ) d−>physstate = PHYS STEP UP ; return true ; } d−>o = old ; return f a l s e ;

void s w i t c h f l o o r ( physent ∗d , vec &dir , const vec &f l o o r ) { i f ( f l o o r . z >= FLOORZ) d−>f a l l i n g = vec ( 0 , 0 , 0) ; vec o l d v e l ( d−>v e l ) ; o l d v e l . add ( d−>f a l l i n g ) ; i f ( d i r . dot ( f l o o r ) >= 0) { i f ( d−>physstate < PHYS SLIDE || fabs ( d i r . dot ( d−>f l o o r ) ) > 0.01 f∗d i r . magnitude ( ) ) return ; d−>v e l . projectxy ( f l o o r , 0.0 f ) ; } e l s e d−>v e l . projectxy ( f l o o r ) ; d−>f a l l i n g . p r o j e c t ( f l o o r ) ; r e c a l c d i r ( d , oldvel , d i r ) ; }

}

bool trystepup ( physent ∗d , vec &dir , const vec &obstacle , f l o a t maxstep , const vec &f l o o r ) { vec old ( d−>o ) , s t a i r d i r = ( obstacle . z >= 0 && obstacle . z < SLOPEZ ? vec(−obstacle . x , −obstacle . y , 0) : vec ( d i r . x , d i r . y , 0) ) . rescale ( 1 ) ; bool cansmooth = true ; /∗ check i f there i s space atop the s t a i r to move to ∗/ i f ( d−>physstate ! = PHYS STEP UP ) { vec checkdir = s t a i r d i r ; checkdir . mul( 0 . 1 f ) ; checkdir . z += maxstep + 0.1 f ; d−>o . add ( checkdir ) ; i f ( ! collide (d) ) { d−>o = old ; i f ( c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) return f a l s e ; cansmooth = f a l s e ; } }

bool trystepdown ( physent ∗d , vec &dir , f l o a t step , f l o a t xy , f l o a t z , bool i n i t = f a l s e ) { vec s t e p d i r ( d i r . x , d i r . y , 0) ; s t e p d i r . z = −s t e p d i r . magnitude2 ( ) ∗z/xy ; i f ( ! s t e p d i r . z ) return f a l s e ; s t e p d i r . normalize ( ) ; vec old ( d−>o ) ; d−>o . add ( vec ( s t e p d i r ) . mul ( STAIRHEIGHT/fabs ( s t e p d i r . z ) ) ) . z −= STAIRHEIGHT; d−>zmargin = −STAIRHEIGHT; i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) { d−>o = old ; d−>o . add ( vec ( s t e p d i r ) . mul ( step ) ) ; d−>zmargin = 0; i f ( c o l l i d e ( d , vec ( 0 , 0 , −1) ) ) { vec s t e p f l o o r ( s t e p d i r ) ; s t e p f l o o r . mul(−s t e p f l o o r . z ) . z += 1; s t e p f l o o r . normalize ( ) ; i f ( d−>physstate >= PHYS SLOPE && d−>f l o o r ! = s t e p f l o o r ) { // prevent a l t e r n a t i n g step−down/step−up s t a t e s i f player would keep bumping i n t o the same f l o o r vec stepped ( d−>o ) ; d−>o . z −= 0.5 f ; d−>zmargin = −0.5f ; i f ( ! c o l l i d e ( d , s t e p d i r ) && wall == d−>f l o o r ) { d−>o = old ; i f ( ! i n i t ) { d−>o . x += d i r . x ; d−>o . y += d i r . y ; i f ( d i r . z o . z += d i r . z ; } d−>zmargin = 0; d−>physstate = PHYS STEP DOWN; d−>timeinair = 0; return true ; } d−>o = i n i t ? old : stepped ; d−>zmargin = 0; } e l s e i f ( i n i t ) d−>o = old ; s w i t c h f l o o r ( d , dir , s t e p f l o o r ) ; d−>f l o o r = s t e p f l o o r ; d−>physstate = PHYS STEP DOWN; d−>timeinair = 0; return true ; } } d−>o = old ; d−>zmargin = 0; return f a l s e ;

i f ( cansmooth ) { vec checkdir = s t a i r d i r ; checkdir . z += 1; checkdir . mul ( maxstep ) ; d−>o = old ; d−>o . add ( checkdir ) ; i n t scale = 2; i f ( ! c o l l i d e ( d , checkdir ) ) { i f ( c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) { d−>o = old ; return f a l s e ; } d−>o . add ( checkdir ) ; i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) scale = 1; } i f ( scale ! = 1) { d−>o = old ; d−>o . sub ( checkdir . mul ( vec ( 2 , 2 , 1) ) ) ; i f ( c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) scale = 1; } d−>o = old ; vec smoothdir ( d i r . x , d i r . y , 0) ; f l o a t magxy = smoothdir . magnitude ( ) ; i f ( magxy > 1e−9f ) { i f ( magxy > scale∗d i r . z ) { smoothdir . mul(1/magxy ) ; smoothdir . z = 1.0 f /scale ; smoothdir . mul ( d i r . magnitude ( ) /smoothdir . magnitude ( ) ) ; } e l s e smoothdir . z = d i r . z ; d−>o . add ( smoothdir ) ; d−>o . z += maxstep + 0.1 f ; i f ( c o l l i d e ( d , smoothdir ) ) { d−>o . z −= maxstep + 0.1 f ; i f ( d−>physstate == PHYS FALL || d−>f l o o r ! = f l o o r )

} bool trystepdown ( physent ∗d , vec &dir , bool i n i t = f a l s e ) { i f ( ! game : : allowmove ( d ) || ( ! d−>move && ! d−>s t r a f e ) ) return f a l s e ; vec old ( d−>o ) ; d−>o . z −= STAIRHEIGHT; d−>zmargin = −STAIRHEIGHT; i f ( c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) { d−>o = old ; d−>zmargin = 0;

engine/physics.cpp return f a l s e ; } d−>o = old ; d−>zmargin = 0; f l o a t step = d i r . magnitude ( ) ; #if 1 // weaker check , j u s t enough to i f ( trystepdown ( d , dir , step , 4 , #e l s e i f ( trystepdown ( d , dir , step , 2 , i f ( trystepdown ( d , dir , step , 1 , i f ( trystepdown ( d , dir , step , 1 , #endif return f a l s e ; }

vec old ( d−>o ) ; bool c o l l i d e d = f a l s e , s l i d e c o l l i d e = f a l s e ; vec obstacle ; d−>o . add ( d i r ) ; i f ( ! c o l l i d e ( d , d i r ) || ( ( d−>type==ENT AI || d−>type==ENT INANIMATE ) && ! c o l l i d e ( d , vec ( 0 , 0 , 0) , 0 , f a l s e ) ) ) { obstacle = wall ; /∗ check to see i f there i s an obstacle that would prevent t h i s one from being used as a f l o o r ( or c e i l i n g bump) ∗/ i f ( d−>type==ENT PLAYER && ( ( wall . z>=SLOPEZ && d i r . z= 0) s l i d e c o l l i d e = true ; obstacle = wall ; } d−>o = old ; d−>o . z −= STAIRHEIGHT; d−>zmargin = −STAIRHEIGHT; i f ( d−>physstate == PHYS SLOPE || d−>physstate == PHYS FLOOR || ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) && ( d−>physstate== PHYS STEP UP || d−>physstate==PHYS STEP DOWN || wall . z>= FLOORZ) ) ) { d−>o = old ; d−>zmargin = 0; i f ( trystepup ( d , dir , obstacle , STAIRHEIGHT, d−>physstate == PHYS SLOPE || d−>physstate == PHYS FLOOR ? d−>f l o o r : vec ( wall ) ) ) return true ; } else { d−>o = old ; d−>zmargin = 0; } /∗ can ’ t step over the obstacle , so j u s t s l i d e against i t ∗/ c o l l i d e d = true ; } e l s e i f ( d−>physstate == PHYS STEP UP ) { i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) { d−>o = old ; i f ( trystepup ( d , dir , vec ( 0 , 0 , 1) , STAIRHEIGHT, vec ( wall ) ) ) return true ; d−>o . add ( d i r ) ; } } e l s e i f ( d−>physstate == PHYS STEP DOWN && d i r . dot ( d−>f l o o r ) o ) ; d−>o = old ; i f ( trystepdown ( d , d i r ) ) return true ; d−>o = moved ; } vec f l o o r ( 0 , 0 , 0) ; bool s l i d e = c o l l i d e d , found = f i n d f l o o r ( d , c o l l i d e d , obstacle , s l i d e , f l o o r ) ; i f ( s l i d e || ( ! c o l l i d e d && f l o o r . z > 0 && f l o o r . z < WALLZ) ) { s l i d e a g a i n s t ( d , dir , s l i d e ? obstacle : f l o o r , found , s l i d e c o l l i d e ) ; // i f ( d−>type == ENT AI || d−>type == ENT INANIMATE ) d−>blocked = true ; } i f ( found ) landing ( d , dir , f l o o r , c o l l i d e d ) ; e l s e f a l l i n g ( d , dir , f l o o r ) ; return ! c o l l i d e d ;

avoid hopping up slopes 1 , i n i t ) ) return true ; 1 , i n i t ) ) return true ; 1 , i n i t ) ) return true ; 2 , i n i t ) ) return true ;

void f a l l i n g ( physent ∗d , vec &dir , const vec &f l o o r ) { i f ( f l o o r . z > 0.0 f && f l o o r . z < SLOPEZ) { i f ( f l o o r . z >= WALLZ) s w i t c h f l o o r ( d , dir , f l o o r ) ; d−>timeinair = 0; d−>physstate = PHYS SLIDE ; d−>f l o o r = f l o o r ; } e l s e i f ( d−>physstate < PHYS SLOPE || d i r . dot ( d−>f l o o r ) > 0.01 f∗d i r . magnitude ( ) || ( f l o o r . z ! = 0.0 f && f l o o r . z ! = 1.0 f ) || ! trystepdown ( d , dir , true ) ) d−>physstate = PHYS FALL ; } void landing ( physent ∗d , vec &dir , const vec &f l o o r , bool c o l l i d e d ) { #if 0 i f ( d−>physstate == PHYS FALL ) { d−>timeinair = 0; i f ( d i r . z < 0.0 f ) d i r . z = d−>v e l . z = 0.0 f ; } #endif s w i t c h f l o o r ( d , dir , f l o o r ) ; d−>timeinair = 0; i f ( ( d−>physstate ! =PHYS STEP UP && d−>physstate ! =PHYS STEP DOWN) || ! collided ) d−>physstate = f l o o r . z >= FLOORZ ? PHYS FLOOR : PHYS SLOPE; d−>f l o o r = f l o o r ; } bool f i n d f l o o r ( physent ∗d , bool c o l l i d e d , const vec &obstacle , bool & s l i d e , vec &f l o o r ) { bool found = f a l s e ; vec moved ( d−>o ) ; d−>o . z −= 0.1 f ; i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , d−>physstate == PHYS SLOPE || d−> physstate == PHYS STEP DOWN ? SLOPEZ : FLOORZ) ) { f l o o r = wall ; found = true ; } e l s e i f ( c o l l i d e d && obstacle . z >= SLOPEZ) { f l o o r = obstacle ; found = true ; slide = false ; } e l s e i f ( d−>physstate == PHYS STEP UP || d−>physstate == PHYS SLIDE ) { i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) ) && wall . z > 0.0 f ) { f l o o r = wall ; i f ( f l o o r . z >= SLOPEZ) found = true ; } } e l s e i f ( d−>physstate >= PHYS SLOPE && d−>f l o o r . z < 1.0 f ) { i f ( ! c o l l i d e ( d , vec ( d−>f l o o r ) . neg ( ) , 0.95 f ) || ! c o l l i d e ( d , vec ( 0 , 0 , −1) ) ) { f l o o r = wall ; i f ( f l o o r . z >= SLOPEZ && f l o o r . z < 1.0 f ) found = true ; } } i f ( c o l l i d e d && ( ! found || obstacle . z > f l o o r . z ) ) { f l o o r = obstacle ; s l i d e = ! found && ( f l o o r . z < WALLZ || f l o o r . z >= SLOPEZ) ; } d−>o = moved ; return found ; } bool move ( physent ∗d , vec &d i r ) {

365

} bool bounce ( physent ∗d , f l o a t secs , f l o a t e l a s t i c i t y , f l o a t w a t e r f r i c , f l o a t grav ) { // make sure bouncers don ’ t s t a r t inside geometry i f ( d−>physstate ! =PHYS BOUNCE && ! c o l l i d e ( d , vec ( 0 , 0 , 0) , 0 , f a l s e ) ) return true ; i n t mat = lookupmaterial ( vec ( d−>o . x , d−>o . y , d−>o . z + ( d−>aboveeye − d −>eyeheight ) /2) ) ; bool water = i s l i q u i d ( mat ) ; i f ( water ) { d−>v e l . z −= grav∗GRAVITY/16∗secs ; d−>v e l . mul (max( 1 . 0 f − secs/ w a t e r f r i c , 0.0 f ) ) ; } e l s e d−>v e l . z −= grav∗GRAVITY∗secs ; vec old ( d−>o ) ; loopi ( 2 ) { vec d i r ( d−>v e l ) ; d i r . mul ( secs ) ; d−>o . add ( d i r ) ; i f ( collide ( d, dir ) ) {

366

Foundations of Videogame Programming Code Repository i f ( inside ) { d−>o = old ; d−>v e l . mul(− e l a s t i c i t y ) ; } break ;

} e l s e i f ( h i t p l a y e r ) break ; d−>o = old ; game : : bounced ( d , wall ) ; f l o a t c = wall . dot ( d−>v e l ) , k = 1.0 f + ( 1 . 0 f−e l a s t i c i t y )∗c/d−>v e l . magnitude ( ) ; d−>v e l . mul ( k ) ; d−>v e l . sub ( vec ( wall ) . mul ( e l a s t i c i t y ∗2.0 f∗c ) ) ; } i f ( d−>physstate ! =PHYS BOUNCE) { // make sure bouncers don ’ t s t a r t inside geometry i f ( d−>o == old ) return ! h i t p l a y e r ; d−>physstate = PHYS BOUNCE; } return h i t p l a y e r ! = 0 ; } void a v o i d c o l l i s i o n ( physent ∗d , const vec &dir , physent ∗obstacle , f l o a t space ) { f l o a t rad = obstacle−>radius+d−>radius ; vec bbmin ( obstacle−>o ) ; bbmin . x −= rad ; bbmin . y −= rad ; bbmin . z −= obstacle−>eyeheight+d−>aboveeye ; bbmin . sub ( space ) ; vec bbmax( obstacle−>o ) ; bbmax. x += rad ; bbmax. y += rad ; bbmax. z += obstacle−>aboveeye+d−>eyeheight ; bbmax. add ( space ) ; l o o p i ( 3 ) i f ( d−>o [ i ] o [ i ] >= bbmax[ i ] ) return ; f l o a t mindist = 1e16f ; l o o p i ( 3 ) i f ( d i r [ i ] ! = 0) { f l o a t d i s t = ( ( d i r [ i ] > 0 ? bbmax[ i ] : bbmin [ i ] ) − d−>o [ i ] ) / d i r [ i ]; mindist = min ( mindist , d i s t ) ; } i f ( mindist >= 0.0 f && mindist < 1e15f ) d−>o . add ( vec ( d i r ) . mul ( mindist ) ) ;

d . eyeheight = height ; d . aboveeye = radius ; i f ( ! movecamera(&d , d . vel , worldsize , 1) ) { o = d.o; return true ; } return f a l s e ; } f l o a t dropheight ( e n t i t y &e ) { switch ( e . type ) { case ET PARTICLES : case ET MAPMODEL: return 0.0 f ; default : i f ( e . type >= ET GAMESPECIFIC ) return e n t i t i e s : : dropheight ( e ) ; return 4.0 f ; } } void dropenttofloor ( e n t i t y ∗e ) { d r o p t o f l o o r ( e−>o , 1.0 f , dropheight (∗e ) ) ; } void phystest ( ) { s t a t i c const char ∗s t a t e s [ ] = {” f l o a t ” , ” f a l l ” , ” s l i d e ” , ” slope ” , ” f l o o r ” , ” step up” , ” step down” , ” bounce”}; p r i n t f ( ” PHYS( pl ) : %s , a i r %d , f l o o r : (%f , %f , %f ) , v e l : (%f , %f , %f ) , g : (%f , %f , %f )\n” , s t a t e s [ player−>physstate ] , player−> timeinair , player−>f l o o r . x , player−>f l o o r . y , player−>f l o o r . z , player−>v e l . x , player−>v e l . y , player−>v e l . z , player−>f a l l i n g . x , player−>f a l l i n g . y , player−>f a l l i n g . z ) ; p r i n t f ( ” PHYS(cam) : %s , a i r %d , f l o o r : (%f , %f , %f ) , v e l : (%f , %f , %f ) , g : (%f , %f , %f )\n” , s t a t e s [ camera1−>physstate ] , camera1−> timeinair , camera1−>f l o o r . x , camera1−>f l o o r . y , camera1−>f l o o r . z , camera1−>v e l . x , camera1−>v e l . y , camera1−>v e l . z , camera1−> f a l l i n g . x , camera1−>f a l l i n g . y , camera1−>f a l l i n g . z ) ; } COMMAND( phystest , ” ” ) ; void vecfromyawpitch ( f l o a t yaw , f l o a t pitch , i n t move, i n t s t r a f e , vec &m ) { i f ( move ) { m. x = move∗−s i n f (RAD∗yaw ) ; m. y = move∗cosf (RAD∗yaw ) ; } e l s e m. x = m. y = 0;

} bool movecamera ( physent ∗pl , const vec &dir , f l o a t dist , f l o a t s t e p d i s t ) { i n t steps = ( i n t ) c e i l ( d i s t / s t e p d i s t ) ; i f ( steps o ) ; pl−>o . add ( d ) ; i f ( ! c o l l i d e ( pl , vec ( 0 , 0 , 0) , 0 , f a l s e ) ) { pl−>o = oldpos ; return f a l s e ; } } return true ; } bool d r o p t o f l o o r ( vec &o , f l o a t radius , f l o a t height ) { s t a t i c struct dropent : physent { dropent ( ) { type = ENT CAMERA; c o l l i d e t y p e = COLLIDE AABB; v e l = vec ( 0 , 0 , −1) ; } } d; d.o = o; i f ( ! insideworld ( d . o ) ) { i f ( d . o . z < worldsize ) return f a l s e ; d . o . z = worldsize − 1e−3f ; i f ( ! insideworld ( d . o ) ) return f a l s e ; } vec v (0.0001 f , 0.0001 f , −1) ; v . normalize ( ) ; i f ( raycube ( d . o , v , worldsize ) >= worldsize ) return f a l s e ; d . radius = d . xradius = d . yradius = radius ;

i f ( pitch ) { m. x ∗= cosf (RAD∗pitch ) ; m. y ∗= cosf (RAD∗pitch ) ; m. z = move∗s i n f (RAD∗pitch ) ; } e l s e m. z = 0; i f ( strafe ) { m. x += s t r a f e∗cosf (RAD∗yaw ) ; m. y += s t r a f e∗s i n f (RAD∗yaw ) ; } } void vectoyawpitch ( const vec &v , f l o a t &yaw , f l o a t &pitch ) { yaw = −atan2 ( v . x , v . y ) /RAD; pitch = asin ( v . z/v . magnitude ( ) ) /RAD; } #define PHYSFRAMETIME 5 VARP( maxroll , 0 , 0 , 20) ; FVAR( s t r a f e r o l l , 0 , 0.033 f , 90) ; FVAR( f a d e r o l l , 0 , 0.95 f , 1) ; VAR( floatspeed , 1 , 100, 10000) ; void modifyvelocity ( physent ∗pl , bool l o c a l , bool water , bool f l o a t i n g , i n t curtime ) { i f ( floating ) { i f ( pl−>jumping ) { pl−>jumping = f a l s e ; pl−>v e l . z = max( pl−>v e l . z , JUMPVEL) ; } }

engine/physics.cpp e l s e i f ( pl−>physstate >= PHYS SLOPE || water ) { i f ( water && ! pl−>inwater ) pl−>v e l . div ( 8 ) ; i f ( pl−>jumping ) { pl−>jumping = f a l s e ;

aboveeye − pl−>eyeheight ) /4) ) ; bool water = i s l i q u i d ( material&MATF VOLUME) ; bool f l o a t i n g = pl−>type==ENT PLAYER && ( pl−>s t a t e ==CS EDITING || pl−> s t a t e ==CS SPECTATOR) ; f l o a t secs = curtime/1000. f ; // apply g r a v i t y i f ( ! f l o a t i n g ) modifygravity ( pl , water , curtime ) ; // apply any player generated changes in v e l o c i t y modifyvelocity ( pl , l o c a l , water , f l o a t i n g , curtime ) ;

pl−>v e l . z = max( pl−>v e l . z , JUMPVEL) ; // physics impulse upwards i f ( water ) { pl−>v e l . x /= 8.0 f ; pl−>v e l . y /= 8.0 f ; } // dampen v e l o c i t y change even harder , g i v e s c o r r e c t water f e e l game : : p h y s i c s t r i g g e r ( pl , l o c a l , 1 , 0) ; } } i f ( ! f l o a t i n g && pl−>physstate == PHYS FALL ) pl−>timeinair += curtime ;

vec d ( pl−>v e l ) ; i f ( ! f l o a t i n g && water ) d . mul( 0 . 5 f ) ; d . add ( pl−>f a l l i n g ) ; d . mul ( secs ) ;

vec m( 0 . 0 f , 0.0 f , 0.0 f ) ; i f ( game : : allowmove ( pl ) && ( pl−>move || pl−>s t r a f e ) ) { vecfromyawpitch ( pl−>yaw , f l o a t i n g || water || pl−>type==ENT CAMERA ? pl−>pitch : 0 , pl−>move, pl−>s t r a f e , m) ;

pl−>blocked = f a l s e ; i f ( floating ) // j u s t apply v e l o c i t y { i f ( pl−>physstate ! = PHYS FLOAT ) { pl−>physstate = PHYS FLOAT ; pl−>timeinair = 0; pl−>f a l l i n g = vec ( 0 , 0 , 0) ; } pl−>o . add ( d ) ; } else // apply v e l o c i t y with c o l l i s i o n { const f l o a t f = 1.0 f /moveres ; const i n t timeinair = pl−>timeinair ; i n t c o l l i s i o n s = 0;

i f ( ! f l o a t i n g && pl−>physstate >= PHYS SLOPE ) { /∗ move up or down slopes in a i r ∗ but only move up slopes in water ∗/ f l o a t dz = −(m. x∗pl−>f l o o r . x + m. y∗pl−>f l o o r . y ) /pl−>f l o o r . z ; m. z = water ? max(m. z , dz ) : dz ; } m. normalize ( ) ; }

// // // // }

d . mul ( f ) ; l o o p i ( moveres ) i f ( ! move ( pl , d ) && ++ c o l l i s i o n s 800 && ! pl−>timeinair && ! water ) // i f we land a f t e r long time must have been a high jump, make thud sound { game : : p h y s i c s t r i g g e r ( pl , l o c a l , −1, 0) ; }

vec d (m) ; d . mul ( pl−>maxspeed ) ; i f ( pl−>type==ENT PLAYER ) { i f ( floating ) { i f ( pl==player ) d . mul ( floatspeed /100.0 f ) ; } e l s e i f ( ! water && game : : allowmove ( pl ) ) d . mul ( ( pl−>move && ! pl−> s t r a f e ? 1.3 f : 1.0 f ) ∗ ( pl−>physstate < PHYS SLOPE ? 1.3 f : 1.0 f ) ) ; } f l o a t f r i c = water && ! f l o a t i n g ? 20.0 f : ( pl−>physstate >= PHYS SLOPE || f l o a t i n g ? 6.0 f : 30.0 f ) ; pl−>v e l . l e r p ( d , pl−>vel , pow(1 − 1/ f r i c , curtime/20.0 f ) ) ; old fps f r i c t i o n f l o a t f r i c t i o n = water && ! f l o a t i n g ? 20.0 f : ( pl−>physstate >= PHYS SLOPE || f l o a t i n g ? 6.0 f : 30.0 f ) ; f l o a t f p s f r i c = min ( curtime /(20.0 f∗f r i c t i o n ) , 1.0 f ) ; pl−>v e l . l e r p ( pl−>vel , d , f p s f r i c ) ;

} i f ( pl−>s t a t e ==CS ALIVE ) updatedynentcache ( pl ) ; // automatically apply smooth r o l l when s t r a f i n g i f ( pl−>s t r a f e && maxroll ) pl−>r o l l = clamp ( pl−>r o l l − pow ( clamp ( 1 . 0 f + pl−>s t r a f e∗pl−>r o l l /maxroll , 0.0 f , 1.0 f ) , 0.33 f )∗pl−>s t r a f e∗ curtime∗s t r a f e r o l l , −maxroll , maxroll ) ; e l s e pl−>r o l l ∗= curtime == PHYSFRAMETIME ? f a d e r o l l : pow ( f a d e r o l l , curtime/ f l o a t (PHYSFRAMETIME) ) ; // play sounds on water t r a n s i t i o n s

void modifygravity ( physent ∗pl , bool water , i n t curtime ) { f l o a t secs = curtime/1000.0 f ; vec g ( 0 , 0 , 0) ; i f ( pl−>physstate == PHYS FALL ) g . z −= GRAVITY∗secs ; e l s e i f ( pl−>f l o o r . z > 0 && pl−>f l o o r . z < FLOORZ) { g . z = −1; g . p r o j e c t ( pl−>f l o o r ) ; g . normalize ( ) ; g . mul ( GRAVITY∗secs ) ; } i f ( ! water || ! game : : allowmove ( pl ) || ( ! pl−>move && ! pl−>s t r a f e ) ) pl−> f a l l i n g . add ( g ) ;

// // // // //

367

i f ( water || pl−>physstate >= PHYS SLOPE ) { f l o a t f r i c = water ? 2.0 f : 6.0 f , c = water ? 1.0 f : clamp ( ( pl−>f l o o r . z − SLOPEZ) / (FLOORZ−SLOPEZ ) , 0.0 f , 1.0 f ) ; pl−>f a l l i n g . mul ( pow(1 − c/ f r i c , curtime/20.0 f ) ) ; old fps f r i c t i o n f l o a t f r i c t i o n = water ? 2.0 f : 6.0 f , f p s f r i c = f r i c t i o n /curtime∗20.0 f , c = water ? 1.0 f : clamp ( ( pl−>f l o o r . z − SLOPEZ) / (FLOORZ− SLOPEZ) , 0.0 f , 1.0 f ) ; pl−>f a l l i n g . mul(1 − c/ f p s f r i c ) ; }

} // main physics routine , moves a player/monster f o r a curtime step // moveres indicated the physics precision ( which i s lower f o r monsters and multiplayer prediction ) // l o c a l i s f a l s e f o r multiplayer prediction bool moveplayer ( physent ∗pl , i n t moveres , bool l o c a l , i n t curtime ) { i n t material = lookupmaterial ( vec ( pl−>o . x , pl−>o . y , pl−>o . z + (3∗ pl−>

i f ( pl−>inwater && ! water ) { material = lookupmaterial ( vec ( pl−>o . x , pl−>o . y , pl−>o . z + ( pl−> aboveeye − pl−>eyeheight ) /2) ) ; water = i s l i q u i d ( material&MATF VOLUME) ; } i f ( ! pl−>inwater && water ) game : : p h y s i c s t r i g g e r ( pl , l o c a l , 0 , −1, material&MATF VOLUME) ; e l s e i f ( pl−>inwater && ! water ) game : : p h y s i c s t r i g g e r ( pl , l o c a l , 0 , 1 , pl−>inwater ) ; pl−>inwater = water ? material&MATF VOLUME : MAT AIR ; i f ( pl−>s t a t e ==CS ALIVE && ( pl−>o . z < 0 || material&MAT DEATH) ) game : : suicide ( pl ) ; return true ; } i n t physsteps = 0 , physframetime = PHYSFRAMETIME, lastphysframe = 0; void physicsframe ( ) // optimally schedule physics frames inside the graphics frames { i n t d i f f = l a s t m i l l i s − lastphysframe ; i f ( d i f f o = pl−>newpos ; i n t d i f f = lastphysframe − l a s t m i l l i s ; i f ( d i f f deltapos ) ; deltapos . mul ( min ( d i f f , physframetime ) / f l o a t ( physframetime ) ) ; pl−>o . add ( deltapos ) ;

platforment ( ) {} platforment ( physent ∗d ) : d ( d ) , stacks(−1) , chains(−1) {}

} bool operator ==( const physent ∗o ) const { return d == o ; } void moveplayer ( physent ∗pl , i n t moveres , bool l o c a l ) { i f ( physsteps o = pl−>newpos ; l o o p i ( physsteps−1) moveplayer ( pl , moveres , l o c a l , physframetime ) ; i f ( l o c a l ) pl−>deltapos = pl−>o ; moveplayer ( pl , moveres , l o c a l , physframetime ) ; i f ( local ) { pl−>newpos = pl−>o ; pl−>deltapos . sub ( pl−>newpos ) ; interppos ( pl ) ; } } bool bounce ( physent ∗d , f l o a t e l a s t i c i t y , f l o a t w a t e r f r i c , f l o a t grav ) { i f ( physsteps o = d−>newpos ; bool h i t p l a y e r = f a l s e ; l o o p i ( physsteps−1) { i f ( bounce ( d , physframetime/1000.0 f , e l a s t i c i t y , w a t e r f r i c , grav ) ) h i t p l a y e r = true ; } d−>deltapos = d−>o ; i f ( bounce ( d , physframetime/1000.0 f , e l a s t i c i t y , w a t e r f r i c , grav ) ) h i t p l a y e r = true ; d−>newpos = d−>o ; d−>deltapos . sub ( d−>newpos ) ; interppos ( d ) ; return h i t p l a y e r ;

}; struct p l a t f o r m c o l l i s i o n { platforment ∗ent ; i n t next ; p l a t f o r m c o l l i s i o n ( ) {} p l a t f o r m c o l l i s i o n ( platforment ∗ent , i n t next ) : ent ( ent ) , next ( next ) {} }; template s t a t i c i n l i n e bool p l a t f o r m c o l l i d e ( physent ∗d , const vec &dir , physent ∗o , f l o a t margin ) { E entvol ( d ) ; O obvol ( o , margin ) ; vec cp ; i f ( mpr : : c o l l i d e ( entvol , obvol , NULL, NULL, &cp ) ) { vec wn = vec ( cp ) . sub ( obvol . center ( ) ) ; return obvol . contactface (wn, d i r . i s z e r o ( ) ? vec (wn) . neg ( ) : d i r ) . iszero ( ) ; } return true ; } bool p l a t f o r m c o l l i d e ( physent ∗d , physent ∗o , const vec &dir , f l o a t margin = 0) { i f ( d−>c o l l i d e t y p e == COLLIDE ELLIPSE ) { i f ( o−>c o l l i d e t y p e == COLLIDE ELLIPSE ) return e l l i p s e c o l l i d e ( d , dir , o−>o , vec ( 0 , 0 , 0) , o−>yaw , o−>xradius , o−>yradius , o−> aboveeye , o−>eyeheight + margin ) ; e l s e return e l l i p s e r e c t c o l l i d e ( d , dir , o−>o , vec ( 0 , 0 , 0) , o−>yaw , o−>xradius , o−>yradius , o−>aboveeye , o−>eyeheight + margin ) ; } e l s e i f ( o−>c o l l i d e t y p e == COLLIDE ELLIPSE ) return p l a t f o r m c o l l i d e(d , dir , o , margin ) ; e l s e return p l a t f o r m c o l l i d e(d , dir , o , margin ) ;

} } void updatephysstate ( physent ∗d ) { i f ( d−>physstate == PHYS FALL ) return ; d−>timeinair = 0; vec old ( d−>o ) ; /∗ Attempt to reconstruct the f l o o r s t a t e . ∗ May be inaccurate since movement c o l l i s i o n s are not considered . ∗ I f good f l o o r i s not found , j u s t keep the old f l o o r and hope i t ’ s c o r r e c t enough . ∗/ switch ( d−>physstate ) { case PHYS SLOPE: case PHYS FLOOR: case PHYS STEP DOWN: d−>o . z −= 0.15 f ; i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , d−>physstate == PHYS SLOPE || d−> physstate == PHYS STEP DOWN ? SLOPEZ : FLOORZ) ) d−>f l o o r = wall ; break ; case PHYS STEP UP : d−>o . z −= STAIRHEIGHT+0.15 f ; i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) , SLOPEZ) ) d−>f l o o r = wall ; break ; case PHYS SLIDE : d−>o . z −= 0.15 f ; i f ( ! c o l l i d e ( d , vec ( 0 , 0 , −1) ) && wall . z < SLOPEZ) d−>f l o o r = wall ; break ; } i f ( d−>physstate > PHYS FALL && d−>f l o o r . z f l o o r = vec ( 0 , 0 , 1) ; d−>o = old ; } const f l o a t PLATFORMMARGIN = 0.2 f ;

bool moveplatform ( physent ∗p , const vec &d i r ) { i f ( ! insideworld ( p−>newpos ) ) return f a l s e ; vec oldpos ( p−>o ) ; ( p−>o = p−>newpos ) . add ( d i r ) ; i f ( ! c o l l i d e ( p , dir , 0 , d i r . zo = oldpos ; return f a l s e ; } p−>o = oldpos ; s t a t i c vector ents ; ents . s e t s i z e ( 0 ) ; f o r ( i n t x = i n t (max( p−>o . x−p−>radius−PLATFORMBORDER, 0.0 f ) )>> dynentsize , ex = i n t ( min ( p−>o . x+p−>radius+PLATFORMBORDER, worldsize −1.0f ) )>>dynentsize ; x o . y−p−>radius−PLATFORMBORDER, 0.0 f ) )>> dynentsize , ey = i n t ( min ( p−>o . y+p−>radius+PLATFORMBORDER, worldsize −1.0f ) )>>dynentsize ; y o . z−d−>eyeheight < p−>o . z+p−>aboveeye || p−>o . r e j e c t ( d−>o , p−>radius+PLATFORMBORDER+d−>radius ) || ents . f i n d ( d ) >= 0) continue ; ents . add ( d ) ; } } s t a t i c vector passengers , c o l l i d e r s ; passengers . s e t s i z e ( 0 ) ; colliders . setsize (0) ; s t a t i c vector

c o l l i s i o n s ; collisions . setsize (0) ; // build up c o l l i s i o n DAG o f c o l l i d e r s to be pushed o f f , and DAG o f

engine/pvs.cpp stacked passengers loopv ( ents ) { platforment &ent = ents [ i ] ; physent ∗d = ent . d ; // check i f the dynent i s on top o f the platform i f ( ! p l a t f o r m c o l l i d e ( p , d , vec ( 0 , 0 , 1) , PLATFORMMARGIN) ) passengers . add(&ent ) ; vec doldpos ( d−>o ) ; ( d−>o = d−>newpos ) . add ( d i r ) ; i f ( ! c o l l i d e ( d , dir , 0 , f a l s e ) ) c o l l i d e r s . add(&ent ) ; d−>o = doldpos ; l o o p v j ( ents ) { platforment &o = ents [ j ] ; i f ( ! platformcollide ( d, o .d, dir ) ) { c o l l i s i o n s . add ( p l a t f o r m c o l l i s i o n (&ent , o . chains ) ) ; o . chains = c o l l i s i o n s . length ( ) − 1; } i f ( d−>o . z < o . d−>o . z && ! p l a t f o r m c o l l i d e ( d , o . d , vec ( 0 , 0 , 1) , PLATFORMMARGIN) ) { c o l l i s i o n s . add ( p l a t f o r m c o l l i s i o n (&o , ent . stacks ) ) ; ent . stacks = c o l l i s i o n s . length ( ) − 1; } } } loopv ( c o l l i d e r s ) // propagate c o l l i s i o n s { platforment ∗ent = c o l l i d e r s [ i ] ; f o r ( i n t n = ent−>chains ; n>=0; n = c o l l i s i o n s [ n ] . next ) { platforment ∗o = c o l l i s i o n s [ n ] . ent ; i f ( c o l l i d e r s . f i n d ( o )0) { loopv ( passengers ) // i f any stacked passengers c o l l i d e , stop the platform { platforment ∗ent = passengers [ i ] ; i f ( c o l l i d e r s . f i nd ( ent )>=0) return f a l s e ; f o r ( i n t n = ent−>stacks ; n>=0; n = c o l l i s i o n s [ n ] . next ) { platforment ∗o = c o l l i s i o n s [ n ] . ent ; i f ( passengers . f i n d ( o )d ; d−>o . add ( d i r ) ; d−>newpos . add ( d i r ) ; i f ( d i r . x || d i r . y ) updatedynentcache ( d ) ; } } e l s e loopv ( passengers ) // move any stacked passengers who aren ’ t c o l l i d i n g with non−passengers { platforment ∗ent = passengers [ i ] ; i f ( c o l l i d e r s . f i n d ( ent )>=0) continue ; physent ∗d = ent−>d ; d−>o . add ( d i r ) ;

369

d−>newpos . add ( d i r ) ; i f ( d i r . x || d i r . y ) updatedynentcache ( d ) ; f o r ( i n t n = ent−>stacks ; n>=0; n = c o l l i s i o n s [ n ] . next ) { platforment ∗o = c o l l i s i o n s [ n ] . ent ; i f ( passengers . f i n d ( o )o . add ( d i r ) ; p−>newpos . add ( d i r ) ; i f ( d i r . x || d i r . y ) updatedynentcache ( p ) ; return true ; } #define d i r ( name, v , d , s , os ) ICOMMAND( name, ”D” , ( i n t ∗down ) , { player−>s = ∗down! = 0 ; player−>v = player−>s ? d : ( player−>os ? −(d ) : 0) ; }) ; d i r ( backward , move, −1, k down , k up ) ; d i r ( forward , move, 1 , k up , k down ) ; dir ( l e f t , strafe , 1, k l e f t , k right ) ; d i r ( right , s t r a f e , −1, k r i g h t , k l e f t ) ; ICOMMAND( jump, ”D” , ( i n t ∗down ) , { i f ( ! ∗down || game : : canjump ( ) ) player −>jumping = ∗down! = 0 ; }) ; ICOMMAND( attack , ”D” , ( i n t ∗down ) , { game : : doattack (∗down! = 0 ) ; }) ; bool entinmap ( dynent ∗d , bool avoidplayers ) // brute f o r c e but e f f e c t i v e way to f i n d a f r e e spawn spot in the map { d−>o . z += d−>eyeheight ; // pos s p e c i f i e d i s at f e e t vec o r i g = d−>o ; l o o p i (100) // t r y max 100 times { if ( i ) { d−>o = o r i g ; d−>o . x += ( rnd ( 2 1 )−10)∗i /5; // increasing distance d−>o . y += ( rnd ( 2 1 )−10)∗i /5; d−>o . z += ( rnd ( 2 1 )−10)∗i /5; } i f ( c o l l i d e ( d ) && ! inside ) { i f ( hitplayer ) { i f ( ! avoidplayers ) continue ; d−>o = o r i g ; d−>r e s e t i n t e r p ( ) ; return f a l s e ; } d−>r e s e t i n t e r p ( ) ; return true ; } } // leave ent at o r i g i n a l pos , possibly stuck d−>o = o r i g ; d−>r e s e t i n t e r p ( ) ; conoutf (CON WARN, ” can ’ t f i n d e n t i t y spawn spot ! (%.1 f , %.1f , %.1 f ) ” , d−>o . x , d−>o . y , d−>o . z ) ; return f a l s e ; }

engine/pvs.cpp #include ” engine . h” #include ” SDL thread . h” enum { PVS HIDE GEOM = 11; \ res . row = l o . row ; \ res . c o l = l o . c o l ; \

} \ e l s e i f ( l o . row ! = hi . row || l o . c o l ! = hi . c o l || ( l o . coord&0xF0 ) !=0x80 || ( hi . coord&0xF ) ! = 0 ) return f a l s e ; \ else \ { \ res . coord = ( ( l o . coord&˜0xF1 )>>1) | ( ( ( hi . coord&˜0x1F )>>1) + 0 x40 ) ; \ res . row = l o . row ; \ res . c o l = l o . c o l ; \ }

struct shaftbb { union { ushort v [ 6 ] ; struct { usvec min , max; }; }; shaftbb ( ) {} shaftbb ( const i v e c &o , i n t s i z e ) { min . x = o . x ; min . y = o . y ; min . z = o . z ; max. x = o . x + s i z e ; max. y = o . y + s i z e ; max. z = o . z + s i z e ; } shaftbb ( const i v e c &o , i n t size , const bvec &edges ) { min . x = o . x + ( s i z e ∗( edges . x&0xF ) ) /8; min . y = o . y + ( s i z e ∗( edges . y&0xF ) ) /8; min . z = o . z + ( s i z e ∗( edges . z&0xF ) ) /8; max. x = o . x + ( s i z e ∗( edges . x>>4)) /8; max. y = o . y + ( s i z e ∗( edges . y>>4)) /8; max. z = o . z + ( s i z e ∗( edges . z>>4)) /8; }

bvec &res = bbs [ x + 2∗y ] ; MERGEBBS( res , z , x , y ) ; res . x = l o . x ; res . y = l o . y ; } loop ( x , 2) { bvec &l o = bbs [ x ] , &hi = bbs [ x + 2 ] ; MERGEBBS( lo , y , x , z ) ; } bvec &l o = bbs [ 0 ] , &hi = bbs [ 1 ] ; MERGEBBS( p . edges , x , y , z ) ; return true ; } s t a t i c void genpvsnodes ( cube ∗c , i n t parent = 0 , const i v e c &co = i v e c ( 0 , 0 , 0) , i n t s i z e = worldsize /2) { i n t index = origpvsnodes . length ( ) ; loopi ( 8 ) { i v e c o ( i , co . x , co . y , co . z , s i z e ) ; pvsnode &n = origpvsnodes . add ( ) ; n . f l a g s = 0; n . children = 0; i f ( c [ i ] . children || isempty ( c [ i ] ) || c [ i ] . material&MAT ALPHA) memset ( n . edges . v , 0xFF , 3) ; e l s e loopk ( 3 ) { uint face = c [ i ] . faces [ k ] ; i f ( face==F SOLID ) n . edges [ k ] = 0x80 ; else { uchar low = max(max( face&0xF , ( face>>8)&0xF ) , max ( ( face>>16) &0xF , ( face>>24)&0xF ) ) , high = min ( min ( ( face>>4)&0xF , ( face>>12)&0xF ) , min ( ( face >>20)&0xF , ( face>>28)&0xF ) ) ; i f ( size = high ) { memset ( n . edges . v , 0xFF , 3) ; break ; } n . edges [ k ] = low | ( high1); i f ( origpvsnodes [ index+ i ] . children ) branches ++; } i f ( ! branches && mergepvsnodes ( origpvsnodes [ parent ] , &origpvsnodes [ index ] ) ) origpvsnodes . s e t s i z e ( index ) ; e l s e origpvsnodes [ parent ] . children = index ; } struct shaftplane { float r , c , offset ; uchar rnear , cnear , r f a r , c f a r ; }; struct usvec { union { struct { ushort x , y , z ; }; ushort v [ 3 ] ; }; ushort &operator [ ] ( i n t i ) { return v [ i ] ; } ushort operator [ ] ( i n t i ) const { return v [ i ] ; } i v e c t o i v e c ( ) const { return i v e c ( x , y , z ) ; }

ushort &operator [ ] ( i n t i ) { return v [ i ] ; } ushort operator [ ] ( i n t i ) const { return v [ i ] ; } bool contains ( const shaftbb &o ) const { return min . x=o .max. z ; } bool outside ( const i v e c &o , i n t s i z e ) const { return o . x>=max. x || o . y>=max. y || o . z>=max. z || o . x+sizemax. z || o .max. x>= c u r l e v e l ; while ( d i f f ) { c u r l e v e l ++; d i f f >>= 1; }

f o r ( const shaftplane ∗p = planes ; p < &planes [ numplanes ] ; p++) { i f ( o [ p−>r f a r ]∗p−>r + o [ p−>c f a r ]∗p−>c + p−>o f f s e t > 0) return false ; } return true ; }

pvsnode ∗cur = l e v e l s [ c u r l e v e l ] ; while ( cur−>children && ! ( cur−>f l a g s&PVS HIDE BB ) ) { cur = &pvsnodes [ cur−>children ] ; c u r l e v e l−−; cur += ( ( p . z>>(c u r l e v e l −2)) &4) | ( ( p . y>>(c u r l e v e l −1)) &2) | ( ( p . x >>c u r l e v e l ) &1) ; l e v e l s [ c u r l e v e l ] = cur ; }

}; struct pvsdata { i n t o f f s e t , len ; pvsdata ( ) {} pvsdata ( i n t o f f s e t , i n t len ) : o f f s e t ( o f f s e t ) , len ( len ) {} };

o r i g i n = i v e c ( p . x&(˜0>1; i v e c dmin = i v e c ( co ) . add ( csize>>1). sub ( viewcellbb . min . t o i v e c ( ) . add ( viewcellbb .max. t o i v e c ( ) ) . shr ( 1 ) ) , dmax = i v e c ( dmin ) . add ( c s i z e ) ; dmin . mul ( dmin ) ; dmax. mul (dmax) ; i v e c d i f f = i v e c (dmax) . sub ( dmin ) ; c u l l o r d e r order [ 8 ] ; i n t d i r = 0; i f ( d i f f . x < 0) { d i f f . x = −d i f f . x ; d i r |= 1; } i f ( d i f f . y < 0) { d i f f . y = −d i f f . y ; d i r |= 2; } i f ( d i f f . z < 0) { d i f f . z = −d i f f . z ; d i r |= 4; } order [ 0 ] = c u l l o r d e r ( 0 , 0) ; order [ 7 ] = c u l l o r d e r ( 7 , d i f f . x + d i f f . y + d i f f . z ) ; order [ 1 ] = c u l l o r d e r ( 1 , d i f f . x ) ; order [ 2 ] = c u l l o r d e r ( 2 , d i f f . y ) ; order [ 3 ] = c u l l o r d e r ( 4 , d i f f . z ) ; i f ( order [ 2 ] . d i s t < order [ 1 ] . d i s t ) swap ( order [ 1 ] , order [ 2 ] ) ; i f ( order [ 3 ] . d i s t < order [ 2 ] . d i s t ) swap ( order [ 2 ] , order [ 3 ] ) ; i f ( order [ 2 ] . d i s t < order [ 1 ] . d i s t ) swap ( order [ 1 ] , order [ 2 ] ) ; c u l l o r d e r dxy ( order [ 1 ] . index | order [ 2 ] . index , order [ 1 ] . d i s t +order [ 2 ] . dist ) , dxz ( order [ 1 ] . index | order [ 3 ] . index , order [ 1 ] . d i s t +order [ 3 ] . dist ) , dyz ( order [ 2 ] . index | order [ 3 ] . index , order [ 2 ] . d i s t +order [ 3 ] . dist ) ; int j ; f o r ( j = 4; j > 0 && dxy . d i s t < order [ j −1]. d i s t ; −−j ) order [ j ] = order [ j −1]; order [ j ] = dxy ; f o r ( j = 5; j > 0 && dxz . d i s t < order [ j −1]. d i s t ; −−j ) order [ j ] = order [ j −1]; order [ j ] = dxz ; f o r ( j = 6; j > 0 && dyz . d i s t < order [ j −1]. d i s t ; −−j ) order [ j ] = order [ j −1]; order [ j ] = dyz ; loopi ( 8 ) { i n t index = order [ i ] . index ˆ d i r ; i v e c o ( index , co . x , co . y , co . z , c s i z e ) ; cullpvs ( children [ index ] , o , c s i z e ) ; } i f ( ! ( p . f l a g s & PVS HIDE BB ) ) return ; } bvec edges = p . children ? bvec (0x80 , 0x80 , 0x80 ) : p . edges ; i f ( edges . x==0xFF ) return ; shaftbb geom ( co , size , edges ) ; i v e c d i f f = geom .max. t o i v e c ( ) . sub ( viewcellbb . min . t o i v e c ( ) ) . abs ( ) ; c u l l o r d e r order [ 3 ] = { c u l l o r d e r ( 0 , d i f f . x ) , c u l l o r d e r ( 1 , d i f f . y ) , c u l l o r d e r ( 2 , d i f f . z ) };

i f ( order [ 1 ] . d i s t > order [ 0 ] . d i s t ) swap ( order [ 0 ] , order [ 1 ] ) ; i f ( order [ 2 ] . d i s t > order [ 1 ] . d i s t ) swap ( order [ 1 ] , order [ 2 ] ) ; i f ( order [ 1 ] . d i s t > order [ 0 ] . d i s t ) swap ( order [ 0 ] , order [ 1 ] ) ; loopi ( 6 ) { i n t dim = order [ i >= 3 ? i−3 : i ] . index , dc = ( i >= 3) ! = ( geom . max[ dim ] =geom . min [ r ] && rmax [ r]=geom . min [ r ] && rmax [ r]o ) ; i f ( ! d ) return ; i n t wbytes = d−>len%9, len = d−>len − wbytes ; lockedpvs = new uchar [ len ] ; memcpy( lockedpvs , &pvsbuf [ d−>o f f s e t + wbytes ] , len ) ; lockedwaterpvs = 0; l o o p i ( wbytes ) lockedwaterpvs |= pvsbuf [ d−>o f f s e t + i ] o . x , camera1 −>o . y , camera1−>o . z ) ; } VARF( lockpvs , 0 , 0 , 1 , lockpvs ( lockpvs ! = 0 ) ) ;

VARN( pvs , usepvs , 0 , 1 , 1) ; VARN( waterpvs , usewaterpvs , 0 , 1 , 1) ; void s e t v i e w c e l l ( const vec &p ) { i f ( ! usepvs ) curpvs = NULL; e l s e i f ( lockedpvs ) { curpvs = lockedpvs ; curwaterpvs = lockedwaterpvs ; } else { pvsdata ∗d = lookupviewcell ( p ) ; curpvs = d ? &pvsbuf [ d−>o f f s e t ] : NULL; curwaterpvs = 0; i f (d) { l o o p i ( d−>len%9) curwaterpvs |= ∗curpvs++ matsurfs ) { materialsurface &m = va−>matbuf [ j ] ; i f ( (m. material&MATF VOLUME) ! =MAT WATER || m. o r i e n t ==O BOTTOM) { j += m. skip ; continue ; } i f (m. o r i e n t ! =O TOP ) { w a t e r f a l l s . add(&m) ; continue ; } loopk ( numwaterplanes ) i f ( waterplanes [ k ] . height == m. o . z ) { waterplanes [ k ] . matsurfs . add(&m) ; goto nextmatsurf ; } i f ( numwaterplanes < MAXWATERPVS) { waterplanes [ numwaterplanes ] . height = m. o . z ; waterplanes [ numwaterplanes ] . matsurfs . add(&m) ; numwaterplanes++; } nextmatsurf : ; } } i f ( w a t e r f a l l s . length ( ) > 0 && numwaterplanes < MAXWATERPVS) numwaterplanes++; } void testpvs ( i n t ∗v c s i z e ) { lockpvs ( f a l s e ) ; uint oldnumwaterplanes = numwaterplanes ; i n t oldwaterplanes [MAXWATERPVS] ; l o o p i ( numwaterplanes ) oldwaterplanes [ i ] = waterplanes [ i ] . height ; findwaterplanes ( ) ; pvsnode &root = origpvsnodes . add ( ) ; memset ( root . edges . v , 0xFF , 3) ; root . f l a g s = 0; root . children = 0; genpvsnodes ( worldroot ) ;

376

Foundations of Videogame Programming Code Repository

genpvs canceled = f a l s e ; check genpvs progress = f a l s e ;

origpvsnodes . s e t s i z e ( 0 ) ; pvscompress . c l e a r ( ) ;

i n t s i z e = ∗vcsize>0 ? ∗v c s i z e : 32; f o r ( i n t mask = 1; mask < s i z e ; mask o . x , camera1−>o . y , camera1−>o . z , len ) ;

} COMMAND( genpvs , ” i ” ) ;

origpvsnodes . s e t s i z e ( 0 ) ; numwaterplanes = oldnumwaterplanes ; l o o p i ( numwaterplanes ) waterplanes [ i ] . height = oldwaterplanes [ i ] ; } COMMAND( testpvs , ” i ” ) ; void genpvs ( i n t ∗v i e w c e l l s i z e ) { i f ( worldsize > 11, bborigin , bbsize ) ) return f a l s e ; } return true ;

renderbackground ( ” generating PVS ( esc to abort ) ” ) ; genpvs canceled = f a l s e ; Uint32 s t a r t = SDL GetTicks ( ) ; renderprogress ( 0 , ” f i n d i n g view c e l l s ” ) ; clearpvs ( ) ; calcpvsbounds ( ) ; findwaterplanes ( ) ; pvsnode &root = origpvsnodes . add ( ) ; memset ( root . edges . v , 0xFF , 3) ; root . f l a g s = 0; root . children = 0; genpvsnodes ( worldroot ) ; t o t a l v i e w c e l l s = countviewcells ( worldroot , i v e c ( 0 , 0 , 0) , worldsize >>1, ∗v i e w c e l l s i z e>0 ? ∗v i e w c e l l s i z e : 32) ; numviewcells = 0; genpvs canceled = f a l s e ; check genpvs progress = f a l s e ; SDL TimerID timer = NULL; i n t numthreads = pvsthreads > 0 ? pvsthreads : numcpus; i f ( numthreads>1, ∗ v i e w c e l l s i z e>0 ? ∗v i e w c e l l s i z e : 32) ; i f ( numthreadsthread = SDL CreateThread ( pvsworker : : run , w) ; } show genpvs progress ( 0 , 0) ; while ( ! genpvs canceled ) { SDL Delay (500) ; SDL LockMutex ( viewcellmutex ) ; i n t unique = pvs . length ( ) , processed = numviewcells , remaining = viewcellrequests . length ( ) ; SDL UnlockMutex ( viewcellmutex ) ; show genpvs progress ( unique , processed ) ; i f ( ! remaining ) break ; } SDL LockMutex ( viewcellmutex ) ; viewcellrequests . s e t s i z e ( 0 ) ; SDL UnlockMutex ( viewcellmutex ) ; loopv ( pvsworkers ) SDL WaitThread ( pvsworkers [ i]−>thread , NULL) ; } pvsworkers . deletecontents ( ) ;

} s t a t i c i n l i n e bool pvsoccluded ( uchar ∗buf , const i v e c &bborigin , const i v e c &bbsize ) { i n t d i f f = ( bborigin . x ˆ ( bborigin . x+bbsize . x ) ) | ( bborigin . y ˆ ( bborigin . y+bbsize . y ) ) | ( bborigin . z ˆ ( bborigin . z+bbsize . z ) ) ; i f ( d i f f &˜((1read ( pvsbuf . reserve ( t o t a l l e n ) . buf , t o t a l l e n ) ; pvsbuf . advance ( t o t a l l e n ) ; viewcells = loadviewcells ( f ) ; }

viewcellnode ∗l o a d v i e w c e l l s ( stream ∗f ) { viewcellnode ∗p = new viewcellnode ;

i n t getnumviewcells ( ) { return pvs . length ( ) ; }

engine/ragdoll.h struct r a g d o l l s k e l { struct v e r t { vec pos ; f l o a t radius , weight ; }; struct t r i { int vert [ 3 ] ; bool shareverts ( const t r i &t ) const { l o o p i ( 3 ) l o o p j ( 3 ) i f ( v e r t [ i ] == t . v e r t [ j ] ) return true ; return f a l s e ; } }; struct d i s t l i m i t { int vert [ 2 ] ; f l o a t mindist , maxdist ; };

vector r o t f r i c t i o n s ; vector j o i n t s ; vector r e l j o i n t s ; r a g d o l l s k e l ( ) : loaded ( f a l s e ) , animjoints ( f a l s e ) , eye(−1) {} void setupjoints ( ) { loopv ( v e r t s ) v e r t s [ i ] . weight = 0; loopv ( j o i n t s ) { j o i n t &j = j o i n t s [ i ] ; j . weight = 0; vec pos ( 0 , 0 , 0) ; loopk ( 3 ) i f ( j . v e r t [ k]>=0) { pos . add ( v e r t s [ j . v e r t [ k ] ] . pos ) ; j . weight ++; v e r t s [ j . v e r t [ k ] ] . weight ++; } i f ( j . weight ) j . weight = 1/ j . weight ; pos . mul ( j . weight ) ; t r i &t = t r i s [ j . t r i ] ; matrix3x3 m; const vec &v1 = v e r t s [ t . v e r t [ 0 ] ] . pos , &v2 = v e r t s [ t . v e r t [ 1 ] ] . pos , &v3 = v e r t s [ t . v e r t [ 2 ] ] . pos ; m. a = vec ( v2 ) . sub ( v1 ) . normalize ( ) ; m. c . cross (m. a , vec ( v3 ) . sub ( v1 ) ) . normalize ( ) ; m. b . cross (m. c , m. a ) ;

struct r o t l i m i t { int t r i [ 2 ] ; f l o a t maxangle ; matrix3x3 middle ; }; struct r o t f r i c t i o n { int t r i [ 2 ] ; matrix3x3 middle ; };

j . o r i e n t = matrix3x4 (m, m. transform ( pos ) . neg ( ) ) ; } loopv ( v e r t s ) i f ( v e r t s [ i ] . weight ) v e r t s [ i ] . weight = 1/v e r t s [ i ] . weight ; r e l j o i n t s . shrink ( 0 ) ; }

struct j o i n t { i n t bone , t r i , v e r t [ 3 ] ; f l o a t weight ; matrix3x4 o r i e n t ; }; struct r e l j o i n t { i n t bone , parent ; }; bool loaded , animjoints ; i n t eye ; vector v e r t s ; vector t r i s ; vector d i s t l i m i t s ; vector r o t l i m i t s ;

void s e t u p r o t f r i c t i o n s ( ) { r o t f r i c t i o n s . shrink ( 0 ) ; loopv ( t r i s ) f o r ( i n t j = i +1; j < t r i s . length ( ) ; j ++) i f ( t r i s [ i ] . shareverts ( t r i s [ j ] ) ) { r o t f r i c t i o n &r = r o t f r i c t i o n s . add ( ) ; r . tri [0] = i ; r . tri [1] = j ; } } void setup ( ) { setupjoints ( ) ; setuprotfrictions ( ) ;

378

Foundations of Videogame Programming Code Repository

loaded = true ;

center = vec ( 0 , 0 , 0) ; loopv ( skel−>v e r t s ) center . add ( v e r t s [ i ] . pos ) ; center . div ( skel−>v e r t s . length ( ) ) ; radius = 0; loopv ( skel−>v e r t s ) radius = max( radius , v e r t s [ i ] . pos . d i s t ( center ) ) ;

} void a d d r e l j o i n t ( i n t bone , i n t parent ) { r e l j o i n t &r = r e l j o i n t s . add ( ) ; r . bone = bone ; r . parent = parent ; }

} void i n i t ( dynent ∗d ) { extern i n t ragdolltimestepmin ; f l o a t t s = ragdolltimestepmin/1000.0 f ; loopv ( skel−>v e r t s ) ( v e r t s [ i ] . oldpos = v e r t s [ i ] . pos ) . sub ( vec ( d−>v e l ) . add ( d−>f a l l i n g ) . mul ( t s ) ) ; timestep = t s ;

}; struct ragdolldata { struct v e r t { vec oldpos , pos , newpos ; f l o a t weight ; bool c o l l i d e d , stuck ;

calctris ( ) ; calcboundsphere ( ) ; o f f s e t = d−>o ; o f f s e t . sub ( skel−>eye >= 0 ? v e r t s [ skel−>eye ] . pos : center ) ; o f f s e t . z += ( d−>eyeheight + d−>aboveeye ) /2;

v e r t ( ) : pos ( 0 , 0 , 0) , newpos ( 0 , 0 , 0) , weight ( 0 ) , c o l l i d e d ( f a l s e ) , stuck ( true ) {}

}

}; void void void void void

r a g d o l l s k e l ∗skel ; i n t m i l l i s , c o l l i d e m i l l i s , c o l l i s i o n s , f l o a t i n g , lastmove , unsticks ; vec o f f s e t , center ; f l o a t radius , timestep , scale ; v e r t ∗v e r t s ; matrix3x3 ∗t r i s ; matrix3x4 ∗animjoints , ∗r e l j o i n t s ;

void void void void

ragdolldata ( r a g d o l l s k e l ∗skel , f l o a t scale = 1) : skel ( skel ) , millis ( lastmillis ) , collidemillis (0) , collisions (0) , floating (0) , lastmove ( l a s t m i l l i s ) , unsticks ( INT MAX ) , timestep ( 0 ) , scale ( scale ) , v e r t s ( new v e r t [ skel−>v e r t s . length ( ) ] ) , t r i s ( new matrix3x3 [ skel−>t r i s . length ( ) ] ) , animjoints ( ! skel−>animjoints || skel−>j o i n t s . empty ( ) ? NULL : new matrix3x4 [ skel−>j o i n t s . length ( ) ] ) , r e l j o i n t s ( skel−>r e l j o i n t s . empty ( ) ? NULL : new matrix3x4 [ skel−> r e l j o i n t s . length ( ) ] ) { }

move ( dynent ∗pl , f l o a t t s ) ; updatepos ( ) ; constrain ( ) ; constraindist ( ) ; a p p l y r o t l i m i t ( r a g d o l l s k e l : : t r i &t1 , r a g d o l l s k e l : : t r i &t2 , f l o a t angle , const vec &axis ) ; constrainrot ( ) ; calcrotfriction ( ) ; applyrotfriction ( f l o a t ts ) ; tryunstick ( f l o a t speed ) ;

s t a t i c i n l i n e bool c o l l i d e v e r t ( const vec &pos , const vec &dir , f l o a t radius ) { s t a t i c struct vertent : physent { vertent ( ) { type = ENT BOUNCE; c o l l i d e t y p e = COLLIDE AABB; radius = xradius = yradius = eyeheight = aboveeye = 1; } } v; v . o = pos ; i f ( v . radius ! = radius ) v . radius = v . xradius = v . yradius = v . eyeheight = v . aboveeye = radius ; return c o l l i d e (&v , dir , 0 , f a l s e ) ; }

˜ ragdolldata ( ) { delete [ ] verts ; delete [ ] t r i s ; i f ( animjoints ) d e l e t e [ ] animjoints ; i f ( r e l j o i n t s ) delete [ ] r e l j o i n t s ; }

}; /∗ seed p a r t i c l e p o s i t i o n = avg ( modelview ∗ base2anim ∗ spherepos ) mapped transform = i n v e r t ( c u r t r i ) ∗ o r i g t r i g parented transform = parent{i n v e r t ( c u r t r i ) ∗ o r i g t r i g} ∗ ( i n v e r t ( parent{base2anim}) ∗ base2anim ) ∗/

void calcanimjoint ( i n t i , const matrix3x4 &anim ) { i f ( ! animjoints ) return ; r a g d o l l s k e l : : j o i n t &j = skel−>j o i n t s [ i ] ; vec pos ( 0 , 0 , 0) ; loopk ( 3 ) i f ( j . v e r t [ k]>=0) pos . add ( v e r t s [ j . v e r t [ k ] ] . pos ) ; pos . mul ( j . weight ) ; r a g d o l l s k e l : : t r i &t = skel−>t r i s [ j . t r i ] ; matrix3x3 m; const vec &v1 = v e r t s [ t . v e r t [ 0 ] ] . pos , &v2 = v e r t s [ t . v e r t [ 1 ] ] . pos , &v3 = v e r t s [ t . v e r t [ 2 ] ] . pos ; m. a = vec ( v2 ) . sub ( v1 ) . normalize ( ) ; m. c . cross (m. a , vec ( v3 ) . sub ( v1 ) ) . normalize ( ) ; m. b . cross (m. c , m. a ) ; animjoints [ i ] . mul (m, m. transform ( pos ) . neg ( ) , anim ) ; } void c a l c t r i s ( ) { loopv ( skel−>t r i s ) { r a g d o l l s k e l : : t r i &t = skel−>t r i s [ i ] ; matrix3x3 &m = t r i s [ i ] ; const vec &v1 = v e r t s [ t . v e r t [ 0 ] ] . pos , &v2 = v e r t s [ t . v e r t [ 1 ] ] . pos , &v3 = v e r t s [ t . v e r t [ 2 ] ] . pos ; m. a = vec ( v2 ) . sub ( v1 ) . normalize ( ) ; m. c . cross (m. a , vec ( v3 ) . sub ( v1 ) ) . normalize ( ) ; m. b . cross (m. c , m. a ) ; } } void calcboundsphere ( ) {

void ragdolldata : : constraindist ( ) { f l o a t invscale = 1.0 f /scale ; loopv ( skel−>d i s t l i m i t s ) { r a g d o l l s k e l : : d i s t l i m i t &d = skel−>d i s t l i m i t s [ i ] ; v e r t &v1 = v e r t s [ d . v e r t [ 0 ] ] , &v2 = v e r t s [ d . v e r t [ 1 ] ] ; vec d i r = vec ( v2 . pos ) . sub ( v1 . pos ) ; f l o a t d i s t = d i r . magnitude ( ) ∗invscale , c d i s t ; i f ( d i s t < d . mindist ) c d i s t = d . mindist ; e l s e i f ( d i s t > d . maxdist ) c d i s t = d . maxdist ; e l s e continue ; i f ( d i s t > 1e−4f ) d i r . mul ( c d i s t ∗0.5 f / d i s t ) ; e l s e d i r = vec ( 0 , 0 , c d i s t ∗0.5 f /invscale ) ; vec center = vec ( v1 . pos ) . add ( v2 . pos ) . mul( 0 . 5 f ) ; v1 . newpos . add ( vec ( center ) . sub ( d i r ) ) ; v1 . weight ++; v2 . newpos . add ( vec ( center ) . add ( d i r ) ) ; v2 . weight ++; } } i n l i n e void ragdolldata : : a p p l y r o t l i m i t ( r a g d o l l s k e l : : t r i &t1 , r a g d o l l s k e l : : t r i &t2 , f l o a t angle , const vec &axis ) { v e r t &v1a = v e r t s [ t1 . v e r t [ 0 ] ] , &v1b = v e r t s [ t1 . v e r t [ 1 ] ] , &v1c = v e r t s [ t1 . v e r t [ 2 ] ] , &v2a = v e r t s [ t2 . v e r t [ 0 ] ] , &v2b = v e r t s [ t2 . v e r t [ 1 ] ] , &v2c = v e r t s [ t2 . v e r t [ 2 ] ] ; vec m1 = vec ( v1a . pos ) . add ( v1b . pos ) . add ( v1c . pos ) . div ( 3 ) , m2 = vec ( v2a . pos ) . add ( v2b . pos ) . add ( v2c . pos ) . div ( 3 ) , q1a , q1b , q1c , q2a , q2b , q2c ; f l o a t w1 = q1a . cross ( axis , vec ( v1a . pos ) . sub (m1) ) . magnitude ( ) + q1b . cross ( axis , vec ( v1b . pos ) . sub (m1) ) . magnitude ( ) + q1c . cross ( axis , vec ( v1c . pos ) . sub (m1) ) . magnitude ( ) ,

engine/ragdoll.h w2 = q2a . cross ( axis , vec ( v2a . pos ) . sub (m2) ) . magnitude ( ) + q2b . cross ( axis , vec ( v2b . pos ) . sub (m2) ) . magnitude ( ) + q2c . cross ( axis , vec ( v2c . pos ) . sub (m2) ) . magnitude ( ) ; angle /= w1 + w2 + 1e−9f ; f l o a t a1 = angle∗w2, a2 = −angle∗w1, s1 = s i n f ( a1 ) , s2 = s i n f ( a2 ) ; vec c1 = vec ( axis ) . mul(1 − cosf ( a1 ) ) , c2 = vec ( axis ) . mul(1 − cosf ( a2 ) ) ; v1a . newpos . add ( vec ( ) . cross ( c1 , q1a ) . add ( vec ( q1a ) . mul ( s1 ) ) . add ( v1a . pos ) ); v1a . weight ++; v1b . newpos . add ( vec ( ) . cross ( c1 , q1b ) . add ( vec ( q1b ) . mul ( s1 ) ) . add ( v1b . pos ) ); v1b . weight ++; v1c . newpos . add ( vec ( ) . cross ( c1 , q1c ) . add ( vec ( q1c ) . mul ( s1 ) ) . add ( v1c . pos ) ); v1c . weight ++; v2a . newpos . add ( vec ( ) . cross ( c2 , q2a ) . add ( vec ( q2a ) . mul ( s2 ) ) . add ( v2a . pos ) ); v2a . weight ++; v2b . newpos . add ( vec ( ) . cross ( c2 , q2b ) . add ( vec ( q2b ) . mul ( s2 ) ) . add ( v2b . pos ) ); v2b . weight ++; v2c . newpos . add ( vec ( ) . cross ( c2 , q2c ) . add ( vec ( q2c ) . mul ( s2 ) ) . add ( v2c . pos ) ); v2c . weight ++;

{ vec unstuck ( 0 , 0 , 0) ; i n t stuck = 0; loopv ( skel−>v e r t s ) { v e r t &v = v e r t s [ i ] ; i f ( v . stuck ) { i f ( ! c o l l i d e v e r t ( v . pos , vec ( 0 , 0 , 0) , skel−>v e r t s [ i ] . radius ) ) { stuck ++; continue ; } v . stuck = f a l s e ; } unstuck . add ( v . pos ) ; } unsticks = 0; i f ( ! stuck || stuck >= skel−>v e r t s . length ( ) ) return ; unstuck . div ( skel−>v e r t s . length ( ) − stuck ) ; loopv ( skel−>v e r t s ) { v e r t &v = v e r t s [ i ] ; i f ( v . stuck ) { v . pos . add ( vec ( unstuck ) . sub ( v . pos ) . r e s c a l e ( speed ) ) ; unsticks ++; } }

}

}

void ragdolldata : : constrainrot ( ) { loopv ( skel−>r o t l i m i t s ) { r a g d o l l s k e l : : r o t l i m i t &r = skel−>r o t l i m i t s [ i ] ; matrix3x3 r o t ; r o t . transposemul ( t r i s [ r . t r i [ 0 ] ] , r . middle ) ; r o t . mul ( t r i s [ r . t r i [ 1 ] ] ) ;

extern vec wall ;

vec axis ; f l o a t angle ; i f ( ! r o t . calcangleaxis ( angle , axis ) ) continue ; angle = r . maxangle − fabs ( angle ) ; i f ( angle >= 0) continue ; angle += 1e−3f ; a p p l y r o t l i m i t ( skel−>t r i s [ r . t r i [ 0 ] ] , skel−>t r i s [ r . t r i [ 1 ] ] , angle , axis ) ; } } VAR( ragdolltimestepmin , 1 , 5 , 50) ; VAR( ragdolltimestepmax , 1 , 10, 50) ; FVAR( r a g d o l l r o t f r i c , 0 , 0.85 f , 1) ; FVAR( r a g d o l l r o t f r i c s t o p , 0 , 0.1 f , 1) ;

void ragdolldata : : updatepos ( ) { loopv ( skel−>v e r t s ) { v e r t &v = v e r t s [ i ] ; i f ( v . weight ) { v . newpos . div ( v . weight ) ; i f ( c o l l i d e v e r t ( v . newpos , vec ( v . newpos ) . sub ( v . pos ) , skel−>v e r t s [ i ] . radius ) ) v . pos = v . newpos ; else { vec d i r = vec ( v . newpos ) . sub ( v . oldpos ) ; i f ( d i r . dot ( wall ) < 0) v . oldpos = vec ( v . pos ) . sub ( d i r . r e f l e c t ( wall ) ) ; v . c o l l i d e d = true ; } } v . newpos = vec ( 0 , 0 , 0) ; v . weight = 0; } } VAR( ragdollconstrain , 1 , 5 , 100) ;

void ragdolldata : : c a l c r o t f r i c t i o n ( ) { loopv ( skel−>r o t f r i c t i o n s ) { r a g d o l l s k e l : : r o t f r i c t i o n &r = skel−>r o t f r i c t i o n s [ i ] ; r . middle . multranspose ( t r i s [ r . t r i [ 0 ] ] , t r i s [ r . t r i [ 1 ] ] ) ; } } void ragdolldata : : a p p l y r o t f r i c t i o n ( f l o a t t s ) { calctris ( ) ; f l o a t stopangle = 2∗M PI∗t s∗r a g d o l l r o t f r i c s t o p , r o t f r i c = 1.0 f − pow ( r a g d o l l r o t f r i c , t s ∗1000.0 f /ragdolltimestepmin ) ; loopv ( skel−>r o t f r i c t i o n s ) { r a g d o l l s k e l : : r o t f r i c t i o n &r = skel−>r o t f r i c t i o n s [ i ] ; matrix3x3 r o t ; r o t . transposemul ( t r i s [ r . t r i [ 0 ] ] , r . middle ) ; r o t . mul ( t r i s [ r . t r i [ 1 ] ] ) ; vec axis ; f l o a t angle ; i f ( r o t . calcangleaxis ( angle , axis ) ) { angle ∗= −(fabs ( angle ) >= stopangle ? r o t f r i c : 1.0 f ) ; a p p l y r o t l i m i t ( skel−>t r i s [ r . t r i [ 0 ] ] , skel−>t r i s [ r . t r i [ 1 ] ] , angle , axis ) ; } } loopv ( skel−>v e r t s ) { v e r t &v = v e r t s [ i ] ; i f ( v . weight ) v . pos = v . newpos . div ( v . weight ) ; v . newpos = vec ( 0 , 0 , 0) ; v . weight = 0; } } void ragdolldata : : tryunstick ( f l o a t speed )

379

void ragdolldata : : constrain ( ) { l o o p i ( ragdollconstrain ) { constraindist ( ) ; updatepos ( ) ; calctris ( ) ; constrainrot ( ) ; updatepos ( ) ; } } FVAR( r a g d o l l b o d y f r i c , 0 , 0.95 f , 1) ; FVAR( r a g d o l l b o d y f r i c s c a l e , 0 , 2 , 10) ; FVAR( r a g d o l l w a t e r f r i c , 0 , 0.85 f , 1) ; FVAR( ragdollgroundfric , 0 , 0.8 f , 1) ; FVAR( r a g d o l l a i r f r i c , 0 , 0.996 f , 1) ; FVAR( ragdollunstick , 0 , 10, 1e3f ) ; VAR( r a g d o l l e x p i r e o f f s e t , 0 , 1500, 30000) ; VAR( r a g d o l l w a t e r e x p i r e o f f s e t , 0 , 3000, 30000) ; void ragdolldata : : move ( dynent ∗pl , f l o a t t s ) { extern const f l o a t GRAVITY ; i f ( c o l l i d e m i l l i s && l a s t m i l l i s > c o l l i d e m i l l i s ) return ; i n t material = lookupmaterial ( vec ( center . x , center . y , center . z + radius /2) ) ; bool water = i s l i q u i d ( material&MATF VOLUME) ; i f ( ! pl−>inwater && water ) game : : p h y s i c s t r i g g e r ( pl , true , 0 , −1, material&MATF VOLUME) ; e l s e i f ( pl−>inwater && ! water ) { material = lookupmaterial ( center ) ; water = i s l i q u i d ( material&MATF VOLUME) ; i f ( ! water ) game : : p h y s i c s t r i g g e r ( pl , true , 0 , 1 , pl−>inwater ) ; } pl−>inwater = water ? material&MATF VOLUME : MAT AIR ;

380

Foundations of Videogame Programming Code Repository

calcrotfriction ( ) ; f l o a t t s f r i c = timestep ? t s /timestep : 1 , a i r f r i c = r a g d o l l a i r f r i c + min ( ( r a g d o l l b o d y f r i c s c a l e∗ c o l l i s i o n s ) /skel−>v e r t s . length ( ) , 1.0 f ) ∗( ragdollbodyfric − r a g d o l l a i r f r i c ) ; c o l l i s i o n s = 0; loopv ( skel−>v e r t s ) { v e r t &v = v e r t s [ i ] ; vec dpos = vec ( v . pos ) . sub ( v . oldpos ) ; dpos . z −= GRAVITY∗t s∗t s ; i f ( water ) dpos . z += 0.25 f∗s i n f ( detrnd ( s i z e t ( t h i s ) + i , 360)∗RAD + l a s t m i l l i s /10000.0 f∗M PI )∗t s ; dpos . mul ( pow ( ( water ? r a g d o l l w a t e r f r i c : 1.0 f ) ∗ ( v . c o l l i d e d ? ragdollgroundfric : a i r f r i c ) , t s ∗1000.0 f /ragdolltimestepmin ) ∗t s f r i c ) ; v . oldpos = v . pos ; v . pos . add ( dpos ) ; } applyrotfriction ( ts ) ; loopv ( skel−>v e r t s ) { v e r t &v = v e r t s [ i ] ; i f ( v . pos . z < 0) { v . pos . z = 0; v . oldpos = v . pos ; c o l l i s i o n s ++; } vec d i r = vec ( v . pos ) . sub ( v . oldpos ) ; v . c o l l i d e d = ! c o l l i d e v e r t ( v . pos , dir , skel−>v e r t s [ i ] . radius ) ; i f ( v . collided ) { v . pos = v . oldpos ; v . oldpos . sub ( d i r . r e f l e c t ( wall ) ) ; c o l l i s i o n s ++; } }

} e l s e i f (++ f l o a t i n g > 1 && l a s t m i l l i s < c o l l i d e m i l l i s ) c o l l i d e m i l l i s = 0; constrain ( ) ; calctris ( ) ; calcboundsphere ( ) ; } FVAR( ragdolleyesmooth , 0 , 0.5 f , 1) ; VAR( ragdolleyesmoothmillis , 1 , 250, 10000) ; void moveragdoll ( dynent ∗d ) { i f ( ! curtime || ! d−>r a g d o l l ) return ; i f ( ! d−>ragdoll−>c o l l i d e m i l l i s || l a s t m i l l i s < d−>ragdoll−> collidemillis ) { i n t lastmove = d−>ragdoll−>lastmove ; while ( d−>ragdoll−>lastmove + ( lastmove == d−>ragdoll−>lastmove ? ragdolltimestepmin : ragdolltimestepmax ) ragdoll−> lastmove ) ; d−>ragdoll−>move ( d , timestep/1000.0 f ) ; d−>ragdoll−>lastmove += timestep ; } } vec eye = d−>ragdoll−>skel−>eye >= 0 ? d−>ragdoll−>v e r t s [ d−>ragdoll−> skel−>eye ] . pos : d−>ragdoll−>center ; eye . add ( d−>ragdoll−>o f f s e t ) ; f l o a t k = pow ( ragdolleyesmooth , f l o a t ( curtime ) /ragdolleyesmoothmillis ) ; d−>o . mul ( k ) . add ( eye . mul(1−k ) ) ;

i f ( unsticks && ragdollunstick ) tryunstick ( t s∗ragdollunstick ) ; } timestep = t s ; i f ( collisions ) { f l o a t i n g = 0; i f ( ! c o l l i d e m i l l i s ) c o l l i d e m i l l i s = l a s t m i l l i s + ( water ? ragdollwaterexpireoffset : ragdollexpireoffset ) ;

void cleanragdoll ( dynent ∗d ) { DELETEP( d−>r a g d o l l ) ; }

engine/rendergl.cpp // rendergl . cpp : core opengl rendering s t u f f

PFNGLPROGRAMENVPARAMETER4FARBPROC PFNGLPROGRAMENVPARAMETER4FVARBPROC

= NULL; glProgramEnvParameter4fARB glProgramEnvParameter4fvARB = NULL;

#include ” engine . h” bool hasVBO = f a l s e , hasDRE = f a l s e , hasOQ = f a l s e , hasTR = f a l s e , hasFBO = f a l s e , hasDS = f a l s e , hasTF = f a l s e , hasBE = f a l s e , hasBC = f a l s e , hasCM = f a l s e , hasNP2 = f a l s e , hasTC = f a l s e , hasS3TC = f a l s e , hasFXT1 = f a l s e , hasTE = f a l s e , hasMT = f a l s e , hasD3 = f a l s e , hasAF = f a l s e , hasVP2 = f a l s e , hasVP3 = f a l s e , hasPP = f a l s e , hasMDA = f a l s e , hasTE3 = f a l s e , hasTE4 = f a l s e , hasVP = f a l s e , hasFP = f a l s e , hasGLSL = f a l s e , hasGM = f a l s e , hasNVFB = f a l s e , hasSGIDT = f a l s e , hasSGISH = f a l s e , hasDT = f a l s e , hasSH = f a l s e , hasNVPCF = f a l s e , hasRN = f a l s e , hasPBO = f a l s e , hasFBB = f a l s e , hasUBO = f a l s e , hasBUE = f a l s e , hasMBR = f a l s e , hasFC = f a l s e , hasTEX = f a l s e ; i n t hasstencil = 0; VAR( renderpath , 1 , 0 , 0) ; VAR( glversion , 1 , 0 , 0) ; VAR( g l s l v e r s i o n , 1 , 0 , 0) ;

// GL ARB vertex program , GL ARB fragment program PFNGLGENPROGRAMSARBPROC glGenProgramsARB PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB PFNGLBINDPROGRAMARBPROC glBindProgramARB PFNGLPROGRAMSTRINGARBPROC glProgramStringARB PFNGLGETPROGRAMIVARBPROC glGetProgramivARB

= NULL = NULL

// GL ARB occlusion query = NULL; PFNGLGENQUERIESARBPROC glGenQueries PFNGLDELETEQUERIESARBPROC glDeleteQueries = NULL; PFNGLBEGINQUERYARBPROC glBeginQuery = NULL; PFNGLENDQUERYARBPROC glEndQuery = NULL; PFNGLGETQUERYIVARBPROC glGetQueryiv = NULL; PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectiv = NULL; PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuiv = NULL; // GL EXT framebuffer object PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbuffer = NULL; PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffers = NULL; PFNGLGENFRAMEBUFFERSEXTPROC glGenRenderbuffers = NULL; = NULL; PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorage PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatus = NULL; = NULL; PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebuffer PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffers = NULL; PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffers = NULL; PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2D = NULL; PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbuffer = NULL; PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmap = NULL;

// GL ARB vertex buffer object , GL ARB pixel buffer object = NULL; PFNGLGENBUFFERSARBPROC glGenBuffers PFNGLBINDBUFFERARBPROC glBindBuffer = NULL; = NULL; PFNGLMAPBUFFERARBPROC glMapBuffer PFNGLUNMAPBUFFERARBPROC glUnmapBuffer = NULL; PFNGLBUFFERDATAARBPROC glBufferData = NULL; PFNGLBUFFERSUBDATAARBPROC glBufferSubData = NULL; = NULL; PFNGLDELETEBUFFERSARBPROC glDeleteBuffers PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubData = NULL; // GL ARB multitexture PFNGLACTIVETEXTUREARBPROC glActiveTexture = PFNGLCLIENTACTIVETEXTUREARBPROC g l C l i e n t A c t i v e T e x t u r e PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2f = PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3f = PFNGLMULTITEXCOORD4FARBPROC glMultiTexCoord4f =

// GL EXT gpu program parameters PFNGLPROGRAMENVPARAMETERS4FVEXTPROC glProgramEnvParameters4fv ; PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC glProgramLocalParameters4fv ;

NULL; = NULL; NULL; NULL; NULL;

= NULL; = NULL; = NULL; = NULL; = NULL;

// GL EXT framebuffer blit PFNGLBLITFRAMEBUFFEREXTPROC // OpenGL 2 . 0 : GL ARB shading GL ARB fragment shader , #ifndef APPLE PFNGLCREATEPROGRAMPROC PFNGLDELETEPROGRAMPROC PFNGLUSEPROGRAMPROC PFNGLCREATESHADERPROC PFNGLDELETESHADERPROC PFNGLSHADERSOURCEPROC PFNGLCOMPILESHADERPROC

glBlitFramebuffer

= NULL;

language 100 , GL ARB shader objects , GL ARB vertex shader glCreateProgram glDeleteProgram glUseProgram glCreateShader glDeleteShader glShaderSource glCompileShader

= = = = = = =

NULL; NULL; NULL; NULL; NULL; NULL; NULL;

engine/rendergl.cpp PFNGLGETSHADERIVPROC glGetShaderiv = NULL; PFNGLGETPROGRAMIVPROC glGetProgramiv = NULL; PFNGLATTACHSHADERPROC glAttachShader = NULL; PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = NULL; PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = NULL; PFNGLLINKPROGRAMPROC glLinkProgram = NULL; PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL; PFNGLUNIFORM1FPROC glUniform1f = NULL; PFNGLUNIFORM2FPROC glUniform2f = NULL; PFNGLUNIFORM3FPROC glUniform3f = NULL; PFNGLUNIFORM4FPROC glUniform4f = NULL; = NULL; PFNGLUNIFORM1FVPROC glUniform1fv PFNGLUNIFORM2FVPROC glUniform2fv = NULL; PFNGLUNIFORM3FVPROC glUniform3fv = NULL; PFNGLUNIFORM4FVPROC glUniform4fv = NULL; = NULL; PFNGLUNIFORM1IPROC glUniform1i PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation = NULL; PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = NULL; PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = NULL; PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = NULL; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL;

VAR( minimizetcusage , 1 , 0 , 0) ; VAR( emulatefog , 1 , 0 , 0) ; VAR( usevp2 , 1 , 0 , 0) ; VAR( usevp3 , 1 , 0 , 0) ; VAR( usetexrect , 1 , 0 , 0) ; VAR( hasglsl , 1 , 0 , 0) ; VAR( useubo , 1 , 0 , 0) ; VAR( usebue , 1 , 0 , 0) ; VAR( usetexcompress , 1 , 0 , 0) ; VAR( r t s c i s s o r , 0 , 1 , 1) ; VAR( b l u r t i l e , 0 , 1 , 1) ; VAR( rtsharefb , 0 , 1 , 1) ;

PFNGLUNIFORMMATRIX2X3FVPROC PFNGLUNIFORMMATRIX3X2FVPROC PFNGLUNIFORMMATRIX2X4FVPROC PFNGLUNIFORMMATRIX4X2FVPROC PFNGLUNIFORMMATRIX3X4FVPROC PFNGLUNIFORMMATRIX4X3FVPROC #endif

VAR( dbgexts , 0 , 0 , 1) ;

glUniformMatrix2x3fv glUniformMatrix3x2fv glUniformMatrix2x4fv glUniformMatrix4x2fv glUniformMatrix3x4fv glUniformMatrix4x3fv

// GL EXT draw range elements PFNGLDRAWRANGEELEMENTSEXTPROC glDrawRangeElements // GL EXT blend minmax PFNGLBLENDEQUATIONEXTPROC glBlendEquation // GL EXT blend color PFNGLBLENDCOLOREXTPROC glBlendColor

= = = = = =

NULL; NULL; NULL; NULL; NULL; NULL;

= NULL;

= NULL;

= NULL; = NULL;

// GL ARB texture compression PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glCompressedTexImage3D = PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2D = PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glCompressedTexImage1D = PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glCompressedTexSubImage3D PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glCompressedTexSubImage2D PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glCompressedTexSubImage1D PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImage

NULL; NULL; NULL; = NULL; = NULL; = NULL; = NULL;

// GL ARB uniform buffer object PFNGLGETUNIFORMINDICESPROC glGetUniformIndices = NULL; PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv = NULL; PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex = NULL; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv = NULL; = NULL; PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding PFNGLBINDBUFFERBASEPROC glBindBufferBase = NULL; PFNGLBINDBUFFERRANGEPROC glBindBufferRange = NULL; // GL EXT bindable uniform PFNGLUNIFORMBUFFEREXTPROC glUniformBuffer = NULL; PFNGLGETUNIFORMBUFFERSIZEEXTPROC glGetUniformBufferSize = NULL; PFNGLGETUNIFORMOFFSETEXTPROC glGetUniformOffset = NULL; // GL EXT fog coord PFNGLFOGCOORDPOINTEREXTPROC glFogCoordPointer

= NULL;

// GL ARB map buffer range PFNGLMAPBUFFERRANGEPROC glMapBufferRange = NULL; PFNGLFLUSHMAPPEDBUFFERRANGEPROC glFlushMappedBufferRange = NULL; void ∗getprocaddress ( const char ∗name) { return SDL GL GetProcAddress (name) ; } VARP( ati skybox bug , 0 , 0 , 1) ; VAR( ati oq bug , 0 , 0 , 1) ; VAR( ati minmax bug , 0 , 0 , 1) ; VAR( ati dph bug , 0 , 0 , 1) ; VAR( a t i l i n e b u g , 0 , 0 , 1) ; VAR( ati cubemap bug , 0 , 0 , 1) ; VAR( ati ubo bug , 0 , 0 , 1) ; VAR( nvidia scissor bug , 0 , 0 , 1) ; VAR( intel immediate bug , 0 , 0 , 1) ; VAR( i n t e l v e r t e x a r r a y b u g , 0 , 0 , 1) ; VAR( apple glsldepth bug , 0 , 0 , 1) ; VAR( apple ff bug , 0 , 0 , 1) ; VAR( apple vp bug , 0 , 0 , 1) ; VAR( sdl backingstore bug , −1, 0 , 1) ; VAR( avoidshaders , 1 , 0 , 0) ; VAR( p r e f e r g l s l , 1 , 0 , 0) ;

s t a t i c bool checkseries ( const char ∗s , i n t low , i n t high ) { while (∗s && ! i s d i g i t (∗s ) ) ++s ; i f ( ! ∗ s ) return f a l s e ; i n t n = 0; while ( i s d i g i t (∗s ) ) n = n∗10 + (∗s++ − ’ 0 ’ ) ; return n >= low && n < high ; }

bool hasext ( const char ∗exts , const char ∗ext ) { i n t len = s t r l e n ( ext ) ; i f ( len ) f o r ( const char ∗cur = exts ; ( cur = s t r s t r ( cur , ext ) ) ; cur += len ) { i f ( ( cur == exts || cur[−1] == ’ ’ ) && ( cur [ len ] == ’ ’ || ! cur [ len ] ) ) return true ; } return f a l s e ; } void gl checkextensions ( ) { const char ∗vendor = ( const char ∗) glGetString (GL VENDOR) ; const char ∗exts = ( const char ∗) glGetString ( GL EXTENSIONS ) ; const char ∗renderer = ( const char ∗) glGetString (GL RENDERER) ; const char ∗version = ( const char ∗) glGetString ( GL VERSION ) ; conoutf ( CON INIT , ” Renderer : %s (%s ) ” , renderer , vendor ) ; conoutf ( CON INIT , ” Driver : %s ” , version ) ;

= NULL;

// GL EXT multi draw arrays PFNGLMULTIDRAWARRAYSEXTPROC glMultiDrawArrays PFNGLMULTIDRAWELEMENTSEXTPROC glMultiDrawElements

381

#ifdef APPLE extern i n t mac osversion ( ) ; i n t osversion = mac osversion ( ) ; sdl backingstore bug = −1; #endif

/∗ 0x0A0500 = 10.5 ( Leopard ) ∗/

bool mesa = f a l s e , i n t e l = f a l s e , a t i = f a l s e , nvidia = f a l s e ; i f ( s t r s t r ( renderer , ”Mesa ” ) || s t r s t r ( version , ”Mesa ” ) ) { mesa = true ; i f ( s t r s t r ( renderer , ” I n t e l ” ) ) i n t e l = true ; } e l s e i f ( s t r s t r ( vendor , ” NVIDIA ” ) ) nvidia = true ; e l s e i f ( s t r s t r ( vendor , ” ATI ” ) || s t r s t r ( vendor , ” Advanced Micro Devices ” ) ) a t i = true ; e l s e i f ( s t r s t r ( vendor , ” I n t e l ” ) ) i n t e l = true ; uint glmajorversion , glminorversion ; i f ( sscanf ( version , ” %u.%u” , &glmajorversion , &glminorversion ) ! = 2) g l v e r s i o n = 100; e l s e g l v e r s i o n = glmajorversion∗100 + glminorversion∗10; //extern i n t shaderprecision ; // d e f a u l t to low precision shaders on c e r t a i n cards , can be overridden with −f3 // char ∗weakcards [ ] = { ” GeForce FX” , ”Quadro FX” , ”6200” , ”9500” , ”9550” , ”9600” , ”9700” , ”9800” , ”X300” , ”X600” , ” FireGL ” , ” I n t e l ” , ”Chrome” , NULL } // i f ( shaderprecision ==2) f o r ( char ∗∗wc = weakcards ; ∗wc ; wc++) i f ( s t r s t r ( renderer , ∗wc ) ) shaderprecision = 1; GLint v a l ; glGetIntegerv ( GL MAX TEXTURE SIZE, &v a l ) ; hwtexsize = v a l ; i f ( hasext ( exts , ” GL EXT texture env combine ” ) || hasext ( exts , ” GL ARB texture env combine ” ) ) { hasTE = true ; i f ( hasext ( exts , ” GL ARB texture env crossbar ” ) ) hasTEX = true ; i f ( hasext ( exts , ” GL ATI texture env combine3 ” ) ) hasTE3 = true ; i f ( hasext ( exts , ” GL NV texture env combine4 ” ) ) hasTE4 = true ; i f ( hasext ( exts , ” GL EXT texture env dot3 ” ) || hasext ( exts , ” GL ARB texture env dot3 ” ) ) hasD3 = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB texture env combine extension . ” ) ;

382

Foundations of Videogame Programming Code Repository

} e l s e conoutf (CON WARN, ”WARNING: No texture env combine extension ! ( your video card i s WAY too old ) ” ) ; i f ( hasext ( exts , ” GL ARB multitexture ” ) ) { glActiveTexture = (PFNGLACTIVETEXTUREARBPROC) getprocaddress ( ” glActiveTextureARB ” ) ; g l C l i e n t A c t i v e T e x t u r e = (PFNGLCLIENTACTIVETEXTUREARBPROC) getprocaddress ( ” glClientActiveTextureARB ” ) ; glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FARBPROC) getprocaddress ( ” glMultiTexCoord2fARB ” ) ; glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FARBPROC) getprocaddress ( ” glMultiTexCoord3fARB ” ) ; glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FARBPROC) getprocaddress ( ” glMultiTexCoord4fARB ” ) ; hasMT = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB multitexture extension .”) ; } e l s e conoutf (CON WARN, ”WARNING: No multitexture extension ! ” ) ;

i f ( hasext ( exts , ” GL ARB vertex buffer object ” ) ) { hasVBO = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB vertex buffer object extension . ” ) ; } e l s e conoutf (CON WARN, ”WARNING: No v e r t e x b u f f e r o b j e c t extension ! ( geometry heavy maps w i l l be SLOW) ” ) ; #ifdef APPLE /∗ VBOs over 256KB seem to destroy performance on 10.5 , but not in 10.6 ∗/ extern i n t maxvbosize ; i f ( osversion < 0x0A0600 ) maxvbosize = min ( maxvbosize , 8192) ; #endif i f ( hasext ( exts , ” GL ARB pixel buffer object ” ) ) { hasPBO = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB pixel buffer object extension . ” ) ; } i f (hasVBO || hasPBO ) { glGenBuffers = (PFNGLGENBUFFERSARBPROC) getprocaddress ( ” glGenBuffersARB ” ) ; glBindBuffer = (PFNGLBINDBUFFERARBPROC) getprocaddress ( ” glBindBufferARB ” ) ; glMapBuffer = (PFNGLMAPBUFFERARBPROC) getprocaddress ( ” glMapBufferARB ” ) ; glUnmapBuffer = (PFNGLUNMAPBUFFERARBPROC) getprocaddress ( ” glUnmapBufferARB ” ) ; glBufferData = (PFNGLBUFFERDATAARBPROC) getprocaddress ( ” glBufferDataARB ” ) ; glBufferSubData = (PFNGLBUFFERSUBDATAARBPROC) getprocaddress ( ” glBufferSubDataARB ” ) ; glDeleteBuffers = (PFNGLDELETEBUFFERSARBPROC) getprocaddress ( ” glDeleteBuffersARB ” ) ; glGetBufferSubData = (PFNGLGETBUFFERSUBDATAARBPROC) getprocaddress ( ” glGetBufferSubDataARB ” ) ; } i f ( hasext ( exts , ” GL EXT draw range elements ” ) ) { glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSEXTPROC) getprocaddress ( ” glDrawRangeElementsEXT ” ) ; hasDRE = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT draw range elements extension . ” ) ; } i f ( hasext ( exts , ” GL EXT multi draw arrays ” ) ) { glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSEXTPROC) getprocaddress ( ” glMultiDrawArraysEXT ” ) ; glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSEXTPROC) getprocaddress ( ” glMultiDrawElementsEXT ” ) ; hasMDA = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT multi draw arrays extension . ” ) ; } #ifdef APPLE // f l o a t i n g point FBOs not f u l l y supported u n t i l 10.5 i f ( osversion>=0x0A0500 ) #endif i f ( hasext ( exts , ” GL ARB texture float ” ) || hasext ( exts , ” GL ATI texture float ” ) ) { hasTF = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB texture float extension

.”) ; shadowmap = 1; extern i n t smoothshadowmappeel ; smoothshadowmappeel = 1; } i f ( hasext ( exts , ” GL NV float buffer ” ) ) { hasNVFB = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL NV float buffer extension .”) ; } i f ( hasext ( exts , ” GL EXT framebuffer object ” ) ) { glBindRenderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC) getprocaddress ( ” glBindRenderbufferEXT ” ) ; glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC) getprocaddress ( ” glDeleteRenderbuffersEXT ” ) ; glGenRenderbuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) getprocaddress ( ” glGenRenderbuffersEXT ” ) ; glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEEXTPROC) getprocaddress ( ” glRenderbufferStorageEXT ” ) ; glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) getprocaddress ( ” glCheckFramebufferStatusEXT ” ) ; glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) getprocaddress ( ” glBindFramebufferEXT ” ) ; = (PFNGLDELETEFRAMEBUFFERSEXTPROC) glDeleteFramebuffers getprocaddress ( ” glDeleteFramebuffersEXT ” ) ; glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) getprocaddress ( ” glGenFramebuffersEXT ” ) ; glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) getprocaddress ( ” glFramebufferTexture2DEXT ” ) ; glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) getprocaddress ( ” glFramebufferRenderbufferEXT ” ) ; glGenerateMipmap = (PFNGLGENERATEMIPMAPEXTPROC) getprocaddress ( ” glGenerateMipmapEXT ” ) ; hasFBO = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT framebuffer object extension . ” ) ; i f ( hasext ( exts , ” GL EXT framebuffer blit ” ) ) { glBlitFramebuffer = (PFNGLBLITFRAMEBUFFEREXTPROC) getprocaddress ( ” glBlitFramebufferEXT ” ) ; hasFBB = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT framebuffer blit extension . ” ) ; } } e l s e conoutf (CON WARN, ”WARNING: No framebuffer o b j e c t support . ( r e f l e c t i v e water may be slow ) ” ) ; i f ( hasext ( exts , ” GL ARB occlusion query ” ) ) { GLint b i t s ; glGetQueryiv = (PFNGLGETQUERYIVARBPROC) getprocaddress ( ” glGetQueryivARB ” ) ; glGetQueryiv ( GL SAMPLES PASSED ARB, GL QUERY COUNTER BITS ARB, & bits ) ; i f ( bits ) { glGenQueries = (PFNGLGENQUERIESARBPROC) getprocaddress ( ” glGenQueriesARB ” ) ; glDeleteQueries = (PFNGLDELETEQUERIESARBPROC) getprocaddress ( ” glDeleteQueriesARB ” ) ; glBeginQuery = (PFNGLBEGINQUERYARBPROC) getprocaddress ( ” glBeginQueryARB ” ) ; glEndQuery = (PFNGLENDQUERYARBPROC) getprocaddress ( ” glEndQueryARB ” ) ; glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVARBPROC) getprocaddress ( ” glGetQueryObjectivARB ” ) ; glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVARBPROC) getprocaddress ( ” glGetQueryObjectuivARB ” ) ; hasOQ = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB occlusion query extension . ” ) ; # i f defined ( APPLE ) && SDL BYTEORDER == SDL BIG ENDIAN i f ( a t i && ( osversion=0x0A0500 ) // f i x e d in 1055 f o r some hardware . . but not a l l . . { a p p l e f f b u g = 1; //conoutf (CON WARN, ”WARNING: Using Leopard ARB position invariant bug workaround . ( use \”/ a p p l e f f b u g 0\” to disable i f unnecessary ) ” ) ; }

i f ( g l v e r s i o n >= 210) { glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) getprocaddress ( ” glUniformMatrix2x3fv ” ) ; glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) getprocaddress ( ” glUniformMatrix3x2fv ” ) ; glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) getprocaddress ( ” glUniformMatrix2x4fv ” ) ; (PFNGLUNIFORMMATRIX4X2FVPROC) glUniformMatrix4x2fv = getprocaddress ( ” glUniformMatrix4x2fv ” ) ; glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) getprocaddress ( ” glUniformMatrix3x4fv ” ) ; glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) getprocaddress ( ” glUniformMatrix4x3fv ” ) ; } #endif extern bool checkglslsupport ( ) ; i f ( checkglslsupport ( ) ) { hasGLSL = true ; hasglsl = 1; #ifdef APPLE // i f ( osversion= 0x0A0600 ) { i f ( g l s l v e r s i o n >= 120) p r e f e r g l s l = 1; } else

#endif } i f ( g l v e r s i o n >= 200) { #ifndef APPLE glCreateProgram = (PFNGLCREATEPROGRAMPROC) getprocaddress ( ” glCreateProgram ” ) ; glDeleteProgram = (PFNGLDELETEPROGRAMPROC) getprocaddress ( ” glDeleteProgram ” ) ; glUseProgram = (PFNGLUSEPROGRAMPROC) getprocaddress ( ” glUseProgram ” ) ; glCreateShader = (PFNGLCREATESHADERPROC) getprocaddress ( ” glCreateShader ” ) ; glDeleteShader = (PFNGLDELETESHADERPROC) getprocaddress ( ” glDeleteShader ” ) ; glShaderSource = (PFNGLSHADERSOURCEPROC) getprocaddress ( ” glShaderSource ” ) ; glCompileShader = (PFNGLCOMPILESHADERPROC) getprocaddress ( ” glCompileShader ” ) ; glGetShaderiv = (PFNGLGETSHADERIVPROC) getprocaddress ( ” glGetShaderiv ” ) ; glGetProgramiv = (PFNGLGETPROGRAMIVPROC) getprocaddress ( ” glGetProgramiv ” ) ; (PFNGLATTACHSHADERPROC) glAttachShader = getprocaddress ( ” glAttachShader ” ) ; glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) getprocaddress ( ” glGetProgramInfoLog ” ) ; glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) getprocaddress ( ” glGetShaderInfoLog ” ) ; (PFNGLLINKPROGRAMPROC) glLinkProgram = getprocaddress ( ” glLinkProgram ” ) ; glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) getprocaddress ( ” glGetUniformLocation ” ) ; glUniform1f = (PFNGLUNIFORM1FPROC) getprocaddress ( ” glUniform1f ” ) ; glUniform2f = (PFNGLUNIFORM2FPROC) getprocaddress ( ” glUniform2f ” ) ; glUniform3f = (PFNGLUNIFORM3FPROC) getprocaddress ( ” glUniform3f ” ) ; glUniform4f = (PFNGLUNIFORM4FPROC) getprocaddress ( ” glUniform4f ” ) ; glUniform1fv = (PFNGLUNIFORM1FVPROC) getprocaddress ( ” glUniform1fv ” ) ; glUniform2fv = (PFNGLUNIFORM2FVPROC) getprocaddress ( ” glUniform2fv ” ) ; glUniform3fv = (PFNGLUNIFORM3FVPROC) getprocaddress ( ” glUniform3fv ” ) ; glUniform4fv = (PFNGLUNIFORM4FVPROC) getprocaddress ( ” glUniform4fv ” ) ; glUniform1i = (PFNGLUNIFORM1IPROC) getprocaddress ( ” glUniform1i ” ) ; glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) getprocaddress ( ” glBindAttribLocation ” ) ; glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)

383

#ifdef

#endif i f ( g l s l v e r s i o n >= 130) p r e f e r g l s l = 1; } } extern i n t reservedynlighttc , reserveshadowmaptc , batchlightmaps , f f d y n l i g h t s , fpdepthfx ; i f ( ati ) { //conoutf (CON WARN, ”WARNING: ATI cards may show garbage in skybox . ( use \”/ati skybox bug 1\” to f i x ) ” ) ; r e s e r v e d y n l i g h t t c = 2; reserveshadowmaptc = 3; minimizetcusage = 1; emulatefog = 1; i f ( hasTF && hasNVFB ) fpdepthfx = 1; } e l s e i f ( nvidia ) { reservevpparams = 10; rtsharefb = 0; // work−around f o r strange d r i v e r s t a l l s i n v o l v i n g when using many FBOs extern i n t f i l l t j o i n t s ; i f ( ! hasext ( exts , ” GL EXT gpu shader4 ” ) ) f i l l t j o i n t s = 0; // DX9 or l e s s NV cards seem to not cause many sparklies i f ( hasFBO && ! hasTF ) nvidia scissor bug = 1; // 5200 bug , c l e a r i n g with s c i s s o r on an FBO messes up on r e f l e c t i o n s , may a f f e c t l e s s e r cards too extern i n t fpdepthfx ; i f ( hasTF && ( ! s t r s t r ( renderer , ” GeForce ” ) || ! checkseries ( renderer , 6000, 6600) ) ) fpdepthfx = 1; // FP f i l t e r i n g causes software f a l l b a c k on 6200? } else { i f ( intel ) { APPLE #ifdef apple vp bug = 1; intel immediate bug = 1; #endif # i f d e f WIN32 intel immediate bug = 1; i n t e l v e r t e x a r r a y b u g = 1;

384

Foundations of Videogame Programming Code Repository

#endif }

i f ( hasext ( exts , ” GL EXT texture rectangle ” ) || hasext ( exts , ” GL ARB texture rectangle ” ) )

i f ( ! hasGLSL || ! p r e f e r g l s l ) { avoidshaders = 1; i f ( hwtexsize < 4096) { maxtexsize = hwtexsize >= 2048 ? 512 : 256; batchlightmaps = 0; } i f ( ! hasTF ) f f d y n l i g h t s = 0; }

{

reservevpparams = 20;

{

usetexrect = 1; hasTR = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB texture rectangle extension . ” ) ; } e l s e i f ( hasMT && hasshaders ) conoutf (CON WARN, ”WARNING: No texture rectangle support . ( no f u l l screen shaders ) ” ) ; i f ( hasext ( exts , ” GL EXT packed depth stencil ” ) || hasext ( exts , ” GL NV packed depth stencil ” ) ) hasDS = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT packed depth stencil extension . ” ) ;

i f ( ! hasOQ) w a t e r r e f r a c t = 0; } } bool hasshaders = ( hasVP && hasFP ) || hasGLSL ; i f ( hasshaders ) { extern i n t matskel ; i f ( ! avoidshaders ) { matskel = 0; } } i f ( hasext ( exts , ” GL NV vertex program2 option ” ) ) { usevp2 = 1; hasVP2 = true ; } i f ( hasext ( exts , ” GL NV vertex program3 ” ) ) { usevp3 = 1; hasVP3 = true ; } i f ( hasext ( exts , ” GL EXT gpu program parameters ” ) ) { = (PFNGLPROGRAMENVPARAMETERS4FVEXTPROC glProgramEnvParameters4fv ) getprocaddress ( ” glProgramEnvParameters4fvEXT ” ) ; glProgramLocalParameters4fv = ( PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) getprocaddress ( ” glProgramLocalParameters4fvEXT ” ) ; hasPP = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT gpu program parameters extension . ” ) ; } i f ( hasext ( exts , ” GL ARB map buffer range ” ) ) { glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) getprocaddress ( ” glMapBufferRange ” ) ; glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) getprocaddress ( ” glFlushMappedBufferRange ” ) ; hasMBR = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB map buffer range . ” ) ; } i f ( hasext ( exts , ” GL ARB uniform buffer object ” ) ) { = (PFNGLGETUNIFORMINDICESPROC) glGetUniformIndices getprocaddress ( ” glGetUniformIndices ” ) ; = (PFNGLGETACTIVEUNIFORMSIVPROC) glGetActiveUniformsiv getprocaddress ( ” glGetActiveUniformsiv ” ) ; glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) getprocaddress ( ” glGetUniformBlockIndex ” ) ; glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) getprocaddress ( ” glGetActiveUniformBlockiv ” ) ; glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) getprocaddress ( ” glUniformBlockBinding ” ) ; glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) getprocaddress ( ” glBindBufferBase ” ) ; glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) getprocaddress ( ” glBindBufferRange ” ) ; useubo = 1; hasUBO = true ; i f ( a t i ) ati ubo bug = 1; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB uniform buffer object extension . ” ) ; } e l s e i f ( hasext ( exts , ” GL EXT bindable uniform ” ) ) { glUniformBuffer = (PFNGLUNIFORMBUFFEREXTPROC) getprocaddress ( ” glUniformBufferEXT ” ) ; glGetUniformBufferSize = (PFNGLGETUNIFORMBUFFERSIZEEXTPROC) getprocaddress ( ” glGetUniformBufferSizeEXT ” ) ; glGetUniformOffset = (PFNGLGETUNIFORMOFFSETEXTPROC) getprocaddress ( ” glGetUniformOffsetEXT ” ) ; usebue = 1; hasBUE = true ; i f ( a t i ) ati ubo bug = 1; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT bindable uniform extension . ” ) ; }

i f ( hasext ( exts , ” GL EXT blend minmax ” ) ) { glBlendEquation = (PFNGLBLENDEQUATIONEXTPROC) getprocaddress ( ” glBlendEquationEXT ” ) ; hasBE = true ; i f ( a t i ) ati minmax bug = 1; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT blend minmax extension .”) ; } i f ( hasext ( exts , ” GL EXT blend color ” ) ) { glBlendColor = (PFNGLBLENDCOLOREXTPROC) getprocaddress ( ” glBlendColorEXT ” ) ; hasBC = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT blend color extension .”) ; } i f ( hasext ( exts , ” GL EXT fog coord ” ) ) { glFogCoordPointer = (PFNGLFOGCOORDPOINTEREXTPROC) getprocaddress ( ” glFogCoordPointerEXT ” ) ; hasFC = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT fog coord extension . ” ) ; } i f ( hasext ( exts , ” GL ARB texture cube map ” ) ) { GLint v a l ; glGetIntegerv ( GL MAX CUBE MAP TEXTURE SIZE ARB, &v a l ) ; hwcubetexsize = v a l ; hasCM = true ; // On Catalyst 10.2 , issuing an occlusion query on the f i r s t draw using a given cubemap texture causes a nasty crash i f ( a t i ) ati cubemap bug = 1; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB texture cube map extension . ” ) ; } e l s e conoutf (CON WARN, ”WARNING: No cube map texture support . ( no r e f l e c t i v e glass ) ” ) ; extern i n t usenp2 ; i f ( hasext ( exts , ” GL ARB texture non power of two ” ) ) { hasNP2 = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB texture non power of two extension . ” ) ; } e l s e i f ( usenp2 ) conoutf (CON WARN, ”WARNING: Non−power−of−two textures not supported ! ” ) ; i f ( hasext ( exts , ” GL ARB texture compression ” ) ) { glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) getprocaddress ( ” glCompressedTexImage3DARB ” ) ; glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) getprocaddress ( ” glCompressedTexImage2DARB ” ) ; (PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) glCompressedTexImage1D = getprocaddress ( ” glCompressedTexImage1DARB ” ) ; glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) getprocaddress ( ” glCompressedTexSubImage3DARB ” ) ; glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) getprocaddress ( ” glCompressedTexSubImage2DARB ” ) ; glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) getprocaddress ( ” glCompressedTexSubImage1DARB ” ) ; glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) getprocaddress ( ” glGetCompressedTexImageARB ” ) ; hasTC = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB texture compression . ” ) ; i f ( hasext ( exts , ” GL EXT texture compression s3tc ” ) ) {

engine/rendergl.cpp

#ifdef

hasS3TC = true ; APPLE usetexcompress = 1;

385 d e p t h f x f i l t e r = 0; blurdepthfx = 0;

} }

#e l s e }

i f ( ! mesa ) usetexcompress = 2; }

#endif i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT texture compression s3tc extension . ” ) ; } e l s e i f ( hasext ( exts , ” GL EXT texture compression dxt1 ” ) && hasext ( exts , ” GL ANGLE texture compression dxt3 ” ) && hasext ( exts , ” GL ANGLE texture compression dxt5 ” ) ) { hasS3TC = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL EXT texture compression dxt1 extension . ” ) ; } i f ( hasext ( exts , ” GL 3DFX texture compression FXT1 ” ) ) { hasFXT1 = true ; i f ( mesa ) usetexcompress = max( usetexcompress , 1) ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL 3DFX texture compression FXT1 . ” ) ; }

void g l e x t ( char ∗ext ) { const char ∗exts = ( const char ∗) glGetString ( GL EXTENSIONS ) ; i n t r e t ( hasext ( exts , ext ) ? 1 : 0) ; } COMMAND( g l e x t , ” s ” ) ; void g l i n i t ( i n t w, i n t h , i n t bpp , i n t depth , i n t fsaa ) { glViewport ( 0 , 0 , w, h ) ; glClearColor ( 0 , 0 , 0 , 0) ; glClearDepth ( 1 ) ; glDepthFunc ( GL LESS ) ; glDisable ( GL DEPTH TEST ) ; glShadeModel (GL SMOOTH) ;

}

glDisable (GL FOG) ; g l F o g i (GL FOG MODE, GL LINEAR ) ; //glHint ( GL FOG HINT , GL NICEST ) ; GLfloat f o g c o l o r [ 4 ] = { 0 , 0 , 0 , 0 }; glFogfv (GL FOG COLOR, f o g c o l o r ) ;

i f ( hasext ( exts , ” G L E X T t e x t u r e f i l t e r a n i s o t r o p i c ” ) ) { GLint v a l ; glGetIntegerv ( GL MAX TEXTURE MAX ANISOTROPY EXT, &v a l ) ; hwmaxaniso = v a l ; hasAF = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using G L E X T t e x t u r e f i l t e r a n i s o t r o p i c extension . ” ) ; } i f ( hasext ( exts , ” GL SGIS generate mipmap ” ) ) { hasGM = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL SGIS generate mipmap extension . ” ) ; } i f ( hasext ( exts , ” GL ARB depth texture ” ) ) { hasSGIDT = hasDT = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL ARB depth texture extension .”) ; } e l s e i f ( hasext ( exts , ” GL SGIX depth texture ” ) ) { hasSGIDT = true ; i f ( dbgexts ) conoutf ( CON INIT , ” Using GL SGIX depth texture extension . ” ) ; }

glEnable ( GL LINE SMOOTH ) ; //glHint ( GL LINE SMOOTH HINT, GL NICEST ) ; glFrontFace (GL CW) ; glCullFace (GL BACK) ; glDisable ( GL CULL FACE ) ; #ifdef APPLE i f ( sdl backingstore bug ) { i f ( fsaa ) { sdl backingstore bug = 1; // since SDL doesn ’ t add kCGLPFABackingStore to the pixelformat and so i t isn ’ t guaranteed to be preserved − only manifests when using fsaa ? //conoutf (CON WARN, ”WARNING: Using SDL backingstore workaround . ( use \”/sdl backingstore bug 0\” to disable i f unnecessary ) ” ) ; } e l s e sdl backingstore bug = −1; } #endif extern i n t useshaders , f o r c e g l s l ; bool hasshaders = ( hasVP && hasFP ) || hasGLSL ; i f ( ! useshaders || ( useshadersyaw ) ) ; ICOMMAND( getcampitch , ” ” , ( ) , f l o a t r e t ( camera1−>pitch ) ) ; ICOMMAND( getcamroll , ” ” , ( ) , f l o a t r e t ( camera1−>r o l l ) ) ; ICOMMAND( getcampos , ” ” , ( ) , { defformatstring ( pos ) (”%s %s %s ” , f l o a t s t r ( camera1−>o . x ) , f l o a t s t r ( camera1−>o . y ) , f l o a t s t r ( camera1−>o . z ) ) ; r e s u l t ( pos ) ; }) ; vec worldpos , camdir , camright , camup; void f i n d o r i e n t a t i o n ( ) { vecfromyawpitch ( camera1−>yaw , camera1−>pitch , 1 , 0 , camdir ) ; vecfromyawpitch ( camera1−>yaw , 0 , 0 , −1, camright ) ; vecfromyawpitch ( camera1−>yaw , camera1−>pitch +90, 1 , 0 , camup) ; i f ( raycubepos ( camera1−>o , camdir , worldpos , 0 , RAY CLIPMAT| RAY SKIPFIRST ) == −1) worldpos = vec ( camdir ) . mul(2∗ worldsize ) . add ( camera1−>o ) ; // otherwise 3dgui won ’ t work when outside o f map } void transplayer ( ) { // move from RH to Z−up LH quake s t y l e worldspace glLoadMatrixf ( viewmatrix . v ) ; g l R o t a t e f ( camera1−>r o l l , 0 , 1 , 0) ; g l R o t a t e f ( camera1−>pitch , −1, 0 , 0) ; g l R o t a t e f ( camera1−>yaw , 0 , 0 , −1) ; g l T r a n s l a t e f(−camera1−>o . x , −camera1−>o . y , −camera1−>o . z ) ; } f l o a t curfov = 100, curavatarfov = 65, fovy , aspect ; i n t farplane ; VARP( zoominvel , 0 , 250, 5000) ; VARP( zoomoutvel , 0 , 100, 5000) ; VARP( zoomfov , 10, 35, 60) ; VARP( fov , 10, 100, 150) ; VAR( avatarzoomfov , 10, 25, 60) ; VAR( avatarfov , 10, 65, 150) ; FVAR( avatardepth , 0 , 0.5 f , 1) ; FVARNP( aspect , forceaspect , 0 , 0 , 1e3f ) ; s t a t i c i n t zoommillis = 0; VARF( zoom, −1, 0 , 1 , i f ( zoom ) zoommillis = t o t a l m i l l i s ; ); void disablezoom ( ) { zoom = 0; zoommillis = t o t a l m i l l i s ; } void computezoom ( ) { i f ( ! zoom ) { curfov = f o v ; curavatarfov = avatarfov ; return ; } i f ( zoom < 0 && curfov >= f o v ) { zoom = 0; curfov = f o v ; curavatarfov = avatarfov ; return ; } // don ’ t zoom−out i f not zoomed−in i n t zoomvel = zoom > 0 ? zoominvel : zoomoutvel , o l d f o v = zoom > 0 ? f o v : zoomfov , newfov = zoom > 0 ? zoomfov : fov , oldavatarfov = zoom > 0 ? avatarfov : avatarzoomfov , newavatarfov = zoom > 0 ? avatarzoomfov : avatarfov ; f l o a t t = zoomvel ? f l o a t ( zoomvel − ( t o t a l m i l l i s − zoommillis ) ) / zoomvel : 0; i f ( t = 1) { curfov = newfov ; curavatarfov = newavatarfov ; } zoom = max( zoom, 0) ; } else { curfov = o l d f o v∗t + newfov∗(1 − t ) ; curavatarfov = oldavatarfov∗t + newavatarfov∗(1 − t ) ; } } FVARP( zoomsens , 1e−3f , 1 , 1000) ;

FVARP( zoomaccel , 0 , 0 , 1000) ; VARP( zoomautosens , 0 , 1 , 1) ; FVARP( s e n s i t i v i t y , 1e−3f , 3 , 1000) ; FVARP( s e n s i t i v i t y s c a l e , 1e−3f , 1 , 1000) ; VARP( invmouse , 0 , 0 , 1) ; FVARP( mouseaccel , 0 , 0 , 1000) ; VAR( thirdperson , 0 , 0 , 2) ; FVAR( thirdpersondistance , 0 , 20, 50) ; FVAR( thirdpersonup , −25, 0 , 25) ; FVAR( thirdpersonside , −25, 0 , 25) ; physent ∗camera1 = NULL; bool detachedcamera = f a l s e ; bool isthirdperson ( ) { return player ! = camera1 || detachedcamera || reflecting ; } void fixcamerarange ( ) { const f l o a t MAXPITCH = 90.0 f ; i f ( camera1−>pitch>MAXPITCH) camera1−>pitch = MAXPITCH; i f ( camera1−>pitchpitch = −MAXPITCH; while ( camera1−>yawyaw += 360.0 f ; while ( camera1−>yaw>=360.0 f ) camera1−>yaw −= 360.0 f ; } void mousemove ( i n t dx , i n t dy ) { i f ( ! game : : allowmouselook ( ) ) return ; f l o a t cursens = s e n s i t i v i t y , curaccel = mouseaccel ; i f ( zoom ) { i f ( zoomautosens ) { cursens = f l o a t ( s e n s i t i v i t y∗zoomfov ) / f o v ; curaccel = f l o a t ( mouseaccel∗zoomfov ) / f o v ; } else { cursens = zoomsens ; curaccel = zoomaccel ; } } i f ( curaccel && curtime && ( dx || dy ) ) cursens += curaccel ∗ s q r t f ( dx∗ dx + dy∗dy ) /curtime ; cursens /= 33.0 f∗s e n s i t i v i t y s c a l e ; camera1−>yaw += dx∗cursens ; camera1−>pitch −= dy∗cursens∗(invmouse ? −1 : 1) ; fixcamerarange ( ) ; i f ( camera1 ! = player && ! detachedcamera ) { player−>yaw = camera1−>yaw ; player−>pitch = camera1−>pitch ; } } void recomputecamera ( ) { game : : setupcamera ( ) ; computezoom ( ) ; bool shoulddetach = thirdperson > 1 || game : : detachcamera ( ) ; i f ( ! thirdperson && ! shoulddetach ) { camera1 = player ; detachedcamera = f a l s e ; } else { s t a t i c physent tempcamera ; camera1 = &tempcamera ; i f ( detachedcamera && shoulddetach ) camera1−>o = player−>o ; else { ∗camera1 = ∗player ; detachedcamera = shoulddetach ; } camera1−>r e s e t ( ) ; camera1−>type = ENT CAMERA; camera1−>c o l l i d e t y p e = COLLIDE AABB; camera1−>move = −1; camera1−>eyeheight = camera1−>aboveeye = camera1−>radius = camera1 −>xradius = camera1−>yradius = 2; vec dir , up, side ; vecfromyawpitch ( camera1−>yaw , camera1−>pitch , −1, 0 , d i r ) ; vecfromyawpitch ( camera1−>yaw , camera1−>pitch +90, 1 , 0 , up ) ; vecfromyawpitch ( camera1−>yaw , 0 , 0 , −1, side ) ; i f ( game : : collidecamera ( ) ) { movecamera ( camera1 , dir , thirdpersondistance , 1) ; movecamera ( camera1 , dir , clamp ( thirdpersondistance − camera1−>o . d i s t ( player−>o ) , 0.0 f , 1.0 f ) , 0.1 f ) ; i f ( thirdpersonup ) {

engine/rendergl.cpp vec pos = camera1−>o ; f l o a t d i s t = fabs ( thirdpersonup ) ; i f ( thirdpersonup < 0) up . neg ( ) ; movecamera ( camera1 , up, dist , 1) ; movecamera ( camera1 , up, clamp ( d i s t − camera1−>o . d i s t ( pos ) , 0.0 f , 1.0 f ) , 0.1 f ) ; } i f ( thirdpersonside ) { vec pos = camera1−>o ; f l o a t d i s t = fabs ( thirdpersonside ) ; i f ( thirdpersonside < 0) side . neg ( ) ; movecamera ( camera1 , side , dist , 1) ; movecamera ( camera1 , side , clamp ( d i s t − camera1−>o . d i s t ( pos ) , 0.0 f , 1.0 f ) , 0.1 f ) ; } } else { camera1−>o . add ( vec ( d i r ) . mul ( thirdpersondistance ) ) ; i f ( thirdpersonup ) camera1−>o . add ( vec ( up ) . mul ( thirdpersonup ) ) ; i f ( thirdpersonside ) camera1−>o . add ( vec ( side ) . mul ( thirdpersonside )); } } s e t v i e w c e l l ( camera1−>o ) ; } extern const g l m a t r i x f viewmatrix ( vec4(−1, 0 , 0 , 0) , vec4 ( 0 , 0 , 1 , 0) , vec4 ( 0 , −1, 0 , 0) ) ; g l m a t r i x f mvmatrix , projmatrix , mvpmatrix , invmvmatrix , invmvpmatrix ;

projectioncount ++; } void popprojection ( ) { −−projectioncount ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; i f ( projectioncount > 0) { glPushMatrix ( ) ; i f ( fogging ) { glMultMatrixf ( mvmatrix . v ) ; glMultMatrixf ( invfogmatrix . v ) ; } } glMatrixMode (GL MODELVIEW) ; } FVAR( p o l y g o n o f f s e t f a c t o r , −1e4f , −3.0f , 1e4f ) ; FVAR( polygonoffsetunits , −1e4f , −3.0f , 1e4f ) ; FVAR( depthoffset , −1e4f , 0.01 f , 1e4f ) ; void enablepolygonoffset (GLenum type ) { i f ( ! depthoffset ) { glPolygonOffset ( p o l y g o n o f f s e t f a c t o r , polygonoffsetunits ) ; glEnable ( type ) ; return ; }

void readmatrices ( ) { glGetFloatv (GL MODELVIEW MATRIX, mvmatrix . v ) ; glGetFloatv ( GL PROJECTION MATRIX, projmatrix . v ) ;

bool clipped = r e f l e c t z < 1e15f && r e f l e c t c l i p ; g l m a t r i x f o f f s e t m a t r i x = clipped ? clipmatrix : projmatrix ; o f f s e t m a t r i x [ 1 4 ] += depthoffset ∗ projmatrix [ 1 0 ] ;

mvpmatrix . mul ( projmatrix , mvmatrix ) ; invmvmatrix . i n v e r t ( mvmatrix ) ; invmvpmatrix . i n v e r t ( mvpmatrix ) ;

glMatrixMode ( GL PROJECTION ) ; i f ( ! clipped ) glPushMatrix ( ) ; glLoadMatrixf ( o f f s e t m a t r i x . v ) ; i f ( fogging ) { glMultMatrixf ( mvmatrix . v ) ; glMultMatrixf ( invfogmatrix . v ) ; } glMatrixMode (GL MODELVIEW) ;

} FVAR( nearplane , 0.01 f , 0.54 f , 2.0 f ) ; void p r o j e c t ( f l o a t fovy , f l o a t aspect , i n t farplane , bool f l i p x = f a l s e , bool f l i p y = f a l s e , bool swapxy = f a l s e , f l o a t zscale = 1) {

387

} glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; i f ( swapxy ) g l R o t a t e f (90 , 0 , 0 , 1) ; i f ( f l i p x || f l i p y ! = swapxy || zscale ! = 1 ) g l S c a l e f ( f l i p x ? −1 : 1 , f l i p y ! = swapxy ? −1 : 1 , zscale ) ; GLdouble y d i s t = nearplane ∗ tan ( fovy/2∗RAD) , x d i s t = y d i s t ∗ aspect ; glFrustum(−xdist , xdist , −ydist , ydist , nearplane , farplane ) ; glMatrixMode (GL MODELVIEW) ;

void d i s a b l e p o l y g o n o f f s e t (GLenum type ) { i f ( ! depthoffset ) { glDisable ( type ) ; return ; }

} bool clipped = r e f l e c t z < 1e15f && r e f l e c t c l i p ; vec calcavatarpos ( const vec &pos , f l o a t d i s t ) { vec eyepos ; mvmatrix . transform ( pos , eyepos ) ; GLdouble y d i s t = nearplane ∗ tan ( curavatarfov/2∗RAD) , x d i s t = y d i s t ∗ aspect ; vec4 scrpos ; scrpos . x = eyepos . x∗nearplane/ x d i s t ; scrpos . y = eyepos . y∗nearplane/ y d i s t ; scrpos . z = ( eyepos . z ∗( farplane + nearplane ) − 2∗nearplane∗farplane ) / ( farplane − nearplane ) ; scrpos .w = −eyepos . z ; vec worldpos = invmvpmatrix . perspectivetransform ( scrpos ) ; vec d i r = vec ( worldpos ) . sub ( camera1−>o ) . r e s c a l e ( d i s t ) ; return d i r . add ( camera1−>o ) ; }

glMatrixMode ( GL PROJECTION ) ; i f ( clipped ) { glLoadMatrixf ( clipmatrix . v ) ; i f ( fogging ) { glMultMatrixf ( mvmatrix . v ) ; glMultMatrixf ( invfogmatrix . v ) ; } } e l s e glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; } void calcspherescissor ( const vec ¢er , f l o a t size , f l o a t &sx1 , f l o a t & sy1 , f l o a t &sx2 , f l o a t &sy2 ) {

VAR( r e f l e c t c l i p , 0 , 6 , 64) ; VAR( r e f l e c t c l i p a v a t a r , −64, 0 , 64) ; g l m a t r i x f clipmatrix ; s t a t i c const g l m a t r i x f dummymatrix ; s t a t i c i n t projectioncount = 0; void pushprojection ( const g l m a t r i x f &m = dummymatrix ) { glMatrixMode ( GL PROJECTION ) ; i f ( projectioncount 2∗s i z e ) { sx1 = sy1 = 1; sx2 = sy2 = −1; return ; } f l o a t z z r r = e . z∗e . z − s i z e∗size , dx = e . x∗e . x + zzrr , dy = e . y∗e . y + zzrr , f o c a l d i s t = 1.0 f /tan ( fovy ∗0.5 f∗RAD) ; sx1 = sy1 = −1; sx2 = sy2 = 1; #define CHECKPLANE( c , dir , f o c a l d i s t , low , high ) \ do { \ f l o a t nzc = ( cz∗cz + 1) / ( cz d i r drt ) − cz , \ pz = ( d##c ) / ( nzc∗e . c − e . z ) ; \ i f ( pz > 0) \ { \ f l o a t c = ( f o c a l d i s t )∗nzc , \

388

Foundations of Videogame Programming Code Repository pc = pz∗nzc ; \ i f ( pc < e . c ) low = c ; \ e l s e i f ( pc > e . c ) high = c ; \

} \ } while ( 0 ) i f ( dx > 0) { f l o a t cz = e . x/e . z , drt = s q r t f ( dx ) / s i z e ; CHECKPLANE( x , −, f o c a l d i s t /aspect , sx1 , sx2 ) ; CHECKPLANE( x , + , f o c a l d i s t /aspect , sx1 , sx2 ) ; } i f ( dy > 0) { f l o a t cz = e . y/e . z , drt = s q r t f ( dy ) / s i z e ; CHECKPLANE( y , −, f o c a l d i s t , sy1 , sy2 ) ; CHECKPLANE( y , + , f o c a l d i s t , sy1 , sy2 ) ; }

i n t mat = c . material&MATF VOLUME; i f ( mat ! = fogmat ) { abovemat = i s l i q u i d ( mat ) ? c . material : MAT AIR ; return o . z ; } o . z = co . z + c s i z e ; } while ( o . z < worldsize ) ; abovemat = MAT AIR ; return worldsize ; } s t a t i c void blendfog ( i n t fogmat , f l o a t blend , f l o a t logblend , f l o a t & s t a r t , f l o a t &end , f l o a t ∗fogc ) { switch ( fogmat&MATF VOLUME) { case MAT WATER: { const bvec &wcol = getwatercolor ( fogmat ) ; i n t wfog = getwaterfog ( fogmat ) ; loopk ( 3 ) fogc [ k ] += blend∗wcol [ k]/255.0 f ; end += logblend∗min ( fog , max( wfog∗4, 32) ) ; break ; }

} s t a t i c i n t s c i s s o r i n g = 0; s t a t i c GLint o l d s c i s s o r [ 4 ] ; i n t pushscissor ( f l o a t sx1 , f l o a t sy1 , f l o a t sx2 , f l o a t sy2 ) { s c i s s o r i n g = 0; i f ( sx1 = 1) return 0; sx1 sy1 sx2 sy2

= = = =

max( sx1 , max( sy1 , min ( sx2 , min ( sy2 ,

case MAT LAVA: { const bvec &l c o l = g e t l a v a c o l o r ( fogmat ) ; i n t l f o g = g e t l a v a f o g ( fogmat ) ; loopk ( 3 ) fogc [ k ] += blend∗l c o l [ k]/255.0 f ; end += logblend∗min ( fog , max( l f o g ∗4, 32) ) ; break ; }

−1.0f ) ; −1.0f ) ; 1.0 f ) ; 1.0 f ) ;

GLint viewport [ 4 ] ; glGetIntegerv ( GL VIEWPORT, viewport ) ; i n t sx = viewport [ 0 ] + i n t ( f l o o r ( ( sx1+1)∗0.5 f∗viewport [ 2 ] ) ) , sy = viewport [ 1 ] + i n t ( f l o o r ( ( sy1 +1)∗0.5 f∗viewport [ 3 ] ) ) , sw = viewport [ 0 ] + i n t ( c e i l ( ( sx2+1)∗0.5 f∗viewport [ 2 ] ) ) − sx , sh = viewport [ 1 ] + i n t ( c e i l ( ( sy2 +1)∗0.5 f∗viewport [ 3 ] ) ) − sy ; i f ( sw 8)&0xFF , fogcolour&0 xFF ) ; }) ; s t a t i c f l o a t findsurface ( i n t fogmat , const vec &v , i n t &abovemat ) { fogmat &= MATF VOLUME; i v e c o ( v ) , co ; int csize ; do { cube &c = lookupcube ( o . x , o . y , o . z , 0 , co , c s i z e ) ;

default : loopk ( 3 ) overlay [ k ] += blend ; break ; } } void drawfogoverlay ( i n t fogmat , f l o a t fogblend , i n t abovemat ) { notextureshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ;

engine/rendergl.cpp

glEnable (GL BLEND) ; glBlendFunc ( GL ZERO, GL SRC COLOR) ; f l o a t overlay [ 3 ] = { 0 , 0 , 0 }; blendfogoverlay ( fogmat , fogblend , overlay ) ; blendfogoverlay ( abovemat , 1−fogblend , overlay ) ; glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; g l C o l o r 3 f v ( overlay ) ; glBegin ( GL TRIANGLE STRIP ) ; g l V e r t e x 2 f (−1, −1) ; g l V e r t e x 2 f ( 1 , −1) ; g l V e r t e x 2 f (−1, 1) ; g l V e r t e x 2 f ( 1 , 1) ; glEnd ( ) ; glDisable (GL BLEND) ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } bool renderedgame = f a l s e ; void rendergame ( bool mainpass ) { game : : rendergame ( mainpass ) ; i f ( ! shadowmapping ) renderedgame = true ; } VARP( skyboxglare , 0 , 1 , 1) ; void drawglare ( ) { g l a r i n g = true ; r e f r a c t i n g = −1; f l o a t o l d f o g s t a r t , oldfogend , o l d f o g c o l o r [ 4 ] , zerofog [ 4 ] = { 0 , 0 , 0 , 1 }; glGetFloatv ( GL FOG START, &o l d f o g s t a r t ) ; glGetFloatv (GL FOG END, &oldfogend ) ; glGetFloatv (GL FOG COLOR, o l d f o g c o l o r ) ; g l F o g f ( GL FOG START, ( fog +64) /8) ; g l F o g f (GL FOG END, fog ) ; glFogfv (GL FOG COLOR, zerofog ) ; glClearColor ( 0 , 0 , 0 , 1) ; glClear ( ( skyboxglare ? 0 : GL COLOR BUFFER BIT ) | GL DEPTH BUFFER BIT ) ;

389

void drawreflection ( f l o a t z , bool r e f r a c t , i n t fogdepth , const bvec &c o l ) { r e f l e c t z = z < 0 ? 1e16f : z ; reflecting = ! refract ; r e f r a c t i n g = r e f r a c t ? ( z < 0 || camera1−>o . z >= z ? −1 : 1) : 0; fading = renderpath ! =R FIXEDFUNCTION && w a t e r r e f r a c t && waterfade && hasFBO && z>=0; fogging = r e f r a c t i n g=0; r e f r a c t f o g = fogdepth ; r e f r a c t c o l o r = fogging ? c o l : f o g c o l o r ; f l o a t o l d f o g s t a r t , oldfogend , o l d f o g c o l o r [ 4 ] ; glGetFloatv ( GL FOG START, &o l d f o g s t a r t ) ; glGetFloatv (GL FOG END, &oldfogend ) ; glGetFloatv (GL FOG COLOR, o l d f o g c o l o r ) ; i f ( fogging ) { g l F o g f ( GL FOG START, camera1−>o . z − z ) ; g l F o g f (GL FOG END, camera1−>o . z − ( z−max( r e f r a c t f o g , 1) ) ) ; GLfloat m[ 1 6 ] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, −camera1−>o . x , −camera1−>o . y , −camera1−>o . z , 1 }; memcpy( fogmatrix . v , m, s i z e o f (m) ) ; invfogmatrix . i n v e r t ( fogmatrix ) ; pushprojection ( ) ; glPushMatrix ( ) ; glLoadMatrixf ( fogmatrix . v ) ; f l o a t fogc [ 4 ] = { c o l . x/255.0 f , c o l . y/255.0 f , c o l . z/255.0 f , 1.0 f }; glFogfv (GL FOG COLOR, fogc ) ; } else { g l F o g f ( GL FOG START, ( fog +64) /8) ; g l F o g f (GL FOG END, fog ) ; f l o a t fogc [ 4 ] = { f o g c o l o r . x/255.0 f , f o g c o l o r . y/255.0 f , f o g c o l o r . z /255.0 f , 1.0 f }; glFogfv (GL FOG COLOR, fogc ) ; } i f ( fading ) { f l o a t scale = fogging ? −0.25 f : 0.25 f , o f f s e t = 2∗fabs ( scale ) − scale∗z ; setenvparamf ( ” waterfadeparams ” , SHPARAM VERTEX, 8 , scale , o f f s e t , − scale , o f f s e t + camera1−>o . z∗scale ) ; setenvparamf ( ” waterfadeparams ” , SHPARAM PIXEL, 8 , scale , o f f s e t , − scale , o f f s e t + camera1−>o . z∗scale ) ; } i f ( reflecting ) { glPushMatrix ( ) ; g l T r a n s l a t e f ( 0 , 0 , 2∗z ) ; g l S c a l e f ( 1 , 1 , −1) ; glFrontFace (GL CCW) ; }

rendergeom ( ) ; setenvmatrix ( ) ; i f ( skyboxglare ) drawskybox ( farplane , f a l s e ) ; renderreflectedmapmodels ( ) ; rendergame ( ) ; i f ( ! isthirdperson ( ) ) { p r o j e c t ( curavatarfov , aspect , farplane , f a l s e , f a l s e , f a l s e , avatardepth ) ; game : : renderavatar ( ) ; p r o j e c t ( fovy , aspect , farplane ) ; } renderwater ( ) ; rendermaterials ( ) ; renderalphageom ( ) ; renderparticles ( ) ; g l F o g f ( GL FOG START, o l d f o g s t a r t ) ; g l F o g f (GL FOG END, oldfogend ) ; glFogfv (GL FOG COLOR, o l d f o g c o l o r ) ; r e f r a c t i n g = 0; glaring = false ;

i f ( r e f l e c t c l i p && z>=0) { f l o a t z o f f s e t = r e f l e c t c l i p /4.0 f , z c l i p ; i f ( r e f r a c t i n g o . zo . z>=z c l i p && camera1−>o . z0 ? 1 : −1, r e f r a c t i n g>0 ? −z c l i p : z c l i p ) , clipplane ) ; clipmatrix . c l i p ( clipplane , projmatrix ) ; pushprojection ( clipmatrix ) ; } renderreflectedgeom ( r e f r a c t i n g=0 && caustics , fogging ) ;

} VARP( reflectmms , 0 , 1 , 1) ; VARR( refractsky , 0 , 0 , 1) ; g l m a t r i x f fogmatrix , invfogmatrix ;

i f ( r e f l e c t i n g || r e f r a c t i n g>0 || ( r e f r a c t i n g=0) pushprojection ( clipmatrix ) ; i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ;

i n t fogmat = lookupmaterial ( o ) &(MATF VOLUME|MATF INDEX ) ; s e t f o g ( fogmat ) ; glClear ( GL DEPTH BUFFER BIT ) ; i n t farplane = worldsize ∗2; p r o j e c t (90.0 f , 1.0 f , farplane , ! side . f l i p x , ! side . f l i p y , side . swapxy ) ; transplayer ( ) ; readmatrices ( ) ; findorientation ( ) ; setenvmatrix ( ) ;

} e l s e i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ;

glEnable (GL FOG) ; glEnable ( GL CULL FACE ) ; glEnable ( GL DEPTH TEST ) ; glEnable ( GL TEXTURE 2D ) ;

renderdecals ( ) ; i f ( reflectmms ) renderreflectedmapmodels ( ) ; rendergame ( ) ; i f ( r e f r a c t i n g && z>=0 && ! isthirdperson ( ) && fabs ( camera1−>o . z−z ) eyeheight + player−>aboveeye ) )

xtravertsva = x t r a v e r t s = glde = gbatches = 0;

{

visiblecubes ( ) ; g l m a t r i x f avatarproj ; avatarproj . perspective ( curavatarfov , aspect , nearplane , farplane ) ; if ( reflectclip ) { popprojection ( ) ; glmatrixf avatarclip ; plane clipplane ; invmvmatrix . transposedtransform ( plane ( 0 , 0 , r e f r a c t i n g , r e f l e c t c l i p a v a t a r /4.0 f − r e f r a c t i n g∗z ) , clipplane ) ; a v a t a r c l i p . c l i p ( clipplane , avatarproj ) ; pushprojection ( a v a t a r c l i p ) ; } e l s e pushprojection ( avatarproj ) ; game : : renderavatar ( ) ; popprojection ( ) ; i f ( r e f l e c t c l i p ) pushprojection ( clipmatrix ) ;

i f ( l i m i t s k y ( ) ) drawskybox ( farplane , true ) ; rendergeom ( ) ; i f ( ! l i m i t s k y ( ) ) drawskybox ( farplane , f a l s e ) ; //

queryreflections ( ) ; rendermapmodels ( ) ; renderalphageom ( ) ;

//

drawreflections ( ) ;

// //

renderwater ( ) ; rendermaterials ( ) ;

} glDisable ( GL TEXTURE 2D ) ; glDisable ( GL DEPTH TEST ) ; glDisable ( GL CULL FACE ) ; glDisable (GL FOG) ;

i f ( r e f r a c t i n g ) rendergrass ( ) ; rendermaterials ( ) ; renderalphageom ( fogging ) ; renderparticles ( ) ;

camera1 = oldcamera ; envmapping = f a l s e ;

i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; } i f ( r e f l e c t c l i p && z>=0) popprojection ( ) ;

bool modelpreviewing = f a l s e ; i f ( reflecting ) { glPopMatrix ( ) ; glFrontFace (GL CW) ;

namespace modelpreview { physent ∗oldcamera ; f l o a t o l d f o g s t a r t , oldfogend , o l d f o g c o l o r [ 4 ] ;

} physent camera ; i f ( fogging ) { popprojection ( ) ; glPopMatrix ( ) ; } g l F o g f ( GL FOG START, o l d f o g s t a r t ) ; g l F o g f (GL FOG END, oldfogend ) ; glFogfv (GL FOG COLOR, o l d f o g c o l o r ) ; r e f l e c t z = 1e16f ; r e f r a c t i n g = 0; r e f l e c t i n g = fading = fogging = f a l s e ; setenvmatrix ( ) ; } bool envmapping = f a l s e ; void drawcubemap ( i n t size , const vec &o , f l o a t yaw , f l o a t pitch , const cubemapside &side ) { envmapping = true ; physent ∗oldcamera = camera1 ; s t a t i c physent cmcamera ; cmcamera = ∗player ; cmcamera . r e s e t ( ) ; cmcamera . type = ENT CAMERA; cmcamera . o = o ; cmcamera . yaw = yaw ; cmcamera . pitch = pitch ; cmcamera . r o l l = 0; camera1 = &cmcamera ; s e t v i e w c e l l ( camera1−>o ) ; defaultshader−>set ( ) ;

void s t a r t ( bool background ) { f l o a t fovy = 90. f , aspect = 1. f ; envmapping = modelpreviewing = true ; oldcamera = camera1 ; camera = ∗camera1 ; camera . r e s e t ( ) ; camera . type = ENT CAMERA; camera . o = vec ( 0 , 0 , 0) ; camera . yaw = 0; camera . pitch = 0; camera . r o l l = 0; camera1 = &camera ; glGetFloatv ( GL FOG START, &o l d f o g s t a r t ) ; glGetFloatv (GL FOG END, &oldfogend ) ; glGetFloatv (GL FOG COLOR, o l d f o g c o l o r ) ; GLfloat fogc [ 4 ] = { 0 , 0 , 0 , 1 }; g l F o g f ( GL FOG START, 0) ; g l F o g f (GL FOG END, 1000000) ; glFogfv (GL FOG COLOR, fogc ) ; glClearColor ( fogc [ 0 ] , fogc [ 1 ] , fogc [ 2 ] , fogc [ 3 ] ) ; glClear ( ( background ? GL COLOR BUFFER BIT : 0) | GL DEPTH BUFFER BIT ) ; glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPushMatrix ( ) ; p r o j e c t ( fovy , aspect , 1024) ; transplayer ( ) ;

engine/rendergl.cpp readmatrices ( ) ; setenvmatrix ( ) ; glEnable ( GL CULL FACE ) ; glEnable ( GL DEPTH TEST ) ; } void end ( ) { glDisable ( GL CULL FACE ) ; glDisable ( GL DEPTH TEST ) ; defaultshader−>set ( ) ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ; g l F o g f ( GL FOG START, o l d f o g s t a r t ) ; g l F o g f (GL FOG END, oldfogend ) ; glFogfv (GL FOG COLOR, o l d f o g c o l o r ) ; glClearColor ( o l d f o g c o l o r [ 0 ] , o l d f o g c o l o r [ 1 ] , o l d f o g c o l o r [ 2 ] , oldfogcolor [ 3 ] ) ; camera1 = oldcamera ; envmapping = modelpreviewing = f a l s e ; } } bool minimapping = f a l s e ; GLuint minimaptex = 0; vec minimapcenter ( 0 , 0 , 0) , minimapradius ( 0 , 0 , 0) , minimapscale ( 0 , 0 , 0) ; void clearminimap ( ) { i f ( minimaptex ) { glDeleteTextures ( 1 , &minimaptex ) ; minimaptex = 0; } } VARR( minimapheight , 0 , 0 , 216)&0xFF , ( minimapcolour>>8)&0xFF , minimapcolour&0xFF ) ; }) ; VARR( minimapclip , 0 , 0 , 1) ; VARFP( minimapsize , 7 , 8 , 10, { i f ( minimaptex ) drawminimap ( ) ; }) ; void bindminimap ( ) { glBindTexture ( GL TEXTURE 2D, minimaptex ) ; } void clipminimap ( i v e c &bbmin , i v e c &bbmax, cube ∗c = worldroot , i n t x = 0 , i n t y = 0 , i n t z = 0 , i n t s i z e = worldsize>>1) { loopi ( 8 ) { ivec o ( i , x , y , z , size ) ; i f ( c [ i ] . children ) clipminimap ( bbmin , bbmax, c [ i ] . children , o . x , o . y , o . z , size>>1); e l s e i f ( ! i s e n t i r e l y s o l i d ( c [ i ] ) && ( c [ i ] . material&MATF CLIP ) ! = MAT CLIP ) { loopk ( 3 ) bbmin [ k ] = min ( bbmin [ k ] , o [ k ] ) ; loopk ( 3 ) bbmax[ k ] = max(bbmax[ k ] , o [ k ] + s i z e ) ; } } }

} } i f ( minimapclip ) { i v e c clipmin ( worldsize , worldsize , worldsize ) , clipmax ( 0 , 0 , 0) ; clipminimap ( clipmin , clipmax ) ; loopk ( 2 ) bbmin [ k ] = max( bbmin [ k ] , clipmin [ k ] ) ; loopk ( 2 ) bbmax[ k ] = min (bbmax[ k ] , clipmax [ k ] ) ; } minimapradius = bbmax. tovec ( ) . sub ( bbmin . tovec ( ) ) . mul( 0 . 5 f ) ; minimapcenter = bbmin . tovec ( ) . add ( minimapradius ) ; minimapradius . x = minimapradius . y = max( minimapradius . x , minimapradius .y) ; minimapscale = vec ( ( 0 . 5 f − 1.0 f / s i z e ) /minimapradius . x , ( 0 . 5 f − 1.0 f / s i z e ) /minimapradius . y , 1.0 f ) ; envmapping = minimapping = true ; physent ∗oldcamera = camera1 ; s t a t i c physent cmcamera ; cmcamera = ∗player ; cmcamera . r e s e t ( ) ; cmcamera . type = ENT CAMERA; cmcamera . o = vec ( minimapcenter . x , minimapcenter . y , max( minimapcenter . z + minimapradius . z + 1 , f l o a t ( minimapheight ) ) ) ; cmcamera . yaw = 0; cmcamera . pitch = −90; cmcamera . r o l l = 0; camera1 = &cmcamera ; s e t v i e w c e l l ( vec(−1, −1, −1) ) ; glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; glOrtho(−minimapradius . x , minimapradius . x , −minimapradius . y , minimapradius . y , 0 , camera1−>o . z + 1) ; g l S c a l e f (−1, 1 , 1) ; glMatrixMode (GL MODELVIEW) ; transplayer ( ) ; defaultshader−>set ( ) ; GLfloat fogc [ 4 ] = { minimapcolor . x/255.0 f , minimapcolor . y/255.0 f , minimapcolor . z/255.0 f , 1.0 f }; g l F o g f ( GL FOG START, 0) ; g l F o g f (GL FOG END, 1000000) ; glFogfv (GL FOG COLOR, fogc ) ; glClearColor ( fogc [ 0 ] , fogc [ 1 ] , fogc [ 2 ] , fogc [ 3 ] ) ; glClear ( GL DEPTH BUFFER BIT | GL COLOR BUFFER BIT ) ; glViewport ( 0 , 0 , size , s i z e ) ; glDisable (GL FOG) ; glEnable ( GL CULL FACE ) ; glEnable ( GL DEPTH TEST ) ; glEnable ( GL TEXTURE 2D ) ; glFrontFace (GL CCW) ; xtravertsva = x t r a v e r t s = glde = gbatches = 0; visiblecubes ( f a l s e ) ; queryreflections ( ) ; drawreflections ( ) ; l o o p i ( minimapheight > 0 && minimapheight < minimapcenter . z + minimapradius . z ? 2 : 1) { if ( i ) { glClear ( GL DEPTH BUFFER BIT ) ; camera1−>o . z = minimapheight ; transplayer ( ) ; } rendergeom ( ) ; rendermapmodels ( ) ; renderwater ( ) ; rendermaterials ( ) ; renderalphageom ( ) ;

void drawminimap ( ) { i f ( ! game : : needminimap ( ) ) { clearminimap ( ) ; return ; } renderprogress ( 0 , ” generating mini−map . . . ” , 0 , ! renderedframe ) ; i n t s i z e = 1h ) ) ; while ( s i z e > s i z e l i m i t ) s i z e /= 2; i f ( ! minimaptex ) glGenTextures ( 1 , &minimaptex ) ;

} glFrontFace (GL CW) ;

extern vector v a l i s t ; i v e c bbmin ( worldsize , worldsize , worldsize ) , bbmax( 0 , 0 , 0) ; loopv ( v a l i s t ) { vtxarray ∗va = v a l i s t [ i ] ; loopk ( 3 ) { i f ( va−>geommin [ k]>va−>geommax[ k ] ) continue ; bbmin [ k ] = min ( bbmin [ k ] , va−>geommin [ k ] ) ; bbmax[ k ] = max(bbmax[ k ] , va−>geommax[ k ] ) ;

391

glDisable ( GL TEXTURE 2D ) ; glDisable ( GL DEPTH TEST ) ; glDisable ( GL CULL FACE ) ; glDisable (GL FOG) ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ; camera1 = oldcamera ; envmapping = minimapping = f a l s e ;

392

Foundations of Videogame Programming Code Repository

glBindTexture ( GL TEXTURE 2D, minimaptex ) ; glCopyTexImage2D ( GL TEXTURE 2D, 0 , GL RGB5, 0 , 0 , size , size , 0) ; setuptexparameters ( minimaptex , NULL, 3 , 1 , GL RGB5, GL TEXTURE 2D ) ; glTexParameteri ( GL TEXTURE 2D, GL TEXTURE WRAP S, GL CLAMP TO BORDER) ; glTexParameteri ( GL TEXTURE 2D, GL TEXTURE WRAP T, GL CLAMP TO BORDER) ; GLfloat border [ 4 ] = { minimapcolor . x/255.0 f , minimapcolor . y/255.0 f , minimapcolor . z/255.0 f , 1.0 f }; glTexParameterfv ( GL TEXTURE 2D, GL TEXTURE BORDER COLOR, border ) ; glBindTexture ( GL TEXTURE 2D, 0) ; }

glCopyTexSubImage2D (GL TEXTURE RECTANGLE ARB, 0 , 0 , 0 , 0 , 0 , screen −>w, screen−>h ) ; } } bool dopostfx = f a l s e ; void i n v a l i d a t e p o s t f x ( ) { dopostfx = f a l s e ; }

bool deferdrawtextures = f a l s e ; void gl drawhud ( i n t w, i n t h ) ; void drawtextures ( ) { i f ( minimized ) { deferdrawtextures = true ; return ; } deferdrawtextures = f a l s e ; genenvmaps ( ) ; drawminimap ( ) ; }

i n t xtraverts , xtravertsva ; void gl drawframe ( i n t w, i n t h ) { i f ( deferdrawtextures ) drawtextures ( ) ; defaultshader−>set ( ) ;

GLuint motiontex = 0; i n t motionw = 0 , motionh = 0 , lastmotion = 0; void cleanupmotionblur ( ) { i f ( motiontex ) { glDeleteTextures ( 1 , &motiontex ) ; motiontex = 0; } motionw = motionh = 0; lastmotion = 0; } VARFP( motionblur , 0 , 0 , 1 , { i f ( ! motionblur ) cleanupmotionblur ( ) ; }) ; VARP( motionblurmillis , 1 , 5 , 1000) ; FVARP( motionblurscale , 0 , 0.5 f , 1) ; void addmotionblur ( ) { i f ( ! motionblur || ! hasTR || max( screen−>w, screen−>h ) > hwtexsize ) return ; i f ( game : : ispaused ( ) ) { lastmotion = 0; return ; } i f ( ! motiontex || motionw ! = screen−>w || motionh ! = screen−>h ) { i f ( ! motiontex ) glGenTextures ( 1 , &motiontex ) ; motionw = screen−>w; motionh = screen−>h ; lastmotion = 0; createtexture ( motiontex , motionw , motionh , NULL, 3 , 0 , GL RGB, GL TEXTURE RECTANGLE ARB) ; } glBindTexture (GL TEXTURE RECTANGLE ARB, motiontex ) ; glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glDisable ( GL TEXTURE 2D ) ; glEnable (GL TEXTURE RECTANGLE ARB) ; rectshader−>set ( ) ; g l C o l o r 4 f ( 1 , 1 , 1 , lastmotion ? pow ( motionblurscale , max( f l o a t ( l a s t m i l l i s − lastmotion ) /motionblurmillis , 1.0 f ) ) : 0) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0, 0) ; g l V e r t e x 2 f (−1, −1) ; glTexCoord2f ( motionw , 0) ; g l V e r t e x 2 f ( 1 , −1) ; glTexCoord2f ( 0 , motionh ) ; g l V e r t e x 2 f (−1, 1) ; glTexCoord2f ( motionw , motionh ) ; g l V e r t e x 2 f ( 1 , 1) ; glEnd ( ) ; glDisable (GL TEXTURE RECTANGLE ARB) ; glEnable ( GL TEXTURE 2D ) ;

updatedynlights ( ) ; aspect = forceaspect ? forceaspect : w/ f l o a t ( h ) ; fovy = 2∗atan2 ( tan ( curfov/2∗RAD) , aspect ) /RAD; i n t fogmat = lookupmaterial ( camera1−>o ) &(MATF VOLUME|MATF INDEX ) , abovemat = MAT AIR ; f l o a t fogblend = 1.0 f , causticspass = 0.0 f ; i f ( i s l i q u i d ( fogmat&MATF VOLUME) ) { f l o a t z = findsurface ( fogmat , camera1−>o , abovemat ) − WATER OFFSET; i f ( camera1−>o . z < z + 1) fogblend = min ( z + 1 − camera1−>o . z , 1.0 f ) ; e l s e fogmat = abovemat ; i f ( caustics && ( fogmat&MATF VOLUME) ==MAT WATER && camera1−>o . z < z ) causticspass = renderpath==R FIXEDFUNCTION ? 1.0 f : min ( z − camera1−>o . z , 1.0 f ) ; } e l s e fogmat = MAT AIR ; s e t f o g ( fogmat , fogblend , abovemat ) ; i f ( fogmat ! =MAT AIR ) { f l o a t blend = abovemat==MAT AIR ? fogblend : 1.0 f ; fovy += blend∗s i n f ( l a s t m i l l i s /1000.0)∗2.0 f ; aspect += blend∗s i n f ( l a s t m i l l i s /1000.0+ PI ) ∗0.1 f ; } farplane = worldsize ∗2; p r o j e c t ( fovy , aspect , farplane ) ; transplayer ( ) ; readmatrices ( ) ; findorientation ( ) ; setenvmatrix ( ) ; glEnable (GL FOG) ; glEnable ( GL CULL FACE ) ; glEnable ( GL DEPTH TEST ) ; glEnable ( GL TEXTURE 2D ) ; xtravertsva = x t r a v e r t s = glde = gbatches = 0; i f ( ! hasFBO ) { i f ( dopostfx ) { drawglaretex ( ) ; drawdepthfxtex ( ) ; drawreflections ( ) ; } e l s e dopostfx = true ; } visiblecubes ( ) ; i f ( shadowmap && ! hasFBO ) rendershadowmap ( ) ; glClear ( GL DEPTH BUFFER BIT | ( wireframe && editmode ? GL COLOR BUFFER BIT : 0) | ( hasstencil ? GL STENCIL BUFFER BIT : 0) ) ;

glDisable (GL BLEND) ; i f ( wireframe && editmode ) glPolygonMode ( GL FRONT AND BACK, GL LINE ) ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ; i f ( l a s t m i l l i s − lastmotion >= motionblurmillis ) { lastmotion = l a s t m i l l i s − l a s t m i l l i s%motionblurmillis ;

i f ( l i m i t s k y ( ) ) drawskybox ( farplane , true ) ; rendergeom ( causticspass ) ; extern i n t o u t l i n e ; i f ( ! wireframe && editmode && o u t l i n e ) renderoutline ( ) ; queryreflections ( ) ;

engine/rendergl.cpp

f l o a t yaw , pitch ; i f ( d e l t a . magnitude ( ) yaw ; e l s e vectoyawpitch ( delta , yaw , pitch ) ; yaw −= camera1−>yaw ; i f ( yaw >= 360) yaw = fmod ( yaw , 360) ; e l s e i f ( yaw < 0) yaw = 360 − fmod(−yaw , 360) ; i n t d i r = ( i n t ( yaw+22.5 f ) %360)/45; dcompass [ d i r ] += max( n , damagecompassmin ) / f l o a t ( damagecompassmax ) ; i f ( dcompass [ d i r ]>1) dcompass [ d i r ] = 1;

generategrass ( ) ; i f ( ! l i m i t s k y ( ) ) drawskybox ( farplane , f a l s e ) ; renderdecals ( true ) ; rendermapmodels ( ) ; rendergame ( true ) ; i f ( ! isthirdperson ( ) ) { p r o j e c t ( curavatarfov , aspect , farplane , f a l s e , f a l s e , f a l s e , avatardepth ) ; game : : renderavatar ( ) ; p r o j e c t ( fovy , aspect , farplane ) ; } i f ( wireframe && editmode ) glPolygonMode ( GL FRONT AND BACK, GL FILL ) ; i f ( hasFBO ) { drawglaretex ( ) ; drawdepthfxtex ( ) ; drawreflections ( ) ; }

} void drawdamagecompass ( i n t w, i n t h ) { i n t d i r s = 0; f l o a t s i z e = damagecompasssize/100.0 f∗min ( h , w) /2.0 f ; l o o p i ( 8 ) i f ( dcompass [ i ]>0) { i f ( ! dirs ) { glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; g l C o l o r 4 f ( 1 , 0 , 0 , damagecompassalpha/100.0 f ) ; } d i r s ++; glPushMatrix ( ) ; g l T r a n s l a t e f (w/2 , h/2 , 0) ; g l R o t a t e f ( i ∗45, 0 , 0 , 1) ; g l T r a n s l a t e f ( 0 , −s i z e /2.0 f−min ( h , w) /4.0 f , 0) ; f l o a t l o g s c a l e = 32, scale = l o g (1 + ( l o g s c a l e − 1)∗dcompass [ i ] ) / l o g ( l o g s c a l e ) ; g l S c a l e f ( s i z e∗scale , s i z e∗scale , 0) ;

i f ( wireframe && editmode ) glPolygonMode ( GL FRONT AND BACK, GL LINE ) ; renderwater ( ) ; rendergrass ( ) ; rendermaterials ( ) ; renderalphageom ( ) ;

glBegin ( GL TRIANGLES ) ; g l V e r t e x 3 f ( 1 , 1 , 0) ; g l V e r t e x 3 f (−1, 1 , 0) ; g l V e r t e x 3 f ( 0 , 0 , 0) ; glEnd ( ) ; glPopMatrix ( ) ;

i f ( wireframe && editmode ) glPolygonMode ( GL FRONT AND BACK, GL FILL ) ; r e n d e r p a r t i c l e s ( true ) ; glDisable (GL FOG) ; glDisable ( GL CULL FACE ) ; glDisable ( GL DEPTH TEST ) ; addmotionblur ( ) ; addglare ( ) ; i f ( i s l i q u i d ( fogmat&MATF VOLUME) ) drawfogoverlay ( fogmat , fogblend , abovemat ) ; renderpostfx ( ) ; defaultshader−>set ( ) ; g3d render ( ) ; glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; gl drawhud (w, h ) ; renderedgame = f a l s e ; } void gl drawmainmenu ( i n t w, i n t h ) { xtravertsva = x t r a v e r t s = glde = gbatches = 0; renderbackground (NULL, NULL, NULL, NULL, true , true ) ; renderpostfx ( ) ; glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; glLoadIdentity ( ) ;

393

// fade in l o g space so short b l i p s don ’ t disappear too quickly scale −= f l o a t ( curtime ) /damagecompassfade ; dcompass [ i ] = scale > 0 ? ( pow ( logscale , scale ) − 1) / ( l o g s c a l e − 1) : 0; } } i n t damageblendmillis = 0; VARFP( damagescreen , 0 , 1 , 1 , { i f ( ! damagescreen ) damageblendmillis = 0; }) ; VARP( damagescreenfactor , 1 , 7 , 100) ; VARP( damagescreenalpha , 1 , 45, 100) ; VARP( damagescreenfade , 0 , 125, 1000) ; VARP( damagescreenmin , 1 , 10, 1000) ; VARP( damagescreenmax , 1 , 100, 1000) ; void damageblend ( i n t n ) { i f ( ! damagescreen || minimized ) return ; i f ( l a s t m i l l i s > damageblendmillis ) damageblendmillis = l a s t m i l l i s ; damageblendmillis += clamp ( n , damagescreenmin , damagescreenmax )∗ damagescreenfactor ; } void drawdamagescreen ( i n t w, i n t h ) { i f ( l a s t m i l l i s >= damageblendmillis ) return ; defaultshader−>set ( ) ; glEnable ( GL TEXTURE 2D ) ; s t a t i c Texture ∗damagetex = NULL; i f ( ! damagetex ) damagetex = textureload ( ” packages/hud/damage . png ” , 3) ;

defaultshader−>set ( ) ; glEnable ( GL TEXTURE 2D ) ; g3d render ( ) ;

}

glBlendFunc (GL ONE, GL ONE MINUS SRC ALPHA ) ; glBindTexture ( GL TEXTURE 2D, damagetex−>id ) ; f l o a t fade = damagescreenalpha/100.0 f ; i f ( damageblendmillis − l a s t m i l l i s < damagescreenfade ) fade ∗= f l o a t ( damageblendmillis − l a s t m i l l i s ) /damagescreenfade ; g l C o l o r 4 f ( fade , fade , fade , fade ) ;

VARNP( damagecompass , usedamagecompass , 0 , 1 , 1) ; VARP( damagecompassfade , 1 , 1000, 10000) ; VARP( damagecompasssize , 1 , 30, 100) ; VARP( damagecompassalpha , 1 , 25, 100) ; VARP( damagecompassmin , 1 , 25, 1000) ; VARP( damagecompassmax, 1 , 200, 1000) ;

glBegin ( GL TRIANGLE glTexCoord2f ( 0 , 0) ; glTexCoord2f ( 1 , 0) ; glTexCoord2f ( 0 , 1) ; glTexCoord2f ( 1 , 1) ; glEnd ( ) ;

notextureshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ; gl drawhud (w, h ) ;

f l o a t dcompass [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; void damagecompass ( i n t n , const vec &l o c ) { i f ( ! usedamagecompass || minimized ) return ; vec d e l t a ( l o c ) ; d e l t a . sub ( camera1−>o ) ;

STRIP ) ; glVertex2f (0 , g l V e r t e x 2 f (w, glVertex2f (0 , g l V e r t e x 2 f (w,

glDisable ( GL TEXTURE 2D ) ; notextureshader−>set ( ) ; } VAR( hidestats , 0 , 0 , 1) ; VAR( hidehud , 0 , 0 , 1) ;

0) ; 0) ; h) ; h) ;

394

Foundations of Videogame Programming Code Repository

VARP( crosshairsize , 0 , 15, 50) ; VARP( cursorsize , 0 , 30, 50) ; VARP( crosshairfx , 0 , 1 , 1) ; VARP( crosshaircolors , 0 , 1 , 1) ; #define MAXCROSSHAIRS 4 s t a t i c Texture ∗crosshairs [MAXCROSSHAIRS] = { NULL, NULL, NULL, NULL }; void loadcrosshair ( const char ∗name, i n t i ) { i f ( i < 0 || i >= MAXCROSSHAIRS) return ; crosshairs [ i ] = name ? textureload ( name, 3 , true ) : notexture ; i f ( crosshairs [ i ] == notexture ) { name = game : : defaultcrosshair ( i ) ; i f ( ! name) name = ” data/crosshair . png ” ; crosshairs [ i ] = textureload ( name, 3 , true ) ; } }

VARP( wallclock24 , 0 , 0 , 1) ; VARP( wallclocksecs , 0 , 0 , 1) ; s t a t i c t i m e t walltime = 0; VARP( showfps , 0 , 1 , 1) ; VARP( showfpsrange , 0 , 0 , 1) ; VAR( showeditstats , 0 , 0 , 1) ; VAR( s t a t r a t e , 1 , 200, 1000) ; FVARP( conscale , 1e−3f , 0.33 f , 1e3f ) ; void gl drawhud ( i n t w, i n t h ) { i f ( forceaspect ) w = i n t ( c e i l ( h∗forceaspect ) ) ; i f ( editmode && ! hidehud && !mainmenu) { glEnable ( GL DEPTH TEST ) ; glDepthMask ( GL FALSE ) ;

void loadcrosshair ( const char ∗name, i n t ∗i ) { loadcrosshair ( name, ∗i ) ; }

renderblendbrush ( ) ; rendereditcursor ( ) ; glDepthMask ( GL TRUE ) ; glDisable ( GL DEPTH TEST ) ;

COMMANDN( loadcrosshair , loadcrosshair , ” s i ” ) ; } ICOMMAND( getcrosshair , ” i ” , ( i n t ∗i ) , { const char ∗name = ” ” ; i f (∗ i >= 0 && ∗i < MAXCROSSHAIRS) { name = crosshairs [∗ i ] ? crosshairs [∗ i]−>name : game : : defaultcrosshair (∗ i ) ; i f ( ! name) name = ” data/crosshair . png ” ; } r e s u l t (name) ; }) ; void writecrosshairs ( stream ∗f ) { l o o p i (MAXCROSSHAIRS) i f ( crosshairs [ i ] && crosshairs [ i ] ! = notexture ) f−>p r i n t f ( ” loadcrosshair %s %d\n” , escapestring ( crosshairs [ i]−>name ), i); f−>p r i n t f (”\n ” ) ; } void drawcrosshair ( i n t w, i n t h ) { bool windowhit = g3d windowhit ( true , f a l s e ) ; i f ( ! windowhit && ( hidehud || mainmenu) ) return ; //(hidehud || player−> s t a t e ==CS SPECTATOR || player−>s t a t e ==CS DEAD) ) return ; f l o a t r = 1 , g = 1 , b = 1 , cx = 0.5 f , cy = 0.5 f , chsize ; Texture ∗crosshair ; i f ( windowhit ) { s t a t i c Texture ∗cursor = NULL; i f ( ! cursor ) cursor = textureload ( ” data/guicursor . png ” , 3 , true ) ; crosshair = cursor ; chsize = cursorsize∗w/900.0 f ; g3d cursorpos ( cx , cy ) ; } else { i n t index = game : : s e l e c t c r o s s h a i r ( r , g , b ) ; i f ( index < 0) return ; i f ( ! crosshairfx ) index = 0; i f ( ! crosshairfx || ! crosshaircolors ) r = g = b = 1; crosshair = crosshairs [ index ] ; i f ( ! crosshair ) { loadcrosshair (NULL, index ) ; crosshair = crosshairs [ index ] ; } chsize = c r o s s h a i r s i z e∗w/900.0 f ; } i f ( crosshair−>type&Texture : : ALPHA) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; e l s e glBlendFunc (GL ONE, GL ONE) ; glColor3f ( r , g , b ) ; f l o a t x = cx∗w − ( windowhit ? 0 : chsize /2.0 f ) ; f l o a t y = cy∗h − ( windowhit ? 0 : chsize /2.0 f ) ; glBindTexture ( GL TEXTURE 2D, crosshair−>id ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f ( x , y) ; glTexCoord2f ( 1 , 0) ; g l V e r t e x 2 f ( x + chsize , y ) ; glTexCoord2f ( 0 , 1) ; g l V e r t e x 2 f ( x , y + chsize ) ; glTexCoord2f ( 1 , 1) ; g l V e r t e x 2 f ( x + chsize , y + chsize ) ; glEnd ( ) ; } VARP( wallclock , 0 , 0 , 1) ;

g e t t e x t r e s (w, h ) ; glMatrixMode ( GL PROJECTION ) ; glLoadIdentity ( ) ; glOrtho ( 0 , w, h , 0 , −1, 1) ; glMatrixMode (GL MODELVIEW) ; glLoadIdentity ( ) ; g l C o l o r 3 f ( 1 , 1 , 1) ; extern i n t debugsm ; i f ( debugsm ) { extern void viewshadowmap ( ) ; viewshadowmap ( ) ; } extern i n t debugglare ; i f ( debugglare ) { extern void viewglaretex ( ) ; viewglaretex ( ) ; } extern i n t debugdepthfx ; i f ( debugdepthfx ) { extern void viewdepthfxtex ( ) ; viewdepthfxtex ( ) ; } glEnable (GL BLEND) ; i f ( ! mainmenu) { drawdamagescreen (w, h ) ; drawdamagecompass (w, h ) ; } glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; i n t conw = i n t (w/conscale ) , conh = i n t ( h/conscale ) , abovehud = conh − FONTH, l i m i t g u i = abovehud ; i f ( ! hidehud && !mainmenu) { i f ( ! hidestats ) { glPushMatrix ( ) ; g l S c a l e f ( conscale , conscale , 1) ; i n t r o f f s e t = 0; i f ( showfps ) { s t a t i c i n t l a s t f p s = 0 , prevfps [ 3 ] = { 0 , 0 , 0 }, curfps [ 3 ] = { 0 , 0 , 0 }; i f ( t o t a l m i l l i s − l a s t f p s >= s t a t r a t e ) { memcpy( prevfps , curfps , s i z e o f ( prevfps ) ) ; l a s t f p s = t o t a l m i l l i s − ( t o t a l m i l l i s%s t a t r a t e ) ; } i n t nextfps [ 3 ] ; g e t f p s ( nextfps [ 0 ] , nextfps [ 1 ] , nextfps [ 2 ] ) ; l o o p i ( 3 ) i f ( prevfps [ i ]== curfps [ i ] ) curfps [ i ] = nextfps [ i ] ; i f ( showfpsrange ) draw textf ( ” fps %d+%d−%d ” , conw−7∗FONTH,

engine/rendermodel.cpp conh−FONTH∗3/2, curfps [ 0 ] , curfps [ 1 ] , curfps [ 2 ] ) ; e l s e draw textf ( ” fps %d ” , conw−5∗FONTH, conh−FONTH∗3/2, curfps [ 0 ] ) ; r o f f s e t += FONTH;

395

draw textf ( ” cube %s%d%s ” , FONTH/2 , abovehud , selchildcount 0 ? getmaterialdesc ( selchildmat , ” : ” ) : ””) ;

} char ∗e d i t i n f o = executestr ( ” edithud ” ) ; i f ( editinfo ) { i f ( editinfo [ 0 ] ) { i n t tw , th ; text bounds ( e d i t i n f o , tw , th ) ; th += FONTH−1; th −= th%FONTH; abovehud −= max( th , FONTH) ; draw text ( e d i t i n f o , FONTH/2 , abovehud ) ; } DELETEA( e d i t i n f o ) ; }

i f ( wallclock ) { i f ( ! walltime ) { walltime = time (NULL) ; walltime −= t o t a l m i l l i s /1000; i f ( ! walltime ) walltime ++; } t i m e t w a l l o f f s e t = walltime + t o t a l m i l l i s /1000; struct tm ∗l o c a l v a l s = l o c a l t i m e (& w a l l o f f s e t ) ; s t a t i c s t r i n g buf ; i f ( l o c a l v a l s && s t r f t i m e ( buf , s i z e o f ( buf ) , wallclocksecs ? ( wallclock24 ? ”%H:%M:%S” : ”%I :%M:%S%p ” ) : ( wallclock24 ? ”%H:%M” : ”%I :%M%p ” ) , l o c a l v a l s ) ) { // hack because not a l l platforms ( windows ) support %P lowercase option // also s t r i p leading 0 from 12 hour time char ∗dst = buf ; const char ∗src = &buf [ ! wallclock24 && buf [ 0 ] = = ’ 0 ’ ? 1 : 0]; while (∗ src ) ∗dst++ = tolower (∗ src ++) ; ∗dst++ = ’ \ 0 ’ ; draw text ( buf , conw−5∗FONTH, conh−FONTH∗3/2−r o f f s e t ) ; r o f f s e t += FONTH; } }

} e l s e i f ( i d e n t e x i s t s ( ” gamehud ” ) ) { char ∗gameinfo = executestr ( ” gamehud ” ) ; i f ( gameinfo ) { i f ( gameinfo [ 0 ] ) { i n t tw , th ; text bounds ( gameinfo , tw , th ) ; th += FONTH−1; th −= th%FONTH; r o f f s e t += max( th , FONTH) ; draw text ( gameinfo , conw−max(5∗FONTH, 2∗FONTH+tw ) , conh −FONTH/2−r o f f s e t ) ; } DELETEA( gameinfo ) ; } }

i f ( editmode || showeditstats ) { s t a t i c i n t l a s t s t a t s = 0 , prevstats [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 }, curstats [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 }; i f ( t o t a l m i l l i s − l a s t s t a t s >= s t a t r a t e ) { memcpy( prevstats , curstats , s i z e o f ( prevstats ) ) ; l a s t s t a t s = t o t a l m i l l i s − ( t o t a l m i l l i s%s t a t r a t e ) ; } i n t nextstats [ 8 ] = { v t r i s ∗100/max( wtris , 1) , v v e r t s∗100/max( wverts , 1) , x t r a v e r t s /1024, xtravertsva /1024, glde , gbatches , getnumqueries ( ) , rplanes }; l o o p i ( 8 ) i f ( prevstats [ i ]== curstats [ i ] ) curstats [ i ] = nextstats [ i ] ;

glPopMatrix ( ) ; } i f ( hidestats || ( ! editmode && ! showeditstats ) ) { glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; game : : gameplayhud (w, h ) ; l i m i t g u i = abovehud = min ( abovehud , i n t ( conh∗game : : abovegameplayhud (w, h ) ) ) ; } rendertexturepanel (w, h ) ; } g 3 d l i m i t s c a l e ((2∗ l i m i t g u i − conh ) / f l o a t ( conh ) ) ;

abovehud −= 2∗FONTH; draw textf ( ” wtr:%dk(%d%%) wvt:%dk(%d%%) evt:%dk eva:%dk ” , FONTH/2 , abovehud , wtris /1024, curstats [ 0 ] , wverts /1024, curstats [ 1 ] , curstats [ 2 ] , curstats [ 3 ] ) ; draw textf ( ” ond:%d va:%d g l :%d(%d ) oq:%d lm:%d rp:%d pvs:%d ” , FONTH/2 , abovehud+FONTH, allocnodes ∗8, allocva , curstats [ 4 ] , curstats [ 5 ] , curstats [ 6 ] , lightmaps . length ( ) , curstats [ 7 ] , getnumviewcells ( ) ) ; l i m i t g u i = abovehud ;

glPushMatrix ( ) ; g l S c a l e f ( conscale , conscale , 1) ; abovehud −= rendercommand (FONTH/2 , abovehud − FONTH/2 , conw−FONTH) ; extern i n t f u l l c o n s o l e ; i f ( ! hidehud || f u l l c o n s o l e ) renderconsole ( conw , conh , abovehud − FONTH /2) ; glPopMatrix ( ) ; drawcrosshair (w, h ) ;

} glDisable (GL BLEND) ; glDisable ( GL TEXTURE 2D ) ;

i f ( editmode ) { abovehud −= FONTH;

}

engine/rendermodel.cpp #include ” engine . h” VARP( oqdynent , 0 , 1 , 1) ; VARP( animationinterpolationtime , 0 , 150, 1000) ; model ∗loadingmodel = NULL; #include #include #include #include

” r a g d o l l . h” ”animmodel . h” ” vertmodel . h” ” skelmodel . h”

s t a t i c model ∗(

c d e c l ∗modeltypes [NUMMODELTYPES] ) ( const char ∗) ;

s t a t i c i n t addmodeltype ( i n t type , model ∗( { modeltypes [ type ] = loader ; return type ; }

#define MODELTYPE( modeltype , modelclass ) \ s t a t i c model ∗ loadmodel ##modelclass ( const char ∗filename ) \ { \ return new modelclass ( filename ) ; \ } \ static int dummy ##modelclass = addmodeltype ( ( modeltype ) , loadmodel ##modelclass ) ; #include #include #include #include #include #include

”md2. h” ”md3. h” ”md5. h” ” obj . h” ”smd. h” ”iqm . h”

c d e c l ∗loader ) ( const char ∗) ) MODELTYPE(MDL MD2, MODELTYPE(MDL MD3, MODELTYPE(MDL MD5, MODELTYPE(MDL OBJ, MODELTYPE(MDL SMD,

md2) ; md3) ; md5) ; obj ) ; smd) ;

396

Foundations of Videogame Programming Code Repository

MODELTYPE(MDL IQM, iqm ) ;

COMMAND( mdlglow , ” i i f ” ) ;

#define checkmdl i f ( ! loadingmodel ) { conoutf (CON ERROR, ” not loading a model ” ) ; return ; }

void mdlglare ( f l o a t ∗specglare , f l o a t ∗glowglare ) { checkmdl ; loadingmodel−>s e t g l a r e (∗ specglare , ∗glowglare ) ; }

void mdlcullface ( i n t ∗c u l l f a c e ) { checkmdl ; loadingmodel−>s e t c u l l f a c e (∗ c u l l f a c e ! = 0 ) ; } COMMAND( mdlcullface , ” i ” ) ; void mdlcollide ( i n t ∗c o l l i d e ) { checkmdl ; loadingmodel−>c o l l i d e = ∗c o l l i d e ! = 0 ; } COMMAND( mdlcollide , ” i ” ) ; void m d l e l l i p s e c o l l i d e ( i n t ∗c o l l i d e ) { checkmdl ; loadingmodel−>e l l i p s e c o l l i d e = ∗c o l l i d e ! = 0 ; } COMMAND( m d l e l l i p s e c o l l i d e , ” i ” ) ; void mdlspec ( i n t ∗percent ) { checkmdl ; f l o a t spec = 1.0 f ; i f (∗ percent>0) spec = ∗percent/100.0 f ; e l s e i f (∗ percentsetspec ( spec ) ; } COMMAND( mdlspec , ” i ” ) ; void mdlambient ( i n t ∗percent ) { checkmdl ; f l o a t ambient = 0.3 f ; i f (∗ percent>0) ambient = ∗percent/100.0 f ; e l s e i f (∗ percentsetambient ( ambient ) ; } COMMAND( mdlambient , ” i ” ) ; void mdlalphatest ( f l o a t ∗c u t o f f ) { checkmdl ; loadingmodel−>setalphatest (max( 0 . 0 f , min( 1 . 0 f , ∗c u t o f f ) ) ) ; } COMMAND( mdlalphatest , ” f ” ) ; void mdlalphablend ( i n t ∗blend ) { checkmdl ; loadingmodel−>setalphablend (∗ blend ! = 0 ) ; } COMMAND( mdlalphablend , ” i ” ) ; void mdlalphadepth ( i n t ∗depth ) { checkmdl ; loadingmodel−>alphadepth = ∗depth ! = 0 ; } COMMAND( mdlalphadepth , ” i ” ) ; void mdldepthoffset ( i n t ∗o f f s e t ) { checkmdl ; loadingmodel−>depthoffset = ∗o f f s e t ! = 0 ; } COMMAND( mdldepthoffset , ” i ” ) ; void mdlglow ( i n t ∗percent , i n t ∗delta , f l o a t ∗pulse ) { checkmdl ; f l o a t glow = 3.0 f , glowdelta = ∗d e l t a /100.0 f , glowpulse = ∗pulse > 0 ? ∗pulse/1000.0 f : 0; i f (∗ percent>0) glow = ∗percent/100.0 f ; e l s e i f (∗ percentsetglow ( glow , glowdelta , glowpulse ) ; }

COMMAND( mdlglare , ” f f ” ) ; void mdlenvmap ( f l o a t ∗envmapmax, f l o a t ∗envmapmin, char ∗envmap ) { checkmdl ; loadingmodel−>setenvmap(∗envmapmin, ∗envmapmax, envmap [ 0 ] ? cubemapload ( envmap ) : NULL) ; } COMMAND( mdlenvmap, ” f f s ” ) ; void m d l f u l l b r i g h t ( f l o a t ∗f u l l b r i g h t ) { checkmdl ; loadingmodel−>s e t f u l l b r i g h t (∗ f u l l b r i g h t ) ; } COMMAND( mdlfullbright , ” f ” ) ; void mdlshader ( char ∗shader ) { checkmdl ; loadingmodel−>setshader ( lookupshaderbyname ( shader ) ) ; } COMMAND( mdlshader , ” s ” ) ; void mdlspin ( f l o a t ∗yaw , f l o a t ∗pitch ) { checkmdl ; loadingmodel−>spinyaw = ∗yaw ; loadingmodel−>spinpitch = ∗pitch ; } COMMAND( mdlspin , ” f f ” ) ; void mdlscale ( i n t ∗percent ) { checkmdl ; f l o a t scale = 0.3 f ; i f (∗ percent>0) scale = ∗percent/100.0 f ; e l s e i f (∗ percentscale = scale ; } COMMAND( mdlscale , ” i ” ) ; void mdltrans ( f l o a t ∗x , f l o a t ∗y , f l o a t ∗z ) { checkmdl ; loadingmodel−>t r a n s l a t e = vec (∗x , ∗y , ∗z ) ; } COMMAND( mdltrans , ” f f f ” ) ; void mdlyaw ( f l o a t ∗angle ) { checkmdl ; loadingmodel−>offsetyaw = ∗angle ; } COMMAND( mdlyaw , ” f ” ) ; void mdlpitch ( f l o a t ∗angle ) { checkmdl ; loadingmodel−>o f f s e t p i t c h = ∗angle ; } COMMAND( mdlpitch , ” f ” ) ; void mdlshadow ( i n t ∗shadow ) { checkmdl ; loadingmodel−>shadow = ∗shadow! = 0 ; } COMMAND( mdlshadow , ” i ” ) ; void mdlbb ( f l o a t ∗rad , f l o a t ∗h , f l o a t ∗eyeheight ) { checkmdl ; loadingmodel−>c o l l i d e r a d i u s = ∗rad ; loadingmodel−>c o l l i d e h e i g h t = ∗h ; loadingmodel−>eyeheight = ∗eyeheight ; }

engine/rendermodel.cpp

COMMAND( mdlbb , ” f f f ” ) ; void mdlextendbb ( f l o a t ∗x , f l o a t ∗y , f l o a t ∗z ) { checkmdl ; loadingmodel−>bbextend = vec (∗x , ∗y , ∗z ) ; }

397

void rdanimjoints ( i n t ∗on ) { checkragdoll ; ragdoll−>animjoints = ∗on ! = 0 ; } COMMAND( rdanimjoints , ” i ” ) ; // mapmodels

COMMAND( mdlextendbb , ” f f f ” ) ;

vector mapmodels ;

void mdlname ( ) { checkmdl ; r e s u l t ( loadingmodel−>name ( ) ) ; }

void mmodel ( char ∗name) { mapmodelinfo &mmi = mapmodels . add ( ) ; copystring (mmi.name, name) ; mmi.m = NULL; }

COMMAND( mdlname, ” ” ) ; #define checkragdoll \ i f ( ! loadingmodel−>s k e l e t a l ( ) ) { conoutf (CON ERROR, ” not loading a s k e l e t a l model ” ) ; return ; } \ skelmodel ∗m = ( skelmodel ∗) loadingmodel ; \ skelmodel : : skelmeshgroup ∗meshes = ( skelmodel : : skelmeshgroup ∗)m−> parts . l a s t ( )−>meshes ; \ i f ( ! meshes ) return ; \ skelmodel : : skeleton ∗skel = meshes−>skel ; \ i f ( ! skel−>r a g d o l l ) skel−>r a g d o l l = new r a g d o l l s k e l ; \ r a g d o l l s k e l ∗r a g d o l l = skel−>r a g d o l l ; \ i f ( ragdoll−>loaded ) return ;

void rdvert ( f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗radius ) { checkragdoll ; r a g d o l l s k e l : : v e r t &v = ragdoll−>v e r t s . add ( ) ; v . pos = vec (∗x , ∗y , ∗z ) ; v . radius = ∗radius > 0 ? ∗radius : 1; } COMMAND( rdvert , ” f f f f ” ) ; void rdeye ( i n t ∗v ) { checkragdoll ; ragdoll−>eye = ∗v ; } COMMAND( rdeye , ” i ” ) ; void r d t r i ( i n t ∗v1 , i n t ∗v2 , i n t ∗v3 ) { checkragdoll ; r a g d o l l s k e l : : t r i &t = ragdoll−>t r i s . add ( ) ; t . v e r t [ 0 ] = ∗v1 ; t . v e r t [ 1 ] = ∗v2 ; t . v e r t [ 2 ] = ∗v3 ; } COMMAND( r d t r i , ” i i i ” ) ; void r d j o i n t ( i n t ∗n , i n t ∗t , i n t ∗v1 , i n t ∗v2 , i n t ∗v3 ) { checkragdoll ; i f (∗n < 0 || ∗n >= skel−>numbones ) return ; r a g d o l l s k e l : : j o i n t &j = ragdoll−>j o i n t s . add ( ) ; j . bone = ∗n ; j . t r i = ∗t ; j . v e r t [ 0 ] = ∗v1 ; j . v e r t [ 1 ] = ∗v2 ; j . v e r t [ 2 ] = ∗v3 ; } COMMAND( r d j o i n t , ” iibbb ” ) ; void r d l i m i t d i s t ( i n t ∗v1 , i n t ∗v2 , f l o a t ∗mindist , f l o a t ∗maxdist ) { checkragdoll ; r a g d o l l s k e l : : d i s t l i m i t &d = ragdoll−>d i s t l i m i t s . add ( ) ; d . v e r t [ 0 ] = ∗v1 ; d . v e r t [ 1 ] = ∗v2 ; d . mindist = ∗mindist ; d . maxdist = max(∗maxdist , ∗mindist ) ; } COMMAND( r d l i m i t d i s t , ” i i f f ” ) ; void r d l i m i t r o t ( i n t ∗t1 , i n t ∗t2 , f l o a t ∗maxangle , f l o a t ∗qx , f l o a t ∗qy , f l o a t ∗qz , f l o a t ∗qw) { checkragdoll ; r a g d o l l s k e l : : r o t l i m i t &r = ragdoll−>r o t l i m i t s . add ( ) ; r . t r i [ 0 ] = ∗t1 ; r . t r i [ 1 ] = ∗t2 ; r . maxangle = ∗maxangle ∗ RAD; r . middle = matrix3x3 ( quat(∗qx , ∗qy , ∗qz , ∗qw) ) ; } COMMAND( r d l i m i t r o t , ” i i f f f f f ” ) ;

void mapmodelcompat ( i n t ∗rad , i n t ∗h , i n t ∗tex , char ∗name, char ∗shadow ) { mmodel (name) ; } void mapmodelreset ( i n t ∗n ) { i f ( ! ( i d e n t f l a g s&IDF OVERRIDDEN) && ! game : : a l l o w e d i t t o g g l e ( ) ) return ; mapmodels . shrink ( clamp(∗n , 0 , mapmodels . length ( ) ) ) ; } mapmodelinfo ∗getmminfo ( i n t i ) { return mapmodels . inrange ( i ) ? &mapmodels [ i ] : 0; } const char ∗mapmodelname ( i n t i ) { return mapmodels . inrange ( i ) ? mapmodels [ i ] . name : NULL; } COMMAND( mmodel, ” s ” ) ; COMMANDN( mapmodel, mapmodelcompat , ” i i i s s ” ) ; COMMAND( mapmodelreset , ” i ” ) ; ICOMMAND( mapmodelname, ” i ” , ( i n t ∗index ) , { r e s u l t ( mapmodels . inrange (∗ index ) ? mapmodels[∗ index ] . name : ” ” ) ; }) ; ICOMMAND( nummapmodels, ” ” , ( ) , { i n t r e t ( mapmodels . length ( ) ) ; }) ; // model r e g i s t r y hashtable mdllookup ; vector preloadmodels ; void preloadmodel ( const char ∗name) { i f ( ! name || !name[ 0 ] || mdllookup . access (name) ) return ; preloadmodels . add ( newstring (name) ) ; } void flushpreloadedmodels ( bool msg ) { loopv ( preloadmodels ) { loadprogress = f l o a t ( i +1)/preloadmodels . length ( ) ; model ∗m = loadmodel ( preloadmodels [ i ] , −1, msg ) ; i f ( !m) { i f ( msg ) conoutf (CON WARN, ” could not load model : %s ” , preloadmodels [ i ] ) ; } else { m−>preloadmeshes ( ) ; } } preloadmodels . deletearrays ( ) ; loadprogress = 0; } void preloadusedmapmodels ( bool msg, bool bih ) { vector &ents = e n t i t i e s : : getents ( ) ; vector mapmodels ; loopv ( ents ) { e x t e n t i t y &e = ∗ents [ i ] ; i f ( e . type==ET MAPMODEL && e . a t t r 2 >= 0 && mapmodels . f i n d ( e . a t t r 2 ) < 0) mapmodels . add ( e . a t t r 2 ) ; } loopv ( mapmodels ) { loadprogress = f l o a t ( i +1)/mapmodels . length ( ) ; i n t mmindex = mapmodels [ i ] ; mapmodelinfo ∗mmi = getmminfo ( mmindex ) ; i f ( ! mmi) { i f ( msg ) conoutf (CON WARN, ” could not f i n d map model : %d ” , mmindex ) ; } e l s e i f (mmi−>name[ 0 ] && ! loadmodel (NULL, mmindex, msg ) ) { i f ( msg ) conoutf (CON WARN, ” could not load model : %s ” , mmi−>name) ; } e l s e i f (mmi−>m) { i f ( bih ) mmi−>m−>preloadBIH ( ) ; mmi−>m−>preloadmeshes ( ) ; }

398

Foundations of Videogame Programming Code Repository

} loadprogress = 0;

i f ( yradiusset ( ) ; glDisable ( GL TEXTURE 2D ) ; g l C o l o r 3 f ( 1 , 1 , 1) ; render2dbox ( c , xsz , 0 , h ) ; render2dbox ( c , 0 , ysz , h ) ; c . add ( vec ( xsz , ysz , 0) ) ; render2dbox ( c , −xsz , 0 , h ) ; render2dbox ( c , 0 , −ysz , h ) ; x t r a v e r t s += 16; glEnable ( GL TEXTURE 2D ) ;

} model ∗loadmodel ( const char ∗name, i n t i , bool msg ) { i f ( ! name) { i f ( ! mapmodels . inrange ( i ) ) return NULL; mapmodelinfo &mmi = mapmodels [ i ] ; i f (mmi.m) return mmi.m; name = mmi.name; } model ∗∗mm = mdllookup . access (name) ; model ∗m; i f (mm) m = ∗mm; else { i f ( ! name[ 0 ] || loadingmodel || lightmapping > 1) return NULL; i f ( msg ) { defformatstring ( filename ) ( ” packages/models/%s ” , name) ; renderprogress ( loadprogress , filename ) ; } l o o p i (NUMMODELTYPES) { m = modeltypes [ i ] ( name) ; i f ( !m) continue ; loadingmodel = m; i f (m−>load ( ) ) break ; DELETEP(m) ; } loadingmodel = NULL; i f ( !m) return NULL; mdllookup . access (m−>name ( ) , m) ; m−>preloadshaders ( ) ; } i f ( mapmodels . inrange ( i ) && ! mapmodels [ i ] .m) mapmodels [ i ] .m = m; return m; } void preloadmodelshaders ( ) { i f ( i n i t i n g ) return ; enumerate ( mdllookup , model ∗, m, m−>preloadshaders ( ) ) ; } void clear mdls ( ) { enumerate ( mdllookup , model ∗, m, d e l e t e m) ; } void cleanupmodels ( ) { enumerate ( mdllookup , model ∗, m, m−>cleanup ( ) ) ; } void clearmodel ( char ∗name) { model ∗∗m = mdllookup . access (name) ; i f ( !m) { conoutf ( ” model %s i s not loaded ” , name) ; return ; } loopv ( mapmodels ) i f ( mapmodels [ i ] .m==∗m) mapmodels [ i ] .m = NULL; mdllookup . remove (name) ; (∗m)−>cleanup ( ) ; d e l e t e ∗m; conoutf ( ” cleared model %s ” , name) ; } COMMAND( clearmodel , ” s ” ) ; bool modeloccluded ( const vec ¢er , f l o a t radius ) { i n t br = i n t ( radius ∗2) +1; return pvsoccluded ( i v e c ( i n t ( center . x−radius ) , i n t ( center . y−radius ) , i n t ( center . z−radius ) ) , i v e c ( br , br , br ) ) || bboccluded ( i v e c ( i n t ( center . x−radius ) , i n t ( center . y−radius ) , i n t ( center . z−radius ) ) , i v e c ( br , br , br ) ) ; } VAR( showboundingbox , 0 , 0 , 2) ; void render2dbox ( vec &o , f l o a t x , f l o a t y , f l o a t z ) { glBegin ( GL LINE LOOP ) ; glVertex3f ( o . x , o . y , o . z ) ; g l V e r t e x 3 f ( o . x , o . y , o . z+z ) ; g l V e r t e x 3 f ( o . x+x , o . y+y , o . z+z ) ; g l V e r t e x 3 f ( o . x+x , o . y+y , o . z ) ; glEnd ( ) ; } void render3dbox ( vec &o , f l o a t t o f l o o r , f l o a t t o c e i l , f l o a t xradius , f l o a t yradius ) {

} void r e n d e r e l l i p s e ( vec &o , f l o a t xradius , f l o a t yradius , f l o a t yaw ) { lineshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ; g l C o l o r 3 f ( 0 . 5 f , 0.5 f , 0.5 f ) ; glBegin ( GL LINE LOOP ) ; loopi (15) { const vec2 &sc = sincos360 [ i ∗(360/15) ] ; vec p ( xradius∗sc . x , yradius∗sc . y , 0) ; p . rotate around z ( ( yaw+90)∗RAD) ; p . add ( o ) ; glVertex3fv ( p . v ) ; } glEnd ( ) ; glEnable ( GL TEXTURE 2D ) ; } struct batchedmodel { vec pos , color , d i r ; i n t anim ; f l o a t yaw , pitch , transparent ; i n t basetime , basetime2 , f l a g s ; dynent ∗d ; i n t attached ; occludequery ∗query ; }; struct modelbatch { model ∗m; int flags ; vector batched ; }; s t a t i c vector batches ; s t a t i c vector modelattached ; s t a t i c i n t numbatches = −1; s t a t i c occludequery ∗modelquery = NULL; void startmodelbatches ( ) { numbatches = 0; modelattached . s e t s i z e ( 0 ) ; } modelbatch &addbatchedmodel ( model ∗m) { modelbatch ∗b = NULL; i f (m−>batch>=0 && m−>batchbatch]−>m==m) b = batches [m−>batch ] ; else { i f ( numbatchesbatched . s e t s i z e ( 0 ) ; } e l s e b = batches . add ( new modelbatch ) ; b−>m = m; b−>f l a g s = 0; m−>batch = numbatches++; } return ∗b ; } void renderbatchedmodel ( model ∗m, batchedmodel &b ) { modelattach ∗a = NULL; i f ( b . attached>=0) a = &modelattached [ b . attached ] ; i n t anim = b . anim ; i f ( shadowmapping ) { anim |= ANIM NOSKIN ; i f ( renderpath ! =R FIXEDFUNCTION ) setenvparamf ( ” shadowintensity ” , SHPARAM VERTEX, 1 , b . transparent ) ; }

engine/rendermodel.cpp else { i f ( b . f l a g s&MDL FULLBRIGHT) anim |= ANIM FULLBRIGHT; i f ( b . f l a g s&MDL GHOST) anim |= ANIM GHOST; }

loopv ( transparent ) { transparentmodel &tm = transparent [ i ] ; i f ( lastmodel ! =tm .m) { i f ( lastmodel ) lastmodel−>endrender ( ) ; ( lastmodel = tm .m)−>startrender ( ) ; } i f ( query ! =tm . batched−>query ) { i f ( query ) endquery ( query ) ; query = tm . batched−>query ; i f ( query ) startquery ( query ) ; } renderbatchedmodel ( tm .m, ∗tm . batched ) ; } i f ( query ) endquery ( query ) ; i f ( lastmodel ) lastmodel−>endrender ( ) ;

m−>render ( anim , b . basetime , b . basetime2 , b . pos , b . yaw , b . pitch , b . d , a , b . color , b . dir , b . transparent ) ; } struct transparentmodel { model ∗m; batchedmodel ∗batched ; float dist ; }; s t a t i c i n l i n e bool sorttransparentmodels ( const transparentmodel &x , const transparentmodel &y )

} numbatches = −1;

{ return x . d i s t < y . d i s t ;

399

}

} void endmodelbatches ( ) { vector transparent ; l o o p i ( numbatches ) { modelbatch &b = ∗batches [ i ] ; i f ( b . batched . empty ( ) ) continue ; i f ( b . f l a g s &(MDL SHADOW|MDL DYNSHADOW) ) { vec center , bbradius ; b .m−>boundbox(0/∗frame∗/, center , bbradius ) ; // FIXME l o o p v j ( b . batched ) { batchedmodel &bm = b . batched [ j ] ; i f (bm. f l a g s &(MDL SHADOW|MDL DYNSHADOW) ) renderblob (bm. f l a g s&MDL DYNSHADOW ? BLOB DYNAMIC : BLOB STATIC, bm. d && bm. d−>r a g d o l l ? bm. d−>ragdoll −>center : bm. pos , bm. d ? bm. d−>radius : max( bbradius . x , bbradius . y ) , bm. transparent ) ; } flushblobs ( ) ; } bool rendered = f a l s e ; occludequery ∗query = NULL; i f ( b . f l a g s&MDL GHOST) { l o o p v j ( b . batched ) { batchedmodel &bm = b . batched [ j ] ; i f ( (bm. f l a g s &(MDL CULL VFC|MDL GHOST) ) ! =MDL GHOST || bm. query ) continue ; i f ( ! rendered ) { b .m−>startrender ( ) ; rendered = true ; } renderbatchedmodel ( b .m, bm) ; } i f ( rendered ) { b .m−>endrender ( ) ; rendered = f a l s e ; } } l o o p v j ( b . batched ) { batchedmodel &bm = b . batched [ j ] ; i f (bm. f l a g s &(MDL CULL VFC|MDL GHOST) ) continue ; i f (bm. query ! = query ) { i f ( query ) endquery ( query ) ; query = bm. query ; i f ( query ) startquery ( query ) ; } i f (bm. transparent < 1 && ( ! query || query−>owner==bm. d ) && ! shadowmapping ) { transparentmodel &tm = transparent . add ( ) ; tm .m = b .m; tm . batched = &bm; tm . d i s t = camera1−>o . d i s t (bm. d && bm. d−>r a g d o l l ? bm. d−> ragdoll−>center : bm. pos ) ; continue ; } i f ( ! rendered ) { b .m−>startrender ( ) ; rendered = true ; } renderbatchedmodel ( b .m, bm) ; } i f ( query ) endquery ( query ) ; i f ( rendered ) b .m−>endrender ( ) ; } i f ( transparent . length ( ) ) { transparent . s o r t ( sorttransparentmodels ) ; model ∗lastmodel = NULL; occludequery ∗query = NULL;

void startmodelquery ( occludequery ∗query ) { modelquery = query ; } void endmodelquery ( ) { i n t querybatches = 0; l o o p i ( numbatches ) { modelbatch &b = ∗batches [ i ] ; i f ( b . batched . empty ( ) || b . batched . l a s t ( ) . query ! = modelquery ) continue ; querybatches ++; } i f ( querybatchesfragments = 0; modelquery = NULL; return ; } i n t minattached = modelattached . length ( ) ; startquery ( modelquery ) ; l o o p i ( numbatches ) { modelbatch &b = ∗batches [ i ] ; i f ( b . batched . empty ( ) || b . batched . l a s t ( ) . query ! = modelquery ) continue ; b .m−>startrender ( ) ; do { batchedmodel &bm = b . batched . pop ( ) ; i f (bm. attached>=0) minattached = min ( minattached , bm. attached ) ; renderbatchedmodel ( b .m, bm) ; } while ( b . batched . length ( ) && b . batched . l a s t ( ) . query==modelquery ) ; b .m−>endrender ( ) ; } endquery ( modelquery ) ; modelquery = NULL; modelattached . s e t s i z e ( minattached ) ; } VARP( maxmodelradiusdistance , 10, 200, 1000) ; void rendermodelquery ( model ∗m, dynent ∗d , const vec ¢er , f l o a t radius ) { i f ( fabs ( camera1−>o . x−center . x ) < radius+1 && fabs ( camera1−>o . y−center . y ) < radius+1 && fabs ( camera1−>o . z−center . z ) < radius +1) { d−>query = NULL; return ; } d−>query = newquery ( d ) ; i f ( ! d−>query ) return ; nocolorshader−>set ( ) ; glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ; glDepthMask ( GL FALSE ) ; startquery ( d−>query ) ; i n t br = i n t ( radius ∗2) +1; drawbb ( i v e c ( i n t ( center . x−radius ) , i n t ( center . y−radius ) , i n t ( center . z− radius ) ) , i v e c ( br , br , br ) ) ; endquery ( d−>query ) ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, fading ? GL FALSE : GL TRUE ) ; glDepthMask ( GL TRUE ) ; } extern i n t oqfrags ; void rendermodel ( e n t i t y l i g h t ∗l i g h t , const char ∗mdl , i n t anim , const vec

400

Foundations of Videogame Programming Code Repository &o , f l o a t yaw , f l o a t pitch , i n t f l a g s , dynent ∗d , modelattach ∗a , i n t basetime , i n t basetime2 , f l o a t trans )

{

render3dbox ( center , radius . z , radius . z , radius . x , radius . y ) ; } }

i f ( shadowmapping && ! ( f l a g s &(MDL SHADOW|MDL DYNSHADOW) ) ) return ; model ∗m = loadmodel ( mdl ) ; i f ( !m) return ; vec center ( 0 , 0 , 0) , bbradius ( 0 , 0 , 0) ; f l o a t radius = 0; bool shadow = ! shadowmap && ! g l a r i n g && ( f l a g s &(MDL SHADOW| MDL DYNSHADOW) ) && showblobs , doOQ = f l a g s&MDL CULL QUERY && hasOQ && oqfrags && oqdynent ; i f ( f l a g s &(MDL CULL VFC|MDL CULL DIST|MDL CULL OCCLUDED|MDL CULL QUERY| MDL SHADOW|MDL DYNSHADOW) ) { m−>boundbox(0/∗frame∗/, center , bbradius ) ; // FIXME radius = bbradius . magnitude ( ) ; i f ( d && d−>r a g d o l l ) { radius = max( radius , d−>ragdoll−>radius ) ; center = d−>ragdoll−>center ; } else { center . rotate around z ( yaw∗RAD) ; center . add ( o ) ; } i f ( f l a g s&MDL CULL DIST && center . d i s t ( camera1−>o ) /radius> maxmodelradiusdistance ) return ; i f ( f l a g s&MDL CULL VFC ) { i f ( r e f l e c t i n g || r e f r a c t i n g ) { i f ( r e f l e c t i n g || r e f r a c t i n g >0) { i f ( center . z+radiuso )−radius>r e f l e c t d i s t ) return ; } i f ( isfoggedsphere ( radius , center ) ) return ; i f ( shadowmapping && ! isshadowmapcaster ( center , radius ) ) return ; } i f ( shadowmapping ) { i f (d) { i f ( f l a g s&MDL CULL OCCLUDED && d−>occluded>=OCCLUDE PARENT) return ; i f (doOQ && d−>occluded+1>=OCCLUDE BB && d−>query && d−>query −>owner==d && checkquery ( d−>query ) ) return ; } i f ( ! addshadowmapcaster ( center , radius , radius ) ) return ; } e l s e i f ( f l a g s&MDL CULL OCCLUDED && modeloccluded ( center , radius ) ) { i f ( ! r e f l e c t i n g && ! r e f r a c t i n g && d ) { d−>occluded = OCCLUDE PARENT; i f (doOQ) rendermodelquery (m, d , center , radius ) ; } return ; } e l s e i f (doOQ && d && d−>query && d−>query−>owner==d && checkquery ( d −>query ) ) { i f ( ! r e f l e c t i n g && ! r e f r a c t i n g ) { i f ( d−>occludedoccluded ++; rendermodelquery (m, d , center , radius ) ; } return ; } } i f ( f l a g s&MDL NORENDER) anim |= ANIM NORENDER; e l s e i f ( showboundingbox && ! shadowmapping && ! r e f l e c t i n g && ! r e f r a c t i n g && editmode ) { i f ( d && showboundingbox==1) { render3dbox ( d−>o , d−>eyeheight , d−>aboveeye , d−>radius ) ; r e n d e r e l l i p s e ( d−>o , d−>xradius , d−>yradius , d−>yaw ) ; } else { vec center , radius ; i f ( showboundingbox==1) m−>c o l l i s i o n b o x ( 0 , center , radius ) ; e l s e m−>boundbox ( 0 , center , radius ) ; rotatebb ( center , radius , i n t ( yaw ) ) ; center . add ( o ) ;

vec l i g h t c o l o r ( 1 , 1 , 1) , l i g h t d i r ( 0 , 0 , 1) ; i f ( ! shadowmapping ) { vec pos = o ; i f (d) { i f ( ! r e f l e c t i n g && ! r e f r a c t i n g ) d−>occluded = OCCLUDE NOTHING; i f ( ! l i g h t ) l i g h t = &d−>l i g h t ; i f ( f l a g s&MDL LIGHT && l i g h t−>m i l l i s ! = l a s t m i l l i s ) { i f ( d−>r a g d o l l ) { pos = d−>ragdoll−>center ; pos . z += radius /2; } e l s e i f ( d−>type < ENT CAMERA) pos . z += 0.75 f ∗(d−>eyeheight + d−>aboveeye ) ; l i g h t r e a c h i n g ( pos , l i g h t−>color , l i g h t−>dir , ( f l a g s& MDL LIGHT FAST ) ! = 0 ) ; dynlightreaching ( pos , l i g h t−>color , l i g h t−>dir , ( f l a g s& MDL HUD) ! = 0 ) ; game : : l i g h t e f f e c t s ( d , l i g h t−>color , l i g h t−>d i r ) ; l i g h t−>m i l l i s = l a s t m i l l i s ; } } e l s e i f ( f l a g s&MDL LIGHT ) { i f ( ! light ) { l i g h t r e a c h i n g ( pos , l i g h t c o l o r , l i g h t d i r , ( f l a g s& MDL LIGHT FAST ) ! = 0 ) ; dynlightreaching ( pos , l i g h t c o l o r , l i g h t d i r , ( f l a g s&MDL HUD) !=0) ; } e l s e i f ( l i g h t−>m i l l i s ! = l a s t m i l l i s ) { l i g h t r e a c h i n g ( pos , l i g h t−>color , l i g h t−>dir , ( f l a g s& MDL LIGHT FAST ) ! = 0 ) ; dynlightreaching ( pos , l i g h t−>color , l i g h t−>dir , ( f l a g s& MDL HUD) ! = 0 ) ; l i g h t−>m i l l i s = l a s t m i l l i s ; } } i f ( l i g h t ) { l i g h t c o l o r = l i g h t−>c o l o r ; l i g h t d i r = l i g h t−>d i r ; } i f ( f l a g s&MDL DYNLIGHT) dynlightreaching ( pos , l i g h t c o l o r , l i g h t d i r , ( f l a g s&MDL HUD) ! = 0 ) ; } i f ( a ) f o r ( i n t i = 0; a [ i ] . tag ; i ++) { i f ( a [ i ] . name) a [ i ] .m = loadmodel ( a [ i ] . name) ; // i f ( a [ i ] .m && a [ i ] .m−>type ( ) ! =m−>type ( ) ) a [ i ] .m = NULL; } i f ( ! d || r e f l e c t i n g || r e f r a c t i n g || shadowmapping ) doOQ = f a l s e ; i f ( numbatches>=0) { modelbatch &mb = addbatchedmodel (m) ; batchedmodel &b = mb. batched . add ( ) ; b . query = modelquery ; b . pos = o ; b. color = lightcolor ; b. dir = lightdir ; b . anim = anim ; b . yaw = yaw ; b . pitch = pitch ; b . basetime = basetime ; b . basetime2 = basetime2 ; b . transparent = trans ; b . f l a g s = f l a g s & ˜ ( MDL CULL VFC | MDL CULL DIST | MDL CULL OCCLUDED) ; i f ( ! shadow || r e f l e c t i n g || r e f r a c t i n g >0) { b . f l a g s &= ˜ (MDL SHADOW|MDL DYNSHADOW) ; i f ( ( f l a g s&MDL CULL VFC ) && r e f r a c t i n g= r e f l e c t z ) b . f l a g s |= MDL CULL VFC; } mb. f l a g s |= b . f l a g s ; b.d = d; b . attached = a ? modelattached . length ( ) : −1; i f ( a ) f o r ( i n t i = 0 ; ; i ++) { modelattached . add ( a [ i ] ) ; i f ( ! a [ i ] . tag ) break ; } i f (doOQ) d−>query = b . query = newquery ( d ) ; return ; } i f ( shadow && ! r e f l e c t i n g && r e f r a c t i n g r a g d o l l ? center : o , d ? d−>radius : max( bbradius . x , bbradius . y ) , trans ) ; flushblobs ( ) ; i f ( ( f l a g s&MDL CULL VFC ) && r e f r a c t i n g= r e f l e c t z ) return ; } m−>startrender ( ) ; i f ( shadowmapping ) { anim |= ANIM NOSKIN ; i f ( renderpath ! =R FIXEDFUNCTION ) setenvparamf ( ” shadowintensity ” , SHPARAM VERTEX, 1 , trans ) ; } else { i f ( f l a g s&MDL FULLBRIGHT) anim |= ANIM FULLBRIGHT; i f ( f l a g s&MDL GHOST) anim |= ANIM GHOST; } i f (doOQ) { d−>query = newquery ( d ) ; i f ( d−>query ) startquery ( d−>query ) ; } m−>render ( anim , basetime , basetime2 , o , yaw , pitch , d , a , l i g h t c o l o r , l i g h t d i r , trans ) ; i f (doOQ && d−>query ) endquery ( d−>query ) ; m−>endrender ( ) ; } void abovemodel ( vec &o , const char ∗mdl ) { model ∗m = loadmodel ( mdl ) ; i f ( !m) return ; o . z += m−>above(0/∗frame∗/) ; } bool matchanim ( const char ∗name, const char ∗pattern ) { f o r ( ; ; pattern ++) { const char ∗s = name; char c ; f o r ( ; ; pattern ++) { c = ∗pattern ; i f ( ! c || c = = ’ | ’ ) break ; else i f ( c == ’∗ ’) { i f ( ! ∗ s || iscubespace (∗s ) ) break ; do s ++; while (∗s && ! iscubespace (∗s ) ) ; } e l s e i f ( c!=∗s ) break ; e l s e s ++; } i f ( ! ∗ s && ( ! c || c = = ’ | ’ ) ) return true ; pattern = strchr ( pattern , ’ | ’ ) ; i f ( ! pattern ) break ; } return f a l s e ; } void findanims ( const char ∗pattern , vector &anims ) { l o o p i ( s i z e o f ( animnames ) / s i z e o f ( animnames [ 0 ] ) ) i f ( matchanim ( animnames [ i ] , pattern ) ) anims . add ( i ) ; } ICOMMAND( findanims , ” s ” , ( char ∗name) , { vector anims ; findanims ( name, anims ) ; vector buf ; s t r i n g num; loopv ( anims ) { formatstring (num) (”%d ” , anims [ i ] ) ; i f ( i > 0) buf . add ( ’ ’ ) ; buf . put (num, s t r l e n (num) ) ; } buf . add ( ’ \ 0 ’ ) ; r e s u l t ( buf . getbuf ( ) ) ; }) ; void loadskin ( const char ∗dir , const char ∗a l t d i r , Texture ∗&skin , Texture ∗&masks ) // model skin sharing { #define i f n o l o a d ( tex , path ) i f ( ( tex = textureload ( path , 0 , true , f a l s e ) ) ==notexture )

401

#define tryload ( tex , p r e f i x , cmd, name) \ i f n o l o a d ( tex , makerelpath ( mdir , name ” . jpg ” , p r e f i x , cmd) ) \ { \ i f n o l o a d ( tex , makerelpath ( mdir , name ” . png ” , p r e f i x , cmd) ) \ { \ i f n o l o a d ( tex , makerelpath ( maltdir , name ” . jpg ” , p r e f i x , cmd) ) \ { \ i f n o l o a d ( tex , makerelpath ( maltdir , name ” . png ” , p r e f i x , cmd) ) return ; \ } \ } \ } defformatstring ( mdir ) ( ” packages/models/%s ” , d i r ) ; defformatstring ( maltdir ) ( ” packages/models/%s ” , a l t d i r ) ; masks = notexture ; tryload ( skin , NULL, NULL, ” skin ” ) ; tryload ( masks, ””, NULL, ”masks ” ) ; } // convenient function that covers the usual anims f o r players/monsters/ npcs VAR( animoverride , −1, 0 , NUMANIMS−1); VAR( testanims , 0 , 0 , 1) ; VAR( t e s t p i t c h , −90, 0 , 90) ; void r e n d e r c l i e n t ( dynent ∗d , const char ∗mdlname, modelattach ∗ attachments , i n t hold , i n t attack , i n t attackdelay , i n t l a s t a c t i o n , i n t lastpain , f l o a t fade , bool r a g d o l l ) { i n t anim = hold ? hold : ANIM IDLE|ANIM LOOP; f l o a t yaw = testanims && d==player ? 0 : d−>yaw+90, pitch = t e s t p i t c h && d==player ? t e s t p i t c h : d−>pitch ; vec o = d−>feetpos ( ) ; i n t basetime = 0; i f ( animoverride ) anim = ( animoverrides t a t e ==CS DEAD) { anim = ANIM DYING|ANIM NOPITCH ; basetime = lastpain ; i f ( ragdoll ) { i f ( ! d−>r a g d o l l || d−>ragdoll−>m i l l i s < basetime ) { DELETEP( d−>r a g d o l l ) ; anim |= ANIM RAGDOLL; } } e l s e i f ( l a s t m i l l i s−basetime>1000) anim = ANIM DEAD|ANIM LOOP| ANIM NOPITCH ; } e l s e i f ( d−>s t a t e ==CS EDITING || d−>s t a t e ==CS SPECTATOR) anim = ANIM EDIT|ANIM LOOP; e l s e i f ( d−>s t a t e ==CS LAGGED) anim = ANIM LAG| ANIM LOOP; else { i f ( l a s t m i l l i s−lastpain < 300) { anim = ANIM PAIN ; basetime = lastpain ; } e l s e i f ( lastpain < l a s t a c t i o n && ( attack < 0 || ( d−>type ! = ENT AI && l a s t m i l l i s−l a s t a c t i o n < attackdelay ) ) ) { anim = attack < 0 ? −attack : attack ; basetime = l a s t a c t i o n ; } i f ( d−>inwater && d−>physstatemove || d−>s t r a f e ) ) || d−>v e l . z+d−> f a l l i n g . z>0 ? ANIM SWIM : ANIM SINK ) |ANIM LOOP )timeinair >100) anim |= (ANIM JUMP|ANIM END)move || d−>s t r a f e ) ) { i f ( d−>move>0) anim |= (ANIM FORWARD|ANIM LOOP )s t r a f e>0 ? ANIM LEFT : ANIM RIGHT ) |ANIM LOOP )>= ANIM SECONDARY; } i f ( d−>r a g d o l l && ( ! r a g d o l l || ( anim&ANIM INDEX ) ! =ANIM DYING ) ) DELETEP( d−>r a g d o l l ) ; i f ( ! ( ( anim>>ANIM SECONDARY)&ANIM INDEX ) ) anim |= ( ANIM IDLE|ANIM LOOP ) s t a t e ==CS LAGGED) fade = min ( fade , 0.3 f ) ; e l s e f l a g s |= MDL DYNSHADOW; i f ( modelpreviewing ) f l a g s &= ˜ ( MDL LIGHT | MDL FULLBRIGHT | MDL CULL VFC | MDL CULL OCCLUDED | MDL CULL QUERY | MDL CULL DIST | MDL DYNSHADOW) ; rendermodel (NULL, mdlname, anim , o , yaw , pitch , f l a g s , d , attachments , basetime , 0 , fade ) ;

i f ( !m) return ; vec center , radius ; m−>c o l l i s i o n b o x ( 0 , center , radius ) ; i f ( d−>type==ENT INANIMATE && !m−>e l l i p s e c o l l i d e ) { d−>c o l l i d e t y p e = COLLIDE OBB; //d−>c o l l i d e t y p e = COLLIDE AABB; //rotatebb ( center , radius , i n t ( d−>yaw ) ) ; } d−>xradius = radius . x + fabs ( center . x ) ; d−>yradius = radius . y + fabs ( center . y ) ; d−>radius = d−>c o l l i d e t y p e ==COLLIDE OBB ? s q r t f ( d−>xradius∗d−> xradius + d−>yradius∗d−>yradius ) : max( d−>xradius , d−>yradius ) ; d−>eyeheight = ( center . z−radius . z ) + radius . z∗2∗m−>eyeheight ; d−>aboveeye = radius . z∗2∗(1.0 f−m−>eyeheight ) ;

} void setbbfrommodel ( dynent ∗d , const char ∗mdl ) { model ∗m = loadmodel ( mdl ) ;

}

engine/renderparticles.cpp // r e n d e r p a r t i c l e s . cpp

};

#include ” engine . h” #include ” rendertarget . h”

s t a t i c vector

emitters ; s t a t i c p a r t i c l e e m i t t e r ∗seedemitter = NULL;

Shader ∗particleshader = NULL, ∗particlenotextureshader = NULL;

void c l e a r p a r t i c l e e m i t t e r s ( ) { emitters . shrink ( 0 ) ; regenemitters = true ; }

VARP( p a r t i c l e s i z e , 20, 100, 500) ; // Check e m i t p a r t i c l e s ( ) to l i m i t the rate that p a r i c l e s can be emitted f o r models/sparklies // Automatically stops p a r t i c l e s being emitted when paused or in r e f l e c t i v e drawing VARP( e m i t m i l l i s , 1 , 17, 1000) ; s t a t i c i n t lastemitframe = 0 , e m i t o f f s e t = 0; s t a t i c bool canemit = f a l s e , regenemitters = f a l s e , canstep = f a l s e ; s t a t i c bool e m i t p a r t i c l e s ( ) { i f ( r e f l e c t i n g || r e f r a c t i n g ) return f a l s e ; return canemit || e m i t o f f s e t ; } VARP( showparticles , 0 , 1 , 1) ; VAR( c u l l p a r t i c l e s , 0 , 1 , 1) ; VAR( r e p l a y p a r t i c l e s , 0 , 1 , 1) ; VARN( seedparticles , seedmillis , 0 , 3000, 10000) ; VAR( dbgpcull , 0 , 0 , 1) ; VAR( dbgpseed , 0 , 0 , 1) ; struct p a r t i c l e e m i t t e r { e x t e n t i t y ∗ent ; vec bbmin , bbmax; vec center ; f l o a t radius ; i v e c bborigin , bbsize ; i n t maxfade , lastemit , l a s t c u l l ;

void addparticleemitters ( ) { emitters . shrink ( 0 ) ; const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) { e x t e n t i t y &e = ∗ents [ i ] ; i f ( e . type ! = ET PARTICLES ) continue ; emitters . add ( p a r t i c l e e m i t t e r (&e ) ) ; } regenemitters = f a l s e ; } enum { PT PART = 0 , PT TAPE , PT TRAIL , PT TEXT , PT TEXTUP, PT METER, PT METERVS, PT FIREBALL , PT LIGHTNING , PT FLARE , PT MOD PT RND4 PT LERP PT TRACK PT GLARE PT SOFT PT HFLIP PT VFLIP PT ROT PT CULL PT FEW PT ICON PT FLIP

p a r t i c l e e m i t t e r ( e x t e n t i t y ∗ent ) : ent ( ent ) , bbmin ( ent−>o ) , bbmax( ent−>o ) , maxfade(−1) , lastemit ( 0 ) , lastcull (0) {} void f i n a l i z e ( ) { center = vec ( bbmin ) . add (bbmax) . mul( 0 . 5 f ) ; radius = bbmin . d i s t (bbmax) /2; bborigin = i v e c ( i n t ( f l o o r ( bbmin . x ) ) , i n t ( f l o o r ( bbmin . y ) ) , i n t ( f l o o r ( bbmin . z ) ) ) ; bbsize = i v e c ( i n t ( c e i l (bbmax. x ) ) , i n t ( c e i l (bbmax. y ) ) , i n t ( c e i l ( bbmax. z ) ) ) . sub ( bborigin ) ; i f ( dbgpseed ) conoutf (CON DEBUG, ” radius : %f , maxfade : %d ” , radius , maxfade ) ; } void extendbb ( const vec &o , f l o a t s i z e = 0) { bbmin . x = min ( bbmin . x , o . x − s i z e ) ; bbmin . y = min ( bbmin . y , o . y − s i z e ) ; bbmin . z = min ( bbmin . z , o . z − s i z e ) ; bbmax. x = max(bbmax. x , o . x + s i z e ) ; bbmax. y = max(bbmax. y , o . y + s i z e ) ; bbmax. z = max(bbmax. z , o . z + s i z e ) ; } void extendbb ( f l o a t z , f l o a t s i z e = 0) { bbmin . z = min ( bbmin . z , z − s i z e ) ; bbmax. z = max(bbmax. z , z + s i z e ) ; }

= = = = = = = = = = = = =

1next = parempty ; parempty = l i s t ; l i s t = NULL; } void resettracked ( physent ∗owner ) { i f ( ! ( type&PT TRACK ) ) return ; f o r ( l i s t p a r t i c l e ∗∗prev = &l i s t , ∗cur = l i s t ; cur ; cur = ∗prev ) { i f ( ! owner || cur−>owner==owner ) { ∗prev = cur−>next ; cur−>next = parempty ; parempty = cur ; } e l s e prev = &cur−>next ; } } p a r t i c l e ∗addpart ( const vec &o , const vec &d , i n t fade , i n t color , f l o a t size , i n t g r a v i t y ) { i f ( ! parempty ) { l i s t p a r t i c l e ∗ps = new l i s t p a r t i c l e [ 2 5 6 ] ; l o o p i (255) ps [ i ] . next = &ps [ i + 1 ] ; ps [ 2 5 5 ] . next = parempty ; parempty = ps ; } l i s t p a r t i c l e ∗p = parempty ; parempty = p−>next ; p−>next = l i s t ; l i s t = p; p−>o = o ; p−>d = d ; p−>g r a v i t y = g r a v i t y ; p−>fade = fade ; p−>m i l l i s = l a s t m i l l i s + e m i t o f f s e t ; p−>c o l o r = bvec ( color>>16, ( color>>8)&0xFF , c o l o r&0xFF ) ; p−>s i z e = s i z e ; p−>owner = NULL; p−>f l a g s = 0;

404

Foundations of Videogame Programming Code Repository f l o a t r i g h t = 8∗FONTH, l e f t = p−>progress/100.0 f∗r i g h t ; g l T r a n s l a t e f(−r i g h t /2.0 f , 0 , 0) ;

return p ; } i n t count ( ) { i n t num = 0; l i s t p a r t i c l e ∗lp ; f o r ( lp = l i s t ; lp ; lp = lp−>next ) num++; return num; }

i f ( outlinemeters ) { g l C o l o r 3 f ( 0 , 0.8 f , 0) ; glBegin ( GL TRIANGLE STRIP ) ; loopk ( 1 0 ) { const vec2 &sc = sincos360 [ k∗(180/(10−1) ) ] ; f l o a t c = ( 0 . 5 f + 0.1 f )∗sc . y , s = 0.5 f − ( 0 . 5 f + 0.1 f )∗sc . x ; g l V e r t e x 2 f(−c∗FONTH, s∗FONTH) ; g l V e r t e x 2 f ( r i g h t + c∗FONTH, s∗FONTH) ; } glEnd ( ) ; }

bool haswork ( ) { return ( l i s t ! = NULL) ; } v i r t u a l void startrender ( ) = 0; v i r t u a l void endrender ( ) = 0; v i r t u a l void renderpart ( l i s t p a r t i c l e ∗p , const vec &o , const vec &d , i n t blend , i n t ts , uchar ∗c o l o r ) = 0;

i f ( basetype==PT METERVS) glColor3ubv ( p−>color2 ) ; e l s e g l C o l o r 3 f ( 0 , 0 , 0) ; glBegin ( GL TRIANGLE STRIP ) ; loopk ( 1 0 ) { const vec2 &sc = sincos360 [ k∗(180/(10−1) ) ] ; f l o a t c = 0.5 f∗sc . y , s = 0.5 f − 0.5 f∗sc . x ; g l V e r t e x 2 f ( l e f t + c∗FONTH, s∗FONTH) ; g l V e r t e x 2 f ( r i g h t + c∗FONTH, s∗FONTH) ; } glEnd ( ) ;

void render ( ) { startrender ( ) ; i f ( texname ) { i f ( ! tex ) tex = textureload ( texname , texclamp ) ; glBindTexture ( GL TEXTURE 2D, tex−>id ) ; }

i f ( outlinemeters ) { g l C o l o r 3 f ( 0 , 0.8 f , 0) ; glBegin ( GL TRIANGLE FAN ) ; loopk ( 1 0 ) { const vec2 &sc = sincos360 [ k∗(180/(10−1) ) ] ; f l o a t c = ( 0 . 5 f + 0.1 f )∗sc . y , s = 0.5 f − ( 0 . 5 f + 0.1 f )∗sc . x ; g l V e r t e x 2 f ( l e f t + c∗FONTH, s∗FONTH) ; } glEnd ( ) ; }

f o r ( l i s t p a r t i c l e ∗∗prev = &l i s t , ∗p = l i s t ; p ; p = ∗prev ) { vec o , d ; i n t blend , t s ; calc ( p , blend , ts , o , d , canstep ) ; i f ( blend > 0) { renderpart ( p , o , d , blend , ts , p−>c o l o r . v ) ; i f ( p−>fade > 5 || ! canstep ) { prev = &p−>next ; continue ; }

glColor3ubv ( c o l o r ) ; glBegin ( GL TRIANGLE STRIP ) ; loopk ( 1 0 ) { const vec2 &sc = sincos360 [ k∗(180/(10−1) ) ] ; f l o a t c = 0.5 f∗sc . y , s = 0.5 f − 0.5 f∗sc . x ; g l V e r t e x 2 f(−c∗FONTH, s∗FONTH) ; g l V e r t e x 2 f ( l e f t + c∗FONTH, s∗FONTH) ; } glEnd ( ) ;

} //remove ∗prev = p−>next ; p−>next = parempty ; killpart (p) ; parempty = p ; } endrender ( ) ; } }; l i s t p a r t i c l e ∗l i s t r e n d e r e r : : parempty = NULL; struct meterrenderer : l i s t r e n d e r e r { meterrenderer ( i n t type ) : l i s t r e n d e r e r ( type ) {} void startrender ( ) { glDisable (GL BLEND) ; glDisable ( GL TEXTURE 2D ) ; particlenotextureshader−>set ( ) ; } void endrender ( ) { glEnable (GL BLEND) ; glEnable ( GL TEXTURE 2D ) ; particleshader−>set ( ) ; } void renderpart ( l i s t p a r t i c l e ∗p , const vec &o , const vec &d , i n t blend , i n t ts , uchar ∗c o l o r ) { i n t basetype = type&0xFF ; glPushMatrix ( ) ; f l o a t scale = p−>s i z e /80.0 f ; GLfloat billboardmatrix [ 1 6 ] = { scale∗camright . x , scale∗camright . y , scale∗camright . z , 0 , −scale∗camup. x , −scale∗camup. y , −scale∗camup. z , 0 , −scale∗camdir . x , −scale∗camdir . y , −scale∗camdir . z , 0 , o.x, o.y, o.z , 1 }; glMultMatrixf ( billboardmatrix ) ;

glPopMatrix ( ) ; } }; s t a t i c meterrenderer meters (PT METER|PT LERP ) , metervs (PT METERVS|PT LERP ); struct textrenderer : l i s t r e n d e r e r { textrenderer ( i n t type ) : l i s t r e n d e r e r ( type ) {} void startrender ( ) { } void endrender ( ) { } void k i l l p a r t ( l i s t p a r t i c l e ∗p ) { i f ( p−>t e x t && p−>f l a g s &1) d e l e t e [ ] p−>t e x t ; } void renderpart ( l i s t p a r t i c l e ∗p , const vec &o , const vec &d , i n t blend , i n t ts , uchar ∗c o l o r ) { glPushMatrix ( ) ; f l o a t scale = p−>s i z e /80.0 f ; GLfloat billboardmatrix [ 1 6 ] = { scale∗camright . x , scale∗camright . y , scale∗camright . z , 0 , −scale∗camup. x , −scale∗camup. y , −scale∗camup. z , 0 , −scale∗camdir . x , −scale∗camdir . y , −scale∗camdir . z , 0 , o.x, o.y, o.z , 1 }; glMultMatrixf ( billboardmatrix ) ; f l o a t x o f f = −text width ( p−>t e x t ) /2;

engine/renderparticles.cpp f l o a t y o f f = 0; i f ( ( type&0xFF ) ==PT TEXTUP ) { x o f f += detrnd ( ( s i z e t ) p , 100)−50; y o f f −= detrnd ( ( s i z e t ) p , 101) ; } //@TODO instead in worldspace beforehand? g l T r a n s l a t e f ( x o f f , y o f f , 50) ; draw text ( p−>text , 0 , 0 , c o l o r [ 0 ] , c o l o r [ 1 ] , c o l o r [ 2 ] , blend ) ; glPopMatrix ( ) ; } }; s t a t i c textrenderer t e x t s ( PT TEXT|PT LERP ) ; template s t a t i c i n l i n e void modifyblend ( const vec &o , i n t &blend ) { blend = min ( blendo ) ; c . cross ( dir2 , dir1 ) . normalize ( ) . mul ( s i z e ) ; vs [ 0 ] . pos = vec ( d . x−c . x , d . y−c . y , d . z−c . z ) ; vs [ 1 ] . pos = vec ( o . x−c . x , o . y−c . y , o . z−c . z ) ; vs [ 2 ] . pos = vec ( o . x+c . x , o . y+c . y , o . z+c . z ) ; vs [ 3 ] . pos = vec ( d . x+c . x , d . y+c . y , d . z+c . z ) ; } template i n l i n e void genpos(const vec &o , const vec &d , f l o a t size , i n t ts , i n t grav , p a r t v e r t ∗vs ) { vec e = d ; i f ( grav ) e . z −= f l o a t ( t s ) /grav ; e . div(−75.0 f ) . add ( o ) ; genpos(o , e , size , ts , grav , vs ) ; } template s t a t i c i n l i n e void genrotpos ( const vec &o , const vec &d , f l o a t size , i n t grav , i n t ts , p a r t v e r t ∗vs , i n t r o t ) { genpos(o , d , size , grav , ts , vs ) ; } #define ROTCOEFFS( n ) { \ vec(−1, 1 , 0) . rotate around z ( n∗2∗M PI/32.0 f ) , \ vec ( 1 , 1 , 0) . rotate around z ( n∗2∗M PI/32.0 f ) , \ vec ( 1 , −1, 0) . rotate around z ( n∗2∗M PI/32.0 f ) , \ vec(−1, −1, 0) . rotate around z ( n∗2∗M PI/32.0 f ) \ } s t a t i c const vec r o t c o e f f s [ 3 2 ] [ 4 ] = { ROTCOEFFS( 0 ) , ROTCOEFFS( 1 ) , ROTCOEFFS( 2 ) , ROTCOEFFS( 3 ) , ( 4 ) , ROTCOEFFS( 5 ) , ROTCOEFFS( 6 ) , ROTCOEFFS( 7 ) , ROTCOEFFS( 8 ) , ROTCOEFFS( 9 ) , ROTCOEFFS( 1 0 ) , ROTCOEFFS( 1 1 ) , ( 1 2 ) , ROTCOEFFS( 1 3 ) , ROTCOEFFS( 1 4 ) , ROTCOEFFS( 1 5 ) , ROTCOEFFS( 1 6 ) , ROTCOEFFS( 1 7 ) , ROTCOEFFS( 1 8 ) , ROTCOEFFS( 1 9 ) , ( 2 0 ) , ROTCOEFFS( 2 1 ) , ROTCOEFFS( 2 2 ) , ROTCOEFFS( 7 ) , ROTCOEFFS( 2 4 ) , ROTCOEFFS( 2 5 ) , ROTCOEFFS( 2 6 ) , ROTCOEFFS( 2 7 ) , ( 2 8 ) , ROTCOEFFS( 2 9 ) , ROTCOEFFS( 3 0 ) , ROTCOEFFS( 3 1 ) , };

405

template i n l i n e void seedpos(p a r t i c l e e m i t t e r &pe , const vec &o , const vec &d , i n t fade , f l o a t size , i n t grav ) { pe . extendbb ( d , s i z e ) ; } template i n l i n e void seedpos(p a r t i c l e e m i t t e r &pe , const vec &o , const vec &d , i n t fade , f l o a t size , i n t grav ) { vec e = d ; i f ( grav ) e . z −= f l o a t ( fade ) /grav ; e . div(−75.0 f ) . add ( o ) ; pe . extendbb ( e , s i z e ) ; } template struct varenderer : partrenderer { p a r t v e r t ∗v e r t s ; p a r t i c l e ∗parts ; i n t maxparts , numparts , lastupdate , rndmask ; varenderer ( const char ∗texname , i n t type , i n t c o l l i d e = 0) : partrenderer ( texname , 3 , type , c o l l i d e ) , v e r t s (NULL) , parts (NULL) , maxparts ( 0 ) , numparts ( 0 ) , lastupdate (−1) , rndmask ( 0 ) { i f ( type & PT HFLIP ) rndmask |= 0x01 ; i f ( type & PT VFLIP ) rndmask |= 0x02 ; i f ( type & PT ROT ) rndmask |= 0x1F>8, ( p−>c o l o r [1]∗ blend )>>8, ( p−>c o l o r [2]∗ blend )>>8, 255) i f ( type&PT MOD) SETMODCOLOR; e l s e SETCOLOR( p−>c o l o r [ 0 ] , p−>c o l o r [ 1 ] , p−>c o l o r [ 2 ] , blend ) ;

return ( numparts > 0) ; } bool usesvertexarray ( ) { return true ; }

} e l s e i f ( type&PT MOD) SETMODCOLOR; e l s e l o o p i ( 4 ) vs [ i ] . alpha = blend ;

p a r t i c l e ∗addpart ( const vec &o , const vec &d , i n t fade , i n t color , f l o a t size , i n t g r a v i t y ) { p a r t i c l e ∗p = parts + ( numparts < maxparts ? numparts++ : rnd ( maxparts ) ) ; //next f r e e s l o t , or k i l l a random k i t t e n p−>o = o ; p−>d = d ; p−>g r a v i t y = g r a v i t y ; p−>fade = fade ; p−>m i l l i s = l a s t m i l l i s + e m i t o f f s e t ; p−>c o l o r = bvec ( color>>16, ( color>>8)&0xFF , c o l o r&0xFF ) ; p−>s i z e = s i z e ; p−>owner = NULL; p−>f l a g s = 0x80 | ( rndmask ? rnd (0x80 ) & rndmask : 0) ; lastupdate = −1; return p ; }

i f ( type&PT ROT ) genrotpos(o , d , p−>size , ts , p−>g r a v i t y , vs , ( p −>f l a g s>>2)&0x1F ) ; e l s e genpos(o , d , p−>size , ts , p−>g r a v i t y , vs ) ; } void update ( ) { i f ( l a s t m i l l i s == lastupdate ) return ; lastupdate = l a s t m i l l i s ; l o o p i ( numparts ) { p a r t i c l e ∗p = &parts [ i ] ; p a r t v e r t ∗vs = &v e r t s [ i ∗4]; i f ( p−>fade < 0) { do { −−numparts ; i f ( numparts f l a g s&0x80 ) ! = 0 ) ; }

void seedemitter ( p a r t i c l e e m i t t e r &pe , const vec &o , const vec &d , i n t fade , f l o a t size , i n t g r a v i t y ) { pe . maxfade = max( pe . maxfade , fade ) ; s i z e ∗= SQRT2; pe . extendbb ( o , s i z e ) ; seedpos(pe , o , d , fade , size , g r a v i t y ) ; i f ( ! g r a v i t y ) return ; vec end ( o ) ; f l o a t t = fade ; end . add ( vec ( d ) . mul ( t /5000.0 f ) ) ; end . z −= t∗t /(2.0 f ∗ 5000.0 f ∗ g r a v i t y ) ; pe . extendbb ( end , s i z e ) ;

} void render ( ) { i f ( ! tex ) tex = textureload ( texname , texclamp ) ; glBindTexture ( GL TEXTURE 2D, tex−>id ) ; glVertexPointer ( 3 , GL FLOAT, s i z e o f ( p a r t v e r t ) , &verts−>pos ) ; glTexCoordPointer ( 2 , GL FLOAT, s i z e o f ( p a r t v e r t ) , &verts−>u ) ; glColorPointer ( 4 , GL UNSIGNED BYTE, s i z e o f ( p a r t v e r t ) , &verts−>c o l o r ); glDrawArrays (GL QUADS, 0 , numparts∗4) ; }

f l o a t tpeak = d . z∗g r a v i t y ; i f ( tpeak > 0 && tpeak < fade ) pe . extendbb ( o . z + 1.5 f∗d . z∗tpeak /5000.0 f , s i z e ) ; } void genverts ( p a r t i c l e ∗p , p a r t v e r t ∗vs , bool regen ) { vec o , d ; i n t blend , t s ; calc ( p , blend , ts , o , d ) ; i f ( blend fade fade = −1; //mark to remove on next pass ( i . e . a f t e r render ) modifyblend(o , blend ) ; i f ( regen ) { p−>f l a g s &= ˜0x80 ; #define SETTEXCOORDS( u1c , u2c , v1c , v2c , body ) \ { \ f l o a t u1 = u1c , u2 = u2c , v1 = v1c , v2 = v2c ; \ body ; \ vs [ 0 ] . u = u1 ; \ vs [ 0 ] . v = v1 ; \ vs [ 1 ] . u = u2 ; \ vs [ 1 ] . v = v1 ; \ vs [ 2 ] . u = u2 ; \ vs [ 2 ] . v = v2 ; \ vs [ 3 ] . u = u1 ; \ vs [ 3 ] . v = v2 ; \ } i f ( type&PT RND4 ) { f l o a t tx = 0.5 f ∗ ( ( p−>f l a g s>>5)&1) , ty = 0.5 f ∗ ( ( p−>f l a g s>>6) &1) ; SETTEXCOORDS( tx , tx + 0.5 f , ty , ty + 0.5 f , { i f ( p−>f l a g s&0x01 ) swap ( u1, u2 ) ; i f ( p−>f l a g s&0x02 ) swap ( v1 , v2 ) ; }) ; } e l s e i f ( type&PT ICON ) { f l o a t tx = 0.25 f ∗(p−>f l a g s &3) , ty = 0.25 f ∗ ( ( p−>f l a g s>>2)&3) ; SETTEXCOORDS( tx , tx + 0.25 f , ty , ty + 0.25 f , {}) ; } e l s e SETTEXCOORDS( 0 , 1 , 0 , 1 , {}) ; #define SETCOLOR( r , g , b , a ) \ do { \ uchar c o l [ 4 ] = { uchar ( r ) , uchar ( g ) , uchar ( b ) , uchar ( a ) }; \ l o o p i ( 4 ) memcpy( vs [ i ] . c o l o r . v , col , s i z e o f ( c o l ) ) ; \ } while ( 0 )

}; typedef varenderer quadrenderer ; typedef varenderer taperenderer ; typedef varenderer t r a i l r e n d e r e r ; #include #include #include #include

” depthfx . h” ” explosion . h” ” l e n s f l a r e . h” ” l i g h t n i n g . h”

struct softquadrenderer : quadrenderer { softquadrenderer ( const char ∗texname , i n t type , i n t c o l l i d e = 0) : quadrenderer ( texname , type |PT SOFT , c o l l i d e ) { } i n t adddepthfx ( vec &bbmin , vec &bbmax) { i f ( ! depthfxtex . highprecision ( ) && ! depthfxtex . emulatehighprecision ( ) ) return 0; i n t numsoft = 0; l o o p i ( numparts ) { p a r t i c l e &p = parts [ i ] ; f l o a t radius = p . s i z e∗SQRT2; vec o , d ; i n t blend , t s ; calc (&p , blend , ts , o , d , f a l s e ) ; i f ( ! isfoggedsphere ( radius , p . o ) && ( depthfxscissor !=2 || depthfxtex . addscissorbox ( p . o , radius ) ) ) { numsoft++; loopk ( 3 ) { bbmin [ k ] = min ( bbmin [ k ] , o [ k ] − radius ) ; bbmax[ k ] = max(bbmax[ k ] , o [ k ] + radius ) ; } } } return numsoft ; } }; s t a t i c partrenderer ∗parts [ ] = { new quadrenderer(”packages/ p a r t i c l e s /blood . png ” , PT PART| PT FLIP

engine/renderparticles.cpp |PT MOD|PT RND4, DECAL BLOOD) , // blood spats ( note : rgb i s inverted ) new t r a i l r e n d e r e r ( ” packages/ p a r t i c l e s /base . png ” , PT TRAIL|PT LERP ) , // water , e n t i t y new quadrenderer(”packages/ p a r t i c l e s /smoke . png ” , PT PART| PT FLIP |PT LERP ) , // smoke new quadrenderer(”packages/ p a r t i c l e s /steam . png ” , PT PART| PT FLIP ), // steam new quadrenderer(”packages/ p a r t i c l e s /flames . png ” , PT PART| PT HFLIP|PT RND4|PT GLARE ) , // flame on − no f l i p p i n g please , they have o r i e n t a t i o n new quadrenderer ( ” packages/ p a r t i c l e s /ball1 . png ” , PT PART|PT FEW| PT GLARE ) , // f i r e b a l l 1 new quadrenderer ( ” packages/ p a r t i c l e s /ball2 . png ” , PT PART|PT FEW| PT GLARE ) , // f i r e b a l l 2 new quadrenderer ( ” packages/ p a r t i c l e s /ball3 . png ” , PT PART|PT FEW| PT GLARE ) , // f i r e b a l l 3 new taperenderer ( ” packages/ p a r t i c l e s / f l a r e . jpg ” , PT TAPE|PT GLARE ) , // streak &lightnings , // l i g h t n i n g &f i r e b a l l s , // explosion f i r e b a l l &b l u e f i r e b a l l s , // bluish explosion f i r e b a l l new quadrenderer ( ” packages/ p a r t i c l e s /spark . png ” , PT PART| PT FLIP | PT GLARE ) , // sparks new quadrenderer ( ” packages/ p a r t i c l e s /base . png ” , PT PART| PT FLIP | PT GLARE ) , // e d i t mode e n t i t i e s new quadrenderer(”packages/ p a r t i c l e s /snow . png ” , PT PART| PT FLIP | PT RND4, −1) , // c o l l i d i n g snow new quadrenderer ( ” packages/ p a r t i c l e s /muzzleflash1 . jpg ” , PT PART|PT FEW | PT FLIP |PT GLARE|PT TRACK ) , // muzzle f l a s h new quadrenderer ( ” packages/ p a r t i c l e s /muzzleflash2 . jpg ” , PT PART|PT FEW | PT FLIP |PT GLARE|PT TRACK ) , // muzzle f l a s h new quadrenderer ( ” packages/ p a r t i c l e s /muzzleflash3 . jpg ” , PT PART|PT FEW | PT FLIP |PT GLARE|PT TRACK ) , // muzzle f l a s h new quadrenderer ( ” packages/hud/items . png ” , PT PART|PT FEW|PT ICON ) , // hud icon new quadrenderer(”< c o l o r i f y :1/1/1>packages/hud/items . png ” , PT PART| PT FEW|PT ICON ) , // grey hud icon &texts , // t e x t &meters , // meter &metervs , // meter vs . &f l a r e s // lens f l a r e s − must be done l a s t }; void finddepthfxranges ( ) { depthfxmin = vec (1 e16f , 1e16f , 1e16f ) ; depthfxmax = vec ( 0 , 0 , 0) ; numdepthfxranges = f i r e b a l l s . finddepthfxranges ( depthfxowners , depthfxranges , 0 , MAXDFXRANGES, depthfxmin , depthfxmax ) ; numdepthfxranges = b l u e f i r e b a l l s . finddepthfxranges ( depthfxowners , depthfxranges , numdepthfxranges , MAXDFXRANGES, depthfxmin , depthfxmax ) ; loopk ( 3 ) { depthfxmin [ k ] −= depthfxmargin ; depthfxmax [ k ] += depthfxmargin ; } i f ( depthfxparts ) { l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) { partrenderer ∗p = parts [ i ] ; i f ( p−>type&PT SOFT && p−>adddepthfx ( depthfxmin , depthfxmax ) ) { i f ( ! numdepthfxranges ) { numdepthfxranges = 1; depthfxowners [ 0 ] = NULL; depthfxranges [ 0 ] = 0; } } } } i f ( depthfxscissor0) depthfxtex . addscissorbox ( depthfxmin , depthfxmax ) ; } VARFP( maxparticles , 10, 4000, 40000, p a r t i c l e i n i t ( ) ) ; VARFP( f e w p a r t i c l e s , 10, 100, 40000, p a r t i c l e i n i t ( ) ) ;

407

void p a r t i c l e i n i t ( ) { i f ( ! particleshader ) particleshader = lookupshaderbyname ( ” p a r t i c l e ” ) ; i f ( ! particlenotextureshader ) particlenotextureshader = lookupshaderbyname ( ” p a r t i c l e n o t e x t u r e ” ) ; l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) parts [ i]−>i n i t ( parts [ i]−>type& PT FEW ? min ( f e w p a r t i c l e s , maxparticles ) : maxparticles ) ; } void c l e a r p a r t i c l e s ( ) { l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) parts [ i]−>r e s e t ( ) ; clearparticleemitters ( ) ; } void cleanupparticles ( ) { l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) parts [ i]−>cleanup ( ) ; } void removetrackedparticles ( physent ∗owner ) { l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) parts [ i]−>resettracked ( owner ) ; } VARP( p a r t i c l e g l a r e , 0 , 2 , 100) ; VAR( debugparticles , 0 , 0 , 1) ; void r e n d e r p a r t i c l e s ( bool mainpass ) { canstep = mainpass ; //want to debug BEFORE the lastpass render ( that would d e l e t e particles ) i f ( debugparticles && ! g l a r i n g && ! r e f l e c t i n g && ! r e f r a c t i n g ) { i n t n = s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ; glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glOrtho ( 0 , FONTH∗n∗2∗screen−>w/ f l o a t ( screen−>h ) , FONTH∗n∗2, 0 , −1, 1) ; //squeeze i n t o top−l e f t corner glMatrixMode (GL MODELVIEW) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glDisable ( GL DEPTH TEST ) ; glEnable (GL BLEND) ; defaultshader−>set ( ) ; loopi (n) { i n t type = parts [ i]−>type ; const char ∗ t i t l e = parts [ i]−>texname ? s t r r c h r ( parts [ i]−> texname , ’ / ’ ) +1 : NULL; string info = ” ” ; i f ( type&PT GLARE ) concatstring ( info , ” g , ” ) ; i f ( type&PT LERP ) concatstring ( info , ” l , ” ) ; i f ( type&PT MOD) concatstring ( info , ”m, ” ) ; i f ( type&PT RND4 ) concatstring ( info , ” r , ” ) ; i f ( type&PT TRACK ) concatstring ( info , ” t , ” ) ; i f ( type&PT FLIP ) concatstring ( info , ” f , ” ) ; i f ( parts [ i]−>c o l l i d e ) concatstring ( info , ” c , ” ) ; i f ( i n f o [ 0 ] ) i n f o [ s t r l e n ( i n f o )−1] = ’ \ 0 ’ ; defformatstring ( ds ) (”%d\t%s(%s ) %s ” , parts [ i]−>count ( ) , partnames [ type&0xFF ] , info , t i t l e ? t i t l e : ” ” ) ; draw text ( ds , FONTH, ( i +n/2)∗FONTH) ; } glDisable (GL BLEND) ; glEnable ( GL DEPTH TEST ) ; glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ; } i f ( g l a r i n g && ! p a r t i c l e g l a r e ) return ; l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) { i f ( g l a r i n g && ! ( parts [ i]−>type&PT GLARE ) ) continue ; parts [ i]−>update ( ) ; } s t a t i c f l o a t zerofog [ 4 ] = { 0 , 0 , 0 , 1 }; f l o a t oldfogc [ 4 ] ; bool rendered = f a l s e ; uint l a s t f l a g s = PT LERP , flagmask = PT LERP|PT MOD; i f ( binddepthfxtex ( ) ) flagmask |= PT SOFT ; l o o p i ( s i z e o f ( parts ) / s i z e o f ( parts [ 0 ] ) ) { partrenderer ∗p = parts [ i ] ; i f ( g l a r i n g && ! ( p−>type&PT GLARE ) ) continue ;

408

Foundations of Videogame Programming Code Repository i f ( ! p−>haswork ( ) ) continue ;

i f ( seedemitter ) { parts [ type]−>seedemitter (∗ seedemitter , o , d , fade , size , g r a v i t y ) ; return &dummy; } i f ( fade + e m i t o f f s e t < 0) return &dummy; addedparticles ++; return parts [ type]−>addpart ( o , d , fade , color , size , g r a v i t y ) ;

i f ( ! rendered ) { rendered = true ; glDepthMask ( GL FALSE ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } i f ( g l a r i n g ) setenvparamf ( ” c o l o r s c a l e ” , SHPARAM VERTEX, 4 , p a r t i c l e g l a r e , p a r t i c l e g l a r e , p a r t i c l e g l a r e , 1) ; e l s e setenvparamf ( ” c o l o r s c a l e ” , SHPARAM VERTEX, 4 , 1 , 1 , 1 , 1) ; particleshader−>set ( ) ; glGetFloatv (GL FOG COLOR, oldfogc ) ;

VARP( maxparticledistance , 256, 1024, 4096) ; s t a t i c void splash ( i n t type , i n t color , i n t radius , i n t num, i n t fade , const vec &p , f l o a t size , i n t g r a v i t y ) { i f ( camera1−>o . d i s t ( p ) > maxparticledistance && ! seedemitter ) return ; f l o a t c o l l i d e z = parts [ type]−>c o l l i d e ? p . z − raycube ( p , vec ( 0 , 0 , −1) , COLLIDERADIUS, RAY CLIPMAT ) + ( parts [ type]−>c o l l i d e >= 0 ? COLLIDEERROR : 0) : −1; i n t fmin = 1; i n t fmax = fade ∗3; l o o p i (num) { int x , y , z ; do { x = rnd ( radius ∗2)−radius ; y = rnd ( radius ∗2)−radius ; z = rnd ( radius ∗2)−radius ; } while ( x∗x+y∗y+z∗z>radius∗radius ) ; vec tmp = vec ( ( f l o a t ) x , ( f l o a t ) y , ( f l o a t ) z ) ; i n t f = (num < 10) ? ( fmin + rnd ( fmax ) ) : ( fmax − ( i ∗(fmax−fmin ) ) / ( num−1)) ; //help d e a l l o c a t e r by using fade d i s t r i b u t i o n rather than random newparticle ( p , tmp, f , type , color , size , g r a v i t y )−>v a l = c o l l i d e z ; }

} uint f l a g s = p−>type & flagmask ; i f ( p−>usesvertexarray ( ) ) f l a g s |= 0x01 ; //0x01 = VA marker uint changedbits = ( f l a g s ˆ l a s t f l a g s ) ; i f ( changedbits ! = 0x0000 ) { i f ( changedbits&0x01 ) { i f ( f l a g s&0x01 ) { glEnableClientState ( GL VERTEX ARRAY ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; glEnableClientState (GL COLOR ARRAY) ; } else { g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL COLOR ARRAY) ; } } i f ( changedbits&PT LERP ) glFogfv (GL FOG COLOR, ( f l a g s&PT LERP ) ? oldfogc : zerofog ) ; i f ( changedbits&(PT LERP|PT MOD) ) { i f ( f l a g s&PT LERP ) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; e l s e i f ( f l a g s&PT MOD) glBlendFunc ( GL ZERO, GL ONE MINUS SRC COLOR ) ; e l s e glBlendFunc ( GL SRC ALPHA, GL ONE) ; } i f ( changedbits&PT SOFT ) { i f ( f l a g s&PT SOFT ) { i f ( depthfxtex . t a r g e t ==GL TEXTURE RECTANGLE ARB) { i f ( ! depthfxtex . highprecision ( ) ) SETSHADER( particlesoft8rect ) ; e l s e SETSHADER( p a r t i c l e s o f t r e c t ) ; } else { i f ( ! depthfxtex . highprecision ( ) ) SETSHADER( p a r t i c l e s o f t 8 ); e l s e SETSHADER( p a r t i c l e s o f t ) ; } binddepthfxparams ( depthfxpartblend ) ; } e l s e particleshader−>set ( ) ; } lastflags = flags ; } p−>render ( ) ;

} s t a t i c void regularsplash ( i n t type , i n t color , i n t radius , i n t num, i n t fade , const vec &p , f l o a t size , i n t g r a v i t y , i n t delay = 0) { i f ( ! e m i t p a r t i c l e s ( ) || ( delay > 0 && rnd ( delay ) ! = 0) ) return ; splash ( type , color , radius , num, fade , p , size , g r a v i t y ) ; } bool canaddparticles ( ) { return ! renderedgame && ! shadowmapping ; } void r e g u l a r p a r t i c l e s p l a s h ( i n t type , i n t num, i n t fade , const vec &p , i n t color , f l o a t size , i n t radius , i n t g r a v i t y , i n t delay ) { i f ( ! canaddparticles ( ) ) return ; regularsplash ( type , color , radius , num, fade , p , size , g r a v i t y , delay ) ; } void p a r t i c l e s p l a s h ( i n t type , i n t num, i n t fade , const vec &p , i n t color , f l o a t size , i n t radius , i n t g r a v i t y ) { i f ( ! canaddparticles ( ) ) return ; splash ( type , color , radius , num, fade , p , size , g r a v i t y ) ; } VARP( maxtrail , 1 , 500, 10000) ; void p a r t i c l e t r a i l ( i n t type , i n t fade , const vec &s , const vec &e , i n t color , f l o a t size , i n t g r a v i t y ) {

} i f ( rendered ) { i f ( l a s t f l a g s &(PT LERP|PT MOD) ) glBlendFunc ( GL SRC ALPHA, GL ONE) ; i f ( ! ( l a s t f l a g s&PT LERP ) ) glFogfv (GL FOG COLOR, oldfogc ) ; i f ( l a s t f l a g s&0x01 ) { g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL COLOR ARRAY) ; } glDisable (GL BLEND) ; glDepthMask ( GL TRUE ) ; } }

i f ( ! canaddparticles ( ) ) return ; vec v ; float d = e . dist ( s , v ) ; i n t steps = clamp ( i n t ( d∗2) , 1 , maxtrail ) ; v . div ( steps ) ; vec p = s ; l o o p i ( steps ) { p . add ( v ) ; vec tmp = vec ( f l o a t ( rnd ( 1 1 )−5) , f l o a t ( rnd ( 1 1 )−5) , f l o a t ( rnd ( 1 1 )−5) ) ; newparticle ( p , tmp, rnd ( fade ) +fade , type , color , size , g r a v i t y ) ; } } VARP( p a r t i c l e t e x t , 0 , 1 , 1) ; VARP( maxparticletextdistance , 0 , 128, 10000) ;

s t a t i c i n t addedparticles = 0; s t a t i c i n l i n e p a r t i c l e ∗newparticle ( const vec &o , const vec &d , i n t fade , i n t type , i n t color , f l o a t size , i n t g r a v i t y = 0) { s t a t i c p a r t i c l e dummy;

void p a r t i c l e t e x t ( const vec &s , const char ∗t , i n t type , i n t fade , i n t color , f l o a t size , i n t g r a v i t y ) { i f ( ! canaddparticles ( ) ) return ; i f ( ! p a r t i c l e t e x t || camera1−>o . d i s t ( s ) > maxparticletextdistance )

engine/renderparticles.cpp {

return ; p a r t i c l e ∗p = newparticle ( s , vec ( 0 , 0 , 1) , fade , type , color , size , gravity ) ; p−>t e x t = t ;

const vec2 &sc = sincos360 [ rnd (360) ] ; to [ d i r %3] = sc . y∗radius ; to [ ( d i r +1)%3] = sc . x∗radius ; to [ ( d i r +2)%3] = 0 . 0 ; to . add ( p ) ; i f ( d i r < 3) // c i r c l e from = p ; e l s e i f ( d i r < 6) // c y l i n d e r { from = to ; to [ ( d i r +2)%3] += radius ; from [ ( d i r +2)%3] −= radius ; } e l s e //cone { from = p ; to [ ( d i r +2)%3] += ( d i r < 9) ?radius:(−radius ) ; }

} void p a r t i c l e t e x t c o p y ( const vec &s , const char ∗t , i n t type , i n t fade , i n t color , f l o a t size , i n t g r a v i t y ) { i f ( ! canaddparticles ( ) ) return ; i f ( ! p a r t i c l e t e x t || camera1−>o . d i s t ( s ) > maxparticletextdistance ) return ; p a r t i c l e ∗p = newparticle ( s , vec ( 0 , 0 , 1) , fade , type , color , size , gravity ) ; p−>t e x t = newstring ( t ) ; p−>f l a g s = 1; } void p a r t i c l e i c o n ( const vec &s , i n t ix , i n t iy , i n t type , i n t fade , i n t color , f l o a t size , i n t g r a v i t y )

} e l s e i f ( d i r < 15) //plane { to [ d i r %3] = f l o a t ( rnd ( radius>16; p−>color2 [ 1 ] = ( color2>>8)&0xFF ; p−>color2 [ 2 ] = color2&0xFF ; p−>progress = clamp ( i n t ( e . a t t r 2 ) , 0 , 100) ; break ; } case 11: // flame − radius=100, height=100 i s the c l a s s i c s i z e regularflame ( PART FLAME, e . o , f l o a t ( e . a t t r 2 ) /100.0 f , f l o a t ( e . a t t r 3 ) /100.0 f , colorfromattr ( e . a t t r 4 ) , 3 , 2.0 f ) ; break ; case 12: // smoke plume regularflame (PART SMOKE, e . o , f l o a t ( e . a t t r 2 ) /100.0 f , f l o a t ( e . a t t r 3 ) /100.0 f , colorfromattr ( e . a t t r 4 ) , 1 , 4.0 f , 100.0 f , 2000.0 f , −20) ; break ; case 32: //lens f l a r e s − plain/sparkle/sun/sparklesun

case 33: case 34: case 35: f l a r e s . addflare ( e . o , e . attr2 , e . attr3 , e . attr4 , ( e . a t t r 1&0x02 ) !=0 , ( e . a t t r 1&0x01 ) ! = 0 ) ; break ; default : i f ( ! editmode ) { defformatstring ( ds ) ( ” p a r t i c l e s %d? ” , e . a t t r 1 ) ; p a r t i c l e t e x t c o p y ( e . o , ds , PART TEXT, 1 , 0x6496FF , 2.0 f ) ; } break ;

i f ( ! e m i t p a r t i c l e s ( ) ) return ; f l o a t s i z e = scale ∗ min ( radius , height ) ; vec v ( 0 , 0 , min( 1 . 0 f , height )∗speed ) ; l o o p i ( density ) { vec s = p ; s . x += rndscale ( radius∗2.0 f )−radius ; s . y += rndscale ( radius∗2.0 f )−radius ; newparticle ( s , v , rnd (max( i n t ( fade∗height ) , 1) ) +1 , type , color , size , g r a v i t y ) ; } } void r e g u l a r p a r t i c l e f l a m e ( i n t type , const vec &p , f l o a t radius , f l o a t height , i n t color , i n t density , f l o a t scale , f l o a t speed , f l o a t fade , i n t g r a v i t y ) { i f ( ! canaddparticles ( ) ) return ; regularflame ( type , p , radius , height , color , density , scale , speed , fade , g r a v i t y ) ; } s t a t i c void makeparticles ( e n t i t y &e ) { switch ( e . a t t r 1 ) { case 0: // f i r e and smoke − − 0 values d e f a u l t to compat f o r old maps { //regularsplash ( PART FIREBALL1 , 0xFFC8C8, 150, 1 , 40, e . o , 4.8 f ) ; //regularsplash (PART SMOKE, 0x897661 , 50, 1 , 200, vec ( e . o . x , e . o . y , e . o . z +3.0 f ) , 2.4 f , −20, 3) ; f l o a t radius = e . a t t r 2 ? f l o a t ( e . a t t r 2 ) /100.0 f : 1.5 f , height = e . a t t r 3 ? f l o a t ( e . a t t r 3 ) /100.0 f : radius /3; regularflame ( PART FLAME, e . o , radius , height , e . a t t r 4 ? colorfromattr ( e . a t t r 4 ) : 0x903020 , 3 , 2.0 f ) ; regularflame (PART SMOKE, vec ( e . o . x , e . o . y , e . o . z + 4.0 f∗min ( radius , height ) ) , radius , height , 0x303020 , 1 , 4.0 f , 100.0 f , 2000.0 f , −20) ; break ; } case 1: //steam vent − regularsplash ( PART STEAM, 0x897661 , 50, 1 , 200, o f f s e t v e c ( e . o , e . attr2 , rnd ( 1 0 ) ) , 2.4 f , −20) ; break ; case 2: //water fountain − { int color ; i f ( e . a t t r 3 > 0) c o l o r = colorfromattr ( e . a t t r 3 ) ; else { i n t mat = MAT WATER + clamp(−e . attr3 , 0 , 3) ; const bvec &wfcol = g e t w a t e r f a l l c o l o r ( mat ) ; c o l o r = ( i n t ( wfcol [ 0 ] ) 5 && pe . l a s t c u l l > pe . lastemit ) { f o r ( e m i t o f f s e t = max( pe . lastemit + e m i t m i l l i s − l a s t m i l l i s , − pe . maxfade ) ; e m i t o f f s e t < 0; e m i t o f f s e t += e m i t m i l l i s ) { makeparticles ( e ) ; replayed ++;

411

} i f ( dbgpcull && ( canemit || replayed ) && addedparticles ) conoutf ( CON DEBUG, ”%d emitters , %d p a r t i c l e s ” , emitted , addedparticles ) ; } i f ( editmode ) // show sparkly t h i n g i e s f o r map e n t i t i e s in e d i t mode { const vector &ents = e n t i t i e s : : getents ( ) ; // note : order matters in t h i s case as p a r t i c l e s o f the same type are drawn in the reverse order that they are added loopv ( entgroup ) { e n t i t y &e = ∗ents [ entgroup [ i ] ] ; p a r t i c l e t e x t c o p y ( e . o , entname ( e ) , PART TEXT, 1 , 0xFF4B19, 2.0 f ) ; } loopv ( ents ) { e n t i t y &e = ∗ents [ i ] ; i f ( e . type==ET EMPTY ) continue ; p a r t i c l e t e x t c o p y ( e . o , entname ( e ) , PART TEXT, 1 , 0x1EC850, 2.0 f ) ; r e g u l a r p a r t i c l e s p l a s h ( PART EDIT , 2 , 40, e . o , 0x3232FF , 0.32 f∗ p a r t i c l e s i z e /100.0 f ) ; } } }

engine/rendersky.cpp #include ” engine . h”

return t ; }

Texture ∗sky [ 6 ] = { 0 , 0 , 0 , 0 , 0 , 0 }, ∗clouds [ 6 ] = { 0 , 0 , 0 , 0 , 0 , 0 }; void loadsky ( const char ∗basename , Texture ∗texs [ 6 ] ) { const char ∗wildcard = strchr ( basename , ’ ∗ ’ ) ; loopi ( 6 ) { const char ∗side = cubemapsides [ i ] . name; s t r i n g name; copystring ( name, makerelpath ( ” packages ” , basename ) ) ; i f ( wildcard ) { char ∗chop = strchr ( name, ’ ∗ ’ ) ; i f ( chop ) { ∗chop = ’ \ 0 ’ ; concatstring ( name, side ) ; concatstring ( name, wildcard +1) ; } texs [ i ] = textureload ( name, 3 , true , f a l s e ) ; } else { defformatstring ( ext ) ( ” %s . jpg ” , side ) ; concatstring ( name, ext ) ; i f ( ( texs [ i ] = textureload ( name, 3 , true , f a l s e ) ) ==notexture ) { strcpy (name+ s t r l e n (name)−3, ”png ” ) ; texs [ i ] = textureload ( name, 3 , true , f a l s e ) ; } } i f ( texs [ i ]== notexture ) conoutf (CON ERROR, ” could not load side %s o f sky texture %s ” , side , basename ) ; } } Texture ∗cloudoverlay = NULL; Texture ∗loadskyoverlay ( const char ∗basename ) { const char ∗ext = s t r rc h r ( basename , ’ . ’ ) ; s t r i n g name; copystring ( name, makerelpath ( ” packages ” , basename ) ) ; Texture ∗t = notexture ; i f ( ext ) t = textureload ( name, 0 , true , f a l s e ) ; else { concatstring ( name, ” . jpg ” ) ; i f ( ( t = textureload ( name, 0 , true , f a l s e ) ) == notexture ) { strcpy (name+ s t r l e n (name)−3, ”png ” ) ; t = textureload ( name, 0 , true , f a l s e ) ; } } i f ( t ==notexture ) conoutf (CON ERROR, ” could not load sky overlay texture %s ” , basename ) ;

SVARFR( skybox , ” ” , { i f ( skybox [ 0 ] ) loadsky ( skybox , sky ) ; }) ; HVARR( skyboxcolour , 0 , 0xFFFFFF, 0xFFFFFF ) ; FVARR( spinsky , −720, 0 , 720) ; VARR( yawsky , 0 , 0 , 360) ; SVARFR( cloudbox , ” ” , { i f ( cloudbox [ 0 ] ) loadsky ( cloudbox , clouds ) ; }) ; HVARR( cloudboxcolour , 0 , 0xFFFFFF, 0xFFFFFF ) ; FVARR( cloudboxalpha , 0 , 1 , 1) ; FVARR( spinclouds , −720, 0 , 720) ; VARR( yawclouds , 0 , 0 , 360) ; FVARR( cloudclip , 0 , 0.5 f , 1) ; SVARFR( cloudlayer , ” ” , { i f ( cloudlayer [ 0 ] ) cloudoverlay = loadskyoverlay ( cloudlayer ) ; }) ; FVARR( cloudoffsetx , 0 , 0 , 1) ; FVARR( cloudoffsety , 0 , 0 , 1) ; FVARR( cloudscrollx , −16, 0 , 16) ; FVARR( cloudscrolly , −16, 0 , 16) ; FVARR( cloudscale , 0.001 , 1 , 64) ; FVARR( spincloudlayer , −720, 0 , 720) ; VARR( yawcloudlayer , 0 , 0 , 360) ; FVARR( cloudheight , −1, 0.2 f , 1) ; FVARR( cloudfade , 0 , 0.2 f , 1) ; FVARR( cloudalpha , 0 , 1 , 1) ; VARR( cloudsubdiv , 4 , 16, 64) ; HVARR( cloudcolour , 0 , 0xFFFFFF, 0xFFFFFF ) ; void draw envbox face ( f l o a t s0 , f l o a t t0 , i n t x0 , i n t y0 , i n t z0 , f l o a t s1 , f l o a t t1 , i n t x1 , i n t y1 , i n t z1 , f l o a t s2 , f l o a t t2 , i n t x2 , i n t y2 , i n t z2 , f l o a t s3 , f l o a t t3 , i n t x3 , i n t y3 , i n t z3 , GLuint texture ) { glBindTexture ( GL TEXTURE 2D, texture ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( s3 , t3 ) ; g l V e r t e x 3 f ( x3 , y3 , z3 ) ; glTexCoord2f ( s2 , t2 ) ; g l V e r t e x 3 f ( x2 , y2 , z2 ) ; glTexCoord2f ( s0 , t0 ) ; g l V e r t e x 3 f ( x0 , y0 , z0 ) ; glTexCoord2f ( s1 , t1 ) ; g l V e r t e x 3 f ( x1 , y1 , z1 ) ; glEnd ( ) ; x t r a v e r t s += 4; } void draw envbox ( i n t w, f l o a t z 1 c l i p = 0.0 f , f l o a t z 2 c l i p = 1.0 f , i n t faces = 0x3F , Texture ∗∗sky = NULL) { i f ( z 1 c l i p >= z 2 c l i p ) return ; f l o a t v1 = 1−z1clip , v2 = 1−z 2 c l i p ; i n t z1 = i n t ( c e i l (2∗w∗( z1clip −0.5f ) ) ) , z2 = i n t ( c e i l (2∗w∗( z2clip −0.5f ) )); i f ( faces&0x01 ) draw envbox face ( 0 . 0 f , v2 ,

−w, −w, z2 ,

412

Foundations of Videogame Programming Code Repository 1.0 f , v2 , −w, w, z2 , 1.0 f , v1 , −w, w, z1 , 0.0 f , v1 , −w, −w, z1 , sky [ 0 ] ? sky[0]−>id : notexture −>id ) ;

i f ( faces&0x02 ) draw envbox face ( 1 . 0 f , v1 , w, 0.0 f , v1 , w, w, 0.0 f , v2 , w, w, 1.0 f , v2 , w, −w, id ) ; i f ( faces&0x04 ) draw envbox face ( 1 . 0 f , v1 , 0.0 f , v1 , w, 0.0 f , v2 , w, 1.0 f , v2 , −w, −>id ) ; i f ( faces&0x08 ) draw envbox face ( 1 . 0 f , v1 , 0.0 f , v1 , −w, 0.0 f , v2 , −w, 1.0 f , v2 , w, −>id ) ;

−w, z1 , z1 , z2 , z2 , sky [ 1 ] ? sky[1]−>id : notexture−>

−w, −w, −w, −w,

−w, z1 , z1 , z2 , z2 , sky [ 2 ] ? sky[2]−>id : notexture

w, w, z1 , w, z1 , w, z2 , w, z2 , sky [ 3 ] ? sky[3]−>id : notexture

i f ( z 1 c l i p id : notexture−>id ) ; i f ( z 2 c l i p >= 1 && faces&0x20 ) draw envbox face ( 0 . 0 f , 1.0 f , w, w, w, 0.0 f , 0.0 f , −w, w, w, 1.0 f , 0.0 f , −w, −w, w, 1.0 f , 1.0 f , w, −w, w, sky [ 5 ] ? sky[5]−>id : notexture −>id ) ; } void draw env overlay ( i n t w, Texture ∗overlay = NULL, f l o a t tx = 0 , f l o a t ty = 0) { f l o a t z = w∗cloudheight , t s z = 0.5 f∗(1−cloudfade ) /cloudscale , psz = w ∗(1−cloudfade ) ; glBindTexture ( GL TEXTURE 2D, overlay ? overlay−>id : notexture−>id ) ; f l o a t r = ( cloudcolour>>16)/255.0 f , g = ( ( cloudcolour>>8)&255)/255.0 f , b = ( cloudcolour&255)/255.0 f ; g l C o l o r 4 f ( r , g , b , cloudalpha ) ; glBegin ( GL TRIANGLE FAN ) ; l o o p i ( cloudsubdiv +1) { vec p ( 1 , 1 , 0) ; p . rotate around z ((−2.0 f∗M PI∗i ) /cloudsubdiv ) ; glTexCoord2f ( tx + p . x∗tsz , ty + p . y∗t s z ) ; g l V e r t e x 3 f ( p . x∗psz , p . y∗ psz , z ) ; } glEnd ( ) ; f l o a t tsz2 = 0.5 f /cloudscale ; glBegin ( GL TRIANGLE STRIP ) ; l o o p i ( cloudsubdiv +1) { vec p ( 1 , 1 , 0) ; p . rotate around z ((−2.0 f∗M PI∗i ) /cloudsubdiv ) ; g l C o l o r 4 f ( r , g , b , cloudalpha ) ; glTexCoord2f ( tx + p . x∗tsz , ty + p . y∗t s z ) ; g l V e r t e x 3 f ( p . x∗psz , p . y∗ psz , z ) ; g l C o l o r 4 f ( r , g , b , 0) ; glTexCoord2f ( tx + p . x∗tsz2 , ty + p . y∗tsz2 ) ; g l V e r t e x 3 f ( p . x∗w, p . y∗w , z) ; } glEnd ( ) ; } s t a t i c struct domevert { vec pos ; uchar c o l o r [ 4 ] ; domevert ( ) {} domevert ( const vec &pos , const bvec &f c o l o r , f l o a t alpha ) : pos ( pos ) { memcpy( color , f c o l o r . v , 3) ; c o l o r [ 3 ] = uchar ( alpha∗255) ; } domevert ( const domevert &v0 , const domevert &v1 ) : pos ( vec ( v0 . pos ) . add ( v1 . pos ) . normalize ( ) ) { memcpy( color , v0 . color , 4) ; i f ( v0 . pos . z ! = v1 . pos . z ) c o l o r [ 3 ] += uchar ( ( v1 . c o l o r [ 3 ] − v0 . c o l o r [ 3 ] ) ∗ ( pos . z − v0 . pos . z ) / ( v1 . pos . z − v0 . pos . z ) ) ; }

} ∗domeverts = NULL; s t a t i c GLushort ∗domeindices = NULL; s t a t i c i n t domenumverts = 0 , domenumindices = 0 , domecapindices = 0; s t a t i c GLuint domevbuf = 0 , domeebuf = 0; s t a t i c bvec domecolor ( 0 , 0 , 0) ; s t a t i c f l o a t domeminalpha = 0 , domemaxalpha = 0 , domecapsize = −1, domeclipz = 1; s t a t i c void subdivide ( i n t depth , i n t face ) ; s t a t i c void genface ( i n t depth , i n t i1 , i n t i2 , i n t i3 ) { i n t face = domenumindices ; domenumindices += 3; domeindices [ face ] = i3 ; domeindices [ face +1] = i2 ; domeindices [ face +2] = i1 ; subdivide ( depth , face ) ; } s t a t i c void subdivide ( i n t depth , i n t face ) { i f ( depth−− = 0 || xv . x < yv . x : yv . y >= 0 && xv . x > yv . x ; } s t a t i c void initdome ( const bvec &color , f l o a t minalpha = 0.0 f , f l o a t maxalpha = 1.0 f , f l o a t capsize = −1, f l o a t c l i p z = 1 , i n t hres = 16, i n t depth = 2) { const i n t t r i s = hres = 0 ? 1 : 0) ] ; domeindices = new GLushort [ ( t r i s + ( capsize >= 0 ? hres>8)&0xFF , fogdomecolour&0xFF ) ; }) ;

i f ( ! g l a r i n g ) defaultshader−>set ( ) ; }

s t a t i c void drawdome ( ) { f l o a t capsize = fogdomecap && fogdomeheight < 1 ? (1 + fogdomeheight ) / (1 − fogdomeheight ) : −1; bvec c o l o r = fogdomecolour ? fogdomecolor : f o g c o l o r ; i f ( ! domenumverts || domecolor ! = c o l o r || domeminalpha ! = fogdomemin || domemaxalpha ! = fogdomemax || domecapsize ! = capsize || domeclipz ! = fogdomeclip ) { initdome ( color , min ( fogdomemin , fogdomemax ) , fogdomemax , capsize , fogdomeclip ) ; domecolor = c o l o r ; domeminalpha = fogdomemin ; domemaxalpha = fogdomemax ; domecapsize = capsize ; domeclipz = fogdomeclip ; }

VAR( clampsky , 0 , 1 , 1) ; VARR( fogdomeclouds , 0 , 1 , 1) ; s t a t i c void drawfogdome ( i n t farplane ) { notextureshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glPushMatrix ( ) ; glLoadMatrixf ( viewmatrix . v ) ; g l R o t a t e f ( camera1−>r o l l , 0 , 1 , 0) ; g l R o t a t e f ( camera1−>pitch , −1, 0 , 0) ; g l R o t a t e f ( camera1−>yaw , 0 , 0 , −1) ; i f ( r e f l e c t i n g ) g l S c a l e f ( 1 , 1 , −1) ; g l T r a n s l a t e f ( 0 , 0 , farplane∗fogdomeheight∗0.5 f ) ; g l S c a l e f ( farplane /2 , farplane /2 , farplane ∗(0.5 f − fogdomeheight∗0.5 f ) ) ; drawdome ( ) ; glPopMatrix ( ) ;

i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, domevbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, domeebuf ) ; } glEnableClientState ( GL VERTEX ARRAY ) ; glEnableClientState (GL COLOR ARRAY) ; glVertexPointer ( 3 , GL FLOAT, s i z e o f ( domevert ) , &domeverts−>pos ) ; glColorPointer ( 4 , GL UNSIGNED BYTE, s i z e o f ( domevert ) , &domeverts−> color ) ; i f (hasDRE) glDrawRangeElements ( GL TRIANGLES, 0 , domenumverts−1, domenumindices + fogdomecap∗domecapindices , GL UNSIGNED SHORT, domeindices ) ; e l s e glDrawElements ( GL TRIANGLES, domenumindices + fogdomecap∗ domecapindices , GL UNSIGNED SHORT, domeindices ) ; x t r a v e r t s += domenumverts ; glde ++; g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; g l D i s a b l e C l i e n t S t a t e (GL COLOR ARRAY) ; i f (hasVBO) {

glDisable (GL BLEND) ; glEnable ( GL TEXTURE 2D ) ; } s t a t i c i n t yawskyfaces ( i n t faces , i n t yaw , f l o a t spin = 0) { i f ( spin || yaw%90) return faces&0x0F ? faces | 0x0F : faces ; s t a t i c const i n t faceidxs [ 3 ] [ 4 ] = { { 3 , 2 , 0 , 1 }, { 1 , 0 , 3 , 2 }, { 2, 3, 1, 0 } }; yaw /= 90; i f ( yaw < 1 || yaw > 3) return faces ; const i n t ∗idxs = faceidxs [ yaw − 1 ] ; return ( faces & ˜0x0F ) | ( ( ( faces>>idxs [ 0 ] ) &1)idxs [ 1 ] ) &1)idxs [ 2 ] ) &1)idxs [ 3 ] ) &1)

414

Foundations of Videogame Programming Code Repository next ) { i f ( va−>occluded >= OCCLUDE BB && va−>skyfaces&0x80 ) continue ; renderedskyfaces |= va−>skyfaces&0x3F ; i f ( ! ( va−>skyfaces&0x1F ) || camera1−>o . z < va−>skyclip ) renderedskyclip = min ( renderedskyclip , va−>skyclip ) ; e l s e renderedskyclip = 0; } i f ( ! renderedskyfaces ) return ; }

glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; g l C o l o r 4 f ( ( cloudboxcolour>>16)/255.0 f , ( ( cloudboxcolour>>8)&255) /255.0 f , ( cloudboxcolour&255)/255.0 f , cloudboxalpha ) ; glPushMatrix ( ) ; glLoadMatrixf ( viewmatrix . v ) ; g l R o t a t e f ( camera1−>r o l l , 0 , 1 , 0) ; g l R o t a t e f ( camera1−>pitch , −1, 0 , 0) ; g l R o t a t e f ( camera1−>yaw+spinclouds∗l a s t m i l l i s /1000.0 f +yawclouds , 0 , 0 , −1) ; i f ( r e f l e c t i n g ) g l S c a l e f ( 1 , 1 , −1) ; draw envbox ( farplane /2 , skyclip ? skyclip : cloudclip , t o p c l i p , yawskyfaces ( renderedskyfaces , yawclouds , spinclouds ) , clouds ); glPopMatrix ( ) ; glDisable (GL BLEND) ; } i f ( ! g l a r i n g && cloudlayer [ 0 ] && cloudheight && renderedskyfaces &( cloudheight < 0 ? 0x1F : 0x2F ) ) { i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ; glDisable ( GL CULL FACE ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ;

i f ( alwaysrender ) { renderedskyfaces = 0x3F ; renderedskyclip = 0; }

glPushMatrix ( ) ; glLoadMatrixf ( viewmatrix . v ) ; g l R o t a t e f ( camera1−>r o l l , 0 , 1 , 0) ; g l R o t a t e f ( camera1−>pitch , −1, 0 , 0) ; g l R o t a t e f ( camera1−>yaw+spincloudlayer∗l a s t m i l l i s /1000.0 f + yawcloudlayer , 0 , 0 , −1) ; i f ( r e f l e c t i n g ) g l S c a l e f ( 1 , 1 , −1) ; draw env overlay ( farplane /2 , cloudoverlay , c l o u d o f f s e t x + c l o u d s c r o l l x ∗ l a s t m i l l i s /1000.0 f , c l o u d o f f s e t y + c l o u d s c r o l l y ∗ l a s t m i l l i s /1000.0 f ) ; glPopMatrix ( ) ;

f l o a t skyclip = clipsky ? max( renderedskyclip −1, 0) : 0 , t o p c l i p = 1; i f ( r e f l e c t zskyclip ) skyclip = r e f l e c t z ; } i f ( skyclip ) skyclip = 0.5 f + 0.5 f ∗( skyclip−camera1−>o . z ) / f l o a t ( worldsize ) ;

glDisable (GL BLEND) ;

i f ( g l a r i n g ) SETSHADER( skyboxglare ) ; e l s e defaultshader−>set ( ) ;

glEnable ( GL CULL FACE ) ; }

glDisable (GL FOG) ;

i f ( ! g l a r i n g && fogdomemax && fogdomeclouds ) { i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ; drawfogdome ( farplane ) ; }

i f ( limited ) { i f ( e x p l i c i t o n l y ) glDisable ( GL DEPTH TEST ) ; e l s e glDepthFunc (GL GEQUAL) ; } e l s e glDepthFunc (GL LEQUAL) ;

i f ( clampsky ) glDepthRange ( 0 , 1) ; glDepthMask ( GL TRUE ) ;

glDepthMask ( GL FALSE ) ; i f ( limited ) { i f ( e x p l i c i t o n l y ) glEnable ( GL DEPTH TEST ) ; e l s e glDepthFunc ( GL LESS ) ; i f ( ! r e f l e c t i n g && ! r e f r a c t i n g && ! envmapping && editmode && showsky ) drawskyoutline ( ) ; } e l s e glDepthFunc ( GL LESS ) ;

i f ( clampsky ) glDepthRange ( 1 , 1) ; g l C o l o r 3 f ( ( skyboxcolour>>16)/255.0 f , ( ( skyboxcolour>>8)&255)/255.0 f , ( skyboxcolour&255)/255.0 f ) ; glPushMatrix ( ) ; glLoadMatrixf ( viewmatrix . v ) ; g l R o t a t e f ( camera1−>r o l l , 0 , 1 , 0) ; g l R o t a t e f ( camera1−>pitch , −1, 0 , 0) ; g l R o t a t e f ( camera1−>yaw+spinsky∗l a s t m i l l i s /1000.0 f +yawsky , 0 , 0 , −1) ; i f ( r e f l e c t i n g ) g l S c a l e f ( 1 , 1 , −1) ; draw envbox ( farplane /2 , skyclip , t o p c l i p , yawskyfaces ( renderedskyfaces , yawsky , spinsky ) , sky ) ; glPopMatrix ( ) ; i f ( ! g l a r i n g && fogdomemax && ! fogdomeclouds ) { i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ; drawfogdome ( farplane ) ; defaultshader−>set ( ) ; }

engine/rendertarget.h

glEnable (GL FOG) ; } VARNR( skytexture , useskytexture , 0 , 1 , 1) ; i n t e x p l i c i t s k y = 0; double skyarea = 0; bool l i m i t s k y ( ) { return ( e x p l i c i t s k y && ( useskytexture || editmode ) ) || ( s p a r k l y f i x && skyarea / ( double ( worldsize )∗double ( worldsize ) ∗6) < 0 . 9 ) ; }

engine/rendertarget.h

415

extern i n t rtsharefb , r t s c i s s o r , b l u r t i l e ; struct rendertarget { i n t texw , texh , vieww , viewh ; GLenum colorfmt , depthfmt , t a r g e t ; GLuint rendertex , renderfb , renderdb , blurtex , blurfb , blurdb ; int blursize ; f l o a t blursigma ; f l o a t blurweights [MAXBLURRADIUS+1] , b l u r o f f s e t s [MAXBLURRADIUS+ 1 ] ;

void setup ( i n t w, i n t h ) { i f ( hasFBO ) { i f ( ! renderfb ) glGenFramebuffers ( 1 , &renderfb ) ; glBindFramebuffer (GL FRAMEBUFFER EXT, renderfb ) ; } i f ( ! rendertex ) glGenTextures ( 1 , &rendertex ) ; t a r g e t = t e x r e c t ( ) ? GL TEXTURE RECTANGLE ARB : GL TEXTURE 2D;

f l o a t scissorx1 , scissory1 , scissorx2 , scissory2 ; #define BLURTILES 32 #define BLURTILEMASK (0xFFFFFFFFU>>(32−BLURTILES ) ) uint b l u r t i l e s [ BLURTILES+ 1 ] ;

GLenum attach = attachment ( ) ; i f ( hasFBO && attach == GL DEPTH ATTACHMENT EXT) { glDrawBuffer (GL NONE) ; glReadBuffer (GL NONE) ; }

bool i n i t i a l i z e d ; rendertarget ( ) : texw ( 0 ) , texh ( 0 ) , vieww ( 0 ) , viewh ( 0 ) , colorfmt ( GL FALSE ) , depthfmt ( GL FALSE ) , t a r g e t ( GL TEXTURE 2D ) , rendertex ( 0 ) , renderfb ( 0 ) , renderdb ( 0 ) , blurtex ( 0 ) , blurfb ( 0 ) , blurdb ( 0 ) , b l u r s i z e ( 0 ) , blursigma ( 0 ) , i n i t i a l i z e d ( f a l s e ) { }

const GLenum ∗colorfmts = colorformats ( ) ; i n t f i n d = 0; do { createtexture ( rendertex , w, h , NULL, 3 , f i l t e r ( ) ? 1 : 0 , colorfmt ? colorfmt : colorfmts [ f i n d ] , t a r g e t ) ; i f ( ! hasFBO ) break ; else { glFramebufferTexture2D (GL FRAMEBUFFER EXT, attach , target , rendertex , 0) ; i f ( glCheckFramebufferStatus (GL FRAMEBUFFER EXT) == GL FRAMEBUFFER COMPLETE EXT) break ; } } while ( ! colorfmt && colorfmts [++ f i n d ] ) ; i f ( ! colorfmt ) colorfmt = colorfmts [ f i n d ] ;

v i r t u a l ˜ rendertarget ( ) {} v i r t u a l GLenum attachment ( ) const { return GL COLOR ATTACHMENT0 EXT; } v i r t u a l const GLenum ∗colorformats ( ) const { s t a t i c const GLenum colorfmts [ ] = { GL RGB, GL RGB8, GL FALSE }; return colorfmts ; }

i f ( attach == GL DEPTH ATTACHMENT EXT && shadowcompare ( ) ) { i f ( hasDT && hasSH ) { glTexParameteri ( target , GL TEXTURE COMPARE MODE ARB, GL COMPARE R TO TEXTURE ARB ) ; glTexParameteri ( target , GL TEXTURE COMPARE FUNC ARB, GL GEQUAL) ; glTexParameteri ( target , GL DEPTH TEXTURE MODE ARB, GL LUMINANCE) ; } else { glTexParameteri ( target , GL TEXTURE COMPARE SGIX, GL TRUE ) ; glTexParameteri ( target , GL TEXTURE COMPARE OPERATOR SGIX, GL TEXTURE GEQUAL R SGIX ) ; } }

v i r t u a l const GLenum ∗depthformats ( ) const { s t a t i c const GLenum depthfmts [ ] = { GL DEPTH COMPONENT24, GL DEPTH COMPONENT, GL DEPTH COMPONENT16, GL DEPTH COMPONENT32, GL FALSE }; return depthfmts ; } v i r t u a l bool depthtest ( ) const { return true ; } void cleanup ( bool f u l l c l e a n = f a l s e ) { i f ( renderfb ) { glDeleteFramebuffers ( 1 , &renderfb ) ; renderfb = 0; } i f ( renderdb ) { glDeleteRenderbuffers ( 1 , &renderdb ) ; renderdb = 0; } i f ( rendertex ) { glDeleteTextures ( 1 , &rendertex ) ; rendertex = 0; } texw = texh = 0; cleanupblur ( ) ;

i f ( hasFBO && attach ! = GL DEPTH ATTACHMENT EXT && depthtest ( ) ) { i f ( ! renderdb ) { glGenRenderbuffers ( 1 , &renderdb ) ; depthfmt = GL FALSE ; } i f ( ! depthfmt ) glBindRenderbuffer (GL RENDERBUFFER EXT, renderdb ) ; const GLenum ∗depthfmts = depthformats ( ) ; f i n d = 0; do { i f ( ! depthfmt ) glRenderbufferStorage (GL RENDERBUFFER EXT, depthfmts [ f i n d ] , w, h ) ; glFramebufferRenderbuffer (GL FRAMEBUFFER EXT, GL DEPTH ATTACHMENT EXT, GL RENDERBUFFER EXT, renderdb ); i f ( glCheckFramebufferStatus (GL FRAMEBUFFER EXT) == GL FRAMEBUFFER COMPLETE EXT) break ; } while ( ! depthfmt && depthfmts [++ f i n d ] ) ; i f ( ! depthfmt ) depthfmt = depthfmts [ f i n d ] ; }

i f ( f u l l c l e a n ) colorfmt = depthfmt = GL FALSE ; } void cleanupblur ( ) { i f ( blurfb ) { glDeleteFramebuffers ( 1 , &blurfb ) ; blurfb = 0; } i f ( blurtex ) { glDeleteTextures ( 1 , &blurtex ) ; blurtex = 0; } i f ( blurdb ) { glDeleteRenderbuffers ( 1 , &blurdb ) ; blurdb = 0; } b l u r s i z e = 0; blursigma = 0.0 f ; } void setupblur ( ) { i f ( ! hasFBO ) return ; i f ( ! blurtex ) glGenTextures ( 1 , &blurtex ) ; createtexture ( blurtex , texw , texh , NULL, 3 , 1 , colorfmt , t a r g e t ) ; i f ( ! swaptexs ( ) || rtsharefb ) return ; i f ( ! blurfb ) glGenFramebuffers ( 1 , &blurfb ) ; glBindFramebuffer (GL FRAMEBUFFER EXT, blurfb ) ; glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, target , blurtex , 0) ; i f ( depthtest ( ) ) { i f ( ! blurdb ) glGenRenderbuffers ( 1 , &blurdb ) ; glGenRenderbuffers ( 1 , &blurdb ) ; glBindRenderbuffer (GL RENDERBUFFER EXT, blurdb ) ; glRenderbufferStorage (GL RENDERBUFFER EXT, depthfmt , texw , texh ); glFramebufferRenderbuffer (GL FRAMEBUFFER EXT, GL DEPTH ATTACHMENT EXT, GL RENDERBUFFER EXT, blurdb ) ; } glBindFramebuffer (GL FRAMEBUFFER EXT, 0) ; } v i r t u a l bool shadowcompare ( ) const { return f a l s e ; }

i f ( hasFBO ) glBindFramebuffer (GL FRAMEBUFFER EXT, 0) ; texw = w; texh = h ; initialized = false ; } bool a d d b l u r t i l e s ( f l o a t x1 , f l o a t y1 , f l o a t x2 , f l o a t y2 , f l o a t blurmargin = 0) { i f ( x1 >= 1 || y1 >= 1 || x2 h−viewh ; } e l s e i f ( ! blurtex ) setupblur ( ) ; i f ( b l u r s i z e ! = wantsblursize || ( wantsblursize && blursigma ! = wantsblursigma ) ) { setupblurkernel ( wantsblursize , wantsblursigma , blurweights , bluroffsets ) ; b l u r s i z e = wantsblursize ; blursigma = wantsblursigma ; }

uint mask = (BLURTILEMASK>>(BLURTILES − ( tx2 +1) ) ) & (BLURTILEMASK scissory2 ) return f a l s e ;

glDisable ( GL DEPTH TEST ) ; glDisable ( GL CULL FACE ) ; i f ( scissor ) { g l S c i s s o r ( x , y , w, h ) ; glEnable ( GL SCISSOR TEST ) ; }

i f ( ! b l u r t i l e ) return true ; i n t tx1 = max( 0 , min ( BLURTILES − 1 , i n t ( ( x1 + 1)/2 ∗ BLURTILES ) ) ) , ty1 = max( 0 , min ( BLURTILES − 1 , i n t ( ( y1 + 1)/2 ∗ BLURTILES ) ) ) , tx2 = max( 0 , min ( BLURTILES − 1 , i n t ( ( x2 + 1)/2 ∗ BLURTILES ) ) ) , ty2 = max( 0 , min ( BLURTILES − 1 , i n t ( ( y2 + 1)/2 ∗ BLURTILES ) ) ) ;

loopi ( 2 ) { setblurshader ( i , t a r g e t == GL TEXTURE RECTANGLE ARB ? 1 : ( i ? texh : texw ) , blursize , blurweights , b l u r o f f s e t s , t a r g e t ) ;

uint mask = (BLURTILEMASK>>(BLURTILES − ( tx2 +1) ) ) & (BLURTILEMASK= 8; x += 8; } while ( ! ( mask&1) ) { mask >>= 1; x++; } int xstart = x ; do { mask >>= 1; x++; } while (mask&1) ; uint s t r i p = (BLURTILEMASK>>(BLURTILES − x ) ) & ( BLURTILEMASKh−viewh ) , x , y , w, h ) ; } i f ( s c i s s o r ) glDisable ( GL SCISSOR TEST ) ; glEnable ( GL DEPTH TEST ) ; glEnable ( GL CULL FACE ) ; } v i r t u a l bool swaptexs ( ) const { return f a l s e ; } v i r t u a l bool dorender ( ) { return true ; } v i r t u a l bool shouldrender ( ) { return true ; } v i r t u a l void doblur ( i n t blursize , f l o a t blursigma ) { i n t sx , sy , sw, sh ; bool s c i s s o r i n g = r t s c i s s o r && scissorblur ( sx , sy , sw, sh ) && sw > 0 && sh > 0; i f ( ! s c i s s o r i n g ) { sx = sy = 0; sw = vieww ; sh = viewh ; } blur ( blursize , blursigma , sx , sy , sw, sh , s c i s s o r i n g ) ; } v i r t u a l bool scissorrender ( i n t &x , i n t &y , i n t &w, i n t &h ) { i f ( scissorx1 >= scissorx2 || scissory1 >= scissory2 ) { i f ( vieww < texw || viewh < texh ) { x = y = 0; w = vieww ; h = viewh ; return true ; } return f a l s e ; } x = max( i n t ( f l o o r ( ( scissorx1 +1)/2∗vieww ) ) − 2∗blursize , 0) ; y = max( i n t ( f l o o r ( ( scissory1 +1)/2∗viewh ) ) − 2∗blursize , 0) ; w = min ( i n t ( c e i l ( ( scissorx2 +1)/2∗vieww ) ) + 2∗blursize , vieww ) − x ; h = min ( i n t ( c e i l ( ( scissory2 +1)/2∗viewh ) ) + 2∗blursize , viewh ) − y ; return true ; }

engine/rendertarget.h v i r t u a l bool scissorblur ( i n t &x , i n t &y , i n t &w, i n t &h ) { i f ( scissorx1 >= scissorx2 || scissory1 >= scissory2 ) { i f ( vieww < texw || viewh < texh ) { x = y = 0; w = vieww ; h = viewh ; return true ; } return f a l s e ; } x = max( i n t ( f l o o r ( ( scissorx1 +1)/2∗vieww ) ) , 0) ; y = max( i n t ( f l o o r ( ( scissory1 +1)/2∗viewh ) ) , 0) ; w = min ( i n t ( c e i l ( ( scissorx2 +1)/2∗vieww ) ) , vieww ) − x ; h = min ( i n t ( c e i l ( ( scissory2 +1)/2∗viewh ) ) , viewh ) − y ; return true ; }

417

v i r t u a l void doclear ( ) {}

i n t sx , sy , sw, sh ; bool s c i s s o r i n g = r t s c i s s o r && scissorrender ( sx , sy , sw, sh ) && sw > 0 && sh > 0; i f ( scissoring ) { i f ( ! hasFBO ) { sx += screen−>w−vieww ; sy += screen−>h−viewh ; } g l S c i s s o r ( sx , sy , sw, sh ) ; glEnable ( GL SCISSOR TEST ) ; } else { sx = hasFBO ? 0 : screen−>w−vieww ; sy = hasFBO ? 0 : screen−>h−viewh ; sw = vieww ; sh = viewh ; }

v i r t u a l bool screenview ( ) const { return f a l s e ; } v i r t u a l bool t e x r e c t ( ) const { return f a l s e ; } v i r t u a l bool f i l t e r ( ) const { return true ; }

bool succeeded = dorender ( ) ;

void render ( i n t w, i n t h , i n t b l u r s i z e = 0 , f l o a t blursigma = 0) { w = min (w, hwtexsize ) ; h = min ( h , hwtexsize ) ; i f ( texrect ( ) ) { i f (w > screen−>w) w = screen−>w; i f ( h > screen−>h ) h = screen−>h ; vieww = w; viewh = h ; } e l s e i f ( screenview ( ) ) { while ( screen−>w < (w∗3) /4) w /= 2; while ( screen−>h < ( h∗3) /4) h /= 2; vieww = min (w, screen−>w) ; viewh = min ( h , screen−>h ) ; } else { i f ( ! hasFBO ) { while (w > screen−>w) w /= 2; while ( h > screen−>h ) h /= 2; } vieww = w; viewh = h ; } i f (w! = texw || h! = texh || ( t e x r e c t ( ) ? t a r g e t ! = GL TEXTURE RECTANGLE ARB : t a r g e t ! =GL TEXTURE 2D ) || ( hasFBO && ( swaptexs ( ) && ! rtsharefb ? ! blurfb : blurfb ) ) ) cleanup () ; if (! filter () ) { i f ( blurtex ) cleanupblur ( ) ; b l u r s i z e = 0; } i f ( ! rendertex ) setup (w, h ) ; scissorx2 = scissory2 = −1; scissorx1 = scissory1 = 1; memset ( b l u r t i l e s , 0 , s i z e o f ( b l u r t i l e s ) ) ; i f ( ! shouldrender ( ) ) return ; i f ( hasFBO ) { i f ( b l u r s i z e && ! blurtex ) setupblur ( ) ; i f ( swaptexs ( ) && b l u r s i z e ) { swap ( rendertex , blurtex ) ; i f ( ! rtsharefb ) { swap ( renderfb , blurfb ) ; swap ( renderdb , blurdb ) ; } } glBindFramebuffer (GL FRAMEBUFFER EXT, renderfb ) ; i f ( swaptexs ( ) && b l u r s i z e && rtsharefb ) glFramebufferTexture2D (GL FRAMEBUFFER EXT, attachment ( ) , target , rendertex , 0) ; glViewport ( 0 , 0 , vieww , viewh ) ; } e l s e glViewport ( screen−>w−vieww , screen−>h−viewh , vieww , viewh ) ; doclear ( ) ;

i f ( ! depthtest ( ) ) glDisable ( GL DEPTH TEST ) ;

i f ( ! depthtest ( ) ) glEnable ( GL DEPTH TEST ) ; i f ( s c i s s o r i n g ) glDisable ( GL SCISSOR TEST ) ; i f ( succeeded ) { i f ( ! hasFBO ) { glBindTexture ( target , rendertex ) ; if (! initialized ) { sx = screen−>w−vieww ; sy = screen−>h−viewh ; sw = vieww ; sh = viewh ; } glCopyTexSubImage2D ( target , 0 , sx−(screen−>w−vieww ) , sy−( screen−>h−viewh ) , sx , sy , sw, sh ) ; } i n i t i a l i z e d = true ; i f ( b l u r s i z e ) doblur ( blursize , blursigma ) ; } i f ( hasFBO ) glBindFramebuffer (GL FRAMEBUFFER EXT, 0) ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ; } v i r t u a l void dodebug ( i n t w, i n t h ) {} v i r t u a l bool flipdebug ( ) const { return true ; } void debugscissor ( i n t w, i n t h , bool l i n e s = f a l s e ) { i f ( ! r t s c i s s o r || scissorx1 >= scissorx2 || scissory1 >= scissory2 ) return ; i n t sx = i n t ( 0 . 5 f ∗( scissorx1 + 1)∗w) , sy = i n t ( 0 . 5 f ∗( scissory1 + 1)∗h ) , sw = i n t ( 0 . 5 f ∗( scissorx2 − scissorx1 )∗w) , sh = i n t ( 0 . 5 f ∗( scissory2 − scissory1 )∗h ) ; i f ( flipdebug ( ) ) { sy = h − sy ; sh = −sh ; } glBegin ( l i n e s ? GL LINE LOOP : GL TRIANGLE STRIP ) ; g l V e r t e x 2 i ( sx , sy ) ; g l V e r t e x 2 i ( sx + sw, sy ) ; i f ( l i n e s ) g l V e r t e x 2 i ( sx + sw, sy + sh ) ; g l V e r t e x 2 i ( sx , sy + sh ) ; i f ( ! l i n e s ) g l V e r t e x 2 i ( sx + sw, sy + sh ) ; glEnd ( ) ; } void debugblurtiles ( i n t w, i n t h , bool l i n e s = f a l s e ) { i f ( ! b l u r t i l e ) return ; f l o a t vxsz = f l o a t (w) /BLURTILES, vysz = f l o a t ( h ) /BLURTILES ; loop ( y , BLURTILES+1) { uint mask = b l u r t i l e s [ y ] ; i n t x = 0; while (mask) { while ( ! ( mask&0xFF ) ) { mask >>= 8; x += 8; } while ( ! ( mask&1) ) { mask >>= 1; x++; } int xstart = x ; do { mask >>= 1; x++; } while (mask&1) ; uint s t r i p = (BLURTILEMASK>>(BLURTILES − x ) ) & (BLURTILEMASK set ( ) ; glDisable ( t a r g e t ) ; dodebug (w, h ) ;

f l o a t vx = x s t a r t∗vxsz , vy = y∗vysz , vw = ( x−x s t a r t )∗vxsz , vh = ( yend−y )∗vysz ; i f ( flipdebug ( ) ) { vy = h − vy ; vh = −vh ; } l o o p i ( l i n e s ? 1 : 2) { i f ( ! l i n e s ) g l C o l o r 3 f ( 1 , 1 , i ? 1.0 f : 0.5 f ) ; glBegin ( l i n e s || i ? GL LINE LOOP : GL TRIANGLE STRIP ) ; g l V e r t e x 2 f ( vx , vy ) ; g l V e r t e x 2 f ( vx+vw, vy ) ; i f ( l i n e s || i ) g l V e r t e x 2 f ( vx+vw, vy+vh ) ; g l V e r t e x 2 f ( vx , vy+vh ) ; i f ( ! l i n e s && ! i ) g l V e r t e x 2 f ( vx+vw, vy+vh ) ; glEnd ( ) ; } } } } void debug ( ) { i f ( ! rendertex ) return ; i n t w = min ( screen−>w, screen−>h ) /2 , h = (w∗screen−>h ) /screen−>w;

} };

engine/rendertext.cpp c . o f f s e t x = ∗o f f s e t x ; c . o f f s e t y = ∗o f f s e t y ; c . advance = ∗advance ? ∗advance : c . o f f s e t x + c .w; c . tex = f o n t d e f t e x ;

#include ” engine . h” s t a t i c i n l i n e bool htcmp ( const char ∗key , const font &f ) { return ! strcmp ( key , f .name) ; } } s t a t i c hashset fonts ; s t a t i c font ∗f o n t d e f = NULL; s t a t i c i n t f o n t d e f t e x = 0; font ∗curfont = NULL; i n t curfonttex = 0; void newfont ( char ∗name, char ∗tex , i n t ∗defaultw , i n t ∗defaulth ) { font ∗f = &fonts [name ] ; i f ( ! f−>name) f−>name = newstring (name) ; f−>texs . shrink ( 0 ) ; f−>texs . add ( textureload ( tex ) ) ; f−>chars . shrink ( 0 ) ; f−>c h a r o f f s e t = ’ ! ’ ; f−>defaultw = ∗defaultw ; f−>defaulth = ∗defaulth ; f−>scale = f−>defaulth ; fontdef = f ; f o n t d e f t e x = 0; } void f o n t o f f s e t ( char ∗c ) { i f ( ! f o n t d e f ) return ; fontdef−>c h a r o f f s e t = c [ 0 ] ; } void f o n t s c a l e ( i n t ∗scale ) { i f ( ! f o n t d e f ) return ; fontdef−>scale = ∗scale > 0 ? ∗scale : fontdef−>defaulth ;

void fontskip ( i n t ∗n ) { i f ( ! f o n t d e f ) return ; l o o p i (max(∗n , 1) ) { font : : charinfo &c = fontdef−>chars . add ( ) ; c . x = c . y = c .w = c . h = c . o f f s e t x = c . o f f s e t y = c . advance = c . tex = 0; } } COMMANDN( font , newfont , ” s s i i ” ) ; COMMAND( f o n t o f f s e t , ” s ” ) ; COMMAND( fontscale , ” i ” ) ; COMMAND( fonttex , ” s ” ) ; COMMAND( fontchar , ” i i i i i i i ” ) ; COMMAND( fontskip , ” i ” ) ; void f o n t a l i a s ( const char ∗dst , const char ∗src ) { font ∗s = fonts . access ( src ) ; i f ( ! s ) return ; font ∗d = &fonts [ dst ] ; i f ( ! d−>name) d−>name = newstring ( dst ) ; d−>texs = s−>texs ; d−>chars = s−>chars ; d−>c h a r o f f s e t = s−>c h a r o f f s e t ; d−>defaultw = s−>defaultw ; d−>defaulth = s−>defaulth ; d−>scale = s−>scale ; fontdef = d; f o n t d e f t e x = d−>texs . length ( ) −1; }

} COMMAND( f o n t a l i a s , ” ss ” ) ; void f o n t t e x ( char ∗s ) { i f ( ! f o n t d e f ) return ; Texture ∗t = textureload ( s ) ; loopv ( fontdef−>texs ) i f ( fontdef−>texs [ i ] == t ) { f o n t d e f t e x = i ; return ; } f o n t d e f t e x = fontdef−>texs . length ( ) ; fontdef−>texs . add ( t ) ;

bool s e t f o n t ( const char ∗name) { font ∗f = fonts . access (name) ; i f ( ! f ) return f a l s e ; curfont = f ; return true ; }

}

s t a t i c vector fontstack ;

void fontchar ( i n t ∗x , i n t ∗y , i n t ∗w, i n t ∗h , i n t ∗o f f s e t x , i n t ∗o f f s e t y , i n t ∗advance ) { i f ( ! f o n t d e f ) return ;

void pushfont ( ) { fontstack . add ( curfont ) ; }

font : : charinfo &c = fontdef−>chars . add ( ) ; c . x = ∗x ; c . y = ∗y ; c .w = ∗w ? ∗w : fontdef−>defaultw ; c . h = ∗h ? ∗h : fontdef−>defaulth ;

bool popfont ( ) { i f ( fontstack . empty ( ) ) return f a l s e ; curfont = fontstack . pop ( ) ; return true ;

engine/rendertext.cpp }

talk case ’ 1 ’ : c o l o r = bvec ( 96, 160, 255) ; command case ’ 2 ’ : c o l o r = bvec (255 , 192, 64) ; gameplay messages case ’ 3 ’ : c o l o r = bvec (255 , 64, 64) ; important errors case ’ 4 ’ : c o l o r = bvec (128 , 128, 128) ; case ’ 5 ’ : c o l o r = bvec (192 , 64, 192) ; case ’ 6 ’ : c o l o r = bvec (255 , 128, 0) ; case ’ 7 ’ : c o l o r = bvec (255 , 255, 255) ; // provided c o l o r : everything e l s e

void g e t t e x t r e s ( i n t &w, i n t &h ) { i f (w < MINRESW || h < MINRESH) { i f (MINRESW > w∗MINRESH/h ) { h = h∗MINRESW/w; w = MINRESW; } else { w = w∗MINRESH/h ; h = MINRESH; } } }

// blue : ” echo ”

break ;

// yellow :

break ;

// red :

break ; break ; break ; break ;

// // // //

gray magenta orange white

} glColor4ub ( c o l o r . x , c o l o r . y , c o l o r . z , a ) ; }

#define FONTTAB (4∗FONTW) #define TEXTTAB( x ) ( ( i n t ( ( x ) /FONTTAB) +1.0 f )∗FONTTAB) void t a b i f y ( const char ∗str , i n t ∗numtabs ) { i n t tw = max(∗numtabs , 0)∗FONTTAB−1, tabs = 0; f o r ( f l o a t w = t e x t w i d t h f ( s t r ) ; w chars [ c−curfont−>c h a r o f f s e t ] ; i f ( tex ! = curfont−>texs [ i n f o . tex ] ) { x t r a v e r t s += varray : : end ( ) ; tex = curfont−>texs [ i n f o . tex ] ; glBindTexture ( GL TEXTURE 2D, tex−>id ) ; } f l o a t x1 = x + scale∗i n f o . o f f s e t x , y1 = y + scale∗i n f o . o f f s e t y , x2 = x + scale ∗( i n f o . o f f s e t x + i n f o .w) , y2 = y + scale ∗( i n f o . o f f s e t y + i n f o . h ) , tx1 = i n f o . x / f l o a t ( tex−>xs ) , ty1 = i n f o . y / f l o a t ( tex−>ys ) , tx2 = ( i n f o . x + i n f o .w) / f l o a t ( tex−>xs ) , ty2 = ( i n f o . y + i n f o . h ) / f l o a t ( tex−>ys ) ; y1 ) ; y1 ) ; y2 ) ; y2 ) ;

break ;

}

f l o a t t e x t w i d t h f ( const char ∗s t r ) { f l o a t width , height ; text boundsf ( str , width , height ) ; return width ; }

varray : : a t t r i b(x1 , varray : : a t t r i b(x2 , varray : : a t t r i b(x2 , varray : : a t t r i b(x1 ,

419

varray : : varray : : varray : : varray : :

a t t r i b(tx1 , a t t r i b(tx2 , a t t r i b(tx2 , a t t r i b(tx1 ,

#define TEXTSKELETON \ f l o a t y = 0 , x = 0 , scale = curfont−>scale/ f l o a t ( curfont−>defaulth ) ;\ i n t i ;\ f o r ( i = 0; s t r [ i ] ; i ++)\ {\ TEXTINDEX( i )\ i n t c = uchar ( s t r [ i ] ) ;\ i f ( c==’\ t ’ ) { x = TEXTTAB( x ) ; TEXTWHITE( i ) }\ e l s e i f ( c == ’ ’ ) { x += scale∗curfont−>defaultw ; TEXTWHITE( i ) }\ e l s e i f ( c==’\n ’ ) { TEXTLINE ( i ) x = 0; y += FONTH; }\ e l s e i f ( c==’\ f ’ ) { i f ( s t r [ i + 1 ] ) { i ++; TEXTCOLOR( i ) }}\ e l s e i f ( curfont−>chars . inrange ( c−curfont−>c h a r o f f s e t ) )\ {\ f l o a t cw = scale∗curfont−>chars [ c−curfont−>c h a r o f f s e t ] . advance;\ i f ( cw 16) break;\ i f ( ! curfont−>chars . inrange ( c−curfont−>c h a r o f f s e t ) ) break;\ f l o a t cw = scale∗curfont−>chars [ c−curfont−>c h a r o f f s e t ] . advance;\ i f ( cw maxwidth ) break;\ w += cw;\ }\ i f ( x + w > maxwidth && j ! = 0 ) { TEXTLINE ( j −1) x = 0; y += FONTH; }\ TEXTWORD\ }\ e l s e\ { TEXTCHAR( i ) }\ }\ } // a l l the chars are guaranteed to be e i t h e r drawable or c o l o r commands #define TEXTWORDSKELETON \ f o r ( ; j chars [ c−curfont−> c h a r o f f s e t ] . advance ; TEXTCHAR( j ) }\ } #define TEXTEND( cursor ) i f ( cursor >= i ) { do { TEXTINDEX( cursor ) ; } while (0) ; }

ty1 ) ; ty1 ) ; ty2 ) ; ty2 ) ;

return scale∗i n f o . advance ; } //stack [ sp ] i s current c o l o r index s t a t i c void t e x t c o l o r ( char c , char ∗stack , i n t size , i n t &sp , bvec color , int a ) { i f ( c == ’ s ’ ) // save c o l o r { c = stack [ sp ] ; i f ( sp 0) ? −−sp : sp ] ; // r e s t o r e c o l o r e l s e stack [ sp ] = c ; switch ( c ) { case ’ 0 ’ : c o l o r = bvec ( 64, 255, 128) ; break ; // green : player

i n t t e x t v i s i b l e ( const char ∗str , f l o a t hitx , f l o a t hity , i n t maxwidth ) { #define TEXTINDEX( idx ) #define TEXTWHITE( idx ) i f ( y+FONTH > h i t y && x >= h i t x ) return idx ; #define TEXTLINE ( idx ) i f ( y+FONTH > h i t y ) return idx ; #define TEXTCOLOR( idx ) #define TEXTCHAR( idx ) x += cw ; TEXTWHITE( idx ) #define TEXTWORD TEXTWORDSKELETON TEXTSKELETON #undef TEXTINDEX #undef TEXTWHITE #undef TEXTLINE #undef TEXTCOLOR #undef TEXTCHAR #undef TEXTWORD return i ; } //inverse o f t e x t v i s i b l e void t e x t p o s f ( const char ∗str , i n t cursor , f l o a t &cx , f l o a t &cy , i n t maxwidth ) { #define TEXTINDEX( idx ) i f ( idx == cursor ) { cx = x ; cy = y ; break ; } #define TEXTWHITE( idx )

420

Foundations of Videogame Programming Code Repository

#define TEXTLINE ( idx ) #define TEXTCOLOR( idx ) #define TEXTCHAR( idx ) x += cw ; #define TEXTWORD TEXTWORDSKELETON i f ( i >= cursor ) break ; cx = cy = 0; TEXTSKELETON TEXTEND( cursor ) #undef TEXTINDEX #undef TEXTWHITE #undef TEXTLINE #undef TEXTCOLOR #undef TEXTCHAR #undef TEXTWORD

#define TEXTCHAR( idx ) draw char ( tex , c , l e f t +x , top+y , scale ) ; x += cw ; #define TEXTWORD TEXTWORDSKELETON char colorstack [ 1 0 ] ; colorstack [ 0 ] = ’ c ’ ; // i n d i c a t e user c o l o r bvec c o l o r ( r , g , b ) ; i n t colorpos = 0; f l o a t cx = −FONTW, cy = 0; bool usecolor = true ; i f ( a < 0) { usecolor = f a l s e ; a = −a ; } Texture ∗tex = curfont−>texs [ 0 ] ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glBindTexture ( GL TEXTURE 2D, tex−>id ) ; glColor4ub ( c o l o r . x , c o l o r . y , c o l o r . z , a ) ; varray : : enable ( ) ; varray : : d e f a t t r i b ( varray : : ATTRIB VERTEX, 2 , GL FLOAT ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 2 , GL FLOAT ) ; varray : : begin (GL QUADS) ; TEXTSKELETON TEXTEND( cursor ) x t r a v e r t s += varray : : end ( ) ; i f ( cursor >= 0 && ( t o t a l m i l l i s /250)&1) { glColor4ub ( r , g , b , a ) ; i f ( maxwidth ! = −1 && cx >= maxwidth ) { cx = 0; cy += FONTH; } draw char ( tex , ’ ’ , l e f t +cx , top+cy , scale ) ; x t r a v e r t s += varray : : end ( ) ; } varray : : disable ( ) ; #undef TEXTINDEX #undef TEXTWHITE #undef TEXTLINE #undef TEXTCOLOR #undef TEXTCHAR #undef TEXTWORD

} void text boundsf ( const char ∗str , f l o a t &width , f l o a t &height , i n t maxwidth ) { #define TEXTINDEX( idx ) #define TEXTWHITE( idx ) #define TEXTLINE ( idx ) i f ( x > width ) width = x ; #define TEXTCOLOR( idx ) #define TEXTCHAR( idx ) x += cw ; #define TEXTWORD x += w; width = 0; TEXTSKELETON height = y + FONTH; TEXTLINE ( ) #undef TEXTINDEX #undef TEXTWHITE #undef TEXTLINE #undef TEXTCOLOR #undef TEXTCHAR #undef TEXTWORD } } void draw int { #define #define #define #define

text ( const char ∗str , i n t l e f t , i n t top , i n t r , i n t g , i n t b , a , i n t cursor , i n t maxwidth ) TEXTINDEX( idx ) i f ( idx == cursor ) { cx = x ; cy = y ; } TEXTWHITE( idx ) TEXTLINE ( idx ) TEXTCOLOR( idx ) i f ( usecolor ) t e x t c o l o r ( s t r [ idx ] , colorstack , s i z e o f ( colorstack ) , colorpos , color , a ) ;

void reloadfonts ( ) { enumerate ( fonts , font , f , loopv ( f . texs ) i f ( ! reloadtexture (∗ f . texs [ i ] ) ) f a t a l ( ” f a i l e d to reload font texture ” ) ; ); }

engine/renderva.cpp // renderva . cpp : handles the occlusion and rendering o f vertex arrays

d i s t = vfcP [ i ] . d i s t ( cv ) ; i f ( d i s t < −rad ) return VFC NOT VISIBLE ; i f ( d i s t < rad ) v = VFC PART VISIBLE ;

#include ” engine . h” } s t a t i c i n l i n e void drawtris ( GLsizei numindices , const GLvoid ∗indices , ushort minvert , ushort maxvert ) { i f (hasDRE) glDrawRangeElements ( GL TRIANGLES, minvert , maxvert , numindices , GL UNSIGNED SHORT, indices ) ; e l s e glDrawElements ( GL TRIANGLES, numindices , GL UNSIGNED SHORT, indices ) ; glde ++; } s t a t i c i n l i n e void drawvatris ( vtxarray ∗va , GLsizei numindices , const GLvoid ∗indices ) { drawtris ( numindices , indices , va−>minvert , va−>maxvert ) ; } ///////// view frustrum c u l l i n g /////////////////////// plane vfcP [ 5 ] ; // perpindictular vectors to view frustrum bounding planes f l o a t vfcDfog ; // f a r plane c u l l i n g distance ( fog l i m i t ) . f l o a t vfcDnear [ 5 ] , vfcDfar [ 5 ] ; vtxarray ∗v i s i b l e v a ; bool isfoggedsphere ( f l o a t rad , const vec &cv ) { l o o p i ( 4 ) i f ( vfcP [ i ] . d i s t ( cv ) < −rad ) return true ; f l o a t d i s t = vfcP [ 4 ] . d i s t ( cv ) ; return d i s t < −rad || d i s t > vfcDfog + rad ; } i n t i s v i s i b l e s p h e r e ( f l o a t rad , const vec &cv ) { i n t v = VFC FULL VISIBLE ; float dist ; loopi ( 5 ) {

d i s t −= vfcDfog ; i f ( d i s t > rad ) return VFC FOGGED; //VFC NOT VISIBLE ; // c u l l i n g when fog i s c l o s e r than s i z e o f world r e s u l t s in HOM i f ( d i s t > −rad ) v = VFC PART VISIBLE ; return v ; } s t a t i c i n l i n e i n t ishiddencube ( const i v e c &o , i n t s i z e ) { l o o p i ( 5 ) i f ( o . d i s t ( vfcP [ i ] ) < −vfcDfar [ i ]∗ s i z e ) return true ; return f a l s e ; } s t a t i c i n l i n e i n t isfoggedcube ( const i v e c &o , i n t s i z e ) { l o o p i ( 4 ) i f ( o . d i s t ( vfcP [ i ] ) < −vfcDfar [ i ]∗ s i z e ) return true ; f l o a t d i s t = o . d i s t ( vfcP [ 4 ] ) ; return d i s t < −vfcDfar [4]∗ s i z e || d i s t > vfcDfog − vfcDnear [4]∗ s i z e ; } i n t i s v i s i b l e c u b e ( const i v e c &o , i n t s i z e ) { i n t v = VFC FULL VISIBLE ; float dist ; loopi ( 5 ) { d i s t = o . d i s t ( vfcP [ i ] ) ; i f ( d i s t < −vfcDfar [ i ]∗ s i z e ) return VFC NOT VISIBLE ; i f ( d i s t < −vfcDnear [ i ]∗ s i z e ) v = VFC PART VISIBLE ; } d i s t −= vfcDfog ; i f ( d i s t > −vfcDnear [4]∗ s i z e ) return VFC FOGGED; i f ( d i s t > −vfcDfar [4]∗ s i z e ) v = VFC PART VISIBLE ; return v ;

engine/renderva.cpp } f l o a t vadist ( vtxarray ∗va , const vec &p ) { return p . d i s t t o b b ( va−>bbmin , va−>bbmax) ; } #define VASORTSIZE 64 s t a t i c vtxarray ∗vasort [ VASORTSIZE ] ; void addvisibleva ( vtxarray ∗va ) { f l o a t d i s t = vadist ( va , camera1−>o ) ; va−>distance = i n t ( d i s t ) ; /∗cv . d i s t ( camera1−>o ) − va−>s i z e∗SQRT3/2∗/ i n t hash = min ( i n t ( d i s t∗VASORTSIZE/worldsize ) , VASORTSIZE−1); vtxarray ∗∗prev = &vasort [ hash ] , ∗cur = vasort [ hash ] ;

421

extern i n t fog ; vfcDfog = fog ; calcvfcD ( ) ; } plane oldvfcP [ 5 ] ; void savevfcP ( ) { memcpy( oldvfcP , vfcP , s i z e o f ( vfcP ) ) ; } void r e s t o r e v f c P ( ) { memcpy( vfcP , oldvfcP , s i z e o f ( vfcP ) ) ; calcvfcD ( ) ; } extern vector varoot , v a l i s t ;

while ( cur && va−>distance >= cur−>distance ) { prev = &cur−>next ; cur = cur−>next ; }

void visiblecubes ( bool c u l l ) { memset ( vasort , 0 , s i z e o f ( vasort ) ) ; i f ( cull ) { setvfcP ( ) ; f i n d v i s i b l e v a s ( varoot ) ; sortvisiblevas ( ) ; } else { memset ( vfcP , 0 , s i z e o f ( vfcP ) ) ; vfcDfog = 1000000; memset ( vfcDnear , 0 , s i z e o f ( vfcDnear ) ) ; memset ( vfcDfar , 0 , s i z e o f ( vfcDfar ) ) ; v i s i b l e v a = NULL; loopv ( v a l i s t ) { vtxarray ∗va = v a l i s t [ i ] ; va−>distance = 0; va−>curvfc = VFC FULL VISIBLE ; va−>occluded = ! va−>texs ? OCCLUDE GEOM : OCCLUDE NOTHING; va−>query = NULL; va−>next = v i s i b l e v a ; v i s i b l e v a = va ; } }

va−>next = ∗prev ; ∗prev = va ; } void s o r t v i s i b l e v a s ( ) { v i s i b l e v a = NULL; vtxarray ∗∗l a s t = &v i s i b l e v a ; l o o p i ( VASORTSIZE ) i f ( vasort [ i ] ) { vtxarray ∗va = vasort [ i ] ; ∗l a s t = va ; while ( va−>next ) va = va−>next ; l a s t = &va−>next ; } } void f i n d v i s i b l e v a s ( vector &vas , bool resetocclude = f a l s e ) { loopv ( vas ) { vtxarray &v = ∗vas [ i ] ; i n t prevvfc = resetocclude ? VFC NOT VISIBLE : v . curvfc ; v . curvfc = i s v i s i b l e c u b e ( v . o , v . s i z e ) ; i f ( v . curvfc ! =VFC NOT VISIBLE ) { i f ( pvsoccluded ( v . o , v . s i z e ) ) { v . curvfc += PVS FULL VISIBLE − VFC FULL VISIBLE ; continue ; } addvisibleva (&v ) ; i f ( v . children . length ( ) ) f i n d v i s i b l e v a s ( v . children , prevvfc>= VFC NOT VISIBLE ) ; i f ( prevvfc>=VFC NOT VISIBLE ) { v . occluded = ! v . texs ? OCCLUDE GEOM : OCCLUDE NOTHING; v . query = NULL; } } } } void calcvfcD ( ) { loopi ( 5 ) { plane &p = vfcP [ i ] ; vfcDnear [ i ] = vfcDfar [ i ] = 0; loopk ( 3 ) i f ( p [ k ] > 0) vfcDfar [ i ] += p [ k ] ; e l s e vfcDnear [ i ] += p [ k ] ; } } void s e t v f c P ( f l o a t z , const vec &bbmin , const vec &bbmax) { vec4 px = mvpmatrix . getrow ( 0 ) , py = mvpmatrix . getrow ( 1 ) , pz = mvpmatrix . getrow ( 2 ) , pw = mvpmatrix . getrow ( 3 ) ; vfcP [ 0 ] = plane ( vec4 (pw) . mul(−bbmin . x ) . add ( px ) ) . normalize ( ) ; // l e f t plane vfcP [ 1 ] = plane ( vec4 (pw) . mul (bbmax. x ) . sub ( px ) ) . normalize ( ) ; // r i g h t plane vfcP [ 2 ] = plane ( vec4 (pw) . mul(−bbmin . y ) . add ( py ) ) . normalize ( ) ; // bottom plane vfcP [ 3 ] = plane ( vec4 (pw) . mul (bbmax. y ) . sub ( py ) ) . normalize ( ) ; // top plane vfcP [ 4 ] = plane ( vec4 (pw) . add ( pz ) ) . normalize ( ) ; // near/ f a r planes i f ( z >= 0) l o o p i ( 5 ) vfcP [ i ] . r e f l e c t z ( z ) ;

} s t a t i c i n l i n e bool insideva ( const vtxarray ∗va , const vec &v , i n t margin = 2) { i n t s i z e = va−>s i z e + margin ; return v . x>=va−>o . x−margin && v . y>=va−>o . y−margin && v . z>=va−>o . z− margin && v . xo . x+ s i z e && v . yo . y+ s i z e && v . zo . z+ s i z e ; } ///////// occlusion queries ///////////// #define MAXQUERY 2048 struct queryframe { i n t cur , max; occludequery queries [MAXQUERY] ; }; s t a t i c queryframe queryframes [ 2 ] = {{0, 0}, {0, 0}}; s t a t i c uint f l i p q u e r y = 0; i n t getnumqueries ( ) { return queryframes [ f l i p q u e r y ] . cur ; } void f l i p q u e r i e s ( ) { f l i p q u e r y = ( f l i p q u e r y + 1) % 2; queryframe &q f = queryframes [ f l i p q u e r y ] ; l o o p i ( q f . cur ) q f . queries [ i ] . owner = NULL; q f . cur = 0; } occludequery ∗newquery ( void ∗owner ) { queryframe &q f = queryframes [ f l i p q u e r y ] ; i f ( q f . cur >= q f .max) { i f ( q f .max >= MAXQUERY) return NULL; glGenQueries ( 1 , &q f . queries [ q f .max+ + ] . id ) ; } occludequery ∗query = &q f . queries [ q f . cur + + ] ;

422

Foundations of Videogame Programming Code Repository

query−>owner = owner ; query−>fragments = −1; return query ;

oe−>bbmax) . sub ( oe−>bbmin ) ) ) continue ; bool occluded = oe−>query && oe−>query−>owner == oe && checkquery ( oe−>query ) ; i f ( occluded ) { oe−>distance = −1;

} void resetqueries ( ) { l o o p i ( 2 ) l o o p j ( queryframes [ i ] . max) queryframes [ i ] . queries [ j ] . owner = NULL; }

oe−>next = NULL; ∗lastvisiblemms = oe ; lastvisiblemms = &oe−>next ; } else { i n t v i s i b l e = 0; loopv ( oe−>mapmodels ) { e x t e n t i t y &e = ∗ents [ oe−>mapmodels [ i ] ] ; i f ( e . f l a g s&e x t e n t i t y : : F NOVIS ) continue ; e . v i s i b l e = true ; ++ v i s i b l e ; } i f ( ! v i s i b l e ) continue ;

void clearqueries ( ) { loopi ( 2 ) { queryframe &q f = queryframes [ i ] ; l o o p j ( q f .max) { glDeleteQueries ( 1 , &q f . queries [ j ] . id ) ; q f . queries [ j ] . owner = NULL; } q f . cur = q f .max = 0; } }

oe−>distance = i n t ( camera1−>o . d i s t t o b b ( oe−>o , oe−>s i z e ) ) ; VAR( oqfrags , 0 , 8 , 64) ; VAR( oqwait , 0 , 1 , 1) ; bool checkquery ( occludequery ∗query , bool nowait ) { GLuint fragments ; i f ( query−>fragments >= 0) fragments = query−>fragments ; else { i f ( nowait || ! oqwait ) { GLint a v a i l ; glGetQueryObjectiv ( query−>id , GL QUERY RESULT AVAILABLE, &a v a i l ); i f ( ! a v a i l ) return f a l s e ; } glGetQueryObjectuiv ( query−>id , GL QUERY RESULT ARB, &fragments ) ; query−>fragments = fragments ; } return fragments < uint ( oqfrags ) ; } void drawbb ( const i v e c &bo , const i v e c &br , const vec &camera ) { glBegin (GL QUADS) ; #define GENFACEORIENT( orient , v0 , v1 , v2 , v3 ) do { \ i n t dim = dimension ( o r i e n t ) ; \ i f ( dimcoord ( o r i e n t ) ) \ { \ i f ( camera [ dim ] < bo [ dim ] + br [ dim ] ) continue ; \ } \ e l s e i f ( camera [ dim ] > bo [ dim ] ) continue ; \ v0 v1 v2 v3 \ x t r a v e r t s += 4; \ } while ( 0 ) ; #define GENFACEVERT( orient , vert , ox , oy , oz , rx , ry , rz ) \ g l V e r t e x 3 f ( ox rx , oy ry , oz rz ) ; GENFACEVERTS( bo . x , bo . x + br . x , bo . y , bo . y + br . y , bo . z , bo . z + br . z , , , , , , ) #undef GENFACEORIENT #undef GENFACEVERTS glEnd ( ) ; } extern i n t octaentsize ; s t a t i c o c t a e n t i t i e s ∗visiblemms , ∗∗lastvisiblemms ; s t a t i c i n l i n e bool insideoe ( const o c t a e n t i t i e s ∗oe , const vec &v , i n t margin = 1) { return v . x>=oe−>bbmin . x−margin && v . y>=oe−>bbmin . y−margin && v . z>=oe−> bbmin . z−margin && v . xbbmax. x+margin && v . ybbmax. y+margin && v . z bbmax. z+margin ; } void findvisiblemms ( const vector &ents ) { f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( va−>mapmodels . empty ( ) || va−>curvfc >= VFC FOGGED || va−> occluded >= OCCLUDE BB) continue ; loopv ( va−>mapmodels ) { o c t a e n t i t i e s ∗oe = va−>mapmodels [ i ] ; i f ( isfoggedcube ( oe−>o , oe−>s i z e ) || pvsoccluded ( oe−>bbmin , i v e c (

o c t a e n t i t i e s ∗∗prev = &visiblemms , ∗cur = visiblemms ; while ( cur && cur−>distance >= 0 && oe−>distance > cur−> distance ) { prev = &cur−>next ; cur = cur−>next ; } i f (∗ prev == NULL) lastvisiblemms = &oe−>next ; oe−>next = ∗prev ; ∗prev = oe ; } } } } VAR(oqmm, 0 , 4 , 8) ; extern bool getentboundingbox ( e x t e n t i t y &e , i v e c &o , i v e c &r ) ; void rendermapmodel ( e x t e n t i t y &e ) { i n t anim = ANIM MAPMODEL|ANIM LOOP, basetime = 0; i f ( e . f l a g s&e x t e n t i t y : : F ANIM ) e n t i t i e s : : animatemapmodel ( e , anim , basetime ) ; mapmodelinfo ∗mmi = getmminfo ( e . a t t r 2 ) ; i f (mmi) rendermodel(&e . l i g h t , mmi−>name, anim , e . o , e . attr1 , 0 , MDL CULL VFC | MDL CULL DIST | MDL DYNLIGHT, NULL, NULL, basetime ) ; } extern i n t r e f l e c t d i s t ; vtxarray ∗r e f l e c t e d v a ; void renderreflectedmapmodels ( ) { const vector &ents = e n t i t i e s : : getents ( ) ; o c t a e n t i t i e s ∗mms = visiblemms ; i f ( reflecting ) { o c t a e n t i t i e s ∗∗lastmms = &mms; f o r ( vtxarray ∗va = r e f l e c t e d v a ; va ; va = va−>rnext ) { i f ( va−>mapmodels . empty ( ) || va−>distance > r e f l e c t d i s t ) continue ; loopv ( va−>mapmodels ) { o c t a e n t i t i e s ∗oe = va−>mapmodels [ i ] ; ∗lastmms = oe ; lastmms = &oe−>rnext ; } } ∗lastmms = NULL; } f o r ( o c t a e n t i t i e s ∗oe = mms; oe ; oe = r e f l e c t i n g ? oe−>rnext : oe−>next ) i f ( r e f l e c t i n g || oe−>distance >= 0) { i f ( r e f l e c t i n g || r e f r a c t i n g>0 ? oe−>bbmax. z bbmin . z >= r e f l e c t z ) continue ; i f ( isfoggedcube ( oe−>o , oe−>s i z e ) ) continue ; loopv ( oe−>mapmodels ) { e x t e n t i t y &e = ∗ents [ oe−>mapmodels [ i ] ] ; i f ( e . v i s i b l e || e . f l a g s&e x t e n t i t y : : F NOVIS ) continue ; e . v i s i b l e = true ; }

engine/renderva.cpp } i f (mms) { startmodelbatches ( ) ; f o r ( o c t a e n t i t i e s ∗oe = mms; oe ; oe = r e f l e c t i n g ? oe−>rnext : oe−> next ) { loopv ( oe−>mapmodels ) { e x t e n t i t y &e = ∗ents [ oe−>mapmodels [ i ] ] ; i f ( ! e . v i s i b l e ) continue ; rendermapmodel ( e ) ; e . visible = false ; } } endmodelbatches ( ) ; } } void rendermapmodels ( ) { const vector &ents = e n t i t i e s : : getents ( ) ; visiblemms = NULL; lastvisiblemms = &visiblemms ; findvisiblemms ( ents ) ; s t a t i c i n t skipoq = 0; bool doquery = hasOQ && oqfrags && oqmm; startmodelbatches ( ) ; f o r ( o c t a e n t i t i e s ∗oe = visiblemms ; oe ; oe = oe−>next ) i f ( oe−>distance >=0) { bool rendered = f a l s e ; loopv ( oe−>mapmodels ) { e x t e n t i t y &e = ∗ents [ oe−>mapmodels [ i ] ] ; i f ( ! e . v i s i b l e ) continue ; i f ( ! rendered ) { rendered = true ; oe−>query = doquery && oe−>distance>0 && ! ( + + skipoq%oqmm) ? newquery ( oe ) : NULL; i f ( oe−>query ) startmodelquery ( oe−>query ) ; } rendermapmodel ( e ) ; e . visible = false ; } i f ( rendered && oe−>query ) endmodelquery ( ) ; } endmodelbatches ( ) ; bool colormask = true ; f o r ( o c t a e n t i t i e s ∗oe = visiblemms ; oe ; oe = oe−>next ) i f ( oe−>distance query = doquery && ! insideoe ( oe , camera1−>o ) ? newquery ( oe ) : NULL; i f ( ! oe−>query ) continue ; i f ( colormask ) { glDepthMask ( GL FALSE ) ; glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ; nocolorshader−>set ( ) ; colormask = f a l s e ; } startquery ( oe−>query ) ; drawbb ( oe−>bbmin , i v e c ( oe−>bbmax) . sub ( oe−>bbmin ) ) ; endquery ( oe−>query ) ; } i f ( ! colormask ) { glDepthMask ( GL TRUE ) ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, fading ? GL FALSE : GL TRUE ) ; }

423

i f ( c [ i ] . ext && c [ i ] . ext−>va ) { vtxarray ∗va = c [ i ] . ext−>va ; i f ( va−>curvfc >= VFC FOGGED || ( va−>occluded >= OCCLUDE BB && bbinsideva ( bo , br , va ) ) ) continue ; } i f ( c [ i ] . children && bboccluded ( bo , br , c [ i ] . children , co , size>>1)) continue ; return f a l s e ; } return true ; } bool bboccluded ( const i v e c &bo , const i v e c &br ) { i n t d i f f = ( bo . x ˆ ( bo . x+br . x ) ) | ( bo . y ˆ ( bo . y+br . y ) ) | ( bo . z ˆ ( bo . z+br . z ) ); i f ( d i f f &˜((1curvfc >= VFC FOGGED || ( va−>occluded >= OCCLUDE BB && bbinsideva ( bo , br , va ) ) ) return true ; } scale−−; while ( c−>children && ! ( d i f f&(1ext && c−>ext−>va ) { vtxarray ∗va = c−>ext−>va ; i f ( va−>curvfc >= VFC FOGGED || ( va−>occluded >= OCCLUDE BB && bbinsideva ( bo , br , va ) ) ) return true ; } scale−−; } i f ( c−>children ) return bboccluded ( bo , br , c−>children , i v e c ( bo ) .mask (˜((2set ( ) ; glDisable ( GL TEXTURE 2D ) ; glEnableClientState ( GL VERTEX ARRAY ) ; glPolygonMode ( GL FRONT AND BACK, GL LINE ) ; glColor3ub ( ( outlinecolour>>16)&0xFF , ( outlinecolour>>8)&0xFF , outlinecolour&0xFF ) ;

}

enablepolygonoffset ( GL POLYGON OFFSET LINE ) ;

s t a t i c i n l i n e bool bbinsideva ( const i v e c &bo , const i v e c &br , vtxarray ∗ va ) { return bo . x >= va−>bbmin . x && bo . y >= va−>bbmin . y && va−>o . z >= va−> bbmin . z && bo . x + br . x bbmax. x && bo . y + br . y bbmax. y && bo . z + br . z bbmax. z ; }

vtxarray ∗prev = NULL; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( va−>occluded >= OCCLUDE BB) continue ; i f ( ! va−>alphaback && ! va−>alphafront && ( ! va−>texs || va−>occluded >= OCCLUDE GEOM) ) continue ;

s t a t i c i n l i n e bool bboccluded ( const i v e c &bo , const i v e c &br , cube ∗c , const i v e c &o , i n t s i z e ) { loopoctabox ( o , size , bo , br ) { i v e c co ( i , o . x , o . y , o . z , s i z e ) ;

i f ( ! d t o u t l i n e ) glDisable ( GL DEPTH TEST ) ;

i f ( ! prev || va−>vbuf ! = prev−>vbuf ) { i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>ebuf ) ;

424

Foundations of Videogame Programming Code Repository } glVertexPointer ( 3 , GL FLOAT, VTXSIZE, va−>vdata [ 0 ] . pos . v ) ;

glDepthFunc ( GL LESS ) ;

} i f ( va−>texs && va−>occluded < OCCLUDE GEOM) { drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; xtravertsva += va−>v e r t s ; } i f ( va−>alphaback || va−>alphafront ) { drawvatris ( va , 3∗(va−>alphabacktris + va−>a l p h a f r o n t t r i s ) , &va−> edata [3∗( va−>t r i s + va−>b l e n d t r i s ) ] ) ; xtravertsva += 3∗(va−>alphabacktris + va−>a l p h a f r o n t t r i s ) ; }

i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; notextureshader−>set ( ) ; } void rendershadowmapreceivers ( ) { i f ( ! hasBE ) return ;

prev = va ; }

SETSHADER( shadowmapreceiver ) ;

i f ( ! d t o u t l i n e ) glEnable ( GL DEPTH TEST ) ;

glDisable ( GL TEXTURE 2D ) ; glEnableClientState ( GL VERTEX ARRAY ) ;

d i s a b l e p o l y g o n o f f s e t ( GL POLYGON OFFSET LINE ) ; glCullFace (GL FRONT) ; glDepthMask ( GL FALSE ) ; glDepthFunc (GL GREATER) ;

glPolygonMode ( GL FRONT AND BACK, GL FILL ) ; i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; glEnable ( GL TEXTURE 2D ) ;

extern i n t ati minmax bug ; i f ( ! ati minmax bug ) glColorMask ( GL FALSE, GL FALSE, GL TRUE, GL FALSE ) ; glEnable (GL BLEND) ; glBlendEquation ( GL MAX EXT ) ; glBlendFunc (GL ONE, GL ONE) ;

defaultshader−>set ( ) ; vtxarray ∗prev = NULL; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( ! va−>texs || va−>curvfc >= VFC FOGGED || ! isshadowmapreceiver ( va ) ) continue ;

} HVAR( blendbrushcolor , 0 , 0x0000C0, 0xFFFFFF ) ; void renderblendbrush ( GLuint tex , f l o a t x , f l o a t y , f l o a t w, f l o a t h ) { SETSHADER( blendbrush ) ;

i f ( ! prev || va−>vbuf ! = prev−>vbuf ) { i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>ebuf ) ; } glVertexPointer ( 3 , GL FLOAT, VTXSIZE, va−>vdata [ 0 ] . pos . v ) ; }

glEnableClientState ( GL VERTEX ARRAY ) ; glDepthFunc (GL LEQUAL) ; glEnable (GL BLEND) ; glBlendFunc (GL ONE, GL ONE MINUS SRC ALPHA ) ; glEnable ( GL TEXTURE 2D ) ; glBindTexture ( GL TEXTURE 2D, tex ) ; glColor4ub ( ( blendbrushcolor>>16)&0xFF , ( blendbrushcolor>>8)&0xFF , blendbrushcolor&0xFF , 0x40 ) ;

drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; xtravertsva += va−>v e r t s ; prev = va ;

GLfloat s [ 4 ] = { 1.0 f /w, 0 , 0 , −x/w }, t [ 4 ] = { 0 , 1.0 f /h , 0 , −y/h }; i f ( renderpath==R FIXEDFUNCTION ) { setuptexgen ( ) ; glTexGenfv ( GL S , GL OBJECT PLANE, s ) ; glTexGenfv ( GL T , GL OBJECT PLANE, t ) ; } else { setlocalparamfv ( ” texgenS ” , SHPARAM VERTEX, 0 , s ) ; setlocalparamfv ( ” texgenT ” , SHPARAM VERTEX, 1 , t ) ; } vtxarray ∗prev = NULL; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( ! va−>texs || va−>occluded >= OCCLUDE GEOM) continue ; i f ( va−>o . x + va−>s i z e o . y + va−>s i z e o . x >= x + w || va−>o . y >= y + h ) continue ; i f ( ! prev || va−>vbuf ! = prev−>vbuf ) { i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>ebuf ) ; } glVertexPointer ( 3 , GL FLOAT, VTXSIZE, va−>vdata [ 0 ] . pos . v ) ; } drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; xtravertsva += va−>v e r t s ; prev = va ; } i f ( renderpath==R FIXEDFUNCTION ) disabletexgen ( ) ; glDisable ( GL TEXTURE 2D ) ; glDisable (GL BLEND) ;

} glDisable (GL BLEND) ; glBlendEquation ( GL FUNC ADD EXT ) ; glCullFace (GL BACK) ; glDepthMask ( GL TRUE ) ; glDepthFunc ( GL LESS ) ; i f ( ! ati minmax bug ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; glEnable ( GL TEXTURE 2D ) ; } void renderdepthobstacles ( const vec &bbmin , const vec &bbmax, f l o a t scale , f l o a t ∗ranges , i n t numranges ) { f l o a t scales [ 4 ] = { 0 , 0 , 0 , 0 }, o f f s e t s [ 4 ] = { 0 , 0 , 0 , 0 }; i f ( numranges < 0) { SETSHADER( depthfxsplitworld ) ; l o o p i(−numranges ) { i f ( ! i ) scales [ i ] = 1.0 f /scale ; e l s e scales [ i ] = scales [ i −1]∗256; } } else { SETSHADER( depthfxworld ) ; i f ( ! numranges ) l o o p i ( 4 ) scales [ i ] = 1.0 f /scale ; e l s e l o o p i ( numranges )

engine/renderva.cpp {

nocolorshader−>set ( ) ; i f ( cur . colormask ) { cur . colormask = f a l s e ; glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ; } i f ( cur . depthmask ) { cur . depthmask = f a l s e ; glDepthMask ( GL FALSE ) ; }

scales [ i ] = 1.0 f /scale ; o f f s e t s [ i ] = −ranges [ i ] / scale ; } } setlocalparamfv ( ” depthscale ” , SHPARAM VERTEX, 0 , scales ) ; setlocalparamfv ( ” depthoffsets ” , SHPARAM VERTEX, 1 , o f f s e t s ) ;

vec camera ( camera1−>o ) ; i f ( r e f l e c t i n g ) camera . z = r e f l e c t z ;

glDisable ( GL TEXTURE 2D ) ; glEnableClientState ( GL VERTEX ARRAY ) ; vtxarray ∗prev = NULL; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( ! va−>texs || va−>occluded >= OCCLUDE GEOM || va−>o . x > bbmax. x || va−>o . y > bbmax. y || va−>o . z > bbmax. z || va−>o . x + va−>s i z e < bbmin . x || va−>o . y + va−>s i z e < bbmin . y || va−>o . z + va−>s i z e < bbmin . z ) continue ; i f ( ! prev || va−>vbuf ! = prev−>vbuf ) { i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>ebuf ) ; } glVertexPointer ( 3 , GL FLOAT, VTXSIZE, va−>vdata [ 0 ] . pos . v ) ; } drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; xtravertsva += va−>v e r t s ; i f ( va−>alphabacktris + va−>a l p h a f r o n t t r i s > 0) { drawvatris ( va , 3∗(va−>alphabacktris + va−>a l p h a f r o n t t r i s ) , va−> edata + 3∗(va−>t r i s + va−>b l e n d t r i s ) ) ; xtravertsva += 3∗(va−>alphabacktris + va−>a l p h a f r o n t t r i s ) ; } prev = va ; }

startquery ( query ) ; i f ( f u l l ) drawbb ( i v e c ( va−>bbmin ) . sub ( 1 ) , i v e c ( va−>bbmax) . sub ( va−>bbmin ) . add ( 2 ) , camera ) ; e l s e drawbb ( va−>geommin, i v e c ( va−>geommax) . sub ( va−>geommin ) , camera ) ; endquery ( query ) ; extern i n t intel immediate bug ; i f ( intel immediate bug && cur . vbuf ) cur . vbuf = 0; } enum { RENDERPASS LIGHTMAP = 0 , RENDERPASS COLOR, RENDERPASS Z, RENDERPASS GLOW, RENDERPASS ENVMAP, RENDERPASS CAUSTICS, RENDERPASS FOG, RENDERPASS SHADOWMAP, RENDERPASS DYNLIGHT, RENDERPASS LIGHTMAP BLEND }; struct geombatch { const elementset &es ; VSlot &v s l o t ; ushort ∗edata ; vtxarray ∗va ; i n t next , batch ; geombatch ( const elementset &es , ushort ∗edata , vtxarray ∗va ) : es ( es ) , v s l o t ( lookupvslot ( es . texture ) ) , edata ( edata ) , va ( va ) , next(−1) , batch(−1) {}

i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; glEnable ( GL TEXTURE 2D ) ;

i n t compare ( const geombatch &b ) const { i f ( va−>vbuf < b . va−>vbuf ) return −1; i f ( va−>vbuf > b . va−>vbuf ) return 1; i f ( renderpath ! =R FIXEDFUNCTION ) { i f ( va−>dynlightmask < b . va−>dynlightmask ) return −1; i f ( va−>dynlightmask > b . va−>dynlightmask ) return 1; i f ( v s l o t . s l o t−>shader < b . v s l o t . s l o t−>shader ) return −1; i f ( v s l o t . s l o t−>shader > b . v s l o t . s l o t−>shader ) return 1; i f ( v s l o t . s l o t−>params . length ( ) < b . v s l o t . s l o t−>params . length ( ) ) return −1; i f ( v s l o t . s l o t−>params . length ( ) > b . v s l o t . s l o t−>params . length ( ) ) return 1; } i f ( es . texture < b . es . texture ) return −1; i f ( es . texture > b . es . texture ) return 1; i f ( es . lmid < b . es . lmid ) return −1; i f ( es . lmid > b . es . lmid ) return 1; i f ( es . envmap < b . es . envmap ) return −1; i f ( es . envmap > b . es . envmap ) return 1; i f ( es . dim < b . es . dim ) return −1; i f ( es . dim > b . es . dim ) return 1; return 0; }

defaultshader−>set ( ) ; } VAR( oqdist , 0 , 256, 1024) ; VAR( zpass , 0 , 1 , 1) ; VAR( glowpass , 0 , 1 , 1) ; VAR( envpass , 0 , 1 , 1) ; struct renderstate { bool colormask , depthmask , blending , mtglow ; i n t skipped , alphaing ; GLuint vbuf ; i n t diffusetmu , lightmaptmu , glowtmu , causticstmu ; GLfloat c o l o r [ 4 ] , f o g c o l o r [ 4 ] ; vec colorscale , glowcolor , envscale , l i g h t c o l o r ; f l o a t alphascale ; GLuint textures [ 8 ] ; S l o t ∗s l o t , ∗texgenslot ; VSlot ∗v s l o t , ∗texgenvslot ; f l o a t texgenscrollS , texgenscrollT ; i n t texgendim ; bool mttexgen , specmask ; int visibledynlights ; uint dynlightmask ; vec dynlightpos ; f l o a t dynlightradius ; renderstate ( ) : colormask ( true ) , depthmask ( true ) , blending ( f a l s e ) , mtglow ( f a l s e ) , skipped ( 0 ) , alphaing ( 0 ) , vbuf ( 0 ) , diffusetmu ( 0 ) , lightmaptmu ( 1 ) , glowtmu(−1) , causticstmu(−1) , c o l o r s c a l e ( 1 , 1 , 1) , glowcolor ( 1 , 1 , 1) , envscale ( 0 , 0 , 0) , alphascale ( 0 ) , s l o t (NULL) , texgenslot (NULL) , v s l o t (NULL) , texgenvslot (NULL) , texgenscrollS ( 0 ) , texgenscrollT ( 0 ) , texgendim(−1) , mttexgen ( f a l s e ) , specmask ( f a l s e ) , v i s i b l e d y n l i g h t s ( 0 ) , dynlightmask ( 0 ) { loopk ( 4 ) c o l o r [ k ] = 1; loopk ( 8 ) textures [ k ] = 0; } }; void renderquery ( renderstate &cur , occludequery ∗query , vtxarray ∗va , bool f u l l = true ) {

425

}; s t a t i c vector geombatches ; s t a t i c i n t f i r s t b a t c h = −1, numbatches = 0; s t a t i c void mergetexs ( renderstate &cur , vtxarray ∗va , elementset ∗texs = NULL, i n t numtexs = 0 , ushort ∗edata = NULL) { i f ( ! texs ) { texs = va−>e s l i s t ; numtexs = va−>texs ; edata = va−>edata ; i f ( cur . alphaing ) { texs += va−>texs + va−>blends ; edata += 3∗(va−>t r i s + va−>b l e n d t r i s ) ; numtexs = va−>alphaback ; i f ( cur . alphaing > 1) numtexs += va−>alphafront ; } }

426

Foundations of Videogame Programming Code Repository

i f ( f i r s t b a t c h < 0) { f i r s t b a t c h = geombatches . length ( ) ; numbatches = numtexs ; l o o p i ( numtexs−1) { geombatches . add ( geombatch ( texs [ i ] , edata , va ) ) . next = i +1; edata += texs [ i ] . length [ 1 ] ; } geombatches . add ( geombatch ( texs [ numtexs−1], edata , va ) ) ; return ; } i n t prevbatch = −1, curbatch = f i r s t b a t c h , curtex = 0; do { geombatch &b = geombatches . add ( geombatch ( texs [ curtex ] , edata , va ) ) ; edata += texs [ curtex ] . length [ 1 ] ; i n t d i r = −1; while ( curbatch >= 0) { d i r = b . compare ( geombatches [ curbatch ] ) ; i f ( d i r edata , ∗startdata = NULL; i n t f i r s t t e x = 0 , numtexs = va−>texs ; i f ( cur . alphaing ) { f i r s t t e x += va−>texs + va−>blends ; edata += 3∗(va−>t r i s + va−>b l e n d t r i s ) ; numtexs = va−>alphaback ; i f ( cur . alphaing > 1) numtexs += va−>alphafront ; } f o r ( i n t i = f i r s t t e x ; i < f i r s t t e x + numtexs ; i ++) { elementset &es = va−>e s l i s t [ i ] ; VSlot &v s l o t = lookupvslot ( es . texture , f a l s e ) ; i f ( v s l o t . s l o t−>shader−>type&SHADER ENVMAP && v s l o t . skipped&(1e s l i s t [ s t a r t ] , i−s t a r t , startdata ) ; s t a r t = −1; } edata += es . length [ 1 ] ; } i f ( s t a r t >=0) mergetexs ( cur , va , &va−>e s l i s t [ s t a r t ] , f i r s t t e x +numtexs− s t a r t , startdata ) ; } s t a t i c void changedynlightpos ( renderstate &cur ) { GLfloat tx [ 4 ] = { 0.5 f /cur . dynlightradius , 0 , 0 , 0.5 f − 0.5 f∗cur . dynlightpos . x/cur . dynlightradius }, ty [ 4 ] = { 0 , 0.5 f /cur . dynlightradius , 0 , 0.5 f − 0.5 f∗cur . dynlightpos . y/cur . dynlightradius }, t z [ 4 ] = { 0 , 0 , 0.5 f /cur . dynlightradius , 0.5 f − 0.5 f∗cur . dynlightpos . z/cur . dynlightradius }; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; glTexGenfv ( GL S , GL OBJECT PLANE, tx ) ; glTexGenfv ( GL T , GL OBJECT PLANE, ty ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glTexGenfv ( GL S , GL OBJECT PLANE, t z ) ; g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ; } s t a t i c void changevbuf ( renderstate &cur , i n t pass , vtxarray ∗va ) { i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>ebuf ) ; } cur . vbuf = va−>vbuf ; glVertexPointer ( 3 , GL FLOAT, VTXSIZE, va−>vdata [ 0 ] . pos . v ) ; i f ( pass==RENDERPASS LIGHTMAP) { glTexCoordPointer ( 2 , GL FLOAT, VTXSIZE, &va−>vdata [ 0 ] . u ) ; i f ( cur . glowtmu >= 0) { g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . glowtmu ) ; glTexCoordPointer ( 2 , GL FLOAT, VTXSIZE, &va−>vdata [ 0 ] . u ) ; } g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . lightmaptmu ) ; glTexCoordPointer ( 2 , renderpath==R FIXEDFUNCTION ? GL FLOAT : GL SHORT, VTXSIZE, &va−>vdata [ 0 ] . lmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; i f ( renderpath ! =R FIXEDFUNCTION ) { glNormalPointer ( GL BYTE, VTXSIZE, va−>vdata [ 0 ] . norm . v ) ; glColorPointer ( 4 , GL UNSIGNED BYTE, VTXSIZE, va−>vdata [ 0 ] . tangent . v ) ; } e l s e i f ( cur . glowtmu >= 0 && hasCM && maxtmus >= 2) glNormalPointer ( GL BYTE, VTXSIZE, va−>vdata [ 0 ] . norm . v ) ; } e l s e i f ( pass == RENDERPASS ENVMAP) { glTexCoordPointer ( 2 , GL FLOAT, VTXSIZE, &va−>vdata [ 0 ] . u ) ; glNormalPointer ( GL BYTE, VTXSIZE, va−>vdata [ 0 ] . norm . v ) ; } e l s e i f ( pass==RENDERPASS COLOR || pass==RENDERPASS GLOW || pass== RENDERPASS DYNLIGHT) glTexCoordPointer ( 2 , GL FLOAT, VTXSIZE, &va−>vdata [ 0 ] . u ) ;

} s t a t i c void mergeglowtexs ( renderstate &cur , vtxarray ∗va ) { i n t s t a r t = −1; ushort ∗edata = va−>edata , ∗startdata = NULL; i n t f i r s t t e x = 0 , numtexs = va−>texs ; i f ( cur . alphaing ) { f i r s t t e x += va−>texs + va−>blends ; edata += 3∗(va−>t r i s + va−>b l e n d t r i s ) ; numtexs = va−>alphaback ; i f ( cur . alphaing > 1) numtexs += va−>alphafront ; } f o r ( i n t i = f i r s t t e x ; i < f i r s t t e x + numtexs ; i ++) { elementset &es = va−>e s l i s t [ i ] ; VSlot &v s l o t = lookupvslot ( es . texture , f a l s e ) ; i f ( v s l o t . s l o t−>texmask&(1shader−>type&SHADER ENVMAP && b . es . envmap! = EMID CUSTOM && cur . envscale . x ) { GLuint emtex = lookupenvmap ( b . es . envmap ) ; i f ( cur . textures [ cur . glowtmu ] ! = emtex ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . glowtmu ) ; glBindTexture (GL TEXTURE CUBE MAP ARB, cur . textures [ cur . glowtmu ] = emtex ) ; changed = true ; } } } else { i n t tmu = cur . lightmaptmu+1; i f ( b . v s l o t . s l o t−>shader−>type&SHADER NORMALSLMS) { i f ( cur . textures [tmu ] ! = lightmaptexs [ lmid + 1 ] . id ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+tmu ) ; glBindTexture ( GL TEXTURE 2D, cur . textures [ tmu ] = lightmaptexs [ lmid + 1 ] . id ) ; changed = true ; } tmu++; } i f ( b . v s l o t . s l o t−>shader−>type&SHADER ENVMAP && b . es . envmap! = EMID CUSTOM) { GLuint emtex = lookupenvmap ( b . es . envmap ) ; i f ( cur . textures [tmu ] ! = emtex ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+tmu ) ; glBindTexture (GL TEXTURE CUBE MAP ARB, cur . textures [ tmu ] = emtex ) ; changed = true ; } } } i f ( changed ) g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; i f ( cur . dynlightmask ! = b . va−>dynlightmask ) { cur . v i s i b l e d y n l i g h t s = setdynlights ( b . va ) ; cur . dynlightmask = b . va−>dynlightmask ; } } s t a t i c i n l i n e void disableenv ( renderstate &cur ) { glDisable (GL TEXTURE CUBE MAP ARB) ; g l D i s a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ; glDisable ( GL TEXTURE GEN S ) ; glDisable ( GL TEXTURE GEN T ) ; glDisable ( GL TEXTURE GEN R ) ; } s t a t i c i n l i n e void enableenv ( renderstate &cur ) { setuptmu ( cur . glowtmu , ”T , P @ Pa ” , ”= Pa ” ) ; glEnableClientState (GL NORMAL ARRAY) ; glTexGeni ( GL S , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glTexGeni ( GL T , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glTexGeni ( GL R , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glEnable ( GL TEXTURE GEN S ) ; glEnable ( GL TEXTURE GEN T ) ; glEnable ( GL TEXTURE GEN R ) ; glMatrixMode (GL TEXTURE) ; glLoadMatrixf ( envmatrix . v ) ; glMatrixMode (GL MODELVIEW) ; glEnable (GL TEXTURE CUBE MAP ARB) ; } s t a t i c i n l i n e void disableglow ( renderstate &cur ) { glDisable ( GL TEXTURE 2D ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; } s t a t i c i n l i n e void enableglow ( renderstate &cur , bool shouldsetuptmu = true ) {

427

i f ( shouldsetuptmu ) setuptmu ( cur . glowtmu , ”P + T ” , ”= Pa ” ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; glEnable ( GL TEXTURE 2D ) ; } s t a t i c void changeenv ( renderstate &cur , i n t pass , S l o t &s l o t , VSlot & v s l o t , geombatch ∗b = NULL) { i f ( pass==RENDERPASS ENVMAP) { bool specmask = s l o t . sts . length ( ) && s l o t . sts [ 0 ] . t−>bpp >= 4; i f ( cur . envscale ! = v s l o t . envscale ) { i f ( v s l o t . envscale . x ! = v s l o t . envscale . y || v s l o t . envscale . y ! = v s l o t . envscale . z ) { i f ( hasBC && ! specmask ) { i f ( cur . envscale . x == cur . envscale . y && cur . envscale . y == cur . envscale . z ) glBlendFunc (GL CONSTANT COLOR EXT, GL ONE MINUS CONSTANT COLOR EXT) ; glBlendColor ( v s l o t . envscale . x , v s l o t . envscale . y , v s l o t . envscale . z , 1) ; cur . envscale = v s l o t . envscale ; } else { i f ( cur . envscale . x ! = cur . envscale . y || cur . envscale . y ! = cur . envscale . z ) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; // fake i t , take the average and do a constant blend f l o a t envscale = ( min ( v s l o t . envscale . x , min ( v s l o t . envscale . y , v s l o t . envscale . z ) ) + max( v s l o t . envscale . x , max( v s l o t . envscale . y , v s l o t . envscale . z ) ) ) /2; g l C o l o r 4 f ( 1 , 1 , 1 , envscale ) ; cur . envscale = vec ( envscale , envscale , envscale ) ; } } else { i f ( cur . envscale . x ! = cur . envscale . y || cur . envscale . y ! = cur . envscale . z ) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; g l C o l o r 4 f ( 1 , 1 , 1 , v s l o t . envscale . x ) ; cur . envscale = v s l o t . envscale ; } } i f ( cur . specmask ! = specmask ) { i f ( ! specmask ) glDisable ( GL TEXTURE 2D ) ; e l s e i f ( specmask ) glEnable ( GL TEXTURE 2D ) ; cur . specmask = specmask ; } GLuint tex = 0; i f (b) { i f ( b−>es . envmap==EMID CUSTOM) return ; tex = lookupenvmap ( b−>es . envmap ) ; } else { i f ( ! ( s l o t . texmask&(1sts [ 0 ] . t , ∗tex = s l o t . sts . empty ( ) ? notexture : s l o t . sts [ 0 ] . t ; i f ( ! cur . texgenvslot || s l o t . sts . empty ( ) || ( curtex−>xs ! = tex−>xs || curtex−>ys ! = tex−>ys || cur . texgenvslot−>r o t a t i o n ! = v s l o t . r o t a t i o n || cur . texgenvslot −>scale ! = v s l o t . scale || cur . texgenvslot−>x o f f s e t ! = v s l o t . x o f f s e t || cur . texgenvslot−> y o f f s e t ! = v s l o t . y o f f s e t || cur . texgenvslot−>s c r o l l S ! = v s l o t . s c r o l l S || cur . texgenvslot−> scrollT != vslot . scrollT ) ) { f l o a t xs = v s l o t . rotation>=2 && v s l o t . rotationxs : tex−>xs , ys = ( v s l o t . rotation>=1 && v s l o t . rotation ys : tex−>ys , scrollS = vslot . scrollS , scrollT = vslot . scrollT ; i f ( ( v s l o t . r o t a t i o n &5)==1) swap ( s c r o l l S , s c r o l l T ) ; s c r o l l S ∗= l a s t m i l l i s∗tex−>xs/xs ; s c r o l l T ∗= l a s t m i l l i s∗tex−>ys/ys ; i f ( cur . texgenscrollS ! = s c r o l l S || cur . texgenscrollT ! = s c r o l l T ) { cur . texgenscrollS = s c r o l l S ; cur . texgenscrollT = s c r o l l T ; cur . texgendim = −1; } } cur . texgenslot = &s l o t ; cur . texgenvslot = &v s l o t ; } i f ( renderpath==R FIXEDFUNCTION ) { bool mtglow = cur . mtglow && ! cur . envscale . x ; i f ( cur . texgendim == dim && ( cur . mttexgen || ! mtglow ) ) return ; glMatrixMode (GL TEXTURE) ; i f ( cur . texgendim ! =dim ) { glLoadIdentity ( ) ; i f ( dim es . length [ curbatch−>va−>shadowed ? 0 : 1 ] ; i f ( len ) { i f ( rendered < 0) { i f ( renderpath ! =R FIXEDFUNCTION ) changeshader ( cur , b . v s l o t . s l o t−>shader , ∗b . v s l o t . s l o t , b . v s l o t , f a l s e ) ; rendered = 0; gbatches ++; } ushort minvert = curbatch−>es . minvert [ 0 ] , maxvert = curbatch−>es . maxvert [ 0 ] ; i f ( ! curbatch−>va−>shadowed ) { minvert = min ( minvert , curbatch−> es . minvert [ 1 ] ) ; maxvert = max( maxvert , curbatch−>es . maxvert [ 1 ] ) ; } drawtris ( len , curbatch−>edata , minvert , maxvert ) ; v t r i s += len /3; } i f ( curbatch−>es . length [ 1 ] > len && ! shadowed ) shadowed = curbatch ; i f ( curbatch−>batch < 0) break ; } i f ( shadowed ) f o r ( geombatch ∗curbatch = shadowed ; ; curbatch = & geombatches [ curbatch−>batch ] ) { i f ( curbatch−>va−>shadowed && curbatch−>es . length [ 1 ] > curbatch−>es . length [ 0 ] ) { i f ( rendered < 1) { i f ( renderpath ! =R FIXEDFUNCTION ) changeshader ( cur , b . v s l o t . s l o t−>shader , ∗b . v s l o t . s l o t , b . v s l o t , true ) ; rendered = 1; gbatches ++; } ushort len = curbatch−>es . length [ 1 ] − curbatch−>es . length [ 0 ] ; drawtris ( len , curbatch−>edata + curbatch−>es . length [ 0 ] , curbatch −>es . minvert [ 1 ] , curbatch−>es . maxvert [ 1 ] ) ; v t r i s += len /3; } i f ( curbatch−>batch < 0) break ; } } s t a t i c void resetbatches ( ) { geombatches . s e t s i z e ( 0 ) ; f i r s t b a t c h = −1; numbatches = 0; } s t a t i c void renderbatches ( renderstate &cur , i n t pass ) { cur . s l o t = NULL; cur . v s l o t = NULL; i n t curbatch = f i r s t b a t c h ; i f ( curbatch >= 0) { i f ( cur . alphaing ) { i f ( cur . depthmask ) { cur . depthmask = f a l s e ; glDepthMask ( GL FALSE ) ; } } e l s e i f ( ! cur . depthmask ) { cur . depthmask = true ; glDepthMask ( GL TRUE ); } i f ( ! cur . colormask ) { cur . colormask = true ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, cur . alphaing ? GL FALSE : GL TRUE ) ; } } while ( curbatch >= 0) { geombatch &b = geombatches [ curbatch ] ; curbatch = b . next ; i f ( cur . vbuf ! = b . va−>vbuf ) changevbuf ( cur , pass , b . va ) ; i f ( cur . v s l o t ! = &b . v s l o t ) { changeslottmus ( cur , pass , ∗b . v s l o t . s l o t , b . v s l o t ) ; i f ( cur . texgendim ! = b . es . dim || ( cur . texgendim vbuf ) changevbuf ( cur , RENDERPASS Z, va ) ; i f ( ! cur . depthmask ) { cur . depthmask = true ; glDepthMask ( GL TRUE ) ; } i f ( cur . colormask ) { cur . colormask = f a l s e ; glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ; } extern i n t apple glsldepth bug ; i n t f i r s t t e x = 0 , numtexs = va−>texs , numtris = va−>t r i s ; ushort ∗edata = va−>edata ; i f ( cur . alphaing ) { f i r s t t e x += va−>texs + va−>blends ; edata += 3∗(va−>t r i s + va−>b l e n d t r i s ) ; numtexs = va−>alphaback + va−>alphafront ; numtris = va−>alphabacktris + va−>a l p h a f r o n t t r i s ; xtravertsva += 3∗numtris ; } e l s e xtravertsva += va−>v e r t s ; i f ( renderpath ! =R ASMGLSLANG || ! apple glsldepth bug ) { nocolorshader−>set ( ) ; drawvatris ( va , 3∗numtris , edata ) ; } else { i n t l a s t f l a g s = 0 , lastdraw = 0 , o f f s e t = 0; f o r ( i n t i = f i r s t t e x ; i < f i r s t t e x + numtexs ; i ++) { i n t f l a g s = lookupvslot ( va−>e s l i s t [ i ] . texture ) . s l o t−>shader−> type&SHADER GLSLANG; i f ( f l a g s ! = l a s t f l a g s && o f f s e t > lastdraw ) { ( l a s t f l a g s ? nocolorglslshader : nocolorshader )−>set ( ) ; drawvatris ( va , o f f s e t−lastdraw , edata+lastdraw ) ; lastdraw = o f f s e t ; } lastflags = flags ; o f f s e t += va−>e s l i s t [ i ] . length [ 1 ] ; } i f ( o f f s e t > lastdraw ) { ( l a s t f l a g s ? nocolorglslshader : nocolorshader )−>set ( ) ; drawvatris ( va , o f f s e t−lastdraw , va−>edata+lastdraw ) ; } } } vector foggedvas ; #define startvaquery ( va , flush ) \ do { \ i f ( va−>query ) \ { \ flush ; \ startquery ( va−>query ) ; \ } \ } while ( 0 )

#define endvaquery ( va , flush ) \ do { \ i f ( va−>query ) \ { \ flush ; \ endquery ( va−>query ) ; \ } \ } while ( 0 ) void renderfoggedvas ( renderstate &cur , bool doquery = f a l s e ) { s t a t i c Shader ∗fogshader = NULL; i f ( ! fogshader ) fogshader = lookupshaderbyname ( ” fogworld ” ) ; i f ( fading ) fogshader−>s e t v a r i a n t ( 0 , 2) ; e l s e fogshader−>set ( ) ;

engine/renderva.cpp

431

e l s e i f ( ! batchgeom && geombatches . length ( ) ) renderbatches ( cur , RENDERPASS LIGHTMAP) ;

glDisable ( GL TEXTURE 2D ) ;

break ; glColor3ubv ( fogging ? r e f r a c t c o l o r . v : f o g c o l o r . v ) ;

}

loopv ( foggedvas ) { vtxarray ∗va = foggedvas [ i ] ; i f ( cur . vbuf ! = va−>vbuf ) changevbuf ( cur , RENDERPASS FOG, va ) ;

case RENDERPASS DYNLIGHT: i f ( cur . dynlightpos . d i s t t o b b ( va−>geommin, va−>geommax) >= cur . dynlightradius ) break ; v v e r t s += va−>v e r t s ; mergetexs ( cur , va ) ; i f ( ! batchgeom && geombatches . length ( ) ) renderbatches ( cur , pass ) ; break ;

i f ( doquery ) startvaquery ( va , ) ; drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; v t r i s += va−>t r i s ; i f ( doquery ) endvaquery ( va , ) ;

case RENDERPASS FOG: i f ( cur . vbuf ! = va−>vbuf ) changevbuf ( cur , pass , va ) ; drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; xtravertsva += va−>v e r t s ; break ;

} glEnable ( GL TEXTURE 2D ) ; foggedvas . s e t s i z e ( 0 ) ; }

case RENDERPASS SHADOWMAP: i f ( isshadowmapreceiver ( va ) ) rendershadowmappass ( cur , va ) ; break ;

void rendershadowmappass ( renderstate &cur , vtxarray ∗va ) { i f ( cur . vbuf ! = va−>vbuf ) changevbuf ( cur , RENDERPASS SHADOWMAP, va ) ; elementset ∗texs = va−>e s l i s t ; ushort ∗edata = va−>edata ; l o o p i ( va−>texs ) { elementset &es = texs [ i ] ; i n t len = es . length [ 1 ] − es . length [ 0 ] ; i f ( len > 0) { drawtris ( len , &edata [ es . length [ 0 ] ] , es . minvert [ 1 ] , es . maxvert [1]) ; v t r i s += len /3; } edata += es . length [ 1 ] ; } } VAR( batchgeom , 0 , 1 , 1) ; void renderva ( renderstate &cur , vtxarray ∗va , i n t pass = RENDERPASS LIGHTMAP, bool fogpass = f a l s e , bool doquery = f a l s e ) { switch ( pass ) { case RENDERPASS GLOW: i f ( ! ( va−>texmask&(1dynlightmask = 0; i f ( fogpass ? va−>geommax. zcurvfc==VFC FOGGED) { i f ( ! cur . alphaing && ! cur . blending ) foggedvas . add ( va ) ; break ; } i f ( renderpath ! =R FIXEDFUNCTION && ! envmapping && ! g l a r i n g && ! cur . alphaing ) { va−>shadowed = isshadowmapreceiver ( va ) ; calcdynlightmask ( va ) ; } i f ( doquery ) startvaquery ( va , { i f ( geombatches . length ( ) ) renderbatches ( cur , pass ) ; }) ; mergetexs ( cur , va ) ; i f ( doquery ) endvaquery ( va , { i f ( geombatches . length ( ) ) renderbatches ( cur , pass ) ; }) ; e l s e i f ( ! batchgeom && geombatches . length ( ) ) renderbatches ( cur , pass ) ; break ; case RENDERPASS LIGHTMAP BLEND: { i f ( doquery ) startvaquery ( va , { i f ( geombatches . length ( ) ) renderbatches ( cur , RENDERPASS LIGHTMAP) ; }) ; mergetexs ( cur , va , &va−>e s l i s t [ va−>texs ] , va−>blends , va−>edata + 3∗va−>t r i s ) ; i f ( doquery ) endvaquery ( va , { i f ( geombatches . length ( ) ) renderbatches ( cur , RENDERPASS LIGHTMAP) ; }) ;

case RENDERPASS CAUSTICS: i f ( cur . vbuf ! = va−>vbuf ) changevbuf ( cur , pass , va ) ; drawvatris ( va , 3∗va−>t r i s , va−>edata ) ; xtravertsva += va−>v e r t s ; break ; case RENDERPASS Z: i f ( doquery ) startvaquery ( va , ) ; renderzpass ( cur , va ) ; i f ( doquery ) endvaquery ( va , ) ; break ; } } GLuint attenxytex = 0 , attenztex = 0; s t a t i c GLuint createattenxytex ( i n t s i z e ) { uchar ∗data = new uchar [ s i z e∗s i z e ] , ∗dst = data ; loop ( y , s i z e ) loop ( x , s i z e ) { f l o a t dx = 2∗f l o a t ( x ) / ( size −1) − 1 , dy = 2∗f l o a t ( y ) / ( size −1) − 1; f l o a t atten = max( 0 . 0 f , 1.0 f − dx∗dx − dy∗dy ) ; ∗dst++ = uchar ( atten∗255) ; } GLuint tex = 0; glGenTextures ( 1 , &tex ) ; createtexture ( tex , size , size , data , 3 , 1 , GL ALPHA ) ; d e l e t e [ ] data ; return tex ; } s t a t i c GLuint createattenztex ( i n t s i z e ) { uchar ∗data = new uchar [ s i z e ] , ∗dst = data ; loop ( z , s i z e ) { f l o a t dz = 2∗f l o a t ( z ) / ( size −1) − 1; f l o a t atten = dz∗dz ; ∗dst++ = uchar ( atten∗255) ; } GLuint tex = 0; glGenTextures ( 1 , &tex ) ; createtexture ( tex , size , 1 , data , 3 , 1 , GL ALPHA, GL TEXTURE 1D ) ; d e l e t e [ ] data ; return tex ; } #define NUMCAUSTICS 32 s t a t i c Texture ∗caustictex [NUMCAUSTICS] = { NULL }; void loadcaustics ( bool f o r c e ) { s t a t i c bool needcaustics = f a l s e ; i f ( f o r c e ) needcaustics = true ; i f ( ! caustics || ! needcaustics ) return ; useshaderbyname ( ” caustic ” ) ; i f ( caustictex [ 0 ] ) return ; l o o p i (NUMCAUSTICS) { defformatstring (name) ( renderpath==R FIXEDFUNCTION ? ”packages/caustics/caust%.2d . png” : ”packages/caustics/caust%.2d . png ” , i); caustictex [ i ] = textureload (name) ; } } void cleanupva ( )

432

Foundations of Videogame Programming Code Repository

{

setenvparamf ( ” camera ” , SHPARAM VERTEX, 4 , camera1−>o . x , camera1−>o . y , camera1−>o . z , 1) ; setenvparamf ( ” ambient ” , SHPARAM PIXEL, 5 , ambientcolor . x/255.0 f , ambientcolor . y/255.0 f , ambientcolor . z/255.0 f ) ; setenvparamf ( ” m i l l i s ” , SHPARAM VERTEX, 6 , l a s t m i l l i s /1000.0 f , l a s t m i l l i s /1000.0 f , l a s t m i l l i s /1000.0 f ) ;

clearvas ( worldroot ) ; clearqueries ( ) ; i f ( attenxytex ) { glDeleteTextures ( 1 , &attenxytex ) ; attenxytex = 0; } i f ( attenztex ) { glDeleteTextures ( 1 , &attenztex ) ; attenztex = 0; } l o o p i (NUMCAUSTICS) caustictex [ i ] = NULL; }

}

VARR( causticscale , 0 , 50, 10000) ; VARR( c a u s t i c m i l l i s , 0 , 75, 1000) ; VARFP( caustics , 0 , 1 , 1 , loadcaustics ( ) ) ;

g l C o l o r 4 f v ( cur . c o l o r ) ; i f ( cur . lightmaptmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . lightmaptmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . lightmaptmu ) ;

void setupcaustics ( i n t tmu, f l o a t blend , GLfloat ∗c o l o r = NULL) { i f ( ! caustictex [ 0 ] ) loadcaustics ( true ) ; GLfloat s [ 4 ] = { 0.011 f , 0 , 0.0066 f , 0 }; GLfloat t [ 4 ] = { 0 , 0.011 f , 0.0066 f , 0 }; loopk ( 3 ) { s [ k ] ∗= 100.0 f /causticscale ; t [ k ] ∗= 100.0 f /causticscale ; } i n t tex = ( l a s t m i l l i s / c a u s t i c m i l l i s )%NUMCAUSTICS; f l o a t f r a c = f l o a t ( l a s t m i l l i s%c a u s t i c m i l l i s ) / c a u s t i c m i l l i s ; i f ( color ) color [ 3 ] = frac ; else glColor4f (1 , 1, 1, frac ) ; loopi ( 2 ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+tmu+ i ) ; glEnable ( GL TEXTURE 2D ) ; glBindTexture ( GL TEXTURE 2D, caustictex [ ( tex+ i )%NUMCAUSTICS]−>id ) ; i f ( renderpath==R FIXEDFUNCTION ) { setuptexgen ( ) ; i f ( c o l o r ) setuptmu ( tmu+ i , ! i ? ”$1 , $0 @ Ca” : ”= P ” ) ; e l s e setuptmu ( tmu+ i , ! i ? ”= T” : ”T , P @ Ca ” ) ; glTexGenfv ( GL S , GL OBJECT PLANE, s ) ; glTexGenfv ( GL T , GL OBJECT PLANE, t ) ; } } i f ( renderpath ! =R FIXEDFUNCTION ) { SETSHADER( caustic ) ; setlocalparamfv ( ” texgenS ” , SHPARAM VERTEX, 0 , s ) ; setlocalparamfv ( ” texgenT ” , SHPARAM VERTEX, 1 , t ) ; setlocalparamf ( ” frameoffset ” , SHPARAM PIXEL, 0 , blend∗(1−f r a c ) , blend∗frac , blend ) ; } } void setupTMUs ( renderstate &cur , f l o a t causticspass , bool fogpass ) { i f ( renderpath==R FIXEDFUNCTION ) { i f ( nolights ) cur . lightmaptmu = −1; e l s e i f ( maxtmus>=3) { i f ( maxtmus>=4 && ( hasTEX || hasTE4 ) && causticspass>=1) { cur . causticstmu = 0; cur . diffusetmu = 2; cur . lightmaptmu = 3; i f ( maxtmus>=5 && glowpass && ! cur . alphaing ) cur . glowtmu = 4; } e l s e i f ( glowpass && ! cur . alphaing ) cur . glowtmu = 2; } i f ( cur . glowtmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . glowtmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . glowtmu ) ; setuptmu ( cur . glowtmu , ”P + T ” , ”= Pa ” ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; } i f ( cur . causticstmu>=0) setupcaustics ( cur . causticstmu , causticspass , cur . c o l o r ) ; } else { // need to i n v a l i d a t e vertex params in case they were used somewhere e l s e f o r streaming params invalidateenvparams (SHPARAM VERTEX, 10, RESERVEDSHADERPARAMS + MAXSHADERPARAMS − 10) ; glEnableClientState (GL NORMAL ARRAY) ; glEnableClientState (GL COLOR ARRAY) ; l o o p i (8−2) { g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB+ i ) ; glEnable ( GL TEXTURE 2D ) ; } g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; setenvparamf ( ” colorparams ” , SHPARAM PIXEL, 6 , 2 , 2 , 2 , 1) ;

setuptmu ( cur . lightmaptmu , ”P ∗ T x 2” , ”= Pa ” ) ; glEnable ( GL TEXTURE 2D ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; glEnable ( GL TEXTURE 2D ) ; setuptmu ( cur . diffusetmu , cur . diffusetmu>0 ? ”P ∗ T” : ”= T ” , cur . alphaing ? ”= Ca” : ( cur . diffusetmu>0 ? ”= Ta ” : ”Ca ∗ Ta ” ) ) ; } // diffusetmu glEnableClientState (GL TEXTURE COORD ARRAY) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; } void cleanupTMUs ( renderstate &cur , f l o a t causticspass , bool fogpass ) { i f ( cur . lightmaptmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . lightmaptmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . lightmaptmu ) ; resettmu ( cur . lightmaptmu ) ; glDisable ( GL TEXTURE 2D ) ; g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; } i f ( cur . glowtmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . glowtmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . glowtmu ) ; resettmu ( cur . glowtmu ) ; i f ( cur . envscale . x ) disableenv ( cur ) ; e l s e disableglow ( cur ) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; } i f ( cur . causticstmu>=0) l o o p i ( 2 ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . causticstmu+ i ) ; resettmu ( cur . causticstmu+ i ) ; disabletexgen ( ) ; glDisable ( GL TEXTURE 2D ) ; } i f ( cur . lightmaptmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; resettmu ( cur . diffusetmu ) ; glDisable ( GL TEXTURE 2D ) ; } g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; glMatrixMode (GL TEXTURE) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; i f ( renderpath ! =R FIXEDFUNCTION ) { g l D i s a b l e C l i e n t S t a t e (GL NORMAL ARRAY) ; g l D i s a b l e C l i e n t S t a t e (GL COLOR ARRAY) ; l o o p i (8−2) { g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB+ i ) ; glDisable ( GL TEXTURE 2D ) ; } } i f ( cur . lightmaptmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ;

engine/renderva.cpp g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; glEnable ( GL TEXTURE 2D ) ; }

433

glDisable (GL TEXTURE CUBE MAP ARB) ; glEnable ( GL TEXTURE 2D ) ; }

} #define FIRSTVA ( r e f l e c t i n g ? r e f l e c t e d v a : v i s i b l e v a ) #define NEXTVA ( r e f l e c t i n g ? va−>rnext : va−>next ) s t a t i c void rendergeommultipass ( renderstate &cur , i n t pass , bool fogpass ) { cur . vbuf = 0; cur . texgendim = −1; f o r ( vtxarray ∗va = FIRSTVA ; va ; va = NEXTVA) { i f ( ! va−>texs ) continue ; i f ( refracting ) { i f ( ( r e f r a c t i n g < 0 ? va−>geommin . z > r e f l e c t z : va−>geommax. z occluded >= OCCLUDE GEOM) continue ; i f ( ishiddencube ( va−>o , va−>s i z e ) ) continue ; } else i f ( reflecting ) { i f ( va−>geommax. z occluded >= OCCLUDE GEOM) continue ; i f ( fogpass ? va−>geommax. z curvfc==VFC FOGGED) continue ; renderva ( cur , va , pass , fogpass ) ; } i f ( geombatches . length ( ) ) renderbatches ( cur , pass ) ; } VAR( oqgeom , 0 , 1 , 1) ; VAR( dbgffsm , 0 , 0 , 1) ; VAR( dbgffdl , 0 , 0 , 1) ; VAR( f f d l s c i s s o r , 0 , 1 , 1) ; s t a t i c void setupenvpass ( renderstate &cur ) { glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; g l C o l o r 4 f ( 1 , 1 , 1 , 1) ; cur . envscale = vec(−1, −1, −1) ; cur . glowtmu = 0; setuptmu ( 0 , ”= T ” , ”= Ca ” ) ; glDisable ( GL TEXTURE 2D ) ; glEnable (GL TEXTURE CUBE MAP ARB) ; glTexGeni ( GL S , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glTexGeni ( GL T , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glTexGeni ( GL R , GL TEXTURE GEN MODE, GL REFLECTION MAP ARB ) ; glEnable ( GL TEXTURE GEN S ) ; glEnable ( GL TEXTURE GEN T ) ; glEnable ( GL TEXTURE GEN R ) ; glMatrixMode (GL TEXTURE) ; glLoadMatrixf ( envmatrix . v ) ; glMatrixMode (GL MODELVIEW) ; glEnableClientState (GL NORMAL ARRAY) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; cur . specmask = f a l s e ; cur . diffusetmu = 1; setuptmu ( 1 , ”= P ” , ” Pa ∗ Ta ” ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; } s t a t i c void cleanuptexgen ( renderstate &cur ) { i f ( cur . texgendim >= 0 && cur . texgendim geommin . z > r e f l e c t z : va−>geommax. z occluded >= OCCLUDE GEOM) continue ; i f ( ishiddencube ( va−>o , va−>s i z e ) ) continue ; } else i f ( reflecting ) { i f ( va−>geommax. z distance > oqdist ) && ! insideva ( va , camera1−>o ) ) { i f ( va−>parent && va−>parent−>occluded >= OCCLUDE BB) { va−>query = NULL; va−>occluded = OCCLUDE PARENT; continue ; } va−>occluded = va−>query && va−>query−>owner == va && checkquery ( va−>query ) ? min ( va−>occluded +1 , i n t (OCCLUDE BB) ) : OCCLUDE NOTHING; va−>query = newquery ( va ) ; i f ( ( ! va−>query && zpass ) || ! va−>occluded ) va−>occluded = pvsoccluded ( va−>geommin, va−>geommax) ? OCCLUDE GEOM : OCCLUDE NOTHING; i f ( va−>occluded >= OCCLUDE GEOM) { i f ( va−>query ) { i f ( ! zpass && geombatches . length ( ) ) renderbatches ( cur , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP) ; renderquery ( cur , va−>query , va ) ; } continue ; } } else { va−>query = NULL; va−>occluded = pvsoccluded ( va−>geommin, va−>geommax) ? OCCLUDE GEOM : OCCLUDE NOTHING; i f ( va−>occluded >= OCCLUDE GEOM) continue ; } i f ( ! doZP ) blends += va−>blends ; renderva ( cur , va , doZP ? RENDERPASS Z : ( nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP) , fogpass , doOQ) ; } i f ( geombatches . length ( ) ) renderbatches ( cur , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP) ; i f ( ! cur . colormask ) { cur . colormask = true ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; } i f ( ! cur . depthmask ) { cur . depthmask = true ; glDepthMask ( GL TRUE ) ; }

434

Foundations of Videogame Programming Code Repository

bool multipassing = f a l s e ; i f ( doZP ) { glFlush ( ) ; i f ( shadowmap && hasFBO && mainpass ) { g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } rendershadowmap ( ) ; glEnableClientState ( GL VERTEX ARRAY ) ; } setupTMUs ( cur , causticspass , fogpass ) ; i f (doSM) pushshadowmap ( ) ; i f ( ! multipassing ) { multipassing = true ; glDepthFunc (GL LEQUAL) ; } cur . vbuf = 0; cur . texgendim = −1;

glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; glDisable (GL BLEND) ; glDepthMask ( GL TRUE ) ; } i f (doSM) popshadowmap ( ) ; cleanupTMUs ( cur , causticspass , fogpass ) ; i f ( foggedvas . length ( ) ) renderfoggedvas ( cur , doOQ && ! zpass ) ; i f ( renderpath==R FIXEDFUNCTION ? ( glowpass && cur . skipped ) || ( causticspass>=1 && cur . causticstmutexs || va−>occluded >= OCCLUDE GEOM) continue ; blends += va−>blends ; renderva ( cur , va , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP, fogpass ) ; } i f ( geombatches . length ( ) ) renderbatches ( cur , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP) ; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( ! va−>texs || va−>occluded < OCCLUDE GEOM) continue ; e l s e i f ( ( va−>parent && va−>parent−>occluded >= OCCLUDE BB) || ( va−>query && checkquery ( va−>query ) ) ) { va−>occluded = OCCLUDE BB; continue ; } else { va−>occluded = pvsoccluded ( va−>geommin, va−>geommax) ? OCCLUDE GEOM : OCCLUDE NOTHING; i f ( va−>occluded >= OCCLUDE GEOM) continue ; }

setupenvpass ( cur ) ; rendergeommultipass ( cur , RENDERPASS ENVMAP, fogpass ) ; cleanupenvpass ( cur ) ; } s t a t i c GLfloat zerofog [ 4 ] = { 0 , 0 , 0 , 1 }, onefog [ 4 ] = { 1 , 1 , 1 , 1 }; glGetFloatv (GL FOG COLOR, cur . f o g c o l o r ) ; i f ( renderpath==R FIXEDFUNCTION && glowpass && cur . skipped&(1=1 && cur . causticstmu blends ; renderva ( cur , va , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP, fogpass ) ;

setupcaustics ( 0 , causticspass ) ; glBlendFunc ( GL ZERO, renderpath==R FIXEDFUNCTION ? GL SRC COLOR : GL ONE MINUS SRC COLOR ) ; glFogfv (GL FOG COLOR, renderpath==R FIXEDFUNCTION ? onefog : zerofog ) ; i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ; rendergeommultipass ( cur , RENDERPASS CAUSTICS, fogpass ) ; i f ( fading ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; loopi ( 2 ) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+ i ) ; resettmu ( i ) ; i f ( renderpath==R FIXEDFUNCTION || ! i ) { resettmu ( i ) ; disabletexgen ( ) ; } i f ( i ) glDisable ( GL TEXTURE 2D ) ; } g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ;

} i f ( geombatches . length ( ) ) renderbatches ( cur , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP) ; } i f ( blends && ( renderpath ! =R FIXEDFUNCTION || ! nolights ) ) { i f ( ! multipassing ) { multipassing = true ; glDepthFunc (GL LEQUAL) ; } glDepthMask ( GL FALSE ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ; cur . vbuf = 0; cur . texgendim = −1; cur . blending = true ; i f ( cur . lightmaptmu>=0) { g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . lightmaptmu ) ; setuptmu ( cur . lightmaptmu , ”P ∗ T x 2” , ”= Ta ” ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB+cur . diffusetmu ) ; } f o r ( vtxarray ∗va = FIRSTVA ; va ; va = NEXTVA) { i f ( ! va−>blends ) continue ; i f ( refracting ) { i f ( r e f r a c t i n g < 0 ? va−>geommin . z > r e f l e c t z : va−>geommax. z o , va−>s i z e ) ) continue ; i f ( va−>occluded >= OCCLUDE GEOM) continue ; } else i f ( reflecting ) { i f ( va−>geommax. z occluded >= OCCLUDE GEOM) continue ; i f ( fogpass ? va−>geommax. z curvfc==VFC FOGGED) continue ; renderva ( cur , va , RENDERPASS LIGHTMAP BLEND, fogpass ) ; } i f ( geombatches . length ( ) ) renderbatches ( cur , RENDERPASS LIGHTMAP) ; cur . blending = f a l s e ;

} i f ( renderpath==R FIXEDFUNCTION && shadowmap && shadowmapcasters ) { glBlendFunc ( GL ZERO, GL ONE MINUS SRC COLOR ) ; glFogfv (GL FOG COLOR, zerofog ) ; pushshadowmap ( ) ; i f ( dbgffsm ) { glDisable (GL BLEND) ; glDisable ( GL TEXTURE 2D ) ; g l C o l o r 3 f ( 1 , 0 , 1) ; } rendergeommultipass ( cur , RENDERPASS SHADOWMAP, fogpass ) ; i f ( dbgffsm ) { glEnable (GL BLEND) ; glEnable ( GL TEXTURE 2D ) ; } popshadowmap ( ) ; } i f ( renderpath==R FIXEDFUNCTION && hasdynlights ) { glBlendFunc ( GL SRC ALPHA, d b g f f d l ? GL ZERO : GL ONE) ; glFogfv (GL FOG COLOR, zerofog ) ; i f ( ! attenxytex ) attenxytex = createattenxytex ( 6 4 ) ; glBindTexture ( GL TEXTURE 2D, attenxytex ) ; setuptmu ( 0 , ”= C” , ”= Ta ” ) ; setuptexgen ( ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; setuptmu ( 1 , ”= P ” , ” Pa − Ta ” ) ;

engine/renderva.cpp setuptexgen ( 1 ) ; i f ( ! attenztex ) attenztex = createattenztex ( 6 4 ) ; glBindTexture ( GL TEXTURE 1D, attenztex ) ; glEnable ( GL TEXTURE 1D ) ; g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ; cur . diffusetmu = 2; setuptmu ( 2 , ”P ∗ T x 4” , ”= Pa ” ) ; glEnable ( GL TEXTURE 2D ) ; glEnableClientState (GL TEXTURE COORD ARRAY) ; f o r ( i n t n = 0; getdynlight ( n , cur . dynlightpos , cur . dynlightradius , cur . l i g h t c o l o r ) ; n++) { cur . l i g h t c o l o r . mul( 0 . 5 f ) ; cur . c o l o r s c a l e = vec ( 1 , 1 , 1) ; g l C o l o r 3 f ( cur . l i g h t c o l o r . x , cur . l i g h t c o l o r . y , cur . l i g h t c o l o r . z) ; i f ( ffdlscissor ) { f l o a t sx1 , sy1 , sx2 , sy2 ; calcspherescissor ( cur . dynlightpos , cur . dynlightradius , sx1 , sy1 , sx2 , sy2 ) ; pushscissor ( sx1 , sy1 , sx2 , sy2 ) ; } changedynlightpos ( cur ) ; rendergeommultipass ( cur , RENDERPASS DYNLIGHT, fogpass ) ; i f ( f f d l s c i s s o r ) popscissor ( ) ;

resetbatches ( ) ; renderstate cur ; cur . alphaing = 1; glEnableClientState ( GL VERTEX ARRAY ) ; glGetFloatv (GL FOG COLOR, cur . f o g c o l o r ) ; loop ( front , 2) i f ( f r o n t || hasback ) { cur . alphaing = f r o n t +1; i f ( ! f r o n t ) glCullFace (GL FRONT) ; cur . vbuf = 0; cur . texgendim = −1; loopv ( alphavas ) renderva ( cur , alphavas [ i ] , RENDERPASS Z) ; i f ( cur . depthmask ) { cur . depthmask = f a l s e ; glDepthMask ( GL FALSE ) ; } cur . colormask = true ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL FALSE ) ; setupTMUs ( cur , 0 , fogpass ) ; glDepthFunc (GL LEQUAL) ; glEnable (GL BLEND) ; i f ( renderpath==R FIXEDFUNCTION ) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; e l s e glBlendFunc (GL ONE, GL ONE MINUS SRC ALPHA ) ; cur . vbuf = 0; cur . texgendim = −1; cur . skipped = 0; cur . c o l o r s c a l e = vec ( 1 , 1 , 1) ; loopk ( 3 ) cur . c o l o r [ k ] = 1; cur . alphascale = −1; loopv ( alphavas ) i f ( f r o n t || alphavas [ i]−>alphabacktris ) renderva ( cur , alphavas [ i ] , RENDERPASS LIGHTMAP, fogpass ) ; i f ( geombatches . length ( ) ) renderbatches ( cur , nolights ? RENDERPASS COLOR : RENDERPASS LIGHTMAP) ;

} g l D i s a b l e C l i e n t S t a t e (GL TEXTURE COORD ARRAY) ; glDisable ( GL TEXTURE 2D ) ; resettmu ( 2 ) ; cleanuptexgen ( cur ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glDisable ( GL TEXTURE 1D ) ; resettmu ( 1 ) ; disabletexgen ( 1 ) ;

cleanupTMUs ( cur , 0 , fogpass ) ;

g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; resettmu ( 0 ) ; disabletexgen ( ) ;

i f ( renderpath==R FIXEDFUNCTION && glowpass && cur . skipped ) { i f ( cur . depthmask ) { cur . depthmask = f a l s e ; glDepthMask ( GL FALSE ) ; } i f ( cur . skipped&(1a l p h a f r o n t t r i s ) continue ; i f ( refracting ) { i f ( ( r e f r a c t i n g < 0 ? va−>geommin . z > r e f l e c t z : va−>geommax. z occluded >= OCCLUDE BB) continue ; i f ( ishiddencube ( va−>o , va−>s i z e ) ) continue ; i f ( va−>occluded >= OCCLUDE GEOM && pvsoccluded ( va−>geommin, va−> geommax) ) continue ; } else i f ( reflecting ) { i f ( va−>geommax. z occluded >= OCCLUDE BB) continue ; i f ( va−>occluded >= OCCLUDE GEOM && pvsoccluded ( va−>geommin, va−> geommax) ) continue ; } i f ( fogpass ? va−>geommax. z curvfc==VFC FOGGED) continue ; alphavas . add ( va ) ; i f ( va−>alphabacktris ) hasback = true ; } i f ( alphavas . empty ( ) ) return ;

435

} glColorMask ( GL TRUE, GL TRUE, GL TRUE, fading ? GL FALSE : GL TRUE ) ;

436

Foundations of Videogame Programming Code Repository

i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; } void f i n d r e f l e c t e d v a s ( vector &vas , i n t prevvfc = VFC PART VISIBLE ) { loopv ( vas ) { vtxarray ∗va = vas [ i ] ; i f ( prevvfc >= VFC NOT VISIBLE ) va−>curvfc = prevvfc ; i f ( va−>curvfc == VFC FOGGED || va−>curvfc == PVS FOGGED || va−>o . z+ va−>s i z e o , va−>s i z e ) ) continue ; bool render = true ; i f ( va−>curvfc == VFC FULL VISIBLE ) { i f ( va−>occluded >= OCCLUDE BB) continue ; i f ( va−>occluded >= OCCLUDE GEOM) render = f a l s e ; } e l s e i f ( va−>curvfc == PVS FULL VISIBLE ) continue ; i f ( render ) { i f ( va−>curvfc >= VFC NOT VISIBLE ) va−>distance = ( i n t ) vadist ( va , camera1−>o ) ; vtxarray ∗∗vprev = &r e f l e c t e d v a , ∗vcur = r e f l e c t e d v a ; while ( vcur && va−>distance > vcur−>distance ) { vprev = &vcur−>rnext ; vcur = vcur−>rnext ; } va−>rnext = ∗vprev ; ∗vprev = va ; } i f ( va−>children . length ( ) ) f i n d r e f l e c t e d v a s ( va−>children , va−>curvfc ); } } void renderreflectedgeom ( bool causticspass , bool fogpass ) { i f ( reflecting ) { r e f l e c t e d v a = NULL; f i n d r e f l e c t e d v a s ( varoot ) ; rendergeom ( causticspass ? 1 : 0 , fogpass ) ; } e l s e rendergeom ( causticspass ? 1 : 0 , fogpass ) ; }

x t r a v e r t s += va−>e x p l i c i t s k y /3; prevskyva = va ; } i n t renderedsky = 0 , renderedexplicitsky = 0 , renderedskyfaces = 0 , renderedskyclip = INT MAX ; s t a t i c i n l i n e void updateskystats ( vtxarray ∗va ) { renderedsky += va−>sky ; renderedexplicitsky += va−>e x p l i c i t s k y ; renderedskyfaces |= va−>skyfaces&0x3F ; i f ( ! ( va−>skyfaces&0x1F ) || camera1−>o . z < va−>skyclip ) renderedskyclip = min ( renderedskyclip , va−>skyclip ) ; e l s e renderedskyclip = 0; } void renderreflectedskyvas ( vector &vas , i n t prevvfc = VFC PART VISIBLE ) { loopv ( vas ) { vtxarray ∗va = vas [ i ] ; i f ( prevvfc >= VFC NOT VISIBLE ) va−>curvfc = prevvfc ; i f ( ( va−>curvfc == VFC FULL VISIBLE && va−>occluded >= OCCLUDE BB) || va−>curvfc==PVS FULL VISIBLE ) continue ; i f ( va−>o . z+va−>s i z e o , va−>s i z e ) ) continue ; i f ( va−>sky+va−>e x p l i c i t s k y ) { updateskystats ( va ) ; renderskyva ( va ) ; } i f ( va−>children . length ( ) ) renderreflectedskyvas ( va−>children , va−> curvfc ) ; } } bool rendersky ( bool e x p l i c i t o n l y ) { prevskyva = NULL; renderedsky = renderedexplicitsky = renderedskyfaces = 0; renderedskyclip = INT MAX ; i f ( reflecting ) { renderreflectedskyvas ( varoot ) ; } e l s e f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( ( va−>occluded >= OCCLUDE BB && va−>skyfaces&0x80 ) || ! ( va−>sky+ va−>e x p l i c i t s k y ) ) continue ;

s t a t i c vtxarray ∗prevskyva = NULL;

// count possibly v i s i b l e sky even i f not a c t u a l l y rendered updateskystats ( va ) ; i f ( e x p l i c i t o n l y && ! va−>e x p l i c i t s k y ) continue ; renderskyva ( va , e x p l i c i t o n l y ) ;

void renderskyva ( vtxarray ∗va , bool e x p l i c i t o n l y = f a l s e ) { i f ( ! prevskyva || va−>vbuf ! = prevskyva−>vbuf ) { i f ( ! prevskyva ) glEnableClientState ( GL VERTEX ARRAY ) ; i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, va−>vbuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, va−>skybuf ) ; } glVertexPointer ( 3 , GL FLOAT, VTXSIZE, va−>vdata [ 0 ] . pos . v ) ; }

} i f ( prevskyva ) { g l D i s a b l e C l i e n t S t a t e ( GL VERTEX ARRAY ) ; i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, 0) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } }

drawvatris ( va , e x p l i c i t o n l y ? va−>e x p l i c i t s k y : va−>sky+va−> e x p l i c i t s k y , e x p l i c i t o n l y ? va−>skydata+va−>sky : va−>skydata ) ;

return renderedsky+renderedexplicitsky > 0; }

i f ( ! e x p l i c i t o n l y ) x t r a v e r t s += va−>sky/3;

engine/scale.h s t a t i c void FUNCNAME( halvetexture ) ( uchar ∗src , uint sw, uint sh , uint s t r i d e , uchar ∗dst )

} }

{ f o r ( uchar ∗yend = &src [ sh∗s t r i d e ] ; src < yend ; ) { f o r ( uchar ∗xend = &src [ s t r i d e ] ; src < xend ; src += 2∗BPP, dst += BPP ) { #define OP( c , n ) dst [ n ] = ( uint ( src [ n ] ) + uint ( src [ n+BPP ] ) + uint ( src [ s t r i d e +n ] ) + uint ( src [ s t r i d e +n+BPP ] ) )>>2 PIXELOP #undef OP } src += s t r i d e ;

s t a t i c void FUNCNAME( s h i f t t e x t u r e ) ( uchar ∗src , uint sw, uint sh , uint s t r i d e , uchar ∗dst , uint dw, uint dh ) { uint wfrac = sw/dw, hfrac = sh/dh , wshift = 0 , h s h i f t = 0; while ( dw12)) )>>cscale PIXELOP #undef OP i f (h) { xsrc += s t r i d e ; xend += s t r i d e ; f o r ( uint hcur = h ; −−hcur ; xsrc += s t r i d e , xend += s t r i d e ) { #define OP( c , n ) c = 0 DEFPIXEL #undef OP f o r ( const uchar ∗xcur = &xsrc [ BPP ] ; xcur < xend ; xcur += BPP ) { #define OP( c , n ) c += xcur [ n ] PIXELOP #undef OP } #define OP( c , n ) c##t += ( ( ccscale ; PIXELOP #undef OP } #define OP( c , n ) c = 0 DEFPIXEL #undef OP f o r ( const uchar ∗xcur = &xsrc [ BPP ] ; xcur < xend ; xcur += BPP ) { #define OP( c , n ) c += xcur [ n ] PIXELOP #undef OP } #define OP( c , n ) c##t += ( yhigh ∗(c + ( ( xsrc [ n]∗xlow + xend [ n ]∗ xhigh )>>12)) )>>cscale PIXELOP #undef OP } #define OP( c , n ) dst [ n ] = ( c##t ∗ area )>>dscale PIXELOP #undef OP

} src += ( hfrac−1)∗s t r i d e ; } } s t a t i c void FUNCNAME( scaletexture ) ( uchar ∗src , uint sw, uint sh , uint s t r i d e , uchar ∗dst , uint dw, uint dh ) { uint wfrac = ( sw>24))&0xFFFU ) + 1 − ( y&0xFFFU ) , yhigh = ( yn &0xFFFU ) + 1; const uchar ∗ysrc = &src [ y i∗s t r i d e ] ; f o r ( uint x = 0; x < dw; x += wfrac , dst += BPP ) { const uint xn = x + wfrac − 1 , x i = x>>12, w = ( xn>>12) − xi , xlow = ( ( w+0xFFFU )&0x1000U ) − ( x&0xFFFU ) , xhigh = ( xn&0 xFFFU ) + 1; const uchar ∗xsrc = &ysrc [ x i∗BPP ] , ∗xend = &xsrc [w∗BPP ] ; #define OP( c , n ) c##t = 0 DEFPIXEL #undef OP f o r ( const uchar ∗xcur = &xsrc [ BPP ] ; xcur < xend ; xcur += BPP ) {

437

} } } #undef #undef #undef #undef

FUNCNAME DEFPIXEL PIXELOP BPP

engine/server.cpp // server . cpp : l i t t l e more than enhanced multicaster // runs dedicated or as c l i e n t coroutine

fname = f i n d f i l e ( fname , ”w” ) ; i f ( fname ) l o g f i l e = fopen ( fname , ”w” ) ; } FILE ∗f = g e t l o g f i l e ( ) ; i f ( f ) setvbuf ( f , NULL, IOLBF , BUFSIZ ) ;

#include ” engine . h” #define LOGSTRLEN 512

}

s t a t i c FILE ∗ l o g f i l e = NULL;

void l o g o u t f ( const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; l o g o u t f v ( fmt , args ) ; va end ( args ) ; }

void c l o s e l o g f i l e ( ) { if ( logfile ) { fclose ( l o g f i l e ) ; l o g f i l e = NULL; } } FILE ∗g e t l o g f i l e ( ) { # i f d e f WIN32 return l o g f i l e ; #e l s e return l o g f i l e ? l o g f i l e : stdout ; #endif } void s e t l o g f i l e ( const char ∗fname ) { closelogfile ( ) ; i f ( fname && fname [ 0 ] ) {

s t a t i c void w r i t e l o g ( FILE ∗f i l e , const char ∗buf ) { s t a t i c uchar ubuf [ 5 1 2 ] ; i n t len = s t r l e n ( buf ) , carry = 0; while ( carry < len ) { i n t numu = encodeutf8 ( ubuf , s i z e o f ( ubuf )−1, & ( ( const uchar ∗) buf ) [ carry ] , len − carry , &carry ) ; i f ( carry >= len ) ubuf [numu++] = ’\n ’ ; f w r i t e ( ubuf , 1 , numu, f i l e ) ; } } s t a t i c void w r i t e l o g v ( FILE ∗f i l e , const char ∗fmt , v a l i s t args ) { s t a t i c char buf [LOGSTRLEN ] ;

438

Foundations of Videogame Programming Code Repository

vformatstring ( buf , fmt , args , s i z e o f ( buf ) ) ; w r i t e l o g ( f i l e , buf ) ;

i f ( ! c ) return ; switch ( c−>type ) { case ST TCPIP : nonlocalclients−−; i f ( c−>peer ) c−>peer−>data = NULL; break ; case ST LOCAL : l o c a l c l i e n t s −−; break ; case ST EMPTY : return ; } c−>type = ST EMPTY ; c−>peer = NULL; i f ( c−>i n f o ) { server : : d e l e t e c l i e n t i n f o ( c−>i n f o ) ; c−>i n f o = NULL; }

} # i f d e f STANDALONE void f a t a l ( const char ∗fmt , . . . ) { void cleanupserver ( ) ; cleanupserver ( ) ; defvformatstring ( msg, fmt , fmt ) ; i f ( l o g f i l e ) l o g o u t f (”%s ” , msg ) ; # i f d e f WIN32 MessageBox (NULL, msg, ”Cube 2: Sauerbraten f a t a l e r r o r ” , MB OK| MB SYSTEMMODAL) ; #e l s e f p r i n t f ( stderr , ” server e r r o r : %s\n” , msg ) ; #endif closelogfile ( ) ; e x i t ( EXIT FAILURE ) ; } void conoutfv ( i n t type , const char ∗fmt , v a l i s t args ) { s t r i n g sf , sp ; vformatstring ( sf , fmt , args ) ; f i l t e r t e x t ( sp , s f ) ; l o g o u t f (”%s ” , sp ) ; } void conoutf ( const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv ( CON INFO, fmt , args ) ; va end ( args ) ; } void conoutf ( i n t type , const char ∗fmt , . . . ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; conoutfv ( type , fmt , args ) ; va end ( args ) ; } #endif enum { ST EMPTY, ST LOCAL, ST TCPIP }; struct c l i e n t { i n t type ; i n t num; ENetPeer ∗peer ; s t r i n g hostname ; void ∗i n f o ; };

// server side version o f ” dynent ” type

vector c l i e n t s ; ENetHost ∗serverhost = NULL; i n t l a s t s t a t u s = 0; ENetSocket pongsock = ENET SOCKET NULL, lansock = ENET SOCKET NULL;

} void cleanupserver ( ) { i f ( serverhost ) enet host destroy ( serverhost ) ; serverhost = NULL; i f ( pongsock ! = ENET SOCKET NULL) enet socket destroy ( pongsock ) ; i f ( lansock ! = ENET SOCKET NULL) enet socket destroy ( lansock ) ; pongsock = lansock = ENET SOCKET NULL; } void process ( ENetPacket ∗packet , i n t sender , i n t chan ) ; //void d i s c o n n e c t c l i e n t ( i n t n , i n t reason ) ; i n t getservermtu ( ) { return serverhost ? serverhost−>mtu : −1; } void ∗g e t c l i e n t i n f o ( i n t i ) { return ! c l i e n t s . inrange ( i ) || c l i e n t s [ i]−> type==ST EMPTY ? NULL : c l i e n t s [ i]−>i n f o ; } ENetPeer ∗g e t c l i e n t p e e r ( i n t i ) { return c l i e n t s . inrange ( i ) && c l i e n t s [ i ]−>type==ST TCPIP ? c l i e n t s [ i]−>peer : NULL; } i n t getnumclients ( ) { return c l i e n t s . length ( ) ; } uint g e t c l i e n t i p ( i n t n ) { return c l i e n t s . inrange ( n ) && c l i e n t s [ n]−>type ==ST TCPIP ? c l i e n t s [ n]−>peer−>address . host : 0; } void sendpacket ( i n t n , i n t chan , ENetPacket ∗packet , i n t exclude ) { i f ( ndata , packet−>dataLength ) ; loopv ( c l i e n t s ) i f ( i ! = exclude && server : : allowbroadcast ( i ) ) sendpacket ( i , chan , packet ) ; return ; } switch ( c l i e n t s [ n]−>type ) { case ST TCPIP : { enet peer send ( c l i e n t s [ n]−>peer , chan , packet ) ; break ; } # i f n d e f STANDALONE case ST LOCAL : l o c a l s e r v e r t o c l i e n t ( chan , packet ) ; break ; #endif } }

i n t l o c a l c l i e n t s = 0 , n o n l o c a l c l i e n t s = 0; bool hasnonlocalclients ( ) { return n o n l o c a l c l i e n t s ! = 0 ; } bool h a s l o c a l c l i e n t s ( ) { return l o c a l c l i e n t s ! = 0 ; } c l i e n t &addclient ( i n t type ) { c l i e n t ∗c = NULL; loopv ( c l i e n t s ) i f ( c l i e n t s [ i]−>type==ST EMPTY ) { c = clients [ i ] ; break ; } if (!c) { c = new c l i e n t ; c−>num = c l i e n t s . length ( ) ; c l i e n t s . add ( c ) ; } c−>i n f o = server : : newclientinfo ( ) ; c−>type = type ; switch ( type ) { case ST TCPIP : n o n l o c a l c l i e n t s ++; break ; case ST LOCAL : l o c a l c l i e n t s ++; break ; } return ∗c ; } void d e l c l i e n t ( c l i e n t ∗c ) {

ENetPacket ∗sendf ( i n t cn , i n t chan , const char ∗format , . . . ) { i n t exclude = −1; bool r e l i a b l e = f a l s e ; i f (∗ format == ’ r ’ ) { r e l i a b l e = true ; ++format ; } packetbuf p (MAXTRANS, r e l i a b l e ? ENET PACKET FLAG RELIABLE : 0) ; v a l i s t args ; v a s t a r t ( args , format ) ; while (∗ format ) switch (∗ format ++) { case ’ x ’ : exclude = va arg ( args , i n t ) ; break ; case ’ v ’ : { i n t n = va arg ( args , i n t ) ; i n t ∗v = va arg ( args , i n t ∗) ; l o o p i ( n ) putint ( p , v [ i ] ) ; break ; } case ’ i ’ : { i n t n = i s d i g i t (∗ format ) ? ∗format++−’0’ : 1; l o o p i ( n ) putint ( p , va arg ( args , i n t ) ) ; break ; } case ’ f ’ : {

engine/server.cpp i n t n = i s d i g i t (∗ format ) ? ∗format++−’0’ : 1; l o o p i ( n ) p u t f l o a t ( p , ( f l o a t ) va arg ( args , double ) ) ; break ; } case ’ s ’ : sendstring ( va arg ( args , const char ∗) , p ) ; break ; case ’m’ : { i n t n = va arg ( args , i n t ) ; p . put ( va arg ( args , uchar ∗) , n ) ; break ; } } va end ( args ) ; ENetPacket ∗packet = p . f i n a l i z e ( ) ; sendpacket ( cn , chan , packet , exclude ) ; return packet−>referenceCount > 0 ? packet : NULL; } ENetPacket ∗s e n d f i l e ( i n t cn , i n t chan , stream ∗f i l e , const char ∗format , ...)

439

{ loopv ( c l i e n t s ) i f ( c l i e n t s [ i]−>type==ST TCPIP ) d i s c o n n e c t c l i e n t ( i , reason ) ; } void process ( ENetPacket ∗packet , i n t sender , i n t chan ) −1

// sender may be

{ packetbuf p ( packet ) ; server : : parsepacket ( sender , chan , p ) ; i f ( p . overread ( ) ) { d i s c o n n e c t c l i e n t ( sender , DISC EOP ) ; return ; } } void l o c a l c l i e n t t o s e r v e r ( i n t chan , ENetPacket ∗packet ) { c l i e n t ∗c = NULL; loopv ( c l i e n t s ) i f ( c l i e n t s [ i]−>type==ST LOCAL ) { c = c l i e n t s [ i ] ; break ; } i f ( c ) process ( packet , c−>num, chan ) ; }

{ i f ( cn < 0) { # i f d e f STANDALONE return NULL; #endif } e l s e i f ( ! c l i e n t s . inrange ( cn ) ) return NULL; i n t len = ( i n t ) min ( f i l e−>s i z e ( ) , stream : : o f f s e t ( INT MAX ) ) ; i f ( len 16read ( p . subbuf ( len ) . buf , len ) ;

# i f d e f STANDALONE bool r e s o l v e r w a i t ( const char ∗name, ENetAddress ∗address ) { return enet address set host ( address , name) >= 0; } i n t connectwithtimeout ( ENetSocket sock , const char ∗hostname , const ENetAddress &remoteaddress ) { i n t r e s u l t = enet socket connect ( sock , &remoteaddress ) ; i f ( result = 0) sendpacket ( cn , chan , packet , −1) ; # i f n d e f STANDALONE e l s e sendclientpacket ( packet , chan ) ; #endif return packet−>referenceCount > 0 ? packet : NULL; } const char ∗disconnectreason ( i n t reason ) { switch ( reason ) { case DISC EOP : return ” end o f packet ” ; case DISC LOCAL : return ” server i s in l o c a l mode ” ; case DISC KICK : return ” kicked/banned ” ; case DISC MSGERR: return ” message e r r o r ” ; case DISC IPBAN : return ” ip i s banned ” ; case DISC PRIVATE : return ” server i s in p r i v a t e mode ” ; case DISC MAXCLIENTS : return ” server FULL ” ; case DISC TIMEOUT : return ” connection timed out ” ; case DISC OVERFLOW: return ” overflow ” ; case DISC PASSWORD: return ” i n v a l i d password ” ; d e f a u l t : return NULL; } } void d i s c o n n e c t c l i e n t ( i n t n , i n t reason ) { i f ( ! c l i e n t s . inrange ( n ) || c l i e n t s [ n]−>type ! = ST TCPIP ) return ; enet peer disconnect ( c l i e n t s [ n]−>peer , reason ) ; server : : clientdisconnect ( n ) ; delclient ( clients [n ] ) ; const char ∗msg = disconnectreason ( reason ) ; string s ; i f ( msg ) formatstring ( s ) ( ” c l i e n t (%s ) disconnected because : %s ” , c l i e n t s [ n]−>hostname , msg ) ; e l s e formatstring ( s ) ( ” c l i e n t (%s ) disconnected ” , c l i e n t s [ n]−>hostname ) ; l o g o u t f (”%s ” , s ) ; server : : sendservmsg ( s ) ; } void k i c k n o n l o c a l c l i e n t s ( i n t reason )

masterout . s e t s i z e ( 0 ) ; masterin . s e t s i z e ( 0 ) ; masteroutpos = masterinpos = 0; masteraddress . host = ENET HOST ANY; masteraddress . port = ENET PORT ANY ; lastupdatemaster = 0; } SVARF( mastername , server : : defaultmaster ( ) , disconnectmaster ( ) ) ; VARF( masterport , 1 , server : : masterport ( ) , 0xFFFF , disconnectmaster ( ) ) ; ENetSocket connectmaster ( ) { i f ( ! mastername [ 0 ] ) return ENET SOCKET NULL; i f ( masteraddress . host == ENET HOST ANY ) { # i f d e f STANDALONE l o g o u t f ( ” looking up %s . . . ” , mastername ) ; #endif masteraddress . port = masterport ; i f ( ! r e s o l v e r w a i t ( mastername , &masteraddress ) ) return ENET SOCKET NULL; } ENetSocket sock = e n e t s o c k e t c r e a t e (ENET SOCKET TYPE STREAM) ; i f ( sock ! = ENET SOCKET NULL && serveraddress . host ! = ENET HOST ANY && enet socket bind ( sock , &serveraddress ) < 0) { enet socket destroy ( sock ) ; sock = ENET SOCKET NULL; } i f ( sock == ENET SOCKET NULL || connectwithtimeout ( sock , mastername , masteraddress ) < 0) { # i f d e f STANDALONE l o g o u t f ( sock==ENET SOCKET NULL ? ” could not open socket ” : ” could not connect ” ) ; #endif return ENET SOCKET NULL; }

440

Foundations of Videogame Programming Code Repository s t a t i c ENetAddress pongaddr ;

e n e t s o c k e t s e t o p t i o n ( sock , ENET SOCKOPT NONBLOCK, 1) ; return sock ; } bool requestmaster ( const char ∗req ) { i f ( mastersock == ENET SOCKET NULL) { mastersock = connectmaster ( ) ; i f ( mastersock == ENET SOCKET NULL) return f a l s e ; } masterout . put ( req , s t r l e n ( req ) ) ; return true ; } bool requestmasterf ( const char ∗fmt , . . . ) { defvformatstring ( req , fmt , fmt ) ; return requestmaster ( req ) ; } void processmasterinput ( ) { i f ( masterinpos >= masterin . length ( ) ) return ;

void sendserverinforeply ( ucharbuf &p ) { ENetBuffer buf ; buf . data = p . buf ; buf . dataLength = p . length ( ) ; enet socket send ( pongsock , &pongaddr , &buf , 1) ; } void checkserversockets ( ) // reply a l l server i n f o requests { s t a t i c ENetSocketSet sockset ; ENET SOCKETSET EMPTY( sockset ) ; ENetSocket maxsock = pongsock ; ENET SOCKETSET ADD( sockset , pongsock ) ; i f ( mastersock ! = ENET SOCKET NULL) { maxsock = max( maxsock , mastersock ) ; ENET SOCKETSET ADD( sockset , mastersock ) ; } i f ( lansock ! = ENET SOCKET NULL) { maxsock = max( maxsock , lansock ) ; ENET SOCKETSET ADD( sockset , lansock ) ; } i f ( e n e t s o c k e t s e t s e l e c t ( maxsock , &sockset , NULL, 0) = masterin . length ( ) ) { masterin . s e t s i z e ( 0 ) ; masterinpos = 0; } } void flushmasteroutput ( ) { i f ( masterout . empty ( ) ) return ; ENetBuffer buf ; buf . data = &masterout [ masteroutpos ] ; buf . dataLength = masterout . length ( ) − masteroutpos ; i n t sent = enet socket send ( mastersock , NULL, &buf , 1) ; i f ( sent >= 0) { masteroutpos += sent ; i f ( masteroutpos >= masterout . length ( ) ) { masterout . s e t s i z e ( 0 ) ; masteroutpos = 0; } } e l s e disconnectmaster ( ) ; } void flushmasterinput ( ) { i f ( masterin . length ( ) >= masterin . capacity ( ) ) masterin . reserve (4096) ; ENetBuffer buf ; buf . data = masterin . getbuf ( ) + masterin . length ( ) ; buf . dataLength = masterin . capacity ( ) − masterin . length ( ) ; i n t recv = e n e t s o c k e t r e c e i v e ( mastersock , NULL, &buf , 1) ; i f ( recv > 0) { masterin . advance ( recv ) ; processmasterinput ( ) ; } e l s e disconnectmaster ( ) ; }

VARF( maxclients , 0 , DEFAULTCLIENTS, MAXCLIENTS, { i f ( ! maxclients ) maxclients = DEFAULTCLIENTS; }) ; VAR( serveruprate , 0 , 0 , INT MAX ) ; SVAR( serverip , ” ” ) ; VARF( serverport , 0 , server : : serverport ( ) , 0xFFFF , { i f ( ! serverport ) serverport = server : : serverport ( ) ; }) ; # i f d e f STANDALONE i n t curtime = 0 , l a s t m i l l i s = 0 , t o t a l m i l l i s = 0; #endif void updatemasterserver ( ) { i f ( mastername [ 0 ] && allowupdatemaster ) requestmasterf ( ” regserv %d\n” , serverport ) ; lastupdatemaster = t o t a l m i l l i s ? t o t a l m i l l i s : 1; } uint t o t a l s e c s = 0; void updatetime ( ) { s t a t i c i n t l a s t s e c = 0; i f ( t o t a l m i l l i s − l a s t s e c >= 1000) { i n t cursecs = ( t o t a l m i l l i s − l a s t s e c ) / 1000; t o t a l s e c s += cursecs ; l a s t s e c += cursecs ∗ 1000; } } void s e r v e r s l i c e ( bool dedicated , uint timeout ) // main server update , c a l l e d from main loop in sp , or from below in dedicated server { i f ( ! serverhost ) { server : : serverupdate ( ) ; server : : sendpackets ( ) ; return ; } // below i s network only

engine/server.cpp i f ( dedicated ) { i n t m i l l i s = ( i n t ) e n e t t i m e g e t ( ) , elapsed = m i l l i s − t o t a l m i l l i s ; s t a t i c i n t timeerr = 0; i n t scaledtime = server : : scaletime ( elapsed ) + timeerr ; curtime = scaledtime /100; timeerr = scaledtime%100; i f ( server : : ispaused ( ) ) curtime = 0; l a s t m i l l i s += curtime ; totalmillis = millis ; updatetime ( ) ; } server : : serverupdate ( ) ; flushmasteroutput ( ) ; checkserversockets ( ) ; i f ( ! lastupdatemaster || t o t a l m i l l i s−lastupdatemaster>60∗60∗1000) // send a l i v e si g n a l to masterserver every hour o f uptime updatemasterserver ( ) ; i f ( t o t a l m i l l i s−laststatus >60∗1000) useful f o r server ops

// display bandwidth stats ,

{ laststatus = t o t a l m i l l i s ; i f ( n o n l o c a l c l i e n t s || serverhost−>totalSentData || serverhost−> totalReceivedData ) l o g o u t f ( ” status : %d remote c l i e n t s , %.1 f send , %.1 f rec (K/sec ) ” , nonlocalclients , serverhost−> totalSentData /60.0 f /1024, serverhost−>totalReceivedData /60.0 f /1024) ; serverhost−>totalSentData = serverhost−>totalReceivedData = 0; } ENetEvent event ; bool serviced = f a l s e ; while ( ! serviced ) { i f ( enet host check events ( serverhost , &event ) address , hn, s i z e o f ( hn ) ) ==0) ? hn : ”unknown ” ) ; l o g o u t f ( ” c l i e n t connected (%s ) ” , c . hostname ) ; i n t reason = server : : clientconnect ( c .num, c . peer−>address . host ) ; i f ( reason ) d i s c o n n e c t c l i e n t ( c .num, reason ) ; break ; } case ENET EVENT TYPE RECEIVE : { c l i e n t ∗c = ( c l i e n t ∗) event . peer−>data ; i f ( c ) process ( event . packet , c−>num, event . channelID ) ; i f ( event . packet−>referenceCount ==0) enet packet destroy ( event . packet ) ; break ; } case ENET EVENT TYPE DISCONNECT: { c l i e n t ∗c = ( c l i e n t ∗) event . peer−>data ; i f ( ! c ) break ; l o g o u t f ( ” disconnected c l i e n t (%s ) ” , c−>hostname ) ; server : : clientdisconnect ( c−>num) ; delclient ( c ) ; break ; } default : break ; } } i f ( server : : sendpackets ( ) ) e n e t h o s t f l u s h ( serverhost ) ; } void flushserver ( bool f o r c e ) { i f ( server : : sendpackets ( f o r c e ) && serverhost ) e n e t h o s t f l u s h ( serverhost ) ; } # i f n d e f STANDALONE void localdisconnect ( bool cleanup ) { bool disconnected = f a l s e ; loopv ( c l i e n t s ) i f ( c l i e n t s [ i]−>type==ST LOCAL ) {

441

server : : localdisconnect ( i ) ; delclient ( clients [ i ] ) ; disconnected = true ; } i f ( ! disconnected ) return ; game : : gamedisconnect ( cleanup ) ; mainmenu = 1; } void localconnect ( ) { c l i e n t &c = addclient ( ST LOCAL ) ; copystring ( c . hostname , ” l o c a l ” ) ; game : : gameconnect ( f a l s e ) ; server : : localconnect ( c .num) ; } #endif # i f d e f WIN32 #include ” s h e l l a p i . h” #define IDI ICON1 1 static static static static static static static static struct static

s t r i n g apptip = ” ” ; HINSTANCE appinstance = NULL; ATOM wndclass = 0; HWND appwindow = NULL, conwindow = NULL; HICON appicon = NULL; HMENU appmenu = NULL; HANDLE outhandle = NULL; const i n t MAXLOGLINES = 200; l o g l i n e { i n t len ; char buf [LOGSTRLEN ] ; }; ringbuf l o g l i n e s ;

s t a t i c void cleanupsystemtray ( ) { NOTIFYICONDATA nid ; memset(&nid , 0 , s i z e o f ( nid ) ) ; nid . cbSize = s i z e o f ( nid ) ; nid .hWnd = appwindow ; nid . uID = IDI ICON1 ; S h e l l N o t i f y I c o n ( NIM DELETE, &nid ) ; } s t a t i c bool setupsystemtray ( UINT uCallbackMessage ) { NOTIFYICONDATA nid ; memset(&nid , 0 , s i z e o f ( nid ) ) ; nid . cbSize = s i z e o f ( nid ) ; nid .hWnd = appwindow ; nid . uID = IDI ICON1 ; nid . uCallbackMessage = uCallbackMessage ; nid . uFlags = NIF MESSAGE | NIF ICON | NIF TIP ; nid . hIcon = appicon ; strcpy ( nid . szTip , apptip ) ; i f ( S h e l l N o t i f y I c o n ( NIM ADD, &nid ) ! = TRUE) return f a l s e ; a t e x i t ( cleanupsystemtray ) ; return true ; } #if 0 s t a t i c bool modifysystemtray ( ) { NOTIFYICONDATA nid ; memset(&nid , 0 , s i z e o f ( nid ) ) ; nid . cbSize = s i z e o f ( nid ) ; nid .hWnd = appwindow ; nid . uID = IDI ICON1 ; nid . uFlags = NIF TIP ; strcpy ( nid . szTip , apptip ) ; return S h e l l N o t i f y I c o n ( NIM MODIFY, &nid ) == TRUE; } #endif s t a t i c void cleanupwindow ( ) { i f ( ! appinstance ) return ; i f (appmenu) { DestroyMenu (appmenu) ; appmenu = NULL; } i f ( wndclass ) { UnregisterClass (MAKEINTATOM( wndclass ) , appinstance ) ; wndclass = 0; } } s t a t i c BOOL WINAPI consolehandler (DWORD dwCtrlType ) { switch ( dwCtrlType ) {

442

Foundations of Videogame Programming Code Repository

case CTRL C EVENT : case CTRL BREAK EVENT: case CTRL CLOSE EVENT: e x i t ( EXIT SUCCESS ) ; return TRUE;

break ; case MENU HIDECONSOLE: ShowWindow ( conwindow , SW HIDE) ; ModifyMenu ( appmenu, 0 , MF BYPOSITION|MF STRING, MENU SHOWCONSOLE, ”Show Console ” ) ; break ; case MENU EXIT: PostMessage (hWnd, WM CLOSE, 0 , 0) ; break ;

} return FALSE; } s t a t i c void w r i t e l i n e ( l o g l i n e &l i n e ) { s t a t i c uchar ubuf [ 5 1 2 ] ; i n t len = s t r l e n ( l i n e . buf ) , carry = 0; while ( carry < len ) { i n t numu = encodeutf8 ( ubuf , s i z e o f ( ubuf ) , & ( ( uchar ∗) l i n e . buf ) [ carry ] , len − carry , &carry ) ; DWORD written = 0; WriteConsole ( outhandle , ubuf , numu, &written , NULL) ; } } s t a t i c void setupconsole ( ) { i f ( conwindow ) return ; i f ( ! AllocConsole ( ) ) return ; SetConsoleCtrlHandler ( consolehandler , TRUE) ; conwindow = GetConsoleWindow ( ) ; SetConsoleTitle ( apptip ) ; //SendMessage ( conwindow , WM SETICON, ICON SMALL, (LPARAM) appicon ) ; SendMessage ( conwindow , WM SETICON, ICON BIG , (LPARAM) appicon ) ; outhandle = GetStdHandle (STD OUTPUT HANDLE) ; CONSOLE SCREEN BUFFER INFO coninfo ; GetConsoleScreenBufferInfo ( outhandle , &coninfo ) ; coninfo . dwSize . Y = MAXLOGLINES; SetConsoleScreenBufferSize ( outhandle , coninfo . dwSize ) ; SetConsoleCP ( CP UTF8 ) ; SetConsoleOutputCP ( CP UTF8 ) ; loopv ( l o g l i n e s ) w r i t e l i n e ( l o g l i n e s [ i ] ) ; }

} return 0; case WM CLOSE: PostQuitMessage ( 0 ) ; return 0; } return DefWindowProc (hWnd, uMsg, wParam, lParam ) ; } s t a t i c void setupwindow ( const char ∗ t i t l e ) { copystring ( apptip , t i t l e ) ; //appinstance = GetModuleHandle (NULL) ; i f ( ! appinstance ) f a t a l ( ” f a i l e d g e t t i n g application instance ” ) ; appicon = LoadIcon ( appinstance , MAKEINTRESOURCE( IDI ICON1 ) ) ; / / ( HICON) LoadImage ( appinstance , MAKEINTRESOURCE( IDI ICON1 ) , IMAGE ICON, 0 , 0 , LR DEFAULTSIZE ) ; i f ( ! appicon ) f a t a l ( ” f a i l e d loading icon ” ) ; appmenu = CreatePopupMenu ( ) ; i f ( ! appmenu) f a t a l ( ” f a i l e d creating popup menu” ) ; AppendMenu ( appmenu, MF STRING, MENU OPENCONSOLE, ”Open Console ” ) ; AppendMenu ( appmenu, MF SEPARATOR, 0 , NULL) ; AppendMenu ( appmenu, MF STRING, MENU EXIT, ” Exit ” ) ; //SetMenuDefaultItem ( appmenu, 0 , FALSE ) ; WNDCLASS wc ; memset(&wc, 0 , s i z e o f ( wc ) ) ; wc . hCursor = NULL; //LoadCursor (NULL, IDC ARROW) ; wc . hIcon = appicon ; wc . lpszMenuName = NULL; wc . lpszClassName = t i t l e ; wc . s t y l e = 0; wc . hInstance = appinstance ; wc . lpfnWndProc = handlemessages ; wc . cbWndExtra = 0; wc . cbClsExtra = 0; wndclass = RegisterClass (&wc ) ; i f ( ! wndclass ) f a t a l ( ” f a i l e d r e g i s t e r i n g window class ” ) ;

enum { MENU OPENCONSOLE = 0 , MENU SHOWCONSOLE, MENU HIDECONSOLE, MENU EXIT }; s t a t i c LRESULT CALLBACK handlemessages (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch (uMsg) { case WM APP: SetForegroundWindow (hWnd) ; switch ( lParam ) { //case WMMOUSEMOVE: // break ; case WM LBUTTONUP: case WM RBUTTONUP: { POINT pos ; GetCursorPos(&pos ) ; TrackPopupMenu ( appmenu, TPM CENTERALIGN| TPM BOTTOMALIGN| TPM RIGHTBUTTON, pos . x , pos . y , 0 , hWnd, NULL) ; PostMessage (hWnd, WM NULL, 0 , 0) ; break ; } } return 0; case WMCOMMAND: switch (LOWORD( wParam ) ) { case MENU OPENCONSOLE: setupconsole ( ) ; i f ( conwindow ) ModifyMenu ( appmenu, 0 , MF BYPOSITION| MF STRING, MENU HIDECONSOLE, ” Hide Console ” ) ; break ; case MENU SHOWCONSOLE: ShowWindow ( conwindow , SWSHOWNORMAL) ; ModifyMenu ( appmenu, 0 , MF BYPOSITION|MF STRING, MENU HIDECONSOLE, ” Hide Console ” ) ;

appwindow = CreateWindow (MAKEINTATOM( wndclass ) , t i t l e , 0 , CW USEDEFAULT, CW USEDEFAULT, 0 , 0 , HWND MESSAGE, NULL, appinstance , NULL) ; i f ( ! appwindow ) f a t a l ( ” f a i l e d creating window ” ) ; a t e x i t ( cleanupwindow ) ; i f ( ! setupsystemtray (WM APP) ) f a t a l ( ” f a i l e d adding to system tray ” ) ; } s t a t i c char ∗parsecommandline ( const char ∗src , vector &args ) { char ∗buf = new char [ s t r l e n ( src ) + 1 ] , ∗dst = buf ; for ( ; ; ) { while ( isspace (∗ src ) ) src ++; i f ( ! ∗ src ) break ; args . add ( dst ) ; f o r ( bool quoted = f a l s e ; ∗src && ( quoted || ! isspace (∗ src ) ) ; src ++) { i f (∗ src ! = ’ ” ’ ) ∗dst++ = ∗src ; e l s e i f ( dst > buf && src[−1] == ’\\ ’) dst[−1] = ’” ’; e l s e quoted = ! quoted ; } ∗dst++ = ’ \ 0 ’ ; } args . add (NULL) ; return buf ; }

i n t WINAPI WinMain (HINSTANCE hInst , HINSTANCE hPrev , LPSTR szCmdLine , i n t sw ) { vector args ; char ∗buf = parsecommandline ( GetCommandLine ( ) , args ) ; appinstance = hInst ; # i f d e f STANDALONE i n t standalonemain ( i n t argc , char ∗∗argv ) ; i n t status = standalonemain ( args . length ( ) −1, args . getbuf ( ) ) ;

engine/server.cpp #define main standalonemain #e l s e SDL SetModuleHandle ( hInst ) ; i n t status = SDL main ( args . length ( ) −1, args . getbuf ( ) ) ; #endif d e l e t e [ ] buf ; e x i t ( status ) ; return 0; } void l o g o u t f v ( const char ∗fmt , v a l i s t args ) { i f ( appwindow ) { l o g l i n e &l i n e = l o g l i n e s . add ( ) ; vformatstring ( l i n e . buf , fmt , args , s i z e o f ( l i n e . buf ) ) ; i f ( l o g f i l e ) w r i t e l o g ( l o g f i l e , l i n e . buf ) ; l i n e . len = min ( s t r l e n ( l i n e . buf ) , s i z e o f ( l i n e . buf )−2) ; l i n e . buf [ l i n e . len ++] = ’\n ’ ; l i n e . buf [ l i n e . len ] = ’ \ 0 ’ ; i f ( outhandle ) w r i t e l i n e ( l i n e ) ; } e l s e i f ( l o g f i l e ) w r i t e l o g v ( l o g f i l e , fmt , args ) ; } #e l s e void l o g o u t f v ( const char ∗fmt , v a l i s t args ) { FILE ∗f = g e t l o g f i l e ( ) ; i f ( f ) w r i t e l o g v ( f , fmt , args ) ; }

443

pongsock = e n e t s o c k e t c r e a t e (ENET SOCKET TYPE DATAGRAM) ; i f ( pongsock ! = ENET SOCKET NULL && enet socket bind ( pongsock , &address ) < 0) { enet socket destroy ( pongsock ) ; pongsock = ENET SOCKET NULL; } i f ( pongsock == ENET SOCKET NULL) return s e r v e r e r r o r ( dedicated , ” could not create server i n f o socket ” ) ; e l s e e n e t s o c k e t s e t o p t i o n ( pongsock , ENET SOCKOPT NONBLOCK, 1) ; address . port = server : : l a n i n f o p o r t ( ) ; lansock = e n e t s o c k e t c r e a t e (ENET SOCKET TYPE DATAGRAM) ; i f ( lansock ! = ENET SOCKET NULL && ( e n e t s o c k e t s e t o p t i o n ( lansock , ENET SOCKOPT REUSEADDR, 1) < 0 || enet socket bind ( lansock , & address ) < 0) ) { enet socket destroy ( lansock ) ; lansock = ENET SOCKET NULL; } i f ( lansock == ENET SOCKET NULL) conoutf (CON WARN, ”WARNING: could not create LAN server i n f o socket ” ) ; e l s e e n e t s o c k e t s e t o p t i o n ( lansock , ENET SOCKOPT NONBLOCK, 1) ; return true ; } void i n i t s e r v e r ( bool l i s t e n , bool dedicated ) { i f ( dedicated ) { # i f d e f WIN32 setupwindow ( ” Cube 2: Sauerbraten server ” ) ; #endif }

#endif e x e c f i l e ( ” server−i n i t . c f g ” , f a l s e ) ; s t a t i c bool dedicatedserver = f a l s e ; i f ( l i s t e n ) s e t u p l i s t e n s e r v e r ( dedicated ) ; bool isdedicatedserver ( ) { return dedicatedserver ; } server : : s e r v e r i n i t ( ) ; void rundedicatedserver ( ) { dedicatedserver = true ; l o g o u t f ( ” dedicated server started , waiting f o r c l i e n t s . . . ” ) ; # i f d e f WIN32 S e t P r i o r i t y C l a s s ( GetCurrentProcess ( ) , HIGH PRIORITY CLASS ) ; for ( ; ; ) { MSG msg; while ( PeekMessage(&msg, NULL, 0 , 0 , PM REMOVE) ) { i f ( msg. message == WM QUIT) e x i t ( EXIT SUCCESS ) ; TranslateMessage(&msg ) ; DispatchMessage(&msg ) ; } s e r v e r s l i c e ( true , 5) ; } #e l s e f o r ( ; ; ) s e r v e r s l i c e ( true , 5) ; #endif dedicatedserver = f a l s e ; }

i f ( listen ) { updatemasterserver ( ) ; i f ( dedicated ) rundedicatedserver ( ) ; // never returns # i f n d e f STANDALONE e l s e conoutf ( ” l i s t e n server started ” ) ; #endif } } # i f n d e f STANDALONE void s t a r t l i s t e n s e r v e r ( i n t ∗usemaster ) { i f ( serverhost ) { conoutf (CON ERROR, ” l i s t e n server i s already running ” ) ; return ; } allowupdatemaster = ∗usemaster>0 ? 1 : 0; i f ( ! s e t u p l i s t e n s e r v e r ( f a l s e ) ) return ; updatemasterserver ( ) ;

bool s e r v e r e r r o r ( bool dedicated , const char ∗desc ) { # i f n d e f STANDALONE i f ( ! dedicated ) { conoutf (CON ERROR, ”%s ” , desc ) ; cleanupserver ( ) ; } else #endif f a t a l (”%s ” , desc ) ; return f a l s e ; }

conoutf ( ” l i s t e n server started f o r %d c l i e n t s%s ” , maxclients , allowupdatemaster ? ” and l i s t e d with master server ” : ” ” ) ; } COMMAND( s t a r t l i s t e n s e r v e r , ” i ” ) ;

bool s e t u p l i s t e n s e r v e r ( bool dedicated ) { ENetAddress address = { ENET HOST ANY, enet uint16 ( serverport 0 ? serverport : −1) ;

conoutf ( ” l i s t e n server stopped ” ) ; } COMMAND( s t o p l i s t e n s e r v e r , ” ” ) ; #endif

void s t o p l i s t e n s e r v e r ( ) { i f ( ! serverhost ) { conoutf (CON ERROR, ” l i s t e n server i s not running ” ) ; return ; } kicknonlocalclients ( ) ; e n e t h o s t f l u s h ( serverhost ) ; cleanupserver ( ) ;

bool serveroption ( char ∗opt ) { switch ( opt [ 1 ] ) { case ’ u ’ : setvar ( ” serveruprate ” , a t o i ( opt +2) ) ; return true ; case ’ c ’ : setvar ( ” maxclients ” , a t o i ( opt +2) ) ; return true ; case ’ i ’ : setsvar ( ” s e r v e r i p ” , opt +2) ; return true ; case ’ j ’ : setvar ( ” serverport ” , a t o i ( opt +2) ) ; return true ; case ’m’ : setsvar ( ” mastername ” , opt +2) ; setvar ( ” updatemaster ” , mastername [ 0 ] ? 1 : 0) ; return true ; # i f d e f STANDALONE case ’ q ’ : l o g o u t f ( ” Using home d i r e c t o r y : %s ” , opt ) ; sethomedir ( opt +2) ; return true ;

444

Foundations of Videogame Programming Code Repository

case ’ k ’ : l o g o u t f ( ” Adding package d i r e c t o r y : %s ” , opt ) ; addpackagedir ( opt +2) ; return true ; case ’ g ’ : l o g o u t f ( ” Setting l o g f i l e : %s ” , opt ) ; s e t l o g f i l e ( opt +2) ; return true ;

{ s e t l o g f i l e (NULL) ; i f ( e n e t i n i t i a l i z e ( ) thread ) { SDL LockMutex ( resolvermutex ) ; while ( r e s o l v e r q u e r i e s . empty ( ) ) SDL CondWait ( querycond , resolvermutex ) ; rt−>query = r e s o l v e r q u e r i e s . pop ( ) ; rt−>starttime = t o t a l m i l l i s ; SDL UnlockMutex ( resolvermutex ) ;

} void resolverquery ( const char ∗name) { i f ( resolverthreads . empty ( ) ) r e s o l v e r i n i t ( ) ; SDL LockMutex ( resolvermutex ) ; r e s o l v e r q u e r i e s . add (name) ; SDL CondSignal ( querycond ) ; SDL UnlockMutex ( resolvermutex ) ; }

ENetAddress address = { ENET HOST ANY, ENET PORT ANY }; enet address set host (&address , rt−>query ) ; SDL LockMutex ( resolvermutex ) ; i f ( rt−>query && thread == rt−>thread ) { r e s o l v e r r e s u l t &r r = r e s o l v e r r e s u l t s . add ( ) ; r r . query = rt−>query ; r r . address = address ; rt−>query = NULL; rt−>starttime = 0; SDL CondSignal ( resultcond ) ; } SDL UnlockMutex ( resolvermutex ) ; } return 0; } void r e s o l v e r i n i t ( ) { resolvermutex = SDL CreateMutex ( ) ; querycond = SDL CreateCond ( ) ; resultcond = SDL CreateCond ( ) ; SDL LockMutex ( resolvermutex ) ; l o o p i (RESOLVERTHREADS) { resolverthread &r t = resolverthreads . add ( ) ; r t . query = NULL;

bool resolvercheck ( const char ∗∗name, ENetAddress ∗address ) { bool resolved = f a l s e ; SDL LockMutex ( resolvermutex ) ; i f ( ! r e s o l v e r r e s u l t s . empty ( ) ) { r e s o l v e r r e s u l t &r r = r e s o l v e r r e s u l t s . pop ( ) ; ∗name = r r . query ; address−>host = r r . address . host ; resolved = true ; } e l s e loopv ( resolverthreads ) { resolverthread &r t = resolverthreads [ i ] ; i f ( r t . query && t o t a l m i l l i s − r t . starttime > RESOLVERLIMIT ) { resolverstop ( rt ) ; ∗name = r t . query ; resolved = true ; } } SDL UnlockMutex ( resolvermutex ) ; return resolved ; } bool r e s o l v e r w a i t ( const char ∗name, ENetAddress ∗address ) { i f ( resolverthreads . empty ( ) ) r e s o l v e r i n i t ( ) ;

engine/serverbrowser.cpp for ( ; ; ) { i f ( ! SDL CondWaitTimeout ( conncond , connmutex , 250) ) { i f ( cd . result CONNLIMIT ) break ; }

defformatstring ( t e x t ) ( ” r e s o l v i n g %s . . . ( esc to abort ) ” , name) ; renderprogress ( 0 , t e x t ) ; SDL LockMutex ( resolvermutex ) ; r e s o l v e r q u e r i e s . add (name) ; SDL CondSignal ( querycond ) ; i n t starttime = SDL GetTicks ( ) , timeout = 0; bool resolved = f a l s e ; for ( ; ; ) { SDL CondWaitTimeout ( resultcond , resolvermutex , 250) ; loopv ( r e s o l v e r r e s u l t s ) i f ( r e s o l v e r r e s u l t s [ i ] . query == name) { address−>host = r e s o l v e r r e s u l t s [ i ] . address . host ; r e s o l v e r r e s u l t s . remove ( i ) ; resolved = true ; break ; } i f ( resolved ) break ; timeout = SDL GetTicks ( ) − starttime ; renderprogress ( min ( f l o a t ( timeout ) /RESOLVERLIMIT, 1.0 f ) , t e x t ) ; i f ( interceptkey (SDLK ESCAPE) ) timeout = RESOLVERLIMIT + 1; i f ( timeout > RESOLVERLIMIT ) break ; } i f ( ! resolved && timeout > RESOLVERLIMIT ) { loopv ( resolverthreads ) { resolverthread &r t = resolverthreads [ i ] ; i f ( r t . query == name) { r e s o l v e r s t o p ( r t ) ; break ; } } } SDL UnlockMutex ( resolvermutex ) ; return resolved ; } SDL Thread ∗connthread = NULL; SDL mutex ∗connmutex = NULL; SDL cond ∗conncond = NULL; struct connectdata { ENetSocket sock ; ENetAddress address ; int result ; }; // do t h i s in a thread to prevent timeouts // could set timeouts on sockets , but t h i s i s more r e l i a b l e and g i v e s more control i n t connectthread ( void ∗data ) { SDL LockMutex ( connmutex ) ; i f ( ! connthread || SDL GetThreadID ( connthread ) ! = SDL ThreadID ( ) ) { SDL UnlockMutex ( connmutex ) ; return 0; } connectdata cd = ∗( connectdata ∗) data ; SDL UnlockMutex ( connmutex ) ;

445

/∗ thread w i l l a c t u a l l y timeout eventually i f i t s s t i l l t r y i n g to connect ∗ so j u s t leave i t ( and l e t i t destroy socket ) instead o f causing problems on some platforms by k i l l i n g i t ∗/ connthread = NULL; SDL UnlockMutex ( connmutex ) ; return cd . r e s u l t ; } enum { UNRESOLVED = 0 , RESOLVING, RESOLVED }; struct s e r v e r i n f o { enum { WAITING = INT MAX , MAXPINGS = 3 }; s t r i n g name, map, sdesc ; i n t port , numplayers , resolved , ping , lastping , nextping ; i n t pings [MAXPINGS ] ; vector a t t r ; ENetAddress address ; bool keep ; const char ∗password ; serverinfo ( ) : port(−1) , numplayers ( 0 ) , resolved (UNRESOLVED) , keep ( f a l s e ) , password (NULL) { name[ 0 ] = map[ 0 ] = sdesc [ 0 ] = ’ \ 0 ’ ; clearpings ( ) ; } ˜ serverinfo ( ) { DELETEA( password ) ; } void clearpings ( ) { ping = WAITING ; loopk (MAXPINGS) pings [ k ] = WAITING ; nextping = 0; l a s t p i n g = −1; }

i n t r e s u l t = enet socket connect ( cd . sock , &cd . address ) ; SDL LockMutex ( connmutex ) ; i f ( ! connthread || SDL GetThreadID ( connthread ) ! = SDL ThreadID ( ) ) { enet socket destroy ( cd . sock ) ; SDL UnlockMutex ( connmutex ) ; return 0; } ( ( connectdata ∗) data )−>r e s u l t = r e s u l t ; SDL CondSignal ( conncond ) ; SDL UnlockMutex ( connmutex ) ; return 0; } #define CONNLIMIT 20000 i n t connectwithtimeout ( ENetSocket sock , const char ∗hostname , const ENetAddress &address ) { defformatstring ( t e x t ) ( ” connecting to %s . . . ( esc to abort ) ” , hostname ) ; renderprogress ( 0 , t e x t ) ; i f ( ! connmutex ) connmutex = SDL CreateMutex ( ) ; i f ( ! conncond ) conncond = SDL CreateCond ( ) ; SDL LockMutex ( connmutex ) ; connectdata cd = { sock , address , −1 }; connthread = SDL CreateThread ( connectthread , &cd ) ; i n t starttime = SDL GetTicks ( ) , timeout = 0;

void cleanup ( ) { clearpings ( ) ; attr . setsize (0) ; numplayers = 0; } void r e s e t ( ) { l a s t p i n g = −1; } void checkdecay ( i n t decay ) { i f ( l a s t p i n g >= 0 && t o t a l m i l l i s − l a s t p i n g >= decay ) cleanup ( ) ; i f ( l a s t p i n g < 0) l a s t p i n g = t o t a l m i l l i s ; } void calcping ( ) { i n t numpings = 0 , t o t a l p i n g s = 0; loopk (MAXPINGS) i f ( pings [ k ] ! = WAITING ) { t o t a l p i n g s += pings [ k ] ; numpings++; } ping = numpings ? t o t a l p i n g s /numpings : WAITING ; } void addping ( i n t r t t , i n t m i l l i s ) { i f ( m i l l i s >= l a s t p i n g ) l a s t p i n g = −1;

446

Foundations of Videogame Programming Code Repository

pings [ nextping ] = r t t ; nextping = ( nextping +1)%MAXPINGS; calcping ( ) ;

ENetBuffer buf ; uchar ping [MAXTRANS] ; ucharbuf p ( ping , s i z e o f ( ping ) ) ; putint ( p , t o t a l m i l l i s ) ;

} s t a t i c bool compare ( s e r v e r i n f o ∗a , s e r v e r i n f o ∗b ) { bool ac = server : : servercompatible ( a−>name, a−>sdesc , a−>map, a−> ping , a−>a t t r , a−>numplayers ) , bc = server : : servercompatible ( b−>name, b−>sdesc , b−>map, b−> ping , b−>a t t r , b−>numplayers ) ; i f ( ac > bc ) return true ; i f ( bc > ac ) return f a l s e ; i f ( a−>keep > b−>keep ) return true ; i f ( a−>keep < b−>keep ) return f a l s e ; i f ( a−>numplayers < b−>numplayers ) return f a l s e ; i f ( a−>numplayers > b−>numplayers ) return true ; i f ( a−>ping > b−>ping ) return f a l s e ; i f ( a−>ping < b−>ping ) return true ; i n t cmp = strcmp ( a−>name, b−>name) ; i f (cmp ! = 0) return cmp < 0; i f ( a−>port < b−>port ) return true ; i f ( a−>port > b−>port ) return f a l s e ; return f a l s e ; }

s t a t i c i n t l a s t p i n g = 0; i f ( l a s t p i n g >= servers . length ( ) ) l a s t p i n g = 0; l o o p i ( maxservpings ? min ( servers . length ( ) , maxservpings ) : servers . length ( ) ) { s e r v e r i n f o &s i = ∗servers [ l a s t p i n g ] ; i f (++ l a s t p i n g >= servers . length ( ) ) l a s t p i n g = 0; i f ( s i . address . host == ENET HOST ANY ) continue ; buf . data = ping ; buf . dataLength = p . length ( ) ; enet socket send ( pingsock , &s i . address , &buf , 1) ; s i . checkdecay ( servpingdecay ) ; } i f ( searchlan ) { ENetAddress address ; address . host = ENET HOST BROADCAST; address . port = server : : l a n i n f o p o r t ( ) ; buf . data = ping ; buf . dataLength = p . length ( ) ; enet socket send ( pingsock , &address , &buf , 1) ; } lastinfo = totalmillis ;

}; vector servers ; ENetSocket pingsock = ENET SOCKET NULL; i n t l a s t i n f o = 0; s t a t i c s e r v e r i n f o ∗newserver ( const char ∗name, i n t port , uint ip = ENET HOST ANY ) { s e r v e r i n f o ∗s i = new s e r v e r i n f o ; si−>address . host = ip ; si−>address . port = server : : s e r v e r i n f o p o r t ( port ) ; i f ( ip ! =ENET HOST ANY ) si−>resolved = RESOLVED; si−>port = port ; i f (name) copystring ( si−>name, name) ; e l s e i f ( ip==ENET HOST ANY || e n e t a d d r e s s g e t h o s t i p (& si−>address , si −>name, s i z e o f ( si−>name) ) < 0) { delete si ; return NULL;

} void checkresolver ( ) { i n t r e s o l v i n g = 0; loopv ( servers ) { s e r v e r i n f o &s i = ∗servers [ i ] ; i f ( s i . resolved == RESOLVED) continue ; i f ( s i . address . host == ENET HOST ANY ) { i f ( s i . resolved == UNRESOLVED) { s i . resolved = RESOLVING; resolverquery ( s i .name) ; } r e s o l v i n g ++; } } i f ( ! r e s o l v i n g ) return ; const char ∗name = NULL; for ( ; ; ) { ENetAddress addr = { ENET HOST ANY, ENET PORT ANY }; i f ( ! resolvercheck (&name, &addr ) ) break ; loopv ( servers ) { s e r v e r i n f o &s i = ∗servers [ i ] ; i f (name == s i .name) { s i . resolved = RESOLVED; s i . address . host = addr . host ; break ; } } }

} servers . add ( s i ) ; return s i ; } void addserver ( const char ∗name, i n t port , const char ∗password , bool keep ) { i f ( port name, name) || s−>port ! = port ) continue ; i f ( password && ( ! s−>password || strcmp ( s−>password , password ) ) ) { DELETEA( s−>password ) ; s−>password = newstring ( password ) ; } i f ( keep && ! s−>keep ) s−>keep = true ; return ; } s e r v e r i n f o ∗s = newserver ( name, port ) ; i f ( ! s ) return ; i f ( password ) s−>password = newstring ( password ) ; s−>keep = keep ; } VARP( searchlan , 0 , 0 , 1) ; VARP( servpingrate , 1000, 5000, 60000) ; VARP( servpingdecay , 1000, 15000, 60000) ; VARP( maxservpings , 0 , 10, 1000) ; void pingservers ( ) { i f ( pingsock == ENET SOCKET NULL) { pingsock = e n e t s o c k e t c r e a t e (ENET SOCKET TYPE DATAGRAM) ; i f ( pingsock == ENET SOCKET NULL) { lastinfo = totalmillis ; return ; } e n e t s o c k e t s e t o p t i o n ( pingsock , ENET SOCKOPT NONBLOCK, 1) ; e n e t s o c k e t s e t o p t i o n ( pingsock , ENET SOCKOPT BROADCAST, 1) ; }

} s t a t i c i n t l a s t r e s e t = 0; void checkpings ( ) { i f ( pingsock==ENET SOCKET NULL) return ; enet uint32 events = ENET SOCKET WAIT RECEIVE ; ENetBuffer buf ; ENetAddress addr ; uchar ping [MAXTRANS] ; char t e x t [MAXTRANS] ; buf . data = ping ; buf . dataLength = s i z e o f ( ping ) ; while ( enet socket wait ( pingsock , &events , 0) >= 0 && events ) { i n t len = e n e t s o c k e t r e c e i v e ( pingsock , &addr , &buf , 1) ; i f ( len address . host && addr . port == servers [ i]−>address . port ) { s i = servers [ i ] ; break ; } i f ( ! s i && searchlan ) s i = newserver (NULL, server : : serverport ( addr . port ) , addr . host ) ; i f ( ! s i ) continue ; ucharbuf p ( ping , len ) ; i n t m i l l i s = g e t i n t ( p ) , r t t = clamp ( t o t a l m i l l i s − m i l l i s , 0 , min ( servpingdecay , t o t a l m i l l i s ) ) ; i f ( m i l l i s >= l a s t r e s e t && r t t < servpingdecay ) si−>addping ( r t t , millis ) ; si−>numplayers = g e t i n t ( p ) ;

engine/serverbrowser.cpp i n t numattr = g e t i n t ( p ) ; si−>a t t r . s e t s i z e ( 0 ) ; l o o p j ( numattr ) { i n t a t t r = g e t i n t ( p ) ; i f ( p . overread ( ) ) break ; si−> a t t r . add ( a t t r ) ; } g e t s t r i n g ( text , p ) ; f i l t e r t e x t ( si−>map, text , f a l s e ) ; g e t s t r i n g ( text , p ) ; f i l t e r t e x t ( si−>sdesc , t e x t ) ;

447

void c l e a r s e r v e r s ( bool f u l l = f a l s e ) { resolverclear ( ) ; i f ( f u l l ) servers . deletecontents ( ) ; e l s e loopvrev ( servers ) i f ( ! servers [ i]−>keep ) d e l e t e servers . remove ( i ) ; s e l e c t e d s e r v e r = NULL; }

} }

#define RETRIEVELIMIT 20000

void s o r t s e r v e r s ( ) { servers . s o r t ( s e r v e r i n f o : : compare ) ; } COMMAND( sortservers , ” ” ) ;

void r e t r i e v e s e r v e r s ( vector &data ) { ENetSocket sock = connectmaster ( ) ; i f ( sock == ENET SOCKET NULL) return ; extern char ∗mastername ; defformatstring ( t e x t ) ( ” r e t r i e v i n g servers from %s . . . ( esc to abort ) ” , mastername ) ; renderprogress ( 0 , t e x t ) ;

VARP( autosortservers , 0 , 1 , 1) ; VARP( autoupdateservers , 0 , 1 , 1) ; void r e f r e s h s e r v e r s ( ) { s t a t i c i n t l a s t r e f r e s h = 0; i f ( l a s t r e f r e s h == t o t a l m i l l i s ) return ; i f ( t o t a l m i l l i s − l a s t r e f r e s h > 1000) { loopv ( servers ) servers [ i]−>r e s e t ( ) ; lastreset = totalmillis ; } lastrefresh = totalmillis ;

i n t starttime = SDL GetTicks ( ) , timeout = 0; const char ∗req = ” l i s t \n ” ; i n t reqlen = s t r l e n ( req ) ; ENetBuffer buf ; while ( reqlen > 0) { enet uint32 events = ENET SOCKET WAIT SEND; i f ( enet socket wait ( sock , &events , 250) >= 0 && events ) { buf . data = ( void ∗) req ; buf . dataLength = reqlen ; i n t sent = enet socket send ( sock , NULL, &buf , 1) ; i f ( sent < 0) break ; req += sent ; reqlen −= sent ; i f ( reqlen RETRIEVELIMIT ) break ; }

checkresolver ( ) ; checkpings ( ) ; i f ( t o t a l m i l l i s − l a s t i n f o >= servpingrate / ( maxservpings ? max( 1 , ( servers . length ( ) + maxservpings − 1) / maxservpings ) : 1) ) pingservers ( ) ; i f ( autosortservers ) s o r t s e r v e r s ( ) ; } s e r v e r i n f o ∗s e l e c t e d s e r v e r = NULL; const char ∗showservers ( g3d gui ∗cgui , uint ∗header , i n t pagemin , i n t pagemax ) { refreshservers ( ) ; i f ( servers . empty ( ) ) { i f ( header ) execute ( header ) ; return NULL; } s e r v e r i n f o ∗sc = NULL; f o r ( i n t s t a r t = 0; s t a r t < servers . length ( ) ; ) { i f ( s t a r t > 0) cgui−>tab ( ) ; i f ( header ) execute ( header ) ; i n t end = servers . length ( ) ; cgui−>pushlist ( ) ; loopi (10) { i f ( ! game : : serverinfostartcolumn ( cgui , i ) ) break ; f o r ( i n t j = s t a r t ; j < end ; j ++) { i f ( ! i && j +1 − s t a r t >= pagemin && ( j +1 − s t a r t >= pagemax || cgui−>shouldtab ( ) ) ) { end = j ; break ; } s e r v e r i n f o &s i = ∗servers [ j ] ; const char ∗sdesc = s i . sdesc ; i f ( s i . address . host == ENET HOST ANY ) sdesc = ” [ unknown host ]”; e l s e i f ( s i . ping == s e r v e r i n f o : : WAITING ) sdesc = ” [ waiting f o r response ] ” ; i f ( game : : s e r v e r i n f o e n t r y ( cgui , i , s i .name, s i . port , sdesc , s i .map, sdesc == s i . sdesc ? s i . ping : −1, s i . a t t r , s i . numplayers ) ) sc = &s i ; } game : : serverinfoendcolumn ( cgui , i ) ; } cgui−>p o p l i s t ( ) ; s t a r t = end ; } i f ( s e l e c t e d s e r v e r || ! sc ) return NULL; s e l e c t e d s e r v e r = sc ; return ” connectselected ” ; } void connectselected ( ) { i f ( ! s e l e c t e d s e r v e r ) return ; connectserv ( selectedserver−>name, selectedserver−>port , selectedserver −>password ) ; s e l e c t e d s e r v e r = NULL; } COMMAND( connectselected , ” ” ) ;

i f ( reqlen = 0 && events ) { i f ( data . length ( ) >= data . capacity ( ) ) data . reserve (4096) ; buf . data = data . getbuf ( ) + data . length ( ) ; buf . dataLength = data . capacity ( ) − data . length ( ) ; i n t recv = e n e t s o c k e t r e c e i v e ( sock , NULL, &buf , 1) ; i f ( recv RETRIEVELIMIT ) break ; } i f ( data . length ( ) ) data . add ( ’ \ 0 ’ ) ; enet socket destroy ( sock ) ; } bool updatedservers = f a l s e ; void updatefrommaster ( ) { vector data ; r e t r i e v e s e r v e r s ( data ) ; i f ( data . empty ( ) ) conoutf ( ” master server not replying ” ) ; else { clearservers ( ) ; execute ( data . getbuf ( ) ) ; } refreshservers ( ) ; updatedservers = true ; } void i n i t s e r v e r s ( ) { s e l e c t e d s e r v e r = NULL; i f ( autoupdateservers && ! updatedservers ) updatefrommaster ( ) ; } ICOMMAND( addserver , ” s i s ” , ( const char ∗name, i n t ∗port , const char ∗ password ) , addserver ( name, ∗port , password [ 0 ] ? password : NULL) ) ; ICOMMAND( keepserver , ” s i s ” , ( const char ∗name, i n t ∗port , const char ∗ password ) , addserver ( name, ∗port , password [ 0 ] ? password : NULL, true ) ) ; ICOMMAND( clearservers , ” i ” , ( i n t ∗f u l l ) , c l e a r s e r v e r s (∗ f u l l ! = 0 ) ) ;

448

Foundations of Videogame Programming Code Repository ;

COMMAND( updatefrommaster , ” ” ) ; COMMAND( i n i t s e r v e r s , ” ” ) ;

kept ++; }

void w r i t e s e r v e r c f g ( ) { i f ( ! game : : savedservers ( ) ) return ; stream ∗f = o p e n u t f 8 f i l e ( path ( game : : savedservers ( ) , true ) , ”w” ) ; i f ( ! f ) return ; i n t kept = 0; loopv ( servers ) { s e r v e r i n f o ∗s = servers [ i ] ; i f ( s−>keep ) { i f ( ! kept ) f−>p r i n t f ( ” // servers that should never be cleared from the server l i s t \n\n ” ) ; i f ( s−>password ) f−>p r i n t f ( ” keepserver %s %d %s\n” , escapeid ( s−> name) , s−>port , escapestring ( s−>password ) ) ; e l s e f−>p r i n t f ( ” keepserver %s %d\n” , escapeid ( s−>name) , s−>port )

} i f ( kept ) f−>p r i n t f (”\n ” ) ; f−>p r i n t f ( ” / / servers connected to are added here automatically\n\n ” ) ; loopv ( servers ) { s e r v e r i n f o ∗s = servers [ i ] ; i f ( ! s−>keep ) { i f ( s−>password ) f−>p r i n t f ( ” addserver %s %d %s\n” , escapeid ( s−> name) , s−>port , escapestring ( s−>password ) ) ; e l s e f−>p r i n t f ( ” addserver %s %d\n” , escapeid ( s−>name) , s−>port ) ; } } delete f ; }

engine/shader.cpp // shader . cpp : OpenGL assembly/GLSL shader management

defaultshader−>set ( ) ; }

#include ” engine . h” struct GlobalShaderParamState : ShaderParamState { uint version ;

Shader ∗lookupshaderbyname ( const char ∗name) { Shader ∗s = shaders . access (name) ; return s && s−>detailshader ? s : NULL; }

GlobalShaderParamState ( ) : version ( 0 ) {} }; Shader ∗Shader : : lastshader = NULL; Shader ∗defaultshader = NULL, ∗rectshader = NULL, ∗cubemapshader = NULL, ∗notextureshader = NULL, ∗nocolorshader = NULL, ∗nocolorglslshader = NULL, ∗foggedshader = NULL, ∗foggednotextureshader = NULL, ∗ stdworldshader = NULL, ∗lineshader = NULL, ∗foggedlineshader = NULL; hashtable shaders ; Shader ∗curshader = NULL; vector curparams ; GlobalShaderParamState vertexparamstate [RESERVEDSHADERPARAMS + MAXSHADERPARAMS] , pixelparamstate [RESERVEDSHADERPARAMS + MAXSHADERPARAMS] ; s t a t i c bool dirtyenvparams = f a l s e , standardshader = f a l s e , forceshaders = true ; s t a t i c uint paramversion = 0;

static static static static

VAR( reservevpparams , 1 , 16, 0) ; VAR( maxvpenvparams , 1 , 0 , 0) ; VAR( maxvplocalparams , 1 , 0 , 0) ; VAR( maxfpenvparams , 1 , 0 , 0) ; VAR( maxfplocalparams , 1 , 0 , 0) ; VAR( maxtexcoords , 1 , 0 , 0) ; VAR( maxvsuniforms , 1 , 0 , 0) ; VAR( maxfsuniforms , 1 , 0 , 0) ; VAR( maxvaryings , 1 , 0 , 0) ; VAR( dbgshader , 0 , 0 , 2) ; void loadshaders ( ) { standardshader = true ; e x e c f i l e ( renderpath==R GLSLANG ? ” data/ g l s l . c f g ” : ” data/stdshader . c f g ”) ; standardshader = f a l s e ; defaultshader = lookupshaderbyname ( ” d e f a u l t ” ) ; stdworldshader = lookupshaderbyname ( ” stdworld ” ) ; i f ( ! defaultshader || ! stdworldshader ) f a t a l ( ” cannot f i n d shader definitions ” ) ; extern S l o t dummyslot ; dummyslot . shader = stdworldshader ; extern i n t a t i l i n e b u g ; rectshader = lookupshaderbyname ( ” r e c t ” ) ; cubemapshader = lookupshaderbyname ( ” cubemap ” ) ; notextureshader = lookupshaderbyname ( ” notexture ” ) ; nocolorshader = lookupshaderbyname ( ” nocolor ” ) ; nocolorglslshader = lookupshaderbyname ( ” n o c o l o r g l s l ” ) ; foggedshader = lookupshaderbyname ( ” fogged ” ) ; foggednotextureshader = lookupshaderbyname ( ” foggednotexture ” ) ; lineshader = lookupshaderbyname ( a t i l i n e b u g && renderpath == R ASMGLSLANG ? ” n o t e x t u r e g l s l ” : ” notexture ” ) ; foggedlineshader = lookupshaderbyname ( a t i l i n e b u g && renderpath == R ASMGLSLANG ? ” foggednotextureglsl ” : ” foggednotexture ” ) ;

s t a t i c bool compileasmshader (GLenum type , GLuint &idx , const char ∗def , const char ∗tname , const char ∗name, bool msg = true , bool nativeonly = f a l s e ) { glGenProgramsARB ( 1 , &idx ) ; glBindProgramARB ( type , idx ) ; def += strspn ( def , ” \t\r\n ” ) ; glProgramStringARB ( type , GL PROGRAM FORMAT ASCII ARB, ( GLsizei ) s t r l e n ( def ) , def ) ; GLint e r r = −1, native = 1; glGetIntegerv ( GL PROGRAM ERROR POSITION ARB, &e r r ) ; extern i n t apple vp bug ; i f ( type ! =GL VERTEX PROGRAM ARB || ! apple vp bug ) glGetProgramivARB ( type , GL PROGRAM UNDER NATIVE LIMITS ARB, & native ) ; i f ( msg && e r r !=−1) { conoutf (CON ERROR, ”COMPILE ERROR (%s:%s ) − %s ” , tname , name, glGetString (GL PROGRAM ERROR STRING ARB) ) ; i f ( err>=0 && err 1) { conoutf (CON ERROR, ”GLSL ERROR (%s:%s ) ” , type == GL VERTEX SHADER ? ”VS” : ( type == GL FRAGMENT SHADER ? ”FS” : ”PROG” ) , name) ; FILE ∗l = g e t l o g f i l e ( ) ; if ( l ) { GLchar ∗l o g = new GLchar [ length ] ; i f ( type ) glGetShaderInfoLog ( obj , length , &length , l o g ) ; e l s e glGetProgramInfoLog ( obj , length , &length , l o g ) ; f p r i n t f ( l , ”%s\n” , l o g ) ;

engine/shader.cpp i f ( source ) l o o p i (1000) { const char ∗next = strchr ( source , ’\n ’ ) ; f p r i n t f ( l , ”%d : ” , i +1) ; f w r i t e ( source , 1 , next ? next − source + 1 : s t r l e n ( source ) , l); i f ( ! next ) { fputc ( ’ \n ’ , l ) ; break ; } source = next + 1; } delete [ ] log ;

glAttachShader ( s . program , s . psobj ) ; loopv ( s . a t t r i b l o c s ) { AttribLoc &a = s . a t t r i b l o c s [ i ] ; glBindAttribLocation ( s . program , a . loc , a .name) ; } glLinkProgram ( s . program ) ; glGetProgramiv ( s . program , GL LINK STATUS , &success ) ; } i f ( success ) { glUseProgram ( s . program ) ; loopi ( 8 ) { defformatstring ( arg ) ( ” tex%d ” , i ) ; GLint l o c = glGetUniformLocation ( s . program , arg ) ; i f ( l o c ! = −1) glUniform1i ( loc , i ) ; } loopv ( s . defaultparams ) { ShaderParam ¶m = s . defaultparams [ i ] ; s t r i n g pname; i f ( param . type==SHPARAM UNIFORM) copystring ( pname, param .name) ; e l s e formatstring (pname) (”%s%d ” , param . type==SHPARAM VERTEX ? ” v ” : ”p ” , param . index ) ; param . l o c = glGetUniformLocation ( s . program , pname) ; } loopv ( s . uniformlocs ) bindglsluniform ( s , s . uniformlocs [ i ] ) ; glUseProgram ( 0 ) ; } e l s e i f ( s . program ) { i f ( msg ) showglslinfo ( GL FALSE, s . program , s .name, NULL) ; glDeleteProgram ( s . program ) ; s . program = 0; }

} } } s t a t i c void compileglslshader (GLenum type , GLuint &obj , const char ∗def , const char ∗name, bool msg = true ) { const GLchar ∗source = ( const GLchar ∗) ( def + strspn ( def , ” \t\r\n ” ) ) ; obj = glCreateShader ( type ) ; glShaderSource ( obj , 1 , &source , NULL) ; glCompileShader ( obj ) ; GLint success ; glGetShaderiv ( obj , GL COMPILE STATUS, &success ) ; i f ( ! success ) { i f ( msg ) showglslinfo ( type , obj , name, source ) ; glDeleteShader ( obj ) ; obj = 0; } e l s e i f ( dbgshader > 1 && msg ) showglslinfo ( type , obj , name, source ) ; } VAR( dbgubo , 0 , 0 , 1) ; s t a t i c void bindglsluniform ( Shader &s , UniformLoc &u ) { u . l o c = glGetUniformLocation ( s . program , u .name) ; i f ( ! u . blockname ) return ; i f (hasUBO) { GLuint bidx = glGetUniformBlockIndex ( s . program , u . blockname ) ; GLuint uidx = GL INVALID INDEX ; glGetUniformIndices ( s . program , 1 , &u .name, &uidx ) ; i f ( bidx ! = GL INVALID INDEX && uidx ! = GL INVALID INDEX ) { GLint s i z e v a l = 0 , o f f s e t v a l = 0 , s t r i d e v a l = 0; glGetActiveUniformBlockiv ( s . program , bidx , GL UNIFORM BLOCK DATA SIZE, &s i z e v a l ) ; i f ( s i z e v a l 0) { glGetActiveUniformsiv ( s . program , 1 , &uidx , GL UNIFORM ARRAY STRIDE, &s t r i d e v a l ) ; i f ( s t r i d e v a l > u . s t r i d e ) return ; } u. o f f s e t = o f f s e t v a l ; u. size = sizeval ; glUniformBlockBinding ( s . program , bidx , u . binding ) ; i f ( dbgubo ) conoutf (CON DEBUG, ”UBO: %s:%s:%d , o f f s e t : %d , s i z e : %d , s t r i d e : %d ” , u .name, u . blockname , u . binding , offsetval , sizeval , strideval ) ; } } e l s e i f (hasBUE) { GLint s i z e = glGetUniformBufferSize ( s . program , u . l o c ) , s t r i d e = 0; i f ( s i z e 0) { defformatstring ( elem1name ) (”%s [ 1 ] ” , u .name) ; GLint elem1loc = glGetUniformLocation ( s . program , elem1name ) ; i f ( elem1loc == −1) return ; GLintptr elem0off = glGetUniformOffset ( s . program , u . l o c ) , elem1off = glGetUniformOffset ( s . program , elem1loc ) ; s t r i d e = elem1off − elem0off ; i f ( s t r i d e > u . s t r i d e ) return ; } u . o f f s e t = 0; u. size = size ; i f ( dbgubo ) conoutf (CON DEBUG, ”BUE: %s:%s:%d , o f f s e t : %d , s i z e : %d , s t r i d e : %d ” , u .name, u . blockname , u . binding , 0 , size , stride ) ; } } s t a t i c void linkglslprogram ( Shader &s , bool msg = true ) { s . program = s . vsobj && s . psobj ? glCreateProgram ( ) : 0; GLint success = 0; i f ( s . program ) { glAttachShader ( s . program , s . vsobj ) ;

449

} bool checkglslsupport ( ) { const GLchar ∗v s s t r = ” void main ( void ) {\n” ” g l P o s i t i o n = ftransform ( ) ;\n” ”}\n ” ; #if 0 /∗ check i f GLSL p r o f i l e supports loops ∗/ const GLchar ∗psstr = ” uniform i n t N;\n” ” uniform vec4 d e l t a ;\n” ” void main ( void ) {\n” ” vec4 t e s t = vec4 ( 0 . 0 , 0.0 , 0.0 , 0 . 0 ) ;\n” ” f o r ( i n t i = 0; i < N; i ++) t e s t += d e l t a ;\n” ” gl FragColor = t e s t ;\n” ”}\n ” ; #e l s e const GLchar ∗psstr = ” void main ( void ) {\n” ” gl FragColor = vec4 ( 0 . 0 ) ;\n” ”}\n ” ; #endif GLuint vsobj = glCreateShader (GL VERTEX SHADER) , psobj = glCreateShader (GL FRAGMENT SHADER) ; GLuint program = glCreateProgram ( ) ; GLint success = 0; i f ( vsobj && psobj && program ) { glShaderSource ( vsobj , 1 , &vsstr , NULL) ; glCompileShader ( vsobj ) ; glGetShaderiv ( vsobj , GL COMPILE STATUS, &success ) ; i f ( success ) { glShaderSource ( psobj , 1 , &psstr , NULL) ; glCompileShader ( psobj ) ; glGetShaderiv ( psobj , GL COMPILE STATUS, &success ) ; i f ( success ) { glAttachShader ( program , vsobj ) ; glAttachShader ( program , psobj ) ; glLinkProgram ( program ) ; glGetProgramiv ( program , GL LINK STATUS , &success ) ; } } } i f ( vsobj ) glDeleteShader ( vsobj ) ; i f ( psobj ) glDeleteShader ( psobj ) ; i f ( program ) glDeleteProgram ( program ) ; return success ! = 0 ; } #define ALLOCEXTPARAM 0xFF #define UNUSEDEXTPARAM 0xFE s t a t i c i n t addextparam ( Shader &s , const char ∗name, i n t type , i n t index ,

450

Foundations of Videogame Programming Code Repository int loc )

{ i f ( ! ( s . numextparams%4) ) { LocalShaderParamState ∗extparams = new LocalShaderParamState [ s . numextparams+ 4 ] ; i f ( s . extparams ) { memcpy( extparams , s . extparams , s . numextparams∗s i z e o f ( LocalShaderParamState ) ) ; d e l e t e [ ] s . extparams ; } s . extparams = extparams ; } i n t extindex = s . numextparams ; LocalShaderParamState &ext = s . extparams [ extindex ] ; ext .name = name; ext . type = type ; ext . index = index ; ext . l o c = l o c ; s . numextparams++; return extindex ; } s t a t i c void allocglsluniformparam ( Shader &s , i n t type , i n t index , bool local = false ) { ShaderParamState &v a l = ( type==SHPARAM VERTEX ? vertexparamstate [ index ] : pixelparamstate [ index ] ) ; i n t l o c = v a l .name ? glGetUniformLocation ( s . program , v a l .name) : −1; i f ( l o c == −1) { defformatstring ( altname ) (”%s%d ” , type==SHPARAM VERTEX ? ” v ” : ”p ” , index ) ; l o c = glGetUniformLocation ( s . program , altname ) ; } i f ( l o c >= 0) l o o p i ( s . numextparams ) { LocalShaderParamState &ext = s . extparams [ i ] ; i f ( ext . l o c ! = l o c ) continue ; i f ( ext . type==SHPARAM LOOKUP) { ext .name = v a l .name; ext . type = type ; ext . index = l o c a l ? −1 : index ; } i f ( type==SHPARAM VERTEX) s . extvertparams [ index ] = i ; e l s e s . extpixparams [ index ] = i ; return ; } i f ( l o c == −1) { i f ( type==SHPARAM VERTEX) s . extvertparams [ index ] = l o c a l ? UNUSEDEXTPARAM : ALLOCEXTPARAM; e l s e s . extpixparams [ index ] = l o c a l ? UNUSEDEXTPARAM : ALLOCEXTPARAM ; return ; } i n t extindex = addextparam ( s , v a l .name, type , l o c a l ? −1 : index , l o c ) ; i f ( type==SHPARAM VERTEX) s . extvertparams [ index ] = extindex ; e l s e s . extpixparams [ index ] = extindex ; } s t a t i c void setglsluniformformat ( Shader &s , const char ∗name, GLenum format , i n t s i z e ) { switch ( format ) { case GL FLOAT : case GL FLOAT VEC2 : case GL FLOAT VEC3 : break ; case GL FLOAT VEC4 : default : return ; } i f ( s i z e > 1 || ! strncmp ( name, ” g l ” , 3) ) return ; i n t l o c = glGetUniformLocation ( s . program , name) ; i f ( l o c < 0) return ; l o o p v j ( s . defaultparams ) i f ( s . defaultparams [ j ] . l o c == l o c ) { s . defaultparams [ j ] . format = format ; return ; } l o o p j ( s . numextparams ) i f ( s . extparams [ j ] . l o c == l o c ) { s . extparams [ j ] . format = format ; return ; } i n t extindex = addextparam ( s , NULL, SHPARAM LOOKUP, −1, l o c ) ; i f ( extindex >= 0) s . extparams [ extindex ] . format = format ; }

s t a t i c void a l l o c g l s l a c t i v e u n i f o r m s ( Shader &s ) { GLint numactive = 0; glGetProgramiv ( s . program , GL ACTIVE UNIFORMS, &numactive ) ; s t r i n g name; l o o p i ( numactive ) { GLsizei namelen = 0; GLint s i z e = 0; GLenum format = GL FLOAT VEC4 ; name[ 0 ] = ’ \ 0 ’ ; glGetActiveUniform ( s . program , i , s i z e o f (name)−1, &namelen , &size , &format , name) ; i f ( namelen param . version && ! strcmp ( vp .name, param .name) ) return true ; GlobalShaderParamState &pp = pixelparamstate [ j ] ; i f ( pp .name && ! pp . l o c a l && pp . version > param . version && ! strcmp ( pp .name, param .name) ) return true ; } return f a l s e ; } void Shader : : allocenvparams ( S l o t ∗s l o t ) { i f ( ! ( type & SHADER GLSLANG) ) return ; i f ( slot ) { #define UNIFORMTEX( name, tmu ) \ { \ l o c = glGetUniformLocation ( program , name) ; \ i n t v a l = tmu; \ i f ( l o c ! = −1) glUniform1i ( loc , v a l ) ; \ } i n t loc , tmu = 2; i f ( type & SHADER NORMALSLMS) { UNIFORMTEX( ” lmcolor ” , 1) ; UNIFORMTEX( ” lmdir ” , 2) ; tmu++; } e l s e UNIFORMTEX( ” lightmap ” , 1) ; i f ( type & SHADER ENVMAP) UNIFORMTEX( ” envmap” , tmu++) ; UNIFORMTEX( ” shadowmap” , 7) ; i n t stex = 0; loopv ( s l o t−>sts ) { S l o t : : Tex &t = s l o t−>sts [ i ] ; switch ( t . type ) { case TEX DIFFUSE : UNIFORMTEX( ” diffusemap ” , 0) ; break ; case TEX NORMAL: UNIFORMTEX( ” normalmap” , tmu++) ; break ; case TEX GLOW: UNIFORMTEX( ” glowmap ” , tmu++) ; break ; case TEX DECAL: UNIFORMTEX( ” decal ” , tmu++) ; break ; case TEX SPEC : i f ( t . combined extvertparams [ index ] : Shader : : lastshader−>extpixparams [ index ] ) ; i f ( extindex == ALLOCEXTPARAM) allocglsluniformparam (∗Shader : : lastshader , type , index , v a l . l o c a l ) ; i f ( extindex < Shader : : lastshader−>numextparams ) setuniformval ( Shader : : lastshader−>extparams [ extindex ] , v a l . v a l ) ; } e l s e i f ( v a l . d i r t y ==ShaderParamState : : DIRTY ) { glProgramEnvParameter4fvARB ( type==SHPARAM VERTEX ? GL VERTEX PROGRAM ARB : GL FRAGMENT PROGRAM ARB, index , v a l . val ) ; v a l . d i r t y = ShaderParamState : : CLEAN; } }

} void setenvparamf ( const char ∗name, i n t type , i n t index , f l o a t x , f l o a t y , f l o a t z , f l o a t w) { ShaderParamState &v a l = setparamf ( name, type , index , x , y , z , w) ; val . local = false ; i f ( v a l . d i r t y ==ShaderParamState : : DIRTY ) dirtyenvparams = true ; } void setenvparamfv ( const char ∗name, i n t type , i n t index , const f l o a t ∗v ) { ShaderParamState &v a l = setparamfv ( name, type , index , v ) ; val . local = false ; i f ( v a l . d i r t y ==ShaderParamState : : DIRTY ) dirtyenvparams = true ; } void flushenvparamf ( const char ∗name, i n t type , i n t index , f l o a t x , f l o a t y , f l o a t z , f l o a t w) { ShaderParamState &v a l = setparamf ( name, type , index , x , y , z , w) ; val . local = false ; flushparam ( type , index ) ; } void flushenvparamfv ( const char ∗name, i n t type , i n t index , const f l o a t ∗ v) { ShaderParamState &v a l = setparamfv ( name, type , index , v ) ; val . local = false ; flushparam ( type , index ) ; } void setlocalparamf ( const char ∗name, i n t type , i n t index , f l o a t x , f l o a t y , f l o a t z , f l o a t w) {

s t a t i c i n l i n e bool sortparamversions ( const GlobalShaderParamState ∗x , const GlobalShaderParamState ∗y )

ShaderParamState &v a l = setparamf ( name, type , index , x , y , z , w) ; v a l . l o c a l = true ; flushparam ( type , index ) ;

{ return x−>version < y−>version ;

}

} s t a t i c uint resetparamversions ( ) { GlobalShaderParamState ∗params[2∗(RESERVEDSHADERPARAMS + MAXSHADERPARAMS) ] ; l o o p i (RESERVEDSHADERPARAMS + MAXSHADERPARAMS) { params[2∗ i +0] = &vertexparamstate [ i ] ; params[2∗ i +1] = &pixelparamstate [ i ] ; } quicksort ( params , 2∗(RESERVEDSHADERPARAMS + MAXSHADERPARAMS) , sortparamversions ) ; paramversion = 0; l o o p i (2∗(RESERVEDSHADERPARAMS + MAXSHADERPARAMS) ) params [ i]−>version = ++paramversion ; return paramversion ; } s t a t i c i n l i n e ShaderParamState &setparamf ( const char ∗name, i n t type , i n t index , f l o a t x , f l o a t y , f l o a t z , f l o a t w) { GlobalShaderParamState &v a l = ( type==SHPARAM VERTEX ? vertexparamstate [ index ] : pixelparamstate [ index ] ) ; v a l .name = name; v a l . version = ++paramversion > 0 ? paramversion : resetparamversions ( ) ; i f ( v a l . d i r t y ==ShaderParamState : : INVALID || v a l . v a l [ 0 ] ! = x || v a l . v a l [ 1 ] ! = y || v a l . va l [ 2 ] ! = z || v a l . v a l [ 3 ] ! =w) { val . val [ 0 ] = x ; val . val [ 1 ] = y ; val . val [ 2 ] = z ; v a l . v a l [ 3 ] = w; v a l . d i r t y = ShaderParamState : : DIRTY ; } return v a l ; } s t a t i c i n l i n e ShaderParamState &setparamfv ( const char ∗name, i n t type , i n t index , const f l o a t ∗v ) { GlobalShaderParamState &v a l = ( type==SHPARAM VERTEX ? vertexparamstate [ index ] : pixelparamstate [ index ] ) ; v a l .name = name; v a l . version = ++paramversion > 0 ? paramversion : resetparamversions ( ) ; i f ( v a l . d i r t y ==ShaderParamState : : INVALID || memcmp( v a l . val , v , s i z e o f (

void setlocalparamfv ( const char ∗name, i n t type , i n t index , const f l o a t ∗ v) { ShaderParamState &v a l = setparamfv ( name, type , index , v ) ; v a l . l o c a l = true ; flushparam ( type , index ) ; } void invalidateenvparams ( i n t type , i n t s t a r t , i n t count ) { GlobalShaderParamState ∗paramstate = type==SHPARAM VERTEX ? vertexparamstate : pixelparamstate ; i n t end = min ( s t a r t + count , RESERVEDSHADERPARAMS + MAXSHADERPARAMS) ; while ( s t a r t < end ) { paramstate [ s t a r t ] . d i r t y = ShaderParamState : : INVALID ; s t a r t ++; } } void Shader : : flushenvparams ( S l o t ∗s l o t ) { i f ( type & SHADER GLSLANG) { i f ( ! used ) allocenvparams ( s l o t ) ; l o o p i ( numextparams ) { LocalShaderParamState &ext = extparams [ i ] ; i f ( ext . index >= 0) setuniformval ( ext , ext . type==SHPARAM VERTEX ? vertexparamstate [ ext . index ] . v a l : pixelparamstate [ ext . index ] . v a l ) ; } } e l s e i f ( dirtyenvparams ) { l o o p i (RESERVEDSHADERPARAMS) { ShaderParamState &v a l = vertexparamstate [ i ] ; i f ( v a l . l o c a l || v a l . d i r t y ! = ShaderParamState : : DIRTY ) continue ; glProgramEnvParameter4fvARB (GL VERTEX PROGRAM ARB, i , v a l . v a l ) ; v a l . d i r t y = ShaderParamState : : CLEAN; } l o o p i (RESERVEDSHADERPARAMS) { ShaderParamState &v a l = pixelparamstate [ i ] ;

452

Foundations of Videogame Programming Code Repository i f ( v a l . l o c a l || v a l . d i r t y ! = ShaderParamState : : DIRTY ) continue ; glProgramEnvParameter4fvARB (GL FRAGMENT PROGRAM ARB, i , v a l . v a l ); v a l . d i r t y = ShaderParamState : : CLEAN;

} dirtyenvparams = f a l s e ; } used = true ; } s t a t i c i n l i n e void setglslslotparam ( const ShaderParam &p , LocalShaderParamState &l , uint &mask, i n t i ) { i f ( ! ( mask&(1type&SHADER INVALID ? 0 : reuseps−>ps ; e l s e i f ( ! compileasmshader (GL FRAGMENT PROGRAM ARB, ps , psstr , ”PS” , name, dbgshader || ! variantshader , variantshader ! =NULL) ) native = f a l s e ; return vs && ps && ( ! variantshader || native ) ; } } void Shader : : cleanup ( bool i n v a l i d ) { detailshader = NULL; used = f a l s e ; native = true ; i f ( vs ) { i f ( ! reusevs ) glDeleteProgramsARB ( 1 , &vs ) ; vs = i f ( ps ) { i f ( ! reuseps ) glDeleteProgramsARB ( 1 , &ps ) ; ps = i f ( vsobj ) { i f ( ! reusevs ) glDeleteShader ( vsobj ) ; vsobj = i f ( psobj ) { i f ( ! reuseps ) glDeleteShader ( psobj ) ; psobj = i f ( program ) { glDeleteProgram ( program ) ; program = 0; } numextparams = 0; DELETEA( extparams ) ; DELETEA( extvertparams ) ; extpixparams = NULL; loopv ( defaultparams ) memset ( defaultparams [ i ] . curval , −1, defaultparams [ i ] . curval ) ) ; i f ( standard || i n v a l i d ) { type = SHADER INVALID ; l o o p i (MAXVARIANTROWS) variants [ i ] . s e t s i z e ( 0 ) ; DELETEA( v s s t r ) ; DELETEA( psstr ) ; DELETEA( defer ) ; defaultparams . s e t s i z e ( 0 ) ; attriblocs . setsize (0) ; uniformlocs . s e t s i z e ( 0 ) ; altshader = NULL; l o o p i (MAXSHADERDETAIL) fastshader [ i ] = t h i s ; reusevs = reuseps = NULL; } }

0; 0; 0; 0;

} } } }

sizeof (

engine/shader.cpp s t a t i c void g e n a t t r i b l o c s ( Shader &s , const char ∗vs , const char ∗ps ) { s t a t i c i n t len = s t r l e n ( ” # pragma CUBE2 attrib ” ) ; s t r i n g name; int loc ; while ( ( vs = s t r s t r ( vs , ”#pragma CUBE2 attrib ” ) ) ) { i f ( sscanf ( vs , ”#pragma CUBE2 attrib %100s %d ” , name, &l o c ) == 2) s . a t t r i b l o c s . add ( AttribLoc ( getshaderparamname (name) , l o c ) ) ; vs += len ; } } s t a t i c void genuniformlocs ( Shader &s , const char ∗vs , const char ∗ps ) { s t a t i c i n t len = s t r l e n ( ” # pragma CUBE2 uniform ” ) ; s t r i n g name, blockname ; i n t binding , s t r i d e ; while ( ( vs = s t r s t r ( vs , ”#pragma CUBE2 uniform ” ) ) ) { i n t numargs = sscanf ( vs , ”#pragma CUBE2 uniform %100s %100s %d %d ” , name, blockname , &binding , &s t r i d e ) ; i f ( numargs >= 3) s . uniformlocs . add ( UniformLoc ( getshaderparamname ( name) , getshaderparamname ( blockname ) , binding , numargs >= 4 ? s t r i d e : 0) ) ; e l s e i f ( numargs >= 1) s . uniformlocs . add ( UniformLoc ( getshaderparamname (name) ) ) ; vs += len ; } } Shader ∗newshader ( i n t type , const char ∗name, const char ∗vs , const char ∗ps , Shader ∗variant = NULL, i n t row = 0) { i f ( Shader : : lastshader ) { i f ( renderpath==R ASMSHADER || renderpath==R ASMGLSLANG) { glBindProgramARB (GL VERTEX PROGRAM ARB, 0) ; glBindProgramARB (GL FRAGMENT PROGRAM ARB, 0) ; } i f ( renderpath==R GLSLANG || renderpath==R ASMGLSLANG) glUseProgram (0) ; Shader : : lastshader = NULL; } Shader ∗e x i s t s = shaders . access (name) ; char ∗rname = e x i s t s ? e x i s t s−>name : newstring (name) ; Shader &s = shaders [ rname ] ; s .name = rname ; s . v s s t r = newstring ( vs ) ; s . psstr = newstring ( ps ) ; DELETEA( s . defer ) ; s . type = type ; s . variantshader = variant ; s . standard = standardshader ; i f ( forceshaders ) s . forced = true ; s . reusevs = s . reuseps = NULL; i f ( variant ) { i n t row = 0 , c o l = 0; i f ( ! vs [ 0 ] || sscanf ( vs , ”%d , %d ” , &row , &c o l ) >= 1) { DELETEA( s . v s s t r ) ; s . reusevs = ! vs [ 0 ] ? variant : ( variant−>variants [ row ] . inrange ( c o l ) ? variant−>variants [ row ] [ c o l ] : NULL) ; } row = c o l = 0; i f ( ! ps [ 0 ] || sscanf ( ps , ”%d , %d ” , &row , &c o l ) >= 1) { DELETEA( s . psstr ) ; s . reuseps = ! ps [ 0 ] ? variant : ( variant−>variants [ row ] . inrange ( c o l ) ? variant−>variants [ row ] [ c o l ] : NULL) ; } } i f ( variant ) loopv ( variant−>defaultparams ) s . defaultparams . add ( variant −>defaultparams [ i ] ) ; e l s e loopv ( curparams ) s . defaultparams . add ( curparams [ i ] ) ; s . attriblocs . setsize (0) ; s . uniformlocs . s e t s i z e ( 0 ) ; i f ( type & SHADER GLSLANG) { g e n a t t r i b l o c s ( s , vs , ps ) ; genuniformlocs ( s , vs , ps ) ; } i f ( renderpath ! =R FIXEDFUNCTION && ! s . compile ( ) ) { s . cleanup ( true ) ; i f ( variant ) shaders . remove ( rname ) ; return NULL; } i f ( variant ) variant−>variants [ row ] . add(&s ) ; s . fixdetailshader ( ) ; return &s ;

453

} void setupshaders ( ) { i f ( renderpath==R ASMSHADER || renderpath==R ASMGLSLANG) { GLint v a l ; glGetProgramivARB (GL VERTEX PROGRAM ARB, GL MAX PROGRAM ENV PARAMETERS ARB, &v a l ) ; maxvpenvparams = v a l ; glGetProgramivARB (GL VERTEX PROGRAM ARB, GL MAX PROGRAM LOCAL PARAMETERS ARB, &v a l ) ; maxvplocalparams = v a l ; glGetProgramivARB (GL FRAGMENT PROGRAM ARB, GL MAX PROGRAM ENV PARAMETERS ARB, &v a l ) ; maxfpenvparams = v a l ; glGetProgramivARB (GL FRAGMENT PROGRAM ARB, GL MAX PROGRAM LOCAL PARAMETERS ARB, &v a l ) ; maxfplocalparams = v a l ; } i f ( renderpath==R GLSLANG || renderpath==R ASMGLSLANG) { GLint v a l ; glGetIntegerv (GL MAX VERTEX UNIFORM COMPONENTS, &v a l ) ; maxvsuniforms = v a l /4; glGetIntegerv (GL MAX FRAGMENT UNIFORM COMPONENTS, &v a l ) ; maxfsuniforms = v a l /4; glGetIntegerv ( GL MAX VARYING FLOATS, &v a l ) ; maxvaryings = v a l ; } i f ( renderpath ! = R FIXEDFUNCTION ) { GLint v a l ; glGetIntegerv (GL MAX TEXTURE COORDS ARB, &v a l ) ; maxtexcoords = v a l ; } standardshader = true ; i f ( renderpath == R GLSLANG) { defaultshader = newshader (SHADER GLSLANG, ”d e f a u l t ” , ” void main ( void ) {\n” ” g l P o s i t i o n = ftransform ( ) ;\n” ” gl TexCoord [ 0 ] = gl MultiTexCoord0 ;\n” ” gl FrontColor = g l C o l o r ;\n” ”}\n” , ” uniform sampler2D tex0 ;\n” ” void main ( void ) {\n” ” gl FragColor = g l C o l o r ∗ texture2D ( tex0 , gl TexCoord [ 0 ] . xy ) ;\n” ”}\n ” ) ; notextureshader = newshader (SHADER GLSLANG, ”notexture ” , ” void main ( void ) {\n” ” g l P o s i t i o n = ftransform ( ) ;\n” ” gl FrontColor = g l C o l o r ;\n” ”}\n” , ” void main ( void ) {\n” ” gl FragColor = g l C o l o r ;\n” ”}\n ” ) ; } else { i f ( renderpath==R ASMSHADER || renderpath==R ASMGLSLANG) { glEnable (GL VERTEX PROGRAM ARB) ; glEnable (GL FRAGMENT PROGRAM ARB) ; } defaultshader = newshader ( 0 , ”d e f a u l t ” , ” ! ! ARBvp1.0\n” ”OPTION ARB position invariant ;\n” ”MOV r e s u l t . texcoord [ 0 ] , vertex . texcoord [ 0 ] ; \n” ”MOV r e s u l t . color , vertex . c o l o r ;\n” ”END\n” , ” ! ! ARBfp1.0\n” ”TEMP c o l o r ;\n” ”TEX color , fragment . texcoord [ 0 ] , texture [ 0 ] , 2D;\n” ”MUL r e s u l t . color , fragment . color , c o l o r ;\n” ”END\n ” ) ; notextureshader = newshader ( 0 , ”notexture ” , ” ! ! ARBvp1.0\n” ”OPTION ARB position invariant ;\n” ”MOV r e s u l t . color , vertex . c o l o r ;\n” ”END\n” , ” ! ! ARBfp1.0\n” ”TEX r e s u l t . color , fragment . texcoord [ 0 ] , texture [ 0 ] , 2D;\n” ”END\n ” ) ; } standardshader = f a l s e ; i f ( ! defaultshader || ! notextureshader ) f a t a l ( ” f a i l e d to setup shaders ”) ; }

454

Foundations of Videogame Programming Code Repository

s t a t i c const char ∗findglslmain ( const char ∗s ) { const char ∗main = s t r s t r ( s , ”main ” ) ; i f ( ! main ) return NULL; f o r ( ; main >= s ; main−−) switch (∗main ) { case ’\ r ’ : case ’\n ’ : case ’ ; ’ : return main + 1; } return s ; } s t a t i c uint findusedtexcoords ( const char ∗s t r ) { uint used = 0; for ( ; ; ) { const char ∗t c = s t r s t r ( str , ” r e s u l t . texcoord [ ” ) ; i f ( ! t c ) break ; t c += s t r l e n ( ” r e s u l t . texcoord [ ” ) ; i n t n = s t r t o l ( tc , ( char ∗∗)&str , 10) ; i f ( n=16) continue ; used |= 1name : newstring (name) ; Shader &s = shaders [ rname ] ; s .name = rname ; DELETEA( s . defer ) ; s . defer = newstring ( contents ) ; s . type = SHADER DEFERRED | ∗type ; s . standard = standardshader ; } void useshader ( Shader ∗s ) { i f ( ! ( s−>type&SHADER DEFERRED) || ! s−>defer ) return ; char ∗defer = s−>defer ; s−>defer = NULL; bool wasstandard = standardshader , wasforcing = forceshaders ; int oldflags = identflags ; standardshader = s−>standard ; forceshaders = f a l s e ; i d e n t f l a g s &= ˜IDF PERSIST ; curparams . shrink ( 0 ) ; execute ( defer ) ; identflags = oldflags ; forceshaders = wasforcing ; standardshader = wasstandard ; d e l e t e [ ] defer ;

457

i f ( s−>type&SHADER DEFERRED) { DELETEA( s−>defer ) ; s−>type = SHADER INVALID ; } } void f i x s h a d e r d e t a i l ( ) { // must null out separately because f i x d e t a i l s h a d e r can r e c u r s i v e l y set i t enumerate ( shaders , Shader , s , { i f ( ! s . forced ) s . detailshader = NULL; }) ; enumerate ( shaders , Shader , s , { i f ( s . forced ) s . f i x d e t a i l s h a d e r ( ) ; }) ; linkslotshaders ( ) ; } i n t Shader : : uniformlocversion ( ) { s t a t i c i n t version = 0; i f (++ version >= 0) return version ; version = 0; enumerate ( shaders , Shader , s , { l o o p v j ( s . uniformlocs ) s . uniformlocs [ j ] . version = −1; }) ; return version ; } VARF( nativeshaders , 0 , 1 , 1 , f i x s h a d e r d e t a i l ( ) ) ; VARFP( shaderdetail , 0 , MAXSHADERDETAIL, MAXSHADERDETAIL, f i x s h a d e r d e t a i l () ) ; void Shader : : f i x d e t a i l s h a d e r ( bool force , bool recurse ) { Shader ∗a l t = t h i s ; detailshader = NULL; do { Shader ∗cur = shaderdetail < MAXSHADERDETAIL ? a l t−>fastshader [ shaderdetail ] : a l t ; i f ( cur−>type&SHADER DEFERRED && f o r c e ) useshader ( cur ) ; i f ( ! ( cur−>type&SHADER INVALID ) ) { i f ( cur−>type&SHADER DEFERRED) break ; detailshader = cur ; i f ( cur−>native || ! nativeshaders ) break ; } a l t = a l t−>altshader ; } while ( a l t && a l t ! = t h i s ) ; i f ( recurse && detailshader ) l o o p i (MAXVARIANTROWS) l o o p v j ( detailshader −>variants [ i ] ) detailshader−>variants [ i ] [ j]−>f i x d e t a i l s h a d e r ( force , f a l s e ) ; } Shader ∗useshaderbyname ( const char ∗name) { Shader ∗s = shaders . access (name) ; i f ( ! s ) return NULL; i f ( ! s−>detailshader ) s−>f i x d e t a i l s h a d e r ( ) ; s−>forced = true ; return s ; } void shader ( i n t ∗type , char ∗name, char ∗vs , char ∗ps ) { i f ( lookupshaderbyname (name) ) return ; i f ( ( ∗ type & SHADER GLSLANG ? renderpath ! =R GLSLANG && renderpath ! = R ASMGLSLANG : renderpath==R GLSLANG) || ( ! hasCM && s t r s t r ( ps , ∗type & SHADER GLSLANG ? ” textureCube ” : ” CUBE; ” ) ) || ( ! hasTR && s t r s t r ( ps , ∗type & SHADER GLSLANG ? ” texture2DRect ” : ” RECT ; ” ) ) ) { curparams . shrink ( 0 ) ; return ; } i f ( renderpath ! =R FIXEDFUNCTION ) { defformatstring ( i n f o ) ( ” shader %s ” , name) ; renderprogress ( loadprogress , i n f o ) ; } vector vsbuf , psbuf , vsbak , psbak ; #define GENSHADER( cond , body ) \ i f ( cond ) \ { \ i f ( vsbuf . length ( ) ) { vsbak . s e t s i z e ( 0 ) ; vsbak . put ( vs , s t r l e n ( vs ) +1) ; vs = vsbak . getbuf ( ) ; vsbuf . s e t s i z e ( 0 ) ; } \ i f ( psbuf . length ( ) ) { psbak . s e t s i z e ( 0 ) ; psbak . put ( ps , s t r l e n ( ps ) +1) ; ps = psbak . getbuf ( ) ; psbuf . s e t s i z e ( 0 ) ; } \ body ; \ i f ( vsbuf . length ( ) ) vs = vsbuf . getbuf ( ) ; \ i f ( psbuf . length ( ) ) ps = psbuf . getbuf ( ) ; \

458

Foundations of Videogame Programming Code Repository

} i f ( renderpath ! =R FIXEDFUNCTION ) { i f (∗ type & SHADER GLSLANG) { GENSHADER( curparams . length ( ) , genuniformdefs ( vsbuf , psbuf , vs , ps ) ) ; GENSHADER( s t r s t r ( vs , ”#pragma CUBE2 fog ” ) || s t r s t r ( ps , ”#pragma CUBE2 fog ” ) , genfogshader ( vsbuf , psbuf , vs , ps ) ) ; } } Shader ∗s = newshader(∗ type , name, vs , ps ) ; i f ( s && renderpath ! =R FIXEDFUNCTION ) { // ’ # ’ i s a comment in vertex/fragment programs , while ’#pragma ’ allows an escape f o r GLSL, so can handle both at once i f ( s t r s t r ( vs , ”#pragma CUBE2 water ” ) ) genwatervariant (∗s , s−>name, vs , ps ) ; i f ( s t r s t r ( vs , ”#pragma CUBE2 shadowmap ” ) ) genshadowmapvariant(∗s , s −>name, vs , ps ) ; i f ( s t r s t r ( vs , ”#pragma CUBE2 dynlight ” ) ) gendynlightvariant (∗s , s−> name, vs , ps ) ; } curparams . shrink ( 0 ) ; } void variantshader ( i n t ∗type , char ∗name, i n t ∗row , char ∗vs , char ∗ps ) { i f (∗row < 0) { shader ( type , name, vs , ps ) ; return ; } i f ( renderpath==R FIXEDFUNCTION && standardshader ) return ;

} return NULL; } ShaderParam ∗findshaderparam ( VSlot &s , const char ∗name, i n t type = −1, i n t index = −1) { loopv ( s . params ) { ShaderParam ¶m = s . params [ i ] ; i f ( ( name && param .name && ! strcmp ( name, param .name) ) || ( param . type ==type && param . index==index ) ) return ¶m ; } return findshaderparam(∗s . s l o t , name, type , index ) ; } void resetslotshader ( ) { curshader = NULL; curparams . shrink ( 0 ) ; } void setslotshader ( S l o t &s ) { s . shader = curshader ; i f ( ! s . shader ) { s . shader = stdworldshader ; return ; } loopv ( curparams ) s . params . add ( curparams [ i ] ) ; } s t a t i c void linkslotshaderparams ( vector ¶ms , Shader ∗sh , bool load ) {

Shader ∗s = lookupshaderbyname (name) ; i f ( ! s ) return ; defformatstring ( varname ) (”%s ” , s−>variants [∗row ] . length ( ) , ∗row , name) ; //defformatstring ( i n f o ) ( ” shader %s ” , varname ) ; //renderprogress ( loadprogress , i n f o ) ; vector vsbuf , psbuf , vsbak , psbak ; i f ( renderpath ! =R FIXEDFUNCTION ) { i f (∗ type & SHADER GLSLANG) { GENSHADER( s−>defaultparams . length ( ) , genuniformdefs ( vsbuf , psbuf , vs , ps , s ) ) ; GENSHADER( s t r s t r ( vs , ”#pragma CUBE2 fog ” ) || s t r s t r ( ps , ”#pragma CUBE2 fog ” ) , genfogshader ( vsbuf , psbuf , vs , ps ) ) ; } } Shader ∗v = newshader(∗ type , varname , vs , ps , s , ∗row ) ; i f ( v && renderpath ! =R FIXEDFUNCTION ) { // ’ # ’ i s a comment in vertex/fragment programs , while ’#pragma ’ allows an escape f o r GLSL, so can handle both at once i f ( s t r s t r ( vs , ”#pragma CUBE2 dynlight ” ) ) gendynlightvariant (∗s , varname , vs , ps , ∗row ) ; i f ( s t r s t r ( ps , ”#pragma CUBE2 variant ” ) || s t r s t r ( vs , ”#pragma CUBE2 variant ” ) ) gengenericvariant (∗s , varname , vs , ps , ∗row ); } } void setshader ( char ∗name) { curparams . shrink ( 0 ) ; Shader ∗s = shaders . access (name) ; if (!s) { i f ( renderpath ! =R FIXEDFUNCTION ) conoutf (CON ERROR, ”no such shader : %s ” , name) ; } e l s e curshader = s ; }

i f ( sh ) loopv ( params ) { i n t l o c = −1; ShaderParam ¶m = params [ i ] ; loopv ( sh−>defaultparams ) { ShaderParam &dparam = sh−>defaultparams [ i ] ; i f ( param .name ? dparam .name==param .name : dparam . type==param . type && dparam . index==param . index ) { i f (memcmp( param . val , dparam . val , s i z e o f ( param . v a l ) ) ) l o c = i ; break ; } } param . l o c = l o c ; } e l s e i f ( load ) loopv ( params ) params [ i ] . l o c = −1; } void linkslotshader ( S l o t &s , bool load ) { i f ( ! s . shader ) return ; i f ( load && ! s . shader−>detailshader ) s . shader−>f i x d e t a i l s h a d e r ( ) ; Shader ∗sh = s . shader−>detailshader ; linkslotshaderparams ( s . params , sh , load ) ; } void linkvslotshader ( VSlot &s , bool load ) { i f ( ! s . s l o t−>shader ) return ; Shader ∗sh = s . s l o t−>shader−>detailshader ; linkslotshaderparams ( s . params , sh , load ) ; i f ( ! sh ) return ; i f ( s . s l o t−>texmask&(1v a l [ k ] , 0.0 f , 1.0 f ) ; i f ( speedparam ) s . pulseglowspeed = speedparam−>v a l [0]/1000.0 f ; } i f ( sh−>type&SHADER ENVMAP) { ShaderParam ∗envparam = findshaderparam ( s , ” envscale ” ) ; i f ( envparam ) loopk ( 3 ) s . envscale [ k ] = clamp ( envparam−>v a l [ k ] , 0.0 f , 1.0 f ) ; }

ShaderParam ∗findshaderparam ( S l o t &s , const char ∗name, i n t type = −1, i n t index = −1) { loopv ( s . params ) { ShaderParam ¶m = s . params [ i ] ; i f ( ( name && param .name && ! strcmp ( name, param .name) ) || ( param . type ==type && param . index==index ) ) return ¶m ; } i f ( ! s . shader−>detailshader ) return NULL; loopv ( s . shader−>detailshader−>defaultparams ) { ShaderParam ¶m = s . shader−>detailshader−>defaultparams [ i ] ; i f ( ( name && param .name && ! strcmp ( name, param .name) ) || ( param . type ==type && param . index==index ) ) return ¶m ;

} void altshader ( char ∗origname , char ∗altname )

engine/shader.cpp { Shader ∗o r i g = shaders . access ( origname ) , ∗a l t = shaders . access ( altname ); i f ( ! o r i g || ! a l t ) return ; orig−>altshader = a l t ; orig−>f i x d e t a i l s h a d e r ( f a l s e ) ;

, f l o a t ∗z , f l o a t ∗w) , addshaderparam (name[ 0 ] ? name : NULL, SHPARAM PIXEL, ∗n , ∗x , ∗y , ∗z , ∗w) ) ; ICOMMAND( defuniformparam , ” s f f f f ” , ( char ∗name, f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗w) , addshaderparam ( name, SHPARAM UNIFORM, −1, ∗x , ∗y , ∗z , ∗w) ) ;

}

#define NUMPOSTFXBINDS 10

void fastshader ( char ∗nice , char ∗f a s t , i n t ∗d e t a i l ) { Shader ∗ns = shaders . access ( nice ) , ∗f s = shaders . access ( f a s t ) ; i f ( ! ns || ! f s ) return ; l o o p i ( min(∗ d e t a i l +1 , MAXSHADERDETAIL) ) ns−>fastshader [ i ] = f s ; ns−>f i x d e t a i l s h a d e r ( f a l s e ) ; }

struct postfxtex { GLuint id ; i n t scale , used ;

COMMAND( shader , ” i s s s ” ) ; COMMAND( variantshader , ” i s i s s ” ) ; COMMAND( setshader , ” s ” ) ; COMMAND( altshader , ” ss ” ) ; COMMAND( fastshader , ” s s i ” ) ; COMMAND( defershader , ” i s s ” ) ; ICOMMAND( forceshader , ” s ” , ( const char ∗name) , useshaderbyname (name) ) ; void isshaderdefined ( char ∗name) { Shader ∗s = lookupshaderbyname (name) ; i n t r e t ( s ? 1 : 0) ; } void isshadernative ( char ∗name) { Shader ∗s = lookupshaderbyname (name) ; i n t r e t ( s && s−>native ? 1 : 0) ; } COMMAND( isshaderdefined , ” s ” ) ; COMMAND( isshadernative , ” s ” ) ; s t a t i c hashset shaderparamnames(256) ; const char ∗getshaderparamname ( const char ∗name) { const char ∗∗e x i s t s = shaderparamnames . access (name) ; i f ( e x i s t s ) return ∗e x i s t s ; name = newstring (name) ; shaderparamnames [name] = name; return name; } void addshaderparam ( const char ∗name, i n t type , i n t n , f l o a t x , f l o a t y , f l o a t z , f l o a t w) { i f ( ( type==SHPARAM VERTEX || type==SHPARAM PIXEL ) && ( n= MAXSHADERPARAMS) )

postfxtex ( ) : id ( 0 ) , scale ( 0 ) , used(−1) {} }; vector postfxtexs ; i n t postfxbinds [NUMPOSTFXBINDS ] ; GLuint p o s t f x f b = 0; i n t postfxw = 0 , postfxh = 0; struct postfxpass { Shader ∗shader ; vec4 params ; uint inputs , freeinputs ; i n t outputbind , outputscale ; postfxpass ( ) : shader (NULL) , inputs ( 1 ) , freeinputs ( 1 ) , outputbind ( 0 ) , outputscale ( 0 ) {} }; vector postfxpasses ; s t a t i c i n t a l l o c a t e p o s t f x t e x ( i n t scale ) { loopv ( postfxtexs ) { postfxtex &t = postfxtexs [ i ] ; i f ( t . scale==scale && t . used < 0) return i ; } postfxtex &t = postfxtexs . add ( ) ; t . scale = scale ; glGenTextures ( 1 , &t . id ) ; createtexture ( t . id , max( screen−>w>>scale , 1) , max( screen−>h>>scale , 1) , NULL, 3 , 1 , GL RGB, GL TEXTURE RECTANGLE ARB) ; return postfxtexs . length ( ) −1; } void cleanuppostfx ( bool f u l l c l e a n ) { i f ( f u l l c l e a n && p o s t f x f b ) { glDeleteFramebuffers ( 1 , &p o s t f x f b ) ; p o s t f x f b = 0; }

{

loopv ( postfxtexs ) glDeleteTextures ( 1 , &postfxtexs [ i ] . id ) ; postfxtexs . shrink ( 0 ) ;

conoutf (CON ERROR, ” shader param index must be 0..%d\n” , MAXSHADERPARAMS−1); return ; } i f (name) name = getshaderparamname (name) ; loopv ( curparams ) { ShaderParam ¶m = curparams [ i ] ; i f ( param . type == type && (name ? param .name==name : param . index == n) ) { param . v a l [ 0 ] = x ; param . v a l [ 1 ] = y ; param . v a l [ 2 ] = z ; param . v a l [ 3 ] = w; return ; } } ShaderParam param = {name, type , n , −1, {x , y , z , w}}; curparams . add ( param ) ;

459

postfxw = 0; postfxh = 0; } void renderpostfx ( ) { i f ( postfxpasses . empty ( ) || renderpath==R FIXEDFUNCTION ) return ; i f ( postfxw ! = screen−>w || postfxh ! = screen−>h ) { cleanuppostfx ( f a l s e ) ; postfxw = screen−>w; postfxh = screen−>h ; } i n t binds [NUMPOSTFXBINDS ] ; l o o p i (NUMPOSTFXBINDS) binds [ i ] = −1; loopv ( postfxtexs ) postfxtexs [ i ] . used = −1;

} ICOMMAND( setvertexparam , ” i f f f f ” , ( i n t ∗n , f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗w) , addshaderparam (NULL, SHPARAM VERTEX, ∗n , ∗x , ∗y , ∗z , ∗w )); ICOMMAND( setpixelparam , ” i f f f f ” , ( i n t ∗n , f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗w) , addshaderparam (NULL, SHPARAM PIXEL, ∗n , ∗x , ∗y , ∗z , ∗w) ); ICOMMAND( setuniformparam , ” s f f f f ” , ( char ∗name, f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗w) , addshaderparam ( name, SHPARAM UNIFORM, −1, ∗x , ∗y , ∗z , ∗w) ) ; ICOMMAND( setshaderparam , ” s f f f f ” , ( char ∗name, f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗w) , addshaderparam ( name, SHPARAM LOOKUP, −1, ∗x , ∗y , ∗z , ∗w) ) ; ICOMMAND( defvertexparam , ” s i f f f f ” , ( char ∗name, i n t ∗n , f l o a t ∗x , f l o a t ∗ y , f l o a t ∗z , f l o a t ∗w) , addshaderparam (name[ 0 ] ? name : NULL, SHPARAM VERTEX, ∗n , ∗x , ∗y , ∗z , ∗w) ) ; ICOMMAND( defpixelparam , ” s i f f f f ” , ( char ∗name, i n t ∗n , f l o a t ∗x , f l o a t ∗y

binds [ 0 ] = a l l o c a t e p o s t f x t e x ( 0 ) ; postfxtexs [ binds [ 0 ] ] . used = 0; glBindTexture (GL TEXTURE RECTANGLE ARB, postfxtexs [ binds [ 0 ] ] . id ) ; glCopyTexSubImage2D (GL TEXTURE RECTANGLE ARB, 0 , 0 , 0 , 0 , 0 , screen−>w , screen−>h ) ; i f ( hasFBO && postfxpasses . length ( ) > 1) { i f ( ! p o s t f x f b ) glGenFramebuffers ( 1 , &p o s t f x f b ) ; glBindFramebuffer (GL FRAMEBUFFER EXT, p o s t f x f b ) ; } setenvparamf ( ” m i l l i s ” , SHPARAM VERTEX, 1 , l a s t m i l l i s /1000.0 f , l a s t m i l l i s /1000.0 f , l a s t m i l l i s /1000.0 f ) ; loopv ( postfxpasses ) {

460

Foundations of Videogame Programming Code Repository

postfxpass &p = postfxpasses [ i ] ; i n t tex = −1; i f ( ! postfxpasses . inrange ( i +1) ) { i f ( hasFBO && postfxpasses . length ( ) > 1) glBindFramebuffer ( GL FRAMEBUFFER EXT, 0) ; } else { tex = a l l o c a t e p o s t f x t e x ( p . outputscale ) ; i f ( hasFBO ) glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE RECTANGLE ARB, postfxtexs [ tex ] . id , 0) ; } i n t w = tex >= 0 ? max( screen−>w>>postfxtexs [ tex ] . scale , 1) : screen−>w, h = tex >= 0 ? max( screen−>h>>postfxtexs [ tex ] . scale , 1) : screen −>h ; glViewport ( 0 , 0 , w, h ) ; p . shader−>set ( ) ; setlocalparamfv ( ” params ” , SHPARAM VERTEX, 0 , p . params . v ) ; setlocalparamfv ( ” params ” , SHPARAM PIXEL, 0 , p . params . v ) ; i n t tw = w, th = h , tmu = 0; l o o p j (NUMPOSTFXBINDS) i f ( p . inputs&(1w>>postfxtexs [ binds [ j ] ] . scale , 1) ; th = max( screen−>h>>postfxtexs [ binds [ j ] ] . scale , 1) ; } e l s e g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB + tmu ) ; glBindTexture (GL TEXTURE RECTANGLE ARB, postfxtexs [ binds [ j ] ] . id ) ; ++tmu; } i f ( tmu ) g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; glBegin ( GL TRIANGLE STRIP ) ; glTexCoord2f ( 0 , 0) ; g l V e r t e x 2 f (−1, −1) ; glTexCoord2f ( tw , 0) ; g l V e r t e x 2 f ( 1 , −1) ; glTexCoord2f ( 0 , th ) ; g l V e r t e x 2 f (−1, 1) ; glTexCoord2f ( tw , th ) ; g l V e r t e x 2 f ( 1 , 1) ; glEnd ( ) ; l o o p j (NUMPOSTFXBINDS) i f ( p . freeinputs&(1= 0) { i f ( binds [ p . outputbind ] >= 0) postfxtexs [ binds [ p . outputbind ] ] . used = −1; binds [ p . outputbind ] = tex ; postfxtexs [ tex ] . used = p . outputbind ; i f ( ! hasFBO ) { glBindTexture (GL TEXTURE RECTANGLE ARB, postfxtexs [ tex ] . id ) ; glCopyTexSubImage2D (GL TEXTURE RECTANGLE ARB, 0 , 0 , 0 , 0 , 0 , w, h ) ; } } } } s t a t i c bool addpostfx ( const char ∗name, i n t outputbind , i n t outputscale , uint inputs , uint freeinputs , const vec4 ¶ms ) { i f ( ! hasTR || !∗name) return f a l s e ; Shader ∗s = useshaderbyname (name) ; if (!s) { conoutf (CON ERROR, ”no such postfx shader : %s ” , name) ; return f a l s e ; } postfxpass &p = postfxpasses . add ( ) ; p . shader = s ; p . outputbind = outputbind ; p . outputscale = outputscale ; p . inputs = inputs ; p . freeinputs = freeinputs ; p . params = params ; return true ; } void c l e a r p o s t f x ( ) { postfxpasses . shrink ( 0 ) ; cleanuppostfx ( f a l s e ) ; } COMMAND( clearpostfx , ” ” ) ;

ICOMMAND( addpostfx , ” s i i s f f f f ” , ( char ∗name, i n t ∗bind , i n t ∗scale , char ∗inputs , f l o a t ∗x , f l o a t ∗y , f l o a t ∗z , f l o a t ∗w) , { i n t inputmask = inputs [ 0 ] ? 0 : 1; i n t freemask = inputs [ 0 ] ? 0 : 1; bool freeinputs = true ; f o r ( ; ∗inputs ; inputs ++) i f ( i s d i g i t (∗ inputs ) ) { inputmask |= 1type&SHADER INVALID ) || ! v−>compile ( ) ) v−>cleanup ( true ) ;

462

Foundations of Videogame Programming Code Repository i f ( radiusMAXBLURRADIUS) return ; s t a t i c Shader ∗blurshader [ 7 ] [ 2 ] = { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } }, ∗blurrectshader [ 7 ] [ 2 ] = { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } }; Shader ∗&s = ( t a r g e t == GL TEXTURE RECTANGLE ARB ? blurrectshader : blurshader ) [ radius −1][pass ] ; if (!s) { defformatstring (name) ( ” blur%c%d%s ” , ’ x ’ + pass , radius , t a r g e t == GL TEXTURE RECTANGLE ARB ? ” r e c t ” : ” ” ) ; s = lookupshaderbyname (name) ; } s−>set ( ) ; setlocalparamfv ( ” weights ” , SHPARAM PIXEL, 0 , weights ) ; setlocalparamfv ( ” weights2 ” , SHPARAM PIXEL, 2 , &weights [ 4 ] ) ; setlocalparamf ( ” o f f s e t s ” , SHPARAM VERTEX, 1 , pass==0 ? o f f s e t s [ 1 ] / s i z e : o f f s e t s [ 0 ] / size , pass==1 ? o f f s e t s [ 1 ] / s i z e : o f f s e t s [ 0 ] / size , ( o f f s e t s [ 2 ] − o f f s e t s [ 1 ] ) /size , ( offsets [3] − offsets [ 2 ] ) /size ) ; loopk ( 4 ) { s t a t i c const char ∗names [ 4 ] = { ” o f f s e t 4 ” , ” o f f s e t 5 ” , ” o f f s e t 6 ” , ” o f f s e t 7 ” }; setlocalparamf ( names [ k ] , SHPARAM PIXEL, 3+k , pass==0 ? o f f s e t s [4+k] / s i z e : o f f s e t s [ 0 ] / size , pass==1 ? o f f s e t s [4+k] / s i z e : o f f s e t s [ 0 ] / size , 0 , 0) ; }

} } i f ( s . forced && ! s . detailshader ) s . f i x d e t a i l s h a d e r ( ) ; }) ; } void setupblurkernel ( i n t radius , f l o a t sigma , f l o a t ∗weights , f l o a t ∗ offsets ) { i f ( radiusMAXBLURRADIUS) return ; sigma ∗= 2∗radius ; f l o a t t o t a l = 1.0 f /sigma ; weights [ 0 ] = t o t a l ; o f f s e t s [ 0 ] = 0; // r e l y on b i l i n e a r f i l t e r i n g to sample 2 p i x e l s at once // transforms a∗X + b∗Y i n t o ( u+v ) ∗[X∗u/ (u+v ) + Y∗(1 − u/ (u+v ) ) ] l o o p i ( radius ) { f l o a t weight1 = exp(−((2∗ i ) ∗(2∗ i ) ) / (2∗sigma∗sigma ) ) / sigma , weight2 = exp(−((2∗ i +1)∗(2∗ i +1) ) / (2∗sigma∗sigma ) ) / sigma , scale = weight1 + weight2 , o f f s e t = 2∗i +1 + weight2 / scale ; weights [ i +1] = scale ; o f f s e t s [ i +1] = o f f s e t ; t o t a l += 2∗scale ; } l o o p i ( radius +1) weights [ i ] /= t o t a l ; f o r ( i n t i = radius +1; i o . y , −camera1−>o . z ) ; shadowfocus = camera1−>o ; shadowfocus . add ( d i r ) ; shadowfocus . add ( vec ( shadowdir ) . mul ( shadowmapheight ) ) ; shadowfocus . add ( dirx . mul ( shadowoffset . x ) ) ; shadowfocus . add ( d i r y . mul ( shadowoffset . y ) ) ;

s t a t i c const GLenum rgbfmts [ ] = { GL RGB, GL RGB8, GL FALSE }, rgbafmts [ ] = { GL RGBA16F ARB, GL RGBA16, GL RGBA, GL RGBA8, GL FALSE }; return hasFBO ? &rgbafmts [ fpshadowmap && hasTF ? 0 : ( shadowmapprecision ? 1 : 2) ] : rgbfmts ;

g l m a t r i x f proj , mv; glGetFloatv ( GL PROJECTION MATRIX, proj . v ) ; glGetFloatv (GL MODELVIEW MATRIX, mv. v ) ; shadowmapmatrix . mul ( proj , mv) ; i f ( renderpath==R FIXEDFUNCTION ) shadowmapmatrix . p r o j e c t i v e ( ) ; e l s e shadowmapmatrix . p r o j e c t i v e (−1, 1−shadowmapbias/ f l o a t ( shadowmapdist ) ) ;

} bool shadowcompare ( ) const { return renderpath==R FIXEDFUNCTION; } bool f i l t e r ( ) const { return renderpath ! =R FIXEDFUNCTION || hasNVPCF; } bool swaptexs ( ) const { return renderpath ! =R FIXEDFUNCTION; } bool { x y w

g l C o l o r 3 f ( 0 , 0 , 0) ; glDisable ( GL TEXTURE 2D ) ;

scissorblur ( i n t &x , i n t &y , i n t &w, i n t &h )

i f ( renderpath ! =R FIXEDFUNCTION ) setenvparamf ( ” shadowmapbias ” , SHPARAM VERTEX, 0 , −shadowmapbias/ f l o a t ( shadowmapdist ) , 1 − ( shadowmapbias + ( smoothshadowmappeel ? 0 : shadowmappeelbias ) ) / f l o a t ( shadowmapdist ) ) ; e l s e glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ;

= max( i n t ( f l o o r ( ( scissorx1 +1)/2∗vieww ) ) − 2∗blursize , 2) ; = max( i n t ( f l o o r ( ( scissory1 +1)/2∗viewh ) ) − 2∗blursize , 2) ; = min ( i n t ( c e i l ( ( scissorx2 +1)/2∗vieww ) ) + 2∗blursize , vieww−2) − x ; h = min ( i n t ( c e i l ( ( scissory2 +1)/2∗viewh ) ) + 2∗blursize , viewh−2) − y ; return true ;

shadowmapcasters = 0; shadowmapmaxz = shadowfocus . z − shadowmapdist ; shadowmapping = true ; rendergame ( ) ; shadowmapping = f a l s e ; shadowmapmaxz = min ( shadowmapmaxz, shadowfocus . z ) ;

} bool scissorrender ( i n t &x , i n t &y , i n t &w, i n t &h ) { x = y = 2; w = vieww − 2∗2; h = viewh − 2∗2; return true ; }

glEnable ( GL TEXTURE 2D ) ; i f ( renderpath==R FIXEDFUNCTION ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; e l s e i f ( shadowmapcasters && smdepthpeel ) { i n t sx , sy , sw, sh ; bool s c i s s o r i n g = r t s c i s s o r && scissorblur ( sx , sy , sw, sh ) && sw > 0 && sh > 0; i f ( scissoring ) { i f ( ! hasFBO ) { sx += screen−>w−vieww ; sy += screen−>h−viewh ; } g l S c i s s o r ( sx , sy , sw, sh ) ; } i f ( ! r t s c i s s o r || s c i s s o r i n g ) rendershadowmapreceivers ( ) ; }

void doclear ( ) { i f ( ! hasFBO && r t s c i s s o r ) { glEnable ( GL SCISSOR TEST ) ; g l S c i s s o r ( screen−>w−vieww , screen−>h−viewh , vieww , viewh ) ; } glClearColor ( 0 , 0 , 0 , 0) ; glClear ( GL DEPTH BUFFER BIT | ( renderpath ! =R FIXEDFUNCTION ? GL COLOR BUFFER BIT : 0) ) ; i f ( ! hasFBO && r t s c i s s o r ) glDisable ( GL SCISSOR TEST ) ; } bool dorender ( ) { // nvidia bug , must push modelview here , then switch to projection , then back to modelview before can s a f e l y modify i t glPushMatrix ( ) ; glMatrixMode ( GL PROJECTION ) ; glPushMatrix ( ) ; glLoadIdentity ( ) ; glOrtho(−shadowmapradius , shadowmapradius , −shadowmapradius , shadowmapradius , renderpath==R FIXEDFUNCTION ? 0 : −shadowmapdist , renderpath==R FIXEDFUNCTION ? ffshadowmapdist : shadowmapdist ) ; glMatrixMode (GL MODELVIEW) ; vec skewdir ( shadowdir ) ; skewdir . rotate around z(−camera1−>yaw∗RAD) ; vec d i r ; vecfromyawpitch ( camera1−>yaw , camera1−>pitch , 1 , 0 , d i r ) ; d i r . z = 0; d i r . mul ( shadowmapradius ) ; vec dirx , d i r y ; vecfromyawpitch ( camera1−>yaw , 0 , 0 , 1 , dirx ) ; vecfromyawpitch ( camera1−>yaw , 0 , 1 , 0 , d i r y ) ; shadowoffset . x = −fmod ( dirx . dot ( camera1−>o ) − skewdir . x∗camera1−>o . z , 2.0 f∗shadowmapradius/vieww ) ; shadowoffset . y = −fmod ( d i r y . dot ( camera1−>o ) − skewdir . y∗camera1−>o . z , 2.0 f∗shadowmapradius/viewh ) ;

glMatrixMode ( GL PROJECTION ) ; glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; glPopMatrix ( ) ; return shadowmapcasters>0; } bool flipdebug ( ) const { return f a l s e ; } void dodebug ( i n t w, i n t h ) { i f ( shadowmapcasters ) { glColorMask ( GL TRUE, GL FALSE, GL FALSE, GL FALSE ) ; debugscissor (w, h ) ; glColorMask ( GL FALSE, GL FALSE, GL TRUE, GL FALSE ) ; debugblurtiles (w, h ) ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; } } } shadowmaptex ; void cleanshadowmap ( ) { shadowmaptex . cleanup ( true ) ; } VAR( ffsmscissor , 0 , 1 , 1) ;

GLfloat skew [ ] = { 1, 0, 0, 0, 0, 1, 0, 0, skewdir . x , skewdir . y , 1 , 0 , 0, 0, 0, 1 }; glLoadMatrixf ( skew ) ; g l T r a n s l a t e f ( skewdir . x∗shadowmapheight + shadowoffset . x , skewdir . y∗ shadowmapheight + shadowoffset . y + d i r . magnitude ( ) , −

463

s t a t i c void calcscissorbox ( ) { i n t smx, smy, smw, smh; shadowmaptex . scissorblur ( smx, smy, smw, smh) ; vec forward , r i g h t ; vecfromyawpitch ( camera1−>yaw , 0 , −1, 0 , forward ) ; vecfromyawpitch ( camera1−>yaw , 0 , 0 , −1, r i g h t ) ; forward . mul ( shadowmapradius∗2.0 f /shadowmaptex . viewh ) ;

464

Foundations of Videogame Programming Code Repository

r i g h t . mul ( shadowmapradius∗2.0 f /shadowmaptex . vieww ) ;

i f ( ! shadowmap || ! shadowmapcasters ) return f a l s e ;

vec bottom ( shadowfocus ) ; bottom . sub ( vec ( shadowdir ) . mul ( shadowmapdist ) ) ; bottom . add ( vec ( forward ) . mul ( smy − shadowmaptex . viewh /2) ) . add ( vec ( r i g h t ) . mul (smx − shadowmaptex . vieww/2) ) ; vec top ( bottom ) ; top . add ( vec ( shadowdir ) . mul ( shadowmapmaxz − ( shadowfocus . z − shadowmapdist ) ) ) ;

i f ( va−>shadowmapmax. z shadowmapmin. z >= shadowmapmaxz ) return f a l s e ;

vec4 v [ 8 ] ; f l o a t sx1 = 1 , sy1 = 1 , sx2 = −1, sy2 = −1; loopi ( 8 ) { vec c = i&4 ? top : bottom ; i f ( i &1) c . add ( vec ( r i g h t ) . mul (smw) ) ; i f ( i &2) c . add ( vec ( forward ) . mul (smh) ) ; i f ( r e f l e c t i n g ) c . z = 2∗r e f l e c t z − c . z ; vec4 &p = v [ i ] ; mvpmatrix . transform ( c , p ) ; i f ( p . z >= −p .w) { f l o a t x = p . x / p .w, y = p . y / p .w; sx1 = min ( sx1 , x ) ; sy1 = min ( sy1 , y ) ; sx2 = max( sx2 , x ) ; sy2 = max( sy2 , y ) ; } } i f ( sx1 >= sx2 || sy1 >= sy2 ) return ; loopi ( 8 ) { const vec4 &p = v [ i ] ; i f ( p . z >= −p .w) continue ; loopj ( 3 ) { const vec4 &o = v [ iˆ(1yaw+180)∗RAD) ; ro . x += ro . z ∗ skewdir . x + shadowoffset . x ; ro . y += ro . z ∗ skewdir . y + shadowmapradius ∗ cosf ( camera1−>pitch∗RAD) + shadowoffset . y ;

f l o a t xyrad = SQRT2∗0.5 f∗max( va−>shadowmapmax. x−va−>shadowmapmin. x , va −>shadowmapmax. y−va−>shadowmapmin. y ) , zrad = 0.5 f ∗(va−>shadowmapmax. z−va−>shadowmapmin. z ) , x1 , y1 , x2 , y2 ; i f ( xyradshadowmapmax. tovec ( ) ) . mul( 0 . 5 f ) ; calcshadowmapbb ( center , xyrad , zrad , x1 , y1 , x2 , y2 ) ; return shadowmaptex . c h e c k b l u r t i l e s ( x1 , y1 , x2 , y2 , 2) ; #if 0 // cheaper inexact t e s t f l o a t dz = va−>o . z + va−>s i z e /2 − shadowfocus . z ; f l o a t cx = shadowfocus . x + dz∗shadowdir . x , cy = shadowfocus . y + dz∗ shadowdir . y ; f l o a t skew = va−>s i z e/2∗SHADOWSKEW; i f ( ! shadowmap || ! shadowmaptex || va−>o . z + va−>s i z e o . z >= shadowmapmaxz || va−>o . x + va−>s i z e o . x >= cx + shadowmapradius+skew || va−>o . y + va−>s i z e o . y >= cy + shadowmapradius+skew ) return f a l s e ; return true ; #endif } bool isshadowmapcaster ( const vec &o , f l o a t rad ) { // cheaper inexact t e s t f l o a t dz = o . z − shadowfocus . z ; f l o a t cx = shadowfocus . x + dz∗shadowdir . x , cy = shadowfocus . y + dz∗ shadowdir . y ; f l o a t skew = rad∗SHADOWSKEW; i f ( ! shadowmapping || o . z + rad = shadowfocus . z || o . x + rad = cx + shadowmapradius+skew || o . y + rad = cy + shadowmapradius+skew ) return f a l s e ; return true ; } void pushshadowmap ( ) { i f ( ! shadowmap || ! shadowmaptex . rendertex ) return ; i f ( renderpath==R FIXEDFUNCTION ) { glBindTexture ( GL TEXTURE 2D, shadowmaptex . rendertex ) ; const GLfloat ∗v = GLfloat texgenS [ 4 ] texgenT [ 4 ] = texgenR [ 4 ] =

vec high ( ro ) , low ( ro ) ; high . x += zrad ∗ skewdir . x ; high . y += zrad ∗ skewdir . y ; low . x −= zrad ∗ skewdir . x ; low . y −= zrad ∗ skewdir . y ; x1 y1 x2 y2

= = = =

( min ( high . x , ( min ( high . y , (max( high . x , (max( high . y ,

low . x ) low . y ) low . x ) low . y )

− − + +

shadowmapmatrix . v ; = { v [ 0 ] , v [ 4 ] , v [ 8 ] , v [ 1 2 ] }, { v [ 1 ] , v [ 5 ] , v [ 9 ] , v [ 1 3 ] }, { v [ 2 ] , v [ 6 ] , v [ 1 0 ] , v [ 1 4 ] };

glTexGeni ( GL S , GL TEXTURE GEN MODE, GL OBJECT LINEAR ) ; glTexGenfv ( GL S , GL OBJECT PLANE, texgenS ) ; glEnable ( GL TEXTURE GEN S ) ; xyrad ) xyrad ) xyrad ) xyrad )

/ / / /

shadowmapradius ; shadowmapradius ; shadowmapradius ; shadowmapradius ;

glTexGeni ( GL T , GL TEXTURE GEN MODE, GL OBJECT LINEAR ) ; glTexGenfv ( GL T , GL OBJECT PLANE, texgenT ) ; glEnable ( GL TEXTURE GEN T ) ;

}

glTexGeni ( GL R , GL TEXTURE GEN MODE, GL OBJECT LINEAR ) ; glTexGenfv ( GL R , GL OBJECT PLANE, texgenR ) ; glEnable ( GL TEXTURE GEN R ) ;

bool addshadowmapcaster ( const vec &o , f l o a t xyrad , f l o a t zrad ) { i f ( o . z + zrad = shadowfocus . z ) return f a l s e ; shadowmapmaxz = max( shadowmapmaxz, o . z + zrad ) ;

// i n t e l d r i v e r bug workaround : when R texgen i s enabled , i t uses the value o f Q, even i f not enabled ! // MUST set Q with glTexCoord4f , glTexCoord3f does not work glTexCoord4f ( 0 , 0 , 0 , 1) ;

f l o a t x1 , y1 , x2 , y2 ; calcshadowmapbb ( o , xyrad , zrad , x1 , y1 , x2 , y2 ) ;

g l C o l o r 3 f ( shadowmapintensity/100.0 f , shadowmapintensity/100.0 f , shadowmapintensity/100.0 f ) ;

i f ( ! shadowmaptex . a d d b l u r t i l e s ( x1 , y1 , x2 , y2 , 2) ) return f a l s e ; shadowmapcasters++; return true ; } bool isshadowmapreceiver ( vtxarray ∗va ) {

i f ( ffsmscissor ) calcscissorbox ( ) ; return ; } g l A c t i v e T e x t u r e ( GL TEXTURE7 ARB ) ; glBindTexture ( GL TEXTURE 2D, shadowmaptex . rendertex ) ; g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ;

engine/skelmodel.h glMatrixMode (GL TEXTURE) ; glLoadMatrixf ( shadowmapmatrix . v ) ; glMatrixMode (GL MODELVIEW) ;

{ i f ( ! shadowmap || ! shadowmaptex . rendertex ) return ; i f ( renderpath==R FIXEDFUNCTION ) { popscissor ( ) ;

g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; float r , g , b; i f ( ! shadowmapambient ) { i f ( s k y l i g h t c o l o r [ 0 ] || s k y l i g h t c o l o r [ 1 ] || s k y l i g h t c o l o r [2]) { r = max(25.0 f , 0.4 f∗ambientcolor [ 0 ] + 0.6 f∗max( ambientcolor [ 0 ] , s k y l i g h t c o l o r [ 0 ] ) ) ; g = max(25.0 f , 0.4 f∗ambientcolor [ 1 ] + 0.6 f∗max( ambientcolor [ 1 ] , s k y l i g h t c o l o r [ 1 ] ) ) ; b = max(25.0 f , 0.4 f∗ambientcolor [ 2 ] + 0.6 f∗max( ambientcolor [ 2 ] , s k y l i g h t c o l o r [ 2 ] ) ) ; } else { r = max(25.0 f , 2.0 f∗ambientcolor [ 0 ] ) ; g = max(25.0 f , 2.0 f∗ambientcolor [ 1 ] ) ; b = max(25.0 f , 2.0 f∗ambientcolor [ 2 ] ) ; } } e l s e { r = shadowmapambientcolor [ 0 ] ; g = shadowmapambientcolor [ 1 ] ; b = shadowmapambientcolor [ 2 ] ; } setenvparamf ( ” shadowmapambient ” , SHPARAM PIXEL, 7 , r /255.0 f , g/255.0 f , b/255.0 f ) ; }

465

glDisable ( GL TEXTURE GEN S ) ; glDisable ( GL TEXTURE GEN T ) ; glDisable ( GL TEXTURE GEN R ) ; } } void rendershadowmap ( ) { i f ( ! shadowmap || ( renderpath==R FIXEDFUNCTION && ( ! hasSGIDT || ! hasSGISH ) ) ) return ; // Apple/ATI bug − fixed−function fog s t a t e can f o r c e software f a l l b a c k even when fragment program i s enabled glDisable (GL FOG) ; shadowmaptex . render(1 255) { v . weights [ k ]−−; t o t a l−−; } }

466

Foundations of Videogame Programming Code Repository while ( t o t a l < 255) { loopk ( 4 ) i f ( v . weights [ k ] < 255 && t o t a l < 255) { v . weights [ k ] + + ; t o t a l ++; } } loopk ( 4 ) v . bones [ k ] = ( matskel ? 3 : 2)∗interpbones [ k ] ; }

} void smoothnorms ( f l o a t l i m i t = 0 , bool areaweight = true ) { mesh : : smoothnorms ( verts , numverts , t r i s , numtris , l i m i t , areaweight ) ; }

} };

struct animcacheentry { animstate as [MAXANIMPARTS ] ; f l o a t pitch ; int millis ; uchar ∗partmask ; ragdolldata ∗r a g d o l l ; animcacheentry ( ) : r a g d o l l (NULL) { loopk (MAXANIMPARTS) as [ k ] . cur . f r 1 = as [ k ] . prev . f r 1 = −1; } bool operator ==( const animcacheentry &c ) const { l o o p i (MAXANIMPARTS) i f ( as [ i ] ! = c . as [ i ] ) return f a l s e ; return pitch==c . pitch && partmask==c . partmask && r a g d o l l ==c . r a g d o l l && ( ! r a g d o l l || min ( m i l l i s , c . m i l l i s ) >= ragdoll −>lastmove ) ; } }; struct vbocacheentry : animcacheentry { uchar ∗vdata ; GLuint vbuf ; i n t owner ;

void buildnorms ( bool areaweight = true ) { mesh : : buildnorms ( verts , numverts , t r i s , numtris , areaweight ) ; } void calctangents ( bool areaweight = true ) { i f ( bumpverts ) return ; bumpverts = new bumpvert [ numverts ] ; mesh : : calctangents ( bumpverts , verts , verts , numverts , t r i s , numtris , areaweight ) ; } void calcbb ( i n t frame , vec &bbmin , vec &bbmax, const matrix3x4 &m) { l o o p j ( numverts ) { vec v = m. transform ( v e r t s [ j ] . pos ) ; loopi ( 3 ) { bbmin [ i ] = min ( bbmin [ i ] , v [ i ] ) ; bbmax[ i ] = max(bbmax[ i ] , v [ i ] ) ; } } } void g e n t r i s ( i n t frame , Texture ∗tex , vector ∗out , const matrix3x4 &m) { l o o p j ( numtris ) { BIH : : t r i &t = out [ noclip ? 1 : 0 ] . add ( ) ; t . tex = tex ; v e r t &av = v e r t s [ t r i s [ j ] . v e r t [ 0 ] ] , &bv = v e r t s [ t r i s [ j ] . v e r t [ 1 ] ] , &cv = v e r t s [ t r i s [ j ] . v e r t [ 2 ] ] ; t . a = m. transform ( av . pos ) ; t . b = m. transform ( bv . pos ) ; t . c = m. transform ( cv . pos ) ; t . t c [ 0 ] = av . u ; t . t c [ 1 ] = av . v ; t . t c [ 2 ] = bv . u ; t . t c [ 3 ] = bv . v ; t . t c [ 4 ] = cv . u ; t . t c [ 5 ] = cv . v ; }

vbocacheentry ( ) : vdata (NULL) , vbuf ( 0 ) , owner(−1) {} }; struct skelcacheentry : animcacheentry { dualquat ∗bdata ; matrix3x4 ∗mdata ; i n t version ; bool d i r t y ; skelcacheentry ( ) : bdata (NULL) , mdata (NULL) , version (−1) , d i r t y ( f a l s e ) {} void nextversion ( ) { version = Shader : : uniformlocversion ( ) ; d i r t y = true ; } }; struct blendcacheentry : skelcacheentry { i n t owner ; blendcacheentry ( ) : owner(−1) {} }; struct skelmeshgroup ; struct skelmesh : mesh { v e r t ∗v e r t s ; bumpvert ∗bumpverts ; t r i ∗t r i s ; i n t numverts , numtris , maxweights ; i n t v o f f s e t , e o f f s e t , elen ; ushort minvert , maxvert ; skelmesh ( ) : v e r t s (NULL) , bumpverts (NULL) , t r i s (NULL) , numverts ( 0 ) , numtris ( 0 ) , maxweights ( 0 ) { } v i r t u a l ˜ skelmesh ( ) { DELETEA( v e r t s ) ; DELETEA( bumpverts ) ; DELETEA( t r i s ) ; } i n t addblendcombo ( const blendcombo &c ) { maxweights = max( maxweights , c . s i z e ( ) ) ; return ( ( skelmeshgroup ∗)group )−>addblendcombo ( c ) ;

} s t a t i c i n l i n e bool comparevert ( v v e r t &w, i n t j , v e r t &v ) { return v . u==w. u && v . v==w. v && v . pos==w. pos ; } s t a t i c i n l i n e bool comparevert ( vvertn &w, i n t j , v e r t &v ) { return v . u==w. u && v . v==w. v && v . pos==w. pos && v . norm==w. norm ; } i n l i n e bool comparevert ( vvertbump &w, i n t j , v e r t &v ) { return v . u==w. u && v . v==w. v && v . pos==w. pos && v . norm==w. norm && ( ! bumpverts || ( bumpverts [ j ] . tangent==w. tangent && bumpverts [ j ] . bitangent==w. bitangent ) ) ; } s t a t i c i n l i n e void assignvert ( v v e r t &vv , i n t j , v e r t &v , blendcombo &c ) { vv . pos = v . pos ; vv . u = v . u ; vv . v = v . v ; } s t a t i c i n l i n e void assignvert ( vvertn &vv , i n t j , v e r t &v , blendcombo &c ) { vv . pos = v . pos ; vv . norm = v . norm ; vv . u = v . u ; vv . v = v . v ; } i n l i n e void assignvert ( vvertbump &vv , i n t j , v e r t &v , blendcombo &c ) { vv . pos = v . pos ;

engine/skelmodel.h vv . norm = v . norm ; vv . u = v . u ; vv . v = v . v ; i f ( bumpverts ) { vv . tangent = bumpverts [ j ] . tangent ; vv . bitangent = bumpverts [ j ] . bitangent ; } else { vv . tangent = vec ( 0 , 0 , 0) ; vv . bitangent = 0; }

} i n t genvbo ( vector &idxs , i n t o f f s e t ) { l o o p i ( numverts ) v e r t s [ i ] . interpindex = ( ( skelmeshgroup ∗)group ) −>remapblend ( v e r t s [ i ] . blend ) ; voffset = offset ; e o f f s e t = idxs . length ( ) ; l o o p i ( numtris ) { t r i &t = t r i s [ i ] ; l o o p j ( 3 ) idxs . add ( v o f f s e t + t . v e r t [ j ] ) ; } minvert = v o f f s e t ; maxvert = v o f f s e t + numverts−1; elen = idxs . length ( )−e o f f s e t ; return numverts ;

} s t a t i c i n l i n e void assignvert ( vvertw &vv , i n t j , v e r t &v , blendcombo &c ) { vv . pos = v . pos ; vv . norm = v . norm ; vv . u = v . u ; vv . v = v . v ; c . s e r i a l i z e ( vv ) ; } i n l i n e void assignvert ( vvertbumpw &vv , i n t j , v e r t &v , blendcombo & c) { vv . pos = v . pos ; vv . norm = v . norm ; vv . u = v . u ; vv . v = v . v ; i f ( bumpverts ) { vv . tangent = bumpverts [ j ] . tangent ; vv . bitangent = bumpverts [ j ] . bitangent ; } else { vv . tangent = vec ( 0 , 0 , 0) ; vv . bitangent = 0; } c . s e r i a l i z e ( vv ) ; } template i n t genvbo ( vector &idxs , i n t o f f s e t , vector &v v e r t s ) { voffset = offset ; e o f f s e t = idxs . length ( ) ; l o o p i ( numverts ) { v e r t &v = v e r t s [ i ] ; assignvert ( v v e r t s . add ( ) , i , v , ( ( skelmeshgroup ∗)group )−> blendcombos [ v . blend ] ) ; } l o o p i ( numtris ) l o o p j ( 3 ) idxs . add ( v o f f s e t + t r i s [ i ] . v e r t [ j ] ) ; elen = idxs . length ( )−e o f f s e t ; minvert = v o f f s e t ; maxvert = v o f f s e t + numverts−1; return numverts ; } template i n t genvbo ( vector &idxs , i n t o f f s e t , vector &vverts , i n t ∗htdata , i n t htlen ) { voffset = offset ; e o f f s e t = idxs . length ( ) ; minvert = 0xFFFF ; l o o p i ( numtris ) { t r i &t = t r i s [ i ] ; loopj ( 3 ) { i n t index = t . v e r t [ j ] ; v e r t &v = v e r t s [ index ] ; i n t htidx = hthash ( v . pos ) &( htlen−1); loopk ( htlen ) { i n t &vidx = htdata [ ( htidx+k ) &( htlen−1) ] ; i f ( vidx < 0) { vidx = idxs . add ( ushort ( v v e r t s . length ( ) ) ) ; assignvert ( v v e r t s . add ( ) , index , v , ( ( skelmeshgroup ∗)group )−>blendcombos [ v . blend ] ) ; break ; } e l s e i f ( comparevert ( v v e r t s [ vidx ] , index , v ) ) { minvert = min ( minvert , idxs . add ( ushort ( vidx ) ) ) ; break ; } } } } elen = idxs . length ( )−e o f f s e t ; minvert = min ( minvert , ushort ( v o f f s e t ) ) ; maxvert = max( minvert , ushort ( v v e r t s . length ( ) −1) ) ; return v v e r t s . length ( )−v o f f s e t ;

467

} void f i l l t c ( uchar ∗vdata , s i z e t s t r i d e ) { vdata = ( uchar ∗) & ( ( v v e r t ∗)&vdata [ v o f f s e t∗s t r i d e ] )−>u ; l o o p i ( numverts ) { ( ( f l o a t ∗) vdata ) [ 0 ] = v e r t s [ i ] . u ; ( ( f l o a t ∗) vdata ) [ 1 ] = v e r t s [ i ] . v ; vdata += s t r i d e ; } } void fillbump ( uchar ∗vdata , s i z e t s t r i d e ) { i f ( s t r i d e == s i z e o f ( vvertbumpw ) ) vdata = ( uchar ∗) & ( (vvertbumpw ∗) &vdata [ v o f f s e t∗s t r i d e ] )−>tangent ; e l s e vdata = ( uchar ∗) & ( ( vvertbump ∗)&vdata [ v o f f s e t∗s t r i d e ] )−> tangent ; i f ( bumpverts ) l o o p i ( numverts ) { ( ( bumpvert ∗) vdata )−>bitangent = bumpverts [ i ] . bitangent ; vdata += s t r i d e ; } e l s e l o o p i ( numverts ) { memset ( vdata , 0 , s i z e o f ( bumpvert ) ) ; vdata += s t r i d e ; } } template void i n t e r p v e r t s ( const M ∗ RESTRICT mdata1 , const M ∗ RESTRICT mdata2 , bool norms , bool tangents , void ∗ RESTRICT vdata , skin &s ) { const i n t b l e n d o f f s e t = ( ( skelmeshgroup ∗)group )−>skel−> numgpubones ; mdata2 −= b l e n d o f f s e t ; #define IPLOOP ( type , dosetup , dotransform ) \ l o o p i ( numverts ) \ { \ const v e r t &src = v e r t s [ i ] ; \ type &dst = ( ( type ∗ RESTRICT ) vdata ) [ i ] ; \ dosetup ; \ const M &m = ( src . interpindex < b l e n d o f f s e t ? mdata1 : mdata2 ) [ src . interpindex ] ; \ dst . pos = m. transform ( src . pos ) ; \ dotransform ; \ } i f ( tangents ) { i f ( bumpverts ) { IPLOOP ( vvertbump , bumpvert &bsrc = bumpverts [ i ] , { dst . norm = m. transformnormal ( src . norm ) ; dst . tangent = m. transformnormal ( bsrc . tangent ) ; }) ; } e l s e { IPLOOP ( vvertbump , , dst . norm = m. transformnormal ( src . norm ) ) ; } } e l s e i f ( norms ) { IPLOOP ( vvertn , , dst . norm = m. transformnormal ( src . norm ) ) ; } e l s e { IPLOOP ( vvert , , ) ; } #undef IPLOOP } void setshader ( Shader ∗s ) { skelmeshgroup ∗g = ( skelmeshgroup ∗)group ; i f ( glaring )

468

Foundations of Videogame Programming Code Repository {

{ i f ( ! g−>skel−>usegpuskel ) s−>s e t v a r i a n t ( 0 , 2) ; e l s e i f ( g−>skel−>usematskel ) s−>s e t v a r i a n t ( min ( maxweights , g −>vweights ) , 2) ; e l s e s−>s e t v a r i a n t ( min ( maxweights , g−>vweights )−1, 3) ;

i f ( s . multitextured ( ) ) { glPopMatrix ( ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; }

} e l s e i f ( ! g−>skel−>usegpuskel ) s−>set ( ) ; e l s e i f ( g−>skel−>usematskel ) s−>s e t v a r i a n t ( min ( maxweights , g−> vweights )−1, 0) ; e l s e s−>s e t v a r i a n t ( min ( maxweights , g−>vweights )−1, 1) ;

glPopMatrix ( ) ; glMatrixMode (GL MODELVIEW) ; }

} return ; void render ( const animstate ∗as , skin &s , vbocacheentry &vc ) { i f ( ! ( as−>cur . anim&ANIM NOSKIN ) ) { i f ( s . multitextured ( ) ) { i f ( ! enablemtc || lastmtcbuf ! = lastvbuf ) { g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; i f ( ! enablemtc ) glEnableClientState ( GL TEXTURE COORD ARRAY) ; i f ( lastmtcbuf ! = lastvbuf ) { v v e r t ∗v v e r t s = hasVBO ? 0 : ( v v e r t ∗) vc . vdata ; glTexCoordPointer ( 2 , GL FLOAT, ( ( skelmeshgroup ∗) group )−>v e r t s i z e , &vverts−>u ) ; } g l C l i e n t A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; lastmtcbuf = lastvbuf ; enablemtc = true ; } } e l s e i f ( enablemtc ) disablemtc ( ) ; i f ( s . tangents ( ) ) { i f ( ! enabletangents || lastxbuf ! = lastvbuf ) { i f ( ! enabletangents ) glEnableVertexAttribArray ( 1 ) ; i f ( lastxbuf ! = lastvbuf ) { i f ( ( ( skelmeshgroup ∗)group )−>v e r t s i z e == s i z e o f ( vvertbumpw ) ) { vvertbumpw ∗v v e r t s = hasVBO ? 0 : ( vvertbumpw ∗) vc . vdata ; g l V e r t e x A t t r i b P o i n t e r ( 1 , 4 , GL FLOAT, GL FALSE, ( ( skelmeshgroup ∗)group )−>v e r t s i z e , & vverts−>tangent . x ) ; } else { vvertbump ∗v v e r t s = hasVBO ? 0 : ( vvertbump ∗) vc . vdata ; g l V e r t e x A t t r i b P o i n t e r ( 1 , 4 , GL FLOAT, GL FALSE, ( ( skelmeshgroup ∗)group )−>v e r t s i z e , & vverts−>tangent . x ) ; } } lastxbuf = lastvbuf ; enabletangents = true ; } } e l s e i f ( enabletangents ) disabletangents ( ) ; i f ( renderpath==R FIXEDFUNCTION && ( s . s c r o l l u || s . s c r o l l v ) ) { glMatrixMode (GL TEXTURE) ; glPushMatrix ( ) ; g l T r a n s l a t e f ( s . s c r o l l u∗l a s t m i l l i s /1000.0 f , s . s c r o l l v∗ l a s t m i l l i s /1000.0 f , 0) ; i f ( s . multitextured ( ) ) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glPushMatrix ( ) ; g l T r a n s l a t e f ( s . s c r o l l u∗l a s t m i l l i s /1000.0 f , s . s c r o l l v∗ l a s t m i l l i s /1000.0 f , 0) ; }

} };

struct tag { char ∗name; i n t bone ; matrix3x4 matrix ; tag ( ) : name(NULL) {} ˜ tag ( ) { DELETEA(name) ; } }; struct skelanimspec { char ∗name; i n t frame , range ; skelanimspec ( ) : name(NULL) , frame ( 0 ) , range ( 0 ) {} ˜ skelanimspec ( ) { DELETEA(name) ; } }; struct boneinfo { const char ∗name; i n t parent , children , next , group , scheduled , interpindex , interpparent , ragdollindex , correctindex ; f l o a t pitchscale , p i t c h o f f s e t , pitchmin , pitchmax ; dualquat base , invbase ; boneinfo ( ) : name(NULL) , parent(−1) , children (−1) , next(−1) , group ( INT MAX ) , scheduled(−1) , interpindex (−1) , interpparent(−1) , ragdollindex (−1) , correctindex (−1) , pitchscale ( 0 ) , p i t c h o f f s e t ( 0 ) , pitchmin ( 0 ) , pitchmax ( 0 ) {} ˜ boneinfo ( ) { DELETEA(name) ; } }; struct antipode { i n t parent , c h i l d ; antipode ( i n t parent , i n t c h i l d ) : parent ( parent ) , c h i l d ( c h i l d ) {} }; struct pitchdep { i n t bone , parent ; dualquat pose ; }; struct p i t c h t a r g e t { i n t bone , frame , corrects , deps ; f l o a t pitchmin , pitchmax , deviated ; dualquat pose ; }; struct p i t c h c o r r e c t { i n t bone , target , parent ; f l o a t pitchmin , pitchmax , pitchscale , pitchangle , p i t c h t o t a l ; };

} } i f (hasDRE) glDrawRangeElements ( GL TRIANGLES, minvert , maxvert , elen , GL UNSIGNED SHORT, & ( ( skelmeshgroup ∗)group )−>edata [ eoffset ] ) ; e l s e glDrawElements ( GL TRIANGLES, elen , GL UNSIGNED SHORT, & ( ( skelmeshgroup ∗)group )−>edata [ e o f f s e t ] ) ; glde ++; xtravertsva += numverts ; i f ( renderpath==R FIXEDFUNCTION && ! ( as−>cur . anim&ANIM NOSKIN ) && ( s . s c r o l l u || s . s c r o l l v ) )

struct skeleton { char ∗name; i n t shared ; vector users ; boneinfo ∗bones ; i n t numbones, numinterpbones , numgpubones, numframes ; dualquat ∗framebones ; vector skelanims ; vector tags ; vector antipodes ; r a g d o l l s k e l ∗r a g d o l l ;

engine/skelmodel.h

const boneinfo &i n f o = bones [ bone ] ; l o o p j ( numbones ) i f ( abs ( bones [ j ] . group ) == bone && bones [ j ] . scheduled < 0) { antipodes . add ( antipode ( i n f o . interpindex , bones [ j ] . interpindex ) ) ; bones [ j ] . scheduled = schedule . length ( ) ; schedule . add ( j ) ; } i f ( i + 1 == schedule . length ( ) ) { i n t c o n f l i c t = INT MAX ; l o o p j ( numbones ) i f ( bones [ j ] . group < numbones && bones [ j ] . scheduled < 0) c o n f l i c t = min ( c o n f l i c t , abs ( bones [ j ] . group ) ) ; i f ( c o n f l i c t < numbones ) { bones [ c o n f l i c t ] . scheduled = schedule . length ( ) ; schedule . add ( c o n f l i c t ) ; } }

vector pitchdeps ; vector p i t c h t a r g e t s ; vector p i t c h c o r r e c t s ; bool usegpuskel , usematskel ; vector skelcache ; hashtable b l e n d o f f s e t s ; skeleton ( ) : name(NULL) , shared ( 0 ) , bones (NULL) , numbones ( 0 ) , numinterpbones ( 0 ) , numgpubones ( 0 ) , numframes ( 0 ) , framebones ( NULL) , r a g d o l l (NULL) , usegpuskel ( f a l s e ) , usematskel ( f a l s e ) , blendoffsets (32) { } ˜ skeleton ( ) { DELETEA(name) ; DELETEA( bones ) ; DELETEA( framebones ) ; DELETEP( r a g d o l l ) ; loopv ( skelcache ) { DELETEA( skelcache [ i ] . bdata ) ; DELETEA( skelcache [ i ] . mdata ) ; } } skelanimspec ∗findskelanim ( const char ∗name, char sep = ’ \ 0 ’ ) { i n t len = sep ? s t r l e n (name) : 0; loopv ( skelanims ) { i f ( skelanims [ i ] . name) { i f ( sep ) { const char ∗end = strchr ( skelanims [ i ] . name, ’ : ’ ) ; i f ( end && end − skelanims [ i ] . name == len && !memcmp( name, skelanims [ i ] . name, len ) ) return &skelanims [ i ]; } i f ( ! strcmp ( name, skelanims [ i ] . name) ) return &skelanims [ i ] ; } } return NULL; } skelanimspec &addskelanim ( const char ∗name) { skelanimspec &sa = skelanims . add ( ) ; sa .name = name ? newstring (name) : NULL; return sa ; } i n t findbone ( const char ∗name) { l o o p i ( numbones ) i f ( bones [ i ] . name && ! strcmp ( bones [ i ] . name, name) ) return i ; return −1; } i n t f i n d t a g ( const char ∗name) { loopv ( tags ) i f ( ! strcmp ( tags [ i ] . name, name) ) return i ; return −1; } bool addtag ( const char ∗name, i n t bone , const matrix3x4 &matrix ) { i f ( f i n d t a g (name) >= 0) return f a l s e ; tag &t = tags . add ( ) ; t .name = newstring (name) ; t . bone = bone ; t . matrix = matrix ; return true ; } void calcantipodes ( ) { antipodes . shrink ( 0 ) ; vector schedule ; l o o p i ( numbones ) { i f ( bones [ i ] . group >= numbones ) { bones [ i ] . scheduled = schedule . length ( ) ; schedule . add ( i ) ; } e l s e bones [ i ] . scheduled = −1; } loopv ( schedule ) { i n t bone = schedule [ i ] ;

469

} } void remapbones ( ) { l o o p i ( numbones ) { boneinfo &i n f o = bones [ i ] ; i n f o . interpindex = −1; i n f o . ragdollindex = −1; } numgpubones = 0; loopv ( users ) { skelmeshgroup ∗group = users [ i ] ; l o o p v j ( group−>blendcombos ) { blendcombo &c = group−>blendcombos [ j ] ; loopk ( 4 ) { i f ( ! c . weights [ k ] ) { c . interpbones [ k ] = k > 0 ? c . interpbones [ k−1] : 0; continue ; } boneinfo &i n f o = bones [ c . bones [ k ] ] ; i f ( i n f o . interpindex < 0) i n f o . interpindex = numgpubones ++; c . interpbones [ k ] = i n f o . interpindex ; i f ( i n f o . group < 0) continue ; loopl ( 4 ) { i f ( ! c . weights [ l ] ) break ; i f ( l == k ) continue ; i n t parent = c . bones [ l ] ; i f ( i n f o . parent == parent || ( i n f o . parent >= 0 && i n f o . parent == bones [ parent ] . parent ) ) { i n f o . group = −i n f o . parent ; break ; } i f ( i n f o . group c h i l d ) parent = bones [ parent ] . parent ; i f ( parent ! = c h i l d ) i n f o . group = c . bones [ l ] ; } } } } numinterpbones = numgpubones ; loopv ( tags ) { boneinfo &i n f o = bones [ tags [ i ] . bone ] ; i f ( i n f o . interpindex < 0) i n f o . interpindex = numinterpbones++; } i f ( ragdoll ) { loopv ( ragdoll−>j o i n t s ) { boneinfo &i n f o = bones [ ragdoll−>j o i n t s [ i ] . bone ] ; i f ( i n f o . interpindex < 0) i n f o . interpindex = numinterpbones ++; i n f o . ragdollindex = i ; } } l o o p i ( numbones ) { boneinfo &i n f o = bones [ i ] ; i f ( i n f o . interpindex < 0) continue ; f o r ( i n t parent = i n f o . parent ; parent >= 0 && bones [ parent ] . interpindex < 0; parent = bones [ parent ] . parent ) bones [ parent ] . interpindex = numinterpbones++; } l o o p i ( numbones ) { boneinfo &i n f o = bones [ i ] ; i f ( i n f o . interpindex < 0) continue ; i n f o . interpparent = i n f o . parent >= 0 ? bones [ i n f o . parent ] . interpindex : −1;

470

Foundations of Videogame Programming Code Repository } i f ( ragdoll ) { l o o p i ( numbones ) { boneinfo &i n f o = bones [ i ] ; i f ( i n f o . interpindex < 0 || i n f o . ragdollindex >= 0) continue ; f o r ( i n t parent = i n f o . parent ; parent >= 0; parent = bones [ parent ] . parent ) { i f ( bones [ parent ] . ragdollindex >= 0) { ragdoll−> a d d r e l j o i n t ( i , bones [ parent ] . ragdollindex ) ; break ; } } } } calcantipodes ( ) ;

t . c o r r e c t s = f i n d p i t c h c o r r e c t ( parent ) ; i f ( t . c o r r e c t s >= 0) break ; } } loopv ( p i t c h c o r r e c t s ) { p i t c h c o r r e c t &c = p i t c h c o r r e c t s [ i ] ; bones [ c . bone ] . correctindex = i ; c . parent = −1; f o r ( i n t parent = c . bone ; ; ) { parent = bones [ parent ] . parent ; i f ( parent < 0) break ; c . parent = f i n d p i t c h c o r r e c t ( parent ) ; i f ( c . parent >= 0) break ; } } }

}

void addpitchdep ( i n t bone , i n t frame ) { f o r ( ; bone >= 0; bone = bones [ bone ] . parent ) { i n t pos = pitchdeps . length ( ) ; l o o p v j ( pitchdeps ) i f ( bone = 0) { t . deps = j ; t . pose = pitchdeps [ j ] . pose ; } t . c o r r e c t s = −1; f o r ( i n t parent = t . bone ; parent >= 0; parent = bones [ parent ] . parent ) {

void optimize ( ) { cleanup ( ) ; i f ( r a g d o l l ) ragdoll−>setup ( ) ; remapbones ( ) ; initpitchdeps ( ) ; } void expandbonemask ( uchar ∗expansion , i n t bone , i n t v a l ) { expansion [ bone ] = v a l ; bone = bones [ bone ] . children ; while ( bone>=0) { expandbonemask ( expansion , bone , v a l ) ; bone = bones [ bone ] . next ; } } void applybonemask ( ushort ∗mask, uchar ∗partmask , i n t partindex ) { i f ( ! mask || ∗mask==BONEMASK END) return ; uchar ∗expansion = new uchar [ numbones ] ; memset ( expansion , ∗mask&BONEMASK NOT ? 1 : 0 , numbones ) ; while (∗mask! =BONEMASK END) { expandbonemask ( expansion , ∗mask&BONEMASK BONE, ∗mask& BONEMASK NOT ? 0 : 1) ; mask++; } l o o p i ( numbones ) i f ( expansion [ i ] ) partmask [ i ] = partindex ; d e l e t e [ ] expansion ; } void l i n k c h i l d r e n ( ) { l o o p i ( numbones ) { boneinfo &b = bones [ i ] ; b . children = −1; i f ( b . parent= 0 ? p i t c h c o r r e c t s [ c . parent ] . pitchtotal : 0, avail = tpitch − total , used = t p i t c h∗c . pitchscale ; i f ( c . pitchmin || c . pitchmax ) { i f ( used < 0) used = clamp ( c . pitchmin , used , 0.0 f ) ; e l s e used = clamp ( c . pitchmax , 0.0 f , used ) ; } i f ( used < 0) used = clamp ( a v a i l , used , 0.0 f ) ; e l s e used = clamp ( a v a i l , 0.0 f , used ) ; c . pitchangle = used ; c . p i t c h t o t a l = used + t o t a l ; } } } #define INTERPBONE( bone ) \ const animstate &s = as [ partmask [ bone ] ] ; \ const framedata &f = partframes [ partmask [ bone ] ] ; \ dualquat d ; \ ( d = f . f r 1 [ bone ] ) . mul((1−s . cur . t )∗s . interp ) ; \ d . accumulate ( f . f r 2 [ bone ] , s . cur . t∗s . interp ) ; \ i f ( s . interp =0) \ { \ INTERPBONE( i ) ; \ const boneinfo &b = bones [ i ] ; \ outbody ; \ f l o a t angle ; \ i f ( b . pitchscale ) { angle = b . pitchscale∗pitch + b . p i t c h o f f s e t ; i f ( b . pitchmin || b . pitchmax ) angle = clamp ( angle , b . pitchmin , b . pitchmax ) ; } \ e l s e i f ( b . correctindex >= 0) angle = p i t c h c o r r e c t s [ b .

471 correctindex ] . pitchangle ; \ e l s e continue ; \ i f ( as−>cur . anim&ANIM NOPITCH || ( as−>interp < 1 && as−>prev . anim&ANIM NOPITCH ) ) \ angle ∗= ( as−>cur . anim&ANIM NOPITCH ? 0 : as−>interp ) + ( as−>interp < 1 && as−>prev . anim&ANIM NOPITCH ? 0 : 1−as−>interp ) ; \ rotbody ; \

} void interpmatbones ( const animstate ∗as , f l o a t pitch , const vec & axis , const vec &forward , i n t numanimparts , const uchar ∗ partmask , skelcacheentry &sc ) { i f ( ! sc . mdata ) sc . mdata = new matrix3x4 [ numinterpbones ] ; i f ( lastsdata == sc . mdata ) lastsdata = NULL; INTERPBONES( { matrix3x4 m( d ) ; i f ( b . interpparent j o i n t s [ i ] ; \ const boneinfo &b = bones [ j . bone ] ; \ const ptype &p = pdata [ b . interpindex ] ; \ loopk ( 3 ) i f ( j . v e r t [ k ] >= 0) \ { \ r a g d o l l s k e l : : v e r t &v = ragdoll−>v e r t s [ j . v e r t [ k ] ] ; \ ragdolldata : : v e r t &dv = d . v e r t s [ j . v e r t [ k ] ] ; \ dv . pos . add ( p . transform ( v . pos ) . mul ( v . weight ) ) ; \ } \ } \ i f ( ragdoll−>animjoints ) loopv ( ragdoll−>j o i n t s ) \ { \ const r a g d o l l s k e l : : j o i n t &j = ragdoll−>j o i n t s [ i ] ; \ const boneinfo &b = bones [ j . bone ] ; \ const ptype &p = pdata [ b . interpindex ] ; \ d . calcanimjoint ( i , p ) ; \ } \ loopv ( ragdoll−>v e r t s ) \ { \ ragdolldata : : v e r t &dv = d . v e r t s [ i ] ; \ matrixstack [ matrixpos ] . transform ( vec ( dv . pos ) . add ( p−>t r a n s l a t e ) . mul ( p−>model−>scale ) , dv . pos ) ; \ } \ loopv ( ragdoll−>r e l j o i n t s ) \ { \ const r a g d o l l s k e l : : r e l j o i n t &r = ragdoll−>r e l j o i n t s [ i ] ; \ const r a g d o l l s k e l : : j o i n t &j = ragdoll−>j o i n t s [ r . parent ] ; \ const boneinfo &br = bones [ r . bone ] , &bj = bones [ j . bone ] ; \ relbody ; \ } void i n i t m a t r a g d o l l ( ragdolldata &d , skelcacheentry &sc , part ∗p ) { INITRAGDOLL ( matrix3x4 , mdata , { d . r e l j o i n t s [ i ] . transposemul ( mdata [ bj . interpindex ] , mdata [ br . interpindex ] ) ; }) ; }

472

Foundations of Videogame Programming Code Repository

void i n i t r a g d o l l ( ragdolldata &d , skelcacheentry &sc , part ∗p ) { INITRAGDOLL ( dualquat , bdata , { dualquat q = bdata [ bj . interpindex ] ; q . i n v e r t ( ) . mul ( bdata [ br . interpindex ] ) ; d . r e l j o i n t s [ i ] = matrix3x4 ( q ) ; }) ; } #define GENRAGDOLLBONES( outbody , relbody ) \ sc . nextversion ( ) ; \ loopv ( ragdoll−>j o i n t s ) \ { \ const r a g d o l l s k e l : : j o i n t &j = ragdoll−>j o i n t s [ i ] ; \ const boneinfo &b = bones [ j . bone ] ; \ vec pos ( 0 , 0 , 0) ; \ loopk ( 3 ) i f ( j . v e r t [ k]>=0) pos . add ( d . v e r t s [ j . v e r t [ k ] ] . pos ) ; \ pos . mul ( j . weight/p−>model−>scale ) . sub ( p−>t r a n s l a t e ) ; \ outbody ; \ } \ loopv ( ragdoll−>r e l j o i n t s ) \ { \ const r a g d o l l s k e l : : r e l j o i n t &r = ragdoll−>r e l j o i n t s [ i ] ; \ const r a g d o l l s k e l : : j o i n t &j = ragdoll−>j o i n t s [ r . parent ] ; \ const boneinfo &br = bones [ r . bone ] , &bj = bones [ j . bone ] ; \ relbody ; \ } void genmatragdollbones ( ragdolldata &d , skelcacheentry &sc , part ∗p ) { i f ( ! sc . mdata ) sc . mdata = new matrix3x4 [ numinterpbones ] ; i f ( lastsdata == sc . mdata ) lastsdata = NULL; GENRAGDOLLBONES( { sc . mdata [ b . interpindex ] . transposemul ( d . t r i s [ j . t r i ] , pos , d . animjoints ? d . animjoints [ i ] : j . o r i e n t ) ; }, { sc . mdata [ br . interpindex ] . mul ( sc . mdata [ bj . interpindex ] , d . reljoints [ i ] ) ; }) ;

} } void cleanup ( bool f u l l = true ) { loopv ( skelcache ) { skelcacheentry &sc = skelcache [ i ] ; l o o p j (MAXANIMPARTS) sc . as [ j ] . cur . f r 1 = −1; DELETEA( sc . bdata ) ; DELETEA( sc . mdata ) ; } skelcache . s e t s i z e ( 0 ) ; blendoffsets . clear ( ) ; lastsdata = lastbdata = NULL; i f ( f u l l ) loopv ( users ) users [ i]−>cleanup ( ) ; } bool canpreload ( ) { return ! numframes || gpuaccelerate ( ) ; } void preload ( ) { i f ( ! numframes ) return ; i f ( skelcache . empty ( ) ) { usegpuskel = gpuaccelerate ( ) ; usematskel = matskel ! = 0 ; } } skelcacheentry &checkskelcache ( part ∗p , const animstate ∗as , f l o a t pitch , const vec &axis , const vec &forward , ragdolldata ∗ rdata ) { i f ( skelcache . empty ( ) ) { usegpuskel = gpuaccelerate ( ) ; usematskel = matskel ! = 0 ; } i n t numanimparts = ( ( skelpart ∗)as−>owner )−>numanimparts ; uchar ∗partmask = ( ( skelpart ∗)as−>owner )−>partmask ; skelcacheentry ∗sc = NULL; bool match = f a l s e ; loopv ( skelcache ) { skelcacheentry &c = skelcache [ i ] ; l o o p j ( numanimparts ) i f ( c . as [ j ] ! = as [ j ] ) goto mismatch ; i f ( c . pitch ! = pitch || c . partmask ! = partmask || c . r a g d o l l ! = rdata || ( rdata && c . m i l l i s < rdata−>lastmove ) ) goto mismatch ; match = true ; sc = &c ; break ; mismatch : i f ( c . m i l l i s < l a s t m i l l i s ) { sc = &c ; break ; } } i f ( ! sc ) sc = &skelcache . add ( ) ; i f ( ! match ) { l o o p i ( numanimparts ) sc−>as [ i ] = as [ i ] ; sc−>pitch = pitch ; sc−>partmask = partmask ; sc−>r a g d o l l = rdata ; i f ( rdata ) { i f ( matskel ) genmatragdollbones (∗ rdata , ∗sc , p ) ; e l s e genragdollbones (∗ rdata , ∗sc , p ) ; } e l s e i f ( matskel ) interpmatbones ( as , pitch , axis , forward , numanimparts , partmask , ∗sc ) ; e l s e interpbones ( as , pitch , axis , forward , numanimparts , partmask , ∗sc ) ; } sc−>m i l l i s = l a s t m i l l i s ; return ∗sc ;

} void genragdollbones ( ragdolldata &d , skelcacheentry &sc , part ∗p ) { i f ( ! sc . bdata ) sc . bdata = new dualquat [ numinterpbones ] ; i f ( lastsdata == sc . bdata ) lastsdata = NULL; GENRAGDOLLBONES( { matrix3x4 m; m. transposemul ( d . t r i s [ j . t r i ] , pos , d . animjoints ? d . animjoints [ i ] : j . o r i e n t ) ; sc . bdata [ b . interpindex ] = dualquat (m) ; }, { sc . bdata [ br . interpindex ] . mul ( sc . bdata [ bj . interpindex ] , dualquat ( d . r e l j o i n t s [ i ] ) ) ; }) ; loopv ( antipodes ) sc . bdata [ antipodes [ i ] . c h i l d ] . f i x a n t i p o d a l ( sc . bdata [ antipodes [ i ] . parent ] ) ; } void concattagtransform ( part ∗p , i n t frame , i n t i , const matrix3x4 &m, matrix3x4 &n ) { matrix3x4 t ; t . mul ( bones [ tags [ i ] . bone ] . base , tags [ i ] . matrix ) ; t . t r a n s l a t e ( vec ( p−>t r a n s l a t e ) . mul ( p−>model−>scale ) ) ; n . mul (m, t ) ; } void calctags ( part ∗p , skelcacheentry ∗sc = NULL) { loopv ( p−>l i n k s ) { linkedpart &l = p−>l i n k s [ i ] ; tag &t = tags [ l . tag ] ; matrix3x4 m; m. mul ( bones [ t . bone ] . base , t . matrix ) ; i f ( sc ) { i n t interpindex = bones [ t . bone ] . interpindex ; m. mul ( usematskel ? sc−>mdata [ interpindex ] : sc−>bdata [ interpindex ] , matrix3x4 (m) ) ; } l . matrix = m; l . matrix [ 1 2 ] = ( l . matrix [ 1 2 ] + p−>t r a n s l a t e . x ) ∗ p−>model−> scale ; l . matrix [ 1 3 ] = ( l . matrix [ 1 3 ] + p−>t r a n s l a t e . y ) ∗ p−>model−> scale ; l . matrix [ 1 4 ] = ( l . matrix [ 1 4 ] + p−>t r a n s l a t e . z ) ∗ p−>model−> scale ;

} void setasmbones ( skelcacheentry &sc , i n t count = 0) { i f ( sc . d i r t y ) sc . d i r t y = f a l s e ; e l s e i f ( ( count ? lastbdata : lastsdata ) == ( usematskel ? ( void ∗) sc . mdata : ( void ∗) sc . bdata ) ) return ; i n t o f f s e t = count ? numgpubones : 0; i f ( ! o f f s e t ) count = numgpubones ; i f ( hasPP ) { i f ( usematskel ) glProgramEnvParameters4fv ( GL VERTEX PROGRAM ARB, 10 + 3∗o f f s e t , 3∗count , sc . mdata [ 0 ] . a . v ) ; e l s e glProgramEnvParameters4fv (GL VERTEX PROGRAM ARB, 10 + 2∗o f f s e t , 2∗count , sc . bdata [ 0 ] . r e a l . v ) ; } e l s e i f ( usematskel ) l o o p i ( count )

engine/skelmodel.h

473

{ glProgramEnvParameter4fvARB o f f s e t + i ) , sc . mdata [ i glProgramEnvParameter4fvARB o f f s e t + i ) , sc . mdata [ i glProgramEnvParameter4fvARB o f f s e t + i ) , sc . mdata [ i

(GL VERTEX PROGRAM ARB, 10 + 3∗( ]. a.v) ; (GL VERTEX PROGRAM ARB, 11 + 3∗( ] . b. v ) ; (GL VERTEX PROGRAM ARB, 12 + 3∗( ].c.v) ;

} e l s e l o o p i ( count ) { glProgramEnvParameter4fvARB (GL VERTEX PROGRAM ARB, o f f s e t + i ) , sc . bdata [ i ] . r e a l . v ) ; glProgramEnvParameter4fvARB (GL VERTEX PROGRAM ARB, o f f s e t + i ) , sc . bdata [ i ] . dual . v ) ; } i f ( o f f s e t ) lastbdata = usematskel ? ( void ∗) sc . mdata : sc . bdata ; e l s e lastsdata = usematskel ? ( void ∗) sc . mdata : ( void bdata ;

10 + 2∗(

s t a t i c const i n t MAXBLENDCACHE = 16; blendcacheentry blendcache [MAXBLENDCACHE] ; s t a t i c const i n t MAXVBOCACHE = 16; vbocacheentry vbocache [MAXVBOCACHE] ; ushort ∗edata ; GLuint ebuf ; bool vnorms , vtangents ; i n t vlen , v e r t s i z e , vblends , vweights ; uchar ∗vdata ;

11 + 2∗(

( void ∗) ∗) sc .

skelmeshgroup ( ) : skel (NULL) , edata (NULL) , ebuf ( 0 ) , vnorms ( f a l s e ) , vtangents ( f a l s e ) , vlen ( 0 ) , v e r t s i z e ( 0 ) , vblends ( 0 ) , vweights ( 0 ) , vdata (NULL) { memset ( numblends , 0 , s i z e o f ( numblends ) ) ; }

} i n t g e t b l e n d o f f s e t ( UniformLoc &u ) { i n t &o f f s e t = b l e n d o f f s e t s . access ( Shader : : lastshader−>program , −1) ; i f ( o f f s e t < 0) { defformatstring ( offsetname ) (”%s[%d ] ” , u .name, ( usematskel ? 3 : 2)∗numgpubones ) ; o f f s e t = glGetUniformLocation ( Shader : : lastshader−>program , offsetname ) ; } return o f f s e t ; } void setglslbones ( UniformLoc &u, skelcacheentry &sc , skelcacheentry &bc , i n t count ) { i f ( u . version == bc . version && u . data == ( usematskel ? ( void ∗)bc . mdata : ( void ∗)bc . bdata ) ) return ; i f ( usematskel ) { glUniform4fv ( u . loc , 3∗numgpubones, sc . mdata [ 0 ] . a . v ) ; i f ( count > 0) { int offset = getblendoffset (u) ; i f ( o f f s e t >= 0) glUniform4fv ( o f f s e t , 3∗count , bc . mdata [0].a.v) ; } } else { glUniform4fv ( u . loc , 2∗numgpubones, sc . bdata [ 0 ] . r e a l . v ) ; i f ( count > 0) { int offset = getblendoffset (u) ; i f ( o f f s e t >= 0) glUniform4fv ( o f f s e t , 2∗count , bc . bdata [ 0 ] . real . v ) ; } } u . version = bc . version ; u . data = usematskel ? ( void ∗)bc . mdata : ( void ∗)bc . bdata ; } void setgpubones ( skelcacheentry &sc , blendcacheentry ∗bc , i n t count ) { i f ( ! Shader : : lastshader ) return ; i f ( Shader : : lastshader−>type & SHADER GLSLANG) { i f ( Shader : : lastshader−>uniformlocs . length ( ) < 1) return ; UniformLoc &u = Shader : : lastshader−>uniformlocs [ 0 ] ; setglslbones ( u, sc , bc ? ∗bc : sc , count ) ; } else { setasmbones ( sc ) ; i f ( bc ) setasmbones(∗bc , count ) ; } } bool shouldcleanup ( ) const { return numframes && ( skelcache . empty ( ) || gpuaccelerate ( ) ! = usegpuskel || ( matskel ! = 0 ) ! = usematskel ) ; } }; struct skelmeshgroup : meshgroup { skeleton ∗skel ; vector blendcombos ; i n t numblends [ 4 ] ;

v i r t u a l ˜ skelmeshgroup ( ) { i f ( skel ) { i f ( skel−>shared ) skel−>users . removeobj ( t h i s ) ; e l s e DELETEP( skel ) ; } i f ( ebuf ) g l D e l e t e B u f f e r s ( 1 , &ebuf ) ; l o o p i (MAXBLENDCACHE) { DELETEA( blendcache [ i ] . bdata ) ; DELETEA( blendcache [ i ] . mdata ) ; } l o o p i (MAXVBOCACHE) { DELETEA( vbocache [ i ] . vdata ) ; i f ( vbocache [ i ] . vbuf ) g l D e l e t e B u f f e r s ( 1 , &vbocache [ i ] . vbuf ) ; } DELETEA( vdata ) ; } void shareskeleton ( char ∗name) { i f ( ! name) { skel = new skeleton ; skel−>users . add ( t h i s ) ; return ; } s t a t i c hashtable skeletons ; i f ( skeletons . access (name) ) skel = skeletons [name ] ; else { skel = new skeleton ; skel−>name = newstring (name) ; skeletons [ skel−>name] = skel ; } skel−>users . add ( t h i s ) ; skel−>shared ++; } i n t f i n d t a g ( const char ∗name) { return skel−>f i n d t a g (name) ; } i n t totalframes ( ) const { return max( skel−>numframes , 1) ; } v i r t u a l skelanimspec ∗loadanim ( const char ∗filename ) { return NULL; } void genvbo ( bool norms , bool tangents , vbocacheentry &vc ) { i f (hasVBO) { i f ( ! vc . vbuf ) glGenBuffers ( 1 , &vc . vbuf ) ; i f ( ebuf ) return ; } e l s e i f ( edata ) { #define ALLOCVDATA( vdata ) \ do \ { \ DELETEA( vdata ) ; \ vdata = new uchar [ vlen∗v e r t s i z e ] ; \ loopv ( meshes ) \ { \ skelmesh &m = ∗(skelmesh ∗)meshes [ i ] ; \ m. f i l l t c ( vdata , v e r t s i z e ) ; \ i f ( tangents ) m. fillbump ( vdata , v e r t s i z e ) ; \ } \ } while ( 0 ) i f ( ! vc . vdata ) ALLOCVDATA( vc . vdata ) ;

474

Foundations of Videogame Programming Code Repository return ;

i f (hasVBO) { glGenBuffers ( 1 , &ebuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, ebuf ) ; glBufferData (GL ELEMENT ARRAY BUFFER ARB, idxs . length ( ) ∗ s i z e o f ( ushort ) , idxs . getbuf ( ) , GL STATIC DRAW ARB ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } else { edata = new ushort [ idxs . length ( ) ] ; memcpy( edata , idxs . getbuf ( ) , idxs . length ( ) ∗s i z e o f ( ushort ) ) ; } #undef GENVBO #undef GENVBOANIM #undef GENVBOSTAT #undef ALLOCVDATA

} vector idxs ; vnorms = norms ; vtangents = tangents ; vlen = 0; vblends = 0; i f ( skel−>numframes && ! skel−>usegpuskel ) { vweights = 1; loopv ( blendcombos ) { blendcombo &c = blendcombos [ i ] ; c . interpindex = c . weights [ 1 ] ? skel−>numgpubones + vblends ++ : −1; } v e r t s i z e = tangents ? s i z e o f ( vvertbump ) : ( norms ? s i z e o f ( vvertn ) : s i z e o f ( v v e r t ) ) ; loopv ( meshes ) vlen += ( ( skelmesh ∗)meshes [ i ] )−>genvbo ( idxs , vlen ) ; DELETEA( vdata ) ; i f (hasVBO) ALLOCVDATA( vdata ) ; e l s e ALLOCVDATA( vc . vdata ) ;

} void bindvbo ( const animstate ∗as , vbocacheentry &vc , skelcacheentry ∗sc = NULL, blendcacheentry ∗bc = NULL) { vvertn ∗v v e r t s = hasVBO ? 0 : ( vvertn ∗) vc . vdata ; i f (hasVBO && lastebuf ! = ebuf ) { glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, ebuf ) ; lastebuf = ebuf ; } i f ( lastvbuf ! = (hasVBO ? ( void ∗) ( s i z e t ) vc . vbuf : vc . vdata ) ) { i f (hasVBO) glBindBuffer ( GL ARRAY BUFFER ARB, vc . vbuf ) ; i f ( ! lastvbuf ) glEnableClientState ( GL VERTEX ARRAY ) ; glVertexPointer ( 3 , GL FLOAT, v e r t s i z e , &vverts−>pos ) ; lastvbuf = hasVBO ? ( void ∗) ( s i z e t ) vc . vbuf : vc . vdata ; } i f ( as−>cur . anim&ANIM NOSKIN ) { i f ( enabletc ) d i s a b l e t c ( ) ; i f ( enablenormals ) disablenormals ( ) ; } else { i f ( vnorms || vtangents ) { i f ( ! enablenormals ) { glEnableClientState (GL NORMAL ARRAY) ; enablenormals = true ; } i f ( lastnbuf ! = lastvbuf ) { glNormalPointer ( GL FLOAT, v e r t s i z e , &vverts−>norm ) ; lastnbuf = lastvbuf ; } } e l s e i f ( enablenormals ) disablenormals ( ) ;

} else { i f ( skel−>numframes ) { vweights = 4; i n t availbones = skel−>availgpubones ( ) − skel−>numgpubones ; while ( vweights > 1 && availbones >= numblends [ vweights −1]) availbones −= numblends[−−vweights ] ; loopv ( blendcombos ) { blendcombo &c = blendcombos [ i ] ; c . interpindex = c . s i z e ( ) > vweights ? skel−>numgpubones + vblends++ : −1; } } else { vweights = 0; loopv ( blendcombos ) blendcombos [ i ] . interpindex = −1; } i f (hasVBO) glBindBuffer ( GL ARRAY BUFFER ARB, vc . vbuf ) ; #define GENVBO( type , args ) \ do \ { \ v e r t s i z e = s i z e o f ( type ) ; \ vector v v e r t s ; \ loopv ( meshes ) vlen += ( ( skelmesh ∗)meshes [ i ] )−>genvbo args ; \ i f (hasVBO) glBufferData ( GL ARRAY BUFFER ARB, v v e r t s . length ( ) ∗s i z e o f ( type ) , v v e r t s . getbuf ( ) , GL STATIC DRAW ARB ) ; \ else \ { \ DELETEA( vc . vdata ) ; \ vc . vdata = new uchar [ v v e r t s . length ( ) ∗s i z e o f ( type ) ] ; \ memcpy( vc . vdata , v v e r t s . getbuf ( ) , v v e r t s . length ( ) ∗ s i z e o f ( type ) ) ; \ } \ } while ( 0 ) #define GENVBOANIM( type ) GENVBO( type , ( idxs , vlen , v v e r t s ) ) #define GENVBOSTAT( type ) GENVBO( type , ( idxs , vlen , vverts , htdata , htlen ) ) i f ( skel−>numframes ) { i f ( tangents ) GENVBOANIM( vvertbumpw ) ; e l s e GENVBOANIM( vvertw ) ; } else { i n t numverts = 0 , htlen = 128; loopv ( meshes ) numverts += ( ( skelmesh ∗)meshes [ i ] )−> numverts ; while ( htlen < numverts ) htlen ∗= 2; i f ( numverts∗4 > htlen ∗3) htlen ∗= 2; i n t ∗htdata = new i n t [ htlen ] ; memset ( htdata , −1, htlen∗s i z e o f ( i n t ) ) ; i f ( tangents ) GENVBOSTAT( vvertbump ) ; e l s e i f ( norms ) GENVBOSTAT( vvertn ) ; e l s e GENVBOSTAT( v v e r t ) ; d e l e t e [ ] htdata ; } i f (hasVBO) glBindBuffer ( GL ARRAY BUFFER ARB, 0) ;

i f ( ! enabletc ) { glEnableClientState (GL TEXTURE COORD ARRAY) ; enabletc = true ; } i f ( l a s t t c b u f ! = lastvbuf ) { glTexCoordPointer ( 2 , GL FLOAT, v e r t s i z e , &vverts−>u ) ; l a s t t c b u f = lastvbuf ; } } i f ( ! sc || ! skel−>usegpuskel ) { i f ( enablebones ) disablebones ( ) ; return ; } i f ( ! enablebones ) { glEnableVertexAttribArray ( 6 ) ; glEnableVertexAttribArray ( 7 ) ; enablebones = true ; } i f ( lastbbuf ! = lastvbuf ) { g l V e r t e x A t t r i b P o i n t e r ( 6 , 4 , GL UNSIGNED BYTE, GL TRUE, v e r t s i z e , & ( ( vvertw ∗) v v e r t s )−>weights ) ; g l V e r t e x A t t r i b P o i n t e r ( 7 , 4 , GL UNSIGNED BYTE, GL FALSE, v e r t s i z e , & ( ( vvertw ∗) v v e r t s )−>bones ) ; lastbbuf = lastvbuf ; } } void concattagtransform ( part ∗p , i n t frame , i n t i , const matrix3x4 &m, matrix3x4 &n ) {

}

skel−>concattagtransform ( p , frame , i , m, n ) ; }

engine/skelmodel.h

i n t addblendcombo ( const blendcombo &c ) { loopv ( blendcombos ) i f ( blendcombos [ i ]== c ) { blendcombos [ i ] . uses += c . uses ; return i ; } numblends [ c . s i z e ( ) −1]++; blendcombo &a = blendcombos . add ( c ) ; return a . interpindex = blendcombos . length ( ) −1; } void sortblendcombos ( ) { blendcombos . s o r t ( blendcombo : : sortcmp ) ; i n t ∗remap = new i n t [ blendcombos . length ( ) ] ; loopv ( blendcombos ) remap [ blendcombos [ i ] . interpindex ] = i ; loopv ( meshes ) { skelmesh ∗m = ( skelmesh ∗)meshes [ i ] ; l o o p j (m−>numverts ) { v e r t &v = m−>v e r t s [ j ] ; v . blend = remap [ v . blend ] ; } } d e l e t e [ ] remap ; } i n t remapblend ( i n t blend ) { const blendcombo &c = blendcombos [ blend ] ; return c . weights [ 1 ] ? c . interpindex : c . interpbones [ 0 ] ; } template s t a t i c i n l i n e void blendbones (B &d , const B ∗bdata , const blendcombo &c ) { d = bdata [ c . interpbones [ 0 ] ] ; d . mul ( c . weights [ 0 ] ) ; d . accumulate ( bdata [ c . interpbones [ 1 ] ] , c . weights [ 1 ] ) ; i f ( c . weights [ 2 ] ) { d . accumulate ( bdata [ c . interpbones [ 2 ] ] , c . weights [ 2 ] ) ; i f ( c . weights [ 3 ] ) d . accumulate ( bdata [ c . interpbones [ 3 ] ] , c . weights [ 3 ] ) ; } } void blendmatbones ( const skelcacheentry &sc , blendcacheentry &bc ) { bc . nextversion ( ) ; i f ( ! bc . mdata ) bc . mdata = new matrix3x4 [ vblends ] ; i f ( lastbdata == bc . mdata ) lastbdata = NULL; matrix3x4 ∗dst = bc . mdata − skel−>numgpubones ; loopv ( blendcombos ) { const blendcombo &c = blendcombos [ i ] ; i f ( c . interpindex numgpubones ; bool normalize = ! skel−>usegpuskel || vweightscanpreload ( ) ) return ; bool norms = f a l s e , tangents = f a l s e ; loopv ( p−>skins ) { i f ( p−>skins [ i ] . normals ( ) ) norms = true ; i f ( p−>skins [ i ] . tangents ( ) ) tangents = true ; } i f ( skel−>shouldcleanup ( ) ) skel−>cleanup ( ) ; e l s e i f ( norms! =vnorms || tangents ! = vtangents ) cleanup ( ) ; skel−>preload ( ) ; i f (hasVBO ? ! vbocache−>vbuf : ! vbocache−>vdata ) genvbo ( norms , tangents , ∗vbocache ) ; } void render ( const animstate ∗as , f l o a t pitch , const vec &axis , const vec &forward , dynent ∗d , part ∗p ) { bool norms = f a l s e , tangents = f a l s e ; loopv ( p−>skins ) { i f ( p−>skins [ i ] . normals ( ) ) norms = true ; i f ( p−>skins [ i ] . tangents ( ) ) tangents = true ; } i f ( skel−>shouldcleanup ( ) ) { skel−>cleanup ( ) ; disablevbo ( ) ; } e l s e i f ( norms! =vnorms || tangents ! = vtangents ) { cleanup ( ) ; disablevbo ( ) ; } i f ( ! skel−>numframes ) { i f ( ! ( as−>cur . anim&ANIM NORENDER) ) { i f (hasVBO ? ! vbocache−>vbuf : ! vbocache−>vdata ) genvbo ( norms , tangents , ∗vbocache ) ; bindvbo ( as , ∗vbocache ) ; loopv ( meshes ) { skelmesh ∗m = ( skelmesh ∗)meshes [ i ] ; p−>skins [ i ] . bind (m, as ) ; m−>render ( as , p−>skins [ i ] , ∗vbocache ) ; } } skel−>calctags ( p ) ; return ; } skelcacheentry &sc = skel−>checkskelcache ( p , as , pitch , axis , forward , as−>cur . anim&ANIM RAGDOLL || ! d || ! d−>r a g d o l l || d−>ragdoll−>skel ! = skel−>r a g d o l l ? NULL : d−>r a g d o l l ) ; i f ( ! ( as−>cur . anim&ANIM NORENDER) ) { i n t owner = &sc−&skel−>skelcache [ 0 ] ;

476

Foundations of Videogame Programming Code Repository vbocacheentry &vc = skel−>usegpuskel ? ∗vbocache : checkvbocache ( sc , owner ) ; vc . m i l l i s = l a s t m i l l i s ; i f (hasVBO ? ! vc . vbuf : ! vc . vdata ) genvbo ( norms , tangents , vc ) ; blendcacheentry ∗bc = NULL; i f ( vblends ) { bc = &checkblendcache ( sc , owner ) ; bc−>m i l l i s = l a s t m i l l i s ; i f ( bc−>owner ! = owner ) { bc−>owner = owner ; ∗(animcacheentry ∗)bc = sc ; i f ( skel−>usematskel ) blendmatbones ( sc , ∗bc ) ; e l s e blendbones ( sc , ∗bc ) ; } } i f ( ! skel−>usegpuskel && vc . owner ! = owner ) { vc . owner = owner ; ( animcacheentry &)vc = sc ; loopv ( meshes ) { skelmesh &m = ∗(skelmesh ∗)meshes [ i ] ; i f ( skel−>usematskel ) m. i n t e r p v e r t s ( sc . mdata , bc ? bc−> mdata : NULL, norms , tangents , (hasVBO ? vdata : vc . vdata ) + m. v o f f s e t∗v e r t s i z e , p−>skins [ i ] ) ; e l s e m. i n t e r p v e r t s ( sc . bdata , bc ? bc−>bdata : NULL, norms , tangents , (hasVBO ? vdata : vc . vdata ) + m . v o f f s e t∗v e r t s i z e , p−>skins [ i ] ) ; } i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, vc . vbuf ) ; glBufferData ( GL ARRAY BUFFER ARB, vlen∗v e r t s i z e , vdata , GL STREAM DRAW ARB) ; } }

o−>next = p ; partmasks = o ; return o−>bones ; } animpartmask ∗newpartmask ( ) { animpartmask ∗p = ( animpartmask ∗)new uchar [ s i z e o f ( animpartmask ) + ( ( skelmeshgroup ∗)meshes )−>skel−>numbones−1]; p−>numbones = ( ( skelmeshgroup ∗)meshes )−>skel−>numbones ; memset ( p−>bones , 0 , p−>numbones ) ; return p ; } void initanimparts ( ) { DELETEA( buildingpartmask ) ; buildingpartmask = newpartmask ( ) ; } bool addanimpart ( ushort ∗bonemask ) { i f ( ! buildingpartmask || numanimparts>=MAXANIMPARTS) return f a l s e ; ( ( skelmeshgroup ∗)meshes )−>skel−>applybonemask ( bonemask, buildingpartmask−>bones , numanimparts ) ; numanimparts++; return true ; } void endanimparts ( ) { i f ( buildingpartmask ) { partmask = sharepartmask ( buildingpartmask ) ; buildingpartmask = NULL; } ( ( skelmeshgroup ∗)meshes )−>skel−>optimize ( ) ; }

bindvbo ( as , vc , &sc , bc ) ; loopv ( meshes ) { skelmesh ∗m = ( skelmesh ∗)meshes [ i ] ; p−>skins [ i ] . bind (m, as ) ; i f ( skel−>usegpuskel ) skel−>setgpubones ( sc , bc , vblends ) ; m−>render ( as , p−>skins [ i ] , vc ) ; }

}; skelmodel ( const char ∗name) : animmodel (name) { } i n t linktype ( animmodel ∗m) const { return type ( ) ==m−>type ( ) && ( ( skelmeshgroup ∗) parts[0]−>meshes )−>skel == ( ( skelmeshgroup ∗)m −>parts[0]−>meshes )−>skel ? LINK REUSE : LINK TAG ; }

} skel−>calctags ( p , &sc ) ; i f ( as−>cur . anim&ANIM RAGDOLL && skel−>r a g d o l l && ! d−>r a g d o l l ) { d−>r a g d o l l = new ragdolldata ( skel−>ragdoll , p−>model−>scale ) ; i f ( matskel ) skel−>i n i t m a t r a g d o l l (∗d−>ragdoll , sc , p ) ; e l s e skel−>i n i t r a g d o l l (∗d−>ragdoll , sc , p ) ; d−>ragdoll−>i n i t ( d ) ; } } }; struct animpartmask { animpartmask ∗next ; i n t numbones ; uchar bones [ 1 ] ; };

bool s k e l e t a l ( ) const { return true ; } }; struct skeladjustment { f l o a t yaw , pitch , r o l l ; vec t r a n s l a t e ; skeladjustment ( f l o a t yaw , f l o a t pitch , f l o a t r o l l , const vec & t r a n s l a t e ) : yaw ( yaw ) , pitch ( pitch ) , r o l l ( r o l l ) , t r a n s l a t e ( t r a n s l a t e ) {} void adjust ( dualquat &dq ) { i f ( yaw ) dq . mulorient ( quat ( vec ( 0 , 0 , 1) , yaw∗RAD) ) ; i f ( pitch ) dq . mulorient ( quat ( vec ( 0 , −1, 0) , pitch∗RAD) ) ; i f ( r o l l ) dq . mulorient ( quat ( vec(−1, 0 , 0) , r o l l∗RAD) ) ; i f ( ! t r a n s l a t e . i s z e r o ( ) ) dq . t r a n s l a t e ( t r a n s l a t e ) ; }

struct skelpart : part { animpartmask ∗buildingpartmask ; uchar ∗partmask ; skelpart ( ) : buildingpartmask (NULL) , partmask (NULL) { } v i r t u a l ˜ skelpart ( ) { DELETEA( buildingpartmask ) ; } uchar ∗sharepartmask ( animpartmask ∗o ) { s t a t i c animpartmask ∗partmasks = NULL; animpartmask ∗p = partmasks ; f o r ( ; p ; p = p−>next ) i f ( p−>numbones==o−>numbones && !memcmp( p−> bones , o−>bones , p−>numbones ) ) { d e l e t e [ ] ( uchar ∗)o ; return p−>bones ; }

}; template struct skelloader : modelloader { s t a t i c vector adjustments ; }; template vector skelloader::adjustments ; template struct skelcommands : modelcommands { typedef typedef typedef typedef typedef typedef typedef

modelcommands commands; struct MDL: : skeleton skeleton ; struct MDL: : skelmeshgroup meshgroup ; struct MDL: : skelpart part ; struct MDL: : skin skin ; struct MDL: : boneinfo boneinfo ; struct MDL: : skelanimspec animspec ;

engine/skelmodel.h typedef struct MDL: : pitchdep pitchdep ; typedef struct MDL: : p i t c h t a r g e t p i t c h t a r g e t ; typedef struct MDL: : p i t c h c o r r e c t p i t c h c o r r e c t ; s t a t i c void loadpart ( char ∗meshfile , char ∗skelname , f l o a t ∗smooth ) { i f ( !MDL: : loading ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } defformatstring ( filename ) (”%s/%s ” , MDL: : dir , meshfile ) ; part &mdl = ∗new part ; MDL: : loading−>parts . add(&mdl ) ; mdl . model = MDL: : loading ; mdl . index = MDL: : loading−>parts . length ( ) −1; mdl . pitchscale = mdl . p i t c h o f f s e t = mdl . pitchmin = mdl . pitchmax = 0; MDL: : adjustments . s e t s i z e ( 0 ) ; mdl . meshes = MDL: : loading−>sharemeshes ( path ( filename ) , skelname [ 0 ] ? skelname : NULL, double(∗smooth > 0 ? cos ( clamp(∗smooth , 0.0 f , 180.0 f )∗RAD) : 2) ) ; i f ( ! mdl . meshes ) conoutf ( ” could not load %s ” , filename ) ; else { mdl . initanimparts ( ) ; mdl . i n i t s k i n s ( ) ; } }

477

} } s t a t i c void s e t p i t c h t a r g e t ( char ∗name, char ∗animfile , i n t ∗ frameoffset , f l o a t ∗pitchmin , f l o a t ∗pitchmax ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf (”\ f r n o t loading an %s ” , MDL: : formatname ( ) ) ; return ; } part &mdl = ∗( part ∗)MDL: : loading−>parts . l a s t ( ) ; i f ( ! mdl . meshes ) return ; defformatstring ( filename ) (”%s/%s ” , MDL: : dir , animfile ) ; animspec ∗sa = ( ( meshgroup ∗)mdl . meshes )−>loadanim ( path ( filename ) ) ; i f ( ! sa ) { conoutf (”\ frcould not load %s anim f i l e %s ” , MDL: : formatname ( ) , filename ) ; return ; } skeleton ∗skel = ( ( meshgroup ∗)mdl . meshes )−>skel ; i n t bone = skel ? skel−>findbone (name) : −1; i f ( bone < 0) { conoutf (”\ frcould not f i n d bone %s to pitch t a r g e t ” , name) ; return ; } loopv ( skel−>p i t c h t a r g e t s ) i f ( skel−>p i t c h t a r g e t s [ i ] . bone == bone ) return ; p i t c h t a r g e t &t = skel−>p i t c h t a r g e t s . add ( ) ; t . bone = bone ; t . frame = sa−>frame + clamp(∗ frameoffset , 0 , sa−>range−1); t . pitchmin = ∗pitchmin ; t . pitchmax = ∗pitchmax ;

s t a t i c void settag ( char ∗name, char ∗tagname , f l o a t ∗tx , f l o a t ∗ty , f l o a t ∗tz , f l o a t ∗rx , f l o a t ∗ry , f l o a t ∗rz ) }

{ i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } part &mdl = ∗( part ∗)MDL: : loading−>parts . l a s t ( ) ; i n t i = mdl . meshes ? ( ( meshgroup ∗)mdl . meshes )−>skel−>findbone (name ) : −1; i f ( i >= 0) { f l o a t cx = ∗rx ? cosf (∗ rx/2∗RAD) : 1 , sx = ∗rx ? s i n f (∗ rx/2∗RAD) : 0, cy = ∗ry ? cosf (∗ ry/2∗RAD) : 1 , sy = ∗ry ? s i n f (∗ ry/2∗RAD) : 0, cz = ∗rz ? cosf (∗ rz/2∗RAD) : 1 , sz = ∗rz ? s i n f (∗ rz/2∗RAD) : 0; matrix3x4 m( matrix3x3 ( quat ( sx∗cy∗cz − cx∗sy∗sz , cx∗sy∗cz + sx∗cy ∗sz , cx∗cy∗sz − sx∗sy∗cz , cx∗cy∗cz + sx∗sy∗sz ) ) , vec (∗ tx , ∗ty , ∗t z ) ) ; ( ( meshgroup ∗)mdl . meshes )−>skel−>addtag ( tagname , i , m) ; return ; } conoutf ( ” could not f i n d bone %s f o r tag %s ” , name, tagname ) ;

s t a t i c void s e t p i t c h c o r r e c t ( char ∗name, char ∗targetname , f l o a t ∗scale , f l o a t ∗pitchmin , f l o a t ∗pitchmax ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf (”\ f r n o t loading an %s ” , MDL: : formatname ( ) ) ; return ; } part &mdl = ∗( part ∗)MDL: : loading−>parts . l a s t ( ) ; i f ( ! mdl . meshes ) return ; skeleton ∗skel = ( ( meshgroup ∗)mdl . meshes )−>skel ; i n t bone = skel ? skel−>findbone (name) : −1; i f ( bone < 0) { conoutf (”\ frcould not f i n d bone %s to pitch c o r r e c t ” , name) ; return ; } i f ( skel−>f i n d p i t c h c o r r e c t ( bone ) >= 0) return ; i n t targetbone = skel−>findbone ( targetname ) , t a r g e t = −1; i f ( targetbone >= 0) loopv ( skel−>p i t c h t a r g e t s ) i f ( skel−>p i t c h t a r g e t s [ i ] . bone == targetbone ) { t a r g e t = i ; break ; } i f ( t a r g e t < 0) { conoutf (”\ frcould not f i n d pitch t a r g e t %s to pitch c o r r e c t %s ” , targetname , name) ; return ; } pitchcorrect c ; c . bone = bone ; c . target = target ; c . pitchmin = ∗pitchmin ; c . pitchmax = ∗pitchmax ; c . pitchscale = ∗scale ; i n t pos = skel−>p i t c h c o r r e c t s . length ( ) ; loopv ( skel−>p i t c h c o r r e c t s ) i f ( bone p i t c h c o r r e c t s [ i ] . bone ) { pos = i ; break ; break ; } skel−>p i t c h c o r r e c t s . i n s e r t ( pos , c ) ;

} s t a t i c void s e t p i t c h ( char ∗name, f l o a t ∗pitchscale , f l o a t ∗p i t c h o f f s e t , f l o a t ∗pitchmin , f l o a t ∗pitchmax ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } part &mdl = ∗( part ∗)MDL: : loading−>parts . l a s t ( ) ; i f (name [ 0 ] ) { i n t i = mdl . meshes ? ( ( meshgroup ∗)mdl . meshes )−>skel−>findbone ( name) : −1; i f ( i >=0) { boneinfo &b = ( ( meshgroup ∗)mdl . meshes )−>skel−>bones [ i ] ; b . pitchscale = ∗pitchscale ; b . p i t c h o f f s e t = ∗p i t c h o f f s e t ; i f (∗ pitchmin || ∗pitchmax ) { b . pitchmin = ∗pitchmin ; b . pitchmax = ∗pitchmax ; } else { b . pitchmin = −360∗fabs ( b . pitchscale ) + b . p i t c h o f f s e t ; b . pitchmax = 360∗fabs ( b . pitchscale ) + b . p i t c h o f f s e t ; } return ; } conoutf ( ” could not f i n d bone %s to pitch ” , name) ; return ; } mdl . pitchscale = ∗pitchscale ; mdl . p i t c h o f f s e t = ∗p i t c h o f f s e t ; i f (∗ pitchmin || ∗pitchmax ) { mdl . pitchmin = ∗pitchmin ; mdl . pitchmax = ∗pitchmax ; } else { mdl . pitchmin = −360∗fabs ( mdl . pitchscale ) + mdl . p i t c h o f f s e t ; mdl . pitchmax = 360∗fabs ( mdl . pitchscale ) + mdl . p i t c h o f f s e t ;

} s t a t i c void setanim ( char ∗anim , char ∗animfile , f l o a t ∗speed , i n t ∗ p r i o r i t y , i n t ∗s t a r t o f f s e t , i n t ∗endoffset ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } vector anims ; findanims ( anim , anims ) ; i f ( anims . empty ( ) ) conoutf ( ” could not f i n d animation %s ” , anim ) ; else { part ∗p = ( part ∗)MDL: : loading−>parts . l a s t ( ) ; i f ( ! p−>meshes ) return ; defformatstring ( filename ) (”%s/%s ” , MDL: : dir , animfile ) ; animspec ∗sa = ( ( meshgroup ∗)p−>meshes )−>loadanim ( path ( filename ) ); i f ( ! sa ) conoutf ( ” could not load %s anim f i l e %s ” , MDL: : formatname ( ) , filename ) ; e l s e loopv ( anims ) { i n t s t a r t = sa−>frame , end = sa−>range ; i f (∗ s t a r t o f f s e t > 0) s t a r t += min(∗ s t a r t o f f s e t , end−1); e l s e i f (∗ s t a r t o f f s e t < 0) s t a r t += max( end + ∗s t a r t o f f s e t , 0) ; end −= s t a r t − sa−>frame ; i f (∗ endoffset > 0) end = min ( end , ∗endoffset ) ; e l s e i f (∗ endoffset < 0) end = max( end + ∗endoffset , 1) ; MDL: : loading−>parts . l a s t ( )−>setanim ( p−>numanimparts−1, anims [

478

Foundations of Videogame Programming Code Repository i ] , s t a r t , end , ∗speed , ∗p r i o r i t y ) ;

s t a t i c void setadjust ( char ∗name, f l o a t ∗yaw , f l o a t ∗pitch , f l o a t ∗ r o l l , f l o a t ∗tx , f l o a t ∗ty , f l o a t ∗t z )

} }

{

}

i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } part &mdl = ∗( part ∗)MDL: : loading−>parts . l a s t ( ) ;

s t a t i c void setanimpart ( char ∗maskstr ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; }

i f ( ! name [ 0 ] ) return ; i n t i = mdl . meshes ? ( ( meshgroup ∗)mdl . meshes )−>skel−>findbone (name ) : −1; i f ( i < 0) { conoutf ( ” could not f i n d bone %s to adjust ” , name) ; return ; } while ( !MDL: : adjustments . inrange ( i ) ) MDL: : adjustments . add ( skeladjustment ( 0 , 0 , 0 , vec ( 0 , 0 , 0) ) ) ; MDL: : adjustments [ i ] = skeladjustment (∗yaw , ∗pitch , ∗r o l l , vec (∗ tx /4 , ∗ty /4 , ∗t z /4) ) ;

part ∗p = ( part ∗)MDL: : loading−>parts . l a s t ( ) ; vector bonestrs ; e x p l o d e l i s t ( maskstr , bonestrs ) ; vector bonemask ; loopv ( bonestrs ) { char ∗bonestr = bonestrs [ i ] ; i n t bone = p−>meshes ? ( ( meshgroup ∗)p−>meshes )−>skel−>findbone ( bonestr [ 0 ] = = ’ ! ’ ? bonestr+1 : bonestr ) : −1; i f ( bonemodelcommand ( loadpart , ” load ” , ” s s f ” ) ; this−>modelcommand ( settag , ” tag ” , ” s s f f f f f f ” ) ; this−>modelcommand ( setpitch , ” pitch ” , ” s f f f f ” ) ; this−>modelcommand ( s e t p i t c h t a r g e t , ” p i t c h t a r g e t ” , ” s s i f f ” ) ; this−>modelcommand ( s e t p i t c h c o r r e c t , ” p i t c h c o r r e c t ” , ” s s f f f ” ) ; i f (MDL: : animated ( ) ) { this−>modelcommand ( setanim , ”anim ” , ” s s f i i i ” ) ; this−>modelcommand ( setanimpart , ” animpart ” , ” s ” ) ; this−>modelcommand ( setadjust , ” adjust ” , ” s f f f f f f ” ) ; } }

i f ( ! p−>addanimpart ( bonemask . getbuf ( ) ) ) conoutf ( ” too many animation parts ” ) ; } };

engine/smd.h

s t a t i c const char ∗formatname ( ) { return ”smd” ; } i n t type ( ) const { return MDL SMD; }

void readname ( char ∗&curbuf , char ∗name, s i z e t namesize ) { char ∗curname = name; while (∗ curbuf && isspace (∗ curbuf ) ) curbuf ++; bool allowspace = f a l s e ; i f (∗ curbuf == ’ ” ’ ) { curbuf ++; allowspace = true ; } while (∗ curbuf ) { char c = ∗curbuf ++; i f ( c == ’ ” ’ ) break ; i f ( isspace ( c ) && ! allowspace ) break ; i f ( curname < &name[ namesize−1]) ∗curname++ = c ; } ∗curname = ’ \ 0 ’ ; }

struct smdmesh : skelmesh { };

{

struct smd; struct smdbone { s t r i n g name; i n t parent ; smdbone ( ) : parent(−1) { name[ 0 ] = ’ \ 0 ’ ; } }; struct smd : skelmodel , skelloader { smd( const char ∗name) : skelmodel (name) {}

void readnodes ( stream ∗f , char ∗buf , s i z e t bufsize , vector &bones ) while ( f−>g e t l i n e ( buf , bufsize ) ) { char ∗curbuf = buf ; i f ( skipcomment ( curbuf ) ) continue ; i f ( ! strncmp ( curbuf , ” end ” , 3) ) break ; i n t id = s t r t o l ( curbuf , &curbuf , 10) ; s t r i n g name; readname ( curbuf , name, s i z e o f (name) ) ; i n t parent = s t r t o l ( curbuf , &curbuf , 10) ; i f ( id < 0 || id > 255 || parent > 255 || !name [ 0 ] ) continue ; while ( ! bones . inrange ( id ) ) bones . add ( ) ; smdbone &bone = bones [ id ] ; copystring ( bone .name, name) ; bone . parent = parent ; }

struct smdmeshgroup : skelmeshgroup { smdmeshgroup ( ) { } bool skipcomment ( char ∗&curbuf ) { while (∗ curbuf && isspace (∗ curbuf ) ) curbuf ++; switch (∗ curbuf ) { case ’ # ’ : case ’ ; ’ : case ’\ r ’ : case ’\n ’ : case ’ \ 0 ’ : return true ; case ’ / ’ : i f ( curbuf [ 1 ] == ’ / ’ ) return true ; break ; } return f a l s e ; } void skipsection ( stream ∗f , char ∗buf , s i z e t bufsize ) { while ( f−>g e t l i n e ( buf , bufsize ) ) { char ∗curbuf = buf ; i f ( skipcomment ( curbuf ) ) continue ; i f ( ! strncmp ( curbuf , ” end ” , 3) ) break ; } }

} void readmaterial ( char ∗&curbuf , char ∗name, s i z e t namesize ) { char ∗curname = name; while (∗ curbuf && isspace (∗ curbuf ) ) curbuf ++; while (∗ curbuf ) { char c = ∗curbuf ++; i f ( isspace ( c ) ) break ; i f ( c == ’ . ’ ) { while (∗ curbuf && ! isspace (∗ curbuf ) ) curbuf ++; break ; } i f ( curname < &name[ namesize−1]) ∗curname++ = c ; } ∗curname = ’ \ 0 ’ ; }

engine/smd.h struct smdmeshdata { smdmesh ∗mesh; vector v e r t s ; vector t r i s ; void f i n a l i z e ( ) { i f ( v e r t s . empty ( ) || t r i s . empty ( ) ) return ; v e r t ∗mverts = new v e r t [ mesh−>numverts + v e r t s . length ( ) ] ; i f ( mesh−>numverts ) { memcpy( mverts , mesh−>verts , mesh−>numverts∗s i z e o f ( v e r t ) ) ; d e l e t e [ ] mesh−>v e r t s ; } memcpy(&mverts [ mesh−>numverts ] , v e r t s . getbuf ( ) , v e r t s . length ( ) ∗s i z e o f ( v e r t ) ) ; mesh−>numverts += v e r t s . length ( ) ; mesh−>v e r t s = mverts ; t r i ∗mtris = new t r i [ mesh−>numtris + t r i s . length ( ) ] ; i f ( mesh−>numtris ) { memcpy( mtris , mesh−>t r i s , mesh−>numtris∗s i z e o f ( t r i ) ) ; d e l e t e [ ] mesh−>t r i s ; } memcpy(& mtris [ mesh−>numtris ] , t r i s . getbuf ( ) , t r i s . length ( ) ∗ sizeof ( t r i ) ) ; mesh−>numtris += t r i s . length ( ) ; mesh−>t r i s = mtris ; } }; struct smdvertkey : v e r t { smdmeshdata ∗mesh; smdvertkey ( smdmeshdata ∗mesh ) : mesh ( mesh ) {} }; void r e a d t r i a n g l e s ( stream ∗f , char ∗buf , s i z e t bufsize ) { smdmeshdata ∗curmesh = NULL; hashtable materials(1group = t h i s ; m−>name = newstring ( material ) ; meshes . add (m) ; curmesh = &materials [m−>name ] ; curmesh−>mesh = m; } } t r i curtri ; loopi ( 3 ) { char ∗curbuf ; do { i f ( ! f−>g e t l i n e ( buf , bufsize ) ) goto endsection ; curbuf = buf ; } while ( skipcomment ( curbuf ) ) ; smdvertkey key ( curmesh ) ; i n t parent = −1, numlinks = 0 , len = 0; i f ( sscanf ( curbuf , ” %d %f %f %f %f %f %f %f %f %d%n” , & parent , &key . pos . x , &key . pos . y , &key . pos . z , &key . norm . x , &key . norm . y , &key . norm . z , &key . u, &key . v , & numlinks , &len ) < 9) goto endsection ; curbuf += len ; key . pos . y = −key . pos . y ; key . norm . y = −key . norm . y ; key . v = 1 − key . v ; blendcombo c ; i n t sorted = 0; f l o a t pweight = 0 , tweight = 0; f o r ( ; numlinks > 0; numlinks−−) { i n t bone = −1, len = 0; f l o a t weight = 0; i f ( sscanf ( curbuf , ” %d %f%n” , &bone , &weight , &len ) < 2) break ; curbuf += len ; tweight += weight ;

479 i f ( bone == parent ) pweight += weight ; e l s e sorted = c . addweight ( sorted , weight , bone ) ; } i f ( tweight < 1) pweight += 1 − tweight ; i f ( pweight > 0) sorted = c . addweight ( sorted , pweight , parent ) ; c . f i n a l i z e ( sorted ) ; key . blend = curmesh−>mesh−>addblendcombo ( c ) ; i n t index = v e r t s . access ( key , curmesh−>v e r t s . length ( ) ) ; i f ( index == curmesh−>v e r t s . length ( ) ) curmesh−>v e r t s . add ( key ) ; c u r t r i . v e r t[2− i ] = index ;

} curmesh−>t r i s . add ( c u r t r i ) ; } endsection : enumerate ( materials , smdmeshdata, data , data . f i n a l i z e ( ) ) ; } void readskeleton ( stream ∗f , char ∗buf , s i z e t bufsize ) { i n t frame = −1; while ( f−>g e t l i n e ( buf , bufsize ) ) { char ∗curbuf = buf ; i f ( skipcomment ( curbuf ) ) continue ; i f ( sscanf ( curbuf , ” time %d ” , &frame ) == 1) continue ; e l s e i f ( ! strncmp ( curbuf , ” end ” , 3) ) break ; e l s e i f ( frame ! = 0) continue ; i n t bone ; vec pos , r o t ; i f ( sscanf ( curbuf , ” %d %f %f %f %f %f %f ” , &bone , &pos . x , & pos . y , &pos . z , &r o t . x , &r o t . y , &r o t . z ) ! = 7) continue ; i f ( bone < 0 || bone >= skel−>numbones ) continue ; r o t . x = −r o t . x ; r o t . z = −r o t . z ; f l o a t cx = cosf ( r o t . x/2) , sx = s i n f ( r o t . x/2) , cy = cosf ( r o t . y /2) , sy = s i n f ( r o t . y /2) , cz = cosf ( r o t . z /2) , sz = s i n f ( r o t . z /2) ; pos . y = −pos . y ; dualquat dq ( quat ( sx∗cy∗cz − cx∗sy∗sz , cx∗sy∗cz + sx∗cy∗sz , cx∗cy∗sz − sx∗sy∗cz , cx∗cy∗cz + sx∗sy∗sz ) , pos ) ; boneinfo &b = skel−>bones [ bone ] ; i f ( b . parent < 0) b . base = dq ; e l s e b . base . mul ( skel−>bones [ b . parent ] . base , dq ) ; ( b . invbase = b . base ) . i n v e r t ( ) ; } } bool loadmesh ( const char ∗filename ) { stream ∗f = o p e n f i l e ( filename , ” r ” ) ; i f ( ! f ) return f a l s e ; char buf [ 5 1 2 ] ; i n t version = −1; while ( f−>g e t l i n e ( buf , s i z e o f ( buf ) ) ) { char ∗curbuf = buf ; i f ( skipcomment ( curbuf ) ) continue ; i f ( sscanf ( curbuf , ” version %d ” , &version ) == 1) { i f ( version ! = 1) { d e l e t e f ; return f a l s e ; } } e l s e i f ( ! strncmp ( curbuf , ” nodes ” , 5) ) { i f ( skel−>numbones > 0) { skipsection ( f , buf , s i z e o f ( buf ) ) ; continue ; } vector bones ; readnodes ( f , buf , s i z e o f ( buf ) , bones ) ; i f ( bones . empty ( ) ) continue ; skel−>numbones = bones . length ( ) ; skel−>bones = new boneinfo [ skel−>numbones ] ; loopv ( bones ) { boneinfo &dst = skel−>bones [ i ] ; smdbone &src = bones [ i ] ; dst .name = newstring ( src .name) ; dst . parent = src . parent ; } skel−>l i n k c h i l d r e n ( ) ; } e l s e i f ( ! strncmp ( curbuf , ” t r i a n g l e s ” , 9) ) r e a d t r i a n g l e s ( f , buf , s i z e o f ( buf ) ) ; e l s e i f ( ! strncmp ( curbuf , ” skeleton ” , 8) ) { i f ( skel−>shared > 1) skipsection ( f , buf , s i z e o f ( buf ) ) ; e l s e readskeleton ( f , buf , s i z e o f ( buf ) ) ; }

480

Foundations of Videogame Programming Code Repository i f ( bones . length ( ) ! = skel−>numbones ) { d e l e t e f ; return NULL; } } e l s e i f ( ! strncmp ( curbuf , ” t r i a n g l e s ” , 9) ) skipsection ( f , buf , s i z e o f ( buf ) ) ; e l s e i f ( ! strncmp ( curbuf , ” skeleton ” , 8) ) readframes ( f , buf , s i z e o f ( buf ) , animbones ) ; e l s e i f ( ! strncmp ( curbuf , ” vertexanimation ” , 15) ) skipsection ( f , buf , s i z e o f ( buf ) ) ;

e l s e i f ( ! strncmp ( curbuf , ” vertexanimation ” , 15) ) skipsection ( f , buf , s i z e o f ( buf ) ) ; } sortblendcombos ( ) ; delete f ; return true ; } i n t readframes ( stream ∗f , char ∗buf , s i z e t bufsize , vector< dualquat> &animbones ) { i n t frame = −1, numframes = 0 , lastbone = skel−>numbones ; while ( f−>g e t l i n e ( buf , bufsize ) ) { char ∗curbuf = buf ; i f ( skipcomment ( curbuf ) ) continue ; i n t nextframe = −1; i f ( sscanf ( curbuf , ” time %d ” , &nextframe ) == 1) { f o r ( ; lastbone < skel−>numbones ; lastbone ++) animbones [ frame∗skel−>numbones + lastbone ] = animbones [ lastbone ] ; i f ( nextframe >= numframes ) { databuf framebones = animbones . reserve ( skel−> numbones ∗ ( nextframe + 1 − numframes ) ) ; l o o p i ( nextframe − numframes ) framebones . put ( animbones . getbuf ( ) , skel−>numbones ) ; animbones . addbuf ( framebones ) ; animbones . advance ( skel−>numbones ) ; numframes = nextframe + 1; } frame = nextframe ; lastbone = 0; continue ; } e l s e i f ( ! strncmp ( curbuf , ” end ” , 3) ) break ; i n t bone ; vec pos , r o t ; i f ( sscanf ( curbuf , ” %d %f %f %f %f %f %f ” , &bone , &pos . x , & pos . y , &pos . z , &r o t . x , &r o t . y , &r o t . z ) ! = 7) continue ; i f ( bone < 0 || bone >= skel−>numbones ) continue ; f o r ( ; lastbone < bone ; lastbone ++) animbones [ frame∗skel−> numbones + lastbone ] = animbones [ lastbone ] ; lastbone ++; f l o a t cx = cosf ( r o t . x/2) , sx = s i n f ( r o t . x/2) , cy = cosf ( r o t . y /2) , sy = s i n f ( r o t . y /2) , cz = cosf ( r o t . z /2) , sz = s i n f ( r o t . z /2) ; pos . y = −pos . y ; dualquat dq ( quat(−(sx∗cy∗cz − cx∗sy∗sz ) , cx∗sy∗cz + sx∗cy∗sz , −(cx∗cy∗sz − sx∗sy∗cz ) , cx∗cy∗cz + sx∗sy∗sz ) , pos ) ; i f ( adjustments . inrange ( bone ) ) adjustments [ bone ] . adjust ( dq ) ; dq . mul ( skel−>bones [ bone ] . invbase ) ; dualquat &dst = animbones [ frame∗skel−>numbones + bone ] ; i f ( skel−>bones [ bone ] . parent < 0) dst = dq ; e l s e dst . mul ( skel−>bones [ skel−>bones [ bone ] . parent ] . base , dq ) ; dst . f i x a n t i p o d a l ( skel−>numframes > 0 ? skel−>framebones [ bone ] : animbones [ bone ] ) ; } f o r ( ; lastbone < skel−>numbones ; lastbone ++) animbones [ frame∗ skel−>numbones + lastbone ] = animbones [ lastbone ] ; return numframes ; } skelanimspec ∗loadanim ( const char ∗filename ) { skelanimspec ∗sa = skel−>findskelanim ( filename ) ; i f ( sa || skel−>numbones g e t l i n e ( buf , s i z e o f ( buf ) ) ) { char ∗curbuf = buf ; i f ( skipcomment ( curbuf ) ) continue ; i f ( sscanf ( curbuf , ” version %d ” , &version ) == 1) { i f ( version ! = 1) { d e l e t e f ; return NULL; } } e l s e i f ( ! strncmp ( curbuf , ” nodes ” , 5) ) { vector bones ; readnodes ( f , buf , s i z e o f ( buf ) , bones ) ;

} i n t numframes = animbones . length ( ) / skel−>numbones ; dualquat ∗framebones = new dualquat [ ( skel−>numframes+numframes )∗ skel−>numbones ] ; i f ( skel−>framebones ) { memcpy( framebones , skel−>framebones , skel−>numframes∗skel−> numbones∗s i z e o f ( dualquat ) ) ; d e l e t e [ ] skel−>framebones ; } memcpy(&framebones [ skel−>numframes∗skel−>numbones ] , animbones . getbuf ( ) , numframes∗skel−>numbones∗s i z e o f ( dualquat ) ) ; skel−>framebones = framebones ; sa = &skel−>addskelanim ( filename ) ; sa−>frame = skel−>numframes ; sa−>range = numframes ; skel−>numframes += numframes ; delete f ; return sa ; } bool load ( const char ∗meshfile ) { name = newstring ( meshfile ) ; i f ( ! loadmesh ( meshfile ) ) return f a l s e ; return true ; } }; meshgroup ∗loadmeshes ( const char ∗name, v a l i s t args ) { smdmeshgroup ∗group = new smdmeshgroup ; group−>shareskeleton ( va arg ( args , char ∗) ) ; i f ( ! group−>load (name) ) { d e l e t e group ; return NULL; } return group ; } bool loaddefaultparts ( ) { skelpart &mdl = ∗new skelpart ; parts . add(&mdl ) ; mdl . model = t h i s ; mdl . index = 0; mdl . pitchscale = mdl . p i t c h o f f s e t = mdl . pitchmin = mdl . pitchmax = 0; adjustments . s e t s i z e ( 0 ) ; const char ∗fname = loadname + s t r l e n ( loadname ) ; do −−fname ; while ( fname >= loadname && ∗fname ! = ’ / ’ && ∗fname ! = ’ \ \ ’ ) ; fname++; defformatstring (meshname) ( ” packages/models/%s/%s .smd” , loadname , fname ) ; mdl . meshes = sharemeshes ( path (meshname) , NULL) ; i f ( ! mdl . meshes ) return f a l s e ; mdl . initanimparts ( ) ; mdl . i n i t s k i n s ( ) ; return true ; } bool load ( ) { i f ( loaded ) return true ; formatstring ( d i r ) ( ” packages/models/%s ” , loadname ) ; defformatstring ( cfgname ) ( ” packages/models/%s/smd. c f g ” , loadname ) ; loading = t h i s ; i d e n t f l a g s &= ˜IDF PERSIST ; i f ( e x e c f i l e ( cfgname , f a l s e ) && parts . length ( ) ) // configured smd, w i l l c a l l the smd∗ commands below { i d e n t f l a g s |= IDF PERSIST ; loading = NULL; loopv ( parts ) i f ( ! parts [ i]−>meshes ) return f a l s e ; } e l s e // smd without configuration , t r y d e f a u l t t r i s and skin { i d e n t f l a g s |= IDF PERSIST ; i f ( ! loaddefaultparts ( ) ) { loading = NULL; return f a l s e ; }

engine/sound.cpp loading = NULL; } scale /= 4; parts[0]−>t r a n s l a t e = t r a n s l a t e ; loopv ( parts ) { skelpart ∗p = ( skelpart ∗) parts [ i ] ; p−>endanimparts ( ) ; p−>meshes−>shared ++; } return loaded = true ; }

481

{ return hthash ( k . pos ) ; } s t a t i c i n l i n e bool htcmp ( const smd : : smdmeshgroup : : smdvertkey &k , i n t index ) { i f ( ! k . mesh−>v e r t s . inrange ( index ) ) return f a l s e ; const smd : : v e r t &v = k . mesh−>v e r t s [ index ] ; return k . pos == v . pos && k . norm == v . norm && k . u == v . u && k . v == v . v && k . blend == v . blend ; }

}; skelcommands smdcommands; s t a t i c i n l i n e uint hthash ( const smd : : smdmeshgroup : : smdvertkey &k )

engine/sound.cpp // sound . cpp : basic p o s i t i o n a l sound using sdl mixer

i n t maxchannels = 0;

#include ” engine . h”

soundchannel &newchannel ( i n t n , soundslot ∗s l o t , const vec ∗l o c = NULL, e x t e n t i t y ∗ent = NULL, i n t f l a g s = 0 , i n t radius = 0) { i f ( ent ) { l o c = &ent−>o ; ent−>v i s i b l e = true ; } while ( ! channels . inrange ( n ) ) channels . add ( channels . length ( ) ) ; soundchannel &chan = channels [ n ] ; chan . r e s e t ( ) ; chan . inuse = true ; i f ( l o c ) chan . l o c = ∗l o c ; chan . s l o t = s l o t ; chan . ent = ent ; chan . f l a g s = 0; chan . radius = radius ; return chan ; }

#include ” SDL mixer . h” #define MAXVOL MIX MAX VOLUME bool nosound = true ; struct soundsample { char ∗name; Mix Chunk ∗chunk ; soundsample ( ) : name(NULL) , chunk (NULL) {} ˜soundsample ( ) { DELETEA(name) ; } void cleanup ( ) { i f ( chunk ) { Mix FreeChunk ( chunk ) ; chunk = NULL; } } }; struct soundslot { soundsample ∗sample ; i n t volume ; }; struct soundconfig { i n t s l o t s , numslots ; i n t maxuses ; bool hasslot ( const soundslot ∗p , const vector &v ) const { return p >= v . getbuf ( ) + s l o t s && p < v . getbuf ( ) + s l o t s +numslots && s l o t s +numslots < v . length ( ) ; } i n t chooseslot ( ) const { return numslots > 1 ? s l o t s + rnd ( numslots ) : s l o t s ; } }; struct soundchannel { i n t id ; bool inuse ; vec l o c ; soundslot ∗s l o t ; e x t e n t i t y ∗ent ; i n t radius , volume , pan , f l a g s ; bool d i r t y ; soundchannel ( i n t id ) : id ( id ) { r e s e t ( ) ; } bool hasloc ( ) const { return l o c . x >= −1e15f ; } void c l e a r l o c ( ) { l o c = vec(−1e16f , −1e16f , −1e16f ) ; } void r e s e t ( ) { inuse = f a l s e ; clearloc ( ) ; s l o t = NULL; ent = NULL; radius = 0; volume = −1; pan = −1; f l a g s = 0; dirty = false ; } }; vector channels ;

void freechannel ( i n t n ) { // Note that t h i s can p o t e n t i a l l y be c a l l e d from the SDL mixer audio thread . // Be c a r e f u l o f race conditions when checking chan . inuse without locking audio . // Can ’ t use Mix Playing ( ) checks due to bug with looping sounds in SDL mixer . i f ( ! channels . inrange ( n ) || ! channels [ n ] . inuse ) return ; soundchannel &chan = channels [ n ] ; chan . inuse = f a l s e ; i f ( chan . ent ) chan . ent−>v i s i b l e = f a l s e ; } void syncchannel ( soundchannel &chan ) { i f ( ! chan . d i r t y ) return ; i f ( ! Mix FadingChannel ( chan . id ) ) Mix Volume ( chan . id , chan . volume ) ; Mix SetPanning ( chan . id , 255−chan . pan , chan . pan ) ; chan . d i r t y = f a l s e ; } void stopchannels ( ) { loopv ( channels ) { soundchannel &chan = channels [ i ] ; i f ( ! chan . inuse ) continue ; Mix HaltChannel ( i ) ; freechannel ( i ) ; } } void setmusicvol ( i n t musicvol ) ; VARFP( soundvol , 0 , 255, 255, i f ( ! soundvol ) { stopchannels ( ) ; setmusicvol ( 0 ) ; }) ; VARFP( musicvol , 0 , 128, 255, setmusicvol ( soundvol ? musicvol : 0) ) ; char ∗m u s i c f i l e = NULL, ∗musicdonecmd = NULL; Mix Music ∗music = NULL; SDL RWops ∗musicrw = NULL; stream ∗musicstream = NULL; void setmusicvol ( i n t musicvol ) { i f ( nosound ) return ; i f ( music ) Mix VolumeMusic ( ( musicvol∗MAXVOL) /255) ; }

482

Foundations of Videogame Programming Code Repository

void stopmusic ( ) { i f ( nosound ) return ; DELETEA( m u s i c f i l e ) ; DELETEA( musicdonecmd ) ; i f ( music ) { Mix HaltMusic ( ) ; Mix FreeMusic ( music ) ; music = NULL; } i f ( musicrw ) { SDL FreeRW ( musicrw ) ; musicrw = NULL; } DELETEP( musicstream ) ; } VARF( soundchans , 1 , 32, 128, initwarning ( ” sound configuration ” , INIT RESET , CHANGE SOUND) ) ; VARF( soundfreq , 0 , MIX DEFAULT FREQUENCY, 44100, initwarning ( ” sound configuration ” , INIT RESET , CHANGE SOUND) ) ; VARF( soundbufferlen , 128, 1024, 4096, initwarning ( ” sound configuration ” , INIT RESET , CHANGE SOUND) ) ; void initsound ( ) { i f ( Mix OpenAudio ( soundfreq , MIX DEFAULT FORMAT, 2 , soundbufferlen )rwops ( ) ; i f ( ! musicrw ) DELETEP( musicstream ) ; } i f ( musicrw ) music = Mix LoadMUS RW ( musicrw ) ; e l s e music = Mix LoadMUS ( f i n d f i l e ( name, ” rb ” ) ) ; i f ( ! music ) { i f ( musicrw ) { SDL FreeRW ( musicrw ) ; musicrw = NULL; } DELETEP( musicstream ) ; } return music ; } void startmusic ( char ∗name, char ∗cmd) { i f ( nosound ) return ; stopmusic ( ) ; i f ( soundvol && musicvol && ∗name) { defformatstring ( f i l e ) ( ” packages/%s ” , name) ; path ( f i l e ) ; i f ( loadmusic ( f i l e ) ) { DELETEA( m u s i c f i l e ) ; DELETEA( musicdonecmd ) ; m u s i c f i l e = newstring ( f i l e ) ; i f (cmd [ 0 ] ) musicdonecmd = newstring (cmd) ; Mix PlayMusic ( music , cmd[ 0 ] ? 0 : −1) ; Mix VolumeMusic ( ( musicvol∗MAXVOL) /255) ; intret (1) ; } else { conoutf (CON ERROR, ” could not play music : %s ” , f i l e ) ; intret (0) ; } } }

COMMANDN( music , startmusic , ” ss ” ) ; s t a t i c hashtable samples ; s t a t i c vector gameslots , mapslots ; s t a t i c vector gamesounds , mapsounds ; s t a t i c i n t findsound ( const char ∗name, i n t vol , vector & sounds , vector &s l o t s ) { loopv ( sounds ) { soundconfig &s = sounds [ i ] ; l o o p j ( s . numslots ) { soundslot &c = s l o t s [ s . s l o t s + j ] ; i f ( ! strcmp ( c . sample−>name, name) && ( ! v o l || c . volume== v o l ) ) return i ; } } return −1; } s t a t i c i n t addslot ( const char ∗name, i n t vol , vector &s l o t s ) { soundsample ∗s = samples . access (name) ; if (!s) { char ∗n = newstring (name) ; s = &samples [ n ] ; s−>name = n ; s−>chunk = NULL; } soundslot ∗o l d s l o t s = s l o t s . getbuf ( ) ; i n t oldlen = s l o t s . length ( ) ; soundslot &s l o t = s l o t s . add ( ) ; // soundslots . add ( ) may r e l o c a t e s l o t pointers i f ( s l o t s . getbuf ( ) ! = o l d s l o t s ) loopv ( channels ) { soundchannel &chan = channels [ i ] ; i f ( chan . inuse && chan . s l o t >= o l d s l o t s && chan . s l o t < &o l d s l o t s [ oldlen ] ) chan . s l o t = &s l o t s [ chan . s l o t − o l d s l o t s ] ; } s l o t . sample = s ; s l o t . volume = v o l ? v o l : 100; return oldlen ; } s t a t i c i n t addsound ( const char ∗name, i n t vol , i n t maxuses , vector< soundconfig> &sounds , vector &s l o t s ) { soundconfig &s = sounds . add ( ) ; s . s l o t s = addslot ( name, vol , s l o t s ) ; s . numslots = 1; s . maxuses = maxuses ; return sounds . length ( ) −1; } void registersound ( char ∗name, i n t ∗v o l ) { i n t r e t ( addsound ( name, ∗vol , 0 , gamesounds , gameslots ) ) ; } COMMAND( registersound , ” s i ” ) ; void mapsound ( char ∗name, i n t ∗vol , i n t ∗maxuses ) { i n t r e t ( addsound ( name, ∗vol , ∗maxuses < 0 ? 0 : max( 1 , ∗maxuses ) , mapsounds, mapslots ) ) ; } COMMAND( mapsound, ” s i i ” ) ; void altsound ( char ∗name, i n t ∗v o l ) { i f ( gamesounds . empty ( ) ) return ; addslot ( name, ∗vol , gameslots ) ; gamesounds . l a s t ( ) . numslots++; } COMMAND( altsound , ” s i ” ) ; void altmapsound ( char ∗name, i n t ∗v o l ) { i f ( mapsounds . empty ( ) ) return ; addslot ( name, ∗vol , mapslots ) ; mapsounds . l a s t ( ) . numslots++; } COMMAND( altmapsound , ” s i ” ) ; void resetchannels ( ) { loopv ( channels ) i f ( channels [ i ] . inuse ) freechannel ( i ) ; channels . shrink ( 0 ) ; } void clear sound ( ) { closemumble ( ) ; i f ( nosound ) return ; stopmusic ( ) ;

engine/sound.cpp enumerate ( samples , soundsample , s , s . cleanup ( ) ) ; Mix CloseAudio ( ) ; resetchannels ( ) ; gameslots . s e t s i z e ( 0 ) ; gamesounds . s e t s i z e ( 0 ) ; mapslots . s e t s i z e ( 0 ) ; mapsounds . s e t s i z e ( 0 ) ; samples . c l e a r ( ) ; } void stopmapsounds ( ) { loopv ( channels ) i f ( channels [ i ] . inuse && channels [ i ] . ent ) { Mix HaltChannel ( i ) ; freechannel ( i ) ; } } void clearmapsounds ( ) { stopmapsounds ( ) ; mapslots . s e t s i z e ( 0 ) ; mapsounds . s e t s i z e ( 0 ) ; } void stopmapsound ( e x t e n t i t y ∗e ) { loopv ( channels ) { soundchannel &chan = channels [ i ] ; i f ( chan . inuse && chan . ent == e ) { Mix HaltChannel ( i ) ; freechannel ( i ) ; } } } void checkmapsounds ( ) { const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) { e x t e n t i t y &e = ∗ents [ i ] ; i f ( e . type ! =ET SOUND) continue ; i f ( camera1−>o . d i s t ( e . o ) < e . a t t r 2 ) { i f ( ! e . v i s i b l e ) playsound ( e . attr1 , NULL, &e , SND MAP, −1) ; } e l s e i f ( e . v i s i b l e ) stopmapsound(&e ) ; } }

483

} void updatesounds ( ) { updatemumble ( ) ; i f ( nosound ) return ; i f ( minimized ) stopsounds ( ) ; e l s e i f (mainmenu) stopmapsounds ( ) ; e l s e checkmapsounds ( ) ; i n t d i r t y = 0; loopv ( channels ) { soundchannel &chan = channels [ i ] ; i f ( chan . inuse && chan . hasloc ( ) && updatechannel ( chan ) ) d i r t y ++; } i f ( dirty ) { SDL LockAudio ( ) ; // workaround f o r race conditions inside Mix SetPanning loopv ( channels ) { soundchannel &chan = channels [ i ] ; i f ( chan . inuse && chan . d i r t y ) syncchannel ( chan ) ; } SDL UnlockAudio ( ) ; } i f ( music ) { i f ( ! Mix PlayingMusic ( ) ) musicdone ( ) ; e l s e i f ( Mix PausedMusic ( ) ) Mix ResumeMusic ( ) ; } } VARP( maxsoundsatonce , 0 , 7 , 100) ; VAR( dbgsound , 0 , 0 , 1) ; s t a t i c Mix Chunk ∗loadwav ( const char ∗name) { Mix Chunk ∗c = NULL; stream ∗z = o p e n z i p f i l e ( name, ” rb ” ) ; if (z) { SDL RWops ∗rw = z−>rwops ( ) ; i f ( rw ) { c = Mix LoadWAV RW ( rw , 0) ; SDL FreeRW ( rw ) ; } delete z ; } i f ( ! c ) c = Mix LoadWAV ( f i n d f i l e ( name, ” rb ” ) ) ; return c ; }

VAR( stereo , 0 , 1 , 1) ; VARP( maxsoundradius , 0 , 340, 10000) ; bool updatechannel ( soundchannel &chan ) { i f ( ! chan . s l o t ) return f a l s e ; i n t v o l = soundvol , pan = 255/2; i f ( chan . hasloc ( ) ) { vec v ; f l o a t d i s t = chan . l o c . d i s t ( camera1−>o , v ) ; i n t rad = maxsoundradius ; i f ( chan . ent ) { rad = chan . ent−>a t t r 2 ; i f ( chan . ent−>a t tr 3 ) { rad −= chan . ent−>a t t r 3 ; d i s t −= chan . ent−>a t t r 3 ; } } e l s e i f ( chan . radius > 0) rad = maxsoundradius ? min ( maxsoundradius , chan . radius ) : chan . radius ; i f ( rad > 0) v o l −= i n t ( clamp ( d i s t /rad , 0.0 f , 1.0 f )∗soundvol ) ; // simple mono distance attenuation i f ( stereo && ( v . x != 0 || v . y ! = 0) && dist >0) { v . rotate around z(−camera1−>yaw∗RAD) ; pan = i n t (255.9 f ∗(0.5 f − 0.5 f∗v . x/v . magnitude2 ( ) ) ) ; // range i s from 0 ( l e f t ) to 255 ( r i g h t ) } } v o l = ( v o l∗MAXVOL∗chan . s l o t−>volume ) /255/255; v o l = min ( vol , MAXVOL) ; i f ( v o l == chan . volume && pan == chan . pan ) return f a l s e ; chan . volume = v o l ; chan . pan = pan ; chan . d i r t y = true ; return true ;

s t a t i c bool loadsoundslot ( soundslot &s l o t , bool msg = f a l s e ) { i f ( s l o t . sample−>chunk ) return true ; i f ( ! s l o t . sample−>name [ 0 ] ) return f a l s e ; s t a t i c const char ∗ const exts [ ] = { ” ” , ” . wav ” , ” . ogg ” }; s t r i n g filename ; l o o p i ( s i z e o f ( exts ) / s i z e o f ( exts [ 0 ] ) ) { formatstring ( filename ) ( ” packages/sounds/%s%s ” , s l o t . sample−>name, exts [ i ] ) ; i f ( msg && ! i ) renderprogress ( 0 , filename ) ; path ( filename ) ; s l o t . sample−>chunk = loadwav ( filename ) ; i f ( s l o t . sample−>chunk ) return true ; } conoutf (CON ERROR, ” f a i l e d to load sample : packages/sounds/%s ” , s l o t . sample−>name) ; return f a l s e ; } s t a t i c i n l i n e void preloadsound ( vector &sounds , vector< soundslot> &s l o t s , i n t n ) { i f ( ! sounds . inrange ( n ) ) return ; soundconfig &c o n f i g = sounds [ n ] ; loopk ( c o n f i g . numslots ) loadsoundslot ( s l o t s [ c o n f i g . s l o t s +k ] , true ) ; } void preloadsound ( i n t n ) { preloadsound ( gamesounds , gameslots , n ) ; } void preloadmapsound ( i n t n ) { preloadsound ( mapsounds, mapslots , n ) ; }

484

Foundations of Videogame Programming Code Repository i f ( fade ) { Mix Volume ( chanid , chan . volume ) ; playing = expire >= 0 ? Mix FadeInChannelTimed ( chanid , s l o t . sample −>chunk , loops , fade , expire ) : Mix FadeInChannel ( chanid , s l o t . sample−>chunk , loops , fade ) ; } e l s e playing = expire >= 0 ? Mix PlayChannelTimed ( chanid , s l o t . sample −>chunk , loops , expire ) : Mix PlayChannel ( chanid , s l o t . sample−> chunk , loops ) ; i f ( playing >= 0) syncchannel ( chan ) ; e l s e freechannel ( chanid ) ; SDL UnlockAudio ( ) ; return playing ;

void preloadmapsounds ( ) { const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) { e x t e n t i t y &e = ∗ents [ i ] ; i f ( e . type==ET SOUND) preloadsound ( mapsounds, mapslots , e . a t t r 1 ) ; } } i n t playsound ( i n t n , const vec ∗loc , e x t e n t i t y ∗ent , i n t f l a g s , i n t loops , i n t fade , i n t chanid , i n t radius , i n t expire ) { i f ( nosound || ! soundvol || minimized ) return −1;

}

vector &s l o t s = ent || f l a g s&SND MAP ? mapslots : gameslots ; vector &sounds = ent || f l a g s&SND MAP ? mapsounds : gamesounds ; i f ( ! sounds . inrange ( n ) ) { conoutf (CON WARN, ” unregistered sound : %d ” , n ) ; return −1; } soundconfig &c o n f i g = sounds [ n ] ;

void stopsounds ( ) { loopv ( channels ) i f ( channels [ i ] . inuse ) { Mix HaltChannel ( i ) ; freechannel ( i ) ; } }

i f ( l o c && ( maxsoundradius || radius > 0) ) { // c u l l sounds that are unlikely to be heard i n t rad = radius > 0 ? ( maxsoundradius ? min ( maxsoundradius , radius ) : radius ) : maxsoundradius ; i f ( camera1−>o . d i s t (∗ l o c ) > 1.5 f∗rad ) { i f ( channels . inrange ( chanid ) && channels [ chanid ] . inuse && c o n f i g . hasslot ( channels [ chanid ] . s l o t , s l o t s ) ) { Mix HaltChannel ( chanid ) ; freechannel ( chanid ) ; } return −1; } } i f ( chanid < 0) { i f ( c o n f i g . maxuses ) { i n t uses = 0; loopv ( channels ) i f ( channels [ i ] . inuse && c o n f i g . hasslot ( channels [ i ] . s l o t , s l o t s ) && ++uses >= c o n f i g . maxuses ) return −1; } // avoid bursts o f sounds with heavy packetloss and in sp s t a t i c i n t soundsatonce = 0 , lastsoundmillis = 0; i f ( t o t a l m i l l i s == lastsoundmillis ) soundsatonce ++; e l s e soundsatonce = 1; lastsoundmillis = t o t a l m i l l i s ; i f ( maxsoundsatonce && soundsatonce > maxsoundsatonce ) return −1; } i f ( channels . inrange ( chanid ) ) { soundchannel &chan = channels [ chanid ] ; i f ( chan . inuse && c o n f i g . hasslot ( chan . s l o t , s l o t s ) ) { i f ( l o c ) chan . l o c = ∗l o c ; e l s e i f ( chan . hasloc ( ) ) chan . c l e a r l o c ( ) ; return chanid ; } } i f ( fade < 0) return −1; soundslot &s l o t = s l o t s [ c o n f i g . chooseslot ( ) ] ; i f ( ! s l o t . sample−>chunk && ! loadsoundslot ( s l o t ) ) return −1; i f ( dbgsound ) conoutf ( ” sound : %s ” , s l o t . sample−>name) ; chanid = −1; loopv ( channels ) i f ( ! channels [ i ] . inuse ) { chanid = i ; break ; } i f ( chanid < 0 && channels . length ( ) < maxchannels ) chanid = channels . length ( ) ; i f ( chanid < 0) loopv ( channels ) i f ( ! channels [ i ] . volume ) { chanid = i ; break ; } i f ( chanid < 0) return −1; SDL LockAudio ( ) ; // must lock here to prevent freechannel/ Mix SetPanning race conditions i f ( channels . inrange ( chanid ) && channels [ chanid ] . inuse ) { Mix HaltChannel ( chanid ) ; freechannel ( chanid ) ; } soundchannel &chan = newchannel ( chanid , &s l o t , loc , ent , f l a g s , radius ); updatechannel ( chan ) ; i n t playing = −1;

bool stopsound ( i n t n , i n t chanid , i n t fade ) { i f ( ! channels . inrange ( chanid ) || ! channels [ chanid ] . inuse || ! gamesounds . inrange ( n ) || ! gamesounds [ n ] . hasslot ( channels [ chanid ] . s l o t , gameslots ) ) return f a l s e ; i f ( dbgsound ) conoutf ( ” stopsound : %s ” , channels [ chanid ] . s l o t−>sample−> name) ; i f ( ! fade || ! Mix FadeOutChannel ( chanid , fade ) ) { Mix HaltChannel ( chanid ) ; freechannel ( chanid ) ; } return true ; } i n t playsoundname ( const char ∗s , const vec ∗loc , i n t vol , i n t f l a g s , i n t loops , i n t fade , i n t chanid , i n t radius , i n t expire ) { i f ( ! v o l ) v o l = 100; i n t id = findsound ( s , vol , gamesounds , gameslots ) ; i f ( id < 0) id = addsound ( s , vol , 0 , gamesounds , gameslots ) ; return playsound ( id , loc , NULL, f l a g s , loops , fade , chanid , radius , expire ) ; } void sound ( i n t ∗n ) { playsound(∗n ) ; } COMMAND( sound , ” i ” ) ; void resetsound ( ) { const SDL version ∗v = Mix Linked Version ( ) ; i f (SDL VERSIONNUM( v−>major , v−>minor , v−>patch ) seek ( 0 , SEEK SET ) ; Mix CloseAudio ( ) ; } initsound ( ) ; resetchannels ( ) ; i f ( nosound ) { DELETEA( m u s i c f i l e ) ; DELETEA( musicdonecmd ) ; music = NULL; gamesounds . s e t s i z e ( 0 ) ; mapsounds . s e t s i z e ( 0 ) ; samples . c l e a r ( ) ; return ; } i f ( music && loadmusic ( m u s i c f i l e ) ) { Mix PlayMusic ( music , musicdonecmd ? 0 : −1) ; Mix VolumeMusic ( ( musicvol∗MAXVOL) /255) ; } else

engine/textedit.h {

485

{ mumbleinfo = ( MumbleInfo ∗)MapViewOfFile ( mumblelink , FILE MAP ALL ACCESS , 0 , 0 , s i z e o f ( MumbleInfo ) ) ; i f ( mumbleinfo ) wcsncpy ( mumbleinfo−>name, L ” Sauerbraten ” , 256) ;

DELETEA( m u s i c f i l e ) ; DELETEA( musicdonecmd ) ; } } COMMAND( resetsound , ” ” ) ; # i f d e f WIN32 #include #e l s e #include # i f d e f POSIX SHARED MEMORY OBJECTS #include #include #include #include #include #endif #endif # i f defined ( WIN32 ) || defined ( POSIX SHARED MEMORY OBJECTS) struct MumbleInfo { i n t version , timestamp ; vec pos , front , top ; wchar t name[ 2 5 6 ] ; }; #endif # i f d e f WIN32 s t a t i c HANDLE mumblelink = NULL; s t a t i c MumbleInfo ∗mumbleinfo = NULL; #define VALID MUMBLELINK ( mumblelink && mumbleinfo ) # e l i f defined ( POSIX SHARED MEMORY OBJECTS) s t a t i c i n t mumblelink = −1; s t a t i c MumbleInfo ∗mumbleinfo = ( MumbleInfo ∗)−1; #define VALID MUMBLELINK ( mumblelink >= 0 && mumbleinfo ! = ( MumbleInfo ∗) −1) #endif # i f d e f VALID MUMBLELINK VARFP( mumble, 0 , 1 , 1 , { i f (mumble) initmumble ( ) ; e l s e closemumble ( ) ; }) ; #e l s e VARFP( mumble, 0 , 0 , 1 , { i f (mumble) initmumble ( ) ; e l s e closemumble ( ) ; }) ; #endif void initmumble ( ) { i f ( ! mumble) return ; # i f d e f VALID MUMBLELINK i f (VALID MUMBLELINK) return ; # i f d e f WIN32 mumblelink = OpenFileMapping ( FILE MAP ALL ACCESS , FALSE, ” MumbleLink ” ) ; i f ( mumblelink )

} # e l i f defined ( POSIX SHARED MEMORY OBJECTS) defformatstring (shmname) ( ” / MumbleLink.%d ” , getuid ( ) ) ; mumblelink = shm open (shmname, O RDWR, 0) ; i f ( mumblelink >= 0) { mumbleinfo = ( MumbleInfo ∗)mmap(NULL, s i z e o f ( MumbleInfo ) , PROT READ|PROT WRITE, MAP SHARED, mumblelink , 0) ; i f ( mumbleinfo ! = ( MumbleInfo ∗)−1) wcsncpy ( mumbleinfo−>name, L ” Sauerbraten ” , 256) ; } #endif i f ( ! VALID MUMBLELINK) closemumble ( ) ; #e l s e conoutf (CON ERROR, ”Mumble p o s i t i o n a l audio i s not a v a i l a b l e on t h i s platform . ” ) ; #endif } void closemumble ( ) { # i f d e f WIN32 i f ( mumbleinfo ) { UnmapViewOfFile ( mumbleinfo ) ; mumbleinfo = NULL; } i f ( mumblelink ) { CloseHandle ( mumblelink ) ; mumblelink = NULL; } # e l i f defined ( POSIX SHARED MEMORY OBJECTS) i f ( mumbleinfo ! = ( MumbleInfo ∗)−1) { munmap( mumbleinfo , s i z e o f ( MumbleInfo ) ) ; mumbleinfo = ( MumbleInfo ∗)−1; } i f ( mumblelink >= 0) { close ( mumblelink ) ; mumblelink = −1; } #endif } s t a t i c i n l i n e vec mumblevec ( const vec &v , bool pos = f a l s e ) { // change from X l e f t , Z up, Y forward to X right , Y up, Z forward // 8 cube units = 1 meter vec m(−v . x , v . z , v . y ) ; i f ( pos ) m. div ( 8 ) ; return m; } void updatemumble ( ) { # i f d e f VALID MUMBLELINK i f ( ! VALID MUMBLELINK) return ; s t a t i c i n t timestamp = 0; mumbleinfo−>version = 1; mumbleinfo−>timestamp = ++timestamp ; mumbleinfo−>pos = mumblevec ( player−>o , true ) ; mumbleinfo−>f r o n t = mumblevec ( vec (RAD∗player−>yaw , RAD∗player−>pitch ) ) ; mumbleinfo−>top = mumblevec ( vec (RAD∗player−>yaw , RAD∗( player−>pitch +90) ) ) ; #endif }

engine/textedit.h maxlen = ( t o t a l + CHUNKSIZE) − t o t a l%CHUNKSIZE; char ∗newtext = new char [ maxlen ] ; i f ( fmt ) { v a l i s t args ; v a s t a r t ( args , fmt ) ; v s n p r i n t f ( newtext , maxlen , fmt , args ) ; va end ( args ) ; } DELETEA( t e x t ) ; t e x t = newtext ; return true ;

struct e d i t l i n e { enum { CHUNKSIZE = 256 }; char ∗t e x t ; i n t len , maxlen ; e d i t l i n e ( ) : t e x t (NULL) , len ( 0 ) , maxlen ( 0 ) {} e d i t l i n e ( const char ∗i n i t ) : t e x t (NULL) , len ( 0 ) , maxlen ( 0 ) { set ( i n i t ) ; } bool empty ( ) { return len 0 && t e x t [ len−1] == ’\n ’ ) { t e x t[−−len ] = ’ \ 0 ’ ; return true ; } i f ( len + 1 >= maxlen && len + 1 < chop ) grow ( len + CHUNKSIZE, ”% s ” , text ) ; } i f ( len + 1 >= chop ) { char buf [CHUNKSIZE ] ; while ( f−>g e t l i n e ( buf , s i z e o f ( buf ) ) ) { i n t blen = s t r l e n ( buf ) ; i f ( blen > 0 && buf [ blen−1] == ’\n ’ ) return true ; } } return len > 0; } void del ( i n t s t a r t , i n t count ) { i f ( ! t e x t ) return ; i f ( s t a r t < 0) { count += s t a r t ; s t a r t = 0; } i f ( count = len ) return ; i f ( s t a r t + count > len ) count = len − s t a r t − 1; memmove(& t e x t [ s t a r t ] , &t e x t [ s t a r t +count ] , len + 1 − ( s t a r t + count ) ); len −= count ; } void chop ( i n t newlen ) { i f ( ! t e x t ) return ; len = clamp ( newlen , 0 , len ) ; t e x t [ len ] = ’ \ 0 ’ ; } void i n s e r t ( char ∗str , i n t s t a r t , i n t count = 0) { i f ( count = n ) cy = n−1; i n t len = l i n e s [ cy ] . len ; i f ( cx < 0) cx = 0; e l s e i f ( cx > len ) cx = len ; i f (mx >= 0) { i f (my < 0) my = 0; e l s e i f (my >= n ) my = n−1; len = l i n e s [my ] . len ; i f (mx > len ) mx = len ; } sx = (mx >= 0) ? mx : cx ; sy = (mx >= 0) ? my : cy ; ex = cx ; ey = cy ; i f ( sy > ey ) { swap ( sy , ey ) ; swap ( sx , ex ) ; } e l s e i f ( sy==ey && sx > ex ) swap ( sx , ex ) ; return ( sx ! = ex ) || ( sy ! = ey ) ; } bool region ( ) { i n t sx , sy , ex , ey ; return region ( sx , sy , ex , ey ) ; } // also ensures that cy i s always within l i n e s [ ] and cx i s v a l i d e d i t l i n e ¤tline ( ) { i n t n = l i n e s . length ( ) ; assert ( n ! = 0) ; i f ( cy < 0) cy = 0; e l s e i f ( cy >= n ) cy = n−1; i f ( cx < 0) cx = 0; e l s e i f ( cx > l i n e s [ cy ] . len ) cx = l i n e s [ cy ] . len ; return l i n e s [ cy ] ; } void copyselectionto ( e d i t o r ∗b ) { i f ( b== t h i s ) return ; b−>c l e a r (NULL) ; i n t sx , sy , ex , ey ; region ( sx , sy , ex , ey ) ; l o o p i (1+ ey−sy ) { i f ( b−>maxy ! = −1 && b−>l i n e s . length ( ) >= b−>maxy ) break ; i n t y = sy+ i ; char ∗l i n e = l i n e s [ y ] . t e x t ; i n t len = l i n e s [ y ] . len ; i f ( y == sy && y == ey ) { l i n e += sx ; len = ex − sx ; } e l s e i f ( y == sy ) l i n e += sx ; e l s e i f ( y == ey ) len = ex ; b−>l i n e s . add ( ) . set ( l i n e , len ) ; } i f ( b−>l i n e s . empty ( ) ) b−>l i n e s . add ( ) . set ( ” ” ) ; } char ∗t o s t r i n g ( ) { i n t len = 0; loopv ( l i n e s ) len += l i n e s [ i ] . len + 1; char ∗s t r = newstring ( len ) ; i n t o f f s e t = 0; loopv ( l i n e s ) { e d i t l i n e &l = l i n e s [ i ] ; memcpy(& s t r [ o f f s e t ] , l . text , l . len ) ; o f f s e t += l . len ; s t r [ o f f s e t ++] = ’\n ’ ; } str [ offset ] = ’\0 ’; return s t r ; } char ∗s e l e c t i o n t o s t r i n g ( ) { vector buf ; i n t sx , sy , ex , ey ; region ( sx , sy , ex , ey ) ; l o o p i (1+ ey−sy ) { i n t y = sy+ i ; char ∗l i n e = l i n e s [ y ] . t e x t ; i n t len = l i n e s [ y ] . len ; i f ( y == sy && y == ey ) { l i n e += sx ; len = ex − sx ; } e l s e i f ( y == sy ) l i n e += sx ; e l s e i f ( y == ey ) len = ex ; buf . put ( l i n e , len ) ; buf . add ( ’ \n ’ ) ; }

487

buf . add ( ’ \ 0 ’ ) ; return newstring ( buf . getbuf ( ) , buf . length ( ) −1) ; } void removelines ( i n t s t a r t , i n t count ) { l o o p i ( count ) l i n e s [ s t a r t + i ] . c l e a r ( ) ; l i n e s . remove ( s t a r t , count ) ; } bool del ( ) // removes the current s e l e c t i o n ( i f any ) { i n t sx , sy , ex , ey ; i f ( ! region ( sx , sy , ex , ey ) ) { mark ( f a l s e ) ; return f a l s e ; } i f ( sy == ey ) { i f ( sx == 0 && ex == l i n e s [ ey ] . len ) removelines ( sy , 1) ; e l s e l i n e s [ sy ] . del ( sx , ex − sx ) ; } else { i f ( ey > sy +1) { removelines ( sy+1 , ey−(sy +1) ) ; ey = sy +1; } i f ( ex == l i n e s [ ey ] . len ) removelines ( ey , 1) ; e l s e l i n e s [ ey ] . del ( 0 , ex ) ; i f ( sx == 0) removelines ( sy , 1) ; e l s e l i n e s [ sy ] . del ( sx , l i n e s [ sy ] . len − sx ) ; } i f ( l i n e s . empty ( ) ) l i n e s . add ( ) . set ( ” ” ) ; mark ( f a l s e ) ; cx = sx ; cy = sy ; e d i t l i n e ¤t = currentline ( ) ; i f ( cx >= current . len && cy < l i n e s . length ( ) − 1) { current . append ( l i n e s [ cy + 1 ] . t e x t ) ; removelines ( cy + 1 , 1) ; } return true ; } void i n s e r t ( char ch ) { del ( ) ; e d i t l i n e ¤t = currentline ( ) ; i f ( ch == ’\n ’ ) { i f ( maxy == −1 || cy < maxy−1) { e d i t l i n e newline(& current . t e x t [ cx ] ) ; current . chop ( cx ) ; cy = min ( l i n e s . length ( ) , cy +1) ; l i n e s . i n s e r t ( cy , newline ) ; } e l s e current . chop ( cx ) ; cx = 0; } else { i n t len = current . len ; i f ( maxx >= 0 && len > maxx−1) len = maxx−1; i f ( cx l i n e s . length ( ) == 1 || maxy == 1) { e d i t l i n e ¤t = currentline ( ) ; char ∗s t r = b−>l i n e s [ 0 ] . t e x t ; i n t slen = b−>l i n e s [ 0 ] . len ; i f ( maxx >= 0 && b−>l i n e s [ 0 ] . len + cx > maxx ) slen = maxx−cx ; i f ( slen > 0) { i n t len = current . len ; i f ( maxx >= 0 && slen + cx + len > maxx ) len = max( 0 , maxx−(cx +slen ) ) ; current . i n s e r t ( str , cx , slen ) ; cx += slen ; } }

488

Foundations of Videogame Programming Code Repository

else { loopv ( b−>l i n e s ) { if (! i ) { l i n e s [ cy + + ] . append ( b−>l i n e s [ i ] . t e x t ) ; } e l s e i f ( i >= b−>l i n e s . length ( ) ) { cx = b−>l i n e s [ i ] . len ; l i n e s [ cy ] . prepend ( b−>l i n e s [ i ] . t e x t ) ; } e l s e i f ( maxy < 0 || l i n e s . length ( ) < maxy ) l i n e s . i n s e r t ( cy ++ , e d i t l i n e ( b−>l i n e s [ i ] . t e x t ) ) ; } } } void key ( i n t code , i n t cooked ) { switch ( code ) { case SDLK UP: i f ( linewrap ) { int x , y ; char ∗s t r = currentline ( ) . t e x t ; t e x t p o s ( str , cx+1 , x , y , pixelwidth ) ; i f ( y > 0) { cx = t e x t v i s i b l e ( str , x , y−FONTH, pixelwidth ) ; break ; } } cy−−; break ; case SDLK DOWN: i f ( linewrap ) { i n t x , y , width , height ; char ∗s t r = currentline ( ) . t e x t ; t e x t p o s ( str , cx , x , y , pixelwidth ) ; text bounds ( str , width , height , pixelwidth ) ; y += FONTH; i f ( y < height ) { cx = t e x t v i s i b l e ( str , x , y , pixelwidth ) ; break ; } } cy ++; break ; case −4: cy−−; break ; case −5: cy ++; break ; case SDLK PAGEUP: cy−=p i x e l h e i g h t /FONTH; break ; case SDLK PAGEDOWN: cy+= p i x e l h e i g h t /FONTH; break ; case SDLK HOME: cx = cy = 0; break ; case SDLK END: cx = cy = INT MAX ; break ; case SDLK LEFT : cx−−; break ; case SDLK RIGHT : cx ++; break ; case SDLK DELETE: i f ( ! del ( ) ) { e d i t l i n e ¤t = currentline ( ) ; i f ( cx < current . len ) current . del ( cx , 1) ; e l s e i f ( cy < l i n e s . length ( ) −1) { //combine with next l i n e current . append ( l i n e s [ cy + 1 ] . t e x t ) ; removelines ( cy+1 , 1) ; } } break ; case SDLK BACKSPACE: i f ( ! del ( ) ) { e d i t l i n e ¤t = currentline ( ) ; i f ( cx > 0) current . del(−−cx , 1) ; e l s e i f ( cy > 0) { //combine with previous l i n e cx = l i n e s [ cy−1].len ; l i n e s [ cy−1].append ( current . t e x t ) ; removelines ( cy−−, 1) ; }

} break ; case SDLK LSHIFT : case SDLK RSHIFT : break ; case SDLK RETURN: cooked = ’\n ’ ; // f a l l through default : i n s e r t ( cooked ) ; break ; } } void h i t ( i n t hitx , i n t hity , bool dragged ) { i n t maxwidth = linewrap? pixelwidth:−1; i n t h = 0; f o r ( i n t i = s c r o l l y ; i < l i n e s . length ( ) ; i ++) { i n t width , height ; text bounds ( l i n e s [ i ] . text , width , height , maxwidth ) ; i f ( h + height > p i x e l h e i g h t ) break ; i f ( h i t y >= h && h i t y 0 && ph > 0 ; ) { i n t width , height ; text bounds ( l i n e s [ slines −1]. text , width , height , maxwidth ) ; i f ( height > ph ) break ; ph −= height ; slines−−; } return s l i n e s ; } void draw ( i n t x , i n t y , i n t color , bool h i t ) { i n t maxwidth = linewrap? pixelwidth:−1; i n t sx , sy , ex , ey ; bool s e l e c t i o n = region ( sx , sy , ex , ey ) ; // f i x s c r o l l y so that i s always on screen i f ( cy < s c r o l l y ) s c r o l l y = cy ; else { i f ( s c r o l l y < 0) s c r o l l y = 0; i n t h = 0; f o r ( i n t i = cy ; i >= s c r o l l y ; i−−) { i n t width , height ; text bounds ( l i n e s [ i ] . text , width , height , maxwidth ) ; i f ( h + height > p i x e l h e i g h t ) { s c r o l l y = i +1; break ; } h += height ; } } i f ( selection ) { // convert from cursor coords i n t o p i x e l coords i n t psx , psy , pex , pey ; t e x t p o s ( l i n e s [ sy ] . text , sx , psx , psy , maxwidth ) ; t e x t p o s ( l i n e s [ ey ] . text , ex , pex , pey , maxwidth ) ; i n t maxy = l i n e s . length ( ) ; i n t h = 0; f o r ( i n t i = s c r o l l y ; i < maxy; i ++) { i n t width , height ; text bounds ( l i n e s [ i ] . text , width , height , maxwidth ) ; i f ( h + height > p i x e l h e i g h t ) { maxy = i ; break ; } i f ( i == sy ) psy += h ; i f ( i == ey ) { pey += h ; break ; } h += height ; } maxy−−; i f ( ey >= s c r o l l y && sy maxy ) { ey = maxy; pey = p i x e l h e i g h t − FONTH; pex = pixelwidth ; } notextureshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ; glColor3ub (0xA0 , 0x80 , 0x80 ) ; glBegin (GL QUADS) ; i f ( psy == pey ) { g l V e r t e x 2 f ( x+psx , y+psy ) ; g l V e r t e x 2 f ( x+pex , y+psy ) ; g l V e r t e x 2 f ( x+pex , y+pey+FONTH) ; g l V e r t e x 2 f ( x+psx , y+pey+FONTH) ; } else { g l V e r t e x 2 f ( x+psx , y+psy ) ; g l V e r t e x 2 f ( x+psx , y+psy+FONTH) ; g l V e r t e x 2 f ( x+pixelwidth , y+psy+FONTH) ; g l V e r t e x 2 f ( x+pixelwidth , y+psy ) ; i f ( pey−psy > FONTH) { glVertex2f ( x , y+psy+FONTH) ; g l V e r t e x 2 f ( x+pixelwidth , y+psy+FONTH) ; g l V e r t e x 2 f ( x+pixelwidth , y+pey ) ; glVertex2f ( x , y+pey ) ; } glVertex2f ( x , y+pey ) ; glVertex2f ( x , y+pey+FONTH) ; g l V e r t e x 2 f ( x+pex , y+pey+FONTH) ; g l V e r t e x 2 f ( x+pex , y+pey ) ; } glEnd ( ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } } i n t h = 0; f o r ( i n t i = s c r o l l y ; i < l i n e s . length ( ) ; i ++) { i n t width , height ; text bounds ( l i n e s [ i ] . text , width , height , maxwidth ) ; i f ( h + height > p i x e l h e i g h t ) break ; draw text ( l i n e s [ i ] . text , x , y+h , color>>16, ( color>>8)&0xFF , c o l o r&0xFF , 0xFF , h i t&&(cy== i ) ?cx:−1, maxwidth ) ; i f ( linewrap && height > FONTH) // l i n e wrap i n d i c a t o r { notextureshader−>set ( ) ; glDisable ( GL TEXTURE 2D ) ; glColor3ub (0x80 , 0xA0 , 0x80 ) ; glBegin ( GL TRIANGLE STRIP ) ; glVertex2f ( x , y+h+FONTH) ; glVertex2f ( x , y+h+height ) ; g l V e r t e x 2 f ( x−FONTW/2 , y+h+FONTH) ; g l V e r t e x 2 f ( x−FONTW/2 , y+h+height ) ; glEnd ( ) ; glEnable ( GL TEXTURE 2D ) ; defaultshader−>set ( ) ; } h+=height ; } } }; // a ’ stack ’ where the l a s t i s the current focused e d i t o r s t a t i c vector e d i t o r s ; s t a t i c e d i t o r ∗currentfocus ( ) { return e d i t o r s . length ( ) ? e d i t o r s . l a s t ( ) : NULL; } s t a t i c void readyeditors ( ) { loopv ( e d i t o r s ) e d i t o r s [ i]−>a c t i v e = ( e d i t o r s [ i]−>mode==EDITORFOREVER) ; } s t a t i c void f l u s h e d i t o r s ( ) { loopvrev ( e d i t o r s ) i f ( ! e d i t o r s [ i]−>a c t i v e ) { e d i t o r ∗e = e d i t o r s . remove ( i ) ; DELETEP( e ) ; } } s t a t i c e d i t o r ∗useeditor ( const char ∗name, i n t mode, bool focus , const char ∗i n i t v a l = NULL) { loopv ( e d i t o r s ) { e d i t o r ∗e = i f ( focus ) { last e−>a c t i v e =

i f ( strcmp ( e d i t o r s [ i]−>name, name) == 0) editors [ i ] ; e d i t o r s . add ( e ) ; e d i t o r s . remove ( i ) ; } // re−p o s i t i o n as true ;

489

return e ; } e d i t o r ∗e = new e d i t o r ( name, mode, i n i t v a l ) ; i f ( focus ) e d i t o r s . add ( e ) ; e l s e e d i t o r s . i n s e r t ( 0 , e ) ; return e ; }

#define TEXTCOMMAND( f , s , d , body ) ICOMMAND( f , s , d,\ e d i t o r ∗top = currentfocus ( ) ;\ i f ( ! top || i d e n t f l a g s&IDF OVERRIDDEN) return ;\ body\ ) ICOMMAND( t e x t l i s t , ” ” , ( ) , // @DEBUG return l i s t o f a l l the e d i t o r s vector s ; loopv ( e d i t o r s ) { i f ( i > 0) s . put ( ” , ” , 2) ; s . put ( e d i t o r s [ i]−>name, s t r l e n ( e d i t o r s [ i]−>name) ) ; } s . add ( ’ \ 0 ’ ) ; r e s u l t ( s . getbuf ( ) ) ; ); TEXTCOMMAND( textshow , ” ” , ( ) , // @DEBUG return the s t a r t o f the b u f f e r editline line ; l i n e . combinelines ( top−>l i n e s ) ; result ( line . text ) ; line . clear ( ) ; ); ICOMMAND( textfocus , ” s i ” , ( char ∗name, i n t ∗mode ) , // focus on a ( or create a p e r s i s t e n t ) s p e c i f i c editor , e l s e returns current name i f (∗name) useeditor ( name, ∗mode 0) r e s u l t ( e d i t o r s . l a s t ( )−>name) ; ); TEXTCOMMAND( textprev , ” ” , ( ) , e d i t o r s . i n s e r t ( 0 , top ) ; e d i t o r s . pop ( ) ; ) ; // return to the previous e d i t o r TEXTCOMMAND( textmode , ” i ” , ( i n t ∗m) , // (1= keep while focused , 2= keep while used in gui , 3= keep f o r e v e r ( i . e . u n t i l mode changes ) ) topmost editor , return current s e t t i n g i f no args i f (∗m) top−>mode = ∗m; e l s e i n t r e t ( top−>mode ) ; ); TEXTCOMMAND( textsave , ” s ” , ( char ∗ f i l e ) , // saves the topmost ( filename i s optional ) i f (∗ f i l e ) top−>s e t f i l e ( path ( f i l e , true ) ) ; top−>save ( ) ; ); TEXTCOMMAND( textload , ” s ” , ( char ∗ f i l e ) , // loads i n t o the topmost editor , returns filename i f no args i f (∗ f i l e ) { top−>s e t f i l e ( path ( f i l e , true ) ) ; top−>load ( ) ; } e l s e i f ( top−>filename ) r e s u l t ( top−>filename ) ; ); TEXTCOMMAND( t e x t i n i t , ” sss ” , ( char ∗name, char ∗f i l e , char ∗i n i t v a l ) , // loads i n t o named e d i t o r i f no f i l e assigned and e d i t o r has been rendered { e d i t o r ∗e = NULL; loopv ( e d i t o r s ) i f ( ! strcmp ( e d i t o r s [ i]−>name, name) ) { e = e d i t o r s [ i ] ; break ; } i f ( e && e−>rendered && ! e−>filename && ∗ f i l e && ( e−>l i n e s . empty ( ) || ( e−>l i n e s . length ( ) == 1 && ! strcmp ( e−>l i n e s [ 0 ] . text , i n i t v a l ) ) ) ) { e−>s e t f i l e ( path ( f i l e , true ) ) ; e−>load ( ) ; } }) ; #define PASTEBUFFER ”# pastebuffer ” TEXTCOMMAND( textcopy , ” ” , ( ) , e d i t o r ∗b = useeditor (PASTEBUFFER, EDITORFOREVER, f a l s e ) ; top−>copyselectionto ( b ) ; ) ; TEXTCOMMAND( textpaste , ” ” , ( ) , e d i t o r ∗b = useeditor (PASTEBUFFER, EDITORFOREVER, f a l s e ) ; top−>i n s e r t a l l f r o m ( b ) ; ) ; TEXTCOMMAND( textmark , ” i ” , ( i n t ∗m) , // (1=mark, 2=unmark ) , return current mark s e t t i n g i f no args i f (∗m) top−>mark(∗m==1) ; e l s e i n t r e t ( top−>region ( ) ? 1 : 2) ; ); TEXTCOMMAND( t e x t s e l e c t a l l , ” ” , ( ) , top−>s e l e c t a l l ( ) ; ) ; TEXTCOMMAND( t e x t c l e a r , ” ” , ( ) , top−>c l e a r ( ) ; ) ; TEXTCOMMAND( t e x t c u r r e n t l i n e , ” ” , ( ) , r e s u l t ( top−>currentline ( ) . t e x t ) ; ) ; TEXTCOMMAND( textexec , ” i ” , ( i n t ∗s e l e c t e d ) , // execute s c r i p t commands from the b u f f e r (0= a l l , 1=s e l e c t e d region only ) char ∗s c r i p t = ∗s e l e c t e d ? top−>s e l e c t i o n t o s t r i n g ( ) : top−>t o s t r i n g ( ) ; execute ( s c r i p t ) ; delete [ ] script ; );

490

Foundations of Videogame Programming Code Repository

engine/texture.cpp // texture . cpp : texture s l o t management #include ” engine . h” #define FUNCNAME(name) name##1 #define DEFPIXEL uint OP( r , 0) ; #define PIXELOP OP( r , 0) ; #define BPP 1 #include ” scale . h” #define FUNCNAME(name) name##2 #define DEFPIXEL uint OP( r , 0) , OP( g , 1) ; #define PIXELOP OP( r , 0) ; OP( g , 1) ; #define BPP 2 #include ” scale . h” #define FUNCNAME(name) name##3 #define DEFPIXEL uint OP( r , 0) , OP( g , 1) , OP( b , 2) ; #define PIXELOP OP( r , 0) ; OP( g , 1) ; OP( b , 2) ; #define BPP 3 #include ” scale . h” #define FUNCNAME(name) name##4 #define DEFPIXEL uint OP( r , 0) , OP( g , 1) , OP( b , 2) , OP( a , 3) ; #define PIXELOP OP( r , 0) ; OP( g , 1) ; OP( b , 2) ; OP( a , 3) ; #define BPP 4 #include ” scale . h” s t a t i c void scaletexture ( uchar ∗src , uint sw, uint sh , uint bpp , uint pitch , uchar ∗dst , uint dw, uint dh ) { i f ( sw == dw∗2 && sh == dh∗2) { switch ( bpp ) { case 1: return halvetexture1 ( src , case 2: return halvetexture2 ( src , case 3: return halvetexture3 ( src , case 4: return halvetexture4 ( src , } } e l s e i f ( sw < dw || sh < dh || sw&(sw−1) { switch ( bpp ) { case 1: return scaletexture1 ( src , case 2: return scaletexture2 ( src , case 3: return scaletexture3 ( src , case 4: return scaletexture4 ( src , } } else { switch ( bpp ) { case 1: return s h i f t t e x t u r e 1 ( src , case 2: return s h i f t t e x t u r e 2 ( src , case 3: return s h i f t t e x t u r e 3 ( src , case 4: return s h i f t t e x t u r e 4 ( src , } }

sw, sw, sw, sw,

sh , sh , sh , sh ,

pitch , pitch , pitch , pitch ,

dst ) ; dst ) ; dst ) ; dst ) ;

|| sh&(sh−1))

sw, sw, sw, sw,

sh , sh , sh , sh ,

pitch , pitch , pitch , pitch ,

dst , dst , dst , dst ,

dw, dw, dw, dw,

dh ) ; dh ) ; dh ) ; dh ) ;

sw, sw, sw, sw,

sh , sh , sh , sh ,

pitch , pitch , pitch , pitch ,

dst , dst , dst , dst ,

dw, dw, dw, dw,

dh ) ; dh ) ; dh ) ; dh ) ;

} s t a t i c i n l i n e void r e o r i e n t t e x t u r e ( uchar ∗src , i n t sw, i n t sh , i n t bpp , i n t s t r i d e , uchar ∗dst , bool f l i p x , bool f l i p y , bool swapxy , bool normals = f a l s e ) { i n t s t r i d e x = bpp , s t r i d e y = bpp ; i f ( swapxy ) s t r i d e x ∗= sh ; e l s e s t r i d e y ∗= sw ; i f ( f l i p x ) { dst += ( sw−1)∗s t r i d e x ; s t r i d e x = −s t r i d e x ; } i f ( f l i p y ) { dst += ( sh−1)∗s t r i d e y ; s t r i d e y = −s t r i d e y ; } uchar ∗srcrow = src ; l o o p i ( sh ) { f o r ( uchar ∗curdst = dst , ∗src = srcrow , ∗end = &srcrow [ sw∗bpp ] ; src < end ; ) { loopk ( bpp ) curdst [ k ] = ∗src ++; i f ( normals ) { i f ( f l i p x ) curdst [ 0 ] = 255−curdst [ 0 ] ; i f ( f l i p y ) curdst [ 1 ] = 255−curdst [ 1 ] ; i f ( swapxy ) swap ( curdst [ 0 ] , curdst [ 1 ] ) ; } curdst += s t r i d e x ; } srcrow += s t r i d e ; dst += s t r i d e y ; } }

s t a t i c void r e o r i e n t s 3 t c (GLenum format , i n t blocksize , i n t w, i n t h , uchar ∗src , uchar ∗dst , bool f l i p x , bool f l i p y , bool swapxy , bool normals = f a l s e ) { i n t bx1 = 0 , by1 = 0 , bx2 = min (w, 4) , by2 = min ( h , 4) , bw = (w+3) /4 , bh = ( h+3) /4 , s t r i d e x = blocksize , s t r i d e y = blocksize ; i f ( swapxy ) s t r i d e x ∗= bw; e l s e s t r i d e y ∗= bh ; i f ( f l i p x ) { dst += ( bw−1)∗s t r i d e x ; s t r i d e x = −s t r i d e x ; bx1 += 4−bx2 ; bx2 = 4; } i f ( f l i p y ) { dst += ( bh−1)∗s t r i d e y ; s t r i d e y = −s t r i d e y ; by1 += 4−by2 ; by2 = 4; } l o o p i ( bh ) { f o r ( uchar ∗curdst = dst , ∗end = &src [bw∗blocksize ] ; src < end ; src += blocksize , curdst += s t r i d e x ) { i f ( format == GL COMPRESSED RGBA S3TC DXT3 EXT) { ullong salpha = l i l s w a p ( ∗ ( ullong ∗) src ) , dalpha = 0; uint xmask = f l i p x ? 15 : 0 , ymask = f l i p y ? 15 : 0 , x s h i f t = 2 , y s h i f t = 4; i f ( swapxy ) swap ( x s h i f t , y s h i f t ) ; f o r ( i n t y = by1 ; y < by2 ; y ++) f o r ( i n t x = bx1 ; x < bx2 ; x++) { dalpha |= ( ( salpha&15) >i , 1) , src , dst , f l i p x , f l i p y , swapxy , type==TEX NORMAL) ; src += s . c a l c l e v e l s i z e ( i ) ; dst += d . c a l c l e v e l s i z e ( i ) ; } break ; } default : r e o r i e n t t e x t u r e ( s . data , s .w, s . h , s . bpp , s . pitch , d . data , f l i p x , f l i p y , swapxy , type==TEX NORMAL) ; break ; } s . replace ( d ) ; }

491

void t e x r o t a t e ( ImageData &s , i n t numrots , i n t type = TEX DIFFUSE ) { // 1 . . 3 r o t a t e through 90..270 degrees , 4 f l i p s X, 5 f l i p s Y i f ( numrots>=1 && numrots=2 && numrots= s . bpp ) return ; w r i t e t e x ( s , dst [ dstchan ] = dst [ srcchan ] ) ; } void texdecal ( ImageData &s ) { i f ( renderpath ! =R FIXEDFUNCTION || hasTE ) return ; ImageData m( s .w, s .w, 2) ; readwritetex (m, s , dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = 255 − src [ 0 ] ;

492

Foundations of Videogame Programming Code Repository

); s . replace (m) ; } void texmix ( ImageData &s , i n t c1 , i n t c2 , i n t c3 , i n t c4 ) { i n t numchans = c1 < 0 ? 0 : ( c2 < 0 ? 1 : ( c3 < 0 ? 2 : ( c4 < 0 ? 3 : 4) ) ) ; i f ( numchans = 4) { readwritetex ( d , s , dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 3 ] ; ); } else { readwritetex ( d , s , dst [ 0 ] = src [ 0 ] ) ; } s . replace ( d ) ; } void texpremul ( ImageData &s ) { switch ( s . bpp ) { case 2: writetex ( s , dst [ 0 ] = uchar ( ( uint ( dst [ 0 ] ) ∗uint ( dst [ 1 ] ) ) /255) ; ); break ; case 4: writetex ( s , uint alpha = dst [ 3 ] ; dst [ 0 ] = uchar ( ( uint ( dst [ 0 ] ) ∗alpha ) /255) ; dst [ 1 ] = uchar ( ( uint ( dst [ 1 ] ) ∗alpha ) /255) ; dst [ 2 ] = uchar ( ( uint ( dst [ 2 ] ) ∗alpha ) /255) ; ); break ; } } void texagrad ( ImageData &s , f l o a t x2 , f l o a t y2 , f l o a t x1 , f l o a t y1 ) { i f ( s . bpp ! = 2 && s . bpp ! = 4) return ; y1 = 1 − y1 ; y2 = 1 − y2 ; f l o a t minx = 1 , miny = 1 , maxx = 1 , maxy = 1; i f ( x1 ! = x2 ) { minx = (0 − x1 ) / ( x2 − x1 ) ; maxx = (1 − x1 ) / ( x2 − x1 ) ; } i f ( y1 ! = y2 ) { miny = (0 − y1 ) / ( y2 − y1 ) ; maxy = (1 − y1 ) / ( y2 − y1 ) ; } f l o a t dx = ( maxx − minx ) /max( s .w−1, 1) , dy = ( maxy − miny ) /max( s . h−1, 1) , cury = miny ; f o r ( uchar ∗dstrow = s . data + s . bpp − 1 , ∗endrow = dstrow + s . h∗s . pitch ; dstrow < endrow ; dstrow += s . pitch ) { f l o a t curx = minx ; f o r ( uchar ∗dst = dstrow , ∗end = &dstrow [ s .w∗s . bpp ] ; dst < end ; dst += s . bpp ) { dst [ 0 ] = uchar ( dst [0]∗clamp ( curx , 0.0 f , 1.0 f )∗clamp ( cury , 0.0 f , 1.0 f ) ) ; curx += dx ; } cury += dy ; } }

VAR( hwtexsize , 1 , 0 , 0) ; VAR( hwcubetexsize , 1 , 0 , 0) ; VAR( hwmaxaniso , 1 , 0 , 0) ; VARFP( maxtexsize , 0 , 0 , 1>texreduce , 1) ; h = max( h>>texreduce , 1) ; } w = min (w, s i z e l i m i t ) ; h = min ( h , s i z e l i m i t ) ; i f ( ( ! hasNP2 || ! usenp2 ) && t a r g e t ! =GL TEXTURE RECTANGLE ARB && (w&(w −1) || h&(h−1)) ) { tw = th = 1; while ( tw < w) tw ∗= 2; while ( th < h ) th ∗= 2; i f (w < tw − tw/4) tw /= 2; i f ( h < th − th /4) th /= 2; } else { tw = w; th = h ;

engine/texture.cpp }

493

}

} void uploadtexture (GLenum target , GLenum internal , i n t tw , i n t th , GLenum format , GLenum type , void ∗pixels , i n t pw, i n t ph , i n t pitch , bool mipmap) { i n t bpp = formatsize ( format ) , row = 0 , rowalign = 0; i f ( ! pitch ) pitch = pw∗bpp ; uchar ∗buf = NULL; i f (pw! = tw || ph! = th ) { buf = new uchar [ tw∗th∗bpp ] ; scaletexture ( ( uchar ∗) pixels , pw, ph , bpp , pitch , buf , tw , th ) ; } e l s e i f ( tw∗bpp ! = pitch ) { row = pitch/bpp ; rowalign = t e x a l i g n ( pixels , pitch , 1) ; while ( rowalign > 0 && ( ( row∗bpp + rowalign − 1)/rowalign )∗rowalign ! = pitch ) rowalign >>= 1; i f ( ! rowalign ) { row = 0; buf = new uchar [ tw∗th∗bpp ] ; l o o p i ( th ) memcpy(&buf [ i∗tw∗bpp ] , & ( ( uchar ∗) p i x e l s ) [ i∗pitch ] , tw ∗bpp ) ; } } f o r ( i n t l e v e l = 0 , a l i g n = 0 ; ; l e v e l ++) { uchar ∗src = buf ? buf : ( uchar ∗) p i x e l s ; i f ( buf ) pitch = tw∗bpp ; i n t s r c a l i g n = row > 0 ? rowalign : t e x a l i g n ( src , pitch , 1) ; i f ( a l i g n ! = s r c a l i g n ) g l P i x e l S t o r e i (GL UNPACK ALIGNMENT, a l i g n = srcalign ) ; i f ( row > 0) g l P i x e l S t o r e i (GL UNPACK ROW LENGTH, row ) ; i f ( t a r g e t ==GL TEXTURE 1D ) glTexImage1D ( target , l e v e l , internal , tw , 0 , format , type , src ) ; e l s e glTexImage2D ( target , l e v e l , internal , tw , th , 0 , format , type , src ) ; i f ( row > 0) g l P i x e l S t o r e i (GL UNPACK ROW LENGTH, row = 0) ; i f ( ! mipmap || (hasGM && hwmipmap) || max( tw , th ) 1) tw /= 2; i f ( th > 1) th /= 2; i f ( ! buf ) buf = new uchar [ tw∗th∗bpp ] ; scaletexture ( src , srcw , srch , bpp , pitch , buf , tw , th ) ; } i f ( buf ) d e l e t e [ ] buf ; } void uploadcompressedtexture (GLenum target , GLenum subtarget , GLenum format , i n t w, i n t h , uchar ∗data , i n t align , i n t blocksize , i n t l e v e l s , bool mipmap) { i n t hwlimit = t a r g e t ==GL TEXTURE CUBE MAP ARB ? hwcubetexsize : hwtexsize , s i z e l i m i t = l e v e l s > 1 && maxtexsize ? min ( maxtexsize , hwlimit ) : hwlimit ; i n t l e v e l = 0; loopi ( levels ) { i n t s i z e = ( ( w + align −1)/ a l i g n ) ∗ ( ( h + align −1)/ a l i g n ) ∗ blocksize ; i f (w 0 && f i l t e r > 1) glTexParameteri ( target , GL TEXTURE MAX ANISOTROPY EXT, min ( aniso , hwmaxaniso ) ) ; glTexParameteri ( target , GL TEXTURE MAG FILTER, f i l t e r && b i l i n e a r ? GL LINEAR : GL NEAREST) ; glTexParameteri ( target , GL TEXTURE MIN FILTER , filter > 1 ? ( trilinear ? ( b i l i n e a r ? GL LINEAR MIPMAP LINEAR : GL NEAREST MIPMAP LINEAR ) : ( b i l i n e a r ? GL LINEAR MIPMAP NEAREST : GL NEAREST MIPMAP NEAREST ) ) : ( f i l t e r && b i l i n e a r ? GL LINEAR : GL NEAREST) ) ; i f (hasGM && f i l t e r > 1 && p i x e l s && hwmipmap && ! uncompressedformat ( format ) ) glTexParameteri ( target , GL GENERATE MIPMAP SGIS, GL TRUE ) ; } void createtexture ( i n t tnum, i n t w, i n t h , void ∗pixels , i n t clamp , i n t f i l t e r , GLenum component , GLenum subtarget , i n t pw, i n t ph , i n t pitch , bool r e s i z e , GLenum format ) { GLenum t a r g e t = t e x t a r g e t ( subtarget ) , type = GL UNSIGNED BYTE; switch ( component ) { case GL FLOAT RG16 NV : case GL FLOAT R32 NV : case GL RGB16F ARB: case GL RGB32F ARB: i f ( ! format ) format = GL RGB; type = GL FLOAT ; break ; case GL RGBA16F ARB: case GL RGBA32F ARB: i f ( ! format ) format = GL RGBA; type = GL FLOAT ; break ; case GL DEPTH COMPONENT16: case GL DEPTH COMPONENT24: case GL DEPTH COMPONENT32: i f ( ! format ) format = GL DEPTH COMPONENT; break ; case GL RGB5: case GL RGB8: case GL RGB16: case GL COMPRESSED RGB ARB: case GL COMPRESSED RGB S3TC DXT1 EXT: i f ( ! format ) format = GL RGB; break ; case GL RGBA8: case GL RGBA16: case GL COMPRESSED RGBA ARB: case GL COMPRESSED RGBA S3TC DXT1 EXT: case GL COMPRESSED RGBA S3TC DXT3 EXT: case GL COMPRESSED RGBA S3TC DXT5 EXT: i f ( ! format ) format = GL RGBA; break ; } i f ( ! format ) format = component ; i f (tnum) setuptexparameters ( tnum, pixels , clamp , f i l t e r , format , target ) ; i f ( ! pw) pw = w; i f ( ! ph ) ph = h ; i n t tw = w, th = h ;

494

Foundations of Videogame Programming Code Repository

bool mipmap = f i l t e r > 1 && p i x e l s ; i f ( r e s i z e && p i x e l s ) { r e s i z e t e x t u r e (w, h , mipmap, f a l s e , target , 0 , tw , th ) ; i f (mipmap) component = compressedformat ( component , tw , th ) ; } uploadtexture ( subtarget , component , tw , th , format , type , pixels , pw, ph , pitch , mipmap) ;

t−>h = t−>ys = s . h ; i n t f i l t e r = ! canreduce || r e d u c e f i l t e r ? ( mipit ? 2 : 1) : 0; glGenTextures ( 1 , &t−>id ) ; i f ( s . compressed ) { uchar ∗data = s . data ; i n t l e v e l s = s . l e v e l s , l e v e l = 0; i f ( canreduce && texreduce ) l o o p i ( min ( texreduce , s . l e v e l s −1)) { data += s . c a l c l e v e l s i z e ( l e v e l ++) ; l e v e l s−−; i f ( t−>w > 1) t−>w /= 2; i f ( t−>h > 1) t−>h /= 2; } i n t s i z e l i m i t = mipit && maxtexsize ? min ( maxtexsize , hwtexsize ) : hwtexsize ; while ( t−>w > s i z e l i m i t || t−>h > s i z e l i m i t ) { data += s . c a l c l e v e l s i z e ( l e v e l ++) ; l e v e l s−−; i f ( t−>w > 1) t−>w /= 2; i f ( t−>h > 1) t−>h /= 2; } createcompressedtexture ( t−>id , t−>w, t−>h , data , s . align , s . bpp , l e v e l s , clamp , f i l t e r , s . compressed , GL TEXTURE 2D ) ; } else { r e s i z e t e x t u r e ( t−>w, t−>h , mipit , canreduce , GL TEXTURE 2D, compress , t−>w, t−>h ) ; GLenum component = compressedformat ( format , t−>w, t−>h , compress ) ; createtexture ( t−>id , t−>w, t−>h , s . data , clamp , f i l t e r , component , GL TEXTURE 2D, t−>xs , t−>ys , s . pitch , f a l s e , format ) ; } return t ;

} void createcompressedtexture ( i n t tnum, i n t w, i n t h , uchar ∗data , i n t align , i n t blocksize , i n t l e v e l s , i n t clamp , i n t f i l t e r , GLenum format , GLenum subtarget ) { GLenum t a r g e t = t e x t a r g e t ( subtarget ) ; i f (tnum) setuptexparameters ( tnum, data , clamp , f i l t e r , format , t a r g e t ) ; uploadcompressedtexture ( target , subtarget , format , w, h , data , align , blocksize , l e v e l s , f i l t e r > 1) ; } hashtable textures ; Texture ∗notexture = NULL; // used as default , ensured to be loaded s t a t i c GLenum texformat ( i n t bpp ) { switch ( bpp ) { case 1: return GL LUMINANCE; case 2: return GL LUMINANCE ALPHA; case 3: return GL RGB; case 4: return GL RGBA; d e f a u l t : return 0; } } } s t a t i c bool alphaformat (GLenum format ) { switch ( format ) { case GL ALPHA : case GL LUMINANCE ALPHA: case GL RGBA: return true ; default : return f a l s e ; } } i n t t e x a l i g n ( void ∗data , i n t w, i n t bpp ) { s i z e t address = s i z e t ( data ) | (w∗bpp ) ; i f ( address&1) return 1; i f ( address&2) return 2; i f ( address&4) return 4; return 8; } s t a t i c Texture ∗newtexture ( Texture ∗t , const char ∗rname , ImageData &s , i n t clamp = 0 , bool mipit = true , bool canreduce = f a l s e , bool transient = f a l s e , i n t compress = 0) { if (! t ) { char ∗key = newstring ( rname ) ; t = &textures [ key ] ; t−>name = key ; } t−>clamp = clamp ; t−>mipmap = mipit ; t−>type = Texture : : IMAGE; i f ( transient ) t−>type |= Texture : : TRANSIENT; i f ( ! s . data ) { t−>type |= Texture : : STUB; t−>w = t−>h = t−>xs = t−>ys = t−>bpp = 0; return t ; } GLenum format ; i f ( s . compressed ) { format = uncompressedformat ( s . compressed ) ; t−>bpp = formatsize ( format ) ; t−>type |= Texture : :COMPRESSED; } else { format = texformat ( s . bpp ) ; t−>bpp = s . bpp ; } i f ( alphaformat ( format ) ) t−>type |= Texture : : ALPHA; t−>w = t−>xs = s .w;

# i f SDL BYTEORDER #define RGBAMASKS #define RGBMASKS #e l s e #define RGBAMASKS #define RGBMASKS #endif

== SDL BIG ENDIAN 0xff000000 , 0x00ff0000 , 0x0000ff00 , 0x000000ff 0xff0000 , 0x00ff00 , 0x0000ff , 0 0x000000ff , 0x0000ff00 , 0x00ff0000 , 0xff000000 0x0000ff , 0x00ff00 , 0xff0000 , 0

SDL Surface ∗wrapsurface ( void ∗data , i n t width , i n t height , i n t bpp ) { switch ( bpp ) { case 3: return SDL CreateRGBSurfaceFrom ( data , width , height , 8∗bpp , bpp∗width , RGBMASKS) ; case 4: return SDL CreateRGBSurfaceFrom ( data , width , height , 8∗bpp , bpp∗width , RGBAMASKS) ; } return NULL; } SDL Surface ∗creatergbsurface ( SDL Surface ∗os ) { SDL Surface ∗ns = SDL CreateRGBSurface (SDL SWSURFACE, os−>w, os−>h , 24, RGBMASKS) ; i f ( ns ) SDL BlitSurface ( os , NULL, ns , NULL) ; SDL FreeSurface ( os ) ; return ns ; } SDL Surface ∗creatergbasurface ( SDL Surface ∗os ) { SDL Surface ∗ns = SDL CreateRGBSurface (SDL SWSURFACE, os−>w, os−>h , 32, RGBAMASKS) ; i f ( ns ) { SDL SetAlpha ( os , 0 , 0) ; SDL BlitSurface ( os , NULL, ns , NULL) ; } SDL FreeSurface ( os ) ; return ns ; } bool checkgrayscale ( SDL Surface ∗s ) { // gray scale images have 256 l e v e l s , no colorkey , and the p a l e t t e i s a ramp i f ( s−>format−>p a l e t t e ) { i f ( s−>format−>p a l e t t e−>ncolors ! = 256 || s−>format−>colorkey ) return f a l s e ; const SDL Color ∗c o l o r s = s−>format−>p a l e t t e−>c o l o r s ; l o o p i (256) i f ( c o l o r s [ i ] . r ! = i || c o l o r s [ i ] . g ! = i || c o l o r s [ i ] . b ! = i ) return f a l s e ; } return true ; }

engine/texture.cpp

f o r ( i n t x = margin ; x < w−margin ; x++) { i n t dr = 0 , dg = 0 , db = 0; const uchar ∗p = src − s t a r t o f f s e t ; const i n t ∗m = mat + m s t a r t o f f s e t ; f o r ( i n t t = y ; t >= y−n ; t−−, p −= nextoffset1 , m −= mstride ) { i f ( t < 0) p += s t r i d e ; i n t a = 0; i f ( n > 1) { a += m[ −2]; i f ( x >= 2) { dr += p [ 0 ] ∗ a ; dg += p [ 1 ] ∗ a ; db += p [ 2 ] ∗ a ; a = 0; } p += bpp ; } a += m[ −1]; i f ( x >= 1) { dr += p [ 0 ] ∗ a ; dg += p [ 1 ] ∗ a ; db += p [ 2 ] ∗ a ; a = 0; } p += bpp ; i n t cr = p [ 0 ] , cg = p [ 1 ] , cb = p [ 2 ] ; a += m[ 0 ] ; dr += cr ∗ a ; dg += cg ∗ a ; db += cb ∗ a ; p += bpp ; i f ( x+1 < w) { cr = p [ 0 ] ; cg = p [ 1 ] ; cb = p [ 2 ] ; } dr += cr ∗ m [ 1 ] ; dg += cg ∗ m[ 1 ] ; db += cb ∗ m[ 1 ] ; p += bpp ; i f ( n > 1) { i f ( x+2 < w) { cr = p [ 0 ] ; cg = p [ 1 ] ; cb = p [ 2 ] ; } dr += cr ∗ m[ 2 ] ; dg += cg ∗ m[ 2 ] ; db += cb ∗ m[ 2 ] ; p += bpp ; } } p = src − s t a r t o f f s e t + s t r i d e ; m = mat + m s t a r t o f f s e t + mstride ; f o r ( i n t t = y+1; t = h ) p −= s t r i d e ; i n t a = 0; i f ( n > 1) { a += m[ −2]; i f ( x >= 2) { dr += p [ 0 ] ∗ a ; dg += p [ 1 ] ∗ a ; db += p [ 2 ] ∗ a ; a = 0; } p += bpp ; } a += m[ −1]; i f ( x >= 1) { dr += p [ 0 ] ∗ a ; dg += p [ 1 ] ∗ a ; db += p [ 2 ] ∗ a ; a = 0; } p += bpp ; i n t cr = p [ 0 ] , cg = p [ 1 ] , cb = p [ 2 ] ; a += m[ 0 ] ; dr += cr ∗ a ; dg += cg ∗ a ; db += cb ∗ a ; p += bpp ; i f ( x+1 < w) { cr = p [ 0 ] ; cg = p [ 1 ] ; cb = p [ 2 ] ; } dr += cr ∗ m [ 1 ] ; dg += cg ∗ m[ 1 ] ; db += cb ∗ m[ 1 ] ; p += bpp ; i f ( n > 1) { i f ( x+2 < w) { cr = p [ 0 ] ; cg = p [ 1 ] ; cb = p [ 2 ] ; } dr += cr ∗ m[ 2 ] ; dg += cg ∗ m[ 2 ] ; db += cb ∗ m[ 2 ] ; p += bpp ; } } i f ( normals ) { vec v ( dr−0x7F80 , dg−0x7F80 , db−0x7F80 ) ; f l o a t mag = 127.5 f /v . magnitude ( ) ; dst [ 0 ] = uchar ( v . x∗mag + 127.5 f ) ; dst [ 1 ] = uchar ( v . y∗mag + 127.5 f ) ; dst [ 2 ] = uchar ( v . z∗mag + 127.5 f ) ; } else { dst [ 0 ] = dr>>8; dst [ 1 ] = dg>>8; dst [ 2 ] = db>>8; } i f ( bpp > 3) dst [ 3 ] = src [ 3 ] ; dst += bpp ; src += bpp ; } src += 2∗margin∗bpp ;

SDL Surface ∗fixsurfaceformat ( SDL Surface ∗s ) { i f ( ! s ) return NULL; i f ( ! s−>p i x e l s || min ( s−>w, s−>h ) format−>BytesPerPixel format−>BytesPerPixel ) { case 1: i f ( ! checkgrayscale ( s ) ) return s−>format−>colorkey ? creatergbasurface ( s ) : creatergbsurface ( s ) ; break ; case 3: i f ( s−>format−>Rmask ! = rgbmasks [ 0 ] || s−>format−>Gmask ! = rgbmasks [ 1 ] || s−>format−>Bmask ! = rgbmasks [ 2 ] ) return creatergbsurface ( s ) ; break ; case 4: i f ( s−>format−>Rmask ! = rgbamasks [ 0 ] || s−>format−>Gmask ! = rgbamasks [ 1 ] || s−>format−>Bmask ! = rgbamasks [ 2 ] || s−> format−>Amask ! = rgbamasks [ 3 ] ) return s−>format−>Amask ? creatergbasurface ( s ) : creatergbsurface ( s ) ; break ; } return s ; } void t e x f l i p ( ImageData &s ) { ImageData d ( s .w, s . h , s . bpp ) ; uchar ∗dst = d . data , ∗src = &s . data [ s . pitch∗s . h ] ; loopi ( s .h) { src −= s . pitch ; memcpy( dst , src , s . bpp∗s .w) ; dst += d . pitch ; } s . replace ( d ) ; } void texnormal ( ImageData &s , i n t emphasis ) { ImageData d ( s .w, s . h , 3) ; uchar ∗src = s . data , ∗dst = d . data ; loop ( y , s . h ) loop ( x , s .w) { vec normal ( 0 . 0 f , 0.0 f , 255.0 f /emphasis ) ; normal . x += src [ y∗s . pitch + ( ( x+s .w−1)%s .w)∗s . bpp ] ; normal . x −= src [ y∗s . pitch + ( ( x+1)%s .w)∗s . bpp ] ; normal . y += src [ ( ( y+s . h−1)%s . h )∗s . pitch + x∗s . bpp ] ; normal . y −= src [ ( ( y +1)%s . h )∗s . pitch + x∗s . bpp ] ; normal . normalize ( ) ; ∗dst++ = uchar(127.5 f + normal . x∗127.5 f ) ; ∗dst++ = uchar(127.5 f + normal . y∗127.5 f ) ; ∗dst++ = uchar(127.5 f + normal . z∗127.5 f ) ; } s . replace ( d ) ; } template s t a t i c void blurtexture ( i n t w, i n t h , uchar ∗dst , const uchar ∗src , i n t margin ) { s t a t i c const i n t matrix3x3 [ 9 ] = { 0x10 , 0x20 , 0x10 , 0x20 , 0x40 , 0x20 , 0x10 , 0x20 , 0x10 }; s t a t i c const i n t matrix5x5 [ 2 5 ] = { 0x05 , 0x05 , 0x09 , 0x05 , 0x05 , 0x05 , 0x0A , 0x14 , 0x0A , 0x05 , 0x09 , 0x14 , 0x28 , 0x14 , 0x09 , 0x05 , 0x0A , 0x14 , 0x0A , 0x05 , 0x05 , 0x05 , 0x09 , 0x05 , 0x05 }; const i n t ∗mat = n > 1 ? matrix5x5 : matrix3x3 ; i n t mstride = 2∗n + 1 , m s t a r t o f f s e t = n∗( mstride + 1) , s t r i d e = bpp∗w, s t a r t o f f s e t = n∗bpp , n e x t o f f s e t 1 = s t r i d e + mstride∗bpp , n e x t o f f s e t 2 = s t r i d e − mstride∗bpp ; src += margin∗( s t r i d e + bpp ) ; f o r ( i n t y = margin ; y < h−margin ; y ++) {

495

} } void blurtexture ( i n t n , i n t bpp , i n t w, i n t h , uchar ∗dst , const uchar ∗ src , i n t margin ) { switch ( ( clamp ( n , 1 , 2)v , margin ) ; break ; case 2: blurtexture (w, h , dst−>v , src−>v , margin ) ; break ; } } void texblur ( ImageData &s , i n t n , i n t r ) { i f ( s . bpp < 3) return ; loopi ( r ) { ImageData d ( s .w, s . h , s . bpp ) ; blurtexture ( n , s . bpp , s .w, s . h , d . data , s . data ) ; s . replace ( d ) ; }

496

Foundations of Videogame Programming Code Repository

} void scaleimage ( ImageData &s , i n t w, i n t h ) { ImageData d (w, h , s . bpp ) ; scaletexture ( s . data , s .w, s . h , s . bpp , s . pitch , d . data , w, h ) ; s . replace ( d ) ; } #define readwritergbtex ( t , s , body ) \ { \ i f ( t . bpp >= 3) { readwritetex ( t , s , body ) ; } \ else \ { \ ImageData rgb ( t .w, t . h , 3) ; \ read2writetex ( rgb , t , orig , s , src , \ { \ switch ( t . bpp ) \ { \ case 1: dst [ 0 ] = o r i g [ 0 ] ; dst [ 1 ] = o r i g [ 0 ] ; dst [ 2 ] = o r i g [ 0 ] ; break ; \ case 2: dst [ 0 ] = o r i g [ 0 ] ; dst [ 1 ] = o r i g [ 1 ] ; dst [ 2 ] = o r i g [ 1 ] ; break ; \ } \ body ; \ }) ; \ t . replace ( rgb ) ; \ } \ } void forcergbimage ( ImageData &s ) { i f ( s . bpp >= 3) return ; ImageData d ( s .w, s . h , 3) ; readwritetex ( d , s , switch ( s . bpp ) { case 1: dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 0 ] ; dst [ 2 ] = src [ 0 ] ; break ; case 2: dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 1 ] ; dst [ 2 ] = src [ 1 ] ; break ; } ); s . replace ( d ) ; } #define readwritergbatex ( t , s , body ) \ { \ i f ( t . bpp >= 4) { readwritetex ( t , s , body ) ; } \ else \ { \ ImageData rgba ( t .w, t . h , 4) ; \ read2writetex ( rgba , t , orig , s , src , \ { \ switch ( t . bpp ) \ { \ case 1: dst [ 0 ] = o r i g [ 0 ] ; dst [ 1 ] = o r i g [ 0 ] ; dst [ 2 ] = o r i g [ 0 ] ; break ; \ case 2: dst [ 0 ] = o r i g [ 0 ] ; dst [ 1 ] = o r i g [ 1 ] ; dst [ 2 ] = o r i g [ 1 ] ; break ; \ case 3: dst [ 0 ] = o r i g [ 0 ] ; dst [ 1 ] = o r i g [ 1 ] ; dst [ 2 ] = o r i g [ 2 ] ; break ; \ } \ body ; \ }) ; \ t . replace ( rgba ) ; \ } \ } void forcergbaimage ( ImageData &s ) { i f ( s . bpp >= 4) return ; ImageData d ( s .w, s . h , 4) ; readwritetex ( d , s , switch ( s . bpp ) { case 1: dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 0 ] ; dst [ 2 ] = src [ 0 ] ; break ; case 2: dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 1 ] ; dst [ 2 ] = src [ 1 ] ; break ; case 3: dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = src [ 1 ] ; dst [ 2 ] = src [ 2 ] ; break ; } ); s . replace ( d ) ; } bool canloadsurface ( const char ∗name) { stream ∗f = o p e n f i l e ( name, ” rb ” ) ; i f ( ! f ) return f a l s e ; delete f ; return true ; }

SDL Surface ∗loadsurface ( const char ∗name) { SDL Surface ∗s = NULL; stream ∗z = o p e n z i p f i l e ( name, ” rb ” ) ; if (z) { SDL RWops ∗rw = z−>rwops ( ) ; i f ( rw ) { s = IMG Load RW ( rw , 0) ; SDL FreeRW ( rw ) ; } delete z ; } i f ( ! s ) s = IMG Load ( f i n d f i l e ( name, ” rb ” ) ) ; return fixsurfaceformat ( s ) ; } s t a t i c vec parsevec ( const char ∗arg ) { vec v ( 0 , 0 , 0) ; i n t i = 0; f o r ( ; arg [ 0 ] && ( ! i || arg [ 0 ] = = ’ / ’ ) && i name[0]== ’ < ’) { cmds = tex−>name; f i l e = s t r r c h r ( tex−>name, ’ > ’) ; i f ( ! f i l e ) { i f ( msg ) conoutf (CON ERROR, ” could not load texture packages/%s ” , tex−>name) ; return f a l s e ; } f i l e ++; } e l s e f i l e = tex−>name; s t a t i c s t r i n g pname; formatstring (pname) ( ” packages/%s ” , f i l e ) ; f i l e = path (pname) ; } e l s e i f ( tname[0]== ’ < ’) { cmds = tname ; f i l e = s t r r c h r ( tname , ’ > ’) ; i f ( ! f i l e ) { i f ( msg ) conoutf (CON ERROR, ” could not load texture %s ” , tname ) ; return NULL; } f i l e ++; } bool raw = ! usedds || ! compress , dds = f a l s e ; f o r ( const char ∗pcmds = cmds ; pcmds ; ) { #define PARSETEXCOMMANDS( cmds ) \ const char ∗cmd = NULL, ∗end = NULL, ∗arg [ 4 ] = { NULL, NULL, NULL, NULL }; \ cmd = &cmds [ 1 ] ; \ end = strchr (cmd, ’ > ’) ; \ i f ( ! end ) break ; \ cmds = strchr (cmd, ’ < ’) ; \ s i z e t len = strcspn (cmd, ”:,>= end ) arg [ i ] = ” ” ; \ e l s e arg [ i ] + + ; \ } PARSETEXCOMMANDS( pcmds ) ; i f ( ! strncmp (cmd, ” n o f f ” , len ) ) { i f ( renderpath==R FIXEDFUNCTION ) return true ; } e l s e i f ( ! strncmp (cmd, ” ffmask ” , len ) || ! strncmp (cmd, ” f f s k i p ” , len )) { i f ( renderpath==R FIXEDFUNCTION ) raw = true ; }

engine/texture.cpp else { if } else else else

i f ( ! strncmp (cmd, ” decal ” , len ) )

497

e l s e i f ( ! strncmp (cmd, ” f f s k i p ” , len ) ) { i f ( renderpath==R FIXEDFUNCTION ) break ; }

( renderpath==R FIXEDFUNCTION && ! hasTE ) raw = true ; }

i f ( ! strncmp (cmd, ” dds ” , len ) ) dds = true ; i f ( ! strncmp (cmd, ” thumbnail ” , len ) ) raw = true ; i f ( ! strncmp (cmd, ” stub ” , len ) ) return canloadsurface ( f i l e ) ;

return true ;

}

}

i f ( msg ) renderprogress ( loadprogress , f i l e ) ;

void loadalphamask ( Texture ∗t ) { i f ( t−>alphamask || ( t−>type &(Texture : : ALPHA| Texture : :COMPRESSED) ) ! = Texture : : ALPHA) return ; ImageData s ; i f ( ! texturedata ( s , t−>name, NULL, f a l s e ) || ! s . data || s . compressed ) return ; t−>alphamask = new uchar [ s . h ∗ ( ( s .w+7) /8) ] ; uchar ∗srcrow = s . data , ∗dst = t−>alphamask−1; loop ( y , s . h ) { uchar ∗src = srcrow+s . bpp−1; loop ( x , s .w) { i n t o f f s e t = x%8; i f ( ! o f f s e t ) ∗++dst = 0; i f (∗ src ) ∗dst |= 1format−>B i t s P e r P i x e l ; i f ( bpp%8 || ! texformat ( bpp/8) ) { SDL FreeSurface ( s ) ; conoutf (CON ERROR , ” texture must be 8 , 16, 24, or 32 bpp : %s ” , f i l e ) ; return false ; } i f (max( s−>w, s−>h ) > (1type : TEX DIFFUSE ) ; e l s e i f ( ! strncmp (cmd, ” mix ” , len ) ) texmix ( d , ∗arg [ 0 ] ? a t o i ( arg [ 0 ] ) : −1, ∗arg [ 1 ] ? a t o i ( arg [ 1 ] ) : −1, ∗arg [ 2 ] ? a t o i ( arg [ 2 ] ) : −1, ∗arg [ 3 ] ? a t o i ( arg [ 3 ] ) : −1) ; e l s e i f ( ! strncmp (cmd, ” grey ” , len ) ) texgrey ( d ) ; e l s e i f ( ! strncmp (cmd, ” blur ” , len ) ) { i n t emphasis = a t o i ( arg [ 0 ] ) , repeat = a t o i ( arg [ 1 ] ) ; texblur ( d , emphasis > 0 ? clamp ( emphasis , 1 , 2) : 1 , repeat > 0 ? repeat : 1) ; } e l s e i f ( ! strncmp (cmd, ” premul ” , len ) ) texpremul ( d ) ; e l s e i f ( ! strncmp (cmd, ” agrad ” , len ) ) texagrad ( d , a t o f ( arg [ 0 ] ) , a t o f ( arg [ 1 ] ) , a t o f ( arg [ 2 ] ) , a t o f ( arg [ 3 ] ) ) ; e l s e i f ( ! strncmp (cmd, ” compress ” , len ) || ! strncmp (cmd, ” dds ” , len ) ) { i n t scale = a t o i ( arg [ 0 ] ) ; i f ( scale next ) vs−>s l o t = & dummyslot ; delete s ; } slots . setsize ( limit ) ; } COMMAND( texturereset , ” i ” ) ; void materialreset ( ) { i f ( ! ( i d e n t f l a g s&IDF OVERRIDDEN) && ! game : : a l l o w e d i t t o g g l e ( ) ) return ; l o o p i ( ( MATF VOLUME|MATF INDEX ) +1) m a t e r i a l s l o t s [ i ] . r e s e t ( ) ; } COMMAND( materialreset , ” ” ) ; s t a t i c i n t compactedvslots = 0 , compactvslotsprogress = 0 , clonedvslots = 0; s t a t i c bool markingvslots = f a l s e ; void c l e a r s l o t s ( ) { resetslotshader ( ) ; s l o t s . deletecontents ( ) ; v s l o t s . deletecontents ( ) ; l o o p i ( ( MATF VOLUME|MATF INDEX ) +1) m a t e r i a l s l o t s [ i ] . r e s e t ( ) ; clonedvslots = 0;

498

Foundations of Videogame Programming Code Repository

}

compactvslots ( worldroot ) ; t o t a l = compactedvslots ; compacteditvslots ( ) ;

s t a t i c void a s s i g n v s l o t ( VSlot &vs ) ;

} compactmruvslots ( ) ; loopv ( v s l o t s ) { VSlot &vs = ∗v s l o t s [ i ] ; i f ( vs . index >= 0 && vs . l a y e r && v s l o t s . inrange ( vs . l a y e r ) ) vs . l a y e r = v s l o t s [ vs . l a y e r]−>index ; } loopv ( v s l o t s ) { while ( v s l o t s [ i]−>index >= 0 && v s l o t s [ i]−>index ! = i ) swap ( v s l o t s [ i ] , v s l o t s [ v s l o t s [ i]−>index ] ) ; } f o r ( i n t i = compactedvslots ; i < v s l o t s . length ( ) ; i ++) d e l e t e v s l o t s [ i ]; v s l o t s . s e t s i z e ( compactedvslots ) ; return t o t a l ;

s t a t i c i n l i n e void a s s i g n v s l o t l a y e r ( VSlot &vs ) { i f ( vs . l a y e r && v s l o t s . inrange ( vs . l a y e r ) ) { VSlot &l a y e r = ∗v s l o t s [ vs . l a y e r ] ; i f ( l a y e r . index < 0) a s s i g n v s l o t ( l a y e r ) ; } } s t a t i c void a s s i g n v s l o t ( VSlot &vs ) { vs . index = compactedvslots ++; a s s i g n v s l o t l a y e r ( vs ) ; } void compactvslot ( i n t &index ) { i f ( v s l o t s . inrange ( index ) ) { VSlot &vs = ∗v s l o t s [ index ] ; i f ( vs . index < 0) a s s i g n v s l o t ( vs ) ; i f ( ! markingvslots ) index = vs . index ; } } void compactvslots ( cube ∗c , i n t n ) { i f ( ( compactvslotsprogress++&0xFFF ) ==0) renderprogress ( min ( f l o a t ( compactvslotsprogress ) /allocnodes , 1.0 f ) , markingvslots ? ” marking s l o t s . . . ” : ” compacting s l o t s . . . ” ) ; loopi (n) { i f ( c [ i ] . children ) compactvslots ( c [ i ] . children ) ; e l s e l o o p j ( 6 ) i f ( v s l o t s . inrange ( c [ i ] . texture [ j ] ) ) { VSlot &vs = ∗v s l o t s [ c [ i ] . texture [ j ] ] ; i f ( vs . index < 0) a s s i g n v s l o t ( vs ) ; i f ( ! markingvslots ) c [ i ] . texture [ j ] = vs . index ; } } } i n t compactvslots ( ) { clonedvslots = 0; markingvslots = f a l s e ; compactedvslots = 0; compactvslotsprogress = 0; loopv ( v s l o t s ) v s l o t s [ i]−>index = −1; loopv ( s l o t s ) s l o t s [ i]−>variants−>index = compactedvslots ++; loopv ( s l o t s ) a s s i g n v s l o t l a y e r (∗ s l o t s [ i]−>variants ) ; loopv ( v s l o t s ) { VSlot &vs = ∗v s l o t s [ i ] ; i f ( ! vs . changed && vs . index < 0) { markingvslots = true ; break ; } } compactvslots ( worldroot ) ; i n t t o t a l = compactedvslots ; compacteditvslots ( ) ; loopv ( v s l o t s ) { VSlot ∗vs = v s l o t s [ i ] ; i f ( vs−>changed ) continue ; while ( vs−>next ) { i f ( vs−>next−>index < 0) vs−>next = vs−>next−>next ; e l s e vs = vs−>next ; } } i f ( markingvslots ) { markingvslots = f a l s e ; compactedvslots = 0; compactvslotsprogress = 0; i n t l a s t d i s c a r d = 0; loopv ( v s l o t s ) { VSlot &vs = ∗v s l o t s [ i ] ; i f ( vs . changed || ( vs . index < 0 && ! vs . next ) ) vs . index = −1; else { while ( l a s t d i s c a r d < i ) { VSlot &ds = ∗v s l o t s [ l a s t d i s c a r d + + ] ; i f ( ! ds . changed && ds . index < 0) ds . index = compactedvslots ++; } vs . index = compactedvslots ++; } }

} ICOMMAND( compactvslots , ” ” , ( ) , { extern i n t nompedit ; i f ( nompedit && multiplayer ( ) ) return ; compactvslots ( ) ; allchanged ( ) ; }) ; s t a t i c S l o t &l o a d s l o t ( S l o t &s , bool forceload ) ; s t a t i c void c l a m p v s l o t o f f s e t ( VSlot &dst , S l o t ∗s l o t = NULL) { i f ( ! s l o t ) s l o t = dst . s l o t ; i f ( s l o t && s l o t−>sts . inrange ( 0 ) ) { i f ( ! s l o t−>loaded ) l o a d s l o t (∗ s l o t , f a l s e ) ; i n t xs = s l o t−>sts [ 0 ] . t−>xs , ys = s l o t−>sts [ 0 ] . t−>ys ; i f ( ( dst . r o t a t i o n &5)==1) swap ( xs , ys ) ; dst . x o f f s e t %= xs ; i f ( dst . x o f f s e t < 0) dst . x o f f s e t += xs ; dst . y o f f s e t %= ys ; i f ( dst . y o f f s e t < 0) dst . y o f f s e t += ys ; } else { dst . x o f f s e t = max( dst . x o f f s e t , 0) ; dst . y o f f s e t = max( dst . y o f f s e t , 0) ; } } s t a t i c void propagatevslot ( VSlot &dst , const VSlot &src , i n t d i f f , bool edit = false ) { i f ( d i f f & (1id : 0 , w, h , data , s . align , s . bpp , l e v e l s , 3 , mipit ? 2 : 1 , s . compressed , side . t a r g e t ) ; } else { createtexture ( ! i ? t−>id : 0 , t−>w, t−>h , s . data , 3 , mipit ? 2 : 1 , component , side . target , s .w, s . h , s . pitch , f a l s e , format ) ; } } forcecubemapload ( t−>id ) ; return t ; } Texture ∗cubemapload ( const char ∗name, bool mipit , bool msg, bool transient ) { i f ( ! hasCM) return NULL; s t r i n g pname; copystring ( pname, makerelpath ( ” packages ” , name) ) ; path (pname) ; Texture ∗t = NULL; i f ( ! strchr ( pname, ’ ∗ ’ ) ) { defformatstring ( jpgname ) (”% s ∗. jpg ” , pname) ; t = cubemaploadwildcard (NULL, jpgname , mipit , f a l s e , transient ) ; if (! t ) { defformatstring ( pngname ) (”% s ∗.png ” , pname) ; t = cubemaploadwildcard (NULL, pngname, mipit , f a l s e , transient ) ; i f ( ! t && msg ) conoutf (CON ERROR, ” could not load envmap %s ” , name) ; } } e l s e t = cubemaploadwildcard (NULL, pname, mipit , msg, transient ) ; return t ; } VAR( envmapradius , 0 , 128, 10000) ; struct envmap { i n t radius , size , blur ; vec o ; GLuint tex ; }; s t a t i c vector envmaps ; s t a t i c Texture ∗skyenvmap = NULL; void clearenvmaps ( ) { i f ( skyenvmap ) { i f ( skyenvmap−>type&Texture : : TRANSIENT ) cleanuptexture ( skyenvmap ) ; skyenvmap = NULL; } loopv ( envmaps ) glDeleteTextures ( 1 , &envmaps [ i ] . tex ) ; envmaps . shrink ( 0 ) ; } VAR( aaenvmap , 0 , 2 , 4) ; GLuint genenvmap ( const vec &o , i n t envmapsize , i n t blur ) { i n t rendersize = 1h ) ) ; i f ( maxtexsize ) s i z e l i m i t = min ( s i z e l i m i t , maxtexsize ) ; while ( rendersize > s i z e l i m i t ) rendersize /= 2; i n t t e x s i z e = min ( rendersize , 1 0) { blurtexture ( blur , 3 , t e x s i z e , t e x s i z e , src , dst ) ; swap ( src , dst ) ; } createtexture ( tex , t e x s i z e , t e x s i z e , src , 3 , 2 , GL RGB5, side . target ) ; } glFrontFace (GL CW) ; delete [ ] pixels ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ; clientkeepalive ( ) ; forcecubemapload ( tex ) ; return tex ; } void initenvmaps ( ) { i f ( ! hasCM) return ; clearenvmaps ( ) ; extern char ∗skybox ; skyenvmap = skybox [ 0 ] ? cubemapload ( skybox , true , f a l s e , true ) : NULL; const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) { const e x t e n t i t y &ent = ∗ents [ i ] ; i f ( ent . type ! = ET ENVMAP) continue ; envmap &em = envmaps . add ( ) ; em. radius = ent . a t t r 1 ? clamp ( i n t ( ent . a t t r 1 ) , 0 , 10000) : envmapradius ; em. s i z e = ent . a t t r 2 ? clamp ( i n t ( ent . a t t r 2 ) , 4 , 9) : 0; em. blur = ent . a t t r 3 ? clamp ( i n t ( ent . a t t r 3 ) , 1 , 2) : 0; em. o = ent . o ; em. tex = 0; } } void genenvmaps ( ) { i f ( envmaps . empty ( ) ) return ; renderprogress ( 0 , ” generating environment maps . . . ” ) ; i n t lastprogress = SDL GetTicks ( ) ; loopv ( envmaps ) { envmap &em = envmaps [ i ] ; em. tex = genenvmap (em. o , em. s i z e ? min (em. size , envmapsize ) : envmapsize , em. blur ) ; i f ( renderedframe ) continue ; i n t m i l l i s = SDL GetTicks ( ) ; i f ( m i l l i s − lastprogress >= 250) { renderprogress ( f l o a t ( i +1)/envmaps . length ( ) , ” generating environment maps . . . ” , 0 , true ) ; lastprogress = m i l l i s ; } } } ushort closestenvmap ( const vec &o ) { ushort minemid = EMID SKY ; f l o a t mindist = 1e16f ; loopv ( envmaps ) { envmap &em = envmaps [ i ] ; f l o a t d i s t = em. o . d i s t ( o ) ; i f ( d i s t < em. radius && d i s t < mindist ) { minemid = EMID RESERVED + i ; mindist = d i s t ; }

} return minemid ;

// r t // f t

GLuint lookupenvmap ( S l o t &s l o t ) { loopv ( s l o t . sts ) i f ( s l o t . sts [ i ] . type==TEX ENVMAP && s l o t . sts [ i ] . t ) return s l o t . sts [ i ] . t−>id ; return skyenvmap ? skyenvmap−>id : 0; } GLuint lookupenvmap ( ushort emid ) { i f ( emid==EMID SKY || emid==EMID CUSTOM) return skyenvmap ? skyenvmap−> id : 0; i f ( emid==EMID NONE || ! envmaps . inrange ( emid−EMID RESERVED) ) return 0; GLuint tex = envmaps [ emid−EMID RESERVED ] . tex ; return tex ? tex : ( skyenvmap ? skyenvmap−>id : 0) ; } void cleanuptexture ( Texture ∗t ) { DELETEA( t−>alphamask ) ; i f ( t−>id ) { glDeleteTextures ( 1 , &t−>id ) ; t−>id = 0; } i f ( t−>type&Texture : : TRANSIENT ) textures . remove ( t−>name) ; } void cleanuptextures ( ) { clearenvmaps ( ) ; loopv ( s l o t s ) s l o t s [ i]−>cleanup ( ) ; loopv ( v s l o t s ) v s l o t s [ i]−>cleanup ( ) ; l o o p i ( ( MATF VOLUME|MATF INDEX ) +1) m a t e r i a l s l o t s [ i ] . cleanup ( ) ; enumerate ( textures , Texture , tex , cleanuptexture (& tex ) ) ; } bool reloadtexture ( const char ∗name) { Texture ∗t = textures . access ( path ( name, true ) ) ; i f ( t ) return reloadtexture (∗ t ) ; return true ; } bool reloadtexture ( Texture &tex ) { i f ( tex . id ) return true ; switch ( tex . type&Texture : : TYPE ) { case Texture : : IMAGE: { i n t compress = 0; ImageData s ; i f ( ! texturedata ( s , tex .name, NULL, true , &compress ) || ! newtexture(& tex , NULL, s , tex . clamp , tex .mipmap, f a l s e , f a l s e , compress ) ) return f a l s e ; break ; } case Texture : :CUBEMAP: i f ( ! cubemaploadwildcard(& tex , NULL, tex .mipmap, true ) ) return false ; break ; } return true ; } void reloadtex ( char ∗name) { Texture ∗t = textures . access ( path ( name, true ) ) ; i f ( ! t ) { conoutf (CON ERROR, ” texture %s i s not loaded ” , name) ; return ; } i f ( t−>type&Texture : : TRANSIENT ) { conoutf (CON ERROR, ” can ’ t reload transient texture %s ” , name) ; return ; } DELETEA( t−>alphamask ) ; Texture oldtex = ∗t ; t−>id = 0; i f ( ! reloadtexture (∗ t ) ) { i f ( t−>id ) glDeleteTextures ( 1 , &t−>id ) ; ∗t = oldtex ; conoutf (CON ERROR, ” f a i l e d to reload texture %s ” , name) ; } }

engine/texture.cpp

s i z e o f (DDPIXELFORMAT) ) { d e l e t e f ; return f a l s e ; } i f ( d . ddpfPixelFormat . dwFlags & DDPF FOURCC) { switch ( d . ddpfPixelFormat .dwFourCC ) { case FOURCC DXT1: format = d . ddpfPixelFormat . dwFlags & DDPF ALPHAPIXELS ? GL COMPRESSED RGBA S3TC DXT1 EXT : GL COMPRESSED RGB S3TC DXT1 EXT; break ; case FOURCC DXT2: case FOURCC DXT3: format = GL COMPRESSED RGBA S3TC DXT3 EXT; break ; case FOURCC DXT4: case FOURCC DXT5: format = GL COMPRESSED RGBA S3TC DXT5 EXT; break ; } } i f ( ! format ) { d e l e t e f ; return f a l s e ; } i f ( dbgdds ) conoutf (CON DEBUG, ”%s : format 0x%X, %d x %d , %d mipmaps” , filename , format , d . dwWidth , d . dwHeight , d .dwMipMapCount ) ; i n t bpp = 0; switch ( format ) { case GL COMPRESSED RGB S3TC DXT1 EXT: case GL COMPRESSED RGBA S3TC DXT1 EXT: bpp = 8; break ; case GL COMPRESSED RGBA S3TC DXT3 EXT: case GL COMPRESSED RGBA S3TC DXT5 EXT: bpp = 16; break ; } image . setdata (NULL, d . dwWidth , d . dwHeight , bpp , d . dwMipMapCount, 4 , format ) ; i n t s i z e = image . c a l c s i z e ( ) ; i f ( f−>read ( image . data , s i z e ) ! = s i z e ) { d e l e t e f ; image . cleanup ( ) ; return f a l s e ; } delete f ; return true ;

COMMAND( reloadtex , ” s ” ) ; void reloadtextures ( ) { i n t reloaded = 0; enumerate ( textures , Texture , tex , { loadprogress = f l o a t (++ reloaded ) /textures . numelems ; reloadtexture ( tex ) ; }) ; loadprogress = 0; } enum { DDSD CAPS = 0x00000001 , DDSD HEIGHT = 0x00000002 , DDSD WIDTH = 0x00000004 , DDSD PITCH = 0x00000008 , DDSD PIXELFORMAT = 0x00001000 , DDSD MIPMAPCOUNT = 0x00020000 , = 0x00080000 , DDSD LINEARSIZE DDSD BACKBUFFERCOUNT = 0x00800000 , = 0x00000001 , DDPF ALPHAPIXELS DDPF FOURCC = 0x00000004 , DDPF INDEXED = 0x00000020 , = 0x00000002 , DDPF ALPHA DDPF RGB = 0x00000040 , DDPF COMPRESSED = 0x00000080 , DDPF LUMINANCE = 0x00020000 , DDSCAPS COMPLEX = 0x00000008 , DDSCAPS TEXTURE = 0x00001000 , DDSCAPS MIPMAP = 0x00400000 , DDSCAPS2 CUBEMAP = 0x00000200 , DDSCAPS2 CUBEMAP POSITIVEX = 0x00000400 , DDSCAPS2 CUBEMAP NEGATIVEX = 0x00000800 , DDSCAPS2 CUBEMAP POSITIVEY = 0x00001000 , DDSCAPS2 CUBEMAP NEGATIVEY = 0x00002000 , DDSCAPS2 CUBEMAP POSITIVEZ = 0x00004000 , DDSCAPS2 CUBEMAP NEGATIVEZ = 0x00008000 , DDSCAPS2 VOLUME = 0x00200000 , FOURCC DXT1 = 0x31545844 , FOURCC DXT2 = 0x32545844 , FOURCC DXT3 = 0x33545844 , FOURCC DXT4 = 0x34545844 , FOURCC DXT5 = 0x35545844 }; struct DDCOLORKEY { uint dwColorSpaceLowValue , dwColorSpaceHighValue ; }; struct DDPIXELFORMAT { uint dwSize , dwFlags , dwFourCC; union { uint dwRGBBitCount , dwYUVBitCount , dwZBufferBitDepth , dwAlphaBitDepth , dwLuminanceBitCount , dwBumpBitCount, dwPrivateFormatBitCount ; }; union { uint dwRBitMask , dwYBitMask , dwStencilBitDepth , dwLuminanceBitMask , dwBumpDuBitMask, dwOperations ; }; union { uint dwGBitMask, dwUBitMask , dwZBitMask , dwBumpDvBitMask; struct { ushort wFlipMSTypes , wBltMSTypes ; } MultiSampleCaps ; }; union { uint dwBBitMask , dwVBitMask , dwStencilBitMask , dwBumpLuminanceBitMask ; }; union { uint dwRGBAlphaBitMask , dwYUVAlphaBitMask , dwLuminanceAlphaBitMask , dwRGBZBitMask, dwYUVZBitMask ; }; }; struct DDSCAPS2 { uint dwCaps, dwCaps2, dwCaps3, dwCaps4 ; }; struct DDSURFACEDESC2 { uint dwSize , dwFlags , dwHeight , dwWidth ; union { i n t l P i t c h ; uint dwLinearSize ; }; uint dwBackBufferCount ; union { uint dwMipMapCount, dwRefreshRate , dwSrcVBHandle ; }; uint dwAlphaBitDepth , dwReserved , lpSurface ; union { DDCOLORKEY ddckCKDestOverlay ; uint dwEmptyFaceColor ; }; DDCOLORKEY ddckCKDestBlt , ddckCKSrcOverlay , ddckCKSrcBlt ; union { DDPIXELFORMAT ddpfPixelFormat ; uint dwFVF; }; DDSCAPS2 ddsCaps ; uint dwTextureStage ; }; bool loaddds ( const char ∗filename , ImageData &image ) { stream ∗f = o p e n f i l e ( filename , ” rb ” ) ; i f ( ! f ) return f a l s e ; GLenum format = GL FALSE ; uchar magic [ 4 ] ; i f ( f−>read ( magic , 4) ! = 4 || memcmp( magic , ”DDS ” , 4) ) { d e l e t e f ; return f a l s e ; } DDSURFACEDESC2 d ; i f ( f−>read(&d , s i z e o f ( d ) ) ! = s i z e o f ( d ) ) { d e l e t e f ; return f a l s e ; } l i l s w a p ( ( uint ∗)&d , s i z e o f ( d ) / s i z e o f ( uint ) ) ; i f ( d . dwSize ! = s i z e o f (DDSURFACEDESC2) || d . ddpfPixelFormat . dwSize ! =

505

} void gendds ( char ∗i n f i l e , char ∗o u t f i l e ) { i f ( ! hasS3TC || usetexcompress id ) ; GLint compressed = 0 , format = 0 , width glGetTexLevelParameteriv ( GL TEXTURE 2D, &compressed ) ; glGetTexLevelParameteriv ( GL TEXTURE 2D, &format ) ; glGetTexLevelParameteriv ( GL TEXTURE 2D, glGetTexLevelParameteriv ( GL TEXTURE 2D, ;

= 0 , height = 0; 0 , GL TEXTURE COMPRESSED ARB, 0 , GL TEXTURE INTERNAL FORMAT, 0 , GL TEXTURE WIDTH, &width ) ; 0 , GL TEXTURE HEIGHT, &height )

i f ( ! compressed ) { conoutf (CON ERROR, ” f a i l e d compressing %s ” , i n f i l e ) ; return ; } i n t fourcc = 0; switch ( format ) { case GL COMPRESSED RGB S3TC DXT1 EXT: fourcc = FOURCC DXT1; conoutf ( ” compressed as DXT1” ) ; break ; case GL COMPRESSED RGBA S3TC DXT1 EXT: fourcc = FOURCC DXT1; conoutf ( ” compressed as DXT1a ” ) ; break ; case GL COMPRESSED RGBA S3TC DXT3 EXT: fourcc = FOURCC DXT3; conoutf ( ” compressed as DXT3” ) ; break ; case GL COMPRESSED RGBA S3TC DXT5 EXT: fourcc = FOURCC DXT5; conoutf ( ” compressed as DXT5” ) ; break ; default : conoutf (CON ERROR, ” f a i l e d compressing %s : unknown format : 0x%X ” , i n f i l e , format ) ; break ; return ; } i f ( ! outfile [0]) { s t a t i c s t r i n g buf ; copystring ( buf , i n f i l e ) ; i n t len = s t r l e n ( buf ) ; i f ( len > 4 && buf [ len −4]== ’. ’) memcpy(&buf [ len −4], ” . dds ” , 4) ; e l s e concatstring ( buf , ” . dds ” ) ; o u t f i l e = buf ; } stream ∗f = o p e n f i l e ( path ( o u t f i l e , true ) , ”wb ” ) ; i f ( ! f ) { conoutf (CON ERROR, ” f a i l e d w r i t i n g to %s ” , o u t f i l e ) ; return ; }

506

Foundations of Videogame Programming Code Repository writepngchunk ( f , ”IHDR” , ( uchar ∗)&ihdr , 13) ;

i n t c s i z e = 0; f o r ( i n t lw = width , lh = height , l e v e l = 0 ; ; ) { GLint s i z e = 0; glGetTexLevelParameteriv ( GL TEXTURE 2D, l e v e l ++ , GL TEXTURE COMPRESSED IMAGE SIZE ARB, &s i z e ) ; c s i z e += s i z e ; i f (max( lw , lh ) 1) lw /= 2; i f ( lh > 1) lh /= 2; }

stream : : o f f s e t i d a t = f−>t e l l ( ) ; uint len = 0; f−>write(”\0\0\0\0IDAT ” , 8) ; uint crc = crc32 ( 0 , Z NULL , 0) ; crc = crc32 ( crc , ( const Bytef ∗) ”IDAT ” , 4) ; z stream z ; z . z a l l o c = NULL; z . z f r e e = NULL; z . opaque = NULL;

DDSURFACEDESC2 d ; memset(&d , 0 , s i z e o f ( d ) ) ; d . dwSize = s i z e o f (DDSURFACEDESC2) ; d . dwWidth = width ; d . dwHeight = height ; d . dwLinearSize = c s i z e ; d . dwFlags = DDSD CAPS | DDSD HEIGHT | DDSD WIDTH | DDSD PIXELFORMAT | DDSD LINEARSIZE | DDSD MIPMAPCOUNT; d . ddsCaps . dwCaps = DDSCAPS TEXTURE | DDSCAPS COMPLEX | DDSCAPS MIPMAP; d . ddpfPixelFormat . dwSize = s i z e o f (DDPIXELFORMAT) ; d . ddpfPixelFormat . dwFlags = DDPF FOURCC | ( format ! = GL COMPRESSED RGB S3TC DXT1 EXT ? DDPF ALPHAPIXELS : 0) ; d . ddpfPixelFormat .dwFourCC = fourcc ;

i f ( d e f l a t e I n i t (&z , compresspng ) ! = Z OK ) goto e r r o r ; uchar buf[1write ( buf , flush ) ; \ z . next out = ( Bytef ∗) buf ; \ z . a v a i l o u t = s i z e o f ( buf ) ; \ } while ( 0 ) FLUSHZ; } } }

uchar ∗data = new uchar [ c s i z e ] , ∗dst = data ; f o r ( i n t lw = width , lh = height ; ; ) { GLint s i z e ; glGetTexLevelParameteriv ( GL TEXTURE 2D, d . dwMipMapCount, GL TEXTURE COMPRESSED IMAGE SIZE ARB, &s i z e ) ; glGetCompressedTexImage ( GL TEXTURE 2D, d .dwMipMapCount++ , dst ) ; dst += s i z e ; i f (max( lw , lh ) 1) lw /= 2; i f ( lh > 1) lh /= 2; } l i l s w a p ( ( uint ∗)&d , s i z e o f ( d ) / s i z e o f ( uint ) ) ; f−>write ( ”DDS ” , 4) ; f−>write (&d , s i z e o f ( d ) ) ; f−>write ( data , c s i z e ) ; delete f ;

for ( ; ; ) { i n t e r r = d e f l a t e (&z , Z FINISH ) ; i f ( e r r ! = Z OK && e r r ! = Z STREAM END) goto cleanuperror ; FLUSHZ; i f ( e r r == Z STREAM END) break ; }

d e l e t e [ ] data ; conoutf ( ” wrote DDS f i l e %s ” , o u t f i l e ) ; setuptexcompress ( ) ; } COMMAND( gendds , ” ss ” ) ;

deflateEnd (&z ) ;

void writepngchunk ( stream ∗f , const char ∗type , uchar ∗data = NULL, uint len = 0) { f−>putbig(len ) ; f−>write ( type , 4) ; f−>write ( data , len ) ; uint crc = crc32 ( 0 , Z NULL , 0) ; crc = crc32 ( crc , ( const Bytef ∗) type , 4) ; i f ( data ) crc = crc32 ( crc , data , len ) ; f−>putbig(crc ) ; } VARP( compresspng , 0 , 9 , 9) ; void savepng ( const char ∗filename , ImageData &image , bool f l i p ) { uchar ctype = 0; switch ( image . bpp ) { case 1: ctype = 0; break ; case 2: ctype = 4; break ; case 3: ctype = 2; break ; case 4: ctype = 6; break ; d e f a u l t : conoutf (CON ERROR, ” f a i l e d saving png to %s ” , filename ) ; return ; } stream ∗f = o p e n f i l e ( filename , ”wb ” ) ; i f ( ! f ) { conoutf (CON ERROR, ” could not write to %s ” , filename ) ; return ; } uchar signature [ ] = { 137, 80, 78, 71, 13, 10, 26, 10 }; f−>write ( signature , s i z e o f ( signature ) ) ; struct pngihdr { uint width , height ; uchar bitdepth , colortype , compress , f i l t e r , i n t e r l a c e ; } ihdr = { bigswap(image .w) , bigswap(image . h ) , 8 , ctype , 0 , 0 , 0 };

f−>seek ( idat , SEEK SET ) ; f−>putbig(len ) ; f−>seek ( 0 , SEEK END) ; f−>putbig(crc ) ; writepngchunk ( f , ”IEND ” ) ; delete f ; return ; cleanuperror : deflateEnd (&z ) ; error : delete f ; conoutf (CON ERROR, ” f a i l e d saving png to %s ” , filename ) ; } struct tgaheader { uchar i d e n t s i z e ; uchar cmaptype ; uchar imagetype ; uchar cmaporigin [ 2 ] ; uchar cmapsize [ 2 ] ; uchar cmapentrysize ; uchar x o r i g i n [ 2 ] ; uchar y o r i g i n [ 2 ] ; uchar width [ 2 ] ; uchar height [ 2 ] ; uchar p i x e l s i z e ; uchar descbyte ; }; VARP( compresstga , 0 , 1 , 1) ; void savetga ( const char ∗filename , ImageData &image , bool f l i p ) { switch ( image . bpp )

engine/texture.cpp {

507

return format ; case 3: case 4: break ; d e f a u l t : conoutf (CON ERROR, ” f a i l e d saving tga to %s ” , filename ) ; return ;

}

} void saveimage ( const char ∗filename , i n t format , ImageData &image , bool f l i p = false ) {

stream ∗f = o p e n f i l e ( filename , ”wb ” ) ; i f ( ! f ) { conoutf (CON ERROR, ” could not write to %s ” , filename ) ; return ; }

switch ( format ) { case IMG PNG: savepng ( filename , image , f l i p ) ; break ; case IMG TGA : savetga ( filename , image , f l i p ) ; break ; default : { ImageData f l i p p e d ( image .w, image . h , image . bpp , image . data ) ; i f ( f l i p ) t e x f l i p ( flipped ) ; SDL Surface ∗s = wrapsurface ( f l i p p e d . data , f l i p p e d .w, f l i p p e d . h , f l i p p e d . bpp ) ; i f ( ! s ) break ; stream ∗f = o p e n f i l e ( filename , ”wb ” ) ; if ( f ) { SDL SaveBMP RW ( s , f−>rwops ( ) , 1) ; delete f ; } SDL FreeSurface ( s ) ; break ; } }

tgaheader hdr ; memset(&hdr , 0 , s i z e o f ( hdr ) ) ; hdr . p i x e l s i z e = image . bpp∗8; hdr . width [ 0 ] = image .w&0xFF ; hdr . width [ 1 ] = ( image .w>>8)&0xFF ; hdr . height [ 0 ] = image . h&0xFF ; hdr . height [ 1 ] = ( image . h>>8)&0xFF ; hdr . imagetype = compresstga ? 10 : 2; f−>write (&hdr , s i z e o f ( hdr ) ) ; uchar buf [128∗4]; l o o p i ( image . h ) { uchar ∗src = image . data + ( f l i p ? i : image . h − i − 1)∗image . pitch ; f o r ( i n t remaining = image .w; remaining > 0 ; ) { i n t raw = 1; i f ( compresstga ) { i n t run = 1; f o r ( uchar ∗scan = src ; run < min ( remaining , 128) ; run++) { scan += image . bpp ; i f ( src [ 0 ] ! = scan [ 0 ] || src [ 1 ] ! = scan [ 1 ] || src [ 2 ] ! = scan [ 2 ] || ( image . bpp==4 && src [ 3 ] ! = scan [ 3 ] ) ) break ; } i f ( run > 1) { f−>putchar (0x80 | ( run−1)) ; f−>putchar ( src [ 2 ] ) ; f−>putchar ( src [ 1 ] ) ; f−>putchar ( src [ 0 ] ) ; i f ( image . bpp==4) f−>putchar ( src [ 3 ] ) ; src += run∗image . bpp ; remaining −= run ; i f ( remaining putchar ( raw − 1) ; } e l s e raw = min ( remaining , 128) ; uchar ∗dst = buf ; l o o p j ( raw ) { dst [ 0 ] = src [ 2 ] ; dst [ 1 ] = src [ 1 ] ; dst [ 2 ] = src [ 0 ] ; i f ( image . bpp==4) dst [ 3 ] = src [ 3 ] ; dst += image . bpp ; src += image . bpp ; } f−>write ( buf , raw∗image . bpp ) ; remaining −= raw ; } }

} bool loadimage ( const char ∗filename , ImageData &image ) { SDL Surface ∗s = loadsurface ( path ( filename , true ) ) ; i f ( ! s ) return f a l s e ; image . wrap ( s ) ; return true ; } SVARP( screenshotdir , ” ” ) ; void screenshot ( char ∗filename ) { s t a t i c s t r i n g buf ; i n t format = −1; copystring ( buf , screenshotdir ) ; i f ( screenshotdir [ 0 ] ) { i n t len = s t r l e n ( buf ) ; i f ( buf [ len ] ! = ’ / ’ && buf [ len ] ! = ’\\ ’ && len+1 < ( i n t ) s i z e o f ( buf ) ) { buf [ len ] = ’ / ’ ; buf [ len +1] = ’ \ 0 ’ ; } const char ∗d i r = f i n d f i l e ( buf , ”w” ) ; i f ( ! f i l e e x i s t s ( dir , ”w” ) ) c r e a t e d i r ( d i r ) ; } i f ( filename [ 0 ] ) { concatstring ( buf , filename ) ; format = guessimageformat ( buf , −1) ; } else { defformatstring (name) ( ” screenshot %d ” , t o t a l m i l l i s ) ; concatstring ( buf , name) ; } i f ( format < 0) { format = screenshotformat ; concatstring ( buf , imageexts [ format ] ) ; } ImageData image ( screen−>w, screen−>h , 3) ; g l P i x e l S t o r e i ( GL PACK ALIGNMENT, t e x a l i g n ( image . data , screen−>w, 3) ) ; glReadPixels ( 0 , 0 , screen−>w, screen−>h , GL RGB, GL UNSIGNED BYTE, image . data ) ; saveimage ( path ( buf ) , format , image , true ) ;

delete f ; } enum { IMG BMP = 0 , IMG TGA = 1 , IMG PNG = 2 , NUMIMG };

} COMMAND( screenshot , ” s ” ) ; void flipnormalmapy ( char ∗d e s t f i l e , char ∗normalfile ) // jpg/png /tga−> tga { ImageData ns ; i f ( ! loadimage ( normalfile , ns ) ) return ; ImageData d ( ns .w, ns . h , 3) ; readwritetex ( d , ns , dst [ 0 ] = src [ 0 ] ; dst [ 1 ] = 255 − src [ 1 ] ; dst [ 2 ] = src [ 2 ] ; ); saveimage ( d e s t f i l e , guessimageformat ( d e s t f i l e , IMG TGA ) , d ) ;

VARP( screenshotformat , 0 , IMG PNG, NUMIMG−1); const char ∗imageexts [NUMIMG] = { ” .bmp” , ” . tga ” , ” . png” }; i n t guessimageformat ( const char ∗filename , i n t format = IMG BMP) { i n t len = s t r l e n ( filename ) ; l o o p i (NUMIMG) { i n t extlen = s t r l e n ( imageexts [ i ] ) ; i f ( len >= extlen && ! strcasecmp(& filename [ len−extlen ] , imageexts [ i ] ) ) return i ; }

} void mergenormalmaps ( char ∗h e i g h t f i l e , char ∗normalfile ) // jpg/png/tga + tga −> tga {

508

Foundations of Videogame Programming Code Repository

ImageData hs , ns ; i f ( ! loadimage ( h e i g h t f i l e , hs ) || ! loadimage ( normalfile , ns ) || hs .w ! = ns .w || hs . h ! = ns . h ) return ; ImageData d ( ns .w, ns . h , 3) ; read2writetex ( d , hs , srch , ns , srcn , ∗(bvec ∗) dst = bvec ( ( ( bvec ∗) srcn )−>tovec ( ) . mul ( 2 ) . add ( ( ( bvec ∗) srch )−>tovec ( ) ) . normalize ( ) ) ;

); saveimage ( normalfile , guessimageformat ( normalfile , IMG TGA ) , d ) ; } COMMAND( flipnormalmapy , ” ss ” ) ; COMMAND( mergenormalmaps , ” ss ” ) ;

engine/texture.cpp // GL ARB vertex program , GL ARB fragment program extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB ; extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB ; extern PFNGLBINDPROGRAMARBPROC glBindProgramARB ; extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB ; extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB ; extern PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB ; extern PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB ; // GL EXT gpu program parameters # i f n d e f GL EXT gpu program parameters #define GL EXT gpu program parameters 1 typedef void ( APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target , GLuint index , GLsizei count , const GLfloat ∗params ) ; typedef void ( APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target , GLuint index , GLsizei count , const GLfloat ∗params ) ; #endif extern PFNGLPROGRAMENVPARAMETERS4FVEXTPROC glProgramEnvParameters4fv ; extern PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC glProgramLocalParameters4fv ; # i f n d e f GL VERSION 2 1 #define GL VERSION 2 1 1 #define GL FLOAT MAT2x3 0x8B65 #define GL FLOAT MAT2x4 0x8B66 #define GL FLOAT MAT3x2 0x8B67 #define GL FLOAT MAT3x4 0x8B68 #define GL FLOAT MAT4x2 0x8B69 #define GL FLOAT MAT4x3 0x8B6A typedef void ( APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) ( GLint location GLsizei count , GLboolean transpose , const GLfloat ∗value ) ; typedef void ( APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) ( GLint location GLsizei count , GLboolean transpose , const GLfloat ∗value ) ; typedef void ( APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) ( GLint location GLsizei count , GLboolean transpose , const GLfloat ∗value ) ; typedef void ( APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) ( GLint location GLsizei count , GLboolean transpose , const GLfloat ∗value ) ; typedef void ( APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) ( GLint location GLsizei count , GLboolean transpose , const GLfloat ∗value ) ; typedef void ( APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) ( GLint location GLsizei count , GLboolean transpose , const GLfloat ∗value ) ; #endif

, ,

#define glUniformMatrix3x2fv glUniformMatrix3x2fv #define glUniformMatrix2x4fv glUniformMatrix2x4fv #define glUniformMatrix4x2fv glUniformMatrix4x2fv #define glUniformMatrix3x4fv glUniformMatrix3x4fv #define glUniformMatrix4x3fv glUniformMatrix4x3fv #e l s e extern PFNGLCREATEPROGRAMPROC glCreateProgram ; extern PFNGLDELETEPROGRAMPROC glDeleteProgram ; extern PFNGLUSEPROGRAMPROC glUseProgram ; extern PFNGLCREATESHADERPROC glCreateShader ; extern PFNGLDELETESHADERPROC glDeleteShader ; extern PFNGLSHADERSOURCEPROC glShaderSource ; extern PFNGLCOMPILESHADERPROC glCompileShader ; extern PFNGLGETSHADERIVPROC glGetShaderiv ; extern PFNGLGETPROGRAMIVPROC glGetProgramiv ; extern PFNGLATTACHSHADERPROC glAttachShader ; extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog ; extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog ; extern PFNGLLINKPROGRAMPROC glLinkProgram ; extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation ; extern PFNGLUNIFORM1FPROC glUniform1f ; extern PFNGLUNIFORM2FPROC glUniform2f ; extern PFNGLUNIFORM3FPROC glUniform3f ; extern PFNGLUNIFORM4FPROC glUniform4f ; extern PFNGLUNIFORM1FVPROC glUniform1fv ; extern PFNGLUNIFORM2FVPROC glUniform2fv ; extern PFNGLUNIFORM3FVPROC glUniform3fv ; extern PFNGLUNIFORM4FVPROC glUniform4fv ; extern PFNGLUNIFORM1IPROC glUniform1i ; extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation ; extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform ; extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray ; extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray ; extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer ;

, , , ,

extern extern extern extern extern extern #endif

PFNGLUNIFORMMATRIX2X3FVPROC PFNGLUNIFORMMATRIX3X2FVPROC PFNGLUNIFORMMATRIX2X4FVPROC PFNGLUNIFORMMATRIX4X2FVPROC PFNGLUNIFORMMATRIX3X4FVPROC PFNGLUNIFORMMATRIX4X3FVPROC

glUniformMatrix2x3fv glUniformMatrix3x2fv glUniformMatrix2x4fv glUniformMatrix4x2fv glUniformMatrix3x4fv glUniformMatrix4x3fv

; ; ; ; ; ;

// OpenGL 2 . 0 : GL ARB shading language 100 , GL ARB shader objects , GL ARB fragment shader , GL ARB vertex shader APPLE #ifdef #define glCreateProgram glCreateProgram #define glDeleteProgram glDeleteProgram #define glUseProgram glUseProgram #define glCreateShader glCreateShader #define glDeleteShader glDeleteShader #define glShaderSource glShaderSource #define glCompileShader glCompileShader #define glGetShaderiv glGetShaderiv #define glGetProgramiv glGetProgramiv #define glAttachShader glAttachShader #define glGetProgramInfoLog glGetProgramInfoLog #define glGetShaderInfoLog glGetShaderInfoLog #define glLinkProgram glLinkProgram #define glGetUniformLocation glGetUniformLocation #define glUniform1f glUniform1f #define glUniform2f glUniform2f #define glUniform3f glUniform3f #define glUniform4f glUniform4f #define glUniform1fv glUniform1fv #define glUniform2fv glUniform2fv #define glUniform3fv glUniform3fv #define glUniform4fv glUniform4fv #define glUniform1i glUniform1i #define glUniformMatrix2fv glUniformMatrix2fv #define glUniformMatrix3fv glUniformMatrix3fv #define glUniformMatrix4fv glUniformMatrix4fv #define glBindAttribLocation glBindAttribLocation #define glGetActiveUniform glGetActiveUniform #define glEnableVertexAttribArray glEnableVertexAttribArray #define glDisableVertexAttribArray glDisableVertexAttribArray #define g l V e r t e x A t t r i b P o i n t e r g l V e r t e x A t t r i b P o i n t e r

#ifndef #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define

#define glUniformMatrix2x3fv

typedef void ( APIENTRYP PFNGLGETUNIFORMINDICESPROC) ( GLuint program ,

glUniformMatrix2x3fv

GL ARB uniform buffer object GL ARB uniform buffer object 1 GL UNIFORM BUFFER 0x8A11 0x8A28 GL UNIFORM BUFFER BINDING GL UNIFORM BUFFER START 0x8A29 GL UNIFORM BUFFER SIZE 0x8A2A GL MAX VERTEX UNIFORM BLOCKS 0x8A2B GL MAX GEOMETRY UNIFORM BLOCKS 0x8A2C GL MAX FRAGMENT UNIFORM BLOCKS 0x8A2D GL MAX COMBINED UNIFORM BLOCKS 0x8A2E GL MAX UNIFORM BUFFER BINDINGS 0x8A2F GL MAX UNIFORM BLOCK SIZE 0x8A30 GL MAX COMBINED VERTEX UNIFORM COMPONENTS 0x8A31 GL MAX COMBINED GEOMETRY UNIFORM COMPONENTS 0x8A32 GL MAX COMBINED FRAGMENT UNIFORM COMPONENTS 0x8A33 GL UNIFORM BUFFER OFFSET ALIGNMENT 0x8A34 GL ACTIVE UNIFORM BLOCK MAX NAME LENGTH 0x8A35 0x8A36 GL ACTIVE UNIFORM BLOCKS GL UNIFORM TYPE 0x8A37 GL UNIFORM SIZE 0x8A38 GL UNIFORM NAME LENGTH 0x8A39 0x8A3A GL UNIFORM BLOCK INDEX GL UNIFORM OFFSET 0x8A3B GL UNIFORM ARRAY STRIDE 0x8A3C GL UNIFORM MATRIX STRIDE 0x8A3D GL UNIFORM IS ROW MAJOR 0x8A3E GL UNIFORM BLOCK BINDING 0x8A3F GL UNIFORM BLOCK DATA SIZE 0x8A40 GL UNIFORM BLOCK NAME LENGTH 0x8A41 GL UNIFORM BLOCK ACTIVE UNIFORMS 0x8A42 GL UNIFORM BLOCK ACTIVE UNIFORM INDICES 0x8A43 GL UNIFORM BLOCK REFERENCED BY VERTEX SHADER 0x8A44 GL UNIFORM BLOCK REFERENCED BY GEOMETRY SHADER 0x8A45 GL UNIFORM BLOCK REFERENCED BY FRAGMENT SHADER 0x8A46 GL INVALID INDEX 0xFFFFFFFFu

engine/texture.cpp GLsizei uniformCount , const GLchar∗ ∗uniformNames , GLuint ∗ uniformIndices ) ; typedef void ( APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) ( GLuint program , GLsizei uniformCount , const GLuint ∗uniformIndices , GLenum pname, GLint ∗params ) ; typedef GLuint ( APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) ( GLuint program , const GLchar ∗uniformBlockName ) ; typedef void ( APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) ( GLuint program , GLuint uniformBlockIndex , GLenum pname, GLint ∗params ) ; typedef void ( APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) ( GLuint program , GLuint uniformBlockIndex , GLuint uniformBlockBinding ) ; #endif # i f n d e f GL INVALID INDEX #define GL INVALID INDEX #endif

509

struct ShaderParamState { enum { CLEAN = 0 , INVALID , DIRTY }; const char ∗name; f l o a t val [ 4 ] ; bool l o c a l ; int dirty ; ShaderParamState ( ) : name(NULL) , l o c a l ( f a l s e ) , d i r t y ( INVALID )

0xFFFFFFFFu

{ # i f n d e f GL VERSION 3 0 #define GL VERSION 3 0 1 typedef void ( APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target , GLuint index , GLuint buffer , GLintptr o f f s e t , GLsizeiptr s i z e ) ; typedef void ( APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target , GLuint index , GLuint b u f f e r ) ; # e l i f GL GLEXT VERSION < 43 typedef void ( APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target , GLuint index , GLuint buffer , GLintptr o f f s e t , GLsizeiptr s i z e ) ; typedef void ( APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target , GLuint index , GLuint b u f f e r ) ; #endif // GL extern extern extern extern extern extern extern

ARB uniform buffer object PFNGLGETUNIFORMINDICESPROC glGetUniformIndices ; PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv ; PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex ; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv ; PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding ; PFNGLBINDBUFFERBASEPROC glBindBufferBase ; PFNGLBINDBUFFERRANGEPROC glBindBufferRange ;

#ifndef #define #define #define #define #define #define #define

GL EXT bindable uniform GL EXT bindable uniform 1 GL MAX VERTEX BINDABLE UNIFORMS EXT 0x8DE2 GL MAX FRAGMENT BINDABLE UNIFORMS EXT 0x8DE3 GL MAX GEOMETRY BINDABLE UNIFORMS EXT 0x8DE4 GL MAX BINDABLE UNIFORM SIZE EXT 0x8DED GL UNIFORM BUFFER EXT 0x8DEE GL UNIFORM BUFFER BINDING EXT 0x8DEF

typedef void ( APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) ( GLuint program , GLint location , GLuint b u f f e r ) ; typedef GLint ( APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) ( GLuint program , GLint l o c a t i o n ) ; typedef GLintptr ( APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) ( GLuint program , GLint l o c a t i o n ) ; #endif // GL EXT bindable uniform extern PFNGLUNIFORMBUFFEREXTPROC glUniformBuffer ; extern PFNGLGETUNIFORMBUFFERSIZEEXTPROC glGetUniformBufferSize ; extern PFNGLGETUNIFORMOFFSETEXTPROC glGetUniformOffset ; extern i n t renderpath ;

memset ( val , −1, s i z e o f ( v a l ) ) ; } }; enum { SHADER DEFAULT = 0, SHADER NORMALSLMS = 1flushenvparams ( ) ; } void s e t v a r i a n t ( i n t col , i n t row , S l o t &s l o t , VSlot &v s l o t , Shader ∗ fallbackshader ) { i f ( ! t h i s || ! detailshader || renderpath==R FIXEDFUNCTION ) return ; s e t v a r i a n t ( col , row , fallbackshader ) ; lastshader−>flushenvparams(& s l o t ) ; lastshader−>setslotparams ( s l o t , v s l o t ) ; } void s e t v a r i a n t ( i n t col , i n t row , S l o t &s l o t , VSlot &v s l o t ) { i f ( ! t h i s || ! detailshader || renderpath==R FIXEDFUNCTION ) return ; s e t v a r i a n t ( col , row , detailshader ) ; lastshader−>flushenvparams(& s l o t ) ; lastshader−>setslotparams ( s l o t , v s l o t ) ; } void s e t ( ) { i f ( lastshader ! = detailshader ) detailshader−>bindprograms ( ) ; } void set ( ) { i f ( ! t h i s || ! detailshader || renderpath==R FIXEDFUNCTION ) return ; set ( ) ; lastshader−>flushenvparams ( ) ; } void set ( S l o t &s l o t , VSlot &v s l o t ) { i f ( ! t h i s || ! detailshader || renderpath==R FIXEDFUNCTION ) return ; set ( ) ; lastshader−>flushenvparams(& s l o t ) ; lastshader−>setslotparams ( s l o t , v s l o t ) ; } bool compile ( ) ; void cleanup ( bool i n v a l i d = f a l s e ) ; s t a t i c i n t uniformlocversion ( ) ; }; #define SETSHADER(name) \ do { \ s t a t i c Shader ∗name##shader = NULL; \ i f ( ! name##shader ) name##shader = lookupshaderbyname (#name) ; \

ImageData ( i n t nw, i n t nh, i n t nbpp , uchar ∗data ) : owner (NULL) , freefunc (NULL) { setdata ( data , nw, nh, nbpp ) ; } ImageData ( SDL Surface ∗s ) { wrap ( s ) ; } ˜ImageData ( ) { cleanup ( ) ; } void setdata ( uchar ∗ndata , i n t nw, i n t nh, i n t nbpp , i n t n l e v e l s = 1 , i n t nalign = 0 , GLenum ncompressed = GL FALSE ) w = nw; h = nh ; bpp = nbpp ; levels = nlevels ; a l i g n = nalign ; pitch = a l i g n ? 0 : w∗bpp ; compressed = ncompressed ; data = ndata ? ndata : new uchar [ c a l c s i z e ( ) ] ; i f ( ! ndata ) { owner = t h i s ; freefunc = NULL; } } i n t c a l c l e v e l s i z e ( i n t l e v e l ) const { return ( ( max(w>>l e v e l , 1)+align −1)/ a l i g n ) ∗ ( (max( h>>l e v e l , 1)+align −1)/ a l i g n )∗bpp ; } i n t c a l c s i z e ( ) const { i f ( ! a l i g n ) return w∗h∗bpp ; i n t lw = w, lh = h , s i z e = 0; loopi ( levels ) { i f ( lw= 1; lh >>= 1; } return s i z e ; } void disown ( ) { data = NULL; owner = NULL; freefunc = NULL; } void cleanup ( ) { i f ( owner== t h i s ) d e l e t e [ ] data ; e l s e i f ( freefunc ) (∗ freefunc ) ( owner ) ; disown ( ) ; } void replace ( ImageData &d ) { cleanup ( ) ; ∗t h i s = d ; i f ( owner == &d ) owner = t h i s ; d . disown ( ) ; } void wrap ( SDL Surface ∗s ) { setdata ( ( uchar ∗)s−>pixels , s−>w, s−>h , s−>format−>BytesPerPixel ) ; pitch = s−>pitch ; owner = s ; freefunc = ( void ( ∗ ) ( void ∗) ) SDL FreeSurface ; }

engine/texture.cpp }; // management o f texture // each texture s l o t can only the f i r s t i s // a d d i t i o n a l frames can struct Texture { enum { IMAGE CUBEMAP TYPE

511

glowcolor = vec ( 1 , 1 , 1) ; pulseglowcolor = vec ( 0 , 0 , 0) ; pulseglowspeed = 0; envscale = vec ( 0 , 0 , 0) ;

slots have multiple texture frames , o f which currently used be used f o r various shaders

} void cleanup ( ) { linked = f a l s e ; } };

= 0, = 1, = 0xFF ,

STUB = 1t r a n s l a t e . y ) ∗ p−>model−>scale ; matrix [ 1 4 ] = ( matrix [ 1 4 ] + p−>t r a n s l a t e . z ) ∗ p−>model−>scale ; } void genvbo ( bool norms , bool tangents , vbocacheentry &vc ) { i f (hasVBO) { i f ( ! vc . vbuf ) glGenBuffers ( 1 , &vc . vbuf ) ; i f ( ebuf ) return ; } e l s e i f ( edata ) { #define ALLOCVDATA( vdata ) \ do \ { \ DELETEA( vdata ) ; \ vdata = new uchar [ vlen∗v e r t s i z e ] ; \ loopv ( meshes ) ( ( vertmesh ∗)meshes [ i ] )−>f i l l t c ( vdata , vertsize ) ; \ } while ( 0 ) i f ( ! vc . vdata ) ALLOCVDATA( vc . vdata ) ; return ; } vector idxs ; vnorms = norms ; vtangents = tangents ; v e r t s i z e = tangents ? s i z e o f ( vvertbump ) : ( norms ? s i z e o f ( v v e r t ) : sizeof ( vvertff ) ) ; vlen = 0; i f ( numframes>1) { loopv ( meshes ) vlen += ( ( vertmesh ∗)meshes [ i ] )−>genvbo ( idxs , vlen ) ; DELETEA( vdata ) ; i f (hasVBO) ALLOCVDATA( vdata ) ; e l s e ALLOCVDATA( vc . vdata ) ; } else { i f (hasVBO) glBindBuffer ( GL ARRAY BUFFER ARB, vc . vbuf ) ; #define GENVBO( type ) \ do \ { \ vector v v e r t s ; \ loopv ( meshes ) vlen += ( ( vertmesh ∗)meshes [ i ] )−>genvbo ( idxs , vlen , vverts , htdata , htlen ) ; \ i f (hasVBO) glBufferData ( GL ARRAY BUFFER ARB, v v e r t s . length ( ) ∗s i z e o f ( type ) , v v e r t s . getbuf ( ) , GL STATIC DRAW ARB ) ; \

} i f (hasVBO) { glGenBuffers ( 1 , &ebuf ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, ebuf ) ; glBufferData (GL ELEMENT ARRAY BUFFER ARB, idxs . length ( ) ∗ s i z e o f ( ushort ) , idxs . getbuf ( ) , GL STATIC DRAW ARB ) ; glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, 0) ; } else { edata = new ushort [ idxs . length ( ) ] ; memcpy( edata , idxs . getbuf ( ) , idxs . length ( ) ∗s i z e o f ( ushort ) ) ; } #undef GENVBO #undef ALLOCVDATA } void bindvbo ( const animstate ∗as , vbocacheentry &vc ) { v v e r t ∗v v e r t s = hasVBO ? 0 : ( v v e r t ∗) vc . vdata ; i f (hasVBO && lastebuf ! = ebuf ) { glBindBuffer (GL ELEMENT ARRAY BUFFER ARB, ebuf ) ; lastebuf = ebuf ; } i f ( lastvbuf ! = (hasVBO ? ( void ∗) ( s i z e t ) vc . vbuf : vc . vdata ) ) { i f (hasVBO) glBindBuffer ( GL ARRAY BUFFER ARB, vc . vbuf ) ; i f ( ! lastvbuf ) glEnableClientState ( GL VERTEX ARRAY ) ; glVertexPointer ( 3 , GL FLOAT, v e r t s i z e , &vverts−>pos ) ; lastvbuf = hasVBO ? ( void ∗) ( s i z e t ) vc . vbuf : vc . vdata ; } i f ( as−>cur . anim&ANIM NOSKIN ) { i f ( enabletc ) d i s a b l e t c ( ) ; i f ( enablenormals ) disablenormals ( ) ; } else { i f ( vnorms || vtangents ) { i f ( ! enablenormals ) { glEnableClientState (GL NORMAL ARRAY) ; enablenormals = true ; } i f ( lastnbuf ! = lastvbuf ) { glNormalPointer ( GL FLOAT, v e r t s i z e , &vverts−>norm ) ; lastnbuf = lastvbuf ; } } e l s e i f ( enablenormals ) disablenormals ( ) ; i f ( ! enabletc ) { glEnableClientState (GL TEXTURE COORD ARRAY) ; enabletc = true ; } i f ( l a s t t c b u f ! = lastvbuf ) { glTexCoordPointer ( 2 , GL FLOAT, v e r t s i z e , &vverts−>u ) ; l a s t t c b u f = lastvbuf ; } } i f ( enablebones ) disablebones ( ) ; } void cleanup ( ) { l o o p i (MAXVBOCACHE) {

engine/vertmodel.h

517 p−>skins [ i ] . bind (m, as ) ; m−>render ( as , p−>skins [ i ] , ∗vc ) ;

vbocacheentry &c = vbocache [ i ] ; i f ( c . vbuf ) { g l D e l e t e B u f f e r s ( 1 , &c . vbuf ) ; c . vbuf = 0; } DELETEA( c . vdata ) ; c . as . cur . f r 1 = −1;

}

} i f (hasVBO) { i f ( ebuf ) { g l D e l e t e B u f f e r s ( 1 , &ebuf ) ; ebuf = 0; } } e l s e DELETEA( vdata ) ;

loopv ( p−>l i n k s ) calctagmatrix ( p , p−>l i n k s [ i ] . tag , ∗as , p−>l i n k s [ i ] . matrix ) ; } };

} void preload ( part ∗p ) { i f ( numframes > 1) return ; bool norms = f a l s e , tangents = f a l s e ; loopv ( p−>skins ) { i f ( p−>skins [ i ] . normals ( ) ) norms = true ; i f ( p−>skins [ i ] . tangents ( ) ) tangents = true ; } i f ( norms! =vnorms || tangents ! = vtangents ) cleanup ( ) ; i f (hasVBO ? ! vbocache−>vbuf : ! vbocache−>vdata ) genvbo ( norms , tangents , ∗vbocache ) ; }

vertmodel ( const char ∗name) : animmodel (name) { } }; template struct v e r t l o a d e r : modelloader { }; template struct vertcommands : modelcommands { typedef struct MDL: : part part ; typedef struct MDL: : skin skin ;

void render ( const animstate ∗as , f l o a t pitch , const vec &axis , const vec &forward , dynent ∗d , part ∗p )

s t a t i c void loadpart ( char ∗model , f l o a t ∗smooth ) { i f ( !MDL: : loading ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } defformatstring ( filename ) (”%s/%s ” , MDL: : dir , model ) ; part &mdl = ∗new part ; MDL: : loading−>parts . add(&mdl ) ; mdl . model = MDL: : loading ; mdl . index = MDL: : loading−>parts . length ( ) −1; i f ( mdl . index ) mdl . pitchscale = mdl . p i t c h o f f s e t = mdl . pitchmin = mdl . pitchmax = 0; mdl . meshes = MDL: : loading−>sharemeshes ( path ( filename ) , double(∗ smooth > 0 ? cos ( clamp(∗smooth , 0.0 f , 180.0 f )∗RAD) : 2) ) ; i f ( ! mdl . meshes ) conoutf ( ” could not load %s ” , filename ) ; e l s e mdl . i n i t s k i n s ( ) ; }

{ i f ( as−>cur . anim&ANIM NORENDER) { loopv ( p−>l i n k s ) calctagmatrix ( p , p−>l i n k s [ i ] . tag , ∗as , p−> l i n k s [ i ] . matrix ) ; return ; } bool norms = f a l s e , tangents = f a l s e ; loopv ( p−>skins ) { i f ( p−>skins [ i ] . normals ( ) ) norms = true ; i f ( p−>skins [ i ] . tangents ( ) ) tangents = true ; } i f ( norms! =vnorms || tangents ! = vtangents ) { cleanup ( ) ; disablevbo () ; } vbocacheentry ∗vc = NULL; i f ( numframesvbuf : ! vc−>vdata ) || vc−>m i l l i s < l a s t m i l l i s ) break ; } } i f (hasVBO ? ! vc−>vbuf : ! vc−>vdata ) genvbo ( norms , tangents , ∗vc ) ; i f ( numframes>1) { i f ( vc−>as!=∗as ) { vc−>as = ∗as ; vc−>m i l l i s = l a s t m i l l i s ; loopv ( meshes ) { vertmesh &m = ∗(vertmesh ∗)meshes [ i ] ; m. i n t e r p v e r t s (∗as , norms , tangents , (hasVBO ? vdata : vc−>vdata ) + m. v o f f s e t∗v e r t s i z e , p−>skins [ i ] ) ; } i f (hasVBO) { glBindBuffer ( GL ARRAY BUFFER ARB, vc−>vbuf ) ; glBufferData ( GL ARRAY BUFFER ARB, vlen∗v e r t s i z e , vdata , GL STREAM DRAW ARB) ; } } vc−>m i l l i s = l a s t m i l l i s ; } bindvbo ( as , ∗vc ) ; loopv ( meshes ) { vertmesh ∗m = ( vertmesh ∗)meshes [ i ] ;

engine/water.cpp

s t a t i c void s e t p i t c h ( f l o a t ∗pitchscale , f l o a t ∗p i t c h o f f s e t , f l o a t ∗ pitchmin , f l o a t ∗pitchmax ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } part &mdl = ∗MDL: : loading−>parts . l a s t ( ) ; mdl . pitchscale = ∗pitchscale ; mdl . p i t c h o f f s e t = ∗p i t c h o f f s e t ; i f (∗ pitchmin || ∗pitchmax ) { mdl . pitchmin = ∗pitchmin ; mdl . pitchmax = ∗pitchmax ; } else { mdl . pitchmin = −360∗fabs ( mdl . pitchscale ) + mdl . p i t c h o f f s e t ; mdl . pitchmax = 360∗fabs ( mdl . pitchscale ) + mdl . p i t c h o f f s e t ; } } s t a t i c void setanim ( char ∗anim , i n t ∗frame , i n t ∗range , f l o a t ∗speed , i n t ∗p r i o r i t y ) { i f ( !MDL: : loading || MDL: : loading−>parts . empty ( ) ) { conoutf ( ” not loading an %s ” , MDL: : formatname ( ) ) ; return ; } vector anims ; findanims ( anim , anims ) ; i f ( anims . empty ( ) ) conoutf ( ” could not f i n d animation %s ” , anim ) ; e l s e loopv ( anims ) { MDL: : loading−>parts . l a s t ( )−>setanim ( 0 , anims [ i ] , ∗frame , ∗range , ∗speed , ∗p r i o r i t y ) ; } } vertcommands ( ) { i f (MDL: : multiparted ( ) ) this−>modelcommand ( loadpart , ” load ” , ” s f ” ) ; this−>modelcommand ( setpitch , ” pitch ” , ” f f f f ” ) ; i f (MDL: : animated ( ) ) this−>modelcommand ( setanim , ”anim ” , ” s i i f f ” ) ; } };

518

Foundations of Videogame Programming Code Repository

#include ” engine . h” VARFP( w a t e r r e f l e c t , 0 , 1 , 1 , { c l e a n r e f l e c t i o n s ( ) ; preloadwatershaders ( ) ; }) ; VARFP( waterrefract , 0 , 1 , 1 , { c l e a n r e f l e c t i o n s ( ) ; preloadwatershaders ( ) ; }) ; VARFP( waterenvmap , 0 , 1 , 1 , { c l e a n r e f l e c t i o n s ( ) ; preloadwatershaders ( ) ; }) ; VARFP( w a t e r f a l l r e f r a c t , 0 , 0 , 1 , { c l e a n r e f l e c t i o n s ( ) ; preloadwatershaders ( ) ; }) ; /∗ vertex water ∗/ VARP( watersubdiv , 0 , 2 , 3) ; VARP( waterlod , 0 , 1 , 3) ; s t a t i c i n t wx1, wy1, wx2, wy2, wsize ; s t a t i c f l o a t whscale , whoffset ; s t a t i c uchar wcol [ 4 ] ; #define VERTW( vertw , defbody , body ) \ s t a t i c i n l i n e void def##vertw ( ) \ { \ varray : : d e f a t t r i b ( varray : : ATTRIB VERTEX, 3 , GL FLOAT ) ; \ defbody ; \ } \ s t a t i c i n l i n e void vertw ( f l o a t v1 , f l o a t v2 , f l o a t v3 ) \ { \ f l o a t angle = f l o a t ( ( v1−wx1 ) ∗(v2−wy1 ) )∗f l o a t ( ( v1−wx2 ) ∗(v2−wy2 ) )∗ whscale+whoffset ; \ f l o a t s = angle − i n t ( angle ) − 0.5 f ; \ s ∗= 8 − fabs ( s ) ∗16; \ f l o a t h = WATER AMPLITUDE∗s−WATER OFFSET; \ varray : : a t t r i b(v1 , v2 , v3+h ) ; \ body ; \ } #define VERTWN( vertw , defbody , body ) \ s t a t i c i n l i n e void def##vertw ( ) \ { \ varray : : d e f a t t r i b ( varray : : ATTRIB VERTEX, 3 , GL FLOAT ) ; \ defbody ; \ } \ s t a t i c i n l i n e void vertw ( f l o a t v1 , f l o a t v2 , f l o a t v3 ) \ { \ f l o a t h = −WATER OFFSET; \ varray : : a t t r i b(v1 , v2 , v3+h ) ; \ body ; \ } #define VERTWT( vertwt , defbody , body ) \ VERTW( vertwt , defbody , { \ f l o a t v = angle − i n t ( angle +0.25) − 0.25; \ v ∗= 8 − fabs ( v ) ∗16; \ f l o a t duv = 0.5 f∗v ; \ body ; \ }) VERTW( vertwt , { varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 2 , GL FLOAT ) ; }, { varray : : a t t r i b(v1/8.0 f , v2/8.0 f ) ; }) VERTWN( vertwtn , { varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 2 , GL FLOAT ) ; }, { varray : : a t t r i b(v1/8.0 f , v2/8.0 f ) ; }) VERTW( vertwc , { varray : : d e f a t t r i b ( varray : : ATTRIB COLOR, 4 , GL UNSIGNED BYTE ) ; }, { varray : : a t t r i b(wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , clamp ( i n t ( wcol [ 3 ] + fabs ( s )∗0x18 ) , 0 , 255) ) ; }) VERTWN( vertwcn , { varray : : d e f a t t r i b ( varray : : ATTRIB COLOR, 4 , GL UNSIGNED BYTE ) ; }, { varray : : a t t r i b v (wcol ) ; }) VERTWT( vertwtc , { varray : : d e f a t t r i b ( varray : : ATTRIB COLOR, 4 , GL UNSIGNED BYTE ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , i n t (0x33 + fabs ( s )∗0 x18 ) ) ; varray : : a t t r i b(v1+duv , v2+duv , v3+h ) ; }) VERTWN( vertwtcn , { glColor4ub ( wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , 0x33 ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(v1 , v2 , v3+h ) ; }) VERTWT( vertwmtc , { varray : : d e f a t t r i b ( varray : : ATTRIB COLOR, 4 , GL UNSIGNED BYTE ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD1, 3 , GL FLOAT ) ;

}, { varray : : a t t r i b(wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , i n t (0x33 + fabs ( s )∗0 x18 ) ) ; varray : : a t t r i b(v1−duv , v2+duv , v3+h ) ; varray : : a t t r i b(v1+duv , v2+duv , v3+h ) ; }) VERTWN( vertwmtcn , { glColor4ub ( wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , 0x33 ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD1, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(v1 , v2 , v3+h ) ; varray : : a t t r i b(v1 , v2 , v3+h ) ; }) VERTWT( vertwetc , { varray : : d e f a t t r i b ( varray : : ATTRIB COLOR, 4 , GL UNSIGNED BYTE ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , i n t (0x33 + fabs ( s )∗0 x18 ) ) ; varray : : a t t r i b(v1+duv−camera1−>o . x , v2+duv−camera1−>o . y , camera1−>o . z−(v3+h ) ) ; }) VERTWN( vertwetcn , { glColor4ub ( wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , 0x33 ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(v1−camera1−>o . x , v2−camera1−>o . y , camera1−>o . z−( v3+h ) ) ; }) VERTWT( vertwemtc , { varray : : d e f a t t r i b ( varray : : ATTRIB COLOR, 4 , GL UNSIGNED BYTE ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD1, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , i n t (0x33 + fabs ( s )∗0 x18 ) ) ; varray : : a t t r i b(v1−duv , v2+duv , v3+h ) ; varray : : a t t r i b(v1+duv−camera1−>o . x , v2+duv−camera1−>o . y , camera1−>o . z−(v3+h ) ) ; }) VERTWN( vertwemtcn , { glColor4ub ( wcol [ 0 ] , wcol [ 1 ] , wcol [ 2 ] , 0x33 ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 3 , GL FLOAT ) ; varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD1, 3 , GL FLOAT ) ; }, { varray : : a t t r i b(v1 , v2 , v3+h ) ; varray : : a t t r i b(v1−camera1−>o . x , v2−camera1−>o . y , camera1−>o . z−( v3+h ) ) ; }) s t a t i c f l o a t lavaxk = 1.0 f , lavayk = 1.0 f , l a v a s c r o l l = 0.0 f ; VERTW( v e r t l , { varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 2 , GL FLOAT ) ; }, { varray : : a t t r i b(lavaxk ∗(v1+ l a v a s c r o l l ) , lavayk ∗(v2+ l a v a s c r o l l ) ) ; }) VERTWN( vertln , { varray : : d e f a t t r i b ( varray : : ATTRIB TEXCOORD0, 2 , GL FLOAT ) ; }, { varray : : a t t r i b(lavaxk ∗(v1+ l a v a s c r o l l ) , lavayk ∗(v2+ l a v a s c r o l l ) ) ; }) #define renderwaterstrips ( vertw , z ) { \ def##vertw ( ) ; \ f o r ( i n t x = wx1 ; x= s i z e ) rendervertwater ( c h i l d s i z e , x , y + c h i l d s i z e , z , c h i l d s i z e , mat ) ; } } return minsubdiv ; } } #define renderwaterquad ( vertwn , z ) \ { \ i f ( varray : : data . empty ( ) ) { def##vertwn ( ) ; varray : : begin (GL QUADS) ; } \ vertwn ( x , y , z ) ; \ vertwn ( x+ r s i z e , y , z ) ; \ vertwn ( x+ r s i z e , y+csize , z ) ; \ vertwn ( x , y+csize , z ) ; \ x t r a v e r t s += 4; \ } void renderflatwater ( i n t x , i n t y , i n t z , uint r s i z e , uint csize , i n t mat ) { switch ( mat ) { case MAT WATER: i f ( renderpath ! =R FIXEDFUNCTION ) { renderwaterquad ( vertwtn , z ) ; } else { bool below = camera1−>o . z < z−WATER OFFSET; i f ( nowater || minimapping ) { renderwaterquad ( vertwcn , z ) ; } else i f ( waterrefract ) { i f ( w a t e r r e f l e c t && ! below ) { renderwaterquad ( vertwmtcn , z ) ; } e l s e i f ( waterenvmap && hasCM && ! below ) { renderwaterquad ( vertwemtcn , z ) ; } e l s e { renderwaterquad ( vertwtcn , z ) ; } } e l s e i f ( w a t e r r e f l e c t && ! below ) { renderwaterquad ( vertwtcn , z ); } e l s e i f ( waterenvmap && hasCM && ! below ) { renderwaterquad ( vertwetcn , z ) ; } e l s e { renderwaterquad ( vertwcn , z ) ; } } break ;

case MAT LAVA: { whoffset = fmod ( f l o a t ( l a s t m i l l i s /2000.0 f /(2∗M PI ) ) , 1.0 f ) ; renderwaterstrips ( v e r t l , z ) ; break ; } } } uint calcwatersubdiv ( i n t x , i n t y , i n t z , uint s i z e ) { float dist ; i f ( camera1−>o . x >= x && camera1−>o . x < x + s i z e && camera1−>o . y >= y && camera1−>o . y < y + s i z e ) d i s t = fabs ( camera1−>o . z − f l o a t ( z ) ) ; else { vec t ( x + s i z e /2 , y + s i z e /2 , z + s i z e /2) ; d i s t = t . d i s t ( camera1−>o ) − s i z e ∗1.42 f /2; } uint subdiv = watersubdiv + i n t ( d i s t ) / (32 = 8∗s i z e o f ( subdiv ) ) subdiv = ˜ 0 ; else subdiv = 1 = s i z e ) rendervertwater ( c h i l d s i z e , x , y , z , c h i l d s i z e , mat ) ; i f ( subdiv2 >= s i z e ) rendervertwater ( c h i l d s i z e , x + c h i l d s i z e , y , z , c h i l d s i z e , mat ) ; i f ( subdiv3 >= s i z e ) rendervertwater ( c h i l d s i z e , x + c h i l d s i z e , y + c h i l d s i z e , z , c h i l d s i z e , mat ) ;

519

case MAT LAVA: renderwaterquad ( vertln , z ) ; break ; } } VARFP( vertwater , 0 , 1 , 1 , allchanged ( ) ) ; s t a t i c i n l i n e void renderwater ( const materialsurface &m, i n t mat = MAT WATER) { i f ( ! vertwater || minimapping ) renderflatwater (m. o . x , m. o . y , m. o . z , m. r s i z e , m. csize , mat ) ; e l s e i f ( renderwaterlod (m. o . x , m. o . y , m. o . z , m. csize , mat ) >= ( uint )m. c s i z e ∗ 2) rendervertwater (m. csize , m. o . x , m. o . y , m. o . z , m. csize , mat ) ; } void renderlava ( const materialsurface &m, Texture ∗tex , f l o a t scale ) { lavaxk = 8.0 f / ( tex−>xs∗scale ) ; lavayk = 8.0 f / ( tex−>ys∗scale ) ; l a v a s c r o l l = l a s t m i l l i s /1000.0 f ; renderwater (m, MAT LAVA ) ; } /∗ r e f l e c t i v e / r e f r a c t i v e water ∗/ #define MAXREFLECTIONS 16 struct R e f l e c t i o n { GLuint tex , r e f r a c t t e x ; i n t material , height , depth , lastupdate , lastused ; g l m a t r i x f projmat ; occludequery ∗query , ∗prevquery ; vector matsurfs ; R e f l e c t i o n ( ) : tex ( 0 ) , r e f r a c t t e x ( 0 ) , material (−1) , height(−1) , depth ( 0 ) , lastused ( 0 ) , query (NULL) , prevquery (NULL) {} }; VARP( r e f l e c t d i s t , 0 , 2000, 10000) ; #define WATERVARS(name) \ bvec name##c o l o r (0x14 , 0x46 , 0x50 ) , name## f a l l c o l o r ( 0 , 0 , 0) ; \ HVARFR(name##colour , 0 , 0x144650 , 0xFFFFFF, \

520

Foundations of Videogame Programming Code Repository

{ \

resettmu ( 1 ) ; glLoadIdentity ( ) ; glDisable ( w a t e r r e f l e c t ? GL TEXTURE 2D : GL TEXTURE CUBE MAP ARB ); g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ;

i f ( ! name##colour ) name##colour = 0x144650 ; \ name##c o l o r = bvec ( ( name##colour>>16)&0xFF , (name##colour>>8)&0xFF , name##colour&0xFF ) ; \ }) ; \ VARR(name##fog , 0 , 150, 10000) ; \ VARR(name##spec , 0 , 150, 1000) ; \ HVARFR(name## f a l l c o l o u r , 0 , 0 , 0xFFFFFF, \ { \ name## f a l l c o l o r = bvec ( ( name## f a l l c o l o u r >>16)&0xFF , (name## f a l l c o l o u r >>8)&0xFF , name## f a l l c o l o u r&0xFF ) ; \ }) ;

} } else { glDisable (GL BLEND) ; glDepthMask ( GL TRUE ) ; } }

WATERVARS( water ) WATERVARS( water2 ) WATERVARS( water3 ) WATERVARS( water4 ) GETMATIDXVAR( water , GETMATIDXVAR( water , GETMATIDXVAR( water , GETMATIDXVAR( water , GETMATIDXVAR( water , GETMATIDXVAR( water ,

R e f l e c t i o n r e f l e c t i o n s [MAXREFLECTIONS ] ; Reflection waterfallrefraction ; GLuint r e f l e c t i o n f b = 0 , r e f l e c t i o n d b = 0; colour , i n t ) color , const bvec &) fallcolour , int ) f a l l c o l o r , const bvec &) fog , i n t ) spec , i n t )

#define LAVAVARS(name) \ bvec name##c o l o r (0xFF , 0x40 , 0x00 ) ; \ HVARFR(name##colour , 0 , 0xFF4000 , 0xFFFFFF, \ { \ i f ( ! name##colour ) name##colour = 0xFF4000 ; \ name##c o l o r = bvec ( ( name##colour>>16)&0xFF , (name##colour>>8)&0xFF , name##colour&0xFF ) ; \ }) ; \ VARR(name##fog , 0 , 50, 10000) ; LAVAVARS( lava ) LAVAVARS( lava2 ) LAVAVARS( lava3 ) LAVAVARS( lava4 ) GETMATIDXVAR( lava , colour , i n t ) GETMATIDXVAR( lava , color , const bvec &) GETMATIDXVAR( lava , fog , i n t ) void setprojtexmatrix ( R e f l e c t i o n &r e f , bool i n i t = true ) { i f ( i n i t && r e f . lastupdate== t o t a l m i l l i s ) ( r e f . projmat = mvpmatrix ) . projective ( ) ;

GLuint g e t w a t e r f a l l t e x ( ) { return w a t e r f a l l r e f r a c t i o n . r e f r a c t t e x ? w a t e r f a l l r e f r a c t i o n . r e f r a c t t e x : notexture−>id ; } VAR( oqwater , 0 , 2 , 2) ; extern i n t oqfrags ; void renderwaterff ( ) { glDisable ( GL CULL FACE ) ; i f ( minimapping ) glDisable ( GL TEXTURE 2D ) ; e l s e i f ( ! nowater && ( w a t e r r e f l e c t || w a t e r r e f r a c t || ( waterenvmap && hasCM) ) ) { i f ( w a t e r r e f r a c t ) setuprefractTMUs ( ) ; e l s e setupreflectTMUs ( ) ; glMatrixMode (GL TEXTURE) ; } else { glDisable ( GL TEXTURE 2D ) ; glDepthMask ( GL FALSE ) ; glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } f l o a t o f f s e t = −WATER OFFSET; varray : : enable ( ) ;

glLoadMatrixf ( r e f . projmat . v ) ; } void setuprefractTMUs ( ) { setuptmu ( 0 , ”= T ” ) ; i f ( w a t e r r e f l e c t || ( waterenvmap && hasCM) ) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glEnable ( w a t e r r e f l e c t ? GL TEXTURE 2D : GL TEXTURE CUBE MAP ARB) ; i f ( ! w a t e r r e f l e c t ) glBindTexture (GL TEXTURE CUBE MAP ARB, lookupenvmap ( lookupmaterialslot (MAT WATER) ) ) ; setuptmu ( 1 , ”T , P @ Ca ” ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; } } void setupreflectTMUs ( ) { setuptmu ( 0 , ”T , K @ Ca” , ”Ka ∗ C˜a ” ) ; glDepthMask ( GL FALSE ) ; glEnable (GL BLEND) ; glBlendFunc (GL ONE, GL SRC ALPHA ) ; i f ( ! waterreflect ) { glDisable ( GL TEXTURE 2D ) ; glEnable (GL TEXTURE CUBE MAP ARB) ; glBindTexture (GL TEXTURE CUBE MAP ARB, lookupenvmap ( lookupmaterialslot (MAT WATER) ) ) ; } } void cleanupwaterTMUs ( bool r e f r a c t ) { resettmu ( 0 ) ; i f ( refract ) { i f ( w a t e r r e f r a c t || ( waterenvmap && hasCM) ) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ;

bool wasbelow = f a l s e ; l o o p i (MAXREFLECTIONS) { R e f l e c t i o n &r e f = r e f l e c t i o n s [ i ] ; i f ( r e f . heightowner ==&r e f ) { i f ( ! r e f . prevquery || r e f . prevquery−>owner!=& r e f || checkquery ( r e f . prevquery ) ) { i f ( checkquery ( r e f . query ) ) continue ; } } bool p r o j t e x = f a l s e ; i f ( w a t e r r e f l e c t || ( waterenvmap && hasCM) ) { bool tmu1 = w a t e r r e f r a c t && ( ! below || ! wasbelow ) ; i f ( tmu1 ) g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; i f ( ! below ) { i f ( wasbelow ) { wasbelow = f a l s e ; glEnable ( w a t e r r e f l e c t ? GL TEXTURE 2D : GL TEXTURE CUBE MAP ARB) ; i f ( ! w a t e r r e f r a c t ) glBlendFunc (GL ONE, GL SRC ALPHA ) ; } i f ( waterreflect ) { glBindTexture ( GL TEXTURE 2D, r e f . tex ) ; setprojtexmatrix ( r e f ) ; p r o j t e x = true ; } else { glBindTexture (GL TEXTURE CUBE MAP ARB, lookupenvmap (

engine/water.cpp lookupmaterialslot ( r e f . material ) ) ) ; } } e l s e i f ( ! wasbelow ) { wasbelow = true ; glDisable ( w a t e r r e f l e c t ? GL TEXTURE 2D : GL TEXTURE CUBE MAP ARB) ; i f ( ! w a t e r r e f r a c t ) glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } i f ( tmu1 ) g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; } i f ( waterrefract ) { glBindTexture ( GL TEXTURE 2D, r e f . r e f r a c t t e x ) ; setprojtexmatrix ( r e f , ! p r o j t e x ) ; } } memcpy( wcol , getwatercolor ( r e f . material ) . v , 3) ; i n t wfog = getwaterfog ( r e f . material ) ; i n t lastdepth = −1; l o o p v j ( r e f . matsurfs ) { materialsurface &m = ∗r e f . matsurfs [ j ] ; i f (m. depth ! = lastdepth ) { f l o a t depth = ! wfog ? 1.0 f : min(0.75 f∗m. depth/wfog , 0.95 f ) ; i f ( nowater || ! w a t e r r e f r a c t ) depth = max( depth , nowater || ( ! w a t e r r e f l e c t && ( ! waterenvmap || !hasCM) ) || below ? 0.6 f : 0.3 f ) ; wcol [ 3 ] = i n t ( depth∗255) ; i f ( ! nowater && ! w a t e r r e f r a c t && ( ( w a t e r r e f l e c t || ( waterenvmap && hasCM) ) && ! below ) ) { i f ( varray : : data . length ( ) ) varray : : end ( ) ; colortmu ( 0 , depth∗wcol [0]/255.0 f , depth∗wcol [1]/255.0 f , depth∗wcol [2]/255.0 f , 1−depth ) ; } lastdepth = m. depth ; } renderwater (m) ; } i f ( varray : : data . length ( ) ) varray : : end ( ) ; } varray : : disable ( ) ; i f ( minimapping ) glEnable ( GL TEXTURE 2D ) ; e l s e i f ( ! nowater && ( w a t e r r e f r a c t || w a t e r r e f l e c t || ( waterenvmap && hasCM) ) ) { i f ( ! w a t e r r e f r a c t && ( wasbelow || ! w a t e r r e f l e c t ) ) { i f ( ! w a t e r r e f l e c t && ! wasbelow ) glDisable (GL TEXTURE CUBE MAP ARB ); glEnable ( GL TEXTURE 2D ) ; } cleanupwaterTMUs ( w a t e r r e f r a c t ! = 0 ) ; glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; } else { glEnable ( GL TEXTURE 2D ) ; glDepthMask ( GL TRUE ) ; glDisable (GL BLEND) ; } glEnable ( GL CULL FACE ) ; } VARFP( waterfade , 0 , 1 , 1 , { c l e a n r e f l e c t i o n s ( ) ; preloadwatershaders ( ) ; }) ; void preloadwatershaders ( bool f o r c e ) { s t a t i c bool needwater = f a l s e ; i f ( f o r c e ) needwater = true ; i f ( ! needwater ) return ; useshaderbyname ( ” waterglare ” ) ; i f ( waterenvmap && ! w a t e r r e f l e c t && hasCM) useshaderbyname ( w at e r r e f r a c t ? ( waterfade && hasFBO ? ” waterenvfade ” : ” waterenvrefract ” ) : ” waterenv ” ) ; e l s e useshaderbyname ( w a t e r r e f r a c t ? ( waterfade && hasFBO ? ” waterfade ” : ” w a t e r r e f r a c t ” ) : ( w a t e r r e f l e c t ? ” w a t e r r e f l e c t ” : ” water ” ) ) ;

521

useshaderbyname ( w a t e r r e f r a c t ? ( waterfade && hasFBO ? ” underwaterfade ” : ” underwaterrefract ” ) : ” underwater ” ) ; extern i n t w a t e r f a l l e n v ; i f ( w a t e r f a l l e n v && hasCM) useshaderbyname ( ” w a t e r f a l l e n v ” ) ; i f ( w a t e r f a l l r e f r a c t ) useshaderbyname ( w a t e r f a l l e n v && hasCM ? ” waterfallenvrefract ” : ” waterfallrefract ” ) ; } void renderwater ( ) { i f ( editmode && showmat && ! envmapping ) return ; i f ( ! rplanes ) return ; i f ( renderpath==R FIXEDFUNCTION ) { renderwaterff ( ) ; return ; } glDisable ( GL CULL FACE ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glEnable ( GL TEXTURE 2D ) ; g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ; glEnable ( GL TEXTURE 2D ) ; i f ( ! g l a r i n g && ! minimapping ) { i f ( waterrefract ) { g l A c t i v e T e x t u r e ( GL TEXTURE3 ARB ) ; glEnable ( GL TEXTURE 2D ) ; i f ( waterfade && hasFBO ) { glEnable (GL BLEND) ; glBlendFunc ( GL SRC ALPHA, GL ONE MINUS SRC ALPHA ) ; } } else { glDepthMask ( GL FALSE ) ; glEnable (GL BLEND) ; glBlendFunc (GL ONE, GL SRC ALPHA ) ; } } g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; i f ( ! g l a r i n g && waterenvmap && ! w a t e r r e f l e c t && hasCM && ! minimapping ) { glDisable ( GL TEXTURE 2D ) ; glEnable (GL TEXTURE CUBE MAP ARB) ; } setenvparamf ( ” camera ” , SHPARAM VERTEX, 0 , camera1−>o . x , camera1−>o . y , camera1−>o . z ) ; setenvparamf ( ” m i l l i s ” , SHPARAM VERTEX, 1 , l a s t m i l l i s /1000.0 f , l a s t m i l l i s /1000.0 f , l a s t m i l l i s /1000.0 f ) ; #define SETWATERSHADER( which , name) \ do { \ s t a t i c Shader ∗name##shader = NULL; \ i f ( ! name##shader ) name##shader = lookupshaderbyname (#name) ; \ which##shader = name##shader ; \ } while ( 0 ) Shader ∗aboveshader = NULL; i f ( g l a r i n g ) SETWATERSHADER( above , waterglare ) ; e l s e i f ( minimapping ) aboveshader = notextureshader ; e l s e i f ( waterenvmap && ! w a t e r r e f l e c t && hasCM) { i f ( waterrefract ) { i f ( waterfade && hasFBO ) SETWATERSHADER( above , waterenvfade ) ; e l s e SETWATERSHADER( above , waterenvrefract ) ; } e l s e SETWATERSHADER( above , waterenv ) ; } else i f ( waterrefract ) { i f ( waterfade && hasFBO ) SETWATERSHADER( above , waterfade ) ; e l s e SETWATERSHADER( above , w a t e r r e f r a c t ) ; } e l s e i f ( w a t e r r e f l e c t ) SETWATERSHADER( above , w a t e r r e f l e c t ) ; e l s e SETWATERSHADER( above , water ) ; Shader ∗belowshader = NULL; i f ( ! g l a r i n g && ! minimapping ) { i f ( waterrefract ) { i f ( waterfade && hasFBO ) SETWATERSHADER( below , underwaterfade ) ; e l s e SETWATERSHADER( below , underwaterrefract ) ; } e l s e SETWATERSHADER( below , underwater ) ; i f ( w a t e r r e f l e c t || w a t e r r e f r a c t ) glMatrixMode (GL TEXTURE) ; }

522

Foundations of Videogame Programming Code Repository lastlight = light ;

varray : : enable ( ) ;

}

vec ambient (max( s k y l i g h t c o l o r [ 0 ] , ambientcolor [ 0 ] ) , max( s k y l i g h t c o l o r [ 1 ] , ambientcolor [ 1 ] ) , max( s k y l i g h t c o l o r [ 2 ] , ambientcolor [ 2 ] ) ) ; f l o a t o f f s e t = −WATER OFFSET; l o o p i (MAXREFLECTIONS) { R e f l e c t i o n &r e f = r e f l e c t i o n s [ i ] ; i f ( r e f . heightowner!=& r e f || checkquery ( r e f . prevquery ) ) { i f ( checkquery ( r e f . query ) ) continue ; } }

i f ( ! g l a r i n g && ! w a t e r r e f r a c t && m. depth ! = lastdepth ) { i f ( varray : : data . length ( ) ) varray : : end ( ) ; f l o a t depth = ! wfog ? 1.0 f : min(0.75 f∗m. depth/wfog , 0.95 f ) ; depth = max( depth , ! below && ( w a t e r r e f l e c t || ( waterenvmap && hasCM) ) ? 0.3 f : 0.6 f ) ; setlocalparamf ( ” depth ” , SHPARAM PIXEL, 5 , depth , 1.0 f−depth ) ; lastdepth = m. depth ; } renderwater (m) ; } i f ( varray : : data . length ( ) ) varray : : end ( ) ; } varray : : disable ( ) ; i f ( ! g l a r i n g && ! minimapping ) { i f ( w a t e r r e f l e c t || w a t e r r e f r a c t ) { glLoadIdentity ( ) ; glMatrixMode (GL MODELVIEW) ; } i f ( waterrefract ) { g l A c t i v e T e x t u r e ( GL TEXTURE3 ARB ) ; glDisable ( GL TEXTURE 2D ) ; i f ( hasFBO && renderpath ! =R FIXEDFUNCTION && waterfade ) glDisable (GL BLEND) ; } else { glDepthMask ( GL TRUE ) ; glDisable (GL BLEND) ; } }

bool below = camera1−>o . z < r e f . height+ o f f s e t ; i f ( below ) { i f ( ! belowshader ) continue ; belowshader−>set ( ) ; } e l s e aboveshader−>set ( ) ; i f ( ! g l a r i n g && ! minimapping ) { i f ( w a t e r r e f l e c t || w a t e r r e f r a c t ) { i f ( w a t e r r e f l e c t || ! waterenvmap || !hasCM) glBindTexture ( GL TEXTURE 2D, w a t e r r e f l e c t ? r e f . tex : r e f . r e f r a c t t e x ); setprojtexmatrix ( r e f ) ; } i f ( waterrefract ) { g l A c t i v e T e x t u r e ( GL TEXTURE3 ARB ) ; glBindTexture ( GL TEXTURE 2D, r e f . r e f r a c t t e x ) ; i f ( waterfade ) { f l o a t fadeheight = r e f . height+ o f f s e t + ( below ? −2 : 2) ; setlocalparamf ( ” waterheight ” , SHPARAM VERTEX, 7 , fadeheight , fadeheight , fadeheight ) ; } }

loopi ( 2 ) { g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB+ i ) ; glDisable ( GL TEXTURE 2D ) ; } g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; i f ( ! g l a r i n g && waterenvmap && ! w a t e r r e f l e c t && hasCM && ! minimapping ) { glDisable (GL TEXTURE CUBE MAP ARB) ; glEnable ( GL TEXTURE 2D ) ; }

} MSlot &mslot = lookupmaterialslot ( r e f . material ) ; g l A c t i v e T e x t u r e ( GL TEXTURE1 ARB ) ; glBindTexture ( GL TEXTURE 2D, mslot . sts . inrange ( 2 ) ? mslot . sts [ 2 ] . t −>id : notexture−>id ) ; g l A c t i v e T e x t u r e ( GL TEXTURE2 ARB ) ; glBindTexture ( GL TEXTURE 2D, mslot . sts . inrange ( 3 ) ? mslot . sts [ 3 ] . t −>id : notexture−>id ) ; g l A c t i v e T e x t u r e ( GL TEXTURE0 ARB ) ; i f ( ! g l a r i n g && waterenvmap && ! w a t e r r e f l e c t && hasCM && ! minimapping ) { glBindTexture (GL TEXTURE CUBE MAP ARB, lookupenvmap ( mslot ) ) ; } glColor3ubv ( getwatercolor ( r e f . material ) . v ) ; i n t wfog = getwaterfog ( r e f . material ) , wspec = getwaterspec ( r e f . material ) ; e n t i t y ∗l a s t l i g h t = ( e n t i t y ∗)−1; i n t lastdepth = −1; l o o p v j ( r e f . matsurfs ) { materialsurface &m = ∗r e f . matsurfs [ j ] ; e n t i t y ∗l i g h t = (m. l i g h t && m. l i g h t−>type==ET LIGHT ? m. l i g h t : NULL) ; i f ( l i g h t != l a s t l i g h t ) { i f ( varray : : data . length ( ) ) varray : : end ( ) ; const vec &l i g h t p o s = l i g h t ? l i g h t−>o : vec ( worldsize /2 , worldsize /2 , worldsize ) ; f l o a t l i g h t r a d = l i g h t && l i g h t−>a t t r 1 ? l i g h t−>a t t r 1 : worldsize ∗8.0 f ; const vec &l i g h t c o l = ( l i g h t ? vec ( l i g h t−>attr2 , l i g h t−>attr3 , l i g h t−>a t t r 4 ) : vec ( ambient ) ) . div (255.0 f ) . mul ( wspec /100.0 f ) ; setlocalparamf ( ” l i g h t p o s ” , SHPARAM VERTEX, 2 , l i g h t p o s . x , lightpos . y , lightpos . z ) ; setlocalparamf ( ” l i g h t c o l o r ” , SHPARAM PIXEL, 3 , l i g h t c o l . x , lightcol .y , lightcol . z ) ; setlocalparamf ( ” l i g h t r a d i u s ” , SHPARAM PIXEL, 4 , lightrad , lightrad , l i g h t r a d ) ;

glEnable ( GL CULL FACE ) ; } void s e t u p w a t e r f a l l r e f r a c t (GLenum tmu1, GLenum tmu2 ) { g l A c t i v e T e x t u r e ( tmu1 ) ; glBindTexture ( GL TEXTURE 2D, w a t e r f a l l r e f r a c t i o n . r e f r a c t t e x ? w a t e r f a l l r e f r a c t i o n . r e f r a c t t e x : notexture−>id ) ; g l A c t i v e T e x t u r e ( tmu2 ) ; glMatrixMode (GL TEXTURE) ; setprojtexmatrix ( w a t e r f a l l r e f r a c t i o n ) ; glMatrixMode (GL MODELVIEW) ; } void c l e a n r e f l e c t i o n ( R e f l e c t i o n &r e f ) { r e f . material = −1; r e f . height = −1; r e f . lastupdate = 0; r e f . query = r e f . prevquery = NULL; r e f . matsurfs . s e t s i z e ( 0 ) ; i f ( r e f . tex ) { glDeleteTextures ( 1 , &r e f . tex ) ; r e f . tex = 0; } i f ( ref . refracttex ) { glDeleteTextures ( 1 , &r e f . r e f r a c t t e x ) ; r e f . r e f r a c t t e x = 0; } } void c l e a n r e f l e c t i o n s ( ) { l o o p i (MAXREFLECTIONS) c l e a n r e f l e c t i o n ( r e f l e c t i o n s [ i ] ) ; cleanreflection ( waterfallrefraction ) ; i f ( reflectionfb ) { glDeleteFramebuffers ( 1 , &r e f l e c t i o n f b ) ;

engine/water.cpp r e f l e c t i o n f b = 0; } i f ( reflectiondb ) { glDeleteRenderbuffers ( 1 , &r e f l e c t i o n d b ) ; r e f l e c t i o n d b = 0; }

523

depthfmt = depthfmts [ f i n d ] ; s t e n c i l f m t = findscreen−>h ) s i z e /= 2; while ( size>hwtexsize ) s i z e /= 2; glGenTextures ( 1 , &tex ) ; char ∗buf = new char [ s i z e∗s i z e ∗4]; memset ( buf , 0 , s i z e∗s i z e ∗4) ; GLenum &colorfmt = r e f r a c t ? r e f r a c t f m t : r e f l e c t f m t ; i f ( colorfmt && ( ! hasFBO || ( fb && db ) ) ) { createtexture ( tex , size , size , buf , 3 , 1 , colorfmt ) ; d e l e t e [ ] buf ; return ; } i f ( hasFBO ) { i f ( ! fb ) glGenFramebuffers ( 1 , &fb ) ; glBindFramebuffer (GL FRAMEBUFFER EXT, fb ) ; } i n t f i n d = needsalpha ? 0 : 2; do { createtexture ( tex , size , size , buf , 3 , 1 , colorfmt ? colorfmt : colorfmts [ f i n d ] ) ; i f ( ! hasFBO ) break ; else { glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE 2D, tex , 0) ; i f ( glCheckFramebufferStatus (GL FRAMEBUFFER EXT) == GL FRAMEBUFFER COMPLETE EXT) break ; } } while ( ! colorfmt && colorfmts [++ f i n d ] ) ; i f ( ! colorfmt ) colorfmt = colorfmts [ f i n d ] ; d e l e t e [ ] buf ;

void a d d w a t e r f a l l r e f r a c t i o n ( materialsurface &m) { R e f l e c t i o n &r e f = w a t e r f a l l r e f r a c t i o n ; i f ( r e f . lastused ! = t o t a l m i l l i s ) { r e f . lastused = t o t a l m i l l i s ; r e f . matsurfs . s e t s i z e ( 0 ) ; r e f . material = MAT WATER; r e f . height = INT MAX ; } r e f . matsurfs . add(&m) ; i f ( ! r e f . r e f r a c t t e x ) genwatertex ( r e f . r e f r a c t t e x , r e f l e c t i o n f b , reflectiondb ) ; } void a d d r e f l e c t i o n ( materialsurface &m) { i n t mat = m. material , height = m. o . z ; R e f l e c t i o n ∗r e f = NULL, ∗o l d e s t = NULL; l o o p i (MAXREFLECTIONS) { R e f l e c t i o n &r = r e f l e c t i o n s [ i ] ; i f ( r . heightlastused== t o t a l m i l l i s ) return ; ref = oldest ; } i f ( r e f−>height ! = height || r e f−>material ! =mat ) { r e f−>material = mat ; r e f−>height = height ; r e f−>prevquery = NULL; } rplanes ++; r e f−>lastused = t o t a l m i l l i s ; r e f−>matsurfs . s e t s i z e ( 0 ) ; r e f−>matsurfs . add(&m) ; r e f−>depth = m. depth ; i f ( nowater || minimapping ) return ; i f ( w a t e r r e f l e c t && ! r e f−>tex ) genwatertex ( r e f−>tex , r e f l e c t i o n f b , reflectiondb ) ; i f ( w a t e r r e f r a c t && ! r e f−>r e f r a c t t e x ) genwatertex ( r e f−>r e f r a c t t e x , r e f l e c t i o n f b , r e f l e c t i o n d b , true ) ; } s t a t i c void drawmaterialquery ( const materialsurface &m, f l o a t o f f s e t , f l o a t border = 0) {

i f ( hasFBO ) { i f ( ! db ) { glGenRenderbuffers ( 1 , &db ) ; depthfmt = s t e n c i l f m t = GL FALSE ; } i f ( ! depthfmt ) glBindRenderbuffer (GL RENDERBUFFER EXT, db ) ; f i n d = hasstencil && hasDS ? 0 : s t e n c i l f m t s ; do { i f ( ! depthfmt ) glRenderbufferStorage (GL RENDERBUFFER EXT, depthfmts [ f i n d ] , size , s i z e ) ; glFramebufferRenderbuffer (GL FRAMEBUFFER EXT, GL DEPTH ATTACHMENT EXT, GL RENDERBUFFER EXT, db ) ; i f ( depthfmt ? s t e n c i l f m t : find 1 ? r e f . query : NULL; r e f . query = r e f . height>=0 && r e f . lastused>=t o t a l m i l l i s && r e f . matsurfs . length ( ) ? newquery(& r e f ) : NULL; i f ( r e f . query ) q u e r y r e f l e c t i o n ( r e f , ! r e f s ++) ; } i f ( renderpath ! =R FIXEDFUNCTION && w a t e r f a l l r e f r a c t ) { R e f l e c t i o n &r e f = w a t e r f a l l r e f r a c t i o n ; r e f . prevquery = oqwater > 1 ? r e f . query : NULL; r e f . query = r e f . height>=0 && r e f . lastused>=t o t a l m i l l i s && r e f . matsurfs . length ( ) ? newquery(& r e f ) : NULL; i f ( r e f . query ) q u e r y r e f l e c t i o n ( r e f , ! r e f s ++) ; }

s t a t i c i n t lastquery = 0; void q u e r y r e f l e c t i o n ( R e f l e c t i o n &r e f , bool i n i t ) { if ( init ) { nocolorshader−>set ( ) ; glDepthMask ( GL FALSE ) ; glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ; glDisable ( GL CULL FACE ) ; } startquery ( r e f . query ) ; l o o p v j ( r e f . matsurfs ) { materialsurface &m = ∗r e f . matsurfs [ j ] ; f l o a t o f f s e t = 0.1 f ; i f (m. o r i e n t ==O TOP ) { o f f s e t = WATER OFFSET + ( vertwater ? WATER AMPLITUDE∗(camera1−>pitch > 0 || m. depth < WATER AMPLITUDE+0.5 f ? −1 : 1) : 0) ; i f ( fabs (m. o . z−o f f s e t − camera1−>o . z ) < 0.5 f && m. depth > WATER AMPLITUDE+1.5 f ) o f f s e t += camera1−>pitch > 0 ? −1 : 1; } drawmaterialquery (m, o f f s e t ) ; } x t r a v e r t s += varray : : end ( ) ; endquery ( r e f . query ) ; }

varray : : disable ( ) ; i f ( refs ) { defaultshader−>set ( ) ; glDepthMask ( GL TRUE ) ; glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ; glEnable ( GL CULL FACE ) ; } glFlush ( ) ; } VARP( maxreflect , 1 , 1 , 8) ;

void q u e r y r e f l e c t i o n s ( ) { rplanes = 0; s t a t i c i n t l a s t s i z e = 0; i n t s i z e = 1w || size>screen−>h ) s i z e /= 2; while ( size>hwtexsize ) s i z e /= 2; i f ( size != l a s t s i z e ) { i f ( l a s t s i z e ) cleanreflections ( ) ; l a s t s i z e = size ; }

i n t r e f r a c t i n g = 0 , r e f r a c t f o g = 0; bvec r e f r a c t c o l o r ( 0 , 0 , 0) ; bool r e f l e c t i n g = f a l s e , fading = f a l s e , fogging = f a l s e ; f l o a t r e f l e c t z = 1e16f ; VAR( maskreflect , 0 , 2 , 16) ; void maskreflection ( R e f l e c t i o n &r e f , f l o a t o f f s e t , bool r e f l e c t , bool clear = false ) {

bool shouldrefract = w a t e r f a l l r e f r a c t && renderpath ! =R FIXEDFUNCTION; f o r ( vtxarray ∗va = v i s i b l e v a ; va ; va = va−>next ) { i f ( ! va−>matsurfs || va−>occluded >= OCCLUDE BB || va−>curvfc >= VFC FOGGED) continue ; i n t lastmat = −1; l o o p i ( va−>matsurfs ) { materialsurface &m = va−>matbuf [ i ] ; i f (m. material ! = lastmat ) { i f ( (m. material&MATF VOLUME) ! = MAT WATER || m. o r i e n t == O BOTTOM) { i += m. skip ; continue ; } i f (m. o r i e n t ! = O TOP ) { i f ( ! shouldrefract || ! getwaterfog (m. material ) ) { i += m. skip ; continue ; } } lastmat = m. material ; } i f (m. o r i e n t ==O TOP ) a d d r e f l e c t i o n (m) ; e l s e a d d w a t e r f a l l r e f r a c t i o n (m) ; } } l o o p i (MAXREFLECTIONS) { R e f l e c t i o n &r e f = r e f l e c t i o n s [ i ] ; i f ( r e f . height>=0 && r e f . lastused>=t o t a l m i l l i s && r e f . matsurfs . length ( ) ) { i f ( waterpvsoccluded ( r e f . height ) ) r e f . matsurfs . s e t s i z e ( 0 ) ; } } i f ( renderpath ! =R FIXEDFUNCTION && w a t e r f a l l r e f r a c t ) { R e f l e c t i o n &r e f = w a t e r f a l l r e f r a c t i o n ; i f ( r e f . height>=0 && r e f . lastused>=t o t a l m i l l i s && r e f . matsurfs . length ( ) ) { i f ( waterpvsoccluded(−1) ) r e f . matsurfs . s e t s i z e ( 0 ) ; } } lastquery = t o t a l m i l l i s ; i f ( ( editmode && showmat && ! envmapping ) || !hasOQ || ! oqfrags || ! oqwater || nowater || minimapping ) return ;

const bvec &wcol = getwatercolor ( r e f . material ) ; f l o a t fogc [ 4 ] = { wcol [0]/255.0 f , wcol [1]/255.0 f , wcol [2]/255.0 f , 1.0 f }; bool inside = r e f . height < INT MAX && ! hasFBO && vertwater && fabs ( r e f . height + o f f s e t − camera1−>o . z ) set ( ) ; g l C o l o r 3 f ( fogc [ 0 ] , fogc [ 1 ] , fogc [ 2 ] ) ; } else { nocolorshader−>set ( ) ; glColorMask ( GL FALSE, GL FALSE, GL FALSE, GL FALSE ) ; } if ( reflect ) { glPushMatrix ( ) ; g l T r a n s l a t e f ( 0 , 0 , 2∗( r e f . height+ o f f s e t ) ) ; g l S c a l e f ( 1 , 1 , −1) ; } varray : : enable ( ) ; loopv ( r e f . matsurfs ) { materialsurface &m = ∗r e f . matsurfs [ i ] ; drawmaterialquery (m, −o f f s e t , maskreflect ) ; } x t r a v e r t s += varray : : end ( ) ; varray : : disable ( ) ; i f ( r e f l e c t ) glPopMatrix ( ) ; i f ( ! c l e a r ) glColorMask ( GL TRUE, GL TRUE, GL TRUE, GL TRUE ) ;

engine/water.cpp defaultshader−>set ( ) ; glEnable ( GL CULL FACE ) ; glEnable ( GL TEXTURE 2D ) ; glDepthFunc ( GL LESS ) ; glDepthRange ( 0 , 1) ; } VAR( r e f l e c t s c i s s o r , 0 , 1 , 1) ; VAR( r e f l e c t v f c , 0 , 1 , 1) ; s t a t i c bool calcscissorbox ( R e f l e c t i o n &r e f , i n t size , vec &clipmin , vec & clipmax , i n t &sx , i n t &sy , i n t &sw, i n t &sh ) { materialsurface &m0 = ∗r e f . matsurfs [ 0 ] ; i n t dim = dimension (m0. o r i e n t ) , r = R[ dim ] , c = C[ dim ] ; i v e c bbmin = m0. o , bbmax = bbmin ; bbmax[ r ] += m0. r s i z e ; bbmax[ c ] += m0. c s i z e ; l o o p v j ( r e f . matsurfs ) { materialsurface &m = ∗r e f . matsurfs [ j ] ; bbmin [ r ] = min ( bbmin [ r ] , m. o [ r ] ) ; bbmin [ c ] = min ( bbmin [ c ] , m. o [ c ] ) ; bbmax[ r ] = max(bbmax[ r ] , m. o [ r ] + m. r s i z e ) ; bbmax[ c ] = max(bbmax[ c ] , m. o [ c ] + m. c s i z e ) ; bbmin [ dim ] = min ( bbmin [ dim ] , m. o [ dim ] ) ; bbmax[ dim ] = max(bbmax[ dim ] , m. o [ dim ] ) ; } vec4 v [ 8 ] ; f l o a t sx1 = 1 , sy1 = 1 , sx2 = −1, sy2 = −1; loopi ( 8 ) { vec4 &p = v [ i ] ; mvpmatrix . transform ( vec ( i&1 ? bbmax. x : bbmin . x , i&2 ? bbmax. y : bbmin . y , ( i&4 ? bbmax. z + WATER AMPLITUDE : bbmin . z − WATER AMPLITUDE) − WATER OFFSET) , p ) ; i f ( p . z >= −p .w) { f l o a t x = p . x / p .w, y = p . y / p .w; sx1 = min ( sx1 , x ) ; sy1 = min ( sy1 , y ) ; sx2 = max( sx2 , x ) ; sy2 = max( sy2 , y ) ; } } i f ( sx1 >= sx2 || sy1 >= sy2 ) return f a l s e ; loopi ( 8 ) { const vec4 &p = v [ i ] ; i f ( p . z >= −p .w) continue ; loopj ( 3 ) { const vec4 &o = v [ iˆ(1w−s i z e ) + ( sx2+1)∗0.5 f∗s i z e ) ) − sx , 0) ; sh = max( i n t ( c e i l ( ( hasFBO ? 0 : screen−>h−s i z e ) + ( sy2 +1)∗0.5 f∗s i z e ) ) − sy , 0) ; return true ; } VARR( r e f r a c t c l e a r , 0 , 0 , 1) ; void drawreflections ( ) { i f ( ( editmode && showmat && ! envmapping ) || nowater || minimapping ) return ; extern i n t nvidia scissor bug ;

525

s t a t i c i n t lastdrawn = 0; i n t r e f s = 0 , n = lastdrawn ; f l o a t o f f s e t = −WATER OFFSET; i n t s i z e = 1w || size>screen−>h ) s i z e /= 2; while ( size>hwtexsize ) s i z e /= 2; i f ( w a t e r r e f l e c t || w a t e r r e f r a c t ) l o o p i (MAXREFLECTIONS) { R e f l e c t i o n &r e f = r e f l e c t i o n s [++n%MAXREFLECTIONS ] ; i f ( r e f . heightowner!=& r e f || checkquery ( r e f . prevquery ) ) { i f ( checkquery ( r e f . query ) ) continue ; } } i f ( ! refs ) { glViewport ( hasFBO ? 0 : screen−>w−size , hasFBO ? 0 : screen−>h− size , size , s i z e ) ; i f ( hasFBO ) glBindFramebuffer (GL FRAMEBUFFER EXT, r e f l e c t i o n f b ) ; } r e f s ++; r e f . lastupdate = t o t a l m i l l i s ; lastdrawn = n ; vec clipmin(−1, −1, −1) , clipmax ( 1 , 1 , 1) ; i n t sx , sy , sw, sh ; bool s c i s s o r = r e f l e c t s c i s s o r && calcscissorbox ( r e f , size , clipmin , clipmax , sx , sy , sw, sh ) ; i f ( s c i s s o r ) g l S c i s s o r ( sx , sy , sw, sh ) ; else { sx = hasFBO ? 0 : screen−>w−s i z e ; sy = hasFBO ? 0 : screen−>h−s i z e ; sw = sh = s i z e ; } const bvec &wcol = getwatercolor ( r e f . material ) ; i n t wfog = getwaterfog ( r e f . material ) ; i f ( w a t e r r e f l e c t && r e f . tex && camera1−>o . z >= r e f . height+ o f f s e t ) { i f ( hasFBO ) glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE 2D, r e f . tex , 0) ; i f ( s c i s s o r && ! nvidia scissor bug ) glEnable ( GL SCISSOR TEST ) ; maskreflection ( r e f , o f f s e t , true ) ; i f ( s c i s s o r && nvidia scissor bug ) glEnable ( GL SCISSOR TEST ) ; savevfcP ( ) ; s e t v f c P ( r e f . height+ o f f s e t , clipmin , clipmax ) ; drawreflection ( r e f . height+ o f f s e t , f a l s e ) ; restorevfcP ( ) ; i f ( s c i s s o r ) glDisable ( GL SCISSOR TEST ) ; i f ( ! hasFBO ) { glBindTexture ( GL TEXTURE 2D, r e f . tex ) ; glCopyTexSubImage2D ( GL TEXTURE 2D, 0 , sx−(screen−>w−s i z e ) , sy −(screen−>h−s i z e ) , sx , sy , sw, sh ) ; } } i f ( w a t e r r e f r a c t && r e f . r e f r a c t t e x ) { i f ( hasFBO ) glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE 2D, r e f . r e f r a c t t e x , 0) ; i f ( s c i s s o r && ! nvidia scissor bug ) glEnable ( GL SCISSOR TEST ) ; maskreflection ( r e f , o f f s e t , f a l s e , r e f r a c t c l e a r || ! wfog || ( r e f . depth>=10000 && camera1−>o . z >= r e f . height + o f f s e t ) ) ; i f ( s c i s s o r && nvidia scissor bug ) glEnable ( GL SCISSOR TEST ) ; i f ( wfog || ( renderpath ! =R FIXEDFUNCTION && waterfade && hasFBO ) ) { savevfcP ( ) ; s e t v f c P (−1, clipmin , clipmax ) ; drawreflection ( r e f . height+ o f f s e t , true , wfog , wcol ) ; restorevfcP ( ) ; } i f ( s c i s s o r ) glDisable ( GL SCISSOR TEST ) ; i f ( ! hasFBO ) { glBindTexture ( GL TEXTURE 2D, r e f . r e f r a c t t e x ) ; glCopyTexSubImage2D ( GL TEXTURE 2D, 0 , sx−(screen−>w−s i z e ) , sy −(screen−>h−s i z e ) , sx , sy , sw, sh ) ; } } i f ( r e f s>=maxreflect ) break ;

526

Foundations of Videogame Programming Code Repository

}

else { sx = hasFBO ? 0 : screen−>w−s i z e ; sy = hasFBO ? 0 : screen−>h−s i z e ; sw = sh = s i z e ; }

i f ( renderpath ! =R FIXEDFUNCTION && w a t e r f a l l r e f r a c t && waterfallrefraction . refracttex ) { R e f l e c t i o n &r e f = w a t e r f a l l r e f r a c t i o n ; i f ( r e f . heightowner!=& r e f || checkquery ( r e f . prevquery ) ) { i f ( checkquery ( r e f . query ) ) goto nowaterfall ; } } i f ( ! refs ) { glViewport ( hasFBO ? 0 : screen−>w−size , hasFBO ? 0 : screen−>h− size , size , s i z e ) ; i f ( hasFBO ) glBindFramebuffer (GL FRAMEBUFFER EXT, r e f l e c t i o n f b ) ; } r e f s ++; r e f . lastupdate = t o t a l m i l l i s ; vec clipmin(−1, −1, −1) , clipmax ( 1 , 1 , 1) ; i n t sx , sy , sw, sh ; bool s c i s s o r = r e f l e c t s c i s s o r && calcscissorbox ( r e f , size , clipmin , clipmax , sx , sy , sw, sh ) ; i f ( s c i s s o r ) g l S c i s s o r ( sx , sy , sw, sh ) ;

i f ( hasFBO ) glFramebufferTexture2D (GL FRAMEBUFFER EXT, GL COLOR ATTACHMENT0 EXT, GL TEXTURE 2D, r e f . r e f r a c t t e x , 0) ; i f ( s c i s s o r && ! nvidia scissor bug ) glEnable ( GL SCISSOR TEST ) ; maskreflection ( r e f , −0.1f , f a l s e ) ; i f ( s c i s s o r && nvidia scissor bug ) glEnable ( GL SCISSOR TEST ) ; savevfcP ( ) ; s e t v f c P (−1, clipmin , clipmax ) ; drawreflection (−1, true ) ; restorevfcP ( ) ; i f ( s c i s s o r ) glDisable ( GL SCISSOR TEST ) ; i f ( ! hasFBO ) { glBindTexture ( GL TEXTURE 2D, r e f . r e f r a c t t e x ) ; glCopyTexSubImage2D ( GL TEXTURE 2D, 0 , sx−(screen−>w−s i z e ) , sy−( screen−>h−s i z e ) , sx , sy , sw, sh ) ; } } nowaterfall : i f ( ! r e f s ) return ; glViewport ( 0 , 0 , screen−>w, screen−>h ) ; i f ( hasFBO ) glBindFramebuffer (GL FRAMEBUFFER EXT, 0) ; defaultshader−>set ( ) ; }

engine/world.cpp // world . cpp : core map management s t u f f #include ” engine . h” VARR( mapversion , 1 , MAPVERSION, 0) ; VARNR( mapscale , worldscale , 1 , 0 , 0) ; VARNR( mapsize , worldsize , 1 , 0 , 0) ; SVARR( maptitle , ” Untitled Map by Unknown ” ) ; VAR( octaentsize , 0 , 128, 1024) ; VAR( entselradius , 0 , 2 , 10) ; bool getentboundingbox ( e x t e n t i t y &e , i v e c &o , i v e c &r ) { switch ( e . type ) { case ET EMPTY: return f a l s e ; case ET MAPMODEL: { model ∗m = loadmodel (NULL, e . a t t r 2 ) ; i f (m) { vec center , radius ; m−>boundbox ( 0 , center , radius ) ; rotatebb ( center , radius , e . a t t r 1 ) ; o = e.o; o . add ( center ) ; r = radius ; r . add ( 1 ) ; o . sub ( r ) ; r . mul ( 2 ) ; break ; } } // i n v i s i b l e mapmodels use entselradius default : o = e.o; o . sub ( entselradius ) ; r . x = r . y = r . z = entselradius ∗2; break ; } return true ; } enum { = 1ents ) ext ( c [ i ] ) . ents = new octaentities ( o , size ) ; o c t a e n t i t i e s &oe = ∗c [ i ] . ext−>ents ; switch ( e . type ) { case ET MAPMODEL: i f ( loadmodel (NULL, e . a t t r 2 ) ) { i f ( va ) { va−>bbmin . x = −1; i f ( oe . mapmodels . empty ( ) ) va−>mapmodels . add(&oe ) ; } oe . mapmodels . add ( id ) ; loopk ( 3 ) { oe . bbmin [ k ] = min ( oe . bbmin [ k ] , max( oe . o [ k ] , bo [ k ] ) ) ; oe .bbmax[ k ] = max( oe .bbmax[ k ] , min ( oe . o [ k] + size , bo [ k] + br [ k ] ) ) ; } break ; } // i n v i s i b l e mapmodel default : oe . other . add ( id ) ; break ; } } e l s e i f ( c [ i ] . ext && c [ i ] . ext−>ents ) { o c t a e n t i t i e s &oe = ∗c [ i ] . ext−>ents ; switch ( e . type ) { case ET MAPMODEL: i f ( loadmodel (NULL, e . a t t r 2 ) ) { oe . mapmodels . removeobj ( id ) ; i f ( va ) { va−>bbmin . x = −1; i f ( oe . mapmodels . empty ( ) ) va−>mapmodels . removeobj(&oe

engine/world.cpp ); } oe . bbmin = oe .bbmax = oe . o ; oe . bbmin . add ( oe . s i z e ) ; l o o p v j ( oe . mapmodels ) { e x t e n t i t y &e = ∗e n t i t i e s : : getents ( ) [ oe . mapmodels [ j ]]; i v e c eo , er ; i f ( getentboundingbox ( e , eo , er ) ) loopk ( 3 ) { oe . bbmin [ k ] = min ( oe . bbmin [ k ] , eo [ k ] ) ; oe .bbmax[ k ] = max( oe .bbmax[ k ] , eo [ k] + er [ k ] ) ; } } loopk ( 3 ) { oe . bbmin [ k ] = max( oe . bbmin [ k ] , oe . o [ k ] ) ; oe .bbmax[ k ] = min ( oe .bbmax[ k ] , oe . o [ k] + s i z e ) ; } break ;

while ( c . ext−>ents && ! c . ext−>ents−>mapmodels . empty ( ) ) removeentity ( c . ext−>ents−>mapmodels . pop ( ) ) ; while ( c . ext−>ents && ! c . ext−>ents−>other . empty ( ) ) removeentity ( c . ext−>ents−>other . pop ( ) ) ; } i f ( c . ext−>ents ) { d e l e t e c . ext−>ents ; c . ext−>ents = NULL; } } void entitiesinoctanodes ( ) { vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) modifyoctaent (MODOE ADD, i , ∗ents [ i ] ) ; } s t a t i c i n l i n e void findents ( o c t a e n t i t i e s &oe , i n t low , i n t high , bool notspawned , const vec &pos , const vec &radius , vector &found ) {

} // i n v i s i b l e mapmodel default : oe . other . removeobj ( id ) ; break ;

vector &ents = e n t i t i e s : : getents ( ) ; loopv ( oe . other ) { i n t id = oe . other [ i ] ; e x t e n t i t y &e = ∗ents [ id ] ; i f ( e . type >= low && e . type ents−>query = NULL; i f ( va && va ! = l a s t v a ) { i f ( lastva ) { i f ( va−>bbmin . x < 0) lastva−>bbmin . x = −1; } e l s e i f ( f l a g s&MODOE UPDATEBB) updatevabb ( va ) ; } } } vector outsideents ; s t a t i c bool modifyoctaent ( i n t f l a g s , i n t id , e x t e n t i t y &e ) { i f ( f l a g s&MODOE ADD ? e . inoctanode : ! e . inoctanode ) return f a l s e ; ivec o , r ; i f ( ! getentboundingbox ( e , o , r ) ) return f a l s e ;

527

} s t a t i c i n l i n e void findents ( cube ∗c , const i v e c &o , i n t size , const i v e c &bo , const i v e c &br , i n t low , i n t high , bool notspawned , const vec &pos , const vec &radius , vector &found ) { loopoctabox ( o , size , bo , br ) { i f ( c [ i ] . ext && c [ i ] . ext−>ents ) findents (∗c [ i ] . ext−>ents , low , high , notspawned , pos , radius , found ) ; i f ( c [ i ] . children && s i z e > octaentsize ) { i v e c co ( i , o . x , o . y , o . z , s i z e ) ; findents ( c [ i ] . children , co , size>>1, bo , br , low , high , notspawned , pos , radius , found ) ; } } } void findents ( i n t low , i n t high , bool notspawned , const vec &pos , const vec &radius , vector &found ) { vec invradius (1/ radius . x , 1/radius . y , 1/radius . z ) ; i v e c bo = vec ( pos ) . sub ( radius ) . sub ( 1 ) , br = vec ( radius ) . add ( 1 ) . mul ( 2 ) ; i n t d i f f = ( bo . x ˆ ( bo . x+br . x ) ) | ( bo . y ˆ ( bo . y+br . y ) ) | ( bo . z ˆ ( bo . z+br . z ) ) | octaentsize , scale = worldscale−1; i f ( d i f f &˜((1ents ) findents (∗c−>ext−>ents , low , high , notspawned , pos , invradius , found ) ; scale−−; while ( c−>children && ! ( d i f f&(1ext && c−>ext−>ents ) findents (∗c−>ext−>ents , low , high , notspawned , pos , invradius , found ) ; scale−−; } i f ( c−>children && 1children , i v e c ( bo ) .mask(˜((2>1, o , r , l e a f s i z e ) ; } e . inoctanode = f l a g s&MODOE ADD ? 1 : 0; i f ( e . type == ET LIGHT ) c l e a r l i g h t c a c h e ( id ) ; e l s e i f ( e . type == ET PARTICLES ) c l e a r p a r t i c l e e m i t t e r s ( ) ; e l s e i f ( f l a g s&MODOE LIGHTENT) l i g h t e n t ( e ) ; return true ; } s t a t i c i n l i n e bool modifyoctaent ( i n t f l a g s , i n t id ) { vector &ents = e n t i t i e s : : getents ( ) ; return ents . inrange ( id ) && modifyoctaent ( f l a g s , id , ∗ents [ id ] ) ; } s t a t i c i n l i n e void addentity ( i n t id ) { modifyoctaent (MODOE ADD| MODOE UPDATEBB|MODOE LIGHTENT, id ) ; } s t a t i c i n l i n e void removeentity ( i n t id ) { modifyoctaent (MODOE UPDATEBB, id ) ; } void f r e e o c t a e n t i t i e s ( cube &c ) { i f ( ! c . ext ) return ; i f ( e n t i t i e s : : getents ( ) . length ( ) ) {

} char ∗entname ( e n t i t y &e ) { s t a t i c s t r i n g fullentname ; copystring ( fullentname , e n t i t i e s : : entname ( e . type ) ) ; const char ∗e i n f o = e n t i t i e s : : entnameinfo ( e ) ; i f (∗ e i n f o ) { concatstring ( fullentname , ” : ” ) ; concatstring ( fullentname , e i n f o ) ; } return fullentname ; } extern s e l i n f o s e l ;

528

Foundations of Videogame Programming Code Repository

extern bool havesel , selectcorners ; i n t e n t l o o p l e v e l = 0; i n t efocus = −1, enthover = −1, entorient = −1, oldhover = −1; bool undonext = true ;

f l o a t c l o s e d i s t = 1e10f ; loopv ( ents ) { e x t e n t i t y ∗a = ents [ i ] ; i f ( a−>attached ) continue ; switch ( e . type ) { case ET SPOTLIGHT : i f ( a−>type ! =ET LIGHT ) continue ; break ;

VARF( entediting , 0 , 0 , 1 , { i f ( ! e n t e d i t i n g ) { entcancel ( ) ; efocus = enthover = −1; } }) ; bool noentedit ( ) { i f ( ! editmode ) { conoutf (CON ERROR, ” operation only allowed in e d i t mode ” ) ; return true ; } return ! e n t e d i t i n g ; }

default : i f ( e . typeo ) ; i f ( dist < closedist ) { closest = i ; closedist = dist ; }

bool p o i n t i n s e l ( s e l i n f o &sel , vec &o ) { return ( o . x = s e l . o . x && o . y = s e l . o . y && o . z = s e l . o . z ) ; }

} i f ( c l o s e d i s t>attachradius ) return ; e . attached = ents [ c l o s e s t ] ; ents [ c l o s e s t]−>attached = &e ;

vector entgroup ; } bool haveselent ( ) { return entgroup . length ( ) > 0; } void entcancel ( ) { entgroup . shrink ( 0 ) ; } void entadd ( i n t id ) { undonext = true ; entgroup . add ( id ) ; } undoblock ∗newundoent ( ) { i n t numents = entgroup . length ( ) ; i f ( numents numents = numents ; undoent ∗e = ( undoent ∗) ( u + 1) ; loopv ( entgroup ) { e−>i = entgroup [ i ] ; e−>e = ∗e n t i t i e s : : getents ( ) [ entgroup [ i ] ] ; e ++; } return u ; } void makeundoent ( ) { i f ( ! undonext ) return ; undonext = f a l s e ; oldhover = enthover ; undoblock ∗u = newundoent ( ) ; i f ( u ) addundo ( u ) ; } void detachentity ( e x t e n t i t y &e ) { i f ( ! e . attached ) return ; e . attached−>attached = NULL; e . attached = NULL; } VAR( attachradius , 1 , 100, 1000) ; void a t t a c h e n t i t y ( e x t e n t i t y &e ) { switch ( e . type ) { case ET SPOTLIGHT : break ; default : i f ( e . type=0) { entadd ( enthover ) ; undonext = ( enthover ! = oldhover ) ; f ; entgroup . drop ( ) ; } else f ; } #define entfocus ( i , f ) { i n t n = efocus = ( i ) ; i f ( n>=0) { e x t e n t i t y &e = ∗e n t i t i e s : : getents ( ) [ n ] ; f ; } } #define e n t e d i t ( i , f ) \ { \ entfocus ( i , \ i n t oldtype = e . type ; \ removeentity ( n ) ; \ f; \ i f ( oldtype ! = e . type ) detachentity ( e ) ; \ i f ( e . type ! =ET EMPTY ) { addentity ( n ) ; i f ( oldtype ! = e . type ) a t t a c h e n t i t y ( e) ; } \ e n t i t i e s : : e d i t e n t ( n , true ) ) ; \ } #define addgroup ( exp ) { loopv ( e n t i t i e s : : getents ( ) ) entfocus ( i , i f ( exp ) entadd ( n ) ) ; } #define setgroup ( exp ) { entcancel ( ) ; addgroup ( exp ) ; } #define groupeditloop ( f ){ e n t l o o p l e v e l ++; i n t = efocus ; loopv ( entgroup ) e n t e d i t ( entgroup [ i ] , f ) ; efocus = ; e n t l o o p l e v e l−−; } #define groupeditpure ( f ){ i f ( e n t l o o p l e v e l >0) { e n t e d i t ( efocus , f ) ; } e l s e groupeditloop ( f ) ; } #define groupeditundo ( f ){ makeundoent ( ) ; groupeditpure ( f ) ; } #define groupedit ( f ) { addimplicit ( groupeditundo ( f ) ) ; } vec getselpos ( ) { vector &ents = e n t i t i e s : : getents ( ) ; i f ( entgroup . length ( ) && ents . inrange ( entgroup [ 0 ] ) ) return ents [ entgroup[0]]−>o ; i f ( ents . inrange ( enthover ) ) return ents [ enthover]−>o ; return s e l . o . tovec ( ) ; } undoblock ∗copyundoents ( undoblock ∗u ) { entcancel ( ) ; undoent ∗e = u−>ents ( ) ; l o o p i ( u−>numents ) entadd ( e [ i ] . i ) ; undoblock ∗c = newundoent ( ) ; l o o p i ( u−>numents ) i f ( e [ i ] . e . type==ET EMPTY ) entgroup . removeobj ( e [ i ] . i ) ; return c ; } void pasteundoents ( undoblock ∗u ) { undoent ∗ue = u−>ents ( ) ; l o o p i ( u−>numents ) e n t e d i t ( ue [ i ] . i , ( e n t i t y &)e = ue [ i ] . e ) ; }

detachentity ( e ) ; vector &ents = e n t i t i e s : : getents ( ) ; i n t c l o s e s t = −1;

void e n t f l i p ( ) { i f ( noentedit ( ) ) return ;

engine/world.cpp i n t d = dimension ( s e l . o r i e n t ) ; f l o a t mid = s e l . s [ d]∗ s e l . g r i d/2+ s e l . o [ d ] ; groupeditundo ( e . o [ d ] −= ( e . o [ d]−mid ) ∗2) ; } void entrotate ( i n t ∗cw ) { i f ( noentedit ( ) ) return ; i n t d = dimension ( s e l . o r i e n t ) ; i n t dd = (∗cwc o l l i s i o n b o x ( 0 , eo , es ) ; i f ( es . x > es . y ) es . y = es . x ; e l s e es . x = es . y ; // square es . z = ( es . z + eo . z + 1 + entselradius ) /2; // enclose ent radius box and model box eo . x += e . o . x ; eo . y += e . o . y ; eo . z = e . o . z − entselradius + es . z ; } e l s e i f ( e . type == ET MAPMODEL && (m = loadmodel (NULL, e . a t t r 2 ) ) ) { m−>c o l l i s i o n b o x ( 0 , eo , es ) ; rotatebb ( eo , es , e . a t t r 1 ) ; #if 0 i f (m−>c o l l i d e ) eo . z −= player−>aboveeye ; // wacky but true . see physics c o l l i d e else es . div ( 2 ) ; // cause the usual bb i s too big . . . #endif eo . add ( e . o ) ; } else { es = vec ( entselradius ) ; eo = e . o ; } eo . sub ( es ) ; es . mul ( 2 ) ; } VAR( entselsnap , 0 , 0 , 1) ; VAR( entmovingshadow , 0 , 1 , 1) ; extern void boxs ( i n t orient , vec o , const vec &s ) ; extern void boxs3D ( const vec &o , vec s , i n t g ) ; extern void editmoveplane ( const vec &o , const vec &ray , i n t d , f l o a t o f f , vec &handle , vec &dest , bool f i r s t ) ;

529

VAR( showentradius , 0 , 1 , 1) ; void renderentring ( const e x t e n t i t y &e , f l o a t radius , i n t axis ) { i f ( radius =2 ? 1 : 0] += radius∗sc . x ; p [ axis>=1 ? 2 : 1] += radius∗sc . y ; glVertex3fv ( p . v ) ; } glEnd ( ) ; } void renderentsphere ( const e x t e n t i t y &e , f l o a t radius ) { i f ( radius o . v ) ; glEnd ( ) ; } void renderentarrow ( const e x t e n t i t y &e , const vec &dir , f l o a t radius ) { i f ( radius o ) ; vec d i r = vec ( e . o ) . sub ( e . attached−>o ) . normalize ( ) ; f l o a t angle = clamp ( i n t ( e . a t t r 1 ) , 1 , 89) ; renderentattachment ( e ) ; renderentcone (∗e . attached , dir , radius , angle ) ; } break ; case ET SOUND: i f ( c o l o r ) g l C o l o r 3 f ( 0 , 1 , 1) ; renderentsphere ( e , e . a t t r 2 ) ; break ; case ET ENVMAP: { extern i n t envmapradius ; i f ( c o l o r ) g l C o l o r 3 f ( 0 , 1 , 1) ; renderentsphere ( e , e . a t t r 1 ? max( 0 , min(10000 , i n t ( e . a t t r 1 ) ) ) : envmapradius ) ; break ; } case ET MAPMODEL: case ET PLAYERSTART : { i f ( c o l o r ) g l C o l o r 3 f ( 0 , 1 , 1) ; e n t i t i e s : : entradius ( e , c o l o r ) ; vec d i r ; vecfromyawpitch ( e . attr1 , 0 , 1 , 0 , d i r ) ; renderentarrow ( e , dir , 4) ; break ; } default : i f ( e . type>=ET GAMESPECIFIC ) { i f ( c o l o r ) g l C o l o r 3 f ( 0 , 1 , 1) ; e n t i t i e s : : entradius ( e , c o l o r ) ; } break ; } } void renderentselection ( const vec &o , const vec &ray , bool entmoving ) { i f ( noentedit ( ) ) return ; vec eo , es ; glColor3ub ( 0 , 40, 0) ; loopv ( entgroup ) entfocus ( entgroup [ i ] , entselectionbox ( e , eo , es ) ; boxs3D ( eo , es , 1) ; ); i f ( enthover >= 0) { entfocus ( enthover , entselectionbox ( e , eo , es ) ) ; // enthover i s back in focus boxs3D ( eo , es , 1) ; i f ( entmoving && entmovingshadow==1) { vec a , b ; glColor3ub (20 , 20, 20) ; ( a = eo ) . x = eo . x − fmod ( eo . x , worldsize ) ; ( b = worldsize ; boxs3D ( a , b , 1) ; ( a = eo ) . y = eo . y − fmod ( eo . y , worldsize ) ; ( b = worldsize ; boxs3D ( a , b , 1) ; ( a = eo ) . z = eo . z − fmod ( eo . z , worldsize ) ; ( b = worldsize ; boxs3D ( a , b , 1) ; } glColor3ub (150 ,0 ,0) ; glLineWidth ( 5 ) ; boxs ( entorient , eo , es ) ; glLineWidth ( 1 ) ; }

also ensures

es ) . x = a . x +

bool e n t t o g g l e ( i n t id ) { undonext = true ; i n t i = entgroup . f i n d ( id ) ; i f ( i < 0) entadd ( id ) ; else entgroup . remove ( i ) ; return i < 0; } bool hoveringonent ( i n t ent , i n t o r i e n t ) { i f ( noentedit ( ) ) return f a l s e ; entorient = o r i e n t ; i f ( ( efocus = enthover = ent ) >= 0) return true ; efocus = entgroup . empty ( ) ? −1 : entgroup . l a s t ( ) ; enthover = −1; return f a l s e ; } VAR( e n t i t y s u r f , 0 , 0 , 1) ; VARF( entmoving , 0 , 0 , 2 , i f ( enthover < 0 || noentedit ( ) ) entmoving = 0; e l s e i f ( entmoving == 1) entmoving = e n t t o g g l e ( enthover ) ; e l s e i f ( entmoving == 2 && entgroup . f i n d ( enthover ) < 0) entadd ( enthover ) ; i f ( entmoving > 0) initentdragging = true ; ); void entpush ( i n t ∗d i r ) { i f ( noentedit ( ) ) return ; i n t d = dimension ( entorient ) ; i n t s = dimcoord ( entorient ) ? −∗d i r : ∗d i r ; i f ( entmoving ) { groupeditpure ( e . o [ d ] += f l o a t ( s∗s e l . g r i d ) ) ; // editdrag supplies the undo } else groupedit ( e . o [ d ] += f l o a t ( s∗s e l . g r i d ) ) ; i f ( e n t i t y s u r f ==1) { player−>o [ d ] += f l o a t ( s∗s e l . g r i d ) ; player−>r e s e t i n t e r p ( ) ; } } VAR( entautoviewdist , 0 , 25, 100) ; void entautoview ( i n t ∗d i r ) { i f ( ! haveselent ( ) ) return ; s t a t i c i n t s = 0; vec v ( player−>o ) ; v . sub ( worldpos ) ; v . normalize ( ) ; v . mul ( entautoviewdist ) ; i n t t = s + ∗d i r ; s = abs ( t ) % entgroup . length ( ) ; i f ( t0) s = entgroup . length ( ) − s ; entfocus ( entgroup [ s ] , v . add ( e . o ) ; player−>o = v ; player−>r e s e t i n t e r p ( ) ; ); }

es ) . y = a . x + es ) . z = a . x +

i f ( showentradius && ( entgroup . length ( ) || enthover >= 0) ) { glDepthFunc (GL GREATER) ; g l C o l o r 3 f (0.25 f , 0.25 f , 0.25 f ) ; loopv ( entgroup ) entfocus ( entgroup [ i ] , renderentradius ( e , f a l s e ) ) ; i f ( enthover>=0) entfocus ( enthover , renderentradius ( e , f a l s e ) ) ; glDepthFunc ( GL LESS ) ; loopv ( entgroup ) entfocus ( entgroup [ i ] , renderentradius ( e , true ) ) ; i f ( enthover>=0) entfocus ( enthover , renderentradius ( e , true ) ) ; }

COMMAND( entautoview , ” i ” ) ; COMMAND( e n t f l i p , ” ” ) ; COMMAND( entrotate , ” i ” ) ; COMMAND( entpush , ” i ” ) ; void delent ( ) { i f ( noentedit ( ) ) return ; groupedit ( e . type = ET EMPTY ; ) ; entcancel ( ) ; } i n t findtype ( char ∗what ) { f o r ( i n t i = 0; ∗e n t i t i e s : : entname ( i ) ; i ++) i f ( strcmp ( what , e n t i t i e s : : entname ( i ) ) ==0) return i ; conoutf (CON ERROR, ”unknown e n t i t y type \”%s \”” , what ) ; return ET EMPTY; }

engine/world.cpp VAR( entdrop , 0 , 2 , 3) ; bool dropentity ( e n t i t y &e , i n t drop = −1) { vec radius ( 4 . 0 f , 4.0 f , 4.0 f ) ; i f ( dropboundbox ( 0 , center , radius ) ; rotatebb ( center , radius , e . a t t r 1 ) ; radius . x += fabs ( center . x ) ; radius . y += fabs ( center . y ) ; } radius . z = 0.0 f ; } switch ( drop ) { case 1: i f ( e . type ! = ET LIGHT && e . type ! = ET SPOTLIGHT ) dropenttofloor (&e ) ; break ; case 2: case 3: i n t cx = 0 , cy = 0; i f ( s e l . cxs == 1 && s e l . cys == 1) { cx = ( s e l . cx ? 1 : −1) ∗ s e l . g r i d / 2; cy = ( s e l . cy ? 1 : −1) ∗ s e l . g r i d / 2; } e . o = s e l . o . tovec ( ) ; i n t d = dimension ( s e l . o r i e n t ) , dc = dimcoord ( s e l . o r i e n t ) ; e . o [R[ d ] ] += s e l . g r i d / 2 + cx ; e . o [C[ d ] ] += s e l . g r i d / 2 + cy ; i f ( ! dc ) e . o [D[ d ] ] −= radius [D[ d ] ] ; else e . o [D[ d ] ] += s e l . g r i d + radius [D[ d ] ] ; i f ( drop == 3) dropenttofloor (&e ) ; break ; } return true ; } void dropent ( ) { i f ( noentedit ( ) ) return ; groupedit ( dropentity ( e ) ) ; } void attachent ( ) { i f ( noentedit ( ) ) return ; groupedit ( a t t a c h e n t i t y ( e ) ) ; } COMMAND( attachent , ” ” ) ; s t a t i c i n t keepents = 0; e x t e n t i t y ∗newentity ( bool l o c a l , const vec &o , i n t type , i n t v1 , i n t v2 , i n t v3 , i n t v4 , i n t v5 , i n t &idx ) { vector &ents = e n t i t i e s : : getents ( ) ; i f ( local ) { idx = −1; f o r ( i n t i = keepents ; i < ents . length ( ) ; i ++) i f ( ents [ i]−>type == ET EMPTY ) { idx = i ; break ; } i f ( idx < 0 && ents . length ( ) >= MAXENTS) { conoutf ( ” too many e n t i t i e s ” ) ; return NULL; } } e l s e while ( ents . length ( ) < idx ) ents . add ( e n t i t i e s : : newentity ( ) )−>type = ET EMPTY; e x t e n t i t y &e = ∗e n t i t i e s : : newentity ( ) ; e.o = o; e . a t t r 1 = v1 ; e . a t t r 2 = v2 ; e . a t t r 3 = v3 ; e . a t t r 4 = v4 ; e . a t t r 5 = v5 ; e . type = type ; e . reserved = 0; e . spawned = f a l s e ; e . inoctanode = f a l s e ; e . l i g h t . c o l o r = vec ( 1 , 1 , 1) ; e . l i g h t . d i r = vec ( 0 , 0 , 1) ; i f ( local ) {

531

switch ( type ) { case ET MAPMODEL: case ET PLAYERSTART : e . attr5 = e . attr4 ; e . attr4 = e . attr3 ; e . attr3 = e . attr2 ; e . attr2 = e . attr1 ; e . a t t r 1 = ( i n t ) camera1−>yaw ; break ; } entities : : fixentity ( e ) ; } i f ( ents . inrange ( idx ) ) { e n t i t i e s : : d e l e t e e n t i t y ( ents [ idx ] ) ; ents [ idx ] = &e ; } e l s e { idx = ents . length ( ) ; ents . add(&e ) ; } return &e ; } void newentity ( i n t type , i n t a1 , i n t a2 , i n t a3 , i n t a4 , i n t a5 ) { i n t idx ; e x t e n t i t y ∗t = newentity ( true , player−>o , type , a1 , a2 , a3 , a4 , a5 , idx ) ; i f ( ! t ) return ; dropentity (∗ t ) ; t−>type = ET EMPTY; e n t t o g g l e ( idx ) ; makeundoent ( ) ; e n t e d i t ( idx , e . type = type ) ; } void newent ( char ∗what , i n t ∗a1 , i n t ∗a2 , i n t ∗a3 , i n t ∗a4 , i n t ∗a5 ) { i f ( noentedit ( ) ) return ; i n t type = findtype ( what ) ; i f ( type ! = ET EMPTY ) newentity ( type , ∗a1 , ∗a2 , ∗a3 , ∗a4 , ∗a5 ) ; } i n t entcopygrid ; vector entcopybuf ; void entcopy ( ) { i f ( noentedit ( ) ) return ; entcopygrid = s e l . g r i d ; entcopybuf . shrink ( 0 ) ; loopv ( entgroup ) entfocus ( entgroup [ i ] , entcopybuf . add ( e ) . o . sub ( s e l . o . tovec ( ) ) ) ; } void entpaste ( ) { i f ( noentedit ( ) ) return ; i f ( entcopybuf . length ( ) ==0) return ; entcancel ( ) ; f l o a t m = f l o a t ( s e l . g r i d ) / f l o a t ( entcopygrid ) ; loopv ( entcopybuf ) { e n t i t y &c = entcopybuf [ i ] ; vec o ( c . o ) ; o . mul (m) . add ( s e l . o . tovec ( ) ) ; i n t idx ; e x t e n t i t y ∗e = newentity ( true , o , ET EMPTY, c . attr1 , c . attr2 , c . attr3 , c . attr4 , c . attr5 , idx ) ; i f ( ! e ) continue ; entadd ( idx ) ; keepents = max( keepents , idx +1) ; } keepents = 0; i n t j = 0; groupeditundo ( e . type = entcopybuf [ j + + ] . type ; ) ; } COMMAND( newent , ” s i i i i i ” ) ; COMMAND( delent , ” ” ) ; COMMAND( dropent , ” ” ) ; COMMAND( entcopy , ” ” ) ; COMMAND( entpaste , ” ” ) ; void entset ( char ∗what , i n t ∗a1 , i n t ∗a2 , i n t ∗a3 , i n t ∗a4 , i n t ∗a5 ) { i f ( noentedit ( ) ) return ; i n t type = findtype ( what ) ; i f ( type ! = ET EMPTY ) groupedit ( e . type=type ; e . a t t r 1=∗a1 ; e . a t t r 2=∗a2 ; e . a t t r 3=∗a3 ; e . a t t r 4=∗a4 ; e . a t t r 5=∗a5 ) ; }

532

Foundations of Videogame Programming Code Repository

void printent ( e x t e n t i t y &e , char ∗buf ) { switch ( e . type ) { case ET PARTICLES : i f ( p r i n t p a r t i c l e s ( e , buf ) ) return ; break ; default : i f ( e . type >= ET GAMESPECIFIC && e n t i t i e s : : printent ( e , buf ) ) return ; break ; } formatstring ( buf ) (”%s %d %d %d %d %d ” , e n t i t i e s : : entname ( e . type ) , e . attr1 , e . attr2 , e . attr3 , e . attr4 , e . a t t r 5 ) ; } void nearestent ( ) { i f ( noentedit ( ) ) return ; i n t c l o s e s t = −1; f l o a t c l o s e d i s t = 1e16f ; vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) { e x t e n t i t y &e = ∗ents [ i ] ; i f ( e . type == ET EMPTY ) continue ; f l o a t d i s t = e . o . d i s t ( player−>o ) ; i f ( dist < closedist ) { closest = i ; closedist = dist ; } } i f ( c l o s e s t >= 0) entadd ( c l o s e s t ) ; } ICOMMAND( enthavesel , ” ” , ( ) , addimplicit ( i n t r e t ( entgroup . length ( ) ) ) ) ; ICOMMAND( entselect , ” e ” , ( uint ∗body ) , i f ( ! noentedit ( ) ) addgroup ( e . type ! = ET EMPTY && entgroup . f i n d ( n )= 1) { i n t typeidx = findtype ( type ) ; i f ( typeidx ! = ET EMPTY ) groupedit ( e . type = typeidx ) ; } e l s e entfocus ( efocus , { r e s u l t ( e n t i t i e s : : entname ( e . type ) ) ; }) } void e n t a t t r ( i n t ∗a t t r , i n t ∗val , i n t ∗numargs ) { i f (∗numargs >= 2) { i f (∗ a t t r >= 0 && ∗a t t r o ; d−>yaw = e n t i t i e s : : getents ( ) [ attempt]−>a t t r 1 ; entinmap ( d ) ; break ; } } } else { d−>o . x = d−>o . y = d−>o . z = 0.5 f∗worldsize ; d−>o . z += 1; entinmap ( d ) ; } } void s p l i t o c t a ( cube ∗c , i n t s i z e ) { i f ( s i z e >1); } } void resetmap ( ) { clearoverrides ( ) ; clearmapsounds ( ) ; cleanreflections ( ) ; resetblendmap ( ) ; resetlightmaps ( ) ; clearpvs ( ) ; clearslots ( ) ; clearparticles ( ) ; cleardecals ( ) ; clearsleep ( ) ; cancelsel ( ) ; pruneundos ( ) ; clearmapcrc ( ) ; e n t i t i e s : : clearents ( ) ; outsideents . s e t s i z e ( 0 ) ; } void startmap ( const char ∗name) {

engine/world.h

533

i f ( noedit ( true ) || ( nompedit && multiplayer ( ) ) ) return ; i f ( worldsize 1); clearmainmenu ( ) ;

shrinkblendmap ( octant ) ; i f ( usecfg ) { i d e n t f l a g s |= IDF OVERRIDDEN; e x e c f i l e ( ” data/default map settings . c f g ” , f a l s e ) ; i d e n t f l a g s &= ˜IDF OVERRIDDEN; }

allchanged ( ) ; conoutf ( ” shrunk map to s i z e %d ” , worldscale ) ; } void newmap( i n t ∗i ) { bool f o r c e = ! isconnected ( ) ; i f ( f o r c e ) game : : f o r c e e d i t ( ” ” ) ; i f ( emptymap(∗ i , force , NULL) ) game : : newmap(max(∗ i , 0) ) ; } void mapenlarge ( ) { i f ( enlargemap ( f a l s e ) ) game : : newmap(−1) ; } COMMAND(newmap, ” i ” ) ; COMMAND( mapenlarge , ” ” ) ; COMMAND( shrinkmap , ” ” ) ;

initlights ( ) ; allchanged ( true ) ; startmap (mname) ; return true ; } bool enlargemap ( bool f o r c e ) { i f ( ! f o r c e && ! editmode ) { conoutf (CON ERROR, ” mapenlarge only allowed in e d i t mode ” ) ; return f a l s e ; } i f ( worldsize >= 1>1); enlargeblendmap ( ) ; allchanged ( ) ; return true ; } s t a t i c bool isallempty ( cube &c ) { i f ( ! c . children ) return isempty ( c ) ; l o o p i ( 8 ) i f ( ! isallempty ( c . children [ i ] ) ) return f a l s e ; return true ; } } void shrinkmap ( ) { extern i n t nompedit ;

i n t getworldsize ( ) { return worldsize ; } i n t getmapversion ( ) { return mapversion ; }

engine/world.h enum { DEFAULT SKY = 0 , DEFAULT GEOM }; #define MAPVERSION 33 cpp

// hardcoded texture numbers

// bump i f map format changes , see worldio .

struct octaheader { char magic [ 4 ] ; i n t version ; i n t headersize ; i n t worldsize ; i n t numents ;

// ”OCTA” // any >8b i t quantity i s l i t t l e endian // s i z e o f ( header )

534 int int int int int

Foundations of Videogame Programming Code Repository numpvs; lightmaps ; blendmap ; numvars ; numvslots ;

};

uchar w a t e r f a l l c o l o u r [ 3 ] ; uchar reserved [ 1 0 ] ; char maptitle [ 1 2 8 ] ; }; #define WATER AMPLITUDE 0.4 f #define WATER OFFSET 1.1 f

struct compatheader // map f i l e format header { char magic [ 4 ] ; // ”OCTA” i n t version ; // any >8b i t quantity i s l i t t l e endian i n t headersize ; // s i z e o f ( header ) i n t worldsize ; i n t numents ; i n t numpvs; i n t lightmaps ; int lightprecision , lighterror , lightlod ; uchar ambient ; uchar watercolour [ 3 ] ; uchar blendmap ; uchar lerpangle , lerpsubdiv , lerpsubdivsize ; uchar bumperror ; uchar s k y l i g h t [ 3 ] ; uchar lavacolour [ 3 ] ;

enum { MATSURF NOT VISIBLE = 0 , MATSURF VISIBLE, MATSURF EDIT ONLY }; #define TEX SCALE 8.0 f struct v e r t e x f f { vec pos ; bvec norm ; uchar reserved ; f l o a t u, v ; f l o a t lmu, lmv ; }; struct vertex { vec pos ; bvec norm ; uchar reserved ; f l o a t u, v ; short lmu , lmv ; bvec tangent ; uchar bitangent ; }; #define VTXSIZE ( renderpath==R FIXEDFUNCTION ? s i z e o f ( v e r t e x f f ) : s i z e o f ( vertex ) )

engine/worldio.cpp // worldio . cpp : loading & saving o f maps and savegames #include ” engine . h” void validmapname ( char ∗dst , const char ∗src , const char ∗p r e f i x = NULL, const char ∗a l t = ” u n t i t l e d ” , s i z e t maxlen = 100) { i f ( p r e f i x ) while (∗ p r e f i x ) ∗dst++ = ∗p r e f i x ++; const char ∗s t a r t = dst ; i f ( src ) l o o p i ( maxlen ) { char c = ∗src ++; i f ( iscubealnum ( c ) || c == ’ ’ || c == ’−’ || c == ’ / ’ || c == ’\\ ’) ∗dst++ = c ; e l s e break ; } i f ( dst > s t a r t ) ∗dst = ’ \ 0 ’ ; e l s e i f ( dst ! = a l t ) copystring ( dst , a l t , maxlen ) ; } void getmapfilenames ( const char ∗fname , const char ∗cname, char ∗pakname, char ∗mapname, char ∗cfgname ) { i f ( ! cname ) cname = fname ; s t r i n g name; validmapname ( name, cname ) ; char ∗slash = strpbrk ( name, ”/\\”) ; i f ( slash ) { copystring ( pakname, name, slash−name+1) ; copystring ( cfgname , slash +1) ; } else { copystring ( pakname, ” base ” ) ; copystring ( cfgname , name) ; } validmapname (mapname, fname , strpbrk ( fname , ”/\\”) ? NULL : ” base / ” ) ; } s t a t i c void f i x e n t ( e n t i t y &e , i n t version ) { i f ( version = 7) e . type ++; i f ( version = 8) e . type ++; i f ( version = ET MAPMODEL && e . type g e t l i l() ; break ; case ID FVAR : f−>g e t l i l() ; break ; case ID SVAR : { i n t slen = f−>g e t l i l() ; f−>seek ( slen , SEEK CUR) ; break ; } } } s t r i n g gametype ; copystring ( gametype , ” fps ” ) ; bool samegame = true ;

engine/worldio.cpp i n t e i f = 0; i f ( hdr . version >=16) { i n t len = f−>getchar ( ) ; f−>read ( gametype , len +1) ; } i f ( strcmp ( gametype , game : : gameident ( ) ) ) { samegame = f a l s e ; conoutf (CON WARN, ”WARNING: loading map from %s game, ignoring e n t i t i e s except f o r l i g h t s /mapmodels ” , gametype ) ; } i f ( hdr . version >=16) { e i f = f−>g e t l i l() ; i n t e x t r a s i z e = f−>g e t l i l() ; f−>seek ( e x t r a s i z e , SEEK CUR) ; } i f ( hdr . version seek (256 , SEEK CUR) ; } else { ushort nummru = f−>g e t l i l() ; f−>seek (nummru∗s i z e o f ( ushort ) , SEEK CUR) ; } l o o p i ( min ( hdr . numents , MAXENTS) ) { e n t i t y &e = ents . add ( ) ; f−>read(&e , s i z e o f ( e n t i t y ) ) ; l i l s w a p (&e . o . x , 3) ; l i l s w a p (&e . attr1 , 5) ; f i x e n t ( e , hdr . version ) ; i f ( e i f > 0) f−>seek ( e i f , SEEK CUR) ; i f ( samegame ) { e n t i t i e s : : readent ( e , NULL, hdr . version ) ; } e l s e i f ( e . type>=ET GAMESPECIFIC || hdr . version seek ( 0 , SEEK END) ; ∗crc = f−>getcrc ( ) ; }

535

void backup ( char ∗name, char ∗backupname ) { s t r i n g backupfile ; copystring ( backupfile , f i n d f i l e ( backupname, ”wb ” ) ) ; remove ( backupfile ) ; rename ( f i n d f i l e ( name, ”wb ” ) , backupfile ) ; } enum { OCTSAV CHILDREN = 0 , OCTSAV EMPTY, OCTSAV SOLID, OCTSAV NORMAL, OCTSAV LODCUBE }; s t a t i c i n t savemapprogress = 0; void savec ( cube ∗c , const i v e c &o , i n t size , stream ∗f , bool nolms ) { i f ( ( savemapprogress++&0xFFF ) ==0) renderprogress ( f l o a t ( savemapprogress ) /allocnodes , ” saving octree . . . ” ) ; loopi ( 8 ) { i v e c co ( i , o . x , o . y , o . z , s i z e ) ; i f ( c [ i ] . children ) { f−>putchar (OCTSAV CHILDREN) ; savec ( c [ i ] . children , co , size>>1, f , nolms ) ; } else { i n t o f l a g s = 0 , surfmask = 0 , t o t a l v e r t s = 0; i f ( c [ i ] . material ! =MAT AIR ) o f l a g s |= 0x40 ; i f ( ! nolms ) { i f ( c [ i ] . merged ) o f l a g s |= 0x80 ; i f ( c [ i ] . ext ) l o o p j ( 6 ) { const surfaceinfo &surf = c [ i ] . ext−>surfaces [ j ] ; i f ( ! surf . used ( ) ) continue ; o f l a g s |= 0x20 ; surfmask |= 1putchar ( o f l a g s | OCTSAV EMPTY) ; e l s e i f ( i s e n t i r e l y s o l i d ( c [ i ] ) ) f−>putchar ( o f l a g s | OCTSAV SOLID ) ; else { f−>putchar ( o f l a g s | OCTSAV NORMAL) ; f−>write ( c [ i ] . edges , 12) ; } l o o p j ( 6 ) f−>p u t l i l(c [ i ] . texture [ j ] ) ;

delete f ; return true ; } # i f n d e f STANDALONE s t r i n g ogzname , bakname, cfgname , picname ; VARP( savebak , 0 , 2 , 2) ; void setmapfilenames ( const char ∗fname , const char ∗cname = NULL) { s t r i n g pakname, mapname, mcfgname ; getmapfilenames ( fname , cname, pakname, mapname, mcfgname ) ; formatstring ( ogzname ) ( ” packages/%s . ogz ” , mapname) ; i f ( savebak==1) formatstring ( bakname ) ( ” packages/%s .BAK” , mapname) ; e l s e formatstring ( bakname ) ( ” packages/%s %d .BAK” , mapname, t o t a l m i l l i s ); formatstring ( cfgname ) ( ” packages/%s/%s . c f g ” , pakname, mcfgname ) ; formatstring ( picname ) ( ” packages/%s . jpg ” , mapname) ; path ( ogzname ) ; path ( bakname ) ; path ( cfgname ) ; path ( picname ) ; } void mapcfgname ( ) { const char ∗mname = game : : getclientmap ( ) ; s t r i n g pakname, mapname, mcfgname ; getmapfilenames (mname, NULL, pakname, mapname, mcfgname ) ; defformatstring ( cfgname ) ( ” packages/%s/%s . c f g ” , pakname, mcfgname ) ; path ( cfgname ) ; r e s u l t ( cfgname ) ; } COMMAND( mapcfgname , ” ” ) ;

i f ( o f l a g s&0x40 ) f−>p u t l i l(c [ i ] . material ) ; i f ( o f l a g s&0x80 ) f−>putchar ( c [ i ] . merged ) ; i f ( o f l a g s&0x20 ) { f−>putchar ( surfmask ) ; f−>putchar ( t o t a l v e r t s ) ; l o o p j ( 6 ) i f ( surfmask&(1v e r t s ( ) + surf . v e r t s ; i n t l a y e r v e r t s = surf . numverts&MAXFACEVERTS, numverts = surf . t o t a l v e r t s ( ) , vertmask = 0 , vertorder = 0 , uvorder = 0 , dim = dimension ( j ) , vc = C[ dim ] , vr = R[ dim ] ; i f ( numverts ) { i f ( c [ i ] . merged&(1p u t l i l< ushort>(v . v ) ; } i f ( hasnorm ) f−>p u t l i l(v . norm ) ;

i n t v i s = v i s i b l e t r i s ( c [ i ] , j , co . x , co . y , co . z , size ) ; i f ( v i s&4 || faceconvexity ( c [ i ] , j ) < 0) vertmask |= 0x01 ; i f ( l a y e r v e r t s < 4 && v i s &2) vertmask |= 0x02 ; } bool matchnorm = true ; loopk ( numverts ) { const v e r t i n f o &v = v e r t s [ k ] ; i f ( v . u || v . v ) vertmask |= 0x40 ; i f ( v . norm ) { vertmask |= 0x80 ; i f ( v . norm ! = v e r t s [ 0 ] . norm ) matchnorm = f a l s e ; } } i f ( matchnorm ) vertmask |= 0x08 ; i f ( vertmask&0x40 && l a y e r v e r t s == 4) { loopk ( 4 ) { const v e r t i n f o &v0 = v e r t s [ k ] , &v1 = v e r t s [ ( k+1) &3] , &v2 = v e r t s [ ( k+2) &3] , &v3 = v e r t s [ ( k+3) &3]; i f ( v1 . u == v0 . u && v1 . v == v2 . v && v3 . u == v2 . u && v3 . v == v0 . v ) { i f ( surf . numverts&LAYER DUP) { const v e r t i n f o &b0 = v e r t s [4+k ] , &b1 = v e r t s [ 4 + ( ( k+1)&3) ] , &b2 = v e r t s [ 4 + ( ( k+2)&3) ] , &b3 = v e r t s [ 4 + ( ( k+3)&3) ] ; i f ( b1 . u ! = b0 . u || b1 . v ! = b2 . v || b3 . u ! = b2 . u || b3 . v ! = b0 . v ) continue ; } uvorder = k ; vertmask |= 0x02 | ( ( ( k+4−vertorder ) &3)p u t l i l(v0 [ vc ] ) ; f−>p u t l i l( v0 [ vr ] ) ; f−>p u t l i l(v2 [ vc ] ) ; f−>p u t l i l( v2 [ vr ] ) ; hasxyz = f a l s e ; } i f ( hasuv && vertmask&0x02 ) { const v e r t i n f o &v0 = v e r t s [ uvorder ] , &v2 = v e r t s [ ( uvorder +2) &3]; f−>p u t l i l(v0 . u ) ; f−>p u t l i l(v0 .v) ; f−>p u t l i l(v2 . u ) ; f−>p u t l i l(v2 .v) ; i f ( surf . numverts&LAYER DUP) { const v e r t i n f o &b0 = v e r t s [4+ uvorder ] , & b2 = v e r t s [ 4 + ( ( uvorder +2)&3) ] ; f−>p u t l i l(b0 . u ) ; f−>p u t l i l(b0 . v ) ; f−>p u t l i l(b2 . u ) ; f−>p u t l i l(b2 . v ) ; } hasuv = f a l s e ; } } i f ( hasnorm && vertmask&0x08 ) { f−>p u t l i l( v e r t s [ 0 ] . norm ) ; hasnorm = f a l s e ; } i f ( hasxyz || hasuv || hasnorm ) loopk ( l a y e r v e r t s ) { const v e r t i n f o &v = v e r t s [ ( k+vertorder )% layerverts ] ; i f ( hasxyz ) { i v e c xyz = v . getxyz ( ) ; f−>p u t l i l(xyz [ vc ] ) ; f−>p u t l i l(xyz [ vr ] ) ;

} i f ( surf . numverts&LAYER DUP) loopk ( l a y e r v e r t s ) { const v e r t i n f o &v = v e r t s [ l a y e r v e r t s + ( k+ vertorder )%l a y e r v e r t s ] ; i f ( hasuv ) { f−>p u t l i l(v . u ) ; f−>p u t l i l< ushort>(v . v ) ; } } } } i f ( c [ i ] . children ) savec ( c [ i ] . children , co , size>>1, f , nolms ) ; } } } struct surfacecompat { uchar texcoords [ 8 ] ; uchar w, h ; ushort x , y ; uchar lmid , l a y e r ; }; struct normalscompat { bvec normals [ 4 ] ; }; struct mergecompat { ushort u1, u2, v1 , v2 ; }; cube ∗loadchildren ( stream ∗f , const i v e c &co , i n t size , bool &f a i l e d ) ; void convertoldsurfaces ( cube &c , const i v e c &co , i n t size , surfacecompat ∗srcsurfs , i n t hassurfs , normalscompat ∗normals , i n t hasnorms , mergecompat ∗merges , i n t hasmerges ) { surfaceinfo dstsurfs [ 6 ] ; v e r t i n f o v e r t s [6∗2∗MAXFACEVERTS] ; i n t t o t a l v e r t s = 0 , numsurfs = 6; memset ( dstsurfs , 0 , s i z e o f ( dstsurfs ) ) ; l o o p i ( 6 ) i f ( ( hassurfs |hasnorms|hasmerges )&(1lmid >= LMID RESERVED && ( src−>x ! = blend−>x || src−>y ! = blend−>y || src−>w ! = blend−>w || src−> h ! = blend−>h || memcmp( src−>texcoords , blend−> texcoords , s i z e o f ( src−>texcoords ) ) ) ) dst . numverts |= LAYER DUP; } e l s e i f ( src−>l a y e r == 1) { dst . lmid [ 1 ] = src−>lmid ; dst . numverts |= LAYER BOTTOM; } e l s e { dst . lmid [ 0 ] = src−>lmid ; dst . numverts |= LAYER TOP ; } } e l s e dst . numverts |= LAYER TOP ; bool uselms = hassurfs&(1= LMID RESERVED || dst . numverts&˜LAYER TOP ) , usemerges = hasmerges&(1w − 1) , v = src−>y + ( src−>texcoords [ k∗2+1] / 255.0 f ) ∗ ( src−>h − 1) ; dv . u = ushort ( f l o o r ( clamp ( ( u ) ∗ f l o a t (USHRT MAX+1)/ LM PACKW + 0.5 f , 0.0 f , f l o a t (USHRT MAX) ) ) ) ; dv . v = ushort ( f l o o r ( clamp ( ( v ) ∗ f l o a t (USHRT MAX+1)/ LM PACKH + 0.5 f , 0.0 f , f l o a t (USHRT MAX) ) ) ) ; } e l s e dv . u = dv . v = 0; dv . norm = usenorms && normals [ i ] . normals [ k ] ! = bvec (128 , 128, 128) ? encodenormal ( normals [ i ] . normals [ k ] . tovec ( ) . normalize ( ) ) : 0; } dst . v e r t s = t o t a l v e r t s ; dst . numverts |= numverts ; t o t a l v e r t s += numverts ; i f ( dst . numverts&LAYER DUP) loopk ( 4 ) { i f ( k > 0 && ( pos [ k ] == pos [ 0 ] || pos [ k ] == pos [ k−1]) ) continue ; v e r t i n f o &bv = v e r t s [ t o t a l v e r t s + + ] ; bv . setxyz ( pos [ k ] ) ; bv . u = ushort ( f l o o r ( clamp ( ( blend−>x + ( blend−>texcoords [ k ∗2] / 255.0 f ) ∗ ( blend−>w − 1) ) ∗ f l o a t (USHRT MAX +1)/LM PACKW, 0.0 f , f l o a t (USHRT MAX) ) ) ) ; bv . v = ushort ( f l o o r ( clamp ( ( blend−>y + ( blend−>texcoords [ k ∗2+1] / 255.0 f ) ∗ ( blend−>h − 1) ) ∗ f l o a t ( USHRT MAX+1)/LM PACKH, 0.0 f , f l o a t (USHRT MAX) ) ) ) ; bv . norm = usenorms && normals [ i ] . normals [ k ] ! = bvec (128 , 128, 128) ? encodenormal ( normals [ i ] . normals [ k ] . tovec ( ) . normalize ( ) ) : 0; } } } setsurfaces ( c , dstsurfs , verts , t o t a l v e r t s ) ; } s t a t i c i n l i n e i n t convertoldmaterial ( i n t mat ) { return ( ( mat&7)3)&3)5)&7)>1, f a i l e d ) ; return ; case OCTSAV LODCUBE: haschildren = true ; break ; case OCTSAV EMPTY: emptyfaces ( c ) ; break ; case OCTSAV SOLID: s o l i d f a c e s ( c ) ; break ; case OCTSAV NORMAL: f−>read ( c . edges , 12) ; break ; d e f a u l t : f a i l e d = true ; return ;

537

} l o o p i ( 6 ) c . texture [ i ] = mapversiongetchar ( ) : f−>g e t l i l< ushort >() ; i f ( mapversion < 7) f−>seek ( 3 , SEEK CUR) ; e l s e i f ( mapversion getchar ( ) ; i f (mask & 0x80 ) { i n t mat = f−>getchar ( ) ; i f ( mapversion < 27) { s t a t i c const ushort matconv [ ] = { MAT AIR , MAT WATER, MAT CLIP , MAT GLASS|MAT CLIP , MAT NOCLIP, MAT LAVA |MAT DEATH, MAT GAMECLIP, MAT DEATH }; c . material = s i z e t ( mat ) < s i z e o f ( matconv ) / s i z e o f ( matconv [ 0 ] ) ? matconv [ mat ] : MAT AIR ; } e l s e c . material = convertoldmaterial ( mat ) ; } surfacecompat surfaces [ 1 2 ] ; normalscompat normals [ 6 ] ; mergecompat merges [ 6 ] ; i n t hassurfs = 0 , hasnorms = 0 , hasmerges = 0; i f (mask & 0x3F ) { i n t numsurfs = 6; l o o p i ( numsurfs ) { i f ( i >= 6 || mask & (1 read(& surfaces [ i ] , s i z e o f ( surfacecompat ) ) ; l i l s w a p (& surfaces [ i ] . x , 2) ; i f ( mapversion < 10) ++surfaces [ i ] . lmid ; i f ( mapversion < 18) { i f ( surfaces [ i ] . lmid >= LMID AMBIENT1 ) ++surfaces [ i ] . lmid ; i f ( surfaces [ i ] . lmid >= LMID BRIGHT1 ) ++surfaces [ i ] . lmid ; } i f ( mapversion < 19) { i f ( surfaces [ i ] . lmid >= LMID DARK) surfaces [ i ] . lmid += 2; } i f ( i < 6) { i f (mask & 0x40 ) { hasnorms |= 1u1 = (m−>u1 − uorigin ) u2 = (m−>u2 − uorigin ) v1 = (m−>v1 − v o r i g i n ) v2 = (m−>v2 − v o r i g i n ) >4; v e r t i n f o &v0 = v e r t s [ uvorder ] , &v1 = v e r t s [ ( uvorder +1) &3] , &v2 = v e r t s [ ( uvorder +2) &3] , &v3 = v e r t s [ ( uvorder +3) &3]; v0 . u = f−>g e t l i l() ; v0 . v = f−>g e t l i l< ushort >() ; v2 . u = f−>g e t l i l() ; v2 . v = f−>g e t l i l< ushort >() ; v1 . u = v0 . u ; v1 . v = v2 . v ; v3 . u = v2 . u ; v3 . v = v0 . v ; i f ( surf . numverts&LAYER DUP) { v e r t i n f o &b0 = v e r t s [4+ uvorder ] , &b1 = v e r t s [ 4 + ( ( uvorder +1)&3) ] , &b2 = v e r t s [ 4 + ( ( uvorder +2)&3) ] , &b3 = v e r t s [ 4 + ( ( uvorder +3)&3) ] ; b0 . u = f−>g e t l i l() ; b0 . v = f−>g e t l i l< ushort >() ; b2 . u = f−>g e t l i l() ; b2 . v = f−>g e t l i l< ushort >() ; b1 . u = b0 . u ; b1 . v = b2 . v ; b3 . u = b2 . u ; b3 . v = b0 . v ; } hasuv = f a l s e ;

} } } } } } i f ( hassurfs || hasnorms || hasmerges ) convertoldsurfaces ( c , co , size , surfaces , hassurfs , normals , hasnorms , merges , hasmerges ) ; } else { i f ( octsav&0x40 ) { i f ( mapversion getchar ( ) ; c . material = convertoldmaterial ( mat ) ; } e l s e c . material = f−>g e t l i l() ; } i f ( octsav&0x80 ) c . merged = f−>getchar ( ) ; i f ( octsav&0x20 ) { i n t surfmask , t o t a l v e r t s ; surfmask = f−>getchar ( ) ; t o t a l v e r t s = f−>getchar ( ) ; newcubeext ( c , t o t a l v e r t s , f a l s e ) ; memset ( c . ext−>surfaces , 0 , s i z e o f ( c . ext−>surfaces ) ) ; memset ( c . ext−>v e r t s ( ) , 0 , t o t a l v e r t s∗s i z e o f ( v e r t i n f o ) ) ; i n t o f f s e t = 0; l o o p i ( 6 ) i f ( surfmask&(1read(& surf , s i z e o f ( surfaceinfo ) ) ; i n t vertmask = surf . verts , numverts = surf . t o t a l v e r t s ( ) ; i f ( ! numverts ) { surf . v e r t s = 0; continue ; } surf . v e r t s = o f f s e t ; v e r t i n f o ∗v e r t s = c . ext−>v e r t s ( ) + o f f s e t ; o f f s e t += numverts ; ivec v [ 4 ] , n; i n t l a y e r v e r t s = surf . numverts&MAXFACEVERTS, dim = dimension ( i ) , vc = C[ dim ] , vr = R[ dim ] , bias = 0; genfaceverts ( c , i , v ) ; bool hasxyz = ( vertmask&0x04 ) !=0 , hasuv = ( vertmask&0x40 ) !=0 , hasnorm = ( vertmask&0x80 ) ! = 0 ; i f ( hasxyz ) { i v e c e1 , e2 , e3 ; n . cross ( ( e1 = v [ 1 ] ) . sub ( v [ 0 ] ) , ( e2 = v [ 2 ] ) . sub ( v [ 0 ] ) ) ; i f ( n . i s z e r o ( ) ) n . cross ( e2 , ( e3 = v [ 3 ] ) . sub ( v [ 0 ] ) ) ; bias = −n . dot ( i v e c ( v [ 0 ] ) . mul ( s i z e ) . add ( i v e c ( co ) .mask (0xFFF ) . shl ( 3 ) ) ) ; } else { i n t v i s = l a y e r v e r t s < 4 ? ( vertmask&0x02 ? 2 : 1) : 3 , order = vertmask&0x01 ? 1 : 0 , k = 0; i v e c vo = i v e c ( co ) .mask(0xFFF ) . shl ( 3 ) ; v e r t s [ k + + ] . setxyz ( v [ order ] . mul ( s i z e ) . add ( vo ) ) ; i f ( v i s &1) v e r t s [ k + + ] . setxyz ( v [ order + 1 ] .mul ( s i z e ) . add ( vo ) ) ; v e r t s [ k + + ] . setxyz ( v [ order + 2 ] .mul ( s i z e ) . add ( vo ) ) ; i f ( v i s &2) v e r t s [ k + + ] . setxyz ( v [ ( order +3) &3].mul ( s i z e ) . add ( vo ) ) ; } i f ( l a y e r v e r t s == 4) { i f ( hasxyz && vertmask&0x01 ) { ushort c1 = f−>g e t l i l() , r1 = f−>g e t l i l< ushort >() , c2 = f−>g e t l i l() , r2 = f−>g e t l i l() ; i v e c xyz ; xyz [ vc ] = c1 ; xyz [ vr ] = r1 ; xyz [ dim ] = −(bias + n [ vc ]∗ xyz [ vc ] + n [ vr ]∗ xyz [ vr ] ) /n [ dim ] ; v e r t s [ 0 ] . setxyz ( xyz ) ; xyz [ vc ] = c1 ; xyz [ vr ] = r2 ; xyz [ dim ] = −(bias + n [ vc ]∗ xyz [ vc ] + n [ vr ]∗ xyz [ vr ] ) /n [ dim ] ; v e r t s [ 1 ] . setxyz ( xyz ) ; xyz [ vc ] = c2 ; xyz [ vr ] = r2 ; xyz [ dim ] = −(bias + n [ vc ]∗ xyz [ vc ] + n [ vr ]∗ xyz [ vr ] ) /n [ dim ] ; v e r t s [ 2 ] . setxyz ( xyz ) ; xyz [ vc ] = c2 ; xyz [ vr ] = r1 ; xyz [ dim ] = −(bias + n [ vc ]∗ xyz [ vc ] + n [ vr ]∗ xyz [ vr ] ) /n [ dim ] ; v e r t s [ 3 ] . setxyz ( xyz ) ; hasxyz = f a l s e ; } i f ( hasuv && vertmask&0x02 ) {

} } i f ( hasnorm && vertmask&0x08 ) { ushort norm = f−>g e t l i l() ; loopk ( l a y e r v e r t s ) v e r t s [ k ] . norm = norm ; hasnorm = f a l s e ; } i f ( hasxyz || hasuv || hasnorm ) loopk ( l a y e r v e r t s ) { v e r t i n f o &v = v e r t s [ k ] ; i f ( hasxyz ) { i v e c xyz ; xyz [ vc ] = f−>g e t l i l() ; xyz [ vr ] = f−> g e t l i l() ; xyz [ dim ] = −(bias + n [ vc ]∗ xyz [ vc ] + n [ vr ]∗ xyz [ vr ] ) /n [ dim ] ; v . setxyz ( xyz ) ; } i f ( hasuv ) { v . u = f−>g e t l i l() ; v . v = f−> g e t l i l() ; } i f ( hasnorm ) v . norm = f−>g e t l i l() ; } i f ( surf . numverts&LAYER DUP) loopk ( l a y e r v e r t s ) { v e r t i n f o &v = v e r t s [ k+ l a y e r v e r t s ] , &t = v e r t s [ k ] ; v . setxyz ( t . x , t . y , t . z ) ; i f ( hasuv ) { v . u = f−>g e t l i l() ; v . v = f−> g e t l i l() ; } v . norm = t . norm ; } } } } c . children = ( haschildren ? loadchildren ( f , co , size>>1, f a i l e d ) : NULL) ; } cube ∗loadchildren ( stream ∗f , const i v e c &co , i n t size , bool &f a i l e d ) { cube ∗c = newcubes ( ) ; loopi ( 8 ) { loadc ( f , c [ i ] , i v e c ( i , co . x , co . y , co . z , s i z e ) , size , f a i l e d ) ; i f ( f a i l e d ) break ; } return c ; } VAR( dbgvars , 0 , 0 , 1) ; void savevslot ( stream ∗f , VSlot &vs , i n t prev ) { f−>p u t l i l(vs . changed ) ; f−>p u t l i l(prev ) ; i f ( vs . changed & (1p u t l i l(s t r l e n ( p .name) ) ; f−>write ( p .name, s t r l e n ( p .name) ) ; loopk ( 4 ) f−>p u t l i l(p . v a l [ k ] ) ; } } i f ( vs . changed & (1= numvslots ) ; i f ( ! vs ) break ; prev [ vs−>index ] = cur−>index ; } } i n t l a s t r o o t = 0; l o o p i ( numvslots ) { VSlot &vs = ∗v s l o t s [ i ] ; i f ( ! vs . changed ) continue ; i f ( l a s t r o o t < i ) f−>p u t l i l(−(i − l a s t r o o t ) ) ; savevslot ( f , vs , prev [ i ] ) ; l a s t r o o t = i +1; } i f ( l a s t r o o t < numvslots ) f−>p u t l i l(−(numvslots − l a s t r o o t ) ) ; d e l e t e [ ] prev ; } void l o a d v s l o t ( stream ∗f , VSlot &vs , i n t changed ) { vs . changed = changed ; i f ( vs . changed & (1g e t l i l() ; f−>read ( name, min ( nlen , MAXSTRLEN−1)) ; name[ min ( nlen , MAXSTRLEN−1)] = ’ \ 0 ’ ; i f ( nlen >= MAXSTRLEN) f−>seek ( nlen − (MAXSTRLEN−1) , SEEK CUR) ; p .name = getshaderparamname (name) ; p . type = SHPARAM LOOKUP; p . index = −1; p . l o c = −1; loopk ( 4 ) p . v a l [ k ] = f−>g e t l i l() ; } } i f ( vs . changed & (1next = vslots [ i ] ; d e l e t e [ ] prev ; } bool save world ( const char ∗mname, bool nolms ) { i f ( ! ∗mname) mname = game : : getclientmap ( ) ; setmapfilenames (mname) ; i f ( savebak ) backup ( ogzname , bakname ) ; stream ∗f = o p e n g z f i l e ( ogzname , ”wb ” ) ; i f ( ! f ) { conoutf (CON WARN, ” could not write map to %s ” , ogzname ) ; return f a l s e ; } i n t numvslots = v s l o t s . length ( ) ; i f ( ! nolms && ! multiplayer ( f a l s e ) ) { numvslots = compactvslots ( ) ; allchanged ( ) ; } savemapprogress = 0; renderprogress ( 0 , ” saving map . . . ” ) ; octaheader hdr ; memcpy( hdr . magic , ”OCTA” , 4) ; hdr . version = MAPVERSION; hdr . headersize = s i z e o f ( hdr ) ; hdr . worldsize = worldsize ; hdr . numents = 0; const vector &ents = e n t i t i e s : : getents ( ) ; loopv ( ents ) i f ( ents [ i]−>type ! =ET EMPTY || nolms ) hdr . numents++; hdr .numpvs = nolms ? 0 : getnumviewcells ( ) ; hdr . lightmaps = nolms ? 0 : lightmaps . length ( ) ; hdr . blendmap = shouldsaveblendmap ( ) ; hdr . numvars = 0; hdr . numvslots = numvslots ; enumerate ( idents , ident , id , { i f ( ( id . type == ID VAR || id . type == ID FVAR || id . type == ID SVAR ) && id . f l a g s&IDF OVERRIDE && ! ( id . f l a g s&IDF READONLY ) && id . f l a g s&IDF OVERRIDDEN) hdr . numvars++; }) ; l i l s w a p (&hdr . version , 9) ; f−>write (&hdr , s i z e o f ( hdr ) ) ; enumerate ( idents , ident , id , { i f ( ( id . type ! = ID VAR && id . type ! =ID FVAR && id . type ! =ID SVAR ) || ! ( id . f l a g s&IDF OVERRIDE ) || id . f l a g s&IDF READONLY || ! ( id . f l a g s&IDF OVERRIDDEN) ) continue ; f−>putchar ( id . type ) ; f−>p u t l i l(s t r l e n ( id .name) ) ; f−>write ( id .name, s t r l e n ( id .name) ) ; switch ( id . type ) { case ID VAR : i f ( dbgvars ) conoutf (CON DEBUG, ” wrote var %s : %d ” , id . name, ∗id . storage . i ) ; f−>p u t l i l(∗id . storage . i ) ; break ; case ID FVAR : i f ( dbgvars ) conoutf (CON DEBUG, ” wrote f v a r %s : %f ” , id . name, ∗id . storage . f ) ; f−>p u t l i l(∗id . storage . f ) ; break ; case ID SVAR : i f ( dbgvars ) conoutf (CON DEBUG, ” wrote svar %s : %s ” , id .

540

Foundations of Videogame Programming Code Repository name, ∗id . storage . s ) ; f−>p u t l i l(s t r l e n (∗ id . storage . s ) ) ; f−>write (∗ id . storage . s , s t r l e n (∗ id . storage . s ) ) ; break ;

{ i f ( f−>read(&chdr . l i g h t p r e c i s i o n , s i z e o f ( chdr ) − 7∗s i z e o f ( i n t ) ) ! = i n t ( s i z e o f ( chdr ) − 7∗s i z e o f ( i n t ) ) ) { conoutf (CON ERROR, ” map %s has malformatted header ” , ogzname ) ; d e l e t e f ; return f a l s e ; }

} }) ; i f ( dbgvars ) conoutf (CON DEBUG, ” wrote %d vars ” , hdr . numvars ) ; f−>putchar ( ( i n t ) s t r l e n ( game : : gameident ( ) ) ) ; f−>write ( game : : gameident ( ) , ( i n t ) s t r l e n ( game : : gameident ( ) ) +1) ; f−>p u t l i l(e n t i t i e s : : e x t r a e n t i n f o s i z e ( ) ) ; vector extras ; game : : writegamedata ( extras ) ; f−>p u t l i l(extras . length ( ) ) ; f−>write ( extras . getbuf ( ) , extras . length ( ) ) ;

} else { i n t extra = 0; i f ( hdr . version read(&hdr . blendmap , s i z e o f ( hdr ) − (7+ extra )∗s i z e o f ( i n t ) ) ! = i n t ( s i z e o f ( hdr ) − (7+ extra )∗s i z e o f ( i n t ) ) ) { conoutf ( CON ERROR, ”map %s has malformatted header ” , ogzname ) ; d e l e t e f ; return f a l s e ; } } resetmap ( ) ;

f−>p u t l i l(texmru . length ( ) ) ; loopv ( texmru ) f−>p u t l i l(texmru [ i ] ) ; char ∗ebuf = new char [ e n t i t i e s : : e x t r a e n t i n f o s i z e ( ) ] ; loopv ( ents ) { i f ( ents [ i]−>type ! =ET EMPTY || nolms ) { e n t i t y tmp = ∗ents [ i ] ; l i l s w a p (&tmp . o . x , 3) ; l i l s w a p (&tmp . attr1 , 5) ; f−>write (&tmp, s i z e o f ( e n t i t y ) ) ; e n t i t i e s : : writeent (∗ ents [ i ] , ebuf ) ; i f ( e n t i t i e s : : e x t r a e n t i n f o s i z e ( ) ) f−>write ( ebuf , e n t i t i e s : : extraentinfosize ( ) ) ; } } d e l e t e [ ] ebuf ; savevslots ( f , numvslots ) ; renderprogress ( 0 , ” saving octree . . . ” ) ; savec ( worldroot , i v e c ( 0 , 0 , 0) , worldsize>>1, f , nolms ) ; i f ( ! nolms ) { i f ( lightmaps . length ( ) ) renderprogress ( 0 , ” saving lightmaps . . . ” ) ; loopv ( lightmaps ) { LightMap &lm = lightmaps [ i ] ; f−>putchar ( lm . type | ( lm . unlitx>=0 ? 0x80 : 0) ) ; i f ( lm . unlitx >=0) { f−>p u t l i l(ushort ( lm . u n l i t x ) ) ; f−>p u t l i l(ushort ( lm . u n l i t y ) ) ; } f−>write ( lm . data , lm . bpp∗LM PACKW∗LM PACKH) ; renderprogress ( f l o a t ( i +1)/lightmaps . length ( ) , ” saving lightmaps . . . ” ) ; } i f ( getnumviewcells ( ) >0) { renderprogress ( 0 , ” saving pvs . . . ” ) ; savepvs ( f ) ; } } i f ( shouldsaveblendmap ( ) ) { renderprogress ( 0 , ” saving blendmap . . . ” ) ; saveblendmap ( f ) ; }

Texture ∗mapshot = textureload ( picname , 3 , true , f a l s e ) ; renderbackground ( ” loading . . . ” , mapshot , mname, game : : getmapinfo ( ) ) ; setvar ( ” mapversion ” , hdr . version , true , f a l s e ) ; i f ( hdr . version g e t l i l() ; vector extras ; f−>read ( extras . pad ( e x t r a s i z e ) , e x t r a s i z e ) ; i f ( samegame ) game : : readgamedata ( extras ) ; } texmru . shrink ( 0 ) ; i f ( hdr . version read ( o l d t l , s i z e o f ( o l d t l ) ) ; l o o p i (256) texmru . add ( o l d t l [ i ] ) ; } else { ushort nummru = f−>g e t l i l() ; l o o p i (nummru) texmru . add ( f−>g e t l i l() ) ; }

541

} i f ( ebuf ) d e l e t e [ ] ebuf ; i f ( hdr . numents > MAXENTS) { conoutf (CON WARN, ” warning : map has %d e n t i t i e s ” , hdr . numents ) ; f−>seek ( ( hdr . numents−MAXENTS) ∗(samegame ? s i z e o f ( e n t i t y ) + e i n f o s i z e : e i f ) , SEEK CUR) ; } renderprogress ( 0 , ” loading s l o t s . . . ” ) ; l o a d v s l o t s ( f , hdr . numvslots ) ; renderprogress ( 0 , ” loading octree . . . ” ) ; bool f a i l e d = f a l s e ; worldroot = loadchildren ( f , i v e c ( 0 , 0 , 0) , hdr . worldsize>>1, f a i l e d ) ; i f ( f a i l e d ) conoutf (CON ERROR, ” garbage in map” ) ; renderprogress ( 0 , ” v a l i d a t i n g . . . ” ) ; v a l i d a t e c ( worldroot , hdr . worldsize>>1); i f ( ! failed ) { i f ( hdr . version >= 7) l o o p i ( hdr . lightmaps ) { renderprogress ( i / ( f l o a t ) hdr . lightmaps , ” loading lightmaps ...”) ; LightMap &lm = lightmaps . add ( ) ; i f ( hdr . version >= 17) { i n t type = f−>getchar ( ) ; lm . type = type&0x7F ; i f ( hdr . version >= 20 && type&0x80 ) { lm . u n l i t x = f−>g e t l i l() ; lm . u n l i t y = f−>g e t l i l() ; } } i f ( lm . type&LM ALPHA && ( lm . type&LM TYPE ) ! =LM BUMPMAP1) lm . bpp = 4; lm . data = new uchar [ lm . bpp∗LM PACKW∗LM PACKH ] ; f−>read ( lm . data , lm . bpp ∗ LM PACKW ∗ LM PACKH) ; lm . f i n a l i z e ( ) ; } i f ( hdr . version >= 25 && hdr .numpvs > 0) loadpvs ( f , hdr .numpvs ) ; i f ( hdr . version >= 28 && hdr . blendmap ) loadblendmap ( f , hdr . blendmap ) ; }

renderprogress ( 0 , ” loading e n t i t i e s . . . ” ) ; vector &ents = e n t i t i e s : : getents ( ) ; int einfosize = entities : : extraentinfosize ( ) ; char ∗ebuf = e i n f o s i z e > 0 ? new char [ e i n f o s i z e ] : NULL; l o o p i ( min ( hdr . numents , MAXENTS) ) { e x t e n t i t y &e = ∗e n t i t i e s : : newentity ( ) ; ents . add(&e ) ; f−>read(&e , s i z e o f ( e n t i t y ) ) ; l i l s w a p (&e . o . x , 3) ; l i l s w a p (&e . attr1 , 5) ; e . spawned = f a l s e ; e . inoctanode = f a l s e ; f i x e n t ( e , hdr . version ) ; i f ( samegame ) { i f ( e i n f o s i z e > 0) f−>read ( ebuf , e i n f o s i z e ) ; e n t i t i e s : : readent ( e , ebuf , mapversion ) ; } else { i f ( e i f > 0) f−>seek ( e i f , SEEK CUR) ; i f ( e . type>=ET GAMESPECIFIC || hdr . version getcrc ( ) ; delete f ; conoutf ( ” read map %s (%.1 f seconds ) ” , ogzname , ( SDL GetTicks ( )− l o a d i n g s t a r t ) /1000.0 f ) ; clearmainmenu ( ) ; i d e n t f l a g s |= IDF OVERRIDDEN; e x e c f i l e ( ” data/default map settings . c f g ” , f a l s e ) ; e x e c f i l e ( cfgname , f a l s e ) ; i d e n t f l a g s &= ˜IDF OVERRIDDEN; extern void fixlightmapnormals ( ) ; i f ( hdr . version p r i n t f ( ” v %d ” , i n t(−v . y ) ) ; i f ( v . z ! = f l o o r ( v . z ) ) f−>p r i n t f (”%.3 f ” , v . z ) ; e l s e f−>p r i n t f (”%d ” , int ( v . z ) ) ; i f ( v . x ! = f l o o r ( v . x ) ) f−>p r i n t f (”%.3 f\n” , v . x ) ; e l s e f−>p r i n t f (”% d\n” , i n t ( v . x ) ) ; } f−>p r i n t f (”\n ” ) ; loopv ( texcoords ) { const vec2 &t c = texcoords [ i ] ; f−>p r i n t f ( ” v t %.6 f %.6 f\n” , t c . x , 1−t c . y ) ; } f−>p r i n t f (”\n ” ) ;

COMMAND( savemap , ” s ” ) ; COMMAND( savecurrentmap , ” ” ) ; void w r i t e o b j ( char ∗name) { defformatstring ( fname ) (”%s . obj ” , name) ; stream ∗f = o p e n f i l e ( path ( fname ) , ”w” ) ; i f ( ! f ) return ; f−>p r i n t f ( ” # obj f i l e o f Cube 2 l e v e l\n\n ” ) ; defformatstring ( mtlname ) (”%s . mtl ” , name) ; path ( mtlname ) ; f−>p r i n t f ( ” m t l l i b %s\n\n” , mtlname ) ; extern vector v a l i s t ; vector v e r t s ; vector texcoords ; hashtable shareverts(1v ) ; i v e c &key = keys . add ( ) ; key . x = shareverts . access ( pos , v e r t s . length ( ) ) ; i f ( key . x == v e r t s . length ( ) ) { v e r t s . add ( pos ) ; loopl ( 3 ) { bbmin [ l ] = min ( bbmin [ l ] , pos [ l ] ) ; bbmax[ l ] = max(bbmax[ l ] , pos [ l ] ) ;

usedmtl . s o r t ( ) ; loopv ( usedmtl ) { vector &keys = mtls [ usedmtl [ i ] ] ; f−>p r i n t f ( ” g s l o t%d\n” , usedmtl [ i ] ) ; f−>p r i n t f ( ” usemtl s l o t%d\n\n” , usedmtl [ i ] ) ; f o r ( i n t i = 0; i < keys . length ( ) ; i += 3) { f−>p r i n t f ( ” f ” ) ; loopk ( 3 ) f−>p r i n t f ( ” %d/%d ” , keys [ i+2−k ] . x+1 , keys [ i+2−k ] . y +1) ; f−>p r i n t f (”\n ” ) ; } f−>p r i n t f (”\n ” ) ; } delete f ; f = o p e n f i l e ( mtlname , ”w” ) ; i f ( ! f ) return ; f−>p r i n t f ( ” # mtl f i l e o f Cube 2 l e v e l\n\n ” ) ; loopv ( usedmtl ) { VSlot &v s l o t = lookupvslot ( usedmtl [ i ] , f a l s e ) ; f−>p r i n t f ( ” newmtl s l o t%d\n” , usedmtl [ i ] ) ; f−>p r i n t f ( ” map Kd %s\n” , v s l o t . s l o t−>sts . empty ( ) ? notexture−> name : path ( makerelpath ( ” packages ” , v s l o t . s l o t−>sts [ 0 ] . name) ) ) ; f−>p r i n t f (”\n ” ) ; } delete f ; } COMMAND( writeobj , ” s ” ) ; #endif