Utility class for choosing alternate human models and materials when character makeup includes arm, chest or back markings. It decides if arm-region models are needed, maps native model paths to arm-region variants, ensures the arm-region model is loaded asynchronously, and computes native body material for an arm material slot.
using System;
using System.Threading.Tasks;
namespace CharacterEditor;
/// <summary>Native human derivatives differ only in arm polygon material assignments.
/// The source Clothing choice stays authoritative; no replacement rig or mesh is sculpted here.</summary>
public static class HumanMarkingModels
{
const string ArmMaterials = "materials/makeup/arms/";
public static bool NeedsArmRegions(CharacterMakeupData makeup) => makeup?.Markings?.Any(x => x.Style != "none" && x.Region is "left-arm" or "right-arm" or "chest" or "back") == true;
public static string ModelPath(string native, CharacterMakeupData makeup) => NeedsArmRegions(makeup) ? native switch
{
"models/citizen_human/citizen_human_male.vmdl" => "models/makeup/human_male_arm_regions.vmdl",
"models/citizen_human/citizen_human_female.vmdl" => "models/makeup/human_female_arm_regions.vmdl",
_ => native
} : native;
public static async Task EnsureLoadedAsync(CharacterAppearanceData data)
{
if (!NeedsArmRegions(data?.Makeup))
return;
var skin = ResourceLibrary.Get<Clothing>(data.SkinPath);
var path = ModelPath(skin.HumanSkinModel, data.Makeup);
var model = await Model.LoadAsync(path);
if (model is null || model.IsError)
throw new InvalidOperationException("The independently marked human arms are unavailable.");
}
public static int ArmSide(Material material) => !material.Name.StartsWith(ArmMaterials, StringComparison.OrdinalIgnoreCase) ? 0 : material.Name.Contains("_left_", StringComparison.OrdinalIgnoreCase) ? 1 : -1;
public static Material NativeMaterial(Material slot)
{
if (ArmSide(slot) == 0)
return slot;
var female = slot.Name.Contains("/female_", StringComparison.OrdinalIgnoreCase);
var light = slot.Name.EndsWith("_light.vmat", StringComparison.OrdinalIgnoreCase);
var name = female ? (light ? "female_body_mode_light" : "female_body_mode_dark") : (light ? "body_male_light" : "body_mode_dark");
return Material.Load($"models/citizen_human/bodies/{(female ? "female" : "male")}/{name}.vmat");
}
}