Utility for resolving and fitting "human" clothing models to a unified character body. It chooses which model path to use for an item (native, male/female human variants), checks whether head replacements should keep the human neck skin, loads the model, and calls AutomaticWardrobeFit.Fit to retarget meshes and apply morphs to the renderer.
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.