A game about forced loneliness, made by TACStudios
1using UnityEngine;
2
3namespace UnityEditor.U2D.Animation
4{
5 internal class SkeletonView : ISkeletonView
6 {
7 internal const string deleteCommandName = "Delete";
8 internal const string softDeleteCommandName = "SoftDelete";
9
10 const float k_PickingRadius = 5f;
11 static readonly int k_BodyHashCode = "Body".GetHashCode();
12 static readonly int k_JointHashCode = "Joint".GetHashCode();
13 static readonly int k_TailHashCode = "Tail".GetHashCode();
14 static readonly int k_CreateBoneHashCode = "CreateBone".GetHashCode();
15
16 public int InvalidID { get; set; }
17 public SkeletonMode mode { get; set; }
18 public int defaultControlID { get; set; }
19 public int hoveredBoneID => m_HoveredBoneID;
20 public int hoveredJointID => m_HoveredJointID;
21 public int hoveredBodyID => m_HoveredBodyID;
22 public int hoveredTailID => m_HoveredTailID;
23 public int hotBoneID => m_HotBoneID;
24
25 IGUIWrapper m_GUIWrapper;
26 int m_RotateControlID = -1;
27 int m_MoveControlID = -1;
28 int m_FreeMoveControlID = -1;
29 int m_MoveJointControlID = -1;
30 int m_MoveEndPositionControlID = -1;
31 int m_ChangeLengthControlID = -1;
32 int m_CreateBoneControlID = -1;
33 int m_HoveredBoneID = 0;
34 int m_PrevHoveredBoneID = 0;
35 int m_HoveredBodyID = 0;
36 int m_HoveredJointID = 0;
37 int m_HoveredTailID = 0;
38 int m_HotBoneID = 0;
39 int m_HoveredBodyControlID = -1;
40 int m_HoveredJointControlID = -1;
41 int m_HoveredTailControlID = -1;
42 float m_NearestDistance;
43 float m_NearestBodyDistance;
44 float m_NearestJointDistance;
45 float m_NearestTailDistance;
46 int m_NearestBodyId = 0;
47 int m_NearestJointId = 0;
48 int m_NearestTailId = 0;
49 SliderData m_HoveredSliderData = SliderData.zero;
50 SliderData m_HotSliderData = SliderData.zero;
51
52 public SkeletonView(IGUIWrapper gw)
53 {
54 m_GUIWrapper = gw;
55 }
56
57 public void BeginLayout()
58 {
59 m_HoveredBodyControlID = m_GUIWrapper.GetControlID(k_BodyHashCode, FocusType.Passive);
60 m_HoveredJointControlID = m_GUIWrapper.GetControlID(k_JointHashCode, FocusType.Passive);
61 m_HoveredTailControlID = m_GUIWrapper.GetControlID(k_TailHashCode, FocusType.Passive);
62 m_CreateBoneControlID = m_GUIWrapper.GetControlID(k_CreateBoneHashCode, FocusType.Passive);
63
64 if (m_GUIWrapper.eventType == EventType.Layout)
65 {
66 m_PrevHoveredBoneID = m_HoveredBoneID;
67 m_NearestDistance = float.MaxValue;
68 m_NearestBodyDistance = float.MaxValue;
69 m_NearestJointDistance = float.MaxValue;
70 m_NearestTailDistance = float.MaxValue;
71 m_NearestBodyId = InvalidID;
72 m_NearestJointId = InvalidID;
73 m_NearestTailId = InvalidID;
74 m_HoveredBoneID = InvalidID;
75 m_HoveredBodyID = InvalidID;
76 m_HoveredJointID = InvalidID;
77 m_HoveredTailID = InvalidID;
78 m_HoveredSliderData = SliderData.zero;
79
80 if (m_GUIWrapper.IsControlHot(0))
81 {
82 m_RotateControlID = -1;
83 m_MoveControlID = -1;
84 m_FreeMoveControlID = -1;
85 m_MoveJointControlID = -1;
86 m_MoveEndPositionControlID = -1;
87 m_ChangeLengthControlID = -1;
88 m_HotBoneID = InvalidID;
89 }
90 }
91 }
92
93 public void EndLayout()
94 {
95 m_GUIWrapper.LayoutControl(m_HoveredBodyControlID, m_NearestBodyDistance * 0.25f);
96 m_GUIWrapper.LayoutControl(m_HoveredJointControlID, m_NearestJointDistance);
97 m_GUIWrapper.LayoutControl(m_HoveredTailControlID, m_NearestTailDistance);
98
99 if (m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID))
100 {
101 m_HoveredBoneID = m_NearestBodyId;
102 m_HoveredBodyID = m_NearestBodyId;
103 }
104
105 if (m_GUIWrapper.IsControlNearest(m_HoveredJointControlID))
106 {
107 m_HoveredBoneID = m_NearestJointId;
108 m_HoveredJointID = m_NearestJointId;
109 }
110
111 if (m_GUIWrapper.IsControlNearest(m_HoveredTailControlID))
112 {
113 m_HoveredBoneID = m_NearestTailId;
114 m_HoveredTailID = m_NearestTailId;
115 }
116
117 if ((m_GUIWrapper.eventType == EventType.Layout && m_PrevHoveredBoneID != m_HoveredBoneID) || m_GUIWrapper.eventType == EventType.MouseMove)
118 m_GUIWrapper.Repaint();
119 }
120
121 public bool CanLayout()
122 {
123 return m_GUIWrapper.eventType == EventType.Layout;
124 }
125
126 public void LayoutBone(int id, Vector3 position, Vector3 endPosition, Vector3 forward, Vector3 up, Vector3 right, bool isChainEnd)
127 {
128 if (mode == SkeletonMode.Disabled)
129 return;
130
131 var sliderData = new SliderData()
132 {
133 position = GetMouseWorldPosition(forward, position),
134 forward = forward,
135 up = up,
136 right = right
137 };
138
139 {
140 var distance = m_GUIWrapper.DistanceToSegmentClamp(position, endPosition);
141
142 if (distance <= m_NearestDistance)
143 {
144 m_NearestDistance = distance;
145 m_NearestBodyDistance = distance;
146 m_NearestBodyId = id;
147 m_HoveredSliderData = sliderData;
148 }
149 }
150
151 {
152 var distance = m_GUIWrapper.DistanceToCircle(position, GetBoneRadiusForPicking(position) * 2f);
153
154 if (distance <= m_NearestDistance)
155 {
156 m_NearestDistance = distance;
157 m_NearestJointDistance = distance;
158 m_NearestJointId = id;
159 m_HoveredSliderData = sliderData;
160 }
161 }
162
163 if (isChainEnd &&
164 (IsCapable(SkeletonAction.ChangeLength) ||
165 IsCapable(SkeletonAction.MoveEndPosition) ||
166 IsCapable(SkeletonAction.CreateBone)))
167 {
168 var distance = m_GUIWrapper.DistanceToCircle(endPosition, GetBoneRadiusForPicking(endPosition));
169
170 if (distance <= m_NearestDistance)
171 {
172 m_NearestDistance = distance;
173 m_NearestTailDistance = distance;
174 m_NearestTailId = id;
175 m_HoveredSliderData = sliderData;
176 }
177 }
178 }
179
180 public Vector3 GetMouseWorldPosition(Vector3 planeNormal, Vector3 planePosition)
181 {
182 return m_GUIWrapper.GUIToWorld(m_GUIWrapper.mousePosition, planeNormal, planePosition);
183 }
184
185 float GetBoneRadiusForPicking(Vector3 position)
186 {
187 if (m_GUIWrapper.HasCurrentCamera())
188 return 0.1f * m_GUIWrapper.GetHandleSize(position);
189
190 return k_PickingRadius;
191 }
192
193 public bool DoSelectBone(out int id, out bool additive)
194 {
195 id = 0;
196 additive = false;
197
198 if (IsActionTriggering(SkeletonAction.Select))
199 {
200 id = m_HoveredBoneID;
201 additive = m_GUIWrapper.isActionKeyDown;
202
203 if (mode == SkeletonMode.Selection)
204 {
205 m_GUIWrapper.UseCurrentEvent();
206 m_GUIWrapper.SetGuiChanged(true);
207 }
208
209 return true;
210 }
211
212 return false;
213 }
214
215 public bool DoRotateBone(Vector3 pivot, Vector3 normal, out float deltaAngle)
216 {
217 deltaAngle = 0f;
218
219 var oldPosition = m_HotSliderData.position;
220 if (DoSliderAction(SkeletonAction.RotateBone, m_HoveredBodyControlID, ref m_RotateControlID, out var newPosition))
221 {
222 deltaAngle = Vector3.SignedAngle(oldPosition - pivot, (Vector3)newPosition - pivot, normal);
223 return true;
224 }
225
226 return false;
227 }
228
229 public bool DoMoveBone(out Vector3 deltaPosition)
230 {
231 deltaPosition = Vector3.zero;
232
233 var oldPosition = m_HotSliderData.position;
234 if (DoSliderAction(SkeletonAction.MoveBone, m_HoveredJointControlID, ref m_MoveControlID, out var newPosition))
235 {
236 deltaPosition = newPosition - oldPosition;
237 return true;
238 }
239
240 return false;
241 }
242
243 public bool DoFreeMoveBone(out Vector3 deltaPosition)
244 {
245 deltaPosition = Vector3.zero;
246
247 var oldPosition = m_HotSliderData.position;
248 if (DoSliderAction(SkeletonAction.FreeMoveBone, m_HoveredBodyControlID, ref m_FreeMoveControlID, out var newPosition))
249 {
250 deltaPosition = newPosition - oldPosition;
251 return true;
252 }
253
254 return false;
255 }
256
257 public bool DoMoveJoint(out Vector3 deltaPosition)
258 {
259 deltaPosition = Vector3.zero;
260
261 var oldPosition = m_HotSliderData.position;
262 if (DoSliderAction(SkeletonAction.MoveJoint, m_HoveredJointControlID, ref m_MoveJointControlID, out var newPosition))
263 {
264 deltaPosition = newPosition - oldPosition;
265 return true;
266 }
267
268 return false;
269 }
270
271 public bool DoMoveEndPosition(out Vector3 endPosition)
272 {
273 return DoSliderAction(SkeletonAction.MoveEndPosition, m_HoveredTailControlID, ref m_MoveEndPositionControlID, out endPosition);
274 }
275
276 public bool DoChangeLength(out Vector3 endPosition)
277 {
278 return DoSliderAction(SkeletonAction.ChangeLength, m_HoveredTailControlID, ref m_ChangeLengthControlID, out endPosition);
279 }
280
281 bool DoSliderAction(SkeletonAction action, int controlID, ref int actionControlID, out Vector3 newPosition)
282 {
283 newPosition = m_HoveredSliderData.position;
284
285 if (IsActionTriggering(action))
286 {
287 actionControlID = controlID;
288 m_HotSliderData = m_HoveredSliderData;
289 m_HotBoneID = hoveredBoneID;
290 }
291
292 if (m_GUIWrapper.DoSlider(actionControlID, m_HotSliderData, out newPosition))
293 {
294 m_HotSliderData.position = newPosition;
295 return true;
296 }
297
298 return false;
299 }
300
301 public bool DoCreateBoneStart(out Vector3 position)
302 {
303 position = GetMouseWorldPosition(m_HoveredSliderData.forward, m_HoveredSliderData.position);
304
305 if (CanCreateBone())
306 m_GUIWrapper.LayoutControl(m_CreateBoneControlID, 0f);
307
308 if (IsActionActive(SkeletonAction.CreateBone))
309 ConsumeMouseMoveEvents();
310
311 if (IsActionTriggering(SkeletonAction.CreateBone))
312 {
313 m_HotBoneID = hoveredBoneID;
314 m_GUIWrapper.SetMultiStepControlHot(m_CreateBoneControlID);
315 m_GUIWrapper.UseCurrentEvent();
316 return true;
317 }
318
319 return false;
320 }
321
322 public bool CanCreateBone()
323 {
324 return mode == SkeletonMode.CreateBone && (m_GUIWrapper.IsControlNearest(defaultControlID) || m_GUIWrapper.IsControlNearest(m_HoveredTailControlID));
325 }
326
327 public bool DoCreateBone(out Vector3 position)
328 {
329 position = GetMouseWorldPosition(m_HoveredSliderData.forward, m_HoveredSliderData.position);
330
331 if (IsActionHot(SkeletonAction.CreateBone))
332 ConsumeMouseMoveEvents();
333
334 if (IsActionFinishing(SkeletonAction.CreateBone))
335 {
336 m_GUIWrapper.UseCurrentEvent();
337 m_GUIWrapper.SetGuiChanged(true);
338 return true;
339 }
340
341 return false;
342 }
343
344 public bool DoSplitBone(out int id, out Vector3 position)
345 {
346 id = m_HoveredBodyID;
347 position = GetMouseWorldPosition(m_HoveredSliderData.forward, m_HoveredSliderData.position);
348
349 if (IsActionActive(SkeletonAction.SplitBone))
350 ConsumeMouseMoveEvents();
351
352 if (IsActionTriggering(SkeletonAction.SplitBone))
353 {
354 m_GUIWrapper.UseCurrentEvent();
355 m_GUIWrapper.SetGuiChanged(true);
356 return true;
357 }
358
359 return false;
360 }
361
362 public bool DoRemoveBone()
363 {
364 if (IsActionTriggering(SkeletonAction.Remove))
365 {
366 m_GUIWrapper.UseCurrentEvent();
367 m_GUIWrapper.SetGuiChanged(true);
368 return true;
369 }
370
371 return false;
372 }
373
374 public bool DoCancelMultistepAction(bool force)
375 {
376 if (force)
377 {
378 m_GUIWrapper.SetMultiStepControlHot(0);
379 return true;
380 }
381
382 if ((!m_GUIWrapper.IsMultiStepControlHot(0) && (m_GUIWrapper.IsMouseDown(1) || m_GUIWrapper.IsKeyDown(KeyCode.Escape))))
383 {
384 m_GUIWrapper.SetMultiStepControlHot(0);
385 m_GUIWrapper.UseCurrentEvent();
386 return true;
387 }
388
389 return false;
390 }
391
392 public bool IsActionActive(SkeletonAction action)
393 {
394 if (m_GUIWrapper.isAltDown || !m_GUIWrapper.IsControlHot(0) || !m_GUIWrapper.IsMultiStepControlHot(0))
395 return false;
396
397 if (action == SkeletonAction.None)
398 return m_GUIWrapper.IsControlNearest(defaultControlID);
399
400 if (!IsCapable(action))
401 return false;
402
403 if (action == SkeletonAction.RotateBone)
404 return m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID);
405
406 if (action == SkeletonAction.ChangeLength)
407 return m_GUIWrapper.IsControlNearest(m_HoveredTailControlID) && !m_GUIWrapper.isShiftDown;
408
409 if (action == SkeletonAction.MoveJoint)
410 return m_GUIWrapper.IsControlNearest(m_HoveredJointControlID);
411
412 if (action == SkeletonAction.MoveEndPosition)
413 return m_GUIWrapper.IsControlNearest(m_HoveredTailControlID) && !m_GUIWrapper.isShiftDown;
414
415 if (action == SkeletonAction.FreeMoveBone)
416 return m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID);
417
418 if (action == SkeletonAction.MoveBone)
419 return m_GUIWrapper.IsControlNearest(m_HoveredJointControlID);
420
421 bool canCreateBone = IsCapable(SkeletonAction.CreateBone) && m_GUIWrapper.IsControlNearest(m_CreateBoneControlID);
422 bool canSplitBone = IsCapable(SkeletonAction.SplitBone) && m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID);
423
424 if (action == SkeletonAction.CreateBone)
425 return canCreateBone;
426
427 if (action == SkeletonAction.SplitBone)
428 return canSplitBone;
429
430 if (action == SkeletonAction.Select)
431 return (m_GUIWrapper.IsControlNearest(m_HoveredBodyControlID) && !canSplitBone) ||
432 m_GUIWrapper.IsControlNearest(m_HoveredJointControlID) ||
433 (m_GUIWrapper.IsControlNearest(m_HoveredTailControlID) && !canCreateBone);
434
435 if (action == SkeletonAction.Remove)
436 return true;
437
438 return false;
439 }
440
441 public bool IsActionHot(SkeletonAction action)
442 {
443 if (action == SkeletonAction.None)
444 return m_GUIWrapper.IsControlHot(0) && m_GUIWrapper.IsMultiStepControlHot(0);
445
446 if (action == SkeletonAction.RotateBone)
447 return m_GUIWrapper.IsControlHot(m_RotateControlID);
448
449 if (action == SkeletonAction.MoveBone)
450 return m_GUIWrapper.IsControlHot(m_MoveControlID);
451
452 if (action == SkeletonAction.FreeMoveBone)
453 return m_GUIWrapper.IsControlHot(m_FreeMoveControlID);
454
455 if (action == SkeletonAction.MoveJoint)
456 return m_GUIWrapper.IsControlHot(m_MoveJointControlID);
457
458 if (action == SkeletonAction.MoveEndPosition)
459 return m_GUIWrapper.IsControlHot(m_MoveEndPositionControlID);
460
461 if (action == SkeletonAction.ChangeLength)
462 return m_GUIWrapper.IsControlHot(m_ChangeLengthControlID);
463
464 if (action == SkeletonAction.CreateBone)
465 return m_GUIWrapper.IsMultiStepControlHot(m_CreateBoneControlID) && !m_GUIWrapper.isAltDown;
466
467 return false;
468 }
469
470 public bool IsActionTriggering(SkeletonAction action)
471 {
472 if (!IsActionActive(action))
473 return false;
474
475 if (action == SkeletonAction.Remove)
476 {
477 if ((m_GUIWrapper.eventType == EventType.ValidateCommand || m_GUIWrapper.eventType == EventType.ExecuteCommand)
478 && (m_GUIWrapper.commandName == softDeleteCommandName || m_GUIWrapper.commandName == deleteCommandName))
479 {
480 if (m_GUIWrapper.eventType == EventType.ExecuteCommand)
481 return true;
482
483 m_GUIWrapper.UseCurrentEvent();
484 }
485
486 return false;
487 }
488
489 return m_GUIWrapper.IsMouseDown(0);
490 }
491
492 public bool IsActionFinishing(SkeletonAction action)
493 {
494 if (!IsActionHot(action) || !IsCapable(action))
495 return false;
496
497 if (m_GUIWrapper.IsEventOutsideWindow())
498 return true;
499
500 if (action == SkeletonAction.CreateBone)
501 return m_GUIWrapper.IsMouseDown(0);
502
503 return m_GUIWrapper.IsMouseUp(0);
504 }
505
506 public bool IsRepainting()
507 {
508 return m_GUIWrapper.IsRepainting();
509 }
510
511 public void DrawBone(Vector3 position, Vector3 right, Vector3 forward, float length, Color color, bool isChained, bool isSelected, bool isJointHovered, bool isTailHovered, bool isHot)
512 {
513 var endPosition = position + right * length;
514 var rotation = Quaternion.LookRotation(forward, Vector3.Cross(right, forward));
515 var boneJointColor = new Color(0f, 0f, 0f, 0.75f * color.a);
516 var tailColor = new Color(0f, 0f, 0f, 0.75f * color.a);
517 var hoveredColor = Handles.preselectionColor;
518 var selectedColor = Handles.selectedColor;
519 var drawRectCap = false;
520
521 if (isJointHovered)
522 boneJointColor = hoveredColor;
523 if (isHot && (IsActionHot(SkeletonAction.MoveBone) || IsActionHot(SkeletonAction.MoveJoint)))
524 boneJointColor = selectedColor;
525
526 if (mode == SkeletonMode.EditPose || mode == SkeletonMode.CreateBone)
527 {
528 if (isJointHovered || isSelected)
529 drawRectCap = true;
530 }
531 else if (mode == SkeletonMode.EditJoints || mode == SkeletonMode.SplitBone)
532 {
533 rotation = Quaternion.identity;
534 drawRectCap = true;
535 }
536
537 if (drawRectCap)
538 Handles.RectangleHandleCap(0, position, rotation, BoneDrawingUtility.GetBoneRadius(position), EventType.Repaint);
539
540 BoneDrawingUtility.DrawBone(position, endPosition, forward, color);
541 BoneDrawingUtility.DrawBoneNode(position, forward, boneJointColor);
542
543 if (!isChained &&
544 (IsCapable(SkeletonAction.ChangeLength) ||
545 IsCapable(SkeletonAction.MoveEndPosition)))
546 {
547 if (isTailHovered)
548 tailColor = hoveredColor;
549
550 if (isHot && (IsActionHot(SkeletonAction.ChangeLength) || IsActionHot(SkeletonAction.MoveEndPosition)))
551 tailColor = selectedColor;
552
553 BoneDrawingUtility.DrawBoneNode(endPosition, forward, tailColor);
554 }
555 }
556
557 public void DrawBoneParentLink(Vector3 parentPosition, Vector3 position, Vector3 forward, Color color)
558 {
559 BoneDrawingUtility.DrawBone(position, parentPosition, forward, color);
560 }
561
562 public void DrawBoneOutline(Vector3 position, Vector3 right, Vector3 forward, float length, Color color, float outlineScale)
563 {
564 BoneDrawingUtility.DrawBoneOutline(position, position + right * length, forward, color, outlineScale);
565 }
566
567 public void DrawCursors(bool canBeActive)
568 {
569 var mouseScreenRect = new Rect(m_GUIWrapper.mousePosition.x - 100f, m_GUIWrapper.mousePosition.y - 100f, 200f, 200f);
570
571 var isRotateHot = IsActionHot(SkeletonAction.RotateBone);
572 if ((canBeActive && IsActionActive(SkeletonAction.RotateBone)) || isRotateHot)
573 EditorGUIUtility.AddCursorRect(mouseScreenRect, MouseCursor.RotateArrow);
574
575 if ((canBeActive && IsActionActive(SkeletonAction.MoveBone)) || IsActionHot(SkeletonAction.MoveBone) ||
576 (canBeActive && IsActionActive(SkeletonAction.FreeMoveBone)) || IsActionHot(SkeletonAction.FreeMoveBone) ||
577 (canBeActive && IsActionActive(SkeletonAction.MoveJoint)) || IsActionHot(SkeletonAction.MoveJoint) ||
578 (canBeActive && IsActionActive(SkeletonAction.MoveEndPosition)) || IsActionHot(SkeletonAction.MoveEndPosition))
579 EditorGUIUtility.AddCursorRect(mouseScreenRect, MouseCursor.MoveArrow);
580 }
581
582 void ConsumeMouseMoveEvents()
583 {
584 if (m_GUIWrapper.eventType == EventType.MouseMove || (m_GUIWrapper.eventType == EventType.MouseDrag && m_GUIWrapper.mouseButton == 0))
585 m_GUIWrapper.UseCurrentEvent();
586 }
587
588 bool IsCapable(SkeletonAction action)
589 {
590 return ((int)mode & (int)action) != 0;
591 }
592 }
593}