Articles tagged “Quake”

I recently picked up a Sega Saturn and a copy of the technical marvel that is Quake for it. The Saturn is not renowned for being a particularly capable 3D machine and so the fact that Quake runs at all is quite remarkable, let alone as well as it does in Lobotomy Software's version. Rather than port the Quake engine to the Saturn the game uses the SlaveDriver engine, and includes conversions of 28 of the original 32 levels with some minor tweaks to improve performance. It certainly captures the atmosphere of Quake far more faithfully than most console ports of DOOM did to that game, leaving the sound and music intact and retaining the gritty aesthetic of Quake's software renderer.

Quake II's OpenGL renderer supports stereoscopic rendering providing you own a video card that has the requisite hardware and driver support ("quad-buffered" OpenGL – rather than a single front and back buffer you have two front buffers and two back buffers, one for each eye). Not owning such a video card I decided to have a go at adding some other stereoscopic rendering modes that worked with regular hardware. The four new stereoscopic rendering modes The ability to enable or disable drawing with a particular colour component in OpenGL makes implementing an anaglyph mode very simple – temporarily switch off red when drawing the view from one eye and temporarily switch off blue and green when drawing the view from the other to produce a final image that can be used with red/cyan 3D glasses.

Having tweaked the stereoscopic rendering code in Quake, I decided to have a go at Quake II. This doesn't natively support row-interleaved stereoscopic rendering, but I thought that the shared code base of Quake and Quake II should make extending Quake II relatively simple. Quake II does have two console variables dedicated to stereoscopic rendering already, cl_stereo (enable/disable stereoscopic rendering) and cl_stereo_separation (controls the displacement of the camera between eyes; the same as LCD_X in Quake).

Some time ago, I posted about using interlaced video to display 3D images. Whilst the idea works very nicely in theory, it's quite tricky to get modern video cards to generate interlaced video at a variety of resolutions and refresh rates. My card limits me to 1920×1080 at i30 or 1920×1080 at i25, and only lets me use this mode on my LCD when I really need it on a CRT. Even if you can coax the video card to switch to a particular mode, this is quite a fragile state of affairs as full-screen games will switch to a different (and likely progressively scanned) mode.

Quake has a few dynamic lights - some projectiles, explosions and the fireballs light up their surroundings. Fortunately, the method used is very simple: take the brightness of the projectile, divide it by the distance between the point on the wall and the light and add it to the light level for that point. This is made evident by pausing the early versions of Quake; when paused the areas around dynamic lights get brighter and brighter as the surface cache gets overwritten multiple times. The next problem is that a light on the far side of a thin wall will light up both sides!

I've updated the collision detection between points and the world with Scet's idea of only testing with the faces in the leaf containing the start position of the point. ZiggyWare's Simple Vector Rendering GameComponent is being very useful for testing the collision routines. I have also extended the collision routines to bounding boxes. This fixes a few issues where a point could drop through a crack in the floor between two faces. My technique is to test the collision between the eight corners of the box and then to pick the shortest collision and use that as the resultant position.

I've reworked the VM completely to use an array of a union of a float and an int, rather than the MemoryStream kludge I was using before. This also removes a lot of the multiply-or-divide-by-four mess I had with converting indices to positions within the memory stream. There are (as far as I know) three ways to invoke QuakeC methods. The first is when an entity is spawned, and this is only valid when the level is being loaded (the function that matches the entity's classname is called). The second is when an entity is touched (its touch function is called) and the third is when its internal timer, nextthink, fires (its think function is called).

After all that time spent trying to work out how the QuakeC VM works I finally have some real-world results. Apart from the obvious boring stuff going on in the background parsing and loading entities go, two functions in particular are of note. The first is a native function, precache_model(string) which loads and caches a model of some description (sprites, Alias models or BSP models). The QuakeC VM I've written raises an event (passing an event containing the name of the model to load), which the XNA project can interpret and use to load a model into a format it's happy with.

