LineManager.cs 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771
  1. /*
  2. Copyright (c) 2021 Omar Duarte
  3. Unauthorized copying of this file, via any medium is strictly prohibited.
  4. Writen by Omar Duarte, 2021.
  5. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  6. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  7. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  8. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  9. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  10. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  11. THE SOFTWARE.
  12. */
  13. using System.Linq;
  14. using UnityEngine;
  15. namespace PluginMaster
  16. {
  17. #region DATA & SETTINGS
  18. [System.Serializable]
  19. public class LineSettings : PaintOnSurfaceToolSettings, IPaintToolSettings
  20. {
  21. public enum SpacingType { BOUNDS, CONSTANT }
  22. [SerializeField] private Vector3 _projectionDirection = Vector3.down;
  23. [SerializeField] private bool _objectsOrientedAlongTheLine = true;
  24. [SerializeField] private AxesUtils.Axis _axisOrientedAlongTheLine = AxesUtils.Axis.X;
  25. [SerializeField] private SpacingType _spacingType = SpacingType.BOUNDS;
  26. [SerializeField] private float _gapSize = 0f;
  27. [SerializeField] private float _spacing = 10f;
  28. public Vector3 projectionDirection
  29. {
  30. get => _projectionDirection;
  31. set
  32. {
  33. if (_projectionDirection == value) return;
  34. _projectionDirection = value;
  35. OnDataChanged();
  36. }
  37. }
  38. public void UpdateProjectDirection(Vector3 value) => _projectionDirection = value;
  39. public bool objectsOrientedAlongTheLine
  40. {
  41. get => _objectsOrientedAlongTheLine;
  42. set
  43. {
  44. if (_objectsOrientedAlongTheLine == value) return;
  45. _objectsOrientedAlongTheLine = value;
  46. OnDataChanged();
  47. }
  48. }
  49. public AxesUtils.Axis axisOrientedAlongTheLine
  50. {
  51. get => _axisOrientedAlongTheLine;
  52. set
  53. {
  54. if (_axisOrientedAlongTheLine == value) return;
  55. _axisOrientedAlongTheLine = value;
  56. OnDataChanged();
  57. }
  58. }
  59. public SpacingType spacingType
  60. {
  61. get => _spacingType;
  62. set
  63. {
  64. if (_spacingType == value) return;
  65. _spacingType = value;
  66. OnDataChanged();
  67. }
  68. }
  69. public float spacing
  70. {
  71. get => _spacing;
  72. set
  73. {
  74. value = Mathf.Max(value, 0.01f);
  75. if (_spacing == value) return;
  76. _spacing = value;
  77. OnDataChanged();
  78. }
  79. }
  80. public float gapSize
  81. {
  82. get => _gapSize;
  83. set
  84. {
  85. if (_gapSize == value) return;
  86. _gapSize = value;
  87. OnDataChanged();
  88. }
  89. }
  90. [SerializeField] private PaintToolSettings _paintTool = new PaintToolSettings();
  91. public Transform parent { get => _paintTool.parent; set => _paintTool.parent = value; }
  92. public bool overwritePrefabLayer
  93. { get => _paintTool.overwritePrefabLayer; set => _paintTool.overwritePrefabLayer = value; }
  94. public int layer { get => _paintTool.layer; set => _paintTool.layer = value; }
  95. public bool autoCreateParent { get => _paintTool.autoCreateParent; set => _paintTool.autoCreateParent = value; }
  96. public bool setSurfaceAsParent { get => _paintTool.setSurfaceAsParent; set => _paintTool.setSurfaceAsParent = value; }
  97. public bool createSubparentPerPalette
  98. {
  99. get => _paintTool.createSubparentPerPalette;
  100. set => _paintTool.createSubparentPerPalette = value;
  101. }
  102. public bool createSubparentPerTool
  103. {
  104. get => _paintTool.createSubparentPerTool;
  105. set => _paintTool.createSubparentPerTool = value;
  106. }
  107. public bool createSubparentPerBrush
  108. {
  109. get => _paintTool.createSubparentPerBrush;
  110. set => _paintTool.createSubparentPerBrush = value;
  111. }
  112. public bool createSubparentPerPrefab
  113. {
  114. get => _paintTool.createSubparentPerPrefab;
  115. set => _paintTool.createSubparentPerPrefab = value;
  116. }
  117. public bool overwriteBrushProperties
  118. { get => _paintTool.overwriteBrushProperties; set => _paintTool.overwriteBrushProperties = value; }
  119. public BrushSettings brushSettings => _paintTool.brushSettings;
  120. public LineSettings() : base() => _paintTool.OnDataChanged += DataChanged;
  121. public override void DataChanged()
  122. {
  123. base.DataChanged();
  124. UpdateStroke();
  125. UnityEditor.SceneView.RepaintAll();
  126. }
  127. protected virtual void UpdateStroke() => PWBIO.UpdateStroke();
  128. public override void Copy(IToolSettings other)
  129. {
  130. var otherLineSettings = other as LineSettings;
  131. if (otherLineSettings == null) return;
  132. base.Copy(other);
  133. _projectionDirection = otherLineSettings._projectionDirection;
  134. _objectsOrientedAlongTheLine = otherLineSettings._objectsOrientedAlongTheLine;
  135. _axisOrientedAlongTheLine = otherLineSettings._axisOrientedAlongTheLine;
  136. _spacingType = otherLineSettings._spacingType;
  137. _spacing = otherLineSettings._spacing;
  138. _paintTool.Copy(otherLineSettings._paintTool);
  139. _gapSize = otherLineSettings._gapSize;
  140. }
  141. }
  142. [System.Serializable]
  143. public class LineSegment
  144. {
  145. public enum SegmentType { STRAIGHT, CURVE }
  146. public SegmentType type = SegmentType.CURVE;
  147. [SerializeField]
  148. private System.Collections.Generic.List<LinePoint> _linePoints = new System.Collections.Generic.List<LinePoint>();
  149. public Vector3[] points => _linePoints.Select(p => p.position).ToArray();
  150. public float[] scales => _linePoints.Select(p => p.scale).ToArray();
  151. public void AddPoint(Vector3 position, float scale = 0.25f) => _linePoints.Add(new LinePoint(position, scale));
  152. }
  153. [System.Serializable]
  154. public class LinePoint : ControlPoint
  155. {
  156. public LineSegment.SegmentType type = LineSegment.SegmentType.CURVE;
  157. public float scale = 0.25f;
  158. public LinePoint() { }
  159. public LinePoint(Vector3 position = new Vector3(), float scale = 0.25f,
  160. LineSegment.SegmentType type = LineSegment.SegmentType.CURVE)
  161. : base(position) => (this.type, this.scale) = (type, scale);
  162. public LinePoint(LinePoint other) : base((ControlPoint)other) => Copy(other);
  163. public override void Copy(ControlPoint other)
  164. {
  165. base.Copy(other);
  166. var otherLinePoint = other as LinePoint;
  167. if (otherLinePoint == null) return;
  168. type = otherLinePoint.type;
  169. scale = otherLinePoint.scale;
  170. }
  171. }
  172. [System.Serializable]
  173. public class LineData : PersistentData<LineToolName, LineSettings, LinePoint>
  174. {
  175. [SerializeField] private bool _closed = false;
  176. private float _lenght = 0f;
  177. private System.Collections.Generic.List<Vector3> _midpoints = new System.Collections.Generic.List<Vector3>();
  178. private System.Collections.Generic.List<Vector3> _pathPoints = new System.Collections.Generic.List<Vector3>();
  179. private System.Collections.Generic.List<Vector3> _onSurfacePathPoints = new System.Collections.Generic.List<Vector3>();
  180. public override ToolManager.ToolState state
  181. {
  182. get => base.state;
  183. set
  184. {
  185. if (state == value) return;
  186. base.state = value;
  187. UpdatePath(forceUpdate: false, updateOnSurfacePoints: false);
  188. }
  189. }
  190. public override bool SetPoint(int idx, Vector3 value, bool registerUndo, bool selectAll, bool moveSelection = true)
  191. {
  192. if (base.SetPoint(idx, value, registerUndo, selectAll, moveSelection))
  193. {
  194. UpdatePath(forceUpdate: false, updateOnSurfacePoints: true);
  195. return true;
  196. }
  197. return false;
  198. }
  199. public void SetRotatedPoint(int idx, Vector3 value, bool registerUndo)
  200. => base.SetPoint(idx, value, registerUndo, selectAll: false, moveSelection: false);
  201. public void AddPoint(Vector3 point, bool registerUndo = true)
  202. {
  203. var linePoint = new LinePoint(point);
  204. base.AddPoint(linePoint, registerUndo);
  205. UpdatePath(forceUpdate: false, updateOnSurfacePoints: true);
  206. }
  207. protected override void UpdatePoints(bool deserializing = false)
  208. {
  209. base.UpdatePoints();
  210. UpdatePath(forceUpdate: false, updateOnSurfacePoints: !deserializing);
  211. if (!deserializing && ToolManager.editMode) PWBIO.ApplyPersistentLine(this);
  212. }
  213. public void ToggleSegmentType()
  214. {
  215. ToolProperties.RegisterUndo(COMMAND_NAME);
  216. for (int i = 0; i < _selection.Count; ++i)
  217. {
  218. var idx = _selection[i];
  219. _controlPoints[idx].type = _controlPoints[idx].type == LineSegment.SegmentType.CURVE
  220. ? LineSegment.SegmentType.STRAIGHT : LineSegment.SegmentType.CURVE;
  221. }
  222. }
  223. public LineSegment[] GetSegments()
  224. {
  225. var segments = new System.Collections.Generic.List<LineSegment>();
  226. if (_controlPoints == null || _controlPoints.Count == 0) return segments.ToArray();
  227. var type = _controlPoints[0].type;
  228. for (int i = 0; i < pointsCount; ++i)
  229. {
  230. var segment = new LineSegment();
  231. segments.Add(segment);
  232. segment.type = type;
  233. segment.AddPoint(_controlPoints[i].position);
  234. do
  235. {
  236. ++i;
  237. if (i >= pointsCount) break;
  238. type = _controlPoints[i].type;
  239. if (type == segment.type) segment.AddPoint(_controlPoints[i].position);
  240. } while (type == segment.type);
  241. if (i >= pointsCount) break;
  242. i -= 2;
  243. }
  244. if (_closed)
  245. {
  246. if (_controlPoints[0].type == _controlPoints.Last().type)
  247. segments.Last().AddPoint(_controlPoints[0].position);
  248. else
  249. {
  250. var segment = new LineSegment();
  251. segment.type = _controlPoints[0].type;
  252. segment.AddPoint(_controlPoints.Last().position);
  253. segment.AddPoint(_controlPoints[0].position);
  254. segments.Add(segment);
  255. }
  256. }
  257. return segments.ToArray();
  258. }
  259. public void ToggleClosed()
  260. {
  261. ToolProperties.RegisterUndo(COMMAND_NAME);
  262. _closed = !_closed;
  263. }
  264. public bool closed
  265. {
  266. get => _closed;
  267. set => _closed = value;
  268. }
  269. public override void ToggleSelection()
  270. {
  271. base.ToggleSelection();
  272. if (isSelected) SelectAll();
  273. else ClearSelection();
  274. UnityEditor.SceneView.RepaintAll();
  275. }
  276. protected override void Initialize()
  277. {
  278. base.Initialize();
  279. for (int i = 0; i < 2; ++i) _controlPoints.Add(new LinePoint(Vector3.zero));
  280. deserializing = true;
  281. UpdatePoints(deserializing);
  282. deserializing = false;
  283. }
  284. public LineData() : base() { }
  285. public LineData((GameObject, int)[] objects, long initialBrushId, LineData lineData)
  286. : base(objects, initialBrushId, lineData) { }
  287. private static LineData _instance = null;
  288. public static LineData instance
  289. {
  290. get
  291. {
  292. if (_instance == null) _instance = new LineData();
  293. if (_instance.points == null || _instance.points.Length == 0)
  294. {
  295. _instance.Initialize();
  296. _instance._settings = LineManager.settings;
  297. }
  298. return _instance;
  299. }
  300. }
  301. private void CopyLineData(LineData other)
  302. {
  303. _closed = other._closed;
  304. _lenght = other.lenght;
  305. _midpoints = other._midpoints.ToList();
  306. _pathPoints = other._pathPoints.ToList();
  307. }
  308. public LineData Clone()
  309. {
  310. var clone = new LineData();
  311. base.Clone(clone);
  312. clone.CopyLineData(this);
  313. return clone;
  314. }
  315. public override void Copy(PersistentData<LineToolName, LineSettings, LinePoint> other)
  316. {
  317. base.Copy(other);
  318. var otherLineData = other as LineData;
  319. if (otherLineData == null) return;
  320. CopyLineData(otherLineData);
  321. }
  322. private float GetLineLength(Vector3[] points, out float[] lengthFromFirstPoint)
  323. {
  324. float lineLength = 0f;
  325. lengthFromFirstPoint = new float[points.Length];
  326. var segmentLength = new float[points.Length];
  327. lengthFromFirstPoint[0] = 0f;
  328. for (int i = 1; i < points.Length; ++i)
  329. {
  330. segmentLength[i - 1] = (points[i] - points[i - 1]).magnitude;
  331. lineLength += segmentLength[i - 1];
  332. lengthFromFirstPoint[i] = lineLength;
  333. }
  334. return lineLength;
  335. }
  336. private Vector3[] GetLineMidpoints(Vector3[] points)
  337. {
  338. if (points.Length == 0) return new Vector3[0];
  339. var midpoints = new System.Collections.Generic.List<Vector3>();
  340. var subSegments = new System.Collections.Generic.List<System.Collections.Generic.List<Vector3>>();
  341. var pathPoints = _pointPositions;
  342. bool IsAPathPoint(Vector3 point) => pathPoints.Contains(point);
  343. subSegments.Add(new System.Collections.Generic.List<Vector3>());
  344. subSegments.Last().Add(points[0]);
  345. for (int i = 1; i < points.Length - 1; ++i)
  346. {
  347. var point = points[i];
  348. subSegments.Last().Add(point);
  349. if (IsAPathPoint(point))
  350. {
  351. subSegments.Add(new System.Collections.Generic.List<Vector3>());
  352. subSegments.Last().Add(point);
  353. }
  354. }
  355. subSegments.Last().Add(points.Last());
  356. Vector3 GetLineMidpoint(Vector3[] subSegmentPoints)
  357. {
  358. var midpoint = subSegmentPoints[0];
  359. float[] lengthFromFirstPoint = null;
  360. var halfLineLength = GetLineLength(subSegmentPoints, out lengthFromFirstPoint) / 2f;
  361. for (int i = 1; i < subSegmentPoints.Length; ++i)
  362. {
  363. if (lengthFromFirstPoint[i] < halfLineLength) continue;
  364. var dir = (subSegmentPoints[i] - subSegmentPoints[i - 1]).normalized;
  365. var localLength = halfLineLength - lengthFromFirstPoint[i - 1];
  366. midpoint = subSegmentPoints[i - 1] + dir * localLength;
  367. break;
  368. }
  369. return midpoint;
  370. }
  371. foreach (var subSegment in subSegments) midpoints.Add(GetLineMidpoint(subSegment.ToArray()));
  372. return midpoints.ToArray();
  373. }
  374. public void UpdatePath(bool forceUpdate, bool updateOnSurfacePoints)
  375. {
  376. if (!forceUpdate && !ToolManager.editMode && state != ToolManager.ToolState.EDIT) return;
  377. _lenght = 0;
  378. _pathPoints.Clear();
  379. _midpoints.Clear();
  380. _onSurfacePathPoints.Clear();
  381. var segments = GetSegments();
  382. void AddSegmentPoints(System.Collections.Generic.List<Vector3> pointList, Vector3[] newPoints)
  383. {
  384. if (pointList.Count > 0 && pointList.Last() == newPoints[0] && newPoints.Length > 1)
  385. for (int i = 1; i < newPoints.Length; ++i) pointList.Add(newPoints[i]);
  386. else pointList.AddRange(newPoints);
  387. }
  388. foreach (var segment in segments)
  389. {
  390. var segmentPoints = new Vector3[] { };
  391. if (segment.type == LineSegment.SegmentType.STRAIGHT) segmentPoints = segment.points.ToArray();
  392. else segmentPoints = (BezierPath.GetBezierPoints(segment.points, segment.scales)).ToArray();
  393. AddSegmentPoints(_pathPoints, segmentPoints);
  394. if (segmentPoints.Length == 0) continue;
  395. var midpoints = GetLineMidpoints(segmentPoints);
  396. AddSegmentPoints(_midpoints, midpoints);
  397. }
  398. if (!updateOnSurfacePoints) return;
  399. var objSet = objectSet;
  400. for (int i = 0; i < _pathPoints.Count; ++i)
  401. {
  402. float distance = 10000f;
  403. if (ToolManager.tool == ToolManager.PaintTool.LINE && !deserializing)
  404. {
  405. var ray = new Ray(_pathPoints[i] - settings.projectionDirection * distance, settings.projectionDirection);
  406. var onSurfacePoint = _pathPoints[i];
  407. if (PWBIO.MouseRaycast(ray, out RaycastHit hit, out GameObject collider, distance * 2, -1,
  408. paintOnPalettePrefabs: false, castOnMeshesWithoutCollider: true,
  409. tags: null, terrainLayers: null, exceptions: objSet, sameOriginAsRay: false, origin: _pathPoints[i]))
  410. {
  411. onSurfacePoint = hit.point;
  412. }
  413. _onSurfacePathPoints.Add(onSurfacePoint);
  414. }
  415. if (i == 0) continue;
  416. _lenght += (_pathPoints[i] - _pathPoints[i - 1]).magnitude;
  417. }
  418. }
  419. public static bool SphereSegmentIntersection(Vector3 segmentStart, Vector3 segmentEnd,
  420. Vector3 sphereCenter, float sphereRadius, out Vector3 intersection)
  421. {
  422. var r = sphereRadius;
  423. var d = segmentEnd - segmentStart;
  424. var f = segmentStart - sphereCenter;
  425. var a = Vector3.Dot(d, d);
  426. var b = 2 * Vector3.Dot(f, d);
  427. var c = Vector3.Dot(f, f) - r * r;
  428. float discriminant = b * b - 4 * a * c;
  429. float t = -1;
  430. intersection = segmentStart;
  431. if (discriminant < 0) return false;
  432. else
  433. {
  434. discriminant = Mathf.Sqrt(discriminant);
  435. var t1 = (-b - discriminant) / (2 * a);
  436. var t2 = (-b + discriminant) / (2 * a);
  437. if (t1 >= 0 && t1 <= 1 && t1 > t2) t = t1;
  438. else if (t2 >= 0 && t2 <= 1 && t2 > t1) t = t2;
  439. }
  440. if (t == -1) return false;
  441. intersection += d * t;
  442. return true;
  443. }
  444. public static Vector3 NearestPathPoint(int startSegmentIdx, Vector3 startPoint, float minPathLenght,
  445. Vector3[] pathPoints, out int nearestPointIdx, out float distanceFromNearestPoint)
  446. {
  447. nearestPointIdx = pathPoints.Length - 1;
  448. var result = pathPoints.Last();
  449. distanceFromNearestPoint = 0f;
  450. startSegmentIdx = Mathf.Max(startSegmentIdx, 1);
  451. for (int i = startSegmentIdx; i < pathPoints.Length; ++i)
  452. {
  453. var start = pathPoints[i - 1];
  454. var end = pathPoints[i];
  455. if(i == pathPoints.Length -1)
  456. {
  457. end = (end - start) * 1000 + start;
  458. }
  459. if (SphereSegmentIntersection(start, end, startPoint, minPathLenght, out Vector3 intersection))
  460. {
  461. result = intersection;
  462. nearestPointIdx = i - 1;
  463. distanceFromNearestPoint = (intersection - pathPoints[nearestPointIdx]).magnitude;
  464. return result;
  465. }
  466. }
  467. return result;
  468. }
  469. public float lenght => _lenght;
  470. public Vector3[] pathPoints => _pathPoints.ToArray();
  471. public Vector3[] onSurfacePathPoints => _onSurfacePathPoints.ToArray();
  472. public Vector3 lastPathPoint => _pathPoints.Last();
  473. public Vector3[] midpoints => _midpoints.ToArray();
  474. public Vector3 lastTangentPos { get; set; }
  475. public bool showHandles { get; set; }
  476. }
  477. public class LineToolName : IToolName { public string value => "Line"; }
  478. [System.Serializable]
  479. public class LineSceneData : SceneData<LineToolName, LineSettings, LinePoint, LineData>
  480. {
  481. public LineSceneData() : base() { }
  482. public LineSceneData(string sceneGUID) : base(sceneGUID) { }
  483. }
  484. [System.Serializable]
  485. public class LineManager : PersistentToolManagerBase<LineToolName, LineSettings, LinePoint, LineData, LineSceneData>
  486. {
  487. public enum EditModeType
  488. {
  489. NODES,
  490. LINE_POSE
  491. }
  492. public static EditModeType editModeType { get; set; }
  493. public static void ToggleEditModeType()
  494. {
  495. editModeType = editModeType == EditModeType.NODES ? EditModeType.LINE_POSE : EditModeType.NODES;
  496. ToolProperties.RepainWindow();
  497. }
  498. }
  499. #endregion
  500. #region PWBIO
  501. public static partial class PWBIO
  502. {
  503. #region HANDLERS
  504. private static void LineInitializeOnLoad()
  505. {
  506. LineManager.settings.OnDataChanged += OnLineSettingsChanged;
  507. BrushSettings.OnBrushSettingsChanged += PreviewSelectedPersistentLines;
  508. }
  509. private static void OnLineToolModeChanged()
  510. {
  511. DeselectPersistentLines();
  512. if (!ToolManager.editMode)
  513. {
  514. ToolProperties.RepainWindow();
  515. return;
  516. }
  517. ResetLineState();
  518. ResetSelectedPersistentLine();
  519. LineManager.editModeType = LineManager.EditModeType.NODES;
  520. }
  521. private static void OnLineSettingsChanged()
  522. {
  523. repaint = true;
  524. if (!ToolManager.editMode)
  525. {
  526. _lineData.settings = LineManager.settings;
  527. updateStroke = true;
  528. return;
  529. }
  530. if (_selectedPersistentLineData == null) return;
  531. _selectedPersistentLineData.settings.Copy(LineManager.settings);
  532. PreviewPersistentLine(_selectedPersistentLineData);
  533. }
  534. private static void OnUndoLine() => ClearLineStroke();
  535. #endregion
  536. #region SPAWN MODE
  537. public static void ResetLineState(bool askIfWantToSave = true)
  538. {
  539. if (_lineData.state == ToolManager.ToolState.NONE) return;
  540. if (askIfWantToSave)
  541. {
  542. void Save()
  543. {
  544. if (UnityEditor.SceneView.lastActiveSceneView != null)
  545. LineStrokePreview(UnityEditor.SceneView.lastActiveSceneView, _lineData,
  546. persistent: false, forceUpdate: true, initialIdx: 0);
  547. CreateLine();
  548. }
  549. AskIfWantToSave(_lineData.state, Save);
  550. }
  551. _snappedToVertex = false;
  552. selectingLinePoints = false;
  553. _lineData.Reset();
  554. OnLineSettingsChanged();
  555. }
  556. private static void LineStateNone(bool in2DMode)
  557. {
  558. if (Event.current.button == 0 && Event.current.type == EventType.MouseDown && !Event.current.alt)
  559. {
  560. _lineData.name = LineData.nextHexId;
  561. _lineData.closed = false;
  562. _lineData.state = ToolManager.ToolState.PREVIEW;
  563. Event.current.Use();
  564. }
  565. if (MouseDot(out Vector3 point, out Vector3 normal, LineManager.settings.mode, in2DMode,
  566. LineManager.settings.paintOnPalettePrefabs, LineManager.settings.paintOnMeshesWithoutCollider, false))
  567. {
  568. point = _snapToVertex ? LinePointSnapping(point)
  569. : SnapAndUpdateGridOrigin(point, SnapManager.settings.snappingEnabled,
  570. LineManager.settings.paintOnPalettePrefabs, LineManager.settings.paintOnMeshesWithoutCollider,
  571. false, Vector3.down);
  572. _lineData.SetPoint(0, point, registerUndo: false, selectAll: false);
  573. _lineData.SetPoint(1, point, registerUndo: false, selectAll: false);
  574. }
  575. DrawDotHandleCap(_lineData.GetPoint(0));
  576. }
  577. private static void LineStateStraightLine(bool in2DMode)
  578. {
  579. if (Event.current.button == 0 && Event.current.type == EventType.MouseDown && !Event.current.alt)
  580. {
  581. _lineData.state = ToolManager.ToolState.EDIT;
  582. updateStroke = true;
  583. }
  584. if (MouseDot(out Vector3 point, out Vector3 normal, LineManager.settings.mode, in2DMode,
  585. LineManager.settings.paintOnPalettePrefabs, LineManager.settings.paintOnMeshesWithoutCollider, false))
  586. {
  587. point = _snapToVertex ? LinePointSnapping(point)
  588. : SnapAndUpdateGridOrigin(point, SnapManager.settings.snappingEnabled,
  589. LineManager.settings.paintOnPalettePrefabs, LineManager.settings.paintOnMeshesWithoutCollider,
  590. false, Vector3.down);
  591. _lineData.SetPoint(1, point, registerUndo: false, selectAll: false);
  592. }
  593. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  594. UnityEditor.Handles.DrawAAPolyLine(8, new Vector3[] { _lineData.GetPoint(0), _lineData.GetPoint(1) });
  595. UnityEditor.Handles.color = new Color(1f, 1f, 1f, 0.7f);
  596. UnityEditor.Handles.DrawAAPolyLine(4, new Vector3[] { _lineData.GetPoint(0), _lineData.GetPoint(1) });
  597. DrawDotHandleCap(_lineData.GetPoint(0));
  598. DrawDotHandleCap(_lineData.GetPoint(1));
  599. }
  600. private static void LineStateBezier(UnityEditor.SceneView sceneView)
  601. {
  602. var pathPoints = _lineData.pathPoints;
  603. var forceStrokeUpdate = updateStroke;
  604. if (updateStroke)
  605. {
  606. _lineData.UpdatePath(forceUpdate: false, updateOnSurfacePoints: false);
  607. pathPoints = _lineData.pathPoints;
  608. BrushstrokeManager.UpdateLineBrushstroke(pathPoints);
  609. updateStroke = false;
  610. }
  611. LineStrokePreview(sceneView, _lineData, persistent: false, forceStrokeUpdate, 0);
  612. DrawLine(_lineData, drawSurfacePath: true);
  613. DrawSelectionRectangle();
  614. LineInput(false, sceneView, false);
  615. if (selectingLinePoints && !Event.current.control) _lineData.ClearSelection();
  616. bool clickOnPoint, wasEdited;
  617. DrawLineControlPoints(_lineData, isPersistent: false, showHandles: true,
  618. out clickOnPoint, out bool multiSelection, out bool addToselection,
  619. out bool removeFromSelection, out wasEdited, out Vector3 delta);
  620. if (wasEdited) updateStroke = true;
  621. SelectionRectangleInput(clickOnPoint);
  622. }
  623. private static void CreateLine()
  624. {
  625. var nextLineId = LineData.nextHexId;
  626. var objDic = Paint(LineManager.settings, PAINT_CMD, addTempCollider: true,
  627. persistent: false, toolObjectId: nextLineId);
  628. if (objDic.Count != 1) return;
  629. var scenePath = UnityEngine.SceneManagement.SceneManager.GetActiveScene().path;
  630. var sceneGUID = UnityEditor.AssetDatabase.AssetPathToGUID(scenePath);
  631. var initialBrushId = PaletteManager.selectedBrush != null ? PaletteManager.selectedBrush.id : -1;
  632. var objs = objDic[nextLineId].ToArray();
  633. var persistentData = new LineData(objs, initialBrushId, _lineData);
  634. LineManager.instance.AddPersistentItem(sceneGUID, persistentData);
  635. PWBItemsWindow.RepainWindow();
  636. }
  637. private static void LineStrokePreview(UnityEditor.SceneView sceneView,
  638. LineData lineData, bool persistent, bool forceUpdate, int initialIdx)
  639. {
  640. var settings = lineData.settings;
  641. var lastPoint = lineData.lastPathPoint;
  642. var objectCount = lineData.objectCount;
  643. var lastObjectTangentPosition = lineData.lastTangentPos;
  644. BrushstrokeItem[] brushstroke = null;
  645. if (PreviewIfBrushtrokestaysTheSame(out brushstroke, sceneView.camera, forceUpdate)) return;
  646. PWBCore.UpdateTempCollidersIfHierarchyChanged();
  647. if (!persistent) _paintStroke.Clear();
  648. var idx = initialIdx;
  649. float maxSurfaceHeight = 0f;
  650. for (int i = 0; i < brushstroke.Length; ++i)
  651. {
  652. var strokeItem = brushstroke[i];
  653. var prefab = strokeItem.settings.prefab;
  654. if (prefab == null) continue;
  655. var bounds = BoundsUtils.GetBoundsRecursive(prefab.transform, prefab.transform.rotation);
  656. BrushSettings brushSettings = strokeItem.settings;
  657. if (LineManager.settings.overwriteBrushProperties) brushSettings = LineManager.settings.brushSettings;
  658. var size = Vector3.Scale(bounds.size, strokeItem.scaleMultiplier);
  659. var pivotToCenter = Vector3.Scale(
  660. prefab.transform.InverseTransformDirection(bounds.center - prefab.transform.position),
  661. strokeItem.scaleMultiplier);
  662. var height = size.x + size.y + size.z + maxSurfaceHeight;
  663. Vector3 segmentDir = Vector3.zero;
  664. if (settings.objectsOrientedAlongTheLine && brushstroke.Length > 1)
  665. {
  666. segmentDir = i < brushstroke.Length - 1
  667. ? strokeItem.nextTangentPosition - strokeItem.tangentPosition
  668. : lastPoint - strokeItem.tangentPosition;
  669. }
  670. if (brushstroke.Length == 1)
  671. {
  672. segmentDir = lastPoint - brushstroke[0].tangentPosition;
  673. if (persistent && objectCount > 0)
  674. segmentDir = lastPoint - lastObjectTangentPosition;
  675. }
  676. if (i == brushstroke.Length - 1)
  677. {
  678. var onLineSize = AxesUtils.GetAxisValue(size, settings.axisOrientedAlongTheLine)
  679. + settings.gapSize;
  680. var segmentSize = segmentDir.magnitude;
  681. if (segmentSize > onLineSize) segmentDir = segmentDir.normalized
  682. * (settings.spacingType == LineSettings.SpacingType.BOUNDS ? onLineSize : settings.spacing);
  683. }
  684. var perpendicularToTheSurface = settings.perpendicularToTheSurface
  685. || (brushSettings.rotateToTheSurface && !brushSettings.alwaysOrientUp);
  686. if (settings.objectsOrientedAlongTheLine && !perpendicularToTheSurface)
  687. {
  688. var projectionAxis = ((AxesUtils.SignedAxis)(settings.projectionDirection)).axis;
  689. segmentDir -= AxesUtils.GetVector(AxesUtils.GetAxisValue(segmentDir, projectionAxis), projectionAxis);
  690. }
  691. var normal = -settings.projectionDirection;
  692. var otherAxes = AxesUtils.GetOtherAxes((AxesUtils.SignedAxis)(-settings.projectionDirection));
  693. var tangetAxis = otherAxes[settings.objectsOrientedAlongTheLine ? 0 : 1];
  694. Vector3 itemTangent = (AxesUtils.SignedAxis)(tangetAxis);
  695. var itemRotation = Quaternion.LookRotation(itemTangent, normal);
  696. var lookAt = Quaternion.LookRotation((Vector3)(AxesUtils.SignedAxis)
  697. (settings.axisOrientedAlongTheLine), Vector3.up);
  698. var itemPosition = strokeItem.tangentPosition + segmentDir / 2;
  699. var ray = new Ray(itemPosition + normal * height, -normal);
  700. Transform surface = null;
  701. if (settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  702. {
  703. if (MouseRaycast(ray, out RaycastHit itemHit,
  704. out GameObject collider, float.MaxValue, layerMask: -1,
  705. settings.paintOnPalettePrefabs, settings.paintOnMeshesWithoutCollider,
  706. sameOriginAsRay: false, origin: itemPosition))
  707. {
  708. itemPosition = itemHit.point;
  709. if (perpendicularToTheSurface) normal = itemHit.normal;
  710. var colObj = PWBCore.GetGameObjectFromTempCollider(collider);
  711. if (colObj != null) surface = colObj.transform;
  712. var surfObj = PWBCore.GetGameObjectFromTempCollider(collider);
  713. var surfSize = BoundsUtils.GetBounds(surfObj.transform).size;
  714. var h = surfSize.x + surfSize.y + surfSize.z;
  715. maxSurfaceHeight = Mathf.Max(h, maxSurfaceHeight);
  716. }
  717. else if (settings.mode == PaintOnSurfaceToolSettingsBase.PaintMode.ON_SURFACE) continue;
  718. }
  719. if (perpendicularToTheSurface && segmentDir != Vector3.zero)
  720. {
  721. if (settings.mode == PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  722. {
  723. var bitangent = Vector3.Cross(segmentDir, normal);
  724. var lineNormal = Vector3.Cross(bitangent, segmentDir);
  725. itemRotation = Quaternion.LookRotation(segmentDir, lineNormal) * lookAt;
  726. }
  727. else
  728. {
  729. var plane = new Plane(normal, itemPosition);
  730. var tangent = plane.ClosestPointOnPlane(segmentDir + itemPosition) - itemPosition;
  731. itemRotation = Quaternion.LookRotation(tangent, normal) * lookAt;
  732. }
  733. }
  734. else if (!perpendicularToTheSurface && segmentDir != Vector3.zero)
  735. itemRotation = Quaternion.LookRotation(segmentDir, normal) * lookAt;
  736. itemRotation *= Quaternion.Euler(strokeItem.additionalAngle);
  737. if (!settings.perpendicularToTheSurface && brushSettings.rotateToTheSurface && brushSettings.alwaysOrientUp)
  738. {
  739. var fw = itemRotation * Vector3.forward;
  740. const float minMag = 1e-6f;
  741. fw.y = 0;
  742. if (Mathf.Abs(fw.x) < minMag && Mathf.Abs(fw.z) < minMag) fw = Quaternion.Euler(0, 90, 0) * normal;
  743. itemRotation = Quaternion.LookRotation(fw, Vector3.up);
  744. }
  745. itemPosition += normal * strokeItem.surfaceDistance;
  746. itemPosition += itemRotation * brushSettings.localPositionOffset;
  747. itemPosition -= itemRotation * (pivotToCenter - Vector3.up * (size.y / 2));
  748. if (brushSettings.embedInSurface
  749. && settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  750. {
  751. if (brushSettings.embedAtPivotHeight)
  752. itemPosition += itemRotation * new Vector3(0f, strokeItem.settings.bottomMagnitude, 0f);
  753. else
  754. {
  755. var TRS = Matrix4x4.TRS(itemPosition, itemRotation,
  756. Vector3.Scale(prefab.transform.localScale, strokeItem.scaleMultiplier));
  757. float magnitudeInDirection;
  758. var localDirection = Quaternion.Inverse(itemRotation) * -normal;
  759. var furthestVertices = strokeItem.settings.GetFurthestVerticesInDirection(localDirection,
  760. out magnitudeInDirection);
  761. var distanceTosurface = GetDistanceToSurface(furthestVertices, TRS, -normal,
  762. Mathf.Abs(magnitudeInDirection), PinManager.settings.paintOnPalettePrefabs,
  763. PinManager.settings.paintOnMeshesWithoutCollider, out Transform surfaceTransform, prefab);
  764. itemPosition -= normal * distanceTosurface;
  765. }
  766. }
  767. var rootToWorld = Matrix4x4.TRS(itemPosition, itemRotation, strokeItem.scaleMultiplier)
  768. * Matrix4x4.Rotate(Quaternion.Inverse(prefab.transform.rotation))
  769. * Matrix4x4.Translate(-prefab.transform.position);
  770. var itemScale = Vector3.Scale(prefab.transform.localScale, strokeItem.scaleMultiplier);
  771. var layer = settings.overwritePrefabLayer ? settings.layer : prefab.layer;
  772. Transform parentTransform = settings.parent;
  773. var paintItem = new PaintStrokeItem(prefab, itemPosition, itemRotation,
  774. itemScale, layer, parentTransform, surface, strokeItem.flipX, strokeItem.flipY, idx++);
  775. paintItem.persistentParentId = persistent ? lineData.hexId : LineData.nextHexId;
  776. _paintStroke.Add(paintItem);
  777. PreviewBrushItem(prefab, rootToWorld, layer, sceneView.camera,
  778. false, false, strokeItem.flipX, strokeItem.flipY);
  779. var prevData = new PreviewData(prefab, rootToWorld, layer, strokeItem.flipX, strokeItem.flipY);
  780. _previewData.Add(prevData);
  781. }
  782. if (_persistentPreviewData.ContainsKey(lineData.id)) _persistentPreviewData[lineData.id] = _previewData.ToArray();
  783. else _persistentPreviewData.Add(lineData.id, _previewData.ToArray());
  784. }
  785. #endregion
  786. #region COMMON
  787. private static LineData _lineData = LineData.instance;
  788. private static bool _selectingLinePoints = false;
  789. private static Rect _selectionRect = new Rect();
  790. private static string _createProfileName = ToolProfile.DEFAULT;
  791. public static LineData lineData
  792. => (ToolManager.editMode && _selectedPersistentLineData != null) ? _selectedPersistentLineData : _lineData;
  793. public static bool selectingLinePoints
  794. {
  795. get => _selectingLinePoints;
  796. set
  797. {
  798. if (value == _selectingLinePoints) return;
  799. _selectingLinePoints = value;
  800. }
  801. }
  802. private static void ClearLineStroke()
  803. {
  804. _paintStroke.Clear();
  805. BrushstrokeManager.ClearBrushstroke();
  806. if (ToolManager.editMode && _selectedPersistentLineData != null)
  807. {
  808. _selectedPersistentLineData.UpdatePath(forceUpdate: true, updateOnSurfacePoints: false);
  809. PreviewPersistentLine(_selectedPersistentLineData);
  810. UnityEditor.SceneView.RepaintAll();
  811. repaint = true;
  812. }
  813. }
  814. private static void LineDuringSceneGUI(UnityEditor.SceneView sceneView)
  815. {
  816. if (LineManager.settings.paintOnMeshesWithoutCollider)
  817. PWBCore.CreateTempCollidersWithinFrustum(sceneView.camera);
  818. if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
  819. {
  820. if (_lineData.state == ToolManager.ToolState.EDIT && _lineData.selectedPointIdx > 0)
  821. _lineData.ClearSelection();
  822. else if (_lineData.state == ToolManager.ToolState.NONE && !ToolManager.editMode)
  823. ToolManager.DeselectTool();
  824. else if (ToolManager.editMode)
  825. {
  826. if (_editingPersistentLine) ResetSelectedPersistentLine();
  827. else ToolManager.DeselectTool();
  828. DeselectPersistentLines();
  829. _initialPersistentLineData = null;
  830. _selectedPersistentLineData = null;
  831. ToolProperties.RepainWindow();
  832. ToolManager.editMode = false;
  833. }
  834. else ResetLineState(false);
  835. OnUndoLine();
  836. UpdateStroke();
  837. BrushstrokeManager.ClearBrushstroke();
  838. }
  839. LineToolEditMode(sceneView);
  840. if (ToolManager.editMode) return;
  841. switch (_lineData.state)
  842. {
  843. case ToolManager.ToolState.NONE:
  844. LineStateNone(sceneView.in2DMode);
  845. break;
  846. case ToolManager.ToolState.PREVIEW:
  847. LineStateStraightLine(sceneView.in2DMode);
  848. break;
  849. case ToolManager.ToolState.EDIT:
  850. LineStateBezier(sceneView);
  851. break;
  852. }
  853. }
  854. private static Quaternion _lineRotation = Quaternion.identity;
  855. private static void RotateLineAround(int idx, Quaternion rotation, LineData lineData)
  856. {
  857. var pivotPosition = lineData.GetPoint(idx);
  858. for (int i = 0; i < lineData.pointsCount; ++i)
  859. {
  860. if (i == idx) continue;
  861. var localPositionUnrotated = Quaternion.Inverse(_lineRotation) * (lineData.GetPoint(i) - pivotPosition);
  862. var localPosition = rotation * localPositionUnrotated;
  863. lineData.SetRotatedPoint(i, pivotPosition + localPosition, true);
  864. }
  865. _lineRotation = rotation;
  866. lineData.UpdatePath(forceUpdate: false, updateOnSurfacePoints: true);
  867. }
  868. public static void ResetLineRotation() => _lineRotation = Quaternion.identity;
  869. public static void UpdateLinePathAndStroke(LineData data)
  870. {
  871. data.UpdatePath(forceUpdate: true, updateOnSurfacePoints: true);
  872. PWBIO.PreviewPersistentLine(data);
  873. }
  874. public static void ApplyPersistentLineAndReset(LineData data)
  875. {
  876. data.UpdatePath(forceUpdate: true, updateOnSurfacePoints: true);
  877. PreviewPersistentLine(data);
  878. DeleteDisabledObjects();
  879. ApplyPersistentLine(data);
  880. _initialPersistentLineData = null;
  881. _selectedPersistentLineData = null;
  882. UnityEditor.SceneView.RepaintAll();
  883. }
  884. public static void DeleteLinePoints(LineData data, int[] indexes, bool isPersistent)
  885. {
  886. if (isPersistent && data.pointsCount - indexes.Length <= 1)
  887. {
  888. LineManager.instance.DeletePersistentItem(data.id, deleteObjects: true);
  889. UnityEditor.SceneView.RepaintAll();
  890. return;
  891. }
  892. data.RemovePoints(indexes);
  893. if (isPersistent) ApplyPersistentLineAndReset(data);
  894. if (data.pointsCount >= 2) updateStroke = true;
  895. }
  896. public static void ShowLineContextMenu(LineData data, bool isPersistent, Vector2 mousePosition, int pointIdx)
  897. {
  898. if (isPersistent && !ToolManager.editMode) return;
  899. var menu = new UnityEditor.GenericMenu();
  900. menu.AddItem(new GUIContent("Delete point ... Delete"), on: false, () =>
  901. {
  902. if (isPersistent && data.pointsCount <= 2)
  903. {
  904. LineManager.instance.DeletePersistentItem(data.id, deleteObjects: true);
  905. UnityEditor.SceneView.RepaintAll();
  906. return;
  907. }
  908. data.RemovePoint(pointIdx);
  909. if (isPersistent)
  910. {
  911. data.UpdatePath(forceUpdate: true, updateOnSurfacePoints: true);
  912. PreviewPersistentLine(data);
  913. DeleteDisabledObjects();
  914. ApplyPersistentLine(data);
  915. _initialPersistentLineData = null;
  916. _selectedPersistentLineData = null;
  917. }
  918. if (data.pointsCount >= 2) updateStroke = true;
  919. });
  920. menu.AddItem(new GUIContent("Delete selected points ... Delete"), on: false, () =>
  921. {
  922. if (isPersistent && data.pointsCount - data.selectionCount <= 1)
  923. {
  924. LineManager.instance.DeletePersistentItem(data.id, deleteObjects: true);
  925. UnityEditor.SceneView.RepaintAll();
  926. return;
  927. }
  928. data.RemoveSelectedPoints();
  929. if (isPersistent)
  930. {
  931. data.UpdatePath(forceUpdate: true, updateOnSurfacePoints: true);
  932. PreviewPersistentLine(data);
  933. DeleteDisabledObjects();
  934. ApplyPersistentLine(data);
  935. _initialPersistentLineData = null;
  936. _selectedPersistentLineData = null;
  937. }
  938. if (data.pointsCount >= 2) updateStroke = true;
  939. });
  940. menu.AddItem(new GUIContent("Select all points ... "
  941. + PWBSettings.shortcuts.lineSelectAllPoints.combination.ToString()), on: false, () => data.SelectAll());
  942. menu.AddItem(new GUIContent("Deselect all points ... "
  943. + PWBSettings.shortcuts.lineDeselectAllPoints.combination.ToString()), on: false,
  944. () => data.ClearSelection());
  945. menu.AddItem(new GUIContent("Set prev segment as straight or curved ... "
  946. + PWBSettings.shortcuts.lineToggleCurve.combination.ToString()), on: false, () =>
  947. {
  948. data.ToggleSegmentType();
  949. updateStroke = true;
  950. });
  951. menu.AddItem(new GUIContent("Close or open the path ... "
  952. + PWBSettings.shortcuts.lineToggleClosed.combination.ToString()), on: false, () =>
  953. {
  954. data.ToggleClosed();
  955. updateStroke = true;
  956. });
  957. menu.AddSeparator(string.Empty);
  958. PersistentItemContextMenu(menu, data, mousePosition);
  959. menu.ShowAsContext();
  960. }
  961. private static bool DrawLineControlPoints(LineData lineData, bool isPersistent, bool showHandles,
  962. out bool clickOnPoint, out bool multiSelection, out bool addToSelection,
  963. out bool removedFromSelection, out bool wasEdited, out Vector3 delta)
  964. {
  965. delta = Vector3.zero;
  966. clickOnPoint = false;
  967. wasEdited = false;
  968. multiSelection = false;
  969. addToSelection = false;
  970. removedFromSelection = false;
  971. bool leftMouseDown = Event.current.button == 0 && Event.current.type == EventType.MouseDown;
  972. bool selectAll = ToolManager.editMode && LineManager.editModeType == LineManager.EditModeType.LINE_POSE;
  973. bool selectionChanged = false;
  974. for (int i = 0; i < lineData.pointsCount; ++i)
  975. {
  976. if (selectingLinePoints)
  977. {
  978. var GUIPos = UnityEditor.HandleUtility.WorldToGUIPoint(lineData.GetPoint(i));
  979. var rect = _selectionRect;
  980. if (_selectionRect.size.x < 0 || _selectionRect.size.y < 0)
  981. {
  982. var max = Vector2.Max(_selectionRect.min, _selectionRect.max);
  983. var min = Vector2.Min(_selectionRect.min, _selectionRect.max);
  984. var size = max - min;
  985. rect = new Rect(min, size);
  986. }
  987. if (rect.Contains(GUIPos))
  988. {
  989. if (!Event.current.control && lineData.selectedPointIdx < 0) lineData.selectedPointIdx = i;
  990. lineData.AddToSelection(i);
  991. clickOnPoint = true;
  992. multiSelection = true;
  993. selectionChanged = true;
  994. }
  995. }
  996. else
  997. {
  998. var controlId = GUIUtility.GetControlID(FocusType.Passive);
  999. float distFromMouse = UnityEditor.HandleUtility.DistanceToRectangle(lineData.GetPoint(i),
  1000. Quaternion.identity, 0f);
  1001. UnityEditor.HandleUtility.AddControl(controlId, distFromMouse);
  1002. if (!clickOnPoint && showHandles && leftMouseDown
  1003. && UnityEditor.HandleUtility.nearestControl == controlId)
  1004. {
  1005. if (!Event.current.control)
  1006. {
  1007. lineData.ClearSelection();
  1008. lineData.selectedPointIdx = i;
  1009. selectionChanged = true;
  1010. }
  1011. if ((!ToolManager.editMode
  1012. || (ToolManager.editMode && LineManager.editModeType == LineManager.EditModeType.NODES))
  1013. && (Event.current.control || lineData.selectionCount == 0))
  1014. {
  1015. if (lineData.ControlPointIsSelected(i))
  1016. {
  1017. lineData.RemoveFromSelection(i);
  1018. lineData.selectedPointIdx = -1;
  1019. removedFromSelection = true;
  1020. }
  1021. else
  1022. {
  1023. lineData.AddToSelection(i);
  1024. lineData.showHandles = true;
  1025. lineData.selectedPointIdx = i;
  1026. if (Event.current.control) addToSelection = true;
  1027. }
  1028. selectionChanged = true;
  1029. }
  1030. clickOnPoint = true;
  1031. Event.current.Use();
  1032. }
  1033. if (Event.current.button == 1 && Event.current.type == EventType.MouseDown
  1034. && !Event.current.control && !Event.current.shift && !Event.current.alt
  1035. && UnityEditor.HandleUtility.nearestControl == controlId)
  1036. {
  1037. ShowLineContextMenu(lineData, isPersistent,
  1038. UnityEditor.EditorGUIUtility.GUIToScreenPoint(Event.current.mousePosition), pointIdx: i);
  1039. Event.current.Use();
  1040. }
  1041. }
  1042. if (Event.current.type != EventType.Repaint) continue;
  1043. DrawDotHandleCap(lineData.GetPoint(i), 1, 1, lineData.ControlPointIsSelected(i));
  1044. }
  1045. if (selectionChanged) ResetLineRotation();
  1046. var midpoints = lineData.midpoints;
  1047. for (int i = 0; i < midpoints.Length; ++i)
  1048. {
  1049. var point = midpoints[i];
  1050. var controlId = GUIUtility.GetControlID(FocusType.Passive);
  1051. if (showHandles)
  1052. {
  1053. float distFromMouse
  1054. = UnityEditor.HandleUtility.DistanceToRectangle(point, Quaternion.identity, 0f);
  1055. UnityEditor.HandleUtility.AddControl(controlId, distFromMouse);
  1056. }
  1057. DrawDotHandleCap(point, 0.4f);
  1058. if (showHandles && UnityEditor.HandleUtility.nearestControl == controlId)
  1059. {
  1060. DrawDotHandleCap(point);
  1061. if (leftMouseDown)
  1062. {
  1063. lineData.InsertPoint(i + 1, new LinePoint(point));
  1064. lineData.ClearSelection();
  1065. lineData.selectedPointIdx = i + 1;
  1066. updateStroke = true;
  1067. clickOnPoint = true;
  1068. Event.current.Use();
  1069. }
  1070. }
  1071. }
  1072. if (showHandles && lineData.showHandles && lineData.selectedPointIdx >= 0)
  1073. {
  1074. var selectedPoint = lineData.selectedPoint;
  1075. if (_updateHandlePosition)
  1076. {
  1077. selectedPoint = _handlePosition;
  1078. _updateHandlePosition = false;
  1079. }
  1080. var prevPosition = lineData.selectedPoint;
  1081. lineData.SetPoint(lineData.selectedPointIdx,
  1082. UnityEditor.Handles.PositionHandle(selectedPoint, Quaternion.identity),
  1083. registerUndo: true, selectAll);
  1084. var point = _snapToVertex ? LinePointSnapping(lineData.selectedPoint)
  1085. : SnapAndUpdateGridOrigin(lineData.selectedPoint, SnapManager.settings.snappingEnabled,
  1086. LineManager.settings.paintOnPalettePrefabs, LineManager.settings.paintOnMeshesWithoutCollider,
  1087. false, Vector3.down);
  1088. lineData.SetPoint(lineData.selectedPointIdx, point, registerUndo: false, selectAll);
  1089. _handlePosition = lineData.selectedPoint;
  1090. if (prevPosition != lineData.selectedPoint)
  1091. {
  1092. wasEdited = true;
  1093. updateStroke = true;
  1094. delta = lineData.selectedPoint - prevPosition;
  1095. ToolProperties.RepainWindow();
  1096. }
  1097. if (LineManager.editModeType == LineManager.EditModeType.LINE_POSE)
  1098. {
  1099. var prevRotation = _lineRotation;
  1100. var handleRotation = UnityEditor.Handles.RotationHandle(_lineRotation, lineData.selectedPoint);
  1101. if (prevRotation != handleRotation)
  1102. {
  1103. RotateLineAround(lineData.selectedPointIdx, handleRotation, lineData);
  1104. wasEdited = true;
  1105. updateStroke = true;
  1106. ToolProperties.RepainWindow();
  1107. }
  1108. }
  1109. }
  1110. if (!showHandles) return false;
  1111. return clickOnPoint || wasEdited;
  1112. }
  1113. private static Vector3 LinePointSnapping(Vector3 point)
  1114. {
  1115. const float snapSqrDistance = 400f;
  1116. var mouseRay = UnityEditor.HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
  1117. var persistentLines = LineManager.instance.GetPersistentItems();
  1118. var result = point;
  1119. var minSqrDistance = snapSqrDistance;
  1120. foreach (var lineData in persistentLines)
  1121. {
  1122. var controlPoints = lineData.points;
  1123. foreach (var controlPoint in controlPoints)
  1124. {
  1125. var intersection = mouseRay.origin + Vector3.Project(controlPoint - mouseRay.origin, mouseRay.direction);
  1126. var GUIControlPoint = UnityEditor.HandleUtility.WorldToGUIPoint(controlPoint);
  1127. var intersectionGUIPoint = UnityEditor.HandleUtility.WorldToGUIPoint(intersection);
  1128. var sqrDistance = (GUIControlPoint - intersectionGUIPoint).sqrMagnitude;
  1129. if (sqrDistance > 0 && sqrDistance < snapSqrDistance && sqrDistance < minSqrDistance)
  1130. {
  1131. minSqrDistance = sqrDistance;
  1132. result = controlPoint;
  1133. }
  1134. }
  1135. }
  1136. return result;
  1137. }
  1138. private static void DrawLine(LineData lineData, bool drawSurfacePath)
  1139. {
  1140. var pathPoints = lineData.pathPoints;
  1141. var surfacePathPoints = lineData.onSurfacePathPoints;
  1142. if (pathPoints.Length == 0 || (drawSurfacePath && surfacePathPoints.Length == 0))
  1143. lineData.UpdatePath(forceUpdate: true, updateOnSurfacePoints: drawSurfacePath);
  1144. UnityEditor.Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
  1145. if (drawSurfacePath)
  1146. {
  1147. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  1148. UnityEditor.Handles.DrawAAPolyLine(8, surfacePathPoints);
  1149. UnityEditor.Handles.color = new Color(0f, 1f, 1f, 0.5f);
  1150. UnityEditor.Handles.DrawAAPolyLine(4, surfacePathPoints);
  1151. }
  1152. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  1153. UnityEditor.Handles.DrawAAPolyLine(8, pathPoints);
  1154. UnityEditor.Handles.color = new Color(1f, 1f, 1f, 0.7f);
  1155. UnityEditor.Handles.DrawAAPolyLine(4, pathPoints);
  1156. }
  1157. private static void DrawSelectionRectangle()
  1158. {
  1159. if (!selectingLinePoints) return;
  1160. var rays = new Ray[]
  1161. {
  1162. UnityEditor.HandleUtility.GUIPointToWorldRay(_selectionRect.min),
  1163. UnityEditor.HandleUtility.GUIPointToWorldRay(new Vector2(_selectionRect.xMax, _selectionRect.yMin)),
  1164. UnityEditor.HandleUtility.GUIPointToWorldRay(_selectionRect.max),
  1165. UnityEditor.HandleUtility.GUIPointToWorldRay(new Vector2(_selectionRect.xMin, _selectionRect.yMax))
  1166. };
  1167. var verts = new Vector3[4];
  1168. for (int i = 0; i < 4; ++i) verts[i] = rays[i].origin + rays[i].direction;
  1169. UnityEditor.Handles.DrawSolidRectangleWithOutline(verts,
  1170. new Color(0f, 0.5f, 0.5f, 0.3f), new Color(0f, 0.5f, 0.5f, 1f));
  1171. }
  1172. private static void SelectionRectangleInput(bool clickOnPoint)
  1173. {
  1174. bool leftMouseDown = Event.current.button == 1 && Event.current.type == EventType.MouseDown;
  1175. if (!selectingLinePoints && Event.current.shift && leftMouseDown && !clickOnPoint)
  1176. {
  1177. selectingLinePoints = true;
  1178. _selectionRect = new Rect(Event.current.mousePosition, Vector2.zero);
  1179. Event.current.Use();
  1180. }
  1181. if ((Event.current.type == EventType.MouseDrag || Event.current.type == EventType.MouseMove)
  1182. && selectingLinePoints)
  1183. {
  1184. _selectionRect.size = Event.current.mousePosition - _selectionRect.position;
  1185. }
  1186. if (Event.current.button == 0 && (Event.current.type == EventType.MouseUp
  1187. || Event.current.type == EventType.Ignore || Event.current.type == EventType.KeyUp))
  1188. selectingLinePoints = false;
  1189. }
  1190. public static void ApplyPersistentLine(LineData data)
  1191. {
  1192. data.UpdatePoses();
  1193. DeleteDisabledObjects();
  1194. PWBCore.staticData.SetSavePending();
  1195. AutoSave.QuickSave();
  1196. }
  1197. private static void LineInput(bool persistent, UnityEditor.SceneView sceneView, bool skipPreview)
  1198. {
  1199. var lineData = persistent ? _selectedPersistentLineData : _lineData;
  1200. if (lineData == null) return;
  1201. if (Event.current.keyCode == KeyCode.Return && Event.current.type == EventType.KeyDown)
  1202. {
  1203. if (persistent)
  1204. {
  1205. if (skipPreview)
  1206. {
  1207. PreviewPersistentLine(lineData);
  1208. LineStrokePreview(sceneView, lineData, persistent: true, forceUpdate: true, _firstNewObjIdx);
  1209. }
  1210. DeleteDisabledObjects();
  1211. ApplySelectedPersistentLine(true);
  1212. DeleteDisabledObjects();
  1213. ToolProperties.RepainWindow();
  1214. }
  1215. else
  1216. {
  1217. CreateLine();
  1218. ResetLineState(false);
  1219. }
  1220. }
  1221. else if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Delete
  1222. && !Event.current.control && !Event.current.alt && !Event.current.shift)
  1223. {
  1224. if (persistent && lineData.pointsCount <= 2)
  1225. {
  1226. LineManager.instance.DeletePersistentItem(lineData.id, deleteObjects: true);
  1227. UnityEditor.SceneView.RepaintAll();
  1228. }
  1229. else
  1230. {
  1231. lineData.RemoveSelectedPoints();
  1232. if (persistent)
  1233. {
  1234. lineData.UpdatePath(forceUpdate: true, updateOnSurfacePoints: true);
  1235. PreviewPersistentLine(lineData);
  1236. LineStrokePreview(sceneView, lineData, persistent: true, forceUpdate: true, _firstNewObjIdx);
  1237. DeleteDisabledObjects();
  1238. ApplySelectedPersistentLine(true);
  1239. _initialPersistentLineData = null;
  1240. _selectedPersistentLineData = null;
  1241. }
  1242. if (lineData.pointsCount >= 2) updateStroke = true;
  1243. }
  1244. }
  1245. else if (Event.current.type == EventType.MouseDown && Event.current.button == 1
  1246. && Event.current.control && !Event.current.alt && !Event.current.shift
  1247. && LineManager.editModeType == LineManager.EditModeType.NODES)
  1248. {
  1249. if (MouseDot(out Vector3 point, out Vector3 normal, lineData.settings.mode, sceneView.in2DMode,
  1250. lineData.settings.paintOnPalettePrefabs, lineData.settings.paintOnMeshesWithoutCollider, false))
  1251. {
  1252. point = _snapToVertex ? LinePointSnapping(point)
  1253. : SnapAndUpdateGridOrigin(point, SnapManager.settings.snappingEnabled,
  1254. lineData.settings.paintOnPalettePrefabs, lineData.settings.paintOnMeshesWithoutCollider,
  1255. false, Vector3.down);
  1256. lineData.AddPoint(point, false);
  1257. if (persistent)
  1258. {
  1259. PreviewPersistentLine(_selectedPersistentLineData);
  1260. LineStrokePreview(sceneView, lineData, persistent: true, forceUpdate: true, _firstNewObjIdx);
  1261. }
  1262. else updateStroke = true;
  1263. }
  1264. }
  1265. else if (PWBSettings.shortcuts.lineSelectAllPoints.Check()
  1266. && LineManager.editModeType == LineManager.EditModeType.NODES)
  1267. lineData.SelectAll();
  1268. else if (PWBSettings.shortcuts.lineDeselectAllPoints.Check()) lineData.ClearSelection();
  1269. else if (PWBSettings.shortcuts.lineToggleCurve.Check())
  1270. {
  1271. lineData.ToggleSegmentType();
  1272. updateStroke = true;
  1273. }
  1274. else if (PWBSettings.shortcuts.lineToggleClosed.Check())
  1275. {
  1276. lineData.ToggleClosed();
  1277. updateStroke = true;
  1278. }
  1279. else if (PWBSettings.shortcuts.lineEditGap.Check())
  1280. {
  1281. var deltaSign = Mathf.Sign(PWBSettings.shortcuts.lineEditGap.combination.delta);
  1282. lineData.settings.gapSize += lineData.lenght * deltaSign * 0.001f;
  1283. ToolProperties.RepainWindow();
  1284. }
  1285. if (!persistent) return;
  1286. if (PWBSettings.shortcuts.editModeSelectParent.Check() && lineData != null)
  1287. {
  1288. var parent = lineData.GetParent();
  1289. if (parent != null) UnityEditor.Selection.activeGameObject = parent;
  1290. }
  1291. else if (PWBSettings.shortcuts.editModeDeleteItemButNotItsChildren.Check())
  1292. LineManager.instance.DeletePersistentItem(lineData.id, false);
  1293. else if (PWBSettings.shortcuts.editModeDeleteItemAndItsChildren.Check())
  1294. LineManager.instance.DeletePersistentItem(lineData.id, true);
  1295. else if (PWBSettings.shortcuts.editModeDuplicate.Check()) DuplicateItem(lineData.id);
  1296. else if (PWBSettings.shortcuts.lineEditModeTypeToggle.Check())
  1297. LineManager.ToggleEditModeType();
  1298. }
  1299. #endregion
  1300. #region EDIT MODE
  1301. private static System.Collections.Generic.HashSet<GameObject> _disabledObjects
  1302. = new System.Collections.Generic.HashSet<GameObject>();
  1303. private static bool _editingPersistentLine = false;
  1304. private static LineData _initialPersistentLineData = null;
  1305. private static LineData _selectedPersistentLineData = null;
  1306. private static System.Collections.Generic.Dictionary<long, IPersistentData.Visibility> _prevDataVisibility
  1307. = new System.Collections.Generic.Dictionary<long, IPersistentData.Visibility>();
  1308. private static void UpdateDataPrevVisibility(IPersistentData data)
  1309. {
  1310. if (data.visibility == IPersistentData.Visibility.HIDE_ALL)
  1311. UnityEditor.SceneVisibilityManager.instance.Hide(data.objects, true);
  1312. else UnityEditor.SceneVisibilityManager.instance.Show(data.objects, true);
  1313. if (_prevDataVisibility.ContainsKey(data.id)) _prevDataVisibility[data.id] = data.visibility;
  1314. else _prevDataVisibility.Add(data.id, data.visibility);
  1315. }
  1316. public static void SelectLine(LineData data)
  1317. {
  1318. ApplySelectedPersistentLine(true);
  1319. _editingPersistentLine = true;
  1320. data.ClearSelection();
  1321. data.selectedPointIdx = 0;
  1322. data.showHandles = true;
  1323. _selectedPersistentLineData = data;
  1324. if (_initialPersistentLineData == null) _initialPersistentLineData = data.Clone();
  1325. LineManager.instance.CopyToolSettings(data.settings);
  1326. }
  1327. private static void LineToolEditMode(UnityEditor.SceneView sceneView)
  1328. {
  1329. var persistentLines = LineManager.instance.GetPersistentItems();
  1330. var selectedLineId = _initialPersistentLineData == null ? -1 : _initialPersistentLineData.id;
  1331. bool clickOnAnyPoint = false;
  1332. bool someLinesWereEdited = false;
  1333. var delta = Vector3.zero;
  1334. var editedData = _selectedPersistentLineData;
  1335. DrawSelectionRectangle();
  1336. foreach (var lineData in persistentLines)
  1337. {
  1338. if(lineData.pointsCount <= 2)
  1339. {
  1340. void DeleteItem()
  1341. {
  1342. LineManager.instance.DeletePersistentItem(lineData.id, deleteObjects: true, registerUndo: false);
  1343. PWBItemsWindow.RepainWindow();
  1344. }
  1345. if (lineData.pointsCount <= 1)
  1346. {
  1347. DeleteItem();
  1348. continue;
  1349. }
  1350. var points = lineData.points;
  1351. if (points[0] == points[1] && points[0] == Vector3.zero)
  1352. {
  1353. DeleteItem();
  1354. continue;
  1355. }
  1356. }
  1357. if (!_prevDataVisibility.ContainsKey(lineData.id) || lineData.visibility != _prevDataVisibility[lineData.id])
  1358. {
  1359. if (lineData.visibility == IPersistentData.Visibility.HIDE_ALL)
  1360. UnityEditor.SceneVisibilityManager.instance.Hide(lineData.objects, true);
  1361. else UnityEditor.SceneVisibilityManager.instance.Show(lineData.objects, true);
  1362. UpdateDataPrevVisibility(lineData);
  1363. }
  1364. if (lineData.visibility != IPersistentData.Visibility.SHOW_ALL) continue;
  1365. DrawLine(lineData, drawSurfacePath: lineData.selectionCount > 0);
  1366. if (DrawLineControlPoints(lineData, isPersistent: true, ToolManager.editMode,
  1367. out bool clickOnPoint, out bool multiSelection, out bool addToselection,
  1368. out bool removedFromSelection, out bool wasEdited, out Vector3 localDelta))
  1369. {
  1370. if (clickOnPoint)
  1371. {
  1372. clickOnAnyPoint = true;
  1373. _editingPersistentLine = true;
  1374. if (selectedLineId != lineData.id)
  1375. {
  1376. ApplySelectedPersistentLine(false);
  1377. if (selectedLineId == -1) _createProfileName = LineManager.instance.selectedProfileName;
  1378. else if (!addToselection && !removedFromSelection)
  1379. {
  1380. var selectedLines
  1381. = persistentLines.Where(i => i != lineData && i.selectionCount > 0).ToArray();
  1382. foreach (var selected in selectedLines)
  1383. {
  1384. PWBCore.SetActiveTempColliders(selected.objects, true);
  1385. selected.showHandles = false;
  1386. selected.ClearSelection();
  1387. }
  1388. }
  1389. LineManager.instance.CopyToolSettings(lineData.settings);
  1390. ToolProperties.RepainWindow();
  1391. PWBCore.SetActiveTempColliders(lineData.objects, false);
  1392. }
  1393. _selectedPersistentLineData = lineData;
  1394. if (_initialPersistentLineData == null) _initialPersistentLineData = lineData.Clone();
  1395. else if (_initialPersistentLineData.id != lineData.id) _initialPersistentLineData = lineData.Clone();
  1396. if (!removedFromSelection) foreach (var l in persistentLines) l.showHandles = (l == lineData);
  1397. }
  1398. if (addToselection) lineData.showHandles = true;
  1399. if (wasEdited)
  1400. {
  1401. _editingPersistentLine = true;
  1402. someLinesWereEdited = true;
  1403. delta = localDelta;
  1404. editedData = lineData;
  1405. _persistentItemWasEdited = true;
  1406. }
  1407. }
  1408. }
  1409. var repaintItemsWindow = false;
  1410. foreach (var lineData in persistentLines)
  1411. {
  1412. var isSelected = lineData.selectionCount > 0;
  1413. if (lineData.isSelected != isSelected) repaintItemsWindow = true;
  1414. lineData.isSelected = lineData.selectionCount > 0;
  1415. }
  1416. if (repaintItemsWindow) PWBItemsWindow.RepainWindow();
  1417. var linesEdited = persistentLines.Where(i => i.selectionCount > 0).ToArray();
  1418. if (someLinesWereEdited)
  1419. {
  1420. if (linesEdited.Length > 0) _disabledObjects.Clear();
  1421. if (linesEdited.Length > 1)
  1422. {
  1423. _paintStroke.Clear();
  1424. foreach (var lineData in linesEdited)
  1425. {
  1426. if (lineData != editedData) lineData.AddDeltaToSelection(delta);
  1427. lineData.UpdatePath(forceUpdate: false, updateOnSurfacePoints: true);
  1428. PreviewPersistentLine(lineData);
  1429. LineStrokePreview(sceneView, lineData, persistent: true, forceUpdate: true, _firstNewObjIdx);
  1430. }
  1431. PWBCore.SetSavePending();
  1432. return;
  1433. }
  1434. }
  1435. if (linesEdited.Length > 1) PreviewPersistent(sceneView.camera);
  1436. if (!ToolManager.editMode) return;
  1437. if (LineManager.editModeType == LineManager.EditModeType.NODES) SelectionRectangleInput(clickOnAnyPoint);
  1438. bool skipPreview = _selectedPersistentLineData != null
  1439. && _selectedPersistentLineData.objectCount > PWBCore.staticData.maxPreviewCountInEditMode;
  1440. if (!skipPreview)
  1441. {
  1442. if ((!someLinesWereEdited && linesEdited.Length <= 1)
  1443. && _editingPersistentLine && _selectedPersistentLineData != null)
  1444. {
  1445. var forceStrokeUpdate = updateStroke;
  1446. if (updateStroke)
  1447. {
  1448. _selectedPersistentLineData.UpdatePath(forceUpdate: false, updateOnSurfacePoints: true);
  1449. PreviewPersistentLine(_selectedPersistentLineData);
  1450. updateStroke = false;
  1451. PWBCore.SetSavePending();
  1452. }
  1453. if (_brushstroke != null
  1454. && !BrushstrokeManager.BrushstrokeEqual(BrushstrokeManager.brushstroke, _brushstroke))
  1455. _paintStroke.Clear();
  1456. LineStrokePreview(sceneView, _selectedPersistentLineData,
  1457. persistent: true, forceStrokeUpdate, _firstNewObjIdx);
  1458. }
  1459. }
  1460. LineInput(true, sceneView, skipPreview);
  1461. }
  1462. private static int _firstNewObjIdx = 0;
  1463. public static void PreviewSelectedPersistentLines()
  1464. {
  1465. if (ToolManager.tool != ToolManager.PaintTool.LINE) return;
  1466. var persistentLines = LineManager.instance.GetPersistentItems();
  1467. foreach (var lineData in persistentLines)
  1468. {
  1469. if (!lineData.isSelected) continue;
  1470. PreviewPersistentLine(lineData);
  1471. }
  1472. }
  1473. public static void PreviewPersistentLine(LineData lineData)
  1474. {
  1475. PWBCore.UpdateTempCollidersIfHierarchyChanged();
  1476. BrushstrokeObject[] objPos = null;
  1477. var objList = lineData.objectList;
  1478. Vector3[] strokePos = null;
  1479. var toolSettings = lineData.settings;
  1480. BrushstrokeManager.UpdatePersistentLineBrushstroke(lineData.pathPoints,
  1481. toolSettings, objList, out objPos, out strokePos, out _firstNewObjIdx);
  1482. _disabledObjects.UnionWith(lineData.objects);
  1483. float pathLength = 0;
  1484. var prevSegmentDir = Vector3.zero;
  1485. BrushSettings brushSettings = LineManager.instance.applyBrushToExisting ?
  1486. PaletteManager.selectedBrush : PaletteManager.GetBrushById(lineData.initialBrushId);
  1487. if (brushSettings == null && PaletteManager.selectedBrush != null)
  1488. {
  1489. brushSettings = PaletteManager.selectedBrush;
  1490. lineData.SetInitialBrushId(brushSettings.id);
  1491. }
  1492. if (toolSettings.overwriteBrushProperties) brushSettings = toolSettings.brushSettings;
  1493. if (brushSettings == null) brushSettings = new BrushSettings();
  1494. var objSet = lineData.objectSet;
  1495. float maxSurfaceHeight = 0f;
  1496. for (int i = 0; i < objPos.Length; ++i)
  1497. {
  1498. var objIdx = objPos[i].objIdx;
  1499. var obj = objList[objIdx];
  1500. if (obj == null)
  1501. {
  1502. lineData.RemovePose(objIdx);
  1503. continue;
  1504. }
  1505. obj.SetActive(true);
  1506. var objScale = objPos[i].objScale;
  1507. if (i > 0) pathLength += (objPos[i].objPosition - objPos[i - 1].objPosition).magnitude;
  1508. var prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(obj);
  1509. if (prefab == null) prefab = obj;
  1510. var bounds = BoundsUtils.GetBoundsRecursive(prefab.transform, prefab.transform.rotation,
  1511. ignoreDissabled: true, BoundsUtils.ObjectProperty.BOUNDING_BOX, recursive: true, useDictionary: false);
  1512. var size = Vector3.Scale(bounds.size, objScale);
  1513. var height = size.x + size.y + size.z + maxSurfaceHeight + pathLength;
  1514. Vector3 segmentDir = Vector3.zero;
  1515. var objOnLineSize = AxesUtils.GetAxisValue(size, toolSettings.axisOrientedAlongTheLine);
  1516. segmentDir = objPos[i].brushstrokeDirection;
  1517. var perpendicularToTheSurface = toolSettings.perpendicularToTheSurface
  1518. || (brushSettings.rotateToTheSurface && !brushSettings.alwaysOrientUp);
  1519. if (toolSettings.objectsOrientedAlongTheLine && !perpendicularToTheSurface)
  1520. {
  1521. var projectionAxis = ((AxesUtils.SignedAxis)(toolSettings.projectionDirection)).axis;
  1522. segmentDir -= AxesUtils.GetVector(AxesUtils.GetAxisValue(segmentDir, projectionAxis), projectionAxis);
  1523. }
  1524. var normal = -toolSettings.projectionDirection;
  1525. var otherAxes = AxesUtils.GetOtherAxes((AxesUtils.SignedAxis)(-toolSettings.projectionDirection));
  1526. var tangetAxis = otherAxes[toolSettings.objectsOrientedAlongTheLine ? 0 : 1];
  1527. Vector3 itemTangent = (AxesUtils.SignedAxis)(tangetAxis);
  1528. var itemRotation = Quaternion.LookRotation(itemTangent, normal);
  1529. var lookAt = Quaternion.LookRotation((Vector3)(AxesUtils.SignedAxis)
  1530. (toolSettings.axisOrientedAlongTheLine), Vector3.up);
  1531. if (segmentDir != Vector3.zero) itemRotation = Quaternion.LookRotation(segmentDir, normal) * lookAt;
  1532. var itemPosition = objPos[i].objPosition;
  1533. var ray = new Ray(itemPosition + normal * height, -normal);
  1534. if (toolSettings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  1535. {
  1536. if (MouseRaycast(ray, out RaycastHit itemHit, out GameObject collider, maxDistance: float.MaxValue,
  1537. layerMask: -1, toolSettings.paintOnPalettePrefabs, toolSettings.paintOnMeshesWithoutCollider,
  1538. tags: null, terrainLayers: null, exceptions: objSet, sameOriginAsRay: false, origin: itemPosition))
  1539. {
  1540. itemPosition = itemHit.point;
  1541. if (perpendicularToTheSurface) normal = itemHit.normal;
  1542. var surfObj = PWBCore.GetGameObjectFromTempCollider(collider);
  1543. var surfSize = BoundsUtils.GetBounds(surfObj.transform).size;
  1544. var h = surfSize.x + surfSize.y + surfSize.z;
  1545. maxSurfaceHeight = Mathf.Max(h, maxSurfaceHeight);
  1546. }
  1547. else if (toolSettings.mode == PaintOnSurfaceToolSettingsBase.PaintMode.ON_SURFACE) continue;
  1548. }
  1549. if (perpendicularToTheSurface && segmentDir != Vector3.zero)
  1550. {
  1551. if (toolSettings.mode == PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  1552. {
  1553. var bitangent = Vector3.Cross(segmentDir, normal);
  1554. var lineNormal = Vector3.Cross(bitangent, segmentDir);
  1555. itemRotation = Quaternion.LookRotation(segmentDir, lineNormal) * lookAt;
  1556. }
  1557. else
  1558. {
  1559. var plane = new Plane(normal, itemPosition);
  1560. var tangent = plane.ClosestPointOnPlane(segmentDir + itemPosition) - itemPosition;
  1561. itemRotation = Quaternion.LookRotation(tangent, normal) * lookAt;
  1562. }
  1563. }
  1564. else if (!perpendicularToTheSurface && segmentDir != Vector3.zero)
  1565. if (!toolSettings.perpendicularToTheSurface
  1566. && brushSettings.rotateToTheSurface && brushSettings.alwaysOrientUp)
  1567. {
  1568. var fw = itemRotation * Vector3.forward;
  1569. const float minMag = 1e-6f;
  1570. fw.y = 0;
  1571. if (Mathf.Abs(fw.x) < minMag && Mathf.Abs(fw.z) < minMag) fw = Quaternion.Euler(0, 90, 0) * normal;
  1572. itemRotation = Quaternion.LookRotation(fw, Vector3.up);
  1573. }
  1574. var pivotToCenter = bounds.center - prefab.transform.position;
  1575. pivotToCenter = new Vector3(pivotToCenter.x / prefab.transform.localScale.x,
  1576. pivotToCenter.y / prefab.transform.localScale.y, pivotToCenter.z / prefab.transform.localScale.z);
  1577. pivotToCenter = itemRotation * Vector3.Scale(pivotToCenter, objScale);
  1578. itemPosition += normal * (size.y / 2) - pivotToCenter;
  1579. if (LineManager.instance.applyBrushToExisting)
  1580. {
  1581. if (brushSettings.embedInSurface
  1582. && toolSettings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  1583. {
  1584. var bottomMagnitude = BoundsUtils.GetBottomMagnitude(obj.transform);
  1585. if (brushSettings.embedAtPivotHeight)
  1586. itemPosition += itemRotation * (normal * bottomMagnitude);
  1587. else
  1588. {
  1589. var TRS = Matrix4x4.TRS(itemPosition, itemRotation, objScale);
  1590. var bottomVertices = BoundsUtils.GetBottomVertices(obj.transform);
  1591. var bottomDistanceToSurfce = GetBottomDistanceToSurface(bottomVertices, TRS,
  1592. Mathf.Abs(bottomMagnitude), toolSettings.paintOnPalettePrefabs,
  1593. toolSettings.paintOnMeshesWithoutCollider, out Transform surfaceTransform,
  1594. exceptions: objSet);
  1595. itemPosition += itemRotation * (normal * -bottomDistanceToSurfce);
  1596. }
  1597. }
  1598. itemPosition += normal * objPos[i].surfaceDistance;
  1599. itemPosition += itemRotation * brushSettings.localPositionOffset;
  1600. var additionalAngle = brushSettings.GetAdditionalAngle();
  1601. if (additionalAngle != Vector3.zero) itemRotation *= Quaternion.Euler(additionalAngle);
  1602. var flipX = brushSettings.GetFlipX();
  1603. var flipY = brushSettings.GetFlipY();
  1604. if (flipX || flipY)
  1605. {
  1606. var spriteRenderers = obj.GetComponentsInChildren<SpriteRenderer>();
  1607. foreach (var spriteRenderer in spriteRenderers)
  1608. {
  1609. UnityEditor.Undo.RecordObject(spriteRenderer, LineData.COMMAND_NAME);
  1610. spriteRenderer.flipX = flipX;
  1611. spriteRenderer.flipY = flipY;
  1612. }
  1613. }
  1614. }
  1615. UnityEditor.Undo.RecordObject(obj.transform, LineData.COMMAND_NAME);
  1616. obj.transform.SetPositionAndRotation(itemPosition, itemRotation);
  1617. obj.transform.localScale = objScale;
  1618. _disabledObjects.Remove(obj);
  1619. lineData.lastTangentPos = objPos[i].objPosition;
  1620. }
  1621. foreach (var obj in _disabledObjects) if (obj != null) obj.SetActive(false);
  1622. }
  1623. private static void ResetSelectedPersistentLine()
  1624. {
  1625. _editingPersistentLine = false;
  1626. if (_initialPersistentLineData == null) return;
  1627. var selectedLine = LineManager.instance.GetItem(_initialPersistentLineData.id);
  1628. if (selectedLine == null) return;
  1629. selectedLine.ResetPoses(_initialPersistentLineData);
  1630. selectedLine.ClearSelection();
  1631. }
  1632. private static void ApplySelectedPersistentLine(bool deselectPoint)
  1633. {
  1634. if (!_persistentItemWasEdited) return;
  1635. _persistentItemWasEdited = false;
  1636. if (!ApplySelectedPersistentObject(deselectPoint, ref _editingPersistentLine, ref _initialPersistentLineData,
  1637. ref _selectedPersistentLineData, LineManager.instance)) return;
  1638. if (_initialPersistentLineData == null) return;
  1639. var selected = LineManager.instance.GetItem(_initialPersistentLineData.id);
  1640. _initialPersistentLineData = selected.Clone();
  1641. }
  1642. private static void DeselectPersistentLines()
  1643. {
  1644. var persistentLines = LineManager.instance.GetPersistentItems();
  1645. foreach (var l in persistentLines) l.ClearSelection();
  1646. }
  1647. #endregion
  1648. }
  1649. #endregion
  1650. }