The Naked NPCs Mod Creation Process

Document Version 1.2

In the interest of ensuring continuity and collaboration, I'm sharing details here about the tools and workflow I use for these mods. 

This process is specific to the context of the Naked NPCs mods I've published for the Cyberpunk 2077 video game,
which is primarily about "Removing and Adjusting Clothing on NPCs".

My hope is that this info can be used as a reference for collaboration, remixes, or for other similar types of mods.

Note: This article may evolve over time as tools and processes improve.

___________________________________________________________________________________________________________________________
About the process

The creation process for these mods isn't very technically sophisticated. There isn't any programming. It is more like modifying a spreadsheet. You just need to know a bit about the structure of the data, which "cells" of the spreadsheet to modify, and the values to use.

This was learned from existing tutorials, experimentation, and info-sharing from other experienced mod authors.
Some good tutorials to start with:


NOTE: These can contain some outdated info related to v1.x of CP2077 but are still very useful.

This guide assumes some basic familiarity with WolvenKit:

If you're brand new to WolvenKit, take a detour at your first opportunity to read the tutorial Getting Started with WolvenKit and explore its wiki. Knowing a bit of its usage and UI will make the this guide easier to follow.

___________________________________________________________________________________________________________________________
Process steps

The following describes the general workflow I use for CP2077 v2.x

  • Open WolvenKit and its asset browser panel.
  • Find character appearance files `.app` that have the NPCs you're interested in changing and include them in a project.
  • Find a corresponding entity file `.ent` that references the appearance file. Open without adding to the project.
  • Use the Entity Preview feature to view a B/W 3d render of the appearance you want to change. This is useful as a visual playground for changes, but does not edit the appearances directly.
  • Scroll down through the component lists of the entity and find clothing that seems good to remove. To hide clothing, disable the component checkbox, or experiment with different submeshes to hide parts of them (if applicable). Make note of the changes.
  • Find the body component (it usually has a "t0_" prefix) and adjust the enabled submeshes so that body will show with any adjusted components (ie. a jacket was removed, and you need to fix invisible arms on the body mesh). Make note of the changes.
  • Go back to the appearance file, and remove/modify the components you experimented with in steps 5 and 6.
  • Consider an alternate meshAppearance name for the body component to remove censors (ie. "01_ca_pale" to "01_ca_pale_naked").
  • Consider adding breast physics: Open "alt.app", expand "naked" appearance, copy "breasts" component and paste into your other appearance component list.
  • Consider adding vag hair: Open "alt.app", expand "naked" appearance, copy "i0_000_wa_base__vagina_hairstyle_015616" component and paste into your other appearance component list. Also expand the "AppearanceVisualController" and copy/paste the corresponding entVisualControllerDependency.
  • Consider adding or replacing clothing from other appearances. Similar process to step 10. Find the component, copy/paste into other.
  • Resolve any mesh or appearanceName changes under the "AppearanceVisualController" dependency list. The mesh file paths and appearanceNames should be the same as their corresponding component.
  • Remove or reset any corresponding component deformers under "parametersBuffer" (new to CP2077 v2.0). Skipping this step can lead to visual distortions in the NPC.
  • Save the .app file changes, install as a mod and start the game. 
  • Use Appearance Menu Mod (AMM) to spawn your appearance and check it out for issues. 
  • Repeat steps 5-15 until it looks OK.

More comprehensive detail can be found below. I'd recommend reading through the whole thing at least once before coming back to this section. What's next intends to walk you through relevant technical background detail, practical examples, and tricks to use when performing these steps.

__________________________________________________________
Data Model Overview

A bit of background about how NPCs appear to work within the game, and about their data model...

In game, there seem to be a few different types of NPC, based on behavior:
  • Random unnamed citizen NPCs that you encounter on the streets and in buildings
  • Random unnamed NPC mobs that will attack you
  • Static NPCs on the street or in quests (ie. joytoys and holograms on jigjig street)
  • Named NPCs in main and side quests (ie. Johnny, Judy, Panam, Alt, Adam Smasher, etc.)

The folder structure that the game data asset files reside in gives clues on the types of NPCs you'd see in game: (a summarized view)
# In WKit Archive asset browser:

