Code/CharacterKit/HumanMakeupAssets.cs

Static helper that loads and caches human makeup materials, eye details, liner textures and marking surfaces for character customization. It validates shader schema and texture sizes, exposes cached Texture/Material getters and an async EnsureLoadedAsync(id) to preload required assets.

File Access
using System;
using System.Threading.Tasks;

namespace CharacterEditor;
/// <summary>Native compiled materials carry the mask textures and compute shader to remote clients.</summary>
public static class HumanMakeupAssets
{
	public const string ShaderPath = "shaders/fate_pending/human_cosmetics.shader";
	public const string CarrierPath = "materials/makeup/cosmetics_compositor.vmat";
	static readonly Dictionary<string, Material> masks = new();
	static readonly Dictionary<string, Material> markings = new();
	static Material compositor;
	static Texture linerDetailTexture;
	static readonly Dictionary<string, Material> eyeDetails = new();
	static readonly Dictionary<string, Texture> surfaces = new();
	public static async Task EnsureLoadedAsync(string id)
	{
		compositor ??= await Material.LoadAsync(CarrierPath);
		var variables = compositor?.Shader?.Schema?.Variables.Select(x => x.Name).ToArray() ?? Array.Empty<string>();
		if (compositor?.ShaderName != ShaderPath || !compositor.Shader.IsValid || !new[]
		{
			"EyeDetail",
			"LinerDetail",
			"LinerOptions",
			"WingTip",
			"Liner",
			"LowerEye"
		}.All(variables.Contains) || variables.Contains("SkinTint"))
		{
			compositor = null;
			throw new InvalidOperationException("The current human cosmetics shader is unavailable or has an incompatible schema.");
		}

		if (!masks.TryGetValue(id, out var material) || material is null)
		{
			material = await Material.LoadAsync($"materials/makeup/{id}.vmat");
			var texture = material?.GetTexture("g_tColor");
			if (texture is null || texture.IsError || texture.Width != 2048 || texture.Height != 2048)
				throw new InvalidOperationException("A human makeup mask is unavailable: " + id);
			masks[id] = material;
		}

		if (!eyeDetails.ContainsKey(id))
		{
			var detail = await Material.LoadAsync($"materials/makeup/{id}_eye.vmat");
			if (detail?.GetTexture("g_tColor")is not { IsError: false, Width: 2048, Height: 2048 })
				throw new InvalidOperationException("An eye makeup mask is unavailable: " + id);
			eyeDetails[id] = detail;
		}
		if (linerDetailTexture is null)
		{
			linerDetailTexture = await Texture.LoadAsync("textures/makeup/eyeliner_details.vtex");
			if (linerDetailTexture is not { IsError: false, Width: 2048, Height: 2048 })
			{
				linerDetailTexture = null;
				throw new InvalidOperationException("The eyeliner detail mask is unavailable.");
			}
		}

		if (!markings.ContainsKey(id))
		{
			var marking = await Material.LoadAsync($"materials/makeup/{id}_marking.vmat");
			if (marking?.GetTexture("g_tColor")is not { IsError: false, Width: 2048, Height: 2048 })
				throw new InvalidOperationException("A human marking mask is unavailable: " + id);
			markings[id] = marking;
		}

		foreach (var surface in new[]
		{
			id,
			id is "human02" or "human04" ? "female" : "male"
		}

		)
			foreach (var kind in new[]
			{
				"position_hi",
				"position_lo",
				"normal"
			}

			)
			{
				var key = surface + "_" + kind;
				if (surfaces.ContainsKey(key))
					continue;
				var texture = await Texture.LoadAsync($"textures/makeup/{key}.vtex");
				if (texture is not { IsError: false, Width: 1024, Height: 1024 })
					throw new InvalidOperationException("A marking surface is unavailable: " + key);
				surfaces[key] = texture;
			}
	}

	public static Texture Mask(string skinPath) => masks[System.IO.Path.GetFileNameWithoutExtension(skinPath)].GetTexture("g_tColor");
	public static Texture EyeDetail(string skinPath) => eyeDetails[System.IO.Path.GetFileNameWithoutExtension(skinPath)].GetTexture("g_tColor");
	public static Texture LinerDetail => linerDetailTexture;
	public static Texture Marking(string skinPath) => markings[System.IO.Path.GetFileNameWithoutExtension(skinPath)].GetTexture("g_tColor");
	public static Texture Surface(string skinPath, string kind, bool head)
	{
		var id = System.IO.Path.GetFileNameWithoutExtension(skinPath);
		var key = (head ? id : id is "human02" or "human04" ? "female" : "male") + "_" + kind;
		return surfaces[key];
	}
}