Greg Dolley’s Weblog

A Blog about Graphics Programming, Game Programming, Tips and Tricks

Archive for January, 2008

Part 1: How to Make Native Calls from Managed Code in C++/CLI

Posted by gregd1024 on January 30, 2008

Sometimes when working in a .NET environment we inevitably need to call a native function. With most .NET languages the programmer doesn’t need to have knowledge of the marshalling going on behind the scenes. However, programming in C++/CLI happens to be the exception.

While C++/CLI certainly does a better job at abstraction than the old managed C++ language in .NET 1.0, it doesn’t compare to the ease of C# or VB.NET.

This tutorial will be split into two posts: basic marshalling and advanced marshalling. This one will cover the basics – how simple data types are marshalled such as strings and character arrays.

The Marshal Class

Take a look at the following namespace:

  • System::Runtime::InteropServices

This namespace contains a class called “Marshal.” It is very versatile and will handle all the marshalling techniques presented in this tutorial. For most cases of managed to native calls, you’ll never have to look anywhere else.

Functions Which Take String Arguments

First, let’s look at a function which takes a series of native strings. We’re going to look at “printf()”. Since native strings are really just character buffer pointers, we need to convert a .NET “String” object into a character buffer pointed to by an IntPtr struct. Once we have an IntPtr, grabbing the native pointer comes down to calling one of its member functions. To do the first step we use the following function in Marshal:

  • System::IntPtr Marshal::StringToHGlobalAnsi(System::String ^s)

This function will allocate a character buffer and return an address via the IntPtr struct. Converting this into a native pointer is a simple matter of calling its “ToPointer()” member function. The “ToPointer()” function looks like this:

  • void *IntPtr::ToPointer(void)

Now we put both functions together like in the following code snippet:

// Get .NET version string, convert to native char *, and print to screen with printf()

 

Version ^v = Environment::Version;

String ^ver_string = String::Format(“{0}.{1}.{2}”, v->Major, v->Minor, v->Build);

 

char *native_string = (char *)Marshal::StringToHGlobalAnsi(ver_string).ToPointer();

printf(“.NET version: %s\r\n”, native_string);

Marshal::FreeHGlobal(IntPtr((void *)native_string));

We must call “FreeHGlobal()” because the previous call to “StringToHGlobalAnsi()” allocated a buffer of characters on the unmanaged heap (just like calling “malloc()” in C or C++). Otherwise we would end up with a memory leak.

Functions Which Return Strings

OK, now we’re going to look at native functions which return strings. This time, we need to do the opposite of what we did in the last section – take a char pointer buffer (or unicode buffer) and convert it into a .NET String object. We do this via the “PtrToStringAnsi()” function inside the Marshal class. It looks like this:

  • System::String ^Marshal::PtrToStringAnsi(System::IntPtr ptr)

You must pass it a managed pointer wrapper instead of a native pointer, but the IntPtr object has a constructor that will do this conversion automatically. You’ll see how to use it in the following example.

We’re going to read some characters from a file via the native “fread()” function. This function fills in a buffer pointed to by the first parameter and returns the number of bytes read. If the function had instead returned a pointer directly via its return value, the conversion technique would be no different.

char file_buffer[256];

FILE *fp = fopen(“c:\\Temp\\test.txt”, “rb”);

 

memset(file_buffer, 0, sizeof(char)*256);

fread(file_buffer, sizeof(char), 10, fp);

String ^file_buffer_string = Marshal::PtrToStringAnsi(IntPtr((void *)file_buffer));

 

fclose(fp);

Objects and Structures

Strings and character buffers may not be the only thing you need to marshal between a managed context and a native call. You may come across a function that takes some predefined object – such as a native struct or class. These cases will be covered in part two of this tutorial which I will be posting in the next couple days.

As always you can get new posts automatically using this RSS feed. Or if you prefer email updates, click here.

-Greg Dolley

Advertisements

Posted in C++/CLI, Tips and Tricks | 2 Comments »

How to Make a Quake 3 Arena Mod

Posted by gregd1024 on January 24, 2008

Now that the Quake III port is done, I can write about creating a mod instead of just installing one. Let’s get started…

Introduction

You’ll need to be at least somewhat familiar with C programming in order to make a mod. Quake III doesn’t do what is typically done by other games these days – which is wrap all game modding into some form of scripting language (such as Lua) or abstract things to the point where you don’t need to be a game programmer or familiar with the entire codebase in order to make simple behavioral changes.

The Two Types of Mods

The first thing you’ll need to know is that there are two types of Quake III mods:

  1. QVM mods
  2. DLL mods