This journal hasn't been updated for a while, I know. That doesn't mean that work on the Quake project has dried up - on the contrary, a fair amount of head-way has been made! The problem is that screenshots like the above are really not very interesting at all. As far as I can tell, Quake's entity data (at runtime) is stored in a different chunk of memory to the memory used for global variables. I've had numerous problems getting the code to work - most of which caused by pointer confusion. Four bytes are generally used for a field (vectors use three singles, so you have 12 bytes), so I've tried multiplying and dividing offsets by four to try and get it all to work.

All of this Quake and XNA 3D stuff has given me a few ideas for calculator (TI-83) 3D. One of my problems with calculator 3D apps is that I have never managed to even get a raycaster working. Raycasters aren't exactly very tricky things to write. So, to help me, I wrote a raycaster in C#, limiting myself to the constraints of the calculator engine - 96×64 display, 256 whole angles in a full revolution, 16×16 map, that sort of thing. This was easy as I had floating-point maths to fall back on. With that done, I went and ripped out all of the floating-point code and replaced it with fixed-point integer arithmetic; I'm using 16-bit values, 8 bits for the whole part and 8 bits for the fractional part.

I've started serious work on the QuakeC virtual machine. The bytecode is stored in a single file, progs.dat. It is made up of a number of different sections:
Definitions data - an unformatted block of data containing a mixture of floating point values, integers and vectors.Statements - individual instructions, each made up of four short integers. Each statement has an operation code and up to three arguments. These arguments are typically pointers into the definitions data block.Functions - these provide a function name, a source file name, storage requirements for local variables and the address of the first statement.

Quake 2 stores its visibility lists differently to Quake 1 - as close leaves on the BSP tree will usually share the same visibility information, the lists are grouped into clusters (Quake 1 stored a visibility list for every leaf). Rather than go from the camera's leaf to find all of the other visible leaves directly, you need to use the leaf's cluster index to look up which other clusters are visible, then search through the other leaves to find out which reference that cluster too. In a nutshell, I now use the visibility cluster information in the BSP to cull large quantities of hidden geometry, which has raised the framerate from 18FPS (base1.bsp) to about 90FPS.

Pixel shaders are fun. I've implemented support for decoding mip-maps from mip textures (embedded in the BSP) and from WAL files (external). Now, I know that non-power-of-two textures are naughty. Quake uses a number of them, and when loading textures previously I've just let Direct3D do its thing which has appeared to work well. However, now that I'm directly populating the entire texture, mip-maps and all, I found that Texture2D.SetData was throwing exceptions when I was attempting to shoe-horn in a non-power-of-two texture.

Following sirob's prompting, I dropped the BasicEffect for rendering and rolled my own effect. After seeing the things that could be done with them (pixel and vertex shaders) I'd assumed they'd be hard to put together, and that I'd need to change my code significantly. In reality all I've had to do is copy and paste the sample from the SDK documentation, load it into the engine (via the content pipeline), create a custom vertex declaration to handle two sets of texture coordinates (diffuse and lightmap) and strip out all of the duplicate code I had for creating and rendering from two vertex arrays.

I've transferred the BSP rendering code to use the new level loading code, so I can now display correctly-coloured Quake 2 levels. The Quake stuff is in its own assembly, and is shared by the WinForms resource browser project and the XNA renderer. I'm also now applying lightmaps via multiplication rather than addition, so they look significantly better. A shader solution would be optimal. I'm currently just drawing the geometry twice, the second time with some alpha blending enabled.

ArchG indicated a bug in the TextInputHandler class I posted a while back - no reference to the delegate instance used for the unmanaged callback is held, so as soon as the garbage collector kicks in things go rather horribly wrong. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * XnaTextInput.TextInputHandler - benryves@benryves.com * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This is quick and very, VERY dirty.

I've started rewriting the underlying resource loading code to better handle multiple versions of the game. To help with this I'm writing a WinForms-based resource browser. (That's the only real Quake-related change visible in the above screenshot. I've written a cinematic (.cin, used in Quake 2) loader). To aid loading resources I've added a number of new generic types. For example, the Picture class always represents a 32-bit per pixel ARGB 2D picture. The decoders for various formats will always have access to the resource manager, so they can request palette information if they need it.

