using UnityEngine;
namespace BrewMonster
{
///
/// Shared static helpers used by ElsePlayerPortraitCapture and HostPlayerPortraitCapture.
///
public static class PortraitCaptureUtils
{
private static readonly string[] HeadBoneNames =
{
"Bip01 Head", "Bip001 Head", "head", "Head", "Bone_Head", "HEAD"
};
// ──────────────────────────────────────────────────────────────────────────────
// Model / bone search
// ──────────────────────────────────────────────────────────────────────────────
///
/// Returns the root transform of the visual model inside .
/// Tries to find the first child that owns a SkinnedMeshRenderer; falls back to the
/// root itself if a SkinnedMeshRenderer is found anywhere in the hierarchy.
///
public static Transform FindVisualModelRoot(Transform playerRoot)
{
if (playerRoot == null) return null;
// Walk immediate children first — model root is usually a direct child
for (int i = 0; i < playerRoot.childCount; i++)
{
var child = playerRoot.GetChild(i);
if (child.GetComponentInChildren() != null)
return child;
}
// Fallback: if the root itself contains a SMR, use it
if (playerRoot.GetComponentInChildren() != null)
return playerRoot;
return null;
}
///
/// Searches 's hierarchy for a head bone by common name patterns.
///
public static Transform FindHeadBone(Transform modelRoot)
{
if (modelRoot == null) return null;
foreach (var boneName in HeadBoneNames)
{
var bone = FindChildByName(modelRoot, boneName);
if (bone != null) return bone;
}
return null;
}
/// Recursive depth-first search for a child transform by exact name.
public static Transform FindChildByName(Transform root, string name)
{
if (root.name == name) return root;
for (int i = 0; i < root.childCount; i++)
{
var result = FindChildByName(root.GetChild(i), name);
if (result != null) return result;
}
return null;
}
// ──────────────────────────────────────────────────────────────────────────────
// Clone setup
// ──────────────────────────────────────────────────────────────────────────────
///
/// Strips non-visual components from a cloned GameObject so it becomes a lightweight
/// render-only puppet. Keeps Animator so idle animations continue to play.
///
public static void CleanupCloneComponents(GameObject go)
{
foreach (var col in go.GetComponentsInChildren(true))
Object.Destroy(col);
foreach (var rb in go.GetComponentsInChildren(true))
Object.Destroy(rb);
foreach (var mb in go.GetComponentsInChildren(true))
{
if (mb is Animator) continue;
Object.Destroy(mb);
}
}
/// Recursively sets the layer of and all its children.
public static void SetLayerRecursive(GameObject go, int layer)
{
go.layer = layer;
foreach (Transform child in go.transform)
SetLayerRecursive(child.gameObject, layer);
}
// ──────────────────────────────────────────────────────────────────────────────
// Camera / RenderTexture factories
// ──────────────────────────────────────────────────────────────────────────────
///
/// Creates a Camera child GameObject under whose culling mask
/// covers only . Camera starts disabled.
///
public static Camera CreatePortraitCamera(Transform parent, string goName, int portraitLayer, float fov)
{
var camGO = new GameObject(goName);
camGO.transform.SetParent(parent, false);
var cam = camGO.AddComponent();
cam.cullingMask = 1 << portraitLayer;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = Color.clear;
cam.fieldOfView = fov;
cam.nearClipPlane = 0.1f;
cam.farClipPlane = 200f;
cam.enabled = false;
return cam;
}
///
/// Creates a 256×256 ARGB32 RenderTexture and wires it to if provided.
///
public static RenderTexture CreatePortraitRT(string rtName, Camera cam)
{
var rt = new RenderTexture(256, 256, 16, RenderTextureFormat.ARGB32)
{
name = rtName,
antiAliasing = 1
};
rt.Create();
if (cam != null) cam.targetTexture = rt;
return rt;
}
}
}