Jump to content

Recommended Posts

Is that even possible?

 

I'll try to be as brief as possible. 

I made a mod that makes you ripped by changing the normals used. I did it by adding new Texture Sets and then adding them to the SkinMaleHumanBody and SkinFemaleHumanBody formlists.

 

That mod works as expected, but then its users gave me some nifty ideas that can only be made with scripts.

After so much digging and testing, I found I could use NiOverride, but I still can't manage to pull this.

 

After many, MANY tests I found out that I could use...

 

AddSkinOverrideString

NiOverride.AddSkinOverrideString(Player, true, false, 0x04, 9, 1, bodyTex, true)

Where 0x04 is supposed to be the body slot, and 9, 1 are indeed the arguments needed to override normals, but THIS CODE IS ALSO SETTING THE HAND NORMALS TO THE BODY ONES (funny enough, the feet normals are left untouched).

 

If I try to fix that doing something like this:

NiOverride.AddSkinOverrideString(Player, true, false, 0x04, 9, 1, bodyTex, true)
NiOverride.AddSkinOverrideString(Player, true, false, 0x08, 9, 1, handTex, true)

Now I get the hands normals applied to the whole body.

It's like if those functions just straight up ignore the supposed slots for body and hands, and making the changes persistent or not (last argument in the function call) doesn't seem to do a thing here.

 

I would think this is a problem with my installation, but Wet Function Redux uses those very functions and it's working well for me.

Wet Function and RaceMenu are up to date.

 

Oh, and the cherry on the top: I would show you screens of my problem, but this thing suddenly decided to instantly revert changes just today; and no, NiOverride.ApplySkinOverrides(Player) doesn't fix that.

 

So I went the other route...

 

AddNodeOverrideString

And still nothing.

 

I did this:

    String Area = "Body"
    String Node = Area + " [ovl" + 5 + "]"
    int index = 1        
    NiOverride.AddNodeOverrideString(Player, true, Node, 9, index, tx, true)
    NiOverride.AddNodeOverrideFloat(Player, true,  Node, 9, index, 1.0, true)

And, according to the log, it gets applied:

[05/07/2020 - 03:21:27PM] @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[05/07/2020 - 03:21:27PM] Before
[05/07/2020 - 03:21:27PM] Overlay5 
[05/07/2020 - 03:21:27PM] Overlay4 
[05/07/2020 - 03:21:27PM] Overlay3 
[05/07/2020 - 03:21:27PM] Overlay2 
[05/07/2020 - 03:21:27PM] Overlay1 
[05/07/2020 - 03:21:27PM] Overlay0 
[05/07/2020 - 03:21:27PM] After
[05/07/2020 - 03:21:27PM] Overlay5 textures\actors\character\female\600 extreme.dds
[05/07/2020 - 03:21:27PM] Overlay4 
[05/07/2020 - 03:21:27PM] Overlay3 
[05/07/2020 - 03:21:27PM] Overlay2 
[05/07/2020 - 03:21:27PM] Overlay1 
[05/07/2020 - 03:21:27PM] Overlay0 
[05/07/2020 - 03:21:27PM] @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

And once again, it doesn't work. Normals stay the same.

 

But if I change index = 0 so this gets applied to the diffuse channel instead the normals one, it gets correctly applied, as expected:

 

59205502_SkyrimSE2020-05-0715-53-47.jpg.cb47c6c7c0df3c12b893446046551041.jpg

 

So... do you have any ideas? I hope someone can help me.

Here are many experienced people with NiOverride and it REALLY doesn't help there's no documentation for it.

 
Link to comment
29 minutes ago, Fotogen said:

Maybe look at how this mod https://www.nexusmods.com/skyrim/mods/37546 works? It can do full texture swap for player.

Thank you so much for your help! I'm quite grateful.

... but it seems I already solved it.

 

I ended up using texture sets because it seems all this normals problem is quite common (even Slave Tats had run into this very problem).

But even that solution wasn't straightforward. I struggled the rest of the day trying to find how to make work that.

 

Here's the code for whoever comes here searching for this.

Oh, and I freely include a method to get the player skin color/tint/whatever, because I neither found that by googling and I spent MANY frustrating hours on that.

 

; You should use a property to get this texture set. This is just for testing.
TextureSet tx = Game.GetFormFromFile(0x01000800, "SandowPP - Ripped Bodies.esp") as TextureSet

; Index is irrelevant for all these specific operations. It's **somewhat** documented in the NiOverride source code.
int irrelevant = -1

; It NEEDS to be this override layer (is that called like that? No info anywhere). Don't ask me why it doesn't work with other nodes, like "Body [Ovl0]" et al.
string node = "Body [Ovl5]"

; Get the skin tint color of the Actor to reapply it soon
int skinColor = NiOverride.GetSkinPropertyInt(player, false, 4, 7, -1)

