Shpoike / Quakespasm

Extra bloaty junk to modernise stuff a bit.
http://triptohell.info/moodles/qss/
GNU General Public License v2.0
184 stars 41 forks source link

MD5 model replacements do not play nicely with mods that replace the base MDLs #101

Closed strangebit closed 12 months ago

strangebit commented 2 years ago

I made a pak2.pak file containing all of the MD5 models taken from KEXQuake and put this into id1 in my regular QuakeSpasm-Spiked setup (where I am using the original pak0.pak and pak1.pak files, not the remaster ones). This works great and looks great as expected, but now when playing Dissolution of Eternity (the rogue gamedir), the statue variants of the Knight and Death Knight monsters do not have their stone skins, and the Multi-Grenade Ogres look just like the regular Ogres. You can quickly test this out on the map r2m1.

It turns out that id1/progs/knight.md5mesh is being loaded rather than rogue/progs/knight.mdl, since there is no rogue/progs/knight.md5mesh to load. This makes some sense, but it doesn't play nicely with mods that change the base MDL files. So I think it makes more sense if we prioritize loading foo/progs/ogre.mdl over id1/progs/ogre.md5mesh (when running QSS with -game foo of course).


However, I guess that this would also make the regular Ogres and Knights within Dissolution of Eternity not use the replacement MD5 models, whereas KEXQuake still manages to do this. I guess KEXQuake uses some more complicated logic than my suggestion above. So in KEXQuake Dissolution of Eternity, the statues and Multi-Grenade Ogres appear without MD5 model replacements and with their correct alternative skins, whereas the regular Ogre and Knight models are still being replaced with the higher-quality MD5 models.

After some testing with KEXQuake, I think I have determined the following.

Firstly, some prerequisites. I'm assuming that for MD5 models, ogre_00_00.lmp is the Ogre's skin at index 0. If there is an ogre_01_00.lmp, that would be an alternative Ogre skin at index 1, etc. I'm guessing that the 2nd number is for skin texture animation frames. So for example, the stock Megahealth MD5 textures have health100_00_00.lmp to health100_00_03.lmp, because the Megahealth skin has four frames of animation for its blinking red light. I haven't really confirmed any of this; I'm just assuming that this is how it works.

So, with that being said, I believe this is how KEXQuake loads MD5 assets: When trying to load an Ogre model with a skin at index 1 (like in Dissolution of Eternity when it randomly decides to spawn a Multi-Grenade Ogre instead of a regular Ogre), KEXQuake checks to see if there exists a progs/ogre_01_00.lmp texture. If it does exist, then it loads the progs/ogre.md5mesh and uses it with the appropriate progs/ogre_01_00.lmp skin. If progs/ogre_01_00.lmp does not exist, then it will fall back to loading progs/ogre.mdl instead, even if there does exist a progs/ogre.md5mesh with a skin at index 0 (i.e. progs/ogre_00_00.lmp). It will use the appropriate skin index within progs/ogre.mdl. Since there does exist a rogue/progs/ogre.mdl with a skin at index 1, this works as expected. Therefore, the regular Ogres still get replaced with high-quality MD5 models, whereas the Multi-Grenade Ogres show up with the old models but at least with their proper alternative skin. So I believe that QSS should at least implement the MD5 model replacement logic like this. It seems simple enough and I would be pretty happy with it.

However, if there is a foo/progs/demon.mdl which makes the Fiend monster have an entirely different geometry that makes it look like a cat, and, crucially, it uses a skin with an index of 0, then I'm sure you see where this is going. If you run KEXQuake with +game foo and turn on Enhanced Models, the cat in the mod will look like a regular Fiend instead of what the author intended. This is because there does exist an id1/progs/demon_00_00.lmp texture, so KEXQuake will load id1/progs/demon.md5mesh and id1/progs/demon_00_00.lmp instead of loading foo/progs/demon.mdl. The cat will only show up if you turn off 'Enhanced Models'. So this is still potentially a huge problem with it not playing nicely with existing mods, and it does not appear to be solved by KEXQuake.

