SADX Mod Loader - Texture Packs, Texture Filtering & Input Event Hooks!
Posted by SonicFreak94 on 2015/07/22 00:48:39
This SADX Mod Loader update introduces a few new features, but the most interesting is probably the texture pack system. It allows you to create high resolution textures to replace custom texture sets, or even create new ones entirely from scratch! More information after the jump.
The newly implemented texture pack system leverages D3DXCreateTextureFromFile from the DirectX 8.1 SDK to load a variety of common image formats, including PNG, DDS, and BMP. They can be just about as high resolution as you like, and mipmaps are automatically generated for formats that don't natively support them--that being everything but DDS.
With the current setup, you can replace entire PVMs with a simple folder containing your textures, or create entirely custom virtual PVMs. It integrates into the mod system as well, so you can easily distribute custom texture packs.
How it works - from the inside
The function that would typically load a PVM is overridden to first check if a texture pack folder exists with the same name. For example, we get "SONIC" and check if there's a texture pack for it. If not, we let the game do its thing. If there is one though, that's where the magic happens.
Once we've found a matching texture pack, we grab a very specific file from it, "index.txt", which contains a list of active textures for this texture pack in a specific order. The order is important because of the way materials work. Materials contain a list of textures in a specific order, so if we mix them up, you'll bet Sonic's feet will be covered in eyes. This of course isn't a problem if you're creating an entirely custom texture set... as long as you don't mix them up after applying them to a model =P
Another thing that index file has in it is each individual texture's global index. The global index is used for texture caching. Say you have two texture packs that load for the same stage--like OBJ_BEACH and BEACH01. They're bound to share some textures, so why load those shared ones twice and waste memory? That's where the global index comes in. If a texture loaded shares a global index with an already loaded texture, that already loaded texture will be used instead to conserve video memory.
From there, after it checks the aforementioned global texture cache, it loads the texture, and the game does the rest of the work.
As it stands now, you can't replace existing textures that aren't normally in a PVM. That means you can't replace any of those stray PVR files that are sitting in the system folder yet. That'll happen soon, though!
Additionally, there's a hard global maximum number of textures that can be loaded at one time. You're not going to run into this issue if you're just replacing textures, but it's something to keep in mind when creating entirely custom texture sets. I'm not dismissing the issue though; I intend to expand the texture list, if not make it entirely dynamically sized.
Originally part of SADX Fixed Edition, texture filtering has been enabled on GUI elements. Here's a comparison image of the title screen. The left is the default filtering method, which is to say none, and the right is with texture filtering enabled.
It's particularly noticeable on Sonic's legs, as well as around the curvature of this eyes and inner-ears. You can view some more comparisons in this imgur album.
Input Event Hooks
For those of you programmers working on mods, the OnInput event allows you to hook into "raw" input before it's processed by the rest of the game. This is layer 2 of the 3 input abstraction layers. Layer 1 is DirectInput (or XInput in the case of my SADX XInput mod), Layer 2 is the data from Layer 1 converted directly to Dreamcast input structures, and Layer 3 is copied from Layer 2, except specialized--meaning that the data in the input structure can be modified. Layers 1 and 2 happen in the same place, but I've defined them as separate for the sake of clarity.
Texture packs were particularly interesting to implement. I was initially inspired to do this because PVR--the default image format--is incredibly limiting. You're stuck with a hard resolution limit of 1024x1024, transparency masks are a pain to deal with, and it has rather limited color as well. None of that is all that bad when you consider the original target platform: the Dreamcast. But as soon as you want a gradient, or you just really want to see every individual grain of sand, it becomes an annoyance.
We've been striving for some way to get around this for years, but nobody really had the time to figure it out. What pushed me over the edge though, was the Steam version of SADX. You might be thinking, "What does that have to do with texture packs?" - a fair question! As it turns out, this system is something they implemented in a more restricted form. If you have the misfortune of owning this version of the game (PLEASE DON'T), you can head over to its system folder right now and see that there's a "texture_replace" folder. It's set up to load the textures by PVM name, followed by the index in the file as I mentioned earlier.
So I figured: if they
* As it turns out, it DOES work, but it seems to be restricted primarily to menu elements. I've tested it with Sonic to no avail. If somebody else wants to test it more extensively, be my guest.
The first challenge I ran into was figuring out which pointer in this obscure structure the game actually uses when it refers to the D3D texture. Once I figured that out (with the help of some friends, of course), I promptly loaded up some textures... and it worked! However, this was only for individual texture replacement, not for entire PVM replacement. That's not a feature anymore, but it'll be re-implemented later for optionally more Dolphin-esque texture replacement.
From there, I moved the code and modified some things here and there in order to allow for entire PVM replacement, as well as custom virtual PVMs. Man, I'm making that whole process sound way easier and less time consuming than it was... Anyway, I ran into a nasty memory leak. This was due to an oversight on my part. When I was replacing individual textures, all of the texture list configuration work had been done for me by the game. This time though, I sort of just guessed how things worked to at least get it displaying in-game. Can you guess what the issue was? No? Well, for each individual texture entry in the texture list, you can theoretically have more than one texture referenced even though there's traditionally only one. As such, a texture count must be specified... I just had it set to 0, so the game assumed there were no textures to free from memory. Amazing! I am best hacker!
Then all I had to do was figure out GBIX (Global Index) texture caching... which took forever! That was only because I was over-thinking things though. It's really just an arbitrarily sized array of texture entries. The game checks if the GBIX you give it is in that array, and if not, gives you an empty texture entry to work with. In retrospect, it would have been super easy for me to make this dynamically sized... But that will come later.
Here's what we're working on next:
Right now you can get a mod that mipmaps every texture in the game. It's rather sizable since it modifies every texture file in the game. Subsequently, I plan to implement a "forced mipmaps" option to generate mipmaps for each texture as it's loaded. Might slow down loading times if you play on a toaster though!
In this imgur album, you can see the current progress on proper widescreen support. There are some clipping issues to work out, but so far so good! Once that's fixed, it'll be ready for release.
Currently being worked on is the level playtest feature. This will allow you to, for example, test your custom stages by the press of a button! It'll drop you right into your stage too, so you don't have to dig through all the menus and wait for Sonic to finish saying "Let's get 'em!" to test your stage already.
Thanks for reading! We hope to do this more in the future as we go along.
No comments have been made for this article yet.