; Add the texture set we want to show
NiOverride.AddNodeOverrideTextureSet(Player, true, node, 6, irrelevant, tx, true)

; Last operation resets the skin tint color to white, making the character's body pale. Restore the color we got earlier.
NiOverride.AddNodeOverrideInt(Player, true,  node, 7, irrelevant, skinColor, true)

; Profit! Have a nice day.

 

Link to comment
  • 8 months later...
On 5/8/2020 at 2:35 AM, Papitas said:

; It NEEDS to be this override layer (is that called like that? No info anywhere). Don't ask me why it doesn't work with other nodes, like "Body [Ovl0]" et al. string node = "Body [Ovl5]"

Sorry to necro a thread, but I did some testing on this myself, and I found out that increasing the number of overlays (iNumOverlays in nioverride.ini, I used 20) allows you to use any layer for this.

Link to comment
  • 2 weeks later...
On 1/25/2021 at 5:26 PM, Code Serpent said:

Sorry to necro a thread, but I did some testing on this myself, and I found out that increasing the number of overlays (iNumOverlays in nioverride.ini, I used 20) allows you to use any layer for this.

 

Yep. I found about the ini file waaaaaaaay later xD

 

BTW, now I know more about this and those are layers indeed, with bigger numbers applied over smaller numbers.

And I found it wasn't obligatory to use Ovl5, but you may get some incompatibilities using Ovl0 and such, because you can expect some mods to use that layer so other layers can be applied over it.

Link to comment
  • 5 months later...
On 5/8/2020 at 4:35 AM, Papitas said:

Oh, and I freely include a method to get the player skin color/tint/whatever, because I neither found that by googling and I spent MANY frustrating hours on that.

 

Sorry about replying on an old post, but I'm trying to work on something similar to what you are describing. Everything is working but the tint:

 

I used your described method (with saving and then setting the skin tint), and that worked to match the tint from a value perspective (same colorform in Racemenu). However, I'm still getting a clear neck and hand seam.

 

For reference, this is even occurring when I set my TextureSet to the base body (SkinBodyFemale_1). Am I missing a step here? Were you able to get no seams with just the code above? It seems like overlays handle lighting differently than the base body or something.

Link to comment
3 hours ago, Bozingar said:

 

Sorry about replying on an old post, but I'm trying to work on something similar to what you are describing. Everything is working but the tint:

 

I used your described method (with saving and then setting the skin tint), and that worked to match the tint from a value perspective (same colorform in Racemenu). However, I'm still getting a clear neck and hand seam.

 

For reference, this is even occurring when I set my TextureSet to the base body (SkinBodyFemale_1). Am I missing a step here? Were you able to get no seams with just the code above? It seems like overlays handle lighting differently than the base body or something.

 

Oh, I know what you're talking about.

It seems sometimes things just go awry with skin tints. 

 

Have you tried to log the actual tint value (instead of checking it in Racemenu)?

 

It seems the value is sometimes "just lost" and I had to do a convoluted method consisting in if checks, storing color to a script variable, forcing the same color on face, hands and body every <n> seconds, and such kind of things, to restore the original skin color.

Still, I would sometimes get lighter hands, even if everything was supposed to be as I wanted; but sometimes I got everything alright.

 

I've also noticed Skyrim's own lighting rendering seems to mess with hands/feet colors under certain circumstances, but that doesn't seem to be your issue. 

 

I'll be honest: I've almost given up on that random coloring mismatching issue and just throw away all what I did.

Lack of documentation and just stumbling blind for months can really take a toll on one's mind. 

Link to comment
  • 2 weeks later...
On 7/8/2021 at 6:08 PM, Papitas said:

I'll be honest: I've almost given up on that random coloring mismatching issue and just throw away all what I did.

Lack of documentation and just stumbling blind for months can really take a toll on one's mind. 

 

I think I managed to get a method to work! It uses Overrides instead of Overlays, but almost everything is chugging away perfectly now. It uses this function below:

 

NiOverride.AddSkinOverrideTextureSet(akActor, True, False, 4, 6, -1, AdaptedBodyFemaleArgonian[i], True)

 

Some things to note:

  1. This doesn't persist on actors once they have been unloaded, so a polling script would need to be keyed off of that.
  2. As this grabs a TextureSet, you need to include all skin texture files, even if just changing the Normal. (This means conditionals and additional TextureSets will be needed for beast races, etc.)
  3. This has an odd bug when called on a naked actor, also overriding the hands and feet (which messes up the hands, as they aren't on the body texture file). This can be worked around by equipping body armor before this function is called (which can just be a naked torso), then unequipping it.
    1. My guess is it grabs all biped objects of the 32 slotmask, swapping all their texture files. When naked, this would grab the SkinNaked armor, which also contains the hands and feet.
  4. I've come across another strange bug specifically with SOS. I've posted about it here: https://www.loverslab.com/topic/172537-odd-sosnioverride-racemenu-interactionbug/ 

Other than the notes above, everything seems to work fine! I'm almost done with my mod that uses this method, just trying to work out compatibility with SOS. Please let me know if you test it out, and if it works for you! I had to do quite a bit of troubleshooting before it behaved as intended.

Link to comment

About the hands and such: I used a method where I set normal maps as overrides and then I just went pragmatic and set a hand overlay over the hands, so the overlay covered that bug you mention.

That's where I used that sorcery about saving the skin color and blah...

 

You can see my code here if you like.

You can copy whatever you want. I made the code public for a reason :)

 

