140 lines
6.2 KiB
C#
140 lines
6.2 KiB
C#
using UnityEngine;
|
||
|
||
namespace BrewMonster
|
||
{
|
||
/// <summary>
|
||
/// Shared static helpers used by ElsePlayerPortraitCapture and HostPlayerPortraitCapture.
|
||
/// </summary>
|
||
public static class PortraitCaptureUtils
|
||
{
|
||
private static readonly string[] HeadBoneNames =
|
||
{
|
||
"Bip01 Head", "Bip001 Head", "head", "Head", "Bone_Head", "HEAD"
|
||
};
|
||
|
||
// ──────────────────────────────────────────────────────────────────────────────
|
||
// Model / bone search
|
||
// ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Returns the root transform of the visual model inside <paramref name="playerRoot"/>.
|
||
/// 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.
|
||
/// </summary>
|
||
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<SkinnedMeshRenderer>() != null)
|
||
return child;
|
||
}
|
||
|
||
// Fallback: if the root itself contains a SMR, use it
|
||
if (playerRoot.GetComponentInChildren<SkinnedMeshRenderer>() != null)
|
||
return playerRoot;
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Searches <paramref name="modelRoot"/>'s hierarchy for a head bone by common name patterns.
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>Recursive depth-first search for a child transform by exact name.</summary>
|
||
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
|
||
// ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Strips non-visual components from a cloned GameObject so it becomes a lightweight
|
||
/// render-only puppet. Keeps Animator so idle animations continue to play.
|
||
/// </summary>
|
||
public static void CleanupCloneComponents(GameObject go)
|
||
{
|
||
foreach (var col in go.GetComponentsInChildren<Collider>(true))
|
||
Object.Destroy(col);
|
||
|
||
foreach (var rb in go.GetComponentsInChildren<Rigidbody>(true))
|
||
Object.Destroy(rb);
|
||
|
||
foreach (var mb in go.GetComponentsInChildren<MonoBehaviour>(true))
|
||
{
|
||
if (mb is Animator) continue;
|
||
Object.Destroy(mb);
|
||
}
|
||
}
|
||
|
||
/// <summary>Recursively sets the layer of <paramref name="go"/> and all its children.</summary>
|
||
public static void SetLayerRecursive(GameObject go, int layer)
|
||
{
|
||
go.layer = layer;
|
||
foreach (Transform child in go.transform)
|
||
SetLayerRecursive(child.gameObject, layer);
|
||
}
|
||
|
||
// ──────────────────────────────────────────────────────────────────────────────
|
||
// Camera / RenderTexture factories
|
||
// ──────────────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Creates a Camera child GameObject under <paramref name="parent"/> whose culling mask
|
||
/// covers only <paramref name="portraitLayer"/>. Camera starts disabled.
|
||
/// </summary>
|
||
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<Camera>();
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Creates a 256×256 ARGB32 RenderTexture and wires it to <paramref name="cam"/> if provided.
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
}
|