ShapeManager.cs 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376
  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 ShapeData : PersistentData<ShapeToolName, ShapeSettings, ControlPoint>
  20. {
  21. [SerializeField] private float _radius = 0f;
  22. [SerializeField] private float _arcAngle = 360f;
  23. [SerializeField] private Vector3 _normal = Vector3.up;
  24. [SerializeField] private Plane _plane;
  25. [SerializeField] private int _firstVertexIdxAfterIntersection = 2;
  26. [SerializeField] private int _lastVertexIdxBeforeIntersection = 1;
  27. [SerializeField] private Vector3[] _arcIntersections = new Vector3[2];
  28. private System.Collections.Generic.List<Vector3> _onSurfacePoints = new System.Collections.Generic.List<Vector3>();
  29. private int _circleSideCount = 8;
  30. public Vector3 normal
  31. {
  32. get => _normal;
  33. set
  34. {
  35. if (_normal == value) return;
  36. _normal = value;
  37. }
  38. }
  39. public int circleSideCount => _circleSideCount;
  40. public void SetCenter(Vector3 value, Vector3 normal)
  41. {
  42. if (pointsCount == 0)
  43. {
  44. AddPoint(value, false);
  45. AddPoint(value, false);
  46. }
  47. else if (points[0] != value)
  48. {
  49. SetPoint(0, value, registerUndo: false, selectAll: false);
  50. SetPoint(1, value, registerUndo: false, selectAll: false);
  51. }
  52. _normal = normal;
  53. _plane = new Plane(_normal, points[0]);
  54. if (_settings.projectInNormalDir) _settings.UpdateProjectDirection(-_normal);
  55. }
  56. public void SetRadius(Vector3 point)
  57. {
  58. SetPoint(1, point, registerUndo: false, selectAll: false);
  59. _radius = Mathf.Max((points[1] - points[0]).magnitude, 0.001f);
  60. if (_settings.shapeType == ShapeSettings.ShapeType.CIRCLE) UpdateCircleSideCount();
  61. }
  62. public float radius => _radius;
  63. public Vector3 radiusPoint => points[1];
  64. public Plane plane => _plane;
  65. public Vector3 center => points[0];
  66. public float arcAngle => _arcAngle;
  67. public Vector3 GetArcIntersection(int idx) => _arcIntersections[idx];
  68. public int firstVertexIdxAfterIntersection => _firstVertexIdxAfterIntersection;
  69. public int lastVertexIdxBeforeIntersection => _lastVertexIdxBeforeIntersection;
  70. public void SetHandlePoints(Vector3[] vertices)
  71. {
  72. if (pointsCount > 2) PointsRemoveRange(2, pointsCount - 2);
  73. var midPoints = new System.Collections.Generic.List<Vector3>();
  74. for (int i = 1; i < vertices.Length; ++i)
  75. {
  76. AddPoint(vertices[i]);
  77. if (_settings.shapeType == ShapeSettings.ShapeType.POLYGON)
  78. midPoints.Add((vertices[i] - vertices[i - 1]) / 2 + vertices[i - 1]);
  79. }
  80. if (_settings.shapeType == ShapeSettings.ShapeType.POLYGON)
  81. {
  82. midPoints.Add((vertices[vertices.Length - 1] - vertices[0]) / 2 + vertices[0]);
  83. AddPointRange(ControlPoint.VectorArrayToPointArray(midPoints.ToArray()));
  84. }
  85. var arcPoint = points[1] + (points[1] - points[0]);
  86. AddPoint(arcPoint);
  87. AddPoint(arcPoint);
  88. _arcIntersections[0] = points[1];
  89. _arcIntersections[1] = points[1];
  90. UpdateOnSurfacePoints();
  91. }
  92. public Vector3[] vertices => ControlPoint.PointArrayToVectorArray(PointsGetRange(1,
  93. _settings.shapeType == ShapeSettings.ShapeType.POLYGON ? _settings.sidesCount : _circleSideCount));
  94. public Vector3[] onSurfacePoints => _onSurfacePoints.ToArray();
  95. public Quaternion planeRotation
  96. {
  97. get
  98. {
  99. var forward = Vector3.Cross(_normal, Vector3.right);
  100. if (forward.sqrMagnitude < 0.000001) forward = Vector3.Cross(_normal, Vector3.down);
  101. return Quaternion.LookRotation(forward, _normal);
  102. }
  103. }
  104. private static bool LineLineIntersection(out Vector3 intersection, Vector3 linePoint1,
  105. Vector3 lineVec1, Vector3 linePoint2, Vector3 lineVec2)
  106. {
  107. Vector3 lineVec3 = linePoint2 - linePoint1;
  108. Vector3 crossVec1and2 = Vector3.Cross(lineVec1, lineVec2);
  109. Vector3 crossVec3and2 = Vector3.Cross(lineVec3, lineVec2);
  110. float planarFactor = Mathf.Abs(90 - Vector3.Angle(lineVec3, crossVec1and2));
  111. if (planarFactor < 0.01f && crossVec1and2.sqrMagnitude > 0.001f)
  112. {
  113. float s = Vector3.Dot(crossVec3and2, crossVec1and2) / crossVec1and2.sqrMagnitude;
  114. intersection = linePoint1 + (lineVec1 * s);
  115. var min = Vector3.Max(Vector3.Min(linePoint1, linePoint1 + lineVec1),
  116. Vector3.Min(linePoint2, linePoint2 + lineVec2));
  117. var max = Vector3.Min(Vector3.Max(linePoint1, linePoint1 + lineVec1),
  118. Vector3.Max(linePoint2, linePoint2 + lineVec2));
  119. var tolerance = Vector3.one * 0.001f;
  120. var minComp = intersection + tolerance - min;
  121. var maxComp = max + tolerance - intersection;
  122. var result = minComp.x >= 0 && minComp.y >= 0 && minComp.z >= 0
  123. && maxComp.x >= 0 && maxComp.y >= 0 && maxComp.z >= 0;
  124. return result;
  125. }
  126. else
  127. {
  128. intersection = Vector3.zero;
  129. return false;
  130. }
  131. }
  132. public void UpdateIntersections()
  133. {
  134. if (state < ToolManager.ToolState.EDIT) return;
  135. var centerToArc1 = GetPoint(-1) - center;
  136. var centerToArc2 = GetPoint(-2) - center;
  137. bool firstPointFound = false;
  138. bool lastPointFound = false;
  139. var sidesCount = _settings.shapeType == ShapeSettings.ShapeType.POLYGON
  140. ? _settings.sidesCount : _circleSideCount;
  141. int GetNextVertexIdx(int currentIdx) => currentIdx == sidesCount ? 1 : currentIdx + 1;
  142. for (int i = 1; i <= sidesCount; ++i)
  143. {
  144. var startPoint = GetPoint(i);
  145. var endIdx = GetNextVertexIdx(i);
  146. var endPoint = GetPoint(endIdx);
  147. var startToEnd = endPoint - startPoint;
  148. if (!firstPointFound)
  149. {
  150. if (LineLineIntersection(out Vector3 intersection, center, centerToArc1,
  151. startPoint, startToEnd))
  152. {
  153. firstPointFound = true;
  154. _firstVertexIdxAfterIntersection = endIdx;
  155. _arcIntersections[0] = intersection;
  156. }
  157. }
  158. if (!lastPointFound)
  159. {
  160. if (LineLineIntersection(out Vector3 intersection, center, centerToArc2,
  161. startPoint, startToEnd))
  162. {
  163. lastPointFound = true;
  164. _lastVertexIdxBeforeIntersection = i;
  165. _arcIntersections[1] = intersection;
  166. }
  167. }
  168. if (firstPointFound && lastPointFound) break;
  169. }
  170. }
  171. public Quaternion rotation
  172. {
  173. get
  174. {
  175. var radiusVector = radiusPoint - center;
  176. if (radiusVector == Vector3.zero)
  177. {
  178. radiusVector = Vector3.Cross(normal, Vector3.right);
  179. if (radiusVector.sqrMagnitude < 0.000001) radiusVector = Vector3.Cross(normal, Vector3.down);
  180. }
  181. return Quaternion.LookRotation(radiusVector, _normal);
  182. }
  183. set
  184. {
  185. var prevRadiusVector = radiusPoint - center;
  186. if (prevRadiusVector == Vector3.zero)
  187. {
  188. prevRadiusVector = Vector3.Cross(normal, Vector3.right);
  189. if (prevRadiusVector.sqrMagnitude < 0.000001) prevRadiusVector = Vector3.Cross(normal, Vector3.down);
  190. }
  191. var prev = Quaternion.LookRotation(prevRadiusVector, normal);
  192. _plane.normal = _normal = value * Vector3.up;
  193. var delta = value * Quaternion.Inverse(prev);
  194. for (int i = 0; i < pointsCount - 2; ++i)
  195. SetPoint(i, delta * (points[i] - center) + center, registerUndo: false, selectAll: false);
  196. SetPoint(pointsCount - 1, delta * (points[pointsCount - 1] - center).normalized
  197. * _radius * 2f + center, registerUndo: false, selectAll: false);
  198. SetPoint(pointsCount - 2, delta * (points[pointsCount - 2] - center).normalized
  199. * _radius * 2f + center, registerUndo: false, selectAll: false);
  200. UpdateIntersections();
  201. if (_settings.projectInNormalDir) _settings.UpdateProjectDirection(-_normal);
  202. UpdateOnSurfacePoints();
  203. }
  204. }
  205. public void MovePoint(int idx, Vector3 position)
  206. {
  207. if (position == points[idx]) return;
  208. var delta = position - points[idx];
  209. if (idx == 0)
  210. {
  211. for (int i = 0; i < pointsCount; ++i)
  212. SetPoint(i, points[i] + delta, registerUndo: true, selectAll: false);
  213. _arcIntersections[0] += delta;
  214. _arcIntersections[1] += delta;
  215. }
  216. else
  217. {
  218. var normalDelta = Vector3.Project(delta, _normal);
  219. var centerToPoint = points[idx] - center;
  220. var radiusDelta = Vector3.Project(delta, centerToPoint);
  221. var newRadius = position - center - normalDelta;
  222. var angle = Vector3.SignedAngle(centerToPoint, newRadius, _normal);
  223. var rotation = Quaternion.AngleAxis(angle, _normal);
  224. if ((_settings.shapeType == ShapeSettings.ShapeType.CIRCLE && idx == 1)
  225. || (_settings.shapeType == ShapeSettings.ShapeType.POLYGON
  226. && idx <= _settings.sidesCount * 2))
  227. {
  228. _radius = newRadius.magnitude;
  229. var radiusScale = _radius < 0.1f ? 1f : 1f + radiusDelta.magnitude / _radius
  230. * (Vector3.Dot(centerToPoint, radiusDelta) >= 0 ? 1f : -1f);
  231. for (int i = 0; i < pointsCount - 2; ++i)
  232. SetPoint(i, rotation * (points[i] - center) * radiusScale + normalDelta + center,
  233. registerUndo: false, selectAll: false);
  234. SetPoint(pointsCount - 1, rotation * (points[pointsCount - 1] - center).normalized
  235. * _radius * 2f + center + normalDelta, registerUndo: false, selectAll: false);
  236. SetPoint(pointsCount - 2, rotation * (points[pointsCount - 2] - center).normalized
  237. * _radius * 2f + center + normalDelta, registerUndo: true, selectAll: false);
  238. }
  239. else
  240. {
  241. SetPoint(idx, rotation * (points[idx] - center) + center, registerUndo: true, selectAll: false);
  242. if (normalDelta != Vector3.zero)
  243. {
  244. for (int i = 0; i < pointsCount; ++i)
  245. SetPoint(i, points[i] + normalDelta, registerUndo: true, selectAll: false);
  246. }
  247. _arcAngle = Vector3.SignedAngle(GetPoint(-1) - center, GetPoint(-2) - center, normal);
  248. if (_arcAngle <= 0) _arcAngle += 360;
  249. }
  250. UpdateIntersections();
  251. }
  252. UpdateOnSurfacePoints();
  253. }
  254. public bool UpdateCircleSideCount()
  255. {
  256. var perimenter = 2 * Mathf.PI * _radius;
  257. var maxItemSize = 1f;
  258. if (PaletteManager.selectedBrush != null)
  259. {
  260. maxItemSize = float.MinValue;
  261. for (int i = 0; i < PaletteManager.selectedBrush.itemCount; ++i)
  262. {
  263. var item = PaletteManager.selectedBrush.items[i];
  264. var scale = item.randomScaleMultiplier
  265. ? item.randomScaleMultiplierRange.randomVector : item.scaleMultiplier;
  266. if (LineManager.settings.overwriteBrushProperties)
  267. scale = LineManager.settings.brushSettings.randomScaleMultiplier
  268. ? LineManager.settings.brushSettings.randomScaleMultiplierRange.max
  269. : LineManager.settings.brushSettings.scaleMultiplier;
  270. maxItemSize = Mathf.Max(BrushstrokeManager.GetLineSpacing(i, _settings, scale), maxItemSize);
  271. }
  272. }
  273. var prevCount = _circleSideCount;
  274. _circleSideCount = Mathf.FloorToInt(perimenter / maxItemSize);
  275. var sideLenght = 2 * _radius * Mathf.Sin(Mathf.PI / _circleSideCount);
  276. if (sideLenght <= maxItemSize) --_circleSideCount;
  277. _circleSideCount = Mathf.Max(_circleSideCount, 32);
  278. return prevCount != _circleSideCount;
  279. }
  280. protected override void Initialize()
  281. {
  282. base.Initialize();
  283. _arcIntersections[0] = _arcIntersections[1] = Vector3.zero;
  284. _radius = 0f;
  285. _arcAngle = 360f;
  286. _normal = Vector3.up;
  287. _plane = new Plane();
  288. _firstVertexIdxAfterIntersection = 2;
  289. _lastVertexIdxBeforeIntersection = 1;
  290. _circleSideCount = 8;
  291. }
  292. public void Update(bool clearSelection)
  293. {
  294. if (pointsCount < 2) return;
  295. ToolProperties.RegisterUndo(COMMAND_NAME);
  296. if (clearSelection) selectedPointIdx = -1;
  297. var arcPoints = PointsGetRange(pointsCount - 2, 2);
  298. var center = points[0];
  299. var polygonVertices = PWBIO.GetPolygonVertices(this);
  300. _controlPoints.Clear();
  301. _controlPoints.Add(center);
  302. _controlPoints.AddRange(ControlPoint.VectorArrayToPointArray(polygonVertices));
  303. if (_settings.shapeType == ShapeSettings.ShapeType.POLYGON)
  304. {
  305. for (int i = 1; i < polygonVertices.Length; ++i)
  306. _controlPoints.Add((polygonVertices[i] - polygonVertices[i - 1]) / 2 + polygonVertices[i - 1]);
  307. _controlPoints.Add((polygonVertices[polygonVertices.Length - 1]
  308. - polygonVertices[0]) / 2 + polygonVertices[0]);
  309. }
  310. _controlPoints.AddRange(arcPoints);
  311. UpdatePoints();
  312. }
  313. private void UpdateOnSurfacePoints()
  314. {
  315. var objSet = objectSet;
  316. Vector3 OnSurface(Vector3 point)
  317. {
  318. var maxDistance = radius * 20;
  319. var downRay = new Ray(point, -_normal);
  320. RaycastHit downHit;
  321. float downDistance = float.MaxValue;
  322. if (PWBIO.MouseRaycast(downRay, out downHit, out GameObject cd1,
  323. maxDistance, -1,
  324. paintOnPalettePrefabs: true, castOnMeshesWithoutCollider: true,
  325. tags: null, terrainLayers: null, exceptions: objSet))
  326. downDistance = downHit.distance;
  327. else
  328. {
  329. downRay = new Ray(point + normal * maxDistance, -_normal);
  330. if (PWBIO.MouseRaycast(downRay, out downHit, out GameObject cd2,
  331. maxDistance * 2, -1, paintOnPalettePrefabs: true, castOnMeshesWithoutCollider: true,
  332. tags: null, terrainLayers: null, exceptions: objSet))
  333. downDistance = downHit.distance;
  334. }
  335. if (downDistance >= float.MaxValue) return point;
  336. return downHit.point;
  337. }
  338. void AddPoints(Vector3 p0, Vector3 p1)
  339. {
  340. var segment = p1 - p0;
  341. var segmentLength = segment.magnitude;
  342. var pointCount = Mathf.CeilToInt(segmentLength / 0.25f);
  343. var delta = segment.normalized * (segmentLength / pointCount);
  344. _onSurfacePoints.Add(OnSurface(p0));
  345. for (int i = 0; i < pointCount - 1; ++i)
  346. {
  347. var p = p0 + delta;
  348. _onSurfacePoints.Add(OnSurface(p));
  349. p0 = p;
  350. }
  351. }
  352. var polygonVertices = vertices.ToList();
  353. polygonVertices.Add(polygonVertices[0]);
  354. _onSurfacePoints.Clear();
  355. for (int i = 0; i < polygonVertices.Count - 1; ++i) AddPoints(polygonVertices[i], polygonVertices[i + 1]);
  356. if (_onSurfacePoints.Count > 0) _onSurfacePoints.Add(_onSurfacePoints[0]);
  357. }
  358. public ShapeData() : base() { }
  359. public ShapeData((GameObject, int)[] objects, long initialBrushId, ShapeData shapeData)
  360. : base(objects, initialBrushId, shapeData) { }
  361. private static ShapeData _instance = null;
  362. public static ShapeData instance
  363. {
  364. get
  365. {
  366. if (_instance == null)
  367. {
  368. _instance = new ShapeData();
  369. _instance._settings = ShapeManager.settings;
  370. }
  371. return _instance;
  372. }
  373. }
  374. private void CopyShapeData(ShapeData other)
  375. {
  376. _radius = other._radius;
  377. _arcAngle = other._arcAngle;
  378. _normal = other._normal;
  379. _plane = other._plane;
  380. _firstVertexIdxAfterIntersection = other._firstVertexIdxAfterIntersection;
  381. _lastVertexIdxBeforeIntersection = other._lastVertexIdxBeforeIntersection;
  382. _arcIntersections = other._arcIntersections.ToArray();
  383. _circleSideCount = other._circleSideCount;
  384. }
  385. public override void Copy(PersistentData<ShapeToolName, ShapeSettings, ControlPoint> other)
  386. {
  387. base.Copy(other);
  388. var otherShapeData = other as ShapeData;
  389. if (otherShapeData == null) return;
  390. CopyShapeData(otherShapeData);
  391. }
  392. public ShapeData Clone()
  393. {
  394. var clone = new ShapeData();
  395. base.Clone(clone);
  396. clone.CopyShapeData(this);
  397. return clone;
  398. }
  399. }
  400. [System.Serializable]
  401. public class ShapeSettings : LineSettings
  402. {
  403. public enum ShapeType { CIRCLE, POLYGON }
  404. [SerializeField] private ShapeType _shapeType = ShapeType.POLYGON;
  405. [SerializeField] private int _sidesCount = 5;
  406. [SerializeField] private bool _axisNormalToSurface = true;
  407. [SerializeField] private Vector3 _normal = Vector3.up;
  408. [SerializeField] private bool _projectInNormalDir = true;
  409. public ShapeType shapeType
  410. {
  411. get => _shapeType;
  412. set
  413. {
  414. if (_shapeType == value) return;
  415. _shapeType = value;
  416. OnDataChanged();
  417. }
  418. }
  419. public int sidesCount
  420. {
  421. get => _sidesCount;
  422. set
  423. {
  424. value = Mathf.Max(value, 3);
  425. if (_sidesCount == value) return;
  426. _sidesCount = value;
  427. OnDataChanged();
  428. }
  429. }
  430. public bool axisNormalToSurface
  431. {
  432. get => _axisNormalToSurface;
  433. set
  434. {
  435. if (_axisNormalToSurface == value) return;
  436. _axisNormalToSurface = value;
  437. OnDataChanged();
  438. }
  439. }
  440. public Vector3 normal
  441. {
  442. get => _normal;
  443. set
  444. {
  445. if (_normal == value) return;
  446. _normal = value;
  447. OnDataChanged();
  448. }
  449. }
  450. public void SetNormalAndDontTriggerChangeEvent(Vector3 value) => _normal = value;
  451. public bool projectInNormalDir
  452. {
  453. get => _projectInNormalDir;
  454. set
  455. {
  456. if (_projectInNormalDir == value) return;
  457. _projectInNormalDir = value;
  458. OnDataChanged();
  459. }
  460. }
  461. public override void Copy(IToolSettings other)
  462. {
  463. base.Copy(other);
  464. var otherShapeSettings = other as ShapeSettings;
  465. if (otherShapeSettings == null) return;
  466. _shapeType = otherShapeSettings._shapeType;
  467. _sidesCount = otherShapeSettings._sidesCount;
  468. _axisNormalToSurface = otherShapeSettings._axisNormalToSurface;
  469. _normal = otherShapeSettings._normal;
  470. _projectInNormalDir = otherShapeSettings._projectInNormalDir;
  471. }
  472. public override void DataChanged()
  473. {
  474. base.DataChanged();
  475. if (!ToolManager.editMode) ShapeData.instance.Update(true);
  476. else PWBIO.OnShapeSettingsChanged();
  477. }
  478. }
  479. public class ShapeToolName : IToolName { public string value => "Shape"; }
  480. [System.Serializable]
  481. public class ShapeSceneData : SceneData<ShapeToolName, ShapeSettings, ControlPoint, ShapeData>
  482. {
  483. public ShapeSceneData() : base() { }
  484. public ShapeSceneData(string sceneGUID) : base(sceneGUID) { }
  485. }
  486. [System.Serializable]
  487. public class ShapeManager
  488. : PersistentToolManagerBase<ShapeToolName, ShapeSettings, ControlPoint, ShapeData, ShapeSceneData>
  489. { }
  490. #endregion
  491. #region PWBIO
  492. public static partial class PWBIO
  493. {
  494. #region HANDLERS
  495. private static void ShapeInitializeOnLoad()
  496. {
  497. ShapeManager.settings.OnDataChanged += OnShapeSettingsChanged;
  498. BrushSettings.OnBrushSettingsChanged += PreviewSelectedPersistentShapes;
  499. }
  500. private static void OnShapeToolModeChanged()
  501. {
  502. DeselectPersistentShapes();
  503. if (!ToolManager.editMode)
  504. {
  505. ToolProperties.RepainWindow();
  506. return;
  507. }
  508. ResetShapeState();
  509. ResetSelectedPersistentShape();
  510. }
  511. public static void OnShapeSettingsChanged()
  512. {
  513. if (!ToolManager.editMode) return;
  514. if (_selectedPersistentShapeData == null) return;
  515. _selectedPersistentShapeData.settings.Copy(ShapeManager.settings);
  516. _selectedPersistentShapeData.Update(false);
  517. PreviewPersistentShape(_selectedPersistentShapeData);
  518. }
  519. private static void OnUndoShape() => ClearShapeStroke();
  520. #endregion
  521. #region SPAWN MODE
  522. public static void ResetShapeState(bool askIfWantToSave = true)
  523. {
  524. if (askIfWantToSave)
  525. {
  526. void Save()
  527. {
  528. if (UnityEditor.SceneView.lastActiveSceneView != null)
  529. ShapeStrokePreview(UnityEditor.SceneView.lastActiveSceneView,
  530. ShapeData.nextHexId, forceUpdate: true, _shapeData);
  531. CreateShape();
  532. }
  533. AskIfWantToSave(_shapeData.state, Save);
  534. }
  535. _snappedToVertex = false;
  536. _shapeData.Reset();
  537. }
  538. private static void ShapeStateNone(bool in2DMode)
  539. {
  540. if (Event.current.button == 0 && Event.current.type == EventType.MouseDown && !Event.current.alt)
  541. _shapeData.state = ToolManager.ToolState.PREVIEW;
  542. if (MouseDot(out Vector3 point, out Vector3 normal, ShapeManager.settings.mode, in2DMode,
  543. ShapeManager.settings.paintOnPalettePrefabs, ShapeManager.settings.paintOnMeshesWithoutCollider, false))
  544. {
  545. if (ShapeManager.settings.projectInNormalDir) ShapeManager.settings.SetNormalAndDontTriggerChangeEvent(normal);
  546. point = SnapAndUpdateGridOrigin(point, SnapManager.settings.snappingEnabled,
  547. ShapeManager.settings.paintOnPalettePrefabs, ShapeManager.settings.paintOnMeshesWithoutCollider,
  548. false, Vector3.down);
  549. _shapeData.SetCenter(point, ShapeManager.settings.normal);
  550. }
  551. if (_shapeData.pointsCount > 0) DrawDotHandleCap(_shapeData.GetPoint(0));
  552. }
  553. private static void ShapeStateRadius(bool in2DMode, ShapeData shapeData)
  554. {
  555. if (Event.current.button == 0 && Event.current.type == EventType.MouseDown && !Event.current.alt)
  556. {
  557. shapeData.SetHandlePoints(GetPolygonVertices());
  558. shapeData.state = ToolManager.ToolState.EDIT;
  559. updateStroke = true;
  560. return;
  561. }
  562. var mouseRay = UnityEditor.HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
  563. if (_snapToVertex)
  564. {
  565. if (SnapToVertex(mouseRay, out RaycastHit closestVertexInfo, in2DMode))
  566. mouseRay.origin = closestVertexInfo.point - mouseRay.direction;
  567. }
  568. var radiusPoint = shapeData.center;
  569. if (shapeData.plane.Raycast(mouseRay, out float distance))
  570. radiusPoint = mouseRay.GetPoint(distance);
  571. radiusPoint = SnapAndUpdateGridOrigin(radiusPoint, SnapManager.settings.snappingEnabled,
  572. shapeData.settings.paintOnPalettePrefabs, shapeData.settings.paintOnMeshesWithoutCollider,
  573. false, Vector3.down);
  574. radiusPoint = ClosestPointOnPlane(radiusPoint, shapeData);
  575. shapeData.SetRadius(radiusPoint);
  576. DrawShapeLines(shapeData, drawSurfaceLine: true);
  577. DrawDotHandleCap(shapeData.center);
  578. DrawDotHandleCap(shapeData.radiusPoint);
  579. }
  580. private static void ShapeStateEdit(UnityEditor.SceneView sceneView)
  581. {
  582. var isCircle = ShapeManager.settings.shapeType == ShapeSettings.ShapeType.CIRCLE;
  583. var isPolygon = ShapeManager.settings.shapeType == ShapeSettings.ShapeType.POLYGON;
  584. var forceUpdate = updateStroke;
  585. if (updateStroke)
  586. {
  587. updateStroke = false;
  588. BrushstrokeManager.UpdateShapeBrushstroke();
  589. }
  590. ShapeStrokePreview(sceneView, ShapeData.nextHexId, forceUpdate, _shapeData);
  591. DrawShapeLines(_shapeData, drawSurfaceLine: true);
  592. DrawDotHandleCap(_shapeData.center);
  593. if (isPolygon)
  594. foreach (var vertex in _shapeData.vertices) DrawDotHandleCap(vertex);
  595. else DrawDotHandleCap(_shapeData.radiusPoint);
  596. if (_shapeData.selectedPointIdx >= 0 && _shapeData.selectedPointIdx < _shapeData.pointsCount)
  597. DrawDotHandleCap(_shapeData.selectedPoint, 1f, 1.2f);
  598. DrawDotHandleCap(_shapeData.GetPoint(-1));
  599. DrawDotHandleCap(_shapeData.GetPoint(-2));
  600. if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
  601. {
  602. CreateShape();
  603. ResetShapeState(false);
  604. }
  605. else if (Event.current.button == 1 && Event.current.type == EventType.MouseDrag
  606. && Event.current.shift && Event.current.control)
  607. {
  608. var deltaSign = Mathf.Sign(Event.current.delta.x + Event.current.delta.y);
  609. ShapeManager.settings.gapSize += Mathf.PI * _shapeData.radius * deltaSign * 0.001f;
  610. ToolProperties.RepainWindow();
  611. Event.current.Use();
  612. }
  613. bool clickOnPoint = false;
  614. for (int i = 0; i < _shapeData.pointsCount; ++i)
  615. {
  616. if (isCircle && i == 2) i = _shapeData.pointsCount - 2;
  617. var controlId = GUIUtility.GetControlID(FocusType.Passive);
  618. if (clickOnPoint) ToolProperties.RepainWindow();
  619. else
  620. {
  621. float distFromMouse = UnityEditor.HandleUtility.DistanceToRectangle(_shapeData.GetPoint(i),
  622. _shapeData.planeRotation, 0f);
  623. UnityEditor.HandleUtility.AddControl(controlId, distFromMouse);
  624. if (UnityEditor.HandleUtility.nearestControl != controlId) continue;
  625. if (isPolygon) DrawDotHandleCap(_shapeData.GetPoint(i));
  626. if (Event.current.button == 0 && Event.current.type == EventType.MouseDown)
  627. {
  628. _shapeData.selectedPointIdx = i;
  629. clickOnPoint = true;
  630. Event.current.Use();
  631. }
  632. }
  633. }
  634. if (_shapeData.selectedPointIdx >= 0)
  635. {
  636. var selectedPoint = _shapeData.selectedPoint;
  637. if (_updateHandlePosition)
  638. {
  639. selectedPoint = _handlePosition;
  640. _updateHandlePosition = false;
  641. }
  642. var prevPosition = _shapeData.selectedPoint;
  643. var snappedPoint = UnityEditor.Handles.PositionHandle(selectedPoint, _shapeData.planeRotation);
  644. snappedPoint = SnapAndUpdateGridOrigin(snappedPoint, SnapManager.settings.snappingEnabled,
  645. ShapeManager.settings.paintOnPalettePrefabs, ShapeManager.settings.paintOnMeshesWithoutCollider,
  646. false, Vector3.down);
  647. if (prevPosition != snappedPoint)
  648. {
  649. _shapeData.MovePoint(_shapeData.selectedPointIdx, snappedPoint);
  650. updateStroke = true;
  651. ToolProperties.RepainWindow();
  652. }
  653. _handlePosition = _shapeData.selectedPoint;
  654. if (_shapeData.selectedPointIdx == 0)
  655. {
  656. var selectedRotation = _shapeData.rotation;
  657. if (_updateHandleRotation)
  658. {
  659. selectedRotation = _handleRotation;
  660. _updateHandleRotation = false;
  661. }
  662. var prevRotation = _shapeData.rotation;
  663. var rotation = UnityEditor.Handles.RotationHandle(selectedRotation, _shapeData.center);
  664. if (prevRotation != rotation)
  665. {
  666. _shapeData.rotation = rotation;
  667. updateStroke = true;
  668. ToolProperties.RepainWindow();
  669. }
  670. _handleRotation = _shapeData.rotation;
  671. }
  672. }
  673. }
  674. private static void CreateShape()
  675. {
  676. var nextShapeId = ShapeData.nextHexId;
  677. var objDic = Paint(ShapeManager.settings, PAINT_CMD, true, false, nextShapeId);
  678. var objs = objDic[nextShapeId].ToArray();
  679. var scenePath = UnityEngine.SceneManagement.SceneManager.GetActiveScene().path;
  680. var sceneGUID = UnityEditor.AssetDatabase.AssetPathToGUID(scenePath);
  681. var initialBrushId = PaletteManager.selectedBrush != null ? PaletteManager.selectedBrush.id : -1;
  682. var persistentData = new ShapeData(objs, initialBrushId, _shapeData);
  683. ShapeManager.instance.AddPersistentItem(sceneGUID, persistentData);
  684. PWBItemsWindow.RepainWindow();
  685. }
  686. private static void ShapeStrokePreview(UnityEditor.SceneView sceneView, string hexId,
  687. bool forceUpdate, ShapeData shapeData)
  688. {
  689. BrushstrokeItem[] brushstroke;
  690. if (PreviewIfBrushtrokestaysTheSame(out brushstroke, sceneView.camera, forceUpdate)) return;
  691. PWBCore.UpdateTempCollidersIfHierarchyChanged();
  692. _paintStroke.Clear();
  693. var settings = ShapeManager.settings;
  694. float maxSurfaceHeight = 0f;
  695. for (int i = 0; i < brushstroke.Length; ++i)
  696. {
  697. var strokeItem = brushstroke[i];
  698. var prefab = strokeItem.settings.prefab;
  699. if (prefab == null) continue;
  700. var bounds = BoundsUtils.GetBoundsRecursive(prefab.transform);
  701. var pivotToCenter = Vector3.Scale(
  702. prefab.transform.InverseTransformDirection(bounds.center - prefab.transform.position),
  703. strokeItem.scaleMultiplier);
  704. var size = Vector3.Scale(bounds.size, strokeItem.scaleMultiplier);
  705. var normal = -settings.projectionDirection;
  706. var itemRotation = Quaternion.Euler(strokeItem.additionalAngle);
  707. var itemPosition = strokeItem.tangentPosition;
  708. var height = size.x + size.y + size.z
  709. + maxSurfaceHeight + Vector3.Distance(itemPosition, shapeData.center) + shapeData.radius;
  710. var ray = new Ray(itemPosition + normal * height, -normal);
  711. Transform surface = null;
  712. if (settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  713. {
  714. if (MouseRaycast(ray, out RaycastHit itemHit, out GameObject collider, height * 2, -1,
  715. settings.paintOnPalettePrefabs, settings.paintOnMeshesWithoutCollider,
  716. sameOriginAsRay: false, origin: itemPosition))
  717. {
  718. itemPosition = itemHit.point;
  719. normal = itemHit.normal;
  720. var colObj = PWBCore.GetGameObjectFromTempCollider(collider);
  721. if (colObj != null) surface = colObj.transform;
  722. var surfObj = PWBCore.GetGameObjectFromTempCollider(collider);
  723. var surfSize = BoundsUtils.GetBounds(surfObj.transform).size;
  724. var h = surfSize.x + surfSize.y + surfSize.z;
  725. maxSurfaceHeight = Mathf.Max(h, maxSurfaceHeight);
  726. }
  727. else if (settings.mode == PaintOnSurfaceToolSettingsBase.PaintMode.ON_SURFACE) continue;
  728. }
  729. BrushSettings brushSettings = strokeItem.settings;
  730. if (settings.overwriteBrushProperties) brushSettings = settings.brushSettings;
  731. var perpendicularToTheSurface = settings.perpendicularToTheSurface
  732. || (brushSettings.rotateToTheSurface && !brushSettings.alwaysOrientUp);
  733. if (settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  734. {
  735. if (perpendicularToTheSurface)
  736. {
  737. var itemForward = itemRotation * Vector3.forward;
  738. var plane = new Plane(normal, itemPosition);
  739. itemForward = plane.ClosestPointOnPlane(itemForward + itemPosition) - itemPosition;
  740. if (itemForward != Vector3.zero) itemRotation = Quaternion.LookRotation(itemForward, normal);
  741. }
  742. }
  743. if (!settings.perpendicularToTheSurface && brushSettings.rotateToTheSurface && brushSettings.alwaysOrientUp)
  744. {
  745. var fw = itemRotation * Vector3.forward;
  746. const float minMag = 1e-6f;
  747. fw.y = 0;
  748. if (Mathf.Abs(fw.x) < minMag && Mathf.Abs(fw.z) < minMag) fw = Quaternion.Euler(0, 90, 0) * normal;
  749. itemRotation = Quaternion.LookRotation(fw, Vector3.up);
  750. }
  751. itemPosition -= itemRotation * (pivotToCenter - normal * (size.y / 2));
  752. if (brushSettings.embedInSurface
  753. && settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  754. {
  755. if (brushSettings.embedAtPivotHeight)
  756. itemPosition += itemRotation * new Vector3(0f, strokeItem.settings.bottomMagnitude, 0f);
  757. else
  758. {
  759. var TRS = Matrix4x4.TRS(itemPosition, itemRotation,
  760. Vector3.Scale(prefab.transform.localScale, strokeItem.scaleMultiplier));
  761. var bottomDistanceToSurfce = GetBottomDistanceToSurface(strokeItem.settings.bottomVertices,
  762. TRS, Mathf.Abs(strokeItem.settings.bottomMagnitude), settings.paintOnPalettePrefabs,
  763. settings.paintOnMeshesWithoutCollider, out Transform surfaceTransform);
  764. itemPosition += itemRotation * new Vector3(0f, -bottomDistanceToSurfce, 0f);
  765. }
  766. }
  767. itemPosition += itemRotation * brushSettings.localPositionOffset;
  768. itemPosition += normal * brushSettings.surfaceDistance;
  769. var rootToWorld = Matrix4x4.TRS(itemPosition, itemRotation, strokeItem.scaleMultiplier)
  770. * Matrix4x4.Translate(-prefab.transform.position);
  771. var itemScale = Vector3.Scale(prefab.transform.localScale, strokeItem.scaleMultiplier);
  772. var layer = settings.overwritePrefabLayer ? settings.layer : prefab.layer;
  773. Transform parentTransform = settings.parent;
  774. var paintItem = new PaintStrokeItem(prefab, itemPosition,
  775. itemRotation * Quaternion.Euler(prefab.transform.eulerAngles),
  776. itemScale, layer, parentTransform, surface, strokeItem.flipX, strokeItem.flipY);
  777. paintItem.persistentParentId = hexId;
  778. _paintStroke.Add(paintItem);
  779. PreviewBrushItem(prefab, rootToWorld, layer, sceneView.camera,
  780. redMaterial: false, reverseTriangles: false, strokeItem.flipX, strokeItem.flipY);
  781. _previewData.Add(new PreviewData(prefab, rootToWorld, layer, strokeItem.flipX, strokeItem.flipY));
  782. }
  783. }
  784. #endregion
  785. #region COMMON
  786. private static ShapeData _shapeData = ShapeData.instance;
  787. private static void ClearShapeStroke()
  788. {
  789. _paintStroke.Clear();
  790. BrushstrokeManager.ClearBrushstroke();
  791. if (ToolManager.editMode)
  792. {
  793. PreviewPersistentShape(_selectedPersistentShapeData);
  794. UnityEditor.SceneView.RepaintAll();
  795. repaint = true;
  796. }
  797. }
  798. public static Vector3 GetShapePlaneNormal()
  799. {
  800. if (!ToolManager.editMode) return -ShapeData.instance.normal;
  801. if (_selectedPersistentShapeData == null) return Vector3.up;
  802. return -_selectedPersistentShapeData.normal;
  803. }
  804. private static void ShapeDuringSceneGUI(UnityEditor.SceneView sceneView)
  805. {
  806. if (ShapeManager.settings.paintOnMeshesWithoutCollider)
  807. PWBCore.CreateTempCollidersWithinFrustum(sceneView.camera);
  808. if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
  809. {
  810. if (_shapeData.state == ToolManager.ToolState.EDIT && _shapeData.selectedPointIdx > 0)
  811. _shapeData.selectedPointIdx = -1;
  812. else if (_shapeData.state == ToolManager.ToolState.NONE) ToolManager.DeselectTool();
  813. else ResetShapeState(false);
  814. OnUndoShape();
  815. UpdateStroke();
  816. BrushstrokeManager.ClearBrushstroke();
  817. }
  818. if (ToolManager.editMode || ShapeManager.instance.showPreexistingElements) ShapeToolEditMode(sceneView);
  819. if (ToolManager.editMode) return;
  820. switch (_shapeData.state)
  821. {
  822. case ToolManager.ToolState.NONE:
  823. ShapeStateNone(sceneView.in2DMode);
  824. break;
  825. case ToolManager.ToolState.PREVIEW:
  826. ShapeStateRadius(sceneView.in2DMode, _shapeData);
  827. break;
  828. case ToolManager.ToolState.EDIT:
  829. ShapeStateEdit(sceneView);
  830. break;
  831. }
  832. }
  833. private static Vector3 ClosestPointOnPlane(Vector3 point, ShapeData shapeData)
  834. {
  835. var plane = new Plane(shapeData.planeRotation * Vector3.up, shapeData.center);
  836. return plane.ClosestPointOnPlane(point);
  837. }
  838. public static void ShowShapeContextMenu(ShapeData data, Vector2 mousePosition)
  839. {
  840. if (!ToolManager.editMode) return;
  841. var menu = new UnityEditor.GenericMenu();
  842. PersistentItemContextMenu(menu, data, mousePosition);
  843. menu.ShowAsContext();
  844. }
  845. private static bool ShapeControlPoints(ShapeData shapeData, out bool clickOnPoint,
  846. out bool wasEdited, bool showHandles, out Vector3 delta)
  847. {
  848. delta = Vector3.zero;
  849. clickOnPoint = false;
  850. wasEdited = false;
  851. var isCircle = shapeData.settings.shapeType == ShapeSettings.ShapeType.CIRCLE;
  852. var isPolygon = shapeData.settings.shapeType == ShapeSettings.ShapeType.POLYGON;
  853. bool leftMouseDown = Event.current.button == 0 && Event.current.type == EventType.MouseDown;
  854. DrawDotHandleCap(shapeData.center);
  855. if (isPolygon) foreach (var vertex in shapeData.vertices) DrawDotHandleCap(vertex);
  856. else DrawDotHandleCap(shapeData.radiusPoint);
  857. if (shapeData.selectedPointIdx >= 0 && shapeData.selectedPointIdx < shapeData.pointsCount)
  858. DrawDotHandleCap(shapeData.selectedPoint, 1f, 1.2f);
  859. DrawDotHandleCap(shapeData.GetPoint(-1));
  860. DrawDotHandleCap(shapeData.GetPoint(-2));
  861. for (int i = 0; i < shapeData.pointsCount; ++i)
  862. {
  863. var controlId = GUIUtility.GetControlID(FocusType.Passive);
  864. if (clickOnPoint) ToolProperties.RepainWindow();
  865. else
  866. {
  867. if (showHandles)
  868. {
  869. float distFromMouse = UnityEditor.HandleUtility.DistanceToRectangle(shapeData.GetPoint(i),
  870. shapeData.planeRotation, 0f);
  871. UnityEditor.HandleUtility.AddControl(controlId, distFromMouse);
  872. if (UnityEditor.HandleUtility.nearestControl != controlId) continue;
  873. if (isPolygon) DrawDotHandleCap(shapeData.GetPoint(i));
  874. if (Event.current.button == 0 && Event.current.type == EventType.MouseDown)
  875. {
  876. shapeData.selectedPointIdx = i;
  877. clickOnPoint = true;
  878. Event.current.Use();
  879. }
  880. }
  881. }
  882. if (Event.current.button == 1 && Event.current.type == EventType.MouseDown
  883. && !Event.current.control && !Event.current.shift && !Event.current.alt
  884. && UnityEditor.HandleUtility.nearestControl == controlId)
  885. {
  886. ShowShapeContextMenu(shapeData,
  887. UnityEditor.EditorGUIUtility.GUIToScreenPoint(Event.current.mousePosition));
  888. Event.current.Use();
  889. }
  890. }
  891. if (showHandles && shapeData.selectedPointIdx >= 0 && shapeData.selectedPointIdx < shapeData.pointsCount)
  892. {
  893. var selectedPoint = shapeData.selectedPoint;
  894. if (_updateHandlePosition)
  895. {
  896. selectedPoint = _handlePosition;
  897. _updateHandlePosition = false;
  898. }
  899. var prevPosition = shapeData.selectedPoint;
  900. var snappedPoint = UnityEditor.Handles.PositionHandle(selectedPoint, shapeData.planeRotation);
  901. snappedPoint = SnapAndUpdateGridOrigin(snappedPoint, SnapManager.settings.snappingEnabled,
  902. shapeData.settings.paintOnPalettePrefabs, shapeData.settings.paintOnMeshesWithoutCollider,
  903. false, Vector3.down);
  904. if (prevPosition != snappedPoint)
  905. {
  906. shapeData.MovePoint(shapeData.selectedPointIdx, snappedPoint);
  907. wasEdited = true;
  908. ToolProperties.RepainWindow();
  909. }
  910. _handlePosition = shapeData.selectedPoint;
  911. if (shapeData.selectedPointIdx == 0)
  912. {
  913. var selectedRotation = shapeData.rotation;
  914. if (_updateHandleRotation)
  915. {
  916. selectedRotation = _handleRotation;
  917. _updateHandleRotation = false;
  918. }
  919. var prevRotation = shapeData.rotation;
  920. var rotation = UnityEditor.Handles.RotationHandle(selectedRotation, shapeData.center);
  921. if (prevRotation != rotation)
  922. {
  923. shapeData.rotation = rotation;
  924. wasEdited = true;
  925. ToolProperties.RepainWindow();
  926. }
  927. _handleRotation = shapeData.rotation;
  928. }
  929. }
  930. if (!showHandles) return false;
  931. return clickOnPoint || wasEdited;
  932. }
  933. public static Vector3[] GetPolygonVertices(ShapeData shapeData)
  934. {
  935. var tangent = Vector3.Cross(Vector3.left, shapeData.normal);
  936. if (tangent.sqrMagnitude < 0.000001) tangent = Vector3.Cross(Vector3.forward, shapeData.normal);
  937. var bitangent = Vector3.Cross(shapeData.normal, tangent);
  938. var polygonSides = shapeData.settings.shapeType == ShapeSettings.ShapeType.CIRCLE
  939. ? shapeData.circleSideCount : shapeData.settings.sidesCount;
  940. var periPoints = new System.Collections.Generic.List<Vector3>();
  941. var centerToRadius = shapeData.radiusPoint - shapeData.center;
  942. var sign = Vector3.Dot(Vector3.Cross(tangent, centerToRadius), shapeData.normal) > 0 ? 1f : -1f;
  943. float mouseAngle = Vector3.Angle(tangent, centerToRadius) * Mathf.Deg2Rad * sign;
  944. for (int i = 0; i < polygonSides; ++i)
  945. {
  946. var radians = TAU * i / polygonSides + mouseAngle;
  947. var tangentDir = new Vector2(Mathf.Cos(radians), Mathf.Sin(radians));
  948. var worldDir = TangentSpaceToWorld(tangent, bitangent, tangentDir).normalized;
  949. periPoints.Add(shapeData.center + (worldDir * shapeData.radius));
  950. }
  951. return periPoints.ToArray();
  952. }
  953. public static Vector3[] GetPolygonVertices() => GetPolygonVertices(_shapeData);
  954. public static Vector3[] GetArcVertices(float radius, ShapeData shapeData)
  955. {
  956. var tangent = Vector3.Cross(Vector3.left, shapeData.normal);
  957. if (tangent.sqrMagnitude < 0.000001) tangent = Vector3.Cross(Vector3.forward, shapeData.normal);
  958. var bitangent = Vector3.Cross(shapeData.normal, tangent);
  959. const float polygonSideSize = 0.3f;
  960. const int minPolygonSides = 8;
  961. const int maxPolygonSides = 60;
  962. var polygonSides = Mathf.Clamp((int)(TAU * radius / polygonSideSize), minPolygonSides, maxPolygonSides);
  963. var periPoints = new System.Collections.Generic.List<Vector3>();
  964. var centerToRadius = shapeData.GetPoint(-1) - shapeData.center;
  965. var sign = Vector3.Dot(Vector3.Cross(tangent, centerToRadius), shapeData.normal) > 0 ? 1 : -1;
  966. float firstAngle = Vector3.Angle(tangent, centerToRadius) * Mathf.Deg2Rad * sign;
  967. var sideDelta = TAU / polygonSides * Mathf.Sign(shapeData.arcAngle);
  968. for (int i = 0; i <= polygonSides; ++i)
  969. {
  970. var delta = sideDelta * i;
  971. bool arcComplete = false;
  972. if (Mathf.Abs(delta * Mathf.Rad2Deg) > Mathf.Abs(shapeData.arcAngle))
  973. {
  974. delta = shapeData.arcAngle * Mathf.Deg2Rad;
  975. arcComplete = true;
  976. }
  977. var radians = delta + firstAngle;
  978. var tangentDir = new Vector2(Mathf.Cos(radians), Mathf.Sin(radians));
  979. var worldDir = TangentSpaceToWorld(tangent, bitangent, tangentDir).normalized;
  980. periPoints.Add(shapeData.center + (worldDir * radius));
  981. if (arcComplete) break;
  982. }
  983. return periPoints.ToArray();
  984. }
  985. private static void DrawShapeLines(ShapeData shapeData, bool drawSurfaceLine)
  986. {
  987. if (shapeData.radius < 0.0001) return;
  988. var points = new System.Collections.Generic.List<Vector3>(shapeData.state == ToolManager.ToolState.PREVIEW
  989. ? GetPolygonVertices(shapeData) : shapeData.vertices);
  990. points.Add(points[0]);
  991. UnityEditor.Handles.zTest = UnityEngine.Rendering.CompareFunction.Always;
  992. var pointsArray = points.ToArray();
  993. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  994. UnityEditor.Handles.DrawAAPolyLine(8, pointsArray);
  995. UnityEditor.Handles.color = new Color(1f, 1f, 1f, 0.7f);
  996. UnityEditor.Handles.DrawAAPolyLine(4, pointsArray);
  997. if (shapeData.state < ToolManager.ToolState.EDIT) return;
  998. if (drawSurfaceLine)
  999. {
  1000. var onSurfacePoints = shapeData.onSurfacePoints;
  1001. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  1002. UnityEditor.Handles.DrawAAPolyLine(8, onSurfacePoints);
  1003. UnityEditor.Handles.color = new Color(0f, 1f, 1f, 0.5f);
  1004. UnityEditor.Handles.DrawAAPolyLine(4, onSurfacePoints);
  1005. }
  1006. var arcLines = new Vector3[] { shapeData.GetPoint(-1), shapeData.center, shapeData.GetPoint(-2) };
  1007. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  1008. UnityEditor.Handles.DrawAAPolyLine(4, arcLines);
  1009. UnityEditor.Handles.color = new Color(1f, 1f, 1f, 0.7f);
  1010. UnityEditor.Handles.DrawAAPolyLine(2, arcLines);
  1011. var arcPoints = GetArcVertices(shapeData.radius * 1.5f, shapeData);
  1012. UnityEditor.Handles.color = new Color(0f, 0f, 0f, 0.7f);
  1013. UnityEditor.Handles.DrawAAPolyLine(4, arcPoints);
  1014. UnityEditor.Handles.color = new Color(1f, 1f, 1f, 0.7f);
  1015. UnityEditor.Handles.DrawAAPolyLine(2, arcPoints);
  1016. }
  1017. #endregion
  1018. #region EDIT MODE
  1019. private static bool _editingPersistentShape = true;
  1020. private static ShapeData _initialPersistentShapeData = null;
  1021. private static ShapeData _selectedPersistentShapeData = null;
  1022. private static void DeselectPersistentShapes()
  1023. {
  1024. var persistentShapes = ShapeManager.instance.GetPersistentItems();
  1025. foreach (var s in persistentShapes) s.ClearSelection();
  1026. }
  1027. private static void ResetSelectedPersistentShape()
  1028. {
  1029. _editingPersistentShape = false;
  1030. if (_initialPersistentShapeData == null) return;
  1031. var selectedShape = ShapeManager.instance.GetItem(_initialPersistentShapeData.id);
  1032. if (selectedShape == null) return;
  1033. selectedShape.ResetPoses(_initialPersistentShapeData);
  1034. selectedShape.ClearSelection();
  1035. }
  1036. public static void SelectShape(ShapeData data)
  1037. {
  1038. ApplySelectedPersistentShape(true);
  1039. _editingPersistentShape = true;
  1040. data.ClearSelection();
  1041. data.selectedPointIdx = 0;
  1042. _selectedPersistentShapeData = data;
  1043. if (_initialPersistentShapeData == null) _initialPersistentShapeData = data.Clone();
  1044. ShapeManager.instance.CopyToolSettings(data.settings);
  1045. }
  1046. private static void ShapeToolEditMode(UnityEditor.SceneView sceneView)
  1047. {
  1048. var persistentItems = ShapeManager.instance.GetPersistentItems();
  1049. var selectedItemId = _initialPersistentShapeData == null ? -1 : _initialPersistentShapeData.id;
  1050. bool skipPreview = _selectedPersistentShapeData != null
  1051. && _selectedPersistentShapeData.objectCount > PWBCore.staticData.maxPreviewCountInEditMode;
  1052. foreach (var shapeData in persistentItems)
  1053. {
  1054. DrawShapeLines(shapeData, drawSurfaceLine: (ToolManager.editMode && selectedItemId == shapeData.id));
  1055. if (ShapeControlPoints(shapeData, out bool clickOnPoint, out bool wasEditted,
  1056. ToolManager.editMode, out Vector3 delta))
  1057. {
  1058. if (clickOnPoint)
  1059. {
  1060. _editingPersistentShape = true;
  1061. if (selectedItemId != shapeData.id)
  1062. {
  1063. ApplySelectedPersistentShape(false);
  1064. if (selectedItemId == -1)
  1065. _createProfileName = ShapeManager.instance.selectedProfileName;
  1066. ShapeManager.instance.CopyToolSettings(shapeData.settings);
  1067. ToolProperties.RepainWindow();
  1068. }
  1069. _selectedPersistentShapeData = shapeData;
  1070. if (_initialPersistentShapeData == null) _initialPersistentShapeData = shapeData.Clone();
  1071. else if (_initialPersistentShapeData.id != shapeData.id)
  1072. _initialPersistentShapeData = shapeData.Clone();
  1073. foreach (var i in persistentItems)
  1074. {
  1075. if (i == shapeData) continue;
  1076. i.selectedPointIdx = -1;
  1077. }
  1078. }
  1079. if (wasEditted)
  1080. {
  1081. _editingPersistentShape = true;
  1082. if (!skipPreview) PreviewPersistentShape(shapeData);
  1083. PWBCore.SetSavePending();
  1084. _persistentItemWasEdited = true;
  1085. }
  1086. }
  1087. }
  1088. if (!ToolManager.editMode) return;
  1089. if (!skipPreview)
  1090. {
  1091. if (_editingPersistentShape && _selectedPersistentShapeData != null)
  1092. {
  1093. var forceStrokeUpdate = updateStroke;
  1094. if (updateStroke)
  1095. {
  1096. PreviewPersistentShape(_selectedPersistentShapeData);
  1097. updateStroke = false;
  1098. PWBCore.SetSavePending();
  1099. }
  1100. ShapeStrokePreview(sceneView, _selectedPersistentShapeData.hexId,
  1101. forceStrokeUpdate, _selectedPersistentShapeData);
  1102. }
  1103. }
  1104. if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
  1105. {
  1106. if (skipPreview)
  1107. {
  1108. PreviewPersistentShape(_selectedPersistentShapeData);
  1109. ShapeStrokePreview(sceneView, _selectedPersistentShapeData.hexId,
  1110. forceUpdate: true, _selectedPersistentShapeData);
  1111. }
  1112. DeleteDisabledObjects();
  1113. ApplySelectedPersistentShape(true);
  1114. DeleteDisabledObjects();
  1115. ToolProperties.RepainWindow();
  1116. }
  1117. else if (PWBSettings.shortcuts.editModeSelectParent.Check() && _selectedPersistentShapeData != null)
  1118. {
  1119. var parent = _selectedPersistentShapeData.GetParent();
  1120. if (parent != null) UnityEditor.Selection.activeGameObject = parent;
  1121. }
  1122. else if (PWBSettings.shortcuts.editModeDeleteItemButNotItsChildren.Check())
  1123. ShapeManager.instance.DeletePersistentItem(_selectedPersistentShapeData.id, false);
  1124. else if (PWBSettings.shortcuts.editModeDeleteItemAndItsChildren.Check())
  1125. ShapeManager.instance.DeletePersistentItem(_selectedPersistentShapeData.id, true);
  1126. else if (PWBSettings.shortcuts.editModeDuplicate.Check()) DuplicateItem(_selectedPersistentShapeData.id);
  1127. }
  1128. public static void PreviewSelectedPersistentShapes()
  1129. {
  1130. if (ToolManager.tool != ToolManager.PaintTool.SHAPE) return;
  1131. if (_selectedPersistentShapeData != null) PreviewPersistentShape(_selectedPersistentShapeData);
  1132. var persistentShapes = ShapeManager.instance.GetPersistentItems();
  1133. foreach (var shapeData in persistentShapes)
  1134. {
  1135. if (!shapeData.isSelected) continue;
  1136. if (_shapeData == _selectedPersistentShapeData) continue;
  1137. PreviewPersistentShape(shapeData);
  1138. }
  1139. }
  1140. private static void PreviewPersistentShape(ShapeData shapeData)
  1141. {
  1142. PWBCore.UpdateTempCollidersIfHierarchyChanged();
  1143. BrushstrokeObject[] objPoses = null;
  1144. var objList = shapeData.objectList;
  1145. BrushstrokeManager.UpdatePersistentShapeBrushstroke(shapeData, objList, out objPoses);
  1146. _disabledObjects = new System.Collections.Generic.HashSet<GameObject>(objList);
  1147. var settings = shapeData.settings;
  1148. BrushSettings brushSettings = ShapeManager.instance.applyBrushToExisting ?
  1149. PaletteManager.selectedBrush : PaletteManager.GetBrushById(shapeData.initialBrushId);
  1150. if (brushSettings == null && PaletteManager.selectedBrush != null)
  1151. {
  1152. brushSettings = PaletteManager.selectedBrush;
  1153. shapeData.SetInitialBrushId(brushSettings.id);
  1154. }
  1155. if (settings.overwriteBrushProperties) brushSettings = settings.brushSettings;
  1156. if (brushSettings == null) brushSettings = new BrushSettings();
  1157. var objSet = shapeData.objectSet;
  1158. float maxSurfaceHeight = 0f;
  1159. for (int i = 0; i < objPoses.Length; ++i)
  1160. {
  1161. var objIdx = objPoses[i].objIdx;
  1162. var obj = objList[objIdx];
  1163. if (obj == null)
  1164. {
  1165. shapeData.RemovePose(objIdx);
  1166. continue;
  1167. }
  1168. obj.SetActive(true);
  1169. var prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(obj);
  1170. var bounds = BoundsUtils.GetBoundsRecursive(prefab.transform, prefab.transform.rotation, ignoreDissabled: true,
  1171. BoundsUtils.ObjectProperty.BOUNDING_BOX, recursive: true, useDictionary: true);
  1172. var size = Vector3.Scale(bounds.size, objPoses[i].objScale);
  1173. var normal = -shapeData.settings.projectionDirection;
  1174. var itemRotation = objPoses[objIdx].objRotation;
  1175. var itemPosition = objPoses[objIdx].objPosition;
  1176. var height = size.x + size.y + size.z + maxSurfaceHeight
  1177. + Vector3.Distance(itemPosition, shapeData.center) + shapeData.radius;
  1178. var ray = new Ray(itemPosition + normal * height, -normal);
  1179. var perpendicularToTheSurface = settings.perpendicularToTheSurface
  1180. || (brushSettings.rotateToTheSurface && !brushSettings.alwaysOrientUp);
  1181. if (settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  1182. {
  1183. if (MouseRaycast(ray, out RaycastHit itemHit, out GameObject collider, height * 2, -1,
  1184. settings.paintOnPalettePrefabs, settings.paintOnMeshesWithoutCollider,
  1185. tags: null, terrainLayers: null, exceptions: objSet))
  1186. {
  1187. itemPosition = itemHit.point;
  1188. if (perpendicularToTheSurface) normal = itemHit.normal;
  1189. var surfObj = PWBCore.GetGameObjectFromTempCollider(collider);
  1190. var surfSize = BoundsUtils.GetBounds(surfObj.transform).size;
  1191. var h = surfSize.x + surfSize.y + surfSize.z;
  1192. maxSurfaceHeight = Mathf.Max(h, maxSurfaceHeight);
  1193. }
  1194. else if (settings.mode == PaintOnSurfaceToolSettingsBase.PaintMode.ON_SURFACE) continue;
  1195. }
  1196. if (settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  1197. {
  1198. if (perpendicularToTheSurface)
  1199. {
  1200. var itemForward = itemRotation * Vector3.forward;
  1201. itemForward = Vector3.ProjectOnPlane(itemForward, normal);
  1202. if (itemForward != Vector3.zero) itemRotation = Quaternion.LookRotation(itemForward, normal);
  1203. }
  1204. }
  1205. if (!settings.perpendicularToTheSurface && brushSettings.rotateToTheSurface && brushSettings.alwaysOrientUp)
  1206. {
  1207. var fw = itemRotation * Vector3.forward;
  1208. const float minMag = 1e-6f;
  1209. fw.y = 0;
  1210. if (Mathf.Abs(fw.x) < minMag && Mathf.Abs(fw.z) < minMag) fw = Quaternion.Euler(0, 90, 0) * normal;
  1211. itemRotation = Quaternion.LookRotation(fw, Vector3.up);
  1212. }
  1213. var pivotToCenter = bounds.center - prefab.transform.position;
  1214. pivotToCenter = new Vector3(pivotToCenter.x / prefab.transform.localScale.x,
  1215. pivotToCenter.y / prefab.transform.localScale.y, pivotToCenter.z / prefab.transform.localScale.z);
  1216. pivotToCenter = itemRotation * Vector3.Scale(pivotToCenter, objPoses[i].objScale);
  1217. UnityEditor.Undo.RecordObject(obj.transform, ShapeData.COMMAND_NAME);
  1218. obj.transform.rotation = Quaternion.identity;
  1219. obj.transform.position = Vector3.zero;
  1220. obj.transform.rotation = itemRotation;
  1221. if (Utils2D.Is2DAsset(obj) && UnityEditor.SceneView.currentDrawingSceneView.in2DMode)
  1222. obj.transform.rotation *= Quaternion.AngleAxis(90, Vector3.right);
  1223. itemPosition -= pivotToCenter - normal * (size.y / 2);
  1224. if (ShapeManager.instance.applyBrushToExisting)
  1225. {
  1226. if (brushSettings.embedInSurface
  1227. && settings.mode != PaintOnSurfaceToolSettingsBase.PaintMode.ON_SHAPE)
  1228. {
  1229. var bottomMagnitude = BoundsUtils.GetBottomMagnitude(obj.transform);
  1230. if (brushSettings.embedAtPivotHeight)
  1231. itemPosition += itemRotation * (normal * bottomMagnitude);
  1232. else
  1233. {
  1234. var TRS = Matrix4x4.TRS(itemPosition, itemRotation, objPoses[i].objScale);
  1235. var bottomVertices = BoundsUtils.GetBottomVertices(obj.transform);
  1236. var bottomDistanceToSurfce = GetBottomDistanceToSurface(bottomVertices, TRS,
  1237. Mathf.Abs(bottomMagnitude), settings.paintOnPalettePrefabs,
  1238. settings.paintOnMeshesWithoutCollider, out Transform surfaceTransform,
  1239. exceptions: shapeData.objectSet);
  1240. itemPosition += itemRotation * (normal * -bottomDistanceToSurfce);
  1241. }
  1242. }
  1243. itemPosition += normal * objPoses[i].surfaceDistance;
  1244. itemPosition += itemRotation * brushSettings.localPositionOffset;
  1245. var additionalAngle = brushSettings.GetAdditionalAngle();
  1246. if (additionalAngle != Vector3.zero) obj.transform.rotation *= Quaternion.Euler(additionalAngle);
  1247. var flipX = brushSettings.GetFlipX();
  1248. var flipY = brushSettings.GetFlipY();
  1249. if (flipX || flipY)
  1250. {
  1251. var spriteRenderers = obj.GetComponentsInChildren<SpriteRenderer>();
  1252. foreach (var spriteRenderer in spriteRenderers)
  1253. {
  1254. UnityEditor.Undo.RecordObject(spriteRenderer, ShapeData.COMMAND_NAME);
  1255. spriteRenderer.flipX = flipX;
  1256. spriteRenderer.flipY = flipY;
  1257. }
  1258. }
  1259. }
  1260. obj.transform.position = itemPosition;
  1261. obj.transform.localScale = objPoses[i].objScale;
  1262. _disabledObjects.Remove(obj);
  1263. }
  1264. foreach (var obj in _disabledObjects) if(obj != null) obj.SetActive(false);
  1265. }
  1266. private static void ApplySelectedPersistentShape(bool deselectPoint)
  1267. {
  1268. if (!_persistentItemWasEdited) return;
  1269. _persistentItemWasEdited = false;
  1270. if (!ApplySelectedPersistentObject(deselectPoint, ref _editingPersistentShape, ref _initialPersistentShapeData,
  1271. ref _selectedPersistentShapeData, ShapeManager.instance)) return;
  1272. if (_initialPersistentShapeData == null) return;
  1273. var selected = ShapeManager.instance.GetItem(_initialPersistentShapeData.id);
  1274. _initialPersistentShapeData = selected.Clone();
  1275. }
  1276. #endregion
  1277. }
  1278. #endregion
  1279. }