Project_WL/AgentScripts/WL834_PoseCapture.cs

104 lines
7.3 KiB
C#

// WL834_PoseCapture.cs — 클립의 포즈를 에디트 모드에서 렌더해 한 장(프레임 띠)으로 저장 (Play 타이밍 운에 기대지 않고 모션 자체를 본다)
// unity command run_script --file AgentScripts/WL834_PoseCapture.cs --entry WL834_PoseCapture.Run --args "[\"<fbx>\", \"<clipName 또는 빈칸>\", \"<weaponPrefab>\", <socketIndex>, \"<outName>\", 8]"
// 예: --args "[\"Assets/ExplosiveLLC/Archer Warrior Mecanim Animation Pack/Animations/Archer@Attack1_S.FBX\", \"\", \"PP_Theme_05_Bow_003\", 6, \"bow_attack1\", 8]"
// 치비(LH_M05 · 런타임 스케일)에 무기를 끼우고 클립을 균등 N 프레임 샘플 → 카메라(앞-왼 3/4 · 내려보기 30°)로 렌더 → Assets/Screenshots_WL/pose_<outName>.png (가로 띠).
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
public static class WL834_PoseCapture
{
public static object Run(string fbx, string clipName, string weaponPrefab, int socketIndex, string outName, int frames)
{
return RunR(fbx, clipName, weaponPrefab, socketIndex, outName, frames, "0,0,0");
}
/// <summary>무기 로컬 회전(오일러 "x,y,z")을 더해 렌더 — 소켓 방향 규약 보정 후보를 비교한다.</summary>
public static object RunR(string fbx, string clipName, string weaponPrefab, int socketIndex, string outName, int frames, string euler)
{
Vector3 e = Vector3.zero;
try { var p = euler.Split(','); if (p.Length == 3) e = new Vector3(float.Parse(p[0]), float.Parse(p[1]), float.Parse(p[2])); } catch (System.Exception) { }
string pcName = WL.Character.WLCharacterSwapSettings.Resolve("Ai01");
var model = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Res_Addr/PC/" + pcName + ".prefab");
if (model == null) return "PC 프리팹 없음";
var clips = fbx.EndsWith(".anim") // #838 FR .anim 에셋도 받는다
? new[] { AssetDatabase.LoadAssetAtPath<AnimationClip>(fbx) }.Where(c => c != null).ToArray()
: AssetDatabase.LoadAllAssetsAtPath(fbx).OfType<AnimationClip>().Where(c => !c.name.StartsWith("__preview")).ToArray();
var clip = string.IsNullOrEmpty(clipName) ? clips.FirstOrDefault() : (clips.FirstOrDefault(c => c.name == clipName) ?? clips.FirstOrDefault(c => c.name.Contains(clipName)));
if (clip == null) return "클립 없음 " + fbx + " [" + string.Join(",", clips.Select(c => c.name).ToArray()) + "]";
float rootScale = 1f;
try
{
var arr = Newtonsoft.Json.Linq.JArray.Parse(System.IO.File.ReadAllText("Assets/ResWork/Table/Export/ClassConfig.json"));
foreach (var row in arr) if ((string)row["n_ClassID"] == "10101") { float f; float.TryParse((string)row["f_Scale"], out f); rootScale = Mathf.Max(f, 0.01f) * Mathf.Max(WL.Character.WLCharacterSwapSettings.HeightCompensation, 0.01f); break; }
}
catch (System.Exception) { }
int n = Mathf.Clamp(frames, 2, 16);
int tw = 300, th = 400;
var scene = EditorSceneManager.NewPreviewScene();
bool animMode = false;
RenderTexture rt = null; Texture2D sheet = null;
try
{
var go = (GameObject)PrefabUtility.InstantiatePrefab(model, scene);
go.transform.position = Vector3.zero; go.transform.rotation = Quaternion.identity; go.transform.localScale = Vector3.one * rootScale;
var pc = go.GetComponentInChildren<PCActor>(true);
if (pc == null || pc.tfs_weapon == null || socketIndex < 0 || socketIndex >= pc.tfs_weapon.Length) return "PCActor/tfs_weapon 없음 또는 소켓 범위 밖";
if (!string.IsNullOrEmpty(weaponPrefab))
{
var wp = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Res_Addr/Weapon/" + weaponPrefab + ".prefab");
if (wp == null) return "무기 프리팹 없음 " + weaponPrefab;
var w = (GameObject)PrefabUtility.InstantiatePrefab(wp, scene);
w.transform.SetParent(pc.tfs_weapon[socketIndex], false); w.transform.localPosition = Vector3.zero; w.transform.localRotation = Quaternion.identity; w.transform.localScale = Vector3.one;
var fitter = go.GetComponent<WL.Character.WLWeaponFitter>();
if (fitter == null) fitter = go.AddComponent<WL.Character.WLWeaponFitter>();
try { fitter.Fit(w.transform, pc.tfs_weapon[socketIndex]); } catch (System.Exception) { }
if (e != Vector3.zero) w.transform.localRotation = Quaternion.Euler(e) * w.transform.localRotation;
}
var anim = go.GetComponentInChildren<Animator>();
var animGo = anim.gameObject; anim.applyRootMotion = false;
var lightGo = new GameObject("light"); SceneManager_Move(lightGo, scene);
var light = lightGo.AddComponent<Light>(); light.type = LightType.Directional; light.intensity = 1.2f; lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
var camGo = new GameObject("cam"); SceneManager_Move(camGo, scene);
var cam = camGo.AddComponent<Camera>(); cam.scene = scene; cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(0.55f, 0.65f, 0.45f); cam.fieldOfView = 30f; cam.nearClipPlane = 0.05f; cam.farClipPlane = 50f;
Vector3 look = new Vector3(0f, 0.55f * rootScale, 0f);
camGo.transform.position = look + Quaternion.Euler(25f, -35f, 0f) * new Vector3(0f, 0f, -3.2f);
camGo.transform.LookAt(look);
rt = new RenderTexture(tw, th, 24); cam.targetTexture = rt;
sheet = new Texture2D(tw * n, th, TextureFormat.RGB24, false);
AnimationMode.StartAnimationMode(); animMode = true;
for (int i = 0; i < n; i++)
{
float t = clip.length * i / (n - 1);
AnimationMode.BeginSampling(); AnimationMode.SampleAnimationClip(animGo, clip, Mathf.Min(t, clip.length - 0.0001f)); AnimationMode.EndSampling();
cam.Render();
var prev = RenderTexture.active; RenderTexture.active = rt;
var tex = new Texture2D(tw, th, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, tw, th), 0, 0); tex.Apply();
RenderTexture.active = prev;
sheet.SetPixels(i * tw, 0, tw, th, tex.GetPixels());
Object.DestroyImmediate(tex);
}
AnimationMode.StopAnimationMode(); animMode = false;
sheet.Apply();
string outPath = "Assets/Screenshots_WL/pose_" + outName + ".png";
System.IO.Directory.CreateDirectory("Assets/Screenshots_WL");
System.IO.File.WriteAllBytes(outPath, sheet.EncodeToPNG());
return "저장 " + outPath + " · 클립 " + clip.name + " len=" + clip.length.ToString("F2") + "s · 프레임 " + n + " · 무기 " + weaponPrefab + "@" + socketIndex + " euler=" + euler;
}
finally
{
if (animMode) AnimationMode.StopAnimationMode();
if (rt != null) { RenderTexture.active = null; rt.Release(); Object.DestroyImmediate(rt); }
if (sheet != null) Object.DestroyImmediate(sheet);
EditorSceneManager.ClosePreviewScene(scene);
}
}
static void SceneManager_Move(GameObject go, UnityEngine.SceneManagement.Scene scene) { UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(go, scene); }
}