Right now I found a better method that seems to be it's compatible with anything and it actually works.

Instead of using NiOverride for normal maps, I use texture lists and

 

ActorBase.SetSkin(armor)

 

to change normal maps on bodies.

 

That's an upcomming mod I'm working on. You can see my code here.

Lines 20 to 24 are the ones that might interest you.

 

If you take a look at the esp in the CK or SSEdit, you will see where those armors are coming from.

 

I still don't know if those bodies work on Argonians (I'm quite sure they don't), but it shouldn't be too hard to make them work. Just add some Armor Addons and that's it.

Link to comment
  • 4 weeks later...

This seems useful for what I'm trying to do, but I can't seem to figure oput how it's done... So, what I'm trying to do is create fur overlays . Last year I started to port the MaTera from Oldrim over to SSE, and ended up completely rebuilding it from the ground up. To save myself the pain of having to make the overlays for all the different skin options for both CBBE and UNP body types, I've been trying to figure out how to do it as an overlay, because there's 30 different colours, multiply that by 5 common skin options (The base, The Pure, Fair Skin, Tempered Skins, and Bijin Skins) for both body types, I'd be looking at making at least 300 different skin textures for just the diffuse. If we're talking the normal and speculars, I'm dealing with at least 900 images here...Hence the overlay route to save my sanity.

 

The textures consist of a diffuse, normal map and a specular map, and the fur (on the upper half) covers the entire arm up to a bit before the shoulder, and on the lower half, from the mid thigh down is just fur., for a mental image of what I'm trying to do here. I initially started looking into using RaceMenu overlays, then realised they were diffuse only, which kind of threw a wrench into the plan for the 3 textures. After enough searching around, I found this post. 

 

Now, where this diverges from the idea that this is trying to do is I only need it for one race (The Matera). What I'm unsure about is armour. I have no idea if that method will work, because it's not a full image overlay, only a partial overlay. 

Link to comment

I would save myself quite a lot of pain and just straight apply up the fur overlays on the first overlay layer; the one named "Body [Ovl0]".

Something like this:

 

NiOverride.AddNodeOverrideString(myActor, isFemale, "Body [Ovl0]", 6, 0, "data/textures/etc/diffuse.dds", true)
NiOverride.AddNodeOverrideString(myActor, isFemale, "Body [Ovl0]", 6, 1, "data/textures/etc/normal.dds", true)
NiOverride.AddNodeOverrideString(myActor, isFemale, "Body [Ovl0]", 6, 7, "data/textures/etc/specular.dds", true)

 

You must beware doing that will overwrite whatever tattoo your users have on that layer, so you would need to warn them they must transfer their tattoos to some other layer.

 

It will also be incompatible with mods that use that layer.

 

You may also try other nodes, like "Body [Ovl1]", "Body [Ovl2]"... but you are only almost guaranted to get up to "Body [Ovl5]", since those are the default number of layers for NiOverride.

Just be careful while doing that, because those are something like Photoshop layers and whatever you put in them, hides whatever is below them.

By the nature of your textures, I think it's not actually a bad option.

 

And don't you try to use Skin Overrides, which are actually directly applied to the body instead of some invisible layer, thinking they are the solution to your problems.

 

If you apply diffuse or normal skin overrides to the body, your body textures will get applied to the hands as well; giving you pizza hands:

 

pizza-hands-diffuse.thumb.jpg.d45c3b7169ebd1f988f055a6df3f84cc.jpg

 

pizza-hands.jpg.c5514e0748f5152cdff7f0174702aa73.jpg

 

That's the problem that originally brought me here and I never got around to fix it.

I just ditched the idea and now use an actually working method that doesn't work for what you want to do.

 

BTW, I don't think this double texture problem will ever be fixed.

My guess is that it's related to the new SSE nif optimizations.

 

Edited by Papitas
Typos
Link to comment
  • 1 month later...
On 8/26/2021 at 1:47 AM, Papitas said:

I would save myself quite a lot of pain and just straight apply up the fur overlays on the first overlay layer; the one named "Body [Ovl0]".

Something like this:

 

