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