It doesn’t matter which type of mod you make. A QVM mod will have the same effect in the game as a DLL mod. The only difference is how these mods are executed at runtime.

QVM mods are created by compiling your source into a QVM file. These files are very similar to Java bytecode – they contain instructions that the Quake III engine interprets at runtime.

DLL mods are created by compiling your source into a Windows DLL. These DLL’s are no different from any other Win32 DLL – the operating system executes these DLL’s as native machine code whenever the Quake III engine needs to call a function contained within them.

Due to the extreme difficulty I experienced trying to load a QVM mod (as explained in a previous Quake III mod post), I’m going to focus on the creation of DLL mods for this post. In my opinion this method is the easier of the two anyway.

Get the Source Code

The first thing you’ll need to do is get the source code for Quake III Arena. You can download it from one of the following links:

You can use either of the two codebase’s. The second link is Id Software’s original C codebase, while the first link is my .NET MC++ port of their C code. Both contain a Visual Studio solution for compiling everything in one shot, but Id Software admits they didn’t do extensive testing after the VS conversion. If you only have Visual Studio 2003 then I would try their C version anyway. For those who have Visual Studio 2005, you can try the C version and just upgrade the project, but why go through the work when VS 2008 Express is free?

If you must use the original native codebase for some special reason, but only have Visual Studio 2008, send me an email through my Contact page and I’ll send you my upgrade checklist – a list of steps I did to convert the original VS 2003 project to VS 2008 while keeping the native C settings intact (once you know what the steps are, it actually only takes about 20 minutes to convert). I’m really glad Microsoft still supports ANSI C in the compiler.

You will need a retail copy of Quake 3 Arena to run mods, it will not work with the shareware demo version.

I know what you’re thinking – “but I have the source code, why can’t I circumvent the license check?!” The license check isn’t the problem. The problem is that there are a few big differences between the structure of a demo PK3 data file and a retail PK3 data file (specifically related to the UI interpreted bytecode instructions). The public source only includes code for reading a retail version’s PK3 data. If you try and force the code to read a demo data file (and believe me, I’ve tried), the game crashes half the time; the other half of the time it will run but with no menu options.

Besides, it’s not worth getting around the license – these days you can buy all three Quake games (Quake I, II, and Quake III Arena) for $20 bucks in the Ultimate Quake collection. I got mine at Fry’s Electronics for $19.95; I’d recommend you get it from a discount electronics store too since buying it from Id Software’s site will cost a little more ($30 bucks at the time of this writing).

Game Project Structure

There are eight project files included in the source. You’ll only need to focus on three of them:

  1. The cgame project.
  2. The game project.
  3. The quake3 project.

These projects build as Win32 DLL’s. However, you must change where the compiler saves them. Open their project settings and set the output directory to the “baseq3” folder of your Quake III installation (i.e. on my computer it is “c:\quake3\baseq3”).

Modify the Code

We’re going to change one line of code that will make the rocket launcher repeat-fire at a faster rate. Open the source file, bg_pmove.c, under the game project and look for a function called PM_Weapon. Once in this function scroll close to the bottom of it where you should see a switch statement that looks like:

