Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Why the game looked wrong

The problems on What the patch fixes share one cause: the game chooses how to draw when it starts, and on many current drivers it chooses its most limited path. This page explains that choice, which drivers lead to which path, and the grass bug in that path.

The game chooses a shading method at startup

KOTOR supports two ways of shading each pixel, one from each of the two big graphics vendors of its time: ATI’s fragment shaders and NVIDIA’s register combiners. Most of the game’s lighting, its reflections, the fog on its shiny surfaces, and its screen effects are written for both methods. Some lighting is written only for NVIDIA’s, as How the patch works describes.

When the game starts, it asks the driver whether it offers ATI’s fragment shaders. If it does, the game uses them. If it doesn’t, the game sets up NVIDIA’s path, and then uses each NVIDIA feature only where the driver offers the extensions that feature needs. Most of what the game draws follows from that choice.

Which drivers offer which method

The method the game uses depends on your driver:

DriverATI’s fragment shadersNVIDIA’s register combinersPath the unpatched game uses
NVIDIA, on Windows or LinuxNoYesNVIDIA’s register combiners
Mesa, on LinuxYesNoATI’s fragment shaders
AMD or Intel, on WindowsNoNoThe path without pixel shaders

If you play on Windows with an AMD or Intel card, the game has been using the path without pixel shaders. That path is where the problems the patch fixes come from.

Where this table comes from

  • The NVIDIA row is an RTX 2070 on driver 610.57. Its compatibility profile lists GL_NV_register_combiners and doesn’t list GL_ATI_fragment_shader.
  • The Mesa row is Mesa 26.2.4, on its radeonsi driver and on llvmpipe, its software rasteriser. Both list GL_ATI_fragment_shader and don’t list the register combiners.
  • The AMD and Intel row comes from the logs of testers on that hardware.

The path without pixel shaders

The game also supports cards older than either method, which can’t shade per pixel at all. When the game finds neither method, its check for per-pixel shading fails for every surface, and it draws with the path it has for those cards. That path is missing the following:

  • Light on lightmapped surfaces. A lightmap is the lighting stored in a level when the level was built. On this path the game draws the lightmap but turns lighting off while it draws the surface, so the lights in the room never reach it.
  • Bump lighting, which needs per-pixel shading. Some bump mapped surfaces come out flat, and some come out black, such as the lekku of Twi’lek characters.
  • One-pass reflections. On this path the game draws shiny surfaces in two passes without per-pixel shading. Armour and metal look flat and lose their detail, water loses its transparency, and parts drawn mostly by their reflection, such as HK-47’s eyes, can disappear.
  • Soft shadows and screen effects. The game draws both only when it uses one of its two methods, together with other extensions it checks for.
  • Fog that draws correctly. In some places, such as the Lower Shadowlands on Kashyyyk, the distance doesn’t fade into the fog.
  • Grass that draws correctly. The next section explains why.

Why the grass breaks

The game has three ways to draw grass, and two of them have a bug.

To draw anything, the game tells the graphics card where in memory to find the corners of each shape. The two buggy ways give the card the location of the grass’s own bookkeeping instead of the positions of the blades. The card reads whatever values are there and uses them as positions. Those values aren’t positions, so the blades land wherever the values place them, depending on what is in memory at the time. Usually the blades stretch from the ground far into the sky. Sometimes they land out of view, and the grass is missing entirely.

The third way gives the card the correct location. The game uses it only when its check for bump mapping passes, which needs one of its two shading methods and several other extensions. NVIDIA’s drivers and Mesa each offer one of the methods, so grass draws correctly on them. AMD’s and Intel’s Windows drivers offer neither, so on those the game uses a buggy way, and “turn off grass” became the usual advice.

The Matale Grounds on Dantooine, with grass stretched into two columns that reach the sky. The same location, with the grass appearing correctly on the ground.
The Matale Grounds on Dantooine. Without the patch, the game draws the grass with one of its buggy ways, and the blades stretch into two columns that reach the sky.

Technical details

CAurTriangleBin::RenderGrassPolys has three draw paths. Each path calls GLRender::DrawLightmappedGrass, which passes its first argument to glVertexPointer. Two of the paths pass the address of the bin’s own field at offset 0x40, so OpenGL reads the bin itself as vertex coordinates:

  • The first path, which the game takes when bit 0x08 of the byte at offset 0x02 is clear, or when renderTextureGrass is clear. In the second case it also leaves the grass untextured.
  • The multitexture fallback, which the game takes when aurATIFragmentShadersBumpMapAvailable() returns false and AurMultiTextureAvailable() returns true. When both return false, the game draws no grass.

The shaded path, at 0x004A6FFC, passes the blade buffer whose address is stored at offset 0x38.

aurATIFragmentShadersBumpMapAvailable depends on AurBumpMapAvailable. That function returns true when the driver offers GL_NV_register_combiners together with GL_ARB_vertex_program, GL_EXT_texture_cube_map, GL_ARB_multitexture, and GL_EXT_bgra, or when the game is on the ATI path and uses ARB vertex programs.

Why the patch uses one path for every driver

On NVIDIA’s drivers and on Mesa, the unpatched game already uses a shaded path. On Mesa that is the ATI path, which leaves out the lighting that only NVIDIA cards drew. Each of those methods also depends on a single vendor or project continuing to support it. Register combiners are NVIDIA’s own extension, and Mesa doesn’t implement them. ATI’s fragment shaders aren’t offered by AMD’s own Windows driver. Leaving each driver on its own method means maintaining three paths, two of which depend on a single vendor or project.

Instead, the patch puts every card on one path, built on GL_ARB_fragment_program, which AMD’s, Intel’s, and NVIDIA’s drivers and Mesa all carry. How the patch works explains that path.