├ base                  # "base" game assets
| └ characters          
|   ├ appearances       # Primary location for the NPC appearances
|   | ├ boss            # Named bosses like Adam Smasher, Bruce Ward, Zoe Alonzo, etc.
|   | ├ citizen         # Random citizens on the street and in buildings (Freaks, Homeless, Rich, etc.)
|   | ├ corpo           # Corporate mobs (Arasaka, Max Tac, Militech, NCPD, etc.)
|   | ├ corpse          # Dead body NPCs
|   | ├ gang            # City mobs (6th Street, Animals, Maelstrom, Valentinos, etc.)
|   | ├ light_crowd     # More random citizens based on location (Corpoplaza, Heywood, Westbrook, etc.)
|   | ├ main_npc        # Named Quest NPCs like Judy, Panam, Misty, etc.
|   | ├ player          # Clothing for V (not for NPCs)
|   | ├ proxy           # Misc "Proxy" meshes and textures (out of scope for this guide)
|   | ├ quest_temp      # Misc quest NPC appearances (unsure why it is named "_temp". I believe they are used)
|   | ├ service         # Service-oriented NPCs (sexworkers, dining, driver, media, medical, etc.)
|   | ├ special         # "special" NPCs (hologram, cyberpsycho, braindance, etc.)
|   | └ to_be_removed   # Some misc appearances, seem to be marked for removal (might be used?)
|   |
|   ├ entities          # Primary location for the NPC entities
|     ├ boss
|     ├ citizen
|     ├ corpo
|     ├ (and so on, similar structure as above)
|
├ ep1                   # Phantom Liberty DLC assets ("ep1" = episode 1? early preview 1?)
└ characters
├ appearances    # Similar NPC appearance file folders for PL
├ entities       # Similar NPC entity file folders for PL

It is fairly well organized into distinct types of NPCs which can help when trying to locate the files you'd like to modify.

Asset Files: Appearance vs Entity 

The primary game asset files within these folders that we are interested in for these mods are:

  • Appearance (.app) files: Contains the list of clothing and meshes (and other components) that configure the look of the NPCs. These are the files we will be primarily editing.
  • Entity (.ent) files: The primary data object for displaying the NPCs in game. They are parent objects that contain references to associated appearances. Entities are useful for testing in game.


For example, when using Appearance Menu Mod (AMM) to spawn NPCs in game, here is what happens behind the scenes...

AMM spawn "Sexworker"
 └-> Entity file (appearance name "prostitute_wa_prostitute_wa_10")
      └-> Appearance file (appearance name "prostitute_wa_10")
           └-> ...and more assets down its graph (meshes, animations, textures, etc.) 


  • AMM knows the entity file location and list of appearances (ie. "base\characters\entities\service\service__sexworker_wa.ent")
  • It spawns an entity with the appearance that was selected by name from its "appearances[]" array (ie. "prostitute_wa_prostitute_wa_10")
  • The entity's appearance refers to the appearance file (ie. "base\characters\appearances\service\service__sexworker_wa.app")
  • ...along with an appearance name, which maps to the name within the appearance file's "appearances[]" array (ie. "prostitute_wa_10")


In the context of these mods, where it is about modifying the clothing and looks of existing NPCs, we are primarily working directly with the appearance (.app) files and NOT with the entity (.ent) files. Though, it is good to understand the relationship between these files for previewing changes in development and for in-game testing.


Previewing NPC Appearances using the Entity Preview Feature in WolvenKit

WolvenKit's preview feature can render a monochrome 3d model of a Mesh or Entity file. While it is not able to render color textures, it is still very useful for working with the appearances and planning modifications to them. 

As an example:
  • In the asset browser, locate the "base\characters\entities\service\service__sexworker_wa.ent"
  • right click "Open without adding to project".
  • Click the "Entity Preview" tab (can take a few seconds to load)
  • You should see a 3d render of "prostitute_wa_prostitute_wa_02" with a cutoff tshirt, pants, and boots
  • On the 3d model view panel, Left mouse button rotates the view, Middle mouse button pans, Mouse scroll to zoom
  • In the panel on the right, select a different "Appearance" name. Select "prostitute_wa_prostitute_wa_01" - you should see the 3d model change.
  • In the hierarchical table on the right, collapse the "deformations -> Root" and the "root -> Root" items (to ignore those parts)
  • Below those, there is a list of meshes (and their submeshes) with checkboxes for toggling visibility
  • Scroll down to "t1_024_wa_tshirt__sweater" and deselect its checkbox - Oopsie! Now she is shirtless
  • But there is a problem. Her elbows and forearms are missing. That usually means that there is a disabled body submesh.
  • In the hierarchical table, find "t0_000_wa__c_base_full0". It has a bunch of submeshes (44 total).
  • Notice that there is a suffix pattern: "_LOD_01", ... "_LOD_02", ... "_LOD_04", ... "_LOD_08" these designate the visibility of the submeshes at different levels of detail (distance from the character).
  • There are 10 of 11 submeshes selected within the set of "_LOD_01". Can you guess what the one deselected submesh is?
  • Enable the checkbox for "submesh_09_LOD_01". Poof! The elbows/forearms appear.
  • NOTE: You will need to note the index of this submesh on the body for later when editing the appearance and its chunkMask property.