switch( pm->ps->weapon ) {

In the switch statement look for the case of WP_ROCKET_LAUNCHER; it is 125 lines below the start of the function. In that case block the variable “addTime” is being set to 800 – change this to 100. So instead of:

case WP_ROCKET_LAUNCHER:
   addTime = 800;

   break;

Now the code should look like:

case WP_ROCKET_LAUNCHER:
   addTime = 100; // for rapid fire
   break;

Under the build configuration drop-down of Visual Studio select “Debug.” You could have selected “Release” but running in debug mode will give you better diagnostic information in the event something crashes. 

Now compile the source – select “Build->Rebuild Solution” from the menu. I recommend a full rebuild instead of a regular build just in case some stray binaries were accidentally left in the original source tree.

Installing and Running the Mod

Now it’s time to take the compiled DLL’s and have Quake III use them as a mod. By now you should have modified the three DLL projects to output their binaries to “c:\quake3\baseq3” (or whatever you chose as your Quake III installation). Open that directory in Explorer and check for the following DLL’s:

  1. cgamex86.dll
  2. qagamex86.dll
  3. uix86.dll

If those DLL’s are present, you’re ready to run your mod! Simply start Quake III with the following command line:

“quake3.exe +set sv_pure 0 +set vm_game 0 +set vm_cgame 0 +set vm_ui 0”

When the Quake III menu options load, select a single player game and run an instance without any bots (so you won’t get distracted). After you’re inside the map, press tilde (”~”) to drop down the Quake 3 console. Here you’ll see a ton a status text. Use the <Page Up> and <Page Down> keys to scroll through everything line by line. You’ll want to look for status messages saying your DLL’s were loaded OK. The status text for qagamex86 is quite a few pages up, while the status of cgamex86 and uix86 are near the bottom. These lines look like, “LoadLibrary qagamex86.dll ok.”

When you see the DLL’s were loaded successfully, you’re done! Now try picking up the rocket launcher and see how fast it fires!! A good map for getting the rocket launcher right away is q3dm1 accessible under Squirmish mode.

More Fun

Here’s something extra you can do to make the rocket launcher feel and act more like a rapid fire weapon. You see, when we changed the firing rate of the rocket launcher above, we really only changed the trigger rate (the minimum amount of time that you have to wait between rocket fires), but didn’t touch the speed at which the rockets travel. It will feel better and you’ll get more frags if the weapon has a faster muzzle velocity.

In order to do this, open up g_missile.c under the game project and search for a function called “fire_rocket.” Once there, you’ll notice that the function is very small and most of what it’s doing is setting projectile properties. You’re interested in the following line near the bottom:

VectorScale( dir, 900, bolt->s.pos.trDelta );

Change this line to:

VectorScale( dir, 1800, bolt->s.pos.trDelta );

So you’re really just changing the “900” to an “1800.” Anything less than 900 will give you a slower rocket, and anything more than 900 will give you a faster rocket. But beware! Just as you can now fire faster rockets, this also means the bots can do so as well.

Conclusion

Coming up with your own complex mods that require more than editing two lines of code will require some study of the Quake III codebase. You can learn quickly by simply playing around with the code and stepping through it with a debugger.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here. Want email updates instead? Click here.

Posted in Game Modding | 26 Comments »

Update: Quake 3 Arena .NET Port is Done!

Posted by gregd1024 on January 22, 2008

Yup – I’m now done porting Quake III Arena to managed C++ with Visual Studio 2008/v9.0 and .NET v3.5. My apologies to the guys on my private email list for getting this post out a week late. I ran into massive problems getting the Release build to work, even though there was no difference between its configuration and the Debug build (the latter of which has been working perfectly for two weeks now). Turned out to be one of those, “…damn it! How could I have been so stupid?!” problems/solutions. The Release build doesn’t look in the same directory for DLL’s as the Debug build.

Before I begin explaining the port, I’d like to clarify one thing. Someone emailed me last week regarding this port and judging from his/her message it led me to believe that some people don’t understand the difference between a .NET port versus simply compiling a C++ application with MSVC++ 2008. Here’s the difference: taking the Quake III codebase and making the changes necessary so that it will compile with Visual Studio 2008 is not a .NET MC++ port. That is a C port to a different compiler. Yes, C not C++; the project files included in Id Software’s codebase are all set to compile as native C. Furthermore, changing the settings to compile everything as C++, then fixing 3,000 compile errors, is also not, I repeat not, a .NET port. That is a native C++ port of Quake III to a different compiler. Lastly, taking the former C++ build I just mentioned, turning on the “/clr” Visual Studio option, fixing 28,000 compile errors with 4,000 warnings, patching all managed to native calls such that the first run doesn’t “blue-screen” your machine, and finally doing everything else necessary to be able to view the EXE with its supporting DLL’s under ILDASM (the .NET CLR disassembler), now THAT is a .NET port! 😉

Anyway, 99% percent of this port gets compiled into IL / CLR bytecode. The exception is a few small functions which contain inline x86 assembly. Obviously I can’t do anything about those. Now let’s get into the details…

Before Getting Started

As I explain in the post which started this effort three weeks ago, the QVM generation code was not ported (see the previous post for why). Therefore, mixing game DLL’s with QVM’s or running your own QVM’s will not work. The known issues in this port stem from old QVM code. So make sure you’re using all DLL’s in order to run this port.

Get the Source Code

First thing’s first: you’ll need the retail copy of Quake III to run this source, it will not work with the demo version. I know what you’re thinking – “if I have the source, I can circumvent the license check, right?” Wrong. The license check isn’t the problem. The problem is that there are a few big differences between the structure of a demo PK3 data file and a retail PK3 data file. You’d have to change the code to read the demo format, which nobody has unless you work for Id Software.

Here is the download link:

Requirements

In order for the source to compile you’ll need the DirectX SDK. Quake III uses it for sound and keyboard / joystick / mouse input. You can download it here. I used the June 2007 release, but it should work with earlier or later versions.

After the DirectX SDK installs, check for this environment variable: DXSDK_DIR. It should have been set automatically by the DirectX installation, but if it is not there, set it manually to the root directory in which the SDK was installed (on my computer it was: “C:\Program Files\DirectX SDK (June 2007)\”).

Projects

The Quake III source is organized into eight project files:

  1. botlib.vcproj
  2. cgame.vcproj
  3. game.vcproj
  4. q3_ui.vcproj
  5. quake3.vcproj
  6. renderer.vcproj
  7. Splines.vcproj
  8. ui.vcproj (originally part of the Id Software source, but I removed it from the solution)

The master solution file containing all these projects is under “<source root>\code\.” It is the only solution file in that directory. Open it with Visual Studio 2008 to get started. Hopefully there are no errors on loading; if there are, please contact me.

Note: the “ui” project was originally part of Id Software’s source tree, but I removed it from the solution because it’s not needed (the project is still contained in the download, just not referenced). This project is used for the Quake III Team Arena (TA) game which is not part of this port.

Game Directory Structure

There are two important directories in the retail Quake III installation: the root and “baseq3” located directly underneath the root. The root folder contains “quake3.exe” and “baseq3” contains the three supporting DLL’s needed to run the game. You won’t find these DLL’s in the commercial install of Quake III, but after compiling the source they will be put there (if you’re curious as to why this is, check out my earlier post about installing a Quake III mod).

I would recommend installing another Quake III instance under the path “c:\quake3” if you don’t already have it there. That way, you won’t have to edit any of the project settings. And, more importantly, you won’t overwrite your commercial copy of Quake III after compiling the source. There’s nothing wrong with having two instances of the game installed on the same machine – you can have one as the original, and the other as a test-bed when messing with the source code.

Steps to Run the Source

First, if you don’t have the commercial version of Quake III installed under “c:\quake3\” then you’ll have to change the project settings a bit. The outputs of the following three projects will have to change:

  1. cgame.vcproj
  2. game.vcproj
  3. q3_ui.vcproj

Locate the “Output File” setting for each project under Visual Studio’s Project Properties dialog (this would be under, Project -> Properties -> Configuration Properties -> Linker -> General -> Output File). Change each of them to the following:

  1. For cgame: c:\<quake3 install dir>\baseq3\cgamex86.dll
  2. For game: c:\<quake3 install dir>\baseq3\qagamex86.dll
  3. For q3_ui: c:\<quake3 install dir>\baseq3\uix86.dll

Change “<quake3 install dir>” with whatever directory you have the Quake III game installed.

Now set the build configuration to Debug. Yes, you could do Release, but if anything goes wrong the Debug build will give you better diagnostic information. You may also notice other build configs in the drop-down list, such as “Debug Alpha,” “Debug TA,” and a few others – don’t touch these!! The only two you should be interested in are “Debug” and “Release.” The others are for Team Arena and non-Intel builds. They won’t work for this port.

Now select “Build -> Rebuild Solution” from Visual Studio’s menu options. If everything builds with zero errors and zero warnings, great! If not, please contact me.

Set quake3.vcproj as the startup project. It should already be set by default, but I’ve noticed Visual Studio 2008 seems to have a bug with this – sometimes, for no reason, the startup project will get changed.

Run the solution directly from Visual Studio. Now start fragging! 😉

Note: you could run the game directly from a command line, but you’ll need to start it like this:

“quake3.exe +set sv_pure 0 +set vm_game 0 +set vm_cgame 0 +set vm_ui 0”

Known Issues

Sometimes on fast machines in single player mode, if you add a lot of additional bots after joining an arena, the game will have connection problems. The phone jack icon will display in the corner and you won’t be able to move your player. This bug also happens in the commercial version so it’s not a new bug.

However, here’s a bug caused by the port which only happens in Release builds (Debug works fine). When connecting to some third-party servers on the internet, usually one supporting DLL fails to load (usually uix86.dll). When this happens, Quake III’s default behavior is to load the QVM files contained in the PK3 data files. As explained earlier, mixing QVM’s and DLL’s don’t work, so you’ll get very weird behavior if this scenario arises.

My Comment Log

If you take a look at the source code you’ll probably notice a lot of lines have comments that begin with this string: “***GREGS_VC9_PORT_MOD***.” A line beginning with this comment indicates that it was changed from the original codebase. It also contains a brief explanation on what was changed. This comment was added to almost every modified line except when getting rid of a keyword conflict caused by member variables being named “generic” (since “generic” is also a .NET keyword). This member was used on nearly 2,500 lines and I didn’t see the point in wasting time repeating the same comment over and over. Instead, one comment was added to the top of every module affected by this conflict along with what was done to resolve it.

A Screenshot

Here’s a screenshot at the startup menu with the console pulled down. Notice how it displays which .NET runtime is executing the game:

image

Conclusion

If you have any questions or problems with this port, please send me an email via my Contact page.

Thanks for reading! 😉

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here. Want email updates instead? Click here.

Posted in Quake 3 C++ .NET Port | 101 Comments »

Tip: What You Didn’t Know About Car Chargers

Posted by gregd1024 on January 20, 2008

What didn’t you know car chargers (i.e. iPod car charger, cell phone car charger, etc.)? Simple: you don’t need them! Yup, that’s right – don’t ever buy a “car” version of a charger for a device that comes with a regular wall A/C adapter. How come?

With a car charger you can only charge one device at a time and only the device it was made for. What if you could charge anything in your car with a device’s regular A/C adapter? Not only that, what if you could charge more than one device at a time?

There’s an easy way to do this: instead of buying a car charger, buy a DC/AC Inverter for your car! What this device does is convert your cigarette lighter socket into a standard wall outlet. Then you can plug the regular A/C adapter into the inverter – as if it was a wall in your house! Also, the cheapest inverters come with two outlets (others have four, six, etc.), so you can charge more than one device at a time.

Best of all, an inverter costs only about $25 dollars, not those rip-off prices you see for typical car chargers ($60 – $120 dollars). Since most people have no clue that an inverter will do the same thing, the OEM’s know they can charge more.

I’ve been using the same inverter for about a year and it works great! It has even survived amazingly hot summer temperatures when my car has been parked under the sun for an extended period (my car’s cabin thermometer would typically read 145 – 175 degrees). That’s what I call durability!

I’d recommend buying an inverter with a power rating of 150 watts. It’s probably more than you’ll ever need. You can charge a high powered laptop, cell phone, and a few other small devices all at the same time with that amount of power. Most laptops need 75 watts, but high powered one’s could require 90 or 120 watts. iPods, PDA’s and other small devices use almost nothing – about five watts max.

You can buy an inverter at any electronics store. I got mine at Fry’s Electronics, but Circuit City and Best Buy should have them too.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here. Want email updates instead? Click here.

Posted in Miscellaneous, Tips and Tricks | 7 Comments »

OpenGL Texture Filter Parameters Explained

Posted by gregd1024 on January 17, 2008

As promised by a previous post entitled, “How to Turn Off Bilinear Filtering in OpenGL,” I’m going to explain the various texture filtering combinations behind these two very common OpenGL calls:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, <texture shrinkage filter>);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, <texture expansion filter>);

