Editing an NPC (Hair Mesh, Hair Eye Colours etc)

So there are several ways to edit properties of an NPC.

If they have unique meshes, materials (for eyes, hair colour and such) then the best method is directly replacing those assets instead of editing their CharacterVisuals file.

This way mods that do need to edit that file will be compatible with your mod if those assets' unique identifiers are not changed.

Character Visuals

So one of the main places where NPC's have their hair, skin, eyes and colours all defined is in the CharacterVisuals. And not all NPC’s have unique assets so if you were to change the eye colour directly rather than swap to another that eye colour would change for all the characters that use it.

So some Pros and Cons of using CharacterVisuals to edit the assets used.

Pros:

  • If doing more than one edit to a character then it is editing one place rather than several.

Cons:

  • Two mods can't edit the same character in the charactervisuals.
  • Though you can add dependency so if there would be issues both mods would work together. Which is the second mod copies the other and makes their edits to it like you would the vanilla files. And then still require the original mod.

Location of CharacterVisuals in the game files.

Public\Shared\Content\[PAK]_CharacterVisuals\ _merged.lsf
Public\SharedDev\Content\[PAK]_CharacterVisuals\ _merged.lsf
Public\Gustav\Content\[PAK]_CharacterVisuals\ _merged.lsf
Public\GustavDev\Content\[PAK]_CharacterVisuals\ _merged.lsf

Gustav\Public\Honour\Content\[PAK]_CharacterVisuals\_merged.lsf

Characters will be split across these files and some like Companions across multiple.

With multitool index search:

Change the drop down to lsf and lsx only.

Then search for CharacterVisuals

Now tick select all and then extract selected. Open each of these in your choice of program whether it is notepad++ or vscode. This way you can do quicker searches for the ids for the characters when we get to it.

Locating the in file name of a Character

Some characters do go by different names rather than directly their name. for example Minthara in CharacterVisuals goes by DrowCommander. So we have to do some backwards searching to find the IDs we need to find the character we want.

To find their in file name we have to search in the .loca of your language. In this tutorial we will be using English.loca (converted to xml) for their name.

English\Localization\English\english.loca

Sometimes they are multiple times in the localisation file so you may need to do multiple searches until you find the correct one.

Just be aware the localisation file is full of spoilers.

Ctrl + F and type Name< so for example searching for Minthara I would search with Minthara<

In this case it comes up with 13 so we want to copy the handle. The section in the "" which is the contentuid is what we need:

    <content contentuid="h2fe1a8e4geec5g49afg8d93g05fb2b0d166d" version="1">Minthara</content>

Then you want to paste this contentuuid into multitool index search and find what it comes up with. What we want is a file that also lists the “CharacterVisualResourceID” in the same section that handle is listed.

In this case it came up with:

Since it has Tags in the file path I know it isn’t the right file, as this just leads to the Tag the game uses to define it really is Minthara. See below:

<?xml version="1.0" encoding="utf-8"?>

<save>

    <version major="4" minor="0" revision="7" build="405" lslib_meta="v1,bswap_guids" />

    <region id="Tags">

        <node id="Tags">

            <attribute id="Description" type="LSString" value="" />

            <attribute id="DisplayDescription" type="TranslatedString" handle="ls::TranslatedStringRepository::s_HandleUnknown" version="0" />

            <attribute id="DisplayName" type="TranslatedString" handle="h2fe1a8e4geec5g49afg8d93g05fb2b0d166d" version="1" />

            <attribute id="Icon" type="FixedString" value="" />

            <attribute id="Name" type="FixedString" value="REALLY_MINTHARA" />

            <attribute id="UUID" type="guid" value="3e84e1cd-2193-4f9f-80b4-c2ededefaea6" />

            <children>

                <node id="Categories">

                    <children>

                        <node id="Category">

                            <attribute id="Name" type="LSString" value="Code" />

                        </node>

                        <node id="Category">

                            <attribute id="Name" type="LSString" value="Dialog" />

                        </node>

                        <node id="Category">

                            <attribute id="Name" type="LSString" value="DialogHidden" />

                        </node>

                    </children>

                </node>

            </children>

        </node>

    </region>

