ObjectPlacementPathManualConstructionSession.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. #if UNITY_EDITOR
  2. using UnityEngine;
  3. using UnityEditor;
  4. using System.Collections.Generic;
  5. namespace O3DWB
  6. {
  7. public class ObjectPlacementPathManualConstructionSession
  8. {
  9. #region Private Variables
  10. private ObjectPlacementPath _path;
  11. private ObjectPlacementPathSettings _pathSettings;
  12. private ObjectPlacementPathTileConnectionSettings _tileConnectionSettings;
  13. private List<ObjectPlacementPathTileConnectionGridCell> _tileConnectionGridCells;
  14. private ObjectPlacementPathManualConstructionSettings _manualConstructionSettings;
  15. private ObjectPlacementPathHeightAdjustmentSettings _heightAdjustmentSettings;
  16. private ObjectPlacementPathPaddingSettings _paddingSettings;
  17. private ObjectPlacementPathBorderSettings _borderSettings;
  18. private List<ObjectPlacementBoxStackSegment> _pathSegments;
  19. private ObjectPlacementExtensionPlane _pathExtensionPlane;
  20. private GameObject _startObject;
  21. private OrientedBox _startObjectHierarchyWorldOrientedBox;
  22. private float _tileConnectionXZSize;
  23. private ObjectPlacementPathManualHeightAdjuster _manualHeightAdjuster = new ObjectPlacementPathManualHeightAdjuster();
  24. private int _currentManualPathHeight = 1;
  25. private ObjectPlacementPathAutomaticRandomHeightAdjuster _automaticRandomHeightAdjuster = new ObjectPlacementPathAutomaticRandomHeightAdjuster();
  26. private ObjectPlacementPathAutomaticPatternHeightAdjuster _automaticPatternHeightAdjuster = new ObjectPlacementPathAutomaticPatternHeightAdjuster();
  27. private ObjectPlacementPathBorderApplyOperation _borderApplyOperation = new ObjectPlacementPathBorderApplyOperation();
  28. private ObjectPlacementPathTileConnectionDetector _tileConnectionDetector = new ObjectPlacementPathTileConnectionDetector();
  29. private PathNoTileConnectionsObjectPlacementDataCaculator _pathNoTileConnectionsObjectPlacementDataCalculator = new PathNoTileConnectionsObjectPlacementDataCaculator();
  30. private PathWithTileConnectionsObjectPlacementDataCalculator _pathWithTileConnectionsObjectPlacementDataCalculator = new PathWithTileConnectionsObjectPlacementDataCalculator();
  31. private bool _isActive = false;
  32. #endregion
  33. #region Private Properties
  34. private ObjectPlacementBoxStackSegment LastSegment { get { return _pathSegments.Count != 0 ? _pathSegments[_pathSegments.Count - 1] : null; } }
  35. private ObjectPlacementBoxStackSegment PenultimateSegment { get { return _pathSegments.Count > 1 ? _pathSegments[_pathSegments.Count - 2] : null; } }
  36. #endregion
  37. #region Public Properties
  38. public bool IsActive { get { return _isActive; } }
  39. #endregion
  40. #region Public Methods
  41. public void SetData(ObjectPlacementPathManualConstructionSessionData sessionData)
  42. {
  43. if(!_isActive)
  44. {
  45. _path = sessionData.Path;
  46. _pathSegments = sessionData.PathSegments;
  47. _pathExtensionPlane = sessionData.PathExtensionPlane;
  48. _tileConnectionGridCells = sessionData.TileConnectionGridCells;
  49. _startObject = sessionData.StartObject;
  50. _startObjectHierarchyWorldOrientedBox = _startObject.GetHierarchyWorldOrientedBox();
  51. _pathSettings = _path.Settings;
  52. _tileConnectionSettings = _pathSettings.TileConnectionSettings;
  53. _manualConstructionSettings = _pathSettings.ManualConstructionSettings;
  54. _heightAdjustmentSettings = _manualConstructionSettings.HeightAdjustmentSettings;
  55. _paddingSettings = _manualConstructionSettings.PaddingSettings;
  56. _borderSettings = _manualConstructionSettings.BorderSettings;
  57. _pathNoTileConnectionsObjectPlacementDataCalculator.Path = _path;
  58. _pathWithTileConnectionsObjectPlacementDataCalculator.Path = _path;
  59. }
  60. }
  61. public void Begin()
  62. {
  63. if (CanBegin())
  64. {
  65. _isActive = true;
  66. _currentManualPathHeight = 1;
  67. _pathSegments.Clear();
  68. if (_tileConnectionSettings.UseTileConnections) EstablshTileConnectionXZSize();
  69. CreateFirst2Segments();
  70. }
  71. }
  72. public List<ObjectPlacementData> End()
  73. {
  74. if(_isActive)
  75. {
  76. _isActive = false;
  77. RemoveSegmentsWithNoStacks();
  78. if (_tileConnectionSettings.UseTileConnections) return _pathWithTileConnectionsObjectPlacementDataCalculator.Calculate();
  79. else return _pathNoTileConnectionsObjectPlacementDataCalculator.Calculate();
  80. }
  81. return new List<ObjectPlacementData>();
  82. }
  83. public void Cancel()
  84. {
  85. _isActive = false;
  86. }
  87. public void ManualRaisePath()
  88. {
  89. if (CanManualRaiseOrLowerPath())
  90. {
  91. _currentManualPathHeight = _manualHeightAdjuster.Raise(_path, _currentManualPathHeight);
  92. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  93. }
  94. }
  95. public void ManualLowerPath()
  96. {
  97. if (CanManualRaiseOrLowerPath())
  98. {
  99. _currentManualPathHeight = _manualHeightAdjuster.Lower(_path, _currentManualPathHeight);
  100. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  101. }
  102. }
  103. public void UpdateForMouseMoveEvent()
  104. {
  105. if (_isActive) ExtendOrShrinkPathAlongExtensionPlane();
  106. }
  107. public void Attach2NewSegments()
  108. {
  109. if(_isActive)
  110. {
  111. RemoveSegmentsWithNoStacks();
  112. // First create the 2 new segments which will replace the current penultimate and last segments
  113. ObjectPlacementBoxStackSegment oldLastSegment = LastSegment;
  114. ObjectPlacementBoxStackSegment newPenultimateSegment = CreateNewSegment();
  115. CreateNewSegment();
  116. // We will want the first stack in the new penultimate segment to replace the last stack in 'LastSegment'.
  117. // Otherwise, when this function is called, an additional box will suddenly appear in the scene apparently
  118. // out of nowhere and it doesn't look very good. Not to mention that it can be quite confusing :).
  119. oldLastSegment.Shrink(1);
  120. newPenultimateSegment.Extend(1);
  121. newPenultimateSegment.SetExtensionDirection(oldLastSegment.ExtensionDirection);
  122. if (CanRotateObjectsToFollowPath())
  123. {
  124. newPenultimateSegment.SetRotationForAllStacks(oldLastSegment.StackRotation);
  125. MakeSegmentFollowPath(LastSegment);
  126. }
  127. newPenultimateSegment.ConnectFirstStackToLastStackInSegment(oldLastSegment, CalculateSegmentConnectionOffset(newPenultimateSegment, oldLastSegment));
  128. AdjustHeightForEntireSegment(PenultimateSegment);
  129. UpdateStackOverlapDataForLast2Segments();
  130. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  131. if (_tileConnectionSettings.UseTileConnections) UpdateTileConnectionInformation();
  132. }
  133. }
  134. public void RemoveLast2Segments()
  135. {
  136. if(_isActive)
  137. {
  138. _pathSegments.RemoveLast();
  139. _pathSegments.RemoveLast();
  140. if (_pathSegments.Count == 0) End();
  141. }
  142. }
  143. public void OnExcludeCornersSettingsChanged()
  144. {
  145. if (_isActive && !_tileConnectionSettings.UseTileConnections)
  146. {
  147. ReconnectAllSegments();
  148. HandleStackOverlapForAllStacksInAllSegments();
  149. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  150. SceneView.RepaintAll();
  151. }
  152. }
  153. public void OnRotateObjectsToFollowPathSettingsChanged()
  154. {
  155. if (_isActive && !_tileConnectionSettings.UseTileConnections)
  156. {
  157. AdjustRotationForAllSegments();
  158. ReconnectAllSegments();
  159. HandleStackOverlapForAllStacksInAllSegments();
  160. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  161. SceneView.RepaintAll();
  162. }
  163. }
  164. public void OnPaddingSettingsChanged()
  165. {
  166. if (_isActive && !_tileConnectionSettings.UseTileConnections)
  167. {
  168. ObjectPlacementBoxStackSegmentActions.SetPaddingForSegments(_pathSegments, _paddingSettings.PaddingAlongExtensionPlane, _paddingSettings.PaddingAlongGrowDirection);
  169. ReconnectAllSegments();
  170. HandleStackOverlapForAllStacksInAllSegments();
  171. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  172. SceneView.RepaintAll();
  173. }
  174. }
  175. public void OnBorderSettingsChanged()
  176. {
  177. if (_isActive)
  178. {
  179. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  180. else ObjectPlacementBoxStackSegmentActions.ClearHideFlagsForAllStacksInSegments(_pathSegments, ObjectPlacementBoxHideFlags.PathApplyBorders);
  181. SceneView.RepaintAll();
  182. }
  183. }
  184. public void OnHeightAdjustmentModeChanged()
  185. {
  186. if (_isActive)
  187. {
  188. AdjustHeightForAllStacksInPath();
  189. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  190. SceneView.RepaintAll();
  191. }
  192. }
  193. public void OnAutomaticRandomHeightAdjustmentSettingsChanged()
  194. {
  195. if (_isActive)
  196. {
  197. AdjustHeightForAllStacksInPath();
  198. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  199. SceneView.RepaintAll();
  200. }
  201. }
  202. public void OnAutomaticPatternHeightAdjustmentSettingsChanged()
  203. {
  204. if (_isActive)
  205. {
  206. AdjustHeightForAllStacksInPath();
  207. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  208. SceneView.RepaintAll();
  209. }
  210. }
  211. public void OnHeightPatternRemoved()
  212. {
  213. if(_isActive && _heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticPattern)
  214. {
  215. ObjectPlacementPathHeightPattern activePattern = ObjectPlacementPathHeightPatternDatabase.Get().ActivePattern;
  216. if (activePattern != null)
  217. {
  218. AdjustHeightForAllStacksInPath();
  219. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  220. }
  221. else
  222. {
  223. Debug.LogWarning("There is no height pattern currently available. Path construction was cancelled.");
  224. End();
  225. }
  226. SceneView.RepaintAll();
  227. }
  228. }
  229. public void OnNewHeightPatternWasActivated()
  230. {
  231. if (_isActive && _heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticPattern)
  232. {
  233. AdjustHeightForAllStacksInPath();
  234. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  235. SceneView.RepaintAll();
  236. }
  237. }
  238. #endregion
  239. #region Private Methods
  240. private bool CanBegin()
  241. {
  242. return !_isActive && IsSessionDataReady();
  243. }
  244. private bool CanRotateObjectsToFollowPath()
  245. {
  246. return _manualConstructionSettings.RotateObjectsToFollowPath && !_tileConnectionSettings.UseTileConnections;
  247. }
  248. private bool CanUsePadding()
  249. {
  250. return !_tileConnectionSettings.UseTileConnections;
  251. }
  252. private bool CanExcludeCorners()
  253. {
  254. return _manualConstructionSettings.ExcludeCorners && !_tileConnectionSettings.UseTileConnections;
  255. }
  256. private bool CanManualRaiseOrLowerPath()
  257. {
  258. return _isActive && _heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.Manual;
  259. }
  260. private void EstablshTileConnectionXZSize()
  261. {
  262. if(_pathSettings.TileConnectionSettings.UsesSprites())
  263. {
  264. Vector3 scaledSize = _startObjectHierarchyWorldOrientedBox.ScaledSize;
  265. _tileConnectionXZSize = (scaledSize.x + scaledSize.y) * 0.5f;
  266. _pathWithTileConnectionsObjectPlacementDataCalculator.TileConnectionXZSize = _tileConnectionXZSize;
  267. }
  268. else
  269. {
  270. Vector3 scaledSize = _startObjectHierarchyWorldOrientedBox.ScaledSize;
  271. _tileConnectionXZSize = (scaledSize.x + scaledSize.z) * 0.5f;
  272. _pathWithTileConnectionsObjectPlacementDataCalculator.TileConnectionXZSize = _tileConnectionXZSize;
  273. }
  274. }
  275. private bool IsSessionDataReady()
  276. {
  277. bool isReady = (_path != null && _pathSegments != null && _startObject != null && _pathExtensionPlane != null);
  278. if (!isReady) return false;
  279. if (_heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticPattern &&
  280. ObjectPlacementPathHeightPatternDatabase.Get().ActivePattern == null)
  281. {
  282. Debug.LogWarning("Can not begin path construction because the automatic pattern height adjustment mode is selected but there is no active pattern.");
  283. isReady = false;
  284. }
  285. if (_tileConnectionSettings.UseTileConnections && !_tileConnectionSettings.DoAllTileConnectionsHavePrefabAssociated(true))
  286. {
  287. Debug.LogWarning("You are using tile connections, but not all tiles have an associated prefab. Please associate a prefab with each tile in order to being path construction.");
  288. isReady = false;
  289. }
  290. OrientedBox hierarchyWorldOrientedBox = _startObject.GetHierarchyWorldOrientedBox();
  291. float absSizeRight = hierarchyWorldOrientedBox.GetRotatedAndScaledSizeAlongDirection(_pathExtensionPlane.RightAxis);
  292. float absSizeLook = hierarchyWorldOrientedBox.GetRotatedAndScaledSizeAlongDirection(_pathExtensionPlane.LookAxis);
  293. if(absSizeRight < 1e-4f || absSizeLook < 1e-4f)
  294. {
  295. Debug.LogWarning("Can not begin path construction because the object has a 0 size component along the extention plane axes.");
  296. isReady = false;
  297. }
  298. return isReady;
  299. }
  300. private void RemoveSegmentsWithNoStacks()
  301. {
  302. _pathSegments.RemoveAll(item => item.NumberOfStacks == 0);
  303. }
  304. private void CreateFirst2Segments()
  305. {
  306. CreateNewSegment();
  307. CreateNewSegment();
  308. Vector3 camLookAxis = SceneViewCamera.Camera.transform.forward;
  309. Vector3 penultimateSegmentDir = _pathExtensionPlane.LookAxis;
  310. if(Mathf.Abs(Vector3.Dot(camLookAxis, _pathExtensionPlane.LookAxis)) <
  311. Mathf.Abs(Vector3.Dot(camLookAxis, _pathExtensionPlane.RightAxis)))
  312. {
  313. penultimateSegmentDir = _pathExtensionPlane.RightAxis;
  314. }
  315. PenultimateSegment.SetFirstStackBasePosition(_startObjectHierarchyWorldOrientedBox.Center);
  316. PenultimateSegment.SetExtensionDirection(penultimateSegmentDir);
  317. PenultimateSegment.Extend(1);
  318. AdjustHeightForEntireSegment(PenultimateSegment);
  319. }
  320. private ObjectPlacementBoxStackSegment CreateNewSegment()
  321. {
  322. var newSegment = new ObjectPlacementBoxStackSegment();
  323. newSegment.SetRotationForAllStacks(_startObjectHierarchyWorldOrientedBox.Rotation);
  324. newSegment.SetBoxSizeForAllStacks(GetSegmentBoxSize());
  325. newSegment.SetExtensionDirection(_pathExtensionPlane.LookAxis);
  326. newSegment.SetGrowAxis(_pathExtensionPlane.UpAxis);
  327. if (CanUsePadding())
  328. {
  329. newSegment.SetPaddingAlongStackGrowDirection(_paddingSettings.PaddingAlongGrowDirection);
  330. newSegment.SetPaddingAlongExtensionDirection(_paddingSettings.PaddingAlongExtensionPlane);
  331. }
  332. _pathSegments.Add(newSegment);
  333. return newSegment;
  334. }
  335. private Vector3 GetSegmentBoxSize()
  336. {
  337. if (_tileConnectionSettings.UseTileConnections)
  338. {
  339. if (_pathSettings.TileConnectionSettings.UsesSprites()) return new Vector3(_tileConnectionXZSize, _tileConnectionXZSize, _startObjectHierarchyWorldOrientedBox.ScaledSize.z);
  340. return new Vector3(_tileConnectionXZSize, _startObjectHierarchyWorldOrientedBox.ScaledSize.y, _tileConnectionXZSize);
  341. }
  342. else return _startObjectHierarchyWorldOrientedBox.ScaledSize;
  343. }
  344. private void ExtendOrShrinkPathAlongExtensionPlane()
  345. {
  346. // Construct a new extension plane in order to take into account the block's Y offset. Otherwise,
  347. // it becomes harder to control the block extension.
  348. Plane extensionPlane = _pathExtensionPlane.Plane;
  349. Vector3 pointOnBlockExtensionPlane = _pathExtensionPlane.PlaneQuad.Center;
  350. pointOnBlockExtensionPlane += extensionPlane.normal * _manualConstructionSettings.OffsetAlongGrowDirection;
  351. extensionPlane = new Plane(extensionPlane.normal, pointOnBlockExtensionPlane);
  352. // We will need to adjust the last 2 segments using the intersection point between the mouse cursor
  353. // and the extension plane, so the first thing that we will do is find this intersection point. This
  354. // intersection point will essentially help us visualize a triangle whose adjacent sides represent the
  355. // extension directions of the 2 segments. The hypotenuse represents the vector which goes from the
  356. // first stack in the penultimate segment to the last stack in the last segment.
  357. Vector3 extensionPlaneIntersectionPoint;
  358. if (MouseCursor.Instance.IntersectsPlane(extensionPlane, out extensionPlaneIntersectionPoint))
  359. {
  360. // We will need to adjust the number of stacks in each segment based on the length of the triangle's
  361. // adjacent sides. We will start with the penultimate segment and construct a vector which goes from
  362. // the first stack in the penultimate segment to the intersection point with the plane. Projecting this
  363. // vector on the extension direction of the penultimate segment will give us the length of the adjacent
  364. // side. This length is then used to calculate the number of stacks in the penultimate segment.
  365. Vector3 toIntersectionPoint = extensionPlaneIntersectionPoint - _pathExtensionPlane.Plane.ProjectPoint(PenultimateSegment.FirstStackBasePosition);
  366. if (_pathSegments.Count == 2 && PenultimateSegment.NumberOfStacks == 1 && !_pathSettings.TileConnectionSettings.UseTileConnections)
  367. {
  368. var extensionPlaneAxes = new List<Vector3> { _pathExtensionPlane.RightAxis, -_pathExtensionPlane.RightAxis, _pathExtensionPlane.LookAxis, -_pathExtensionPlane.LookAxis };
  369. int mostAlignedVector = toIntersectionPoint.GetIndexOfMostAlignedVector(extensionPlaneAxes);
  370. if (mostAlignedVector >= 0)
  371. {
  372. PenultimateSegment.SetExtensionDirection(extensionPlaneAxes[mostAlignedVector]);
  373. }
  374. }
  375. else if (!PenultimateSegment.ExtensionDirection.IsPointingInSameGeneralDirection(toIntersectionPoint)) PenultimateSegment.ReverseExtensionDirection();
  376. // Calculate the number of stacks in the penultimate segment
  377. float adjacentSideLength = PenultimateSegment.ExtensionDirection.GetAbsDot(toIntersectionPoint);
  378. float numberOfStacks = adjacentSideLength / (PenultimateSegment.GetBoxSizeAlongNormalizedDirection(PenultimateSegment.ExtensionDirection) + _paddingSettings.PaddingAlongExtensionPlane);
  379. int integerNumberOfStacks = (int)numberOfStacks + 1;
  380. int currentNumberOfStacks = PenultimateSegment.NumberOfStacks;
  381. int deltaNumberOfStacks = integerNumberOfStacks - currentNumberOfStacks;
  382. if (deltaNumberOfStacks > 0 || (deltaNumberOfStacks == 0 && _heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticPattern))
  383. {
  384. PenultimateSegment.Extend(deltaNumberOfStacks);
  385. AdjustHeightForStackRangeInSegment(PenultimateSegment, currentNumberOfStacks);
  386. }
  387. else PenultimateSegment.Shrink(Mathf.Abs(deltaNumberOfStacks));
  388. // We will have to do the same thing for the last segment. However, this time we will first detect its extension direction. The extension direction
  389. // is one of the extension plane's axes (right or look). The one we choose has to be the one which is not aligned with the penultimate segment's
  390. // extension direction (i.e. it has to be perpendicular to it).
  391. toIntersectionPoint = extensionPlaneIntersectionPoint - _pathExtensionPlane.Plane.ProjectPoint(PenultimateSegment.LastStackBasePosition);
  392. Vector3 extensionPlaneRight = _pathExtensionPlane.RightAxis;
  393. Vector3 extensionPlaneLook = _pathExtensionPlane.LookAxis;
  394. Vector3 lastSegmentExtensionDirection = extensionPlaneRight;
  395. if (lastSegmentExtensionDirection.IsAlignedWith(PenultimateSegment.ExtensionDirection)) lastSegmentExtensionDirection = extensionPlaneLook;
  396. if (!lastSegmentExtensionDirection.IsPointingInSameGeneralDirection(toIntersectionPoint)) lastSegmentExtensionDirection *= -1.0f;
  397. LastSegment.SetExtensionDirection(lastSegmentExtensionDirection);
  398. // Calculate the number of stacks in the segment.
  399. // Note: We no longer add 1 to the integer number of stacks because that stack is actually the last stack in the penultimate segment
  400. numberOfStacks = toIntersectionPoint.magnitude / (LastSegment.GetBoxSizeAlongNormalizedDirection(LastSegment.ExtensionDirection) + _paddingSettings.PaddingAlongExtensionPlane);
  401. integerNumberOfStacks = (int)numberOfStacks;
  402. currentNumberOfStacks = LastSegment.NumberOfStacks;
  403. deltaNumberOfStacks = integerNumberOfStacks - currentNumberOfStacks;
  404. if (deltaNumberOfStacks > 0 || (deltaNumberOfStacks == 0 && _heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticPattern))
  405. {
  406. LastSegment.Extend(deltaNumberOfStacks);
  407. AdjustHeightForStackRangeInSegment(LastSegment, currentNumberOfStacks);
  408. }
  409. else LastSegment.Shrink(Mathf.Abs(deltaNumberOfStacks));
  410. if (CanRotateObjectsToFollowPath()) MakeSegmentFollowPath(LastSegment);
  411. LastSegment.ConnectFirstStackToLastStackInSegment(PenultimateSegment, CalculateSegmentConnectionOffset(LastSegment, PenultimateSegment));
  412. UpdateStackOverlapDataForLast2Segments();
  413. if (_borderSettings.UseBorders) _borderApplyOperation.ApplyBordersToAllPathSegments(_pathSegments, _borderSettings);
  414. if (_tileConnectionSettings.UseTileConnections) UpdateTileConnectionInformation();
  415. SceneView.RepaintAll();
  416. }
  417. }
  418. private void MakeSegmentFollowPath(ObjectPlacementBoxStackSegment segment)
  419. {
  420. float angleFromFirstSegment = Vector3.Angle(_pathSegments[0].ExtensionDirection, segment.ExtensionDirection);
  421. Quaternion rotation = Quaternion.AngleAxis(angleFromFirstSegment, _pathExtensionPlane.Plane.normal);
  422. segment.SetRotationForAllStacks(rotation * _startObjectHierarchyWorldOrientedBox.Rotation);
  423. }
  424. private Vector3 CalculateSegmentConnectionOffset(ObjectPlacementBoxStackSegment sourceSegment, ObjectPlacementBoxStackSegment destinationSegment)
  425. {
  426. if (!CanExcludeCorners()) return CalculateSegmentConnectionOffsetForNoCornerExclusion(sourceSegment, destinationSegment);
  427. else
  428. {
  429. // Note: Corner exclusion is applied only if the 2 segments are perpendicular.
  430. if (sourceSegment.ExtensionDirection.IsPerpendicularTo(destinationSegment.ExtensionDirection)) return CalculateSegmentConnectionOffsetForCornerExclusion(sourceSegment, destinationSegment);
  431. else return CalculateSegmentConnectionOffsetForNoCornerExclusion(sourceSegment, destinationSegment);
  432. }
  433. }
  434. private Vector3 CalculateSegmentConnectionOffsetForNoCornerExclusion(ObjectPlacementBoxStackSegment sourceSegment, ObjectPlacementBoxStackSegment destinationSegment)
  435. {
  436. // No offset if the destination segment doesn't have any stacks
  437. if (destinationSegment.NumberOfStacks == 0) return Vector3.zero;
  438. bool srcPointsInSameDirAsDestination;
  439. bool srcExtensionDirAlignedWithDestDir = sourceSegment.ExtensionDirection.IsAlignedWith(destinationSegment.ExtensionDirection, out srcPointsInSameDirAsDestination);
  440. float multiplicationSign = 1.0f;
  441. if(srcExtensionDirAlignedWithDestDir)
  442. {
  443. if (!srcPointsInSameDirAsDestination) multiplicationSign *= -1.0f;
  444. }
  445. float destBoxSizeAlongDestExtensionDir = destinationSegment.GetBoxSizeAlongNormalizedDirection(destinationSegment.ExtensionDirection);
  446. float srcBoxSizeAlongDestExtensionDir = sourceSegment.GetBoxSizeAlongNormalizedDirection(destinationSegment.ExtensionDirection);
  447. Vector3 offsetAlongDestExtensionDir = destinationSegment.ExtensionDirection * (destBoxSizeAlongDestExtensionDir - srcBoxSizeAlongDestExtensionDir) * 0.5f;
  448. float destBoxSizeAlongSrcExtensionDir = destinationSegment.GetBoxSizeAlongNormalizedDirection(sourceSegment.ExtensionDirection);
  449. float srcBoxSizeAlongSrcExtensionDir = sourceSegment.GetBoxSizeAlongNormalizedDirection(sourceSegment.ExtensionDirection);
  450. Vector3 offsetAlongSourceExtensionDir = multiplicationSign * sourceSegment.ExtensionDirection * ((srcBoxSizeAlongSrcExtensionDir + destBoxSizeAlongSrcExtensionDir) * 0.5f + _paddingSettings.PaddingAlongExtensionPlane);
  451. return offsetAlongDestExtensionDir + offsetAlongSourceExtensionDir;
  452. }
  453. private Vector3 CalculateSegmentConnectionOffsetForCornerExclusion(ObjectPlacementBoxStackSegment sourceSegment, ObjectPlacementBoxStackSegment destinationSegment)
  454. {
  455. bool srcPointsInSameDirAsDestination;
  456. bool srcExtensionDirAlignedWithDestDir = sourceSegment.ExtensionDirection.IsAlignedWith(destinationSegment.ExtensionDirection, out srcPointsInSameDirAsDestination);
  457. float multiplicationSign = 1.0f;
  458. if (srcExtensionDirAlignedWithDestDir)
  459. {
  460. if (!srcPointsInSameDirAsDestination) multiplicationSign *= -1.0f;
  461. }
  462. float destBoxSizeAlongDestExtensionDir = destinationSegment.GetBoxSizeAlongNormalizedDirection(destinationSegment.ExtensionDirection);
  463. float srcBoxSizeAlongDestExtensionDir = sourceSegment.GetBoxSizeAlongNormalizedDirection(destinationSegment.ExtensionDirection);
  464. Vector3 offsetAlongDestExtensionDir = destinationSegment.ExtensionDirection * Mathf.Abs((destBoxSizeAlongDestExtensionDir + srcBoxSizeAlongDestExtensionDir) * 0.5f);
  465. float destBoxSizeAlongSrcExtensionDir = destinationSegment.GetBoxSizeAlongNormalizedDirection(sourceSegment.ExtensionDirection);
  466. float srcBoxSizeAlongSrcExtensionDir = sourceSegment.GetBoxSizeAlongNormalizedDirection(sourceSegment.ExtensionDirection);
  467. Vector3 offsetAlongSourceExtensionDir = multiplicationSign * sourceSegment.ExtensionDirection * (srcBoxSizeAlongSrcExtensionDir + destBoxSizeAlongSrcExtensionDir) * 0.5f;
  468. return offsetAlongDestExtensionDir + offsetAlongSourceExtensionDir;
  469. }
  470. private void AdjustHeightForEntireSegment(ObjectPlacementBoxStackSegment segment)
  471. {
  472. ObjectPlacementPathHeightAdjustmentMode heightAdjustmentMode = _heightAdjustmentSettings.HeightAdjustmentMode;
  473. if (heightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.Manual) _manualHeightAdjuster.AdjustSegmentHeight(segment, _currentManualPathHeight);
  474. else if (heightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticRandom) _automaticRandomHeightAdjuster.AdjustSegmentHeight(segment, _heightAdjustmentSettings.AutomaticRandomHeightAdjustmentSettings);
  475. else _automaticPatternHeightAdjuster.AdjustSegmentHeight(segment, _pathSegments, _heightAdjustmentSettings.AutomaticPatternHeightAdjustmentSettings, ObjectPlacementPathHeightPatternDatabase.Get().ActivePattern);
  476. }
  477. private void AdjustHeightForStackRangeInSegment(ObjectPlacementBoxStackSegment segment, int indexOfFirstStackToAdjust)
  478. {
  479. ObjectPlacementPathHeightAdjustmentMode heightAdjustmentMode = _heightAdjustmentSettings.HeightAdjustmentMode;
  480. if (heightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.Manual) _manualHeightAdjuster.AdjustSegmentHeight(segment, indexOfFirstStackToAdjust, _currentManualPathHeight);
  481. else if (heightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticRandom) _automaticRandomHeightAdjuster.AdjustSegmentHeight(segment, indexOfFirstStackToAdjust, _heightAdjustmentSettings.AutomaticRandomHeightAdjustmentSettings);
  482. else _automaticPatternHeightAdjuster.AdjustSegmentHeight(segment, _pathSegments, _heightAdjustmentSettings.AutomaticPatternHeightAdjustmentSettings, ObjectPlacementPathHeightPatternDatabase.Get().ActivePattern);
  483. }
  484. private void ReconnectAllSegments()
  485. {
  486. for(int segmentIndex = 1; segmentIndex < _pathSegments.Count; ++segmentIndex)
  487. {
  488. ObjectPlacementBoxStackSegment currentSegment = _pathSegments[segmentIndex];
  489. ObjectPlacementBoxStackSegment previousSegment = _pathSegments[segmentIndex - 1];
  490. currentSegment.ConnectFirstStackToLastStackInSegment(previousSegment, CalculateSegmentConnectionOffset(currentSegment, previousSegment));
  491. }
  492. }
  493. private void HandleStackOverlapForAllStacksInAllSegments()
  494. {
  495. for (int segmentIndex = 0; segmentIndex < _pathSegments.Count; ++segmentIndex)
  496. {
  497. _pathSegments[segmentIndex].MarkAllStacksAsNotOverlapped();
  498. ObjectPlacementBoxStackActions.MarkStacksAsOverlapped(ObjectPlacementPathOverlappedStackDetection.GetOverlappedStacksInSegment(segmentIndex, _pathSegments));
  499. }
  500. }
  501. private void AdjustRotationForAllSegments()
  502. {
  503. foreach(ObjectPlacementBoxStackSegment segment in _pathSegments)
  504. {
  505. if (_manualConstructionSettings.RotateObjectsToFollowPath)
  506. {
  507. if (_pathSegments.Count >= 2 && segment == _pathSegments[0] && segment.NumberOfStacks == 1 && _pathSegments[1].NumberOfStacks >= 1)
  508. {
  509. segment.LookStacksAlongDirection(_pathSegments[1].ExtensionDirection);
  510. }
  511. else segment.LookStacksAlongExtensionDirection();
  512. }
  513. else segment.LookStacksAlongDirection(_pathSegments[0].ExtensionDirection);
  514. }
  515. }
  516. private void AdjustHeightForAllStacksInPath()
  517. {
  518. if (_heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.Manual) _manualHeightAdjuster.AdjustHeightForSegments(_pathSegments, _currentManualPathHeight);
  519. else if (_heightAdjustmentSettings.HeightAdjustmentMode == ObjectPlacementPathHeightAdjustmentMode.AutomaticRandom) _automaticRandomHeightAdjuster.AdjustHeightForSegments(_pathSegments, _heightAdjustmentSettings.AutomaticRandomHeightAdjustmentSettings);
  520. else _automaticPatternHeightAdjuster.AdjustHeightForAllSegmentsInPath(_pathSegments, _heightAdjustmentSettings.AutomaticPatternHeightAdjustmentSettings, ObjectPlacementPathHeightPatternDatabase.Get().ActivePattern);
  521. }
  522. private void UpdateTileConnectionInformation()
  523. {
  524. List<ObjectPlacementPathTileConnectionGridCell> tileConnectionGridCells = _tileConnectionDetector.Detect(_path, _tileConnectionXZSize);
  525. if (tileConnectionGridCells.Count == 0) return;
  526. _tileConnectionGridCells.Clear();
  527. _tileConnectionGridCells.AddRange(tileConnectionGridCells);
  528. bool usingSprites = _pathSettings.TileConnectionSettings.UsesSprites();
  529. List<OrientedBox> tileConnectionPrefabWorldOrientedBoxesExceptAutofill = PrefabQueries.GetHierarchyWorldOrientedBoxesForAllPrefabs(_tileConnectionSettings.GetAllTileConnectionPrefabs(true));
  530. foreach (ObjectPlacementPathTileConnectionGridCell tileConnectionGridCell in _tileConnectionGridCells)
  531. {
  532. ObjectPlacementPathTileConnectionTypeSettings tileConnectionTypeSettings = _tileConnectionSettings.GetSettingsForTileConnectionType(tileConnectionGridCell.TileConnectionType);
  533. tileConnectionGridCell.TileConnectionStack.SetBoxSize(GetTileConnectionSize(tileConnectionPrefabWorldOrientedBoxesExceptAutofill[(int)tileConnectionTypeSettings.TileConnectionType], usingSprites));
  534. tileConnectionGridCell.TileConnectionStack.PlaceOnPlane(_pathExtensionPlane.Plane);
  535. }
  536. }
  537. private Vector3 GetTileConnectionSize(OrientedBox tilePrefabOOBB, bool usingSprites)
  538. {
  539. Vector3 boxSize = GetSegmentBoxSize();
  540. if (usingSprites) boxSize.z = 0.0f;
  541. else boxSize.y = tilePrefabOOBB.ScaledSize.y;
  542. return boxSize;
  543. }
  544. private void UpdateStackOverlapDataForLast2Segments()
  545. {
  546. PenultimateSegment.MarkAllStacksAsNotOverlapped();
  547. LastSegment.MarkAllStacksAsNotOverlapped();
  548. ObjectPlacementBoxStackActions.MarkStacksAsOverlapped(ObjectPlacementPathOverlappedStackDetection.GetOverlappedStacksInSegment(_pathSegments.Count - 2, _pathSegments));
  549. ObjectPlacementBoxStackActions.MarkStacksAsOverlapped(ObjectPlacementPathOverlappedStackDetection.GetOverlappedStacksInSegment(_pathSegments.Count - 1, _pathSegments));
  550. }
  551. #endregion
  552. }
  553. }
  554. #endif