@@ -23,6 +23,13 @@ public class AvatarSetupWizard : EditorWindow
2323 private const string VrcftPrefabGuid = "ca618adb2c3333545a1f36d72a73a3ef" ;
2424 private const string VrcftPackageListingUrl = "https://vcc.pawlygon.net/" ;
2525 private const string PatcherHubLatestReleaseApiUrl = "https://api.github.com/repos/PawlygonStudio/PatcherHub/releases/latest" ;
26+ private const string PawlygonWebsiteUrl = "https://www.pawlygon.net" ;
27+ private const string PawlygonTwitterUrl = "https://x.com/Pawlygon_studio" ;
28+ private const string PawlygonYouTubeUrl = "https://www.youtube.com/@Pawlygon" ;
29+ private const string PawlygonDiscordUrl = "https://discord.com/invite/pZew3JGpjb" ;
30+ private const string PackageJsonPath = "Packages/net.pawlygon.unitytools/package.json" ;
31+ private const int SourceFbxPickerControlId = 9001 ;
32+ private const int SourcePrefabPickerControlId = 9002 ;
2633
2734 [ SerializeField ] private string mainFolderName = DefaultMainFolderName ;
2835 [ SerializeField ] private bool useSeparateFolderPerAvatar ;
@@ -41,6 +48,7 @@ public class AvatarSetupWizard : EditorWindow
4148 private string patcherHubImportStatusMessage = string . Empty ;
4249 private bool patcherHubImportedThisSession ;
4350 private Texture2D pawlygonLogoTexture ;
51+ private string packageVersion = "1.0.0" ;
4452 private GUIStyle sectionStyle ;
4553 private GUIStyle titleStyle ;
4654 private GUIStyle stepStyle ;
@@ -49,6 +57,8 @@ public class AvatarSetupWizard : EditorWindow
4957 private GUIStyle richMiniLabelStyle ;
5058 private GUIStyle headerTitleStyle ;
5159 private GUIStyle headerSubtitleStyle ;
60+ private GUIStyle footerStyle ;
61+ private GUIStyle footerLinkStyle ;
5262
5363 private enum WizardStep
5464 {
@@ -114,6 +124,12 @@ private class GitHubReleaseAsset
114124 public string browser_download_url ;
115125 }
116126
127+ [ Serializable ]
128+ private class PackageManifestInfo
129+ {
130+ public string version ;
131+ }
132+
117133 [ MenuItem ( "!Pawlygon/Avatar Setup Wizard" ) ]
118134 public static void ShowWindow ( )
119135 {
@@ -126,6 +142,7 @@ private void OnEnable()
126142 {
127143 FBXImportDetector . FbxReimported += HandleFbxReimported ;
128144 EnsureAtLeastOneEntry ( ) ;
145+ LoadPackageVersion ( ) ;
129146 }
130147
131148 private void OnDisable ( )
@@ -138,6 +155,7 @@ private void OnGUI()
138155 {
139156 EnsureStyles ( ) ;
140157 EnsureAtLeastOneEntry ( ) ;
158+ HandleObjectPickerSelection ( ) ;
141159
142160 DrawHeader ( ) ;
143161 DrawStepIndicator ( ) ;
@@ -167,6 +185,9 @@ private void OnGUI()
167185 EditorGUILayout . Space ( ) ;
168186 EditorGUILayout . HelpBox ( statusMessage , MessageType . Info ) ;
169187 }
188+
189+ EditorGUILayout . Space ( 8f ) ;
190+ DrawFooter ( ) ;
170191 }
171192
172193 private void DrawSetupStep ( )
@@ -259,8 +280,8 @@ private void DrawAvatarEntryEditor(int index, AvatarEntry entry)
259280 }
260281 }
261282
262- entry . sourceFbx = ( GameObject ) EditorGUILayout . ObjectField ( "Source FBX" , entry . sourceFbx , typeof ( GameObject ) , false ) ;
263- entry . sourcePrefab = ( GameObject ) EditorGUILayout . ObjectField ( "Source Prefab" , entry . sourcePrefab , typeof ( GameObject ) , false ) ;
283+ entry . sourceFbx = DrawFilteredAssetField ( "Source FBX" , entry . sourceFbx , "fbx" , SourceFbxPickerControlId + index * 2 ) ;
284+ entry . sourcePrefab = DrawFilteredAssetField ( "Source Prefab" , entry . sourcePrefab , "prefab" , SourcePrefabPickerControlId + index * 2 ) ;
264285
265286 if ( useSeparateFolderPerAvatar )
266287 {
@@ -1726,6 +1747,66 @@ private static void DrawReadOnlyPathField(string label, string value)
17261747 }
17271748 }
17281749
1750+ private GameObject DrawFilteredAssetField ( string label , GameObject currentValue , string extension , int controlId )
1751+ {
1752+ Rect totalRect = EditorGUILayout . GetControlRect ( ) ;
1753+ Rect fieldRect = EditorGUI . PrefixLabel ( totalRect , new GUIContent ( label ) ) ;
1754+
1755+ Event currentEvent = Event . current ;
1756+ if ( currentEvent . type == EventType . MouseDown && fieldRect . Contains ( currentEvent . mousePosition ) )
1757+ {
1758+ bool clickedPickerButton = currentEvent . mousePosition . x >= fieldRect . xMax - 19f ;
1759+ if ( clickedPickerButton )
1760+ {
1761+ EditorGUIUtility . ShowObjectPicker < GameObject > ( currentValue , false , $ "glob:\" *.{ extension } \" ", controlId ) ;
1762+ currentEvent . Use ( ) ;
1763+ }
1764+ }
1765+
1766+ return ( GameObject ) EditorGUI . ObjectField ( fieldRect , GUIContent . none , currentValue , typeof ( GameObject ) , false ) ;
1767+ }
1768+
1769+ private void HandleObjectPickerSelection ( )
1770+ {
1771+ Event currentEvent = Event . current ;
1772+ if ( currentEvent . type != EventType . ExecuteCommand && currentEvent . type != EventType . ValidateCommand )
1773+ {
1774+ return ;
1775+ }
1776+
1777+ if ( currentEvent . commandName != "ObjectSelectorUpdated" && currentEvent . commandName != "ObjectSelectorClosed" )
1778+ {
1779+ return ;
1780+ }
1781+
1782+ int pickerControlId = EditorGUIUtility . GetObjectPickerControlID ( ) ;
1783+ UnityEngine . Object pickedObject = EditorGUIUtility . GetObjectPickerObject ( ) ;
1784+ if ( pickedObject is not GameObject pickedGameObject )
1785+ {
1786+ return ;
1787+ }
1788+
1789+ string assetPath = AssetDatabase . GetAssetPath ( pickedGameObject ) ;
1790+ string extension = Path . GetExtension ( assetPath ) ;
1791+
1792+ for ( int i = 0 ; i < avatarEntries . Count ; i ++ )
1793+ {
1794+ if ( pickerControlId == SourceFbxPickerControlId + i * 2 && string . Equals ( extension , ".fbx" , StringComparison . OrdinalIgnoreCase ) )
1795+ {
1796+ avatarEntries [ i ] . sourceFbx = pickedGameObject ;
1797+ Repaint ( ) ;
1798+ return ;
1799+ }
1800+
1801+ if ( pickerControlId == SourcePrefabPickerControlId + i * 2 && string . Equals ( extension , ".prefab" , StringComparison . OrdinalIgnoreCase ) )
1802+ {
1803+ avatarEntries [ i ] . sourcePrefab = pickedGameObject ;
1804+ Repaint ( ) ;
1805+ return ;
1806+ }
1807+ }
1808+ }
1809+
17291810 private void EnsureStyles ( )
17301811 {
17311812 if ( sectionStyle != null )
@@ -1761,6 +1842,22 @@ private void EnsureStyles()
17611842 ? new Color ( 0.72f , 0.72f , 0.72f )
17621843 : new Color ( 0.35f , 0.35f , 0.35f ) ;
17631844
1845+ footerStyle = new GUIStyle ( EditorStyles . label )
1846+ {
1847+ alignment = TextAnchor . MiddleCenter ,
1848+ fontSize = 11
1849+ } ;
1850+
1851+ footerLinkStyle = new GUIStyle ( EditorStyles . label )
1852+ {
1853+ alignment = TextAnchor . MiddleCenter ,
1854+ fontSize = 11 ,
1855+ stretchWidth = false
1856+ } ;
1857+
1858+ footerLinkStyle . normal . textColor = new Color ( 0.39f , 0.67f , 1f ) ;
1859+ footerLinkStyle . hover . textColor = new Color ( 0.58f , 0.79f , 1f ) ;
1860+
17641861 stepStyle = new GUIStyle ( EditorStyles . miniLabel )
17651862 {
17661863 alignment = TextAnchor . MiddleCenter ,
@@ -1800,11 +1897,12 @@ private void DrawHeader()
18001897
18011898 float logoSize = 70f ;
18021899 float spacing = 16f ;
1803- float totalWidth = logoSize + spacing + 360f ;
1900+ float textBlockWidth = Mathf . Min ( 520f , Mathf . Max ( 300f , headerRect . width - 140f ) ) ;
1901+ float totalWidth = logoSize + spacing + textBlockWidth ;
18041902 float startX = headerRect . x + Mathf . Max ( 0f , ( headerRect . width - totalWidth ) * 0.5f ) ;
18051903 float logoY = headerRect . y + ( headerRect . height - logoSize ) * 0.5f ;
18061904 float textX = startX + logoSize + spacing ;
1807- float textWidth = Mathf . Max ( 220f , headerRect . xMax - textX - 20f ) ;
1905+ float textWidth = textBlockWidth ;
18081906
18091907 if ( pawlygonLogoTexture != null )
18101908 {
@@ -1814,14 +1912,57 @@ private void DrawHeader()
18141912 EditorGUI . LabelField ( new Rect ( textX , headerRect . y + 18f , textWidth , 22f ) , "Pawlygon Avatar Setup Wizard" , headerTitleStyle ) ;
18151913 EditorGUI . LabelField ( new Rect ( textX , headerRect . y + 40f , textWidth , 36f ) , "Tool to duplicate avatars, prepare face tracking assets, and build ready-to-edit prefabs." , headerSubtitleStyle ) ;
18161914
1817- EditorGUILayout . Space ( 10f ) ;
1818- Rect separatorRect = EditorGUILayout . GetControlRect ( false , 1f ) ;
1819- EditorGUI . DrawRect ( separatorRect , EditorGUIUtility . isProSkin ? new Color ( 0.34f , 0.34f , 0.34f ) : new Color ( 0.65f , 0.65f , 0.65f ) ) ;
18201915 }
18211916
18221917 EditorGUILayout . Space ( 6f ) ;
18231918 }
18241919
1920+ private void DrawFooter ( )
1921+ {
1922+ using ( new EditorGUILayout . VerticalScope ( new GUIStyle ( EditorStyles . helpBox ) { padding = new RectOffset ( 12 , 12 , 10 , 10 ) } ) )
1923+ {
1924+ EditorGUILayout . LabelField ( $ "Made with ❤ by Pawlygon Studio • v{ packageVersion } ", footerStyle ) ;
1925+ EditorGUILayout . Space ( 6f ) ;
1926+
1927+ using ( new EditorGUILayout . HorizontalScope ( ) )
1928+ {
1929+ GUILayout . FlexibleSpace ( ) ;
1930+ DrawFooterLink ( "Website" , PawlygonWebsiteUrl ) ;
1931+ GUILayout . Space ( 28f ) ;
1932+ DrawFooterLink ( "X (Twitter)" , PawlygonTwitterUrl ) ;
1933+ GUILayout . Space ( 28f ) ;
1934+ DrawFooterLink ( "YouTube" , PawlygonYouTubeUrl ) ;
1935+ GUILayout . Space ( 28f ) ;
1936+ DrawFooterLink ( "Discord" , PawlygonDiscordUrl ) ;
1937+
1938+ GUILayout . FlexibleSpace ( ) ;
1939+ }
1940+ }
1941+ }
1942+
1943+ private void DrawFooterLink ( string label , string url )
1944+ {
1945+ if ( GUILayout . Button ( label , footerLinkStyle ) )
1946+ {
1947+ Application . OpenURL ( url ) ;
1948+ }
1949+ }
1950+
1951+ private void LoadPackageVersion ( )
1952+ {
1953+ TextAsset packageJson = AssetDatabase . LoadAssetAtPath < TextAsset > ( PackageJsonPath ) ;
1954+ if ( packageJson == null || string . IsNullOrWhiteSpace ( packageJson . text ) )
1955+ {
1956+ return ;
1957+ }
1958+
1959+ PackageManifestInfo manifestInfo = JsonUtility . FromJson < PackageManifestInfo > ( packageJson . text ) ;
1960+ if ( ! string . IsNullOrWhiteSpace ( manifestInfo ? . version ) )
1961+ {
1962+ packageVersion = manifestInfo . version ;
1963+ }
1964+ }
1965+
18251966 private void DrawStepIndicator ( )
18261967 {
18271968 using ( new EditorGUILayout . HorizontalScope ( new GUIStyle ( EditorStyles . helpBox ) { padding = new RectOffset ( 5 , 5 , 5 , 5 ) } ) )
0 commit comments