using LibMMD.Motion; using LibMMD.Reader; using LibMMD.Unity3D.Animation; using System; using System.Threading.Tasks; using UnityEngine; namespace LibMMD.Unity3D { public class MmdCameraObject : MonoBehaviour { private static readonly object CameraCacheLock = new object(); private static string _cameraCacheKey; private static CameraMotion _cachedCameraMotion; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetCameraCache() { lock (CameraCacheLock) { _cameraCacheKey = null; _cachedCameraMotion = null; } } private CameraMotion _cameraMotion = null; public bool Playing = false; public bool Loop = false; private double _playTime; private Camera _camera; public static GameObject CreateGameObject(string name = "MMDCameraObject") { var cameraObj = new GameObject("Camera"); var cameraComponent = cameraObj.AddComponent(); cameraComponent.backgroundColor = Color.black; cameraComponent.clearFlags = CameraClearFlags.Color; var obj = new GameObject(name); cameraComponent.transform.SetParent(obj.transform); var mmdCameraObject = obj.AddComponent(); mmdCameraObject._camera = cameraComponent; return obj; } public bool LoadCameraMotion(string path) { if (path == null) { _cameraMotion = null; return false; } return LoadCameraMotion(LoadCameraMotionData(path)); } public static Task LoadCameraMotionAsync(string path) { if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("A camera VMD path is required.", nameof(path)); return Task.Run(() => LoadCameraMotionData(path)); } public bool LoadCameraMotion(CameraMotion motion) { _cameraMotion = motion; if (_cameraMotion == null || _cameraMotion.KeyFrames.Count == 0) return false; ResetMotion(); return true; } private static CameraMotion LoadCameraMotionData(string path) { var key = MmdMotionLoader.BuildKey(path); CameraMotion cameraMotion; lock (CameraCacheLock) { cameraMotion = key != null && key == _cameraCacheKey ? _cachedCameraMotion : null; } if (cameraMotion == null) { var parsed = new VmdReader().ReadCameraMotion(path); var keyAfterRead = MmdMotionLoader.BuildKey(path); cameraMotion = parsed; if (key != null && key == keyAfterRead) { lock (CameraCacheLock) { _cameraCacheKey = key; _cachedCameraMotion = parsed; } } } return cameraMotion; } public double MotionLength => _cameraMotion != null && _cameraMotion.KeyFrames.Count > 0 ? _cameraMotion.KeyFrames[_cameraMotion.KeyFrames.Count - 1].Key / 30.0 : 0.0; public double PlayTime => _playTime; public void ResetMotion() { _playTime = 0.0; Playing = false; Refresh(); } public void SetPlayPos(double pos) { _playTime = pos; Refresh(); } private void Update() { if (!Playing || _cameraMotion == null) { return; } var deltaTime = Time.deltaTime; _playTime += deltaTime; if (Loop && MotionLength > 0.0 && _playTime >= MotionLength) { ResetMotion(); Playing = true; return; } Refresh(); } private void Refresh() { if (_cameraMotion == null) { return; } var cameraPose = _cameraMotion.GetCameraPose(_playTime); if (cameraPose == null) { return; } transform.position = cameraPose.Position; transform.rotation = Quaternion.Euler(- 180 / Mathf.PI * cameraPose.Rotation); _camera.transform.localPosition = new Vector3(0.0f, 0.0f, cameraPose.FocalLength); _camera.fieldOfView = cameraPose.Fov; _camera.orthographic = cameraPose.Orthographic; } } }