Art: silhouette broadening in-game + PlayerRigTools material-drift fix

Silhouette: broadened the armour's shoulder pads (inflate Shoulder_L/R-weighted pad caps about the
shoulder joint, smooth falloff, arms untouched) + beefed the boots -> Silhouette_Armour.asset (built
idempotently from the stock Synty armour). Bind pose is a T-pose, so broadening is by BONE WEIGHT, not
raw X (a naive |X| threshold would grab the outstretched arms).

Drift fix: baked the correct assignments into the tools so a re-run no longer reverts them -
BuildBathynautPlayer now does the brass/undersuit split + swaps the armour to Silhouette_Armour;
AttachBathynautKit assigns gunmetal tanks / warm beacon / Mark-V brass-gunmetal-glass (3 submeshes).
Full rebuild chain re-run clean + verified live: identical correct diver, reproducible.
(replace_method stripped the [MenuItem] attrs -> re-added; see the swallow-adjacent-line gotcha.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 15:27:29 -07:00
parent 749aedfbde
commit 977b5c1f8e
4 changed files with 5065 additions and 4114 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a93cc4098fe80464ba0840fce6055c9f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 4300000
userData:
assetBundleName:
assetBundleVariant:
File diff suppressed because it is too large Load Diff
@@ -69,16 +69,22 @@ namespace ProjectM.EditorTools
/// root, keep the feet-on-ground Root offset, re-slot the deformation material, and adopt the clone's
/// avatar (same shared humanoid Characters avatar — AC_PlayerTopDown's muscle clips retarget). Both
/// frames share this visual until per-frame player models land. Idempotent / re-runnable.</summary>
[MenuItem("ProjectM/Animation/Player - Build Bathynaut Player (LANTERN)")]
[MenuItem("ProjectM/Animation/Player - Build Bathynaut Player (LANTERN)")]
public static void BuildBathynautPlayer()
{
const string synty = "Assets/Synty/PolygonSciFiSpace/Prefabs/Characters/SM_Chr_SpaceSoldier_HelmetArmor_Male_01.prefab";
const string matPath = "Assets/_Project/Materials/M_SuitFrame_Bathynaut_Animated.mat";
const string brassPath = "Assets/_Project/Art/Materials/M_Diver_Brass_Skinned.mat";
const string underPath = "Assets/_Project/Art/Materials/M_Diver_Undersuit_Skinned.mat";
const string silhouettePath = "Assets/_Project/Art/Models/Silhouette_Armour.asset";
const string output = "Assets/_Project/Prefabs/Player.prefab";
const float rootY = -0.90f; // the player's existing feet-on-ground offset (entity origin = capsule center)
var syntyAsset = AssetDatabase.LoadAssetAtPath<GameObject>(synty);
var mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
var brass = AssetDatabase.LoadAssetAtPath<Material>(brassPath);
var under = AssetDatabase.LoadAssetAtPath<Material>(underPath);
var silhouette = AssetDatabase.LoadAssetAtPath<Mesh>(silhouettePath); // optional: broadened shoulders/boots
if (syntyAsset == null) { Debug.LogError($"[PlayerRigTools] Suit body prefab missing: {synty}"); return; }
if (mat == null) { Debug.LogError($"[PlayerRigTools] Deformation material missing: {matPath}"); return; }
@@ -104,11 +110,17 @@ namespace ProjectM.EditorTools
if (rootBone != null) { var lp = rootBone.localPosition; lp.y = rootY; rootBone.localPosition = lp; }
else Debug.LogWarning("[PlayerRigTools] No 'Root' bone after the swap; feet offset skipped.");
// Deformation material on every SMR slot (else unskinned-static under Entities Graphics).
// Diver material split: brass armour, dark-teal undersuit body, suit default elsewhere. The
// armour ALSO swaps to the broadened Silhouette mesh (stronger shoulders/boots) when present.
// Color MUST ride _BaseColorMap on these (the hit-flash per-instance _BaseColor override is
// white and replaces the material _BaseColor) - see the DiverTex solid-colour textures.
foreach (var smr in root.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
Material use = mat;
if (smr.name.Contains("Armour") && brass != null) { use = brass; if (silhouette != null) smr.sharedMesh = silhouette; }
else if (smr.name == "SM_Chr_SpaceSoldier_Male_01" && under != null) use = under;
var mats = smr.sharedMaterials;
for (int i = 0; i < mats.Length; i++) mats[i] = mat;
for (int i = 0; i < mats.Length; i++) mats[i] = use;
smr.sharedMaterials = mats;
}
@@ -116,7 +128,7 @@ namespace ProjectM.EditorTools
if (anim != null && avatar != null) anim.avatar = avatar; // controller (AC_PlayerTopDown) stays
PrefabUtility.SaveAsPrefabAsset(root, output);
Debug.Log("[PlayerRigTools] Bathynaut suit body swapped onto Player.prefab (in place).");
Debug.Log("[PlayerRigTools] Bathynaut body swapped onto Player.prefab (brass/undersuit split + silhouette armour).");
}
finally
{
@@ -133,23 +145,26 @@ namespace ProjectM.EditorTools
/// are re-bound onto the player's existing flattened skeleton by bone NAME. Brass rides the shared
/// M_Skinned_Palette (AnimatedLitShader + PaletteAtlas); glow rides M_EmissiveGloam_WarmSkinned
/// (steady warm = true light). Idempotent / re-runnable.</summary>
[MenuItem("ProjectM/Animation/Player - Attach Bathynaut Kit (LANTERN)")]
[MenuItem("ProjectM/Animation/Player - Attach Bathynaut Kit (LANTERN)")]
public static void AttachBathynautKit()
{
const string kitFbx = "Assets/_Project/Art/Models/SM_Suit_BathynautKit.fbx";
const string brassMat = "Assets/_Project/Art/Materials/M_Skinned_Palette.mat";
const string gunMat = "Assets/_Project/Art/Materials/M_Diver_Metal_Skinned.mat";
const string brassMat = "Assets/_Project/Art/Materials/M_Diver_Brass_Skinned.mat";
const string glassMat = "Assets/_Project/Art/Materials/M_Diver_PortholeGlow_Skinned.mat";
const string glowMat = "Assets/_Project/Art/Materials/M_EmissiveGloam_WarmSkinned.mat";
const string bodyMat = "Assets/_Project/Materials/M_SuitFrame_Bathynaut_Animated.mat";
const string barePrefab = "Assets/Synty/PolygonSciFiSpace/Prefabs/Characters/SM_Chr_SpaceSoldier_Male_01.prefab";
const string output = "Assets/_Project/Prefabs/Player.prefab";
var kit = AssetDatabase.LoadAssetAtPath<GameObject>(kitFbx);
var mGun = AssetDatabase.LoadAssetAtPath<Material>(gunMat);
var mBrass = AssetDatabase.LoadAssetAtPath<Material>(brassMat);
var mGlass = AssetDatabase.LoadAssetAtPath<Material>(glassMat);
var mGlow = AssetDatabase.LoadAssetAtPath<Material>(glowMat);
var mBody = AssetDatabase.LoadAssetAtPath<Material>(bodyMat);
if (kit == null) { Debug.LogError($"[PlayerRigTools] Kit FBX missing: {kitFbx}"); return; }
if (mBrass == null) { Debug.LogError($"[PlayerRigTools] Brass material missing: {brassMat}"); return; }
if (mGlow == null) { Debug.LogError($"[PlayerRigTools] Glow material missing: {glowMat}"); return; }
if (mGun == null || mBrass == null || mGlow == null) { Debug.LogError("[PlayerRigTools] Diver kit materials missing."); return; }
var root = PrefabUtility.LoadPrefabContents(output);
try
@@ -160,13 +175,23 @@ namespace ProjectM.EditorTools
if (!bones.ContainsKey(t.name)) bones.Add(t.name, t);
foreach (var src in kit.GetComponentsInChildren<SkinnedMeshRenderer>(true))
GraftSmr(root, bones, src, src.name.Contains("Glow") ? mGlow : mBrass);
{
bool isHelmet = src.name.Contains("Helmet") || src.name.Contains("MarkV");
bool isGlow = src.name.Contains("Glow") || src.name.Contains("Beacon");
// graft with a base material, then the Mark-V gets its 3 submesh materials applied.
GraftSmr(root, bones, src, isHelmet ? mBrass : (isGlow ? mGlow : mGun));
if (isHelmet)
{
var hc = root.transform.Find(src.name);
var hs = hc != null ? hc.GetComponent<SkinnedMeshRenderer>() : null;
// submesh order (from the .blend slots): 0 brass bonnet / 1 gunmetal accents / 2 teal glass
if (hs != null) hs.sharedMaterials = new[] { mBrass, mGun, mGlass != null ? mGlass : mGlow };
}
}
// 07-15 operator fork: brass dome + BARE head (the kitbash look) — drop the Synty sci-fi helmet
// and graft the bare head SMR so the face reads through the amber porthole up close.
// Drop the Synty sci-fi helmet + graft the bare head (hidden inside the Mark-V; keeps the neck seam).
var helmet = root.transform.Find("SM_Chr_Attach_SpaceSoldier_Male_Helmet_01");
if (helmet != null) Object.DestroyImmediate(helmet.gameObject);
// Purge any wrong-variant head graft (the Synty container holds Female/Male heads) before re-grafting.
for (int i = root.transform.childCount - 1; i >= 0; i--)
{
var c = root.transform.GetChild(i);
@@ -181,7 +206,7 @@ namespace ProjectM.EditorTools
foreach (var s in bare.GetComponentsInChildren<SkinnedMeshRenderer>(true))
if (s.name == "SM_Chr_SpaceSoldier_Head_Male_01") { headSrc = s; break; }
if (headSrc != null && mBody != null) GraftSmr(root, bones, headSrc, mBody, rebase: false); // meter-scale Synty source + blend-skinned neck: native bindposes are correct
else Debug.LogWarning("[PlayerRigTools] Bare head SMR (or body material) not found helmet removed without a head.");
else Debug.LogWarning("[PlayerRigTools] Bare head SMR (or body material) not found - helmet removed without a head.");
}
PrefabUtility.SaveAsPrefabAsset(root, output);
@@ -192,7 +217,7 @@ namespace ProjectM.EditorTools
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("[PlayerRigTools] Bathynaut kit attached to Player.prefab (in place).");
Debug.Log("[PlayerRigTools] Bathynaut kit attached (gunmetal tanks + warm beacon + Mark-V brass/gunmetal/glass).");
}
/// <summary>Re-bind a source SkinnedMeshRenderer onto the player's existing flattened skeleton by bone