您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
327 行
11 KiB
327 行
11 KiB
using System;
|
|
using System.IO;
|
|
using UniGLTF;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace UniVRM10
|
|
{
|
|
[CustomEditor(typeof(VRM10Expression))]
|
|
public class ExpressionEditor : Editor
|
|
{
|
|
/// <summary>
|
|
/// Preview(Inspectorの下方)を描画するクラス
|
|
///
|
|
/// * PreviewRenderUtility.m_cameraのUnityVersionによる切り分け
|
|
///
|
|
/// </summary>
|
|
PreviewFaceRenderer m_renderer;
|
|
|
|
/// <summary>
|
|
/// Previewを描画するのにシーンが必用である。
|
|
/// m_target.Prefabをインスタンス化したシーンを管理する。
|
|
///
|
|
/// * ExpressionのBake
|
|
/// * MaterialMorphの適用
|
|
/// * Previewカメラのコントロール
|
|
/// * Previewライティングのコントロール
|
|
///
|
|
/// </summary>
|
|
PreviewSceneManager m_scene;
|
|
|
|
/// <summary>
|
|
/// Preview シーンに Expression を適用する
|
|
/// </summary>
|
|
void Bake()
|
|
{
|
|
if (m_scene != null)
|
|
{
|
|
//Debug.Log("Bake");
|
|
m_scene.Bake(CurrentExpression(), 1.0f);
|
|
}
|
|
}
|
|
|
|
void ClearScene()
|
|
{
|
|
if (m_scene != null)
|
|
{
|
|
//Debug.LogFormat("OnDestroy");
|
|
m_scene.Clean();
|
|
GameObject.DestroyImmediate(m_scene.gameObject);
|
|
m_scene = null;
|
|
}
|
|
}
|
|
|
|
void PrefabGUI()
|
|
{
|
|
var prefab = (GameObject)EditorGUILayout.ObjectField("Preview Prefab", m_target.Prefab, typeof(GameObject), false);
|
|
if (prefab == m_target.Prefab)
|
|
{
|
|
return;
|
|
}
|
|
ClearScene();
|
|
m_target.Prefab = prefab;
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
m_target = (VRM10Expression)target;
|
|
m_renderer = new PreviewFaceRenderer();
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
if (m_renderer != null)
|
|
{
|
|
m_renderer.Dispose();
|
|
m_renderer = null;
|
|
}
|
|
ClearScene();
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
// 2018/2019 で OnDisable/OnDestroy の呼ばれ方が違う?
|
|
ClearScene();
|
|
}
|
|
|
|
static void Separator()
|
|
{
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.BeginHorizontal();
|
|
//GUILayout.Space();
|
|
GUILayout.Box("", GUILayout.ExpandWidth(true), GUILayout.Height(1));
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
|
}
|
|
|
|
private static int sliderHash = "Slider".GetHashCode();
|
|
float m_yaw = 180.0f;
|
|
float m_pitch;
|
|
Vector3 m_position = new Vector3(0, 0, -0.8f);
|
|
|
|
// very important to override this, it tells Unity to render an ObjectPreview at the bottom of the inspector
|
|
public override bool HasPreviewGUI() { return true; }
|
|
|
|
public RenderTexture PreviewTexture;
|
|
|
|
// the main ObjectPreview function... it's called constantly, like other IMGUI On*GUI() functions
|
|
public override void OnPreviewGUI(Rect r, GUIStyle background)
|
|
{
|
|
// if this is happening, you have bigger problems
|
|
if (!ShaderUtil.hardwareSupportsRectRenderTexture)
|
|
{
|
|
if (Event.current.type == EventType.Repaint)
|
|
{
|
|
EditorGUI.DropShadowLabel(new Rect(r.x, r.y, r.width, 40f),
|
|
"Mesh preview requires\nrender texture support");
|
|
}
|
|
return;
|
|
}
|
|
|
|
var src = r;
|
|
|
|
var min = Mathf.Min(r.width, r.height);
|
|
r.width = min;
|
|
r.height = min;
|
|
r.x = src.x + (src.width - min) / 2;
|
|
r.y = src.y + (src.height - min) / 2;
|
|
|
|
//previewDir = Drag2D(previewDir, r);
|
|
{
|
|
int controlId = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
|
|
Event e = Event.current;
|
|
switch (e.GetTypeForControl(controlId))
|
|
{
|
|
case EventType.MouseDown:
|
|
if (r.Contains(e.mousePosition) && (double)r.width > 50.0)
|
|
{
|
|
GUIUtility.hotControl = controlId;
|
|
e.Use();
|
|
EditorGUIUtility.SetWantsMouseJumping(1);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case EventType.MouseUp:
|
|
if (GUIUtility.hotControl == controlId)
|
|
GUIUtility.hotControl = 0;
|
|
EditorGUIUtility.SetWantsMouseJumping(0);
|
|
break;
|
|
|
|
case EventType.MouseDrag:
|
|
if (GUIUtility.hotControl == controlId)
|
|
{
|
|
if (e.button == 2)
|
|
{
|
|
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height);
|
|
m_position.x -= shift.x;
|
|
m_position.y += shift.y;
|
|
e.Use();
|
|
GUI.changed = true;
|
|
}
|
|
else if (
|
|
e.button == 0 ||
|
|
e.button == 1)
|
|
{
|
|
var shift = e.delta * (!e.shift ? 1f : 3f) / Mathf.Min(r.width, r.height) * 140f;
|
|
m_yaw += shift.x;
|
|
m_pitch += shift.y;
|
|
m_pitch = Mathf.Clamp(m_pitch, -90f, 90f);
|
|
e.Use();
|
|
GUI.changed = true;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case EventType.ScrollWheel:
|
|
//Debug.LogFormat("wheel: {0}", current.delta);
|
|
if (r.Contains(e.mousePosition))
|
|
{
|
|
if (e.delta.y > 0)
|
|
{
|
|
m_position.z *= 1.1f;
|
|
Repaint();
|
|
}
|
|
else if (e.delta.y < 0)
|
|
{
|
|
m_position.z *= 0.9f;
|
|
Repaint();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
//return scrollPosition;
|
|
}
|
|
//Debug.LogFormat("{0}", previewDir);
|
|
|
|
if (Event.current.type != EventType.Repaint)
|
|
{
|
|
// if we don't need to update yet, then don't
|
|
return;
|
|
}
|
|
|
|
if (m_renderer != null && m_scene != null)
|
|
{
|
|
PreviewTexture = m_renderer.Render(r, background, m_scene, m_yaw, m_pitch, m_position) as RenderTexture;
|
|
if (PreviewTexture != null)
|
|
{
|
|
// draw the RenderTexture in the ObjectPreview pane
|
|
GUI.DrawTexture(r, PreviewTexture, ScaleMode.StretchToFill, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
SerializedExpressionEditor m_serializedEditor;
|
|
|
|
VRM10Expression m_target;
|
|
VRM10Expression CurrentExpression()
|
|
{
|
|
return m_target;
|
|
}
|
|
|
|
float m_previewSlider = 1.0f;
|
|
|
|
static Texture2D SaveResizedImage(RenderTexture rt, UnityPath path, int size)
|
|
{
|
|
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false);
|
|
RenderTexture.active = rt;
|
|
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
|
|
tex.Apply();
|
|
|
|
//TextureScale.Scale(tex, size, size);
|
|
tex = TextureScale.GetResized(tex, size, size);
|
|
|
|
byte[] bytes;
|
|
switch (path.Extension.ToLower())
|
|
{
|
|
case ".png":
|
|
bytes = tex.EncodeToPNG();
|
|
break;
|
|
|
|
case ".jpg":
|
|
bytes = tex.EncodeToJPG();
|
|
break;
|
|
|
|
default:
|
|
throw new Exception();
|
|
}
|
|
|
|
if (Application.isPlaying)
|
|
{
|
|
UnityEngine.Object.Destroy(tex);
|
|
}
|
|
else
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(tex);
|
|
}
|
|
File.WriteAllBytes(path.FullPath, bytes);
|
|
|
|
path.ImportAsset();
|
|
return path.LoadAsset<Texture2D>();
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
var changed = false;
|
|
serializedObject.Update();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.BeginVertical();
|
|
PrefabGUI();
|
|
EditorGUILayout.LabelField("Preview Weight");
|
|
var previewSlider = EditorGUILayout.Slider(m_previewSlider, 0, 1.0f);
|
|
if (m_target.IsBinary)
|
|
{
|
|
previewSlider = Mathf.Round(previewSlider);
|
|
}
|
|
if (previewSlider != m_previewSlider)
|
|
{
|
|
m_previewSlider = previewSlider;
|
|
changed = true;
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
EditorGUILayout.EndHorizontal();
|
|
Separator();
|
|
|
|
if (m_scene == null)
|
|
{
|
|
if (m_target.Prefab != null)
|
|
{
|
|
m_scene = UniVRM10.PreviewSceneManager.GetOrCreate(m_target.Prefab);
|
|
if (m_scene != null)
|
|
{
|
|
m_scene.gameObject.SetActive(false);
|
|
}
|
|
Bake();
|
|
}
|
|
}
|
|
|
|
if (m_scene != null)
|
|
{
|
|
if (m_serializedEditor == null)
|
|
{
|
|
m_serializedEditor = new SerializedExpressionEditor(serializedObject, m_scene);
|
|
}
|
|
if (m_serializedEditor.Draw(out VRM10Expression bakeValue))
|
|
{
|
|
changed = true;
|
|
}
|
|
if (changed)
|
|
{
|
|
m_scene.Bake(bakeValue, m_previewSlider);
|
|
}
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
public override string GetInfoString()
|
|
{
|
|
if (m_scene == null) return "";
|
|
return m_scene.hasError ? "An error occurred while previewing. Check the console log for details." : "";
|
|
}
|
|
}
|
|
}
|