#pragma warning disable 0414, 0649 using System; using UniGLTF; using UnityEngine; namespace VRM { public enum UpdateType { None, Update, LateUpdate, } /// /// Headボーンローカルで目標物のYaw, Pitchを求めて目線に適用する /// /// * VRMLookAtBoneApplyer /// * VRMLookAtBlendShapeApplyer /// /// public class VRMLookAtHead : MonoBehaviour, IVRMComponent { public bool DrawGizmo = true; [SerializeField] public UpdateType UpdateType = UpdateType.Update; [SerializeField] public Transform Target; [SerializeField] public Transform Head; #region Thumbnail public Texture2D CreateThumbnail() { var texture = new Texture2D(2048, 2048); { var go = new GameObject("ThumbCamera"); var camera = go.AddComponent(); CreateThumbnail(camera, texture); if (Application.isPlaying) { GameObject.Destroy(go); } else { GameObject.DestroyImmediate(go); } } return texture; } void CreateThumbnail(Camera camera, Texture2D dst) { RenderTexture currentRT = RenderTexture.active; { var renderTexture = new RenderTexture(dst.width, dst.height, 24); camera.targetTexture = renderTexture; RenderTexture.active = renderTexture; LookFace(camera.transform); camera.Render(); dst.ReadPixels(new Rect(0, 0, dst.width, dst.height), 0, 0); RenderTexture.active = currentRT; camera.targetTexture = null; if (Application.isPlaying) { UnityEngine.Object.Destroy(renderTexture); } else { UnityEngine.Object.DestroyImmediate(renderTexture); } } } public void LookFace(Transform t) { if (Head == null) return; var headPosition = Head.position + new Vector3(0, 0.05f, 0); t.position = headPosition + Head.localToWorldMatrix.ExtractRotation() * new Vector3(0, 0, 0.7f); t.LookAt(headPosition); } #endregion void Awake() { var animator = GetComponent(); if (animator == null) { Debug.LogWarning("animator is not found"); return; } var head = animator.GetBoneTransform(HumanBodyBones.Head); if (head == null) { Debug.LogWarning("head is not found"); return; } Head = head; } public void OnImported(VRMImporterContext context) { var gltfFirstPerson = context.VRM.firstPerson; switch (gltfFirstPerson.lookAtType) { case LookAtType.Bone: { var applyer = gameObject.AddComponent(); applyer.OnImported(context); } break; case LookAtType.BlendShape: { var applyer = gameObject.AddComponent(); applyer.OnImported(context); } break; } } static Matrix4x4 LookAtMatrixFromWorld(Vector3 from, Vector3 target) { var pos = new Vector4(from.x, from.y, from.z, 1); return LookAtMatrix(UnityExtensions.Matrix4x4FromColumns(Vector3.right, Vector3.up, Vector3.forward, pos), target); } static Matrix4x4 LookAtMatrix(Vector3 up_vector, Vector3 localPosition) { var z_axis = localPosition.normalized; var x_axis = Vector3.Cross(up_vector, z_axis).normalized; var y_axis = Vector3.Cross(z_axis, x_axis).normalized; return UnityExtensions.Matrix4x4FromColumns(x_axis, y_axis, z_axis, new Vector4(0, 0, 0, 1)); } static Matrix4x4 LookAtMatrix(Matrix4x4 m, Vector3 target) { return LookAtMatrix(Vector3.up, m.inverse.MultiplyPoint(target)); } public Matrix4x4 YawMatrix { get { var yaw = Quaternion.AngleAxis(m_yaw, Vector3.up); var m = default(Matrix4x4); m.SetTRS(Vector3.zero, yaw, Vector3.one); return m; } } [SerializeField, Header("Debug")] float m_yaw; public float Yaw { get { return m_yaw; } } [SerializeField] float m_pitch; public float Pitch { get { return m_pitch; } } public event Action YawPitchChanged; public void RaiseYawPitchChanged(float yaw, float pitch) { m_yaw = yaw; m_pitch = pitch; var handle = YawPitchChanged; if (handle != null) { handle(yaw, pitch); } } private void Update() { if (Head == null) { enabled = false; return; } if (UpdateType == UpdateType.Update) { LookWorldPosition(); } } private void LateUpdate() { if (Head == null) { enabled = false; return; } if (UpdateType == UpdateType.LateUpdate) { LookWorldPosition(); } } public void LookWorldPosition() { if (Target == null) return; float yaw; float pitch; LookWorldPosition(Target.position, out yaw, out pitch); } public void LookWorldPosition(Vector3 targetPosition, out float yaw, out float pitch) { var localPosition = Head.worldToLocalMatrix.MultiplyPoint(targetPosition); Matrix4x4.identity.CalcYawPitch(localPosition, out yaw, out pitch); RaiseYawPitchChanged(yaw, pitch); } } }