OregonPete Posted September 4, 2021 Share Posted September 4, 2021 I'm creating a mod where the heroine (companion NPC) transforms into a demoness, but I'm having issues getting the tints right with NiOverride. Here's what I've done so far: I was able to add texture overrides for the entire body using NiOverride.AddNodeOverrideXxx, including the head (haven't tried the hair yet), which got the tint right, but messed up everything else (for instance, she no longer had eyebrows because the head texture overrode them). Not quite sure how to add the lip, hair, and eyebrow textures seperately, but I might try that next. If I just attempt to change the tint with NiOverride.AddSkinOverrideInt using the slot, I can't seem to change the head texture. The body (i.e. torso, hands, feet) isn't an issue, but I think the facegen mesh is getting in the way somehow and overriding anything I attempt to do with the head. I can't even change my NPC's hair color, not even with SKSE's SetHairColor on the actor base. It works fine if I remove the facegen mesh and texture (for instance, by renaming them), but then, of course, I get the black face bug. Strangely, if I use NiOverride.AddSkinOverrideInt, I can't seem to remove the override anymore. I've tried using NiOverride.RemoveAllReferenceSkinOverrides for just that NPC, and I tried using NiOverride.RemoveSkinOverride, but no luck. Really strange. If anyone has any ideas on that, I would sure appreciate it. Any ideas on how to override the facegen texture? Is there a specific slot for that? I would be grateful for any ideas. To make things simpler, here's some of my code: ; This attempts to just change the skin tone. ; Works for body, hands, and feet, but could ; not get it to work for head or hair. Function ApplyDemonessSkinOverrides() Actor _marisa = GetMarisa() Int _slotHead = 0x01 Int _slotHair = 0x02 Int _slotBody = 0x04 Int _slotHand = 0x08 Int _slotFeet = 0x80 Int _colorHair = 1053202 ; HairColor12BlackTrue (16 18 18; #101212; 1,053,202) Int _colorBody = 12454415 ; Body Color Red (190 10 15; #BE0A0F; 12,454,415) AddSkinColorOverride( _marisa, _slotHair, _colorHair ) AddSkinColorOverride( _marisa, _slotHead, _colorBody ) AddSkinColorOverride( _marisa, _slotBody, _colorBody ) AddSkinColorOverride( _marisa, _slotHand, _colorBody ) AddSkinColorOverride( _marisa, _slotFeet, _colorBody ) NiOverride.ApplySkinOverrides( _marisa ) EndFunction Function AddSkinColorOverride( Actor Target, Int Slot, Int Color ) Int _idx = -1 ; Index is irrelevant for Skin Color Int _key = 7 ; Shader Tint Color Bool _female = True Bool _persist = True Bool _1stPers = False NiOverride.AddSkinOverrideInt( Target, _female, _1stPers, Slot, _key, _idx, Color, _persist ) Utility.Wait( 0.01 ) EndFunction ; Clearing the tint overrides, however, ; does not work like this Function ClearTextures() Actor _marisa = GetMarisa() NiOverride.RemoveAllReferenceNodeOverrides( _marisa ) EndFunction ; And clearing the tint overrides ; also does not work like this Function RemoveSkinColorOverride( Actor Target, Int Slot ) Int _idx = -1 ; Index is irrelevant for Skin Color Int _key = 7 ; Shader Tint Color Bool _female = True Bool _1stPers = False NiOverride.RemoveSkinOverride( Target, _female, _1stPers, Slot, _key, _idx ) Utility.Wait( 0.01 ) EndFunction ; This applies a single texture, seems to work fine Function ApplySingleTexture( Actor _target, Int TxTo, Int Type ) Bool _female = True Int _slot = 0 Int _glow = 0 Int _color = 16316671 Float _alpha = 1.0 String _type = GetTextureType( Type ) String _node = _type + " [ovl" + _slot + "]" String _path = GetNodePath( TxTo, Type ) NiOverride.AddNodeOverrideString( _target, _female, _node, 9, 0, _path, true) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideInt( _target, _female, _node, 7, -1, _color, true) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideInt( _target, _female, _node, 0, -1, _glow, true) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( _target, _female, _node, 1, -1, 1.0, true) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( _target, _female, _node, 8, -1, _alpha, true) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( _target, _female, _node, 2, -1, 0.0, true) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( _target, _female, _node, 3, -1, 0.0, true) Utility.Wait( 0.01 ) NiOverride.ApplyNodeOverrides( _target ) Debug.Trace("Texture Applied - " + _node + " = " + _path + ", Color: " + _color ) EndFunction ; Clearing the texture works too Function ClearTextureType( Actor _target, Int Type ) Bool _female = True Int _slot = 0 String _type = GetTextureType( Type ) String _node = _type + " [ovl" + _slot + "]" NiOverride.AddNodeOverrideString( _target, _female, _node, 9, 0, _TexturePath + "blank.dds", True ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( _target, _female, _node, 9, 0) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( _target, _female, _node, 7, -1) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( _target, _female, _node, 0, -1) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( _target, _female, _node, 8, -1) Utility.Wait( 0.01 ) Debug.Trace("Texture Cleared - " + _node) EndFunction Link to comment Share on other sites More sharing options...
IsharaMeradin Posted September 4, 2021 Share Posted September 4, 2021 Couldn't you just change their race similar to a werewolf transformation? Link to comment Share on other sites More sharing options...
OregonPete Posted September 4, 2021 Author Share Posted September 4, 2021 Yeah, that with the race never really seems to work for me. I've tried several times with other characters, but I can't get the character to look the way I want her to. As soon as the race change is done, the character's face is completely messed up. Seems it uses some kind of defaults to reset the face. Or do you have any experience with that? She needs to change her skin tone from normal to red, and her hair color from brown to black. But otherwise her face and hair should remain the same. If I can get it to work with a race change, I'd do it, but I've never gotten it to work for me. Any support would be appreciated. Link to comment Share on other sites More sharing options...
OregonPete Posted September 6, 2021 Author Share Posted September 6, 2021 So, I was able to solve my problem, although not quite in the way I was hoping. In the end, I wound up overriding all the body and head textures instead of just the tint of the original textures. Using murfk's SL/LL SlaveTats mod as a basis, and a lot of research on relevant forums, I was able to figure out how to make the switch. It's still not perfect, but I'm getting there. To show you the result, here are two images, before and after. I also have the relevant code below for those who are interested. Is it possible to mark this thread as solved? So, this is Marisa, a rather unaggressive ex-bandit and Breton mage with dark brown hair and dark blue eyes: But Marisa has a secret. Because she's also a black-haired, brown-eyed, Demi-Daedric princess, who can change into her hidden persona in an instant: And for those who are interested in how the texture, hair, and eye color swap was done, here's the relevant code: Function TransformToDemoness() Debug.Trace( "Marisa Follower Quest - TransformToDemoness() called" ) Actor _marisa = GetMarisa() ActorBase _base = _marisa.GetActorBase() If ( !NiOverride.HasOverlays( _marisa ) ) NiOverride.AddOverlays( _marisa ) Utility.Wait( 0.5 ) If ( !NiOverride.HasOverlays( _marisa ) ) Debug.Trace( "Marisa Follower TransformToDemoness() - Marisa is not compatible with overlays." ) Return Endif Endif ;ListWornItems() ListNumOverlays() ListSkinColorsPerSlot() ApplyTextures() If ( _marisa.IsOnMount() ) _marisa.Dismount() EndIf ; HairColor12BlackTrue ColorForm _color = Game.GetFormFromFile( 0x000F7565, "Skyrim.esm" ) as ColorForm Debug.Trace( "Marisa Follower Quest - TransformToDemoness() Color: " + _color ) _base.SetHairColor( _color ) Utility.Wait( 0.15 ) ; Required so we can call QueueNiNodeUpdate() Utility.SetINIBool( _UseFaceGen, False ) Utility.Wait( 0.15 ) _marisa.QueueNiNodeUpdate() ; Required so we can call QueueNiNodeUpdate() Utility.SetINIBool( _UseFaceGen, True ) _marisa.SetEyeTexture( EyesMaleHumanBrown ) Utility.Wait( 0.15 ) _marisa.AddToFaction( OPsMarisa_DemonessFaction ) ListHeadParts() ListNumOverlays() EndFunction Function TransformToHuman() Debug.Trace( "Marisa Follower Quest - TransformToHuman() called" ) ListWornItems() ClearTextures() Actor _marisa = GetMarisa() ActorBase _base = _marisa.GetActorBase() If ( _marisa.IsOnMount() ) _marisa.Dismount() EndIf ; HairColor09DarkBrown ColorForm _color = Game.GetFormFromFile( 0x000A0433, "Skyrim.esm" ) as ColorForm _base.SetHairColor( _color ) Utility.Wait( 0.15 ) ; Required so we can call QueueNiNodeUpdate() Utility.SetINIBool( _UseFaceGen, False ) _marisa.QueueNiNodeUpdate() Utility.Wait( 0.15 ) ; Required so we can call QueueNiNodeUpdate() Utility.SetINIBool( _UseFaceGen, True ) _marisa.SetEyeTexture( EyesMaleHumanDarkBlue ) Utility.Wait( 0.15 ) _marisa.RemoveFromFaction( OPsMarisa_DemonessFaction ) EndFunction Function ApplyTextures() Actor _marisa = GetMarisa() ApplyTextureSet( _marisa, 1 ) ApplyTextureSet( _marisa, 2 ) ApplyTextureSet( _marisa, 3 ) ApplyTextureSet( _marisa, 4 ) ; Target, Texture, Type, Slot, Color, Alpha ApplySingleTexture( _marisa, _TextureLips, 2, 1, 0, 0.8, 4.0 ) ApplySingleTexture( _marisa, _TextureBrows, 2, 2, 0, 1.0 ) ApplySingleTexture( _marisa, _TextureNipples, 1, 2, 4194304, 0.9 ) ; Color #400000; 4194304 ApplySingleTexture( _marisa, _TextureAreolas, 1, 1, 7340032, 0.9 ) ; Color #700000; 7340032 ;ApplySingleTexture( _marisa, _TextureNipples, 1, 2, 6291456, 0.8 ) ; Color #600000; 6291456 ;ApplySingleTexture( _marisa, _TextureAreolas, 1, 1, 9437184, 0.8 ) ; Color #900000; 9437184 EndFunction Function ClearTextures() Actor _marisa = GetMarisa() ClearTextureSet( _marisa, 1 ) ClearTextureSet( _marisa, 2 ) ClearTextureSet( _marisa, 3 ) ClearTextureSet( _marisa, 4 ) ; Target, Type, Slot ClearSingleTexture( _marisa, 2, 1 ) ; Lips ClearSingleTexture( _marisa, 2, 2 ) ; Brows ClearSingleTexture( _marisa, 1, 2 ) ; Nipples ClearSingleTexture( _marisa, 1, 1 ) ; Areolas EndFunction Function ApplyTextureSet( Actor Target, Int Type ) Bool _female = True Bool _persist = True Int _idx = -1 Int _slot = 0 Int _glow = 0 ; Lighter Red w/o ENB ;Int _color = 12454415 ;12454415 = Red (190 10 15, #BE0A0F); 16316671 = White (16 18 18; #101212) ; Darker Red for ENB Int _color = 11471375 ;11471375 = Red (175 10 15, #AF0A0F); 16316671 = White (16 18 18; #101212) Float _mult = 1.0 Float _spec = 0.0 Float _alpha = 1.0 Float _gloss = 2.0 String _type = GetTextureType( Type ) String _path = GetTexturePath( Type ) String _pathNormal = GetTexturePathNormal( Type ) String _node = _type + " [ovl" + _slot + "]" TextureSet _set = GetTextureSetMarisa( Type ) NiOverride.AddNodeOverrideTextureSet( Target, _female, _node, _KeyTexSet, _idx, _set, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideInt( Target, _female, _node, _KeyColor, _idx, _color, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideInt( Target, _female, _node, _KeyGlow, _idx, _glow, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeyMult, _idx, _mult, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeyAlpha, _idx, _alpha, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeyGloss, _idx, _gloss, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeySpec, _idx, _spec, _persist ) Utility.Wait( 0.01 ) Debug.Trace( "Marisa - Applied Texture Set " + _node + " = " + _set.GetName() + ", Color: " + _color ) EndFunction Function ApplySingleTexture( Actor Target, String Texture, Int Type, Int Slot, Int Color, Float Alpha, Float Gloss = 0.0 ) Bool _female = True Bool _persist = True Int _idx = -1 Int _glow = 0 Float _mult = 1.0 Float _spec = 0.0 String _type = GetTextureType( Type ) String _path = _TexturePath + Texture String _node = _type + " [ovl" + Slot + "]" NiOverride.AddNodeOverrideString( Target, _female, _node, _KeyTex, 0, _path, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideInt( Target, _female, _node, _KeyColor, _idx, Color, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideInt( Target, _female, _node, _KeyGlow, _idx, _glow, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeyMult, _idx, _mult, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeyAlpha, _idx, Alpha, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeyGloss, _idx, Gloss, _persist ) Utility.Wait( 0.01 ) NiOverride.AddNodeOverrideFloat( Target, _female, _node, _KeySpec, _idx, _spec, _persist ) Utility.Wait( 0.01 ) Debug.Trace( "Marisa - Applied Texture for " + _node + " = " + _path + ", Color: " + Color ) EndFunction Function ClearTextureSet( Actor Target, Int Type ) Bool _female = True Bool _persist = True Int _idx = -1 Int _slot = 0 String _type = GetTextureType( Type ) String _node = _type + " [ovl" + _slot + "]" TextureSet _set = SkinBlankFemaleMarisa NiOverride.AddNodeOverrideTextureSet( Target, _female, _node, _KeyTexSet, _idx, _set, _persist ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyTexSet, _idx ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyColor, _idx ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyGlow, _idx ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _keyAlpha, _idx ) Utility.Wait( 0.01 ) Debug.Trace( "Marisa - Cleared Texture Sets for " + _node) EndFunction Function ClearSingleTexture( Actor Target, Int Type, Int Slot ) Bool _female = True Bool _persist = True Int _idx = -1 String _type = GetTextureType( Type ) String _node = _type + " [ovl" + Slot + "]" String _path = _TexturePath + "blank.dds" NiOverride.AddNodeOverrideString( Target, _female, _node, _KeyTex, 0, _path, _persist ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyTex, 0 ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyTexSet, _idx ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyColor, _idx ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _KeyGlow, _idx ) Utility.Wait( 0.01 ) NiOverride.RemoveNodeOverride( Target, _female, _node, _keyAlpha, _idx ) Utility.Wait( 0.01 ) Debug.Trace( "Marisa - Cleared Texture for " + _node ) EndFunction Link to comment Share on other sites More sharing options...
OregonPete Posted September 6, 2021 Author Share Posted September 6, 2021 Sorry, realized there are two relevant pieces of information missing in the code. The _UseFaceGen and _type strings weren't defined. Here's the code for those: ; All used textures have to be relative to "Data\\Textures\\" String _TexturePath = "actors\\character\\marisa\\" String _UseFaceGen = "bUseFaceGenPreprocessedHeads:General" String Function GetTextureType( Int Type ) String _type = "" ; Body If ( Type == 1 ) _type = "Body" ; Face ElseIf ( Type == 2 ) _type = "Face" ; Hands ElseIf ( Type == 3 ) _type = "Hands" ; Feet ElseIf ( Type == 4 ) _type = "Feet" EndIf Return _type EndFunction Link to comment Share on other sites More sharing options...
ReDragon2013 Posted September 7, 2021 Share Posted September 7, 2021 Here you can find the script source of https://github.com/Rukan/Grimy-Skyrim-Papyrus-Source/blob/master/NiOverride.psc and I cannot see something like that: NiOverride.AddSkinOverrideInt( Target, _female, _1stPers, Slot, _key, _idx, Color, _persist ) NiOverride.RemoveSkinOverride( Target, _female, _1stPers, Slot, _key, _idx )Both functions are not a part of SKSE extending script "NiOverride.psc". No idea where does it come from. Link to comment Share on other sites More sharing options...
IsharaMeradin Posted September 7, 2021 Share Posted September 7, 2021 Here you can find the script source of https://github.com/Rukan/Grimy-Skyrim-Papyrus-Source/blob/master/NiOverride.psc and I cannot see something like that: NiOverride.AddSkinOverrideInt( Target, _female, _1stPers, Slot, _key, _idx, Color, _persist ) NiOverride.RemoveSkinOverride( Target, _female, _1stPers, Slot, _key, _idx )Both functions are not a part of SKSE extending script "NiOverride.psc". No idea where does it come from. It was probably a typo when posting the initial post. In the script that they did get working, they used AddNodeOverrideInt and RemoveNodeOverride. Both of which are listed on the linked NiOverride source. Link to comment Share on other sites More sharing options...
OregonPete Posted September 8, 2021 Author Share Posted September 8, 2021 It was probably a typo when posting the initial post. In the script that they did get working, they used AddNodeOverrideInt and RemoveNodeOverride. Both of which are listed on the linked NiOverride source. @IsharaMeradin - No, no typo. In the NiOverride version I have, the SkinOverride functions are all readily available. I have my NiOverride from a Modders Package file in the Files section of the RaceMenu mod. It's in the Miscellaneous Files. It contains the latest NiOverride version, and you can create your own standalone package with it if you need it. Just need to make sure you also have the correct skee.dll and skee.ini files from RaceMenu. And, of course, the NiOverride.pex from the same RaceMenu version, otherwise they won't be compatible. Not sure if you guys knew this, but the whole NiOverride thing is now being maintained by the RaceMenu team, that's maybe why you didn't realize there's a more current version available. I would rather use the SkinOverride functions, because then you can override the original skin color instead of using an overlay, which has advantages. But I can't figure out which Slot Mask the FaceGen head uses, so I can't tell NiOverride which slot to color. In NifSkope, The FaceGen file uses slots 130, 143, and 230 for the head, and 131 for the hair. But I'm afraid there are no equivalent Slot Masks. Do either of you have an idea what they might be? Or where I could find that information? Link to comment Share on other sites More sharing options...
IsharaMeradin Posted September 9, 2021 Share Posted September 9, 2021 Regarding body slots, perhaps these will helphttps://wiki.nexusmods.com/index.php/Skyrim_bodyparts_numberhttps://www.creationkit.com/index.php?title=Slot_Masks_-_Armor I am unsure of what the results might be if using 30 instead of 130. The head itself isn't considered "armor" like the body. Thus I am not even sure if any functions designed for armor can affect the head. Link to comment Share on other sites More sharing options...
OregonPete Posted September 9, 2021 Author Share Posted September 9, 2021 Regarding body slots, perhaps these will helphttps://wiki.nexusmods.com/index.php/Skyrim_bodyparts_numberhttps://www.creationkit.com/index.php?title=Slot_Masks_-_Armor I am unsure of what the results might be if using 30 instead of 130. The head itself isn't considered "armor" like the body. Thus I am not even sure if any functions designed for armor can affect the head. Yes, thanks for posting the links. I've used both pages, and the NIF slots I mentioned are listed on the first page (under "Other body parts..."), but there's no equivalent on the second page. And that's my problem. Without that slot mask equivalent, there's no way to color the contents of the slot. If you just use 30 instead of 130 (i.e. 0x01 as the slot value for AddSkinOverrideInt), whatever is in that head slot will get colored. If there's nothing there, well, to be honest, not sure what would happen then. And I'm afraid there's usually nothing there, at least that's what I found when I listed the contents of all the slots. When I listed the body, hands, and feet slots (i.e. 0x04, 0x08, and 0x80), I clearly saw the textures that were being used, and those were what was being colored. The AddSkinOverrideInt works wonderfully on body, hands, and feet, and it has the advantage that it's not subject to the underwater skin overlay transparency issue you observe if you add tattoos using a mod like SlaveTats. There's a workaround for that issue by changing a couple of options in the skee.ini, but that again might have other consequences. It's what I'm using now to get everything to work well together, and if I don't find a solution for the facegen slots, I'll have to add a comment when I publish the mod so users know what to do about it. Do you know how to just override certain options in the skee.ini without having to override the entire INI file? If that were possible, I could offer users just that if they want it. Link to comment Share on other sites More sharing options...
Recommended Posts