The value of parameter number three depends on whether you are setting a GL_TEXTURE_MIN_FILTER variable or a GL_TEXTURE_MAG_FILTER variable. Here are the possible combinations:

  1. When the second parameter is GL_TEXTURE_MIN_FILTER, parameter three can be:
    1. GL_NEAREST_MIPMAP_NEAREST
    2. GL_LINEAR_MIPMAP_NEAREST
    3. GL_NEAREST_MIPMAP_LINEAR
    4. GL_LINEAR_MIPMAP_LINEAR
    5. GL_NEAREST
    6. GL_LINEAR
  2. When the second parameter is GL_TEXTURE_MAG_FILTER, parameter three can be:
    1. GL_LINEAR
    2. GL_NEAREST

The filter value set for GL_TEXTURE_MIN_FILTER is used whenever a surface is rendered with smaller dimensions than its corresponding texture bitmap (far away objects). Whereas the filter value for GL_TEXTURE_MAG_FILTER is used in the exact opposite case – a surface is bigger than the texture being applied (near objects).

There are more options for the min filter because it can potentially have mipmapping. However, it wouldn’t make sense to apply mipmapping to the mag filter since close-up objects don’t need it in the first place. Here’s a list of all the possible combinations and how they impact what is rendered (first constant in the left-most column is the near object filter [mag]; second constant is the far object filter [min]):

