-
Notifications
You must be signed in to change notification settings - Fork 27
Textures in the Mass Effect Trilogy
Textures in the Mass Effect Trilogy, and Unreal Engine 3 in general, are pretty tricky. They are one of two types of objects that heavily depend on file offsets to work properly, in that, they reference another file at an exact offset.
Some quick information on textures:
- Mass Effect games support up to 13 mips, which yields a maximum texture size of 4096x4096 (2^13), but without changing some configuration files, you will rarely see textures above 1024.
- Textures dimensions must be powers of two.
- The normalmap that the game uses for a material is always one mip size lower than the diffuse. This means the maximum effective norm texture is 2048x2048. You can install 4096x4096 norms, but they will waste 64MB of memory for users who have 4K LODs, so don't bother with 4K norms.
- Textures are loaded according to the Unreal Engine 3 memory system. This means that textures with the same full path as one already in memory are loaded, the one in memory is used and the one in the local file is not loaded. This also means that if you have duplicate textures with different full paths, they will use additional memory. Make sure your object paths are the same across all files that share the same texture.
- Texture data is mirrored in application memory as well as stored in GPU memory, according to the maximum LOD settings (and LOD bias). This means that textures can eat up a huge amount of process memory, and Mass Effect games will crash if they try to use more than 3.5GB (ME1 will crash if going above 2GB without patching the executable). The ALOT texture mods are the most popular mods in the modding scene, so if your mod + ALOT crashes the game, users will be unhappy. There are many features of ALOT that developers can use to tamp down their memory usage.
- Texture data is all loaded in when a package loads.
Textures in Mass Effect are a real nightmare, as the externally stored textures are stored in UPK package files. As UPK package files often can change their file offsets (for example, adding an export will add an entry to the export table - which shifts all data after the table), it is extremely easy to break all of the inbound references. If you've tried to save packages in ME3Explorer and are greeted with a message saying you cannot save this package because of texture references, this is exactly why, because that file has known inbound references. The only way to safely update textures in these files is by global replacement, with a tool like Mass Effect Modder.
In ME1 there is a concept of Master
and Slave
textures. Slave
textures have externally referenced mips (extZLib, extUnc) on the higher values. The package name can be determined by traversing up the full path of the object and using the top level package as the package name to search for.
In the above image, the top texture is the slave texture, and the bottom texture is the master package file's version. You can see the slave version has the external mips defined as extLZO, where as the master one has them as pccLZO. The offsets are into the package file itself, rather than into the export. These files are (for some reason) loaded and parsed as package files, so they must behave and look like normal package files.
If a slave texture has multiple package export parents, such as BIOT_STA20_Presidium.Eyeballs.Eyes_Diff (Eyes_Diff is the texture), In BIOT_STA20_Presidium will be a package export named Eyeballs, with the master texture residing within it. Otherwise, it will directly be in the root.
In ME3Explorer 5.1 there will be features that can update your mod's texture pointers that are going into a master texture package that you build. Due to the high levels of complexity for ME1 textures, great care must be taken to avoid breaking user's games and other modding tools, such as Mass Effect Modder. There are still many unknowns with regards to ME1 textures.
ME1 has many additional fun tricks. A few textures have multiple masters with the same CRC, which makes global replacement difficult. Some define a master but never actually use it. It's a real big ugly mess that is painful to support.
Mass Effect has a rather poor implementation of texture loading, and as such, will often hitch and stutter as higher resolution textures are loaded in, which due to Mass Effect's filesystem, happens all the time.
Textures in Mass Effect 2/3 are much easier to work with than Mass Effect, in that the external texture data is stored in a Texture File Cache, or TFC. TFC files begin with 16 bytes representing their GUID, and are followed directly by headerless texture data concatenated one after the other.
These textures are referenced by data offsets in the external mips of a texture (storage types of extUnc, extZlib, extLZO), along with the filename and guid as properties of the texture. Only mips higher than 6 are stored externally, all lower ones are stored locally in the package. Mips above the 6th one, if stored locally, must be in an export that contains the NeverStream
flag, with it's value set to true, otherwise the package will crash the game when LODs are raised and the file is loaded. You will see an I/O failure on the package file in ME3Logger output if this condition occurs.
Textures in these games are loaded on a background thread, so slow disk access is typically not a big issue. Mass Effect 2 seems to have issues with texture loading causing FaceFX to desynchronize from the audio due to FaceFX not being synced up with anything.
You may see the top mips of a texture stored as 'empty', and have no data. When the games were compiled, the source art sizes were often larger than the ones that could be used by the default Texture Level of Detail (LOD) settings. In ME2 and ME3 there are no graphical options, however you can think of LODs as ME1's texture quality settings - lower LODs = lower resolution textures. You can look at the OriginalSizeX and OriginalSizeY properties to see the original source asset size before game compilation.
When the game was compiled, any textures above the maximum vanilla LODs were replaced with stubs known as empty mips. In a vanilla setup, this causes no problems as those mips are never used. However, if LODs are raised, these empty mips are attempted to be used, which are invalid. In ME2/3 this can occasionally crash the game, but most times appears to be ignored. In ME1 loading any empty 'unused' mip will cause the game to assert a crash.
In the above image, you can see the assertion message for ME1. The filename not found strings are because there is not .pdb symbols file for the game (as it's a release build), so there is no referenced filename available.
Because of these offset systems, it is very easy to break textures. A broken texture will appear 100% black in game, and is caused by the referenced offset not pointing to the start of texture data - each texture starts with a texture magic number, and if that magic number is not correct, the game completely ignores the texture data and just fills in blanks.
For this reason, brand new Mass Effect (1) textures are extremely difficult to implement, as they are stored in packages, rather than TFCs. A TFC can be updated by simply appending new data to it and pointing the offset at the new position, but since ME1 stores them in other package files, that's not an option. When the package is saved, the offsets can very easily change, which will break all incoming references.