With this feature, you can experiment with the visibility of different meshes and their submeshes to plan modifications to the associated appearance file, where these mesh definitions are actually located...


About Appearance (.app) Files and Components

For a real example, open the following app file in WolvenKit: "base\characters\appearances\service\service__sexworker_wa.app"...

Expand the "appearances" array to show the list of appearance definition names...
Expand one of the definitions, like "_ow__poor_01" (the default look for the female joytoy on JigJig street)...
Expand the "components" array... (summarized below)

File: "service__sexworker_wa.app"
- RDTDataViewModel
  └ appearances[]               (appearance definitions...)
    ├ 0: "_ow__luxury_01"       
    ├ 1: "_ow__luxury_01_naked" 
    ├ 2: "_ow__poor_01"         
    | └ components[]            (components list...)
        ├ 01: "l0_004_wa_tights__fishnet"    entGarmentSkinnedMeshComponent (Fishnets)
        ├ 03: "t1_066_wa_full__bodysuit_sweater_sleeveless" eGSMC (Bodysuit)
        ├ 05: "i1_090_wa_full__jewlery_earrings_082081" eGSMC     (Earrings)
        ├ 09: "earring_dangles"              entAnimatedComponent (Earring physics)
        ├ 19: "t0_000_wa__c_base_full0"      entGarmentSkinnedMeshComponent (Body)
        ├ 22: "t2_127_wa_jacket__evelyn4721" entGarmentSkinnedMeshComponent (Jacket)
        ├ 24: "AppearanceProxyMesh"          entSkinnedMeshComponent
        └ 25: "AppearanceVisualController"   entVisualControllerComponent (see below)
        

This components[] array contains the list of clothing, jewelry, cybernetics, body parts, and physics components that you can edit to change the look of a particular appearance (in this case, "_ow__poor_01").

Component Types and Naming Conventions 

The component names, like "l0_004_wa_tights__fishnet", can be helpful when descriptive...
  "l0_..._"                     = legs
          "wa_"                 = woman actor
              "tights__fishnet" = fishnet tights


But there are many components with names that aren't recognizable or very descriptive, like "GarmentMesh3041".

How to find out what they are and look like? Multiple ways:

  • Recognize component prefixes by naming convention ("l0_", "i1_", "t0_", etc.)
  • Recognize component types (entSkinnedMeshComponent, entAnimatedComponent, entSlotComponent, etc.)
  • If it is a mesh component, expand the UI object, look for the "mesh" property, press the blue arrow (to the right of its file path) to open its mesh, and then go to the: "Mesh Preview" tab for a 3D view of its shape.


The most common component types I've seen while working on these mods has been:
  ent*MeshComponent    = 3d shapes: clothing, accessories, hair, heads/torsos/arms/legs, etc. (most common)
  entAnimatedCompnent  = Adds physics for meshes: hair_dangle, earring_dangles, breasts, etc.
  entEffectSpawner     = Special visual effects (ie. hologram_effects)
  entSlotComponent     = I don't know this type yet (ie. jaw, head8268). I haven't needed to edit these.


Component Modification Examples

How to modify the look?

Let's walk through some common examples that modify the look of our sexworker "_ow__poor_01" appearance...

We'd like to:
  • Remove the bodysuit component
  • Add breasts physics
  • Add shorts 


To remove the sleeveless bodysuit component:

  • Select and expand the "t1_066_wa_full__bodysuit_sweater_sleeveless" component. This is the primary data entry for the bodysuit.
  • Note the component "id": 1927371120660008960 (needed later)
  • Right click context menu, click "Delete Item in Array/Buffer". 
  • Expand down into the "AppearanceVisualController" component a few levels until you see a list of entVisualControllerDependency entries. More info about this property below.
  • Select the "t1_066_wa_full__bodysuit_sweater_sleeveless" entry, and right click context menu, "Delete Item in Array/Buffer".
  • Back up at the component's top level, expand down into the parametersBuffer property several layers until you see a list of entGarmentParameterComponentData entries.  More info about this property below.
  • Select through the list of entries, looking for one with componentID 1927371120660008960 (from step 2)
  • Once found, select the entGarmentParameterComponentData entry, and right click context menu, "Delete Item in Array/Buffer".
  • Done. Test it.


What is the "AppearanceVisualController" component for? 

