CharacterKit/UnifiedWardrobe.cs

Utility for choosing and fitting human wardrobe models for character clothing. It decides whether head replacements retain the neck skin, selects the correct model path based on character base shape and clothing metadata, loads and resolves models, invokes an automatic fit to body/morphs, and applies body morphs to a renderer.

File Access
namespace CharacterEditor;
/// <summary>Authored human cuts fitted automatically to both unified body endpoints.</summary>
public static partial class UnifiedWardrobe
{
	/// <summary>Head replacements can retain skin or deliberately replace the entire neck.</summary>
	public static bool RetainsHumanNeck(Clothing item)
	{
		if (string.IsNullOrEmpty(item.HumanSkinModel)) return true;
		var skin = Model.Load(item.HumanSkinModel);
		// Use the replacement's native skin presentation. A transparent/non-skin
		// head material must not be replaced with the unified human's skin neck.
		return skin.MeshInfo.Meshes.Where(m => m.Name == "head_01")
			.SelectMany(m => m.DrawCalls).Any(d => d.Material?.ShaderName == "shaders/skin.shader");
	}

	public static string ModelPath(Clothing item, IEnumerable<Clothing> selected, TagSet tags, CharacterAppearanceData data)
	{
		var native = item.GetModel(selected.ToArray(), tags);
		if (data.BaseShape is null)
			return native;
		// Preserve conditional model overrides selected by native clothing metadata.
		if (native != item.Model && native != item.HumanAltModel && native != item.HumanAltFemaleModel)
			return native;
		// Explicit preference selects only the authored cut; the morph still uses
		// the actual body blend. Single-variant items retain their usual fallback.
		if (data.BaseShape.ClothingVariant is not null && !string.IsNullOrEmpty(item.HumanAltModel) && !string.IsNullOrEmpty(item.HumanAltFemaleModel))
		{
			var choice = data.BaseShape.ClothingVariant == "male" ? item.HumanAltModel : item.HumanAltFemaleModel;
			return choice;
		}

		// A single authored human variant is usable across the full body range.
		var preferred = data.BaseShape.MaleBody >= .5f ? item.HumanAltModel : item.HumanAltFemaleModel;
		if (string.IsNullOrEmpty(preferred))
			preferred = string.IsNullOrEmpty(item.HumanAltModel) ? item.HumanAltFemaleModel : item.HumanAltModel;
		return !string.IsNullOrEmpty(preferred) ? preferred : native;
	}

	public static Model Resolve(Clothing item, IEnumerable<Clothing> selected, TagSet tags, CharacterAppearanceData data)
	{
		var path = ModelPath(item, selected, tags, data);
		var native = Model.Load(path);
		if (data.BaseShape is null || string.IsNullOrEmpty(path) || (string.IsNullOrEmpty(item.HumanAltModel) && string.IsNullOrEmpty(item.HumanAltFemaleModel)))
			return native;
		if (item.Category.ToString().StartsWith("FacialHair") || item.Category.ToString().StartsWith("Hair"))
			return native;
		bool female = path == item.HumanAltFemaleModel && path != item.HumanAltModel;
		if (path != item.HumanAltModel && path != item.HumanAltFemaleModel)
			female = tags.Has("female");
		return AutomaticWardrobeFit.Fit(native, female, fitFace: (item.HideBody & Clothing.BodyGroups.Head) == 0);
	}

	public static void Apply(SkinnedModelRenderer renderer, CharacterAppearanceData data)
	{
		if (data.BaseShape is not null && AutomaticWardrobeFit.IsFitted(renderer.Model))
			renderer.Morphs.Set("ce_body_female", 1 - data.BaseShape.MaleBody);
	}
}// Keep hair outside body fitting.