</save>

So first we can see there is no “CharacterVisualResourceID” so this isn’t the correct reference we are looking for.

So we go back to the .loca and search for the next reference until we find our file with the “CharacterVisualResourceID”

In Minthara case it was     <content contentuid="h41f087cdg0f25g4e26g8f40g25f3d7feaeb4" version="1">Nightwarden Minthara</content>

This leads to: Gustav\Mods\Gustav\Globals\WLD_Main_A\Characters\_merged.lsf

                    <attribute id="AnubisConfigName" type="FixedString" value="GLO_DrowCommander_Act1" />

                    <attribute id="Archetype" type="FixedString" value="melee_smart" />

                    <attribute id="CharacterVisualResourceID" type="FixedString" value="383236f9-c7ab-7163-4638-d2e2702ec513" />

                    <attribute id="DisplayName" type="TranslatedString" handle="h41f087cdg0f25g4e26g8f40g25f3d7feaeb4" version="1" />

                    <attribute id="Equipment" type="FixedString" value="GOB_DrowCommander" />

This is only a snippet of the top section which shows both the handle and the CharacterVisualResourceID. So we take the

                    <attribute id="CharacterVisualResourceID" type="FixedString" value="383236f9-c7ab-7163-4638-d2e2702ec513" />

The uuid from this line which is “383236f9-c7ab-7163-4638-d2e2702ec513” and search for that in our CharacterVisuals files.

Which leads to: Public\Gustav\Content\[PAK]_CharacterVisuals\_merged.lsf

                <node id="Resource">

                    <attribute id="BaseVisual" type="FixedString" value="6f9de444-153f-9825-eef7-e18d6de55bbf" />

                    <attribute id="BodySetVisual" type="FixedString" value="9e43f0e4-d063-d85c-87b4-470d569645db" />

                    <attribute id="ID" type="FixedString" value="383236f9-c7ab-7163-4638-d2e2702ec513" />

                    <attribute id="Name" type="LSString" value="S_GOB_DrowCommander_25721313-0c15-4935-8176-9f134385451b" />

                    <attribute id="ShowEquipmentVisuals" type="bool" value="True" />

                    <attribute id="_OriginalFileVersion_" type="int64" value="144115207403209034" />

                    <children>

This is the first section of Minthara’s section. We can change some things here if we want but the lines we want to avoid editing altogether are these:

                    <attribute id="ID" type="FixedString" value="383236f9-c7ab-7163-4638-d2e2702ec513" />

                    <attribute id="Name" type="LSString" value="S_GOB_DrowCommander_25721313-0c15-4935-8176-9f134385451b" />

                    <attribute id="ShowEquipmentVisuals" type="bool" value="True" />

                    <attribute id="_OriginalFileVersion_" type="int64" value="144115207403209034" />

                    <children>

Changing the Body, Skin, Head, Hair, Eyes etc

So now let’s go onto explaining the lines we can edit/add/remove and what they mean. We will continue to use Minthara as an example.

The very first line:

                    <attribute id="BaseVisual" type="FixedString" value="6f9de444-153f-9825-eef7-e18d6de55bbf" />

This leads to the animations the character uses. In most cases you won’t want to change this at all. But you can switch to other existing ones if you want. Just be careful what you change it to as not all animations work for all races.

                    <attribute id="BodySetVisual" type="FixedString" value="9e43f0e4-d063-d85c-87b4-470d569645db" />

This is the body the character uses such as Minthara uses the ELF_F_NKD_Body_A

  • Here you have to be aware if changing it that you should only be changing it to a body that works for that head. By this I mean a Tiefling body will not work out of the box for Minthara as it will have a gap between the body and neck of the head.