I haven't found an explanation about how this is used, but it appears to contain a copy of important properties of the mesh components within the appearance (mesh path and mesh appearanceName). Perhaps a "Visual Controller" system within the game engine uses this property as an efficient way to iterate through the visual components to access their dependencies, possibly for preloading or streaming...but that is just my own guess.

When modifying meshes or mesh appearanceNames, it is good practice to ensure the changes on the primary MeshComponents are applied to the respective entVisualControllerDependency within this list, matched by component name.

What is the parametersBuffer property for?

Down within its object structure is a componentsData list of entGarmentParameterComponentData entries, which map 1:1 to particular component ids. The data entries here can contain "deformers" (chunks[] property), "smoothing" values, and other properties that are applied to the corresponding mesh component. The chunks[] data apply deformed shapes to the mesh (and submeshes) when rendered. The chunks order is the same as the submesh order in the mesh files.

When removing components, I like to remove the associated parameterData entries to help reduce file size, which helps make saving a bit faster in WKit, as well as uploading/downloading from Nexus. But in regards to game functionality, the game engine may ignore that data once the the component is removed, so it may be functionally safe to keep around.

But if glitches are seen on the entity during testing, such as "holes", or odd distortions, it is probable that the deformers chunk[] property in a DIFFERENT entry under the parametersBuffer property needs to be reset. Usually, for me, it has been on the body component, now that more skin is shown due to removing clothing. A common case I see is when removing Judy's tank top (worn on several NPCs): Without resetting the body, the exposed breasts can have holes where the nipples are (where the breasts had been overlapping the tanktop mesh previously).

To reset the body component deformers:

1. In the component listing, look for a component with name prefix "t0_" (torso) and select it. (ie. "t0_000_wa__c_base_full0")
2. Note its component "id": 1249866961039249408
3. Expand down into the parametersBuffer property several layers until you see a list of entGarmentParameterComponentData entries.
4. Select through the list of entries, looking for one with componentID 1249866961039249408 (from step 2)
5. You have a few options here to reset the component:
    5a. Expand the entGarmentParameterComponentData and find the "chunks[]" property array. If it has size, right click on chunks, select "Reset Object". If it does NOT have size (a common occurrence), there is nothing here to do (no deformers). This is what I usually do for components that are not removed but need to be reset.
    5b. Delete the entGarmentParameterComponentData entry completely, like what was done for the removed bodysuit earlier. Also a functionally viable option, though additional baked parameter data is lost (gets recalculated dynamically in game, I believe). Could there be tiny impacts on performance? I would be interested to learn if there are indeed differences/impacts between these options, if anyone happens to know.
6. Done. Test it.

This same process can be used to reset the deformers for any component.

Other clothing may need reset, too, such as when removing a long shirt or jacket that was touching/overlapping pants or shorts. If you see holes in the top part of the pants or shorts, you'll want to reset the pants/shorts parameter data.

Another common component that may need resets is the neck after removing collared shirts, suits or jackets. You may see divots in the skin around the neck. The neck is part of the head component, typically prefixed with "h0_".

Now that the bodysuit has been removed, consider adding breast physics...

About Breast Physics

By default, most NPCs do not have breast physics (there are only a handful that have the physics by default). I am unsure why CDPR chose to do this (could have been related to performance tuning?), but there doesn't seem to be much impact to the game after adding breast physics to many NPCs. When breast physics are added to an NPC, they do not appear to activate unless the player is nearby (perhaps related to level of detal LOD settings) so performance impacts may be minimal. Do your own testing, though. YMMV.

To add breast physics:
  • In asset browser, search for "alt.app". This is Alt Cunningham's appearance. Open without including in the project.
  • Find Alt's "naked" appearance. Expand its components listing.
  • Select the "breasts" component, right click and "Copy From Array/Buffer".
  • Go back over to your other app file, with the "_ow__poor_01" appearance...
  • Right click on its "components[]" property and "Paste paste into Array/Buffer".
  • You should see the "breasts" component added to the bottom of the components array.
  • Done. Test it.

Adding Clothing or Other Components

For an example of adding a new component to an appearance, let's say you want them wearing shorts instead of the NPC being fully naked.

To add shorts:

  • Find another appearance with a shorts component you want to copy. In this example, I found one in the same .app file on the "_q105__skye" appearance as "l1_075_wa_shorts__strap_pants6116"
  • Select that component, right click and "Copy From Array/Buffer".
  • Go back over to the appearance being modified, "_ow__poor_01", select its components[] array, right click "Paste Into Array/Buffer"
  • Return to the "_q105__skye" appearance...
  • Expand down into the "AppearanceVisualController" component a few levels until you see a list of entVisualControllerDependency entries.6. Select the "l1_075_wa_shorts__strap_pants6116" entry, and right click context menu, "Copy From Array/Buffer".
  • Go back to the appearance being modified, "_ow__poor_01", expand down into the AppearanceVisualController component and right click "Paste Info Array/Buffer"
  • Done. Test it.

