General rambling about game development and design by a professional games developer. Pickings at Python, complaints about C, lashings of Lisp, and whatever else is the language du jour.
Sunday, April 24
Brand new Baby Bouncing Triangle
Lisp gets it right
After playing with lots of different methods of linearly interpolating the edges of triangles (well, 3 - DDA, MAI, & Bressenham) in Lisp, I settled for the straight add the gradient to the minor axis. Madness, I hear you say - not really, because Lisp doesn't represent the result of integer division as a float, but as a pair of integers that represent a ratio, so even doing it this way means I can reserve the FPU for perspective - correct texture mapping the way Intel intended. Obviously, the representation of this ratio is implementation dependent, but I'd really hope that in an implemenation with 32 bit fixnums, the ratio is expressed as a 64 bit fixed point integer..of course it's also probably possible to get LISP to use 16 bit integers for coordinates and 32 bit integers for the ratios, but I haven't got that far into the type sysetem, yet.
Friday, April 15
Lisp Software Renderer, Redux
Of course, it's impossible to leave a challenge I got so close to completing, and so I dust off the Lisp software renderer and return to the fray. I've had to grapple a bit with ASDF and learnt the hard way why upper case file names and lower case package names don't mix. Also, trying to write a tail-recursive Bressenham was interesting..only a few more bugs to wrinkle out and it can hit common-lisp.net.
Then I can go onto the freenode #lisp channel and get a critique, I hope. 123 notes at an (optimize (speed 3)) sounds like there's still a lot of work ahead..
Monday, February 21
CL-Ncurses
I'd really like to submit a patch but there doesn't seem to be any point. I wish there was some kind of policy on common-lisp.net for dealing with such orphaned projects. The same as with sourceforge, I guess.
Monday, February 14
LISP Software Renderer RIP
I gave up on my quest to write a software renderer in Lisp - I got as far as creating nice antialiased clipped lines, but was driven to the conclusion that the only sane language for such a beast is assembler...even C doesn't get quite close enough to the metal without generous helpings of inline assembler - which kind of misses the point..
I did discover that a LISP compiler with a decent static type system, vectorisation, SSE/SIMD support, and inline assembler could do just about as good a job as C. Trouble is, there's no LISP system like that in existence. There's at least two good open source Lisps that could be persuaded to do it, though..
Monday, January 31
Debug versus Release builds
The way I think of it, the difference between release and debug is the difference between trying to fly a plane with no instruments at all, and a possibly dodgy engine that won't neccessarily give you any warning that it's about to burn, versus a nice modern jet with thoroughly checked over engines and wiring, a full array of instrumentation and autopilots for everything you could possibly need.
Now - it's your backside in the plane - which do you want to fly?
Wednesday, January 26
Visual Studio and Standard Environments.
Just what is it that people see in it? Working with it as an editor is like swimming through treacle. Without the Visual Assist plugin it's auto completion is limited and without lengthy compile times, it can't manage to tag a project well. You can only really edit three languages with it, and use only the hand full of source code control systems that implement a Microsoft API. It's scripting facilities are ripe for viral attack, and poor tools for automation.
The wizards to help programmers who don't know how to do basic things ignore their ignorance, though. I'm just a little pissed off about this "standard environment" that' s been imposed on everybody at work. The a component of the last "standard environment" was the slowest and most broken source control system I've ever worked with.
When carrying 500kilo weights is the standard environment for the Olympic High Jump, then all this will make sense. I think. Standard environments might be great for managers as they reduce programmers to the lowest common interchangable denominaters, but for programmers looking for an edge and a chance to develop great software, they are a positive handicap.
Somewhere there is a cutoff point where staying within the standard environment becomes ultimately more punishing than the leap forward out of it. I just hope I can recognise that point.
Tuesday, January 25
Progress with Lisp
So far my initial enthusiasm for LISP is wearing off a bit. I can see it's practical potential, but the main upshot of my learning it, so far is that I've switched to Emacs from Vi.
Perhaps my problem is that I'm just put off by the constant fiddling needed with free Linux implementations and the unavailabilty of a good free as in beer Win32 implementation. I miss the ease of integration with a lot of useful libraries I'm used to working with in both Python & C++, as well as all the new idioms I have to absorb.
I can see the language can do things that Python only vaguely aspires to, though. I'm just not sure that in the cases when I don't need the extra grunt of Lisp that I'll be able to prototype as rapidly as I could in Python.
Tuesday, January 18
More Fun With Dungeon Generation
############ #### ## ### ##################
############ #### # ## ##################
#### ##############
### ## # #### # ##############
### ######## # ####### # ##############
######## ## ###### ##############
############# # #### ### ##############
########### # # #### ## ##############
# ########### # ### ################ ##############
# # ################ ##############
# ######### ##### ######################################
######### ## ######################################
# ######### ## # ########################################
# ########### ## ## ###################################
# # ########### ### ###################################
########## ######## ###################################
#### ###################### ###################################
##### ###################### ###################################
##### ###################### ###################################
##### ###################### ###################################
###################### ###################################
# ########################## ###################################
# ##################### ###################################
# ##################### #####################################
# ######################## #####################################
# ##### ############# #####################################
# ##### #### ###################################
# ##### #### # ######## ###################################
# ###################################
#### ##########################################
######## # ######################
# #### #### ######### ################# ######################
#### ############################# ######################
# ###### ################### ######################
# ################ ## ################### ######################
################ ## ################### ######################
################# ## ################### ######################
############## ## ################### ######################
### ############## ################### ####################
### ################ ##################### ####################
### ################ ##################### ####################
### ################ ##################### ####################
### ################ #### ############### ####################
### ################ #### ############### ####################
### ################ ## ############### ####################
### ################# ## ######### ####################
### ################# ##### ######### ##########################
### ################# ##### ##########################
### ###################### ### ### ##########################
### ###################### ### ### ##########################
# ###################### ### ##########################
######################## ### ####################
##################### ### ############ ####### ###########
## ##################### ### ############ ###########
# # ##################### ### ############ ## ## ##########
## # ##################### ############ ## ## ### ##########
## ##################### ############### ## ## ### ##########
################ ########### ## ## ### ##########
######### ### ####### ########### ## # ##########
######### ### ####### ########### ## ## ##########
######### # ####### ########### ## ## ########
######### ########## # ########## ## ## ########
######### ## # ########## ###### ## ########
######### ## ############# #################################
import Numeric
import whrandom
import math
class Burrower:
def __init__(self, x, y, lifetime = 0, dx = 0, dy = 0):
self.data = [ x, y, lifetime, dx, dy ]
def burrower_is_dead(self):
return self.data[2] == -1
def kill_burrower(self):
self.data[2] = -1
def tick_burrower(self,b):
self.data[2] = self.data[2] + 1
def pick_random_motion(self, allow_diagonals):
motion = whrandom.randrange(0,3)
if motion == 0:
dx = - 1
dy = self.data[4]
if not allow_diagonals:
dy = 0
elif motion == 1:
dx = 1
if not allow_diagonals:
dy = 0
elif motion == 2:
dy = -1
if not allow_diagonals:
dx = 0
elif motion == 3:
dy = 1
if not allow_diagonals:
dx = 0
# don't double back on yourself
if dx == -self.data[3] and dy == self.data[4]:
dy = dx
dx = self.data[4]
if dy == -self.data[4] and dx == self.data[3]:
dx = dy
dy = self.data[3]
self.data[3] = dx
self.data[4] = dy
def clip_burrower(self, minx, miny, maxx, maxy):
if self.data[0] < minx:
self.data[0] = minx
if self.data[0] > maxx:
self.data[0] = maxx
if self.data[1] < miny:
self.data[1] = miny
if self.data[1] > maxy:
self.data[1] = miny
def un_move_burrower(self, minx, miny, maxx, maxy, allow_diagonals):
self.data[0] = self.data[0] - self.data[3]
self.data[1] = self.data[1] - self.data[4]
self.clip_burrower(minx, miny, maxx, maxy)
self.pick_random_motion(allow_diagonals)
# move the burrower one cell, carving as you go along
# to do rather than randomly pick a direction, randomly *change* a direction (more control of twistiness, etc)
def move_burrower(self, minx, miny, maxx, maxy, changeprob, allow_diagonals):
if whrandom.random() < changeprob:
self.pick_random_motion(allow_diagonals)
# am i dead ? if so I should be concenrating on a nice firm rigor mortis
if self.burrower_is_dead():
return
self.data[0] = self.data[0] + self.data[3]
self.data[1] = self.data[1] + self.data[4]
if self.data[0] < minx:
self.data[0] = 0
self.data[3] = 1
if self.data[1] < miny:
self.data[1] = 0
self.data[4] = 1
if self.data[0] > maxx:
self.data[0] = maxx
self.data[3] = -1
if self.data[1] > maxy:
self.data[1] = maxy
self.data[4] = -1
return
class Dungeon:
TileIdNumber = { "granite" : 0 , "room" : 1, "corridor" : 2 }
def __init__(self, width, depth):
self.tiles = Numeric.zeros((width, depth))
self.width = width
self.depth = depth
self.room_centres = [ ]
def room_too_close(self, x, y, min_room_distance):
for r in self.room_centres:
dx = r[0] - x
dy = r[1] - y
dist = math.sqrt(x * x + y * y)
if (dist < min_room_distance):
return True
return False
def burrower_room_create(self, b, broom_prob, max_room_width, max_room_depth, min_room_distance, burrower_index):
# we have a room
if whrandom.random() > broom_prob:
return 0
room_width = whrandom.randrange(max_room_width / 2) + 1
room_depth = whrandom.randrange(max_room_depth / 2) + 1
if self.room_too_close(b.data[0], b.data[1], min_room_distance):
return 0
self.room_centres.append((b.data[0], b.data[1]))
for x in range (b.data[0] - room_width, b.data[0] + room_width):
for y in range (b.data[1] - room_depth, b.data[1] + room_depth):
if (x >= 0) and (x < self.width) and (y >= 0) and (y < self.depth):
self.tiles[x][y] = burrower_index
return 1
def burrower_birth(self,bbirth_prob):
return whrandom.random() < bbirth_prob
def baby_burrower(self, b):
return Burrower(b.data[0] + whrandom.randrange(2), b.data[1] + whrandom.randrange(2), 0, 0, 0)
def print_dungeon(self):
for y in range(0,self.depth):
s = ""
for x in range(0,self.width):
if self.tiles[x][y] == Dungeon.TileIdNumber["granite"]:
s = s + "#"
else:
s = s + " "
print s
#
# burrowers start on one side of the level, on each turn they can either split (birth prob),
# create a room (die), have a max lifetime, and the algo terminates when room_count is reached
#
def generate(self, burrower_total, room_total, birth_prob, turn_prob, room_prob, lifetime, max_room_width, max_room_depth, min_room_distance, allow_diagonals):
# set up burrowers
burrowers = []
for i in range(0,burrower_total):
side = whrandom.randrange(0,4)
if side == 0:
burrower = Burrower( 0, whrandom.randrange(0, self.depth) , 0, 1, 0 )
elif side == 1:
burrower = Burrower( self.width-1, whrandom.randrange(0, self.depth) , -1, 0, 0 )
elif side == 2:
burrower = Burrower( whrandom.randrange(0,self.width), 0, 0, 0, 1 )
else:
burrower = Burrower( whrandom.randrange(0,self.width), self.depth-1 , 0, 0, -1 )
burrowers.append(burrower)
room_count = 0
burrower_alive = True
i = 0
while burrower_alive:
print "Generation " , i
print "Rooms # ", room_count
print "Burrowers ", len(burrowers)
self.print_dungeon()
burrower_alive = False
birth_list = []
burrower_index = 1
for b in burrowers:
if not b.burrower_is_dead():
burrower_alive = True
b.move_burrower(0, 0, self.width-1, self.depth-1, turn_prob, allow_diagonals)
# if the burrower reaches another corridor, kill it
burrower_tile = self.tiles[b.data[0]][b.data[1]]
if (burrower_tile != Dungeon.TileIdNumber["granite"]) and (burrower_tile != burrower_index):
b.un_move_burrower(0,0, self.width-1, self.depth-1, allow_diagonals)
b.data[2] = b.data[2] + 1
if b.data[2] > lifetime:
b.kill_burrower()
self.tiles[b.data[0]][b.data[1]] = burrower_index
if self.burrower_birth(birth_prob):
birth_list.append(self.baby_burrower(b))
room_count = room_count + self.burrower_room_create(b, room_prob, max_room_width, max_room_depth, min_room_distance, burrower_index)
burrower_index = burrower_index + 1
if birth_list:
burrowers.extend(birth_list)
if room_count >= room_total:
birth_prob = 0.0
room_prob = 0.0
i = i + 1
return
if __name__=="__main__":
dungeon = Dungeon(64,64)
dungeon.generate(16, # burrower total
12, # room total
0.009, # birth probability
0.25, # turn probability
0.03, # room probability
2, # lifetime (max non self - hits allowwd)
9, # max_room_depth
9, # max room width
6, # min room distance
False)
Thursday, January 13
Fun With Dungeon Generation
One of the more preinnal topics of roguelike game design, or general dungeon cralws is "How do I generate that funky dungeon procedurally?". There's an unbelievable number of algorithms, but this one is my favourite...
import Numeric
import whrandom
class Burrower:
def __init__(self, x, y, lifetime = 0, dx = 0, dy = 0):
self.data = [ x, y, lifetime, dx, dy ]
def burrower_is_dead(self):
return self.data[2] == -1
def kill_burrower(self):
self.data[2] = -1
def tick_burrower(self,b):
self.data[2] = self.data[2] + 1
def pick_random_motion(self, allow_diagonals):
motion = whrandom.randrange(0,3)
if motion == 0:
self.data[3] = - 1
if not allow_diagonals:
self.data[4] = 0
elif motion == 1:
self.data[3] = 1
if not allow_diagonals:
self.data[4] = 0
elif motion == 2:
self.data[4] = -1
if not allow_diagonals:
self.data[3] = 0
elif motion == 3:
if not allow_diagonals:
self.data[3] = 0
self.data[4] = 1
# move the burrower one cell, carving as you go along
# to do rather than randomly pick a direction, randomly *change* a direction (more control of twistiness, etc)
def move_burrower(self, minx, miny, maxx, maxy, changeprob, allow_diagonals):
if whrandom.random() < changeprob:
self.pick_random_motion(allow_diagonals)
# am i dead ? if so I should be concenrating on a nice firm rigor mortis
if self.burrower_is_dead():
return
self.data[0] = self.data[0] + self.data[3]
self.data[1] = self.data[1] + self.data[4]
if self.data[0] < minx:
self.data[0] = 0
self.data[3] = 1
if self.data[1] < miny:
self.data[1] = 0
self.data[4] = 1
if self.data[0] > maxx:
self.data[0] = maxx
self.data[3] = -1
if self.data[1] > maxy:
self.data[1] = maxy
self.data[4] = -1
# up lifetime count
self.data[2] = self.data[2] + 1
return
class Dungeon:
TileIdNumber = { "granite" : 0 , "empty" : 1, "corridor" : 2 }
def __init__(self, width, depth):
self.tiles = Numeric.zeros((width, depth))
self.width = width
self.depth = depth
# die, maybe, if lifetime expired, die with a whimper,
# else die with a bang and create a room
# returns count of rooms created so we can track them
# to do : probability of generating "special" rooms?
#
def burrower_death(self, b, bdeath_prob, lifetime, max_room_width, max_room_depth):
result = 0
# if out of time, just die quietly
if b.data[2] > lifetime:
b.kill_burrower()
# if random chance of death turns up, burst into a room
if whrandom.random() < bdeath_prob:
# we have a room
room_width = whrandom.randrange(max_room_width / 2) + 1
room_depth = whrandom.randrange(max_room_depth / 2) + 1
for x in range (b.data[0] - room_width, b.data[0] + room_width):
for y in range (b.data[1] - room_depth, b.data[1] + room_depth):
if (x >= 1) and (x < self.width-1) and (y >= 1) and (y < self.depth-1):
self.tiles[x][y] = Dungeon.TileIdNumber["empty"]
# b.kill_burrower()
result = 1
return result
def burrower_birth(self,bbirth_prob):
return whrandom.random() < bbirth_prob
def baby_burrower(self, b):
return Burrower(b.data[0] + whrandom.randrange(2), b.data[1] + whrandom.randrange(2), 0, 0, 0)
def print_dungeon(self):
for y in range(0,self.depth):
s = ""
for x in range(0,self.width):
if self.tiles[x][y] == Dungeon.TileIdNumber["granite"]:
s = s + "#"
else:
s = s + " "
print s
#
# burrowers start on one side of the level, on each turn they can either split (birth prob),
# create a room (die), have a max lifetime, and the algo terminates when room_count is reached
#
def generate(self, burrower_total, room_total, birth_prob, death_prob, turn_prob, lifetime, max_room_width, max_room_depth, allow_diagonals):
# set up burrowers
burrowers = []
for i in range(0,burrower_total):
side = whrandom.randrange(0,4)
if side == 0:
burrower = Burrower( 0, whrandom.randrange(0, self.depth) , 0, 1, 0 )
elif side == 1:
burrower = Burrower( self.width-1, whrandom.randrange(0, self.depth) , -1, 0, 0 )
elif side == 2:
burrower = Burrower( whrandom.randrange(0,self.width), 0, 0, 0, 1 )
else:
burrower = Burrower( whrandom.randrange(0,self.width), self.depth-1 , 0, 0, -1 )
burrowers.append(burrower)
room_count = 0
burrower_alive = True
i = 0
while burrower_alive:
print "Generation " , i
print "Rooms # ", room_count
self.print_dungeon()
burrower_alive = False
birth_list = []
for b in burrowers:
if not b.burrower_is_dead():
burrower_alive = True
b.move_burrower(0, 0, self.width-1, self.depth-1, turn_prob, allow_diagonals)
if (self.tiles[b.data[0]][b.data[1]] == Dungeon.TileIdNumber["corridor"]):
b.kill_burrower()
self.tiles[b.data[0]][b.data[1]] = Dungeon.TileIdNumber["corridor"]
if self.burrower_birth(birth_prob):
birth_list.append(self.baby_burrower(b))
room_count = room_count + self.burrower_death(b, death_prob, lifetime, max_room_width, max_room_depth)
if birth_list:
burrowers.extend(birth_list)
if room_count == room_total:
birth_prob = 0
death_prob = 0
return
if __name__=="__main__":
dungeon = Dungeon(64,32)
dungeon.generate(16, 32, 0.01, 0.005, 0.05, 1024, 16, 16, False)
Wednesday, October 27
UML, OO models and Abstraction
C takes you one layer from that - it gives you functions, typing and normal maths notation, but you are still working with raw lumps of memory (structs) and there's a pretty much one to one mapping between expressions and machine instructions - the size assembler output is going to be roughly proportional to source input.
"C with classes" C++ gives you some more layers - You can take a hunk of raw memory and associate it with a set of defined operations and acess it through those operations. You can do this in the above lanuages, too with sufficently disciplined programming, but C++ gives you encapsulation and operator overloading to make it more syntatically "nice". You still have to worry about the consequences of allocating things in memory and where they live - stack, heap, static..
"Polymorphic/OO C++" gives you other tools: the ability to pretend one set of objects are all the same thing - polymorphism, virtual functions, interfaces, inheritance. This saves a lot of repeat coding and lets you abstract away implementation details - for example a car, a character, a prop are all "objects in the world" and can be manipulated via a common "objects in the world" interface : no specific per - case coding.
This leads to..
"Template/Boost/STL C++" gives you the ability to do something like polymorphism at compile time - templates that take parameters, which leads to a lot of compile-time optimisations, and for the first time in the C++ learing curve, code which outperforms C (if written *properly*). Also, the STL takes the level of abstraction offered by the previous level one step further, seperating data and algorthims - on the one hand there's a set of containers: list, vector, map; and on the other hand a set of sorting, searching algorithims that work for every container and don't have
to be specialised for particular containers. STL has a weakness that's inherent to C++ - it has to manage memory allocation and does so nearly exclusively on the heap, although there is a mechanism to abstract away the allocations (an allocator object) and make the code independent of memory-management mechanisms (ie, not care wether the container is on the heap, static, or on the stack), it's flawed..(or at least so it's claimed by people who should know).
Python gives you a slightly different set of abstractions that do the same thing as C++: it doesn't do static typing, pushes function call and object resolution to run time, so if you use object.method() as long as your object implements method() it won't care what object it is. Also it frees you from the problem of allocating/deallocating raw memory, which C++ doesn't. It's
implemented to be easily interfaceable to C++/C and read like "executable pseudo code". It's therefore an ideal prototyping langugage.
Lisp takes a completely different apporach to providing these abstractions, one that is still boiling my brain. Rather than trying to provide abstrations that decouple code and data, code *is* data - a function is just a list of instructions, manipulable by list manipulating primitives, assignable to variables, etc. I suspect it to be ideal for genetic programming and self-modifying structures at a high level of abstraction thus it's niche in AI and 'hard' research problems. The nice thing about this apporoach is that the upshot is - like C++ you degree of abstraction is entirely up to you - you can go with typless programming ala Python, or you can declare your variables typed, and get full on C-type optimisation. You
can construct code in lists at compile time that have the same effect as template expansion. I haven't got into the object system, yet..but I fully expect to find it already doing things the ways it took C/C++ the last 20 years to discover.
Finally: I'm saying that the mechanism that the language uses to provide abstraction away from the machine hardware has consequcnces and it's inescapable, and it may also close off other possible abstractions
Where does UML fit into this hierarchy? It's another step up, isn't it, away from the machine? The fact it's OO oriented troubles me though. LISP, Python, C++ all have differences in the way the implement OO - I can't see UML being free of assumptions about how OO is implemented.
C++ has private members, Python makes everything public, LISP allows you to decide the precedence of multiply inherited classes on a case-by-case basis, Java doesn't do multiple inheritance it's like the old days when everyone had their own BASIC - everyone has their own OO model.
Wotta pain. Ouch!
Tuesday, September 14
Things I'd do if I had the time.
fly over implausibly coloured and pulsating landscapes to feed from
spontaneously erupting particle fountains.
Monday, September 13
Software renderer in Lisp
I'm seized by the insane urge to write a software renderer in Lisp. I don't know where this came from: actually, I think I do - fond memories of Apple II shape tables and the disire to test the lisperati's claim that it's performace is parallel to that of C, and also a desire to find out exactly what high - performance lisp is like.
I've alread created an sbcl binding to the PTC. I really must stick it into a CVS repository somewhere - it may be useful for someone else.
Sunday, September 5
Building Graphics3D-CPP with Scons
G3d-cpp is an ideal (IMHO) library for working with when constructing 3d testbeds or demos: it doesn't aim to be an over-arching SDK that handles every last detail of OS/Game interaction (it delegates most of those tasks to the SDL), and it uses C++ very effectively in abstracting the render device, windows, geometry and all the things you normally have to deal with in finicky detail in C. If I have to deal with Bjarnes monster, this is the arena I'd chose to fight in :)
Here is the SConstruct. At present I'm supporting release build only: and my architecture optimisation flags are set for up an AMD64 and the resulting binary may not be happy on anything less than a P4/SSE2 capable - chip. It also assumes we are linking with a version of SDL & zlib built by the scripts in the two blog entries below.
#
# Sconstruct file for G3D
#
import sys
#
# microsoft tool kit environment
#
env = Environment(tools = [ 'mstoolkit' ])
env['CPPPATH'] = [ "#\\include\\", "#\\include\\G3D", "#\\include\\GLG3D", "#\\include\\glh", "#\\include\\zlib", "..\\SDL-1.2.7\\include" ]
env['LIBPATH'] = [ "#\\win32-lib", "..\\SDL-1.2.7", '..\\zlib' ]
env['LIBS'] = [ "kernel32.lib", "user32.lib", "gdi32.lib", "winspool.lib", "comdlg32.lib", "advapi32.lib", "shell32.lib", "ole32.lib", "oleaut32.lib", "uuid.lib", "winmm.lib", "opengl32.lib", "glu32.lib", "ddraw.lib", "dinput.lib", "dxguid.lib", "zlib.lib", "version.lib" ]
#if project_config == 'debug':
# env['LIBS'] = env['LIBS'] + [ "SDLd.lib", "SDLmaind.lib" ]
# program_name = [ "#\\demos\\GLG3D_DemoD.exe" ]
# library_name = [ "#\\win32-lib\GLG3D-debug.lib" ]
# graphics_library_name = [ "#\\win32-lib\G3D-debug.lib" ]
# env['CPPDEFINES'] = [ '_DEBUG', 'WIN32', '_WINDOWS', 'ENABLE_WINDIB', 'ENABLE_DIRECTX', '_WIN32_WINNT=0x0400', '_LIB' ]
# env['CCFLAGS'] = [ '/nologo', '/W3', '/Zi', '/Od', '/MTd' ]
# env['LINKFLAGS'] = [ '/subsystem:windows', '/machine:x86' ]
#if project_config == 'release':
# env['LIBS'] = env['LIBS'] + [ 'SDL.lib', 'SDLmain.lib' ] # [ "G3D", "GLG3D", "SDL.lib", "SDLmain.lib" ]
program_name = [ "#\demos\\GLG3D_Demo.exe" ]
library_name = [ "#\\win32-lib\GLG3D.lib" ]
graphics_library_name = [ "#\\win32-lib\G3D.lib" ]
jpeg_library_name = [ "#\\win32-lib\libjpeg.lib" ]
env['CPPDEFINES'] = [ 'NDEBUG', 'WIN32', '_WINDOWS', 'ENABLE_WINDIB', 'ENABLE_DIRECTX', '_WIN32_WINNT=0x0400' ]
env['CCFLAGS'] = [ '/nologo', '/W3', '/GF', '/G7', '/Ox', '/arch:SSE2', '/Gy', '/EHsc', '/MT' ]
env['LINKFLAGS'] = [ '/subsystem:windows', '/machine:x86' ]
#
# library
#
env.StaticLibrary( library_name, [
r".\GLG3Dcpp\Draw.cpp",
r".\GLG3Dcpp\edgeFeatures.cpp",
r".\GLG3Dcpp\G3DGameUnits.cpp",
r".\GLG3Dcpp\GApp.cpp",
r".\GLG3Dcpp\getOpenGLState.cpp",
r".\GLG3Dcpp\GFont.cpp",
r".\GLG3Dcpp\glcalls.cpp",
r".\GLG3Dcpp\GLCaps.cpp",
r".\GLG3Dcpp\glenumtostring.cpp",
r".\GLG3Dcpp\GPUProgram.cpp",
r".\GLG3Dcpp\IFSModel.cpp",
r".\GLG3Dcpp\LightingParameters.cpp",
r".\GLG3Dcpp\ManualCameraController.cpp",
r".\GLG3Dcpp\MD2Model.cpp",
r".\GLG3Dcpp\MD2Model_load.cpp",
r".\GLG3Dcpp\Milestone.cpp",
r".\GLG3Dcpp\PixelProgram.cpp",
r".\GLG3Dcpp\PosedModel.cpp",
r".\GLG3Dcpp\RenderDevice.cpp",
r".\GLG3Dcpp\SDLWindow.cpp",
r".\GLG3Dcpp\Shader.cpp",
r".\GLG3Dcpp\shadowVolume.cpp",
r".\GLG3Dcpp\Sky.cpp",
r".\GLG3Dcpp\tesselate.cpp",
r".\GLG3Dcpp\Texture.cpp",
r".\GLG3Dcpp\TextureFormat.cpp",
r".\GLG3Dcpp\TextureManager.cpp",
r".\GLG3Dcpp\UserInput.cpp",
r".\GLG3Dcpp\VAR.cpp",
r".\GLG3Dcpp\VARSystem.cpp"
])
env.StaticLibrary( graphics_library_name, [
r".\G3Dcpp\AABox.cpp",
r".\G3Dcpp\BinaryInput.cpp",
r".\G3Dcpp\BinaryOutput.cpp",
r".\G3Dcpp\Box.cpp",
r".\G3Dcpp\Capsule.cpp",
r".\G3Dcpp\CollisionDetection.cpp",
r".\G3Dcpp\Color3.cpp",
r".\G3Dcpp\Color3uint8.cpp",
r".\G3Dcpp\Color4.cpp",
r".\G3Dcpp\Color4uint8.cpp",
r".\G3Dcpp\Cone.cpp",
r".\G3Dcpp\ConvexPolyhedron.cpp",
r".\G3Dcpp\CoordinateFrame.cpp",
r".\G3Dcpp\debugAssert.cpp",
r".\G3Dcpp\Discovery.cpp",
r".\G3Dcpp\fileutils.cpp",
r".\G3Dcpp\format.cpp",
r".\G3Dcpp\g3derror.cpp",
r".\G3Dcpp\g3dmath.cpp",
r".\G3Dcpp\GCamera.cpp",
r".\G3Dcpp\GImage.cpp",
r".\G3Dcpp\GLight.cpp",
r".\G3Dcpp\license.cpp",
r".\G3Dcpp\Line.cpp",
r".\G3Dcpp\LineSegment.cpp",
r".\G3Dcpp\Log.cpp",
r".\G3Dcpp\Matrix3.cpp",
r".\G3Dcpp\Matrix4.cpp",
r".\G3Dcpp\MeshAlg.cpp",
r".\G3Dcpp\MeshAlgAdjacency.cpp",
r".\G3Dcpp\MeshAlgWeld.cpp",
r".\G3Dcpp\NetworkDevice.cpp",
r".\G3Dcpp\PhysicsFrame.cpp",
r".\G3Dcpp\Plane.cpp",
r".\G3Dcpp\prompt.cpp",
r".\G3Dcpp\Quat.cpp",
r".\G3Dcpp\Ray.cpp",
r".\G3Dcpp\Sphere.cpp",
r".\G3Dcpp\stringutils.cpp",
r".\G3Dcpp\System.cpp",
r".\G3Dcpp\TextInput.cpp",
r".\G3Dcpp\TextOutput.cpp",
r".\G3Dcpp\Triangle.cpp",
r".\G3Dcpp\Vector2.cpp",
r".\G3Dcpp\Vector2int16.cpp",
r".\G3Dcpp\Vector3.cpp",
r".\G3Dcpp\Vector3int16.cpp",
r".\G3Dcpp\Vector4.cpp"
])
env.StaticLibrary(jpeg_library_name,
[
r".\IJG\jcapimin.c",
r".\IJG\jcapistd.c",
r".\IJG\jccoefct.c",
r".\IJG\jccolor.c",
r".\IJG\jcdctmgr.c",
r".\IJG\jchuff.c",
r".\IJG\jcinit.c",
r".\IJG\jcmainct.c",
r".\IJG\jcmarker.c",
r".\IJG\jcmaster.c",
r".\IJG\jcomapi.c",
r".\IJG\jcparam.c",
r".\IJG\jcphuff.c",
r".\IJG\jcprepct.c",
r".\IJG\jcsample.c",
r".\IJG\jctrans.c",
r".\IJG\jdapimin.c",
r".\IJG\jdapistd.c",
r".\IJG\jdatadst.c",
r".\IJG\jdatasrc.c",
r".\IJG\jdcoefct.c",
r".\IJG\jdcolor.c",
r".\IJG\jddctmgr.c",
r".\IJG\jdhuff.c",
r".\IJG\jdinput.c",
r".\IJG\jdmainct.c",
r".\IJG\jdmarker.c",
r".\IJG\jdmaster.c",
r".\IJG\jdmerge.c",
r".\IJG\jdphuff.c",
r".\IJG\jdpostct.c",
r".\IJG\jdsample.c",
r".\IJG\jdtrans.c",
r".\IJG\jerror.c",
r".\IJG\jfdctflt.c",
r".\IJG\jfdctfst.c",
r".\IJG\jfdctint.c",
r".\IJG\jidctflt.c",
r".\IJG\jidctfst.c",
r".\IJG\jidctint.c",
r".\IJG\jidctred.c",
r".\IJG\jmemansi.c",
r".\IJG\jmemmgr.c",
r".\IJG\jquant1.c",
r".\IJG\jquant2.c",
r".\IJG\jutils.c"
])
env['LIBS'] = env['LIBS'] + [ "GLG3D.lib", "G3D.lib", "libjpeg.lib", "SDL.lib", "SDL_main.lib" ]
# demos
env.Program( program_name, [ r'#\demos\GLG3D_Demo\main.cpp' ] )
Building the SDL with SCons
Note that I'm building static libraries - this is because the toolkit doesn't support the DLL versions of the C/C++ runtime libraries.
#
# Sconstruct file for SDL-1.2.7.
#
import sys
#
# microsoft tool kit environment
#
env = Environment(tools = [ 'mstoolkit' ])
#
# debug or release
#
project_config = ARGUMENTS.get('config', 'debug')
project_stdio = ARGUMENTS.get('stdio', 'yes' )
if not project_config in [ 'debug', 'release' ]:
print 'Invalid configuration - should be release or debug'
sys.exit(1)
if not project_stdio in [ 'yes', 'no' ]:
print 'Invalid stdio - must be yes or no'
sys.exit(1)
if project_config == 'debug':
library_names = [ "SDLd.lib", 'SDL_maind.lib' ]
env['CPPDEFINES'] = [ '_DEBUG', 'WIN32', '_WINDOWS', 'ENABLE_WINDIB', 'ENABLE_DIRECTX', '_WIN32_WINNT=0x0400' ]
env['CCFLAGS'] = [ '/nologo', '/W3', '/Zi', '/Od', '/MTd' ]
project_stdio = 'no'
if project_config == 'release':
library_names = [ "SDL.lib", 'SDL_main.lib' ]
env['CPPDEFINES'] = [ 'NDEBUG', 'WIN32', '_WINDOWS', 'ENABLE_WINDIB', 'ENABLE_DIRECTX', '_WIN32_WINNT=0x0400' ]
if project_stdio == 'no':
env['CPPDEFINES'] = env['CPPDEFINES'] + [ 'NO_STDIO_REDIRECT' ]
env['CCFLAGS'] = [ '/nologo', '/W3', '/GF', '/MT', '/G7', '/Ox', '/arch:SSE2', '/Gy' ]
env['CPPPATH'] = [ r'#\src', r'#\src\audio', r'#\src\video', r'src\video\wincommon', r'#\src\video\windx5',
r'#\src\events', r'#\src\joystick', r'#\src\cdrom', r'#\src\thread', r'#\src\thread\win32',
r'#\src\timer', r'#\include', r'#\include\SDL']
env.StaticLibrary( library_names[0], [
r"#\Src\SDL.c",
r"#\Src\Video\SDL_RLEaccel.c",
r"#\Src\Events\SDL_active.c",
r"#\Src\Audio\SDL_audio.c",
r"#\Src\Audio\SDL_audiocvt.c",
r"#\Src\Audio\SDL_audiomem.c",
r"#\Src\Video\SDL_blit.c",
r"#\Src\Video\SDL_blit_0.c",
r"#\Src\Video\SDL_blit_1.c",
r"#\Src\Video\SDL_blit_A.c",
r"#\Src\Video\SDL_blit_N.c",
r"#\Src\Video\SDL_bmp.c",
r"#\Src\Cdrom\SDL_cdrom.c",
r"#\Src\Cpuinfo\SDL_cpuinfo.c",
r"#\Src\Video\SDL_cursor.c",
r"#\src\audio\windib\SDL_dibaudio.c",
r"#\Src\Video\Windib\SDL_dibevents.c",
r"#\Src\Video\Windib\SDL_dibvideo.c",
r"#\src\audio\windx5\SDL_dx5audio.c",
r"#\Src\Video\Windx5\SDL_dx5events.c",
r"#\Src\Video\Windx5\SDL_dx5video.c",
r"#\src\video\windx5\SDL_dx5yuv.c",
r"#\Src\Endian\SDL_endian.c",
r"#\Src\SDL_error.c",
r"#\Src\Events\SDL_events.c",
r"#\Src\Events\SDL_expose.c",
r"#\Src\SDL_fatal.c",
r"#\src\video\SDL_gamma.c",
r"#\src\joystick\SDL_joystick.c",
r"#\Src\Events\SDL_keyboard.c",
r"#\Src\Audio\SDL_mixer.c",
r"#\src\joystick\win32\SDL_mmjoystick.c",
r"#\Src\Events\SDL_mouse.c",
r"#\Src\Video\SDL_pixels.c",
r"#\Src\Events\SDL_quit.c",
r"#\src\events\SDL_resize.c",
r"#\Src\File\SDL_rwops.c",
r"#\Src\Video\SDL_surface.c",
r"#\Src\Cdrom\Win32\SDL_syscdrom.c",
r"#\src\thread\generic\SDL_syscond.c",
r"#\Src\Video\wincommon\SDL_sysevents.c",
r"#\Src\Video\wincommon\SDL_sysmouse.c",
r"#\src\thread\win32\SDL_sysmutex.c",
r"#\src\thread\win32\SDL_syssem.c",
r"#\Src\Thread\Win32\SDL_systhread.c",
r"#\src\timer\win32\SDL_systimer.c",
r"#\Src\Video\wincommon\SDL_syswm.c",
r"#\Src\Thread\SDL_thread.c",
r"#\src\timer\SDL_timer.c",
r"#\Src\Video\SDL_video.c",
r"#\Src\Audio\SDL_wave.c",
r"#\src\video\wincommon\SDL_wingl.c",
r"#\src\video\SDL_yuv.c",
r"#\src\video\SDL_yuv_sw.c",
r"#\src\video\SDL_stretch.c" ] )
env.StaticLibrary(library_names[1], [ r"#\src\Main\Win32\SDL_win32_main.c" ])
Building Zlib With Scons
#
# Sconstruct file for SDL-1.2.7.
#
import sys
#cd
# microsoft tool kit environment
#
env = Environment(tools = [ 'mstoolkit' ])
#
# debug or release
#
project_config = ARGUMENTS.get('config', 'release')
if not project_config in [ 'debug', 'release' ]:
print 'Invalid configuration - should be release or debug'
sys.exit(1)
if project_config == 'debug':
library_name = [ "zlibd.lib" ]
env['CPPDEFINES'] = [ '_DEBUG', 'WIN32', '_WINDOWS', 'ENABLE_WINDIB', 'ENABLE_DIRECTX', '_WIN32_WINNT=0x0400' ]
env['CCFLAGS'] = [ '/nologo', '/W3', '/Zi', '/Od', '/MTd' ]
if project_config == 'release':
library_name = [ "zlib.lib" ]
env['CPPDEFINES'] = [ 'NDEBUG', 'WIN32', '_WINDOWS', 'ENABLE_WINDIB', 'ENABLE_DIRECTX', '_WIN32_WINNT=0x0400' ]
env['CCFLAGS'] = [ '/nologo', '/W3', '/GF', '/MT', '/G7', '/Ox', '/arch:SSE2', '/Gy' ]
env['CPPPATH'] = [ r'#' ]
env.StaticLibrary( library_name, [ "adler32.c", "compress.c", "crc32.c", "deflate.c", "gzio.c", "infback.c", "inffast.c", "inflate.c", "inftrees.c", "trees.c", "uncompr.c", "zutil.c" ])
Cut price Win32 Graphics Coding.
Ever since Microsoft released their VC/C++ 2003 compiler command line tools free (as in beer) I've been using it for my coding at home. I've built a number of ibraries with it on the Win32 system including g3d-cpp and the SDL. What follows is a quick HOWTO.
First, catch your hare. You will need the command line compiler itself. You will then need the SDKS that enable coding on Win32: The Platform SDK and the Direct X SDK. You could also get the .NET Framework while you were are at it, but I haven't used with any of the graphics libraries that I build.
Secondly, you will need the tools that you are going to build the libraries with Python (if you don't already have it) - any recent version will do and Scons via this link 0.96.1 . Scons is "a better make" - it's based on a classic perl build call Cons and builds just about anything on any platform with any tool. The developers have paid great attention to maintaining "build correctess: it pre-computes all the dependencies before building and rebuilds only exactly what needs to be build. In addition the build scripts it uses are signinficantly easier to use than make and it supports parallel building out of the box. It's gradually becoming the build tool of choice for a lot of projects."
Unzip the scons zip into an arbitary directory - we will call it %SCONS%. Scons doesn't support the vc toolkit "out of the box" just yet, but I've written an Scons tool script to support it which you need to copy to your %SCONS%\scons-0.96.1\engine\SCons\tool directory in the fresh Scons you just unzipped. Go to %SCONS%\scons-0.96.1 and type "python setup.py install" and it will all be installed. You should be all set for vc toolkit development with SCons.
Tuesday, July 27
Game Du Jour
TrackBalls: a Marble Madness clone for Win32 and Linux (and from what I see, could easily port to Mac OS X). Marble Madness was one of my favourtie arcade games ever, so I expect this one to result in quite a bit of lost productivity..
Monday, July 26
BlogTidy - HTMLTidy for Blogger
I've been spending my time knocking together a version of HTML-Tidy that understands Blogger block tags. It's an out and out hack, but it seems to work ok, for all it's potential template mangling properties that might be a consequence of redefining the blogger tags to be part of the table content model.
If you are feeling brave, the sources live here and the binaries live here. Note that you will need SCons to build it, although modifying the SConstruct script to your taste should be trivial.
Sunday, July 11
Language Comparisons
I found it interesting that the LISP forced me to decompose the code into much smaller functions. You can see just how radically *different* LISP is, and you can see the parallels between Python and C that make the two languages such a close fit, including the fact that Python delegates much of the performance - related work to C...I'll add Java, C++ with the next post; but it does highlight the fact that LISP is a completely different approach to the process of compiling and composing programs when compared to block - structure of languages like Java, C and Pascal. I'm not sure that this is the best illustrative code possible. I think something animating a set of different but similar objects - boids, would be the best test case to see if a language is "game-worthy"..does anyone have any more suggestions?
Incidentally, in the course of preparing this post, I discovered the insanely handy TOhtml vim plugin for tranforming source code to HTML. I wonder if Emacs has anything similar?