Filter Combination (MAG_FILTER/MIN_FILTER) Bilinear Filtering (Near) Bilinear Filtering (Far) Mipmapping
GL_NEAREST / GL_NEAREST_MIPMAP_NEAREST Off Off Standard
GL_NEAREST / GL_LINEAR_MIPMAP_NEAREST Off On Standard
GL_NEAREST / GL_NEAREST_MIPMAP_LINEAR Off Off Use trilinear filtering
GL_NEAREST / GL_LINEAR_MIPMAP_LINEAR Off On Use trilinear filtering
GL_NEAREST / GL_NEAREST Off Off None
GL_NEAREST / GL_LINEAR Off On None
GL_LINEAR / GL_NEAREST_MIPMAP_NEAREST On Off Standard
GL_LINEAR / GL_LINEAR_MIPMAP_NEAREST On On Standard
GL_LINEAR / GL_NEAREST_MIPMAP_LINEAR On Off Use trilinear filtering
GL_LINEAR / GL_LINEAR_MIPMAP_LINEAR On On Use trilinear filtering
GL_LINEAR / GL_NEAREST On Off None
GL_LINEAR / GL_LINEAR On On None

 

Not all of these combinations make sense to use. For example, there’s no point in applying GL_NEAREST with GL_LINEAR_MIPMAP_LINEAR because you’ll still see a sharp “break” between the trilinear filtered portion of a surface and the non-filtered portion. You should use whichever combination makes sense visually without compromising performance.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here . Want email updates instead? Click here.

