SSTU - Shadow Space Technologies Unlimited

SSTU - Shadow Space Technologies Unlimited

98.5k Downloads

Crazy idea - adjustable texture colors rather than one texture for each color

blowfishpro opened this issue ยท 16 comments

commented

The switchable AO maps with new shaders you implemented a little while ago got me thinking. A lot of textures are used just for different tank colors (even worse if you get the texture pack). Since you proved that custom shaders work (with a bit of painful setup at first), it occurred to me that maybe the color could be a property of the shader. This would reduce the number of textures and also give the user more color options, since theoretically they could pick any color. Current thinking is that it would work like this:

Each colorizable texture comes with a color mask, which defines to which parts of the texture and how much the color would be applied. This only needs to be one channel so it could be in a shared file with other things. When the user selects a colorizable texture in the editor, an option would appear to edit the color, bringing up a color picker with some presets and RGB sliders/fields (or HSB or whatever).

Certainly not without challenges:

  • I know you got the shaders into the asset bundles once, and I'm not sure how much effort it takes to do again. Could be it's easy the next time, could be it's nearly as much work.
  • Getting the color to apply in a way that looks good might not be simple. Generating the color mask might not be trivial either.
    • Thinking about the math a bit, the formula would probably be
      finalDiffuse = initialDiffuse * ( (1 - colorMask) + (colorMask * userColor) )
      This allows the color to scale from not applied to 100% applied based on the mask

Like I said in the title, this is a bit crazy and not at all trivial, so feel free to dismiss it, but just thought I'd share the idea.

commented

Interesting idea. I had been toying with the concept of recoloring the textures in the shader/material as part of #425, but hadn't hit upon a method to allow for specifying which parts of a texture to recolor. Using a mask seems like it would work.

I'm not sure that a single mask would work for all textures though. Consider the striped textures -- you might want separate adjustable colors for the white and black sections (maybe someone wants a red/green striped rocket?). If a full-color mask were used, you could have three/four separate colorable sections per-part, and could even include some mixing in some sections (one section each for R,G,B, if a section had both R+B, it would mix the two user selected colors based on the ratio of R-B).

There is also the problem of the custom textures that would not work well with recoloring -- the gold foil and orange foam texture(s). Would probably still have to make them exempt from color selection.

Before I add any additional custom shaders though there are a few problems with the existing ones that I would like to track down and fix -- the current shaders have pixels that show full white/extremely bright/pixel-popping, almost like some sort of aliasing going on with the specular highlighting. Haven't compared to the stock shaders, so not sure if it is a problem with Unity rendering, the models, the textures, or the custom shaders that are currently in use.

Exporting shaders into asset bundles -- not difficult. Slightly time consuming, but simple. I've got a Unity-editor script that does most of the hard work, from there it is just some manual file renaming, and dropping the updated shader packs into the distribution.

Will certainly give this idea some thought -- even if only implemented for the solid color tank/adapter/nose textures it could eliminate a large portion of the texture sheets for those parts while also adding a huge selection of user-configurable colors. If I could cut 30-50mb of textures out it would be worth it just for the reduced file-upload times when packing releases.

commented

Yeah, the masks couldn't really be shared between textures. My initial thinking was that the striped textures wouldn't be able to change color, since they're only available as black/white currently. But it could also allow the stripes, background, or both to be changed (although I'd say allowing more than one color switcher on a texture could get complicated really fast).

commented

Have been giving this a bit of thought, and I think it is a good idea, and something I would like to investigate a bit more. I did quite a bit of investigation and playing around with the SSTU custom shaders, and whatever problems that they appeared to have are also shared by the KSP shaders (and the legacy Unity shaders).

The main problems that I'm encountering while thinking through the setup will be:

  • UI -- Will need to add UI controls for the colorable portion of the texture.
    • This could be as simple as 3 float-range sliders. Will still need to be added to the UI controls for every module that implements texture swapping (MFT, MSRB, MUS, ISDC, FR, IPA).
    • On parts with multiple modular model segments, each model segment will need its own controls -- MFT module would need controls for top, body, and mount; other modules would be similar. That would be 9 new sliders on each fuel tank (some only visible when applicable mounts/adapters are selected)
  • Data Persistence -- the custom color(s) will need to be saved for each modular model (up to 3 in most parts)
  • Whatever method is used needs to still allow for non-recolorable texture sets to be used, and disable the recoloring UIs when non-recolorable texture-sets are selected.

