Animated model textures
This merge request, built off of my earlier mr, model features ( !2424) adds the ability to animate the textures of any frame of any 3d model. This includes models using the extend feature from model features, blend textures, sprite models, and sprite2 models.
This was previously circumstantially possible by not including a texture with your model, allowing it to default to the animated sprites, but this approach felt contrived and hard to make models around. You could also replace the sprites of every single animation with your model textures through a skin patch addon, but this was also incredibly inconvenient, inefficient, and limited in its usage. This mr aims to provide a relatively simple, hopefully memory efficient, and extensive way to animate any model's textures.
This functionality is accomplished by two things:
- any model is now able to be loaded in a zipped format (.zip/.pk3)
- zip folders have a naming system to largely prevent texture repeats while still giving control over where and when any texture will be shown
The zip format is implemented by porting the android port's individual resource loading functionality. This was used for model packs there, which have a somewhat similar implementation, but they differ in a few ways.
- each zip holds a single md3. because of some additions in model features, you can include multiple sprite models and a single spr2 model in the same file, but exactly one md3 will be read from each zip folder.
- md3s require their file normal extension within the zip.
- zip files are added to the models.dat directly, just like any other model. they're not loaded through any command or through addon loading, and should be for, all intents and purposes treated as a single unit, equivalent to a md3/md2.
- the resource is also freed as soon as the model or its textures are done being loaded, instead of staying open while the model (pack) is active, like in android
Because of this, I recommend not actually using the default pk3/zip extensions. The filename extension isn't even checked here, just if the file signature itself is of a zip. Zip folders are associated with the decompress operation and pk3s with addon loading. We don't want to do either of these. To avoid confusion, i recommend making up your own file extension so people don't mess with it. Standardizing it would probably be nice, but it might be a bit of a hassle to manually rename the file extensions in slade or other archive editing programs. If people think its a good idea, however, I'm a fan of the .mdz extension. A model, like md3/md2, but zipped!
The texture naming system uses at most four folders, which indicate where each texture belongs. I recommend using slade to do this, but it can technically be done just from within a file manager.
If you want to include textures within your zip, (which you don't have to! you could just compress an md3 for no reason!) you must include a "Textures" folder, capitalized exactly as shown. Within this folder, in order for your textures to load, must include a one of two things: "DEFAULT.png" for player models or "[SPRNAME]A.png" for sprite models (THOKA.png for example). These two are last-resort fallback textures for every single animation. If you don't need to animate a model at all, just taking advantage of your whole model being contained in a single file instead of 2-3, you just need these. Any player model animation that doesn't have a dedicated texture will fall back to DEFAULT.png, any sprite model without a dedicated texture will look backwards for another texture until it reaches the "A" frame.
For the rest of the textures, they are named as follows, placed in any order inside the "Textures" folder:
- for player models - [SPR2NAME][NUM].png. for example, WALK5.png for the fifth frame of a SPR2_WALK animation. No SPR2_/SUPER prefix. For 3 letter names, the underscore is included. The number starts at 1 to a max of 256. The prefix should be identical to the suffix used in an animation name while making the model. Frames correspond to model frames, not sprite frames, so if an animation has been extended, the textures will be as well.
- for sprite models - [SPRNAME][CHAR].png OR [SPRNAME][NUM].png. for example, POSSB.png or POSS2.png for a crawla's (POSS) second frame. The char designation is identical to those of sprites, seen here https://wiki.srb2.org/wiki/Sprite#Frame_list. 64 frames are possible using the character naming system, 256 with numbers (same rules as sprite2 models). Note that long sprites must be used in order to use frames past 64, and you must use numbers. However, before then the two are interchangeable, just stay consistent.
This naming convention allows you to fill in the only the textures you need. You don't need a B or C texture to use a D texture. Said B/C frames will fall back to the A texture, while any frames past D will fall back to the D texture. Same rules apply to numbered frames, except you also don't even need a [SPR2NAME]1 texture, as this will fall back to the DEFAULT texture.
The other three folders use these names identically. One feature of this is the ability to define unique textures to super frames! (player models only) This includes an optional DEFAULT(.png) frame, which will serve as a fallback for any unique super animations without a texture, instead of the normal default texture. Animations in a super form that are not unique will use their original texture, or a normal default. Any unique textures for super forms are not named any differently, instead they are placed inside a subfolder of textures called "Super". The names should be identically structured to those outside of the subfolder; i.e. a SPR2_WALK and SUPERWALK would both use a WALK1.png texture, just in different places.
A few examples to demonstrate how fallback textures work. Lets say you have a model with textures that look like this:
- On frame 1 of SPR2_WALK, the default texture (Textures/DEFAULT.png) will be loaded.
- On frame 4 of SPR2_WALK, WALK4.png in the textures folder (Textures/WALK4.png) will be loaded.
- On frame 6 of SPR2_WALK, WALK4.png in the textures folder (Textures/WALK4.png) will be loaded.
- On frame 8 of SPR2_WALK, WALK8.png in the textures folder (Textures/WALK8.png) will be loaded.
- If you extended the walk animation, adding more frames in the same timespan, any frames past this would also use WALK8.png.
You went super!
- On frame 1 of SUPERWALK, the super default texture, (Textures/Super/DEFAULT.png) will be loaded.
- On frame 3 of SUPERWALK, WALK2.png in the super folder (Textures/Super/WALK2.png) will be loaded.
- On frame 4 of SUPERWALK, WALK4.png in the super folder (Textures/Super/WALK4.png) will be loaded.
Blend files can also be included, and they follow the same structure, including super textures as above. However, instead of being placed in the "Textures" folder, they are placed in the "Blends" folder. The super subfolder is named the same. Similarly to regular textures, you must include a DEFAULT.png in the Blends folder if you want to use any blend textures, however, a super default blend texture (Blends/Super/DEFAULT.png) is not required.The only thing different here is when the blend textures actually display. Blend textures are dependent on regular textures. If the blend texture is not placed before or on the same frame as the regular texture, the blend texture will not get applied. Textures/DEFAULT.png will always use Blends/DEFAULT.png, even if there is a relevant blend texture that would be loaded sooner.
Lets say you want to change the blend texture in the SPR2_TRNS animation.
- If you put a single blend on frame 1 (Blends/TRNS1.png), every texture will have its effect only if you include a TRNS1.png in the Textures folder. Any textures afterwards will also use this same blend.
- If you put a single blend on frame 2 (Blends/TRNS2.png), and a regular texture on frame 1 and 3, frames 1 and 2 (using Textures/TRNS1.png) will use the DEFAULT blend texture, while frame 3 and beyond (using Textures/TRNS3.png) will use the blend you added.
A properly set up textures file could look like this:
You should never have less textures than blends. Same rules apply to sprite models as here. However, these don't get any functionality from the super folder.
Here's a lazily put together video showing off some of the features in action. I might replace it with something better later, along with some more example files. Note that the rings use the +n flag from model features to prevent them from facing the camera.
animatedtexturesbadexample.mp4
Right now though, heres both red and blue crawlas put together into one file, textures and all, without duplicating the model. The blue crawla flashes red. Also a cube with the Sonic 3 giant ring applied to all 6 of its faces (incorrectly), goes over rings.
models.dat:
POSS bothcrawlas.zip 3.0 0.0
SPOS bothcrawlas.zip 3.0 0.0
RING ringtest.zip 3.0 0.0
Heres what the folder structure looks like
I think thats all in terms of functionality, I'll edit this as needed if I missed anything.
Also, as usual, all regular functionality should be preserved. You can still load the two textures from separate files if needed.
For modelers wanting to test this out: under the "View exposed artifacts" section, click on the appropriate operating system under the "Artifact" table. Then, find the "bin" folder. Within this should be a compiled build for your OS - srb2win.exe for Windows, lsdlsrb2 for Linux, and srb2.app for MacOS.
Just wanna talk a bit about how this is implemented internally. Besides porting over a bunch of loading functions from android to read from the zip, I've modified the structure of models substantially. Textures are no longer stored in the md2_t struct, just info on if they exist or not. All textures are held in a new textureframe_t struct, which holds a dynamically allocated array of patch_t's, another array of frame numbers (from 0-255), so they can be loaded out of order, and a field that keeps track of the number of textures. These textureframe_ts are held both in spr2frames, superspr2frames as well as the base of the model_t struct. Other functions, such as png/pcx loading (btw pcx files should work everywhere a png is required, although further testing would be good, I don't know many models that even use the format anyway and it was removed in android) have been moved, and the model loading functions now take string buffers instead of file names.
All textures are loaded simultaneously, and then retrieved in a getter function. Blends and regular textures are also loaded at the same time. They are only reloaded if the DEFAULT.png texture/[SPRNAME]A.png texture, both always located at md2->model->textures[0].patches[0] is not downloaded or valid (such as when the texture cache is cleared i think), and hasn't already been marked as not existing. This should prevent potentially large amounts of memory use reloading the zip folder and recreating each texture.
If a non-required texture fails loading, it is just skipped. It should be properly freed if there are any errors, but it doesn't print anything to the console or any other errors if something fails. Maybe a message that says how many textures got loaded would be a good idea, I may implement that later. I tried to be extremely careful about memory management considering that plain png files can get very large, but if you find anything messed up please let me know asap.
The order that textures are loaded in is also different, just because the textures are dependent on frame information now. Given that this is built off of model features, it also supports extended frames and the multi model loading (multiple models are in the bothcrawlas example), the walk animation in the video is extended.
Theres probably some more stuff, I worked on this for a while, but that's all I can think of for now. If you find any bugs or have any suggestions on the implementation of this, please let me know.