Posted in 3D Graphics, OpenGL | 6 Comments »

What’s the Difference Between Bilinear and Trilinear Filtering?

Posted by gregd1024 on January 16, 2008

I’m not sure how many gamers, who aren’t 3D programmers, know what bilinear filtering means; however, I see it in the options of many games. It can be summarized by textures looking blurry up close and eliminating the “blocky pixel” effect which was so common back in the day of pure software-rendered 3D graphics engines. See one of my previous posts entitled, “How to Turn Off Bilinear Filtering in OpenGL” for a few screenshots.

In this post I’ll be explaining what “trilinear filtering” means and how it differs from bilinear. We see an option for trilinear filtering in virtually every game on the market today (although some call it by a different name), and depending on your 3D hardware, it may even be turned on by default. However, most gamers don’t know what trilinear filtering really is (unless they’re graphics programmers) or how it affects the visual characteristics of a game.

In a nutshell, just like bilinear filtering attempts to smooth out blocky pixels via interpolating between one texel and its surrounding four neighbors, trilinear filtering attempts to smooth out mipmapping gradients that are most commonly noticed when the camera is at a narrow angle relative to a wall (or any surface where the texture is very oblique to the point of view). Now, I’d really like to provide a screenshot of what this gradient looks like, but with the high resolution used in today’s games the effect becomes hard to see in a still screenshot. However, the effect is very noticeable when the camera is moving forward or backward.

The effect manifests itself by a sharp break between blurriness levels as a polygon recedes into the distance. At first, the polygon’s texture looks normal (the part closest to the camera). Then as you trace along the polygon’s surface all of sudden the texture loses half its resolution. If you continue tracing, you notice another break where again the resolution is cut in half. In most games this pattern is noticeable about four or five times before the polygon becomes too small to notice.

Trilinear filtering attempts to eliminate this sharp break by dynamically interpolating between different mipmap resolutions at every pixel. For example, instead of choosing between a mipmap resolution 1:1 or 1:2, it dynamically calculates a smooth gradient of mipmap levels between 1:1 and 1:2.

In the end trilinear filtering does a very good job eliminating abrupt changes in texture resolution. In some cases it is hard to tell mipmapping is even being used.

Sometime later in the week or next week, I’m going to cover something known as anisotropic filtering – which is meant to be a step better than trilinear filtering.

If you want to be updated on new posts automatically you can subscribe via RSS here, or if you prefer email updates click here.

-Greg Dolley

Posted in 3D Graphics, DirectX, OpenGL | Leave a Comment »

How to Install a Quake 3 Mod – Common Problems and Solutions

Posted by gregd1024 on January 15, 2008

I’m assuming you’re reading this post for one of two reasons:

  1. You downloaded a mod from a third party, followed the installation instructions, and nothing actually changed in the game.
  2. You programmed your own mod but couldn’t get it to actually run.

This post can be applied to solve both problems, but I’m not going to cover how to actually make a mod; I’m just going to explain what common pitfalls exist and how you can overcome them. (I will be writing a post about how to create a mod in the near future, but that’ll be after I’m done with the Quake III .NET port.)

OK, let me start off by saying if you are having trouble installing a Quake III mod, you’re not alone. I had massive problems with the first mod I tried. It was a very simple mod where I changed the rocket launcher’s trigger rate and rocket speed – so it could rapid fire like the plasma gun. 😉 And, in the end, I was only able to install it via the compiled DLL’s – the QVM files, even though they all compiled successfully, just refused to load. Since Quake III will behave the same way regardless of whether a mod is installed via DLL(s) or QVM(s) (although DLL’s are a bit faster), I didn’t see the point in continuing to beat my head against the wall over those QVM files. Therefore in this post I’m going to explain the installation of mod DLL’s only, not QVM’s. If you have successfully installed QVM’s on the v1.32 point-release, please contact me here or leave a comment on this post.

