option 2 is the right family, and the runtime version of it is simpler than it sounds: the bake is just a projection, so you can run that projection at equip time instead of offline.
the primitive you want is nearest point on surface with uv readback. two routes:
geometry script route: capture the posed skm as a dynamic mesh once per equip (geometry script can read skinned meshes, or grab skinned vertex positions in c++), build its spatial index, then for each vertex of the armor piece find the nearest point on the skm. the query returns the surface position plus the triangle hit, and from the barycentric coords you read the skm’s uv0 at that spot. that uv is your splat target.
c++ route if you want it fast and untethered from bp: the GeometryProcessing module is the same machinery the editor bakers use. fDynamicMesh3 snapshot of the posed skm, fDynamicMeshAABBTree3 for queries, and a projection target call gives you nearest point + barycentrics + uv in one shot. wrap it in a parallelfor inside a world subsystem and that is your “runtime compiler”. a 5 piece loadout is maybe 100-200k projections against a cached tree, that’s a few milliseconds spread over a frame or two, and it only runs on equip like you planned.
the part that gets you “down to the mask’s actual outline” instead of just geometric coverage: don’t project bare vertices. for each piece vertex, first read its own uv in the piece’s painted mask, carry that value through the projection, and splat the mask value (not just a coverage bit) into the skm uv space buffer. the painted opacity travels with the projection. write each piece’s splat into its own render target (or channel pack 5 layers across two rgba targets), run a small dilation and blur pass over the splat to heal the seams, and the mask outline lands on the skm in the piece’s own handwriting.
subsystem structure: key the cache by loadout, recompute only on equip/unequip, reuse a small pool of render targets. one nice shortcut: if the armor pieces are skinned to the same skeleton as the skm, the mapping barely changes with pose, so you can sample at bind pose and cache per asset instead of per loadout, which makes equip nearly free.
compositing is then exactly what you described: skm master material samples the packed masks and resolves per pixel in layer priority, higher priority wins where masks overlap, and a mid only swaps the mask textures. no topology changes, no per region variants.
gotchas up front: uv islands mean splats can straddle seams, dilate a few texels and accept hard island borders, they read as clean material edges anyway. 1k to 2k texture on the skm uv is plenty for silhouettes. and if you want an artist facing validation pass, the same projection can run at import time in the geometry script editor bake path so bad masks get flagged before they ever reach the runtime compiler.
project zomboid’s system is genuinely the right mental model, it’s this exact splat and composite in 2d pixel space. you’re doing it on a uv unwrapped surface instead.