These same steps can be used for most components. Just be sure to copy components that are being used with the same general body type (women, men, average body vs chubby body, etc.) or you may encounter issues.

NOTE: Some copied components will require associated entAnimatedComponents copied as well. For example, some jackets and tops will need the "collar" anim component, otherwise you'll see an issue where the jacket is detached and hovering close to the body without animation.

Keep an eye out for related animation components like that one and others:
  • "collar", "simple_collar" : for some jackets/tops/bodysuits
  • "earring_dangles" : for some earring components
  • "hair_dangle" : for some hair components

About Body (Mesh) Components 

There is (usually) a body entGarmentSkinnedMeshComponent that exists in a character's appearances component list with a "t0_" (torso) prefix (ie. "t0_000_wa__c_base_full0").

These mesh components have a few important properties to call out:
  • chunkMask : A bitmask of the submeshes to show or hide at different levels of detail (See also: Modding Wiki article on chunkMask)
  • mesh DepotPath : The relative path to the mesh file.
  • meshAppearance : The appearance name to use for the mesh which selects different textures (pale vs dark vs dirty, etc.)

The most common female body mesh files I've run across in the NakedNPCs mods are:
  • "base\characters\common\base_bodies\woman_average\t0_000_wa_base__full.mesh"
  • "base\characters\common\base_bodies\woman_average\t0_002_wa_prostitute__full.mesh"
  • "base\characters\common\base_bodies\woman_average\_old_to_delete\t0_000_waf_base__full.mesh" (chubby)

This one is NOT common, but can be fun to swap to:
  • "base\characters\common\base_bodies\woman_average\t0_000_wa_base__full_breast_big.mesh" (bigger boobs)

It is worth opening these files in WolvenKit to get an understanding of their appearanceNames (under the appearances[] array property) and for information on its submeshes (shown in the "Mesh Preview" tab).

For example, this is a reference list I use for "t0_000_wa_base__full.mesh" (`_censored` names removed). Base names on the left -associated suffixes on its right...

- 01_ca_pale (`_old`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`, `_blendable`)
- 01_ca_albino (`_old`, `_old_naked`, `_naked`)
- 02_ca_limestone (`_old`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`)
- 03_ca_senna (`_old`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`)
- 04_ca_almond (`_old`, `_old_skinny`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`)
- 05_bl_expresso (`_old`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`, `_blendable`)
- 06_bl_dark (`_old_naked`, `_skinny`, `_skinny_naked`, `_naked`)
- npc_01_pale_dirt (`_skinny`, `_skinny_naked`, `_naked`)
- npc_01_ca_pale (`_scav_01`, `_scav_02`, `__sick_scav_01`, `__sick_scav_02`)
- npc_02_black_dirt (`_skinny`, `_skinny_naked`, `_naked`)
- npc_02_ca_limestone (`_scav_01`, `_scav_02`, `_sick_scav_01`, `_sick_scav_02`)
- npc_03_limestone_sick (`_skinny`, `_skinny_naked`, `_naked`)
- npc_04_almond_sick (`_skinny`, `_skinny_naked`, `_naked`, `_scav_01`, `_scav_02`)
- npc_05_senna (`_scav_01`, `_scav_02`, `_sick_scav_01`, `_sick_scav_02`)
- npc_05_senna_valentino (`_old`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`)
- npc_05_senna_valentino_01 (`_naked`, `_old`, `_skinny`, `_skinny_naked`)
- npc_05_plastic_white (`_naked`, `_stocking_black`, `_stocking_black_naked`, `_stocking_destroyed`, `_stocking_destroyed_naked`, `_stocking_blue`, `_stocking_blue_naked`, `_stocking_yellow`, `_stocking_yellow_naked`, `_scratches`)
- npc_05_plastic_dark (`_stockingblack`, `_stockingblack_naked`, `_stocking_red`, `_stocking_red_naked`, `_stocking_pink`, `_stocking_pink_naked`, `_stocking_yellow`, `_stocking_yellow_naked`, `_naked`)
- npc_05_plastic_yellow (`_stocking_pink`, `_stocking_pink_naked`)
- npc_05_plastic_pink_stocking_red_naked
- npc_06_limestone_tyger (`_old`, `_old_naked`, `_skinny`, `_skinny_naked`, `_naked`)
- npc_06_chrome
- npc_06_gold
- npc_07_cyberspace
- npc_plastic_red (`_naked`)
- npc_dead (`_01`, `_02`, `_03`, `_04`, `_05`, `_06`)
- cyberspace (`_dark`)
- glow_tatoo (`_pink`, `_violet`, `_green`)
- special_hologram_body (`_naked`)
- hologram_afterlife_target
- crime_scene_holo
- blendable (`_closeup`)

