A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using System.Linq;
3using UnityEditor.U2D.Layout;
4using UnityEditor.U2D.Common;
5
6namespace UnityEditor.U2D.Animation
7{
8 internal class SpriteBoneInfluenceToolController
9 {
10 SkinningEvents m_Events;
11 ISpriteBoneInfluenceToolModel m_Model;
12
13 public SpriteBoneInfluenceToolController(ISpriteBoneInfluenceToolModel model, SkinningEvents events)
14 {
15 m_Events = events;
16 m_Model = model;
17 }
18
19 public void Activate()
20 {
21 m_Events.selectedSpriteChanged.AddListener(OnSpriteSelectionChanged);
22 m_Events.boneSelectionChanged.AddListener(OnBoneSelectionChanged);
23 m_Events.boneNameChanged.AddListener(OnBoneNameChanged);
24 m_Events.skeletonTopologyChanged.AddListener(OnSkeletonTopologyChanged);
25 m_Events.meshChanged.AddListener(OnMeshChanged);
26 m_Events.skinningModeChanged.AddListener(OnSkinningModeChanged);
27
28 ShowHideView(true);
29 }
30
31 public void Deactivate()
32 {
33 m_Events.selectedSpriteChanged.RemoveListener(OnSpriteSelectionChanged);
34 m_Events.boneSelectionChanged.RemoveListener(OnBoneSelectionChanged);
35 m_Events.boneNameChanged.RemoveListener(OnBoneNameChanged);
36 m_Events.skeletonTopologyChanged.RemoveListener(OnSkeletonTopologyChanged);
37 m_Events.meshChanged.RemoveListener(OnMeshChanged);
38 m_Events.skinningModeChanged.RemoveListener(OnSkinningModeChanged);
39
40 ShowHideView(false);
41 }
42
43 void OnMeshChanged(MeshCache mesh)
44 {
45 if (m_Model.view.visible)
46 {
47 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
48 m_Model.view.UpdateSelection(m_Model.selectedBones);
49 UpdateAddRemoveButtons();
50 }
51 }
52
53 void OnSpriteSelectionChanged(SpriteCache sprite)
54 {
55 if (m_Model.view.visible)
56 {
57 UpdateSelectedSpriteBoneInfluence();
58 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
59 m_Model.view.UpdateSelection(m_Model.selectedBones);
60 UpdateAddRemoveButtons();
61 SetViewHeaderText();
62 }
63 }
64
65 void OnBoneSelectionChanged()
66 {
67 if (m_Model.view.visible)
68 {
69 m_Model.view.UpdateSelection(m_Model.selectedBones);
70 UpdateAddRemoveButtons();
71 }
72 }
73
74 void OnBoneNameChanged(BoneCache bone)
75 {
76 if (m_Model.view.visible)
77 {
78 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
79 m_Model.view.UpdateSelection(m_Model.selectedBones);
80 }
81 }
82
83 void OnSkeletonTopologyChanged(SkeletonCache skeleton)
84 {
85 if (m_Model.view.visible)
86 {
87 UpdateSelectedSpriteBoneInfluence();
88
89 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
90 m_Model.view.UpdateSelection(m_Model.selectedBones);
91 }
92 }
93
94 void UpdateAddRemoveButtons()
95 {
96 m_Model.view.ToggleAddButton(ShouldEnableAddButton());
97 m_Model.view.ToggleRemoveButton(InCharacterMode());
98 }
99
100 public void OnViewCreated()
101 {
102 m_Model.view.onAddElement += AddSelectedBoneInfluenceToSprite;
103 m_Model.view.onRemoveElement += RemoveSelectedBoneInfluenceFromSprite;
104 m_Model.view.onReordered += OnReorderBoneInfluenceFromSprite;
105 m_Model.view.onSelectionChanged += SelectedBonesFromList;
106
107 ShowHideView(false);
108 }
109
110 void OnSkinningModeChanged(SkinningMode skinningMode)
111 {
112 UpdateAddRemoveButtons();
113 }
114
115 void AddSelectedBoneInfluenceToSprite()
116 {
117 var character = m_Model.characterSkeleton;
118
119 if (character == null)
120 return;
121
122 var characterPart = m_Model.GetSpriteCharacterPart(m_Model.selectedSprite);
123 var selectedBones = m_Model.selectedBones;
124 var characterBones = characterPart.bones.ToList();
125
126 foreach (var bone in selectedBones)
127 {
128 if (!characterBones.Contains(bone))
129 characterBones.Add(bone as BoneCache);
130 }
131
132 using (m_Model.UndoScope(TextContent.addBoneInfluence))
133 {
134 characterPart.bones = characterBones.ToArray();
135 m_Events.characterPartChanged.Invoke(characterPart);
136
137 UpdateSelectedSpriteBoneInfluence();
138 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
139 m_Model.view.UpdateSelection(m_Model.selectedBones);
140 UpdateAddRemoveButtons();
141 }
142 }
143
144 void RemoveSelectedBoneInfluenceFromSprite()
145 {
146 var character = m_Model.characterSkeleton;
147
148 if (character == null)
149 return;
150
151 var characterPart = m_Model.GetSpriteCharacterPart(m_Model.selectedSprite);
152 var selectedBones = m_Model.selectedBones;
153 var characterBones = characterPart.bones.ToList();
154
155 characterBones.RemoveAll(b => selectedBones.Contains(b));
156
157 using (m_Model.UndoScope(TextContent.removeBoneInfluence))
158 {
159 characterPart.bones = characterBones.ToArray();
160 m_Events.characterPartChanged.Invoke(characterPart);
161
162 characterPart.sprite.SmoothFill();
163 m_Events.meshChanged.Invoke(characterPart.sprite.GetMesh());
164
165 UpdateSelectedSpriteBoneInfluence();
166 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
167 m_Model.view.UpdateSelection(m_Model.selectedBones);
168 UpdateAddRemoveButtons();
169 }
170 }
171
172 void OnReorderBoneInfluenceFromSprite(IEnumerable<TransformCache> transformCache)
173 {
174 var boneCache = transformCache.Cast<BoneCache>();
175
176 var character = m_Model.characterSkeleton;
177
178 if (character != null)
179 {
180 var characterPart = m_Model.GetSpriteCharacterPart(m_Model.selectedSprite);
181 using (m_Model.UndoScope(TextContent.reorderBoneInfluence))
182 {
183 characterPart.bones = boneCache.ToArray();
184 m_Events.characterPartChanged.Invoke(characterPart);
185
186 UpdateSelectedSpriteBoneInfluence();
187 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
188 }
189 }
190 else
191 {
192 using (m_Model.UndoScope(TextContent.reorderBoneInfluence))
193 {
194 m_Model.selectedSprite.GetSkeleton().ReorderBones(boneCache);
195 m_Events.skeletonTopologyChanged.Invoke(m_Model.selectedSprite.GetSkeleton());
196 }
197 }
198 }
199
200 void SelectedBonesFromList(IEnumerable<object> selectedBones)
201 {
202 using (m_Model.UndoScope(TextContent.boneSelection))
203 {
204 m_Model.selectedBones = selectedBones.Cast<BoneCache>();
205 m_Events.boneSelectionChanged.Invoke();
206 }
207 }
208
209 private void ShowHideView(bool show)
210 {
211 m_Model.view.SetHiddenFromLayout(!show);
212 if (show)
213 {
214 UpdateSelectedSpriteBoneInfluence();
215 m_Model.view.UpdateList(m_Model.selectionInfluencedBones);
216 m_Model.view.UpdateSelection(m_Model.selectedBones);
217 m_Model.view.listLabelText = TextContent.boneInfluences;
218 m_Model.view.SetTooltips(TextContent.addBoneInfluenceTooltip, TextContent.removeBoneInfluenceTooltip);
219 UpdateAddRemoveButtons();
220 SetViewHeaderText();
221 }
222 }
223
224 void SetViewHeaderText()
225 {
226 var headerText = m_Model.selectedSprite != null ? m_Model.selectedSprite.name : TextContent.noSpriteSelected;
227 m_Model.view.headerText = headerText;
228 }
229
230 void UpdateSelectedSpriteBoneInfluence()
231 {
232 var selectedSpriteInfluences = new List<TransformCache>();
233 var selectedSprite = m_Model.selectedSprite;
234
235 if (selectedSprite != null)
236 {
237 if (m_Model.hasCharacter)
238 {
239 var characterPart = m_Model.GetSpriteCharacterPart(selectedSprite);
240 selectedSpriteInfluences = characterPart.bones.Cast<TransformCache>().ToList();
241 }
242 else
243 {
244 selectedSpriteInfluences = selectedSprite.GetSkeleton().bones.Cast<TransformCache>().ToList();
245 }
246 }
247
248 m_Model.selectionInfluencedBones = selectedSpriteInfluences;
249 }
250
251 public bool ShouldEnableAddButton()
252 {
253 if (InCharacterMode())
254 {
255 var hasSelectedSprite = m_Model.selectedSprite != null;
256 var selectedBones = m_Model.selectedBones;
257 return hasSelectedSprite && selectedBones.FirstOrDefault(x => !m_Model.selectionInfluencedBones.Contains(x)) != null;
258 }
259
260 return false;
261 }
262
263 private bool InCharacterMode()
264 {
265 return m_Model.hasCharacter && m_Model.skinningMode == SkinningMode.Character;
266 }
267 }
268
269 internal interface ISpriteBoneInfluenceToolModel
270 {
271 IInfluenceWindow view { get; }
272 IEnumerable<BoneCache> selectedBones { get; set; }
273 List<TransformCache> selectionInfluencedBones { get; set; }
274 SpriteCache selectedSprite { get; }
275 bool hasCharacter { get; }
276 SkinningMode skinningMode { get; }
277 SkeletonCache characterSkeleton { get; }
278 UndoScope UndoScope(string description);
279 CharacterPartCache GetSpriteCharacterPart(SpriteCache sprite);
280 }
281
282 internal class SpriteBoneInfluenceTool : BaseTool, ISpriteBoneInfluenceToolModel
283 {
284 private SpriteBoneInfluenceToolController m_Controller;
285 private MeshPreviewBehaviour m_MeshPreviewBehaviour = new MeshPreviewBehaviour();
286 private InfluenceWindow m_View;
287
288 public SkeletonTool skeletonTool { set; private get; }
289 public override IMeshPreviewBehaviour previewBehaviour => m_MeshPreviewBehaviour;
290
291 internal override void OnCreate()
292 {
293 m_Controller = new SpriteBoneInfluenceToolController(this, skinningCache.events);
294 }
295
296 IInfluenceWindow ISpriteBoneInfluenceToolModel.view => m_View;
297
298 IEnumerable<BoneCache> ISpriteBoneInfluenceToolModel.selectedBones
299 {
300 get => skinningCache.skeletonSelection.elements;
301 set => skinningCache.skeletonSelection.elements = value.ToArray();
302 }
303
304 List<TransformCache> ISpriteBoneInfluenceToolModel.selectionInfluencedBones { get; set; }
305
306 SpriteCache ISpriteBoneInfluenceToolModel.selectedSprite => skinningCache.selectedSprite;
307 bool ISpriteBoneInfluenceToolModel.hasCharacter => skinningCache.hasCharacter;
308 SkinningMode ISpriteBoneInfluenceToolModel.skinningMode => skinningCache.mode;
309 SkeletonCache ISpriteBoneInfluenceToolModel.characterSkeleton => skinningCache.character != null ? skinningCache.character.skeleton : null;
310
311 UndoScope ISpriteBoneInfluenceToolModel.UndoScope(string description)
312 {
313 return skinningCache.UndoScope(description);
314 }
315
316 protected override void OnActivate()
317 {
318 m_Controller.Activate();
319 if (skeletonTool != null)
320 skeletonTool.Activate();
321 }
322
323 protected override void OnDeactivate()
324 {
325 m_Controller.Deactivate();
326 if (skeletonTool != null)
327 skeletonTool.Deactivate();
328 }
329
330 public override void Initialize(LayoutOverlay layout)
331 {
332 if (m_View == null)
333 {
334 m_View = InfluenceWindow.CreateFromUxml();
335 m_View.SetListReorderable(true);
336 m_View.SetAllowMultiselect(true);
337 m_View.LocalizeTextInChildren();
338 m_Controller.OnViewCreated();
339 }
340
341 layout.rightOverlay.Add(m_View);
342 }
343
344 protected override void OnGUI()
345 {
346 m_MeshPreviewBehaviour.showWeightMap = true;
347 m_MeshPreviewBehaviour.overlaySelected = true;
348 m_MeshPreviewBehaviour.drawWireframe = true;
349
350 skeletonTool.skeletonStyle = SkeletonStyles.WeightMap;
351 skeletonTool.mode = SkeletonMode.EditPose;
352 skeletonTool.editBindPose = false;
353 skeletonTool.DoGUI();
354 }
355
356 public CharacterPartCache GetSpriteCharacterPart(SpriteCache sprite)
357 {
358 return sprite.GetCharacterPart();
359 }
360 }
361}