This is the main problem: when you run Quake III using command line options, some parameters in the q3config.cfg file (under /baseq3) get overwritten in order to remember what was last set. Then, the next time you run the game, even if you don’t set the same arguments as in the last instance, the game will still run as if they had been set. For example, if you launch the game with, “quake3.exe +set vm_game 0” and later use just “quake3.exe,” those two instances will behave no differently. This can be very confusing and naturally results in methods never getting tested.

Not all command line parameters behave this way, but most do. The one’s which affect you when making mods are the following three:

  1. vm_game
  2. vm_cgame
  3. vm_ui

As you probably already know, these three correspond to the three “mod’able” game DLL’s or QVM’s (qagamex86, cgamex86, and uix86 – if you’re running on a non-Intel platform, the suffix will be different than “x86”).

There’s another big problem with installing mods and it has to do with the server’s operating mode. There are two modes the Quake 3 server can run under – “pure” and “non-pure.” A pure server can only run modified versions of qagamex86, while a non-pure server can run all three modules. Trouble is, even if you tell the game to use a module other than qagamex86, it will simply ignore your request if the server is in pure mode. You won’t even see an error message.

OK, by now I’m sure you’re wondering how to set up things properly so let’s get to it! I’m going to show you a series of steps, but first keep in mind that I’m running the 1.32 Point Release. If you don’t already have this version I would highly recommend installing the patch so we’re both on the same page. I don’t know whether the steps I’m going to list will work in other versions of the game. Now here goes…

Mod install steps:

  1. Copy the mod DLL(s) into the same directory where “quake3.exe” is located. Update (1/21/2008): actually, it’s safer to copy them to the “baseq3” directory under the root because only a Debug build of Quake III will scan both the root and “baseq3” while the Release build will only scan “baseq3.”
  2. Open a DOS command prompt.
  3. Type “cd <drive>:\<quake3_install_dir>\” and hit <Enter> (on my computer the path is “c:\quake3\”)
  4. Type “quake3.exe ” and append one or more of the following command line options:
    • “+set sv_pure 0” (this one is not optional)
    • If you’re installing qagamex86 add: ” +set vm_game 0″
    • If you’re installing cgamex86 add: ” +set vm_cgame 0″
    • If you’re installing uix86 add: ” +set vm_ui 0″
  5. Press <Enter> and the game will load.
  6. Once you see the Quake 3 menu go ahead and start a new single player game.
  7. Press tilde (“~”) to drop down the Quake 3 console.
  8. In the console you’ll see a ton of status text. Scroll through it via the <PageUp> and <PageDown> keys looking for lines that say your DLL’s were loaded successfully. The status text for qagamex86 is quite a few pages up, while the status of cgamex86 and uix86 are near the bottom. The lines look like, “LoadLibrary qagamex86.dll ok.” 
  9. If the console indicates your DLL(s) loaded correctly, then congratulations! Your mod should now be running in the game!

That’s about it. If you have any questions about installing DLL mods, feel free to email me via my Contact page.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here . Want email updates instead? Click here.

Posted in Game Modding | 26 Comments »

How to Turn Off Bilinear Filtering in OpenGL

Posted by gregd1024 on January 14, 2008

I don’t know which video game popularized bilinear filtering (or trilinear filtering for that matter) for close-up objects but I personally hate the effect. The only time I can accept the look of filtering textures in this manner is if the texture’s resolution is massively high (so I don’t see the blur). Note: for this post when I say “bilinear filtering” I’m also referring to “trilinear filtering” – both have the same blurring effects.

Let me show you what I mean and then I’ll explain how to turn it off. The effects of bilinear filtering are best seen on textures that contain straight lines. Therefore, I put a simple checkerboard-like texture on a wall and moved the camera close to it:

no_filtering_close_up_wall_1

Notice how the edges of the alternating dark-green / light-green squares are clear and crisp. Now, let’s look at the exact same thing but with bilinear filtering turned on:

bilinear_filtering_closeup_wall_1

The edges are not clear and crisp anymore. The whole texture looks a bit blurry. If we move closer to the wall, it gets even worse:

bilinear_filtering_really_closeup_wall_1

The screenshot above looks much worse in its original size; click on the picture to enlarge. Now if we didn’t use bilinear filtering it would look like this:

no_filtering_really_close_up_wall_1

Hence the reason why I don’t like bilinear filtering. It just makes things too blurry. However, I have yet to see any game programming tutorial or OpenGL tutorial explain how to turn it off.

Actually it’s very simple to turn off. After loading a texture you need to make the following two function calls:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

