UserPropertiesLoadingSample.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #pragma warning disable 649
  2. using System.Collections.Generic;
  3. using TriLibCore.Extensions;
  4. using UnityEngine;
  5. using UnityEngine.UI;
  6. #if UNITY_EDITOR
  7. using UnityEditor;
  8. #endif
  9. namespace TriLibCore.Samples
  10. {
  11. /// <summary>
  12. /// Represents a sample that allows users to load a Model using the built-in File-Picker and displays the Model User Properties.
  13. /// </summary>
  14. public class UserPropertiesLoadingSample : MonoBehaviour
  15. {
  16. /// <summary>
  17. /// The Dictionaries containing the Model Properties sorted by type.
  18. /// </summary>
  19. private Dictionary<string, float> _floatValues;
  20. private Dictionary<string, int> _intValues;
  21. private Dictionary<string, Vector2> _vector2Values;
  22. private Dictionary<string, Vector3> _vector3Values;
  23. private Dictionary<string, Vector4> _vector4Values;
  24. private Dictionary<string, Color> _colorValues;
  25. private Dictionary<string, bool> _boolValues;
  26. private Dictionary<string, string> _stringValues;
  27. /// <summary>
  28. /// The last loaded GameObject.
  29. /// </summary>
  30. private GameObject _loadedGameObject;
  31. /// <summary>
  32. /// The load Model Button.
  33. /// </summary>
  34. [SerializeField]
  35. private Button _loadModelButton;
  36. /// <summary>
  37. /// The progress indicator Text;
  38. /// </summary>
  39. [SerializeField]
  40. private Text _progressText;
  41. /// <summary>
  42. /// The properties listing Text;
  43. /// </summary>
  44. [SerializeField]
  45. private Text _propertiesText;
  46. /// <summary>
  47. /// Cached Asset Loader Options instance.
  48. /// </summary>
  49. private AssetLoaderOptions _assetLoaderOptions;
  50. /// <summary>
  51. /// Returns the path to the "TriLibSample.obj" Model.
  52. /// </summary>
  53. private string ModelPath
  54. {
  55. get
  56. {
  57. #if UNITY_EDITOR
  58. return $"{Application.dataPath}/TriLib/TriLibSamples/UserPropertiesLoading/Models/WithMyData.fbx";
  59. #else
  60. return "Models/WithMyData.fbx";
  61. #endif
  62. }
  63. }
  64. /// <summary>
  65. /// The callback passed to our custom UserPropertiesMapper, called for every Model and its every User Property.
  66. /// </summary>
  67. /// <param name="gameObject">The GameObject containing the User Property.</param>
  68. /// <param name="propertyName">The User Property name.</param>
  69. /// <param name="propertyValue">The User Property value.</param>
  70. private void OnUserDataProcessed(GameObject gameObject, string propertyName, object propertyValue)
  71. {
  72. var propertyKey = $"{gameObject.name}.{propertyName}";
  73. switch (propertyValue)
  74. {
  75. case float floatValue:
  76. if (!_floatValues.ContainsKey(propertyKey))
  77. {
  78. _floatValues.Add(propertyKey, floatValue);
  79. }
  80. break;
  81. case int intValue:
  82. if (!_intValues.ContainsKey(propertyKey))
  83. {
  84. _intValues.Add(propertyKey, intValue);
  85. }
  86. break;
  87. case Vector2 vector2Value:
  88. if (!_vector2Values.ContainsKey(propertyKey))
  89. {
  90. _vector2Values.Add(propertyKey, vector2Value);
  91. }
  92. break;
  93. case Vector3 vector3Value:
  94. if (!_vector3Values.ContainsKey(propertyKey))
  95. {
  96. _vector3Values.Add(propertyKey, vector3Value);
  97. }
  98. break;
  99. case Vector4 vector4Value:
  100. if (!_vector4Values.ContainsKey(propertyKey))
  101. {
  102. _vector4Values.Add(propertyKey, vector4Value);
  103. }
  104. break;
  105. case Color colorValue:
  106. if (!_colorValues.ContainsKey(propertyKey))
  107. {
  108. _colorValues.Add(propertyKey, colorValue);
  109. }
  110. break;
  111. case bool boolValue:
  112. if (!_boolValues.ContainsKey(propertyKey))
  113. {
  114. _boolValues.Add(propertyKey, boolValue);
  115. }
  116. break;
  117. case string stringValue:
  118. if (!_stringValues.ContainsKey(propertyKey))
  119. {
  120. _stringValues.Add(propertyKey, stringValue);
  121. }
  122. break;
  123. }
  124. }
  125. /// <summary>
  126. /// Creates the AssetLoaderOptions instance and displays the Model file-picker.
  127. /// It also creates our custom UserPropertiesMapper (SampleUserPropertiesMapper) instance and passes a callback function to it.
  128. /// </summary>
  129. /// <remarks>
  130. /// You can create the AssetLoaderOptions by right clicking on the Assets Explorer and selecting "TriLib->Create->AssetLoaderOptions->Pre-Built AssetLoaderOptions".
  131. /// </remarks>
  132. public void LoadModel()
  133. {
  134. var assetLoaderOptions = CreateAssetLoaderOptions();
  135. var assetLoaderFilePicker = AssetLoaderFilePicker.Create();
  136. assetLoaderFilePicker.LoadModelFromFilePickerAsync("Select a Model file", OnLoad, OnMaterialsLoad, OnProgress, OnBeginLoad, OnError, null, assetLoaderOptions);
  137. }
  138. /// <summary>
  139. /// Creates an AssetLoaderOptions with the sample UserPropertiesMapper.
  140. /// </summary>
  141. /// <returns>The created AssetLoaderOptions.</returns>
  142. private AssetLoaderOptions CreateAssetLoaderOptions()
  143. {
  144. if (_assetLoaderOptions == null)
  145. {
  146. _assetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions(false, true);
  147. var userPropertiesMapper = ScriptableObject.CreateInstance<SampleUserPropertiesMapper>();
  148. userPropertiesMapper.OnUserDataProcessed += OnUserDataProcessed;
  149. _assetLoaderOptions.UserPropertiesMapper = userPropertiesMapper;
  150. }
  151. return _assetLoaderOptions;
  152. }
  153. /// <summary>
  154. /// Called when the the Model begins to load, configuring the scene.
  155. /// </summary>
  156. /// <param name="filesSelected">Indicates if any file has been selected.</param>
  157. private void OnBeginLoad(bool filesSelected)
  158. {
  159. _floatValues = new Dictionary<string, float>();
  160. _intValues = new Dictionary<string, int>();
  161. _vector2Values = new Dictionary<string, Vector2>();
  162. _vector3Values = new Dictionary<string, Vector3>();
  163. _vector4Values = new Dictionary<string, Vector4>();
  164. _colorValues = new Dictionary<string, Color>();
  165. _boolValues = new Dictionary<string, bool>();
  166. _stringValues = new Dictionary<string, string>();
  167. _loadModelButton.interactable = !filesSelected;
  168. _progressText.enabled = filesSelected;
  169. _progressText.text = string.Empty;
  170. }
  171. /// <summary>
  172. /// Called when any error occurs.
  173. /// </summary>
  174. /// <param name="obj">The contextualized error, containing the original exception and the context passed to the method where the error was thrown.</param>
  175. private void OnError(IContextualizedError obj)
  176. {
  177. Debug.LogError($"An error occurred while loading your Model: {obj.GetInnerException()}");
  178. }
  179. /// <summary>
  180. /// Called when the Model loading progress changes.
  181. /// </summary>
  182. /// <param name="assetLoaderContext">The context used to load the Model.</param>
  183. /// <param name="progress">The loading progress.</param>
  184. private void OnProgress(AssetLoaderContext assetLoaderContext, float progress)
  185. {
  186. _progressText.text = $"Progress: {progress:P}";
  187. }
  188. /// <summary>
  189. /// Called when the Model (including Textures and Materials) has been fully loaded.
  190. /// </summary>
  191. /// <remarks>The loaded GameObject is available on the assetLoaderContext.RootGameObject field.</remarks>
  192. /// <param name="assetLoaderContext">The context used to load the Model.</param>
  193. private void OnMaterialsLoad(AssetLoaderContext assetLoaderContext)
  194. {
  195. if (assetLoaderContext.RootGameObject != null)
  196. {
  197. Debug.Log("Model fully loaded.");
  198. ListProperties();
  199. }
  200. else
  201. {
  202. Debug.Log("Model could not be loaded.");
  203. }
  204. _loadModelButton.interactable = true;
  205. _progressText.enabled = false;
  206. }
  207. /// <summary>
  208. /// Updates the User Properties Text content with the loaded Model User Properties, categorizing the Properties by type.
  209. /// </summary>
  210. private void ListProperties()
  211. {
  212. var text = string.Empty;
  213. if (_stringValues.Count > 0)
  214. {
  215. text += "<b>String</b>\n";
  216. foreach (var kvp in _stringValues)
  217. {
  218. text += $"{kvp.Key}=\"{kvp.Value}\"\n";
  219. }
  220. }
  221. if (_floatValues.Count > 0)
  222. {
  223. text += "\n<b>Float</b>\n";
  224. foreach (var kvp in _floatValues)
  225. {
  226. text += $"{kvp.Key}={kvp.Value}\n";
  227. }
  228. }
  229. if (_intValues.Count > 0)
  230. {
  231. text += "\n<b>Integer</b>\n";
  232. foreach (var kvp in _intValues)
  233. {
  234. text += $"{kvp.Key}={kvp.Value}\n";
  235. }
  236. }
  237. if (_boolValues.Count > 0)
  238. {
  239. text += "\n<b>Boolean</b>\n";
  240. foreach (var kvp in _boolValues)
  241. {
  242. text += $"{kvp.Key}={kvp.Value}\n";
  243. }
  244. }
  245. if (_vector2Values.Count > 0)
  246. {
  247. text += "\n<b>Vector2</b>\n";
  248. foreach (var kvp in _vector2Values)
  249. {
  250. text += $"{kvp.Key}={kvp.Value}\n";
  251. }
  252. }
  253. if (_vector3Values.Count > 0)
  254. {
  255. text += "\n<b>Vector3</b>\n";
  256. foreach (var kvp in _vector3Values)
  257. {
  258. text += $"{kvp.Key}={kvp.Value}\n";
  259. }
  260. }
  261. if (_vector4Values.Count > 0)
  262. {
  263. text += "\n<b>Vector4</b>\n";
  264. foreach (var kvp in _vector4Values)
  265. {
  266. text += $"{kvp.Key}={kvp.Value}\n";
  267. }
  268. }
  269. if (_colorValues.Count > 0)
  270. {
  271. text += "\n<b>Color</b>\n";
  272. foreach (var kvp in _colorValues)
  273. {
  274. text += "<color=#" + ColorUtility.ToHtmlStringRGB(kvp.Value) + ">";
  275. text += $"{kvp.Key}={kvp.Value}\n";
  276. text += "</color>";
  277. }
  278. }
  279. _propertiesText.text = string.IsNullOrEmpty(text) ? "The model has no user properties" : $"<b>Model User Properties</b>\n\n{text}";
  280. }
  281. /// <summary>
  282. /// Called when the Model Meshes and hierarchy are loaded.
  283. /// </summary>
  284. /// <remarks>The loaded GameObject is available on the assetLoaderContext.RootGameObject field.</remarks>
  285. /// <param name="assetLoaderContext">The context used to load the Model.</param>
  286. private void OnLoad(AssetLoaderContext assetLoaderContext)
  287. {
  288. if (_loadedGameObject != null)
  289. {
  290. Destroy(_loadedGameObject);
  291. }
  292. _loadedGameObject = assetLoaderContext.RootGameObject;
  293. if (_loadedGameObject != null)
  294. {
  295. Camera.main.FitToBounds(assetLoaderContext.RootGameObject, 4f);
  296. }
  297. }
  298. /// <summary>
  299. /// Loads the sample Model.
  300. /// </summary>
  301. private void Start()
  302. {
  303. var assetLoaderOptions = CreateAssetLoaderOptions();
  304. OnBeginLoad(true); //Workaround to create lists
  305. AssetLoader.LoadModelFromFile(ModelPath, OnLoad, OnMaterialsLoad, OnProgress, OnError, null, assetLoaderOptions);
  306. }
  307. }
  308. }