using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using System.Reflection; using System; #if UNITY_EDITOR using UnityEditor; #endif using UniGLTF; namespace VRM { /// /// プレビュー向けのシーンを管理する /// public class PreviewSceneManager : MonoBehaviour { public GameObject Prefab; #if UNITY_EDITOR public static PreviewSceneManager GetOrCreate(GameObject prefab) { if (prefab == null) { return null; } PreviewSceneManager manager = null; // if we already instantiated a PreviewInstance previously but just lost the reference, then use that same instance instead of making a new one var managers = GameObject.FindObjectsOfType(); foreach (var x in managers) { if (x.Prefab == prefab) { Debug.LogFormat("find {0}", manager); return manager; } Debug.LogFormat("destroy {0}", x); GameObject.DestroyImmediate(x.gameObject); } // no previous instance detected, so now let's make a fresh one // very important: this loads the PreviewInstance prefab and temporarily instantiates it into PreviewInstance var go = GameObject.Instantiate(prefab, prefab.transform.position, prefab.transform.rotation ); go.name = "__PREVIEW_SCENE_MANGER__"; manager = go.AddComponent(); manager.Initialize(prefab); // HideFlags are special editor-only settings that let you have *secret* GameObjects in a scene, or to tell Unity not to save that temporary GameObject as part of the scene go.hideFlags |= HideFlags.DontSaveInEditor; go.hideFlags |= HideFlags.DontSaveInBuild; #if VRM_DEVELOP #else go.hideFlags |= HideFlags.HideAndDontSave; ; // you could also hide it from the hierarchy or inspector, but personally I like knowing everything that's there #endif Debug.LogFormat("Create {0}", manager); return manager; } #endif private void Initialize(GameObject prefab) { Prefab = prefab; var flags = BindingFlags.Static | BindingFlags.NonPublic; var propInfo = typeof(Camera).GetProperty("PreviewCullingLayer", flags); PreviewLayer = (int)propInfo.GetValue(null, new object[0]); foreach (var x in transform.Traverse()) { x.gameObject.layer = PreviewLayer; } m_meshes = transform.Traverse() .Select(x => MeshPreviewItem.Create(x, transform)) .Where(x => x != null) .ToArray() ; foreach(var x in m_meshes) { if (x.SkinnedMeshRenderer != null) { var mesh = x.SkinnedMeshRenderer.sharedMesh; m_blendShapeNameMap.Add(m_blendShapeNameMap.Count, Enumerable.Range(0, mesh.blendShapeCount).Select(y => mesh.GetBlendShapeName(y)).ToArray()); } } Bake(); m_rendererPathList = m_meshes.Select(x => x.Path).ToArray(); m_skinnedMeshRendererPathList = m_meshes.Where(x => x.SkinnedMeshRenderer != null).Select(x => x.Path).ToArray(); } public class MeshPreviewItem { public string Path { get; private set; } public SkinnedMeshRenderer SkinnedMeshRenderer { get; private set; } public Mesh Mesh { get; private set; } public int BlendShapeCount { get; private set; } public Material[] Materials { get; private set; } Transform m_transform; public Vector3 Position { get { return m_transform.position; } } public Quaternion Rotation { get { return m_transform.rotation; } } MeshPreviewItem(string path, Transform transform) { Path = path; m_transform = transform; } public void Bake() { if (SkinnedMeshRenderer == null) return; SkinnedMeshRenderer.BakeMesh(Mesh); } public static MeshPreviewItem Create(Transform t, Transform root) { var meshFilter = t.GetComponent(); var meshRenderer = t.GetComponent(); var skinnedMeshRenderer = t.GetComponent(); if (meshFilter != null && meshRenderer != null) { return new MeshPreviewItem(t.RelativePathFrom(root), t) { Mesh = meshFilter.sharedMesh, Materials = meshRenderer.sharedMaterials }; } else if (skinnedMeshRenderer != null) { return new MeshPreviewItem(t.RelativePathFrom(root), t) { SkinnedMeshRenderer = skinnedMeshRenderer, Mesh = new Mesh(), // for bake BlendShapeCount = skinnedMeshRenderer.sharedMesh.blendShapeCount, Materials = skinnedMeshRenderer.sharedMaterials }; } else { return null; } } } MeshPreviewItem[] m_meshes; public IEnumerable EnumRenderItems { get { if (m_meshes != null) { foreach (var x in m_meshes) { yield return x; } } } } string[] m_rendererPathList; public string[] RendererPathList { get { return m_rendererPathList; } } string[] m_skinnedMeshRendererPathList; public string[] SkinnedMeshRendererPathList { get { return m_skinnedMeshRendererPathList; } } Dictionary m_blendShapeNameMap = new Dictionary(); public string[] GetBlendShapeNames(int skinnedMeshIndex) { string[] names = null; if(m_blendShapeNameMap.TryGetValue(skinnedMeshIndex, out names)) { return names; } return null; } Bounds m_bounds; public void Bake() { Debug.LogFormat("Bake"); m_bounds = default(Bounds); foreach (var x in m_meshes) { x.Bake(); m_bounds.Expand(x.Mesh.bounds.min); m_bounds.Expand(x.Mesh.bounds.max); } } int PreviewLayer { get; set; } /// /// カメラパラメーターを決める /// /// public void SetupCamera(Camera camera) { float magnitude = m_bounds.extents.magnitude; float distance = 4f * magnitude; camera.fieldOfView = 27f; camera.backgroundColor = Color.gray; camera.clearFlags = CameraClearFlags.Color; // this used to be "-Vector3.forward * num" but I hardcoded my camera position instead camera.transform.position = new Vector3(0f, 1.4f, 1.5f); camera.transform.rotation = Quaternion.Euler(0, 180f, 0); camera.nearClipPlane = 0.3f; camera.farClipPlane = distance + magnitude * 1.1f; //previewLayer のみ表示する camera.cullingMask = 1 << PreviewLayer; } } }