This is what I have come up with after looking at several other mods and messing around with it a lot. If you find any errors in what I have put here or know of something I missed (or added and didn't need), please comment and I will correct it.
Inserting custom icons in to a mod and having them work in BG3 is a little odd. Hopefully this will help someone. I know, if I don't mess with the icons for a week or so, I will be glad I have this. If there is any care or interest in me doing so, I might just make an example mod and upload that so it would be easier to see how things are referenced. Of course, this could all just be insane rambling and I am not even typing right now.
To have custom icons in the game, you have to create individual icon files and an image map with icons in it. The files have to be DDS and have to have square dimensions. You also have to set up some xml files.
Icon = the tool tip, controller, and tool bar skill/spell image representation in the game.
Image Map = an image containing the icons tiled across, and ummm down.
In the examples I have looked at, and eventually learned from and went with, the individual icon files are 144x144 pixels. The icons in the image map are 64x64 pixels.
The image map's size must be of 2^x (256, 512, 1028, etc) pixels, width and height. And that number must be the result of how many icons should be across as well as down.
Basically everything is a square, man.
In the XML, the reference to where each icon is placed in the image map, is a bit odd. It is percentage based such that it is a reference to Icon width / Image Map width.
For example, if an icon is 64 pixels wide and the image map is 256 pixels wide, then it is 64/265 = 0.25 (25 %)
In the iconsAtlas.lsx file, the X coordinates are represented by U and the Y coordinates are represented by V.
How to Calculate the iconsAtlas.lsx locations
- m = icon number in the row accross, 0 based (left to right)
- n = icon number in the column down, 0 based (top to bottom)
- x = Icon Width / Image Map Width
U1 = xm
U2 = x(m + 1)
V1 = xn
V2 = x(n + 1)
This is making it seem way more complicated than it is. An example is in order. With the dimensions in our example (icon is 64 pixels wide and the image map is 256 pixels wide), lets demonstrate that below for 8 icons (distributed in a way to show how to represent them).
(pretend this is a very pretty image with the icons placed and that each cell is 64x64, etc)
| Spell_Icon1 | Spell_Icon2 | Spell_Icon3 | Spell_Icon4 |
| Spell_Icon5 | Spell_Icon6 | | |
| Spell_Icon7 | | | |
| Spell_Icon8 | | | |
Calculated locations
Spell_Icon1
U1 = xm = 0.25 * 0 = 0
U2 = x(m + 1) = 0.25 * 1 = 0.25
V1 = xn = 0.25 * 0 = 0
V2 = x(n + 1) = 0.25 * 1 = 0.25
Spell_Icon2
U1 = xm = 0.25 * 1 = 0.25
U2 = x(m + 1) = 0.25 * 2 = 0.5
V1 = xn = 0.25 * 0 = 0
V2 = x(n + 1) = 0.25 * 1 = 0.25
Spell_Icon3
U1 = xm = 0.25 * 2 = 0.5
U2 = x(m + 1) = 0.25 * 3 = 0.75
V1 = xn = 0.25 * 0 = 0
V2 = x(n + 1) = 0.25 * 1 = 0.25
Spell_Icon4
U1 = xm = 0.25 * 3 = 0.75
U2 = x(m + 1) = 0.25 * 4 = 1
V1 = xn = 0.25 * 0 = 0
V2 = x(n + 1) = 0.25 * 1 = 0.25
Spell_Icon5
U1 = xm = 0.25 * 0 = 0
U2 = x(m + 1) = 0.25 * 1 = 0.25
V1 = xn = 0.25 * 1 = 0.25
V2 = x(n + 1) = 0.25 * 2 = 0.5
Spell_Icon6
U1 = xm = 0.25 * 1 = 0.25
U2 = x(m + 1) = 0.25 * 2 = 0.5
V1 = xn = 0.25 * 1 = 0.25
V2 = x(n + 1) = 0.25 * 2 = 0.5
Spell_Icon7
U1 = xm = 0.25 * 0 = 0
U2 = x(m + 1) = 0.25 * 1 = 0.25
V1 = xn = 0.25 * 2 = 0.5
V2 = x(n + 1) = 0.25 * 3 = 0.75
Spell_Icon8
U1 = xm = 0.25 * 0 = 0
U2 = x(m + 1) = 0.25 * 1 = 0.25
V1 = xn = 0.25 * 3 = 0.75
V2 = x(n + 1) = 0.25 * 4 = 1
That should be enough to make the pattern apparent. Just increment the values where appropriate. How this is actually represented is shown below in the iconsAtlas.lsx file, in the File References section.
The individual icon files should match the names that are to be referenced in the mod as well as in the iconsAtlas.lsx file. So, if we were to call the first one Spell_Icon1.DDS, it should also be referenced in your mod as "Spell_Icon1" and in the atlas file as "Spell_Icon1".
If you were to use it in one of the "stats" files it would be something like:
data "Icon" "Spell_Icon1"
File Locations
replace MODNAME with whatever the name of the mod is.
MODNAME\Public\Game\GUI\Assets\ControllerUIIcons\skills_png
- place the individual icon DDS files here (the 144x144 ones). Spell_Icon1.DDS, Spell_Icon2.DDS, etc.
- There is a chance the "skills_png" folder name could be named whatever you want
- also place the individual icon DDS files here (the 144x144 ones). Spell_Icon1.DDS, Spell_Icon2.DDS, etc.
- place the icon map DDS file here. You can name it whatever you want. Just make sure to reference it correctly later.
- place the icons.lsf.lsx file here (i have also seen the file named _merged.lsf.lsx instead of icons.lsf.lsx etc)
- place the icons.lsx file here (i have also seen the file named _merged.lsx instead of icons.lsx etc)
- for some reason, it seems both files are needed... not sure why.
- place the iconsAtlas.lsx file here (i think you can name this whatever you want as well)
File References
icons.lsx: the overall image map reference file.
icons.lsf.lsx: another version of the overall image map reference file.
iconsAtlas.lsx: also references the image map but also details sizes and locations of icons, etc.
Generate a custom UUID and put it in the ID field in the icons.lsx and icons.lsf.lsx files. Put the same UUID in the UUID field in the TextureAtlasInfo region section of iconsAtlas.lsx file.
Here are the example XML files. Take care to not just copy and paste... but also change what needs to be changed.
icons.lsx
- replace [GENERATED UUID] with the UUID you created.
- replace [IMAGEMAPNAME] with the name of the image map file without the .DDS (there is one spot that has the .DDS)
save the XML in the location listed above.
<?xml version="1.0" encoding="utf-8"?>
<save>
<version major="4" minor="0" revision="0" build="49"/>
<region id="TextureBank">
<node id="TextureBank">
<children>
<node id="Resource">
<attribute id="ID" type="FixedString" value="[GENERATED UUID]" />
<attribute id="Localized" type="bool" value="False" />
<attribute id="Name" type="LSString" value="[IMAGEMAPNAME]" />
<attribute id="SourceFile" type="LSString" value="Public/[MODNAME]/Assets/Textures/Icons/[IMAGEMAPNAME].DDS" />
<attribute id="Template" type="FixedString" value="[IMAGEMAPNAME]" />
<attribute id="Streaming" type="bool" value="True" />
<attribute id="Type" type="int32" value="1" />
<attribute id="SRGB" type="bool" value="False" />
<attribute id="_OriginalFileVersion_" type="int64" value="144115188075855873" />
</node>
</children>
</node>
</region>
</save>
icons.lsf.lsx
- replace [GENERATED UUID] with the UUID you created.
- replace [IMAGEMAPNAME] with the name of the image map file without the .DDS (there is one spot that has the .DDS)
save the XML in the location listed above.
<?xml version="1.0" encoding="utf-8"?>
<save>
<version major="4" minor="0" revision="4" build="602" lslib_meta="v1,bswap_guids" />
<region id="TextureBank">
<node id="TextureBank">
<children>
<node id="Resource">
<attribute id="ID" type="FixedString" value="[GENERATED UUID]" />
<attribute id="Localized" type="bool" value="False" />
<attribute id="Name" type="LSString" value="[IMAGEMAPNAME]" />
<attribute id="SourceFile" type="LSString" value="Public/[MODNAME]/Assets/Textures/Icons/[IMAGEMAPNAME].DDS" />
<attribute id="Template" type="FixedString" value="[IMAGEMAPNAME]" />
<attribute id="Streaming" type="bool" value="True" />
<attribute id="Type" type="int32" value="1" />
<attribute id="SRGB" type="bool" value="False" />
<attribute id="_OriginalFileVersion_" type="int64" value="144115188075855873" />
</node>
</children>
</node>
</region>
</save>
iconsAtlas.lsx
- replace [GENERATED UUID] with the UUID you created (remember, it is the same in both files).
- replace [IMAGEMAPNAME] with the name of the image map file without the .DDS (there is one spot that has the .DDS).
- replace [IMAGEMAPICONPIXELHEIGHT] with an individual icon's height that is in the image map. Our example is 64.
- replace [IMAGEMAPICONPIXELWIDTH] with an individual icon's width that is in the image map. Our example is 64.
- replace [IMAGEMAPPIXELHEIGHT] with the image map's height. Our example is 256.
- replace [IMAGEMAPPIXELWIDTH] with the image map's width. Our example is 256.
The Spell_Icon1, Spell_Icon2, etc... are just examples, you can name them what you want... just be consistent... match the file names etc. Also, we are not just putting "0" or "1". They are floats, so we are putting "0.0" and "1.0" where needed.
save the XML in the location listed above.
<?xml version="1.0" encoding="utf-8"?>
<save>
<version major="4" minor="0" revision="0" build="49"/>
<region id="IconUVList">
<node id="root">
<children>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon1" />
<attribute id="U1" type="float" value="0.0" />
<attribute id="U2" type="float" value="0.25" />
<attribute id="V1" type="float" value="0.0" />
<attribute id="V2" type="float" value="0.25" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon2" />
<attribute id="U1" type="float" value="0.25" />
<attribute id="U2" type="float" value="0.5" />
<attribute id="V1" type="float" value="0.0" />
<attribute id="V2" type="float" value="0.25" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon3" />
<attribute id="U1" type="float" value="0.5" />
<attribute id="U2" type="float" value="0.75" />
<attribute id="V1" type="float" value="0.0" />
<attribute id="V2" type="float" value="0.25" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon4" />
<attribute id="U1" type="float" value="0.75" />
<attribute id="U2" type="float" value="1.0" />
<attribute id="V1" type="float" value="0.0" />
<attribute id="V2" type="float" value="0.25" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon5" />
<attribute id="U1" type="float" value="0.0" />
<attribute id="U2" type="float" value="0.25" />
<attribute id="V1" type="float" value="0.25" />
<attribute id="V2" type="float" value="0.5" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon6" />
<attribute id="U1" type="float" value="0.25" />
<attribute id="U2" type="float" value="0.5" />
<attribute id="V1" type="float" value="0.25" />
<attribute id="V2" type="float" value="0.5" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon7" />
<attribute id="U1" type="float" value="0.0" />
<attribute id="U2" type="float" value="0.25" />
<attribute id="V1" type="float" value="0.5" />
<attribute id="V2" type="float" value="0.75" />
</node>
<node id="IconUV">
<attribute id="MapKey" type="FixedString" value="Spell_Icon8" />
<attribute id="U1" type="float" value="0.0" />
<attribute id="U2" type="float" value="0.25" />
<attribute id="V1" type="float" value="0.75" />
<attribute id="V2" type="float" value="1.0" />
</node>
</children>
</node>
</region>
<region id="TextureAtlasInfo">
<node id="root">
<children>
<node id="TextureAtlasIconSize">
<children />
<attribute id="Height" type="int64" value="[IMAGEMAPICONPIXELHEIGHT]" />
<attribute id="Width" type="int64" value="[IMAGEMAPICONPIXELWIDTH]" />
</node>
<node id="TextureAtlasPath">
<children />
<attribute id="Path" type="string" value="Assets/Textures/Icons/[IMAGEMAPNAME].DDS" />
<attribute id="UUID" type="FixedString" value="[GENERATED UUID]" />
</node>
<node id="TextureAtlasTextureSize">
<children />
<attribute id="Height" type="int64" value="[IMAGEMAPPIXELHEIGHT]" />
<attribute id="Width" type="int64" value="[IMAGEMAPPIXELWIDTH]" />
</node>
</children>
</node>
</region>
</save>
GIMP
GIMP is awesome, odd and free. That is why I am using it. Another bonus is that it now comes with the DDS plugin by default... you no longer have to hunt it down.
You can use the built in grid system to help guide you to where to place the icons in the image map. It is just an overlay and will not be in the actual image.
- In GIMP, in the menu, go to View and check/x "Show Grid".
- Then, in the menu, go to Image and click on "Configure Grid".
- In the popup that is displayed, in the "Spacing" section, set the Horizontal and Vertical values to the icons width and height.
Exporting as a DDS image in it is straightforward enough. You just export it and put its extension to DDS and set the following options:
- Compression: BC3/DXT5
- Mipmaps: Generate mipmaps
- Filter: Kaiser
- (leave everything else at the defaults)
There seems to be an oddity with it though. Or, at least, I have ran in to an oddity anyway.
It seems that after you are done editing everything (and merged all of the layers etc) and then export, the icon/image map does not work in the game. For me, after I am done editing everything and have merged all of the layers, I have to copy the image and then paste it as a new image. Then, I can export that new image as a DDS file and it will work in the game just fine. I figured I would mention that just in case anyone else is having issues.
I feel like i didn't color image map as blue somewhere.
0 comments