SteamVR_Input_Generator.cs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEditor;
  6. using UnityEngine;
  7. using System.CodeDom;
  8. using Microsoft.CSharp;
  9. using System.IO;
  10. using System.CodeDom.Compiler;
  11. using System.Reflection;
  12. using System.Linq.Expressions;
  13. using UnityEditor.SceneManagement;
  14. using UnityEditor.Callbacks;
  15. using Valve.Newtonsoft.Json;
  16. namespace Valve.VR
  17. {
  18. #pragma warning disable 0219 // variable assigned but not used.
  19. public static class SteamVR_Input_Generator
  20. {
  21. public const string steamVRInputOverwriteBuildKey = "SteamVR_Input_OverwriteBuild";
  22. public const string steamVRInputDeleteUnusedKey = "SteamVR_Input_DeleteUnused";
  23. private const string actionSetClassNamePrefix = "SteamVR_Input_ActionSet_";
  24. public const string generationNeedsReloadKey = "SteamVR_Input_GenerationNeedsReload";
  25. private const string progressBarTitle = "SteamVR Input Generation";
  26. public const string steamVRInputActionSetClassesFolder = "ActionSetClasses";
  27. public const string steamVRInputActionsClass = "SteamVR_Input_Actions";
  28. public const string steamVRInputActionSetsClass = "SteamVR_Input_ActionSets";
  29. public const string steamVRInputInitializationClass = "SteamVR_Input_Initialization";
  30. public const string steamVRActionsAssemblyDefinition = "SteamVR_Actions";
  31. private static bool generating = false;
  32. public static void BeginGeneration()
  33. {
  34. generating = true;
  35. fileChanged = false;
  36. string currentPath = Application.dataPath;
  37. int lastIndex = currentPath.LastIndexOf('/');
  38. currentPath = currentPath.Remove(lastIndex, currentPath.Length - lastIndex);
  39. SteamVR_Input_EditorWindow.SetProgressBarText("Beginning generation...", 0);
  40. GenerationStep_CreateActionSetClasses();
  41. GenerationStep_CreateHelperClasses();
  42. GenerationStep_CreateInitClass();
  43. GenerationStep_CreateAssemblyDefinition();
  44. DeleteUnusedScripts();
  45. if (fileChanged)
  46. EditorPrefs.SetBool(generationNeedsReloadKey, true);
  47. AssetDatabase.Refresh();
  48. SteamVR_Input_EditorWindow.ClearProgressBar();
  49. generating = false;
  50. }
  51. [DidReloadScripts]
  52. private static void OnReload()
  53. {
  54. bool didGenerate = EditorPrefs.GetBool(generationNeedsReloadKey);
  55. if (didGenerate)
  56. {
  57. EditorPrefs.SetBool(generationNeedsReloadKey, false);
  58. if (string.IsNullOrEmpty(EditorSceneManager.GetActiveScene().path) == false)
  59. EditorApplication.delayCall += ReloadScene;
  60. }
  61. }
  62. public static void ReloadScene()
  63. {
  64. EditorPrefs.SetBool(generationNeedsReloadKey, false);
  65. if (string.IsNullOrEmpty(EditorSceneManager.GetActiveScene().path) == false)
  66. {
  67. if (EditorSceneManager.GetActiveScene().isDirty)
  68. {
  69. EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
  70. }
  71. string previousPath = EditorSceneManager.GetActiveScene().path;
  72. EditorSceneManager.NewScene(NewSceneSetup.EmptyScene);
  73. EditorSceneManager.OpenScene(previousPath); //reload open scene to avoid any weird serialization
  74. }
  75. }
  76. public static bool IsGenerating()
  77. {
  78. return generating;
  79. }
  80. public static void CancelGeneration()
  81. {
  82. generating = false;
  83. }
  84. private static List<CodeTypeDeclaration> setClasses = new List<CodeTypeDeclaration>();
  85. private static void GenerationStep_CreateInitClass()
  86. {
  87. CodeCompileUnit compileUnit = new CodeCompileUnit();
  88. CodeTypeDeclaration inputClass = CreatePartialInputClass(compileUnit);
  89. CodeMemberMethod preinitMethod = CreateStaticMethod(inputClass, SteamVR_Input_Generator_Names.preinitializeMethodName, true);
  90. string steamVRInputClassName = typeof(SteamVR_Input).Name;
  91. AddStaticInvokeToMethod(preinitMethod, SteamVR_Input_Generator_Names.actionsClassName, startPreInitActionSetsMethodName);
  92. AddStaticInvokeToMethod(preinitMethod, steamVRInputClassName, initializeActionSetDictionariesMethodName);
  93. AddStaticInvokeToMethod(preinitMethod, SteamVR_Input_Generator_Names.actionsClassName, preInitActionsMethodName);
  94. AddStaticInvokeToMethod(preinitMethod, SteamVR_Input_Generator_Names.actionsClassName, initializeActionsArraysMethodName);
  95. AddStaticInvokeToMethod(preinitMethod, steamVRInputClassName, initializeActionDictionariesMethodName);
  96. AddStaticInvokeToMethod(preinitMethod, steamVRInputClassName, finishPreInitActionSetsMethodName);
  97. // Build the output file name.
  98. string fullSourceFilePath = GetSourceFilePath(steamVRInputInitializationClass);
  99. CreateFile(fullSourceFilePath, compileUnit);
  100. }
  101. private static void GenerationStep_CreateAssemblyDefinition()
  102. {
  103. string fullSourceFilePath = GetSourceFilePath(steamVRActionsAssemblyDefinition, ".asmdef");
  104. if (File.Exists(fullSourceFilePath) == false)
  105. {
  106. SteamVR_Input_Unity_AssemblyFile_Definition actionsAssemblyDefinitionData = new SteamVR_Input_Unity_AssemblyFile_Definition();
  107. actionsAssemblyDefinitionData.autoReferenced = true;
  108. string jsonText = JsonConvert.SerializeObject(actionsAssemblyDefinitionData, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Include });
  109. File.WriteAllText(fullSourceFilePath, jsonText);
  110. }
  111. }
  112. private static void GenerationStep_CreateActionSetClasses()
  113. {
  114. SteamVR_Input_EditorWindow.SetProgressBarText("Generating action set classes...", 0.25f);
  115. SteamVR_Input.InitializeFile();
  116. CreateActionsSubFolder();
  117. setClasses = GenerateActionSetClasses();
  118. Debug.LogFormat("<b>[SteamVR Input]</b> Created input script set classes: {0}", setClasses.Count);
  119. }
  120. private static void GenerationStep_CreateHelperClasses()
  121. {
  122. SteamVR_Input_EditorWindow.SetProgressBarText("Generating actions and actionsets classes...", 0.5f);
  123. GenerateActionHelpers(steamVRInputActionsClass);
  124. GenerateActionSetsHelpers(steamVRInputActionSetsClass);
  125. string actionsFullpath = Path.Combine(GetClassPath(), steamVRInputActionsClass + ".cs");
  126. string actionSetsFullpath = Path.Combine(GetClassPath(), steamVRInputActionSetsClass + ".cs");
  127. Debug.LogFormat("<b>[SteamVR Input]</b> Created input script main classes: {0} and {1}", actionsFullpath, actionSetsFullpath);
  128. }
  129. private static void DeleteUnusedScripts()
  130. {
  131. string folderPath = GetSubFolderPath();
  132. string[] files = Directory.GetFiles(folderPath);
  133. List<string> toDelete = new List<string>();
  134. for (int fileIndex = 0; fileIndex < files.Length; fileIndex++)
  135. {
  136. FileInfo file = new FileInfo(files[fileIndex]);
  137. if (file.Name.EndsWith(".cs") || file.Name.EndsWith(".cs.meta"))
  138. {
  139. bool isSet = false;
  140. if (SteamVR_Input.actionFile.action_sets.Any(set => string.Equals(GetSetClassName(set) + ".cs", file.Name, StringComparison.CurrentCultureIgnoreCase) ||
  141. string.Equals(GetSetClassName(set) + ".cs.meta", file.Name, StringComparison.CurrentCultureIgnoreCase)))
  142. {
  143. isSet = true;
  144. }
  145. bool isAction = false;
  146. if (SteamVR_Input.actionFile.actions.Any(action => string.Equals(action.codeFriendlyName + ".cs", file.Name, StringComparison.CurrentCultureIgnoreCase) ||
  147. string.Equals(action.codeFriendlyName + ".cs.meta", file.Name, StringComparison.CurrentCultureIgnoreCase)))
  148. {
  149. isAction = true;
  150. }
  151. if (isSet == false && isAction == false)
  152. {
  153. toDelete.Add(files[fileIndex]);
  154. }
  155. }
  156. }
  157. if (toDelete.Count > 0)
  158. {
  159. string filesToDelete = "";
  160. foreach (string file in toDelete)
  161. filesToDelete += file + "\n";
  162. bool confirm = EditorUtility.DisplayDialog("SteamVR Input", "Would you like to delete the following unused input files:\n" + filesToDelete, "Delete", "No");
  163. if (confirm)
  164. {
  165. foreach (string fileName in toDelete)
  166. {
  167. FileInfo file = new FileInfo(fileName);
  168. file.IsReadOnly = false;
  169. file.Delete();
  170. }
  171. }
  172. }
  173. }
  174. private static void CreateActionsSubFolder()
  175. {
  176. string folderPath = GetSubFolderPath();
  177. if (Directory.Exists(folderPath) == false)
  178. {
  179. Directory.CreateDirectory(folderPath);
  180. }
  181. }
  182. public static void DeleteActionClassFiles()
  183. {
  184. DeleteActionClass(steamVRInputActionsClass);
  185. DeleteActionClass(steamVRInputActionSetsClass);
  186. string folderPath = GetSubFolderPath();
  187. bool confirm = EditorUtility.DisplayDialog("Confirmation", "Are you absolutely sure you want to delete all code files in " + folderPath + "?", "Delete", "Cancel");
  188. if (confirm)
  189. {
  190. DeleteActionObjects("*.cs*");
  191. }
  192. }
  193. public static void DeleteGeneratedFolder()
  194. {
  195. string generatedFolderPath = GetClassPath();
  196. string subFolderPath = GetSubFolderPath();
  197. bool confirm = EditorUtility.DisplayDialog("Confirmation", "Are you absolutely sure you want to delete all code files in " + generatedFolderPath + "?", "Delete", "Cancel");
  198. if (confirm)
  199. {
  200. DeleteActionObjects("*.cs*", generatedFolderPath);
  201. DeleteActionObjects("*.cs*", subFolderPath);
  202. }
  203. }
  204. public static void DeleteActionObjects(string filter, string folderPath = null)
  205. {
  206. if (folderPath == null)
  207. folderPath = GetSubFolderPath();
  208. string[] assets = Directory.GetFiles(folderPath, filter);
  209. for (int assetIndex = 0; assetIndex < assets.Length; assetIndex++)
  210. {
  211. AssetDatabase.DeleteAsset(assets[assetIndex]);
  212. }
  213. Debug.LogFormat("<b>[SteamVR Input]</b> Deleted {0} files at path: {1}", assets.Length, folderPath);
  214. }
  215. private static void DeleteActionClass(string className)
  216. {
  217. string filePath = GetSourceFilePath(className);
  218. if (File.Exists(filePath) == true)
  219. {
  220. AssetDatabase.DeleteAsset(filePath);
  221. Debug.Log("<b>[SteamVR Input]</b> Deleted: " + filePath);
  222. }
  223. else
  224. {
  225. Debug.Log("<b>[SteamVR Input]</b> No file found at: " + filePath);
  226. }
  227. }
  228. private static string GetTypeStringForAction(SteamVR_Input_ActionFile_Action action)
  229. {
  230. return GetTypeForAction(action).Name;
  231. }
  232. private static Type GetTypeForAction(SteamVR_Input_ActionFile_Action action)
  233. {
  234. string actionType = action.type.ToLower();
  235. if (SteamVR_Input_ActionFile_ActionTypes.boolean == actionType)
  236. {
  237. return typeof(SteamVR_Action_Boolean);
  238. }
  239. if (SteamVR_Input_ActionFile_ActionTypes.vector1 == actionType)
  240. {
  241. return typeof(SteamVR_Action_Single);
  242. }
  243. if (SteamVR_Input_ActionFile_ActionTypes.vector2 == actionType)
  244. {
  245. return typeof(SteamVR_Action_Vector2);
  246. }
  247. if (SteamVR_Input_ActionFile_ActionTypes.vector3 == actionType)
  248. {
  249. return typeof(SteamVR_Action_Vector3);
  250. }
  251. if (SteamVR_Input_ActionFile_ActionTypes.pose == actionType)
  252. {
  253. return typeof(SteamVR_Action_Pose);
  254. }
  255. if (SteamVR_Input_ActionFile_ActionTypes.skeleton == actionType)
  256. {
  257. return typeof(SteamVR_Action_Skeleton);
  258. }
  259. if (SteamVR_Input_ActionFile_ActionTypes.vibration == actionType)
  260. {
  261. return typeof(SteamVR_Action_Vibration);
  262. }
  263. throw new System.Exception("unknown type (" + action.type + ") in actions file for action: " + action.name);
  264. }
  265. private static string GetClassPath()
  266. {
  267. string path = string.Format("Assets/{0}", SteamVR_Settings.instance.steamVRInputPath);
  268. if (path[0] == '/' || path[0] == '\\')
  269. path = path.Remove(0, 1);
  270. return path;
  271. }
  272. private static string GetSubFolderPath()
  273. {
  274. return Path.Combine(GetClassPath(), steamVRInputActionSetClassesFolder);
  275. }
  276. private static string GetSourceFilePath(string classname, string suffix = ".cs")
  277. {
  278. string sourceFileName = string.Format("{0}{1}", classname, suffix);
  279. return Path.Combine(GetClassPath(), sourceFileName);
  280. }
  281. private static bool fileChanged = false;
  282. private static void CreateFile(string fullPath, CodeCompileUnit compileUnit)
  283. {
  284. // Generate the code with the C# code provider.
  285. CSharpCodeProvider provider = new CSharpCodeProvider();
  286. // Build the output file name.
  287. string fullSourceFilePath = fullPath;
  288. //Debug.Log("[SteamVR] Writing class to: " + fullSourceFilePath);
  289. string path = GetClassPath();
  290. string[] parts = path.Split('/');
  291. for (int partIndex = 0; partIndex < parts.Length - 1; partIndex++)
  292. {
  293. string directoryPath = string.Join("/", parts.Take(partIndex + 1).ToArray());
  294. if (Directory.Exists(directoryPath) == false)
  295. {
  296. Directory.CreateDirectory(directoryPath);
  297. //Debug.Log("[SteamVR] Created directory: " + directoryPath);
  298. }
  299. }
  300. string priorMD5 = null;
  301. FileInfo file = new FileInfo(fullSourceFilePath);
  302. if (file.Exists)
  303. {
  304. file.IsReadOnly = false;
  305. priorMD5 = SteamVR_Utils.GetBadMD5HashFromFile(fullSourceFilePath);
  306. }
  307. // Create a TextWriter to a StreamWriter to the output file.
  308. using (StreamWriter sw = new StreamWriter(fullSourceFilePath, false))
  309. {
  310. IndentedTextWriter tw = new IndentedTextWriter(sw, " ");
  311. // Generate source code using the code provider.
  312. provider.GenerateCodeFromCompileUnit(compileUnit, tw,
  313. new CodeGeneratorOptions() { BracingStyle = "C" });
  314. // Close the output file.
  315. tw.Close();
  316. string newMD5 = SteamVR_Utils.GetBadMD5HashFromFile(fullSourceFilePath);
  317. if (priorMD5 != newMD5)
  318. fileChanged = true;
  319. }
  320. //Debug.Log("[SteamVR] Complete! Input class at: " + fullSourceFilePath);
  321. }
  322. private const string getActionMethodParamName = "path";
  323. private const string skipStateUpdatesParamName = "skipStateAndEventUpdates";
  324. private static List<CodeTypeDeclaration> GenerateActionSetClasses()
  325. {
  326. List<CodeTypeDeclaration> setClasses = new List<CodeTypeDeclaration>();
  327. for (int actionSetIndex = 0; actionSetIndex < SteamVR_Input.actionFile.action_sets.Count; actionSetIndex++)
  328. {
  329. SteamVR_Input_ActionFile_ActionSet actionSet = SteamVR_Input.actionFile.action_sets[actionSetIndex];
  330. CodeTypeDeclaration setClass = CreateActionSetClass(actionSet);
  331. setClasses.Add(setClass);
  332. }
  333. return setClasses;
  334. }
  335. private const string initializeActionDictionariesMethodName = "PreinitializeActionDictionaries";
  336. private const string initializeActionSetDictionariesMethodName = "PreinitializeActionSetDictionaries";
  337. private const string preInitActionsMethodName = "PreInitActions";
  338. private const string initializeActionsArraysMethodName = "InitializeActionArrays";
  339. private static void GenerateActionHelpers(string actionsClassFileName)
  340. {
  341. CodeCompileUnit compileUnit = new CodeCompileUnit();
  342. CodeTypeDeclaration inputClass = CreatePartialInputClass(compileUnit);
  343. CodeArrayCreateExpression actionsArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action)));
  344. CodeArrayCreateExpression actionsInArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(ISteamVR_Action_In)));
  345. CodeArrayCreateExpression actionsOutArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(ISteamVR_Action_Out)));
  346. CodeArrayCreateExpression actionsVibrationArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Vibration)));
  347. CodeArrayCreateExpression actionsPoseArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Pose)));
  348. CodeArrayCreateExpression actionsSkeletonArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Skeleton)));
  349. CodeArrayCreateExpression actionsBooleanArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Boolean)));
  350. CodeArrayCreateExpression actionsSingleArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Single)));
  351. CodeArrayCreateExpression actionsVector2Array = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Vector2)));
  352. CodeArrayCreateExpression actionsVector3Array = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_Action_Vector3)));
  353. CodeArrayCreateExpression actionsNonPoseNonSkeletonArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(ISteamVR_Action_In)));
  354. //add the getaction method to
  355. CodeMemberMethod actionsArraysInitMethod = CreateStaticMethod(inputClass, initializeActionsArraysMethodName, false);
  356. CodeMemberMethod actionsPreInitMethod = CreateStaticMethod(inputClass, preInitActionsMethodName, false);
  357. for (int actionSetIndex = 0; actionSetIndex < SteamVR_Input.actionFile.action_sets.Count; actionSetIndex++)
  358. {
  359. SteamVR_Input_ActionFile_ActionSet actionSet = SteamVR_Input.actionFile.action_sets[actionSetIndex];
  360. string actionSetShortName = actionSet.shortName;
  361. actionSetShortName = actionSetShortName.Substring(0, 1).ToLower() + actionSetShortName.Substring(1);
  362. for (int actionIndex = 0; actionIndex < actionSet.actionsList.Count; actionIndex++)
  363. {
  364. SteamVR_Input_ActionFile_Action action = actionSet.actionsList[actionIndex];
  365. string actionShortName = action.shortName;
  366. string typeName = GetTypeStringForAction(action);
  367. string codeFriendlyInstanceName;
  368. if (actionSet.actionsList.Count(findAction => findAction.shortName == actionShortName) >= 2)
  369. codeFriendlyInstanceName = string.Format("{0}_{1}_{2}", actionSetShortName, action.direction.ToString().ToLower(), actionShortName);
  370. else
  371. codeFriendlyInstanceName = string.Format("{0}_{1}", actionSetShortName, actionShortName);
  372. CodeMemberField actionField = CreateFieldAndPropertyWrapper(inputClass, codeFriendlyInstanceName, typeName);
  373. AddAssignActionStatement(actionsPreInitMethod, inputClass.Name, actionField.Name, action.name, typeName);
  374. actionsArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  375. if (action.direction == SteamVR_ActionDirections.In)
  376. {
  377. actionsInArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  378. if (typeName == typeof(SteamVR_Action_Pose).Name)
  379. {
  380. actionsPoseArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  381. }
  382. else if (typeName == typeof(SteamVR_Action_Skeleton).Name)
  383. {
  384. actionsSkeletonArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  385. }
  386. else if (typeName == typeof(SteamVR_Action_Boolean).Name)
  387. {
  388. actionsBooleanArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  389. }
  390. else if (typeName == typeof(SteamVR_Action_Single).Name)
  391. {
  392. actionsSingleArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  393. }
  394. else if (typeName == typeof(SteamVR_Action_Vector2).Name)
  395. {
  396. actionsVector2Array.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  397. }
  398. else if (typeName == typeof(SteamVR_Action_Vector3).Name)
  399. {
  400. actionsVector3Array.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  401. }
  402. if ((typeName == typeof(SteamVR_Action_Skeleton).Name) == false && (typeName == typeof(SteamVR_Action_Pose).Name) == false)
  403. {
  404. actionsNonPoseNonSkeletonArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  405. }
  406. }
  407. else
  408. {
  409. actionsVibrationArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  410. actionsOutArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  411. }
  412. }
  413. }
  414. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsFieldName, actionsArray);
  415. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsInFieldName, actionsInArray);
  416. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsOutFieldName, actionsOutArray);
  417. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsVibrationFieldName, actionsVibrationArray);
  418. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsPoseFieldName, actionsPoseArray);
  419. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsBooleanFieldName, actionsBooleanArray);
  420. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsSingleFieldName, actionsSingleArray);
  421. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsVector2FieldName, actionsVector2Array);
  422. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsVector3FieldName, actionsVector3Array);
  423. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsSkeletonFieldName, actionsSkeletonArray);
  424. AddAssignStatement(actionsArraysInitMethod, SteamVR_Input_Generator_Names.actionsNonPoseNonSkeletonIn, actionsNonPoseNonSkeletonArray);
  425. // Build the output file name.
  426. string fullSourceFilePath = GetSourceFilePath(actionsClassFileName);
  427. CreateFile(fullSourceFilePath, compileUnit);
  428. }
  429. private const string startPreInitActionSetsMethodName = "StartPreInitActionSets";
  430. private const string finishPreInitActionSetsMethodName = "PreinitializeFinishActionSets";
  431. private static void GenerateActionSetsHelpers(string actionSetsClassFileName)
  432. {
  433. CodeCompileUnit compileUnit = new CodeCompileUnit();
  434. CodeTypeDeclaration inputClass = CreatePartialInputClass(compileUnit);
  435. CodeMemberMethod startPreInitActionSetsMethod = CreateStaticMethod(inputClass, startPreInitActionSetsMethodName, false);
  436. CodeArrayCreateExpression actionSetsArray = new CodeArrayCreateExpression(new CodeTypeReference(typeof(SteamVR_ActionSet)));
  437. for (int actionSetIndex = 0; actionSetIndex < SteamVR_Input.actionFile.action_sets.Count; actionSetIndex++)
  438. {
  439. SteamVR_Input_ActionFile_ActionSet actionSet = SteamVR_Input.actionFile.action_sets[actionSetIndex];
  440. string shortName = GetValidIdentifier(actionSet.shortName);
  441. string codeFriendlyInstanceName = shortName;
  442. string setTypeName = GetSetClassName(actionSet);
  443. CodeMemberField actionSetField = CreateFieldAndPropertyWrapper(inputClass, shortName, setTypeName);
  444. AddAssignActionSetStatement(startPreInitActionSetsMethod, inputClass.Name, actionSetField.Name, actionSet.name, setTypeName);
  445. actionSetsArray.Initializers.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), codeFriendlyInstanceName));
  446. }
  447. AddAssignStatement(startPreInitActionSetsMethod, SteamVR_Input_Generator_Names.actionSetsFieldName, actionSetsArray);
  448. // Build the output file name.
  449. string fullSourceFilePath = GetSourceFilePath(actionSetsClassFileName);
  450. CreateFile(fullSourceFilePath, compileUnit);
  451. }
  452. private static CSharpCodeProvider provider = new CSharpCodeProvider();
  453. private static string GetValidIdentifier(string name)
  454. {
  455. string newName = name.Replace("-", "_");
  456. newName = provider.CreateValidIdentifier(newName);
  457. return newName;
  458. }
  459. public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
  460. {
  461. var member = expression.Body as MethodCallExpression;
  462. if (member != null)
  463. return member.Method;
  464. throw new ArgumentException("Expression is not a method", "expression");
  465. }
  466. private static CodeTypeDeclaration CreatePartialInputClass(CodeCompileUnit compileUnit)
  467. {
  468. CodeNamespace codeNamespace = new CodeNamespace(typeof(SteamVR_Input).Namespace);
  469. codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
  470. codeNamespace.Imports.Add(new CodeNamespaceImport("UnityEngine"));
  471. compileUnit.Namespaces.Add(codeNamespace);
  472. CodeTypeDeclaration inputClass = new CodeTypeDeclaration(SteamVR_Input_Generator_Names.actionsClassName);
  473. inputClass.IsPartial = true;
  474. codeNamespace.Types.Add(inputClass);
  475. return inputClass;
  476. }
  477. private static string GetSetClassName(SteamVR_Input_ActionFile_ActionSet set)
  478. {
  479. return actionSetClassNamePrefix + set.shortName;
  480. }
  481. private const string inActionFieldPrefix = "in_";
  482. private const string outActionFieldPrefix = "out_";
  483. private const string setFinishPreInitializeMethodName = "FinishPreInitialize";
  484. private static CodeTypeDeclaration CreateActionSetClass(SteamVR_Input_ActionFile_ActionSet set)
  485. {
  486. CodeCompileUnit compileUnit = new CodeCompileUnit();
  487. CodeNamespace codeNamespace = new CodeNamespace(typeof(SteamVR_Input).Namespace);
  488. codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
  489. codeNamespace.Imports.Add(new CodeNamespaceImport("UnityEngine"));
  490. compileUnit.Namespaces.Add(codeNamespace);
  491. CodeTypeDeclaration setClass = new CodeTypeDeclaration(GetSetClassName(set));
  492. setClass.BaseTypes.Add(typeof(SteamVR_ActionSet));
  493. setClass.Attributes = MemberAttributes.Public;
  494. codeNamespace.Types.Add(setClass);
  495. string actionSetShortName = set.shortName;
  496. actionSetShortName = actionSetShortName.Substring(0, 1).ToLower() + actionSetShortName.Substring(1);
  497. foreach (var inAction in set.actionsInList)
  498. {
  499. string inActionName = inAction.shortName;
  500. if (set.actionsOutList.Any(outAction => inAction.shortName == outAction.shortName))
  501. inActionName = inActionFieldPrefix + inActionName;
  502. string actionClassPropertyName = string.Format("{0}_{1}", actionSetShortName, inActionName);
  503. CreateActionPropertyWrapper(setClass, SteamVR_Input_Generator_Names.actionsClassName, inActionName, actionClassPropertyName, inAction);
  504. }
  505. foreach (var outAction in set.actionsOutList)
  506. {
  507. string outActionName = outAction.shortName;
  508. if (set.actionsInList.Any(inAction => inAction.shortName == outAction.shortName))
  509. outActionName = outActionFieldPrefix + outActionName;
  510. string actionClassPropertyName = string.Format("{0}_{1}", actionSetShortName, outActionName);
  511. CreateActionPropertyWrapper(setClass, SteamVR_Input_Generator_Names.actionsClassName, outActionName, actionClassPropertyName, outAction);
  512. }
  513. // Build the output file name.
  514. string folderPath = GetSubFolderPath();
  515. string fullSourceFilePath = Path.Combine(folderPath, setClass.Name + ".cs");
  516. CreateFile(fullSourceFilePath, compileUnit);
  517. return setClass;
  518. }
  519. private static CodeMemberMethod CreateStaticMethod(CodeTypeDeclaration inputClass, string methodName, bool isPublic)
  520. {
  521. CodeMemberMethod method = new CodeMemberMethod();
  522. method.Name = methodName;
  523. if (isPublic)
  524. method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
  525. else
  526. method.Attributes = MemberAttributes.Private | MemberAttributes.Static;
  527. inputClass.Members.Add(method);
  528. return method;
  529. }
  530. private static CodeMemberMethod CreateStaticConstructorMethod(CodeTypeDeclaration inputClass)
  531. {
  532. CodeTypeConstructor method = new CodeTypeConstructor();
  533. method.Attributes = MemberAttributes.Static;
  534. inputClass.Members.Add(method);
  535. return method;
  536. }
  537. private static CodeMemberField CreateField(CodeTypeDeclaration inputClass, string fieldName, Type fieldType, bool isStatic)
  538. {
  539. if (fieldType == null)
  540. Debug.Log("null fieldType");
  541. CodeMemberField field = new CodeMemberField();
  542. field.Name = fieldName;
  543. field.Type = new CodeTypeReference(fieldType);
  544. field.Attributes = MemberAttributes.Public;
  545. if (isStatic)
  546. field.Attributes |= MemberAttributes.Static;
  547. inputClass.Members.Add(field);
  548. return field;
  549. }
  550. private static CodeMemberField CreateFieldAndPropertyWrapper(CodeTypeDeclaration inputClass, string name, string type)
  551. {
  552. CodeMemberField actionField = CreatePrivateField(inputClass, name, type, true);
  553. CodeMemberProperty actionProperty = CreateStaticProperty(inputClass, name, type, actionField);
  554. return actionField;
  555. }
  556. private static CodeMemberProperty CreateStaticProperty(CodeTypeDeclaration inputClass, string propertyName, string propertyType, CodeMemberField privateField)
  557. {
  558. CodeMemberProperty property = new CodeMemberProperty();
  559. property.Name = propertyName;
  560. property.Type = new CodeTypeReference(propertyType);
  561. property.Attributes = MemberAttributes.Public | MemberAttributes.Static;
  562. CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(inputClass.Name), privateField.Name);
  563. CodeMethodInvokeExpression invokeExpression = new CodeMethodInvokeExpression(fieldReference, "GetCopy");
  564. invokeExpression.Method.TypeArguments.Add(property.Type);
  565. CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(invokeExpression);
  566. property.GetStatements.Add(returnStatement);
  567. inputClass.Members.Add(property);
  568. return property;
  569. }
  570. private static CodeMemberProperty CreateActionPropertyWrapper(CodeTypeDeclaration addToClass, string actionClass, string propertyName, string actionClassFieldName, SteamVR_Input_ActionFile_Action action)
  571. {
  572. string propertyType = GetTypeStringForAction(action);
  573. CodeMemberProperty property = new CodeMemberProperty();
  574. property.Name = propertyName;
  575. property.Type = new CodeTypeReference(propertyType);
  576. property.Attributes = MemberAttributes.Public;
  577. CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(actionClass), actionClassFieldName);
  578. CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(fieldReference);
  579. property.GetStatements.Add(returnStatement);
  580. addToClass.Members.Add(property);
  581. return property;
  582. }
  583. private const string privateFieldPrefix = "p_";
  584. private static CodeMemberField CreatePrivateField(CodeTypeDeclaration inputClass, string fieldName, string fieldType, bool isStatic)
  585. {
  586. return CreateField(inputClass, privateFieldPrefix + fieldName, fieldType, isStatic, false);
  587. }
  588. private static CodeMemberField CreateField(CodeTypeDeclaration inputClass, string fieldName, string fieldType, bool isStatic, bool isPublic = true)
  589. {
  590. CodeMemberField field = new CodeMemberField();
  591. field.Name = fieldName;
  592. field.Type = new CodeTypeReference(fieldType);
  593. if (isPublic)
  594. field.Attributes = MemberAttributes.Public;
  595. else
  596. field.Attributes = MemberAttributes.Private;
  597. if (isStatic)
  598. field.Attributes |= MemberAttributes.Static;
  599. inputClass.Members.Add(field);
  600. return field;
  601. }
  602. private static CodeMethodInvokeExpression AddStaticInvokeToMethod(CodeMemberMethod methodToAddTo, string classToInvoke, string invokeMethodName)
  603. {
  604. CodeMethodInvokeExpression invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(
  605. new CodeTypeReferenceExpression(classToInvoke), invokeMethodName));
  606. methodToAddTo.Statements.Add(invokeMethod);
  607. return invokeMethod;
  608. }
  609. private static void AddAssignStatement(CodeMemberMethod methodToAddTo, string fieldToAssign, CodeArrayCreateExpression array)
  610. {
  611. methodToAddTo.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(SteamVR_Input)), fieldToAssign), array));
  612. }
  613. private const string createActionMethodName = "Create";
  614. private const string createActionSetMethodName = "Create";
  615. private const string getActionFromPathMethodName = "GetActionFromPath";
  616. //grab = SteamVR_Action.Create<SteamVR_Action_Boolean>("path");
  617. private static void AddAssignActionStatement(CodeMemberMethod methodToAddTo, string actionClassName, string fieldToAssign, string actionPath, string actionType)
  618. {
  619. CodeMethodInvokeExpression invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(SteamVR_Action).Name), createActionMethodName));
  620. invokeMethod.Method.TypeArguments.Add(actionType);
  621. invokeMethod.Parameters.Add(new CodePrimitiveExpression(actionPath));
  622. methodToAddTo.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(actionClassName), fieldToAssign), new CodeCastExpression(new CodeTypeReference(actionType), invokeMethod)));
  623. }
  624. private static void AddAssignActionSetStatement(CodeMemberMethod methodToAddTo, string actionClassName, string fieldToAssign, string actionSetName, string actionSetType)
  625. {
  626. CodeMethodInvokeExpression invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(SteamVR_ActionSet).Name), createActionSetMethodName));
  627. invokeMethod.Method.TypeArguments.Add(actionSetType);
  628. invokeMethod.Parameters.Add(new CodePrimitiveExpression(actionSetName));
  629. methodToAddTo.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(actionClassName), fieldToAssign), new CodeCastExpression(new CodeTypeReference(actionSetType), invokeMethod)));
  630. }
  631. private static void AddAssignLocalActionStatement(CodeMemberMethod methodToAddTo, string fieldToAssign, string actionPath, string actionType, bool create)
  632. {
  633. CodeMethodInvokeExpression invokeMethod;
  634. if (create)
  635. invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(SteamVR_Action).Name), createActionMethodName));
  636. else
  637. invokeMethod = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(SteamVR_Input).Name), getActionFromPathMethodName));
  638. invokeMethod.Method.TypeArguments.Add(actionType);
  639. invokeMethod.Parameters.Add(new CodePrimitiveExpression(actionPath));
  640. methodToAddTo.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldToAssign), new CodeCastExpression(new CodeTypeReference(actionType), invokeMethod)));
  641. }
  642. private static void AddAssignNewInstanceStatement(CodeMemberMethod methodToAddTo, string fieldToAssign, string fieldType)
  643. {
  644. CodeObjectCreateExpression createExpression = new CodeObjectCreateExpression(new CodeTypeReference(fieldType));
  645. methodToAddTo.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldToAssign), createExpression));
  646. }
  647. private static CodeConditionStatement CreateStringCompareStatement(CodeMemberMethod methodToAddTo, string action, string paramName, string returnActionName)
  648. {
  649. MethodInfo stringEqualsMethodInfo = GetMethodInfo<string>(set => string.Equals(null, null, StringComparison.CurrentCultureIgnoreCase));
  650. CodeTypeReferenceExpression stringType = new CodeTypeReferenceExpression(typeof(string));
  651. CodePrimitiveExpression actionName = new CodePrimitiveExpression(action);
  652. CodeVariableReferenceExpression pathName = new CodeVariableReferenceExpression(paramName);
  653. CodeVariableReferenceExpression caseInvariantName = new CodeVariableReferenceExpression("StringComparison.CurrentCultureIgnoreCase");
  654. CodeMethodInvokeExpression stringCompare = new CodeMethodInvokeExpression(stringType, stringEqualsMethodInfo.Name, pathName, actionName, caseInvariantName);
  655. CodeMethodReturnStatement returnAction = new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(SteamVR_Input)), returnActionName));
  656. CodeConditionStatement condition = new CodeConditionStatement(stringCompare, returnAction);
  657. methodToAddTo.Statements.Add(condition);
  658. return condition;
  659. }
  660. }
  661. }