Notice the name parts:
  • "pale", "limestone", "almond", "expresso", "dark" = these indicate skin color.
  • "_skinny" = thinner thighs
  • "_old", "_dirt", "_sick" = wrinkled, dirty, or bruised-looking skin
  • "_naked" = removes the black thong censor
  • "_censored" = adds the black thong and bra censor

How to change the textures of a mesh component?

To change textures you can modify the component to use a different mesh appearanceName.

Let's say we want to remove a black thong from a body component that is currently using the appearance name: "01_ca_pale"...

  • Expand the body entGarmentSkinnedMeshComponent
  • Click on the "meshAppearance" property, change "01_ca_pale" to "01_ca_pale_naked"
  • Expand down into the "AppearanceVisualController", find the corresponding body component by name
  • Click on the "appearanceName" property, change "01_ca_pale" to "01_ca_pale_naked"
  • Done. Test it.

This same process can be used for other mesh components like clothing, accessories, cybernetics, etc.. Open their mesh files to see the available list of appearanceNames to choose from.

The Appearance Creator Mod (ACM) is helpful as it allows trying out each of the appearance names (as well as experimenting with other clothing changes) while the game is running. When unsure of which one to use this tool can save a lot of time. Compare this to editing and saving the .app file, installing, relaunching the game and spawning the NPC for each appearanceName of the mesh you'd like to view. 


About MeshComponent chunkMasks and Submeshes

Earlier in the "Entity Preview" section we encountered an issue where a body part (elbow) was invisible after we removed the character's shirt. We found that a particular submesh was disabled. How to fix that in the appearance file and body component?

Open the "base\characters\common\base_bodies\woman_average\t0_000_wa_base__full.mesh" and click "Mesh Preview" to visualize its submeshes.

Some things to cover first...

  • The visibility toggles for the submeshes map to a bitmask value that is stored within the body component's "chunkMask" property of the .app file.
  • This body mesh has 11 submeshes. (Note that EACH mesh has its OWN LIST of submeshes, which can vary in number)
  • Also shown are 4 LOD sets of visibility toggles: LOD_01, LOD_02, LOD_04, and LOD_08. 
  • This means that there are 44 total bits to indicate visibility for this body mesh = 11 submeshes multiplied by 4 LODs
  • In the .app file appearance, select the "t0_" body component and click the "chunkMask" property dropdown...
  • A list of checkboxes and numbers from 0-63 is shown (bit index)
  • For this body mesh and its 44 visibility bits, you would focus on the bit index numbers 0 to 43 and select/deselect the indexes that match the submeshes you would like to show/hide.

Let's see it another way...

Here's a list of the submesh indexes and their corresponding body part:
  • 00: Upper chest/back
  • 01: Clavacle/throat
  • 02: Belly/lower back
  • 03: Waist/pelvis
  • 04: Upper legs/thighs
  • 05: Lower legs/calves
  • 06: Ankles
  • 07: Feet
  • 08: Shoulders
  • 09: Elbows/forearm
  • 10: Hands

As an exercise, we'd like all body parts to show EXCEPT for lower legs (05), ankles (06), and feet (07) at ALL LOD levels.

What should the chunkMask be set to in that case?
Submesh Idx: 00 01 02 03 04 05 06 07 08 09 10
             --------------------------------
  LOD1 bits:  1  1  1  1  1  0  0  0  1  1  1
  LOD2 bits:  1  1  1  1  1  0  0  0  1  1  1
  LOD4 bits:  1  1  1  1  1  0  0  0  1  1  1
  LOD8 bits:  1  1  1  1  1  0  0  0  1  1  1

These bits map to the chunkMask checkbox dropdown...
(read table above Left to right, top to bottom)

Legend: [x] = checkbox ON (1)
        [ ] = checkbox OFF (0)