So if QSS reproduces the same MD5 model replacement logic as KEXQuake, it would necessitate turning off 'Replacement Models' within QSS on a mod-by-mod basis (i.e. depending on which -game you're playing). That could get annoying fast. But perhaps it is quite rare for mods to replace base MDLs like this. If that's the case, it would probably be perfectly fine to only occasionally have to turn this feature off, depending on the mod. Though, how you are supposed to know that you should do this before playing the mod is another issue. You might play a mod and not realize that what the author intended is different to what you are experiencing, finish the whole mod, never go back to it ever again, and you were none the wiser the entire time. I think for this reason I would end up just regretfully never using higher-quality MD5 models at all, and stick to MDLs. I'd be scared of playing a mod that makes changes to the base game which I'm not even aware that I'm not experiencing, because I'd be still seeing the base MD5 models.


Potential best solution

Given all of this, I think it would be better to still do the logic where you fall back to using the MDL asset if an MD5 skin at the appropriate index does not exist, but then to also always prioritize foo/progs/ogre.mdl over an Ogre MD5 asset within id1. Then if, for example, you want the regular Ogres within Dissolution of Eternity to still use the enhanced model, you could just put a copy of the Ogre MD5 asset that exists within id1 into the rogue game directory.

This would duplicate resources on your disk, but I think it would be the best way, because then the regular Ogres within Dissolution of Eternity still load rogue/progs/ogre.md5mesh and rogue/progs/ogre_00_00.lmp, and the Multi-Grenade Ogres would still fall back to loading rogue/progs/ogre.mdl with the skin at index 1 (since there would be no rogue/progs/ogre_01_00.lmp skin). Then, if there are any existing mods that replace Fiends with cats, it would still play nicely with them since it would not load the Fiend's MD5 model replacement from id1, but instead it would prioritize loading the cat-looking demon.mdl that exists within the mod's gamedir.

I wish that KEXQuake did this already (i.e. duplicating the resources), because then it would set the standard for loading MD5s to be done this way. I recognize that implementing it this way only within QSS would make things awkward when wanting to use QSS with the remaster id1/pak0.pak and rogue/pak0.pak. It would still work properly, in the sense that that the worst-case is that while the statue variants in Dissolution of Eternity would still look like statues, the regular Knights and Death Knights would not have the enhanced models anymore like they would in KEXQuake's Dissolution of Eternity, which could end up being quite confusing to the user (assuming they notice the discrepancy). A tool could potentially be created that ships with QSS that would create the appropriate rogue/pak1.pak automatically, with the duplicate MD5 models inside it for the regular Ogre and Knights, but even so it's still a bit awkward. So I can understand not wanting to do it this way, and instead just resigning to do it the way KEXQuake does it.

Alternatively then, perhaps the best thing to do would be to hard-code specific exceptions for Dissolution of Eternity's regular Ogres and Knights (and any other for hipnotic or rogue as appropriate, as this is probably not an exhaustive list). So what I mean is that QSS could use my logic for loading MD5s generally, but for rogue specifically it would short circuit this logic and not use rogue/progs/ogre.mdl with a skin index of 0 if there exists a basedir Ogre MD5 model replacement. In the rogue special case, it would load the basedir Ogre MD5 model instead. That way, any existing mods out there that replace Fiends to make them look like cats would still work as expected, and the official expansion packs would also work generally as expected with the replacement MD5 models, albeit only because of a hard-coded kludge. But the kludge would mean that the user would be saved from having to create a rogue/pak1.pak with duplicated MD5 assets within it, so it is probably worth it.

In the case where the user has changed their own rogue/progs/ogre.mdl (perhaps just its skin at index 0, or the entire model), and they expect to see their change even when they have MD5 models installed into their id1, perhaps the hard-coded rule could operate on a checksum. So the hard-coded rule would only come into play if rogue/progs/ogre.mdl is determined to be the original rogue/progs/ogre.mdl by a checksum. So if the user has the original rogue/progs/ogre.mdl and a replacement Ogre MD5 model is within id1, it will be hard-coded to load the replacement Ogre MD5 model from id1. However, if the rogue/progs/ogre.mdl is not the original, then it will not use the hard-coded logic and instead use the general logic, where it will prioritize loading rogue/progs/ogre.mdl with a skin at index 0 even if there exists a replacement Ogre MD5 model within id1 with a skin at index 0.

This would likely work for 99% of cases appropriately. I'm not sure how many existing community-made mods ship with duplicated MDLs from the base game, where they just add a new skin at a higher index within the MDL, like Dissolution of Eternity does. However, with these kinds of community-made mods, the worst-case scenario is that things will still look generally correct, which is what we want, since it would still load the MDL from the mod. However, with the KEXQuake loading logic, the worst-case scenario is that the model looks completely different to how the mod's author intended it.

I do not think its always safe to assume that the skin at index 0 or the model's geometry has not been changed within the mod's MDL file, even if it is commonly the case (which it might be or might not be, I don't know), so the KEXQuake loading logic seems inappropriate. It is surely best to defer to the skin and model that the mod shipped with, rather than loading assets from id1 that the mod's author may well have intended to be modified. But hard-coded exceptions for the official expansion packs do seem appropriate when the original expansion pack's MDL is determined to be being used by a checksum, because we already know ahead of time that the original intent for Dissolution of Eternity is that the regular Ogres and Knights will look and act the same as the id1 Ogres and Knights. So surely we can make a hard-coded assumption that loading the id1 replacement MD5 assets in these specific, special cases is perfectly fine.

There might be a case where the user prefers to rename their rogue folder to something else, and therefore the hard-coded logic would not come into play, but this is probably a 1% case that isn't worth worrying about. But even here, the worse-case scenario is that things will look generally correct; there just won't be higher-quality models for regular Ogres and Knights (unless the user also just manually copies the MD5 assets into their custom-named Dissolution of Eternity folder).

The other 1% case happens when the user runs a community-made mod with modified MDLs under a gamedir named rogue. In this case, the hard-coded rules would still not come into play because the checksum would protect it. The mod's ogre.mdl would not be the original rogue/progs/ogre.mdl, and so the general loading rules would apply instead of the hard-coded special case. So this would be perfectly fine anyway. And if the community-made mod in the rogue folder is using the original rogue/progs/ogre.mdl, then it must be safe to load the replacement MD5 model from id1 anyway, since by definition it is just a higher-quality version of the very model we know is trying to be loaded. So this would be a non-issue.