MaterialPresets

This section is used for determining the eye colour presets and such to use.

                                        <node id="Object">

                                            <attribute id="ForcePresetValues" type="bool" value="True" />

                                            <attribute id="GroupName" type="FixedString" value="06Eyes" />

                                            <attribute id="MapKey" type="FixedString" value="06Eyes" />

                                            <attribute id="MaterialPresetResource" type="FixedString" value="8849a0c7-25ec-3434-8327-2cf825949fca" />

                                        </node>

So we first get this “06Eyes” which is pointing to the eye colour preset she is using. If we were to search the “MaterialPresetResource” ID “8849a0c7-25ec-3434-8327-2cf825949fca” it will lead us to “EYES_Underdark_Red_A

  • In Public\Shared\Content\Assets\Characters\Character Editor Presets\Eye Presets\[PAK]_Underdark\_merged.lsf

Because of it being a non unique eye colour we don’t want to edit that directly. Swap the “MaterialPresetResource” ID here to the one you want the character to use.

Using Character Creation to find the Colour you want to change it to

First I suggest using vscode so you can use this plugin as it removes the need to search for the names of the colours in the loca

BG3-GUIDinfos - Visual Studio Marketplace

So say I want to change the skin tone to Ice Tone 4 I need to first find the skin tone in the CharacterCreationSkinColors.lsx

Public\Shared\CharacterCreationPresets\CharacterCreationSkinColors.lsx

                <node id="CharacterCreationSkinColor">

                    <attribute id="DisplayName" type="TranslatedString" handle="hb67e1ed3ge301g49c5ga3ceg03d8c95e0851" version="1"/>

                    <attribute id="MaterialPresetUUID" type="guid" value="e3d3712b-4fe5-7cda-758c-1e9d663987c0"/>

                    <attribute id="Name" type="FixedString" value="UND_Ice_3"/>

                    <attribute id="UIColor" type="LSString" value="#FF6F798C"/>

                    <attribute id="UUID" type="guid" value="8e4f370c-4935-480f-b72f-2bc3d4770dce"/>

                </node>

If using the plugin I linked above when hovering over the UUID

It tells me the translated name which is reference to the handle says this is Ice Tone 4.

However the line we need is:

                    <attribute id="MaterialPresetUUID" type="guid" value="e3d3712b-4fe5-7cda-758c-1e9d663987c0"/>

So back to the CharacterVisual for skin we want to change the “02Skin Properties

                                        <node id="Object">

                                            <attribute id="ForcePresetValues" type="bool" value="True" />

                                            <attribute id="GroupName" type="FixedString" value="02Skin Properties" />

                                            <attribute id="MapKey" type="FixedString" value="02Skin Properties" />

                                            <attribute id="MaterialPresetResource" type="FixedString" value="f71102fd-d638-463a-af52-c59c19ee7736" />

                                        </node>

So I would change the “MaterialPresetResource” here to “e3d3712b-4fe5-7cda-758c-1e9d663987c0

You want to do the same for the ones you want to change in this section. Here are some of the other CharacterCreationPresets.

Public\Shared\CharacterCreationPresets\CharacterCreationSkinColors.lsx

Public\Shared\CharacterCreationPresets\CharacterCreationEyeColors.lsx

Public\Shared\CharacterCreationPresets\CharacterCreationHairColors.lsx

ScalarParameters

There are not too many ScalarParameters that you likely will want to change but there are some such as:

                                <node id="ScalarParameters">

                                    <attribute id="Color" type="bool" value="False" />

                                    <attribute id="Custom" type="bool" value="False" />

                                    <attribute id="Enabled" type="bool" value="True" />

                                    <attribute id="Parameter" type="FixedString" value="MakeupIntensity" />

                                    <attribute id="Value" type="float" value="0.65" />

                                </node>

Where you might want to change the strength.

RealMaterialOverrides aka Beard, Hair, Head and such