[x] 0    # begin LOD1 { Submesh 00 
[x] 1                   Submesh 01
[x] 2                   Submesh 02
[x] 3                   Submesh 03
[x] 4                   Submesh 04
[ ] 5                   Submesh 05
[ ] 6                   Submesh 06
[ ] 7                   Submesh 07
[x] 8                   Submesh 08
[x] 9                   Submesh 09
[x] 10   # end LOD1 ->  Submesh 10 }
[x] 11   # begin LOD2 { Submesh 00 
[x] 12                  Submesh 01
[x] 13                  Submesh 02
[x] 14                  Submesh 03
[x] 15                  Submesh 04
[ ] 16                  Submesh 05
[ ] 17                  Submesh 06
[ ] 18                  Submesh 07
[x] 19                  Submesh 08
[x] 20                  Submesh 09
[x] 21   # end LOD2 ->  Submesh 10 }
[x] 22   # begin LOD4 { Submesh 00 

... and so on with the similar pattern until...

[x] 43 # end LOD4

... the rest of the bits aren't used and can be on or off (doesn't matter)

This same approach can be used for other mesh components and their chunkMask value. With different numbers of submeshes, you will adjust how many bit indexes you need to control.

For instance, with a mesh that contains only 1 submesh, it is easy: You only need to worry about the first four values in the chunkMask: 0-3.

For a mesh that contains more submeshes, you will need to calculate by 4 (for the LODs) and correlate the visibility bits similar to how was done above with the body component.

For issues where a component  isn't displaying correctly, it may very well be due to the chunkMask value. You may need to double check the selected bits for correctness.


About Appearance Colorvariants

The game contains "sub-appearances" that adjust the clothing and skin colors and other minor things on the components of a "parent" appearance.

To explain this, let's walk through the data...

Open up the "base\characters\appearances\service\service__sexworker_wa.app" file and look through the appearance names.
There will be some with _1, _2, _3, etc. name suffixes:

doll_06
doll_06_1
doll_06_2
doll_06_3 ...

Expand the "doll_06_1" appearance, and notice it's property "parentAppearance" = "doll_06", which implies that "doll_06" is a parent of "doll_06_1".

Compare the component lists - they are all pretty much the same. In some cases, there may be more or less components (typically accessories and decals). If you dig into each component, you will see differences in the mesh appearanceNames (textures). When testing with AMM and cycling through the variants, you will see a common overall look, but the colors, materials, transparency (textures) of the clothing will change.

Other data files seem to be involved, as well:

  • "base\entities\entityappearance_colorvariants.json"
  • "ep1\entities\entityappearance_colorvariants.json"

