UnityVATTool/Assets/Common/Scripts/Editor/OrientationCalibrator.cs

201 lines
8.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* OrientationCalibrator
*
* 用途:调试 R02 (或任意 3D View 子物体) 的朝向 + 缩放 + 偏移,
* 一键 apply 到 prefab 上持久化保存。
*
* 默认目标:选中场景里的 GameObject (含 "View" 子物体),然后菜单 Tools/OctoberStudio/VAT/Orientation Calibrator
*/
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace OctoberStudio.EditorTools
{
public class OrientationCalibrator : EditorWindow
{
private GameObject _target;
private Transform _view;
private Vector3 _euler = new Vector3(0f, 180f, 0f);
private float _uniformScale = 0.5f;
private Vector3 _localPos = Vector3.zero;
[MenuItem("Tools/OctoberStudio/VAT/Orientation Calibrator", priority = 10)]
public static void ShowWindow()
{
var win = GetWindow<OrientationCalibrator>("Orientation Calibrator");
win.AutoPickTarget();
}
private void OnEnable() => AutoPickTarget();
private void OnSelectionChange() { AutoPickTarget(); Repaint(); }
private void AutoPickTarget()
{
if (Selection.activeGameObject != null)
{
_target = Selection.activeGameObject;
_view = _target.transform.Find("View");
if (_view != null)
{
_euler = _view.localEulerAngles;
var s = _view.localScale;
_uniformScale = (s.x + s.y + s.z) / 3f;
_localPos = _view.localPosition;
}
}
}
private void OnGUI()
{
EditorGUILayout.LabelField("Orientation Calibrator", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(
"1) 在场景里选中 Wizard_R02 (或任意含 'View' 子物体的角色)\n" +
"2) 点 Quick Face 按钮预览朝向\n" +
"3) 调好后点 [Apply to Prefab] 持久化", MessageType.Info);
_target = (GameObject)EditorGUILayout.ObjectField("Target", _target, typeof(GameObject), true);
if (_target == null) return;
_view = _target.transform.Find("View");
if (_view == null)
{
EditorGUILayout.HelpBox("Target 没有名为 'View' 的子物体", MessageType.Warning);
return;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Quick Face (相机看 +Z 方向)", EditorStyles.boldLabel);
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Face Camera\n(Y=180)")) Apply(new Vector3(0, 180, 0));
if (GUILayout.Button("Face Right\n(Y=270)")) Apply(new Vector3(0, 270, 0));
if (GUILayout.Button("Face Left\n(Y=90)")) Apply(new Vector3(0, 90, 0));
if (GUILayout.Button("Face Away\n(Y=0)")) Apply(new Vector3(0, 0, 0));
}
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Lay Down +\n(X=-90)")) Apply(new Vector3(-90, 0, 0));
if (GUILayout.Button("Lay Down -\n(X=90)")) Apply(new Vector3(90, 0, 0));
if (GUILayout.Button("Tilt Up 15°")) Apply(_euler + new Vector3(-15, 0, 0));
if (GUILayout.Button("Tilt Down 15°")) Apply(_euler + new Vector3(15, 0, 0));
}
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("Rotate Y -45")) Apply(_euler + new Vector3(0, -45, 0));
if (GUILayout.Button("Rotate Y -15")) Apply(_euler + new Vector3(0, -15, 0));
if (GUILayout.Button("Rotate Y +15")) Apply(_euler + new Vector3(0, 15, 0));
if (GUILayout.Button("Rotate Y +45")) Apply(_euler + new Vector3(0, 45, 0));
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Manual", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
_euler = EditorGUILayout.Vector3Field("Local Euler", _euler);
_uniformScale = EditorGUILayout.Slider("Uniform Scale", _uniformScale, 0.05f, 5f);
_localPos = EditorGUILayout.Vector3Field("Local Position", _localPos);
if (EditorGUI.EndChangeCheck()) ApplyToScene();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Pivot Helper (基于 MeshRenderer.bounds 自动校正)", EditorStyles.boldLabel);
if (GUILayout.Button("Auto Center Pivot (基于 bounds 把模型对齐到原点)"))
{
AutoCenterPivot();
}
EditorGUILayout.Space();
using (new EditorGUI.DisabledScope(_view == null))
{
if (GUILayout.Button("Apply to Prefab (持久化)", GUILayout.Height(36)))
{
ApplyToPrefab();
}
}
}
private void Apply(Vector3 newEuler)
{
_euler = NormalizeEuler(newEuler);
ApplyToScene();
}
private static Vector3 NormalizeEuler(Vector3 e)
{
return new Vector3(
Mathf.Repeat(e.x + 180f, 360f) - 180f,
Mathf.Repeat(e.y + 180f, 360f) - 180f,
Mathf.Repeat(e.z + 180f, 360f) - 180f
);
}
private void ApplyToScene()
{
if (_view == null) return;
Undo.RecordObject(_view, "Calibrate View");
_view.localEulerAngles = _euler;
_view.localScale = Vector3.one * _uniformScale;
_view.localPosition = _localPos;
EditorUtility.SetDirty(_view);
SceneView.RepaintAll();
}
private void AutoCenterPivot()
{
if (_view == null) return;
var mr = _view.GetComponentInChildren<MeshRenderer>();
if (mr == null) { Debug.LogWarning("No MeshRenderer under View"); return; }
// bounds.center in world → 转 view local → 反向偏移
var centerWorld = mr.bounds.center;
var centerLocalInView = _view.InverseTransformPoint(centerWorld);
// 想让模型中心对齐到 View 的原点localPos -= local-space offset (scaled)
// 简化:直接把 mesh.localBounds.center 当偏移 (避免世界缩放影响)
var mf = _view.GetComponentInChildren<MeshFilter>();
if (mf != null && mf.sharedMesh != null)
{
var meshCenter = mf.sharedMesh.bounds.center; // model-space
_localPos = -Vector3.Scale(meshCenter, Vector3.one * _uniformScale);
ApplyToScene();
Debug.Log($"[Calibrator] Auto-center: model bounds.center={meshCenter}, set View.localPos={_localPos}");
}
}
private void ApplyToPrefab()
{
if (_target == null) return;
ApplyToScene();
// Try outermost prefab instance and apply overrides
var inst = PrefabUtility.GetNearestPrefabInstanceRoot(_target);
if (inst == null)
{
EditorUtility.DisplayDialog("Calibrator", "Target 不是 prefab 实例", "OK");
return;
}
// 1) Apply property modifications (rotation/scale/position) of the View transform back to its corresponding prefab asset
var path = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(inst);
if (string.IsNullOrEmpty(path))
{
EditorUtility.DisplayDialog("Calibrator", "找不到 prefab 资产路径", "OK");
return;
}
// Apply overrides on the entire instance
try
{
PrefabUtility.ApplyPrefabInstance(inst, InteractionMode.AutomatedAction);
AssetDatabase.SaveAssets();
Debug.Log($"[Calibrator] Applied overrides to prefab: {path}");
EditorUtility.DisplayDialog("Calibrator", $"已 Apply 到 prefab:\n{path}", "OK");
}
catch (System.Exception e)
{
Debug.LogException(e);
EditorUtility.DisplayDialog("Calibrator", $"Apply 失败: {e.Message}", "OK");
}
}
}
}