A couple potential half-thought-out solutions:

  • Have the texture-recoloring code be a separate module. One module per modular-segment in a part; so the MFT would get three recoloring modules.
    • This solves the UI and data persistence problems, as each module handles its own UI and persistent data.
    • How would these modules be interlinked into the MFT/MUS/etc modules in a non-painful fashion? They would need to be updated/reset/reloaded every time the MFT/etc changed its models around.
  • Use a separate custom UI window (e.g. volume container window) to specify texture recoloring. This could be added through a KSPAddon, and allow for recoloring of all parts on the vessel through a single centralized UI. It could hook directly into the texture-set data
    • If this method were used, about the only changes needed to the MFT/etc modules would be to add saving/restoring of the persistent data for the texture set coloring.
    • Would need to standardize the model-handling across all of the modular-part-modules to allow for easier integration into the custom UI.
    • The texture set code/classes would be responsible for re-applying persistent data on part reload. The UI would only be responsible for handling of the changing of colors; the KSPAddon would merely drive the UI and would not do any load/save/persistence handling.

... Probably more brainstorming to come soon....

commented

I think a separate UI for color editing makes sense. The part action windows area already busy enough as it is, and it probably makes sense to provide a small set of default options as well as full RGB selection.

commented

Indeed - I'm thinking of a GUI toggled by an app-launcher button that would show a list of recolorable sections on the craft with a sub-window for the per-section color controls.

Would likely implement an interface on all of the MFT/etc parts that have recolorable sections, something like:

//interface IRecolorable //or whatever it ends up being named
public abstract string[] getSectionNames();//return array of names of sections for this module/part, e.g. Nose, Body, Mount
public abstract Color[] getSectionColors();//return array of colors corresponding to the section names
public abstract void setSectionColors(Color[]);//set the input array of colors into the persistent data and update the texture set/shaders

Shader is written and working in Unity editor. Seems that for my purposes I may wish to clamp the color range a bit in the UI, from like 0.2-0.8, as really dark or really bright colors look... bad.

image

Thought will need to be given on how to persist the custom colors / where to store the data, and how to feed it back into the shader / texture set classes. Likely that a bit of additional code will need to be added to the texture set classes to update the shader color data. Persistent data will have to be stored in the module itself, with one color data stored per recolorable section. Unsure if KSPField supports Color class or if it will need to be persisted through manual/custom code.

commented

Unfortunately it doesn't look like KSPField supports colors, so it would have to be serialized and deserialized by hand (this sort of inflexibility is why I ended up basically replacing KSPField in B9PartSwitch)

commented

That is indeed unfortunate, but can be worked around with a bit of effort.

I'll take a look at what you did with the B9PartSwitch modules as I wouldn't be opposed to something a bit cleaner and more reusable than saving them to/from a string in the OnLoad and OnSave methods.

commented

Yeah, it looks like it can read and write vector4 without any trouble. I don't blame you for being skeptical though - the way it's structured, it would be completely possible for one of the read/write code to be there but the other missing.

commented

So, while KSPField doesn't appear to support the Color class/struct directly, it does look like it supports both Vector3 and Vector4, either of which could be used to store color information (v3 for rgb, v4 for argb/rgba).

Should merely take a bit of manual conversion at the module level to work between Color and Vector classes. In fact it looks like Unity has some built-in conversion methods between the two, so should be fairly trivial to get it all working (merely mark the KSPField as a Vector4 and handle as a Color everywhere else).

The last bit would be.. while KSPField supports Vector4, does it support it as a persistent value? Really shouldn't be a problem/question, but with all the other oddities and inconsistencies with stock code, it has to be asked.

commented

Work on this feature is progressing nicely. Have also unified all of the other texture-set-related features to use the same config syntax and share much of the same code. All texture-set and shader code now support arbitrary shader assignment, arbitrary texture assignment, and arbitrary shader property assignment; any and/or all of those can be adjusted through the config. The only real limitation is on if the SSTU code can -find- a shader in the first place; if it can find it, it can set it up.

As a result of the above work and cleanup, there will be a massive number of config changes included with this feature.

Currently everything is working as it should. Shaders are loaded and assigned to the models. Mask textures work, allowing for full, partial, or no masking. Color selection UI, preset color loading, and actual model recoloring by section are all working. Data persistence of customized colors is working.


This brings me back to deliberation on the actual user-accessible feature-set that should be included with this update. I keep returning to 'multi-mask' textures, using each color channel in the mask texture as a potentially separate mask, with up to three user-selected input colors (one for each channel in the mask). The largest bit keeping me from implementing it is actually the persistent color fields; each user-selectable section will need to persist a color field in the module even if it is not actually used.

However, I think this feature (multi-masking) would be desirable and useful, both to me as a dev, and to the end-users. It would allow me to basically ship only one diffuse texture per part for normal colored textures, and let the user select the mask to be used (by selecting the texture set). For example SLS/Saturn 'striped' textures could be done by using a white base texture with 2-3 masks on it (one for the 'white', one for the 'stripes', and perhaps a third for the 'intertank'). Most 'plain' textures could use two masks, one for the tank body, one for the intertank stringer area. Could possibly add a third that covered the top and bottom stringer portions.

