Guest User

Untitled

a guest
Sep 4th, 2020
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.52 KB | None | 0 0
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.Collections.Generic;
  4.  
  5. using Sirenix.OdinInspector;
  6. using Sirenix.OdinInspector.Editor;
  7.  
  8. using HoudiniEngineUnity;
  9.  
  10. namespace Skatebirb
  11. {
  12. //public class SkateMaker : EditorWindow
  13. public class SkateMaker : OdinEditorWindow
  14. {
  15. #region Selection Tracker
  16. private HEU_HoudiniAssetRoot activeHoudiniRoot = null;
  17. private bool noHoudiniRoot = false;
  18. #endregion
  19.  
  20. #region Group and Marker Names
  21. private const string RailNodeMarker = "Grind_Node";
  22. private const string CollisionMarker = "_collision";
  23.  
  24. private const string RailParentName = "Generated_Rail_Nodes";
  25. private const string RailNodeStandalone = "Generated_Rail_Node";
  26.  
  27. private const string RampParentName = "Generated_Ramp_Zones";
  28. private const string RampObjectName = "Generated_Ramp_Zone";
  29.  
  30. private const string VertParentName = "Generated_Vert_Zones";
  31. private const string VertObjectName = "Generated_Vert_Zone";
  32. #endregion
  33.  
  34. #region Rail Binding Paramters
  35. private const float RailDotBreakPoint = 0.174f; // Cos of 80deg
  36. private const float RailDotDestroyPoint = -0.174f; // Cos of 100deg
  37. #endregion
  38.  
  39. #region Vert Parameters
  40. private const float VertThickness = 1.5f;
  41. private const float VertHeightFromRail = 7f;
  42. private const float VertPlaneAdjustment = -0.3f;
  43. #endregion
  44.  
  45. #region Ramp Paramters
  46. private const float RampHeightCutoff = 4f;
  47. private const float VertHeightCutoff = 10f;
  48. #endregion
  49.  
  50. #region Component Trackers
  51. // Each entry in this list could be a multi-segment rail, it does NOT track every node in every rail
  52. private List<GrindableRail> activeRailOrigins = new List<GrindableRail>();
  53.  
  54. private List<VertZone> activeVertZones = new List<VertZone>();
  55.  
  56. private List<RampZone> activeRampZones = new List<RampZone>();
  57. #endregion
  58.  
  59. #region Menu Handling
  60. [MenuItem("Tools/Skate Maker")]
  61. public static void ShowWindow()
  62. {
  63. GetWindow<SkateMaker>().Show();
  64. }
  65.  
  66. protected override void OnGUI()
  67. {
  68. // First, try to establish a Houdini root (which also enables/disables tooling related)
  69. activeHoudiniRoot = null;
  70. if (Selection.activeTransform != null)
  71. {
  72. HEU_HoudiniAssetRoot newRoot = Selection.activeTransform.gameObject.GetComponent<HEU_HoudiniAssetRoot>();
  73. if (newRoot != null && newRoot != activeHoudiniRoot)
  74. {
  75. // If we click between Houdini objects, clear any temp data out
  76. activeRailOrigins.Clear();
  77. }
  78.  
  79. activeHoudiniRoot = newRoot;
  80. }
  81.  
  82. noHoudiniRoot = (activeHoudiniRoot == null) ? true : false;
  83.  
  84. // Once we have a root, try to establish a list of any contained rail segments (if we don't already have one)
  85. if (activeHoudiniRoot != null && activeRailOrigins.Count == 0)
  86. {
  87. Transform railParent = activeHoudiniRoot.transform.Find(RailParentName);
  88. if (railParent != null)
  89. {
  90. GrindableRail[] foundRails = railParent.GetComponentsInChildren<GrindableRail>();
  91. for (int index = 0; index < foundRails.Length; index++)
  92. {
  93. GrindableRail foundRail = foundRails[index];
  94.  
  95. // We're looking for rail origins, which means we need the start of each group of rails
  96. while (foundRail.prevGrindable != null)
  97. {
  98. foundRail = foundRail.prevGrindable;
  99. if (foundRail == foundRails[index]) { break; } // (not gonna catch me, loop boi! get outta here)
  100. }
  101.  
  102. // If we haven't found this rail origin yet, add it!
  103. if (!activeRailOrigins.Contains(foundRail)) { activeRailOrigins.Add(foundRail); }
  104. }
  105. }
  106. }
  107.  
  108. base.OnGUI();
  109. }
  110.  
  111. [FoldoutGroup("Houdini", expanded: true)]
  112. [FoldoutGroup("Houdini/General Tools", expanded: true)]
  113. [ButtonGroup("Houdini/General Tools/Buttons")]
  114. [Button("Enable Physics", ButtonSizes.Large)]
  115. [DisableIf("noHoudiniRoot")]
  116. public void Button_Houdini_EnablePhysics()
  117. {
  118. Houdini_EnablePhysics();
  119. }
  120.  
  121. [FoldoutGroup("Houdini/Ramp Processors", expanded: true)]
  122. [ButtonGroup("Houdini/Ramp Processors/Bulk Operations")]
  123. [Button("Clean", ButtonSizes.Large)]
  124. [DisableIf("noHoudiniRoot")]
  125. public void Button_Houdini_Clean()
  126. {
  127. Houdini_ClearExistingRails();
  128. Houdini_HideRailMarkers();
  129. Houdini_ClearExistingRamps();
  130. Houdini_ClearExistingVerts();
  131. }
  132.  
  133. [ButtonGroup("Houdini/Ramp Processors/Bulk Operations")]
  134. [Button("Ultra Rampify 5000", ButtonSizes.Large)]
  135. [DisableIf("noHoudiniRoot")]
  136. public void Button_Houdini_FullRampProcessing()
  137. {
  138. Houdini_EnablePhysics();
  139. Houdini_ProcessRampRails();
  140. Houdini_ProcessRamps();
  141. Houdini_ProcessVerts();
  142. }
  143.  
  144. [TitleGroup("Houdini/Ramp Processors/Separate Operations")]
  145. [ButtonGroup("Houdini/Ramp Processors/Separate Operations/Buttons")]
  146. [Button("Generate Rails", ButtonSizes.Large)]
  147. [DisableIf("noHoudiniRoot")]
  148. public void Button_Houdini_GenerateRails()
  149. {
  150. Houdini_ProcessRampRails();
  151. }
  152.  
  153. [ButtonGroup("Houdini/Ramp Processors/Separate Operations/Buttons")]
  154. [Button("Add Ramp Zones", ButtonSizes.Large)]
  155. [DisableIf("noHoudiniRoot")]
  156. public void Button_Houdini_AddRampZones()
  157. {
  158. Houdini_ProcessRamps();
  159. }
  160.  
  161. [ButtonGroup("Houdini/Ramp Processors/Separate Operations/Buttons")]
  162. [Button("Add Vert Zones", ButtonSizes.Large)]
  163. [DisableIf("noHoudiniRoot")]
  164. public void Button_Houdini_AddVertZones()
  165. {
  166. Houdini_ProcessVerts();
  167. }
  168.  
  169. [FoldoutGroup("Houdini/In-Order Node Rails", expanded: true)]
  170. [ButtonGroup("Houdini/In-Order Node Rails/Buttons")]
  171. [Button("Clean", ButtonSizes.Large)]
  172. [DisableIf("noHoudiniRoot")]
  173. public void Button_Houdini_CleanInOrderRails()
  174. {
  175. Houdini_ClearExistingRails();
  176. Houdini_HideRailMarkers();
  177. }
  178.  
  179. [ButtonGroup("Houdini/In-Order Node Rails/Buttons")]
  180. [Button("Link In Node Order", ButtonSizes.Large)]
  181. [DisableIf("noHoudiniRoot")]
  182. public void Button_Houdini_LinkRailsByOrder()
  183. {
  184. Houdini_MakeRailsInNodeOrder();
  185. }
  186. #endregion
  187.  
  188. #region Houdini General Operations
  189. void Houdini_EnablePhysics()
  190. {
  191. Houdini_EnablePhysics_Recurisve(activeHoudiniRoot.gameObject);
  192. }
  193.  
  194. void Houdini_EnablePhysics_Recurisve(GameObject curObj)
  195. {
  196. // Look for any collision-flagged objects...
  197. if (curObj.name.Contains(CollisionMarker))
  198. {
  199. // ... and enable any colliders we find, easy!
  200. MeshCollider tempCollider = curObj.GetComponent<MeshCollider>();
  201. if (tempCollider != null)
  202. {
  203. tempCollider.enabled = true;
  204. }
  205. }
  206.  
  207. // Then handle the recursion
  208. foreach (Transform childTrans in curObj.transform)
  209. {
  210. Houdini_EnablePhysics_Recurisve(childTrans.gameObject);
  211. }
  212. }
  213.  
  214. void Houdini_HideRailMarkers()
  215. {
  216. Houdini_HideRailMarkers_Recursive(activeHoudiniRoot.gameObject);
  217. }
  218.  
  219. void Houdini_HideRailMarkers_Recursive(GameObject curObj)
  220. {
  221. bool shouldHide = false;
  222.  
  223. // First we run through, processing nodes and flagging some for hiding
  224. if (curObj.name.Contains(RailNodeMarker))
  225. {
  226. // We're looking for the rail nodes with meshes, those are the ones that are sitting at the right position
  227. MeshRenderer tempRenderer = curObj.GetComponent<MeshRenderer>();
  228. if (tempRenderer != null && tempRenderer.enabled) // (they also need to be enabled, if they're hidden that means they're the instance originals sitting off in another folder)
  229. {
  230.  
  231. // This is one of the nodes we're replacing with something else, so, no reason for it to be visible
  232. shouldHide = true;
  233. }
  234. // If it's an empty object, that means it's one of the empty parents to the rail node instances we also want to ditch
  235. else if (tempRenderer == null) { shouldHide = true; }
  236. }
  237.  
  238. // Then handle the recursion (BEFORE we delete anything)
  239. foreach (Transform childTrans in curObj.transform)
  240. {
  241. Houdini_HideRailMarkers_Recursive(childTrans.gameObject);
  242. }
  243.  
  244. // Finally, hide if we should hide
  245. if (shouldHide && curObj != null)
  246. {
  247. curObj.SetActive(false);
  248. }
  249. }
  250. #endregion
  251.  
  252. #region Houdini Rail Addition
  253. void Houdini_ClearExistingRails()
  254. {
  255. // Clear our rail tracking
  256. activeRailOrigins.Clear();
  257.  
  258. // Also, clear out any prior changes in the Houdini hierarchy
  259. for (int index = 0; index < activeHoudiniRoot.transform.childCount; index++)
  260. {
  261. Transform childTrans = activeHoudiniRoot.transform.GetChild(index);
  262.  
  263. // Clear out any network of rails we may have previously created
  264. if (childTrans.name == RailParentName)
  265. {
  266. while (childTrans.childCount > 0)
  267. {
  268. Transform tempTrans = childTrans.GetChild(0);
  269. DestroyImmediate(tempTrans.gameObject);
  270. }
  271.  
  272. DestroyImmediate(childTrans.gameObject);
  273. index--; // (since we just destroyed a child)
  274. continue;
  275. }
  276. }
  277. }
  278.  
  279. /// <summary>
  280. /// This is used by most rail processing, to go through and find the rail nodes to then link or otherwise operate on
  281. /// </summary>
  282. /// <param name="curObj">the active houdini root object</param>
  283. /// <param name="inProcessRails">the list to add found rail nodes to</param>
  284. /// <param name="activeRailRoot">the node to parent created rail nodes to</param>
  285. void Houdini_ProcessRails_Recursive(GameObject curObj, List<SortableGrindableRail> inProcessRails, Transform activeRailRoot)
  286. {
  287. bool shouldHide = false;
  288.  
  289. // First we run through, processing nodes and flagging some for hiding
  290. if (curObj.name.Contains(RailNodeMarker))
  291. {
  292. // We're looking for the rail nodes with meshes, those are the ones that are sitting at the right position
  293. MeshRenderer tempRenderer = curObj.GetComponent<MeshRenderer>();
  294. if (tempRenderer != null && tempRenderer.enabled) // (they also need to be enabled, if they're hidden that means they're the instance originals sitting off in another folder)
  295. {
  296. // Make a clean clone of the object, that we control, that retains the pos and rot (we'll need the forward vec as a normal, later)
  297. GameObject newRailNode = new GameObject();
  298. newRailNode.name = RailNodeStandalone;
  299. newRailNode.transform.position = curObj.transform.position;
  300. newRailNode.transform.rotation = curObj.transform.rotation;
  301.  
  302. newRailNode.transform.parent = activeRailRoot;
  303.  
  304. // ... and set it up as a rail node
  305. SortableGrindableRail inProcessRail = new SortableGrindableRail();
  306. inProcessRail.theRail = newRailNode.AddComponent<GrindableRail>();
  307.  
  308. // ... and extract the index
  309. string[] containsIndex = curObj.name.Substring(curObj.name.IndexOf(RailNodeMarker) + RailNodeMarker.Length).Split('_');
  310. if (containsIndex.Length > 0)
  311. {
  312. inProcessRail.index = int.Parse(containsIndex[0]);
  313. inProcessRail.theRail.gameObject.name += inProcessRail.index;
  314. }
  315. else { Debug.Log("Couldn't find rail index for '" + curObj.name + "', generated rail ain't gonna be great."); inProcessRail.index = 0; }
  316.  
  317. // And track it (for later sorting)
  318. inProcessRails.Add(inProcessRail);
  319.  
  320. // This is one of the nodes we're replacing with something else, so, no reason for it to be visible
  321. shouldHide = true;
  322. }
  323. // If it's an empty object, that means it's one of the empty parents to the rail node instances we also want to ditch
  324. else if (tempRenderer == null) { shouldHide = true; }
  325. }
  326.  
  327. // Then handle the recursion (BEFORE we delete anything)
  328. foreach (Transform childTrans in curObj.transform)
  329. {
  330. Houdini_ProcessRails_Recursive(childTrans.gameObject, inProcessRails, activeRailRoot);
  331. }
  332.  
  333. // Finally, hide if we should hide
  334. if (shouldHide && curObj != null)
  335. {
  336. curObj.SetActive(false);
  337. }
  338. }
  339.  
  340. private class SortableGrindableRail
  341. {
  342. public GrindableRail theRail;
  343. public int index;
  344. }
  345.  
  346. /// <summary>
  347. /// Processing of rails assumed to be part of ramps (ie. marking out curves, and probably not numbered in-order because ramps are weird)
  348. /// </summary>
  349. void Houdini_ProcessRampRails()
  350. {
  351. if (activeHoudiniRoot == null) { return; }
  352.  
  353. // First, clear out any prior work done on it
  354. Houdini_ClearExistingRails();
  355.  
  356. // Then set the clean object up to be rail-ified
  357. GameObject newRailRoot = new GameObject();
  358. newRailRoot.name = RailParentName;
  359. newRailRoot.transform.parent = activeHoudiniRoot.transform;
  360. newRailRoot.transform.localPosition = Vector3.zero;
  361. newRailRoot.transform.localRotation = Quaternion.identity;
  362. newRailRoot.transform.localScale = Vector3.one;
  363.  
  364. Transform activeRailRoot = newRailRoot.transform;
  365.  
  366. // Now, process it!
  367. List<SortableGrindableRail> inProcessRails = new List<SortableGrindableRail>();
  368. Houdini_ProcessRails_Recursive(activeHoudiniRoot.gameObject, inProcessRails, activeRailRoot);
  369.  
  370. // Finally, we need to thread the rails together (if there are any)
  371. if (inProcessRails.Count > 1)
  372. {
  373. // First, we sort the rails and decide what kind of rail this is
  374. inProcessRails.Sort((r1, r2) => r1.index.CompareTo(r2.index));
  375. bool railIsLinear = (inProcessRails.Count == 2) ? true : false;
  376.  
  377. // First node, post-sort, will be a good starting point. Should be at an end, etc.
  378. GrindableRail curRail = inProcessRails[0].theRail;
  379. Vector3 curDir = (curRail.transform.position - activeHoudiniRoot.transform.position).normalized;
  380. inProcessRails.RemoveAt(0);
  381.  
  382. // Now step through the rest
  383. float signFlip = 1f;
  384. bool lastDitchReverse = false;
  385. while (inProcessRails.Count > 0)
  386. {
  387. int closestNextIndex = -1;
  388. float closestNextDot = -2f;
  389.  
  390. for (int sortedIndex = 0; sortedIndex < inProcessRails.Count; sortedIndex++)
  391. {
  392. SortableGrindableRail sortedRail = inProcessRails[sortedIndex];
  393.  
  394. Vector3 newDir = (sortedRail.theRail.transform.position - activeHoudiniRoot.transform.position).normalized;
  395. // We only link in a clockwise (or anti-clockwise as a fallback) direction
  396. if (Vector3.Dot(Vector3.Cross(curDir, newDir).normalized, Vector3.up) * signFlip < 0.0f) { continue; }
  397.  
  398. // Now see if this is closer than whatever our current best option is
  399. float newDot = Vector3.Dot(curDir, newDir);
  400. if (newDot > closestNextDot)
  401. {
  402. closestNextIndex = sortedIndex;
  403. closestNextDot = newDot;
  404. }
  405. }
  406.  
  407. // If we found something, link it in and continue
  408. if (closestNextIndex >= 0)
  409. {
  410. GrindableRail prevRail = curRail;
  411. curRail = inProcessRails[closestNextIndex].theRail;
  412. inProcessRails.RemoveAt(closestNextIndex);
  413.  
  414. prevRail.nextGrindable = curRail;
  415. curRail.prevGrindable = prevRail;
  416.  
  417. lastDitchReverse = false; // (we found one, so if we were in a last ditch reverse, we're good to keep in this direction for a while)
  418.  
  419. continue;
  420. }
  421.  
  422. // If we get this far, somehow we didn't find a candidate next-node in our current clockwise or anti-clockwise spin - so try again the other way (catches when we start in the wrong direction or have an odd-one-out last point)
  423. if (!lastDitchReverse) { signFlip *= -1f; lastDitchReverse = true; }
  424. // ... or Shit Broke, so just bail on this whole mess
  425. else { break; }
  426. }
  427.  
  428. // Finish up by linking a (non linear) rail's tail back to its head
  429. GrindableRail lastRail = curRail;
  430. GrindableRail firstRail = lastRail;
  431. while (firstRail.prevGrindable != null) { firstRail = firstRail.prevGrindable; }
  432.  
  433. if (!railIsLinear)
  434. {
  435. lastRail.nextGrindable = firstRail;
  436. firstRail.prevGrindable = lastRail;
  437.  
  438. // Now, let's also hard-break any connections too severe to possibly be ground around (because those are probably loop closures we didn't actually want closed)
  439. GrindableRail hardBreakOtherEnd = null;
  440.  
  441. curRail = firstRail;
  442. do
  443. {
  444. GrindableRail nextRail = curRail.nextGrindable;
  445. if (nextRail == null) { break; }
  446. if (curRail.prevGrindable == null) { curRail = nextRail; continue; }
  447.  
  448. Vector3 lastRailDir = (curRail.transform.position - curRail.prevGrindable.transform.position).normalized;
  449. Vector3 curRailDir = (curRail.nextGrindable.transform.position - curRail.transform.position).normalized;
  450.  
  451. // After we destroy a single connection in the loop, all bets are off for continuing to check for these, so we just have to assume there's only ever one (the loop closure point)
  452. if (Vector3.Dot(lastRailDir, curRailDir) < RailDotDestroyPoint)
  453. {
  454. if (hardBreakOtherEnd == null) { hardBreakOtherEnd = curRail; }
  455. else
  456. {
  457. // This is probably overly precise, I don't think there's ever a case where only one end could be too severe, but like... ugghhhh I'm tired
  458. if (curRail.nextGrindable == hardBreakOtherEnd)
  459. {
  460. curRail.nextGrindable = null;
  461. hardBreakOtherEnd.prevGrindable = null;
  462. }
  463. else if (curRail.prevGrindable == hardBreakOtherEnd)
  464. {
  465. curRail.prevGrindable = null;
  466. hardBreakOtherEnd.nextGrindable = null;
  467. }
  468.  
  469. break;
  470. }
  471. }
  472.  
  473. curRail = nextRail;
  474. } while (curRail != null && curRail != firstRail);
  475.  
  476. // Now here, break the rails into separate chunks based on corner sharpness
  477. activeRailOrigins.Add(firstRail);
  478.  
  479. curRail = firstRail;
  480. do
  481. {
  482. GrindableRail nextRail = curRail.nextGrindable;
  483. if (nextRail == null) { break; }
  484. if (curRail.prevGrindable == null) { curRail = nextRail; continue; }
  485.  
  486. Vector3 lastRailDir = (curRail.transform.position - curRail.prevGrindable.transform.position).normalized;
  487. Vector3 curRailDir = (curRail.nextGrindable.transform.position - curRail.transform.position).normalized;
  488.  
  489. // Any time we detect a sharp corner this way, we're on the "far" side of the sharp corner...
  490. if (Vector3.Dot(lastRailDir, curRailDir) < RailDotBreakPoint)
  491. {
  492. // ... meaning we're the new first-node in the next strip of rails (until the next sharp corner), so make a clone of ourselves to cap the prior run
  493. GrindableRail hardCornerEnd = Instantiate(curRail);
  494. hardCornerEnd.transform.parent = curRail.transform.parent;
  495.  
  496. // Since we changed the parent, we have to re-assign the pos/rot/scale? Because it changes arbitrarily when you change parenting? Sigh.
  497. hardCornerEnd.transform.position = curRail.transform.position;
  498. hardCornerEnd.transform.rotation = curRail.transform.rotation;
  499. hardCornerEnd.transform.localScale = curRail.transform.localScale;
  500.  
  501. hardCornerEnd.gameObject.name = curRail.gameObject.name + "(hard corner end)";
  502.  
  503. hardCornerEnd.nextGrindable = null;
  504. hardCornerEnd.prevGrindable.nextGrindable = hardCornerEnd;
  505.  
  506. // Also remember to snip our own link backwards
  507. curRail.prevGrindable = null;
  508.  
  509. // Each time we make a new chunk, we need to make sure the new chunk gets added to the rail tracker
  510. if (!activeRailOrigins.Contains(curRail)) { activeRailOrigins.Add(curRail); }
  511. }
  512.  
  513. curRail = nextRail;
  514. } while (curRail != firstRail);
  515. }
  516. else
  517. {
  518. // Put your single, solitary rail into the tracking group (so sad)
  519. activeRailOrigins.Add(firstRail);
  520. }
  521.  
  522. // Finally, let's step through and flatten / square up our rail forwards
  523. Houdini_RailFlattenOrientation(activeRailOrigins);
  524. }
  525. }
  526.  
  527. /// <summary>
  528. /// Simpler rail processing that simply links them together in the order they say to link them in
  529. /// </summary>
  530. void Houdini_MakeRailsInNodeOrder()
  531. {
  532. if (activeHoudiniRoot == null) { return; }
  533.  
  534. // First, clear out any prior work done on it
  535. Houdini_ClearExistingRails();
  536.  
  537. // Then set the clean object up to be rail-ified
  538. GameObject newRailRoot = new GameObject();
  539. newRailRoot.name = RailParentName;
  540. newRailRoot.transform.parent = activeHoudiniRoot.transform;
  541. newRailRoot.transform.localPosition = Vector3.zero;
  542. newRailRoot.transform.localRotation = Quaternion.identity;
  543. newRailRoot.transform.localScale = Vector3.one;
  544.  
  545. Transform activeRailRoot = newRailRoot.transform;
  546.  
  547. // Now, process it!
  548. List<SortableGrindableRail> inProcessRails = new List<SortableGrindableRail>();
  549. Houdini_ProcessRails_Recursive(activeHoudiniRoot.gameObject, inProcessRails, activeRailRoot);
  550.  
  551. if (inProcessRails.Count == 0) { return; }
  552.  
  553. // First, we sort the rails and decide what kind of rail this is
  554. inProcessRails.Sort((r1, r2) => r1.index.CompareTo(r2.index));
  555.  
  556. // Now we just link the rails together in their sorted order!
  557. GrindableRail curRail = inProcessRails[0].theRail;
  558. activeRailOrigins.Add(curRail); // (it's all one rail, eyyy)
  559.  
  560. GrindableRail prevRail = curRail;
  561. for (int index = 1; index < inProcessRails.Count; index++)
  562. {
  563. curRail = inProcessRails[index].theRail;
  564.  
  565. prevRail.nextGrindable = curRail;
  566. curRail.prevGrindable = prevRail;
  567.  
  568. prevRail = curRail;
  569. }
  570.  
  571. // Finally, let's step through and flatten / square up our rail forwards
  572. Houdini_RailFitOrientationToExisting(activeRailOrigins);
  573. }
  574.  
  575. /// <summary>
  576. /// Steps through the rails, flattening and squaring them up to being aligned with the rail's connectivity
  577. /// </summary>
  578. /// <param name="railOriginsToFlatten">the list of rail origins you want flattened</param>
  579. void Houdini_RailFlattenOrientation(List<GrindableRail> railOriginsToFlatten)
  580. {
  581. foreach (GrindableRail railOrigin in railOriginsToFlatten)
  582. {
  583. GrindableRail curRail = railOrigin;
  584. do
  585. {
  586. // First, we just flatten
  587. Vector3 newForward = curRail.transform.forward - (activeHoudiniRoot.transform.up * Vector3.Dot(curRail.transform.forward, activeHoudiniRoot.transform.up));
  588. newForward.Normalize();
  589.  
  590. // Then, check if we have a next. If so, make us perpendicular to rail dir.
  591. if (curRail.nextGrindable)
  592. {
  593. Vector3 curRailDir = (curRail.nextGrindable.transform.position - curRail.transform.position).normalized;
  594. newForward = newForward - (curRailDir * Vector3.Dot(newForward, curRailDir));
  595. newForward.Normalize();
  596. }
  597. // If no next, we copy our prev's rotation and use that for ours too.
  598. else
  599. {
  600. newForward = curRail.prevGrindable.transform.forward;
  601. }
  602.  
  603. // Apply our new forward!
  604. curRail.transform.LookAt(curRail.transform.position + newForward, activeHoudiniRoot.transform.up);
  605.  
  606. // Next!
  607. curRail = curRail.nextGrindable;
  608. }
  609. while (curRail != null && curRail != railOrigin);
  610. }
  611. }
  612.  
  613. /// <summary>
  614. /// This retains the Houdini-specied "up" (which ends up in our forward) as our up, and conforms rail orientation to that up and the rail's connectivity
  615. /// </summary>
  616. /// <param name="railOriginsToFlatten">the list of rails you want conformed</param>
  617. void Houdini_RailFitOrientationToExisting(List<GrindableRail> railOriginsToFlatten)
  618. {
  619. foreach (GrindableRail railOrigin in railOriginsToFlatten)
  620. {
  621. GrindableRail curRail = railOrigin;
  622. do
  623. {
  624. // First, pull out the Houdini up (which ends up in our Forward). That's the "up" we actually want to align to.
  625. Vector3 houdiniUp = curRail.transform.forward;
  626.  
  627. // Then, check if we have a next. If so, that's to our immediate left.
  628. Vector3 newForward;
  629. Vector3 newRight;
  630.  
  631. if (curRail.nextGrindable)
  632. {
  633. newRight = -(curRail.nextGrindable.transform.position - curRail.transform.position).normalized;
  634. }
  635. // If no next, we copy our prev's right and use that for ours too.
  636. else
  637. {
  638. newRight = curRail.prevGrindable.transform.right;
  639. }
  640.  
  641. // Now generate the new forward from that right
  642. newForward = Vector3.Cross(newRight, houdiniUp).normalized;
  643.  
  644. // Apply our new forward!
  645. curRail.transform.rotation = Quaternion.LookRotation(newForward, houdiniUp);
  646.  
  647. // Next!
  648. curRail = curRail.nextGrindable;
  649. }
  650. while (curRail != null && curRail != railOrigin);
  651. }
  652. }
  653. #endregion
  654.  
  655. #region Houdini Ramp Addition
  656. void Houdini_ClearExistingRamps()
  657. {
  658. // Clear our vert tracking
  659. activeRampZones.Clear();
  660.  
  661. // Also, clear out any prior changes in the Houdini hierarchy
  662. Transform rampParent = activeHoudiniRoot.transform.Find(RampParentName);
  663. if (rampParent != null)
  664. {
  665. while (rampParent.childCount > 0)
  666. {
  667. Transform tempTrans = rampParent.GetChild(0);
  668. DestroyImmediate(tempTrans.gameObject);
  669. }
  670.  
  671. DestroyImmediate(rampParent.gameObject);
  672. }
  673. }
  674.  
  675. void Houdini_ProcessRamps()
  676. {
  677. if (activeHoudiniRoot == null) { return; }
  678.  
  679. // First, clear out any prior work done on it
  680. Houdini_ClearExistingRamps();
  681.  
  682. if (activeRailOrigins.Count == 0)
  683. {
  684. Debug.LogWarning("Skate Maker: Can't make verts until you've defined rails, sorry!");
  685. return;
  686. }
  687.  
  688. // Then set the clean object up to be vert-ified
  689. GameObject newRampRoot = new GameObject();
  690. newRampRoot.name = RampParentName;
  691. newRampRoot.transform.parent = activeHoudiniRoot.transform;
  692. newRampRoot.transform.localPosition = Vector3.zero;
  693. newRampRoot.transform.localRotation = Quaternion.identity;
  694. newRampRoot.transform.localScale = Vector3.one;
  695.  
  696. Transform activeRampRoot = newRampRoot.transform;
  697.  
  698. // Now, we're gonna find the generated ramp mesh to base our ramp volume on
  699. Mesh meshToBound = null;
  700. Transform meshToBoundTrans = null;
  701. MeshRenderer[] childMeshes = activeHoudiniRoot.transform.GetComponentsInChildren<MeshRenderer>();
  702. foreach (MeshRenderer childMesh in childMeshes)
  703. {
  704. // Given that this is called after we've cleaned and disabled any node markers, there SHOULD only ever be one mesh here - the overall mesh!
  705. if (childMesh.gameObject.activeSelf && childMesh.enabled)
  706. {
  707. MeshFilter meshFilter = childMesh.gameObject.GetComponent<MeshFilter>();
  708. if (meshFilter != null)
  709. {
  710. meshToBound = meshFilter.sharedMesh;
  711. meshToBoundTrans = meshFilter.transform;
  712. }
  713. else
  714. {
  715. // Fallback to skinned mesh, just in case we eventually get fancy with our Houdini shit
  716. SkinnedMeshRenderer skinnedMeshRenderer = childMesh.gameObject.GetComponent<SkinnedMeshRenderer>();
  717. if (skinnedMeshRenderer != null)
  718. {
  719. meshToBound = skinnedMeshRenderer.sharedMesh;
  720. meshToBoundTrans = skinnedMeshRenderer.transform;
  721. }
  722. }
  723. break;
  724. }
  725. }
  726.  
  727. // If there's no mesh filter (or no mesh), not much else we can do at this point
  728. if (meshToBound == null)
  729. {
  730. Debug.LogError("Houdini_ProcessRamps: Can't generate a ramp for something that doesn't even have a mesh!");
  731. return;
  732. }
  733.  
  734. // Extract the bounds from that mesh, and so long as the new object has the position, boom, a bounding ramp for our entire shape!
  735. GameObject rampBoundObj = new GameObject();
  736. rampBoundObj.name = RampObjectName;
  737. rampBoundObj.layer = LayerMask.NameToLayer("Skate Zones");
  738.  
  739. rampBoundObj.transform.parent = activeRampRoot;
  740. rampBoundObj.transform.position = meshToBoundTrans.position;
  741. rampBoundObj.transform.rotation = meshToBoundTrans.rotation;
  742. rampBoundObj.transform.localScale = meshToBoundTrans.localScale;
  743.  
  744. BoxCollider rampBound = rampBoundObj.AddComponent<BoxCollider>();
  745. rampBound.isTrigger = true;
  746. Bounds meshBounds = GetMeshBounds(meshToBound);
  747. rampBound.center = meshBounds.center;
  748. rampBound.size = meshBounds.size;
  749.  
  750. RampZone rampZone = rampBoundObj.AddComponent<RampZone>();
  751.  
  752. if (rampBound.size.y > VertHeightCutoff) { rampZone.rampType = RampZone.RampTypes.MegaRamp; }
  753. else if (rampBound.size.y > RampHeightCutoff) { rampZone.rampType = RampZone.RampTypes.Vert; }
  754. }
  755.  
  756. public static Bounds GetMeshBounds(Mesh mesh)
  757. {
  758. if (mesh == null)
  759. {
  760. Debug.LogError("GetMeshBounds: no mesh given");
  761. return new Bounds(Vector3.zero, Vector3.one);
  762. }
  763.  
  764. Vector3[] vertices = mesh.vertices;
  765. if (vertices.Length <= 0)
  766. {
  767. Debug.LogError("GetMeshBounds: mesh doesn't have vertices");
  768. return new Bounds(Vector3.zero, Vector3.one);
  769. }
  770.  
  771. Vector3 min, max;
  772. min = max = vertices[0];
  773. for (int i = 1; i < vertices.Length; i++)
  774. {
  775. Vector3 V = vertices[i];
  776. for (int n = 0; n < 3; n++)
  777. {
  778. if (V[n] > max[n]) { max[n] = V[n]; }
  779. if (V[n] < min[n]) { min[n] = V[n]; }
  780. }
  781. }
  782.  
  783. Bounds meshBounds = new Bounds();
  784. meshBounds.SetMinMax(min, max);
  785.  
  786. return meshBounds;
  787. }
  788. #endregion
  789.  
  790. #region Houdini Vert Addition
  791. void Houdini_ClearExistingVerts()
  792. {
  793. // Clear our vert tracking
  794. activeVertZones.Clear();
  795.  
  796. // Also, clear out any prior changes in the Houdini hierarchy
  797. Transform vertParent = activeHoudiniRoot.transform.Find(VertParentName);
  798. if (vertParent != null)
  799. {
  800. while (vertParent.childCount > 0)
  801. {
  802. Transform tempTrans = vertParent.GetChild(0);
  803. DestroyImmediate(tempTrans.gameObject);
  804. }
  805.  
  806. DestroyImmediate(vertParent.gameObject);
  807. }
  808. }
  809.  
  810. void Houdini_ProcessVerts()
  811. {
  812. if (activeHoudiniRoot == null) { return; }
  813.  
  814. // First, clear out any prior work done on it
  815. Houdini_ClearExistingVerts();
  816.  
  817. if (activeRailOrigins.Count == 0)
  818. {
  819. Debug.LogWarning("Skate Maker: Can't make verts until you've defined rails, sorry!");
  820. return;
  821. }
  822.  
  823. // Then set the clean object up to be vert-ified
  824. GameObject newVertRoot = new GameObject();
  825. newVertRoot.name = VertParentName;
  826. newVertRoot.transform.parent = activeHoudiniRoot.transform;
  827. newVertRoot.transform.localPosition = Vector3.zero;
  828. newVertRoot.transform.localRotation = Quaternion.identity;
  829. newVertRoot.transform.localScale = Vector3.one;
  830.  
  831. Transform activeVertRoot = newVertRoot.transform;
  832.  
  833. // Now, let's add some verts!
  834. VertZone lastNeighborVert = null;
  835. GrindableRail curRail = activeRailOrigins[0];
  836. int nextRailOriginIndex = 1;
  837. while (curRail != null && curRail.nextGrindable != null)
  838. {
  839. // First, extract some base truths about where the rail is and where it's facing
  840. Vector3 vertForward = curRail.transform.forward;
  841. //Vector3 vertUp = curRail.transform.up; (we don't use this because magazine ramps have to be tilted to line up right, but that does NOT mean we want them to generate a tilted vert volume by default)
  842. Vector3 vertUp = Vector3.up;
  843. vertForward -= Vector3.Project(vertForward, vertUp);
  844. vertForward.Normalize();
  845.  
  846. float railHeight = (curRail.transform.localPosition.y + curRail.nextGrindable.transform.localPosition.y) / 2f;
  847. Vector3 railDir = curRail.nextGrindable.transform.position - curRail.transform.position;
  848. float railWidth = railDir.magnitude;
  849. railDir.Normalize();
  850.  
  851. float vertHeight = railHeight + VertHeightFromRail;
  852. Vector3 vertBottomCenter = curRail.transform.position + (railDir * railWidth / 2f) - (vertUp * railHeight) + (vertForward * VertThickness / 2f);
  853. vertBottomCenter += vertUp * vertHeight / 2f;
  854.  
  855. // Now, create the vert zone!
  856. GameObject newVertZoneObj = new GameObject();
  857. newVertZoneObj.name = VertObjectName;
  858. newVertZoneObj.layer = LayerMask.NameToLayer("Skate Zones");
  859.  
  860. newVertZoneObj.transform.parent = activeVertRoot;
  861.  
  862. newVertZoneObj.transform.position = vertBottomCenter;
  863. newVertZoneObj.transform.LookAt(vertBottomCenter + vertForward, vertUp);
  864. newVertZoneObj.transform.localScale = new Vector3(railWidth, vertHeight, VertThickness);
  865.  
  866. VertZone newVertZone = newVertZoneObj.AddComponent<VertZone>();
  867. newVertZone.planeAdjustment = VertPlaneAdjustment;
  868.  
  869. BoxCollider newVertZoneCollider = newVertZoneObj.AddComponent<BoxCollider>();
  870. newVertZoneCollider.isTrigger = true;
  871.  
  872. // Now, link us to the last neighbor vert (if there was any), and move on
  873. if (lastNeighborVert != null)
  874. {
  875. Vector3 dirToNeighborVert = lastNeighborVert.VertPlaneCenter - newVertZone.VertPlaneCenter;
  876. dirToNeighborVert.Normalize();
  877.  
  878. if (Vector3.Dot(dirToNeighborVert, newVertZone.VertPlaneRight) > 0f)
  879. {
  880. newVertZone.rightNeighbor = lastNeighborVert;
  881. lastNeighborVert.leftNeighbor = newVertZone;
  882. }
  883. else
  884. {
  885. newVertZone.leftNeighbor = lastNeighborVert;
  886. lastNeighborVert.rightNeighbor = newVertZone;
  887. }
  888. }
  889.  
  890. lastNeighborVert = newVertZone;
  891.  
  892. // We're done! Now we just select the next rail
  893. curRail = curRail.nextGrindable;
  894. if (curRail.nextGrindable == null) { curRail = null; }
  895.  
  896. // If the current stretch of rail has run out, pop to the next rail origin (if we can)
  897. if (curRail == null && nextRailOriginIndex < activeRailOrigins.Count)
  898. {
  899. curRail = activeRailOrigins[nextRailOriginIndex];
  900. nextRailOriginIndex++;
  901.  
  902. // Clear this, since we're starting a new strip of rails (and thus they won't be a neighbor to current vert)
  903. lastNeighborVert = null;
  904. }
  905. }
  906. }
  907. #endregion
  908. }
  909. }
  910.  
Advertisement
Add Comment
Please, Sign In to add comment