The current design of the Quake project is that there are a bunch of classes in the Data namespace that are used to decode Quake's structures in a fairly brain-dead manner. To do anything useful with it you need to build up your own structures suitable for the way you intend on rendering the level. The problem comes in when you try to load resources from different versions of Quake. Quake 1 and Quake 2 have quite a few differences. One major one is that every BSP level in Quake contains its own mip textures.

Original post by Scet
Looks nice, I could never figure out Quakes lightmaps. The unofficial Quake specs are a bit confusing on this matter. To get the size of the texture, find the bounding rectangle for the face (using the horizontal and vertical vectors to convert the 3D vertices to 2D in the same way as it's done for the texture coordinates). Then divide by 16 and add one, like this:
Width = (Ceiling(Max.X / 16) - Floor(Min.X / 16)) + 1 Height = (Ceiling(Max.Y / 16) - Floor(Min.Y / 16)) + 1 It was meant to make it look like the Doom software renderer but with Direct3D.

Original post by Ravuya
Eventually I gave up on my mod (double shotguns). Heh, you might like the Killer Quake Pack. I've added some primitive parsing and have now loaded the string table, instructions, functions and definitions from progs.dat but can't do a lot with them until I work out what the instructions are. Quake offers some predefined functions as well (quite a lot of them) so that'll require quite a lot of porting. Original post by Evil Steve
I seem to recall that the original Quake used "Truebright" colours (Which were either the first or last 16 colours in the palette), and these colours weren't affected by lighting.

Original post by Evil Steve
Yup, I've done soem QuakeC modding before. The progs.dat is basically all the game code, and quake.exe is all the engine code. if you can load progs.dat properly, you should be able to get behaviour exactly like the original Quake. Progs.dat is responsible for all monster types AI, and I think you're right about triggers and bridges and stuff. Ah, makes sense! According to the Unofficial Quake Specs it's p-code, which at least makes parsing easier. Working out which opcodes do what will (I assume) require a perusal of the Quake/QuakeC compiler source.

Original post by Evil Steve
Do you intend to parse the progs.dat file (I think that's what it was called) - the QuakeC compiled code? I've no idea what's actually involved in that, however... I'm not sure either (I haven't looked into it). As the source code for Quake and the QuakeC compiler have been released I might be able to do something. I have a hunch that said code is responsible for level events (switches, bridges, lifts, doors and so on) so it would seem pretty important. Original post by Scet
You might want to check the version of your BSP files versus the one used in the Unofficial Quake Specs.

Original Post by Scet
Do you plan on making anything out of it, or will it just be a bsp viewer? An XNA Quake implementation the way I intend on working - that is, to start from scratch - is not really worth it (as far as getting the gameplay 100% identical to DOS Quake). As a learning project, there's a decent quantity of further material (as far as maps, high-resolution textures and so on go), and I intend on trying to learn how to write a 'FPS-friendly' 3D engine based around Quake's data. Whether I shoehorn some sort of game into that or not is yet to be decided.

After pratting around with DOOM with MDX, it's logical to move up to Quake with XNA. After all, the levels are "true" 3D, and there's a healthy amount of new textures (including bump and gloss maps) so that it would make creating an advanced engine easier. My problem is always finding resources. On the subject of resources, I'm using a system whereby the various classes for game resources (such as the Level or Model class) can implement ResourceLoader.ILoadable<T>, so I can do things like this:
ResourceLoader = new ResourceLoader(@"C:\Program Files\Quake\ID1"); Palette DefaultPalette = ResourceLoader.Load<Palette>("gfx/palette.lmp"); Level SomeLevel = ResourceLoader.Load<Level>("maps/start.bsp"); This way the ResourceLoader can handle dragging data out of multiple pack (.pak) files or loading override files.

RSSSearchBrowse by dateIndexTags