The main downsides that I can think of are the previously mentioned increase in persistent fields, and the increased complexity in the UI (which might not need to be that much more complex). Nothing technical stopping it from working. It wouldn't even result in an increase in mask texture size; as I'm already using a separate texture for the mask, and DXT doesn't support single-channel textures, I essentially already have two unsued channels in each mask texture (even combining the existing single-channel mask into the alpha channel of the diffuse texture would not save space, as adding an alpha channel doubles texture size anyway).

commented

It might be possible to combine the mask with other things to save a few textures. Probably a bit painful to work with though. I believe that in GLSL you can access color channels by index. So for example you could put the AO map and color mask on different channels of the same texture, then pass the channel index as a shader property. This could get really messy really fast though.

commented

Yeah, would be entirely doable, combining a couple single-channel masks into a single texture. As long as you know what channel contains what information, dealing with them in the shader is quite easy (its just color.r or color.b or color.g, or color.a depending upon the channel). Dynamically choosing what channel is something I'm not familiar with, but should be possible as well (can likely pass a couple of indices through to the shader as shader properties to tell it which channel to use for each function).

However it does get to be quite ugly when making (and especially updating) the textures. It also runs into the problem of 'combination's of textures -- what if a different AO map is needed than is included with the mask? Then you have to create yet-another texture with the new AO/mask combination.

That is the reason I left the AO separate from the DIFF/SPEC textures in the existing AO/spec shader setup; it allows for easy pick-and-choose combinations of textures at the config level, without needing to worry about repacking textures for specific AO/spec maps.

commented

Right, so the idea is that both the mask and AO would be defined by a texture and a channel index. The texture might be the same in some cases but not necessarily.

commented

Ahh, yeah, that certainly would work, and could result in some savings in texture-memory-use. Would be quite a bit more complex to setup, and would still require a separate texture slot in the shader for each mask type (not sure how expensive shader texture slots are). If I were designing a commercial system for a non-moddable game, I likely would use that method or a very close derivative; I'll would take every little optimization that I could get.

In this case though I'm trying to keep it as simple as possible to use (both as a user and a developer), while still being easy to develop for and maintain in the long run (separate textures for each function), and while allowing for easy end-user customization or further additions (such as adding additional masks, setting up masks and configs for other parts/models).

I'll likely put together a few testing masks and an updated testing shader to see how useful the multi-masking feature would actually be. I'm -thinking- that it would be awesome... but I'm going to have to actually play around with it a bit (both on mask setup/creation and in-game use) to see if it is really as neat of a feature as the first impression gives me.

I'm also going to work on merging this feature in with the SSTUTextureSet module so that it might potentially be applied to any other (stock or modded) part in a fairly lightweight manner; simply add the module, create the mask texture, setup the texture-set definition, and enjoy recoloring of arbitrary parts.

commented

Much progress has been made on this feature. Implemented and working for the current selection of MFT tanks, nosecones, and adapters. Working on adding in support for all of the engine mounts.

One thing I've hit upon though is how to allow for the base 'diffuse' texture to support both highlight and shading detail features. The current setup uses a white base texture (1f,1f,1f), with only shading details being available/visible. Works well, looks decent. Could look better though.

What I am going to experiment on is instead of the output color being (baseWhiteDiffuseDetail * userMaskColor), to instead change it to be (userMaskColor + (detailTexture - 0.5f)). This would allow for the 'diffuse' texture to add both shading and highlight details to the user-selected coloring, while still allowing for accurate explicitly set user-color specifications.

Thus if a user selected base color was 0.75f, and the detail texture color was 0.5f, no adjustments would be made. If the detail color was 0.4f, the output color would be adjusted to 0.75f - (0.4f - 0.5f) = 0.65f (a shading detail). If the detail color was 0.6f, the output would be 0.85f (a highlighting detail).

What I am trying to accomplish is to find a method to still allow details like 'edge damage' to be added to the diffuse/detail texture, and be properly applied to all user-selected colors.

One other consideration is how to appropriately apply 'edge damage' to the various user selected base colors. There almost needs to be a way to specify a threshold where the 'damage detail' switches from an add to a subtract, and vise-versa. For example on a white/light colored texture, edge damage might be apparent as a slight darkening and smudging. On a black/dark colored texture, edge damage would be apparent as brighter highlights.

It might be sufficient to leave edge-damage as a purely 'highlight' detail; combined with appropriate highlighting in the specular mask, it will probably look appropriate.

commented

As this is a real thing now, I am closing this ticket in favor of using the 'progress tracking' ticket for further development tracking ( #450 ).