NiOverride.AddNodeOverrideString(myActor, isFemale, "Body [Ovl0]", 6, 0, "data/textures/etc/diffuse.dds", true)
NiOverride.AddNodeOverrideString(myActor, isFemale, "Body [Ovl0]", 6, 1, "data/textures/etc/normal.dds", true)
NiOverride.AddNodeOverrideString(myActor, isFemale, "Body [Ovl0]", 6, 7, "data/textures/etc/specular.dds", true)

 

You must beware doing that will overwrite whatever tattoo your users have on that layer, so you would need to warn them they must transfer their tattoos to some other layer.

 

It will also be incompatible with mods that use that layer.

 

You may also try other nodes, like "Body [Ovl1]", "Body [Ovl2]"... but you are only almost guaranted to get up to "Body [Ovl5]", since those are the default number of layers for NiOverride.

Just be careful while doing that, because those are something like Photoshop layers and whatever you put in them, hides whatever is below them.

By the nature of your textures, I think it's not actually a bad option.

 

And don't you try to use Skin Overrides, which are actually directly applied to the body instead of some invisible layer, thinking they are the solution to your problems.

 

If you apply diffuse or normal skin overrides to the body, your body textures will get applied to the hands as well; giving you pizza hands:

 

pizza-hands-diffuse.thumb.jpg.d45c3b7169ebd1f988f055a6df3f84cc.jpg

 

pizza-hands.jpg.c5514e0748f5152cdff7f0174702aa73.jpg

 

That's the problem that originally brought me here and I never got around to fix it.

I just ditched the idea and now use an actually working method that doesn't work for what you want to do.

 

BTW, I don't think this double texture problem will ever be fixed.

My guess is that it's related to the new SSE nif optimizations.

 

 

 

So.. I got things to work eventually. I'm gonna be honest, I have no idea how the hell to describe it, nor explain it, but it works. By some miracle, it works. 

 

Here's my git repo of it: https://github.com/FoxinTale/Matera-Reborn/tree/main
And a YouTube video of the changes in action: 

 

I tried to document my scripts as well as possible, but if you have questions feel free to ask. 

Link to comment
38 minutes ago, FoxinTale said:

 

 

So.. I got things to work eventually. I'm gonna be honest, I have no idea how the hell to describe it, nor explain it, but it works. By some miracle, it works. 

 

Here's my git repo of it: https://github.com/FoxinTale/Matera-Reborn/tree/main
And a YouTube video of the changes in action: 

 

I tried to document my scripts as well as possible, but if you have questions feel free to ask. 

 

Great. Thanks for sharing your code!

I'll give it a check whenever I have time.

 

I've been writing everything I've discovered from NiOverride so far.

I suppose you don't need it anymore, but I still leave it here for reference.

 

Things I've discovered so far:

  • Everything about nodes.
  • Skin functions.
  • I've solved the problem that originally brought me here (turned out it was node related), but I haven't written about it yet.

 

I also think what you did may help me expand it :)

Edited by Papitas
Link to comment
22 hours ago, Papitas said:

 

Great. Thanks for sharing your code!

I'll give it a check whenever I have time.

 

I've been writing everything I've discovered from NiOverride so far.

I suppose you don't need it anymore, but I still leave it here for reference.

 

Things I've discovered so far:

  • Everything about nodes.
  • Skin functions.
  • I've solved the problem that originally brought me here (turned out it was node related), but I haven't written about it yet.

 

I also think what you did may help me expand it :)

 

It may still prove useful, as I read over it earlier. I'm now attempting a rewrite of my mod on a different branch based off what I read. I've also documented all my attempts, failures and frustrations with the poorly documented NiOverride and nodes in various parts of the scripts. I even went with a similar approach of looking at the source of skee.dll, and attempted to make sense of it. Given that my C++ knowledge is limited, it went about as well as one would expect. Even if it doesn't help me all too much, it will help out other poor sods trying to do similar things. 

 

Alos have to say. Love the overall tone of the website. It gives a very "I've been through some programming hell, but made it through. Here's my journey." type vibe to me. The true minimalism aspect is nice too. Only display what is important, and if it looks pretty that's just a bonus, not a requirement.

Edited by FoxinTale
Link to comment
18 hours ago, FoxinTale said:

 

It may still prove useful, as I read over it earlier. I'm now attempting a rewrite of my mod on a different branch based off what I read. I've also documented all my attempts, failures and frustrations with the poorly documented NiOverride and nodes in various parts of the scripts. I even went with a similar approach of looking at the source of skee.dll, and attempted to make sense of it. Given that my C++ knowledge is limited, it went about as well as one would expect. Even if it doesn't help me all too much, it will help out other poor sods trying to do similar things. 

 

Alos have to say. Love the overall tone of the website. It gives a very "I've been through some programming hell, but made it through. Here's my journey." type vibe to me. The true minimalism aspect is nice too. Only display what is important, and if it looks pretty that's just a bonus, not a requirement.

 