Continuing to use Minthara as an example this is her “RealMaterialOverrides” section.

                        <node id="RealMaterialOverrides" />

                        <node id="Slots">

                            <attribute id="Bone" type="FixedString" value="" />

                            <attribute id="Slot" type="FixedString" value="Head" />

                            <attribute id="VisualResource" type="FixedString" value="d3905dd1-d1fb-d315-0c84-5f877af24354" />

                        </node>

                        <node id="Slots">

                            <attribute id="Bone" type="FixedString" value="" />

                            <attribute id="Slot" type="FixedString" value="ModestyLeaf" />

                            <attribute id="VisualResource" type="FixedString" value="13bb4168-a5e4-fe82-f3df-57721a1ffed3" />

                        </node>

                        <node id="Slots">

                            <attribute id="Bone" type="FixedString" value="" />

                            <attribute id="Slot" type="FixedString" value="Hair" />

                            <attribute id="VisualResource" type="FixedString" value="30207433-976d-372b-a0a3-43d0b0201157" />

                        </node>

                        <node id="Slots">

                            <attribute id="Bone" type="FixedString" value="" />

                            <attribute id="Slot" type="FixedString" value="Private Parts" />

                            <attribute id="VisualResource" type="FixedString" value="1f07d7e4-e0a1-3114-93ae-dd2f0bdfc0b0" />

                        </node>

                        <node id="Slots">

                            <attribute id="Bone" type="FixedString" value="" />

                            <attribute id="Slot" type="FixedString" value="ModestyLeaf" />

                            <attribute id="VisualResource" type="FixedString" value="dec25989-ed08-895e-4ae0-5c9b44352bf0" />

                        </node>

As you can see each “Slots” line says what each node is.

                        <node id="Slots">

                            <attribute id="Bone" type="FixedString" value="" />

                            <attribute id="Slot" type="FixedString" value="Hair" />

                            <attribute id="VisualResource" type="FixedString" value="30207433-976d-372b-a0a3-43d0b0201157" />

                        </node>

So if wanting to replace the Hair for one we find in CC (or a mod) we want to change this line

                            <attribute id="VisualResource" type="FixedString" value="30207433-976d-372b-a0a3-43d0b0201157" />

To the VisualResourceID of the asset we want to have them use.

Public\Shared\CharacterCreation\CharacterCreationAppearanceVisuals.lsx

Is where you will find the hairs in Vanilla and in mods similar locations except the word Shared will be the custom name the mod uses.

If using the previous linked BG3 Guids plugin in visual studio code you don’t need to open the English.loca found in Localization/Shared/English and search for the name you see in character creation.

For mods however you will need to open their English.loca equivalent when you unpack them. Compare the name you see in CharacterCreation and copy the handle it has, then search for that handle in their CharacterCreationAppearanceVisuals.lsx

If the hair is a hair from another NPC and not available even by mods for Tav/Dark Urge then you want to find that hair. A good resource for hairs can be found here Modding Resources - Baldur's Gate 3 Wiki (bg3.wiki)

Replacing the Mesh/Textures Directly

So first and foremost when creating a .pak to replace meshes and/or textures you want to copy the merged of the mesh you're editing.

Using head as an example:

Public\Shared\Content\Assets\Characters\Race You're editing\Heads\Head You're Editing

or for strong types and dragonborn

Public\SharedDev\Content\Assets\Characters\Race You're editing\Heads\Head You're Editing

When creating a .pak you want to copy that head merged you're editing and make the same folder structure to that file in your workspace folder.

Then for the textures and mesh you can copy the same folder structure as the game or make a custom one. Main thing is that the merged file of the head has the path changed to your new path

For example using one of my own mods for the structure:

My meshes and textures go here EA_Gale_Head\Generated\Public\GalePatch6\P4_Gale

EA_Gale_Head is what we call the workspace folder. The very first folder that has all the things we are editing in.