Most code examples already have these calls except the values for parameter three are different. The value for that parameter depends on parameter two. Here are possible combinations:

  1. When the second parameter is GL_TEXTURE_MIN_FILTER, parameter three can be:
    1. GL_NEAREST_MIPMAP_NEAREST
    2. GL_LINEAR_MIPMAP_NEAREST
    3. GL_NEAREST_MIPMAP_LINEAR
    4. GL_LINEAR_MIPMAP_LINEAR
    5. GL_NEAREST
    6. GL_LINEAR
  2. When the second parameter is GL_TEXTURE_MAG_FILTER, parameter three can be:
    1. GL_LINEAR
    2. GL_NEAREST

I don’t want to stray off topic and explain each parameter combination, but don’t worry, I’ll write another post on that topic soon. For now, just know that most examples use GL_LINEAR_MIPMAP_NEAREST for min filter and GL_LINEAR for mag filter. This combination sets up typical bilinear filtering used in most games. Trilinear filtering is almost the same except GL_LINEAR_MIPMAP_LINEAR is used instead of GL_LINEAR_MIPMAP_NEAREST.

When you set both parameters to GL_NEAREST it tells OpenGL to not interpolate any of the texel color values. It takes the calculated (u, v) coordinates, finds the texel nearest to that point in the source bitmap, and uses that color. So essentially it is just like old software rendering engines before everybody started using bilinear filtering.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here . Want email updates instead? Click here.

Posted in 3D Graphics, OpenGL | 14 Comments »

Microsoft Drops GAPI, Replaced by Mobile DirectX – My Thoughts

Posted by gregd1024 on January 11, 2008

For a long time the most common way to get direct access to a Windows Mobile device’s framebuffer (i.e. pointer to the screen) was to use the GAPI (Game API) – Microsoft’s helper API for direct screen access and direct hardware input. Many mobile games that needed fast screen blitting utilized this API (most notably the DOOM port for Pocket PC). Unfortunately Microsoft has recently deemed the graphics portions of GAPI to be obsolete (the hardware input portions are not obsolete yet).

The announcement came from Microsoft about one and a half months ago. Fortunately, they’ve replaced it with DirectDraw and Direct3D Mobile. Here are a couple links you’ll find useful:

So far I’ve played around a little bit with DirectDraw on my Pocket PC doing some simple 2D graphics. It was quite easy to start plotting pixels and drawing things in full screen mode. I used Visual Studio 2005, Windows Mobile SDK 6 Professional,  and the default Win32 Smart Device project. Here’s the code:

This is one area where mobile DirectX beats OpenGL ES, in my opinion. I’m always able to write OpenGL applications faster and with much less code than DirectX, but from what I’ve seen here, DirectX is the better way to go if you’re doing 2D.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here . Want email updates instead? Click here.

Posted in 2D Graphics, DirectX, Mobile Graphics | 3 Comments »

How to Fix – "C2039: ‘GetCurrentDirectoryA()’ : is Not a Member of ‘System::IO::Directory’"

Posted by gregd1024 on January 10, 2008

Have you ever received either one of these annoying error messages from the MSVC++ 9.0 compiler?

“C2039: ‘GetCurrentDirectoryW’ : is not a member of ‘System::IO::Directory'”

“C2039: ‘GetCurrentDirectoryA’ : is not a member of ‘System::IO::Directory'”

You’ll get this error when converting a native C++ project to a MC++ (managed) project and somewhere in your code there is a call to Directory::GetCurrentDirectory(). Below is a very simple example:

#include “stdafx.h”

#include <windows.h>

 

using namespace System;
using namespace System::IO;

 

int main(array<System::String ^> ^args)
{
    Console::WriteLine(L”Dir: “+Directory::GetCurrentDirectory()); // Error!
    Console::ReadKey();
    return 0;
}

The reason for the error is that “GetCurrentDirectory” is also defined as a macro in windows.h. The macro expands to “GetCurrentDirectoryA” or “GetCurrentDirectoryW.” So in the example above, the compiler actually sees “Directory::GetCurrentDirectoryW” which obviously isn’t defined in the “Directory” class.

The fix is simple – just “#undef” the macro:

#include “stdafx.h”

#include <windows.h>

 

using namespace System;

using namespace System::IO;

 

#undef GetCurrentDirectory

 

int main(array<System::String ^> ^args)

{
    Console::WriteLine(L”Dir: “+Directory::GetCurrentDirectory()); // OK

    Console::ReadKey();

    return 0;

}

Now the compiler sees the real “GetCurrentDirectory” function and there are no compile errors. 🙂

-Greg Dolley

Subscribe via RSS here. Want email updates instead? Click here.

Posted in General Programming, Tips and Tricks | 5 Comments »