I've seen your code and it's obvious you have been through hell as well xD

 

And I've also noticed you solved the goddamn nodes-lack-of-info problem and found what to actually input in there. 

You also inadvertently solved The Pizza Hand Syndrome when checking for nails in hands! 

 

It's quite amazing how we arrived to the same conclusions by taking totally different paths. 

You have even the same hatred for Papyrus I have xD

 

Btw, you might want to give Skyrim Platform a chance. 

The prospect of ditching Papyrus forever and use a real programming language instead (Typescript) is nothing to scoff at. 

It's also way damn fast. It feels like you are doing an intelligible C++ plugin. 

 

About your repeated code: have you tried to parameterize all the function? 

I mean, instead of using properties inside the function, you use function arguments and let the script that carries the actual info to supply all info via those function arguments. 

Link to comment
2 hours ago, Papitas said:

 

I've seen your code and it's obvious you have been through hell as well xD

 

And I've also noticed you solved the goddamn nodes-lack-of-info problem and found what to actually input in there. 

You also inadvertently solved The Pizza Hand Syndrome when checking for nails in hands! 

 

It's quite amazing how we arrived to the same conclusions by taking totally different paths. 

You have even the same hatred for Papyrus I have xD

 

Btw, you might want to give Skyrim Platform a chance. 

The prospect of ditching Papyrus forever and use a real programming language instead (Typescript) is nothing to scoff at. 

It's also way damn fast. It feels like you are doing an intelligible C++ plugin. 

 

About your repeated code: have you tried to parameterize all the function? 

I mean, instead of using properties inside the function, you use function arguments and let the script that carries the actual info to supply all info via those function arguments. 

Figuring out the lack of nodes came from a small hint in the NiOverride log, that said "Cannot find node, try passing an empty string. Maybe it's the root node?" Or something along that line. I don't quite remember what it fully was.

 

