Improving UV Generation

I recently engaged in a fruitful conversation on the DaggerXL forums with another DF hacker called DigitalMonk, whose specialty is Daggerfall’s textures. His interest was piqued when I made an informal comment regarding UV generation. After bouncing a couple of posts off each either, DigitalMonk’s thoughts and observations resulted in improvement to my current method of UV generation. He also made a discovery that will result in yet another improvement down the track. If you want to track the whole conversation, read back from here. I will summarise the outcome below so you don’t have to re-read the whole thread unless you want to.

Anyone who used Daggerfall Explorer would have noticed some 3D objects have skewed textures. At the core of this problem is how Daggerfall stores UV coordinates (UV mapping is a way of describing how textures are painted on a mesh). Rather than storing a value from 0.0 to 1.0 for every point, Daggerfall only describes UV coordinates for the first three points of any face (and optionally on the fourth point, but research has shown DF does not use this value). This probably doesn’t sound so bad, triangles only have three points right? Unfortunately Daggerfall doesn’t use triangles to describe 3D objects, it uses ngons that can exceed a dozen points per face. With only the first three UV coordinates to work from, the problem is how do we calculate UVs for the remaining points? Check out the main article on the UESP for more information on the problem and the solution we’ve been using up until now. You get some feeling from this short article on how much special handling is needed to cover all possible faces.

The problem with this solution is the constructed matrix can generate UVs that look a little weird, even when the determinant for that linear equation believes the face should be solvable. There are also multiple linear equations based on whether the face is coplanar to XZ, XY, YZ, etc. When UV generation failed, branching logic was needed to post-fix the UVs for certain faces, or skip the matrix generator for another technique, or just slap the correct UVs on directly. All of this made UV generation a bloated exercise that was tricky to debug. Fixing one problem would often break something else. All in all, there are three classes of texture problems:

  • Type 1. These are faces for which UVs cannot be generated using the existing linear equations, or are generated incorrectly.
  • Type 2. These are faces with bad source UVs (i.e. the coordinates are incorrect in Daggerfall’s files). Check out the eastern side of Wayrest Castle in-game for an example of this problem. This class of problem is visible inside the game.
  • Type 3. I used to think these were Type 2 problems, but DigitalMonk discovered these source UVs actually have extra bits packed into the coordinate. By removing these bits, the UV coordinate works as expected. This class of problem is not visible in the game, meaning Daggerfall uses these bits for some purpose, or at the very least just strips them out.

After my conversation with DigitalMonk, I’ve made some improvements to my UV generation that removes all Type1 problems with no special handling required. I managed this after realising UV generation is really just a 2D problem in 3D space. All of those extra linear equations were only needed due to the extra degree of freedom. By moving all the points into 2D first, I would only need the single XY linear equation to solve for all possible faces.

This worked perfectly, and allowed me to remove all my special handling. Below are a couple of screenshots from the test block viewer. The screenshots on the left demonstrate some problem faces with all special handling disabled. You don’t see these problems in the RDB Block Viewer demo included with DFConnect because of that special handling. On the right is the same scene using the new UV generator, with absolutely no special handling. All UV coordinates “just work”.

uv fixing 1 uv fixing 2
uv fixing 3 uv fixing 4

The next step will be to strip those extra bits for Type3 problems, and eventually write a simple UV patching function for the worst Type2 problems. Once this is completed, UV generation in DFConnect will be almost perfect.

I can’t thank DigitalMonk enough for taking the time to clarify the various UV problems and help me reach a better solution. Cheers mate.

Posted in Daggerfall Connect, Technical Content.