A game about forced loneliness, made by TACStudios
1using UnityEngine;
2using System;
3using System.Linq;
4using System.Collections.Generic;
5using UnityEditor.U2D.Sprites;
6
7namespace UnityEditor.U2D.Animation
8{
9 [RequireSpriteDataProvider(typeof(ISpriteMeshDataProvider), typeof(ISpriteBoneDataProvider))]
10 internal partial class SkinningModule : SpriteEditorModuleBase
11 {
12 private static class Styles
13 {
14 public static string moduleName = L10n.Tr("Skinning Editor");
15 }
16
17 private SkinningCache m_SkinningCache;
18 private int m_PrevNearestControl = -1;
19 private SpriteOutlineRenderer m_SpriteOutlineRenderer;
20 private MeshPreviewTool m_MeshPreviewTool;
21 private SkinningMode m_PreviousSkinningMode;
22 private SpriteBoneInfluenceTool m_CharacterSpriteTool;
23 private HorizontalToggleTools m_HorizontalToggleTools;
24 private AnimationAnalytics m_Analytics;
25 private ModuleToolGroup m_ModuleToolGroup;
26 IMeshPreviewBehaviour m_MeshPreviewBehaviourOverride = null;
27 bool m_CollapseToolbar;
28 bool m_HasUnsavedChanges = false;
29 Texture2D m_WorkspaceBackgroundTexture;
30
31 internal SkinningCache skinningCache
32 {
33 get { return m_SkinningCache; }
34 }
35
36 private BaseTool currentTool
37 {
38 get { return skinningCache.selectedTool; }
39 set { skinningCache.selectedTool = value; }
40 }
41
42 private BaseTool previousTool { get; set; }
43
44 public override string moduleName
45 {
46 get { return Styles.moduleName; }
47 }
48
49 public override void OnModuleActivate()
50 {
51 m_SkinningCache = Cache.Create<SkinningCache>();
52 m_WorkspaceBackgroundTexture = new Texture2D(1, 1, TextureFormat.RGBAHalf, false, true);
53
54 m_WorkspaceBackgroundTexture.hideFlags = HideFlags.HideAndDontSave;
55 m_WorkspaceBackgroundTexture.SetPixel(1, 1, new Color(0, 0, 0, 0));
56 m_WorkspaceBackgroundTexture.Apply();
57
58 AddMainUI(spriteEditor.GetMainVisualContainer());
59
60 using (skinningCache.DisableUndoScope())
61 {
62 skinningCache.Create(spriteEditor.GetDataProvider<ISpriteEditorDataProvider>(), SkinningCachePersistentState.instance);
63 skinningCache.CreateToolCache(spriteEditor, m_LayoutOverlay);
64 m_CharacterSpriteTool = skinningCache.CreateTool<SpriteBoneInfluenceTool>();
65 m_CharacterSpriteTool.Initialize(m_LayoutOverlay);
66 m_MeshPreviewTool = skinningCache.CreateTool<MeshPreviewTool>();
67 SetupModuleToolGroup();
68 m_MeshPreviewTool.Activate();
69
70 m_SpriteOutlineRenderer = new SpriteOutlineRenderer(skinningCache.events);
71
72 spriteEditor.enableMouseMoveEvent = true;
73 EditorApplication.playModeStateChanged += PlayModeStateChanged;
74
75 Undo.undoRedoPerformed += UndoRedoPerformed;
76 skinningCache.events.skeletonTopologyChanged.AddListener(SkeletonTopologyChanged);
77 skinningCache.events.skeletonPreviewPoseChanged.AddListener(SkeletonPreviewPoseChanged);
78 skinningCache.events.skeletonBindPoseChanged.AddListener(SkeletonBindPoseChanged);
79 skinningCache.events.characterPartChanged.AddListener(CharacterPartChanged);
80 skinningCache.events.skinningModeChanged.AddListener(OnViewModeChanged);
81 skinningCache.events.meshChanged.AddListener(OnMeshChanged);
82 skinningCache.events.boneNameChanged.AddListener(OnBoneNameChanged);
83 skinningCache.events.boneDepthChanged.AddListener(OnBoneDepthChanged);
84 skinningCache.events.boneColorChanged.AddListener(OnBoneColorChanged);
85 skinningCache.events.meshPreviewBehaviourChange.AddListener(OnMeshPreviewBehaviourChange);
86 skinningCache.events.pivotChange.AddListener(OnPivotChanged);
87
88 skinningCache.RestoreFromPersistentState();
89 ActivateTool(skinningCache.selectedTool);
90 skinningCache.RestoreToolStateFromPersistentState();
91
92 // Set state for Switch Mode tool
93 m_PreviousSkinningMode = skinningCache.mode;
94 if (skinningCache.mode == SkinningMode.Character)
95 {
96 skinningCache.GetTool(Tools.SwitchMode).Deactivate();
97 }
98 else
99 {
100 skinningCache.GetTool(Tools.SwitchMode).Activate();
101 }
102
103 SetupSpriteEditor(true);
104
105 m_HorizontalToggleTools = new HorizontalToggleTools(skinningCache)
106 {
107 onActivateTool = (b) =>
108 {
109 using (skinningCache.UndoScope(TextContent.setTool))
110 {
111 ActivateTool(b);
112 }
113 }
114 };
115
116 var ai = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>() as AssetImporter;
117 m_Analytics = new AnimationAnalytics(new UnityAnalyticsStorage(),
118 skinningCache.events,
119 new SkinningModuleAnalyticsModel(skinningCache),
120 ai == null ? -1 : ai.GetInstanceID());
121
122 UpdateCollapseToolbar();
123 }
124 }
125
126 public override void OnModuleDeactivate()
127 {
128 if (m_SpriteOutlineRenderer != null)
129 m_SpriteOutlineRenderer.Dispose();
130
131 spriteEditor.enableMouseMoveEvent = false;
132 EditorApplication.playModeStateChanged -= PlayModeStateChanged;
133
134 Undo.undoRedoPerformed -= UndoRedoPerformed;
135 skinningCache.events.skeletonTopologyChanged.RemoveListener(SkeletonTopologyChanged);
136 skinningCache.events.skeletonPreviewPoseChanged.RemoveListener(SkeletonPreviewPoseChanged);
137 skinningCache.events.skeletonBindPoseChanged.RemoveListener(SkeletonBindPoseChanged);
138 skinningCache.events.characterPartChanged.RemoveListener(CharacterPartChanged);
139 skinningCache.events.skinningModeChanged.RemoveListener(OnViewModeChanged);
140 skinningCache.events.meshChanged.RemoveListener(OnMeshChanged);
141 skinningCache.events.boneNameChanged.RemoveListener(OnBoneNameChanged);
142 skinningCache.events.boneDepthChanged.RemoveListener(OnBoneDepthChanged);
143 skinningCache.events.boneColorChanged.RemoveListener(OnBoneColorChanged);
144 skinningCache.events.meshPreviewBehaviourChange.RemoveListener(OnMeshPreviewBehaviourChange);
145 skinningCache.events.pivotChange.RemoveListener(OnPivotChanged);
146
147 RemoveMainUI(spriteEditor.GetMainVisualContainer());
148 RestoreSpriteEditor();
149 m_Analytics.Dispose();
150 m_Analytics = null;
151
152 Cache.Destroy(m_SkinningCache);
153 }
154
155 void PlayModeStateChanged(PlayModeStateChange newState)
156 {
157 if (newState == PlayModeStateChange.ExitingEditMode && m_HasUnsavedChanges)
158 {
159 var shouldApply = EditorUtility.DisplayDialog(TextContent.savePopupTitle, TextContent.savePopupMessage, TextContent.savePopupOptionYes, TextContent.savePopupOptionNo);
160 spriteEditor.ApplyOrRevertModification(shouldApply);
161 }
162 }
163
164 private void UpdateCollapseToolbar()
165 {
166 m_CollapseToolbar = SkinningModuleSettings.compactToolBar;
167 m_PoseToolbar.CollapseToolBar(m_CollapseToolbar);
168 m_WeightToolbar.CollapseToolBar(m_CollapseToolbar);
169 m_MeshToolbar.CollapseToolBar(m_CollapseToolbar);
170 m_BoneToolbar.CollapseToolBar(m_CollapseToolbar);
171 m_RigToolbar.CollapseToolBar(m_CollapseToolbar);
172 m_LayoutOverlay.verticalToolbar.Collapse(m_CollapseToolbar);
173 m_HorizontalToggleTools.collapseToolbar = m_CollapseToolbar;
174 }
175
176 private void OnBoneNameChanged(BoneCache bone)
177 {
178 var character = skinningCache.character;
179
180 if (character != null && character.skeleton == bone.skeleton)
181 skinningCache.SyncSpriteSheetSkeletons();
182 DataModified();
183 }
184
185 private void OnBoneDepthChanged(BoneCache bone)
186 {
187 var sprites = skinningCache.GetSprites();
188 var controller = new SpriteMeshDataController();
189
190 foreach (var sprite in sprites)
191 {
192 var mesh = sprite.GetMesh();
193
194 if (mesh.ContainsBone(bone))
195 {
196 controller.spriteMeshData = mesh;
197 controller.SortTrianglesByDepth();
198 skinningCache.events.meshChanged.Invoke(mesh);
199 }
200 }
201
202 DataModified();
203 }
204
205 private void OnBoneColorChanged(BoneCache bone)
206 {
207 DataModified();
208 }
209
210 private void OnMeshChanged(MeshCache mesh)
211 {
212 DataModified();
213 }
214
215 private void OnPivotChanged()
216 {
217 DataModified();
218 }
219
220 void DataModified()
221 {
222 spriteEditor.SetDataModified();
223 m_HasUnsavedChanges = true;
224 }
225
226 private void OnViewModeChanged(SkinningMode mode)
227 {
228 SetupSpriteEditor();
229 }
230
231 private void SetupSpriteEditor(bool setPreviewTexture = false)
232 {
233 var textureProvider = spriteEditor.GetDataProvider<ITextureDataProvider>();
234 if (textureProvider == null)
235 return;
236
237 int width = 0, height = 0;
238 if (skinningCache.mode == SkinningMode.SpriteSheet)
239 {
240 textureProvider.GetTextureActualWidthAndHeight(out width, out height);
241 }
242 else
243 {
244 width = skinningCache.character.dimension.x;
245 height = skinningCache.character.dimension.y;
246 }
247
248 if (m_PreviousSkinningMode != skinningCache.mode || setPreviewTexture)
249 {
250 spriteEditor.SetPreviewTexture(m_WorkspaceBackgroundTexture, width, height);
251 if (m_PreviousSkinningMode != skinningCache.mode)
252 {
253 m_PreviousSkinningMode = skinningCache.mode;
254 spriteEditor.ResetZoomAndScroll();
255 }
256 }
257
258 spriteEditor.spriteRects = new List<SpriteRect>();
259 }
260
261 private void RestoreSpriteEditor()
262 {
263 var textureProvider = spriteEditor.GetDataProvider<ITextureDataProvider>();
264
265 if (textureProvider != null)
266 {
267 int width, height;
268 textureProvider.GetTextureActualWidthAndHeight(out width, out height);
269
270 var texture = textureProvider.previewTexture;
271 spriteEditor.SetPreviewTexture(texture, width, height);
272 }
273
274 var spriteRectProvider = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
275
276 if (spriteRectProvider != null)
277 spriteEditor.spriteRects = new List<SpriteRect>(spriteRectProvider.GetSpriteRects());
278 }
279
280 public override bool CanBeActivated()
281 {
282 var dataProvider = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>();
283 return dataProvider == null ? false : dataProvider.spriteImportMode != SpriteImportMode.None;
284 }
285
286 public override void DoPostGUI()
287 {
288 if (!spriteEditor.windowDimension.Contains(Event.current.mousePosition))
289 HandleUtility.nearestControl = 0;
290
291 if (Event.current.type == EventType.Layout && m_PrevNearestControl != HandleUtility.nearestControl)
292 {
293 m_PrevNearestControl = HandleUtility.nearestControl;
294 spriteEditor.RequestRepaint();
295 }
296
297 skinningCache.EndUndoOperation();
298 }
299
300 public override void DoMainGUI()
301 {
302 Debug.Assert(currentTool != null);
303
304 DoViewGUI();
305
306 if (!spriteEditor.editingDisabled)
307 skinningCache.selectionTool.DoGUI();
308
309 m_MeshPreviewTool.previewBehaviourOverride = m_MeshPreviewBehaviourOverride != null ? m_MeshPreviewBehaviourOverride : currentTool.previewBehaviour;
310 m_MeshPreviewTool.DoGUI();
311 m_MeshPreviewTool.DrawOverlay();
312
313 if (Event.current.type == EventType.Repaint)
314 m_SpriteOutlineRenderer.RenderSpriteOutline(spriteEditor, skinningCache.selectedSprite);
315
316 m_MeshPreviewTool.OverlayWireframe();
317
318 if (!spriteEditor.editingDisabled)
319 {
320 currentTool.DoGUI();
321 DoCopyPasteKeyboardEventHandling();
322 }
323
324 DrawRectGizmos();
325
326 if (SkinningModuleSettings.compactToolBar != m_CollapseToolbar)
327 UpdateCollapseToolbar();
328 }
329
330 public override void DoToolbarGUI(Rect drawArea)
331 {
332 m_HorizontalToggleTools.DoGUI(drawArea, currentTool, spriteEditor.editingDisabled);
333 }
334
335 void DoCopyPasteKeyboardEventHandling()
336 {
337 var evt = Event.current;
338 if (evt.type == EventType.ValidateCommand)
339 {
340 if (evt.commandName == "Copy" || evt.commandName == "Paste")
341 evt.Use();
342 return;
343 }
344
345 if (evt.type == EventType.ExecuteCommand)
346 {
347 var copyTool = skinningCache.GetTool(Tools.CopyPaste) as CopyTool;
348 if (copyTool != null && evt.commandName == "Copy")
349 {
350 copyTool.OnCopyActivated();
351 evt.Use();
352 }
353 else if (copyTool != null && evt.commandName == "Paste")
354 {
355 var boneReadOnly = skinningCache.bonesReadOnly;
356 copyTool.OnPasteActivated(!boneReadOnly, true, false, false);
357 evt.Use();
358 }
359 }
360 }
361
362 private void DrawRectGizmos()
363 {
364 if (Event.current.type == EventType.Repaint)
365 {
366 var selectedSprite = skinningCache.selectedSprite;
367 var sprites = skinningCache.GetSprites();
368 var unselectedRectColor = new Color(1f, 1f, 1f, 0.5f);
369
370 foreach (var sprite in sprites)
371 {
372 var skeleton = skinningCache.GetEffectiveSkeleton(sprite);
373
374 Debug.Assert(skeleton != null);
375
376 if (skeleton.isPosePreview)
377 continue;
378
379 var color = unselectedRectColor;
380
381 if (sprite == selectedSprite)
382 color = DrawingUtility.spriteBorderColor;
383
384 if (skinningCache.mode == SkinningMode.Character
385 && sprite != selectedSprite)
386 continue;
387
388 var matrix = sprite.GetLocalToWorldMatrixFromMode();
389 var rect = new Rect(matrix.MultiplyPoint3x4(Vector3.zero), sprite.textureRect.size);
390
391 DrawingUtility.BeginLines(color);
392 DrawingUtility.DrawBox(rect);
393 DrawingUtility.EndLines();
394 }
395 }
396 }
397
398 private void UndoRedoPerformed()
399 {
400 using (new DisableUndoScope(skinningCache))
401 {
402 UpdateToggleState();
403 skinningCache.UndoRedoPerformed();
404 SetupSpriteEditor();
405 }
406 }
407
408 #region CharacterConsistency
409
410 //TODO: Bring this to a better place, maybe CharacterController
411 private void SkeletonPreviewPoseChanged(SkeletonCache skeleton)
412 {
413 var character = skinningCache.character;
414
415 if (character != null && character.skeleton == skeleton)
416 skinningCache.SyncSpriteSheetSkeletons();
417 }
418
419 private void SkeletonBindPoseChanged(SkeletonCache skeleton)
420 {
421 var character = skinningCache.character;
422
423 if (character != null && character.skeleton == skeleton)
424 skinningCache.SyncSpriteSheetSkeletons();
425 DataModified();
426 }
427
428 private void SkeletonTopologyChanged(SkeletonCache skeleton)
429 {
430 var character = skinningCache.character;
431
432 if (character == null)
433 {
434 var sprite = FindSpriteFromSkeleton(skeleton);
435
436 Debug.Assert(sprite != null);
437
438 sprite.UpdateMesh(skeleton.bones);
439
440 DataModified();
441 }
442 else if (character.skeleton == skeleton)
443 {
444 skinningCache.CreateSpriteSheetSkeletons();
445 DataModified();
446 }
447 }
448
449 private void CharacterPartChanged(CharacterPartCache characterPart)
450 {
451 var character = skinningCache.character;
452
453 Debug.Assert(character != null);
454
455 using (new DefaultPoseScope(character.skeleton))
456 {
457 skinningCache.CreateSpriteSheetSkeleton(characterPart);
458 DataModified();
459 }
460
461 if (skinningCache.mode == SkinningMode.Character)
462 characterPart.SyncSpriteSheetSkeleton();
463 }
464
465 private SpriteCache FindSpriteFromSkeleton(SkeletonCache skeleton)
466 {
467 var sprites = skinningCache.GetSprites();
468 return sprites.FirstOrDefault(sprite => sprite.GetSkeleton() == skeleton);
469 }
470
471 #endregion
472
473 public override bool ApplyRevert(bool apply)
474 {
475 if (apply)
476 {
477 m_Analytics.FlushEvent();
478 ApplyChanges(skinningCache, spriteEditor.GetDataProvider<ISpriteEditorDataProvider>());
479 DoApplyAnalytics();
480 }
481 else
482 skinningCache.Revert();
483
484 m_HasUnsavedChanges = false;
485 return true;
486 }
487
488 internal static void ApplyChanges(SkinningCache skinningCache, ISpriteEditorDataProvider dataProvider)
489 {
490 skinningCache.applyingChanges = true;
491 skinningCache.RestoreBindPose();
492 ApplyBone(skinningCache, dataProvider);
493 ApplyMesh(skinningCache, dataProvider);
494 ApplyCharacter(skinningCache, dataProvider);
495 skinningCache.applyingChanges = false;
496 }
497
498 private void DoApplyAnalytics()
499 {
500 var sprites = skinningCache.GetSprites();
501 var spriteBoneCount = sprites.Select(s => s.GetSkeleton().boneCount).ToArray();
502 BoneCache[] bones = null;
503
504 if (skinningCache.hasCharacter)
505 bones = skinningCache.character.skeleton.bones;
506 else
507 bones = sprites.SelectMany(s => s.GetSkeleton().bones).ToArray();
508
509 m_Analytics.SendApplyEvent(sprites.Length, spriteBoneCount, bones);
510 }
511
512 static void ApplyBone(SkinningCache skinningCache, ISpriteEditorDataProvider dataProvider)
513 {
514 var boneDataProvider = dataProvider.GetDataProvider<ISpriteBoneDataProvider>();
515 if (boneDataProvider != null)
516 {
517 var sprites = skinningCache.GetSprites();
518 foreach (var sprite in sprites)
519 {
520 var bones = sprite.GetSkeleton().bones;
521 boneDataProvider.SetBones(new GUID(sprite.id), bones.ToSpriteBone(sprite.localToWorldMatrix).ToList());
522 }
523 }
524 }
525
526 static void ApplyMesh(SkinningCache skinningCache, ISpriteEditorDataProvider dataProvider)
527 {
528 var meshDataProvider = dataProvider.GetDataProvider<ISpriteMeshDataProvider>();
529 if (meshDataProvider != null)
530 {
531 var sprites = skinningCache.GetSprites();
532 foreach (var sprite in sprites)
533 {
534 var mesh = sprite.GetMesh();
535 var guid = new GUID(sprite.id);
536
537 var vertices = new Vertex2DMetaData[mesh.vertexCount];
538 for (var i = 0; i < vertices.Length; ++i)
539 {
540 vertices[i].position = mesh.vertices[i];
541 vertices[i].boneWeight = mesh.vertexWeights[i].ToBoneWeight(false);
542 }
543
544 meshDataProvider.SetVertices(guid, vertices);
545 meshDataProvider.SetIndices(guid, mesh.indices);
546
547 var edgeVectArr = EditorUtilities.ToVector2Int(mesh.edges);
548 meshDataProvider.SetEdges(guid, edgeVectArr);
549 }
550 }
551 }
552
553 static void ApplyCharacter(SkinningCache skinningCache, ISpriteEditorDataProvider dataProvider)
554 {
555 var characterDataProvider = dataProvider.GetDataProvider<ICharacterDataProvider>();
556 var character = skinningCache.character;
557 if (characterDataProvider != null && character != null)
558 {
559 var data = new CharacterData();
560 var characterBones = character.skeleton.bones;
561 data.bones = characterBones.ToSpriteBone(Matrix4x4.identity);
562 data.pivot = character.pivot;
563 var parts = character.parts;
564 data.parts = parts.Select(x =>
565 new CharacterPart()
566 {
567 spriteId = x.sprite.id,
568 spritePosition = new RectInt((int)x.position.x, (int)x.position.y, (int)x.sprite.textureRect.width, (int)x.sprite.textureRect.height),
569 bones = x.bones.Select(bone => Array.IndexOf(characterBones, bone)).Where(bone => bone != -1).ToArray()
570 }
571 ).ToArray();
572
573 characterDataProvider.SetCharacterData(data);
574
575 }
576 }
577
578 void OnMeshPreviewBehaviourChange(IMeshPreviewBehaviour meshPreviewBehaviour)
579 {
580 m_MeshPreviewBehaviourOverride = meshPreviewBehaviour;
581 }
582
583 private void SetupModuleToolGroup()
584 {
585 m_ModuleToolGroup = new ModuleToolGroup();
586 m_ModuleToolGroup.AddToolToGroup(0, skinningCache.GetTool(Tools.Visibility), null);
587 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.EditGeometry), () => currentTool = skinningCache.GetTool(Tools.EditGeometry));
588 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.CreateVertex), () => currentTool = skinningCache.GetTool(Tools.CreateVertex));
589 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.CreateEdge), () => currentTool = skinningCache.GetTool(Tools.CreateEdge));
590 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.SplitEdge), () => currentTool = skinningCache.GetTool(Tools.SplitEdge));
591 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.GenerateGeometry), () => currentTool = skinningCache.GetTool(Tools.GenerateGeometry));
592 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.EditPose), () => currentTool = skinningCache.GetTool(Tools.EditPose));
593 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.EditJoints), () => currentTool = skinningCache.GetTool(Tools.EditJoints));
594 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.CreateBone), () => currentTool = skinningCache.GetTool(Tools.CreateBone));
595 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.SplitBone), () => currentTool = skinningCache.GetTool(Tools.SplitBone));
596 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.WeightSlider), () => currentTool = skinningCache.GetTool(Tools.WeightSlider));
597 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.WeightBrush), () => currentTool = skinningCache.GetTool(Tools.WeightBrush));
598 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.GenerateWeights), () => currentTool = skinningCache.GetTool(Tools.GenerateWeights));
599 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.BoneInfluence), () => currentTool = skinningCache.GetTool(Tools.BoneInfluence));
600 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.SpriteInfluence), () => currentTool = skinningCache.GetTool(Tools.SpriteInfluence));
601 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.CopyPaste), () => currentTool = skinningCache.GetTool(Tools.CopyPaste));
602 m_ModuleToolGroup.AddToolToGroup(1, skinningCache.GetTool(Tools.CharacterPivotTool), () =>
603 {
604 if (skinningCache.hasCharacter)
605 currentTool = skinningCache.GetTool(Tools.CharacterPivotTool);
606 else
607 ActivateTool(skinningCache.GetTool(Tools.EditPose));
608 });
609 }
610 }
611}