imported animancer and rainbow folder
This commit is contained in:
8
Packages/com.kybernetik.animancer/Art.meta
Normal file
8
Packages/com.kybernetik.animancer/Art.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07fd09f3df0ed1b4a876daf641301455
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac7b5777c4040094a9e5e2dae7be67b7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,529 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &-5087839347003138994
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Body-URP
|
||||
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _NORMALMAP
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AlphaClip: 1
|
||||
- _AlphaToMask: 1
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0.5
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.1
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _UVSec: 0
|
||||
- _WorkflowMode: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Body-Builtin
|
||||
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _NORMALMAP
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0.5
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8231c727c20d639459e01298ffacd6c6, type: 3}
|
||||
m_Name: AnimancerHumanoid-Body-Variants
|
||||
m_EditorClassIdentifier:
|
||||
_Material: {fileID: 2100000, guid: cac1be5495752be49a0c8b893d7c6865, type: 2}
|
||||
_Variants:
|
||||
- {fileID: -5087839347003138994}
|
||||
- {fileID: 8308403988194906132}
|
||||
- {fileID: 2100000}
|
||||
--- !u!21 &8308403988194906132
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Body-HDRP
|
||||
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _DISABLE_SSR_TRANSPARENT
|
||||
- _NORMALMAP
|
||||
- _NORMALMAP_TANGENT_SPACE
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2475
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- TransparentDepthPrepass
|
||||
- TransparentDepthPostpass
|
||||
- TransparentBackface
|
||||
- RayTracingPrepass
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _AnisotropyMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BaseColorMap:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BentNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BentNormalMapOS:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _CoatMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissiveColorMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _HeightMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _IridescenceMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _IridescenceThicknessMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _NormalMap:
|
||||
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _NormalMapOS:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecularColorMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SubsurfaceMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TangentMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TangentMapOS:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ThicknessMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TransmissionMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TransmittanceColorMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AORemapMax: 1
|
||||
- _AORemapMin: 0
|
||||
- _ATDistance: 1
|
||||
- _AddPrecomputedVelocity: 0
|
||||
- _AlbedoAffectEmissive: 0
|
||||
- _AlphaCutoff: 0.5
|
||||
- _AlphaCutoffEnable: 1
|
||||
- _AlphaCutoffPostpass: 0.5
|
||||
- _AlphaCutoffPrepass: 0.5
|
||||
- _AlphaCutoffShadow: 0.5
|
||||
- _AlphaDstBlend: 0
|
||||
- _AlphaRemapMax: 1
|
||||
- _AlphaRemapMin: 0
|
||||
- _AlphaSrcBlend: 1
|
||||
- _Anisotropy: 0
|
||||
- _BlendMode: 0
|
||||
- _BumpScale: 1
|
||||
- _CoatMask: 0
|
||||
- _CullMode: 2
|
||||
- _CullModeForward: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DepthOffsetEnable: 0
|
||||
- _DetailAlbedoScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DetailNormalScale: 1
|
||||
- _DetailSmoothnessScale: 1
|
||||
- _DiffusionProfile: 0
|
||||
- _DiffusionProfileHash: 0
|
||||
- _DisplacementLockObjectScale: 1
|
||||
- _DisplacementLockTilingScale: 1
|
||||
- _DisplacementMode: 0
|
||||
- _DoubleSidedEnable: 0
|
||||
- _DoubleSidedGIMode: 0
|
||||
- _DoubleSidedNormalMode: 1
|
||||
- _DstBlend: 0
|
||||
- _EmissiveColorMode: 1
|
||||
- _EmissiveExposureWeight: 1
|
||||
- _EmissiveIntensity: 1
|
||||
- _EmissiveIntensityUnit: 0
|
||||
- _EnableBlendModePreserveSpecularLighting: 1
|
||||
- _EnableFogOnTransparent: 1
|
||||
- _EnableGeometricSpecularAA: 0
|
||||
- _EnergyConservingSpecularColor: 1
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _HeightAmplitude: 0.02
|
||||
- _HeightCenter: 0.5
|
||||
- _HeightMapParametrization: 0
|
||||
- _HeightMax: 1
|
||||
- _HeightMin: -1
|
||||
- _HeightOffset: 0
|
||||
- _HeightPoMAmplitude: 2
|
||||
- _HeightTessAmplitude: 2
|
||||
- _HeightTessCenter: 0.5
|
||||
- _InvTilingScale: 1
|
||||
- _Ior: 1.5
|
||||
- _IridescenceMask: 1
|
||||
- _IridescenceThickness: 1
|
||||
- _LinkDetailsWithBase: 0
|
||||
- _MaterialID: 1
|
||||
- _Metallic: 0.21763763
|
||||
- _MetallicRemapMax: 0.21763763
|
||||
- _MetallicRemapMin: 0
|
||||
- _Mode: 1
|
||||
- _NormalMapSpace: 0
|
||||
- _NormalScale: 1
|
||||
- _ObjectSpaceUVMapping: 0
|
||||
- _ObjectSpaceUVMappingEmissive: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OpaqueCullMode: 2
|
||||
- _PPDLodThreshold: 5
|
||||
- _PPDMaxSamples: 15
|
||||
- _PPDMinSamples: 5
|
||||
- _PPDPrimitiveLength: 1
|
||||
- _PPDPrimitiveWidth: 1
|
||||
- _Parallax: 0.02
|
||||
- _RayTracing: 0
|
||||
- _ReceivesSSR: 1
|
||||
- _ReceivesSSRTransparent: 0
|
||||
- _RefractionModel: 0
|
||||
- _Smoothness: 0.1
|
||||
- _SmoothnessRemapMax: 0.1
|
||||
- _SmoothnessRemapMin: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularAAScreenSpaceVariance: 0.1
|
||||
- _SpecularAAThreshold: 0.2
|
||||
- _SpecularHighlights: 1
|
||||
- _SpecularOcclusionMode: 1
|
||||
- _SrcBlend: 1
|
||||
- _StencilRef: 0
|
||||
- _StencilRefDepth: 8
|
||||
- _StencilRefGBuffer: 10
|
||||
- _StencilRefMV: 40
|
||||
- _StencilWriteMask: 6
|
||||
- _StencilWriteMaskDepth: 9
|
||||
- _StencilWriteMaskGBuffer: 15
|
||||
- _StencilWriteMaskMV: 41
|
||||
- _SubsurfaceMask: 1
|
||||
- _SupportDecals: 1
|
||||
- _SurfaceType: 0
|
||||
- _TexWorldScale: 1
|
||||
- _TexWorldScaleEmissive: 1
|
||||
- _Thickness: 1
|
||||
- _TransmissionEnable: 1
|
||||
- _TransmissionMask: 1
|
||||
- _TransparentBackfaceEnable: 0
|
||||
- _TransparentCullMode: 2
|
||||
- _TransparentDepthPostpassEnable: 0
|
||||
- _TransparentDepthPrepassEnable: 0
|
||||
- _TransparentSortPriority: 0
|
||||
- _TransparentWritingMotionVec: 0
|
||||
- _TransparentZWrite: 0
|
||||
- _UVBase: 0
|
||||
- _UVDetail: 0
|
||||
- _UVEmissive: 0
|
||||
- _UVSec: 0
|
||||
- _UseEmissiveIntensity: 0
|
||||
- _UseShadowThreshold: 0
|
||||
- _ZTestDepthEqualForOpaque: 3
|
||||
- _ZTestGBuffer: 3
|
||||
- _ZTestTransparent: 4
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _BaseColorMap_MipInfo: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _DiffusionProfileAsset: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _DoubleSidedConstants: {r: 1, g: 1, b: -1, a: 0}
|
||||
- _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissiveColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissiveColorLDR: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _InvPrimScale: {r: 1, g: 1, b: 0, a: 0}
|
||||
- _IridescenceThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
|
||||
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _ThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
|
||||
- _TransmittanceColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
|
||||
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}
|
||||
- _UVMappingMaskEmissive: {r: 1, g: 0, b: 0, a: 0}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 337e4811ec2d4a848b80babd997d4be5
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Body-Variants.asset
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,139 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &-2036435769910986465
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
version: 9
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Body
|
||||
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _NORMALMAP
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 222dcdea0e5223c44a4021c4bc04bcf1, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 7a55677d26cde6e439d31127b134e95b, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AlphaClip: 1
|
||||
- _AlphaToMask: 1
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0.5
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.1
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _UVSec: 0
|
||||
- _WorkflowMode: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cac1be5495752be49a0c8b893d7c6865
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Body.mat
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,529 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Face-Builtin
|
||||
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _NORMALMAP
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0.5
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 8231c727c20d639459e01298ffacd6c6, type: 3}
|
||||
m_Name: AnimancerHumanoid-Face-Variants
|
||||
m_EditorClassIdentifier:
|
||||
_Material: {fileID: 2100000, guid: 6535c7c5fb8a8934689cbde1388be193, type: 2}
|
||||
_Variants:
|
||||
- {fileID: 1581502679226286254}
|
||||
- {fileID: 6540744349012763745}
|
||||
- {fileID: 2100000}
|
||||
--- !u!21 &1581502679226286254
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Face-URP
|
||||
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _NORMALMAP
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AlphaClip: 1
|
||||
- _AlphaToMask: 1
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0.5
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.1
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _UVSec: 0
|
||||
- _WorkflowMode: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
--- !u!21 &6540744349012763745
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Face-HDRP
|
||||
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _DISABLE_SSR_TRANSPARENT
|
||||
- _NORMALMAP
|
||||
- _NORMALMAP_TANGENT_SPACE
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2475
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- TransparentDepthPrepass
|
||||
- TransparentDepthPostpass
|
||||
- TransparentBackface
|
||||
- RayTracingPrepass
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _AnisotropyMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BaseColorMap:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BentNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BentNormalMapOS:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _CoatMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissiveColorMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _HeightMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _IridescenceMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _IridescenceThicknessMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _NormalMap:
|
||||
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _NormalMapOS:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecularColorMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SubsurfaceMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TangentMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TangentMapOS:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ThicknessMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TransmissionMaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _TransmittanceColorMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AORemapMax: 1
|
||||
- _AORemapMin: 0
|
||||
- _ATDistance: 1
|
||||
- _AddPrecomputedVelocity: 0
|
||||
- _AlbedoAffectEmissive: 0
|
||||
- _AlphaCutoff: 0.5
|
||||
- _AlphaCutoffEnable: 1
|
||||
- _AlphaCutoffPostpass: 0.5
|
||||
- _AlphaCutoffPrepass: 0.5
|
||||
- _AlphaCutoffShadow: 0.5
|
||||
- _AlphaDstBlend: 0
|
||||
- _AlphaRemapMax: 1
|
||||
- _AlphaRemapMin: 0
|
||||
- _AlphaSrcBlend: 1
|
||||
- _Anisotropy: 0
|
||||
- _BlendMode: 0
|
||||
- _BumpScale: 1
|
||||
- _CoatMask: 0
|
||||
- _CullMode: 2
|
||||
- _CullModeForward: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DepthOffsetEnable: 0
|
||||
- _DetailAlbedoScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DetailNormalScale: 1
|
||||
- _DetailSmoothnessScale: 1
|
||||
- _DiffusionProfile: 0
|
||||
- _DiffusionProfileHash: 0
|
||||
- _DisplacementLockObjectScale: 1
|
||||
- _DisplacementLockTilingScale: 1
|
||||
- _DisplacementMode: 0
|
||||
- _DoubleSidedEnable: 0
|
||||
- _DoubleSidedGIMode: 0
|
||||
- _DoubleSidedNormalMode: 1
|
||||
- _DstBlend: 0
|
||||
- _EmissiveColorMode: 1
|
||||
- _EmissiveExposureWeight: 1
|
||||
- _EmissiveIntensity: 1
|
||||
- _EmissiveIntensityUnit: 0
|
||||
- _EnableBlendModePreserveSpecularLighting: 1
|
||||
- _EnableFogOnTransparent: 1
|
||||
- _EnableGeometricSpecularAA: 0
|
||||
- _EnergyConservingSpecularColor: 1
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _HeightAmplitude: 0.02
|
||||
- _HeightCenter: 0.5
|
||||
- _HeightMapParametrization: 0
|
||||
- _HeightMax: 1
|
||||
- _HeightMin: -1
|
||||
- _HeightOffset: 0
|
||||
- _HeightPoMAmplitude: 2
|
||||
- _HeightTessAmplitude: 2
|
||||
- _HeightTessCenter: 0.5
|
||||
- _InvTilingScale: 1
|
||||
- _Ior: 1.5
|
||||
- _IridescenceMask: 1
|
||||
- _IridescenceThickness: 1
|
||||
- _LinkDetailsWithBase: 0
|
||||
- _MaterialID: 1
|
||||
- _Metallic: 0.21763763
|
||||
- _MetallicRemapMax: 0.21763763
|
||||
- _MetallicRemapMin: 0
|
||||
- _Mode: 1
|
||||
- _NormalMapSpace: 0
|
||||
- _NormalScale: 1
|
||||
- _ObjectSpaceUVMapping: 0
|
||||
- _ObjectSpaceUVMappingEmissive: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _OpaqueCullMode: 2
|
||||
- _PPDLodThreshold: 5
|
||||
- _PPDMaxSamples: 15
|
||||
- _PPDMinSamples: 5
|
||||
- _PPDPrimitiveLength: 1
|
||||
- _PPDPrimitiveWidth: 1
|
||||
- _Parallax: 0.02
|
||||
- _RayTracing: 0
|
||||
- _ReceivesSSR: 1
|
||||
- _ReceivesSSRTransparent: 0
|
||||
- _RefractionModel: 0
|
||||
- _Smoothness: 0.1
|
||||
- _SmoothnessRemapMax: 0.1
|
||||
- _SmoothnessRemapMin: 0
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularAAScreenSpaceVariance: 0.1
|
||||
- _SpecularAAThreshold: 0.2
|
||||
- _SpecularHighlights: 1
|
||||
- _SpecularOcclusionMode: 1
|
||||
- _SrcBlend: 1
|
||||
- _StencilRef: 0
|
||||
- _StencilRefDepth: 8
|
||||
- _StencilRefGBuffer: 10
|
||||
- _StencilRefMV: 40
|
||||
- _StencilWriteMask: 6
|
||||
- _StencilWriteMaskDepth: 9
|
||||
- _StencilWriteMaskGBuffer: 15
|
||||
- _StencilWriteMaskMV: 41
|
||||
- _SubsurfaceMask: 1
|
||||
- _SupportDecals: 1
|
||||
- _SurfaceType: 0
|
||||
- _TexWorldScale: 1
|
||||
- _TexWorldScaleEmissive: 1
|
||||
- _Thickness: 1
|
||||
- _TransmissionEnable: 1
|
||||
- _TransmissionMask: 1
|
||||
- _TransparentBackfaceEnable: 0
|
||||
- _TransparentCullMode: 2
|
||||
- _TransparentDepthPostpassEnable: 0
|
||||
- _TransparentDepthPrepassEnable: 0
|
||||
- _TransparentSortPriority: 0
|
||||
- _TransparentWritingMotionVec: 0
|
||||
- _TransparentZWrite: 0
|
||||
- _UVBase: 0
|
||||
- _UVDetail: 0
|
||||
- _UVEmissive: 0
|
||||
- _UVSec: 0
|
||||
- _UseEmissiveIntensity: 0
|
||||
- _UseShadowThreshold: 0
|
||||
- _ZTestDepthEqualForOpaque: 3
|
||||
- _ZTestGBuffer: 3
|
||||
- _ZTestTransparent: 4
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _BaseColorMap_MipInfo: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _DiffusionProfileAsset: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _DoubleSidedConstants: {r: 1, g: 1, b: -1, a: 0}
|
||||
- _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissiveColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EmissiveColorLDR: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _InvPrimScale: {r: 1, g: 1, b: 0, a: 0}
|
||||
- _IridescenceThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
|
||||
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _ThicknessRemap: {r: 0, g: 1, b: 0, a: 0}
|
||||
- _TransmittanceColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
|
||||
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}
|
||||
- _UVMappingMaskEmissive: {r: 1, g: 0, b: 0, a: 0}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36e0a2860b54531479d1a601cb1c61ff
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Face-Variants.asset
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,139 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AnimancerHumanoid-Face
|
||||
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
- _NORMALMAP
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 2800000, guid: 9dcc4e0c655862c45938e956f19ed59c, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: c644b15de86a4f043bfb7c162d1f0b66, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AlphaClip: 1
|
||||
- _AlphaToMask: 1
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BumpScale: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _Cull: 2
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.1
|
||||
- _GlossyReflections: 1
|
||||
- _Metallic: 0.5
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.1
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _UVSec: 0
|
||||
- _WorkflowMode: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
--- !u!114 &949205788524907297
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
version: 9
|
||||
@@ -0,0 +1,15 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6535c7c5fb8a8934689cbde1388be193
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid-Face.mat
|
||||
uploadId: 795566
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f976ca0fb1329b44a8bc3dcca706751a
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/AnimancerHumanoid.prefab
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e53ced3af95a6d44bd6c409fd8b29e5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 808 KiB |
@@ -0,0 +1,134 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a55677d26cde6e439d31127b134e95b
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Body-Albedo.png
|
||||
uploadId: 795566
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 326 KiB |
@@ -0,0 +1,134 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 222dcdea0e5223c44a4021c4bc04bcf1
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 0
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 1
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Body-Normal.png
|
||||
uploadId: 795566
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 631 KiB |
@@ -0,0 +1,134 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c644b15de86a4f043bfb7c162d1f0b66
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Face-Albedo.png
|
||||
uploadId: 795566
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,134 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dcc4e0c655862c45938e956f19ed59c
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
sRGBTexture: 0
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 1
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Humanoid/Textures/AnimancerHumanoid-Face-Normal.png
|
||||
uploadId: 795566
|
||||
BIN
Packages/com.kybernetik.animancer/Art/Animancer Icon.png
Normal file
BIN
Packages/com.kybernetik.animancer/Art/Animancer Icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
113
Packages/com.kybernetik.animancer/Art/Animancer Icon.png.meta
Normal file
113
Packages/com.kybernetik.animancer/Art/Animancer Icon.png.meta
Normal file
@@ -0,0 +1,113 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4e06a71fc03595429cac47cd385c4c1
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: -1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 2
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
- serializedVersion: 2
|
||||
buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Art/Animancer Icon.png
|
||||
uploadId: 795566
|
||||
8
Packages/com.kybernetik.animancer/Editor.meta
Normal file
8
Packages/com.kybernetik.animancer/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3bfb48a622b34b48a451f1ef2c43fe8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cc25f23cb30b8c41ba4bb4390768a6c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] Displays the <see cref="AnimancerSettings"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/AnimancerSettingsTool
|
||||
[Serializable]
|
||||
public class AnimancerSettingsTool : AnimancerToolsWindow.Tool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => int.MaxValue;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Animancer Settings";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL
|
||||
=> $"{Strings.DocsURLs.APIDocumentation}.{nameof(Editor)}/{nameof(AnimancerSettings)}";
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[NonSerialized] private readonly CachedEditor SettingsEditor = new();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
SettingsEditor.Dispose();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
var settings = AnimancerSettings.Instance;
|
||||
if (settings == null)
|
||||
return;
|
||||
|
||||
AnimancerSettings.Editor.HideNextInfo = true;
|
||||
|
||||
SettingsEditor.GetEditor(settings).OnInspectorGUI();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 809481e9287856440bbcfa5e886163d4
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/AnimancerSettingsTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,279 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// An <see cref="EditorWindow"/> with various utilities for managing sprites and generating animations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
|
||||
/// Animancer Tools</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/AnimancerToolsWindow
|
||||
///
|
||||
public sealed partial class AnimancerToolsWindow : EditorWindow
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The display name of this window.</summary>
|
||||
public const string Name = "Animancer Tools";
|
||||
|
||||
/// <summary>The singleton instance of this window.</summary>
|
||||
public static AnimancerToolsWindow Instance { get; private set; }
|
||||
|
||||
[SerializeReference] private List<Tool> _Tools;
|
||||
|
||||
[SerializeField] private Vector2 _Scroll;
|
||||
|
||||
[SerializeField] private int _CurrentTool = -1;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private SerializedObject _SerializedObject;
|
||||
|
||||
private SerializedObject SerializedObject
|
||||
=> _SerializedObject ??= new(this);
|
||||
|
||||
/// <summary>Returns the <see cref="SerializedProperty"/> which represents the specified `tool`.</summary>
|
||||
public SerializedProperty FindSerializedPropertyForTool(Tool tool)
|
||||
{
|
||||
var index = _Tools.IndexOf(tool);
|
||||
var property = SerializedObject.FindProperty(nameof(_Tools));
|
||||
return property.GetArrayElementAtIndex(index);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
titleContent = new(Name);
|
||||
Instance = this;
|
||||
|
||||
InitializeTools();
|
||||
|
||||
Undo.undoRedoPerformed += Repaint;
|
||||
|
||||
OnSelectionChange();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void InitializeTools()
|
||||
{
|
||||
AnimancerEditorUtilities.InstantiateDerivedTypes(ref _Tools);
|
||||
|
||||
for (int i = 0; i < _Tools.Count; i++)
|
||||
_Tools[i].OnEnable(i);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private int IndexOfTool(Type type)
|
||||
{
|
||||
for (int i = 0; i < _Tools.Count; i++)
|
||||
if (_Tools[i].GetType() == type)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Undo.undoRedoPerformed -= Repaint;
|
||||
|
||||
for (int i = 0; i < _Tools.Count; i++)
|
||||
_Tools[i].OnDisable();
|
||||
|
||||
if (_SerializedObject != null)
|
||||
{
|
||||
_SerializedObject.Dispose();
|
||||
_SerializedObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void OnSelectionChange()
|
||||
{
|
||||
for (int i = 0; i < _Tools.Count; i++)
|
||||
_Tools[i].OnSelectionChanged();
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUIUtility.labelWidth = Mathf.Min(EditorGUIUtility.labelWidth, position.width * 0.5f);
|
||||
EditorGUIUtility.wideMode = true;
|
||||
|
||||
_Scroll = GUILayout.BeginScrollView(_Scroll);
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.EndVertical();
|
||||
for (int i = 0; i < _Tools.Count; i++)
|
||||
_Tools[i].DoGUI();
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Causes the <see cref="Instance"/> to redraw its GUI.</summary>
|
||||
public static new void Repaint()
|
||||
{
|
||||
if (Instance != null)
|
||||
((EditorWindow)Instance).Repaint();
|
||||
}
|
||||
|
||||
/// <summary>Calls <see cref="Undo.RecordObject(Object, string)"/> for this window.</summary>
|
||||
public static void RecordUndo()
|
||||
=> Undo.RecordObject(Instance, Name);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calls <see cref="EditorGUI.BeginChangeCheck"/>.</summary>
|
||||
public static void BeginChangeCheck()
|
||||
=> EditorGUI.BeginChangeCheck();
|
||||
|
||||
/// <summary>Calls <see cref="EditorGUI.EndChangeCheck"/> and <see cref="RecordUndo"/> if it returned true.</summary>
|
||||
public static bool EndChangeCheck()
|
||||
{
|
||||
if (!EditorGUI.EndChangeCheck())
|
||||
return false;
|
||||
|
||||
RecordUndo();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Calls <see cref="EndChangeCheck"/> and sets the <c>field = value</c> if it returned true.</summary>
|
||||
public static bool EndChangeCheck<T>(ref T field, T value)
|
||||
{
|
||||
if (!EndChangeCheck())
|
||||
return false;
|
||||
|
||||
field = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates and initializes a new <see cref="ReorderableList"/>.</summary>
|
||||
public static ReorderableList CreateReorderableList<T>(
|
||||
List<T> list,
|
||||
string name,
|
||||
ReorderableList.ElementCallbackDelegate drawElementCallback,
|
||||
bool showFooter = false)
|
||||
{
|
||||
var reorderableList = new ReorderableList(list, typeof(T))
|
||||
{
|
||||
drawHeaderCallback = (area) => GUI.Label(area, name),
|
||||
drawElementCallback = drawElementCallback,
|
||||
elementHeight = AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing,
|
||||
};
|
||||
|
||||
if (!showFooter)
|
||||
{
|
||||
reorderableList.footerHeight = 0;
|
||||
reorderableList.displayAdd = false;
|
||||
reorderableList.displayRemove = false;
|
||||
}
|
||||
|
||||
return reorderableList;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates and initializes a new <see cref="ReorderableList"/> for <see cref="Sprite"/>s.</summary>
|
||||
public static ReorderableList CreateReorderableObjectList<T>(
|
||||
List<T> objects,
|
||||
string name,
|
||||
bool showFooter = false)
|
||||
where T : Object
|
||||
{
|
||||
var reorderableList = CreateReorderableList(objects, name, (area, index, isActive, isFocused) =>
|
||||
{
|
||||
area.y = Mathf.Ceil(area.y + AnimancerGUI.StandardSpacing * 0.5f);
|
||||
area.height = AnimancerGUI.LineHeight;
|
||||
|
||||
BeginChangeCheck();
|
||||
var obj = AnimancerGUI.DoObjectFieldGUI(area, "", objects[index], false);
|
||||
if (EndChangeCheck())
|
||||
{
|
||||
objects[index] = obj;
|
||||
}
|
||||
}, showFooter);
|
||||
|
||||
if (showFooter)
|
||||
{
|
||||
reorderableList.onAddCallback = (list) => list.list.Add(null);
|
||||
}
|
||||
|
||||
return reorderableList;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="ReorderableList"/> for <see cref="string"/>s.</summary>
|
||||
public static ReorderableList CreateReorderableStringList(
|
||||
List<string> strings,
|
||||
string name,
|
||||
Func<Rect, int, string> doElementGUI)
|
||||
{
|
||||
return CreateReorderableList(strings, name, (area, index, isActive, isFocused) =>
|
||||
{
|
||||
area.y = Mathf.Ceil(area.y + AnimancerGUI.StandardSpacing * 0.5f);
|
||||
area.height = AnimancerGUI.LineHeight;
|
||||
|
||||
BeginChangeCheck();
|
||||
var str = doElementGUI(area, index);
|
||||
if (EndChangeCheck())
|
||||
{
|
||||
strings[index] = str;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Creates a new <see cref="ReorderableList"/> for <see cref="string"/>s.</summary>
|
||||
public static ReorderableList CreateReorderableStringList(
|
||||
List<string> strings,
|
||||
string name)
|
||||
{
|
||||
return CreateReorderableStringList(strings, name, (area, index) =>
|
||||
{
|
||||
return EditorGUI.TextField(area, strings[index]);
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Opens the <see cref="AnimancerToolsWindow"/>.</summary>
|
||||
[MenuItem(Strings.AnimancerToolsMenuPath)]
|
||||
public static void Open()
|
||||
=> GetWindow<AnimancerToolsWindow>();
|
||||
|
||||
/// <summary>Opens the <see cref="AnimancerToolsWindow"/> showing the specified `tool`.</summary>
|
||||
public static void Open(Type toolType)
|
||||
{
|
||||
var window = GetWindow<AnimancerToolsWindow>();
|
||||
window._CurrentTool = AnimancerEditorUtilities.IndexOfType(window._Tools, toolType);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92f977efd2d1e4c49ada1f068a739289
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/AnimancerToolsWindow.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,101 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// A base <see cref="AnimancerToolsWindow.Tool"/> for modifying <see cref="AnimationClip"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
|
||||
/// Animancer Tools</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/AnimationModifierTool
|
||||
///
|
||||
[Serializable]
|
||||
public abstract class AnimationModifierTool : AnimancerToolsWindow.Tool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField]
|
||||
private AnimationClip _Animation;
|
||||
|
||||
/// <summary>The currently selected <see cref="AnimationClip"/> asset.</summary>
|
||||
public AnimationClip Animation => _Animation;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
OnAnimationChanged();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnSelectionChanged()
|
||||
{
|
||||
if (Selection.activeObject is AnimationClip animation)
|
||||
{
|
||||
_Animation = animation;
|
||||
OnAnimationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Called whenever the selected <see cref="Animation"/> changes.</summary>
|
||||
protected virtual void OnAnimationChanged() { }
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
|
||||
var animation = AnimancerGUI.DoObjectFieldGUI("Animation", _Animation, false);
|
||||
|
||||
if (AnimancerToolsWindow.EndChangeCheck(ref _Animation, animation))
|
||||
OnAnimationChanged();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calls <see cref="AnimancerToolsWindow.Tool.SaveModifiedAsset"/> on the animation.</summary>
|
||||
protected bool SaveAs()
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
|
||||
if (SaveModifiedAsset(
|
||||
"Save Modified Animation",
|
||||
"Where would you like to save the new animation?",
|
||||
_Animation,
|
||||
Modify))
|
||||
{
|
||||
_Animation = null;
|
||||
OnAnimationChanged();
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Override this to apply the desired modifications to the `animation` before it is saved.</summary>
|
||||
protected virtual void Modify(AnimationClip animation) { }
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ab2916f8d63d1d4e9471536b78698a3
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/AnimationModifierTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,611 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using static Animancer.Editor.AnimancerGUI;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// A <see cref="SpriteModifierTool"/> for generating <see cref="AnimationClip"/>s from <see cref="Sprite"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/generate-sprite-animations">
|
||||
/// Generate Sprite Animations</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/GenerateSpriteAnimationsTool
|
||||
///
|
||||
[Serializable]
|
||||
public class GenerateSpriteAnimationsTool : SpriteModifierTool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
#region Tool
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[NonSerialized] private List<string> _Names;
|
||||
[NonSerialized] private Dictionary<string, List<Sprite>> _NameToSprites;
|
||||
[NonSerialized] private ReorderableList _Display;
|
||||
[NonSerialized] private bool _NamesAreDirty;
|
||||
[NonSerialized] private double _PreviewStartTime;
|
||||
[NonSerialized] private long _PreviewFrameIndex;
|
||||
[NonSerialized] private bool _RequiresRepaint;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 3;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Generate Sprite Animations";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.GenerateSpriteAnimations;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Sprites.Count == 0)
|
||||
return "Select the Sprites you want to generate animations from.";
|
||||
|
||||
return "Configure the animation settings then click Generate.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
|
||||
_Names = new();
|
||||
_NameToSprites = new();
|
||||
|
||||
_Display = AnimancerToolsWindow.CreateReorderableList(
|
||||
_Names,
|
||||
"Animations to Generate",
|
||||
DrawDisplayElement);
|
||||
_Display.elementHeightCallback = CalculateDisplayElementHeight;
|
||||
|
||||
_PreviewStartTime = EditorApplication.timeSinceStartup;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnSelectionChanged()
|
||||
{
|
||||
_NameToSprites.Clear();
|
||||
_Names.Clear();
|
||||
_NamesAreDirty = true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
var property = GenerateSpriteAnimationsSettings.SerializedProperty;
|
||||
property.serializedObject.Update();
|
||||
using (var label = PooledGUIContent.Acquire("Settings"))
|
||||
EditorGUILayout.PropertyField(property, label, true);
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
|
||||
GenerateSpriteAnimationsSettings.Instance.FillDefaults();
|
||||
|
||||
var sprites = Sprites;
|
||||
|
||||
if (_NamesAreDirty)
|
||||
{
|
||||
_NamesAreDirty = false;
|
||||
GatherNameToSprites(sprites, _NameToSprites);
|
||||
_Names.AddRange(_NameToSprites.Keys);
|
||||
}
|
||||
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
var previewCurrentTime = EditorApplication.timeSinceStartup - _PreviewStartTime;
|
||||
_PreviewFrameIndex = (long)(previewCurrentTime * GenerateSpriteAnimationsSettings.FrameRate);
|
||||
|
||||
_Display.DoLayoutList();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.enabled = sprites.Count > 0;
|
||||
|
||||
if (GUILayout.Button("Generate"))
|
||||
{
|
||||
Deselect();
|
||||
GenerateAnimationsBySpriteName(sprites);
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox("This function is also available via:" +
|
||||
"\n• The 'Assets/Create/Animancer' menu." +
|
||||
"\n• The Context Menu in the top right of the Inspector for Sprite and Texture assets",
|
||||
MessageType.Info);
|
||||
|
||||
if (_RequiresRepaint)
|
||||
{
|
||||
_RequiresRepaint = false;
|
||||
AnimancerToolsWindow.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calculates the height of an animation to generate.</summary>
|
||||
private float CalculateDisplayElementHeight(int index)
|
||||
{
|
||||
if (_NameToSprites.Count <= 0 || _Names.Count <= 0)
|
||||
return 0;
|
||||
|
||||
var lineCount = _NameToSprites[_Names[index]].Count + 3;
|
||||
return (LineHeight + StandardSpacing) * lineCount;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the details of an animation to generate.</summary>
|
||||
private void DrawDisplayElement(Rect area, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
area.y = Mathf.Ceil(area.y + StandardSpacing * 0.5f);
|
||||
area.height = LineHeight;
|
||||
|
||||
DrawAnimationHeader(ref area, index, out var sprites);
|
||||
DrawAnimationBody(ref area, sprites);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the name and preview of an animation to generate.</summary>
|
||||
private void DrawAnimationHeader(ref Rect area, int index, out List<Sprite> sprites)
|
||||
{
|
||||
var width = area.width;
|
||||
|
||||
var previewSize = 3 * LineHeight + 2 * StandardSpacing;
|
||||
var previewArea = StealFromRight(ref area, previewSize, StandardSpacing);
|
||||
previewArea.height = previewSize;
|
||||
|
||||
// Name.
|
||||
|
||||
var name = _Names[index];
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
name = EditorGUI.TextField(area, name);
|
||||
if (AnimancerToolsWindow.EndChangeCheck())
|
||||
{
|
||||
_Names[index] = name;
|
||||
}
|
||||
|
||||
NextVerticalArea(ref area);
|
||||
|
||||
// Frame Count.
|
||||
|
||||
sprites = _NameToSprites[name];
|
||||
|
||||
var frame = (int)(_PreviewFrameIndex % sprites.Count);
|
||||
|
||||
var enabled = GUI.enabled;
|
||||
GUI.enabled = false;
|
||||
|
||||
EditorGUI.TextField(area, $"Frame {frame} / {sprites.Count}");
|
||||
|
||||
NextVerticalArea(ref area);
|
||||
|
||||
// Preview Time.
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
var beforeControlID = GUIUtility.GetControlID(FocusType.Passive);
|
||||
|
||||
var newFrame = EditorGUI.IntSlider(area, frame, 0, sprites.Count);
|
||||
|
||||
var afterControlID = GUIUtility.GetControlID(FocusType.Passive);
|
||||
var hotControl = GUIUtility.hotControl;
|
||||
|
||||
if (newFrame != frame ||
|
||||
(hotControl > beforeControlID && hotControl < afterControlID))
|
||||
{
|
||||
_PreviewStartTime = EditorApplication.timeSinceStartup;
|
||||
_PreviewStartTime -= newFrame / GenerateSpriteAnimationsSettings.FrameRate;
|
||||
_PreviewFrameIndex = newFrame;
|
||||
frame = newFrame % sprites.Count;
|
||||
}
|
||||
|
||||
GUI.enabled = enabled;
|
||||
|
||||
NextVerticalArea(ref area);
|
||||
|
||||
area.width = width;
|
||||
|
||||
// Preview.
|
||||
|
||||
DrawSprite(previewArea, sprites[frame]);
|
||||
_RequiresRepaint = true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the sprite contents of an animation to generate.</summary>
|
||||
private void DrawAnimationBody(ref Rect area, List<Sprite> sprites)
|
||||
{
|
||||
var previewFrame = (int)(_PreviewFrameIndex % sprites.Count);
|
||||
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
var sprite = sprites[i];
|
||||
|
||||
var fieldArea = area;
|
||||
var thumbnailArea = StealFromLeft(
|
||||
ref fieldArea,
|
||||
fieldArea.height,
|
||||
StandardSpacing);
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
sprite = DoObjectFieldGUI(fieldArea, "", sprite, false);
|
||||
if (AnimancerToolsWindow.EndChangeCheck())
|
||||
{
|
||||
sprites[i] = sprite;
|
||||
}
|
||||
|
||||
if (i == previewFrame)
|
||||
EditorGUI.DrawRect(fieldArea, new(0.25f, 1, 0.25f, 0.1f));
|
||||
|
||||
DrawSprite(thumbnailArea, sprite);
|
||||
|
||||
NextVerticalArea(ref area);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
#region Methods
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Uses <see cref="GatherNameToSprites"/> and creates new animations from those groups.</summary>
|
||||
private static void GenerateAnimationsBySpriteName(List<Sprite> sprites)
|
||||
{
|
||||
if (sprites.Count == 0)
|
||||
return;
|
||||
|
||||
sprites.Sort(NaturalCompare);
|
||||
|
||||
var nameToSprites = new Dictionary<string, List<Sprite>>();
|
||||
GatherNameToSprites(sprites, nameToSprites);
|
||||
|
||||
var pathToSprites = new Dictionary<string, List<Sprite>>();
|
||||
|
||||
var message = StringBuilderPool.Instance.Acquire()
|
||||
.Append("Do you wish to generate the following animations?");
|
||||
|
||||
const int MaxLines = 25;
|
||||
var line = 0;
|
||||
foreach (var nameToSpriteGroup in nameToSprites)
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(nameToSpriteGroup.Value[0]);
|
||||
path = Path.GetDirectoryName(path);
|
||||
path = Path.Combine(path, nameToSpriteGroup.Key + ".anim");
|
||||
pathToSprites.Add(path, nameToSpriteGroup.Value);
|
||||
|
||||
if (++line <= MaxLines)
|
||||
{
|
||||
message.AppendLine()
|
||||
.Append("- ")
|
||||
.Append(path)
|
||||
.Append(" (")
|
||||
.Append(nameToSpriteGroup.Value.Count)
|
||||
.Append(" frames)");
|
||||
}
|
||||
}
|
||||
|
||||
if (line > MaxLines)
|
||||
{
|
||||
message.AppendLine()
|
||||
.Append("And ")
|
||||
.Append(line - MaxLines)
|
||||
.Append(" others.");
|
||||
}
|
||||
|
||||
if (!EditorUtility.DisplayDialog("Generate Sprite Animations?", message.ReleaseToString(), "Generate", "Cancel"))
|
||||
return;
|
||||
|
||||
foreach (var pathToSpriteGroup in pathToSprites)
|
||||
CreateAnimation(pathToSpriteGroup.Key, pathToSpriteGroup.Value.ToArray());
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static char[] _Numbers, _TrimOther;
|
||||
|
||||
/// <summary>Groups the `sprites` by name into the `nameToSptires`.</summary>
|
||||
private static void GatherNameToSprites(List<Sprite> sprites, Dictionary<string, List<Sprite>> nameToSprites)
|
||||
{
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
var sprite = sprites[i];
|
||||
var name = sprite.name;
|
||||
|
||||
// Remove numbers from the end.
|
||||
_Numbers ??= new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
name = name.TrimEnd(_Numbers);
|
||||
|
||||
// Then remove other characters from the end.
|
||||
_TrimOther ??= new char[] { ' ', '_', '-' };
|
||||
name = name.TrimEnd(_TrimOther);
|
||||
|
||||
// Doing both at once would turn "Attack2-0" (Attack 2 Frame 0) into "Attack" (losing the number).
|
||||
|
||||
if (!nameToSprites.TryGetValue(name, out var spriteGroup))
|
||||
{
|
||||
spriteGroup = new();
|
||||
nameToSprites.Add(name, spriteGroup);
|
||||
}
|
||||
|
||||
// Add the sprite to the group if it's not a duplicate.
|
||||
if (spriteGroup.Count == 0 || spriteGroup[^1] != sprite)
|
||||
spriteGroup.Add(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates and saves a new <see cref="AnimationClip"/> that plays the `sprites`.</summary>
|
||||
private static void CreateAnimation(string path, params Sprite[] sprites)
|
||||
{
|
||||
var frameRate = GenerateSpriteAnimationsSettings.FrameRate;
|
||||
var hierarchyPath = GenerateSpriteAnimationsSettings.HierarchyPath;
|
||||
var type = GenerateSpriteAnimationsSettings.TargetType.Type ?? typeof(SpriteRenderer);
|
||||
|
||||
var property = GenerateSpriteAnimationsSettings.PropertyName;
|
||||
if (string.IsNullOrWhiteSpace(property))
|
||||
property = "m_Sprite";
|
||||
|
||||
var clip = new AnimationClip
|
||||
{
|
||||
frameRate = frameRate,
|
||||
};
|
||||
|
||||
var spriteKeyFrames = new ObjectReferenceKeyframe[sprites.Length];
|
||||
for (int i = 0; i < spriteKeyFrames.Length; i++)
|
||||
{
|
||||
spriteKeyFrames[i] = new()
|
||||
{
|
||||
time = i / (float)frameRate,
|
||||
value = sprites[i]
|
||||
};
|
||||
}
|
||||
|
||||
var spriteBinding = EditorCurveBinding.PPtrCurve(hierarchyPath, type, property);
|
||||
AnimationUtility.SetObjectReferenceCurve(clip, spriteBinding, spriteKeyFrames);
|
||||
|
||||
AssetDatabase.CreateAsset(clip, path);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
#region Menu Functions
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private const string GenerateAnimationsBySpriteNameFunctionName = "Generate Animations By Sprite Name";
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Should <see cref="GenerateAnimationsBySpriteName()"/> be enabled or greyed out?</summary>
|
||||
[MenuItem(Strings.CreateMenuPrefix + GenerateAnimationsBySpriteNameFunctionName, validate = true)]
|
||||
private static bool ValidateGenerateAnimationsBySpriteName()
|
||||
{
|
||||
var selection = Selection.objects;
|
||||
for (int i = 0; i < selection.Length; i++)
|
||||
{
|
||||
var selected = selection[i];
|
||||
if (selected is Sprite || selected is Texture)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Calls <see cref="GenerateAnimationsBySpriteName(List{Sprite})"/> with the selected <see cref="Sprite"/>s.</summary>
|
||||
[MenuItem(
|
||||
itemName: Strings.CreateMenuPrefix + GenerateAnimationsBySpriteNameFunctionName,
|
||||
priority = Strings.AssetMenuOrder + 8)]
|
||||
private static void GenerateAnimationsBySpriteName()
|
||||
{
|
||||
var sprites = new List<Sprite>();
|
||||
|
||||
var selection = Selection.objects;
|
||||
for (int i = 0; i < selection.Length; i++)
|
||||
{
|
||||
var selected = selection[i];
|
||||
if (selected is Sprite sprite)
|
||||
{
|
||||
sprites.Add(sprite);
|
||||
}
|
||||
else if (selected is Texture2D texture)
|
||||
{
|
||||
sprites.AddRange(LoadAllSpritesInTexture(texture));
|
||||
}
|
||||
}
|
||||
|
||||
GenerateAnimationsBySpriteName(sprites);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static List<Sprite> _CachedSprites;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of <see cref="Sprite"/>s which will be passed into
|
||||
/// <see cref="GenerateAnimationsBySpriteName(List{Sprite})"/> by <see cref="EditorApplication.delayCall"/>.
|
||||
/// </summary>
|
||||
private static List<Sprite> GetCachedSpritesToGenerateAnimations()
|
||||
{
|
||||
if (_CachedSprites == null)
|
||||
return _CachedSprites = new();
|
||||
|
||||
// Delay the call in case multiple objects are selected.
|
||||
if (_CachedSprites.Count == 0)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
GenerateAnimationsBySpriteName(_CachedSprites);
|
||||
_CachedSprites.Clear();
|
||||
};
|
||||
}
|
||||
|
||||
return _CachedSprites;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <see cref="MenuCommand.context"/> to the <see cref="GetCachedSpritesToGenerateAnimations"/>.
|
||||
/// </summary>
|
||||
[MenuItem("CONTEXT/" + nameof(Sprite) + GenerateAnimationsBySpriteNameFunctionName)]
|
||||
private static void GenerateAnimationsFromSpriteByName(MenuCommand command)
|
||||
{
|
||||
GetCachedSpritesToGenerateAnimations().Add((Sprite)command.context);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Should <see cref="GenerateAnimationsFromTextureBySpriteName"/> be enabled or greyed out?</summary>
|
||||
[MenuItem("CONTEXT/" + nameof(TextureImporter) + GenerateAnimationsBySpriteNameFunctionName, validate = true)]
|
||||
private static bool ValidateGenerateAnimationsFromTextureBySpriteName(MenuCommand command)
|
||||
{
|
||||
var importer = (TextureImporter)command.context;
|
||||
var sprites = LoadAllSpritesAtPath(importer.assetPath);
|
||||
return sprites.Length > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds all <see cref="Sprite"/> sub-assets of the <see cref="MenuCommand.context"/> to the
|
||||
/// <see cref="GetCachedSpritesToGenerateAnimations"/>.
|
||||
/// </summary>
|
||||
[MenuItem("CONTEXT/" + nameof(TextureImporter) + GenerateAnimationsBySpriteNameFunctionName)]
|
||||
private static void GenerateAnimationsFromTextureBySpriteName(MenuCommand command)
|
||||
{
|
||||
var cachedSprites = GetCachedSpritesToGenerateAnimations();
|
||||
var importer = (TextureImporter)command.context;
|
||||
cachedSprites.AddRange(LoadAllSpritesAtPath(importer.assetPath));
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#region Settings
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only] Settings for <see cref="GenerateSpriteAnimationsTool"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/GenerateSpriteAnimationsSettings
|
||||
[Serializable, InternalSerializableType]
|
||||
public class GenerateSpriteAnimationsSettings : AnimancerSettingsGroup
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Gets or creates an instance.</summary>
|
||||
public static GenerateSpriteAnimationsSettings Instance
|
||||
=> AnimancerSettingsGroup<GenerateSpriteAnimationsSettings>.Instance;
|
||||
|
||||
/// <summary>The <see cref="UnityEditor.SerializedProperty"/> representing the <see cref="Instance"/>.</summary>
|
||||
public static SerializedProperty SerializedProperty
|
||||
=> Instance.GetSerializedProperty(null);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DisplayName
|
||||
=> "Generate Sprite Animations Tool";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Index
|
||||
=> 6;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The frame rate to use for new animations")]
|
||||
private float _FrameRate = 12;
|
||||
|
||||
/// <summary>The frame rate to use for new animations.</summary>
|
||||
public static ref float FrameRate
|
||||
=> ref Instance._FrameRate;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The Transform Hierarchy path from the Animator to the object being animated" +
|
||||
" using forward slashes '/' between each object name")]
|
||||
private string _HierarchyPath;
|
||||
|
||||
/// <summary>The Transform Hierarchy path from the <see cref="Animator"/> to the object being animated.</summary>
|
||||
public static ref string HierarchyPath
|
||||
=> ref Instance._HierarchyPath;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The type of component being animated. Defaults to " + nameof(SpriteRenderer) + " if not set." +
|
||||
" Use the type picker on the right or drag and drop a component onto it to set this field.")]
|
||||
private SerializableTypeReference _TargetType = new(typeof(SpriteRenderer));
|
||||
|
||||
/// <summary>The type of component being animated. Defaults to <see cref="SpriteRenderer"/> if not set.</summary>
|
||||
public static ref SerializableTypeReference TargetType
|
||||
=> ref Instance._TargetType;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The default value for <see cref="PropertyName"/>.</summary>
|
||||
public const string DefaultPropertyName = "m_Sprite";
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The path of the property being animated. Defaults to " + DefaultPropertyName + " if not set.")]
|
||||
private string _PropertyName = DefaultPropertyName;
|
||||
|
||||
/// <summary>The path of the property being animated.</summary>
|
||||
public static ref string PropertyName
|
||||
=> ref Instance._PropertyName;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Reverts any empty values to their defaults.</summary>
|
||||
public void FillDefaults()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_TargetType.QualifiedName))
|
||||
_TargetType = new(typeof(SpriteRenderer));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_PropertyName))
|
||||
_PropertyName = DefaultPropertyName;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 435f0cce717bfc544bbb5dc56db55763
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/GenerateSpriteAnimationsTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,251 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// A <see cref="SpriteModifierTool"/> for modifying <see cref="Sprite"/> detauls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/modify-sprites">
|
||||
/// Modify Sprites</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/ModifySpritesTool
|
||||
///
|
||||
[Serializable]
|
||||
public class ModifySpritesTool : SpriteModifierTool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField] private OffsetRectMode _RectMode;
|
||||
[SerializeField] private Rect _RectOffset;
|
||||
|
||||
[SerializeField] private bool _SetPivot;
|
||||
[SerializeField] private Vector2 _Pivot;
|
||||
|
||||
[SerializeField] private bool _SetAlignment;
|
||||
[SerializeField] private SpriteAlignment _Alignment;
|
||||
|
||||
[SerializeField] private bool _SetBorder;
|
||||
[SerializeField] private RectOffset _Border;
|
||||
|
||||
[SerializeField] private bool _ShowDetails;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private enum OffsetRectMode { None, Add, Subtract }
|
||||
private static readonly string[] OffsetRectModes = { "None", "Add", "Subtract" };
|
||||
|
||||
private SerializedProperty _SerializedProperty;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 1;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Modify Sprites";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.ModifySprites;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Sprites.Count == 0)
|
||||
return "Select the Sprites you want to modify.";
|
||||
|
||||
if (!IsValidModification())
|
||||
return "The current Rect Offset would move some Sprites outside the texture bounds.";
|
||||
|
||||
return "Enter the desired modifications and click Apply.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
|
||||
_SerializedProperty = AnimancerToolsWindow.Instance.FindSerializedPropertyForTool(this);
|
||||
_SerializedProperty = _SerializedProperty.FindPropertyRelative(nameof(_RectMode));
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
base.DoBodyGUI();
|
||||
|
||||
var area = AnimancerGUI.LayoutSingleLineRect();
|
||||
area.xMin += 4;
|
||||
|
||||
using (var label = PooledGUIContent.Acquire("Offset Rects", null))
|
||||
area = EditorGUI.PrefixLabel(area, label);
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
var selected = (OffsetRectMode)GUI.Toolbar(area, (int)_RectMode, OffsetRectModes);
|
||||
AnimancerToolsWindow.EndChangeCheck(ref _RectMode, selected);
|
||||
|
||||
using (var property = _SerializedProperty.Copy())
|
||||
{
|
||||
property.serializedObject.Update();
|
||||
|
||||
var depth = property.depth;
|
||||
while (property.Next(false) && property.depth >= depth)
|
||||
{
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
}
|
||||
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
GUI.enabled = false;
|
||||
for (int i = 0; i < Sprites.Count; i++)
|
||||
{
|
||||
if (_ShowDetails)
|
||||
GUILayout.BeginVertical(GUI.skin.box);
|
||||
|
||||
var sprite = Sprites[i] = AnimancerGUI.DoObjectFieldGUI("", Sprites[i], false);
|
||||
|
||||
if (_ShowDetails)
|
||||
{
|
||||
if (_RectMode != OffsetRectMode.None)
|
||||
EditorGUILayout.RectField("Rect", sprite.rect);
|
||||
|
||||
if (_SetPivot)
|
||||
EditorGUILayout.Vector2Field("Pivot", sprite.pivot);
|
||||
|
||||
if (_SetBorder)
|
||||
EditorGUILayout.Vector4Field("Border", sprite.border);
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.enabled = Sprites.Count > 0 && IsValidModification();
|
||||
|
||||
if (GUILayout.Button("Apply"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
AskAndApply();
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private bool IsValidModification()
|
||||
{
|
||||
switch (_RectMode)
|
||||
{
|
||||
default:
|
||||
case OffsetRectMode.None:
|
||||
return true;
|
||||
|
||||
case OffsetRectMode.Add:
|
||||
case OffsetRectMode.Subtract:
|
||||
break;
|
||||
}
|
||||
|
||||
var offset = GetOffset();
|
||||
|
||||
var sprites = Sprites;
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
var sprite = sprites[i];
|
||||
var rect = Add(sprite.rect, offset);
|
||||
if (rect.xMin < 0 ||
|
||||
rect.yMin < 0 ||
|
||||
rect.xMax >= sprite.texture.width ||
|
||||
rect.xMax >= sprite.texture.height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private Rect GetOffset()
|
||||
{
|
||||
return _RectMode switch
|
||||
{
|
||||
OffsetRectMode.Add
|
||||
=> _RectOffset,
|
||||
OffsetRectMode.Subtract
|
||||
=> new(-_RectOffset.x, -_RectOffset.y, -_RectOffset.width, -_RectOffset.height),
|
||||
_
|
||||
=> throw new InvalidOperationException($"Can't {nameof(GetOffset)} when the mode is {_RectMode}."),
|
||||
};
|
||||
}
|
||||
|
||||
private static Rect Add(Rect a, Rect b)
|
||||
{
|
||||
a.x += b.x;
|
||||
a.y += b.y;
|
||||
a.width += b.width;
|
||||
a.height += b.height;
|
||||
return a;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override string AreYouSure => "Are you sure you want to modify the borders of these Sprites?";
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(SpriteDataEditor data, int index, Sprite sprite)
|
||||
{
|
||||
switch (_RectMode)
|
||||
{
|
||||
default:
|
||||
case OffsetRectMode.None:
|
||||
break;
|
||||
|
||||
case OffsetRectMode.Add:
|
||||
case OffsetRectMode.Subtract:
|
||||
var rect = data.GetRect(index);
|
||||
rect = Add(rect, GetOffset());
|
||||
data.SetRect(index, rect);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_SetPivot)
|
||||
data.SetPivot(index, _Pivot);
|
||||
|
||||
if (_SetAlignment)
|
||||
data.SetAlignment(index, _Alignment);
|
||||
|
||||
if (_SetBorder)
|
||||
data.SetBorder(index, new(_Border.left, _Border.bottom, _Border.right, _Border.top));
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 296afd430bc64e5419309c1f569fe899
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/ModifySpritesTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,534 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// A <see cref="AnimancerToolsWindow.Tool"/> for packing multiple <see cref="Texture2D"/>s into a single image.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/pack-textures">
|
||||
/// Pack Textures</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/PackTexturesTool
|
||||
///
|
||||
[Serializable]
|
||||
public class PackTexturesTool : AnimancerToolsWindow.Tool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField] private List<Object> _AssetsToPack;
|
||||
[SerializeField] private int _Padding;
|
||||
[SerializeField] private int _MaximumSize = 8192;
|
||||
|
||||
[NonSerialized] private ReorderableList _TexturesDisplay;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Pack Textures";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.PackTextures;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_AssetsToPack.Count == 0)
|
||||
return "Add the texture, sprites, and folders you want to pack to the list.";
|
||||
|
||||
return "Set the other details then click Pack and it will ask where you want to save the combined texture.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
_AssetsToPack ??= new();
|
||||
_TexturesDisplay = AnimancerToolsWindow.CreateReorderableObjectList(_AssetsToPack, "Textures", true);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
_TexturesDisplay.DoLayoutList();
|
||||
GUILayout.EndVertical();
|
||||
HandleDragAndDropIntoList(GUILayoutUtility.GetLastRect(), _AssetsToPack, overwrite: false);
|
||||
RemoveDuplicates(_AssetsToPack);
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
var padding = EditorGUILayout.IntField("Padding", _Padding);
|
||||
AnimancerToolsWindow.EndChangeCheck(ref _Padding, padding);
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
var maximumSize = EditorGUILayout.IntField("Maximum Size", _MaximumSize);
|
||||
maximumSize = Math.Max(maximumSize, 16);
|
||||
AnimancerToolsWindow.EndChangeCheck(ref _MaximumSize, maximumSize);
|
||||
|
||||
#if !UNITY_IMAGE_CONVERSION
|
||||
EditorGUILayout.HelpBox(
|
||||
"This feature requires Unity's Built-in Image Conversion module." +
|
||||
"\n1. Click here to open the Package Manager." +
|
||||
"\n2. Open the Packages menu and select 'Built-in'." +
|
||||
"\n3. Select the 'Image Conversion' module and Enable it.",
|
||||
MessageType.Error);
|
||||
if (AnimancerGUI.TryUseClickEventInLastRect())
|
||||
EditorApplication.ExecuteMenuItem("Window/Package Manager");
|
||||
#endif
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.enabled = _AssetsToPack.Count > 0;
|
||||
|
||||
if (GUILayout.Button("Clear"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
AnimancerToolsWindow.RecordUndo();
|
||||
_AssetsToPack.Clear();
|
||||
}
|
||||
|
||||
#if !UNITY_IMAGE_CONVERSION
|
||||
var enabled = GUI.enabled;
|
||||
GUI.enabled = false;
|
||||
#endif
|
||||
|
||||
if (GUILayout.Button("Pack"))
|
||||
{
|
||||
#if UNITY_IMAGE_CONVERSION
|
||||
AnimancerGUI.Deselect();
|
||||
Pack();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UNITY_IMAGE_CONVERSION
|
||||
GUI.enabled = enabled;
|
||||
#endif
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Removes any items from the `list` that are the same as earlier items.</summary>
|
||||
private static void RemoveDuplicates<T>(IList<T> list)
|
||||
{
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var item = list[i];
|
||||
if (item == null)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
if (item.Equals(list[j]))
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#if UNITY_IMAGE_CONVERSION
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Combines the <see cref="_AssetsToPack"/> into a new texture and saves it.</summary>
|
||||
private void Pack()
|
||||
{
|
||||
var textures = GatherTextures();
|
||||
if (textures.Count == 0 ||
|
||||
!MakeTexturesReadable(textures))
|
||||
return;
|
||||
|
||||
var path = GetCommonDirectory(_AssetsToPack);
|
||||
|
||||
path = EditorUtility.SaveFilePanelInProject("Save Packed Texture", "PackedTexture", "png",
|
||||
"Where would you like to save the packed texture?", path);
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
const string ProgressTitle = "Packing Textures";
|
||||
EditorUtility.DisplayProgressBar(ProgressTitle, "Gathering", 0);
|
||||
|
||||
var tightSprites = GatherTightSprites();
|
||||
|
||||
EditorUtility.DisplayProgressBar(ProgressTitle, "Packing", 0.1f);
|
||||
|
||||
var packedTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
|
||||
|
||||
var tightTextures = new Texture2D[tightSprites.Count];
|
||||
var index = 0;
|
||||
foreach (var sprite in tightSprites)
|
||||
tightTextures[index++] = sprite.texture;
|
||||
|
||||
var packedUVs = packedTexture.PackTextures(tightTextures, _Padding, _MaximumSize);
|
||||
|
||||
EditorUtility.DisplayProgressBar(ProgressTitle, "Encoding", 0.4f);
|
||||
var bytes = packedTexture.EncodeToPNG();
|
||||
if (bytes == null)
|
||||
return;
|
||||
|
||||
EditorUtility.DisplayProgressBar(ProgressTitle, "Writing", 0.5f);
|
||||
File.WriteAllBytes(path, bytes);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
var importer = GetTextureImporter(path);
|
||||
importer.maxTextureSize = Math.Max(packedTexture.width, packedTexture.height);
|
||||
importer.textureType = TextureImporterType.Sprite;
|
||||
importer.spriteImportMode = SpriteImportMode.Multiple;
|
||||
|
||||
var data = new SpriteDataEditor(importer)
|
||||
{
|
||||
SpriteCount = 0
|
||||
};
|
||||
|
||||
CopyCompressionSettings(importer, textures);
|
||||
EditorUtility.SetDirty(importer);
|
||||
importer.SaveAndReimport();
|
||||
|
||||
// Use the UV coordinates to set up sprites for the new texture.
|
||||
EditorUtility.DisplayProgressBar(ProgressTitle, "Generating Sprites", 0.7f);
|
||||
|
||||
data.SpriteCount = tightSprites.Count;
|
||||
index = 0;
|
||||
foreach (var sprite in tightSprites)
|
||||
{
|
||||
var rect = packedUVs[index];
|
||||
rect.x *= packedTexture.width;
|
||||
rect.y *= packedTexture.height;
|
||||
rect.width *= packedTexture.width;
|
||||
rect.height *= packedTexture.height;
|
||||
|
||||
var spriteRect = rect;
|
||||
spriteRect.x += spriteRect.width * sprite.rect.x / sprite.texture.width;
|
||||
spriteRect.y += spriteRect.height * sprite.rect.y / sprite.texture.height;
|
||||
spriteRect.width *= sprite.rect.width / sprite.texture.width;
|
||||
spriteRect.height *= sprite.rect.height / sprite.texture.height;
|
||||
|
||||
var pivot = sprite.pivot;
|
||||
pivot.x /= rect.width;
|
||||
pivot.y /= rect.height;
|
||||
|
||||
data.SetName(index, sprite.name);
|
||||
data.SetRect(index, spriteRect);
|
||||
data.SetPivot(index, pivot);
|
||||
data.SetBorder(index, sprite.border);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar(ProgressTitle, "Saving", 0.9f);
|
||||
|
||||
data.Apply();
|
||||
|
||||
EditorUtility.SetDirty(importer);
|
||||
importer.SaveAndReimport();
|
||||
|
||||
Selection.activeObject = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private HashSet<Texture2D> GatherTextures()
|
||||
{
|
||||
var textures = new HashSet<Texture2D>();
|
||||
|
||||
for (int i = 0; i < _AssetsToPack.Count; i++)
|
||||
{
|
||||
var obj = _AssetsToPack[i];
|
||||
var path = AssetDatabase.GetAssetPath(obj);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
continue;
|
||||
|
||||
if (obj is Texture2D texture)
|
||||
textures.Add(texture);
|
||||
else if (obj is Sprite sprite)
|
||||
textures.Add(sprite.texture);
|
||||
else if (obj is DefaultAsset)
|
||||
ForEachTextureInFolder(path, tex => textures.Add(tex));
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private HashSet<Sprite> GatherTightSprites()
|
||||
{
|
||||
var sprites = new HashSet<Sprite>();
|
||||
|
||||
for (int i = 0; i < _AssetsToPack.Count; i++)
|
||||
{
|
||||
var obj = _AssetsToPack[i];
|
||||
var path = AssetDatabase.GetAssetPath(obj);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
continue;
|
||||
|
||||
if (obj is Texture2D texture)
|
||||
GatherTightSprites(sprites, texture);
|
||||
else if (obj is Sprite sprite)
|
||||
sprites.Add(CreateTightSprite(sprite));
|
||||
else if (obj is DefaultAsset)
|
||||
ForEachTextureInFolder(path, tex => GatherTightSprites(sprites, tex));
|
||||
}
|
||||
|
||||
return sprites;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static void GatherTightSprites(ICollection<Sprite> sprites, Texture2D texture)
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(texture);
|
||||
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
|
||||
var foundSprite = false;
|
||||
for (int i = 0; i < assets.Length; i++)
|
||||
{
|
||||
if (assets[i] is Sprite sprite)
|
||||
{
|
||||
sprite = CreateTightSprite(sprite);
|
||||
sprites.Add(sprite);
|
||||
foundSprite = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSprite)
|
||||
{
|
||||
var sprite = Sprite.Create(texture,
|
||||
new(0, 0, texture.width, texture.height),
|
||||
new(0.5f, 0.5f));
|
||||
sprite.name = texture.name;
|
||||
sprites.Add(sprite);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static Sprite CreateTightSprite(Sprite sprite)
|
||||
{
|
||||
var rect = sprite.rect;
|
||||
var width = Mathf.CeilToInt(rect.width);
|
||||
var height = Mathf.CeilToInt(rect.height);
|
||||
if (width == sprite.texture.width &&
|
||||
height == sprite.texture.height)
|
||||
return sprite;
|
||||
|
||||
var pixels = sprite.texture.GetPixels(
|
||||
Mathf.FloorToInt(rect.x),
|
||||
Mathf.FloorToInt(rect.y),
|
||||
width,
|
||||
height);
|
||||
|
||||
var texture = new Texture2D(width, height, sprite.texture.format, false, true);
|
||||
#pragma warning disable UNT0017 // SetPixels invocation is slow.
|
||||
texture.SetPixels(pixels);
|
||||
#pragma warning restore UNT0017 // SetPixels invocation is slow.
|
||||
texture.Apply();
|
||||
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
|
||||
var pivot = sprite.pivot;
|
||||
pivot.x /= rect.width;
|
||||
pivot.y /= rect.height;
|
||||
|
||||
var newSprite = Sprite.Create(texture, rect, pivot, sprite.pixelsPerUnit);
|
||||
newSprite.name = sprite.name;
|
||||
return newSprite;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static bool MakeTexturesReadable(HashSet<Texture2D> textures)
|
||||
{
|
||||
var hasAsked = false;
|
||||
|
||||
foreach (var texture in textures)
|
||||
{
|
||||
var importer = GetTextureImporter(texture);
|
||||
if (importer == null)
|
||||
continue;
|
||||
|
||||
if (importer.isReadable &&
|
||||
importer.textureCompression == TextureImporterCompression.Uncompressed)
|
||||
continue;
|
||||
|
||||
if (!hasAsked)
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Make Textures Readable and Uncompressed?",
|
||||
"This tool requires the source textures to be marked as readable and uncompressed in their import settings.",
|
||||
"Make Textures Readable and Uncompressed", "Cancel"))
|
||||
return false;
|
||||
hasAsked = true;
|
||||
}
|
||||
|
||||
importer.isReadable = true;
|
||||
importer.textureCompression = TextureImporterCompression.Uncompressed;
|
||||
importer.SaveAndReimport();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static void ForEachTextureInFolder(string path, Action<Texture2D> action)
|
||||
{
|
||||
var guids = AssetDatabase.FindAssets($"t:{nameof(Texture2D)}", new string[] { path });
|
||||
for (int i = 0; i < guids.Length; i++)
|
||||
{
|
||||
path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
||||
if (texture != null)
|
||||
action(texture);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static void CopyCompressionSettings(TextureImporter copyTo, IEnumerable<Texture2D> copyFrom)
|
||||
{
|
||||
var first = true;
|
||||
foreach (var texture in copyFrom)
|
||||
{
|
||||
var copyFromImporter = GetTextureImporter(texture);
|
||||
if (copyFromImporter == null)
|
||||
continue;
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
|
||||
copyTo.textureCompression = copyFromImporter.textureCompression;
|
||||
copyTo.crunchedCompression = copyFromImporter.crunchedCompression;
|
||||
copyTo.compressionQuality = copyFromImporter.compressionQuality;
|
||||
copyTo.filterMode = copyFromImporter.filterMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsHigherQuality(copyFromImporter.textureCompression, copyTo.textureCompression))
|
||||
copyTo.textureCompression = copyFromImporter.textureCompression;
|
||||
|
||||
if (copyFromImporter.crunchedCompression)
|
||||
copyTo.crunchedCompression = true;
|
||||
|
||||
if (copyTo.compressionQuality < copyFromImporter.compressionQuality)
|
||||
copyTo.compressionQuality = copyFromImporter.compressionQuality;
|
||||
|
||||
if (copyTo.filterMode > copyFromImporter.filterMode)
|
||||
copyTo.filterMode = copyFromImporter.filterMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static bool IsHigherQuality(TextureImporterCompression higher, TextureImporterCompression lower)
|
||||
{
|
||||
return higher switch
|
||||
{
|
||||
TextureImporterCompression.Uncompressed
|
||||
=> lower != TextureImporterCompression.Uncompressed,
|
||||
TextureImporterCompression.Compressed
|
||||
=> lower == TextureImporterCompression.CompressedLQ,
|
||||
TextureImporterCompression.CompressedHQ
|
||||
=> lower == TextureImporterCompression.Compressed
|
||||
|| lower == TextureImporterCompression.CompressedLQ,
|
||||
TextureImporterCompression.CompressedLQ
|
||||
=> false,
|
||||
_
|
||||
=> throw AnimancerUtilities.CreateUnsupportedArgumentException(higher),
|
||||
};
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static string GetCommonDirectory<T>(IList<T> objects) where T : Object
|
||||
{
|
||||
if (objects == null)
|
||||
return null;
|
||||
|
||||
var count = objects.Count;
|
||||
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
{
|
||||
if (objects[i] == null)
|
||||
{
|
||||
objects.RemoveAt(i);
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return null;
|
||||
|
||||
var path = AssetDatabase.GetAssetPath(objects[0]);
|
||||
path = Path.GetDirectoryName(path);
|
||||
|
||||
for (int i = 1; i < count; i++)
|
||||
{
|
||||
var otherPath = AssetDatabase.GetAssetPath(objects[i]);
|
||||
otherPath = Path.GetDirectoryName(otherPath);
|
||||
|
||||
while (string.Compare(path, 0, otherPath, 0, path.Length) != 0)
|
||||
{
|
||||
path = Path.GetDirectoryName(path);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static TextureImporter GetTextureImporter(Object asset)
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(asset);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return null;
|
||||
|
||||
return GetTextureImporter(path);
|
||||
}
|
||||
|
||||
private static TextureImporter GetTextureImporter(string path)
|
||||
=> AssetImporter.GetAtPath(path) as TextureImporter;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70badd2bf7a12d741aea958ec21943a6
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/PackTexturesTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,358 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR && UNITY_IMGUI
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// A <see cref="AnimancerToolsWindow.Tool"/> for changing which bones an <see cref="AnimationClip"/>s controls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/remap-animation-bindings">
|
||||
/// Remap Animation Bindings</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RemapAnimationBindingsTool
|
||||
///
|
||||
[Serializable]
|
||||
public class RemapAnimationBindingsTool : AnimationModifierTool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField] private List<string> _NewBindingPaths;
|
||||
|
||||
[NonSerialized] private List<List<EditorCurveBinding>> _BindingGroups;
|
||||
[NonSerialized] private List<string> _OldBindingPaths;
|
||||
[NonSerialized] private bool _OldBindingPathsAreDirty;
|
||||
[NonSerialized] private ReorderableList _OldBindingPathsDisplay;
|
||||
[NonSerialized] private ReorderableList _NewBindingPathsDisplay;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 5;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Remap Animation Bindings";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.RemapAnimationBindings;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Animation == null)
|
||||
return "Select the animation you want to remap.";
|
||||
|
||||
if (_OldBindingPaths.Count == 0)
|
||||
{
|
||||
if (Animation.humanMotion)
|
||||
return "The selected animation only has Humanoid bindings which cannot be remapped.";
|
||||
|
||||
return "The selected animation does not have any bindings.";
|
||||
}
|
||||
|
||||
return "Enter the new paths to change the bindings into then click Save As.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
|
||||
_BindingGroups = new();
|
||||
_OldBindingPaths = new();
|
||||
|
||||
_NewBindingPaths ??= new();
|
||||
|
||||
if (Animation == null)
|
||||
_NewBindingPaths.Clear();
|
||||
|
||||
_OldBindingPathsDisplay = AnimancerToolsWindow.CreateReorderableStringList(_OldBindingPaths, "Old Binding Paths");
|
||||
_NewBindingPathsDisplay = AnimancerToolsWindow.CreateReorderableStringList(_NewBindingPaths, "New Binding Paths", (area, i) =>
|
||||
{
|
||||
var color = GUI.color;
|
||||
|
||||
var path = _NewBindingPaths[i];
|
||||
if (path != _OldBindingPaths[i])
|
||||
GUI.color = new(0.15f, 0.7f, 0.15f, 1);
|
||||
|
||||
path = EditorGUI.TextField(area, path);
|
||||
GUI.color = color;
|
||||
return path;
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnAnimationChanged()
|
||||
{
|
||||
base.OnAnimationChanged();
|
||||
_OldBindingPathsAreDirty = true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
base.DoBodyGUI();
|
||||
GatherBindings();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
GUI.enabled = false;
|
||||
_OldBindingPathsDisplay.DoLayoutList();
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
_NewBindingPathsDisplay.DoLayoutList();
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUI.enabled = Animation != null;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Reset"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
AnimancerToolsWindow.RecordUndo();
|
||||
_NewBindingPaths.Clear();
|
||||
_OldBindingPathsAreDirty = true;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Copy All"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
CopyAll();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Paste All"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
PasteAll();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Save As"))
|
||||
{
|
||||
if (SaveAs())
|
||||
{
|
||||
_OldBindingPathsAreDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Gathers the bindings from the <see cref="AnimationModifierTool.Animation"/>.</summary>
|
||||
private void GatherBindings()
|
||||
{
|
||||
if (!_OldBindingPathsAreDirty)
|
||||
return;
|
||||
|
||||
_OldBindingPathsAreDirty = false;
|
||||
|
||||
_BindingGroups.Clear();
|
||||
_OldBindingPaths.Clear();
|
||||
|
||||
if (Animation == null)
|
||||
{
|
||||
_NewBindingPaths.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var isHumanoid = Animation.humanMotion;
|
||||
|
||||
AnimationBindings.OnAnimationChanged(Animation);
|
||||
var bindings = AnimationBindings.GetBindings(Animation);
|
||||
Array.Sort(bindings, (a, b) =>
|
||||
{
|
||||
var result = EditorUtility.NaturalCompare(a.path, b.path);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
return EditorUtility.NaturalCompare(a.propertyName, b.propertyName);
|
||||
});
|
||||
|
||||
string previousPath = null;
|
||||
List<EditorCurveBinding> previousGroup = null;
|
||||
for (int i = 0; i < bindings.Length; i++)
|
||||
{
|
||||
var binding = bindings[i];
|
||||
if (isHumanoid &&
|
||||
string.IsNullOrEmpty(binding.path) &&
|
||||
IsHumanoidBinding(binding.propertyName))
|
||||
continue;
|
||||
|
||||
var path = binding.path;
|
||||
if (path == previousPath)
|
||||
{
|
||||
previousGroup.Add(binding);
|
||||
continue;
|
||||
}
|
||||
|
||||
previousPath = path;
|
||||
previousGroup = new() { binding };
|
||||
|
||||
_BindingGroups.Add(previousGroup);
|
||||
|
||||
_OldBindingPaths.Add(path);
|
||||
if (_NewBindingPaths.Count < _OldBindingPaths.Count)
|
||||
_NewBindingPaths.Add(path);
|
||||
}
|
||||
|
||||
if (_NewBindingPaths.Count > _OldBindingPaths.Count)
|
||||
_NewBindingPaths.RemoveRange(_OldBindingPaths.Count, _NewBindingPaths.Count - _OldBindingPaths.Count);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static HashSet<string> _HumanoidBindingNames;
|
||||
|
||||
/// <summary>Is the `propertyName` one of the bindings used by Humanoid animations?</summary>
|
||||
private static bool IsHumanoidBinding(string propertyName)
|
||||
{
|
||||
_HumanoidBindingNames ??= new()
|
||||
{
|
||||
"RootT.x", "RootT.y", "RootT.z",
|
||||
"RootQ.x", "RootQ.y", "RootQ.z", "RootQ.w",
|
||||
"LeftFootT.x", "LeftFootT.y", "LeftFootT.z",
|
||||
"LeftFootQ.x", "LeftFootQ.y", "LeftFootQ.z", "LeftFootQ.w",
|
||||
"RightFootT.x", "RightFootT.y", "RightFootT.z",
|
||||
"RightFootQ.x", "RightFootQ.y", "RightFootQ.z", "RightFootQ.w",
|
||||
"LeftHandT.x", "LeftHandT.y", "LeftHandT.z",
|
||||
"LeftHandQ.x", "LeftHandQ.y", "LeftHandQ.z", "LeftHandQ.w",
|
||||
"RightHandT.x", "RightHandT.y", "RightHandT.z",
|
||||
"RightHandQ.x", "RightHandQ.y", "RightHandQ.z", "RightHandQ.w",
|
||||
"Spine Front-Back", "Spine Left-Right", "Spine Twist Left-Right",
|
||||
"Chest Front-Back", "Chest Left-Right", "Chest Twist Left-Right",
|
||||
"UpperChest Front-Back", "UpperChest Left-Right", "UpperChest Twist Left-Right",
|
||||
"Neck Nod Down-Up", "Neck Tilt Left-Right", "Neck Turn Left-Right",
|
||||
"Head Nod Down-Up", "Head Tilt Left-Right", "Head Turn Left-Right",
|
||||
"Left Eye Down-Up", "Left Eye In-Out",
|
||||
"Right Eye Down-Up", "Right Eye In-Out",
|
||||
"Jaw Close", "Jaw Left-Right",
|
||||
"Left Upper Leg Front-Back", "Left Upper Leg In-Out", "Left Upper Leg Twist In-Out",
|
||||
"Left Lower Leg Stretch", "Left Lower Leg Twist In-Out",
|
||||
"Left Foot Up-Down", "Left Foot Twist In-Out",
|
||||
"Left Toes Up-Down",
|
||||
"Right Upper Leg Front-Back", "Right Upper Leg In-Out", "Right Upper Leg Twist In-Out",
|
||||
"Right Lower Leg Stretch", "Right Lower Leg Twist In-Out",
|
||||
"Right Foot Up-Down", "Right Foot Twist In-Out",
|
||||
"Right Toes Up-Down",
|
||||
"Left Shoulder Down-Up", "Left Shoulder Front-Back",
|
||||
"Left Arm Down-Up", "Left Arm Front-Back", "Left Arm Twist In-Out",
|
||||
"Left Forearm Stretch", "Left Forearm Twist In-Out",
|
||||
"Left Hand Down-Up", "Left Hand In-Out",
|
||||
"Right Shoulder Down-Up", "Right Shoulder Front-Back",
|
||||
"Right Arm Down-Up", "Right Arm Front-Back", "Right Arm Twist In-Out",
|
||||
"Right Forearm Stretch", "Right Forearm Twist In-Out",
|
||||
"Right Hand Down-Up", "Right Hand In-Out",
|
||||
"LeftHand.Thumb.Spread", "LeftHand.Thumb.1 Stretched", "LeftHand.Thumb.2 Stretched", "LeftHand.Thumb.3 Stretched",
|
||||
"LeftHand.Index.Spread", "LeftHand.Index.1 Stretched", "LeftHand.Index.2 Stretched", "LeftHand.Index.3 Stretched",
|
||||
"LeftHand.Middle.Spread", "LeftHand.Middle.1 Stretched", "LeftHand.Middle.2 Stretched", "LeftHand.Middle.3 Stretched",
|
||||
"LeftHand.Ring.Spread", "LeftHand.Ring.1 Stretched", "LeftHand.Ring.2 Stretched", "LeftHand.Ring.3 Stretched",
|
||||
"LeftHand.Little.Spread", "LeftHand.Little.1 Stretched", "LeftHand.Little.2 Stretched", "LeftHand.Little.3 Stretched",
|
||||
"RightHand.Thumb.Spread", "RightHand.Thumb.1 Stretched", "RightHand.Thumb.2 Stretched", "RightHand.Thumb.3 Stretched",
|
||||
"RightHand.Index.Spread", "RightHand.Index.1 Stretched", "RightHand.Index.2 Stretched", "RightHand.Index.3 Stretched",
|
||||
"RightHand.Middle.Spread", "RightHand.Middle.1 Stretched", "RightHand.Middle.2 Stretched", "RightHand.Middle.3 Stretched",
|
||||
"RightHand.Ring.Spread", "RightHand.Ring.1 Stretched", "RightHand.Ring.2 Stretched", "RightHand.Ring.3 Stretched",
|
||||
"RightHand.Little.Spread", "RightHand.Little.1 Stretched", "RightHand.Little.2 Stretched", "RightHand.Little.3 Stretched",
|
||||
};
|
||||
|
||||
return _HumanoidBindingNames.Contains(propertyName);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Copies all of the <see cref="_NewBindingPaths"/> to the system clipboard.</summary>
|
||||
private void CopyAll()
|
||||
{
|
||||
var text = StringBuilderPool.Instance.Acquire();
|
||||
|
||||
for (int i = 0; i < _NewBindingPaths.Count; i++)
|
||||
{
|
||||
text.AppendLine(_NewBindingPaths[i]);
|
||||
}
|
||||
|
||||
EditorGUIUtility.systemCopyBuffer = text.ReleaseToString();
|
||||
}
|
||||
|
||||
/// <summary>Pastes the string from the system clipboard into the <see cref="_NewBindingPaths"/>.</summary>
|
||||
private void PasteAll()
|
||||
{
|
||||
using var reader = new StringReader(EditorGUIUtility.systemCopyBuffer);
|
||||
|
||||
for (int i = 0; i < _NewBindingPaths.Count; i++)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line == null)
|
||||
return;
|
||||
|
||||
_NewBindingPaths[i] = line;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(AnimationClip animation)
|
||||
{
|
||||
for (int iGroup = 0; iGroup < _BindingGroups.Count; iGroup++)
|
||||
{
|
||||
var oldPath = _OldBindingPaths[iGroup];
|
||||
var newPath = _NewBindingPaths[iGroup];
|
||||
if (oldPath == newPath)
|
||||
continue;
|
||||
|
||||
var group = _BindingGroups[iGroup];
|
||||
for (int iBinding = 0; iBinding < group.Count; iBinding++)
|
||||
{
|
||||
var binding = group[iBinding];
|
||||
if (binding.isPPtrCurve)
|
||||
{
|
||||
var curve = AnimationUtility.GetObjectReferenceCurve(animation, binding);
|
||||
AnimationUtility.SetObjectReferenceCurve(animation, binding, null);
|
||||
|
||||
binding.path = newPath;
|
||||
AnimationUtility.SetObjectReferenceCurve(animation, binding, curve);
|
||||
}
|
||||
else
|
||||
{
|
||||
var curve = AnimationUtility.GetEditorCurve(animation, binding);
|
||||
AnimationUtility.SetEditorCurve(animation, binding, null);
|
||||
|
||||
binding.path = newPath;
|
||||
AnimationUtility.SetEditorCurve(animation, binding, curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d75e4cf8a82981c4f94e52c1de8b0a78
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/RemapAnimationBindingsTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,198 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// An <see cref="AnimationModifierTool"/> for changing which
|
||||
/// <see cref="Sprite"/>s an <see cref="AnimationClip"/> uses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/remap-sprite-animation">
|
||||
/// Remap Sprite Animation</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RemapSpriteAnimationTool
|
||||
///
|
||||
[Serializable]
|
||||
public class RemapSpriteAnimationTool : AnimationModifierTool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeField] private List<Sprite> _NewSprites;
|
||||
|
||||
[NonSerialized] private List<Sprite> _OldSprites;
|
||||
[NonSerialized] private bool _OldSpritesAreDirty;
|
||||
[NonSerialized] private ReorderableList _OldSpriteDisplay;
|
||||
[NonSerialized] private ReorderableList _NewSpriteDisplay;
|
||||
[NonSerialized] private EditorCurveBinding _SpriteBinding;
|
||||
[NonSerialized] private ObjectReferenceKeyframe[] _SpriteKeyframes;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 4;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Remap Sprite Animation";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.RemapSpriteAnimation;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Animation == null)
|
||||
return "Select the animation you want to remap.";
|
||||
|
||||
if (_OldSprites.Count == 0)
|
||||
return "The selected animation does not use Sprites.";
|
||||
|
||||
return "Assign the New Sprites that you want to replace the Old Sprites with then click Save As." +
|
||||
" You can Drag and Drop multiple Sprites onto the New Sprites list at the same time.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
|
||||
_NewSprites ??= new();
|
||||
|
||||
if (Animation == null)
|
||||
_NewSprites.Clear();
|
||||
|
||||
_OldSprites = new();
|
||||
|
||||
_OldSpriteDisplay = AnimancerToolsWindow.CreateReorderableObjectList(_OldSprites, "Old Sprites");
|
||||
_NewSpriteDisplay = AnimancerToolsWindow.CreateReorderableObjectList(_NewSprites, "New Sprites");
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnAnimationChanged()
|
||||
{
|
||||
base.OnAnimationChanged();
|
||||
_OldSpritesAreDirty = true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
base.DoBodyGUI();
|
||||
GatherOldSprites();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
GUI.enabled = false;
|
||||
_OldSpriteDisplay.DoLayoutList();
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
_NewSpriteDisplay.DoLayoutList();
|
||||
GUILayout.EndVertical();
|
||||
|
||||
HandleDragAndDropIntoList(GUILayoutUtility.GetLastRect(), _NewSprites, overwrite: true);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
GUI.enabled = Animation != null;
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("Reset"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
AnimancerToolsWindow.RecordUndo();
|
||||
_NewSprites.Clear();
|
||||
_OldSpritesAreDirty = true;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Save As"))
|
||||
{
|
||||
if (SaveAs())
|
||||
{
|
||||
_OldSpritesAreDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Gathers the <see cref="_OldSprites"/> from the <see cref="AnimationModifierTool.Animation"/>.</summary>
|
||||
private void GatherOldSprites()
|
||||
{
|
||||
if (!_OldSpritesAreDirty)
|
||||
return;
|
||||
|
||||
_OldSpritesAreDirty = false;
|
||||
|
||||
_OldSprites.Clear();
|
||||
_NewSprites.Clear();
|
||||
|
||||
if (Animation == null)
|
||||
return;
|
||||
|
||||
var bindings = AnimationUtility.GetObjectReferenceCurveBindings(Animation);
|
||||
for (int iBinding = 0; iBinding < bindings.Length; iBinding++)
|
||||
{
|
||||
var binding = bindings[iBinding];
|
||||
if (binding.type == typeof(SpriteRenderer) && binding.propertyName == "m_Sprite")
|
||||
{
|
||||
_SpriteBinding = binding;
|
||||
_SpriteKeyframes = AnimationUtility.GetObjectReferenceCurve(Animation, binding);
|
||||
|
||||
for (int iKeyframe = 0; iKeyframe < _SpriteKeyframes.Length; iKeyframe++)
|
||||
{
|
||||
var reference = _SpriteKeyframes[iKeyframe].value as Sprite;
|
||||
if (reference != null)
|
||||
_OldSprites.Add(reference);
|
||||
}
|
||||
|
||||
_NewSprites.AddRange(_OldSprites);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(AnimationClip animation)
|
||||
{
|
||||
for (int i = 0; i < _SpriteKeyframes.Length; i++)
|
||||
{
|
||||
_SpriteKeyframes[i].value = _NewSprites[i];
|
||||
}
|
||||
|
||||
AnimationUtility.SetObjectReferenceCurve(animation, _SpriteBinding, _SpriteKeyframes);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d3732333127095459feb68e4b95a191
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/RemapSpriteAnimationTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,359 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only] A <see cref="SpriteModifierTool"/> for bulk-renaming <see cref="Sprite"/>s.</summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools/rename-sprites">
|
||||
/// Rename Sprites</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/RenameSpritesTool
|
||||
///
|
||||
[Serializable]
|
||||
public class RenameSpritesTool : SpriteModifierTool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[NonSerialized] private List<string> _GeneratedNames;
|
||||
[NonSerialized] private bool _NamesAreDirty;
|
||||
[NonSerialized] private ReorderableList _SpritesDisplay;
|
||||
|
||||
[SerializeField] private List<string> _ManualNames;
|
||||
[SerializeField] private int _FirstIndex = 1;
|
||||
[SerializeField] private int _MinimumDigits = 1;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int DisplayOrder => 2;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "Rename Sprites";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string HelpURL => Strings.DocsURLs.RenameSprites;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Instructions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Sprites.Count == 0)
|
||||
return "Select the Sprites you want to rename.";
|
||||
|
||||
return "Enter the new name(s) you want to give the Sprites then click Apply." +
|
||||
"\n\nEach Sprite below the name you enter will be given the same name" +
|
||||
" until the next name which will restart the counter.";
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnEnable(int index)
|
||||
{
|
||||
base.OnEnable(index);
|
||||
|
||||
_ManualNames ??= new();
|
||||
_GeneratedNames ??= new();
|
||||
|
||||
_SpritesDisplay = AnimancerToolsWindow.CreateReorderableObjectList(Sprites, "Sprites to Rename");
|
||||
_SpritesDisplay.onChangedCallback += list => DirtyNames();
|
||||
_SpritesDisplay.drawElementCallback = DrawItem;
|
||||
_SpritesDisplay.elementHeight = AnimancerGUI.LineHeight * 3 + AnimancerGUI.StandardSpacing * 2;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnSelectionChanged()
|
||||
{
|
||||
base.OnSelectionChanged();
|
||||
DirtyNames();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Refreshes the <see cref="_GeneratedNames"/>.</summary>
|
||||
private void UpdateNames()
|
||||
{
|
||||
if (!_NamesAreDirty)
|
||||
return;
|
||||
|
||||
_NamesAreDirty = false;
|
||||
|
||||
var sprites = Sprites;
|
||||
|
||||
AnimancerEditorUtilities.SetCount(_ManualNames, sprites.Count);
|
||||
AnimancerEditorUtilities.SetCount(_GeneratedNames, sprites.Count);
|
||||
|
||||
string name = null;
|
||||
string digitFormat = null;
|
||||
int index = 0;
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
var newName = _ManualNames[i];
|
||||
if (!string.IsNullOrWhiteSpace(newName))
|
||||
{
|
||||
name = newName;
|
||||
index = 0;
|
||||
|
||||
var nextNameIndex = IndexOfNextManualName(i);
|
||||
|
||||
var digits = Mathf.FloorToInt(Mathf.Log10(nextNameIndex - i)) + 1;
|
||||
if (digits < _MinimumDigits)
|
||||
digits = _MinimumDigits;
|
||||
|
||||
var formatCharacters = new char[digits];
|
||||
for (int iDigit = 0; iDigit < digits; iDigit++)
|
||||
formatCharacters[iDigit] = '0';
|
||||
digitFormat = new string(formatCharacters);
|
||||
}
|
||||
|
||||
_GeneratedNames[i] = string.IsNullOrWhiteSpace(name)
|
||||
? sprites[i].name
|
||||
: name + (index + _FirstIndex).ToString(digitFormat);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private int IndexOfNextManualName(int startIndex)
|
||||
{
|
||||
for (int i = startIndex + 1; i < _ManualNames.Count; i++)
|
||||
if (!string.IsNullOrWhiteSpace(_ManualNames[i]))
|
||||
return i;
|
||||
|
||||
return _ManualNames.Count;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
base.DoBodyGUI();
|
||||
|
||||
#if ! UNITY_2D_SPRITE
|
||||
EditorGUILayout.HelpBox(
|
||||
"Without the 2D Sprite Package," +
|
||||
" any references to the renamed sprites will be lost (including animations).",
|
||||
MessageType.Warning);
|
||||
#endif
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
var firstIndex = EditorGUILayout.IntField("First Index", _FirstIndex);
|
||||
if (AnimancerToolsWindow.EndChangeCheck(ref _FirstIndex, Mathf.Max(firstIndex, 0)))
|
||||
DirtyNames();
|
||||
|
||||
AnimancerToolsWindow.BeginChangeCheck();
|
||||
var digits = EditorGUILayout.IntField("Minimum Digits", _MinimumDigits);
|
||||
if (AnimancerToolsWindow.EndChangeCheck(ref _MinimumDigits, Mathf.Max(digits, 1)))
|
||||
DirtyNames();
|
||||
|
||||
UpdateNames();
|
||||
|
||||
_SpritesDisplay.DoLayoutList();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.enabled = HasAnyNames();
|
||||
|
||||
if (GUILayout.Button("Clear"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
AnimancerToolsWindow.RecordUndo();
|
||||
_ManualNames.Clear();
|
||||
DirtyNames();
|
||||
}
|
||||
|
||||
GUI.enabled = HasAnyDifferentNames();
|
||||
|
||||
if (GUILayout.Button("Apply"))
|
||||
{
|
||||
AnimancerGUI.Deselect();
|
||||
AskAndApply();
|
||||
}
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void DrawItem(Rect area, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
var sprites = Sprites;
|
||||
var sprite = sprites[index];
|
||||
|
||||
var thumbnailWidth = Math.Min(area.height, area.width * 0.5f);
|
||||
var thumbnailArea = AnimancerGUI.StealFromLeft(ref area, thumbnailWidth, AnimancerGUI.StandardSpacing);
|
||||
|
||||
AnimancerGUI.DrawSprite(thumbnailArea, sprite);
|
||||
|
||||
area.y += (AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing) * 0.5f;
|
||||
area.height = AnimancerGUI.LineHeight;
|
||||
|
||||
sprites[index] = DrawSpriteField(area, sprite);
|
||||
|
||||
AnimancerGUI.NextVerticalArea(ref area);
|
||||
|
||||
DrawName(area, index);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private Sprite DrawSpriteField(Rect area, Sprite sprite)
|
||||
=> AnimancerGUI.DoObjectFieldGUI(area, "", sprite, false);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static GUIStyle _TextFieldStyle;
|
||||
|
||||
private void DrawName(Rect area, int index)
|
||||
{
|
||||
area.y += 1;
|
||||
area.height = AnimancerGUI.LineHeight;
|
||||
|
||||
var manualName = _ManualNames[index];
|
||||
var generatedName = _GeneratedNames[index];
|
||||
|
||||
if (Event.current.type == EventType.Repaint)
|
||||
{
|
||||
_TextFieldStyle ??= new(EditorStyles.textField);
|
||||
|
||||
_TextFieldStyle.fontStyle = string.IsNullOrWhiteSpace(manualName)
|
||||
? FontStyle.Italic
|
||||
: FontStyle.Bold;
|
||||
|
||||
GUI.TextField(area, generatedName, _TextFieldStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
_ManualNames[index] = GUI.TextField(area, manualName);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
DirtyNames();
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private bool HasAnyNames()
|
||||
{
|
||||
var sprites = Sprites;
|
||||
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
if (!string.IsNullOrWhiteSpace(_ManualNames[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HasAnyDifferentNames()
|
||||
{
|
||||
var sprites = Sprites;
|
||||
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
if (sprites[i].name != _GeneratedNames[i])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void DirtyNames()
|
||||
=> _NamesAreDirty = true;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override string AreYouSure =>
|
||||
"Are you sure you want to rename these Sprites?"
|
||||
#if UNITY_2D_SPRITE
|
||||
;
|
||||
#else
|
||||
+ "\n\nAny references to the renamed Sprites will be lost (including animations that use them)."
|
||||
+ " This can be avoided by importing Unity's 2D Sprite Package before using this tool.";
|
||||
#endif
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static Dictionary<Sprite, string> _SpriteToName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void BeforeApply()
|
||||
{
|
||||
if (_SpriteToName == null)
|
||||
_SpriteToName = new();
|
||||
else
|
||||
_SpriteToName.Clear();
|
||||
|
||||
var sprites = Sprites;
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
_SpriteToName.Add(sprites[i], _GeneratedNames[i]);
|
||||
}
|
||||
|
||||
// Renaming selected Sprites will lose the selection without triggering OnSelectionChanged.
|
||||
EditorApplication.delayCall += OnSelectionChanged;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(SpriteDataEditor data, int index, Sprite sprite)
|
||||
{
|
||||
data.SetName(index, _SpriteToName[sprite]);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Modify(TextureImporter importer, List<Sprite> sprites)
|
||||
{
|
||||
if (sprites.Count == 1 && importer.spriteImportMode != SpriteImportMode.Multiple)
|
||||
{
|
||||
var sprite = sprites[0];
|
||||
var fileName = Path.GetFileNameWithoutExtension(importer.assetPath);
|
||||
if (fileName == sprite.name)
|
||||
{
|
||||
AssetDatabase.RenameAsset(importer.assetPath, _SpriteToName[sprite]);
|
||||
sprites.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
base.Modify(importer, sprites);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void AfterApply()
|
||||
{
|
||||
base.AfterApply();
|
||||
|
||||
AnimancerToolsWindow.RecordUndo();
|
||||
_ManualNames.Clear();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc63a439782ec8841905e29c181dbd28
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/RenameSpritesTool.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,310 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_2D_SPRITE
|
||||
using UnityEditor.U2D.Sprites;
|
||||
#else
|
||||
#pragma warning disable CS0618 // Type or member is obsolete.
|
||||
#endif
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>A wrapper around the '2D Sprite' package features for editing Sprite data.</summary>
|
||||
public class SpriteDataEditor
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
#if UNITY_2D_SPRITE
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static SpriteDataProviderFactories _Factories;
|
||||
|
||||
private static SpriteDataProviderFactories Factories
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Factories == null)
|
||||
{
|
||||
_Factories = new();
|
||||
_Factories.Init();
|
||||
}
|
||||
|
||||
return _Factories;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The data provider for the target.</summary>
|
||||
public readonly ISpriteEditorDataProvider DataProvider;
|
||||
|
||||
private SpriteRect[] _SpriteRects;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The number of sprites in the target data.</summary>
|
||||
/// <remarks>Setting this value clears all sprites.</remarks>
|
||||
public int SpriteCount
|
||||
{
|
||||
get => _SpriteRects.Length;
|
||||
set
|
||||
{
|
||||
// Unity might have given a UnityEditor.U2D.Sprites.SpriteDataExt[].
|
||||
// We need to ensure it's a base SpriteRect[] so we can new() them.
|
||||
|
||||
if (_SpriteRects == null ||
|
||||
_SpriteRects.Length != value ||
|
||||
_SpriteRects.GetType() != typeof(SpriteRect[]))
|
||||
_SpriteRects = new SpriteRect[value];
|
||||
|
||||
for (int i = 0; i < _SpriteRects.Length; i++)
|
||||
_SpriteRects[i] = new();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns the name of the sprite at the specified `index`.</summary>
|
||||
public string GetName(int index) => _SpriteRects[index].name;
|
||||
|
||||
/// <summary>Sets the name of the sprite at the specified `index`.</summary>
|
||||
public void SetName(int index, string name) => _SpriteRects[index].name = name;
|
||||
|
||||
/// <summary>Returns the rect of the sprite at the specified `index`.</summary>
|
||||
public Rect GetRect(int index) => _SpriteRects[index].rect;
|
||||
|
||||
/// <summary>Sets the rect of the sprite at the specified `index`.</summary>
|
||||
public void SetRect(int index, Rect rect) => _SpriteRects[index].rect = rect;
|
||||
|
||||
/// <summary>Returns the pivot of the sprite at the specified `index`.</summary>
|
||||
public Vector2 GetPivot(int index) => _SpriteRects[index].pivot;
|
||||
|
||||
/// <summary>Sets the pivot of the sprite at the specified `index`.</summary>
|
||||
public void SetPivot(int index, Vector2 pivot)
|
||||
{
|
||||
_SpriteRects[index].pivot = pivot;
|
||||
_SpriteRects[index].alignment = GetSpriteAlignment(pivot);
|
||||
}
|
||||
|
||||
/// <summary>Returns the alignment of the sprite at the specified `index`.</summary>
|
||||
public SpriteAlignment GetAlignment(int index) => _SpriteRects[index].alignment;
|
||||
|
||||
/// <summary>Sets the alignment of the sprite at the specified `index`.</summary>
|
||||
public void SetAlignment(int index, SpriteAlignment alignment) => _SpriteRects[index].alignment = alignment;
|
||||
|
||||
/// <summary>Returns the border of the sprite at the specified `index`.</summary>
|
||||
public Vector4 GetBorder(int index) => _SpriteRects[index].border;
|
||||
|
||||
/// <summary>Sets the border of the sprite at the specified `index`.</summary>
|
||||
public void SetBorder(int index, Vector4 border) => _SpriteRects[index].border = border;
|
||||
|
||||
/// <summary>References the sprite at the specified `index`.</summary>
|
||||
public ref SpriteRect RefSprite(int index) => ref _SpriteRects[index];
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#else
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private SpriteMetaData[] _SpriteSheet;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The number of sprites in the target data.</summary>
|
||||
public int SpriteCount
|
||||
{
|
||||
get => _SpriteSheet.Length;
|
||||
set => System.Array.Resize(ref _SpriteSheet, value);
|
||||
}
|
||||
|
||||
/// <summary>Returns the name of the sprite at the specified `index`.</summary>
|
||||
public string GetName(int index) => _SpriteSheet[index].name;
|
||||
|
||||
/// <summary>Sets the name of the sprite at the specified `index`.</summary>
|
||||
public void SetName(int index, string name) => _SpriteSheet[index].name = name;
|
||||
|
||||
/// <summary>Returns the rect of the sprite at the specified `index`.</summary>
|
||||
public Rect GetRect(int index) => _SpriteSheet[index].rect;
|
||||
|
||||
/// <summary>Sets the rect of the sprite at the specified `index`.</summary>
|
||||
public void SetRect(int index, Rect rect) => _SpriteSheet[index].rect = rect;
|
||||
|
||||
/// <summary>Returns the pivot of the sprite at the specified `index`.</summary>
|
||||
public Vector2 GetPivot(int index) => _SpriteSheet[index].pivot;
|
||||
|
||||
/// <summary>Sets the pivot of the sprite at the specified `index`.</summary>
|
||||
public void SetPivot(int index, Vector2 pivot)
|
||||
{
|
||||
_SpriteSheet[index].pivot = pivot;
|
||||
_SpriteSheet[index].alignment = (int)GetSpriteAlignment(pivot);
|
||||
}
|
||||
|
||||
/// <summary>Returns the alignment of the sprite at the specified `index`.</summary>
|
||||
public SpriteAlignment GetAlignment(int index) => (SpriteAlignment)_SpriteSheet[index].alignment;
|
||||
|
||||
/// <summary>Sets the alignment of the sprite at the specified `index`.</summary>
|
||||
public void SetAlignment(int index, SpriteAlignment alignment) => _SpriteSheet[index].alignment = (int)alignment;
|
||||
|
||||
/// <summary>Returns the border of the sprite at the specified `index`.</summary>
|
||||
public Vector4 GetBorder(int index) => _SpriteSheet[index].border;
|
||||
|
||||
/// <summary>Sets the border of the sprite at the specified `index`.</summary>
|
||||
public void SetBorder(int index, Vector4 border) => _SpriteSheet[index].border = border;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endif
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Returns the appropriate alignment for the given `pivot`.</summary>
|
||||
public static SpriteAlignment GetSpriteAlignment(Vector2 pivot)
|
||||
{
|
||||
switch (pivot.x)
|
||||
{
|
||||
case 0:
|
||||
switch (pivot.y)
|
||||
{
|
||||
case 0: return SpriteAlignment.BottomLeft;
|
||||
case 0.5f: return SpriteAlignment.LeftCenter;
|
||||
case 1: return SpriteAlignment.TopLeft;
|
||||
}
|
||||
break;
|
||||
case 0.5f:
|
||||
switch (pivot.y)
|
||||
{
|
||||
case 0: return SpriteAlignment.BottomCenter;
|
||||
case 0.5f: return SpriteAlignment.Center;
|
||||
case 1: return SpriteAlignment.TopCenter;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (pivot.y)
|
||||
{
|
||||
case 0: return SpriteAlignment.BottomRight;
|
||||
case 0.5f: return SpriteAlignment.RightCenter;
|
||||
case 1: return SpriteAlignment.TopRight;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return SpriteAlignment.Custom;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private readonly TextureImporter Importer;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="SpriteDataEditor"/>.</summary>
|
||||
public SpriteDataEditor(TextureImporter importer)
|
||||
{
|
||||
Importer = importer;
|
||||
|
||||
#if UNITY_2D_SPRITE
|
||||
DataProvider = Factories.GetSpriteEditorDataProviderFromObject(importer);
|
||||
DataProvider.InitSpriteEditorDataProvider();
|
||||
|
||||
_SpriteRects = DataProvider.GetSpriteRects();
|
||||
#else
|
||||
_SpriteSheet = importer.spritesheet;
|
||||
#endif
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Tries to find the index of the data matching the `sprite`.</summary>
|
||||
/// <remarks>
|
||||
/// Returns -1 if there is no data matching the <see cref="UnityEngine.Object.name"/>.
|
||||
/// <para></para>
|
||||
/// Returns -2 if there is more than one data matching the <see cref="UnityEngine.Object.name"/> but no
|
||||
/// <see cref="Sprite.rect"/> match.
|
||||
/// </remarks>
|
||||
public int IndexOf(Sprite sprite)
|
||||
{
|
||||
var nameMatchIndex = -1;
|
||||
|
||||
var count = SpriteCount;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (GetName(i) == sprite.name)
|
||||
{
|
||||
if (GetRect(i) == sprite.rect)
|
||||
return i;
|
||||
|
||||
if (nameMatchIndex == -1)// First name match.
|
||||
nameMatchIndex = i;
|
||||
else
|
||||
nameMatchIndex = -2;// Already found 2 name matches.
|
||||
}
|
||||
}
|
||||
|
||||
if (nameMatchIndex == -1)
|
||||
{
|
||||
Debug.LogError($"No {nameof(SpriteMetaData)} for '{sprite.name}' was found.", sprite);
|
||||
}
|
||||
else if (nameMatchIndex == -2)
|
||||
{
|
||||
Debug.LogError($"More than one {nameof(SpriteMetaData)} for '{sprite.name}' was found" +
|
||||
$" but none of them matched the {nameof(Sprite)}.{nameof(Sprite.rect)}." +
|
||||
$" If the texture's Max Size is smaller than its actual size, increase the Max Size before performing this" +
|
||||
$" operation so that the {nameof(Rect)}s can be used to identify the correct data.", sprite);
|
||||
}
|
||||
|
||||
return nameMatchIndex;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Logs an error and returns false if the data at the specified `index` is out of the texture bounds.</summary>
|
||||
public bool ValidateBounds(int index, Sprite sprite)
|
||||
{
|
||||
var rect = GetRect(index);
|
||||
if (rect.xMin >= 0 &&
|
||||
rect.yMin >= 0 &&
|
||||
rect.xMax <= sprite.texture.width &&
|
||||
rect.yMax <= sprite.texture.height)
|
||||
return true;
|
||||
|
||||
var path = AssetDatabase.GetAssetPath(sprite);
|
||||
|
||||
// The Max Texture Size import setting may cause the loaded texture to be smaller than the actual image.
|
||||
// Sprite dimensions are defined against the actual image though, so we need to check those bounds.
|
||||
var importer = (TextureImporter)AssetImporter.GetAtPath(path);
|
||||
importer.GetSourceTextureWidthAndHeight(out var width, out var height);
|
||||
if (rect.xMin >= 0 &&
|
||||
rect.yMin >= 0 &&
|
||||
rect.xMax <= width &&
|
||||
rect.yMax <= height)
|
||||
return true;
|
||||
|
||||
Debug.LogError(
|
||||
$"This modification would put '{sprite.name}' at {rect}" +
|
||||
$" which is outside of the texture ({width}x{height})" +
|
||||
$" so '{path}' was not modified.",
|
||||
sprite);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Applies any modifications to the target asset.</summary>
|
||||
public void Apply()
|
||||
{
|
||||
#if UNITY_2D_SPRITE
|
||||
DataProvider.SetSpriteRects(_SpriteRects);
|
||||
DataProvider.Apply();
|
||||
#else
|
||||
Importer.spritesheet = _SpriteSheet;
|
||||
EditorUtility.SetDirty(Importer);
|
||||
#endif
|
||||
|
||||
Importer.SaveAndReimport();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8abf35ea1a5a4664b88e197e78f09632
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/SpriteDataEditor.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,223 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only]
|
||||
/// A base <see cref="AnimancerToolsWindow.Tool"/> for modifying <see cref="Sprite"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
|
||||
/// Animancer Tools</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/SpriteModifierTool
|
||||
///
|
||||
[Serializable]
|
||||
public abstract class SpriteModifierTool : AnimancerToolsWindow.Tool
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly List<Sprite> SelectedSprites = new();
|
||||
private static bool _HasGatheredSprites;
|
||||
|
||||
/// <summary>The currently selected <see cref="Sprite"/>s.</summary>
|
||||
public static List<Sprite> Sprites
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_HasGatheredSprites)
|
||||
{
|
||||
_HasGatheredSprites = true;
|
||||
GatherSelectedSprites(SelectedSprites);
|
||||
}
|
||||
|
||||
return SelectedSprites;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnSelectionChanged()
|
||||
{
|
||||
_HasGatheredSprites = false;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoBodyGUI()
|
||||
{
|
||||
#if !UNITY_2D_SPRITE
|
||||
EditorGUILayout.HelpBox(
|
||||
"This tool works best with Unity's '2D Sprite' package." +
|
||||
" You should import it via the Package Manager before using this tool.",
|
||||
MessageType.Warning);
|
||||
|
||||
if (AnimancerGUI.TryUseClickEventInLastRect())
|
||||
EditorApplication.ExecuteMenuItem("Window/Package Manager");
|
||||
#endif
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Adds all <see cref="Sprite"/>s in the <see cref="Selection.objects"/> or their sub-assets to the
|
||||
/// list of `sprites`.
|
||||
/// </summary>
|
||||
public static void GatherSelectedSprites(List<Sprite> sprites)
|
||||
{
|
||||
sprites.Clear();
|
||||
|
||||
var selection = Selection.objects;
|
||||
for (int i = 0; i < selection.Length; i++)
|
||||
{
|
||||
var selected = selection[i];
|
||||
if (selected is Sprite sprite)
|
||||
{
|
||||
sprites.Add(sprite);
|
||||
}
|
||||
else if (selected is Texture2D texture)
|
||||
{
|
||||
sprites.AddRange(LoadAllSpritesInTexture(texture));
|
||||
}
|
||||
}
|
||||
|
||||
sprites.Sort(NaturalCompare);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Returns all the <see cref="Sprite"/> sub-assets of the `texture`.</summary>
|
||||
public static Sprite[] LoadAllSpritesInTexture(Texture2D texture)
|
||||
=> LoadAllSpritesAtPath(AssetDatabase.GetAssetPath(texture));
|
||||
|
||||
/// <summary>Returns all the <see cref="Sprite"/> assets at the `path`.</summary>
|
||||
public static Sprite[] LoadAllSpritesAtPath(string path)
|
||||
{
|
||||
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
|
||||
var sprites = new List<Sprite>();
|
||||
for (int j = 0; j < assets.Length; j++)
|
||||
{
|
||||
if (assets[j] is Sprite sprite)
|
||||
sprites.Add(sprite);
|
||||
}
|
||||
return sprites.ToArray();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calls <see cref="EditorUtility.NaturalCompare"/> on the <see cref="Object.name"/>s.</summary>
|
||||
public static int NaturalCompare(Object a, Object b)
|
||||
=> EditorUtility.NaturalCompare(a.name, b.name);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The message to confirm that the user is certain they want to apply the changes.</summary>
|
||||
protected virtual string AreYouSure
|
||||
=> "Are you sure you want to modify these Sprites?";
|
||||
|
||||
/// <summary>Called immediately after the user confirms they want to apply changes.</summary>
|
||||
protected virtual void BeforeApply() { }
|
||||
|
||||
/// <summary>Called after all changes are applied.</summary>
|
||||
protected virtual void AfterApply() { }
|
||||
|
||||
/// <summary>Applies the desired modifications to the `data` before it is saved.</summary>
|
||||
protected virtual void Modify(SpriteDataEditor data, int index, Sprite sprite) { }
|
||||
|
||||
/// <summary>Applies the desired modifications to the `data` before it is saved.</summary>
|
||||
protected virtual void Modify(TextureImporter importer, List<Sprite> sprites)
|
||||
{
|
||||
var dataEditor = new SpriteDataEditor(importer);
|
||||
|
||||
var hasError = false;
|
||||
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
var sprite = sprites[i];
|
||||
var index = dataEditor.IndexOf(sprite);
|
||||
if (index < 0)
|
||||
continue;
|
||||
|
||||
Modify(dataEditor, index, sprite);
|
||||
sprites.RemoveAt(i--);
|
||||
|
||||
if (!dataEditor.ValidateBounds(index, sprite))
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!hasError)
|
||||
dataEditor.Apply();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Asks the user if they want to modify the target <see cref="Sprite"/>s and calls <see cref="Modify"/>
|
||||
/// on each of them before saving any changes.
|
||||
/// </summary>
|
||||
protected void AskAndApply()
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Are You Sure?",
|
||||
AreYouSure + "\n\nThis operation cannot be undone.",
|
||||
"Modify", "Cancel"))
|
||||
return;
|
||||
|
||||
BeforeApply();
|
||||
|
||||
var pathToSprites = new Dictionary<string, List<Sprite>>();
|
||||
var sprites = Sprites;
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
var sprite = sprites[i];
|
||||
|
||||
var path = AssetDatabase.GetAssetPath(sprite);
|
||||
|
||||
if (!pathToSprites.TryGetValue(path, out var spritesAtPath))
|
||||
pathToSprites.Add(path, spritesAtPath = new());
|
||||
|
||||
spritesAtPath.Add(sprite);
|
||||
}
|
||||
|
||||
foreach (var asset in pathToSprites)
|
||||
{
|
||||
var importer = (TextureImporter)AssetImporter.GetAtPath(asset.Key);
|
||||
|
||||
Modify(importer, asset.Value);
|
||||
|
||||
if (asset.Value.Count > 0)
|
||||
{
|
||||
var message = StringBuilderPool.Instance.Acquire()
|
||||
.Append("Modification failed: unable to find data in '")
|
||||
.Append(asset.Key)
|
||||
.Append("' for ")
|
||||
.Append(asset.Value.Count)
|
||||
.Append(" Sprites:");
|
||||
|
||||
for (int i = 0; i < sprites.Count; i++)
|
||||
{
|
||||
message.AppendLine()
|
||||
.Append(" - ")
|
||||
.Append(sprites[i].name);
|
||||
}
|
||||
|
||||
Debug.LogError(message.ReleaseToString(), AssetDatabase.LoadAssetAtPath<Object>(asset.Key));
|
||||
}
|
||||
}
|
||||
|
||||
AfterApply();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f495ecfca64b9c04aa19924036014776
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/SpriteModifierTool.cs
|
||||
uploadId: 795566
|
||||
252
Packages/com.kybernetik.animancer/Editor/Animancer Tools/Tool.cs
Normal file
252
Packages/com.kybernetik.animancer/Editor/Animancer Tools/Tool.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Animancer.Editor.Tools
|
||||
{
|
||||
partial class AnimancerToolsWindow
|
||||
{
|
||||
/// <summary>[Editor-Only] [Pro-Only] Base class for tools in the <see cref="AnimancerToolsWindow"/>.</summary>
|
||||
/// <remarks>
|
||||
/// <strong>Documentation:</strong>
|
||||
/// <see href="https://kybernetik.com.au/animancer/docs/manual/tools">
|
||||
/// Animancer Tools</see>
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Tools/Tool
|
||||
///
|
||||
[Serializable]
|
||||
public abstract class Tool : IComparable<Tool>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private AnimBool _FullAnimator;
|
||||
private AnimBool _BodyAnimator;
|
||||
|
||||
private int _Index;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Is this tool currently visible?</summary>
|
||||
public bool IsVisible => Instance._CurrentTool == _Index || Instance._CurrentTool < 0;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Is the body of this tool currently visible?</summary>
|
||||
public bool IsExpanded
|
||||
{
|
||||
get { return Instance._CurrentTool == _Index; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
Instance._CurrentTool = _Index;
|
||||
else if (IsExpanded)
|
||||
Instance._CurrentTool = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Lower numbers display first.</summary>
|
||||
public abstract int DisplayOrder { get; }
|
||||
|
||||
/// <summary>Compares the <see cref="DisplayOrder"/> to put lower numbers first.</summary>
|
||||
public int CompareTo(Tool other)
|
||||
=> DisplayOrder.CompareTo(other.DisplayOrder);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The display name of this tool.</summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>The usage instructions to display at the top of this tool.</summary>
|
||||
public abstract string Instructions { get; }
|
||||
|
||||
/// <summary>The URL for the help button in the header to open.</summary>
|
||||
public virtual string HelpURL => Strings.DocsURLs.AnimancerTools;
|
||||
|
||||
/// <summary>Called whenever the <see cref="Selection"/> changes.</summary>
|
||||
public virtual void OnSelectionChanged() { }
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Called by <see cref="AnimancerToolsWindow.OnEnable"/>.</summary>
|
||||
public virtual void OnEnable(int index)
|
||||
{
|
||||
_Index = index;
|
||||
|
||||
_FullAnimator = new(IsVisible);
|
||||
_BodyAnimator = new(IsExpanded);
|
||||
}
|
||||
|
||||
/// <summary>Called by <see cref="AnimancerToolsWindow.OnDisable"/>.</summary>
|
||||
public virtual void OnDisable() { }
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the GUI for this tool.</summary>
|
||||
public virtual void DoGUI()
|
||||
{
|
||||
var enabled = GUI.enabled;
|
||||
|
||||
_FullAnimator.target = IsVisible;
|
||||
|
||||
if (EditorGUILayout.BeginFadeGroup(_FullAnimator.faded))
|
||||
{
|
||||
GUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
DoHeaderGUI();
|
||||
|
||||
_BodyAnimator.target = IsExpanded;
|
||||
|
||||
if (EditorGUILayout.BeginFadeGroup(_BodyAnimator.faded))
|
||||
{
|
||||
var instructions = Instructions;
|
||||
if (!string.IsNullOrEmpty(instructions))
|
||||
EditorGUILayout.HelpBox(instructions, MessageType.Info);
|
||||
|
||||
DoBodyGUI();
|
||||
}
|
||||
EditorGUILayout.EndFadeGroup();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
EditorGUILayout.EndFadeGroup();
|
||||
|
||||
if (_FullAnimator.isAnimating || _BodyAnimator.isAnimating)
|
||||
Repaint();
|
||||
|
||||
GUI.enabled = enabled;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Draws the Header GUI for this tool which is displayed regardless of whether it is expanded or not.
|
||||
/// </summary>
|
||||
public virtual void DoHeaderGUI()
|
||||
{
|
||||
var area = AnimancerGUI.LayoutSingleLineRect(AnimancerGUI.SpacingMode.BeforeAndAfter);
|
||||
var click = GUI.Button(area, Name, EditorStyles.boldLabel);
|
||||
|
||||
area.xMin = area.xMax - area.height;
|
||||
GUI.DrawTexture(area, HelpIcon);
|
||||
|
||||
if (click)
|
||||
{
|
||||
if (area.Contains(Event.current.mousePosition))
|
||||
{
|
||||
Application.OpenURL(HelpURL);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsExpanded = !IsExpanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the Body GUI for this tool which is only displayed while it is expanded.</summary>
|
||||
public abstract void DoBodyGUI();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Asks the user where they want to save a modified asset, calls `modify` on it, and saves it.</summary>
|
||||
public static bool SaveModifiedAsset<T>(string saveTitle, string saveMessage,
|
||||
T obj, Action<T> modify) where T : Object
|
||||
{
|
||||
var originalPath = AssetDatabase.GetAssetPath(obj);
|
||||
|
||||
var extension = Path.GetExtension(originalPath);
|
||||
if (extension[0] == '.')
|
||||
extension = extension[1..];
|
||||
|
||||
var directory = Path.GetDirectoryName(originalPath);
|
||||
|
||||
var newName = Path.GetFileNameWithoutExtension(AssetDatabase.GenerateUniqueAssetPath(originalPath));
|
||||
var savePath = EditorUtility.SaveFilePanelInProject(saveTitle, newName, extension, saveMessage, directory);
|
||||
if (string.IsNullOrEmpty(savePath))
|
||||
return false;
|
||||
|
||||
if (originalPath != savePath)
|
||||
{
|
||||
obj = Instantiate(obj);
|
||||
AssetDatabase.CreateAsset(obj, savePath);
|
||||
}
|
||||
|
||||
modify(obj);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static Texture _HelpIcon;
|
||||
|
||||
/// <summary>The help icon image used in the tool header.</summary>
|
||||
public static Texture HelpIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_HelpIcon == null)
|
||||
_HelpIcon = AnimancerIcons.Load("_Help");
|
||||
return _HelpIcon;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Adds any objects dropped in the `area` to the `list`.</summary>
|
||||
protected void HandleDragAndDropIntoList<T>(
|
||||
Rect area,
|
||||
IList<T> list,
|
||||
bool overwrite)
|
||||
where T : Object
|
||||
{
|
||||
var dropIndex = 0;
|
||||
|
||||
// No easy way to avoid this closure.
|
||||
AnimancerGUI.Handle<T>((obj, isDrop) =>
|
||||
{
|
||||
if (!isDrop)
|
||||
return true;
|
||||
|
||||
if (overwrite)
|
||||
{
|
||||
RecordUndo();
|
||||
if (dropIndex < list.Count)
|
||||
{
|
||||
list[dropIndex++] = obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(obj);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, area);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0981a35e6e25d944dacf3f3665c5ab78
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Animancer Tools/Tool.cs
|
||||
uploadId: 795566
|
||||
1010
Packages/com.kybernetik.animancer/Editor/AnimancerDataMigrator.cs
Normal file
1010
Packages/com.kybernetik.animancer/Editor/AnimancerDataMigrator.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f196e38e5592d9c4cb6a5e51ba201a13
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerDataMigrator.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,372 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Various utilities used throughout Animancer.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEditorUtilities
|
||||
public static partial class AnimancerEditorUtilities
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
#region Misc
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Animancer Extension] [Editor-Only] Is the <see cref="Vector2.x"/> or <see cref="Vector2.y"/> NaN?</summary>
|
||||
public static bool IsNaN(this Vector2 vector)
|
||||
=> float.IsNaN(vector.x)
|
||||
|| float.IsNaN(vector.y);
|
||||
|
||||
/// <summary>[Animancer Extension] [Editor-Only] Is the <see cref="Vector3.x"/>, <see cref="Vector3.y"/>, or <see cref="Vector3.z"/> NaN?</summary>
|
||||
public static bool IsNaN(this Vector3 vector)
|
||||
=> float.IsNaN(vector.x)
|
||||
|| float.IsNaN(vector.y)
|
||||
|| float.IsNaN(vector.z);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Returns the value of `t` linearly interpolated along the X axis of the `rect`.</summary>
|
||||
public static float LerpUnclampedX(this Rect rect, float t)
|
||||
=> rect.x + rect.width * t;
|
||||
|
||||
/// <summary>Returns the value of `t` inverse linearly interpolated along the X axis of the `rect`.</summary>
|
||||
public static float InverseLerpUnclampedX(this Rect rect, float t)
|
||||
=> (t - rect.x) / rect.width;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Finds an asset of the specified type anywhere in the project.</summary>
|
||||
public static T FindAssetOfType<T>()
|
||||
where T : Object
|
||||
{
|
||||
var filter = typeof(Component).IsAssignableFrom(typeof(T))
|
||||
? $"t:{nameof(GameObject)}"
|
||||
: $"t:{typeof(T).Name}";
|
||||
|
||||
var guids = AssetDatabase.FindAssets(filter);
|
||||
if (guids.Length == 0)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < guids.Length; i++)
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
||||
var asset = AssetDatabase.LoadAssetAtPath<T>(path);
|
||||
if (asset != null)
|
||||
return asset;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Finds or creates an instance of <typeparamref name="T"/>.</summary>
|
||||
public static T FindOrCreate<T>(ref T scriptableObject, HideFlags hideFlags = default)
|
||||
where T : ScriptableObject
|
||||
{
|
||||
if (scriptableObject != null)
|
||||
return scriptableObject;
|
||||
|
||||
var instances = Resources.FindObjectsOfTypeAll<T>();
|
||||
if (instances.Length > 0)
|
||||
{
|
||||
scriptableObject = instances[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptableObject = ScriptableObject.CreateInstance<T>();
|
||||
scriptableObject.hideFlags = hideFlags;
|
||||
}
|
||||
|
||||
return scriptableObject;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The most recent <see cref="PlayModeStateChange"/>.</summary>
|
||||
public static PlayModeStateChange PlayModeState { get; private set; }
|
||||
|
||||
/// <summary>Is the Unity Editor is currently changing between Play Mode and Edit Mode?</summary>
|
||||
public static bool IsChangingPlayMode =>
|
||||
PlayModeState == PlayModeStateChange.ExitingEditMode ||
|
||||
PlayModeState == PlayModeStateChange.ExitingPlayMode;
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
private static void WatchForPlayModeChanges()
|
||||
{
|
||||
PlayModeState = EditorApplication.isPlayingOrWillChangePlaymode
|
||||
? EditorApplication.isPlaying
|
||||
? PlayModeStateChange.EnteredPlayMode
|
||||
: PlayModeStateChange.ExitingEditMode
|
||||
: PlayModeStateChange.EnteredEditMode;
|
||||
|
||||
EditorApplication.playModeStateChanged += change => PlayModeState = change;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Deletes the specified `subAsset`.</summary>
|
||||
public static void DeleteSubAsset(Object subAsset)
|
||||
{
|
||||
AssetDatabase.RemoveObjectFromAsset(subAsset);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
Object.DestroyImmediate(subAsset, true);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calculates the overall bounds of all renderers under the `transform`.</summary>
|
||||
public static Bounds CalculateBounds(Transform transform)
|
||||
{
|
||||
using var _ = ListPool<Renderer>.Instance.Acquire(out var renderers);
|
||||
|
||||
transform.GetComponentsInChildren(renderers);
|
||||
if (renderers.Count == 0)
|
||||
return default;
|
||||
|
||||
var bounds = renderers[0].bounds;
|
||||
for (int i = 1; i < renderers.Count; i++)
|
||||
bounds.Encapsulate(renderers[i].bounds);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
#region Collections
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Adds default items or removes items to make the <see cref="List{T}.Count"/> equal to the `count`.</summary>
|
||||
public static void SetCount<T>(List<T> list, int count)
|
||||
{
|
||||
if (list.Count < count)
|
||||
{
|
||||
while (list.Count < count)
|
||||
list.Add(default);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.RemoveRange(count, list.Count - count);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items from the `list` that are <c>null</c> and items that appear multiple times.
|
||||
/// Returns true if the `list` was modified.
|
||||
/// </summary>
|
||||
public static bool RemoveMissingAndDuplicates(ref List<GameObject> list)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
list = new();
|
||||
return false;
|
||||
}
|
||||
|
||||
var modified = false;
|
||||
|
||||
using (SetPool<Object>.Instance.Acquire(out var previousItems))
|
||||
{
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var item = list[i];
|
||||
if (item == null || previousItems.Contains(item))
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
modified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
previousItems.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Removes any <c>null</c> items and ensures that it contains
|
||||
/// an instance of each type derived from <typeparamref name="T"/>.
|
||||
/// </summary>
|
||||
public static void InstantiateDerivedTypes<T>(ref List<T> list)
|
||||
where T : IComparable<T>
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
list = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
if (list[i] == null)
|
||||
list.RemoveAt(i);
|
||||
}
|
||||
|
||||
var types = TypeSelectionMenu.GetDerivedTypes(typeof(T));
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
{
|
||||
var toolType = types[i];
|
||||
if (IndexOfType(list, toolType) >= 0)
|
||||
continue;
|
||||
|
||||
var instance = (T)Activator.CreateInstance(toolType);
|
||||
list.Add(instance);
|
||||
}
|
||||
|
||||
list.Sort();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Finds the index of the first item with the specified `type`.</summary>
|
||||
public static int IndexOfType<T>(IList<T> list, Type type)
|
||||
{
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
if (list[i].GetType() == type)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
#region Context Menus
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu function which passes the result of <see cref="CalculateEditorFadeDuration"/> into `startFade`.
|
||||
/// </summary>
|
||||
public static void AddFadeFunction(
|
||||
GenericMenu menu,
|
||||
string label,
|
||||
bool isEnabled,
|
||||
AnimancerNode node,
|
||||
Action<float> startFade)
|
||||
{
|
||||
// Fade functions need to be delayed twice since the context menu itself causes the next frame delta
|
||||
// time to be unreasonably high (which would skip the start of the fade).
|
||||
menu.AddFunction(label, isEnabled,
|
||||
() => EditorApplication.delayCall +=
|
||||
() => EditorApplication.delayCall +=
|
||||
() =>
|
||||
{
|
||||
startFade(node.CalculateEditorFadeDuration());
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>[Animancer Extension] [Editor-Only]
|
||||
/// Returns the duration of the `node`s current fade (if any), otherwise returns the `defaultDuration`.
|
||||
/// </summary>
|
||||
public static float CalculateEditorFadeDuration(this AnimancerNode node, float defaultDuration = 1)
|
||||
=> node.FadeSpeed > 0
|
||||
? 1 / node.FadeSpeed
|
||||
: defaultDuration;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Adds a menu function to open a web page.
|
||||
/// If the `linkSuffix` starts with a '/' then it will be
|
||||
/// relative to the <see cref="Strings.DocsURLs.Documentation"/>.
|
||||
/// </summary>
|
||||
public static void AddDocumentationLink(GenericMenu menu, string label, string linkSuffix)
|
||||
{
|
||||
if (linkSuffix[0] == '/')
|
||||
linkSuffix = Strings.DocsURLs.Documentation + linkSuffix;
|
||||
|
||||
menu.AddItem(new(label), false, () =>
|
||||
{
|
||||
EditorUtility.OpenWithDefaultApp(linkSuffix);
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Is the <see cref="MenuCommand.context"/> editable?</summary>
|
||||
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Looping", validate = true)]
|
||||
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Legacy", validate = true)]
|
||||
private static bool ValidateEditable(MenuCommand command)
|
||||
{
|
||||
return (command.context.hideFlags & HideFlags.NotEditable) != HideFlags.NotEditable;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Toggles the <see cref="Motion.isLooping"/> flag between true and false.</summary>
|
||||
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Looping")]
|
||||
private static void ToggleLooping(MenuCommand command)
|
||||
{
|
||||
var clip = (AnimationClip)command.context;
|
||||
SetLooping(clip, !clip.isLooping);
|
||||
}
|
||||
|
||||
/// <summary>Sets the <see cref="Motion.isLooping"/> flag.</summary>
|
||||
public static void SetLooping(AnimationClip clip, bool looping)
|
||||
{
|
||||
var settings = AnimationUtility.GetAnimationClipSettings(clip);
|
||||
settings.loopTime = looping;
|
||||
AnimationUtility.SetAnimationClipSettings(clip, settings);
|
||||
|
||||
Debug.Log($"Set {clip.name} to be {(looping ? "Looping" : "Not Looping")}." +
|
||||
" Note that you may need to restart Unity for this change to take effect.", clip);
|
||||
|
||||
// None of these let us avoid the need to restart Unity.
|
||||
//EditorUtility.SetDirty(clip);
|
||||
//AssetDatabase.SaveAssets();
|
||||
|
||||
//var path = AssetDatabase.GetAssetPath(clip);
|
||||
//AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Swaps the <see cref="AnimationClip.legacy"/> flag between true and false.</summary>
|
||||
[MenuItem("CONTEXT/" + nameof(AnimationClip) + "/Toggle Legacy")]
|
||||
private static void ToggleLegacy(MenuCommand command)
|
||||
{
|
||||
var clip = (AnimationClip)command.context;
|
||||
clip.legacy = !clip.legacy;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calls <see cref="Animator.Rebind"/>.</summary>
|
||||
[MenuItem("CONTEXT/" + nameof(Animator) + "/Restore Bind Pose", priority = 110)]
|
||||
private static void RestoreBindPose(MenuCommand command)
|
||||
{
|
||||
var animator = (Animator)command.context;
|
||||
|
||||
Undo.RegisterFullObjectHierarchyUndo(animator.gameObject, "Restore bind pose");
|
||||
|
||||
const string TypeName = "UnityEditor.AvatarSetupTool, UnityEditor";
|
||||
var type = Type.GetType(TypeName)
|
||||
?? throw new TypeLoadException($"Unable to find the type '{TypeName}'");
|
||||
|
||||
const string MethodName = "SampleBindPose";
|
||||
var method = type.GetMethod(MethodName, AnimancerReflection.StaticBindings)
|
||||
?? throw new MissingMethodException($"Unable to find the method '{MethodName}'");
|
||||
|
||||
method.Invoke(null, new object[] { animator.gameObject });
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27eecf5aa14ee5a44b06436c4b4ab75b
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerEditorUtilities.cs
|
||||
uploadId: 795566
|
||||
137
Packages/com.kybernetik.animancer/Editor/AnimancerReadMe.cs
Normal file
137
Packages/com.kybernetik.animancer/Editor/AnimancerReadMe.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] A welcome screen for <see cref="Animancer"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerReadMe
|
||||
///
|
||||
// [CreateAssetMenu]
|
||||
[AnimancerHelpUrl(typeof(AnimancerReadMe))]
|
||||
public class AnimancerReadMe : ReadMe
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The release ID of the current version.</summary>
|
||||
/// <example><list type="bullet">
|
||||
/// <item>[ 1] = v1.0.0: 2018-05-02.</item>
|
||||
/// <item>[ 2] = v1.1.0: 2018-05-29.</item>
|
||||
/// <item>[ 3] = v1.2.0: 2018-08-14.</item>
|
||||
/// <item>[ 4] = v1.3.0: 2018-09-12.</item>
|
||||
/// <item>[ 5] = v2.0.0: 2018-10-08.</item>
|
||||
/// <item>[ 6] = v3.0.0: 2019-05-27.</item>
|
||||
/// <item>[ 7] = v3.1.0: 2019-08-12.</item>
|
||||
/// <item>[ 8] = v4.0.0: 2020-01-28.</item>
|
||||
/// <item>[ 9] = v4.1.0: 2020-02-21.</item>
|
||||
/// <item>[10] = v4.2.0: 2020-03-02.</item>
|
||||
/// <item>[11] = v4.3.0: 2020-03-13.</item>
|
||||
/// <item>[12] = v4.4.0: 2020-03-27.</item>
|
||||
/// <item>[13] = v5.0.0: 2020-07-17.</item>
|
||||
/// <item>[14] = v5.1.0: 2020-07-27.</item>
|
||||
/// <item>[15] = v5.2.0: 2020-09-16.</item>
|
||||
/// <item>[16] = v5.3.0: 2020-10-06.</item>
|
||||
/// <item>[17] = v6.0.0: 2020-12-04.</item>
|
||||
/// <item>[18] = v6.1.0: 2021-04-13.</item>
|
||||
/// <item>[19] = v7.0.0: 2021-07-29.</item>
|
||||
/// <item>[20] = v7.1.0: 2021-08-13.</item>
|
||||
/// <item>[21] = v7.2.0: 2021-10-17.</item>
|
||||
/// <item>[22] = v7.3.0: 2022-07-03.</item>
|
||||
/// <item>[23] = v7.4.0: 2023-01-26.</item>
|
||||
/// <item>[24] = v7.4.1: 2023-01-28.</item>
|
||||
/// <item>[25] = v7.4.2: 2023-01-31.</item>
|
||||
/// <item>[26] = v7.4.3: 2023-04-16.</item>
|
||||
/// <item>[27] = v8.0.0: 2024-08-17.</item>
|
||||
/// <item>[28] = v8.0.1: 2024-09-08.</item>
|
||||
/// <item>[29] = v8.0.2: 2024-11-02.</item>
|
||||
/// <item>[30] = v8.1.0: 2025-02-26.</item>
|
||||
/// <item>[31] = v8.1.1: 2025-05-26.</item>
|
||||
/// <item>[32] = v8.2.0: 2025-09-17.</item>
|
||||
/// <item>[33] = v8.2.1: 2025-09-18.</item>
|
||||
/// <item>[34] = v8.2.2: 2025-09-27.</item>
|
||||
/// </list></example>
|
||||
public override int ReleaseNumber => 34;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string VersionName => Strings.DocsURLs.VersionName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string PrefKey => nameof(Animancer);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string BaseProductName => Strings.ProductName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ProductName => Strings.ProductName + " Pro";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DocumentationURL => Strings.DocsURLs.Documentation;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ChangeLogURL => Strings.DocsURLs.ChangeLogURL;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string SamplesURL => Strings.DocsURLs.Samples;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string UpdateURL => Strings.DocsURLs.LatestVersion;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
public AnimancerReadMe() : base(
|
||||
new("Issues",
|
||||
"for questions, suggestions, and bug reports",
|
||||
Strings.DocsURLs.Issues),
|
||||
new("Discussions",
|
||||
"for general discussions, feedback, and news",
|
||||
Strings.DocsURLs.Discussions),
|
||||
new("Email",
|
||||
"for anything private",
|
||||
GetEmailURL(Strings.DocsURLs.DeveloperEmail, Strings.ProductName),
|
||||
Strings.DocsURLs.DeveloperEmail))
|
||||
{
|
||||
ExtraSamples = new LinkSection[]
|
||||
{
|
||||
new("Platformer Game Kit", null, "https://kybernetik.com.au/platformer"),
|
||||
};
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only] A custom Inspector for <see cref="AnimancerReadMe"/>.</summary>
|
||||
[CustomEditor(typeof(AnimancerReadMe), editorForChildClasses: true)]
|
||||
public new class Editor : ReadMe.Editor
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>A callback to execute data migration.</summary>
|
||||
public static event Action<string> MigrateOldAssetData;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DoNewVersionDetails()
|
||||
{
|
||||
base.DoNewVersionDetails();
|
||||
|
||||
if (MigrateOldAssetData == null)
|
||||
return;
|
||||
|
||||
var text = $"Migrate old asset data to {Target.BaseProductName} {Target.VersionName}";
|
||||
if (GUILayout.Button(text))
|
||||
MigrateOldAssetData(text);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1be19fc3858e524fa17a5518e8c4d39
|
||||
labels:
|
||||
- Documentation
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: d4e06a71fc03595429cac47cd385c4c1, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerReadMe.cs
|
||||
uploadId: 795566
|
||||
323
Packages/com.kybernetik.animancer/Editor/AnimancerSettings.cs
Normal file
323
Packages/com.kybernetik.animancer/Editor/AnimancerSettings.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Persistent settings used by Animancer.</summary>
|
||||
/// <remarks>
|
||||
/// This asset automatically creates itself when first accessed.
|
||||
/// <para></para>
|
||||
/// The default location is <em>Packages/com.kybernetik.animancer/Code/Editor</em>, but you can freely move it
|
||||
/// (and the whole Animancer folder) anywhere in your project.
|
||||
/// <para></para>
|
||||
/// These settings can also be accessed via the Settings in the <see cref="Tools.AnimancerToolsWindow"/>
|
||||
/// (<c>Window/Animation/Animancer Tools</c>).
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettings
|
||||
///
|
||||
[AnimancerHelpUrl(typeof(AnimancerSettings))]
|
||||
public class AnimancerSettings : ScriptableObject
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static AnimancerSettings _Instance;
|
||||
|
||||
/// <summary>
|
||||
/// Loads an existing <see cref="AnimancerSettings"/> if there is one anywhere in your project.
|
||||
/// Otherwise, creates a new one and saves it in the Assets folder.
|
||||
/// </summary>
|
||||
public static AnimancerSettings Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Instance != null)
|
||||
return _Instance;
|
||||
|
||||
_Instance = AnimancerEditorUtilities.FindAssetOfType<AnimancerSettings>();
|
||||
|
||||
if (_Instance != null)
|
||||
return _Instance;
|
||||
|
||||
_Instance = CreateInstance<AnimancerSettings>();
|
||||
_Instance.name = "Animancer Settings";
|
||||
_Instance.hideFlags = HideFlags.DontSaveInBuild;
|
||||
|
||||
var path = $"Assets/{_Instance.name}.asset";
|
||||
path = AssetDatabase.GenerateUniqueAssetPath(path);
|
||||
AssetDatabase.CreateAsset(_Instance, path);
|
||||
|
||||
return _Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Finds an existing instance of this asset anywhere in the project.</summary>
|
||||
[InitializeOnLoadMethod]
|
||||
private static void FindExistingInstance()
|
||||
{
|
||||
if (_Instance == null)
|
||||
_Instance = AnimancerEditorUtilities.FindAssetOfType<AnimancerSettings>();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private SerializedObject _SerializedObject;
|
||||
|
||||
/// <summary>The <see cref="SerializedProperty"/> representing the <see cref="Instance"/>.</summary>
|
||||
public static SerializedObject SerializedObject
|
||||
=> Instance._SerializedObject ?? (Instance._SerializedObject = new(Instance));
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private readonly List<Dictionary<string, SerializedProperty>>
|
||||
SerializedProperties = new();
|
||||
|
||||
private static SerializedProperty GetSerializedProperty(int index, string propertyPath)
|
||||
{
|
||||
while (index >= Instance.SerializedProperties.Count)
|
||||
Instance.SerializedProperties.Add(null);
|
||||
|
||||
var properties = Instance.SerializedProperties[index];
|
||||
properties ??= Instance.SerializedProperties[index] = new();
|
||||
|
||||
if (!properties.TryGetValue(propertyPath, out var property))
|
||||
{
|
||||
property = SerializedObject.FindProperty(propertyPath);
|
||||
properties.Add(propertyPath, property);
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
/// <summary>Returns a <see cref="SerializedProperty"/> relative to the data at the specified `index`.</summary>
|
||||
public static SerializedProperty GetSerializedProperty(
|
||||
int index,
|
||||
ref string basePropertyPath,
|
||||
string propertyPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(basePropertyPath))
|
||||
basePropertyPath =
|
||||
$"{nameof(_Data)}{Serialization.ArrayDataPrefix}{index}{Serialization.ArrayDataSuffix}";
|
||||
|
||||
if (string.IsNullOrEmpty(propertyPath))
|
||||
propertyPath = basePropertyPath;
|
||||
else
|
||||
propertyPath = $"{basePropertyPath}.{propertyPath}";
|
||||
|
||||
return GetSerializedProperty(index, propertyPath);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[SerializeReference]
|
||||
private List<AnimancerSettingsGroup> _Data;
|
||||
|
||||
/// <summary>Returns a stored item of the specified type or creates a new one if necessary.</summary>
|
||||
public static T GetOrCreateData<T>()
|
||||
where T : AnimancerSettingsGroup, new()
|
||||
{
|
||||
ref var data = ref Instance._Data;
|
||||
data ??= new();
|
||||
|
||||
var index = AnimancerEditorUtilities.IndexOfType(Instance._Data, typeof(T));
|
||||
if (index >= 0)
|
||||
return (T)data[index];
|
||||
|
||||
var newT = new T();
|
||||
newT.SetDataIndex(data.Count);
|
||||
data.Add(newT);
|
||||
SetDirty();
|
||||
return newT;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calls <see cref="EditorUtility.SetDirty"/> on the <see cref="Instance"/>.</summary>
|
||||
public static new void SetDirty()
|
||||
=> EditorUtility.SetDirty(_Instance);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that there is an instance of each class derived from <see cref="AnimancerSettingsGroup"/>.
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
AnimancerEditorUtilities.InstantiateDerivedTypes(ref _Data);
|
||||
|
||||
for (int i = 0; i < _Data.Count; i++)
|
||||
_Data[i].SetDataIndex(i);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>A custom Inspector for <see cref="AnimancerSettings"/>.</summary>
|
||||
[CustomEditor(typeof(AnimancerSettings), true), CanEditMultipleObjects]
|
||||
public class Editor : UnityEditor.Editor
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
[NonSerialized]
|
||||
private SerializedProperty _Data;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Called when this object is first loaded.</summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_Data = serializedObject.FindProperty(nameof(AnimancerSettings._Data));
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DoInfoGUI();
|
||||
|
||||
DoOptionalWarningsGUI();
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
var count = _Data.arraySize;
|
||||
for (int i = 0; i < count; i++)
|
||||
DoDataGUI(_Data.GetArrayElementAtIndex(i), i);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// If <c>true</c>, the next <see cref="OnInspectorGUI"/> will skip drawing the info panel.
|
||||
/// </summary>
|
||||
public static bool HideNextInfo { get; set; }
|
||||
|
||||
private void DoInfoGUI()
|
||||
{
|
||||
if (HideNextInfo)
|
||||
{
|
||||
HideNextInfo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
"Feel free to move this asset anywhere in your project." +
|
||||
"\n\nIt should generally not be in the Animancer folder" +
|
||||
" so that if you ever update Animancer you can delete that folder" +
|
||||
" without losing these settings." +
|
||||
"\n\nIf this asset is deleted, it will be automatically recreated" +
|
||||
" with default values when something needs it.",
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void DoDataGUI(SerializedProperty property, int index)
|
||||
{
|
||||
if (property.managedReferenceValue is AnimancerSettingsGroup value)
|
||||
{
|
||||
DoHeading(value.DisplayName);
|
||||
|
||||
var first = true;
|
||||
var depth = property.depth;
|
||||
while (property.NextVisible(first) && property.depth > depth)
|
||||
{
|
||||
first = false;
|
||||
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
DoHeading("Missing Type");
|
||||
|
||||
if (GUILayout.Button("X", AnimancerGUI.MiniButtonStyle))
|
||||
{
|
||||
var count = _Data.arraySize;
|
||||
_Data.DeleteArrayElementAtIndex(index);
|
||||
if (count == _Data.arraySize)
|
||||
_Data.DeleteArrayElementAtIndex(index);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void DoOptionalWarningsGUI()
|
||||
{
|
||||
DoHeading("Optional Warnings");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
using (var label = PooledGUIContent.Acquire("Disabled Warnings"))
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var value = EditorGUILayout.EnumFlagsField(label, Validate.PermanentlyDisabledWarnings);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Validate.PermanentlyDisabledWarnings = (OptionalWarning)value;
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Help", EditorStyles.miniButton, AnimancerGUI.DontExpandWidth))
|
||||
Application.OpenURL(Strings.DocsURLs.OptionalWarning);
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static GUIStyle _HeadingStyle;
|
||||
|
||||
/// <summary>Draws a heading label.</summary>
|
||||
public static void DoHeading(string text)
|
||||
=> GUILayout.Label(text, _HeadingStyle ??= new(EditorStyles.largeLabel)
|
||||
{
|
||||
fontSize = 18,
|
||||
});
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates the Project Settings page.</summary>
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreateSettingsProvider()
|
||||
{
|
||||
UnityEditor.Editor editor = null;
|
||||
|
||||
return new("Project/" + Strings.ProductName, SettingsScope.Project)
|
||||
{
|
||||
keywords = new HashSet<string>() { Strings.ProductName },
|
||||
guiHandler = searchContext =>
|
||||
{
|
||||
if (editor == null)
|
||||
editor = CreateEditor(Instance);
|
||||
|
||||
HideNextInfo = true;
|
||||
|
||||
editor.OnInspectorGUI();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18bd840b706853d4a934ec3199f63a41
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerSettings.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,85 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] A static reference to a persistent setting stored in <see cref="AnimancerSettings"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettingsGroup_1
|
||||
public static class AnimancerSettingsGroup<T>
|
||||
where T : AnimancerSettingsGroup, new()
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Gets or creates a <typeparamref name="T"/> in the <see cref="AnimancerSettings"/> asset.</summary>
|
||||
public static T Instance
|
||||
=> AnimancerSettings.GetOrCreateData<T>();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>Base class for groups of fields that can be serialized inside <see cref="AnimancerSettings"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettingsGroup
|
||||
[Serializable, InternalSerializableType]
|
||||
public abstract class AnimancerSettingsGroup : IComparable<AnimancerSettingsGroup>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private int _DataIndex = -1;
|
||||
private string _BasePropertyPath;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The user-firendly name to display in the Inspector.</summary>
|
||||
public abstract string DisplayName { get; }
|
||||
|
||||
/// <summary>The index to display this data at in the Inspector.</summary>
|
||||
public abstract int Index { get; }
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Sets the index used to find <see cref="SerializedProperty"/> instances for this group.</summary>
|
||||
internal void SetDataIndex(int index)
|
||||
{
|
||||
if (_DataIndex == index)
|
||||
return;
|
||||
|
||||
_DataIndex = index;
|
||||
_BasePropertyPath = null;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Returns a <see cref="SerializedProperty"/> relative to the base of this group.</summary>
|
||||
protected SerializedProperty GetSerializedProperty(string propertyPath)
|
||||
=> AnimancerSettings.GetSerializedProperty(_DataIndex, ref _BasePropertyPath, propertyPath);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Draws a <see cref="EditorGUILayout.PropertyField(SerializedProperty, GUILayoutOption[])"/> for a
|
||||
/// property in this group.
|
||||
/// </summary>
|
||||
protected SerializedProperty DoPropertyField(string propertyPath)
|
||||
{
|
||||
var property = GetSerializedProperty(propertyPath);
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
return property;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Compares the <see cref="Index"/>.</summary>
|
||||
public int CompareTo(AnimancerSettingsGroup other)
|
||||
=> Index.CompareTo(other.Index);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc77ccece8dfec64b9da74266f0ec4fa
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/AnimancerSettingsGroup.cs
|
||||
uploadId: 795566
|
||||
63
Packages/com.kybernetik.animancer/Editor/AssemblyInfo.cs
Normal file
63
Packages/com.kybernetik.animancer/Editor/AssemblyInfo.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyTitle("Kybernetik.Animancer.Editor")]
|
||||
[assembly: AssemblyProduct("Animancer Pro")]
|
||||
[assembly: AssemblyDescription("An animation system for Unity which is based on the Playables API.")]
|
||||
[assembly: AssemblyCompany("Kybernetik")]
|
||||
[assembly: AssemblyCopyright("Copyright © Kybernetik 2018-2025")]
|
||||
[assembly: AssemblyVersion("8.2.2.34")]
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
[assembly: SuppressMessage("Style", "IDE0039:Use local function",
|
||||
Justification = "Locals create a new delegate with each use which is less efficient and can break code.")]
|
||||
[assembly: SuppressMessage("Style", "IDE0044:Make field readonly",
|
||||
Justification = "Using the [SerializeField] attribute on a private field means Unity will set it from serialized data.")]
|
||||
[assembly: SuppressMessage("Code Quality", "IDE0051:Remove unused private members",
|
||||
Justification = "Unity messages can be private, but the IDE will not know that Unity can still call them.")]
|
||||
[assembly: SuppressMessage("Code Quality", "IDE0052:Remove unread private members",
|
||||
Justification = "Unity messages can be private and don't need to be called manually.")]
|
||||
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter",
|
||||
Justification = "Unity messages sometimes need specific signatures, even if you don't use all the parameters.")]
|
||||
[assembly: SuppressMessage("Style", "IDE0062:Make local function 'static'",
|
||||
Justification = "Not supported by Unity")]
|
||||
[assembly: SuppressMessage("Style", "IDE0063:Use simple 'using' statement",
|
||||
Justification = "Not always good for implying intent.")]
|
||||
[assembly: SuppressMessage("Code Quality", "IDE0067:Dispose objects before losing scope",
|
||||
Justification = "Not always relevant.")]
|
||||
[assembly: SuppressMessage("Code Quality", "IDE0068:Use recommended dispose pattern",
|
||||
Justification = "Not always relevant.")]
|
||||
[assembly: SuppressMessage("Code Quality", "IDE0069:Disposable fields should be disposed",
|
||||
Justification = "Not always relevant.")]
|
||||
|
||||
[assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression",
|
||||
Justification = "Don't give code style advice in publically released code.")]
|
||||
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles",
|
||||
Justification = "Don't give code style advice in publically released code.")]
|
||||
|
||||
[assembly: SuppressMessage("Correctness", "UNT0005:Suspicious Time.deltaTime usage",
|
||||
Justification = "Time.deltaTime is not suspicious in FixedUpdate, it has the same value as Time.fixedDeltaTime")]
|
||||
[assembly: SuppressMessage("Type Safety", "UNT0014:Invalid type for call to GetComponent",
|
||||
Justification = "Doesn't account for generic constraints.")]
|
||||
[assembly: SuppressMessage("Correctness", "UNT0029:Pattern matching with null on Unity objects",
|
||||
Justification = "Use a regular equality check if handling destroyed objects is necessary")]
|
||||
|
||||
[assembly: SuppressMessage("Code Quality", "CS0649:Field is never assigned to, and will always have its default value",
|
||||
Justification = "Using the [SerializeField] attribute on a private field means Unity will set it from serialized data.")]
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
|
||||
Justification = "Having a field doesn't mean you are responsible for creating and destroying it.")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
|
||||
Justification = "Not all events need to care about the sender.")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
|
||||
Justification = "No need to pollute the member list of implementing types.")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly",
|
||||
Justification = "No need to pollute the member list of implementing types.")]
|
||||
[assembly: SuppressMessage("Microsoft.Usage", "CA2235:MarkAllNonSerializableFields",
|
||||
Justification = "UnityEngine.Object is serializable by Unity.")]
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44a1a6ee5f922404ab7d9747df2d31a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/AssemblyInfo.cs
|
||||
uploadId: 795566
|
||||
8
Packages/com.kybernetik.animancer/Editor/Caching.meta
Normal file
8
Packages/com.kybernetik.animancer/Editor/Caching.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5900fc4c275cf748a92ed28bbef13c1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e5936589d9791a4288596c4b383b592
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/AnimationBindings.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,54 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Caches <see cref="AnimationClip.events"/> to reduce garbage allocations.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimationEventCache
|
||||
///
|
||||
public static class AnimationEventCache
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly ConditionalWeakTable<AnimationClip, AnimationEvent[]>
|
||||
ClipToEvents = new();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="AnimationClip.events"/> and caches the result to avoid allocating more memory with
|
||||
/// each subsequent call.
|
||||
/// </summary>
|
||||
public static AnimationEvent[] GetCachedEvents(this AnimationClip clip)
|
||||
{
|
||||
if (!ClipToEvents.TryGetValue(clip, out var events))
|
||||
{
|
||||
events = clip.events;
|
||||
ClipToEvents.Add(clip, events);
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Clears the cache.</summary>
|
||||
public static void Clear()
|
||||
=> ClipToEvents.Clear();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Removes the `clip` from the cache so its events will be retrieved again next time.</summary>
|
||||
public static void Remove(AnimationClip clip)
|
||||
=> ClipToEvents.Remove(clip);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb828e75ad28a6441b43f561f568f98d
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/AnimationEventCache.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,140 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] A cache to optimize repeated attribute access.</summary>
|
||||
/// <remarks>
|
||||
/// If <typeparamref name="TAttribute"/> implements <see cref="IInitializable{T}"/> for <see cref="MemberInfo"/>,
|
||||
/// its <see cref="IInitializable{T}.Initialize(T)"/> method will be called automatically.
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AttributeCache_1
|
||||
public static class AttributeCache<TAttribute>
|
||||
where TAttribute : class
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly Dictionary<MemberInfo, TAttribute>
|
||||
MemberToAttribute = new();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <typeparamref name="TAttribute"/> attribute on the specified `member` (if there is one).
|
||||
/// </summary>
|
||||
public static TAttribute GetAttribute(MemberInfo member)
|
||||
{
|
||||
if (!MemberToAttribute.TryGetValue(member, out var attribute))
|
||||
{
|
||||
try
|
||||
{
|
||||
attribute = member.GetAttribute<TAttribute>();
|
||||
|
||||
if (attribute is IInitializable<MemberInfo> initializable)
|
||||
initializable.Initialize(member);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
attribute = null;
|
||||
}
|
||||
|
||||
MemberToAttribute.Add(member, attribute);
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <typeparamref name="TAttribute"/> attribute (if any)
|
||||
/// on the specified `type` or its <see cref="Type.BaseType"/> (recursively).
|
||||
/// </summary>
|
||||
public static TAttribute GetAttribute(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var attribute = GetAttribute((MemberInfo)type);
|
||||
if (attribute != null)
|
||||
return attribute;
|
||||
|
||||
return MemberToAttribute[type] = GetAttribute(type.BaseType);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <typeparamref name="TAttribute"/> attribute on the specified `field` or its
|
||||
/// <see cref="FieldInfo.FieldType"/> or <see cref="MemberInfo.DeclaringType"/>.
|
||||
/// </summary>
|
||||
public static TAttribute FindAttribute(FieldInfo field)
|
||||
{
|
||||
var attribute = GetAttribute(field);
|
||||
if (attribute != null)
|
||||
return attribute;
|
||||
|
||||
attribute = GetAttribute(field.FieldType);
|
||||
if (attribute != null)
|
||||
return MemberToAttribute[field] = attribute;
|
||||
|
||||
attribute = GetAttribute(field.DeclaringType);
|
||||
if (attribute != null)
|
||||
return MemberToAttribute[field] = attribute;
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only]
|
||||
/// Returns the <typeparamref name="TAttribute"/> attribute on the underlying field
|
||||
/// of the `property` or its <see cref="FieldInfo.FieldType"/> or
|
||||
/// <see cref="MemberInfo.DeclaringType"/> or any of the parent properties
|
||||
/// or the type of the <see cref="SerializedObject.targetObject"/>.
|
||||
/// </summary>
|
||||
public static TAttribute FindAttribute(SerializedProperty property)
|
||||
{
|
||||
var accessor = property.GetAccessor();
|
||||
while (accessor != null)
|
||||
{
|
||||
var field = accessor.GetField(property);
|
||||
var attribute = GetAttribute(field);
|
||||
if (attribute != null)
|
||||
return attribute;
|
||||
|
||||
var value = accessor.GetValue(property);
|
||||
if (value != null)
|
||||
{
|
||||
attribute = GetAttribute(value.GetType());
|
||||
if (attribute != null)
|
||||
return attribute;
|
||||
}
|
||||
|
||||
accessor = accessor.Parent;
|
||||
}
|
||||
|
||||
// If none of the fields of types they are declared in have names, try the actual type of the target.
|
||||
{
|
||||
var attribute = GetAttribute(property.serializedObject.targetObject.GetType());
|
||||
if (attribute != null)
|
||||
return attribute;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea23cad192fdb024b83eb42c328bc444
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/AttributeCache.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,257 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
// Inspector Gadgets // https://kybernetik.com.au/animancer // Copyright 2017-2024 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR && UNITY_IMGUI
|
||||
|
||||
using Animancer.Editor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Animancer.Units.Editor
|
||||
//namespace InspectorGadgets.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only]
|
||||
/// A system for formatting floats as strings that fit into a limited area and storing the results so they can be
|
||||
/// reused to minimise the need for garbage collection, particularly for string construction.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// This system only affects the display value. Once you select a field, it shows its actual value.
|
||||
/// <para></para>
|
||||
/// <strong>Example:</strong>
|
||||
/// With <c>"x"</c> as the suffix:
|
||||
/// <list type="bullet">
|
||||
/// <item><c>1.111111</c> could instead show <c>1.111~x</c>.</item>
|
||||
/// <item><c>0.00001234567</c> would normally show <c>1.234567e-05</c>, but with this it instead shows <c>0~x</c>
|
||||
/// because very small values generally aren't useful.</item>
|
||||
/// <item><c>99999999</c> shows <c>1e+08x</c> because very large values are already approximations and trying to
|
||||
/// format them correctly would be very difficult.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
///
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Units.Editor/CompactUnitConversionCache
|
||||
/// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/CompactUnitConversionCache
|
||||
///
|
||||
public class CompactUnitConversionCache
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Should the fields show approximations if the value is too long for the GUI?</summary>
|
||||
public static bool ShowApproximations
|
||||
=> AnimancerSettingsGroup<AnimationTimeAttributeSettings>.Instance.showApproximations;
|
||||
// => PropertyDrawers.TransformPropertyDrawer.ShowApproximations;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The suffix added to the end of each value.</summary>
|
||||
public readonly string Suffix;
|
||||
|
||||
/// <summary>The <see cref="Suffix"/> with a <c>~</c> before it to indicate an approximation.</summary>
|
||||
public readonly string ApproximateSuffix;
|
||||
|
||||
/// <summary>The value <c>0</c> with the <see cref="Suffix"/>.</summary>
|
||||
public readonly string ConvertedZero;
|
||||
|
||||
/// <summary>The value <c>0</c> with the <see cref="ApproximateSuffix"/>.</summary>
|
||||
public readonly string ConvertedSmallPositive;
|
||||
|
||||
/// <summary>The value <c>-0</c> with the <see cref="ApproximateSuffix"/>.</summary>
|
||||
public readonly string ConvertedSmallNegative;
|
||||
|
||||
/// <summary>The pixel width of the <see cref="Suffix"/> when drawn by <see cref="EditorStyles.numberField"/>.</summary>
|
||||
public float _SuffixWidth;
|
||||
|
||||
/// <summary>The caches for each character count.</summary>
|
||||
/// <remarks><c>this[x]</c> is a cache that outputs strings with <c>x</c> characters.</remarks>
|
||||
private readonly List<ConversionCache<float, string>>
|
||||
Caches = new();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Strings mapped to the width they would require for a <see cref="EditorStyles.numberField"/>.</summary>
|
||||
private static ConversionCache<string, float> _WidthCache;
|
||||
|
||||
/// <summary>Padding around the text in a <see cref="EditorStyles.numberField"/>.</summary>
|
||||
public static float _FieldPadding;
|
||||
|
||||
/// <summary>The pixel width of the <c>~</c> character when drawn by <see cref="EditorStyles.numberField"/>.</summary>
|
||||
public static float _ApproximateSymbolWidth;
|
||||
|
||||
/// <summary>The character(s) used to separate decimal values in the current OS language.</summary>
|
||||
public static string _DecimalSeparator;
|
||||
|
||||
/// <summary>Values smaller than this become <c>0~</c> or <c>-0~</c>.</summary>
|
||||
public const float
|
||||
SmallExponentialThreshold = 0.0001f;
|
||||
|
||||
/// <summary>Values larger than this can't be approximated.</summary>
|
||||
public const float
|
||||
LargeExponentialThreshold = 9999999f;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="CompactUnitConversionCache"/>.</summary>
|
||||
public CompactUnitConversionCache(string suffix)
|
||||
{
|
||||
Suffix = suffix;
|
||||
ApproximateSuffix = "~" + Suffix;
|
||||
ConvertedZero = "0" + Suffix;
|
||||
ConvertedSmallPositive = "0" + ApproximateSuffix;
|
||||
ConvertedSmallNegative = "-0" + ApproximateSuffix;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cached string representing the `value` trimmed to fit within the `width` (if necessary) and with
|
||||
/// the <see cref="Suffix"/> added on the end.
|
||||
/// </summary>
|
||||
public string Convert(float value, float width)
|
||||
{
|
||||
if (value == 0)
|
||||
return ConvertedZero;
|
||||
|
||||
if (!ShowApproximations)
|
||||
return GetCache(0).Convert(value);
|
||||
|
||||
if (value < SmallExponentialThreshold &&
|
||||
value > -SmallExponentialThreshold)
|
||||
return value > 0 ? ConvertedSmallPositive : ConvertedSmallNegative;
|
||||
|
||||
var index = CalculateCacheIndex(value, width);
|
||||
return GetCache(index).Convert(value);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calculate the index of the cache to use for the given parameters.</summary>
|
||||
private int CalculateCacheIndex(float value, float width)
|
||||
{
|
||||
//if (value > LargeExponentialThreshold ||
|
||||
// value < -LargeExponentialThreshold)
|
||||
// return 0;
|
||||
|
||||
var valueString = value.ToStringCached();
|
||||
|
||||
// It the approximated string wouldn't be shorter than the original, don't approximate.
|
||||
if (valueString.Length < 2 + ApproximateSuffix.Length)
|
||||
return 0;
|
||||
|
||||
if (_SuffixWidth == 0)
|
||||
{
|
||||
if (_WidthCache == null)
|
||||
{
|
||||
_WidthCache = ConversionCache.CreateWidthCache(EditorStyles.numberField);
|
||||
_FieldPadding = EditorStyles.numberField.padding.horizontal;
|
||||
_ApproximateSymbolWidth = _WidthCache.Convert("~") - _FieldPadding;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Suffix))
|
||||
_SuffixWidth = _WidthCache.Convert(Suffix);
|
||||
}
|
||||
|
||||
// If the field is wide enough to fit the full value, don't approximate.
|
||||
width -= _FieldPadding + _ApproximateSymbolWidth * 0.75f;
|
||||
var valueWidth = _WidthCache.Convert(valueString) + _SuffixWidth;
|
||||
if (valueWidth <= width)
|
||||
return 0;
|
||||
|
||||
// If the number of allowed characters would include the full value, don't approximate.
|
||||
var suffixedLength = valueString.Length + Suffix.Length;
|
||||
var allowedCharacters = (int)(suffixedLength * width / valueWidth);
|
||||
if (allowedCharacters + 2 >= suffixedLength)
|
||||
return 0;
|
||||
|
||||
return allowedCharacters;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates and returns a cache for the specified `characterCount`.</summary>
|
||||
private ConversionCache<float, string> GetCache(int characterCount)
|
||||
{
|
||||
while (Caches.Count <= characterCount)
|
||||
Caches.Add(null);
|
||||
|
||||
var cache = Caches[characterCount];
|
||||
if (cache == null)
|
||||
{
|
||||
if (characterCount == 0)
|
||||
{
|
||||
cache = new((value) =>
|
||||
{
|
||||
return value.ToStringCached() + Suffix;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
cache = new((value) =>
|
||||
{
|
||||
var valueString = value.ToStringCached();
|
||||
|
||||
if (value > LargeExponentialThreshold ||
|
||||
value < -LargeExponentialThreshold)
|
||||
goto IsExponential;
|
||||
|
||||
_DecimalSeparator ??= CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
|
||||
|
||||
var decimalIndex = valueString.IndexOf(_DecimalSeparator);
|
||||
if (decimalIndex < 0 || decimalIndex > characterCount)
|
||||
goto IsExponential;
|
||||
|
||||
// Not exponential.
|
||||
return valueString[..characterCount] + ApproximateSuffix;
|
||||
|
||||
IsExponential:
|
||||
var digits = Math.Max(0, characterCount - ApproximateSuffix.Length - 1);
|
||||
var format = GetExponentialFormat(digits);
|
||||
valueString = value.ToString(format);
|
||||
TrimExponential(ref valueString);
|
||||
return valueString + Suffix;
|
||||
});
|
||||
}
|
||||
|
||||
Caches[characterCount] = cache;
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static List<string> _ExponentialFormats;
|
||||
|
||||
/// <summary>Returns a format string to include the specified number of `digits` in an exponential number.</summary>
|
||||
public static string GetExponentialFormat(int digits)
|
||||
{
|
||||
_ExponentialFormats ??= new();
|
||||
|
||||
while (_ExponentialFormats.Count <= digits)
|
||||
_ExponentialFormats.Add("g" + _ExponentialFormats.Count);
|
||||
|
||||
return _ExponentialFormats[digits];
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static void TrimExponential(ref string valueString)
|
||||
{
|
||||
var length = valueString.Length;
|
||||
if (length <= 4 ||
|
||||
valueString[length - 4] != 'e' ||
|
||||
valueString[length - 2] != '0')
|
||||
return;
|
||||
|
||||
valueString =
|
||||
valueString[..(length - 2)] +
|
||||
valueString[length - 1];
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b282ce649e44335499d21e0472c670e1
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/CompactUnitConversionCache.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,137 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
// Inspector Gadgets // https://kybernetik.com.au/inspector-gadgets // Copyright 2017-2024 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
//#define LOG_CONVERSION_CACHE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
// Shared File Last Modified: 2023-09-02
|
||||
namespace Animancer.Editor
|
||||
//namespace InspectorGadgets.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only]
|
||||
/// A simple system for converting objects and storing the results so they can be reused to minimise the need for
|
||||
/// garbage collection, particularly for string construction.
|
||||
/// </summary>
|
||||
/// <remarks>This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime.</remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache_2
|
||||
/// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache_2
|
||||
///
|
||||
public class ConversionCache<TKey, TValue>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private class CachedValue
|
||||
{
|
||||
public int lastFrameAccessed;
|
||||
public TValue value;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private readonly Dictionary<TKey, CachedValue>
|
||||
Cache = new();
|
||||
private readonly List<TKey>
|
||||
Keys = new();
|
||||
private readonly Func<TKey, TValue>
|
||||
Converter;
|
||||
|
||||
private int _LastCleanupFrame;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ConversionCache{TKey, TValue}"/> which uses the specified delegate to convert values.
|
||||
/// </summary>
|
||||
public ConversionCache(Func<TKey, TValue> converter)
|
||||
=> Converter = converter;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// If a value has already been cached for the specified `key`, return it. Otherwise create a new one using
|
||||
/// the delegate provided in the constructor and cache it.
|
||||
/// <para></para>
|
||||
/// If the `key` is <c>null</c>, this method returns the default <typeparamref name="TValue"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This method also periodically removes values that have not been used recently.</remarks>
|
||||
public TValue Convert(TKey key)
|
||||
{
|
||||
if (key == null)
|
||||
return default;
|
||||
|
||||
CachedValue cached;
|
||||
|
||||
// The next time a value is retrieved after at least 100 frames, clear out any old ones.
|
||||
var frame = Time.frameCount;
|
||||
if (_LastCleanupFrame + 100 < frame)
|
||||
{
|
||||
|
||||
for (int i = Keys.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var checkKey = Keys[i];
|
||||
if (!Cache.TryGetValue(checkKey, out cached) ||
|
||||
cached.lastFrameAccessed <= _LastCleanupFrame)
|
||||
{
|
||||
Cache.Remove(checkKey);
|
||||
Keys.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
_LastCleanupFrame = frame;
|
||||
|
||||
}
|
||||
|
||||
if (!Cache.TryGetValue(key, out cached))
|
||||
{
|
||||
Cache.Add(key, cached = new() { value = Converter(key) });
|
||||
Keys.Add(key);
|
||||
|
||||
}
|
||||
|
||||
cached.lastFrameAccessed = frame;
|
||||
|
||||
return cached.value;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>[Editor-Only] Utilities for <see cref="ConversionCache{TKey, TValue}"/>.</summary>
|
||||
/// <remarks>This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime.</remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache
|
||||
/// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache
|
||||
///
|
||||
public static class ConversionCache
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ConversionCache{TKey, TValue}"/> for calculating the GUI width occupied by text using
|
||||
/// the specified `style`.
|
||||
/// </summary>
|
||||
public static ConversionCache<string, float> CreateWidthCache(GUIStyle style)
|
||||
=> new(style.CalculateWidth);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
// The "g" format gives a lower case 'e' for exponentials instead of upper case 'E'.
|
||||
private static readonly ConversionCache<float, string>
|
||||
FloatToString = new((value) => $"{value:g}");
|
||||
|
||||
/// <summary>[Animancer Extension]
|
||||
/// Calls <see cref="float.ToString(string)"/> using <c>"g"</c> as the format and caches the result.
|
||||
/// </summary>
|
||||
public static string ToStringCached(this float value)
|
||||
=> FloatToString.Convert(value);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b0e970556a97204991d267c5cdb3ca9
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/Caching/ConversionCache.cs
|
||||
uploadId: 795566
|
||||
92
Packages/com.kybernetik.animancer/Editor/DefaultValues.cs
Normal file
92
Packages/com.kybernetik.animancer/Editor/DefaultValues.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Utilities for using <see cref="DefaultValueAttribute"/>s.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/DefaultValues
|
||||
public static class DefaultValues
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only]
|
||||
/// If the field represented by the `property` has a <see cref="DefaultValueAttribute"/>,
|
||||
/// this method sets the `value` to its <see cref="DefaultValueAttribute.Primary"/> value.
|
||||
/// If it was already at the value, it sets it to the <see cref="DefaultValueAttribute.Secondary"/>
|
||||
/// value instead. And if the field has no attribute, it uses the default for the type.
|
||||
/// </summary>
|
||||
public static void SetToDefault<T>(ref T value, SerializedProperty property)
|
||||
{
|
||||
var accessor = property.GetAccessor();
|
||||
var field = accessor.GetField(property);
|
||||
if (field == null)
|
||||
accessor.SetValue(property, null);
|
||||
else
|
||||
SetToDefault(ref value, field);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only]
|
||||
/// If the field represented by the `property` has a <see cref="DefaultValueAttribute"/>,
|
||||
/// this method sets the `value` to its <see cref="DefaultValueAttribute.Primary"/> value.
|
||||
/// If it was already at the value, it sets it to the <see cref="DefaultValueAttribute.Secondary"/>
|
||||
/// value instead. And if the field has no attribute, it uses the default for the type.
|
||||
/// </summary>
|
||||
public static void SetToDefault<T>(ref T value, FieldInfo field)
|
||||
{
|
||||
var defaults = field.GetAttribute<DefaultValueAttribute>();
|
||||
if (defaults != null)
|
||||
defaults.SetToDefault(ref value);
|
||||
else
|
||||
value = default;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only]
|
||||
/// Sets the `value` equal to the <see cref="DefaultValueAttribute.Primary"/> value.
|
||||
/// If it was already at the value, it sets it equal to the <see cref="DefaultValueAttribute.Secondary"/>
|
||||
/// value instead.
|
||||
/// </summary>
|
||||
public static void SetToDefault<T>(this DefaultValueAttribute attribute, ref T value)
|
||||
{
|
||||
var primary = attribute.Primary;
|
||||
if (!Equals(value, primary))
|
||||
{
|
||||
value = (T)primary;
|
||||
return;
|
||||
}
|
||||
|
||||
var secondary = attribute.Secondary;
|
||||
if (secondary != null || !typeof(T).IsValueType)
|
||||
{
|
||||
value = (T)secondary;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>[Editor-Only]
|
||||
/// Sets the `value` equal to the `primary` value.
|
||||
/// If it was already at the value, it sets it equal to the `secondary` value instead.
|
||||
/// </summary>
|
||||
public static void SetToDefault<T>(ref T value, T primary, T secondary)
|
||||
{
|
||||
if (!Equals(value, primary))
|
||||
value = primary;
|
||||
else
|
||||
value = secondary;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83cc1f85e7da4a545a63f04ffb42d8ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/DefaultValues.cs
|
||||
uploadId: 795566
|
||||
8
Packages/com.kybernetik.animancer/Editor/GUI.meta
Normal file
8
Packages/com.kybernetik.animancer/Editor/GUI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef1e58ab0b0cf224d9837d422a8647f1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1072
Packages/com.kybernetik.animancer/Editor/GUI/AnimancerGUI.cs
Normal file
1072
Packages/com.kybernetik.animancer/Editor/GUI/AnimancerGUI.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa67bea4f1d70534987fb1358fd71903
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/AnimancerGUI.cs
|
||||
uploadId: 795566
|
||||
118
Packages/com.kybernetik.animancer/Editor/GUI/AnimancerIcons.cs
Normal file
118
Packages/com.kybernetik.animancer/Editor/GUI/AnimancerIcons.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Icon textures used throughout Animancer.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerIcons
|
||||
public static class AnimancerIcons
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>A standard icon for information.</summary>
|
||||
public static readonly Texture Info = Load("console.infoicon");
|
||||
|
||||
/// <summary>A standard icon for warnings.</summary>
|
||||
public static readonly Texture Warning = Load("console.warnicon");
|
||||
|
||||
/// <summary>A standard icon for errors.</summary>
|
||||
public static readonly Texture Error = Load("console.erroricon");
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static Texture _ScriptableObject;
|
||||
|
||||
/// <summary>The icon for <see cref="UnityEngine.ScriptableObject"/>.</summary>
|
||||
public static Texture ScriptableObject
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
if (_ScriptableObject == null)
|
||||
{
|
||||
_ScriptableObject = Load("d_ScriptableObject Icon");
|
||||
|
||||
if (_ScriptableObject == null)
|
||||
_ScriptableObject = AssetPreview.GetMiniTypeThumbnail(typeof(StringAsset));
|
||||
}
|
||||
|
||||
return _ScriptableObject;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Loads an icon texture.</summary>
|
||||
public static Texture Load(string name, FilterMode filterMode = FilterMode.Bilinear)
|
||||
{
|
||||
var icon = EditorGUIUtility.Load(name) as Texture;
|
||||
if (icon != null)
|
||||
icon.filterMode = filterMode;
|
||||
return icon;
|
||||
}
|
||||
|
||||
/// <summary>Loads an icon `texture` if it was <c>null</c>.</summary>
|
||||
public static Texture Load(ref Texture texture, string name, FilterMode filterMode = FilterMode.Bilinear)
|
||||
=> texture != null
|
||||
? texture
|
||||
: texture = Load(name, filterMode);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static GUIContent
|
||||
_PlayIcon,
|
||||
_PauseIcon,
|
||||
_StepBackwardIcon,
|
||||
_StepForwardIcon,
|
||||
_AddIcon,
|
||||
_ClearIcon,
|
||||
_CopyIcon;
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a play button.</summary>
|
||||
public static GUIContent PlayIcon
|
||||
=> IconContent(ref _PlayIcon, "PlayButton");
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a pause button.</summary>
|
||||
public static GUIContent PauseIcon
|
||||
=> IconContent(ref _PauseIcon, "PauseButton");
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a step backward button.</summary>
|
||||
public static GUIContent StepBackwardIcon
|
||||
=> IconContent(ref _StepBackwardIcon, "Animation.PrevKey");
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a step forward button.</summary>
|
||||
public static GUIContent StepForwardIcon
|
||||
=> IconContent(ref _StepForwardIcon, "Animation.NextKey");
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for an add button.</summary>
|
||||
public static GUIContent AddIcon(string tooltip = "Add")
|
||||
=> IconContent(ref _AddIcon, "Toolbar Plus", tooltip);
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a clear button.</summary>
|
||||
public static GUIContent ClearIcon(string tooltip = "Clear")
|
||||
=> IconContent(ref _ClearIcon, "Grid.EraserTool", tooltip);
|
||||
|
||||
/// <summary><see cref="IconContent(ref GUIContent, string, string)"/> for a copy button.</summary>
|
||||
public static GUIContent CopyIcon(string tooltip = "Copy to clipboard")
|
||||
=> IconContent(ref _CopyIcon, "UnityEditor.ConsoleWindow", tooltip);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calls <see cref="EditorGUIUtility.IconContent(string)"/> if the `content` was null.</summary>
|
||||
public static GUIContent IconContent(ref GUIContent content, string name, string tooltip = "")
|
||||
{
|
||||
content ??= EditorGUIUtility.IconContent(name);
|
||||
content.tooltip = tooltip;
|
||||
return content;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6481076e108e435459e58460a5d52a74
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/AnimancerIcons.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,88 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] <see cref="GUIStyle"/>s for a group of connected buttons.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ButtonGroupStyles
|
||||
public struct ButtonGroupStyles
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The style for the button on the far left.</summary>
|
||||
public GUIStyle left;
|
||||
|
||||
/// <summary>The style for any buttons in the middle.</summary>
|
||||
public GUIStyle middle;
|
||||
|
||||
/// <summary>The style for the button on the far right.</summary>
|
||||
public GUIStyle right;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="ButtonGroupStyles"/>.</summary>
|
||||
public ButtonGroupStyles(
|
||||
GUIStyle left,
|
||||
GUIStyle middle,
|
||||
GUIStyle right)
|
||||
{
|
||||
this.left = left;
|
||||
this.middle = middle;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Copies any <c>null</c> values from another group.</summary>
|
||||
public void CopyMissingStyles(ButtonGroupStyles copyFrom)
|
||||
{
|
||||
left ??= copyFrom.left;
|
||||
middle ??= copyFrom.middle;
|
||||
right ??= copyFrom.right;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The default styles for a mini button.</summary>
|
||||
public static ButtonGroupStyles MiniButton => new(
|
||||
EditorStyles.miniButtonLeft,
|
||||
EditorStyles.miniButtonMid,
|
||||
EditorStyles.miniButtonRight);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static ButtonGroupStyles _Button;
|
||||
|
||||
/// <summary>The default styles for a button.</summary>
|
||||
public static ButtonGroupStyles Button
|
||||
{
|
||||
get
|
||||
{
|
||||
_Button.left ??= MiniToRegularButtonStyle(EditorStyles.miniButtonLeft);
|
||||
_Button.middle ??= MiniToRegularButtonStyle(EditorStyles.miniButtonMid);
|
||||
_Button.right ??= MiniToRegularButtonStyle(EditorStyles.miniButtonRight);
|
||||
return _Button;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a copy of the `style` with the size of a regular button.</summary>
|
||||
public static GUIStyle MiniToRegularButtonStyle(GUIStyle style)
|
||||
=> new(style)
|
||||
{
|
||||
fixedHeight = 0,
|
||||
padding = GUI.skin.button.padding,
|
||||
stretchWidth = false,
|
||||
};
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbe793685698bc24bb5b1087e4c93cd2
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/ButtonGroupStyles.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cf051da751d8b14ab331c9a4d43511d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] A custom GUI for an <see cref="AnimancerEvent.Dispatcher"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEventDispatcherDrawer
|
||||
[CustomGUI(typeof(AnimancerEvent.Dispatcher))]
|
||||
public class AnimancerEventDispatcherDrawer : CustomGUI<AnimancerEvent.Dispatcher>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
{
|
||||
var state = Value.State;
|
||||
var events = state?.SharedEvents;
|
||||
if (events == null)
|
||||
{
|
||||
EditorGUILayout.LabelField("Event Dispatcher", "Null");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPath = state != null
|
||||
? state.GetPath()
|
||||
: "Null";
|
||||
|
||||
var eventSequenceDrawer = EventSequenceDrawer.Get(events);
|
||||
var area = AnimancerGUI.LayoutRect(eventSequenceDrawer.CalculateHeight(events));
|
||||
using (var label = PooledGUIContent.Acquire("Event Dispatcher"))
|
||||
using (var summary = PooledGUIContent.Acquire(targetPath))
|
||||
eventSequenceDrawer.DoGUI(ref area, events, label, summary);
|
||||
|
||||
if (eventSequenceDrawer.IsExpanded && state != null)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
var enabled = GUI.enabled;
|
||||
GUI.enabled = false;
|
||||
EditorGUILayout.Toggle("Has Owned Events", state.HasOwnedEvents);
|
||||
GUI.enabled = enabled;
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 342d6bdafab430c48ac47d5f67eced91
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/AnimancerEventDispatcherDrawer.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,73 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="float"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/FloatGUI
|
||||
///
|
||||
[CustomGUI(typeof(float))]
|
||||
public class FloatGUI : CustomGUI<float>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
=> Value = EditorGUILayout.FloatField(Label, Value);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="int"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/IntGUI
|
||||
///
|
||||
[CustomGUI(typeof(int))]
|
||||
public class IntGUI : CustomGUI<int>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
=> Value = EditorGUILayout.IntField(Label, Value);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="string"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/StringGUI
|
||||
///
|
||||
[CustomGUI(typeof(string))]
|
||||
public class StringGUI : CustomGUI<string>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
=> Value = EditorGUILayout.TextField(Label, Value);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>[Editor-Only] A <see cref="ICustomGUI"/> for <see cref="Object"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ObjectGUI_1
|
||||
///
|
||||
[CustomGUI(typeof(Object))]
|
||||
public class ObjectGUI<T> : CustomGUI<T>
|
||||
where T : Object
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
=> Value = AnimancerGUI.DoObjectFieldGUI(Label, Value, true);
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e4d11fbf9dad8149b1966ed610c9916
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/BasicCustomGUI.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,92 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
|
||||
/// <remarks>
|
||||
/// Every non-abstract type implementing this interface must have at least one <see cref="CustomGUIAttribute"/>.
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/ICustomGUI
|
||||
///
|
||||
public interface ICustomGUI
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The optional label to draw in front of the field.</summary>
|
||||
GUIContent Label { get; set; }
|
||||
|
||||
/// <summary>The target object for which this GUI will be drawn.</summary>
|
||||
object Value { get; set; }
|
||||
|
||||
/// <summary>Draws the GUI for the <see cref="Value"/>.</summary>
|
||||
void DoGUI();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
|
||||
/// <remarks>
|
||||
/// Every non-abstract type inheriting from this class must have at least one <see cref="CustomGUIAttribute"/>.
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUI_1
|
||||
///
|
||||
public abstract class CustomGUI<T> : ICustomGUI
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The object for which this GUI will be drawn.</summary>
|
||||
public T Value { get; protected set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
object ICustomGUI.Value
|
||||
{
|
||||
get => Value;
|
||||
set => Value = (T)value;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public GUIContent Label { get; set; } = GUIContent.none;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract void DoGUI();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
|
||||
/// <summary>[Editor-Only] Extension methods for <see cref="ICustomGUI"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIExtensions
|
||||
///
|
||||
public static class CustomGUIExtensions
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Sets the <see cref="ICustomGUI.Label"/>.</summary>
|
||||
public static void SetLabel(
|
||||
this ICustomGUI customGUI,
|
||||
string text,
|
||||
string tooltip = null,
|
||||
Texture image = null)
|
||||
{
|
||||
var label = customGUI.Label;
|
||||
if (label == null || label == GUIContent.none)
|
||||
customGUI.Label = label = new(text);
|
||||
|
||||
label.text = text;
|
||||
label.tooltip = tooltip;
|
||||
label.image = image;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5792b81ce4ba30448a3367876e92058
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/CustomGUI.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,35 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only]
|
||||
/// Attribute for classes which implement <see cref="CustomGUI{T}"/> to specify the type of objects they apply to.
|
||||
/// </summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIAttribute
|
||||
///
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public sealed class CustomGUIAttribute : Attribute
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The type of object which the attributed <see cref="CustomGUI{T}"/> class applies to.</summary>
|
||||
public readonly Type TargetType;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates a new <see cref="CustomGUIAttribute"/>.</summary>
|
||||
public CustomGUIAttribute(Type targetType)
|
||||
{
|
||||
TargetType = targetType;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea9f8ff21895492479bdc457361a570c
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/CustomGUIAttribute.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,152 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
//#define LOG_CUSTOM_GUI_FACTORY
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] Draws a custom GUI for an object.</summary>
|
||||
/// <remarks>
|
||||
/// Every non-abstract type implementing this interface must have at least one <see cref="CustomGUIAttribute"/>.
|
||||
/// </remarks>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/CustomGUIFactory
|
||||
///
|
||||
public static class CustomGUIFactory
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly Dictionary<Type, Type>
|
||||
TargetTypeToGUIType = new();
|
||||
|
||||
static CustomGUIFactory()
|
||||
{
|
||||
foreach (var guiType in TypeCache.GetTypesWithAttribute(typeof(CustomGUIAttribute)))
|
||||
{
|
||||
if (guiType.IsAbstract ||
|
||||
guiType.IsInterface)
|
||||
continue;
|
||||
|
||||
if (!typeof(ICustomGUI).IsAssignableFrom(guiType))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"{guiType.FullName} has a {nameof(CustomGUIAttribute)}" +
|
||||
$" but doesn't implement {nameof(ICustomGUI)}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var attribute = guiType.GetCustomAttribute<CustomGUIAttribute>();
|
||||
if (attribute.TargetType != null)
|
||||
{
|
||||
|
||||
TargetTypeToGUIType.Add(attribute.TargetType, guiType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly ConditionalWeakTable<object, ICustomGUI>
|
||||
TargetToGUI = new();
|
||||
|
||||
/// <summary>Returns an existing <see cref="ICustomGUI"/> for the `targetType` or creates one if necessary.</summary>
|
||||
/// <remarks>Returns null if the `targetType` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
|
||||
public static ICustomGUI GetOrCreateForType(Type targetType)
|
||||
{
|
||||
if (targetType == null)
|
||||
return null;
|
||||
|
||||
if (TargetToGUI.TryGetValue(targetType, out var gui))
|
||||
return gui;
|
||||
|
||||
gui = Create(targetType);
|
||||
|
||||
TargetToGUI.Add(targetType, gui);
|
||||
|
||||
return gui;
|
||||
}
|
||||
|
||||
/// <summary>Returns an existing <see cref="ICustomGUI"/> for the `value` or creates one if necessary.</summary>
|
||||
/// <remarks>Returns null if the `value` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
|
||||
public static ICustomGUI GetOrCreateForObject(object value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
if (TargetToGUI.TryGetValue(value, out var gui))
|
||||
return gui;
|
||||
|
||||
gui = Create(value.GetType());
|
||||
if (gui != null)
|
||||
gui.Value = value;
|
||||
|
||||
TargetToGUI.Add(value, gui);
|
||||
return gui;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Creates an <see cref="ICustomGUI"/> for the `targetType`.</summary>
|
||||
/// <remarks>Returns null if the `value` is null or no valid <see cref="ICustomGUI"/> type is found.</remarks>
|
||||
public static ICustomGUI Create(Type targetType)
|
||||
{
|
||||
if (!TryGetGUIType(targetType, out var guiType))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (guiType.IsGenericTypeDefinition)
|
||||
guiType = guiType.MakeGenericType(targetType);
|
||||
|
||||
var gui = (ICustomGUI)Activator.CreateInstance(guiType);
|
||||
|
||||
return gui;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Tries to determine the valid <see cref="ICustomGUI"/> type for drawing the `target`.</summary>
|
||||
public static bool TryGetGUIType(Type target, out Type gui)
|
||||
{
|
||||
// Try the target and its base types.
|
||||
|
||||
var type = target;
|
||||
while (type != null && type != typeof(object))
|
||||
{
|
||||
if (TargetTypeToGUIType.TryGetValue(type, out gui))
|
||||
return true;
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
// Try any interfaces.
|
||||
|
||||
var interfaces = target.GetInterfaces();
|
||||
for (int i = 0; i < interfaces.Length; i++)
|
||||
if (TargetTypeToGUIType.TryGetValue(interfaces[i], out gui))
|
||||
return true;
|
||||
|
||||
// Try base object.
|
||||
|
||||
return TargetTypeToGUIType.TryGetValue(typeof(object), out gui);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb3162b4afff1f34eaff91a1a123ffde
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/CustomGUIFactory.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,148 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] An <see cref="ICustomGUI"/> for <see cref="MulticastDelegate"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/DelegateGUI
|
||||
[CustomGUI(typeof(MulticastDelegate))]
|
||||
public class DelegateGUI : CustomGUI<MulticastDelegate>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly HashSet<MulticastDelegate>
|
||||
ExpandedItems = new();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Calculates the number of vertical pixels required to draw the specified <see cref="MulticastDelegate"/>.</summary>
|
||||
public static float CalculateHeight(MulticastDelegate del)
|
||||
=> AnimancerGUI.CalculateHeight(CalculateLineCount(del));
|
||||
|
||||
/// <summary>Calculates the number of lines required to draw the specified <see cref="MulticastDelegate"/>.</summary>
|
||||
public static int CalculateLineCount(MulticastDelegate del)
|
||||
=> del == null || !ExpandedItems.Contains(del)
|
||||
? 1
|
||||
: 1 + CalculateLineCount(AnimancerReflection.GetInvocationList(del));
|
||||
|
||||
/// <summary>Calculates the number of lines required to draw the specified `invocationList`.</summary>
|
||||
public static int CalculateLineCount(Delegate[] invocationList)
|
||||
=> invocationList == null
|
||||
? 3
|
||||
: invocationList.Length * 3;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
{
|
||||
var area = AnimancerGUI.LayoutRect(CalculateHeight(Value));
|
||||
DoGUI(ref area, Label, Value);
|
||||
}
|
||||
|
||||
/// <summary>Draws the GUI for the given delegate.</summary>
|
||||
public static void DoGUI(
|
||||
ref Rect area,
|
||||
GUIContent label,
|
||||
MulticastDelegate del,
|
||||
GUIContent valueLabel = null)
|
||||
{
|
||||
area.height = AnimancerGUI.LineHeight;
|
||||
|
||||
var delegates = AnimancerReflection.GetInvocationList(del);
|
||||
|
||||
var isExpanded = del != null && AnimancerGUI.DoHashedFoldoutGUI(area, ExpandedItems, del);
|
||||
|
||||
if (valueLabel != null)
|
||||
{
|
||||
EditorGUI.LabelField(area, label, valueLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
var count = delegates == null ? 0 : delegates.Length;
|
||||
using (var countLabel = PooledGUIContent.Acquire(count.ToStringCached()))
|
||||
EditorGUI.LabelField(area, label, countLabel);
|
||||
}
|
||||
|
||||
AnimancerGUI.NextVerticalArea(ref area);
|
||||
|
||||
if (!isExpanded)
|
||||
return;
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
if (delegates == null)
|
||||
{
|
||||
DoSingleGUI(ref area, del);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < delegates.Length; i++)
|
||||
DoSingleGUI(ref area, delegates[i]);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private const int TargetFieldCacheCapacity = 128;
|
||||
|
||||
private static readonly Dictionary<object, FastObjectField>
|
||||
TargetFieldCache = new(TargetFieldCacheCapacity);
|
||||
|
||||
/// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary>
|
||||
public static void DoSingleGUI(ref Rect area, Delegate del)
|
||||
{
|
||||
area.height = AnimancerGUI.LineHeight;
|
||||
|
||||
if (del == null)
|
||||
{
|
||||
EditorGUI.LabelField(area, "Delegate", "Null");
|
||||
AnimancerGUI.NextVerticalArea(ref area);
|
||||
return;
|
||||
}
|
||||
|
||||
var method = del.Method;
|
||||
EditorGUI.LabelField(area, "Method", method.ToString());
|
||||
|
||||
AnimancerGUI.NextVerticalArea(ref area);
|
||||
|
||||
EditorGUI.LabelField(area, "Declaring Type", method.DeclaringType.GetNameCS());
|
||||
|
||||
AnimancerGUI.NextVerticalArea(ref area);
|
||||
|
||||
var target = del.Target;
|
||||
|
||||
FastObjectField field;
|
||||
|
||||
if (target is not null)
|
||||
TargetFieldCache.TryGetValue(target, out field);
|
||||
else
|
||||
field = FastObjectField.Null;
|
||||
|
||||
field.Draw(area, "Target", target);
|
||||
|
||||
if (target is not null)
|
||||
{
|
||||
if (TargetFieldCache.Count == TargetFieldCacheCapacity)
|
||||
TargetFieldCache.Clear();
|
||||
|
||||
TargetFieldCache[target] = field;
|
||||
}
|
||||
|
||||
AnimancerGUI.NextVerticalArea(ref area);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a080cd63060e68249ad553cae3671b62
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/DelegateGUI.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,139 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only] A custom GUI for <see cref="FadeGroup"/>.</summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/FadeGroupDrawer
|
||||
[CustomGUI(typeof(FadeGroup))]
|
||||
public class FadeGroupDrawer : CustomGUI<FadeGroup>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private bool _IsExpanded;
|
||||
private AnimationCurve _DisplayCurve;
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
{
|
||||
_IsExpanded = EditorGUILayout.Foldout(_IsExpanded, "", true);
|
||||
|
||||
var area = GUILayoutUtility.GetLastRect();
|
||||
|
||||
InitializeDisplayCurve(ref _DisplayCurve);
|
||||
|
||||
_DisplayCurve = EditorGUI.CurveField(area, TargetName, _DisplayCurve);
|
||||
|
||||
if (_IsExpanded)
|
||||
DoDetailsGUI();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The display name of the target.</summary>
|
||||
protected virtual string TargetName
|
||||
{
|
||||
get
|
||||
{
|
||||
var name = Value.GetType().GetNameCS(false);
|
||||
if (!Value.IsValid)
|
||||
name += " (Cancelled)";
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private static readonly Keyframe[] DisplayCurveKeyframes = new Keyframe[16];
|
||||
|
||||
/// <summary>Initializes the `curve` to represent the target's fade values over normalized time.</summary>
|
||||
protected virtual void InitializeDisplayCurve(ref AnimationCurve curve)
|
||||
{
|
||||
curve ??= new();
|
||||
|
||||
try
|
||||
{
|
||||
var increment = 1f / (DisplayCurveKeyframes.Length - 1);
|
||||
for (int i = 0; i < DisplayCurveKeyframes.Length; i++)
|
||||
{
|
||||
var progress = increment * i;
|
||||
|
||||
var weight = Value.Easing != null
|
||||
? Value.Easing(progress)
|
||||
: progress;
|
||||
|
||||
DisplayCurveKeyframes[i] = new(progress, weight);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
Array.Clear(DisplayCurveKeyframes, 0, DisplayCurveKeyframes.Length);
|
||||
}
|
||||
|
||||
curve.keys = DisplayCurveKeyframes;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the GUI for the target's fields.</summary>
|
||||
protected virtual void DoDetailsGUI()
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Value.NormalizedTime = EditorGUILayout.Slider("Normalized Time", Value.NormalizedTime, 0, 1);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Value.NormalizedTime = Mathf.Clamp(Value.NormalizedTime, 0, 0.99f);
|
||||
Value.ApplyWeights();
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var fadeDuration = EditorGUILayout.FloatField("Fade Duration", Value.FadeDuration);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Value.FadeDuration = fadeDuration;
|
||||
|
||||
EditorGUILayout.LabelField(
|
||||
Value.TargetWeight > 0 ? "Fade In" : "Fade Out",
|
||||
"To " + Value.TargetWeight);
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
DoNodeWeightGUI(Value.FadeIn);
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
var fadeOutCount = Value.FadeOut.Count;
|
||||
if (fadeOutCount > 0)
|
||||
{
|
||||
EditorGUILayout.LabelField("Fade Out", fadeOutCount.ToStringCached());
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
for (int i = 0; i < fadeOutCount; i++)
|
||||
DoNodeWeightGUI(Value.FadeOut[i]);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the GUI for the given `nodeWeight`.</summary>
|
||||
private void DoNodeWeightGUI(NodeWeight nodeWeight)
|
||||
{
|
||||
EditorGUILayout.LabelField(nodeWeight.Node?.GetPath());
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbc29ff53b377cc498adb3c6cad4ec5a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/FadeGroupDrawer.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,43 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only]
|
||||
/// A default <see cref="ICustomGUI"/> which simply draws the <see cref="object.ToString"/>.
|
||||
/// </summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/LabelGUI
|
||||
[CustomGUI(typeof(object))]
|
||||
public class LabelGUI : CustomGUI<object>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
{
|
||||
string text;
|
||||
try
|
||||
{
|
||||
text = Value != null
|
||||
? Value.ToString()
|
||||
: "Null";
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
text = exception.ToString();
|
||||
}
|
||||
|
||||
using (var value = PooledGUIContent.Acquire(text))
|
||||
EditorGUILayout.LabelField(Label, value);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 998ce5fd65a930b428fda6b0a0d9fd2d
|
||||
timeCreated: 1516751545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 293522
|
||||
packageName: Animancer Pro v8
|
||||
packageVersion: 8.2.2
|
||||
assetPath: Packages/com.kybernetik.animancer/Editor/GUI/Custom GUI/LabelGUI.cs
|
||||
uploadId: 795566
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1649432a594f0b149b2190e2160b59e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,535 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2025 Kybernetik //
|
||||
|
||||
#if UNITY_EDITOR && UNITY_IMGUI
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static Animancer.Editor.AnimancerGUI;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Animancer.Editor
|
||||
{
|
||||
/// <summary>[Editor-Only]
|
||||
/// A custom Inspector for an <see cref="AnimancerLayer"/> which sorts and exposes some of its internal values.
|
||||
/// </summary>
|
||||
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerLayerDrawer
|
||||
///
|
||||
[CustomGUI(typeof(AnimancerLayer))]
|
||||
public class AnimancerLayerDrawer : AnimancerNodeDrawer<AnimancerLayer>
|
||||
{
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The states in the target layer which have non-zero <see cref="AnimancerNode.Weight"/>.</summary>
|
||||
public readonly List<AnimancerState> ActiveStates = new();
|
||||
|
||||
/// <summary>The states in the target layer which have zero <see cref="AnimancerNode.Weight"/>.</summary>
|
||||
public readonly List<AnimancerState> InactiveStates = new();
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#region Gathering
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Initializes an editor in the list for each layer in the `graph`.</summary>
|
||||
/// <remarks>
|
||||
/// The `count` indicates the number of elements actually being used.
|
||||
/// Spare elements are kept in the list in case they need to be used again later.
|
||||
/// </remarks>
|
||||
internal static void GatherLayerEditors(
|
||||
AnimancerGraph graph,
|
||||
List<AnimancerLayerDrawer> editors,
|
||||
out int count)
|
||||
{
|
||||
count = graph.Layers.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
AnimancerLayerDrawer editor;
|
||||
if (editors.Count <= i)
|
||||
{
|
||||
editor = new();
|
||||
editors.Add(editor);
|
||||
}
|
||||
else
|
||||
{
|
||||
editor = editors[i];
|
||||
}
|
||||
|
||||
editor.GatherStates(graph.Layers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Sets the target `layer` and sorts its states and their keys into the active/inactive lists.
|
||||
/// </summary>
|
||||
private void GatherStates(AnimancerLayer layer)
|
||||
{
|
||||
Value = layer;
|
||||
|
||||
ActiveStates.Clear();
|
||||
InactiveStates.Clear();
|
||||
|
||||
foreach (var state in layer)
|
||||
{
|
||||
if (state.IsActive ||
|
||||
(!AnimancerGraphDrawer.SeparateActiveFromInactiveStates && AnimancerGraphDrawer.ShowInactiveStates))
|
||||
{
|
||||
ActiveStates.Add(state);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AnimancerGraphDrawer.ShowInactiveStates)
|
||||
InactiveStates.Add(state);
|
||||
}
|
||||
|
||||
SortAndGatherKeys(ActiveStates);
|
||||
SortAndGatherKeys(InactiveStates);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Sorts any entries that use another state as their key to come right after that state.
|
||||
/// See <see cref="AnimancerLayer.Play(AnimancerState, float, FadeMode)"/>.
|
||||
/// </summary>
|
||||
private static void SortAndGatherKeys(List<AnimancerState> states)
|
||||
{
|
||||
var count = states.Count;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
AnimancerGraphDrawer.ApplySortStatesByName(states);
|
||||
|
||||
// Sort any states that use another state as their key to be right after the key.
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var state = states[i];
|
||||
var key = state.Key;
|
||||
|
||||
if (key is not AnimancerState keyState)
|
||||
continue;
|
||||
|
||||
var keyStateIndex = states.IndexOf(keyState);
|
||||
if (keyStateIndex < 0 || keyStateIndex + 1 == i)
|
||||
continue;
|
||||
|
||||
states.RemoveAt(i);
|
||||
|
||||
if (keyStateIndex < i)
|
||||
keyStateIndex++;
|
||||
|
||||
states.Insert(keyStateIndex, state);
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws the layer's name and weight.</summary>
|
||||
protected override void DoLabelGUI(Rect area)
|
||||
{
|
||||
var label = Value.IsAdditive ? "Additive" : "Override";
|
||||
if (Value._Mask != null)
|
||||
label = $"{label} ({Value._Mask.GetCachedName()})";
|
||||
|
||||
area.xMin += FoldoutIndent;
|
||||
|
||||
DoWeightLabel(ref area, Value.Weight, Value.EffectiveWeight);
|
||||
|
||||
EditorGUIUtility.labelWidth -= FoldoutIndent;
|
||||
EditorGUI.LabelField(area, Value.ToString(), label);
|
||||
EditorGUIUtility.labelWidth += FoldoutIndent;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>The number of pixels of indentation required to fit the foldout arrow.</summary>
|
||||
const float FoldoutIndent = 12;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DoFoldoutGUI(Rect area)
|
||||
{
|
||||
var hierarchyMode = EditorGUIUtility.hierarchyMode;
|
||||
EditorGUIUtility.hierarchyMode = true;
|
||||
|
||||
area.xMin += FoldoutIndent;
|
||||
IsExpanded = EditorGUI.Foldout(area, IsExpanded, GUIContent.none, true);
|
||||
|
||||
EditorGUIUtility.hierarchyMode = hierarchyMode;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DoDetailsGUI()
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
base.DoDetailsGUI();
|
||||
|
||||
if (IsExpanded)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(FoldoutIndent);
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
DoLayerDetailsGUI();
|
||||
DoNodeDetailsGUI();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
DoStatesGUI();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// Draws controls for <see cref="AnimancerLayer.IsAdditive"/> and <see cref="AnimancerLayer._Mask"/>.
|
||||
/// </summary>
|
||||
private void DoLayerDetailsGUI()
|
||||
{
|
||||
var area = LayoutSingleLineRect(SpacingMode.Before);
|
||||
area = EditorGUI.IndentedRect(area);
|
||||
area.xMin += ExtraLeftPadding;
|
||||
|
||||
var labelWidth = EditorGUIUtility.labelWidth;
|
||||
var indentLevel = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 0;
|
||||
|
||||
var additiveLabel = "Is Additive";
|
||||
|
||||
var additiveWidth = GUI.skin.toggle.CalculateWidth(additiveLabel) + StandardSpacing * 2;
|
||||
var additiveArea = StealFromLeft(ref area, additiveWidth, StandardSpacing);
|
||||
var maskArea = area;
|
||||
|
||||
// Additive.
|
||||
EditorGUIUtility.labelWidth = CalculateLabelWidth(additiveLabel);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var isAdditive = EditorGUI.Toggle(additiveArea, additiveLabel, Value.IsAdditive);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Value.IsAdditive = isAdditive;
|
||||
|
||||
// Mask.
|
||||
using (var label = PooledGUIContent.Acquire("Mask"))
|
||||
{
|
||||
EditorGUIUtility.labelWidth = CalculateLabelWidth(label.text);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
var mask = DoObjectFieldGUI(maskArea, label, Value.Mask, false);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Value.Mask = mask;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel = indentLevel;
|
||||
EditorGUIUtility.labelWidth = labelWidth;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private void DoStatesGUI()
|
||||
{
|
||||
if (!AnimancerGraphDrawer.ShowInactiveStates)
|
||||
{
|
||||
DoStatesGUI("Active States", ActiveStates);
|
||||
}
|
||||
else if (AnimancerGraphDrawer.SeparateActiveFromInactiveStates)
|
||||
{
|
||||
DoStatesGUI("Active States", ActiveStates);
|
||||
DoStatesGUI("Inactive States", InactiveStates);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoStatesGUI("States", ActiveStates);
|
||||
}
|
||||
|
||||
if (Value.Weight != 0 &&
|
||||
!Value.IsAdditive &&
|
||||
!Mathf.Approximately(Value.GetTotalChildWeight(), 1))
|
||||
{
|
||||
var message =
|
||||
"The total Weight of all states in this layer does not equal 1" +
|
||||
" which will likely give undesirable results.";
|
||||
|
||||
if (AreAllStatesFadingOut())
|
||||
message +=
|
||||
" If you no longer want anything playing on a layer," +
|
||||
" you should fade out that layer instead of fading out its states.";
|
||||
|
||||
message += " Click here for more information.";
|
||||
|
||||
EditorGUILayout.HelpBox(message, MessageType.Warning);
|
||||
|
||||
if (TryUseClickEventInLastRect())
|
||||
EditorUtility.OpenWithDefaultApp(Strings.DocsURLs.Layers);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Are all the target's states fading out to 0?</summary>
|
||||
private bool AreAllStatesFadingOut()
|
||||
{
|
||||
var count = Value.ActiveStates.Count;
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var state = Value.ActiveStates[i];
|
||||
if (state.TargetWeight != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws all `states` in the given list.</summary>
|
||||
public void DoStatesGUI(string label, List<AnimancerState> states)
|
||||
{
|
||||
var area = LayoutSingleLineRect();
|
||||
|
||||
const string Label = "Weight";
|
||||
var width = CalculateLabelWidth(Label);
|
||||
GUI.Label(StealFromRight(ref area, width), Label);
|
||||
|
||||
EditorGUI.LabelField(area, label, states.Count.ToStringCached());
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
for (int i = 0; i < states.Count; i++)
|
||||
{
|
||||
DoStateGUI(states[i]);
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Cached Inspectors that have already been created for states.</summary>
|
||||
private readonly Dictionary<AnimancerState, ICustomGUI>
|
||||
StateInspectors = new();
|
||||
|
||||
/// <summary>Draws the Inspector for the given `state`.</summary>
|
||||
private void DoStateGUI(AnimancerState state)
|
||||
{
|
||||
if (!StateInspectors.TryGetValue(state, out var inspector))
|
||||
{
|
||||
inspector = CustomGUIFactory.GetOrCreateForObject(state);
|
||||
StateInspectors.Add(state, inspector);
|
||||
}
|
||||
|
||||
inspector?.DoGUI();
|
||||
DoChildStatesGUI(state);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>Draws all child states of the `state`.</summary>
|
||||
private void DoChildStatesGUI(AnimancerState state)
|
||||
{
|
||||
if (!state._IsInspectorExpanded)
|
||||
return;
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
foreach (var child in state)
|
||||
if (child != null)
|
||||
DoStateGUI(child);
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void DoHeaderGUI()
|
||||
{
|
||||
if (!AnimancerGraphDrawer.ShowSingleLayerHeader &&
|
||||
Value.Graph.Layers.Count == 1 &&
|
||||
Value.Weight == 1 &&
|
||||
Value.TargetWeight == 1 &&
|
||||
Value.Speed == 1 &&
|
||||
!Value.IsAdditive &&
|
||||
Value._Mask == null &&
|
||||
Value.Graph.Component != null &&
|
||||
Value.Graph.Component.Animator != null &&
|
||||
Value.Graph.Component.Animator.runtimeAnimatorController == null)
|
||||
return;
|
||||
|
||||
base.DoHeaderGUI();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DoGUI()
|
||||
{
|
||||
if (!Value.IsValid())
|
||||
return;
|
||||
|
||||
base.DoGUI();
|
||||
|
||||
var area = GUILayoutUtility.GetLastRect();
|
||||
HandleDragAndDropToPlay(area, Value);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="AnimationClip"/>s or <see cref="IAnimationClipSource"/>s are dropped inside the `dropArea`,
|
||||
/// this method creates a new state in the `target` for each animation.
|
||||
/// </summary>
|
||||
public static void HandleDragAndDropToPlay(Rect area, object layerOrGraph)
|
||||
{
|
||||
if (layerOrGraph == null)
|
||||
return;
|
||||
|
||||
_DragAndDropPlayTarget = layerOrGraph;
|
||||
|
||||
_DragAndDropPlayHandler ??= HandleDragAndDropToPlay;
|
||||
_DragAndDropPlayHandler.Handle(area);
|
||||
|
||||
_DragAndDropPlayTarget = null;
|
||||
}
|
||||
|
||||
private static DragAndDropHandler<Object> _DragAndDropPlayHandler;
|
||||
private static object _DragAndDropPlayTarget;
|
||||
|
||||
private static AnimancerLayer DragAndDropPlayTargetLayer
|
||||
=> _DragAndDropPlayTarget as AnimancerLayer
|
||||
?? (_DragAndDropPlayTarget is AnimancerGraph graph ? graph.Layers[0] : null);
|
||||
|
||||
/// <summary>Handles drag and drop events to play animations and transitions.</summary>
|
||||
public static bool HandleDragAndDropToPlay(Object obj, bool isDrop)
|
||||
{
|
||||
if (_DragAndDropPlayTarget == null)
|
||||
return false;
|
||||
|
||||
if (obj is AnimationClip clip)
|
||||
{
|
||||
if (clip.legacy)
|
||||
return false;
|
||||
|
||||
if (isDrop)
|
||||
DragAndDropPlayTargetLayer.Play(clip);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj is ITransition transition)
|
||||
{
|
||||
if (isDrop)
|
||||
DragAndDropPlayTargetLayer.Play(transition);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var transitionAsset = TryCreateTransitionAttribute.TryCreateTransitionAsset(obj);
|
||||
if (transitionAsset != null)
|
||||
{
|
||||
if (isDrop)
|
||||
DragAndDropPlayTargetLayer.Play(transitionAsset);
|
||||
|
||||
if (!EditorUtility.IsPersistent(transitionAsset))
|
||||
Object.DestroyImmediate(transitionAsset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
using (ListPool<AnimationClip>.Instance.Acquire(out var clips))
|
||||
{
|
||||
clips.GatherFromSource(obj);
|
||||
|
||||
var anyValid = false;
|
||||
|
||||
for (int i = 0; i < clips.Count; i++)
|
||||
{
|
||||
clip = clips[i];
|
||||
if (clip.legacy)
|
||||
continue;
|
||||
|
||||
if (!isDrop)
|
||||
return true;
|
||||
|
||||
anyValid = true;
|
||||
DragAndDropPlayTargetLayer.Play(clip);
|
||||
|
||||
}
|
||||
|
||||
if (anyValid)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#region Context Menu
|
||||
/************************************************************************************************************************/
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void PopulateContextMenu(GenericMenu menu)
|
||||
{
|
||||
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.CurrentState)}: {Value.CurrentState}"));
|
||||
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.CommandCount)}: {Value.CommandCount}"));
|
||||
|
||||
menu.AddFunction("Stop",
|
||||
HasAnyStates((state) => state.IsPlaying || state.Weight != 0),
|
||||
() => Value.Stop());
|
||||
|
||||
AnimancerEditorUtilities.AddFadeFunction(menu, "Fade In",
|
||||
Value.Index > 0 && Value.Weight != 1, Value,
|
||||
(duration) => Value.StartFade(1, duration));
|
||||
AnimancerEditorUtilities.AddFadeFunction(menu, "Fade Out",
|
||||
Value.Index > 0 && Value.Weight != 0, Value,
|
||||
(duration) => Value.StartFade(0, duration));
|
||||
|
||||
AnimancerNodeBase.AddContextMenuIK(menu, Value);
|
||||
|
||||
menu.AddSeparator("");
|
||||
|
||||
menu.AddFunction("Destroy States",
|
||||
ActiveStates.Count > 0 || InactiveStates.Count > 0,
|
||||
() => Value.DestroyStates());
|
||||
|
||||
AnimancerGraphDrawer.AddRootFunctions(menu, Value.Graph);
|
||||
|
||||
menu.AddSeparator("");
|
||||
|
||||
AnimancerGraphDrawer.AddDisplayOptions(menu);
|
||||
|
||||
AnimancerEditorUtilities.AddDocumentationLink(menu, "Layer Documentation", Strings.DocsURLs.Layers);
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
||||
private bool HasAnyStates(Func<AnimancerState, bool> condition)
|
||||
{
|
||||
foreach (var state in Value)
|
||||
{
|
||||
if (condition(state))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
#endregion
|
||||
/************************************************************************************************************************/
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user