104 lines
7.3 KiB
C#
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); }
|
|
}
|