Articles tagged “XNA”

This journal is starting to look a little drab, so here's a splash of colour. Not really all that colourful, on second thoughts. I fancied a short break from BBC BASIC, and seeing that the current XNA CTP supports VS 2008 I thought I'd try a bit more work on hardware-accelerated 3D. I've never worked with shadows, bump mapping or visibility portals, and have a copy of DOOM 3, so thought that would provide a nice set of resources to experiment with. The screenshots are generated by simply brute-force rendering of all surfaces of all models in a level (.proc) file.

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 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.

Edit: The following code has a bug in it. Please make sure you use the corrected code! After a conversation with Thevenin regarding the Game class in XNA not letting you use event-driven keyboard input (useful, for example, for entering text) I rolled out this monstrosity. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * XnaTextInput.TextInputHandler - benryves@benryves.com * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This is quick and very, VERY dirty.

RSSSearchBrowse by dateIndexTags