I've been looking into Skyrim Platform and it seems really promising. I'm willing to learn TypeScript for this, just a matter of what method is best. Papyrus is what it is, a scripting language, not a fully fledged programming language (I wonder if it's Turing complete though. Hm...)

 

As for the parameterization, I'm not sure how to do that, if I'm being honest. I think I tried doing that, but may have done so the wrong way. 

My strongest programming language is Java, followed by C#...I'm not a beginner, but I'm not advanced either.

Link to comment
On 10/20/2021 at 10:17 AM, FoxinTale said:

As for the parameterization, I'm not sure how to do that, if I'm being honest. I think I tried doing that, but may have done so the wrong way. 

My strongest programming language is Java, followed by C#...I'm not a beginner, but I'm not advanced either.

 

Ah, it's just putting variables as function parameters.

 

Say...

 

Scriptname MateraRaceMenuScript extends RaceMenuBase

; I do have plans for multiple tail types.
Function SetTailColour(Actor akActor, bool isFem, Armor arm, ArmorAddon addon, TextureSet tex, int tailType) Global
  ; Beta Matera (Tail type 0) node: "TailM", Original Matera (Tail Type 1)Node: "Albino"
  ; Maybe other tail types in the future. If I can figure out how to have swappable tails, that would be fantastic.

  If(tailType == 0)
    AddOverrideTextureSet(akActor, isFem, arm, addon, "TailM", 6, -1, tex, true)
		
  ElseIf(tailType == 1)
    AddOverrideTextureSet(akActor, isFem, arm, addon, "Albino", 6, -1, tex, true)
  EndIf
EndFunction

 

And you call it from MateraColourChangeScript by doing

 

MateraRaceMenuScript.SetTailColour(PlayerRef, true, MateraBody, MateraTail, TailTexture, TailType)

 

As you can see, I extracted all the variables from the function body and put them as the function parameters.

 

Notice how I appended the Global keyword at the end of the function definition.

I don't quite remember both Java or C#, but if I'm not mistaken, that Global keyword is the equivalent to static.

It makes a function a "class function"; accessible without the need of having instances of that class.

 

 

By the way, how is persistence working with AddOverrideTextureSet?

Is it actually remembering which textures you did override armors with?

Edited by Papitas
Link to comment
1 hour ago, Papitas said:

 

Ah, it's just putting variables as function parameters.

 

Say...

 

Scriptname MateraRaceMenuScript extends RaceMenuBase

; I do have plans for multiple tail types.
Function SetTailColour(Actor akActor, bool isFem, Armor arm, ArmorAddon addon, TextureSet tex, int tailType) Global
  ; Beta Matera (Tail type 0) node: "TailM", Original Matera (Tail Type 1)Node: "Albino"
  ; Maybe other tail types in the future. If I can figure out how to have swappable tails, that would be fantastic.

  If(tailType == 0)
    AddOverrideTextureSet(akActor, isFem, arm, addon, "TailM", 6, -1, tex, true)
		
  ElseIf(tailType == 1)
    AddOverrideTextureSet(akActor, isFem, arm, addon, "Albino", 6, -1, tex, true)
  EndIf
EndFunction

 

And you call it from MateraColourChangeScript by doing

 

MateraRaceMenuScript.SetTailColour(PlayerRef, true, MateraBody, MateraTail, TailTexture, TailType)

 

As you can see, I extracted all the variables from the function body and put them as the function parameters.

 

Notice how I appended the Global keyword at the end of the function definition.

I don't quite remember both Java or C#, but if I'm not mistaken, that Global keyword is the equivalent to static.

It makes a function a "class function"; accessible without the need of having instances of that class.

 

 

By the way, how is persistence working with AddOverrideTextureSet?

Is it actually remembering which textures you did override armors with?

Ah, that's what I was doing wrong! I was so, *so* close, if you check the commented out bits in the color change script, I had "mrms.<function>()", where mrms was a defined script property close to the top (also commented out in the same script).

 

I was trying to attach an object via a property in the CK for this, but it was greyed out and wouldn't let me. This led to "cannot call <function> on a None object", which seems to me it's the equivalent of a null pointer. I am hoping it'll let me use the getters and setters globally. That, would make my life so much easier.  (I played around with this for a bit, and it seems it complains the variables are not defined, when I'm pretty sure they are. When I change it to <data type> Property <name> Global", it's satisfied, but this seems like bad practice to me...goddammit Papyrus.) Keep an eye on the rewrite branch on Github, as that's where all my latest changes are going.

 

Now, as for persistence, I have it coded to behave as if there is no persistence.  When the game is reloaded, it reapplies the current textures. When a new armor is applied, it does its thing. If it's unequipped, I use an "OnUnequipped" type event to reapply to the skin. If the same armor is reequipped, I reapply it. This method works pretty well so far. After reading a note on your site about how persistence can cause save bloat, I changed the "persist" parameter from true to false.  I had already written it to behave as if persistence doesn't work, as quite honestly I don't trust it to work, and wasn't too sure if it could have adverse effects further down the line. 

 

I'm also streamlining this code for the part two of it. Which is dealing with UNP, and then males. CBBE is nice in that the body node is the same, since it replaces the game's own armor meshes. UNP...does not. The node name is different for a fair amount of them.  I'm not looking forward to that. Probably going to need to create a string array of every body node name I've found (I'll have to comb through each and every one...) and search it. The male body and armor will be a similar process.  I have no idea how long that'll take in terms or processing, but only one way to find out. 

Edited by FoxinTale
Link to comment
2 hours ago, FoxinTale said:

Ah, that's what I was doing wrong! I was so, *so* close, if you check the commented out bits in the color change script, I had "mrms.<function>()", where mrms was a defined script property close to the top (also commented out in the same script).

 

I was trying to attach an object via a property in the CK for this, but it was greyed out and wouldn't let me. This led to "cannot call <function> on a None object", which seems to me it's the equivalent of a null pointer. I am hoping it'll let me use the getters and setters globally. That, would make my life so much easier.  (I played around with this for a bit, and it seems it complains the variables are not defined, when I'm pretty sure they are. When I change it to <data type> Property <name> Global", it's satisfied, but this seems like bad practice to me...goddammit Papyrus.) Keep an eye on the rewrite branch on Github, as that's where all my latest changes are going.

 

Now, as for persistence, I have it coded to behave as if there is no persistence.  When the game is reloaded, it reapplies the current textures. When a new armor is applied, it does its thing. If it's unequipped, I use an "OnUnequipped" type event to reapply to the skin. If the same armor is reequipped, I reapply it. This method works pretty well so far. After reading a note on your site about how persistence can cause save bloat, I changed the "persist" parameter from true to false.  I had already written it to behave as if persistence doesn't work, as quite honestly I don't trust it to work, and wasn't too sure if it could have adverse effects further down the line. 

 

I'm also streamlining this code for the part two of it. Which is dealing with UNP, and then males. CBBE is nice in that the body node is the same, since it replaces the game's own armor meshes. UNP...does not. The node name is different for a fair amount of them.  I'm not looking forward to that. Probably going to need to create a string array of every body node name I've found (I'll have to comb through each and every one...) and search it. The male body and armor will be a similar process.  I have no idea how long that'll take in terms or processing, but only one way to find out. 

 

Yeah. That's another thing I don't like about Papyrus. 

It was obviously made for people that don't know how to code. 

 

If you come from an OOP background, their scripts seem to be class declarations, but they actually aren't. 

Scripts are more like a strange blend between Unity scripts and function libraries. 

 

They are more like behavior add-ons that you can attach to Forms than real classes with instances and whatnot. 

 

About persistence, it seems to work best for skin functions and node layer overrides. 

If you use persistence for only those cases and only on the player, you won't get bloat, but if you apply them to armors that will be unequipped over and over (and they don't remember they were "overrided") or NPCs you will surely bloat your players games pretty fast. 

 

Before I dealt with those cases, I managed to severely bloat my game in only 2 game days (around 40 minutes of real playing) . 

But I applied both morph and texture overrides to all NPCs I was getting close to. 

 

And... yeah... finding all names for all nodes is quite a pain in the ass. 

You may want to try to use skin functions, since those don't depend on node names. 

Link to comment
51 minutes ago, Papitas said:

 

Yeah. That's another thing I don't like about Papyrus. 

It was obviously made for people that don't know how to code. 

 

If you come from an OOP background, their scripts seem to be class declarations, but they actually aren't. 

Scripts are more like a strange blend between Unity scripts and function libraries. 

 

They are more like behavior add-ons that you can attach to Forms than real classes with instances and whatnot. 

 

About persistence, it seems to work best for skin functions and node layer overrides. 

If you use persistence for only those cases and only on the player, you won't get bloat, but if you apply them to armors that will be unequipped over and over (and they don't remember they were "overrided") or NPCs you will surely bloat your players games pretty fast. 

 

Before I dealt with those cases, I managed to severely bloat my game in only 2 game days (around 40 minutes of real playing) . 

But I applied both morph and texture overrides to all NPCs I was getting close to. 

 

And... yeah... finding all names for all nodes is quite a pain in the ass. 

You may want to try to use skin functions, since those don't depend on node names. 

 

How much bloat are we talking, out of curiosity, few megabytes, tens, hundreds? Though, yeah. this is player only, not NPCs.  I assume you mean the functions like "AddSkinOverrideTextureSet", and the like. I think I tried that at some point, and it didn't work. Though, that was before I understood keys and indexes and more importantly NiOverride itself. No harm in going back and giving that a try, seeing if it works. Worst case, I have to go visit programming Satan for the pieces of sanity Papyrus stole use the original method I was planning on.  I think the other issue I was having was with the slotmasks, though I have since figured out how to get the given slotmasks of a piece of armor. 

Link to comment
On 10/21/2021 at 6:28 PM, FoxinTale said:

 

How much bloat are we talking, out of curiosity, few megabytes, tens, hundreds? Though, yeah. this is player only, not NPCs.  I assume you mean the functions like "AddSkinOverrideTextureSet", and the like. I think I tried that at some point, and it didn't work. Though, that was before I understood keys and indexes and more importantly NiOverride itself. No harm in going back and giving that a try, seeing if it works. Worst case, I have to go visit programming Satan for the pieces of sanity Papyrus stole use the original method I was planning on.  I think the other issue I was having was with the slotmasks, though I have since figured out how to get the given slotmasks of a piece of armor. 

 

About 500 KB. 

It wasn't that much in disk space usage, but it was quite a lot in saving times. 

A quick save took around 7 seconds (on a freaking M2 disk. So I know it's not hardware related) . 

 

In less than an hour I got the saving time slowness I would have got after 50-70 playing hours. 

 

I don't know why, but the bloat caused by NiOverride seems to make saving times way slower. 

 

I regularly use a tool to clean co-saves and it shaves at max 500 KB, but that's enough to make saves nearly instantaneous again. 

And I have my suspicion most of that data is NiOverride related. 

 

--------

 

And yeah... I'm talking about those kind of functions. 

The good thing about them is that they actually persist without unintentionally bloating your save games.

Edited by Papitas
Link to comment
  • 3 weeks later...

@Papitas

Yo, idk if you're still looking for a solution for pizza hands, but here's mine, an excerpt from my mod.
 

	; Update normal maps, should be ignored if no texture at path
	string bodyTexPath = GetMusclePath("body", muscle)
	string handTexPath = GetMusclePath("hands", muscle)
	if MiscUtil.FileExists("data\\" + handTexPath)
		NiOverride.AddSkinOverrideString(ActorRef, isFemale, 0, 0x08, 9, 1, handTexPath, true) ;Hands
	endif
	if MiscUtil.FileExists("data\\" + bodyTexPath)
		NiOverride.AddSkinOverrideString(ActorRef, isFemale, 0, 0x80, 9, 1, bodyTexPath, true) ;Feet
		NiOverride.AddSkinOverrideString(ActorRef, isFemale, 0, 0x04, 9, 1, bodyTexPath, true) ;Body
	endif
	; The simplest fix I could find for pizza hands
	if !ActorRef.GetEquippedArmorInSlot(33)
		Debug.TraceUser("SkillBasedMuscle", "GetEquippedArmor    " + name)
		ActorRef.EquipItem(PizzaNapkin, 0, 1)
		ActorRef.RemoveItem(PizzaNapkin, 1, 1)
	endif

 

The armor getting equipped is a dummy armor I included in the esp. Not perfectly seamless, but it only occurs when you're not wearing gloves and it's only noticeable if you're staring at the hands when the skin is getting updated.

 

Also thanks a ton for the documentation, it helped immensely.

Edited by jjabrams420
Link to comment
7 hours ago, jjabrams420 said:

@Papitas

Yo, idk if you're still looking for a solution for pizza hands, but here's mine, an excerpt from my mod.
 

	; Update normal maps, should be ignored if no texture at path
	string bodyTexPath = GetMusclePath("body", muscle)
	string handTexPath = GetMusclePath("hands", muscle)
	if MiscUtil.FileExists("data\\" + handTexPath)
		NiOverride.AddSkinOverrideString(ActorRef, isFemale, 0, 0x08, 9, 1, handTexPath, true) ;Hands
	endif
	if MiscUtil.FileExists("data\\" + bodyTexPath)
		NiOverride.AddSkinOverrideString(ActorRef, isFemale, 0, 0x80, 9, 1, bodyTexPath, true) ;Feet
		NiOverride.AddSkinOverrideString(ActorRef, isFemale, 0, 0x04, 9, 1, bodyTexPath, true) ;Body
	endif
	; The simplest fix I could find for pizza hands
	if !ActorRef.GetEquippedArmorInSlot(33)
		Debug.TraceUser("SkillBasedMuscle", "GetEquippedArmor    " + name)
		ActorRef.EquipItem(PizzaNapkin, 0, 1)
		ActorRef.RemoveItem(PizzaNapkin, 1, 1)
	endif

 

The armor getting equipped is a dummy armor I included in the esp. Not perfectly seamless, but it only occurs when you're not wearing gloves and it's only noticeable if you're staring at the hands when the skin is getting updated.

 

Also thanks a ton for the documentation, it helped immensely.

 

Thanks! I found the solution about two months ago, but haven't written about it.

What I'm doing is not so different than what you did and you basically know what technical issue causes the problem.

 

Here's the quick answer:

  • When you get naked and you aren't using gauntlets, hands nodes disappear and fuse with the body, so whatever texture you apply to the body gets applied to the hands.
  • The solution is to equip a naked hands armor whenever an NPC or your PC has no gauntlets equipped. That way you can be sure there will always be a hands node.

The only remaining "problem" is how to equip those hands on NPCs.

It's quite easy to setup an alias for the player and react to OnObjectUnequipped event, but it's not as easy doing that for NPCs.

 

... that is, unless you are using Skyrim Platform to make your mod.

It's trivial to react to (un)equip events for both PC and NPCs in there, so solving The Pizza Hands Syndrome for everyone is quite easy in there.

 

I'm glad whatever I've been writing is being of help. I'll write more things when I get some time to do it.

Right now I also know about applying body morphs using NiOverride and want to write about it.

Link to comment
8 hours ago, Papitas said:

 

Thanks! I found the solution about two months ago, but haven't written about it.

What I'm doing is not so different than what you did and you basically know what technical issue causes the problem.

 

Here's the quick answer:

  • When you get naked and you aren't using gauntlets, hands nodes disappear and fuse with the body, so whatever texture you apply to the body gets applied to the hands.
  • The solution is to equip a naked hands armor whenever an NPC or your PC has no gauntlets equipped. That way you can be sure there will always be a hands node.

The only remaining "problem" is how to equip those hands on NPCs.

It's quite easy to setup an alias for the player and react to OnObjectUnequipped event, but it's not as easy doing that for NPCs.

 

... that is, unless you are using Skyrim Platform to make your mod.

It's trivial to react to (un)equip events for both PC and NPCs in there, so solving The Pizza Hands Syndrome for everyone is quite easy in there.

 

I'm glad whatever I've been writing is being of help. I'll write more things when I get some time to do it.

Right now I also know about applying body morphs using NiOverride and want to write about it.

I just used SPID to distribute a spell with the script attached, it fixes the pizza hands for NPCs too. I'm not too familiar with Skyrim Platform, is it like a new coding language for Skyrim?

Link to comment
21 minutes ago, jjabrams420 said:

I just used SPID to distribute a spell with the script attached, it fixes the pizza hands for NPCs too. I'm not too familiar with Skyrim Platform, is it like a new coding language for Skyrim?

 

Yep, I tried the spell with SPID for the Unequip event, but didn't work for me :S

 

Skyrim Platform is a framework that let's you make mods using Typescript instead of Papyrus. 

I said this in my page and I still hold to it: it has brought to us, the unwashed masses, the kind of power only the legendary beings that can do C++ DLLs have. 

 

Mods done in it are at least 600 times faster that things done in Papyrus, according to my testing and personal experience. 

 

And... you know... Typescript is a real programming language, unlike Papyrus :P

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use