Personal
HomeForumsGalleryJournalsControl Panel
Features
Community
Latest Journals
14 Jan 2010, 07:06:48 PM
25 Nov 2009, 07:37:03 AM
15 Nov 2009, 05:31:09 AM
29 Sep 2009, 08:04:53 AM
21 Sep 2009, 09:06:20 PM
31 Jul 2009, 11:26:33 PM
01 Jul 2009, 03:09:58 PM
21 Jun 2009, 12:16:30 PM
The hub for the promotion and advancement of african game development in South Africa and Africa.
Guest
Nitrogen - Delphi C++ and OpenGL Programming
Korax : Dead Man Walking
Ultimate Quake EngineFriday, May 22, 2009
Korax
Ultimate Quake Engine(8 entries, 22 May 2009
Game Development(2 entries, 27 January 2009
SAGD Technologies(5 entries, 07 July 2008
Excentrax Games(1 entry, 11 July 2007
First PagePrevious Page journal post 1 to 8 of 8, page 1 of 1, 10 journal posts per page Page 1Next PageLast Page
Table of Contents
1. Quake2 BSP Rendering22 May 2009 
2. UQE: uqeModels21 November 2008 
3. UQE: uqeMaps15 October 2008 
4. UQE: The FileSystem12 September 2008 
5. UQE: The Console13 June 2008 
6. UQE: The Skeleton19 May 2008 
7. Living it up!02 January 2008 
8. Emulating Quake18 December 2007 
Quake2 BSP Rendering
Posted : 04:50:01 PMFriday, May 22, 2009
base1.bsp
The past few months I've been working on/off on a project that loads and renders Quake2 (idTech2) BSP files using the .NET language called C# for the program logic and XNA for input and rendering purposes. Initially the idea was to use XNA and build from the ground up game technology to run our titles on using technologies I'm familiar with in the form of the idTech1 and idTech2 engines. Initially the idea was to try and swat two flies at once by developing tech that could carry our game titles on as well as have a clean replacement for our "Unofficial Quake Engine" (UQE) project.

This is the utopia, but it is not possible to accomplish this in a decent timeframe.
On the one hand you want to build technology that is competative regarding capabilities to other engines or renderers out there, but on the other hand you want to remain faithful to what the technology should be able to do regarding the UQE project. Ultimately its impossible to build a single technology that looks into both directions without having a dated or bloated design. We decided the best was to go, as Excentrax Games, is to utilize XNA for simpler arcade-like titles and for larger titles license a commercial engine like Torque3D and the likes.

So, why are we still working on the Quake2 BSP Renderer?
Since we spent so much time getting it as far as it is, we decided to finish-up the project and release the source code using the GPL license to help other like-minded people to gain access to the source code and hopefully learn from it. Its a much better proposition then stuffing it into our archives to gather dust! There is also another twist to the story; The project turned out to be an experimental renderer for our efforts over at our "idTech" community project, UQE. We are still looking at writing a .NET/XNA version of the idTech2 engine making as much as possible use of XNA to faithfully render the Quake2 game. We will also be looking at building loading and translation paths for Quake1 and Hexen2 at a later stage should the Quake2 project be successful.

ware1.bsp
The UQE project is a very personal project of mine and with it I'm sharing my passion for the idTech engines with both gamers and game developers interested in the same subject matter. UQE is not one of our primary game development objectives and its development is being managed as time permits us to work on it. The UQE project is great for expanding our knowledge on XNA and helping us build and use game subsystems we can re-use for our XNA targetted projects. It also stands as proof for anyone out there that game development and its related technologies is completely possible in Africa and in showing she is not as dark and illiterate as the media makes her out to be. As far as my research stretches at the time of this writing, Excentrax Games have developed THE most complete XNA-based idTech2 BSP renderer out there. Yet, sadly, our renderer is not complete, it still lacks entity (MD2 and submodels) rendering as well as rendering static lightmap styles.

Lets get right into the technologies the renderer consists of and what you could learn from it...
When the renderer starts-up it loads the BSP level specified in its config.xml file. If this file doesn't exist, it creates one with some default values. The configuration system implemented is a very quick and dirty one just to allow you to change a few variables that would otherwise be hardcoded.

The very first thing Q2BSP does is either load the assets directly from the disk as single files or it loads the assets from the PAK virtual file system. The code managing the PAK file is very basic and just manages the loading from a specific PAK file and is not a fully fledged virtual file system... yet. Even though optionally loading from a .PAK file is great, I think its not the most optimal solution. For a custom virtual file system to work you must be able to stream any data from it for use, but I found it difficult to near impossible to stream a wave file into a form that can be played back by XNA.

I did some reading up on the .XNB format, and it seems it can store multiple files and the XNA API can easily access that file to read data from it. Since we don't want to re-invent a feature that seems to be present, the best thing most likely to do is to build a little conversion tool that can read PAK data and compile XNB data from it, then use the XNB as storage mechanism rather than PAK files.

jail4.bsp
For this project you'll be able to load any BSP level contained inside a PAK. We have tested it with the pak0.pak file of Quake2 full version, but it should work fine with the demo and other custom PAK files. The only requirement for the loading from a PAK is that ALL assets that has to be loaded needs to be in that very same file, or everything must be external files if the PAK loading option is disabled. Thats how basic the PAK implementation for Q2BSP is.

With the BSP file loaded the renderer is ready to render the world.
The whole world is broken up into surfaces. A surface is a set of vertices that defines a piece of flat geometry. A surface also has a fixed maximum dimensional size limit to easily be broken up for rendering by the PVS and if also an effect of BSP. This means a single flat large wall will be made up by several pieces of surfaces. A surface can have several primitives.

Because of BSP partitioning a leaf can have several surfaces attached to it that forms a piece of geometry. A leaf could for example be that complete large wall, or just a chunck of it. Next we get something we call clusters, or to put it in easier terms, leaf clusters.
A cluster is simply a set of possibly visible leafs. So what happens is when you have a specific position in the world, BSP traversing is used to determine which cluster you are in. Once we know the cluster number (index) we look up the PVS and get a list of leaves visible for that cluster. This set of leaves gets drawn to the screen.

If you want to visualize this in your mind's eye, you can imagine a cluster being a volume of space could be big or small, depending on the geometry, you could move you camera in without the geometry PVS changing. Once you move from one cluster to a neigbouring cluster the PVS needs to be re-checked and a new set of leaves needs to be drawn. What is notable is that clusters that are closer together share relatively the same set of leaves. Its very possible that 5 neigbouring clusters would share 70% of the same leaves, depending on how the world is constructed.

boss1.bsp
One of the performance drawbacks that still remains to be solved is to optimally sort the number of surfaces in the current cluster in such a way that we could make use of Index Buffers and execute draw calls using the DrawIndexedPrimitives() function rather than using the DrawPrimitives() function that we currently use. DrawPrimitives() gets called one for each surface that is visible in the current cluster, but if we could use DrawIndexedPrimitives() we could greatly reduce the number of drawing calls that we need to issue. We are still looking at a few scenarios to see where the use of DrawIndexedPrimitives() works well and where it breaks functionality.

The first priority is to sort surface rendering by texture (texinfo) then from there build a triangle list based index buffer every time the camera changes PVS cluster. This will change the scene rendering from per-surface DrawPrimitives() to per-texture DrawIndexedPrimitives(). I'm not sure if it will be jittery if the camera move from one cluster to the next. The only way to know is to code it and find out. Maybe changing the code from DrawPrimitives() to DrawIndexedPrimitives() frees the CPU enough that you don't notice. For this release the renderer will be rendering the scene primarily using DrawPrimitives() until at a later stage we update the code for DrawIndexPrimitives() rendering.

The BSP the renderer traverse the BSP nodes and mark surfaces for rendering in two distinct groups namely "solid" surfaces and "translucent/warped" surfaces. The reasoning behind this is to render the world in two phases, phase 1 renders all the solid opague surfaces in the world, then phase 2 renders all the translucent and warped surfaces. With the solid surface rendering phase it does not really matter if we re-sort the list of surfaces according to texture (texinfo) and not according to its original depth sort order. Most of the surfaces are solid surfaces, and sorting according to texture greatly improves rendering performance. We are not so lucky with translucent surfaces, because we need to render them in the order we get them from the BSP/VIS to make sure we don't introduce rendering anomalies because of the depth and rendering order.

jail5.bsp
Surfaces with the "warp" flag set gets broken into 64-unit sized sets of sub-surfaces which we call "polygons" within the source code. The reason why warped surfaces gets subdivided is to generate more vertices for the original warped surface to make the ST texture coordinate warping effect possible. This possible large number of primitives using the same texinfo index are being rendered by a call to DrawIndexedPrimitives().

Classic Quake2 lightmapping have also been implemented using multitexturing. In the pixel shader the texture pixels gets multiplied with the lightmap pixels to produce the final pixel for output. We have not yet implemented static lightmap styles, like strobing lights, flickering lights and the likes. There are two types of lightmaps implemented in idTech1, idTech2 and idTech3. They are called "static" lightmaps and "dynamic" lightmaps.

The difference is static lightmaps are pre-processed lightmaps that are fixed and also could have a "style" pre-processed with it, like flickers and strobes and so forth. Then you get "dynamic" lightmaps which gets calculated on-the-fly and gets temporarily blended with the original "static" lightmaps. To give a quick example is when you fire a rocket, it generates a pointlight that lights up the area as it passes through the air and explodes against an object, that light it generates on-the-fly (no pun intended ) is a dynamic lightmap.

This dynamic lightmapping looks pretty ugly, especially with idTech1 and idTech2's low resolution lightmaps. We can remedy this by not implementing all the software routines needed to generate and blend this dynamic lightmap by generating dynamic per-pixel lights straight on the GPU... afterall, we already have access to both the texture pixels as well as the lightmap pixels. In the renderer you can switch on/off a per-pixel pointlight that is attached to your camera position. What this pointlight in reality does is it "eats" the color value of only the static lightmap pixel at the pixel shader level and returns a modified static lightmap pixel, which we then multiply with the texture pixel to get our final pixel output result. The calculations isn't 100% perfect yet.

Just for the fun of it we implemented two types of lighting; Classic lightmapped multitexturing and actual per-pixel hardware lighting. The BSP levels are not completely compatible and designed for per-pixel hardware lighting, but its cool to see it sort-of working. Theres a section of code that I left commented that anyone can uncomment which will add per-pixel pointlights for every pointlight entity present in any given BSP level loaded.

jail3.bsp
The focus of the project was to get good Quake2 BSP rendering up and running with decent performance, not focussing on things like error-checking, good object-orientated design and great performance. When decompressing the archive you'll find a pre-built binary in "release" mode as well as the GPL source code as it stands now at its current state. Included with the binary is a "config.xml" configuration file with a few changeable settings. The build is configured to search and load from PAK0.PAK the "base1.bsp" level. All that needs to be done is to copy the PAK0.PAK of the Quake2 demo or commercial version into the "baseq2" folder located within the "contents" folder.

When Q2BSP starts up it generates a file called "config.txt" which contains a list of the names of all the BSP files thats part of the commercial version of Quake2. This is done to help anyone that doesn't have a PAK reader tool at hand to have a list of BSP files that could be tried out. It is also possible to load some of the deathmatch levels if the commercial version is sufficiently patched, although a PAK editing tool will be needed because the data need to be either merged into a single PAK, or everything needs to be unpacked. This needs to be done since the Q2BSP renderer doesn't have a fully fledged Virtual File System.

Some other features available in the Q2BSP renderer is the ability to switch between solid and wireframe fill modes. The PVS can also be locked to help developers see where the PVS ends from a selected area. Also featuring is the ability is switch the bloom post-process effect on/off. The bloom post-process effect itself is slightly over-emphasized in this experimental renderer, but it demonstrates what the effect can do to make this classic game media slightly (ever so slightly) more current or at least in the right direction.

Lets get to the requirements to be able to execute the Q2BSP renderer successfully:
Microsoft .NET Framework 3.5 Service Pack 1 (or higher)
Microsoft XNA Framework Redistributable 3.0 (or higher)

Quake2 Demo
ftp://ftp.idsoftware.com/idstuff/quake2/q2-314-demo-x86.exe

Additional (previous/older) information may be found at the following links:
Loading idTech2 BSP
Excentrax Games: Project Announcement
UQE Journal

base2.bsp

jail1.bsp

jail4.bsp

mintro.bsp

The project, complete with GPL source code may be downloaded from our UQE website, www.quake-engine.com.
 Return to Top
UQE: uqeModels
Posted : 04:39:54 PMFriday, November 21, 2008
The past while I've been working and talking a lot about converting the Quake1 BSP levels to the Quake2 (idTech2) engine. Since my last post on uqeMaps I did not take a break, I've been working my behind off.. getting the models converted.

The past two weeks I've been doing a lot of reading with regards to the specifications of the Quake1 (and Hexen2) MDL model format and the Quake2 MD2 model format. Mostly looking at where they are similar, where they differ and if there is any functionaly missing in the one and extra in the other and vice versa as well as anything thats engine specific. I mostly relied on 3rd party spec papers as well as Quake1 and Quake2 engine/tools code to understand the structure of both formats.

Most of my time was spent decoding the MDL format and making sure that all data I load DOES make sense because theres no ways you can during the process know if you are doing it right or not, unless you are willing to spend the time to echo your results in a custom renderer to visualize the data. I'm too lazy for that type of effort.

The past few days I've been spending translating the data from uqeModel's MDL structures to its MD2 structures and calculating missing/unknown data. Funny enough, theres quite a few bits of those going around between the two formats.


Something Missing:
Quake1 was designed and released before the time of 3D hardware which means they had their own way of rendering the model triangles. I'm assuming they did standard GL_TRIANGLES style rendering. With the introduction of GLQuake they had to generate extra data for each model to have it render more optimally using triangle strips and triangle fans. To do this they had to generate something called a "gl_commands" list and saved it for rendering. They had to do that optimization back then, since 3D hardware wasn't all that powerful and rendering GL_TRIANGLES style would have killed game playability. If you ever wondered what that "meshing..." and .MS2 files was all about... now you know!

With the Quake2 .MD2 format the "gl_commands" data was stored right in the model format, ready to be used in the engine.
Worst of all is that the "gl_commands" list used in Quake1 (which the engine generated) is not the same as that stored in the Quake2 .MD2 model format. So I ended up having 5 different Visual Studio projects open at the same time to cross reference. I was looking at Quake engine code, Quake2 engine code, the Quake2 "qdata" tool coding as well as the Quake1 "modelgen" tool coding.

The command list is basically a list (array) of integers telling the engine when to render a triangle strip and when to render a triangle fan and how long they are. The list also includes "decompressed" ST texture coordinates for each vertex.

These "compressed" vertex and texture coordinates are quite interesting. The XYZ/ST coordinates was saved as "byte" values obviously ranging from 0 to 255. These "compressed" data was used to save space on model size. To get the actual floating point coordinates they multiplied these "compressed" values with a floating point "scale" value, then added a floating point "translation" value.

Generating Quake2 style "gl_commands" was particularly tricky because I needed to generate these "gl_commands" after I translated the MDL structures into the MD2 structures, because I needed to calculate them based upon the data contained in the MD2 structures.

The biggest headache was to find the vertex seams and ST coordinates since Quake1 models uses planar texture mapping.
Imagine cutting tennis ball in half.
Once a "seam" vertex was found I had to duplicate the ST coordinates and "move" the S coordinate by 0.5 to place it on the backside of the model. This I had to do correctly for all affected "seamed" vertices before attepting to generate the "gl_commands" list otherwize the back-plane skin mapping gets all messed up like one of the below screenshots will show you.


Something Extra:
One of the biggest differences between the Quake1 model format and that of the Quake2 model format is that the Quake1 MDL format has two types of animation frames where-as Quake2 has only one type. The MDL model format has a frametype SINGLE and a frametype GROUPED where-as the MD2 model format only supports the SINGLE frametype.

The difference between the two types is simple: SINGLE frames are the typical framed animations that are controlled by your game logic.. no surprizes there, but GROUPED frames are a whole different ballgame. GROUPED frames in essence are looped framed animations complete with interval timing information for each frame contained in a group header which is controlled by the engine, rendering independantly from game logic.. well as far as I could have gathered at the moment.

Currently our uqeModels tool doesn't support converting them yet, although it does load them correctly from any MDL. Once I fully understand how I will manage this in the Quake2 engine I will start writing supporting tech for them. I'm not too keen on breaking away from the original MD2 format, so I will likely try to find examples in the Quake2 game thats acts similarly on models. Hopefully I do find something.

Over the weekend I should be able to successfully render these newly generated/converted models within the engine.
Without wasting more time, below are some sweet screenshots of the work in action!
All the code doing the conversion was done using C#.

The original
The original "demon" Quake1 MDL model. Rendered here in "qME" (Quake Model Editor). This is as original as it gets.


The converted
The converted "demon" Quake2 MD2 model. Rendered here in "qME" (Quake Model Editor). No skin texture available at the time.


The converted
The converted "demon" Quake2 MD2 model. Rendered here in "qME" (Quake Model Editor). Skinned this time, but the model "seam" vertices are corruping the ST coordinates.


The converted
The converted "demon" Quake2 MD2 model. Rendered here in "qME" (Quake Model Editor). Skinned with the seams and ST coordinates corrected.


The converted
The converted "demon" Quake2 MD2 model. Showing here the internal 8-bit skin exported as a true color TGA.
 Return to Top
UQE: uqeMaps
Posted : 07:40:51 AMWednesday, October 15, 2008
The past few weeks I've spent a great deal of time researching and writing code with regards to typical 3D engine file formats and conversion processes. Like some of you might know already, not only am I attempting here to just write a game engine for Excentrax Games, I'm also trying to swat two flies for the price of one.. so to speak.

Most of the work going into "uqeMaps" is to do a complete conversion of input Quake1 assets and converting them into a format acceptable to the target engine, that being UQE. The goal of this tool is to successfully convert a map from Quake1 to Quake3. to successfully convert a map the tool only needs two input files, that being the Quake1 BSP and its source MAP file.

Firstly, what uqeMaps do is open up the BSP and read all the 8-bit texture data from it, then convert it to its RGBA format, then adds it to a texture list. Once complete, this list of uniquely filtered textures gets written to a WAD file which will be used later in the conversion process. The next step in the conversion is to read-in the original MAP file, which gets written out in a more acceptable format to be used by q3map.exe (Quake III Arena's BSP compiler) where most of the changes to the MAP format happens at entity-level updating key values where needed and tweaking brush sides by changing texture references to shader references.

What happens next is extracting the WAD textures referenced by the MAP to its final external format, this could be TGA or JPG for example. It might seem to you that the WAD step is not needed, but its not as its not required as an in-line execution, you can use that process to create a once-off library of textures all saved to the WAD at any time in the future. As long as the WAD contains your texture references, you should be ok.

Right after getting our textures extracted the next thing on our todo list is generating shaders for all those special textures. Those are textures that are doing more than just looking pretty.. these include warping textures (water, slime, lava, etc.), animated textures and sky textures (duel layered, scrolling). With that done, we are finally ready to begin with q3map.exe to get the map compiled.

Below are a few screenshots of the compilation progress, attempt A through to E.

Rendering my original Quake1 test level, made with QuArK, in the original Quake1 engine.
Rendering my original Quake1 test level, made with QuArK, in the original Quake1 engine.

The first attempt compiling the Q3A BSP.. here the textures and shaders failed.
The first attempt compiling the Q3A BSP.. here the textures and shaders failed.

The second attempt compiling Q3A BSP.. here the textures are now working and the liquid shaders are functional. The texture mapping are still not correct and the animated texture shaders and sky shaders failed. Also no visibility or lightmapping yet.
The second attempt compiling Q3A BSP.. here the textures are now working and the liquid shaders are functional. The texture mapping are still not correct and the animated texture shaders and sky shaders failed. Also no visibility or lightmapping yet.

The third attempt compiling Q3A BSP.. here the visibility is working as well as the lightmapping. This shots is pretty much the same as attempt two. I was mostly testing the liquid shaders.
The third attempt compiling Q3A BSP.. here the visibility is working as well as the lightmapping. This shots is pretty much the same as attempt two. I was mostly testing the liquid shaders.

The fourth attempt compiling Q3A BSP.. here the texture mapping has been corrected and the animated texture shaders is functional.
The fourth attempt compiling Q3A BSP.. here the texture mapping has been corrected and the animated texture shaders is functional.

The fifth attempt compiling Q3A BSP.. this is a pretty close conversion barring the lightmapping that didn't match up properly yet, due to the process of lightmapping being quite different in Q3A than back in the Q1 days. The sky shaders also works.
The fifth attempt compiling Q3A BSP.. this is a pretty close conversion barring the lightmapping that didn't match up properly yet, due to the process of lightmapping being quite different in Q3A than back in the Q1 days. The sky shaders also works.


You would now be able to compare the very first screenshot made in Quake1 with the very last screenshot made in Quake III Arena.
I'm also at a "crossroad".. it might be a better decision to target Quake2 rather than Quake3. Reason being mostly to better acommodate the Quake1/Hexen2 bit of the project. Once done I would add a few useful features from the Q3A engine into the UQE engine.

I'm looking at ways to reduce the amount of time to write the engine coding by looking at the Quake architecture, Quake2 seems to be a good choice if you look at development speed versus features, as a lot of this code done in C in both engines will become obsolete with regards to some savings as a result of C# itself and others as a result of XNA. I'm still researching both engines to come up with a solution.

One fact is that time is not on my side.
 Return to Top
UQE: The FileSystem
Posted : 12:54:49 PMFriday, September 12, 2008
So, we are on the engine's FileSystem at the moment.
This is not one of the easiest bits of coding.. Its been quite a few weeks now since starting work on this, and STILL its not done!

The Quake FileSystem (FS), manages everything relating to any File IO and disk interactions.
Anything from loading PAK files, reading and writing config files to managing gamedirs and overriding previously loaded files etc.
Some other functionalities include restarting the filesystem, hashing files and various directory and file operations.

These past few days I've been struggling to decide on how to manage similar scenarios I found that Carmack used in idTech3 where he passes "void **" params back from some of the FileSystem's reading functions.

Its done that way because the functions themselves are reading any piece of data and doesn't know as what it returns.
The end-point of where the function is used *knows* what data it expects the function to return.
This returning buffer could be cast to a single 4-byte INT or it could be cast to a specific struct etc.

I ended up deciding the likeliest and "cleanest" route would be creating a private base function that does ALL the required processing and returns the data in a simple manner, in the form of a byte array. To get it "cast" to the correct type of data I decided to create public polymorphic functions that makes use of the private base function, receives and byte array data and convert it to the datatype being expected at the end-point of the function.

The "dirty" bit of this approach is that I would need to create a polymorphic function for each type as I encounter them and have the exotic/private datatypes like structs be shared to gain access to them. I would rather take this route then either making use of "unsafe" coding or overcomplexing the code.

I'm obviously open to suggestions if theres better ways of tackling this specific type of problem.

I'm currently working on the actual PAK loading function.
The first bit is the actual loading process and the second bit is registering each PAK with the file system.
The next thing in the PAK loading that is needed is to make sure of the modification status of each PAK as you are loading it.

I've added a new CRC32 class to the engine to CRC files that are contained in PAK files.
As each PAK file gets loaded and the index of each internal file gets created in the FileSystem each file's data gets sent via a MemoryStream to the CRC32 class which returns a CRC number on it. These CRC's gets saved to an int array which in turn will be used to build an MD5.

The MD5 gets saved to the loaded PAK file's FileSystem structure entry, which will be in future be used to make sure you can run a pure server and only clients with the exact checksum PAK files can safely connect to it or decide wheather to download the "pure" data from the server. This is all to prevent modified and unmodified data gets mixed-up when playing a multiplayer game.

At the moment I'm working on the MD5 bit of this code to conclude the PAK loading function.
 Return to Top
UQE: The Console
Posted : 01:22:05 PMFriday, June 13, 2008
Quite some good progress has been made since my last journal entry!
From the looks of it, it seems like I'll be stuck a little while longer with the development of the console, its variables and commands.

Good news so far is that CVARS can now be successfully registered and updated and its values used.
Console CMDS now also registers perfectly and the can also be executed when they get added to the execution buffer.

The current two issues I'm dealing with at the moment is CMDS execution in realtime on the execution buffer itself, at the moment it only works if its hardcoded to write to the buffer. This is due to the fact that I still need to work on the engine's event registration and execution.. the CMDS gets picked up, but never gets written to the execution buffer. Then there is CMDS overriding which also doesn't work yet. What this *should* be doing is when you send a command via the commandline, it adds the commands to the buffer, but right afterwards gets overridden by the defaults archived in the CFGS, then executes their (now overridden) values.. how nice.

Apart from that, I've worked on making the console look nicer.
The pic here below shows some of the new look as well as the hardcoded execution of a typical "cvarlist" command.. the totals are a bit deceiving at the moment.

The current state of the early (dedicated) console.
The current state of the early (dedicated) console.

I will probably have more work done on this after the long weekend.. I should have event execution sorted out by then.
 Return to Top
UQE: The Skeleton
Posted : 12:42:07 PMMonday, May 19, 2008
It's been quite a while since my last update.. so here it is!
The further development of the SAGD system held me up for while as there was important things that needed fixing and upgrading during January, February and March.

With that out of the way, the past while I've been spending doing research and development with regards to our game project. The previous journal entries covered some of the issues I had, mostly IO data-reading and data-rendering to see how fast a managed language like C# can perform standard game rendering routines.

I would say it was quite successful, rendering Quake1 level data directly from the original media and do a few of the interactive actions in the level environment covering about 80% of it. The rendering of the content was done by using TrueVision3D. Best of all is the testing was done on a pretty much dated machine.. Intel 2.4Ghz HT (Pre-Prescott), 1GB Ram, ATI X850XT.

So what does this have to do with the skeleton theme?
Much!

It was precursor work getting us to the point of saying: games are doable in a managed .NET language like C#. A lot of people world argue that its a known fact, why do the trouble of testing, others will argue that the only thing a managed language is good for is prototyping.. well, with our project I'll be attempting to prove the critics wrong.

It's a age-old arguement and nothing new.. the whole ASM versus C, C versus C++. I still remember the days where people was argueing how slow Unreal was opposed to Quake because it was designed in C++, unlike Quake that was designed in C. Critics of a technology is usually always the last ones to jump on the bandwagon as the performance gap between a native language and a managed language will shrink over time as versioning improvements take effect and as hardware improves up until a point where it won't matter all that much and will become a purist issue. Its been proven over and over, historically speaking.

Although I'm off-topic at the moment.. back to "The Skeleton".

The performance evaluation I did using original Quake data in conjunction with a managed language was a very personal goal for me to take a decision to stick to C++ or make the migration to C#; I'm quite happy with the results to make this move.
Now, there are cross-platform issues, but I asked myself: "When did I last NOT work on Windows?". Easy: Like never!

Its more important for me that our company conquer Windows first before anything else.

What has been happening the past few months is that I have been busy looking at different third-party technologies that can integrate with C# and the next important decision that needs to be taken is what will be used for rendering.

I have been doing a lot of research with regards to XNA and TrueVision3D amongst the rest of the graphics engines out there. I have been going like a YO-YO between the two lately, but it seems more likely that I'll be going for TV3D. The decision does mean that the XBox360 is out of the picture for the moment, but it does make sense to conquer the PC/Windows first, this because I'm already using C# it will be easier later to build an XNA-branch/driver later if I do decide to make a XBox version of the technology.

As for progress..
The design is very loosely based upon the architecture of Quake2 / Quake3 since that is where my expertise lie, so it makes it an obvious choice in design direction. I'm working on the skeleton/framework of the game engine at the moment, purely using C# to build it. At the moment all the basic load/unload functionality of the game engine is build including the message pump which also decelerates should it detect the "dedicated" mode.

I have already designed the early (startup) console with a functional command buffer.
The game engine can further process application command lines and read them into the command buffer. At the moment I'm working on the Console Variable (CVAR) class which handles all variables you can set and create on the console. An example of a CVAR would be "dedicated 0" / "dedicated 1". Once I'm done with that I'll be working on the Console Command (CMDS) class. An example of a command would be "+strafe" / "-strafe". You would usually "bind" keys to some of these commands.

I made two screenshots of the early console, it's not much to go by as a lot of the development being done now is non-graphical by nature.. and yes, it DOES look and function a lot like Quake3's console.

Here you'll see that the code detected that there is no Quake2 CD present. Any critical error will block access to the console.
Here you'll see that the code detected that there is no Quake2 CD present. Any critical error will block access to the console.

Here the code found the original Quake2 CD and I have sent a few bits of text to the console buffer.
Here the code found the original Quake2 CD and I have sent a few bits of text to the console buffer.

The first goal is to get a working managed .NET C# game engine developed with the help of third party multimedia tools: graphics engine, sound, networking etc. The second goal is to emulate Quake2's game media (maybe even Quake1 and Hexen2 later) via this Quake-like game engine. It will help greatly to compare the original game to this technology without the burden of managing the actual game development and assets at this point of the project.

Once we consider the engine as complete-enough, we will be ready to build an original classic FPS title on a proven technology developed using a managed language.
 Return to Top
Living it up!
Posted : 11:38:49 AMWednesday, January 02, 2008
Quite a few things have been happening since my last entry.
Yes I did work on the project through christmas and the new year's festivities.

Most of the code since my last entry got restructured and rewritten to accommodate features as I'm moving along.
Its interesting how one's line of thought evolves as you are working on a project.

What have happened?
Unlike 18 December, I have split the data structures now into two distinct parts: "disk" structures and "memory" structures.

The "disk" structures are only used to store data loaded from the Quake media files, in this case BSP data, from where it is used to build the more practical "memory" structures used by the game code.

Apart from all the rewriting some code the new features added to the game project is:


Changing Lists to Arrays
Lists are now only used within the "disk" structures where performance is not such a big deal, since the benefits of lists are in its ability to just add more elements as you are going about loading the data, especially when you are not sure how many pieces of information you are loading.

Once the loading of information is done the data captured in the lists are copied to the "memory" structures in the desired format in arrays which is allocated at "memory" structure load-time, sized to the exact loading capacity.
Arrays are about two 3rd's faster than using lists from what I've gathered while testing.

We have all the performance with no wastage.


Removing Co-linear Vertices
By removing co-linear vertices we get no visual benefit, its purely a performance benefit.
To make things clear, in Quake a "Face" is not a single triangle, its a collection of triangles that form a flat surface having a single texture assigned to it.

In this case we are using a "Triangle Fan" for each "Face".
By removing co-linear vertices we reduce the amount of "fans"/"triangles" each face would have saving us a few vertices, triangles and UV coords for textures and lightmaps to process each frame.

In the bigger picture most vertices are shared between faces, but when we talk about face data in "memory" we have to be able to deal with them individually because soon we will have PVS/VIS implemented that renders per-face, so the connection between faces doesn't matter in the vertex sense.

This means there is a lot of faces that will have odd vertices on their edges for no aparent reason.
Imagine it as knots in a string of wool under tension, or even better dots connected by lines, but when you are done it forms a straight line.. so why do we have five dots when two will do the same job just perfectly.

Basically what happens is we test the vectors of the previous, current and next vertice, if it appears to form a line, we remove current, then we move one vertex onwards and do the same test until we are finished with the number of vertices saved for the "face" in question.
When we are done, all vertices that will remain is those changing the "vector"/"direction" of the edges of the face.

Such a co-linear vertex is formed by two faces meeting on the edge of a third face forming t-junction vertex.


Quake Sky Rendering
To get Quake skies up we need to know which textures are sky textures. This is made easy since any sky texture in Quake starts with "sky".
Once we found the sky texture we should check its size which is supposed to be 256x128 in width and height.
The sky textures consist of two textures actually, the first (left) 128 block is the "alpha" sky texture and the second (right) 128 block is the "solid" texture.

The first order of action is loading the data into two seperate data containers (arrays) because there are a few operations that needs doing on the raw texture data before we upload the texture data to the graphics card.

First we need to calculate the average RGB color of the "solid" sky texture and store that temporarily.
Next we need to update the "alpha" texture, but before we continue I need to explain what was done to the alpha channel; We updated the alpha channel to a value of 0 for all pixels that has an RGB of 0 0 0 and an alpha channel value of 255 for anything else.

Now we update the color pixels on the alpha texture to our avarage color we calculated previously where the pixels has an RGB of 0 0 0.
Why do we do this? Thats because every dark cloud has an average color lining!
Did I say "average color lining"? I meant "silver lining".

Seriously...

We do this to aviod dark fringes when we blend the alpha texture over the solid one.
The fringes are formed because of linear blending of the textures on the edges of the alpha.
By doing this we just "hide" the fringes better, they are still there and the cake still remains a lie.

From here on its just blending the two together and scrolling them, but that stuff we will leave until a next time.


Animated Textures
The was probably one of the more easier things to get implemented.
If you were able to load all the texture data successfully then you are pretty much 90% there.

With quake textures we have a few identifying pieces of information we can derive directly from the texture name itself.
Textures starting with "sky" means its a sky texture (duh?); Starting with "*" means its a UV warping texture, like water; Starting with "+" means its part of an animated texture set.

We can derive how many frames an animation has by using the second character of the texture name out of a maximum of 10 frames normal and an optional maximum of 10 frames alternate.
They are identifiable as "+0" to "+9" for normal and "+a" to "+j" for alternate animations.
Alternate animations are typically used when you trigger something, like a switch, for example normally in Quake a button would flash at you red and lightred, this is the normal animation.. once you trigger it it goes green on you, this is the alternate animation which in this case only has one frame.

For this case we created links between the different frames each being an indice to the frame in the texture information as they are stored in the "memory" structures. All that needs doing now is attaching the animations to the game code's timing and there we have it.. animated textures!


Resampling of Non-Power2 Textures
This was quite a mission to sort out especially when you are not making the needed changes all around your code.
I noticed for some time that only some of the textures in a Quake BSP rendering is coming out all squashing in any of the given dimensions.
At the time I thought its probably some odd rendering bug with the way the wireframe gets built.. definitely not the texture loading code, since then all the textures should have some odd problem, right?

Wrong!

After some fishing for it, I finally uncovered the underlying issue.. some Quake textures are not Power of 2 textures!!
It really started irritating me when I loaded-up start.bsp and found that the coolest part (for me) of the game is messed-up, and that would be the introductory "quake" name.

The "quake" texture is non-pow2 at a size of 288x64 where it should be either 256x64 or 512x64.
But theres a trick involved here beyond the trickiness of writing a function that successfully resamples the texture; We need to store a "scaled" version of the texture width and height and rather use that everywhere where we are working directly with the texture data. This is because we still need the original texture width and height to calculate the UV texture mapping of it.

The texture will look stretched when you are sampling up from 288 for 512, but once you render it and properly UV map it again theres not much difference you can see since theres no loss that takes place other than a bit of extra texture memory.
You would rather always want to sample upwards opposed to downwards, because you will be loosing pixel information even if 256 is closer than 512.


Lets now get to the interesting business of viewing screenshots!

21 December 2007
E1M1. Perfect Quake-style sky rendering that matches the original Quake.
E1M1. Perfect Quake-style sky rendering that matches the original Quake.

26 December 2007
E1M1. Pay attention to the brightness of the red coloring in the animated texture on the side of this slipgate.
E1M1. Pay attention to the brightness of the red coloring in the animated texture on the side of this slipgate.

26 December 2007
E1M1. Pay attention to the brightness of the red coloring in the animated texture on the side of this slipgate.
E1M1. Pay attention to the brightness of the red coloring in the animated texture on the side of this slipgate.

1 January 2008
START. Resampled
START. Resampled "quake" texture, one amongst many non-power-of-2 resampled textures.

Next on my list is getting some of the client structures implemented, loading the entities into the "memory" structures and then get styled lightmaps working.
Now I need to do some more work to be ready to bring more good news on my next entry.
 Return to Top
Emulating Quake
Posted : 11:20:50 AMTuesday, December 18, 2007
Lets start by saying that those two words used in my subject has unleashed quite some interesting responses.. in far more ways than I expected. Given the facts from all sides, this is what is happening and what is possible through this.

First question would probably be: "Why Quake?"
Its very simple actually. Quake is the most simple of the modern-day polygon-based FPS games out there, with the least or none extra logic that is not the gameplay itself in making a simple FPS game.

Your second question would probably be: "Why emulate/re-write Quake?"
To set the record straight, its an emulation of Quake "The Game" and not a re-write of Quake "The Engine". Infact, the old engine is so outdated right now and has such a lot of porting problems and design flaws in it, it will be just as hard fixing it as it will be re-writing it.

So why re-write it, emulate the game logic and media data and render it via a modern-day engine, whatever that engine will be.
Games are becoming so large these days, its easy to decide on which side you want to be, engine development, or game development.. I see myself in the latter.
Given the fact that lately I'm not all that much into C/C++ and being a C# convert I would much rather spend my time honing C# skills than keep on falling back to C development when it comes to Quake and general game development.

Why do I talk about falling back all the time?
The answer is twofold:
It's about doing work once and getting a double-payout from it.
Firstly I have started the UQE project (Unofficial Quake Engine) a few years back and as the months progressed until the current day the project generated quite some following and it will be a shame and a very personal failure to stop supporting it. I'm a very passionate person when it comes to people enjoying your work.

The first step is to get them to still enjoy the old Quake series of games, but in the same breath allow me to maintain less or easier to manage projects.
Once we reach full emulation of Quake (it would be cool to emulate Hexen2 and *maybe* even Quake2) we will be using our newly developed technology to create a small and very highly playable FPS title that we will be selling online either through our own portal or through another; obviously I'm leaning towards having an own portal even though there are people not agreeing with this approach.

Creating an emulated version of the Quake game will help much with regards to us writing technology that will fit and compliment a previous AAA title other than that it will help building some support for international and local people to see and appreciate South African produced technologies and hopefully generate enough interest to help sell the games we make.

With a fully emulated Quake game we have all the technology we can use to make a simple FPS while not competing directly with current AAA titles, then primarily focus on changes and fine-tuning our already developed technology enabling us to give more attention to the art side of the project.

For this project we are currently using Truevision3D version 6.5 unless we find something else along the line.
Changing engines will be fairly simple as most of the Quake interpretation is managed by pure C# code which gets passes to the engine in question in the way the engine expects it.

Now we get to the intresting part.. progress screenshots!!
Sofar the game engine project is in its second week of development.

8 December 2007
E1M1. The first successful rendering on Quake BSP level data loaded with pure C# code. The lower part of the image is the starting spawn point in the level.
E1M1. The first successful rendering on Quake BSP level data loaded with pure C# code. The lower part of the image is the starting spawn point in the level.

8 December 2007
E1M1. Changed the vertex coordinate system from Y being depth to Z being depth and reversed the primitive edges. The shot was made in the first outdoor area with the bridge in view.
E1M1. Changed the vertex coordinate system from Y being depth to Z being depth and reversed the primitive edges. The shot was made in the first outdoor area with the bridge in view.

9 December 2007
E1M1. Shot made in the first room where the starting spawn point in the level is located. (where you start playing Quake)
E1M1. Shot made in the first room where the starting spawn point in the level is located. (where you start playing Quake)

9 December 2007
E1M1. After a little bit more work the texture information got loaded and converted from 8-bit paletted textures to true color RGB(A) textures (runtime) and mapped accordingly to the correct faces after having to calculate each face's UV coordinates.
E1M1. After a little bit more work the texture information got loaded and converted from 8-bit paletted textures to true color RGB(A) textures (runtime) and mapped accordingly to the correct faces after having to calculate each face's UV coordinates.

9 December 2007
E1M1. A little bit deeper into the level (near the finish) another rendering of the newly textured environment. This shows the camera slightly
E1M1. A little bit deeper into the level (near the finish) another rendering of the newly textured environment. This shows the camera slightly "outside" the world. In the background also visible is unfiltered brush models which must still be managed.

9 December 2007
E1M1. The equivalent fullscreen rendering from the original Quake engine, here only lightsmaps are obviously working an a bloom effect I've added to the engine some months ago.
E1M1. The equivalent fullscreen rendering from the original Quake engine, here only lightsmaps are obviously working and a bloom effect I've added to the engine some months ago.

12 December 2007
E1M1. After much trouble getting lightmaps loaded, converting the data into blocklights and calculating the lightmap UV coordinates we have here some lightmapping results with some bugs.
E1M1. After much trouble getting lightmaps loaded, converting the data into blocklights and calculating the lightmap UV coordinates we have here some lightmapping results with some bugs.

13 December 2007
E1M1. After spending another day pulling out some hair we FINALLY have fully functional static lightmapping rendered here in fullscreen mode!! This excludes lightmaps with added styles and dynamic lightmapping.
E1M1. After spending another day pulling out some hair we FINALLY have fully functional static lightmapping rendered here in fullscreen mode!! This excludes lightmaps with added styles and dynamic lightmapping.

13 December 2007
E1M1. The 10 lightmap textures generated. So, now that the lightmapping textures is sorted, heres the lightmap textures being generated (not easy) at runtime (level load) and blockmaps allocated within each lightmap texture (not easy) of which the lightmap coordinates needs to be calculated (not easy) before you get to load these into memory and render them. The lightmaps above are those of E1M1.
E1M1. The 10 lightmap textures generated. So, now that the lightmapping textures is sorted, heres the lightmap textures being generated (not easy) at runtime (level load) and blockmaps allocated within each lightmap texture (not easy) of which the lightmap coordinates needs to be calculated (not easy) before you get to load these into memory and render them. The lightmaps above are those of E1M1.

17 December 2007
NEWMAP. Lots of days have passed and last bits of remaining hair have been pulled. Showing here in fullscreen (not easy to spot) is fully functional water warping! Look closely at the 64x64 texture seams and you'll see the warping.
NEWMAP. Lots of days have passed and last bits of remaining hair have been pulled. Showing here in fullscreen (not easy to spot) is fully functional water warping! Look closely at the 64x64 texture seams and you'll see the warping.

Onto some feedback on where I struggled with the tech up until now..
With the lightmapping there are such a lot of calculation work that needs to be done before you get to the point of being able to render the lightmaps. Its not as straightforward and basically a given like with the Quake III Arena BSP format.
What was needed here is to load the lightmap data into lightmap images roughly estmated by lightmapsize divided by 128x128 blocks, then you know how many lightmap images there are. Like you have seen, each lightmap image contains lots of pieces of lightmaps (for level surfaces). the lightmap's coordinates within the texture had to be calculated, once found you would also need to calculate the UV coordinates on the surfaces to map it correctly. It was quite hard to get all these calculated and working correctly.

The waterwarping was hard too.
When I started out I thought: "Ag lets quickly do the waterwarping, should be easy!"
How wrong I was!
Firstly, I knew already how to do proper UV warping since I've done it before (on the vertices) of a Quake2 level viewer I developed some years back.
With horror I realized that water surfaces in Quake1 wasn't pre-subdivided for you, like they were in Quake2.
Subdivision of water and sky surfaces was done at load-time and the surfaces was updated while the level loaded adding the missing information by calculating and subdividing! The reason why it wasn't already subdivided is simple, Quake1 and its technologies was developed and designed way before hardware 3D, so waterwarping was done very differently in the original Quake's DOS software renderer than the later OpenGL version.

So, after a day and a half of struggling I finally got the water and sky surfaces subdivided, recalculated the face extents and UV coords.. finally I was in a position to implement water warping! Warping is working now and my calculation renders a result that matches Quake's rendering by an estimated 95% accuracy! Our implementation further uses a real Sine calculation opposed to the original Quake's pre-calculated Sine array, which is quicker but renders more jaggy results on faster/slower framerates.

Onto the next challenge: Getting perfect Quake-like sky rendering and writing my next entry..
 Return to Top
First PagePrevious Page journal post 1 to 8 of 8, page 1 of 1, 10 journal posts per page Page 1Next PageLast Page
Gallery Image
2012: Mayan Prophecies
2012: Mayan Prophecies
Latest Threads
08 Feb 2010, 10:49:52 PM
08 Feb 2010, 03:27:00 PM
08 Feb 2010, 03:24:00 PM
08 Feb 2010, 03:16:58 PM
05 Feb 2010, 02:46:27 PM
05 Feb 2010, 01:15:06 PM
04 Feb 2010, 04:19:01 PM
02 Feb 2010, 07:01:56 PM
02 Feb 2010, 04:55:42 PM
31 Jan 2010, 12:55:46 AM
Affiliates
Unofficial Quake Engine
Nitrogen - Delphi C++ and OpenGL Programming
Toxic Bunny
Zelian Games - Winterheart's Guild
South African Game Development - The ultimate African gamedev portal
135391135391135391135391135391135391
unique visitors since
November 3, 2006
0
visitors currently active.
Latest SAGD Technology Update: Friday, January 22, 2010, 10:28:33 AM
Copyright © Excentrax Games 2000-2010. All Rights Reserved
South African Game Development™ and the South African Game Development™ logo are registered ® trademarks of Excentrax Games
Software Development: Jacques Krige, Visual Interface Design: Michael Pote & Nicholas Kogel
privacy policy | forum policy | terms of use
Personal