...When exporting these binary files to JSON, they can be searched upon. For example, when searching for "doll_06":

    ...
{
"$type": "gameEntityAppearanceColorVariantsArray",
"appearanceName": {
    "$type": "CName",
    "$storage": "string",
    "$value": "service__sexworker_wa_doll_06"
},
"colorVariants": [
{
"$type": "CName",
"$storage": "string",
"$value": "doll_06_1"
},
{
"$type": "CName",
"$storage": "string",
"$value": "doll_06_2"
},
{
"$type": "CName",
"$storage": "string",
"$value": "doll_06_3"
},
...

These files appear to be tables of the available variants "_1", "_2", "_3" that are associated with the primary appearance "doll_06".

Based on this info, I'm inferring that the game can randomly choose from these colorVariants appearances when spawning the parent.

Spending more time with the game now makes me think these files are used during the build/generation step for the data asset files, and is not used as a random selection during runtime. The entity files contain direct references these specific variant names, which seems build related.

In my mods, any changes I made to the parent appearance were similarly applied to all of its colorvariants (not just the parent). You should consider doing this as well, especially since various entity files will reference these variant appearanceNames directly.

Tip: When applying similar changes across colorVariants, you can copy the 64-bit number to the right if the chunkMask property and paste that single number to apply the same checkbox settings to the others. It can save time on a lot of clicks (and helps prevent inconsistencies).


Tip for finding appearance files and related entity files

Within WolvenKit's Project Explorer panel is a feature to "Convert to JSON", a right-click option for files (individual) or folders (in bulk).

What I've done is create a new local project called "GameAssetsReference", and added all of the character .app and .ent files to it.
Then I right click on the "archive" folder in the Project Explorer and "Convert to JSON" to convert all, and wait several minutes (warning: this takes up some extra hard drive space).

Those exported JSON files are text files which can be more easily searched through with other programs (I use VSCode) in order to  find strings of text.

I use these to correlate relationships between entity files and appearance files based on a text string (ie. the name of the appearance, or relative file path strings), and to look up a component/mesh by name to see which appearances they may be in across the entire catalog of asset files.

The JSON files can be fairly large and verbose, but with a bit of extra programming you can parse and filter out only the information that you really need to view into new summarized JSON files.

One utility I use for this is jq
#!/usr/bin/env bash
# File: create_app_summaries.bash
#
# Converts exported WKit .app JSON files into a smaller JSON format
# with only the main properties I really care about...
#
# Run within the root of the project: `./create_app_summaries.bash`

appSummaryJson() {
  infile=$1
  cat $infile | jq -r '[.Data.RootChunk.appearances[].Data |
    { name: .name["$value"],
      components: [.components[]? | select(.["$type"] != "entVisualControllerComponent") | {
        id,
        name: .name["$value"],
        type: .["$type"],
        mesh: .mesh?.DepotPath["$value"],
        meshApp: .meshAppearance?["$value"],
        chunkMask: .chunkMask
      }],
      visual: [[.components[] | select(.["$type"] == "entVisualControllerComponent")] | first |
        .appearanceDependency[]? | {
          compName: .componentName["$value"],
          appName: .appearanceName["$value"],
          mesh: .mesh.DepotPath["$value"]
        }],
      paramsBuf: [.parametersBuffer.parameterBuffers[0]? |
        .Data.Files[]?.RootChunk.componentsData[]?.componentID] | to_entries | map( { (.key|tostring): .value} ) | add
    }
  ] | sort_by(.name | ascii_downcase)'
}

for file in `find . -name \*.app.json`; do
  appSummaryJson ${file%.*}.json > ${file%.*}.summary.json
done


This all isn't totally necessary, but it can help for special use-cases other than searching, too, such as diffing and reporting on changes that occurred between your project file edits and the original base files.


About WolvenKit's Scripting Capability

In this same vein of extra programming, custom scripting for WolvenKit (.wscript files) can also aid in development:

I use a modified Wolvenkit_FileValidation.wscript to provide a report of any inconsistent entVisualController dependencies and parameterBuffers entries, which makes finding the entries easier to modify (Example code: https://gist.github.com/mnh86/176c2a5de654985f89765c55eb5fddfd).

This all goes beyond the scope of what I'm willing to explain in this article, but I think it is important capability to mention if you are interested in additional ways to automate and speed up your own workflow.


___________________________________________________________________________________________________________________________
Additional Info

The workflow can be straightforward once you get used to the tooling and asset data structures.

The tooling can be a bit awkward to use at times, but it has been improving and the WolvenKit community is currently very active. They have reverse engineered and implemented a lot of great modding capability for this game across the board within a relatively short amount of time considering the number of developers involved. Hats off to that team.

Testing changes to appearances can be a somewhat slow process. Some advice to consider:
  • Explore options for shortening the time to start the game. Use mods like No Intro Videos and game .exe launch options such as `--launcher-skip` and `-skipStartScreen` parameters to skip the launcher and start screens.
  • Make bulk changes to several appearances in WolvenKit first before testing all at once in game.

Let me know if there are parts of this that could use clarification or additional information. And if you know of extra info or other tutorials that would be helpful to others in this context, I'd be happy to include here if willing to share.


CHANGELOG
- 28 Dec 2023: v1.2 Edited Appearance Colorvariants section with new info.
- 12 Dec 2023: v1.1 Additional info for Appearance Colorvariants; More direct tutorial links.
- 09 Dec 2023: v1.0 Initial release.
- 04 Dec 2023: v0.x First drafts.

(Any updates not logged here can be assumed to be small edits with no new information)

The Naked NPCs Mod Creation Process © 2023 by marnhorn is licensed under CC BY-SA 4.0 

Article information

Added on

Edited on

Written by

marnhorn

3 comments

  1. Mize0
    Mize0
    • member
    • 0 kudos
    "In the panel on the right, select a different "Appearance" name. Select "prostitute_wa_prostitute_wa_01" - you should see the 3d model change."
    "Scroll down to "t1_024_wa_tshirt__sweater" and deselect its checkbox - Oopsie! Now she is shirtless"

    Only the 3D model change but the  hierarchical table on the right isn't updated so i can't hide and edit things.
    How can i solve it please ?
    1. marnhorn
      marnhorn
      • premium
      • 176 kudos
      This example was intended to demo how to hide and show meshes/submeshes in the mesh preview.

      It isn't clear to me what you mean by the "hierarchical table on the right isn't updated so I can't hide and edit things". If you can provide more details, I'll try to help.

      This is ultimately within WolvenKit, so you might try looking at their help docs. It may be a difference in versions I've used vs what you're using (this guide was done with WolvenKit 8.12.3-nightly.2024-02-09+62).
  2. derekgates
    derekgates
    • premium
    • 1 kudos
    This is an incredible practice (removing silos of information) and I greatly appreciate you posting this!