EA_Gale_Head\Public\GalePatch6\Content\Assets\Characters\Humans\Heads[PAK]_HUM_M_Head_Gale_merged.lsf

Textures

So if editing a texture I want to go all the way down to the texture bank section. If all I am editing is the textures then this is the only section I need in my modded _merged.lsf

using this section as an example

Generated/Public/Shared/Assets/Characters/_Models/Humans/HUM_M_NKD/HUM_M_Gale/Resources/HUM_M_NKD_Body_Gale_CLEA.DDS

we'd then just update this line to the path in our workspace folder so

Generated/Public/GalePatch6/P4_Gale/HUM_M_NKD_Body_Gale_CLEA.DDS

repeat for all the textures you are editing.

once finished either manually convert your .lsx to .lsf or let modders multitool to do the conversion for you when you pack by naming the file extension .lsf.lsx

do the same for any head mesh replacements as well

do not touch the UUID's in the <attribute id="ID" type="FixedString" value=" sections keep those as they arae if you are just replacing making sure to change any \ to /

Meshes

In this example we will be swapping Lae’zels hair for another. First we want to find where her hair is in the game files so we can recreate those folders in our paks workspace.

When unpacked with multitool we will find the reference to the hair she has here:

Multitool\UnpackedData\Shared\Public\Shared\Content\Assets\Characters\Githyanki\[PAK]_Female_Body\_merged.lsf.lsx

Which then tells us the GR2 is here:

Generated/Public/Shared/Assets/Characters/_Models/_Hair/Resources/GTY_F_NKD_Hair_Laezel.GR2

So we can create these two folder paths in our mod/pak workspace. For the GR2 we can also do a custom path if we want but keeping the original path also works as long as we name the path exactly the same as above.

An example of a custom path:

Generated\Public\Shared\[PAK]_Laezel\EditedLaezelHair.GR2

Mods\YourSharedFolderName\Meta.lsx

Once we have our GR2 in our Generated file path + the path made for our merged we can go onto editing the merged to work for our mod. First we want

<?xml version="1.0" encoding="utf-8"?>

<save>

    <version major="4" minor="0" revision="8" build="2" />

    <region id="VisualBank">

        <node id="VisualBank">

            <children>

            </children>

        </node>

    </region>

</save>

To be the start of our merged.lsf.lsx (.lsx only if you’re not using multitool to auto convert on packing)

The below section would then be what we want to copy from

