Guide:Hair Mod File Setup

From bg3.wiki
Jump to navigation Jump to search

Bgwiii.png CommunityGuidesModding

Modding guides
Modding resources


Tools

For the UUID generation in Notepad++ once installed ToolBucket you want to press Alt + Shift + G it will bring up a window as shown below. Keep include hyphens ticked and you can also click don't ask again if you won't use any of the other options.

Toolbucketuuid.webp


If wanting to add more than one hair to your mod you can use my mini tool linked above for adding new slots to CharacterCreationAppearanceVisuals

If adding an existing hair from the game without wanting to do any edits you can remove the Generated folder from your copy of the template.

Blender

Once you have your mesh ready in blender (can follow this tutorial if you want, blender steps are still the same just import/export is a bit different.

If using the gr2 plugin you want to first make sure you have your export order setup which you can find the in Object Properties tab:

Exportorder.webp

Set the LOD Distance to 0 and for the first part of your mesh export order 1. Increase the export order number for every part you have.

Now once you have all that setup if you haven't already sometime in your process select the armature and your meshes and apply transforms with Ctrl + A > Apply Transforms

Now you can export the mesh(es). These are the export settings I use when exporting with the GR2 plugin:

Gr2export.webp

The settings below this cutoff remain as they are by default. If you don't apply transforms turn back on the Convert to Y-up

Following the Template

So if following my Template or just using it as a guide to copy parts from the game files you can follow along with this tutorial.

You can choose for your hairs to be autosnapping or non autosnapping

What are autosnapping and non autosnapping hairs?

Autosnapping hairs are basically when you have an asset you want to work for more than one race/bodyshape/bodytype but don't want to make a mesh for every single variation. So when setup to be autosnapping you can apply one mesh to be used by multiple races/bodyshapes/bodytypes. And the game "autosnaps" them into place.


Sometimes you may still need three autosnapping meshes, one for the shorter races, one for the default bodytype and one for the strong bodytype. However we will go through when this is needed further into the tutorial.


Non autosnapping is when you have to create your hair mesh for each race/bodyshape/bodytype or rather the ones you want to create it for. For instance for personal use hairs you might decide you only want it for one character. Then non autosnapping might be the way to go.

Localization/English/custom_name_here.loca.xml

Why is it named .loca.xml?

This is because if you package the Main folder also known as your workspace folder with multitool it will auto convert the .loca.xml to .loca

This way you don't have to manually do the conversions yourself. However if you prefer to do manual conversions change the extension to .xml only.

Change custom_name_here to a custom name. Best to add a prefix beforehand. For example I use my username Padme4000 but shorten it to P4

So for my viking braid my .loca.xml is called p4_viking_braid.loca.xml

Localisation.webp

So the first underscored (yellow) part labeled 1 is a handle. This is technically the same as a UUID except it has no - and instead has 3 extra random digits and adds a h at the beginning. So if you want you can use auto generated UUID's like you can make with notepad ++ or vscode and then remove the - and add a h at the beginning and add 3 more random digits.

For example:

644e4b68-7bdb-4b0a-98ff-9025ce6e20e6 turn this into h644e4b687bdb4b0a98ff9025ce6e20e6a4d

Now the second underscored (pink) part labelled 2 is where we write what we want the game to show when the handle is active. For example when it comes to character creation mods it is what shows up in character creation.

Using Multitool to Generate a Handle

Alternatively you can use multitool to generate a handle for you.

Handle multitool.webp

So once you have multitool open make sure to click the box next to Handle so it has a tick inside. Then you can click Generate for the tool to generate a new handle. Clicking on the box that has the handle inside will copy it to your clipboard so you can go to your file and paste it in.

Hair _merged.lsf

What is this? well its the file where we tell the game where to locate our mesh in our mod and other information it might need.

I setup this file first as it makes it easier for me at least to setup the other files we need.

Public\Autosnapping_Template\Content\Assets\Characters\[PAK]_Hair\_merged.lsf.lsx this is where you will find our file in the Template

       <region id="VisualBank">
       <node id="VisualBank">
           <children>
               <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.1584038 1.846514 0.167948" />
                   <attribute id="BoundsMin" type="fvec3" value="-0.1540881 1.345605 -0.09591679" /> 
                   <attribute id="ClothColliderResourceID" type="FixedString" value="" />
                   <attribute id="HairPresetResourceId" type="FixedString" value="" />
                   <attribute id="HairType" type="uint8" value="0" />
                   <attribute id="ID" type="FixedString" value="955faf79-1707-4ea9-87f4-39a5fba49120" />
                   <attribute id="MaterialType" type="uint8" value="0" />
                   <attribute id="Name" type="LSString" value="AT_Hair01" />
                   <attribute id="NeedsSkeletonRemap" type="bool" value="True" />
                   <attribute id="RemapperSlotId" type="FixedString" value="" />
                   <attribute id="ScalpMaterialId" type="FixedString" value="55534266-4ce2-39d1-d2dd-04a8ec90e841" />
                   <attribute id="SkeletonResource" type="FixedString" value="78d8b5f0-726c-232f-a357-3d05c4b3b859" />
                   <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/Autosnapping_Template/[PAK]_Autosnapping_Template/AT_Test.GR2" /> 
                   <attribute id="SupportsVertexColorMask" type="bool" value="False" />
                   <attribute id="Template" type="FixedString" value="Generated/Public/Autosnapping_Template/[PAK]_Autosnapping_Template/AT_Test.Dummy_Root.0" /> 
                   <attribute id="_OriginalFileVersion_" type="int64" value="144537400540921856" />
                   <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="89b063ff-7bf3-44a8-7e7d-41b29d0dd469" />
                           <attribute id="ObjectID" type="FixedString" value="AT_Test.AT_Test_Mesh.0" />
                       </node>

Let's go through the lines we want to change. Alternatively you can copy this whole section from the hair you used as a base/edit for your hair. Most hairs can be found in the file below, however if you can't find it there please use index search of multitool

location to vanilla hairs merged when unpacked with Multitool. Multitool\UnpackedData\Shared\Public\Shared\Content\Assets\Characters\[PAK]_Hair

If you copied that whole section from the hair you used you can probably skip a few steps here but I will still go through them. If I skip a line this means you can keep that at the vanilla value.

                   <attribute id="BoundsMax" type="fvec3" value="0.1584038 1.846514 0.167948" />
                   <attribute id="BoundsMin" type="fvec3" value="-0.1540881 1.345605 -0.09591679" /> 

These lines you want to match the hair you used as a base, especially if you haven't changed the base race it uses.

                   <attribute id="ID" type="FixedString" value="955faf79-1707-4ea9-87f4-39a5fba49120" />

You want this line to have a unique uuid. Either use multitool to generate a new uuid or an extension/plugin for your program. Such as those mentioned in the tools section of this page.

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

You can give this any name you want in the value section. This is just an internal name.

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

This line is what tells the game whether the asset in this case hair is autosnapping or not.

  • True = Autosnapping
  • False = Non Autosnapping
                   <attribute id="ScalpMaterialId" type="FixedString" value="55534266-4ce2-39d1-d2dd-04a8ec90e841" />

This line determines the scalp material your hair uses. Best to use the one your base hair used but feel free to play around to find the right one. (as of 24.11.23 some hairs in the game files don't have a scalp ID, larian is aware of this bug)

                   <attribute id="SkeletonResource" type="FixedString" value="78d8b5f0-726c-232f-a357-3d05c4b3b859" />

SkeletonResource is normally blank in the game files. However if making an autosnapping hair assigning the skeleton id from the skeleton your hair uses in this line will allow you to skip the conform to original gr2 in lslib. Which is normally needed if having more than two parts to your hair mesh.

                   <attribute id="SourceFile" type="LSString" value="Generated/Public/Autosnapping_Template/[PAK]_Autosnapping_Template/AT_Test.GR2" /> 

This is the path to your GR2 in your mods folders.

  • Make sure to use / and not \
  • always start the path from your Generated folder
  • double check your meshes extension in the folder is .GR2 not .gr2
                   <attribute id="Template" type="FixedString" value="Generated/Public/Autosnapping_Template/[PAK]_Autosnapping_Template/AT_Test.Dummy_Root.0" /> 

Same again so feel free to copy and paste your path from before to this line.

  • Do not remove .Dummy_Root.0 from the line
  • this line does not need .GR2 at the end
                       <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>

This section is another section where you want to be copying from one of the hairs you used to make the hair.

                       <node id="Objects">
                           <attribute id="LOD" type="uint8" value="0" />
                           <attribute id="MaterialID" type="FixedString" value="89b063ff-7bf3-44a8-7e7d-41b29d0dd469" />
                           <attribute id="ObjectID" type="FixedString" value="AT_Test.AT_Test_Mesh.0" />
                       </node>

This section is the parts that makeup our mesh in the GR2. So if you have 3 mesh parts you want 3 of these sections. But you want this part

                           <attribute id="MaterialID" type="FixedString" value="89b063ff-7bf3-44a8-7e7d-41b29d0dd469" />

The value here matching to the MaterialID so same line from the hair you used for that part. If they are using the same materials you can keep this the same for each new section you make.

However when making a new section make sure to also update "_Mesh.0" by increasing the number by 1.

For example:

                       <node id="Objects">
                           <attribute id="LOD" type="uint8" value="0" />
                           <attribute id="MaterialID" type="FixedString" value="89b063ff-7bf3-44a8-7e7d-41b29d0dd469" />
                           <attribute id="ObjectID" type="FixedString" value="AT_Test.AT_Test_Mesh.0" />
                       </node>
                       <node id="Objects">
                           <attribute id="LOD" type="uint8" value="0" />
                           <attribute id="MaterialID" type="FixedString" value="89b063ff-7bf3-44a8-7e7d-41b29d0dd469" />
                           <attribute id="ObjectID" type="FixedString" value="AT_Test.AT_Test_Mesh.1" />
                       </node>

In blender when you assigned the export order here:

Exportorder.webp

Where this says 1 that in our merged is that first block so .0

  • .0 = Export Order 1
  • .1 = Export Order 2
  • .3 = Export Order 3

Congrats we have finished setting up this file!

Character Creation Folder

Full Release Hair Template/Public/Hair_Template/CharacterCreation is the file path for your CharacterCreation Shared/Appearance Visuals lsx files. The path in this example being the one from the template.

For yours it would be your ModName/Public/YourShared/CharacterCreation

So as of full release we can finally use CharacterCreationAppearanceVisuals.lsx again even if we are using autosnapping hairs.

And we can apply the character creation slots for them in CharacterCreationSharedVisuals.lsx or CharacterCreationAppearanceVisuals.lsx

For non autosnapping use only CharacterCreationAppearanceVisuals.lsx.

You can use my mini tool to help create the CharacterCreationAppearanceVisuals.lsx file, as well as its slots. Or alternatively use the file in this template to do it manually.

CharacterCreationAppearanceVisuals

Using Minitool to create your CharacterCreationAppearanceVisuals

Adding modded races to the Minitool files to support those as well

CharacterCreationSharedVisuals

Remember that you need your hair to be autosnapping to work properly in this section.

               <node id="CharacterCreationSharedVisual">
                   <attribute id="DisplayName" type="TranslatedString" handle="h163f3e2e9f634634a5e41553265771e4" version="1" />
                   <attribute id="SlotName" type="FixedString" value="Hair"/>
                   <attribute id="UUID" type="guid" value="3ae3e4fc-e792-473a-8853-43520dfc1147"/>
                   <attribute id="VisualResource" type="guid" value="fa926de0-feae-4218-806e-3d037b9d83d3"/>
               </node>

So what is the above? well it's the section we need for each hair asset we make. So let's break down each line.

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

Remember in the localisation part of the tutorial? When we created a handle and entered a name for it? Well this is the line where we want to copy that handle to.

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

If making a Beard you can change this value from Hair to Beard otherwise keep it the same.

                   <attribute id="UUID" type="guid" value="3ae3e4fc-e792-473a-8853-43520dfc1147"/>

You want this uuid to be unique. We will be copying it to our Races.lsx soon

                   <attribute id="VisualResource" type="guid" value="fa926de0-feae-4218-806e-3d037b9d83d3"/>

So the new UUID we made for our ID line in our merged? This is where you want to copy that uuid to as well.

Congrats this file is now ready. Copy and paste the section if you want to make more than one hair and follow the same principles.

Races.lsx

So this file is what we need to edit alongside CharacterCreationSharedVisuals in order for our hair to appear as a slot in character creation. When made via this your hair will appear at the bottom of the hairs in character creation.

However this file is sadly incompatible with other mods editing the same sections we need to edit. This can be resolved either by opting into my Patches for Races.lsx mod or by using the Compatibility Framework instead of the Races.lsx file.

Compatibility Framework

To use the Compatibility Framework you need to install it alongside your mod. CF however needs to be at the bottom of the load order.

So first you want to make some new folders:

Modname/Mods/ScriptExtender/Lua

In the Script extender folder you want to create a Config.json you can do this by creating a text file and just renaming it and its extension Config.json. In the json you want:

{
   "RequiredVersion": 9,
   "ModTable": "YOUR_MOD_NAME_HERE",
   "FeatureFlags": ["Lua"]
}

Where I have written your mod name here, write the name of your mod.

Now in the Lua folder do the same again make a new txt file but this time rename it BootstrapClient.lua

if Ext.Mod.IsModLoaded("67fbbd53-7c7d-4cfa-9409-6d737b4d92a9") then
   local raceChildData = {
     ModName = {
       modGuid = "the UUID you gave in your meta.lsx",
       raceGuid = "899d275e-9893-490a-9cd5-be856794929f",
       children = {
        entry1 = {
           Type = "Visuals",
           Value = "3ae3e4fc-e792-473a-8853-43520dfc1147",
         },
      }
   }

}

   local function OnStatsLoaded()
     Mods.SubclassCompatibilityFramework.Api.InsertRaceChildData(raceChildData)
   end
 
   Ext.Events.StatsLoaded:Subscribe(OnStatsLoaded)
 end
       raceGuid = "899d275e-9893-490a-9cd5-be856794929f",

this you want to be the race you're adding your hair too. The one currently in there is humanoid. This will apply it to all races including modded races if they use it as the parent. For others see here

           Type = "Visuals",

This line is what you're adding to. Visuals is either Hair or Beards or other assets defined in the SharedVisuals file.

           Value = "3ae3e4fc-e792-473a-8853-43520dfc1147",

This uuid has to match the one you gave for the UUID line in your CharacterCreationSharedVisuals

If wanting to create more than one hair copy and paste this section:

        entry1 = {
           Type = "Visuals",
           Value = "3ae3e4fc-e792-473a-8853-43520dfc1147",
         },

Changing entry1 to entry2 and so on. You can also use entrya, entryb as well.

Congrats you setup your mod for using the Compatibility Framework. Just remember always load Compatibility Framework at the bottom of your load order.