SkySetupWindow.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. using System.IO;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using UnityEngine.SceneManagement;
  6. using UnityEditor.SceneManagement;
  7. namespace Funly.SkyStudio
  8. {
  9. // Utility to make it easy to setup a new sky system in the current scene.
  10. public class SkySetupWindow : EditorWindow
  11. {
  12. private class ProfilePreset : IComparer<ProfilePreset>
  13. {
  14. public string guid;
  15. public string assetPath;
  16. public string name;
  17. public string menuName;
  18. public ProfilePreset(string guid, string assetPath, string name, string menuName)
  19. {
  20. this.guid = guid;
  21. this.assetPath = assetPath;
  22. this.name = name;
  23. this.menuName = menuName;
  24. }
  25. public int Compare(ProfilePreset x, ProfilePreset y)
  26. {
  27. return x.assetPath.CompareTo(y.assetPath);
  28. }
  29. }
  30. private ProfilePreset _selectedProfilePreset;
  31. public const string SKY_CONTROLLER_PREFAB = "SkySystemController";
  32. [MenuItem("Window/Sky Studio/Setup Sky")]
  33. public static void ShowWindow()
  34. {
  35. TimelineSelection.Clear();
  36. EditorWindow window = EditorWindow.GetWindow<SkySetupWindow>();
  37. window.Show();
  38. }
  39. private void OnEnable()
  40. {
  41. name = "Setup Sky";
  42. titleContent = new GUIContent("Setup Sky");
  43. }
  44. private void OnGUI()
  45. {
  46. List<ProfilePreset> presets = LoadListOfPresets();
  47. EditorGUILayout.HelpBox("Setup a new sky system in the current scene. " +
  48. "This will create a copy of the preset you select, and load it into your scene.",
  49. MessageType.Info);
  50. EditorGUILayout.Separator();
  51. RenderPresetPopup(presets);
  52. EditorGUILayout.Space();
  53. EditorGUILayout.Space();
  54. if (GUILayout.Button(new GUIContent("Create Sky System"))) {
  55. SetupSceneWithPreset(_selectedProfilePreset);
  56. }
  57. }
  58. private void RenderPresetPopup(List<ProfilePreset> presets)
  59. {
  60. int selectedIndex = 0;
  61. List<string> displayedPrests = new List<string>();
  62. // Build our list of presets to show in popup.
  63. for (int i = 0; i < presets.Count; i++) {
  64. ProfilePreset preset = presets[i];
  65. displayedPrests.Add(preset.menuName);
  66. // Check if this is the selected preset.
  67. if (_selectedProfilePreset != null && preset.assetPath == _selectedProfilePreset.assetPath) {
  68. selectedIndex = i;
  69. }
  70. }
  71. selectedIndex = EditorGUILayout.Popup("Sky Preset", selectedIndex, displayedPrests.ToArray());
  72. _selectedProfilePreset = presets[selectedIndex];
  73. }
  74. private List<ProfilePreset> LoadListOfPresets()
  75. {
  76. List<ProfilePreset> presets = new List<ProfilePreset>();
  77. string[] guids = AssetDatabase.FindAssets("t:SkyProfile");
  78. if (guids == null || guids.Length == 0) {
  79. return presets;
  80. }
  81. foreach (string guid in guids) {
  82. string presetPath = AssetDatabase.GUIDToAssetPath(guid);
  83. if (presetPath == null) {
  84. Debug.LogError("Failed to get name for profile GUID: " + guid);
  85. continue;
  86. }
  87. string presetName = ObjectNames.NicifyVariableName(Path.GetFileNameWithoutExtension(presetPath));
  88. string menuName = Path.GetDirectoryName(presetPath) + "/" + presetName;
  89. menuName = SkyEditorUtility.WindowsPathToUnixPath(menuName);
  90. string presetDirPrefix = "Assets/" + SkyEditorUtility.PACKAGE_DIR_NAME + "/Internal/Presets/";
  91. if (menuName.StartsWith(presetDirPrefix))
  92. {
  93. menuName = menuName.Remove(0, presetDirPrefix.Length);
  94. }
  95. else
  96. {
  97. menuName = "Your Project/" + menuName;
  98. }
  99. presets.Add(new ProfilePreset(guid, presetPath, presetName, menuName));
  100. }
  101. presets.Sort(delegate(ProfilePreset p1, ProfilePreset p2)
  102. {
  103. return p1.menuName.CompareTo(p2.menuName);
  104. });
  105. return presets;
  106. }
  107. private void SetupSceneWithPreset(ProfilePreset preset)
  108. {
  109. ClearSkyControllers();
  110. Scene currentScene = SceneManager.GetActiveScene();
  111. string sceneDir = Path.GetDirectoryName(currentScene.path);
  112. string profileContainerName = currentScene.name + " - Sky Data";
  113. string profileContainerDir = SkyEditorUtility.GenerateUniqueFolder(sceneDir, profileContainerName, true);
  114. // Create new sky controller.
  115. GameObject skySystemPrefab = SkyEditorUtility.LoadEditorPrefab(SKY_CONTROLLER_PREFAB);
  116. if (skySystemPrefab == null) {
  117. Debug.LogError("Failed to locate sky controller prefab");
  118. return;
  119. }
  120. TimeOfDayController tc = Instantiate(skySystemPrefab).GetComponent<TimeOfDayController>();
  121. tc.name = SKY_CONTROLLER_PREFAB;
  122. // Create a new sky profile.
  123. string profileAssetPath = SkyEditorUtility.GenerateUniqueFilename(profileContainerDir, "SkyProfile", ".asset");
  124. AssetDatabase.CopyAsset(preset.assetPath, profileAssetPath);
  125. // Load the new SKy Profile.
  126. SkyProfile profile = AssetDatabase.LoadAssetAtPath(profileAssetPath, typeof(SkyProfile)) as SkyProfile;
  127. if (profile == null) {
  128. Debug.LogError("Failed to duplicate profile");
  129. return;
  130. }
  131. // Create the skybox material.
  132. Material skyboxMaterial = new Material(GetBestShaderForSkyProfile(profile));
  133. string skyboxPath = SkyEditorUtility.GenerateUniqueFilename(profileContainerDir, "SkyboxMaterial", ".mat");
  134. AssetDatabase.CreateAsset(skyboxMaterial, skyboxPath);
  135. profile.skyboxMaterial = skyboxMaterial;
  136. // Link things together.
  137. tc.skyProfile = profile;
  138. tc.skyProfile.skyboxMaterial = skyboxMaterial;
  139. tc.skyTime = .22f;
  140. // Configure the profile a bit and setup in the current scene.
  141. SkyProfileEditor.ApplyKeywordsToMaterial(tc.skyProfile, skyboxMaterial);
  142. SkyProfileEditor.forceRebuildProfileId = profile.GetInstanceID();
  143. RenderSettings.skybox = skyboxMaterial;
  144. ApplyDefaultSettings(profile);
  145. // Drop a lightning spawn area into the scene in case user enables the feature.
  146. if (!ContainsLightningSpawnArea()) {
  147. CreateLightningSpawnArea();
  148. }
  149. EditorUtility.SetDirty(skyboxMaterial);
  150. EditorUtility.SetDirty(tc.skyProfile);
  151. EditorUtility.SetDirty(tc);
  152. EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
  153. Selection.activeObject = tc.skyProfile;
  154. }
  155. private Shader GetBestShaderForSkyProfile(SkyProfile profile)
  156. {
  157. string shaderName = SkyEditorUtility.GetBestDefaultShaderNameForUnityVersion();
  158. return Shader.Find(shaderName);
  159. }
  160. private string GetActiveSceneDirectory()
  161. {
  162. return Path.GetDirectoryName(SceneManager.GetActiveScene().path);
  163. }
  164. private string GetBestFileName(string baseDir, string fileName, string ext, System.Type fileType)
  165. {
  166. for (int i = 0; i < 100; i++) {
  167. string suffixName = null;
  168. if (i == 0) {
  169. suffixName = fileName + ext;
  170. } else {
  171. suffixName = fileName + "-" + i + ext;
  172. }
  173. string assetPath = baseDir + "/" + suffixName;
  174. if (AssetDatabase.LoadAssetAtPath(assetPath, fileType) == null)
  175. {
  176. return suffixName;
  177. }
  178. }
  179. return null;
  180. }
  181. private void ClearSkyControllers()
  182. {
  183. TimeOfDayController[] skyControllers = GameObject.FindObjectsOfType<TimeOfDayController>();
  184. if (skyControllers != null) {
  185. foreach (TimeOfDayController timeController in skyControllers) {
  186. Debug.Log("Removing old sky controller from scene...");
  187. DestroyImmediate(timeController.gameObject);
  188. }
  189. }
  190. }
  191. private bool ContainsLightningSpawnArea()
  192. {
  193. LightningSpawnArea area = GameObject.FindObjectOfType<LightningSpawnArea>();
  194. return area != null;
  195. }
  196. private void CreateLightningSpawnArea()
  197. {
  198. GameObject area = new GameObject();
  199. area.AddComponent<LightningSpawnArea>();
  200. area.layer = LayerMask.NameToLayer("TransparentFX");
  201. area.name = "Lightning Spawn Area";
  202. if (Camera.main == null) {
  203. Debug.LogWarning("Can't position default spawn area in front of camera since no main camera exists. Tag a camera as Main");
  204. area.transform.position = Vector3.zero;
  205. return;
  206. }
  207. Transform camera = Camera.main.transform;
  208. float distanceBack = 50.0f;
  209. float distanceUp = 10.0f;
  210. Vector3 lightningPos = camera.position + (camera.forward * distanceBack);
  211. lightningPos.y += distanceUp;
  212. area.transform.position = lightningPos;
  213. }
  214. private T GetDefaultArtStyleWithName<T>(string artStyleName) where T : class
  215. {
  216. string[] guids = AssetDatabase.FindAssets(artStyleName);
  217. if (guids.Length == 0) {
  218. return null;
  219. }
  220. string assetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
  221. return AssetDatabase.LoadAssetAtPath(assetPath, typeof(T)) as T;
  222. }
  223. private void ApplyDefaultSettings(SkyProfile profile)
  224. {
  225. // Lightning art.
  226. if (profile.lightningArtSet == null) {
  227. profile.lightningArtSet = GetDefaultArtStyleWithName<LightningArtSet>("DefaultLightningArtSet");
  228. }
  229. // Splash art.
  230. if (profile.rainSplashArtSet == null) {
  231. profile.rainSplashArtSet = GetDefaultArtStyleWithName<RainSplashArtSet>("DefaultRainSplashArtSet");
  232. }
  233. // Rain near texture.
  234. TextureKeyframeGroup group = profile.GetGroup<TextureKeyframeGroup>(ProfilePropertyKeys.RainNearTextureKey);
  235. if (group.keyframes.Count == 1 && group.keyframes[0].texture == null) {
  236. group.keyframes[0].texture = SkyEditorUtility.LoadEditorResourceTexture("RainDownfall-1");
  237. if (group.keyframes[0].texture == null) {
  238. Debug.LogWarning("Failed to locate default near rain texture");
  239. }
  240. }
  241. // Rain far texture.
  242. group = profile.GetGroup<TextureKeyframeGroup>(ProfilePropertyKeys.RainFarTextureKey);
  243. if (group.keyframes.Count == 1 && group.keyframes[0].texture == null) {
  244. group.keyframes[0].texture = SkyEditorUtility.LoadEditorResourceTexture("RainDownfall-3");
  245. if (group.keyframes[0].texture == null) {
  246. Debug.LogWarning("Failed to locate default far rain texture");
  247. }
  248. }
  249. }
  250. }
  251. }