Multitool\UnpackedData\Shared\Public\Shared\Content\Assets\Characters\Githyanki\[PAK]_Female_Body\_merged.lsf.lsx

                <node id="Resource">

                    <attribute id="AttachBone" type="FixedString" value="" />

                    <attribute id="AttachmentSkeletonResource" type="FixedString" value="" />

                    <attribute id="BlueprintInstanceResourceID" type="FixedString" value="" />

                    <attribute id="BoundsMax" type="fvec3" value="0.130333 1.86519 0.1576101" />

                    <attribute id="BoundsMin" type="fvec3" value="-0.1137798 1.448151 -0.09667526" />

                    <attribute id="ClothColliderResourceID" type="FixedString" value="" />

                    <attribute id="HairPresetResourceId" type="FixedString" value="415a075c-cf69-4770-6483-7fecf3ef5cec" />

                    <attribute id="HairType" type="uint8" value="1" />

                    <attribute id="ID" type="FixedString" value="b1701a71-6779-60e7-32fd-e5616b3363b9" />

                    <attribute id="MaterialType" type="uint8" value="0" />

                    <attribute id="Name" type="LSString" value="GTY_F_NKD_Hair_Laezel" />

                    <attribute id="NeedsSkeletonRemap" type="bool" value="True" />

                    <attribute id="RemapperSlotId" type="FixedString" value="" />

                    <attribute id="ScalpMaterialId" type="FixedString" value="" />

                    <attribute id="SkeletonResource" type="FixedString" value="ccc1f758-b56d-1b41-2578-88d5e1f58d55" />

                    <attribute id="SkeletonSlot" type="FixedString" value="" />

                    <attribute id="Slot" type="FixedString" value="Hair" />

                    <attribute id="SoftbodyResourceID" type="FixedString" value="" />

                    <attribute id="SourceFile" type="LSString" value="Generated/Public/Shared/Assets/Characters/_Models/_Hair/Resources/GTY_F_NKD_Hair_Laezel.GR2" />

                    <attribute id="SupportsVertexColorMask" type="bool" value="False" />

                    <attribute id="Template" type="FixedString" value="Generated/Public/Shared/Assets/Characters/_Models/_Hair/Resources/GTY_F_NKD_Hair_Laezel.Dummy_Root.0" />

                    <attribute id="_OriginalFileVersion_" type="int64" value="144115207403209032" />

                    <children>

                        <node id="AnimationWaterfall">

                            <attribute id="Object" type="FixedString" value="" />

                        </node>

                        <node id="Base">

                            <children>

                                <node id="Tags">

                                    <attribute id="Object" type="FixedString" value="Hair" />

                                </node>

                            </children>

                        </node>

                        <node id="ClothProxyMapping" />

                        <node id="Objects">

                            <attribute id="LOD" type="uint8" value="0" />

                            <attribute id="MaterialID" type="FixedString" value="f5e08c0c-048a-d8c5-3ba3-74741a02921d" />

                            <attribute id="ObjectID" type="FixedString" value="GTY_F_NKD_Hair_Laezel.GTY_F_NKD_Hair_Laezel_Accessories_Mesh.0" />

                        </node>

                        <node id="Objects">

                            <attribute id="LOD" type="uint8" value="0" />

                            <attribute id="MaterialID" type="FixedString" value="811abc78-2715-7e8a-2539-9966d3174d0f" />

                            <attribute id="ObjectID" type="FixedString" value="GTY_F_NKD_Hair_Laezel.GTY_F_NKD_Hair_Laezel_Mesh.1" />

                        </node>

                    </children>

                </node>

Copy the above in between the two children:

            <children>

            </children>

Now we chose to use the exact same path as the game uses in our pak then we don’t need to edit anything here and pak.

But if we did choose to edit the path like in my example:

                    <attribute id="SourceFile" type="LSString" value="Generated/Public/Shared/Assets/Characters/_Models/_Hair/Resources/GTY_F_NKD_Hair_Laezel.GR2" />

                    <attribute id="SupportsVertexColorMask" type="bool" value="False" />

                    <attribute id="Template" type="FixedString" value="Generated/Public/Shared/Assets/Characters/_Models/_Hair/Resources/GTY_F_NKD_Hair_Laezel.Dummy_Root.0" />

These lines would become:

                    <attribute id="SourceFile" type="LSString" value="Generated/Public/Shared/[PAK]_Laezel/EditedLaezelHair.GR2" />

                    <attribute id="SupportsVertexColorMask" type="bool" value="False" />

                    <attribute id="Template" type="FixedString" value="Generated/Public/Shared/[PAK]_Laezel/EditedLaezelHair.Dummy_Root.0" />

Character Editor Presets

Public\Shared\Content\Assets\Characters\Character Editor Presets

Public\Shared\Content\Assets\Characters\Character Editor Presets\Origin Presets

Public\Shared\Content\Assets\Characters\Character Editor Presets\Origin Presets\CharacterName

so all these have merged.lsf files that have hair/eye colour values for NPC's and Companions best thing to check if an edit is working is make the value an extreme My mini tool tools section has a colour converter for Hex to the games sRGB 0 - 1.0 values found here.

For these values never have any , in between the numbers.

hair colours include eyebrow, beard, body hair and them all.

Make sure textures and meshes have uppercase extensions

so .DDS not .dds .GR2 not .gr2

Replacing the LSX/LSF Files Directly

So this is placed under Character Editor Presets as it is similar in how it is done, but where the files might be are different.