commit 092217ca7356691611e222f992ed0e5230df1a9b Author: wlt233 <1486185683@qq.com> Date: Thu Nov 17 18:03:57 2022 +0800 init (v0.1) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..945fa34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ + +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ +[Bb]uild/ +[Bb]uilds/ +Assets/AssetStoreTools* + +# Visual Studio cache directory +.vs/ + +# Visual Studio Code cache directory +.vscode/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +Logs/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.opendb + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta + +# Unity3D Generated File On Crash Reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage \ No newline at end of file diff --git a/Assets/Resources.meta b/Assets/Resources.meta new file mode 100644 index 0000000..bf851d0 --- /dev/null +++ b/Assets/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c11a242431890cd439b0b70ce5cd4cf9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge.meta b/Assets/Resources/huge.meta new file mode 100644 index 0000000..70b2ddf --- /dev/null +++ b/Assets/Resources/huge.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f856848c84c89de45b4c4caa95df966a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge/EnemySpine_01.atlas.txt b/Assets/Resources/huge/EnemySpine_01.atlas.txt new file mode 100644 index 0000000..b87cb91 --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01.atlas.txt @@ -0,0 +1,174 @@ + +EnemySpine_01.png +size: 1024,1024 +format: RGBA4444 +filter: Linear,Linear +repeat: none +ArmCenterA + rotate: true + xy: 259, 674 + size: 192, 372 + orig: 192, 372 + offset: 0, 0 + index: -1 +ArmCenterB1 + rotate: false + xy: 535, 40 + size: 107, 228 + orig: 107, 228 + offset: 0, 0 + index: -1 +ArmCenterB2 + rotate: false + xy: 231, 259 + size: 137, 392 + orig: 137, 392 + offset: 0, 0 + index: -1 +ArmCenterEffectEVO + rotate: false + xy: 741, 115 + size: 49, 52 + orig: 49, 52 + offset: 0, 0 + index: -1 +ArmLeftA + rotate: false + xy: 1, 259 + size: 229, 392 + orig: 229, 392 + offset: 0, 0 + index: -1 +ArmLeftB1 + rotate: false + xy: 843, 376 + size: 118, 232 + orig: 118, 232 + offset: 0, 0 + index: -1 +ArmLeftB2 + rotate: false + xy: 558, 269 + size: 115, 404 + orig: 115, 404 + offset: 0, 0 + index: -1 +ArmLeftEffectEVO + rotate: false + xy: 933, 772 + size: 55, 54 + orig: 55, 54 + offset: 0, 0 + index: -1 +ArmRightA + rotate: false + xy: 369, 319 + size: 188, 354 + orig: 188, 354 + offset: 0, 0 + index: -1 +ArmRightB1 + rotate: false + xy: 643, 38 + size: 97, 230 + orig: 97, 230 + offset: 0, 0 + index: -1 +ArmRightB2 + rotate: true + xy: 259, 867 + size: 156, 412 + orig: 156, 412 + offset: 0, 0 + index: -1 +ArmRightEffectEVO + rotate: false + xy: 413, 31 + size: 51, 52 + orig: 51, 52 + offset: 0, 0 + index: -1 +BodyA + rotate: false + xy: 1, 652 + size: 257, 371 + orig: 257, 371 + offset: 0, 0 + index: -1 +BodyLowerBackEVO + rotate: false + xy: 674, 609 + size: 255, 160 + orig: 255, 160 + offset: 0, 0 + index: -1 +BodyLowerEVO + rotate: true + xy: 252, 1 + size: 257, 160 + orig: 257, 160 + offset: 0, 0 + index: -1 +BodyLowerEffectEVO + rotate: false + xy: 741, 168 + size: 91, 106 + orig: 91, 106 + offset: 0, 0 + index: -1 +BodyLowerOpEVO + rotate: false + xy: 674, 378 + size: 168, 230 + orig: 168, 230 + offset: 0, 0 + index: -1 +BodyOpFrontA + rotate: false + xy: 413, 84 + size: 121, 234 + orig: 121, 234 + offset: 0, 0 + index: -1 +BodyUpperBackEVO + rotate: true + xy: 1, 1 + size: 257, 250 + orig: 257, 250 + offset: 0, 0 + index: -1 +BodyUpperEVO + rotate: false + xy: 672, 770 + size: 260, 253 + orig: 260, 253 + offset: 0, 0 + index: -1 +BodyUpperTentacleAEVO + rotate: true + xy: 933, 827 + size: 196, 74 + orig: 196, 74 + offset: 0, 0 + index: -1 +Eye + rotate: false + xy: 632, 843 + size: 23, 23 + orig: 23, 23 + offset: 0, 0 + index: -1 +HeadHat + rotate: true + xy: 674, 277 + size: 100, 140 + orig: 100, 140 + offset: 0, 0 + index: -1 +HeadHatEVO + rotate: true + xy: 815, 275 + size: 100, 140 + orig: 100, 140 + offset: 0, 0 + index: -1 diff --git a/Assets/Resources/huge/EnemySpine_01.atlas.txt.meta b/Assets/Resources/huge/EnemySpine_01.atlas.txt.meta new file mode 100644 index 0000000..86bcf29 --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01.atlas.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c235d8d59cefbcf40b382573b3b3a148 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge/EnemySpine_01.png b/Assets/Resources/huge/EnemySpine_01.png new file mode 100644 index 0000000..978fcde Binary files /dev/null and b/Assets/Resources/huge/EnemySpine_01.png differ diff --git a/Assets/Resources/huge/EnemySpine_01.png.meta b/Assets/Resources/huge/EnemySpine_01.png.meta new file mode 100644 index 0000000..6a98cf3 --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: f0179b89d3c5372469fd5eed3f088172 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + 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: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: bad5d089b3ad69c489d990a9528d4f2f + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge/EnemySpine_01.skel.bytes b/Assets/Resources/huge/EnemySpine_01.skel.bytes new file mode 100644 index 0000000..ad50b88 Binary files /dev/null and b/Assets/Resources/huge/EnemySpine_01.skel.bytes differ diff --git a/Assets/Resources/huge/EnemySpine_01.skel.bytes.meta b/Assets/Resources/huge/EnemySpine_01.skel.bytes.meta new file mode 100644 index 0000000..e3c8b45 --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01.skel.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8832d7fa69024664cb8a4a2944e048aa +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge/EnemySpine_01_Atlas.asset b/Assets/Resources/huge/EnemySpine_01_Atlas.asset new file mode 100644 index 0000000..4fa9102 --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01_Atlas.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: a6b194f808b1af6499c93410e504af42, type: 3} + m_Name: EnemySpine_01_Atlas + m_EditorClassIdentifier: + atlasFile: {fileID: 4900000, guid: c235d8d59cefbcf40b382573b3b3a148, type: 3} + materials: + - {fileID: 2100000, guid: 9b8fad0533e1ea64ca4ca2273a08d2c8, type: 2} diff --git a/Assets/Resources/huge/EnemySpine_01_Atlas.asset.meta b/Assets/Resources/huge/EnemySpine_01_Atlas.asset.meta new file mode 100644 index 0000000..be8ebce --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01_Atlas.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e77f14b977183584d96496d29fdecf2c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge/EnemySpine_01_Material.mat b/Assets/Resources/huge/EnemySpine_01_Material.mat new file mode 100644 index 0000000..0e99bcc --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01_Material.mat @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: EnemySpine_01_Material + m_Shader: {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _AlphaTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: f0179b89d3c5372469fd5eed3f088172, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - PixelSnap: 0 + - _Cutoff: 0.1 + - _EnableExternalAlpha: 0 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _Flip: {r: 1, g: 1, b: 1, a: 1} + - _RendererColor: {r: 1, g: 1, b: 1, a: 1} diff --git a/Assets/Resources/huge/EnemySpine_01_Material.mat.meta b/Assets/Resources/huge/EnemySpine_01_Material.mat.meta new file mode 100644 index 0000000..e13104f --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01_Material.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9b8fad0533e1ea64ca4ca2273a08d2c8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge/EnemySpine_01_SkeletonData.asset b/Assets/Resources/huge/EnemySpine_01_SkeletonData.asset new file mode 100644 index 0000000..ea22c68 --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01_SkeletonData.asset @@ -0,0 +1,23 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: f1b3b4b945939a54ea0b23d3396115fb, type: 3} + m_Name: EnemySpine_01_SkeletonData + m_EditorClassIdentifier: + atlasAssets: + - {fileID: 11400000, guid: e77f14b977183584d96496d29fdecf2c, type: 2} + scale: 0.01 + skeletonJSON: {fileID: 4900000, guid: 8832d7fa69024664cb8a4a2944e048aa, type: 3} + fromAnimation: [] + toAnimation: [] + duration: [] + defaultMix: 0.2 + controller: {fileID: 0} diff --git a/Assets/Resources/huge/EnemySpine_01_SkeletonData.asset.meta b/Assets/Resources/huge/EnemySpine_01_SkeletonData.asset.meta new file mode 100644 index 0000000..0f5600d --- /dev/null +++ b/Assets/Resources/huge/EnemySpine_01_SkeletonData.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f60a4f2675cf5d4d9f93d67a100ebb3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/huge_pet_icon.png b/Assets/Resources/huge_pet_icon.png new file mode 100644 index 0000000..225fbe7 Binary files /dev/null and b/Assets/Resources/huge_pet_icon.png differ diff --git a/Assets/Resources/huge_pet_icon.png.meta b/Assets/Resources/huge_pet_icon.png.meta new file mode 100644 index 0000000..6f39cda --- /dev/null +++ b/Assets/Resources/huge_pet_icon.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 47122c817d8857b4fb0b2b7d336c1823 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 2e6e2b584e641bf4ab81899770fdadea + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes.meta b/Assets/Scenes.meta new file mode 100644 index 0000000..391653c --- /dev/null +++ b/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b3816b9db8dcc34c9dbb4c4c30d2b66 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/main.unity b/Assets/Scenes/main.unity new file mode 100644 index 0000000..85d401f --- /dev/null +++ b/Assets/Scenes/main.unity @@ -0,0 +1,582 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &294664096 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 294664098} + - component: {fileID: 294664097} + - component: {fileID: 294664099} + - component: {fileID: 294664100} + - component: {fileID: 294664101} + - component: {fileID: 294664102} + - component: {fileID: 294664103} + m_Layer: 0 + m_Name: huge + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!50 &294664097 +Rigidbody2D: + serializedVersion: 4 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_BodyType: 0 + m_Simulated: 1 + m_UseFullKinematicContacts: 0 + m_UseAutoMass: 0 + m_Mass: 1 + m_LinearDrag: 1 + m_AngularDrag: 0.05 + m_GravityScale: 1 + m_Material: {fileID: 0} + m_Interpolate: 0 + m_SleepingMode: 1 + m_CollisionDetection: 0 + m_Constraints: 0 +--- !u!224 &294664098 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 901134313} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!61 &294664099 +BoxCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_Enabled: 0 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + m_SpriteTilingProperty: + border: {x: 0, y: 0, z: 0, w: 0} + pivot: {x: 0, y: 0} + oldSize: {x: 0, y: 0} + newSize: {x: 0, y: 0} + adaptiveTilingThreshold: 0 + drawMode: 0 + adaptiveTiling: 0 + m_AutoTiling: 0 + serializedVersion: 2 + m_Size: {x: 2, y: 2} + m_EdgeRadius: 0 +--- !u!114 &294664100 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ee19b8dc16b484546b9ddbf1869a9c7e, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &294664101 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 27cd781cc90caf84a8c2dcda96a6baaf, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!58 &294664102 +CircleCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_Enabled: 0 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + serializedVersion: 2 + m_Radius: 1 +--- !u!60 &294664103 +PolygonCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 294664096} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + m_SpriteTilingProperty: + border: {x: 0, y: 0, z: 0, w: 0} + pivot: {x: 0, y: 0} + oldSize: {x: 0, y: 0} + newSize: {x: 0, y: 0} + adaptiveTilingThreshold: 0 + drawMode: 0 + adaptiveTiling: 0 + m_AutoTiling: 0 + m_Points: + m_Paths: + - - {x: 0.4, y: 1} + - {x: 1, y: 0.3} + - {x: 1, y: -1} + - {x: -1, y: -1} + - {x: -1, y: 0.3} + - {x: -0.4, y: 1} +--- !u!1 &519420028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519420032} + - component: {fileID: 519420031} + - component: {fileID: 519420029} + - component: {fileID: 519420030} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &519420029 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 +--- !u!114 &519420030 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9edbed9fcddfca44c80abfb98a5659da, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!20 &519420031 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} + m_projectionMatrixMode: 1 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_GateFitMode: 2 + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 9 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 0 + m_HDR: 1 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &519420032 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &901134309 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 901134313} + - component: {fileID: 901134312} + - component: {fileID: 901134311} + - component: {fileID: 901134310} + m_Layer: 0 + m_Name: EnemySpine_01 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &901134310 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 901134309} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d247ba06193faa74d9335f5481b2b56c, type: 3} + m_Name: + m_EditorClassIdentifier: + skeletonDataAsset: {fileID: 11400000, guid: 8f60a4f2675cf5d4d9f93d67a100ebb3, type: 2} + initialSkinName: default + initialFlipX: 0 + initialFlipY: 0 + separatorSlotNames: [] + zSpacing: 0 + useClipping: 1 + immutableTriangles: 0 + pmaVertexColors: 1 + clearStateOnDisable: 0 + tintBlack: 0 + singleSubmesh: 0 + addNormals: 0 + calculateTangents: 0 + logErrors: 0 + disableRenderingOnOverride: 1 + _animationName: Wait_A02 + loop: 1 + timeScale: 1 +--- !u!23 &901134311 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 901134309} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 9b8fad0533e1ea64ca4ca2273a08d2c8, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &901134312 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 901134309} + m_Mesh: {fileID: 0} +--- !u!4 &901134313 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 901134309} + m_LocalRotation: {x: 0, y: 0, z: 0.043619405, w: 0.9990483} + m_LocalPosition: {x: 0, y: -0.9, z: 0} + m_LocalScale: {x: 0.2, y: 0.2, z: 1} + m_Children: [] + m_Father: {fileID: 294664098} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 5} +--- !u!1 &1373814920 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1373814922} + - component: {fileID: 1373814921} + m_Layer: 0 + m_Name: edge1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!68 &1373814921 +EdgeCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1373814920} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + m_EdgeRadius: 0 + m_Points: + - {x: 17, y: 10} + - {x: 17, y: -10} + - {x: -17, y: -10} + - {x: -17, y: 10} + - {x: 17, y: 10} +--- !u!4 &1373814922 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1373814920} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2133973752 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2133973754} + - component: {fileID: 2133973753} + m_Layer: 0 + m_Name: edge + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!68 &2133973753 +EdgeCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2133973752} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + m_EdgeRadius: 0 + m_Points: + - {x: 16, y: 9} + - {x: 16, y: -9} + - {x: -16, y: -9} + - {x: -16, y: 9} + - {x: 16, y: 9} +--- !u!4 &2133973754 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2133973752} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scenes/main.unity.meta b/Assets/Scenes/main.unity.meta new file mode 100644 index 0000000..e3a4519 --- /dev/null +++ b/Assets/Scenes/main.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 73a8e2058e32f95459116a81c34f16d2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine.meta b/Assets/Spine.meta new file mode 100644 index 0000000..27f2680 --- /dev/null +++ b/Assets/Spine.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 30918bcaadaaecc42bc215ff52f75b21 +folderAsset: yes +timeCreated: 1488288531 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp.meta b/Assets/Spine/spine-csharp.meta new file mode 100644 index 0000000..81ebc4e --- /dev/null +++ b/Assets/Spine/spine-csharp.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a60dd41ef13d98647b9f963089feb7b0 +folderAsset: yes +timeCreated: 1456265151 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Animation.cs b/Assets/Spine/spine-csharp/Animation.cs new file mode 100644 index 0000000..28a9a9a --- /dev/null +++ b/Assets/Spine/spine-csharp/Animation.cs @@ -0,0 +1,1395 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + public class Animation { + internal ExposedList timelines; + internal float duration; + internal String name; + + public string Name { get { return name; } } + public ExposedList Timelines { get { return timelines; } set { timelines = value; } } + public float Duration { get { return duration; } set { duration = value; } } + + public Animation (string name, ExposedList timelines, float duration) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null."); + this.name = name; + this.timelines = timelines; + this.duration = duration; + } + + /// Applies all the animation's timelines to the specified skeleton. + /// + public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events, float alpha, MixPose pose, MixDirection direction) { + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + + if (loop && duration != 0) { + time %= duration; + if (lastTime > 0) lastTime %= duration; + } + + ExposedList timelines = this.timelines; + for (int i = 0, n = timelines.Count; i < n; i++) + timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, pose, direction); + } + + /// After the first and before the last entry. + internal static int BinarySearch (float[] values, float target, int step) { + int low = 0; + int high = values.Length / step - 2; + if (high == 0) return step; + int current = (int)((uint)high >> 1); + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (int)((uint)(low + high) >> 1); + } + } + + /// After the first and before the last entry. + internal static int BinarySearch (float[] values, float target) { + int low = 0; + int high = values.Length - 2; + if (high == 0) return 1; + int current = (int)((uint)high >> 1); + while (true) { + if (values[(current + 1)] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1); + current = (int)((uint)(low + high) >> 1); + } + } + + internal static int LinearSearch (float[] values, float target, int step) { + for (int i = 0, last = values.Length - step; i <= last; i += step) + if (values[i] > target) return i; + return -1; + } + } + + public interface Timeline { + /// Sets the value(s) for the specified time. + /// The skeleton the timeline is being applied to. This provides access to the bones, slots, and other skeleton components the timeline may change. + /// lastTime The time this timeline was last applied. Timelines such as EventTimeline trigger only at specific times rather than every frame. In that case, the timeline triggers everything between lastTime (exclusive) and time (inclusive). + /// The time within the animation. Most timelines find the key before and the key after this time so they can interpolate between the keys. + /// If any events are fired, they are added to this list. Can be null to ignore firing events or if the timeline does not fire events. May be null. + /// alpha 0 applies the current or setup pose value (depending on pose parameter). 1 applies the timeline + /// value. Between 0 and 1 applies a value between the current or setup pose and the timeline value. By adjusting + /// alpha over time, an animation can be mixed in or out. alpha can also be useful to + /// apply animations on top of each other (layered). + /// Controls how mixing is applied when alpha is than 1. + /// Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions such as DrawOrderTimeline and AttachmentTimeline. + void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha, MixPose pose, MixDirection direction); + int PropertyId { get; } + } + + /// + /// Controls how a timeline is mixed with the setup or current pose. + /// + public enum MixPose { + /// The timeline value is mixed with the setup pose (the current pose is not used). + Setup, + /// The timeline value is mixed with the current pose. The setup pose is used as the timeline value before the first key, + /// except for timelines which perform instant transitions, such as DrawOrderTimeline or AttachmentTimeline. + Current, + /// The timeline value is mixed with the current pose. No change is made before the first key (the current pose is kept until the first key). + CurrentLayered + } + + /// + /// Indicates whether a timeline's alpha is mixing out over time toward 0 (the setup or current pose) or mixing in toward 1 (the timeline's pose). + /// + public enum MixDirection { + In, + Out + } + + internal enum TimelineType { + Rotate = 0, Translate, Scale, Shear, // + Attachment, Color, Deform, // + Event, DrawOrder, // + IkConstraint, TransformConstraint, // + PathConstraintPosition, PathConstraintSpacing, PathConstraintMix, // + TwoColor + } + + /// Base class for frames that use an interpolation bezier curve. + abstract public class CurveTimeline : Timeline { + protected const float LINEAR = 0, STEPPED = 1, BEZIER = 2; + protected const int BEZIER_SIZE = 10 * 2 - 1; + + internal float[] curves; // type, x, y, ... + public int FrameCount { get { return curves.Length / BEZIER_SIZE + 1; } } + + public CurveTimeline (int frameCount) { + if (frameCount <= 0) throw new ArgumentException("frameCount must be > 0: " + frameCount, "frameCount"); + curves = new float[(frameCount - 1) * BEZIER_SIZE]; + } + + abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction); + + abstract public int PropertyId { get; } + + public void SetLinear (int frameIndex) { + curves[frameIndex * BEZIER_SIZE] = LINEAR; + } + + public void SetStepped (int frameIndex) { + curves[frameIndex * BEZIER_SIZE] = STEPPED; + } + + /// Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + /// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + /// the difference between the keyframe's values. + public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) { + float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f; + float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f; + float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; + float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f; + + int i = frameIndex * BEZIER_SIZE; + float[] curves = this.curves; + curves[i++] = BEZIER; + + float x = dfx, y = dfy; + for (int n = i + BEZIER_SIZE - 1; i < n; i += 2) { + curves[i] = x; + curves[i + 1] = y; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + } + + public float GetCurvePercent (int frameIndex, float percent) { + percent = MathUtils.Clamp (percent, 0, 1); + float[] curves = this.curves; + int i = frameIndex * BEZIER_SIZE; + float type = curves[i]; + if (type == LINEAR) return percent; + if (type == STEPPED) return 0; + i++; + float x = 0; + for (int start = i, n = i + BEZIER_SIZE - 1; i < n; i += 2) { + x = curves[i]; + if (x >= percent) { + float prevX, prevY; + if (i == start) { + prevX = 0; + prevY = 0; + } else { + prevX = curves[i - 2]; + prevY = curves[i - 1]; + } + return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + } + } + float y = curves[i - 1]; + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } + public float GetCurveType (int frameIndex) { + return curves[frameIndex * BEZIER_SIZE]; + } + } + + public class RotateTimeline : CurveTimeline { + public const int ENTRIES = 2; + internal const int PREV_TIME = -2, PREV_ROTATION = -1; + internal const int ROTATION = 1; + + internal int boneIndex; + internal float[] frames; + + public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, angle, ... + + override public int PropertyId { + get { return ((int)TimelineType.Rotate << 24) + boneIndex; } + } + + public RotateTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount << 1]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, float degrees) { + frameIndex <<= 1; + frames[frameIndex] = time; + frames[frameIndex + ROTATION] = degrees; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Bone bone = skeleton.bones.Items[boneIndex]; + + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + bone.rotation = bone.data.rotation; + return; + case MixPose.Current: + float rr = bone.data.rotation - bone.rotation; + rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360; + bone.rotation += rr * alpha; + return; + } + return; + } + + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + if (pose == MixPose.Setup) { + bone.rotation = bone.data.rotation + frames[frames.Length + PREV_ROTATION] * alpha; + } else { + float rr = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation; + rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360; // Wrap within -180 and 180. + bone.rotation += rr * alpha; + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + float prevRotation = frames[frame + PREV_ROTATION]; + float frameTime = frames[frame]; + float percent = GetCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + float r = frames[frame + ROTATION] - prevRotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + r = prevRotation + r * percent; + if (pose == MixPose.Setup) { + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + bone.rotation = bone.data.rotation + r * alpha; + } else { + r = bone.data.rotation + r - bone.rotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + bone.rotation += r * alpha; + } + } + } + + public class TranslateTimeline : CurveTimeline { + public const int ENTRIES = 3; + protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1; + protected const int X = 1, Y = 2; + + internal int boneIndex; + internal float[] frames; + + public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, value, value, ... + + override public int PropertyId { + get { return ((int)TimelineType.Translate << 24) + boneIndex; } + } + + public TranslateTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, float x, float y) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + X] = x; + frames[frameIndex + Y] = y; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Bone bone = skeleton.bones.Items[boneIndex]; + + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + bone.x = bone.data.x; + bone.y = bone.data.y; + return; + case MixPose.Current: + bone.x += (bone.data.x - bone.x) * alpha; + bone.y += (bone.data.y - bone.y) * alpha; + return; + } + return; + } + + float x, y; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + x = frames[frames.Length + PREV_X]; + y = frames[frames.Length + PREV_Y]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + x = frames[frame + PREV_X]; + y = frames[frame + PREV_Y]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + x += (frames[frame + X] - x) * percent; + y += (frames[frame + Y] - y) * percent; + } + if (pose == MixPose.Setup) { + bone.x = bone.data.x + x * alpha; + bone.y = bone.data.y + y * alpha; + } else { + bone.x += (bone.data.x + x - bone.x) * alpha; + bone.y += (bone.data.y + y - bone.y) * alpha; + } + } + } + + public class ScaleTimeline : TranslateTimeline { + override public int PropertyId { + get { return ((int)TimelineType.Scale << 24) + boneIndex; } + } + + public ScaleTimeline (int frameCount) + : base(frameCount) { + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Bone bone = skeleton.bones.Items[boneIndex]; + + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + bone.scaleX = bone.data.scaleX; + bone.scaleY = bone.data.scaleY; + return; + case MixPose.Current: + bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; + return; + } + return; + } + + float x, y; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + x = frames[frames.Length + PREV_X] * bone.data.scaleX; + y = frames[frames.Length + PREV_Y] * bone.data.scaleY; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + x = frames[frame + PREV_X]; + y = frames[frame + PREV_Y]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + x = (x + (frames[frame + X] - x) * percent) * bone.data.scaleX; + y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY; + } + if (alpha == 1) { + bone.scaleX = x; + bone.scaleY = y; + } else { + float bx, by; + if (pose == MixPose.Setup) { + bx = bone.data.scaleX; + by = bone.data.scaleY; + } else { + bx = bone.scaleX; + by = bone.scaleY; + } + // Mixing out uses sign of setup or current pose, else use sign of key. + if (direction == MixDirection.Out) { + x = (x >= 0 ? x : -x) * (bx >= 0 ? 1 : -1); + y = (y >= 0 ? y : -y) * (by >= 0 ? 1 : -1); + } else { + bx = (bx >= 0 ? bx : -bx) * (x >= 0 ? 1 : -1); + by = (by >= 0 ? by : -by) * (y >= 0 ? 1 : -1); + } + bone.scaleX = bx + (x - bx) * alpha; + bone.scaleY = by + (y - by) * alpha; + } + } + } + + public class ShearTimeline : TranslateTimeline { + override public int PropertyId { + get { return ((int)TimelineType.Shear << 24) + boneIndex; } + } + + public ShearTimeline (int frameCount) + : base(frameCount) { + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Bone bone = skeleton.bones.Items[boneIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + bone.shearX = bone.data.shearX; + bone.shearY = bone.data.shearY; + return; + case MixPose.Current: + bone.shearX += (bone.data.shearX - bone.shearX) * alpha; + bone.shearY += (bone.data.shearY - bone.shearY) * alpha; + return; + } + return; + } + + float x, y; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + x = frames[frames.Length + PREV_X]; + y = frames[frames.Length + PREV_Y]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + x = frames[frame + PREV_X]; + y = frames[frame + PREV_Y]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + x = x + (frames[frame + X] - x) * percent; + y = y + (frames[frame + Y] - y) * percent; + } + if (pose == MixPose.Setup) { + bone.shearX = bone.data.shearX + x * alpha; + bone.shearY = bone.data.shearY + y * alpha; + } else { + bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha; + bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha; + } + } + } + + public class ColorTimeline : CurveTimeline { + public const int ENTRIES = 5; + protected const int PREV_TIME = -5, PREV_R = -4, PREV_G = -3, PREV_B = -2, PREV_A = -1; + protected const int R = 1, G = 2, B = 3, A = 4; + + internal int slotIndex; + internal float[] frames; + + public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, r, g, b, a, ... + + override public int PropertyId { + get { return ((int)TimelineType.Color << 24) + slotIndex; } + } + + public ColorTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, float r, float g, float b, float a) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + R] = r; + frames[frameIndex + G] = g; + frames[frameIndex + B] = b; + frames[frameIndex + A] = a; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Slot slot = skeleton.slots.Items[slotIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + var slotData = slot.data; + switch (pose) { + case MixPose.Setup: + slot.r = slotData.r; + slot.g = slotData.g; + slot.b = slotData.b; + slot.a = slotData.a; + return; + case MixPose.Current: + slot.r += (slot.r - slotData.r) * alpha; + slot.g += (slot.g - slotData.g) * alpha; + slot.b += (slot.b - slotData.b) * alpha; + slot.a += (slot.a - slotData.a) * alpha; + return; + } + return; + } + + float r, g, b, a; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + int i = frames.Length; + r = frames[i + PREV_R]; + g = frames[i + PREV_G]; + b = frames[i + PREV_B]; + a = frames[i + PREV_A]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + r = frames[frame + PREV_R]; + g = frames[frame + PREV_G]; + b = frames[frame + PREV_B]; + a = frames[frame + PREV_A]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + r += (frames[frame + R] - r) * percent; + g += (frames[frame + G] - g) * percent; + b += (frames[frame + B] - b) * percent; + a += (frames[frame + A] - a) * percent; + } + if (alpha == 1) { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } else { + float br, bg, bb, ba; + if (pose == MixPose.Setup) { + br = slot.data.r; + bg = slot.data.g; + bb = slot.data.b; + ba = slot.data.a; + } else { + br = slot.r; + bg = slot.g; + bb = slot.b; + ba = slot.a; + } + slot.r = br + ((r - br) * alpha); + slot.g = bg + ((g - bg) * alpha); + slot.b = bb + ((b - bb) * alpha); + slot.a = ba + ((a - ba) * alpha); + } + } + } + + public class TwoColorTimeline : CurveTimeline { + public const int ENTRIES = 8; + protected const int PREV_TIME = -8, PREV_R = -7, PREV_G = -6, PREV_B = -5, PREV_A = -4; + protected const int PREV_R2 = -3, PREV_G2 = -2, PREV_B2 = -1; + protected const int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7; + + internal int slotIndex; + internal float[] frames; // time, r, g, b, a, r2, g2, b2, ... + + public int SlotIndex { + get { return slotIndex; } + set { + if (value < 0) + throw new ArgumentOutOfRangeException("index must be >= 0."); + slotIndex = value; + } + } + + public float[] Frames { get { return frames; } } + + override public int PropertyId { + get { return ((int)TimelineType.TwoColor << 24) + slotIndex; } + } + + public TwoColorTimeline (int frameCount) : + base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + R] = r; + frames[frameIndex + G] = g; + frames[frameIndex + B] = b; + frames[frameIndex + A] = a; + frames[frameIndex + R2] = r2; + frames[frameIndex + G2] = g2; + frames[frameIndex + B2] = b2; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Slot slot = skeleton.slots.Items[slotIndex]; + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + var slotData = slot.data; + switch (pose) { + case MixPose.Setup: + // slot.color.set(slot.data.color); + // slot.darkColor.set(slot.data.darkColor); + slot.r = slotData.r; + slot.g = slotData.g; + slot.b = slotData.b; + slot.a = slotData.a; + slot.r2 = slotData.r2; + slot.g2 = slotData.g2; + slot.b2 = slotData.b2; + return; + case MixPose.Current: + slot.r += (slot.r - slotData.r) * alpha; + slot.g += (slot.g - slotData.g) * alpha; + slot.b += (slot.b - slotData.b) * alpha; + slot.a += (slot.a - slotData.a) * alpha; + slot.r2 += (slot.r2 - slotData.r2) * alpha; + slot.g2 += (slot.g2 - slotData.g2) * alpha; + slot.b2 += (slot.b2 - slotData.b2) * alpha; + return; + } + return; + } + + float r, g, b, a, r2, g2, b2; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + int i = frames.Length; + r = frames[i + PREV_R]; + g = frames[i + PREV_G]; + b = frames[i + PREV_B]; + a = frames[i + PREV_A]; + r2 = frames[i + PREV_R2]; + g2 = frames[i + PREV_G2]; + b2 = frames[i + PREV_B2]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + r = frames[frame + PREV_R]; + g = frames[frame + PREV_G]; + b = frames[frame + PREV_B]; + a = frames[frame + PREV_A]; + r2 = frames[frame + PREV_R2]; + g2 = frames[frame + PREV_G2]; + b2 = frames[frame + PREV_B2]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + r += (frames[frame + R] - r) * percent; + g += (frames[frame + G] - g) * percent; + b += (frames[frame + B] - b) * percent; + a += (frames[frame + A] - a) * percent; + r2 += (frames[frame + R2] - r2) * percent; + g2 += (frames[frame + G2] - g2) * percent; + b2 += (frames[frame + B2] - b2) * percent; + } + if (alpha == 1) { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + slot.r2 = r2; + slot.g2 = g2; + slot.b2 = b2; + } else { + float br, bg, bb, ba, br2, bg2, bb2; + if (pose == MixPose.Setup) { + br = slot.data.r; + bg = slot.data.g; + bb = slot.data.b; + ba = slot.data.a; + br2 = slot.data.r2; + bg2 = slot.data.g2; + bb2 = slot.data.b2; + } else { + br = slot.r; + bg = slot.g; + bb = slot.b; + ba = slot.a; + br2 = slot.r2; + bg2 = slot.g2; + bb2 = slot.b2; + } + slot.r = br + ((r - br) * alpha); + slot.g = bg + ((g - bg) * alpha); + slot.b = bb + ((b - bb) * alpha); + slot.a = ba + ((a - ba) * alpha); + slot.r2 = br2 + ((r2 - br2) * alpha); + slot.g2 = bg2 + ((g2 - bg2) * alpha); + slot.b2 = bb2 + ((b2 - bb2) * alpha); + } + } + + } + + public class AttachmentTimeline : Timeline { + internal int slotIndex; + internal float[] frames; + internal string[] attachmentNames; + + public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, ... + public string[] AttachmentNames { get { return attachmentNames; } set { attachmentNames = value; } } + public int FrameCount { get { return frames.Length; } } + + public int PropertyId { + get { return ((int)TimelineType.Attachment << 24) + slotIndex; } + } + + public AttachmentTimeline (int frameCount) { + frames = new float[frameCount]; + attachmentNames = new String[frameCount]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, String attachmentName) { + frames[frameIndex] = time; + attachmentNames[frameIndex] = attachmentName; + } + + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + string attachmentName; + Slot slot = skeleton.slots.Items[slotIndex]; + if (direction == MixDirection.Out && pose == MixPose.Setup) { + attachmentName = slot.data.attachmentName; + slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName); + return; + } + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + if (pose == MixPose.Setup) { + attachmentName = slot.data.attachmentName; + slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName); + } + return; + } + + int frameIndex; + if (time >= frames[frames.Length - 1]) // Time is after last frame. + frameIndex = frames.Length - 1; + else + frameIndex = Animation.BinarySearch(frames, time, 1) - 1; + + attachmentName = attachmentNames[frameIndex]; + slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName); + } + } + + public class DeformTimeline : CurveTimeline { + internal int slotIndex; + internal float[] frames; + internal float[][] frameVertices; + internal VertexAttachment attachment; + + public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, ... + public float[][] Vertices { get { return frameVertices; } set { frameVertices = value; } } + public VertexAttachment Attachment { get { return attachment; } set { attachment = value; } } + + override public int PropertyId { + get { return ((int)TimelineType.Deform << 24) + attachment.id + slotIndex; } + } + + public DeformTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount]; + frameVertices = new float[frameCount][]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, float[] vertices) { + frames[frameIndex] = time; + frameVertices[frameIndex] = vertices; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + Slot slot = skeleton.slots.Items[slotIndex]; + VertexAttachment vertexAttachment = slot.attachment as VertexAttachment; + if (vertexAttachment == null || !vertexAttachment.ApplyDeform(attachment)) return; + + var verticesArray = slot.attachmentVertices; + if (verticesArray.Count == 0) alpha = 1; + + float[][] frameVertices = this.frameVertices; + int vertexCount = frameVertices[0].Length; + float[] frames = this.frames; + float[] vertices; + + if (time < frames[0]) { + + switch (pose) { + case MixPose.Setup: + verticesArray.Clear(); + return; + case MixPose.Current: + if (alpha == 1) { + verticesArray.Clear(); + return; + } + + // verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count. + if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount; + verticesArray.Count = vertexCount; + vertices = verticesArray.Items; + + if (vertexAttachment.bones == null) { + // Unweighted vertex positions. + float[] setupVertices = vertexAttachment.vertices; + for (int i = 0; i < vertexCount; i++) + vertices[i] += (setupVertices[i] - vertices[i]) * alpha; + } else { + // Weighted deform offsets. + alpha = 1 - alpha; + for (int i = 0; i < vertexCount; i++) + vertices[i] *= alpha; + } + return; + default: + return; + } + + } + + // verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count. + if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount; + verticesArray.Count = vertexCount; + vertices = verticesArray.Items; + + if (time >= frames[frames.Length - 1]) { // Time is after last frame. + float[] lastVertices = frameVertices[frames.Length - 1]; + if (alpha == 1) { + // Vertex positions or deform offsets, no alpha. + Array.Copy(lastVertices, 0, vertices, 0, vertexCount); + } else if (pose == MixPose.Setup) { + if (vertexAttachment.bones == null) { + // Unweighted vertex positions, with alpha. + float[] setupVertices = vertexAttachment.vertices; + for (int i = 0; i < vertexCount; i++) { + float setup = setupVertices[i]; + vertices[i] = setup + (lastVertices[i] - setup) * alpha; + } + } else { + // Weighted deform offsets, with alpha. + for (int i = 0; i < vertexCount; i++) + vertices[i] = lastVertices[i] * alpha; + } + } else { + // Vertex positions or deform offsets, with alpha. + for (int i = 0; i < vertexCount; i++) + vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time); + float[] prevVertices = frameVertices[frame - 1]; + float[] nextVertices = frameVertices[frame]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); + + if (alpha == 1) { + // Vertex positions or deform offsets, no alpha. + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } else if (pose == MixPose.Setup) { + if (vertexAttachment.bones == null) { + // Unweighted vertex positions, with alpha. + var setupVertices = vertexAttachment.vertices; + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i], setup = setupVertices[i]; + vertices[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha; + } + } else { + // Weighted deform offsets, with alpha. + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] = (prev + (nextVertices[i] - prev) * percent) * alpha; + } + } + } else { + // Vertex positions or deform offsets, with alpha. + for (int i = 0; i < vertexCount; i++) { + float prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + } + } + } + + public class EventTimeline : Timeline { + internal float[] frames; + private Event[] events; + + public float[] Frames { get { return frames; } set { frames = value; } } // time, ... + public Event[] Events { get { return events; } set { events = value; } } + public int FrameCount { get { return frames.Length; } } + + public int PropertyId { + get { return ((int)TimelineType.Event << 24); } + } + + public EventTimeline (int frameCount) { + frames = new float[frameCount]; + events = new Event[frameCount]; + } + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, Event e) { + frames[frameIndex] = e.Time; + events[frameIndex] = e; + } + + /// Fires events for frames > lastTime and <= time. + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + if (firedEvents == null) return; + float[] frames = this.frames; + int frameCount = frames.Length; + + if (lastTime > time) { // Fire events after last time for looped animations. + Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, pose, direction); + lastTime = -1f; + } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + return; + if (time < frames[0]) return; // Time is before first frame. + + int frame; + if (lastTime < frames[0]) + frame = 0; + else { + frame = Animation.BinarySearch(frames, lastTime); + float frameTime = frames[frame]; + while (frame > 0) { // Fire multiple events with the same frame. + if (frames[frame - 1] != frameTime) break; + frame--; + } + } + for (; frame < frameCount && time >= frames[frame]; frame++) + firedEvents.Add(events[frame]); + } + } + + public class DrawOrderTimeline : Timeline { + internal float[] frames; + private int[][] drawOrders; + + public float[] Frames { get { return frames; } set { frames = value; } } // time, ... + public int[][] DrawOrders { get { return drawOrders; } set { drawOrders = value; } } + public int FrameCount { get { return frames.Length; } } + + public int PropertyId { + get { return ((int)TimelineType.DrawOrder << 24); } + } + + public DrawOrderTimeline (int frameCount) { + frames = new float[frameCount]; + drawOrders = new int[frameCount][]; + } + + /// Sets the time and value of the specified keyframe. + /// May be null to use bind pose draw order. + public void SetFrame (int frameIndex, float time, int[] drawOrder) { + frames[frameIndex] = time; + drawOrders[frameIndex] = drawOrder; + } + + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + ExposedList drawOrder = skeleton.drawOrder; + ExposedList slots = skeleton.slots; + if (direction == MixDirection.Out && pose == MixPose.Setup) { + Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count); + return; + } + + float[] frames = this.frames; + if (time < frames[0]) { + if (pose == MixPose.Setup) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count); + return; + } + + int frame; + if (time >= frames[frames.Length - 1]) // Time is after last frame. + frame = frames.Length - 1; + else + frame = Animation.BinarySearch(frames, time) - 1; + + int[] drawOrderToSetupIndex = drawOrders[frame]; + if (drawOrderToSetupIndex == null) { + drawOrder.Clear(); + for (int i = 0, n = slots.Count; i < n; i++) + drawOrder.Add(slots.Items[i]); + } else { + var drawOrderItems = drawOrder.Items; + var slotsItems = slots.Items; + for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++) + drawOrderItems[i] = slotsItems[drawOrderToSetupIndex[i]]; + } + } + } + + public class IkConstraintTimeline : CurveTimeline { + public const int ENTRIES = 3; + private const int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1; + private const int MIX = 1, BEND_DIRECTION = 2; + + internal int ikConstraintIndex; + internal float[] frames; + + public int IkConstraintIndex { get { return ikConstraintIndex; } set { ikConstraintIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, mix, bendDirection, ... + + override public int PropertyId { + get { return ((int)TimelineType.IkConstraint << 24) + ikConstraintIndex; } + } + + public IkConstraintTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + /// Sets the time, mix and bend direction of the specified keyframe. + public void SetFrame (int frameIndex, float time, float mix, int bendDirection) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + MIX] = mix; + frames[frameIndex + BEND_DIRECTION] = bendDirection; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + constraint.mix = constraint.data.mix; + constraint.bendDirection = constraint.data.bendDirection; + return; + case MixPose.Current: + constraint.mix += (constraint.data.mix - constraint.mix) * alpha; + constraint.bendDirection = constraint.data.bendDirection; + return; + } + return; + } + + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + if (pose == MixPose.Setup) { + constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha; + constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection + : (int)frames[frames.Length + PREV_BEND_DIRECTION]; + } else { + constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha; + if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION]; + } + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + float mix = frames[frame + PREV_MIX]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + if (pose == MixPose.Setup) { + constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha; + constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION]; + } else { + constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha; + if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION]; + } + } + } + + public class TransformConstraintTimeline : CurveTimeline { + public const int ENTRIES = 5; + private const int PREV_TIME = -5, PREV_ROTATE = -4, PREV_TRANSLATE = -3, PREV_SCALE = -2, PREV_SHEAR = -1; + private const int ROTATE = 1, TRANSLATE = 2, SCALE = 3, SHEAR = 4; + + internal int transformConstraintIndex; + internal float[] frames; + + public int TransformConstraintIndex { get { return transformConstraintIndex; } set { transformConstraintIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, scale mix, shear mix, ... + + override public int PropertyId { + get { return ((int)TimelineType.TransformConstraint << 24) + transformConstraintIndex; } + } + + public TransformConstraintTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + ROTATE] = rotateMix; + frames[frameIndex + TRANSLATE] = translateMix; + frames[frameIndex + SCALE] = scaleMix; + frames[frameIndex + SHEAR] = shearMix; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + var data = constraint.data; + switch (pose) { + case MixPose.Setup: + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + constraint.scaleMix = data.scaleMix; + constraint.shearMix = data.shearMix; + return; + case MixPose.Current: + constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha; + constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha; + constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha; + constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha; + return; + } + return; + } + + float rotate, translate, scale, shear; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + int i = frames.Length; + rotate = frames[i + PREV_ROTATE]; + translate = frames[i + PREV_TRANSLATE]; + scale = frames[i + PREV_SCALE]; + shear = frames[i + PREV_SHEAR]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + rotate = frames[frame + PREV_ROTATE]; + translate = frames[frame + PREV_TRANSLATE]; + scale = frames[frame + PREV_SCALE]; + shear = frames[frame + PREV_SHEAR]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + rotate += (frames[frame + ROTATE] - rotate) * percent; + translate += (frames[frame + TRANSLATE] - translate) * percent; + scale += (frames[frame + SCALE] - scale) * percent; + shear += (frames[frame + SHEAR] - shear) * percent; + } + if (pose == MixPose.Setup) { + TransformConstraintData data = constraint.data; + constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha; + constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha; + constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha; + constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha; + } else { + constraint.rotateMix += (rotate - constraint.rotateMix) * alpha; + constraint.translateMix += (translate - constraint.translateMix) * alpha; + constraint.scaleMix += (scale - constraint.scaleMix) * alpha; + constraint.shearMix += (shear - constraint.shearMix) * alpha; + } + } + } + + public class PathConstraintPositionTimeline : CurveTimeline { + public const int ENTRIES = 2; + protected const int PREV_TIME = -2, PREV_VALUE = -1; + protected const int VALUE = 1; + + internal int pathConstraintIndex; + internal float[] frames; + + override public int PropertyId { + get { return ((int)TimelineType.PathConstraintPosition << 24) + pathConstraintIndex; } + } + + public PathConstraintPositionTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + public int PathConstraintIndex { get { return pathConstraintIndex; } set { pathConstraintIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, position, ... + + /// Sets the time and value of the specified keyframe. + public void SetFrame (int frameIndex, float time, float value) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + VALUE] = value; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + constraint.position = constraint.data.position; + return; + case MixPose.Current: + constraint.position += (constraint.data.position - constraint.position) * alpha; + return; + } + return; + } + + float position; + if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame. + position = frames[frames.Length + PREV_VALUE]; + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + position = frames[frame + PREV_VALUE]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + position += (frames[frame + VALUE] - position) * percent; + } + if (pose == MixPose.Setup) + constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; + else + constraint.position += (position - constraint.position) * alpha; + } + } + + public class PathConstraintSpacingTimeline : PathConstraintPositionTimeline { + override public int PropertyId { + get { return ((int)TimelineType.PathConstraintSpacing << 24) + pathConstraintIndex; } + } + + public PathConstraintSpacingTimeline (int frameCount) + : base(frameCount) { + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + constraint.spacing = constraint.data.spacing; + return; + case MixPose.Current: + constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; + return; + } + return; + } + + float spacing; + if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame. + spacing = frames[frames.Length + PREV_VALUE]; + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + spacing = frames[frame + PREV_VALUE]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + spacing += (frames[frame + VALUE] - spacing) * percent; + } + + if (pose == MixPose.Setup) + constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; + else + constraint.spacing += (spacing - constraint.spacing) * alpha; + } + } + + public class PathConstraintMixTimeline : CurveTimeline { + public const int ENTRIES = 3; + private const int PREV_TIME = -3, PREV_ROTATE = -2, PREV_TRANSLATE = -1; + private const int ROTATE = 1, TRANSLATE = 2; + + internal int pathConstraintIndex; + internal float[] frames; + + public int PathConstraintIndex { get { return pathConstraintIndex; } set { pathConstraintIndex = value; } } + public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, ... + + override public int PropertyId { + get { return ((int)TimelineType.PathConstraintMix << 24) + pathConstraintIndex; } + } + + public PathConstraintMixTimeline (int frameCount) + : base(frameCount) { + frames = new float[frameCount * ENTRIES]; + } + + /// Sets the time and mixes of the specified keyframe. + public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix) { + frameIndex *= ENTRIES; + frames[frameIndex] = time; + frames[frameIndex + ROTATE] = rotateMix; + frames[frameIndex + TRANSLATE] = translateMix; + } + + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex]; + float[] frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.Setup: + constraint.rotateMix = constraint.data.rotateMix; + constraint.translateMix = constraint.data.translateMix; + return; + case MixPose.Current: + constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha; + constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha; + return; + } + return; + } + + float rotate, translate; + if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. + rotate = frames[frames.Length + PREV_ROTATE]; + translate = frames[frames.Length + PREV_TRANSLATE]; + } else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, ENTRIES); + rotate = frames[frame + PREV_ROTATE]; + translate = frames[frame + PREV_TRANSLATE]; + float frameTime = frames[frame]; + float percent = GetCurvePercent(frame / ENTRIES - 1, + 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); + + rotate += (frames[frame + ROTATE] - rotate) * percent; + translate += (frames[frame + TRANSLATE] - translate) * percent; + } + + if (pose == MixPose.Setup) { + constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha; + constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha; + } else { + constraint.rotateMix += (rotate - constraint.rotateMix) * alpha; + constraint.translateMix += (translate - constraint.translateMix) * alpha; + } + } + } +} diff --git a/Assets/Spine/spine-csharp/Animation.cs.meta b/Assets/Spine/spine-csharp/Animation.cs.meta new file mode 100644 index 0000000..cde730f --- /dev/null +++ b/Assets/Spine/spine-csharp/Animation.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fb7099b9c6ce91740b7041dabb0752c2 +timeCreated: 1456265156 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/AnimationState.cs b/Assets/Spine/spine-csharp/AnimationState.cs new file mode 100644 index 0000000..465e7b6 --- /dev/null +++ b/Assets/Spine/spine-csharp/AnimationState.cs @@ -0,0 +1,1066 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + public class AnimationState { + static readonly Animation EmptyAnimation = new Animation("", new ExposedList(), 0); + internal const int Subsequent = 0, First = 1, Dip = 2, DipMix = 3; + + private AnimationStateData data; + + Pool trackEntryPool = new Pool(); + private readonly ExposedList tracks = new ExposedList(); + private readonly ExposedList events = new ExposedList(); + private readonly EventQueue queue; // Initialized by constructor. + + private readonly HashSet propertyIDs = new HashSet(); + private readonly ExposedList mixingTo = new ExposedList(); + private bool animationsChanged; + + private float timeScale = 1; + + public AnimationStateData Data { get { return data; } } + /// A list of tracks that have animations, which may contain nulls. + public ExposedList Tracks { get { return tracks; } } + public float TimeScale { get { return timeScale; } set { timeScale = value; } } + + public delegate void TrackEntryDelegate (TrackEntry trackEntry); + public event TrackEntryDelegate Start, Interrupt, End, Dispose, Complete; + + public delegate void TrackEntryEventDelegate (TrackEntry trackEntry, Event e); + public event TrackEntryEventDelegate Event; + + public AnimationState (AnimationStateData data) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + this.data = data; + this.queue = new EventQueue( + this, + delegate { this.animationsChanged = true; }, + trackEntryPool + ); + } + + /// + /// Increments the track entry times, setting queued animations as current if needed + /// delta time + public void Update (float delta) { + delta *= timeScale; + var tracksItems = tracks.Items; + for (int i = 0, n = tracks.Count; i < n; i++) { + TrackEntry current = tracksItems[i]; + if (current == null) continue; + + current.animationLast = current.nextAnimationLast; + current.trackLast = current.nextTrackLast; + + float currentDelta = delta * current.timeScale; + + if (current.delay > 0) { + current.delay -= currentDelta; + if (current.delay > 0) continue; + currentDelta = -current.delay; + current.delay = 0; + } + + TrackEntry next = current.next; + if (next != null) { + // When the next entry's delay is passed, change to the next entry, preserving leftover time. + float nextTime = current.trackLast - next.delay; + if (nextTime >= 0) { + next.delay = 0; + next.trackTime = nextTime + (delta * next.timeScale); + current.trackTime += currentDelta; + SetCurrent(i, next, true); + while (next.mixingFrom != null) { + next.mixTime += currentDelta; + next = next.mixingFrom; + } + continue; + } + } else if (current.trackLast >= current.trackEnd && current.mixingFrom == null) { + // Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. + tracksItems[i] = null; + + queue.End(current); + DisposeNext(current); + continue; + } + if (current.mixingFrom != null && UpdateMixingFrom(current, delta)) { + // End mixing from entries once all have completed. + var from = current.mixingFrom; + current.mixingFrom = null; + while (from != null) { + queue.End(from); + from = from.mixingFrom; + } + } + + current.trackTime += currentDelta; + } + + queue.Drain(); + } + + /// Returns true when all mixing from entries are complete. + private bool UpdateMixingFrom (TrackEntry to, float delta) { + TrackEntry from = to.mixingFrom; + if (from == null) return true; + + bool finished = UpdateMixingFrom(from, delta); + + from.animationLast = from.nextAnimationLast; + from.trackLast = from.nextTrackLast; + + // Require mixTime > 0 to ensure the mixing from entry was applied at least once. + if (to.mixTime > 0 && (to.mixTime >= to.mixDuration || to.timeScale == 0)) { + // Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame). + if (from.totalAlpha == 0 || to.mixDuration == 0) { + to.mixingFrom = from.mixingFrom; + to.interruptAlpha = from.interruptAlpha; + queue.End(from); + } + return finished; + } + + from.trackTime += delta * from.timeScale; + to.mixTime += delta * to.timeScale; + return false; + } + + /// + /// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the + /// animation state can be applied to multiple skeletons to pose them identically. + public bool Apply (Skeleton skeleton) { + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + if (animationsChanged) AnimationsChanged(); + + var events = this.events; + + bool applied = false; + var tracksItems = tracks.Items; + for (int i = 0, m = tracks.Count; i < m; i++) { + TrackEntry current = tracksItems[i]; + if (current == null || current.delay > 0) continue; + applied = true; + MixPose currentPose = i == 0 ? MixPose.Current : MixPose.CurrentLayered; + + // Apply mixing from entries first. + float mix = current.alpha; + if (current.mixingFrom != null) + mix *= ApplyMixingFrom(current, skeleton, currentPose); + else if (current.trackTime >= current.trackEnd && current.next == null) // + mix = 0; // Set to setup pose the last time the entry will be applied. + + // Apply current entry. + float animationLast = current.animationLast, animationTime = current.AnimationTime; + int timelineCount = current.animation.timelines.Count; + var timelines = current.animation.timelines; + var timelinesItems = timelines.Items; + if (mix == 1) { + for (int ii = 0; ii < timelineCount; ii++) + timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, 1, MixPose.Setup, MixDirection.In); + } else { + var timelineData = current.timelineData.Items; + + bool firstFrame = current.timelinesRotation.Count == 0; + if (firstFrame) current.timelinesRotation.EnsureCapacity(timelines.Count << 1); + var timelinesRotation = current.timelinesRotation.Items; + + for (int ii = 0; ii < timelineCount; ii++) { + Timeline timeline = timelinesItems[ii]; + MixPose pose = timelineData[ii] >= AnimationState.First ? MixPose.Setup : currentPose; + var rotateTimeline = timeline as RotateTimeline; + if (rotateTimeline != null) + ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame); + else + timeline.Apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection.In); + } + } + QueueEvents(current, animationTime); + events.Clear(false); + current.nextAnimationLast = animationTime; + current.nextTrackLast = current.trackTime; + } + + queue.Drain(); + return applied; + } + + private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton, MixPose currentPose) { + TrackEntry from = to.mixingFrom; + if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton, currentPose); + + float mix; + if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes. + mix = 1; + currentPose = MixPose.Setup; + } else { + mix = to.mixTime / to.mixDuration; + if (mix > 1) mix = 1; + } + + var eventBuffer = mix < from.eventThreshold ? this.events : null; + bool attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; + float animationLast = from.animationLast, animationTime = from.AnimationTime; + var timelines = from.animation.timelines; + int timelineCount = timelines.Count; + var timelinesItems = timelines.Items; + var timelineData = from.timelineData.Items; + var timelineDipMix = from.timelineDipMix.Items; + + bool firstFrame = from.timelinesRotation.Count == 0; + if (firstFrame) from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize + var timelinesRotation = from.timelinesRotation.Items; + + MixPose pose; + float alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha; + from.totalAlpha = 0; + for (int i = 0; i < timelineCount; i++) { + Timeline timeline = timelinesItems[i]; + switch (timelineData[i]) { + case Subsequent: + if (!attachments && timeline is AttachmentTimeline) continue; + if (!drawOrder && timeline is DrawOrderTimeline) continue; + pose = currentPose; + alpha = alphaMix; + break; + case First: + pose = MixPose.Setup; + alpha = alphaMix; + break; + case Dip: + pose = MixPose.Setup; + alpha = alphaDip; + break; + default: + pose = MixPose.Setup; + TrackEntry dipMix = timelineDipMix[i]; + alpha = alphaDip * Math.Max(0, 1 - dipMix.mixTime / dipMix.mixDuration); + break; + } + from.totalAlpha += alpha; + var rotateTimeline = timeline as RotateTimeline; + if (rotateTimeline != null) { + ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame); + } else { + timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, alpha, pose, MixDirection.Out); + } + } + + if (to.mixDuration > 0) QueueEvents(from, animationTime); + this.events.Clear(false); + from.nextAnimationLast = animationTime; + from.nextTrackLast = from.trackTime; + + return mix; + } + + static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, MixPose pose, + float[] timelinesRotation, int i, bool firstFrame) { + + if (firstFrame) timelinesRotation[i] = 0; + + if (alpha == 1) { + rotateTimeline.Apply(skeleton, 0, time, null, 1, pose, MixDirection.In); + return; + } + + Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex]; + float[] frames = rotateTimeline.frames; + if (time < frames[0]) { + if (pose == MixPose.Setup) bone.rotation = bone.data.rotation; + return; + } + + float r2; + if (time >= frames[frames.Length - RotateTimeline.ENTRIES]) // Time is after last frame. + r2 = bone.data.rotation + frames[frames.Length + RotateTimeline.PREV_ROTATION]; + else { + // Interpolate between the previous frame and the current frame. + int frame = Animation.BinarySearch(frames, time, RotateTimeline.ENTRIES); + float prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; + float frameTime = frames[frame]; + float percent = rotateTimeline.GetCurvePercent((frame >> 1) - 1, + 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); + + r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation; + r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360; + r2 = prevRotation + r2 * percent + bone.data.rotation; + r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360; + } + + // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. + float r1 = pose == MixPose.Setup ? bone.data.rotation : bone.rotation; + float total, diff = r2 - r1; + if (diff == 0) { + total = timelinesRotation[i]; + } else { + diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360; + float lastTotal, lastDiff; + if (firstFrame) { + lastTotal = 0; + lastDiff = diff; + } else { + lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops. + lastDiff = timelinesRotation[i + 1]; // Difference between bones. + } + bool current = diff > 0, dir = lastTotal >= 0; + // Detect cross at 0 (not 180). + if (Math.Sign(lastDiff) != Math.Sign(diff) && Math.Abs(lastDiff) <= 90) { + // A cross after a 360 rotation is a loop. + if (Math.Abs(lastTotal) > 180) lastTotal += 360 * Math.Sign(lastTotal); + dir = current; + } + total = diff + lastTotal - lastTotal % 360; // Store loops as part of lastTotal. + if (dir != current) total += 360 * Math.Sign(lastTotal); + timelinesRotation[i] = total; + } + timelinesRotation[i + 1] = diff; + r1 += total * alpha; + bone.rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360; + } + + private void QueueEvents (TrackEntry entry, float animationTime) { + float animationStart = entry.animationStart, animationEnd = entry.animationEnd; + float duration = animationEnd - animationStart; + float trackLastWrapped = entry.trackLast % duration; + + // Queue events before complete. + var events = this.events; + var eventsItems = events.Items; + int i = 0, n = events.Count; + for (; i < n; i++) { + var e = eventsItems[i]; + if (e.time < trackLastWrapped) break; + if (e.time > animationEnd) continue; // Discard events outside animation start/end. + queue.Event(entry, e); + } + + // Queue complete if completed a loop iteration or the animation. + bool complete = false; + if (entry.loop) + complete = duration == 0 || (trackLastWrapped > entry.trackTime % duration); + else + complete = animationTime >= animationEnd && entry.animationLast < animationEnd; + if (complete) queue.Complete(entry); + + // Queue events after complete. + for (; i < n; i++) { + Event e = eventsItems[i]; + if (e.time < animationStart) continue; // Discard events outside animation start/end. + queue.Event(entry, eventsItems[i]); + } + } + + /// + /// Removes all animations from all tracks, leaving skeletons in their previous pose. + /// It may be desired to use to mix the skeletons back to the setup pose, + /// rather than leaving them in their previous pose. + public void ClearTracks () { + bool oldDrainDisabled = queue.drainDisabled; + queue.drainDisabled = true; + for (int i = 0, n = tracks.Count; i < n; i++) { + ClearTrack(i); + } + tracks.Clear(); + queue.drainDisabled = oldDrainDisabled; + queue.Drain(); + } + + /// + /// Removes all animations from the tracks, leaving skeletons in their previous pose. + /// It may be desired to use to mix the skeletons back to the setup pose, + /// rather than leaving them in their previous pose. + public void ClearTrack (int trackIndex) { + if (trackIndex >= tracks.Count) return; + TrackEntry current = tracks.Items[trackIndex]; + if (current == null) return; + + queue.End(current); + + DisposeNext(current); + + TrackEntry entry = current; + while (true) { + TrackEntry from = entry.mixingFrom; + if (from == null) break; + queue.End(from); + entry.mixingFrom = null; + entry = from; + } + + tracks.Items[current.trackIndex] = null; + + queue.Drain(); + } + + /// Sets the active TrackEntry for a given track number. + private void SetCurrent (int index, TrackEntry current, bool interrupt) { + TrackEntry from = ExpandToIndex(index); + tracks.Items[index] = current; + + if (from != null) { + if (interrupt) queue.Interrupt(from); + current.mixingFrom = from; + current.mixTime = 0; + + // Store interrupted mix percentage. + if (from.mixingFrom != null && from.mixDuration > 0) + current.interruptAlpha *= Math.Min(1, from.mixTime / from.mixDuration); + + from.timelinesRotation.Clear(); // Reset rotation for mixing out, in case entry was mixed in. + } + + queue.Start(current); // triggers AnimationsChanged + } + + + /// Sets an animation by name. + public TrackEntry SetAnimation (int trackIndex, string animationName, bool loop) { + Animation animation = data.skeletonData.FindAnimation(animationName); + if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName"); + return SetAnimation(trackIndex, animation, loop); + } + + /// Sets the current animation for a track, discarding any queued animations. + /// If true, the animation will repeat. + /// If false, it will not, instead its last frame is applied if played beyond its duration. + /// In either case determines when the track is cleared. + /// + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept + /// after . + public TrackEntry SetAnimation (int trackIndex, Animation animation, bool loop) { + if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null."); + bool interrupt = true; + TrackEntry current = ExpandToIndex(trackIndex); + if (current != null) { + if (current.nextTrackLast == -1) { + // Don't mix from an entry that was never applied. + tracks.Items[trackIndex] = current.mixingFrom; + queue.Interrupt(current); + queue.End(current); + DisposeNext(current); + current = current.mixingFrom; + interrupt = false; + } else { + DisposeNext(current); + } + } + TrackEntry entry = NewTrackEntry(trackIndex, animation, loop, current); + SetCurrent(trackIndex, entry, interrupt); + queue.Drain(); + return entry; + } + + /// Queues an animation by name. + /// + public TrackEntry AddAnimation (int trackIndex, string animationName, bool loop, float delay) { + Animation animation = data.skeletonData.FindAnimation(animationName); + if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName"); + return AddAnimation(trackIndex, animation, loop, delay); + } + + /// Adds an animation to be played delay seconds after the current or last queued animation + /// for a track. If the track is empty, it is equivalent to calling . + /// + /// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation + /// duration of the previous track minus any mix duration plus the negative delay. + /// + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept + /// after + public TrackEntry AddAnimation (int trackIndex, Animation animation, bool loop, float delay) { + if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null."); + + TrackEntry last = ExpandToIndex(trackIndex); + if (last != null) { + while (last.next != null) + last = last.next; + } + + TrackEntry entry = NewTrackEntry(trackIndex, animation, loop, last); + + if (last == null) { + SetCurrent(trackIndex, entry, true); + queue.Drain(); + } else { + last.next = entry; + if (delay <= 0) { + float duration = last.animationEnd - last.animationStart; + if (duration != 0) { + if (last.loop) { + delay += duration * (1 + (int)(last.trackTime / duration)); + } else { + delay += duration; + } + delay -= data.GetMix(last.animation, animation); + } else + delay = 0; + } + } + + entry.delay = delay; + return entry; + } + + /// + /// Sets an empty animation for a track, discarding any queued animations, and mixes to it over the specified mix duration. + public TrackEntry SetEmptyAnimation (int trackIndex, float mixDuration) { + TrackEntry entry = SetAnimation(trackIndex, AnimationState.EmptyAnimation, false); + entry.mixDuration = mixDuration; + entry.trackEnd = mixDuration; + return entry; + } + + /// + /// Adds an empty animation to be played after the current or last queued animation for a track, and mixes to it over the + /// specified mix duration. + /// + /// A track entry to allow further customization of animation playback. References to the track entry must not be kept after . + /// + /// Track number. + /// Mix duration. + /// Seconds to begin this animation after the start of the previous animation. May be <= 0 to use the animation + /// duration of the previous track minus any mix duration plus the negative delay. + public TrackEntry AddEmptyAnimation (int trackIndex, float mixDuration, float delay) { + if (delay <= 0) delay -= mixDuration; + TrackEntry entry = AddAnimation(trackIndex, AnimationState.EmptyAnimation, false, delay); + entry.mixDuration = mixDuration; + entry.trackEnd = mixDuration; + return entry; + } + + /// + /// Sets an empty animation for every track, discarding any queued animations, and mixes to it over the specified mix duration. + public void SetEmptyAnimations (float mixDuration) { + bool oldDrainDisabled = queue.drainDisabled; + queue.drainDisabled = true; + for (int i = 0, n = tracks.Count; i < n; i++) { + TrackEntry current = tracks.Items[i]; + if (current != null) SetEmptyAnimation(i, mixDuration); + } + queue.drainDisabled = oldDrainDisabled; + queue.Drain(); + } + + private TrackEntry ExpandToIndex (int index) { + if (index < tracks.Count) return tracks.Items[index]; + while (index >= tracks.Count) + tracks.Add(null); + return null; + } + + /// Object-pooling version of new TrackEntry. Obtain an unused TrackEntry from the pool and clear/initialize its values. + /// May be null. + private TrackEntry NewTrackEntry (int trackIndex, Animation animation, bool loop, TrackEntry last) { + TrackEntry entry = trackEntryPool.Obtain(); // Pooling + entry.trackIndex = trackIndex; + entry.animation = animation; + entry.loop = loop; + + entry.eventThreshold = 0; + entry.attachmentThreshold = 0; + entry.drawOrderThreshold = 0; + + entry.animationStart = 0; + entry.animationEnd = animation.Duration; + entry.animationLast = -1; + entry.nextAnimationLast = -1; + + entry.delay = 0; + entry.trackTime = 0; + entry.trackLast = -1; + entry.nextTrackLast = -1; // nextTrackLast == -1 signifies a TrackEntry that wasn't applied yet. + entry.trackEnd = float.MaxValue; // loop ? float.MaxValue : animation.Duration; + entry.timeScale = 1; + + entry.alpha = 1; + entry.interruptAlpha = 1; + entry.mixTime = 0; + entry.mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation); + return entry; + } + + /// Dispose all track entries queued after the given TrackEntry. + private void DisposeNext (TrackEntry entry) { + TrackEntry next = entry.next; + while (next != null) { + queue.Dispose(next); + next = next.next; + } + entry.next = null; + } + + private void AnimationsChanged () { + animationsChanged = false; + + var propertyIDs = this.propertyIDs; + propertyIDs.Clear(); + var mixingTo = this.mixingTo; + + var tracksItems = tracks.Items; + for (int i = 0, n = tracks.Count; i < n; i++) { + var entry = tracksItems[i]; + if (entry != null) entry.SetTimelineData(null, mixingTo, propertyIDs); + } + } + + /// The track entry for the animation currently playing on the track, or null if no animation is currently playing. + public TrackEntry GetCurrent (int trackIndex) { + return (trackIndex >= tracks.Count) ? null : tracks.Items[trackIndex]; + } + + override public string ToString () { + var buffer = new System.Text.StringBuilder(); + for (int i = 0, n = tracks.Count; i < n; i++) { + TrackEntry entry = tracks.Items[i]; + if (entry == null) continue; + if (buffer.Length > 0) buffer.Append(", "); + buffer.Append(entry.ToString()); + } + return buffer.Length == 0 ? "" : buffer.ToString(); + } + + internal void OnStart (TrackEntry entry) { if (Start != null) Start(entry); } + internal void OnInterrupt (TrackEntry entry) { if (Interrupt != null) Interrupt(entry); } + internal void OnEnd (TrackEntry entry) { if (End != null) End(entry); } + internal void OnDispose (TrackEntry entry) { if (Dispose != null) Dispose(entry); } + internal void OnComplete (TrackEntry entry) { if (Complete != null) Complete(entry); } + internal void OnEvent (TrackEntry entry, Event e) { if (Event != null) Event(entry, e); } + } + + /// State for the playback of an animation. + public class TrackEntry : Pool.IPoolable { + internal Animation animation; + + internal TrackEntry next, mixingFrom; + internal int trackIndex; + + internal bool loop; + internal float eventThreshold, attachmentThreshold, drawOrderThreshold; + internal float animationStart, animationEnd, animationLast, nextAnimationLast; + internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f; + internal float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha; + internal readonly ExposedList timelineData = new ExposedList(); + internal readonly ExposedList timelineDipMix = new ExposedList(); + internal readonly ExposedList timelinesRotation = new ExposedList(); + + // IPoolable.Reset() + public void Reset () { + next = null; + mixingFrom = null; + animation = null; + timelineData.Clear(); + timelineDipMix.Clear(); + timelinesRotation.Clear(); + + Start = null; + Interrupt = null; + End = null; + Dispose = null; + Complete = null; + Event = null; + } + + /// Sets the timeline data. + /// May be null. + internal TrackEntry SetTimelineData (TrackEntry to, ExposedList mixingToArray, HashSet propertyIDs) { + if (to != null) mixingToArray.Add(to); + var lastEntry = mixingFrom != null ? mixingFrom.SetTimelineData(this, mixingToArray, propertyIDs) : this; + if (to != null) mixingToArray.Pop(); + + var mixingTo = mixingToArray.Items; + int mixingToLast = mixingToArray.Count - 1; + var timelines = animation.timelines.Items; + int timelinesCount = animation.timelines.Count; + var timelineDataItems = timelineData.Resize(timelinesCount).Items; // timelineData.setSize(timelinesCount); + timelineDipMix.Clear(); + var timelineDipMixItems = timelineDipMix.Resize(timelinesCount).Items; //timelineDipMix.setSize(timelinesCount); + + // outer: + for (int i = 0; i < timelinesCount; i++) { + int id = timelines[i].PropertyId; + if (!propertyIDs.Add(id)) { + timelineDataItems[i] = AnimationState.Subsequent; + } else if (to == null || !to.HasTimeline(id)) { + timelineDataItems[i] = AnimationState.First; + } else { + for (int ii = mixingToLast; ii >= 0; ii--) { + var entry = mixingTo[ii]; + if (!entry.HasTimeline(id)) { + if (entry.mixDuration > 0) { + timelineDataItems[i] = AnimationState.DipMix; + timelineDipMixItems[i] = entry; + goto continue_outer; // continue outer; + } + break; + } + } + timelineDataItems[i] = AnimationState.Dip; + } + continue_outer: {} + } + return lastEntry; + } + + bool HasTimeline (int id) { + var timelines = animation.timelines.Items; + for (int i = 0, n = animation.timelines.Count; i < n; i++) + if (timelines[i].PropertyId == id) return true; + return false; + } + + /// The index of the track where this entry is either current or queued. + public int TrackIndex { get { return trackIndex; } } + + /// The animation to apply for this track entry. + public Animation Animation { get { return animation; } } + + /// + /// If true, the animation will repeat. If false, it will not, instead its last frame is applied if played beyond its duration. + public bool Loop { get { return loop; } set { loop = value; } } + + /// + /// Seconds to postpone playing the animation. When a track entry is the current track entry, delay postpones incrementing + /// the track time. When a track entry is queued, delay is the time from the start of the previous animation to when the + /// track entry will become the current track entry. + public float Delay { get { return delay; } set { delay = value; } } + + /// + /// Current time in seconds this track entry has been the current track entry. The track time determines + /// . The track time can be set to start the animation at a time other than 0, without affecting looping. + public float TrackTime { get { return trackTime; } set { trackTime = value; } } + + /// + /// The track time in seconds when this animation will be removed from the track. Defaults to the animation duration for + /// non-looping animations and to for looping animations. If the track end time is reached and no + /// other animations are queued for playback, and mixing from any previous animations is complete, properties keyed by the animation, + /// are set to the setup pose and the track is cleared. + /// + /// It may be desired to use to mix the properties back to the + /// setup pose over time, rather than have it happen instantly. + /// + public float TrackEnd { get { return trackEnd; } set { trackEnd = value; } } + + /// + /// Seconds when this animation starts, both initially and after looping. Defaults to 0. + /// + /// When changing the animation start time, it often makes sense to set to the same value to + /// prevent timeline keys before the start time from triggering. + /// + public float AnimationStart { get { return animationStart; } set { animationStart = value; } } + + /// + /// Seconds for the last frame of this animation. Non-looping animations won't play past this time. Looping animations will + /// loop back to at this time. Defaults to the animation duration. + public float AnimationEnd { get { return animationEnd; } set { animationEnd = value; } } + + /// + /// The time in seconds this animation was last applied. Some timelines use this for one-time triggers. Eg, when this + /// animation is applied, event timelines will fire all events between the animation last time (exclusive) and animation time + /// (inclusive). Defaults to -1 to ensure triggers on frame 0 happen the first time this animation is applied. + public float AnimationLast { + get { return animationLast; } + set { + animationLast = value; + nextAnimationLast = value; + } + } + + /// + /// Uses to compute the animation time between . and + /// . When the track time is 0, the animation time is equal to the animation start time. + /// + public float AnimationTime { + get { + if (loop) { + float duration = animationEnd - animationStart; + if (duration == 0) return animationStart; + return (trackTime % duration) + animationStart; + } + return Math.Min(trackTime + animationStart, animationEnd); + } + } + + /// + /// Multiplier for the delta time when the animation state is updated, causing time for this animation to play slower or + /// faster. Defaults to 1. + /// + public float TimeScale { get { return timeScale; } set { timeScale = value; } } + + /// + /// Values less than 1 mix this animation with the last skeleton pose. Defaults to 1, which overwrites the last skeleton pose with + /// this animation. + /// + /// Typically track 0 is used to completely pose the skeleton, then alpha can be used on higher tracks. It doesn't make sense + /// to use alpha on track 0 if the skeleton pose is from the last frame render. + /// + public float Alpha { get { return alpha; } set { alpha = value; } } + + /// + /// When the mix percentage (mix time / mix duration) is less than the event threshold, event timelines for the animation + /// being mixed out will be applied. Defaults to 0, so event timelines are not applied for an animation being mixed out. + public float EventThreshold { get { return eventThreshold; } set { eventThreshold = value; } } + + /// + /// When the mix percentage (mix time / mix duration) is less than the attachment threshold, attachment timelines for the + /// animation being mixed out will be applied. Defaults to 0, so attachment timelines are not applied for an animation being + /// mixed out. + public float AttachmentThreshold { get { return attachmentThreshold; } set { attachmentThreshold = value; } } + + /// + /// When the mix percentage (mix time / mix duration) is less than the draw order threshold, draw order timelines for the + /// animation being mixed out will be applied. Defaults to 0, so draw order timelines are not applied for an animation being + /// mixed out. + /// + public float DrawOrderThreshold { get { return drawOrderThreshold; } set { drawOrderThreshold = value; } } + + /// + /// The animation queued to start after this animation, or null. + public TrackEntry Next { get { return next; } } + + /// + /// Returns true if at least one loop has been completed. + public bool IsComplete { + get { return trackTime >= animationEnd - animationStart; } + } + + /// + /// Seconds from 0 to the mix duration when mixing from the previous animation to this animation. May be slightly more than + /// when the mix is complete. + public float MixTime { get { return mixTime; } set { mixTime = value; } } + + /// + /// Seconds for mixing from the previous animation to this animation. Defaults to the value provided by + /// based on the animation before this animation (if any). + /// + /// The mix duration can be set manually rather than use the value from AnimationStateData.GetMix. + /// In that case, the mixDuration must be set before is next called. + /// + /// When using with a + /// delay less than or equal to 0, note the is set using the mix duration from the + /// + /// + /// + public float MixDuration { get { return mixDuration; } set { mixDuration = value; } } + + /// + /// The track entry for the previous animation when mixing from the previous animation to this animation, or null if no + /// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list. + public TrackEntry MixingFrom { get { return mixingFrom; } } + + public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete; + public event AnimationState.TrackEntryEventDelegate Event; + internal void OnStart () { if (Start != null) Start(this); } + internal void OnInterrupt () { if (Interrupt != null) Interrupt(this); } + internal void OnEnd () { if (End != null) End(this); } + internal void OnDispose () { if (Dispose != null) Dispose(this); } + internal void OnComplete () { if (Complete != null) Complete(this); } + internal void OnEvent (Event e) { if (Event != null) Event(this, e); } + + /// + /// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the + /// long way around when using and starting animations on other tracks. + /// + /// Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way around. + /// The two rotations likely change over time, so which direction is the short or long way also changes. + /// If the short way was always chosen, bones would flip to the other side when that direction became the long way. + /// TrackEntry chooses the short way the first time it is applied and remembers that direction. + public void ResetRotationDirections () { + timelinesRotation.Clear(); + } + + override public string ToString () { + return animation == null ? "" : animation.name; + } + } + + class EventQueue { + private readonly List eventQueueEntries = new List(); + internal bool drainDisabled; + + private readonly AnimationState state; + private readonly Pool trackEntryPool; + internal event Action AnimationsChanged; + + internal EventQueue (AnimationState state, Action HandleAnimationsChanged, Pool trackEntryPool) { + this.state = state; + this.AnimationsChanged += HandleAnimationsChanged; + this.trackEntryPool = trackEntryPool; + } + + struct EventQueueEntry { + public EventType type; + public TrackEntry entry; + public Event e; + + public EventQueueEntry (EventType eventType, TrackEntry trackEntry, Event e = null) { + this.type = eventType; + this.entry = trackEntry; + this.e = e; + } + } + + enum EventType { + Start, Interrupt, End, Dispose, Complete, Event + } + + internal void Start (TrackEntry entry) { + eventQueueEntries.Add(new EventQueueEntry(EventType.Start, entry)); + if (AnimationsChanged != null) AnimationsChanged(); + } + + internal void Interrupt (TrackEntry entry) { + eventQueueEntries.Add(new EventQueueEntry(EventType.Interrupt, entry)); + } + + internal void End (TrackEntry entry) { + eventQueueEntries.Add(new EventQueueEntry(EventType.End, entry)); + if (AnimationsChanged != null) AnimationsChanged(); + } + + internal void Dispose (TrackEntry entry) { + eventQueueEntries.Add(new EventQueueEntry(EventType.Dispose, entry)); + } + + internal void Complete (TrackEntry entry) { + eventQueueEntries.Add(new EventQueueEntry(EventType.Complete, entry)); + } + + internal void Event (TrackEntry entry, Event e) { + eventQueueEntries.Add(new EventQueueEntry(EventType.Event, entry, e)); + } + + /// Raises all events in the queue and drains the queue. + internal void Drain () { + if (drainDisabled) return; + drainDisabled = true; + + var entries = this.eventQueueEntries; + AnimationState state = this.state; + + // Don't cache entries.Count so callbacks can queue their own events (eg, call SetAnimation in AnimationState_Complete). + for (int i = 0; i < entries.Count; i++) { + var queueEntry = entries[i]; + TrackEntry trackEntry = queueEntry.entry; + + switch (queueEntry.type) { + case EventType.Start: + trackEntry.OnStart(); + state.OnStart(trackEntry); + break; + case EventType.Interrupt: + trackEntry.OnInterrupt(); + state.OnInterrupt(trackEntry); + break; + case EventType.End: + trackEntry.OnEnd(); + state.OnEnd(trackEntry); + goto case EventType.Dispose; // Fall through. (C#) + case EventType.Dispose: + trackEntry.OnDispose(); + state.OnDispose(trackEntry); + trackEntryPool.Free(trackEntry); // Pooling + break; + case EventType.Complete: + trackEntry.OnComplete(); + state.OnComplete(trackEntry); + break; + case EventType.Event: + trackEntry.OnEvent(queueEntry.e); + state.OnEvent(trackEntry, queueEntry.e); + break; + } + } + eventQueueEntries.Clear(); + + drainDisabled = false; + } + + internal void Clear () { + eventQueueEntries.Clear(); + } + } + + public class Pool where T : class, new() { + public readonly int max; + readonly Stack freeObjects; + + public int Count { get { return freeObjects.Count; } } + public int Peak { get; private set; } + + public Pool (int initialCapacity = 16, int max = int.MaxValue) { + freeObjects = new Stack(initialCapacity); + this.max = max; + } + + public T Obtain () { + return freeObjects.Count == 0 ? new T() : freeObjects.Pop(); + } + + public void Free (T obj) { + if (obj == null) throw new ArgumentNullException("obj", "obj cannot be null"); + if (freeObjects.Count < max) { + freeObjects.Push(obj); + Peak = Math.Max(Peak, freeObjects.Count); + } + Reset(obj); + } + +// protected void FreeAll (List objects) { +// if (objects == null) throw new ArgumentNullException("objects", "objects cannot be null."); +// var freeObjects = this.freeObjects; +// int max = this.max; +// for (int i = 0; i < objects.Count; i++) { +// T obj = objects[i]; +// if (obj == null) continue; +// if (freeObjects.Count < max) freeObjects.Push(obj); +// Reset(obj); +// } +// Peak = Math.Max(Peak, freeObjects.Count); +// } + + public void Clear () { + freeObjects.Clear(); + } + + protected void Reset (T obj) { + var poolable = obj as IPoolable; + if (poolable != null) poolable.Reset(); + } + + public interface IPoolable { + void Reset (); + } + } + +} diff --git a/Assets/Spine/spine-csharp/AnimationState.cs.meta b/Assets/Spine/spine-csharp/AnimationState.cs.meta new file mode 100644 index 0000000..a6522a7 --- /dev/null +++ b/Assets/Spine/spine-csharp/AnimationState.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ec2f0e7143c8a174994595883f4b1e33 +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/AnimationStateData.cs b/Assets/Spine/spine-csharp/AnimationStateData.cs new file mode 100644 index 0000000..4bceefa --- /dev/null +++ b/Assets/Spine/spine-csharp/AnimationStateData.cs @@ -0,0 +1,115 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + + /// Stores mix (crossfade) durations to be applied when AnimationState animations are changed. + public class AnimationStateData { + internal SkeletonData skeletonData; + readonly Dictionary animationToMixTime = new Dictionary(AnimationPairComparer.Instance); + internal float defaultMix; + + /// The SkeletonData to look up animations when they are specified by name. + public SkeletonData SkeletonData { get { return skeletonData; } } + + /// + /// The mix duration to use when no mix duration has been specifically defined between two animations. + public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } } + + public AnimationStateData (SkeletonData skeletonData) { + if (skeletonData == null) throw new ArgumentException("skeletonData cannot be null.", "skeletonData"); + this.skeletonData = skeletonData; + } + + /// Sets a mix duration by animation names. + public void SetMix (string fromName, string toName, float duration) { + Animation from = skeletonData.FindAnimation(fromName); + if (from == null) throw new ArgumentException("Animation not found: " + fromName, "fromName"); + Animation to = skeletonData.FindAnimation(toName); + if (to == null) throw new ArgumentException("Animation not found: " + toName, "toName"); + SetMix(from, to, duration); + } + + /// Sets a mix duration when changing from the specified animation to the other. + /// See TrackEntry.MixDuration. + public void SetMix (Animation from, Animation to, float duration) { + if (from == null) throw new ArgumentNullException("from", "from cannot be null."); + if (to == null) throw new ArgumentNullException("to", "to cannot be null."); + AnimationPair key = new AnimationPair(from, to); + animationToMixTime.Remove(key); + animationToMixTime.Add(key, duration); + } + + /// + /// The mix duration to use when changing from the specified animation to the other, + /// or the DefaultMix if no mix duration has been set. + /// + public float GetMix (Animation from, Animation to) { + if (from == null) throw new ArgumentNullException("from", "from cannot be null."); + if (to == null) throw new ArgumentNullException("to", "to cannot be null."); + AnimationPair key = new AnimationPair(from, to); + float duration; + if (animationToMixTime.TryGetValue(key, out duration)) return duration; + return defaultMix; + } + + public struct AnimationPair { + public readonly Animation a1; + public readonly Animation a2; + + public AnimationPair (Animation a1, Animation a2) { + this.a1 = a1; + this.a2 = a2; + } + + public override string ToString () { + return a1.name + "->" + a2.name; + } + } + + // Avoids boxing in the dictionary. + public class AnimationPairComparer : IEqualityComparer { + public static readonly AnimationPairComparer Instance = new AnimationPairComparer(); + + bool IEqualityComparer.Equals (AnimationPair x, AnimationPair y) { + return ReferenceEquals(x.a1, y.a1) && ReferenceEquals(x.a2, y.a2); + } + + int IEqualityComparer.GetHashCode (AnimationPair obj) { + // from Tuple.CombineHashCodes // return (((h1 << 5) + h1) ^ h2); + int h1 = obj.a1.GetHashCode(); + return (((h1 << 5) + h1) ^ obj.a2.GetHashCode()); + } + } + } +} diff --git a/Assets/Spine/spine-csharp/AnimationStateData.cs.meta b/Assets/Spine/spine-csharp/AnimationStateData.cs.meta new file mode 100644 index 0000000..9890a1f --- /dev/null +++ b/Assets/Spine/spine-csharp/AnimationStateData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e03d60c517d9b974db35b9fd144a1d09 +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Atlas.cs b/Assets/Spine/spine-csharp/Atlas.cs new file mode 100644 index 0000000..1f48682 --- /dev/null +++ b/Assets/Spine/spine-csharp/Atlas.cs @@ -0,0 +1,311 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) +#define IS_UNITY +#endif + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Reflection; + +#if WINDOWS_STOREAPP +using System.Threading.Tasks; +using Windows.Storage; +#endif + +namespace Spine { + public class Atlas : IEnumerable { + readonly List pages = new List(); + List regions = new List(); + TextureLoader textureLoader; + + #region IEnumerable implementation + public IEnumerator GetEnumerator () { + return regions.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () { + return regions.GetEnumerator(); + } + #endregion + + #if !(IS_UNITY) + #if WINDOWS_STOREAPP + private async Task ReadFile(string path, TextureLoader textureLoader) { + var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; + var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false); + using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) { + try { + Load(reader, Path.GetDirectoryName(path), textureLoader); + } catch (Exception ex) { + throw new Exception("Error reading atlas file: " + path, ex); + } + } + } + + public Atlas(string path, TextureLoader textureLoader) { + this.ReadFile(path, textureLoader).Wait(); + } + #else + + public Atlas (string path, TextureLoader textureLoader) { + + #if WINDOWS_PHONE + Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); + using (StreamReader reader = new StreamReader(stream)) { + #else + using (StreamReader reader = new StreamReader(path)) { + #endif // WINDOWS_PHONE + + try { + Load(reader, Path.GetDirectoryName(path), textureLoader); + } catch (Exception ex) { + throw new Exception("Error reading atlas file: " + path, ex); + } + + } + } + #endif // WINDOWS_STOREAPP + + #endif + + public Atlas (TextReader reader, string dir, TextureLoader textureLoader) { + Load(reader, dir, textureLoader); + } + + public Atlas (List pages, List regions) { + this.pages = pages; + this.regions = regions; + this.textureLoader = null; + } + + private void Load (TextReader reader, string imagesDir, TextureLoader textureLoader) { + if (textureLoader == null) throw new ArgumentNullException("textureLoader", "textureLoader cannot be null."); + this.textureLoader = textureLoader; + + string[] tuple = new string[4]; + AtlasPage page = null; + while (true) { + string line = reader.ReadLine(); + if (line == null) break; + if (line.Trim().Length == 0) + page = null; + else if (page == null) { + page = new AtlasPage(); + page.name = line; + + if (ReadTuple(reader, tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. + page.width = int.Parse(tuple[0], CultureInfo.InvariantCulture); + page.height = int.Parse(tuple[1], CultureInfo.InvariantCulture); + ReadTuple(reader, tuple); + } + page.format = (Format)Enum.Parse(typeof(Format), tuple[0], false); + + ReadTuple(reader, tuple); + page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0], false); + page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1], false); + + string direction = ReadValue(reader); + page.uWrap = TextureWrap.ClampToEdge; + page.vWrap = TextureWrap.ClampToEdge; + if (direction == "x") + page.uWrap = TextureWrap.Repeat; + else if (direction == "y") + page.vWrap = TextureWrap.Repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = TextureWrap.Repeat; + + textureLoader.Load(page, Path.Combine(imagesDir, line)); + + pages.Add(page); + + } else { + AtlasRegion region = new AtlasRegion(); + region.name = line; + region.page = page; + + region.rotate = Boolean.Parse(ReadValue(reader)); + + ReadTuple(reader, tuple); + int x = int.Parse(tuple[0], CultureInfo.InvariantCulture); + int y = int.Parse(tuple[1], CultureInfo.InvariantCulture); + + ReadTuple(reader, tuple); + int width = int.Parse(tuple[0], CultureInfo.InvariantCulture); + int height = int.Parse(tuple[1], CultureInfo.InvariantCulture); + + region.u = x / (float)page.width; + region.v = y / (float)page.height; + if (region.rotate) { + region.u2 = (x + height) / (float)page.width; + region.v2 = (y + width) / (float)page.height; + } else { + region.u2 = (x + width) / (float)page.width; + region.v2 = (y + height) / (float)page.height; + } + region.x = x; + region.y = y; + region.width = Math.Abs(width); + region.height = Math.Abs(height); + + if (ReadTuple(reader, tuple) == 4) { // split is optional + region.splits = new [] {int.Parse(tuple[0], CultureInfo.InvariantCulture), + int.Parse(tuple[1], CultureInfo.InvariantCulture), + int.Parse(tuple[2], CultureInfo.InvariantCulture), + int.Parse(tuple[3], CultureInfo.InvariantCulture)}; + + if (ReadTuple(reader, tuple) == 4) { // pad is optional, but only present with splits + region.pads = new [] {int.Parse(tuple[0], CultureInfo.InvariantCulture), + int.Parse(tuple[1], CultureInfo.InvariantCulture), + int.Parse(tuple[2], CultureInfo.InvariantCulture), + int.Parse(tuple[3], CultureInfo.InvariantCulture)}; + + ReadTuple(reader, tuple); + } + } + + region.originalWidth = int.Parse(tuple[0], CultureInfo.InvariantCulture); + region.originalHeight = int.Parse(tuple[1], CultureInfo.InvariantCulture); + + ReadTuple(reader, tuple); + region.offsetX = int.Parse(tuple[0], CultureInfo.InvariantCulture); + region.offsetY = int.Parse(tuple[1], CultureInfo.InvariantCulture); + + region.index = int.Parse(ReadValue(reader), CultureInfo.InvariantCulture); + + regions.Add(region); + } + } + } + + static string ReadValue (TextReader reader) { + string line = reader.ReadLine(); + int colon = line.IndexOf(':'); + if (colon == -1) throw new Exception("Invalid line: " + line); + return line.Substring(colon + 1).Trim(); + } + + /// Returns the number of tuple values read (1, 2 or 4). + static int ReadTuple (TextReader reader, string[] tuple) { + string line = reader.ReadLine(); + int colon = line.IndexOf(':'); + if (colon == -1) throw new Exception("Invalid line: " + line); + int i = 0, lastMatch = colon + 1; + for (; i < 3; i++) { + int comma = line.IndexOf(',', lastMatch); + if (comma == -1) break; + tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim(); + lastMatch = comma + 1; + } + tuple[i] = line.Substring(lastMatch).Trim(); + return i + 1; + } + + public void FlipV () { + for (int i = 0, n = regions.Count; i < n; i++) { + AtlasRegion region = regions[i]; + region.v = 1 - region.v; + region.v2 = 1 - region.v2; + } + } + + /// Returns the first region found with the specified name. This method uses string comparison to find the region, so the result + /// should be cached rather than calling this method multiple times. + /// The region, or null. + public AtlasRegion FindRegion (string name) { + for (int i = 0, n = regions.Count; i < n; i++) + if (regions[i].name == name) return regions[i]; + return null; + } + + public void Dispose () { + if (textureLoader == null) return; + for (int i = 0, n = pages.Count; i < n; i++) + textureLoader.Unload(pages[i].rendererObject); + } + } + + public enum Format { + Alpha, + Intensity, + LuminanceAlpha, + RGB565, + RGBA4444, + RGB888, + RGBA8888 + } + + public enum TextureFilter { + Nearest, + Linear, + MipMap, + MipMapNearestNearest, + MipMapLinearNearest, + MipMapNearestLinear, + MipMapLinearLinear + } + + public enum TextureWrap { + MirroredRepeat, + ClampToEdge, + Repeat + } + + public class AtlasPage { + public string name; + public Format format; + public TextureFilter minFilter; + public TextureFilter magFilter; + public TextureWrap uWrap; + public TextureWrap vWrap; + public Object rendererObject; + public int width, height; + } + + public class AtlasRegion { + public AtlasPage page; + public string name; + public int x, y, width, height; + public float u, v, u2, v2; + public float offsetX, offsetY; + public int originalWidth, originalHeight; + public int index; + public bool rotate; + public int[] splits; + public int[] pads; + } + + public interface TextureLoader { + void Load (AtlasPage page, string path); + void Unload (Object texture); + } +} diff --git a/Assets/Spine/spine-csharp/Atlas.cs.meta b/Assets/Spine/spine-csharp/Atlas.cs.meta new file mode 100644 index 0000000..8fcbba5 --- /dev/null +++ b/Assets/Spine/spine-csharp/Atlas.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 60626307629cc034bafd42c53a901fff +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments.meta b/Assets/Spine/spine-csharp/Attachments.meta new file mode 100644 index 0000000..e1b89fe --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2afe1c6b912aac54abb5925ca4ac52c2 +folderAsset: yes +timeCreated: 1456265152 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs b/Assets/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs new file mode 100644 index 0000000..b9ebdf5 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs @@ -0,0 +1,109 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + + /// + /// An AttachmentLoader that configures attachments using texture regions from an Atlas. + /// See Loading Skeleton Data in the Spine Runtimes Guide. + /// + public class AtlasAttachmentLoader : AttachmentLoader { + private Atlas[] atlasArray; + + public AtlasAttachmentLoader (params Atlas[] atlasArray) { + if (atlasArray == null) throw new ArgumentNullException("atlas array cannot be null."); + this.atlasArray = atlasArray; + } + + public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { + AtlasRegion region = FindRegion(path); + if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name)); + RegionAttachment attachment = new RegionAttachment(name); + attachment.RendererObject = region; + attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + } + + public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { + AtlasRegion region = FindRegion(path); + if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name)); + MeshAttachment attachment = new MeshAttachment(name); + attachment.RendererObject = region; + attachment.RegionU = region.u; + attachment.RegionV = region.v; + attachment.RegionU2 = region.u2; + attachment.RegionV2 = region.v2; + attachment.RegionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + return new BoundingBoxAttachment(name); + } + + public PathAttachment NewPathAttachment (Skin skin, string name) { + return new PathAttachment(name); + } + + public PointAttachment NewPointAttachment (Skin skin, string name) { + return new PointAttachment(name); + } + + public ClippingAttachment NewClippingAttachment(Skin skin, string name) { + return new ClippingAttachment(name); + } + + public AtlasRegion FindRegion (string name) { + AtlasRegion region; + + for (int i = 0; i < atlasArray.Length; i++) { + region = atlasArray[i].FindRegion(name); + if (region != null) + return region; + } + + return null; + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs.meta b/Assets/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs.meta new file mode 100644 index 0000000..7b0e50e --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3e6ff30e27c28344bad3e67d308c94cd +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/Attachment.cs b/Assets/Spine/spine-csharp/Attachments/Attachment.cs new file mode 100644 index 0000000..e109659 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/Attachment.cs @@ -0,0 +1,50 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + abstract public class Attachment { + public string Name { get; private set; } + + protected Attachment (string name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null"); + Name = name; + } + + override public string ToString () { + return Name; + } + } + + public interface IHasRendererObject { + object RendererObject { get; } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/Attachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/Attachment.cs.meta new file mode 100644 index 0000000..f396651 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/Attachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 05b56321b2ddd8145a888746bc6ab917 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/AttachmentLoader.cs b/Assets/Spine/spine-csharp/Attachments/AttachmentLoader.cs new file mode 100644 index 0000000..25cbe28 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/AttachmentLoader.cs @@ -0,0 +1,49 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine { + public interface AttachmentLoader { + /// May be null to not load any attachment. + RegionAttachment NewRegionAttachment (Skin skin, string name, string path); + + /// May be null to not load any attachment. + MeshAttachment NewMeshAttachment (Skin skin, string name, string path); + + /// May be null to not load any attachment. + BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name); + + /// May be null to not load any attachment + PathAttachment NewPathAttachment (Skin skin, string name); + + PointAttachment NewPointAttachment (Skin skin, string name); + + ClippingAttachment NewClippingAttachment (Skin skin, string name); + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/AttachmentLoader.cs.meta b/Assets/Spine/spine-csharp/Attachments/AttachmentLoader.cs.meta new file mode 100644 index 0000000..6e765cf --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/AttachmentLoader.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 95466a4f5a30dca4aa69e8ee7df8ae85 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/AttachmentType.cs b/Assets/Spine/spine-csharp/Attachments/AttachmentType.cs new file mode 100644 index 0000000..fd94521 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/AttachmentType.cs @@ -0,0 +1,35 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine { + public enum AttachmentType { + Region, Boundingbox, Mesh, Linkedmesh, Path, Point, Clipping + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/AttachmentType.cs.meta b/Assets/Spine/spine-csharp/Attachments/AttachmentType.cs.meta new file mode 100644 index 0000000..3eb0eb4 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/AttachmentType.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d6b1941960a9f6f47be3e865554d8695 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/BoundingBoxAttachment.cs b/Assets/Spine/spine-csharp/Attachments/BoundingBoxAttachment.cs new file mode 100644 index 0000000..641c9e9 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/BoundingBoxAttachment.cs @@ -0,0 +1,40 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// Attachment that has a polygon for bounds checking. + public class BoundingBoxAttachment : VertexAttachment { + public BoundingBoxAttachment (string name) + : base(name) { + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/BoundingBoxAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/BoundingBoxAttachment.cs.meta new file mode 100644 index 0000000..eba3432 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/BoundingBoxAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cd8ad8fc0f5bce448ba26d096ab32e85 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/ClippingAttachment.cs b/Assets/Spine/spine-csharp/Attachments/ClippingAttachment.cs new file mode 100644 index 0000000..2f845bf --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/ClippingAttachment.cs @@ -0,0 +1,42 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class ClippingAttachment : VertexAttachment { + internal SlotData endSlot; + + public SlotData EndSlot { get { return endSlot; } set { endSlot = value; } } + + public ClippingAttachment(string name) : base(name) { + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/ClippingAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/ClippingAttachment.cs.meta new file mode 100644 index 0000000..fb2404f --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/ClippingAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3380954b107f38b4c85a4cdfeceace42 +timeCreated: 1492744746 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/MeshAttachment.cs b/Assets/Spine/spine-csharp/Attachments/MeshAttachment.cs new file mode 100644 index 0000000..f23bbc4 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/MeshAttachment.cs @@ -0,0 +1,120 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// Attachment that displays a texture region using a mesh. + public class MeshAttachment : VertexAttachment, IHasRendererObject { + internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight; + private MeshAttachment parentMesh; + internal float[] uvs, regionUVs; + internal int[] triangles; + internal float r = 1, g = 1, b = 1, a = 1; + internal int hulllength; + internal bool inheritDeform; + + public int HullLength { get { return hulllength; } set { hulllength = value; } } + public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } } + /// The UV pair for each vertex, normalized within the entire texture. + public float[] UVs { get { return uvs; } set { uvs = value; } } + public int[] Triangles { get { return triangles; } set { triangles = value; } } + + public float R { get { return r; } set { r = value; } } + public float G { get { return g; } set { g = value; } } + public float B { get { return b; } set { b = value; } } + public float A { get { return a; } set { a = value; } } + + public string Path { get; set; } + public object RendererObject { get; set; } + public float RegionU { get; set; } + public float RegionV { get; set; } + public float RegionU2 { get; set; } + public float RegionV2 { get; set; } + public bool RegionRotate { get; set; } + public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } } + public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated. + public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } } + public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size. + public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } } + public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size. + + public bool InheritDeform { get { return inheritDeform; } set { inheritDeform = value; } } + + public MeshAttachment ParentMesh { + get { return parentMesh; } + set { + parentMesh = value; + if (value != null) { + bones = value.bones; + vertices = value.vertices; + worldVerticesLength = value.worldVerticesLength; + regionUVs = value.regionUVs; + triangles = value.triangles; + HullLength = value.HullLength; + Edges = value.Edges; + Width = value.Width; + Height = value.Height; + } + } + } + + // Nonessential. + public int[] Edges { get; set; } + public float Width { get; set; } + public float Height { get; set; } + + public MeshAttachment (string name) + : base(name) { + } + + public void UpdateUVs () { + float u = RegionU, v = RegionV, width = RegionU2 - RegionU, height = RegionV2 - RegionV; + float[] regionUVs = this.regionUVs; + if (this.uvs == null || this.uvs.Length != regionUVs.Length) this.uvs = new float[regionUVs.Length]; + float[] uvs = this.uvs; + if (RegionRotate) { + for (int i = 0, n = uvs.Length; i < n; i += 2) { + uvs[i] = u + regionUVs[i + 1] * width; + uvs[i + 1] = v + height - regionUVs[i] * height; + } + } else { + for (int i = 0, n = uvs.Length; i < n; i += 2) { + uvs[i] = u + regionUVs[i] * width; + uvs[i + 1] = v + regionUVs[i + 1] * height; + } + } + } + + override public bool ApplyDeform (VertexAttachment sourceAttachment) { + return this == sourceAttachment || (inheritDeform && parentMesh == sourceAttachment); + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/MeshAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/MeshAttachment.cs.meta new file mode 100644 index 0000000..72ca3dd --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/MeshAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b7f7514a003143844b6d01ecc93ed4d5 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/PathAttachment.cs b/Assets/Spine/spine-csharp/Attachments/PathAttachment.cs new file mode 100644 index 0000000..9c067c8 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/PathAttachment.cs @@ -0,0 +1,48 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + public class PathAttachment : VertexAttachment { + internal float[] lengths; + internal bool closed, constantSpeed; + + /// The length in the setup pose from the start of the path to the end of each curve. + public float[] Lengths { get { return lengths; } set { lengths = value; } } + public bool Closed { get { return closed; } set { closed = value; } } + public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } } + + public PathAttachment (String name) + : base(name) { + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/PathAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/PathAttachment.cs.meta new file mode 100644 index 0000000..f9feef9 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/PathAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c77d9bf384a1e9f41966464e7e3b4870 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/PointAttachment.cs b/Assets/Spine/spine-csharp/Attachments/PointAttachment.cs new file mode 100644 index 0000000..b74d51f --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/PointAttachment.cs @@ -0,0 +1,61 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine { + /// + /// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be + /// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a + /// skin. + ///

+ /// See Point Attachments in the Spine User Guide. + ///

+ public class PointAttachment : Attachment { + internal float x, y, rotation; + public float X { get { return x; } set { x = value; } } + public float Y { get { return y; } set { y = value; } } + public float Rotation { get { return rotation; } set { rotation = value; } } + + public PointAttachment (string name) + : base(name) { + } + + public void ComputeWorldPosition (Bone bone, out float ox, out float oy) { + bone.LocalToWorld(this.x, this.y, out ox, out oy); + } + + public float ComputeWorldRotation (Bone bone) { + float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation); + float ix = cos * bone.a + sin * bone.b; + float iy = cos * bone.c + sin * bone.d; + return MathUtils.Atan2(iy, ix) * MathUtils.RadDeg; + } + } +} + diff --git a/Assets/Spine/spine-csharp/Attachments/PointAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/PointAttachment.cs.meta new file mode 100644 index 0000000..7e28782 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/PointAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4fdde4cc4df0952468946f4f913dcb36 +timeCreated: 1485603478 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/RegionAttachment.cs b/Assets/Spine/spine-csharp/Attachments/RegionAttachment.cs new file mode 100644 index 0000000..8b73134 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/RegionAttachment.cs @@ -0,0 +1,183 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// Attachment that displays a texture region. + public class RegionAttachment : Attachment, IHasRendererObject { + public const int BLX = 0; + public const int BLY = 1; + public const int ULX = 2; + public const int ULY = 3; + public const int URX = 4; + public const int URY = 5; + public const int BRX = 6; + public const int BRY = 7; + + internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height; + internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight; + internal float[] offset = new float[8], uvs = new float[8]; + internal float r = 1, g = 1, b = 1, a = 1; + + public float X { get { return x; } set { x = value; } } + public float Y { get { return y; } set { y = value; } } + public float Rotation { get { return rotation; } set { rotation = value; } } + public float ScaleX { get { return scaleX; } set { scaleX = value; } } + public float ScaleY { get { return scaleY; } set { scaleY = value; } } + public float Width { get { return width; } set { width = value; } } + public float Height { get { return height; } set { height = value; } } + + public float R { get { return r; } set { r = value; } } + public float G { get { return g; } set { g = value; } } + public float B { get { return b; } set { b = value; } } + public float A { get { return a; } set { a = value; } } + + public string Path { get; set; } + public object RendererObject { get; set; } + public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } } + public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated. + public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } } + public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size. + public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } } + public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size. + + public float[] Offset { get { return offset; } } + public float[] UVs { get { return uvs; } } + + public RegionAttachment (string name) + : base(name) { + } + + public void UpdateOffset () { + float width = this.width; + float height = this.height; + float localX2 = width * 0.5f; + float localY2 = height * 0.5f; + float localX = -localX2; + float localY = -localY2; + if (regionOriginalWidth != 0) { // if (region != null) + localX += regionOffsetX / regionOriginalWidth * width; + localY += regionOffsetY / regionOriginalHeight * height; + localX2 -= (regionOriginalWidth - regionOffsetX - regionWidth) / regionOriginalWidth * width; + localY2 -= (regionOriginalHeight - regionOffsetY - regionHeight) / regionOriginalHeight * height; + } + float scaleX = this.scaleX; + float scaleY = this.scaleY; + localX *= scaleX; + localY *= scaleY; + localX2 *= scaleX; + localY2 *= scaleY; + float rotation = this.rotation; + float cos = MathUtils.CosDeg(rotation); + float sin = MathUtils.SinDeg(rotation); + float x = this.x; + float y = this.y; + float localXCos = localX * cos + x; + float localXSin = localX * sin; + float localYCos = localY * cos + y; + float localYSin = localY * sin; + float localX2Cos = localX2 * cos + x; + float localX2Sin = localX2 * sin; + float localY2Cos = localY2 * cos + y; + float localY2Sin = localY2 * sin; + float[] offset = this.offset; + offset[BLX] = localXCos - localYSin; + offset[BLY] = localYCos + localXSin; + offset[ULX] = localXCos - localY2Sin; + offset[ULY] = localY2Cos + localXSin; + offset[URX] = localX2Cos - localY2Sin; + offset[URY] = localY2Cos + localX2Sin; + offset[BRX] = localX2Cos - localYSin; + offset[BRY] = localYCos + localX2Sin; + } + + public void SetUVs (float u, float v, float u2, float v2, bool rotate) { + float[] uvs = this.uvs; + // UV values differ from RegionAttachment.java + if (rotate) { + uvs[URX] = u; + uvs[URY] = v2; + uvs[BRX] = u; + uvs[BRY] = v; + uvs[BLX] = u2; + uvs[BLY] = v; + uvs[ULX] = u2; + uvs[ULY] = v2; + } else { + uvs[ULX] = u; + uvs[ULY] = v2; + uvs[URX] = u; + uvs[URY] = v; + uvs[BRX] = u2; + uvs[BRY] = v; + uvs[BLX] = u2; + uvs[BLY] = v2; + } + } + + /// Transforms the attachment's four vertices to world coordinates. + /// The parent bone. + /// The output world vertices. Must have a length greater than or equal to offset + 8. + /// The worldVertices index to begin writing values. + /// The number of worldVertices entries between the value pairs written. + public void ComputeWorldVertices (Bone bone, float[] worldVertices, int offset, int stride = 2) { + float[] vertexOffset = this.offset; + float bwx = bone.worldX, bwy = bone.worldY; + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + float offsetX, offsetY; + + // Vertex order is different from RegionAttachment.java + offsetX = vertexOffset[BRX]; // 0 + offsetY = vertexOffset[BRY]; // 1 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + offset += stride; + + offsetX = vertexOffset[BLX]; // 2 + offsetY = vertexOffset[BLY]; // 3 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + offset += stride; + + offsetX = vertexOffset[ULX]; // 4 + offsetY = vertexOffset[ULY]; // 5 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + offset += stride; + + offsetX = vertexOffset[URX]; // 6 + offsetY = vertexOffset[URY]; // 7 + worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br + worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; + //offset += stride; + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/RegionAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/RegionAttachment.cs.meta new file mode 100644 index 0000000..0477ce4 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/RegionAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 89cefdd024734a941952a05d2b5dff71 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Attachments/VertexAttachment.cs b/Assets/Spine/spine-csharp/Attachments/VertexAttachment.cs new file mode 100644 index 0000000..16b9b0e --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/VertexAttachment.cs @@ -0,0 +1,130 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// >An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices. + public class VertexAttachment : Attachment { + static int nextID = 0; + static readonly Object nextIdLock = new Object(); + + internal readonly int id; + internal int[] bones; + internal float[] vertices; + internal int worldVerticesLength; + + /// Gets a unique ID for this attachment. + public int Id { get { return id; } } + public int[] Bones { get { return bones; } set { bones = value; } } + public float[] Vertices { get { return vertices; } set { vertices = value; } } + public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } } + + public VertexAttachment (string name) + : base(name) { + + lock (VertexAttachment.nextIdLock) { + id = (VertexAttachment.nextID++ & 65535) << 11; + } + } + + public void ComputeWorldVertices (Slot slot, float[] worldVertices) { + ComputeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0); + } + + /// Transforms local vertices to world coordinates. + /// The index of the first value to transform. Each vertex has 2 values, x and y. + /// The number of world vertex values to output. Must be less than or equal to - start. + /// The output world vertices. Must have a length greater than or equal to + . + /// The index to begin writing values. + /// The number of entries between the value pairs written. + public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride = 2) { + count = offset + (count >> 1) * stride; + Skeleton skeleton = slot.bone.skeleton; + var deformArray = slot.attachmentVertices; + float[] vertices = this.vertices; + int[] bones = this.bones; + if (bones == null) { + if (deformArray.Count > 0) vertices = deformArray.Items; + Bone bone = slot.bone; + float x = bone.worldX, y = bone.worldY; + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + for (int vv = start, w = offset; w < count; vv += 2, w += stride) { + float vx = vertices[vv], vy = vertices[vv + 1]; + worldVertices[w] = vx * a + vy * b + x; + worldVertices[w + 1] = vx * c + vy * d + y; + } + return; + } + int v = 0, skip = 0; + for (int i = 0; i < start; i += 2) { + int n = bones[v]; + v += n + 1; + skip += n; + } + var skeletonBones = skeleton.bones.Items; + if (deformArray.Count == 0) { + for (int w = offset, b = skip * 3; w < count; w += stride) { + float wx = 0, wy = 0; + int n = bones[v++]; + n += v; + for (; v < n; v++, b += 3) { + Bone bone = skeletonBones[bones[v]]; + float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; + wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } else { + float[] deform = deformArray.Items; + for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { + float wx = 0, wy = 0; + int n = bones[v++]; + n += v; + for (; v < n; v++, b += 3, f += 2) { + Bone bone = skeletonBones[bones[v]]; + float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; + wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } + } + + /// Returns true if a deform originally applied to the specified attachment should be applied to this attachment. + virtual public bool ApplyDeform (VertexAttachment sourceAttachment) { + return this == sourceAttachment; + } + } +} diff --git a/Assets/Spine/spine-csharp/Attachments/VertexAttachment.cs.meta b/Assets/Spine/spine-csharp/Attachments/VertexAttachment.cs.meta new file mode 100644 index 0000000..8481a70 --- /dev/null +++ b/Assets/Spine/spine-csharp/Attachments/VertexAttachment.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8b40cfb462a8b774891e1604e5360d32 +timeCreated: 1466772712 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/BlendMode.cs b/Assets/Spine/spine-csharp/BlendMode.cs new file mode 100644 index 0000000..52b0d44 --- /dev/null +++ b/Assets/Spine/spine-csharp/BlendMode.cs @@ -0,0 +1,35 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine { + public enum BlendMode { + Normal, Additive, Multiply, Screen + } +} diff --git a/Assets/Spine/spine-csharp/BlendMode.cs.meta b/Assets/Spine/spine-csharp/BlendMode.cs.meta new file mode 100644 index 0000000..c60eb23 --- /dev/null +++ b/Assets/Spine/spine-csharp/BlendMode.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b08ef68b8e39f40498ef24ef12cca281 +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Bone.cs b/Assets/Spine/spine-csharp/Bone.cs new file mode 100644 index 0000000..a34a572 --- /dev/null +++ b/Assets/Spine/spine-csharp/Bone.cs @@ -0,0 +1,384 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// + /// Stores a bone's current pose. + /// + /// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a + /// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a + /// constraint or application code modifies the world transform after it was computed from the local transform. + /// + /// + public class Bone : IUpdatable { + static public bool yDown; + + internal BoneData data; + internal Skeleton skeleton; + internal Bone parent; + internal ExposedList children = new ExposedList(); + internal float x, y, rotation, scaleX, scaleY, shearX, shearY; + internal float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY; + internal bool appliedValid; + + internal float a, b, worldX; + internal float c, d, worldY; + +// internal float worldSignX, worldSignY; +// public float WorldSignX { get { return worldSignX; } } +// public float WorldSignY { get { return worldSignY; } } + + internal bool sorted; + + public BoneData Data { get { return data; } } + public Skeleton Skeleton { get { return skeleton; } } + public Bone Parent { get { return parent; } } + public ExposedList Children { get { return children; } } + /// The local X translation. + public float X { get { return x; } set { x = value; } } + /// The local Y translation. + public float Y { get { return y; } set { y = value; } } + /// The local rotation. + public float Rotation { get { return rotation; } set { rotation = value; } } + + /// The local scaleX. + public float ScaleX { get { return scaleX; } set { scaleX = value; } } + + /// The local scaleY. + public float ScaleY { get { return scaleY; } set { scaleY = value; } } + + /// The local shearX. + public float ShearX { get { return shearX; } set { shearX = value; } } + + /// The local shearY. + public float ShearY { get { return shearY; } set { shearY = value; } } + + /// The rotation, as calculated by any constraints. + public float AppliedRotation { get { return arotation; } set { arotation = value; } } + + /// The applied local x translation. + public float AX { get { return ax; } set { ax = value; } } + + /// The applied local y translation. + public float AY { get { return ay; } set { ay = value; } } + + /// The applied local scaleX. + public float AScaleX { get { return ascaleX; } set { ascaleX = value; } } + + /// The applied local scaleY. + public float AScaleY { get { return ascaleY; } set { ascaleY = value; } } + + /// The applied local shearX. + public float AShearX { get { return ashearX; } set { ashearX = value; } } + + /// The applied local shearY. + public float AShearY { get { return ashearY; } set { ashearY = value; } } + + public float A { get { return a; } } + public float B { get { return b; } } + public float C { get { return c; } } + public float D { get { return d; } } + + public float WorldX { get { return worldX; } } + public float WorldY { get { return worldY; } } + public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.RadDeg; } } + public float WorldRotationY { get { return MathUtils.Atan2(d, b) * MathUtils.RadDeg; } } + + /// Returns the magnitide (always positive) of the world scale X. + public float WorldScaleX { get { return (float)Math.Sqrt(a * a + c * c); } } + /// Returns the magnitide (always positive) of the world scale Y. + public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } } + + /// May be null. + public Bone (BoneData data, Skeleton skeleton, Bone parent) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + this.data = data; + this.skeleton = skeleton; + this.parent = parent; + SetToSetupPose(); + } + + /// Same as . This method exists for Bone to implement . + public void Update () { + UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY); + } + + /// Computes the world transform using the parent bone and this bone's local transform. + public void UpdateWorldTransform () { + UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY); + } + + /// Computes the world transform using the parent bone and the specified local transform. + public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) { + ax = x; + ay = y; + arotation = rotation; + ascaleX = scaleX; + ascaleY = scaleY; + ashearX = shearX; + ashearY = shearY; + appliedValid = true; + Skeleton skeleton = this.skeleton; + + Bone parent = this.parent; + if (parent == null) { // Root bone. + float rotationY = rotation + 90 + shearY; + float la = MathUtils.CosDeg(rotation + shearX) * scaleX; + float lb = MathUtils.CosDeg(rotationY) * scaleY; + float lc = MathUtils.SinDeg(rotation + shearX) * scaleX; + float ld = MathUtils.SinDeg(rotationY) * scaleY; + if (skeleton.flipX) { + x = -x; + la = -la; + lb = -lb; + } + if (skeleton.flipY != yDown) { + y = -y; + lc = -lc; + ld = -ld; + } + a = la; + b = lb; + c = lc; + d = ld; + worldX = x + skeleton.x; + worldY = y + skeleton.y; + return; + } + + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + worldX = pa * x + pb * y + parent.worldX; + worldY = pc * x + pd * y + parent.worldY; + + switch (data.transformMode) { + case TransformMode.Normal: { + float rotationY = rotation + 90 + shearY; + float la = MathUtils.CosDeg(rotation + shearX) * scaleX; + float lb = MathUtils.CosDeg(rotationY) * scaleY; + float lc = MathUtils.SinDeg(rotation + shearX) * scaleX; + float ld = MathUtils.SinDeg(rotationY) * scaleY; + a = pa * la + pb * lc; + b = pa * lb + pb * ld; + c = pc * la + pd * lc; + d = pc * lb + pd * ld; + return; + } + case TransformMode.OnlyTranslation: { + float rotationY = rotation + 90 + shearY; + a = MathUtils.CosDeg(rotation + shearX) * scaleX; + b = MathUtils.CosDeg(rotationY) * scaleY; + c = MathUtils.SinDeg(rotation + shearX) * scaleX; + d = MathUtils.SinDeg(rotationY) * scaleY; + break; + } + case TransformMode.NoRotationOrReflection: { + float s = pa * pa + pc * pc, prx; + if (s > 0.0001f) { + s = Math.Abs(pa * pd - pb * pc) / s; + pb = pc * s; + pd = pa * s; + prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg; + } else { + pa = 0; + pc = 0; + prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg; + } + float rx = rotation + shearX - prx; + float ry = rotation + shearY - prx + 90; + float la = MathUtils.CosDeg(rx) * scaleX; + float lb = MathUtils.CosDeg(ry) * scaleY; + float lc = MathUtils.SinDeg(rx) * scaleX; + float ld = MathUtils.SinDeg(ry) * scaleY; + a = pa * la - pb * lc; + b = pa * lb - pb * ld; + c = pc * la + pd * lc; + d = pc * lb + pd * ld; + break; + } + case TransformMode.NoScale: + case TransformMode.NoScaleOrReflection: { + float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation); + float za = pa * cos + pb * sin; + float zc = pc * cos + pd * sin; + float s = (float)Math.Sqrt(za * za + zc * zc); + if (s > 0.00001f) s = 1 / s; + za *= s; + zc *= s; + s = (float)Math.Sqrt(za * za + zc * zc); + float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za); + float zb = MathUtils.Cos(r) * s; + float zd = MathUtils.Sin(r) * s; + float la = MathUtils.CosDeg(shearX) * scaleX; + float lb = MathUtils.CosDeg(90 + shearY) * scaleY; + float lc = MathUtils.SinDeg(shearX) * scaleX; + float ld = MathUtils.SinDeg(90 + shearY) * scaleY; + if (data.transformMode != TransformMode.NoScaleOrReflection? pa * pd - pb* pc< 0 : skeleton.flipX != skeleton.flipY) { + zb = -zb; + zd = -zd; + } + a = za * la + zb * lc; + b = za * lb + zb * ld; + c = zc * la + zd * lc; + d = zc * lb + zd * ld; + return; + } + } + + if (skeleton.flipX) { + a = -a; + b = -b; + } + if (skeleton.flipY != Bone.yDown) { + c = -c; + d = -d; + } + } + + public void SetToSetupPose () { + BoneData data = this.data; + x = data.x; + y = data.y; + rotation = data.rotation; + scaleX = data.scaleX; + scaleY = data.scaleY; + shearX = data.shearX; + shearY = data.shearY; + } + + /// + /// Computes the individual applied transform values from the world transform. This can be useful to perform processing using + /// the applied transform after the world transform has been modified directly (eg, by a constraint).. + /// + /// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. + /// + internal void UpdateAppliedTransform () { + appliedValid = true; + Bone parent = this.parent; + if (parent == null) { + ax = worldX; + ay = worldY; + arotation = MathUtils.Atan2(c, a) * MathUtils.RadDeg; + ascaleX = (float)Math.Sqrt(a * a + c * c); + ascaleY = (float)Math.Sqrt(b * b + d * d); + ashearX = 0; + ashearY = MathUtils.Atan2(a * b + c * d, a * d - b * c) * MathUtils.RadDeg; + return; + } + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + float pid = 1 / (pa * pd - pb * pc); + float dx = worldX - parent.worldX, dy = worldY - parent.worldY; + ax = (dx * pd * pid - dy * pb * pid); + ay = (dy * pa * pid - dx * pc * pid); + float ia = pid * pd; + float id = pid * pa; + float ib = pid * pb; + float ic = pid * pc; + float ra = ia * a - ib * c; + float rb = ia * b - ib * d; + float rc = id * c - ic * a; + float rd = id * d - ic * b; + ashearX = 0; + ascaleX = (float)Math.Sqrt(ra * ra + rc * rc); + if (ascaleX > 0.0001f) { + float det = ra * rd - rb * rc; + ascaleY = det / ascaleX; + ashearY = MathUtils.Atan2(ra * rb + rc * rd, det) * MathUtils.RadDeg; + arotation = MathUtils.Atan2(rc, ra) * MathUtils.RadDeg; + } else { + ascaleX = 0; + ascaleY = (float)Math.Sqrt(rb * rb + rd * rd); + ashearY = 0; + arotation = 90 - MathUtils.Atan2(rd, rb) * MathUtils.RadDeg; + } + } + + public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) { + float a = this.a, b = this.b, c = this.c, d = this.d; + float invDet = 1 / (a * d - b * c); + float x = worldX - this.worldX, y = worldY - this.worldY; + localX = (x * d * invDet - y * b * invDet); + localY = (y * a * invDet - x * c * invDet); + } + + public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) { + worldX = localX * a + localY * b + this.worldX; + worldY = localX * c + localY * d + this.worldY; + } + + public float WorldToLocalRotationX { + get { + Bone parent = this.parent; + if (parent == null) return arotation; + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c; + return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.RadDeg; + } + } + + public float WorldToLocalRotationY { + get { + Bone parent = this.parent; + if (parent == null) return arotation; + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d; + return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.RadDeg; + } + } + + public float WorldToLocalRotation (float worldRotation) { + float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation); + return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg; + } + + public float LocalToWorldRotation (float localRotation) { + float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation); + return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg; + } + + /// + /// Rotates the world transform the specified amount and sets isAppliedValid to false. + /// + /// Degrees. + public void RotateWorld (float degrees) { + float a = this.a, b = this.b, c = this.c, d = this.d; + float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees); + this.a = cos * a - sin * c; + this.b = cos * b - sin * d; + this.c = sin * a + cos * c; + this.d = sin * b + cos * d; + appliedValid = false; + } + + override public string ToString () { + return data.name; + } + } +} diff --git a/Assets/Spine/spine-csharp/Bone.cs.meta b/Assets/Spine/spine-csharp/Bone.cs.meta new file mode 100644 index 0000000..687d6b9 --- /dev/null +++ b/Assets/Spine/spine-csharp/Bone.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ed00e3a4b386a964fb0f1c7ffd5544e5 +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/BoneData.cs b/Assets/Spine/spine-csharp/BoneData.cs new file mode 100644 index 0000000..2c267e9 --- /dev/null +++ b/Assets/Spine/spine-csharp/BoneData.cs @@ -0,0 +1,100 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class BoneData { + internal int index; + internal string name; + internal BoneData parent; + internal float length; + internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; + internal TransformMode transformMode = TransformMode.Normal; + + /// The index of the bone in Skeleton.Bones + public int Index { get { return index; } } + + /// The name of the bone, which is unique within the skeleton. + public string Name { get { return name; } } + + /// May be null. + public BoneData Parent { get { return parent; } } + + public float Length { get { return length; } set { length = value; } } + + /// Local X translation. + public float X { get { return x; } set { x = value; } } + + /// Local Y translation. + public float Y { get { return y; } set { y = value; } } + + /// Local rotation. + public float Rotation { get { return rotation; } set { rotation = value; } } + + /// Local scaleX. + public float ScaleX { get { return scaleX; } set { scaleX = value; } } + + /// Local scaleY. + public float ScaleY { get { return scaleY; } set { scaleY = value; } } + + /// Local shearX. + public float ShearX { get { return shearX; } set { shearX = value; } } + + /// Local shearY. + public float ShearY { get { return shearY; } set { shearY = value; } } + + /// The transform mode for how parent world transforms affect this bone. + public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } } + + /// May be null. + public BoneData (int index, string name, BoneData parent) { + if (index < 0) throw new ArgumentException("index must be >= 0", "index"); + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.index = index; + this.name = name; + this.parent = parent; + } + + override public string ToString () { + return name; + } + } + + [Flags] + public enum TransformMode { + //0000 0 Flip Scale Rotation + Normal = 0, // 0000 + OnlyTranslation = 7, // 0111 + NoRotationOrReflection = 1, // 0001 + NoScale = 2, // 0010 + NoScaleOrReflection = 6, // 0110 + } +} diff --git a/Assets/Spine/spine-csharp/BoneData.cs.meta b/Assets/Spine/spine-csharp/BoneData.cs.meta new file mode 100644 index 0000000..006e3ec --- /dev/null +++ b/Assets/Spine/spine-csharp/BoneData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2cf831005966832449a5de742752e578 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Event.cs b/Assets/Spine/spine-csharp/Event.cs new file mode 100644 index 0000000..82a3556 --- /dev/null +++ b/Assets/Spine/spine-csharp/Event.cs @@ -0,0 +1,60 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// Stores the current pose values for an Event. + public class Event { + internal readonly EventData data; + internal readonly float time; + internal int intValue; + internal float floatValue; + internal string stringValue; + + public EventData Data { get { return data; } } + /// The animation time this event was keyed. + public float Time { get { return time; } } + + public int Int { get { return intValue; } set { intValue = value; } } + public float Float { get { return floatValue; } set { floatValue = value; } } + public string String { get { return stringValue; } set { stringValue = value; } } + + public Event (float time, EventData data) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + this.time = time; + this.data = data; + } + + override public string ToString () { + return this.data.Name; + } + } +} diff --git a/Assets/Spine/spine-csharp/Event.cs.meta b/Assets/Spine/spine-csharp/Event.cs.meta new file mode 100644 index 0000000..dbdbc59 --- /dev/null +++ b/Assets/Spine/spine-csharp/Event.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: dec0d9d780605944eb4514125ab6350b +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/EventData.cs b/Assets/Spine/spine-csharp/EventData.cs new file mode 100644 index 0000000..32881f7 --- /dev/null +++ b/Assets/Spine/spine-csharp/EventData.cs @@ -0,0 +1,53 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + /// Stores the setup pose values for an Event. + public class EventData { + internal string name; + + /// The name of the event, which is unique within the skeleton. + public string Name { get { return name; } } + public int Int { get; set; } + public float Float { get; set; } + public string String { get; set; } + + public EventData (string name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.name = name; + } + + override public string ToString () { + return Name; + } + } +} diff --git a/Assets/Spine/spine-csharp/EventData.cs.meta b/Assets/Spine/spine-csharp/EventData.cs.meta new file mode 100644 index 0000000..941b359 --- /dev/null +++ b/Assets/Spine/spine-csharp/EventData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 37bbfb9fb268a644ba75052961a42b81 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/ExposedList.cs b/Assets/Spine/spine-csharp/ExposedList.cs new file mode 100644 index 0000000..5d5d9e7 --- /dev/null +++ b/Assets/Spine/spine-csharp/ExposedList.cs @@ -0,0 +1,624 @@ +// +// System.Collections.Generic.List +// +// Authors: +// Ben Maurer (bmaurer@ximian.com) +// Martin Baulig (martin@ximian.com) +// Carlos Alberto Cortez (calberto.cortez@gmail.com) +// David Waite (mass@akuma.org) +// +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// Copyright (C) 2005 David Waite +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Spine { + [DebuggerDisplay("Count={Count}")] + public class ExposedList : IEnumerable { + public T[] Items; + public int Count; + private const int DefaultCapacity = 4; + private static readonly T[] EmptyArray = new T[0]; + private int version; + + public ExposedList () { + Items = EmptyArray; + } + + public ExposedList (IEnumerable collection) { + CheckCollection(collection); + + // initialize to needed size (if determinable) + ICollection c = collection as ICollection; + if (c == null) { + Items = EmptyArray; + AddEnumerable(collection); + } else { + Items = new T[c.Count]; + AddCollection(c); + } + } + + public ExposedList (int capacity) { + if (capacity < 0) + throw new ArgumentOutOfRangeException("capacity"); + Items = new T[capacity]; + } + + internal ExposedList (T[] data, int size) { + Items = data; + Count = size; + } + + public void Add (T item) { + // If we check to see if we need to grow before trying to grow + // we can speed things up by 25% + if (Count == Items.Length) + GrowIfNeeded(1); + Items[Count++] = item; + version++; + } + + public void GrowIfNeeded (int newCount) { + int minimumSize = Count + newCount; + if (minimumSize > Items.Length) + Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize); + } + + public ExposedList Resize (int newSize) { + int itemsLength = Items.Length; + var oldItems = Items; + if (newSize > itemsLength) { + Array.Resize(ref Items, newSize); +// var newItems = new T[newSize]; +// Array.Copy(oldItems, newItems, Count); +// Items = newItems; + } else if (newSize < itemsLength) { + // Allow nulling of T reference type to allow GC. + for (int i = newSize; i < itemsLength; i++) + oldItems[i] = default(T); + } + Count = newSize; + return this; + } + + public void EnsureCapacity (int min) { + if (Items.Length < min) { + int newCapacity = Items.Length == 0 ? DefaultCapacity : Items.Length * 2; + //if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; + if (newCapacity < min) newCapacity = min; + Capacity = newCapacity; + } + } + + private void CheckRange (int index, int count) { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + if ((uint)index + (uint)count > (uint)Count) + throw new ArgumentException("index and count exceed length of list"); + } + + private void AddCollection (ICollection collection) { + int collectionCount = collection.Count; + if (collectionCount == 0) + return; + + GrowIfNeeded(collectionCount); + collection.CopyTo(Items, Count); + Count += collectionCount; + } + + private void AddEnumerable (IEnumerable enumerable) { + foreach (T t in enumerable) { + Add(t); + } + } + + public void AddRange (IEnumerable collection) { + CheckCollection(collection); + + ICollection c = collection as ICollection; + if (c != null) + AddCollection(c); + else + AddEnumerable(collection); + version++; + } + + public int BinarySearch (T item) { + return Array.BinarySearch(Items, 0, Count, item); + } + + public int BinarySearch (T item, IComparer comparer) { + return Array.BinarySearch(Items, 0, Count, item, comparer); + } + + public int BinarySearch (int index, int count, T item, IComparer comparer) { + CheckRange(index, count); + return Array.BinarySearch(Items, index, count, item, comparer); + } + + public void Clear (bool clearArray = true) { + if (clearArray) + Array.Clear(Items, 0, Items.Length); + + Count = 0; + version++; + } + + public bool Contains (T item) { + return Array.IndexOf(Items, item, 0, Count) != -1; + } + + public ExposedList ConvertAll (Converter converter) { + if (converter == null) + throw new ArgumentNullException("converter"); + ExposedList u = new ExposedList(Count); + for (int i = 0; i < Count; i++) + u.Items[i] = converter(Items[i]); + + u.Count = Count; + return u; + } + + public void CopyTo (T[] array) { + Array.Copy(Items, 0, array, 0, Count); + } + + public void CopyTo (T[] array, int arrayIndex) { + Array.Copy(Items, 0, array, arrayIndex, Count); + } + + public void CopyTo (int index, T[] array, int arrayIndex, int count) { + CheckRange(index, count); + Array.Copy(Items, index, array, arrayIndex, count); + } + + + + public bool Exists (Predicate match) { + CheckMatch(match); + return GetIndex(0, Count, match) != -1; + } + + public T Find (Predicate match) { + CheckMatch(match); + int i = GetIndex(0, Count, match); + return (i != -1) ? Items[i] : default(T); + } + + private static void CheckMatch (Predicate match) { + if (match == null) + throw new ArgumentNullException("match"); + } + + public ExposedList FindAll (Predicate match) { + CheckMatch(match); + return FindAllList(match); + } + + private ExposedList FindAllList (Predicate match) { + ExposedList results = new ExposedList(); + for (int i = 0; i < Count; i++) + if (match(Items[i])) + results.Add(Items[i]); + + return results; + } + + public int FindIndex (Predicate match) { + CheckMatch(match); + return GetIndex(0, Count, match); + } + + public int FindIndex (int startIndex, Predicate match) { + CheckMatch(match); + CheckIndex(startIndex); + return GetIndex(startIndex, Count - startIndex, match); + } + + public int FindIndex (int startIndex, int count, Predicate match) { + CheckMatch(match); + CheckRange(startIndex, count); + return GetIndex(startIndex, count, match); + } + + private int GetIndex (int startIndex, int count, Predicate match) { + int end = startIndex + count; + for (int i = startIndex; i < end; i++) + if (match(Items[i])) + return i; + + return -1; + } + + public T FindLast (Predicate match) { + CheckMatch(match); + int i = GetLastIndex(0, Count, match); + return i == -1 ? default(T) : Items[i]; + } + + public int FindLastIndex (Predicate match) { + CheckMatch(match); + return GetLastIndex(0, Count, match); + } + + public int FindLastIndex (int startIndex, Predicate match) { + CheckMatch(match); + CheckIndex(startIndex); + return GetLastIndex(0, startIndex + 1, match); + } + + public int FindLastIndex (int startIndex, int count, Predicate match) { + CheckMatch(match); + int start = startIndex - count + 1; + CheckRange(start, count); + return GetLastIndex(start, count, match); + } + + private int GetLastIndex (int startIndex, int count, Predicate match) { + // unlike FindLastIndex, takes regular params for search range + for (int i = startIndex + count; i != startIndex; ) + if (match(Items[--i])) + return i; + return -1; + } + + public void ForEach (Action action) { + if (action == null) + throw new ArgumentNullException("action"); + for (int i = 0; i < Count; i++) + action(Items[i]); + } + + public Enumerator GetEnumerator () { + return new Enumerator(this); + } + + public ExposedList GetRange (int index, int count) { + CheckRange(index, count); + T[] tmpArray = new T[count]; + Array.Copy(Items, index, tmpArray, 0, count); + return new ExposedList(tmpArray, count); + } + + public int IndexOf (T item) { + return Array.IndexOf(Items, item, 0, Count); + } + + public int IndexOf (T item, int index) { + CheckIndex(index); + return Array.IndexOf(Items, item, index, Count - index); + } + + public int IndexOf (T item, int index, int count) { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + if ((uint)index + (uint)count > (uint)Count) + throw new ArgumentOutOfRangeException("index and count exceed length of list"); + + return Array.IndexOf(Items, item, index, count); + } + + private void Shift (int start, int delta) { + if (delta < 0) + start -= delta; + + if (start < Count) + Array.Copy(Items, start, Items, start + delta, Count - start); + + Count += delta; + + if (delta < 0) + Array.Clear(Items, Count, -delta); + } + + private void CheckIndex (int index) { + if (index < 0 || (uint)index > (uint)Count) + throw new ArgumentOutOfRangeException("index"); + } + + public void Insert (int index, T item) { + CheckIndex(index); + if (Count == Items.Length) + GrowIfNeeded(1); + Shift(index, 1); + Items[index] = item; + version++; + } + + private void CheckCollection (IEnumerable collection) { + if (collection == null) + throw new ArgumentNullException("collection"); + } + + public void InsertRange (int index, IEnumerable collection) { + CheckCollection(collection); + CheckIndex(index); + if (collection == this) { + T[] buffer = new T[Count]; + CopyTo(buffer, 0); + GrowIfNeeded(Count); + Shift(index, buffer.Length); + Array.Copy(buffer, 0, Items, index, buffer.Length); + } else { + ICollection c = collection as ICollection; + if (c != null) + InsertCollection(index, c); + else + InsertEnumeration(index, collection); + } + version++; + } + + private void InsertCollection (int index, ICollection collection) { + int collectionCount = collection.Count; + GrowIfNeeded(collectionCount); + + Shift(index, collectionCount); + collection.CopyTo(Items, index); + } + + private void InsertEnumeration (int index, IEnumerable enumerable) { + foreach (T t in enumerable) + Insert(index++, t); + } + + public int LastIndexOf (T item) { + return Array.LastIndexOf(Items, item, Count - 1, Count); + } + + public int LastIndexOf (T item, int index) { + CheckIndex(index); + return Array.LastIndexOf(Items, item, index, index + 1); + } + + public int LastIndexOf (T item, int index, int count) { + if (index < 0) + throw new ArgumentOutOfRangeException("index", index, "index is negative"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", count, "count is negative"); + + if (index - count + 1 < 0) + throw new ArgumentOutOfRangeException("count", count, "count is too large"); + + return Array.LastIndexOf(Items, item, index, count); + } + + public bool Remove (T item) { + int loc = IndexOf(item); + if (loc != -1) + RemoveAt(loc); + + return loc != -1; + } + + public int RemoveAll (Predicate match) { + CheckMatch(match); + int i = 0; + int j = 0; + + // Find the first item to remove + for (i = 0; i < Count; i++) + if (match(Items[i])) + break; + + if (i == Count) + return 0; + + version++; + + // Remove any additional items + for (j = i + 1; j < Count; j++) { + if (!match(Items[j])) + Items[i++] = Items[j]; + } + if (j - i > 0) + Array.Clear(Items, i, j - i); + + Count = i; + return (j - i); + } + + public void RemoveAt (int index) { + if (index < 0 || (uint)index >= (uint)Count) + throw new ArgumentOutOfRangeException("index"); + Shift(index, -1); + Array.Clear(Items, Count, 1); + version++; + } + + // Spine Added Method + // Based on Stack.Pop(); https://referencesource.microsoft.com/#mscorlib/system/collections/stack.cs + /// Pops the last item of the list. If the list is empty, Pop throws an InvalidOperationException. + public T Pop () { + if (Count == 0) + throw new InvalidOperationException("List is empty. Nothing to pop."); + + int i = Count - 1; + T item = Items[i]; + Items[i] = default(T); + Count--; + version++; + return item; + } + + public void RemoveRange (int index, int count) { + CheckRange(index, count); + if (count > 0) { + Shift(index, -count); + Array.Clear(Items, Count, count); + version++; + } + } + + public void Reverse () { + Array.Reverse(Items, 0, Count); + version++; + } + + public void Reverse (int index, int count) { + CheckRange(index, count); + Array.Reverse(Items, index, count); + version++; + } + + public void Sort () { + Array.Sort(Items, 0, Count, Comparer.Default); + version++; + } + + public void Sort (IComparer comparer) { + Array.Sort(Items, 0, Count, comparer); + version++; + } + + public void Sort (Comparison comparison) { + Array.Sort(Items, comparison); + version++; + } + + public void Sort (int index, int count, IComparer comparer) { + CheckRange(index, count); + Array.Sort(Items, index, count, comparer); + version++; + } + + public T[] ToArray () { + T[] t = new T[Count]; + Array.Copy(Items, t, Count); + + return t; + } + + public void TrimExcess () { + Capacity = Count; + } + + public bool TrueForAll (Predicate match) { + CheckMatch(match); + + for (int i = 0; i < Count; i++) + if (!match(Items[i])) + return false; + + return true; + } + + public int Capacity { + get { + return Items.Length; + } + set { + if ((uint)value < (uint)Count) + throw new ArgumentOutOfRangeException(); + + Array.Resize(ref Items, value); + } + } + + #region Interface implementations. + + IEnumerator IEnumerable.GetEnumerator () { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator () { + return GetEnumerator(); + } + + #endregion + + public struct Enumerator : IEnumerator, IDisposable { + private ExposedList l; + private int next; + private int ver; + private T current; + + internal Enumerator (ExposedList l) + : this() { + this.l = l; + ver = l.version; + } + + public void Dispose () { + l = null; + } + + private void VerifyState () { + if (l == null) + throw new ObjectDisposedException(GetType().FullName); + if (ver != l.version) + throw new InvalidOperationException( + "Collection was modified; enumeration operation may not execute."); + } + + public bool MoveNext () { + VerifyState(); + + if (next < 0) + return false; + + if (next < l.Count) { + current = l.Items[next++]; + return true; + } + + next = -1; + return false; + } + + public T Current { + get { + return current; + } + } + + void IEnumerator.Reset () { + VerifyState(); + next = 0; + } + + object IEnumerator.Current { + get { + VerifyState(); + if (next <= 0) + throw new InvalidOperationException(); + return current; + } + } + } + } +} diff --git a/Assets/Spine/spine-csharp/ExposedList.cs.meta b/Assets/Spine/spine-csharp/ExposedList.cs.meta new file mode 100644 index 0000000..c51394b --- /dev/null +++ b/Assets/Spine/spine-csharp/ExposedList.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 89690af94a880744989712505f2957b1 +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/IConstraint.cs b/Assets/Spine/spine-csharp/IConstraint.cs new file mode 100644 index 0000000..445d077 --- /dev/null +++ b/Assets/Spine/spine-csharp/IConstraint.cs @@ -0,0 +1,40 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine { + + /// The interface for all constraints. + public interface IConstraint : IUpdatable { + /// The ordinal for the order a skeleton's constraints will be applied. + int Order { get; } + + } + +} \ No newline at end of file diff --git a/Assets/Spine/spine-csharp/IConstraint.cs.meta b/Assets/Spine/spine-csharp/IConstraint.cs.meta new file mode 100644 index 0000000..84d1896 --- /dev/null +++ b/Assets/Spine/spine-csharp/IConstraint.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1193bcbef8900304db4c4ae8c750f617 +timeCreated: 1474766505 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/IUpdatable.cs b/Assets/Spine/spine-csharp/IUpdatable.cs new file mode 100644 index 0000000..4669d8c --- /dev/null +++ b/Assets/Spine/spine-csharp/IUpdatable.cs @@ -0,0 +1,35 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine { + public interface IUpdatable { + void Update (); + } +} diff --git a/Assets/Spine/spine-csharp/IUpdatable.cs.meta b/Assets/Spine/spine-csharp/IUpdatable.cs.meta new file mode 100644 index 0000000..d1c948f --- /dev/null +++ b/Assets/Spine/spine-csharp/IUpdatable.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 44a51df5672fe4249b6763960587a017 +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/IkConstraint.cs b/Assets/Spine/spine-csharp/IkConstraint.cs new file mode 100644 index 0000000..3f132d7 --- /dev/null +++ b/Assets/Spine/spine-csharp/IkConstraint.cs @@ -0,0 +1,227 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class IkConstraint : IConstraint { + internal IkConstraintData data; + internal ExposedList bones = new ExposedList(); + internal Bone target; + internal float mix; + internal int bendDirection; + + public IkConstraintData Data { get { return data; } } + public int Order { get { return data.order; } } + public ExposedList Bones { get { return bones; } } + public Bone Target { get { return target; } set { target = value; } } + public int BendDirection { get { return bendDirection; } set { bendDirection = value; } } + public float Mix { get { return mix; } set { mix = value; } } + + public IkConstraint (IkConstraintData data, Skeleton skeleton) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + this.data = data; + mix = data.mix; + bendDirection = data.bendDirection; + + bones = new ExposedList(data.bones.Count); + foreach (BoneData boneData in data.bones) + bones.Add(skeleton.FindBone(boneData.name)); + target = skeleton.FindBone(data.target.name); + } + + /// Applies the constraint to the constrained bones. + public void Apply () { + Update(); + } + + public void Update () { + Bone target = this.target; + ExposedList bones = this.bones; + switch (bones.Count) { + case 1: + Apply(bones.Items[0], target.worldX, target.worldY, mix); + break; + case 2: + Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, mix); + break; + } + } + + override public string ToString () { + return data.name; + } + + /// Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified + /// in the world coordinate system. + static public void Apply (Bone bone, float targetX, float targetY, float alpha) { + if (!bone.appliedValid) bone.UpdateAppliedTransform(); + Bone p = bone.parent; + float id = 1 / (p.a * p.d - p.b * p.c); + float x = targetX - p.worldX, y = targetY - p.worldY; + float tx = (x * p.d - y * p.b) * id - bone.ax, ty = (y * p.a - x * p.c) * id - bone.ay; + float rotationIK = (float)Math.Atan2(ty, tx) * MathUtils.RadDeg - bone.ashearX - bone.arotation; + if (bone.ascaleX < 0) rotationIK += 180; + if (rotationIK > 180) + rotationIK -= 360; + else if (rotationIK < -180) rotationIK += 360; + bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX, + bone.ashearY); + } + + /// Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as + /// possible. The target is specified in the world coordinate system. + /// A direct descendant of the parent bone. + static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) { + if (alpha == 0) { + child.UpdateWorldTransform (); + return; + } + //float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX; + if (!parent.appliedValid) parent.UpdateAppliedTransform(); + if (!child.appliedValid) child.UpdateAppliedTransform(); + float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, csx = child.ascaleX; + int os1, os2, s2; + if (psx < 0) { + psx = -psx; + os1 = 180; + s2 = -1; + } else { + os1 = 0; + s2 = 1; + } + if (psy < 0) { + psy = -psy; + s2 = -s2; + } + if (csx < 0) { + csx = -csx; + os2 = 180; + } else + os2 = 0; + float cx = child.ax, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d; + bool u = Math.Abs(psx - psy) <= 0.0001f; + if (!u) { + cy = 0; + cwx = a * cx + parent.worldX; + cwy = c * cx + parent.worldY; + } else { + cy = child.ay; + cwx = a * cx + b * cy + parent.worldX; + cwy = c * cx + d * cy + parent.worldY; + } + Bone pp = parent.parent; + a = pp.a; + b = pp.b; + c = pp.c; + d = pp.d; + float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY; + float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + x = cwx - pp.worldX; + y = cwy - pp.worldY; + float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; + float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2; + if (u) { + l2 *= psx; + float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); + if (cos < -1) + cos = -1; + else if (cos > 1) cos = 1; + a2 = (float)Math.Acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * (float)Math.Sin(a2); + a1 = (float)Math.Atan2(ty * a - tx * b, tx * a + ty * b); + } else { + a = psx * l2; + b = psy * l2; + float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = (float)Math.Atan2(ty, tx); + c = bb * l1 * l1 + aa * dd - aa * bb; + float c1 = -2 * bb * l1, c2 = bb - aa; + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + float q = (float)Math.Sqrt(d); + if (c1 < 0) q = -q; + q = -(c1 + q) / 2; + float r0 = q / c2, r1 = c / q; + float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1; + if (r * r <= dd) { + y = (float)Math.Sqrt(dd - r * r) * bendDir; + a1 = ta - (float)Math.Atan2(y, r); + a2 = (float)Math.Atan2(y / psy, (r - l1) / psx); + goto break_outer; // break outer; + } + } + float minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; + float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; + c = -a * l1 / (aa - bb); + if (c >= -1 && c <= 1) { + c = (float)Math.Acos(c); + x = a * (float)Math.Cos(c) + l1; + y = b * (float)Math.Sin(c); + d = x * x + y * y; + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; + } + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; + } + } + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - (float)Math.Atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } else { + a1 = ta - (float)Math.Atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + break_outer: + float os = (float)Math.Atan2(cy, cx) * s2; + float rotation = parent.arotation; + a1 = (a1 - os) * MathUtils.RadDeg + os1 - rotation; + if (a1 > 180) + a1 -= 360; + else if (a1 < -180) a1 += 360; + parent.UpdateWorldTransform(px, py, rotation + a1 * alpha, parent.scaleX, parent.ascaleY, 0, 0); + rotation = child.arotation; + a2 = ((a2 + os) * MathUtils.RadDeg - child.ashearX) * s2 + os2 - rotation; + if (a2 > 180) + a2 -= 360; + else if (a2 < -180) a2 += 360; + child.UpdateWorldTransform(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + } + } +} diff --git a/Assets/Spine/spine-csharp/IkConstraint.cs.meta b/Assets/Spine/spine-csharp/IkConstraint.cs.meta new file mode 100644 index 0000000..bffcd55 --- /dev/null +++ b/Assets/Spine/spine-csharp/IkConstraint.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 916f8e6534860cc40824adfc2916baa7 +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/IkConstraintData.cs b/Assets/Spine/spine-csharp/IkConstraintData.cs new file mode 100644 index 0000000..61e0a46 --- /dev/null +++ b/Assets/Spine/spine-csharp/IkConstraintData.cs @@ -0,0 +1,82 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + /// Stores the setup pose for an IkConstraint. + public class IkConstraintData { + internal string name; + internal int order; + internal List bones = new List(); + internal BoneData target; + internal int bendDirection = 1; + internal float mix = 1; + + /// The IK constraint's name, which is unique within the skeleton. + public string Name { + get { return name; } + } + + public int Order { + get { return order; } + set { order = value; } + } + + /// The bones that are constrained by this IK Constraint. + public List Bones { + get { return bones; } + } + + /// The bone that is the IK target. + public BoneData Target { + get { return target; } + set { target = value; } + } + + /// Controls the bend direction of the IK bones, either 1 or -1. + public int BendDirection { + get { return bendDirection; } + set { bendDirection = value; } + } + + public float Mix { get { return mix; } set { mix = value; } } + + public IkConstraintData (string name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.name = name; + } + + override public string ToString () { + return name; + } + } +} diff --git a/Assets/Spine/spine-csharp/IkConstraintData.cs.meta b/Assets/Spine/spine-csharp/IkConstraintData.cs.meta new file mode 100644 index 0000000..4825594 --- /dev/null +++ b/Assets/Spine/spine-csharp/IkConstraintData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 94ad1e9256073264785f806086a000ba +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Json.cs b/Assets/Spine/spine-csharp/Json.cs new file mode 100644 index 0000000..37ca8d6 --- /dev/null +++ b/Assets/Spine/spine-csharp/Json.cs @@ -0,0 +1,535 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Globalization; +using System.Collections.Generic; + +namespace Spine { + public static class Json { + public static object Deserialize (TextReader text) { + var parser = new SharpJson.JsonDecoder(); + parser.parseNumbersAsFloat = true; + return parser.Decode(text.ReadToEnd()); + } + } +} + +/** + * + * Copyright (c) 2016 Adriano Tinoco d'Oliveira Rezende + * + * Based on the JSON parser by Patrick van Bergen + * http://techblog.procurios.nl/k/news/view/14605/14863/how-do-i-write-my-own-parser-(for-json).html + * + * Changes made: + * + * - Optimized parser speed (deserialize roughly near 3x faster than original) + * - Added support to handle lexer/parser error messages with line numbers + * - Added more fine grained control over type conversions during the parsing + * - Refactory API (Separate Lexer code from Parser code and the Encoder from Decoder) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all copies or substantial + * portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +namespace SharpJson +{ + class Lexer + { + public enum Token { + None, + Null, + True, + False, + Colon, + Comma, + String, + Number, + CurlyOpen, + CurlyClose, + SquaredOpen, + SquaredClose, + }; + + public bool hasError { + get { + return !success; + } + } + + public int lineNumber { + get; + private set; + } + + public bool parseNumbersAsFloat { + get; + set; + } + + char[] json; + int index = 0; + bool success = true; + char[] stringBuffer = new char[4096]; + + public Lexer(string text) + { + Reset(); + + json = text.ToCharArray(); + parseNumbersAsFloat = false; + } + + public void Reset() + { + index = 0; + lineNumber = 1; + success = true; + } + + public string ParseString() + { + int idx = 0; + StringBuilder builder = null; + + SkipWhiteSpaces(); + + // " + char c = json[index++]; + + bool failed = false; + bool complete = false; + + while (!complete && !failed) { + if (index == json.Length) + break; + + c = json[index++]; + if (c == '"') { + complete = true; + break; + } else if (c == '\\') { + if (index == json.Length) + break; + + c = json[index++]; + + switch (c) { + case '"': + stringBuffer[idx++] = '"'; + break; + case '\\': + stringBuffer[idx++] = '\\'; + break; + case '/': + stringBuffer[idx++] = '/'; + break; + case 'b': + stringBuffer[idx++] = '\b'; + break; + case'f': + stringBuffer[idx++] = '\f'; + break; + case 'n': + stringBuffer[idx++] = '\n'; + break; + case 'r': + stringBuffer[idx++] = '\r'; + break; + case 't': + stringBuffer[idx++] = '\t'; + break; + case 'u': + int remainingLength = json.Length - index; + if (remainingLength >= 4) { + var hex = new string(json, index, 4); + + // XXX: handle UTF + stringBuffer[idx++] = (char) Convert.ToInt32(hex, 16); + + // skip 4 chars + index += 4; + } else { + failed = true; + } + break; + } + } else { + stringBuffer[idx++] = c; + } + + if (idx >= stringBuffer.Length) { + if (builder == null) + builder = new StringBuilder(); + + builder.Append(stringBuffer, 0, idx); + idx = 0; + } + } + + if (!complete) { + success = false; + return null; + } + + if (builder != null) + return builder.ToString (); + else + return new string (stringBuffer, 0, idx); + } + + string GetNumberString() + { + SkipWhiteSpaces(); + + int lastIndex = GetLastIndexOfNumber(index); + int charLength = (lastIndex - index) + 1; + + var result = new string (json, index, charLength); + + index = lastIndex + 1; + + return result; + } + + public float ParseFloatNumber() + { + float number; + var str = GetNumberString (); + + if (!float.TryParse (str, NumberStyles.Float, CultureInfo.InvariantCulture, out number)) + return 0; + + return number; + } + + public double ParseDoubleNumber() + { + double number; + var str = GetNumberString (); + + if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out number)) + return 0; + + return number; + } + + int GetLastIndexOfNumber(int index) + { + int lastIndex; + + for (lastIndex = index; lastIndex < json.Length; lastIndex++) { + char ch = json[lastIndex]; + + if ((ch < '0' || ch > '9') && ch != '+' && ch != '-' + && ch != '.' && ch != 'e' && ch != 'E') + break; + } + + return lastIndex - 1; + } + + void SkipWhiteSpaces() + { + for (; index < json.Length; index++) { + char ch = json[index]; + + if (ch == '\n') + lineNumber++; + + if (!char.IsWhiteSpace(json[index])) + break; + } + } + + public Token LookAhead() + { + SkipWhiteSpaces(); + + int savedIndex = index; + return NextToken(json, ref savedIndex); + } + + public Token NextToken() + { + SkipWhiteSpaces(); + return NextToken(json, ref index); + } + + static Token NextToken(char[] json, ref int index) + { + if (index == json.Length) + return Token.None; + + char c = json[index++]; + + switch (c) { + case '{': + return Token.CurlyOpen; + case '}': + return Token.CurlyClose; + case '[': + return Token.SquaredOpen; + case ']': + return Token.SquaredClose; + case ',': + return Token.Comma; + case '"': + return Token.String; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '-': + return Token.Number; + case ':': + return Token.Colon; + } + + index--; + + int remainingLength = json.Length - index; + + // false + if (remainingLength >= 5) { + if (json[index] == 'f' && + json[index + 1] == 'a' && + json[index + 2] == 'l' && + json[index + 3] == 's' && + json[index + 4] == 'e') { + index += 5; + return Token.False; + } + } + + // true + if (remainingLength >= 4) { + if (json[index] == 't' && + json[index + 1] == 'r' && + json[index + 2] == 'u' && + json[index + 3] == 'e') { + index += 4; + return Token.True; + } + } + + // null + if (remainingLength >= 4) { + if (json[index] == 'n' && + json[index + 1] == 'u' && + json[index + 2] == 'l' && + json[index + 3] == 'l') { + index += 4; + return Token.Null; + } + } + + return Token.None; + } + } + + public class JsonDecoder + { + public string errorMessage { + get; + private set; + } + + public bool parseNumbersAsFloat { + get; + set; + } + + Lexer lexer; + + public JsonDecoder() + { + errorMessage = null; + parseNumbersAsFloat = false; + } + + public object Decode(string text) + { + errorMessage = null; + + lexer = new Lexer(text); + lexer.parseNumbersAsFloat = parseNumbersAsFloat; + + return ParseValue(); + } + + public static object DecodeText(string text) + { + var builder = new JsonDecoder(); + return builder.Decode(text); + } + + IDictionary ParseObject() + { + var table = new Dictionary(); + + // { + lexer.NextToken(); + + while (true) { + var token = lexer.LookAhead(); + + switch (token) { + case Lexer.Token.None: + TriggerError("Invalid token"); + return null; + case Lexer.Token.Comma: + lexer.NextToken(); + break; + case Lexer.Token.CurlyClose: + lexer.NextToken(); + return table; + default: + // name + string name = EvalLexer(lexer.ParseString()); + + if (errorMessage != null) + return null; + + // : + token = lexer.NextToken(); + + if (token != Lexer.Token.Colon) { + TriggerError("Invalid token; expected ':'"); + return null; + } + + // value + object value = ParseValue(); + + if (errorMessage != null) + return null; + + table[name] = value; + break; + } + } + + //return null; // Unreachable code + } + + IList ParseArray() + { + var array = new List(); + + // [ + lexer.NextToken(); + + while (true) { + var token = lexer.LookAhead(); + + switch (token) { + case Lexer.Token.None: + TriggerError("Invalid token"); + return null; + case Lexer.Token.Comma: + lexer.NextToken(); + break; + case Lexer.Token.SquaredClose: + lexer.NextToken(); + return array; + default: + object value = ParseValue(); + + if (errorMessage != null) + return null; + + array.Add(value); + break; + } + } + + //return null; // Unreachable code + } + + object ParseValue() + { + switch (lexer.LookAhead()) { + case Lexer.Token.String: + return EvalLexer(lexer.ParseString()); + case Lexer.Token.Number: + if (parseNumbersAsFloat) + return EvalLexer(lexer.ParseFloatNumber()); + else + return EvalLexer(lexer.ParseDoubleNumber()); + case Lexer.Token.CurlyOpen: + return ParseObject(); + case Lexer.Token.SquaredOpen: + return ParseArray(); + case Lexer.Token.True: + lexer.NextToken(); + return true; + case Lexer.Token.False: + lexer.NextToken(); + return false; + case Lexer.Token.Null: + lexer.NextToken(); + return null; + case Lexer.Token.None: + break; + } + + TriggerError("Unable to parse value"); + return null; + } + + void TriggerError(string message) + { + errorMessage = string.Format("Error: '{0}' at line {1}", + message, lexer.lineNumber); + } + + T EvalLexer(T value) + { + if (lexer.hasError) + TriggerError("Lexical error ocurred"); + + return value; + } + } +} diff --git a/Assets/Spine/spine-csharp/Json.cs.meta b/Assets/Spine/spine-csharp/Json.cs.meta new file mode 100644 index 0000000..ca4d324 --- /dev/null +++ b/Assets/Spine/spine-csharp/Json.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 371f40ecc08b2eb4cbec49585d41e2c3 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/MathUtils.cs b/Assets/Spine/spine-csharp/MathUtils.cs new file mode 100644 index 0000000..6726cd9 --- /dev/null +++ b/Assets/Spine/spine-csharp/MathUtils.cs @@ -0,0 +1,100 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public static class MathUtils { + public const float PI = 3.1415927f; + public const float PI2 = PI * 2; + public const float RadDeg = 180f / PI; + public const float DegRad = PI / 180; + + const int SIN_BITS = 14; // 16KB. Adjust for accuracy. + const int SIN_MASK = ~(-1 << SIN_BITS); + const int SIN_COUNT = SIN_MASK + 1; + const float RadFull = PI * 2; + const float DegFull = 360; + const float RadToIndex = SIN_COUNT / RadFull; + const float DegToIndex = SIN_COUNT / DegFull; + static float[] sin = new float[SIN_COUNT]; + + static MathUtils () { + for (int i = 0; i < SIN_COUNT; i++) + sin[i] = (float)Math.Sin((i + 0.5f) / SIN_COUNT * RadFull); + for (int i = 0; i < 360; i += 90) + sin[(int)(i * DegToIndex) & SIN_MASK] = (float)Math.Sin(i * DegRad); + } + + /// Returns the sine in radians from a lookup table. + static public float Sin (float radians) { + return sin[(int)(radians * RadToIndex) & SIN_MASK]; + } + + /// Returns the cosine in radians from a lookup table. + static public float Cos (float radians) { + return sin[(int)((radians + PI / 2) * RadToIndex) & SIN_MASK]; + } + + /// Returns the sine in radians from a lookup table. + static public float SinDeg (float degrees) { + return sin[(int)(degrees * DegToIndex) & SIN_MASK]; + } + + /// Returns the cosine in radians from a lookup table. + static public float CosDeg (float degrees) { + return sin[(int)((degrees + 90) * DegToIndex) & SIN_MASK]; + } + + /// Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323 + /// degrees), largest error of 0.00488 radians (0.2796 degrees). + static public float Atan2 (float y, float x) { + if (x == 0f) { + if (y > 0f) return PI / 2; + if (y == 0f) return 0f; + return -PI / 2; + } + float atan, z = y / x; + if (Math.Abs(z) < 1f) { + atan = z / (1f + 0.28f * z * z); + if (x < 0f) return atan + (y < 0f ? -PI : PI); + return atan; + } + atan = PI / 2 - z / (z * z + 0.28f); + return y < 0f ? atan - PI : atan; + } + + static public float Clamp (float value, float min, float max) { + if (value < min) return min; + if (value > max) return max; + return value; + } + } +} diff --git a/Assets/Spine/spine-csharp/MathUtils.cs.meta b/Assets/Spine/spine-csharp/MathUtils.cs.meta new file mode 100644 index 0000000..00c7de3 --- /dev/null +++ b/Assets/Spine/spine-csharp/MathUtils.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 03b653e54c5403b4191f5003d64c6e18 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/PathConstraint.cs b/Assets/Spine/spine-csharp/PathConstraint.cs new file mode 100644 index 0000000..839dd55 --- /dev/null +++ b/Assets/Spine/spine-csharp/PathConstraint.cs @@ -0,0 +1,417 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class PathConstraint : IConstraint { + const int NONE = -1, BEFORE = -2, AFTER = -3; + const float Epsilon = 0.00001f; + + internal PathConstraintData data; + internal ExposedList bones; + internal Slot target; + internal float position, spacing, rotateMix, translateMix; + + internal ExposedList spaces = new ExposedList(), positions = new ExposedList(); + internal ExposedList world = new ExposedList(), curves = new ExposedList(), lengths = new ExposedList(); + internal float[] segments = new float[10]; + + public int Order { get { return data.order; } } + public float Position { get { return position; } set { position = value; } } + public float Spacing { get { return spacing; } set { spacing = value; } } + public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } + public float TranslateMix { get { return translateMix; } set { translateMix = value; } } + public ExposedList Bones { get { return bones; } } + public Slot Target { get { return target; } set { target = value; } } + public PathConstraintData Data { get { return data; } } + + public PathConstraint (PathConstraintData data, Skeleton skeleton) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + this.data = data; + bones = new ExposedList(data.Bones.Count); + foreach (BoneData boneData in data.bones) + bones.Add(skeleton.FindBone(boneData.name)); + target = skeleton.FindSlot(data.target.name); + position = data.position; + spacing = data.spacing; + rotateMix = data.rotateMix; + translateMix = data.translateMix; + } + + /// Applies the constraint to the constrained bones. + public void Apply () { + Update(); + } + + public void Update () { + PathAttachment attachment = target.Attachment as PathAttachment; + if (attachment == null) return; + + float rotateMix = this.rotateMix, translateMix = this.translateMix; + bool translate = translateMix > 0, rotate = rotateMix > 0; + if (!translate && !rotate) return; + + PathConstraintData data = this.data; + SpacingMode spacingMode = data.spacingMode; + bool lengthSpacing = spacingMode == SpacingMode.Length; + RotateMode rotateMode = data.rotateMode; + bool tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale; + int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1; + Bone[] bonesItems = this.bones.Items; + ExposedList spaces = this.spaces.Resize(spacesCount), lengths = null; + float spacing = this.spacing; + if (scale || lengthSpacing) { + if (scale) lengths = this.lengths.Resize(boneCount); + for (int i = 0, n = spacesCount - 1; i < n;) { + Bone bone = bonesItems[i]; + float setupLength = bone.data.length; + if (setupLength < PathConstraint.Epsilon) { + if (scale) lengths.Items[i] = 0; + spaces.Items[++i] = 0; + } else { + float x = setupLength * bone.a, y = setupLength * bone.c; + float length = (float)Math.Sqrt(x * x + y * y); + if (scale) lengths.Items[i] = length; + spaces.Items[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength; + } + } + } else { + for (int i = 1; i < spacesCount; i++) + spaces.Items[i] = spacing; + } + + float[] positions = ComputeWorldPositions(attachment, spacesCount, tangents, + data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent); + float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + bool tip; + if (offsetRotation == 0) { + tip = rotateMode == RotateMode.Chain; + } else { + tip = false; + Bone p = target.bone; + offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.DegRad : -MathUtils.DegRad; + } + for (int i = 0, p = 3; i < boneCount; i++, p += 3) { + Bone bone = bonesItems[i]; + bone.worldX += (boneX - bone.worldX) * translateMix; + bone.worldY += (boneY - bone.worldY) * translateMix; + float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + if (scale) { + float length = lengths.Items[i]; + if (length >= PathConstraint.Epsilon) { + float s = ((float)Math.Sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1; + bone.a *= s; + bone.c *= s; + } + } + boneX = x; + boneY = y; + if (rotate) { + float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin; + if (tangents) + r = positions[p - 1]; + else if (spaces.Items[i + 1] < PathConstraint.Epsilon) + r = positions[p + 2]; + else + r = MathUtils.Atan2(dy, dx); + r -= MathUtils.Atan2(c, a); + if (tip) { + cos = MathUtils.Cos(r); + sin = MathUtils.Sin(r); + float length = bone.data.length; + boneX += (length * (cos * a - sin * c) - dx) * rotateMix; + boneY += (length * (sin * a + cos * c) - dy) * rotateMix; + } else { + r += offsetRotation; + } + if (r > MathUtils.PI) + r -= MathUtils.PI2; + else if (r < -MathUtils.PI) // + r += MathUtils.PI2; + r *= rotateMix; + cos = MathUtils.Cos(r); + sin = MathUtils.Sin(r); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + } + bone.appliedValid = false; + } + } + + float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents, bool percentPosition, + bool percentSpacing) { + + Slot target = this.target; + float position = this.position; + float[] spacesItems = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world; + bool closed = path.Closed; + int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE; + + float pathLength; + if (!path.ConstantSpeed) { + float[] lengths = path.Lengths; + curveCount -= closed ? 1 : 2; + pathLength = lengths[curveCount]; + if (percentPosition) position *= pathLength; + if (percentSpacing) { + for (int i = 0; i < spacesCount; i++) + spacesItems[i] *= pathLength; + } + world = this.world.Resize(8).Items; + for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { + float space = spacesItems[i]; + position += space; + float p = position; + + if (closed) { + p %= pathLength; + if (p < 0) p += pathLength; + curve = 0; + } else if (p < 0) { + if (prevCurve != BEFORE) { + prevCurve = BEFORE; + path.ComputeWorldVertices(target, 2, 4, world, 0); + } + AddBeforePosition(p, world, 0, output, o); + continue; + } else if (p > pathLength) { + if (prevCurve != AFTER) { + prevCurve = AFTER; + path.ComputeWorldVertices(target, verticesLength - 6, 4, world, 0); + } + AddAfterPosition(p - pathLength, world, 0, output, o); + continue; + } + + // Determine curve containing position. + for (;; curve++) { + float length = lengths[curve]; + if (p > length) continue; + if (curve == 0) + p /= length; + else { + float prev = lengths[curve - 1]; + p = (p - prev) / (length - prev); + } + break; + } + if (curve != prevCurve) { + prevCurve = curve; + if (closed && curve == curveCount) { + path.ComputeWorldVertices(target, verticesLength - 4, 4, world, 0); + path.ComputeWorldVertices(target, 0, 4, world, 4); + } else + path.ComputeWorldVertices(target, curve * 6 + 2, 8, world, 0); + } + AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o, + tangents || (i > 0 && space < PathConstraint.Epsilon)); + } + return output; + } + + // World vertices. + if (closed) { + verticesLength += 2; + world = this.world.Resize(verticesLength).Items; + path.ComputeWorldVertices(target, 2, verticesLength - 4, world, 0); + path.ComputeWorldVertices(target, 0, 2, world, verticesLength - 4); + world[verticesLength - 2] = world[0]; + world[verticesLength - 1] = world[1]; + } else { + curveCount--; + verticesLength -= 4; + world = this.world.Resize(verticesLength).Items; + path.ComputeWorldVertices(target, 2, verticesLength, world, 0); + } + + // Curve lengths. + float[] curves = this.curves.Resize(curveCount).Items; + pathLength = 0; + float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; + float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; + for (int i = 0, w = 2; i < curveCount; i++, w += 6) { + cx1 = world[w]; + cy1 = world[w + 1]; + cx2 = world[w + 2]; + cy2 = world[w + 3]; + x2 = world[w + 4]; + y2 = world[w + 5]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; + pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + curves[i] = pathLength; + x1 = x2; + y1 = y2; + } + if (percentPosition) position *= pathLength; + if (percentSpacing) { + for (int i = 0; i < spacesCount; i++) + spacesItems[i] *= pathLength; + } + + float[] segments = this.segments; + float curveLength = 0; + for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { + float space = spacesItems[i]; + position += space; + float p = position; + + if (closed) { + p %= pathLength; + if (p < 0) p += pathLength; + curve = 0; + } else if (p < 0) { + AddBeforePosition(p, world, 0, output, o); + continue; + } else if (p > pathLength) { + AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o); + continue; + } + + // Determine curve containing position. + for (;; curve++) { + float length = curves[curve]; + if (p > length) continue; + if (curve == 0) + p /= length; + else { + float prev = curves[curve - 1]; + p = (p - prev) / (length - prev); + } + break; + } + + // Curve segment lengths. + if (curve != prevCurve) { + prevCurve = curve; + int ii = curve * 6; + x1 = world[ii]; + y1 = world[ii + 1]; + cx1 = world[ii + 2]; + cy1 = world[ii + 3]; + cx2 = world[ii + 4]; + cy2 = world[ii + 5]; + x2 = world[ii + 6]; + y2 = world[ii + 7]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; + curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy); + segments[0] = curveLength; + for (ii = 1; ii < 8; ii++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + segments[ii] = curveLength; + } + dfx += ddfx; + dfy += ddfy; + curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + segments[8] = curveLength; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy); + segments[9] = curveLength; + segment = 0; + } + + // Weight by segment length. + p *= curveLength; + for (;; segment++) { + float length = segments[segment]; + if (p > length) continue; + if (segment == 0) + p /= length; + else { + float prev = segments[segment - 1]; + p = segment + (p - prev) / (length - prev); + } + break; + } + AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space < PathConstraint.Epsilon)); + } + return output; + } + + static void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) { + float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx); + output[o] = x1 + p * MathUtils.Cos(r); + output[o + 1] = y1 + p * MathUtils.Sin(r); + output[o + 2] = r; + } + + static void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) { + float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx); + output[o] = x1 + p * MathUtils.Cos(r); + output[o + 1] = y1 + p * MathUtils.Sin(r); + output[o + 2] = r; + } + + static void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, + float[] output, int o, bool tangents) { + if (p < PathConstraint.Epsilon || float.IsNaN(p)) p = PathConstraint.Epsilon; + float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; + float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; + float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + output[o] = x; + output[o + 1] = y; + if (tangents) + output[o + 2] = (float)Math.Atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + } + } +} diff --git a/Assets/Spine/spine-csharp/PathConstraint.cs.meta b/Assets/Spine/spine-csharp/PathConstraint.cs.meta new file mode 100644 index 0000000..3ee8122 --- /dev/null +++ b/Assets/Spine/spine-csharp/PathConstraint.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 731d05fbc2874c74984813ce4c5bb8df +timeCreated: 1467213650 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/PathConstraintData.cs b/Assets/Spine/spine-csharp/PathConstraintData.cs new file mode 100644 index 0000000..e468184 --- /dev/null +++ b/Assets/Spine/spine-csharp/PathConstraintData.cs @@ -0,0 +1,79 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class PathConstraintData { + internal string name; + internal int order; + internal ExposedList bones = new ExposedList(); + internal SlotData target; + internal PositionMode positionMode; + internal SpacingMode spacingMode; + internal RotateMode rotateMode; + internal float offsetRotation; + internal float position, spacing, rotateMix, translateMix; + + public string Name { get { return name; } } + public int Order { get { return order; } set { order = value; } } + public ExposedList Bones { get { return bones; } } + public SlotData Target { get { return target; } set { target = value; } } + public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } } + public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } } + public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } } + public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } } + public float Position { get { return position; } set { position = value; } } + public float Spacing { get { return spacing; } set { spacing = value; } } + public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } + public float TranslateMix { get { return translateMix; } set { translateMix = value; } } + + public PathConstraintData (String name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.name = name; + } + + public override string ToString () { + return name; + } + } + + public enum PositionMode { + Fixed, Percent + } + + public enum SpacingMode { + Length, Fixed, Percent + } + + public enum RotateMode { + Tangent, Chain, ChainScale + } +} diff --git a/Assets/Spine/spine-csharp/PathConstraintData.cs.meta b/Assets/Spine/spine-csharp/PathConstraintData.cs.meta new file mode 100644 index 0000000..6506276 --- /dev/null +++ b/Assets/Spine/spine-csharp/PathConstraintData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9d836858269be96428428fb6764dfc3a +timeCreated: 1467213651 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Skeleton.cs b/Assets/Spine/spine-csharp/Skeleton.cs new file mode 100644 index 0000000..a0b1a01 --- /dev/null +++ b/Assets/Spine/spine-csharp/Skeleton.cs @@ -0,0 +1,541 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + public class Skeleton { + internal SkeletonData data; + internal ExposedList bones; + internal ExposedList slots; + internal ExposedList drawOrder; + internal ExposedList ikConstraints; + internal ExposedList transformConstraints; + internal ExposedList pathConstraints; + internal ExposedList updateCache = new ExposedList(); + internal ExposedList updateCacheReset = new ExposedList(); + internal Skin skin; + internal float r = 1, g = 1, b = 1, a = 1; + internal float time; + internal bool flipX, flipY; + internal float x, y; + + public SkeletonData Data { get { return data; } } + public ExposedList Bones { get { return bones; } } + public ExposedList UpdateCacheList { get { return updateCache; } } + public ExposedList Slots { get { return slots; } } + public ExposedList DrawOrder { get { return drawOrder; } } + public ExposedList IkConstraints { get { return ikConstraints; } } + public ExposedList PathConstraints { get { return pathConstraints; } } + public ExposedList TransformConstraints { get { return transformConstraints; } } + public Skin Skin { get { return skin; } set { skin = value; } } + public float R { get { return r; } set { r = value; } } + public float G { get { return g; } set { g = value; } } + public float B { get { return b; } set { b = value; } } + public float A { get { return a; } set { a = value; } } + public float Time { get { return time; } set { time = value; } } + public float X { get { return x; } set { x = value; } } + public float Y { get { return y; } set { y = value; } } + public bool FlipX { get { return flipX; } set { flipX = value; } } + public bool FlipY { get { return flipY; } set { flipY = value; } } + + public Bone RootBone { + get { return bones.Count == 0 ? null : bones.Items[0]; } + } + + public Skeleton (SkeletonData data) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + this.data = data; + + bones = new ExposedList(data.bones.Count); + foreach (BoneData boneData in data.bones) { + Bone bone; + if (boneData.parent == null) { + bone = new Bone(boneData, this, null); + } else { + Bone parent = bones.Items[boneData.parent.index]; + bone = new Bone(boneData, this, parent); + parent.children.Add(bone); + } + bones.Add(bone); + } + + slots = new ExposedList(data.slots.Count); + drawOrder = new ExposedList(data.slots.Count); + foreach (SlotData slotData in data.slots) { + Bone bone = bones.Items[slotData.boneData.index]; + Slot slot = new Slot(slotData, bone); + slots.Add(slot); + drawOrder.Add(slot); + } + + ikConstraints = new ExposedList(data.ikConstraints.Count); + foreach (IkConstraintData ikConstraintData in data.ikConstraints) + ikConstraints.Add(new IkConstraint(ikConstraintData, this)); + + transformConstraints = new ExposedList(data.transformConstraints.Count); + foreach (TransformConstraintData transformConstraintData in data.transformConstraints) + transformConstraints.Add(new TransformConstraint(transformConstraintData, this)); + + pathConstraints = new ExposedList (data.pathConstraints.Count); + foreach (PathConstraintData pathConstraintData in data.pathConstraints) + pathConstraints.Add(new PathConstraint(pathConstraintData, this)); + + UpdateCache(); + UpdateWorldTransform(); + } + + /// Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added + /// or removed. + public void UpdateCache () { + ExposedList updateCache = this.updateCache; + updateCache.Clear(); + this.updateCacheReset.Clear(); + + ExposedList bones = this.bones; + for (int i = 0, n = bones.Count; i < n; i++) + bones.Items[i].sorted = false; + + ExposedList ikConstraints = this.ikConstraints; + var transformConstraints = this.transformConstraints; + var pathConstraints = this.pathConstraints; + int ikCount = IkConstraints.Count, transformCount = transformConstraints.Count, pathCount = pathConstraints.Count; + int constraintCount = ikCount + transformCount + pathCount; + //outer: + for (int i = 0; i < constraintCount; i++) { + for (int ii = 0; ii < ikCount; ii++) { + IkConstraint constraint = ikConstraints.Items[ii]; + if (constraint.data.order == i) { + SortIkConstraint(constraint); + goto continue_outer; //continue outer; + } + } + for (int ii = 0; ii < transformCount; ii++) { + TransformConstraint constraint = transformConstraints.Items[ii]; + if (constraint.data.order == i) { + SortTransformConstraint(constraint); + goto continue_outer; //continue outer; + } + } + for (int ii = 0; ii < pathCount; ii++) { + PathConstraint constraint = pathConstraints.Items[ii]; + if (constraint.data.order == i) { + SortPathConstraint(constraint); + goto continue_outer; //continue outer; + } + } + continue_outer: {} + } + + for (int i = 0, n = bones.Count; i < n; i++) + SortBone(bones.Items[i]); + } + + private void SortIkConstraint (IkConstraint constraint) { + Bone target = constraint.target; + SortBone(target); + + var constrained = constraint.bones; + Bone parent = constrained.Items[0]; + SortBone(parent); + + if (constrained.Count > 1) { + Bone child = constrained.Items[constrained.Count - 1]; + if (!updateCache.Contains(child)) + updateCacheReset.Add(child); + } + + updateCache.Add(constraint); + + SortReset(parent.children); + constrained.Items[constrained.Count - 1].sorted = true; + } + + private void SortPathConstraint (PathConstraint constraint) { + Slot slot = constraint.target; + int slotIndex = slot.data.index; + Bone slotBone = slot.bone; + if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone); + if (data.defaultSkin != null && data.defaultSkin != skin) + SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone); + for (int ii = 0, nn = data.skins.Count; ii < nn; ii++) + SortPathConstraintAttachment(data.skins.Items[ii], slotIndex, slotBone); + + Attachment attachment = slot.attachment; + if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone); + + var constrained = constraint.bones; + int boneCount = constrained.Count; + for (int i = 0; i < boneCount; i++) + SortBone(constrained.Items[i]); + + updateCache.Add(constraint); + + for (int i = 0; i < boneCount; i++) + SortReset(constrained.Items[i].children); + for (int i = 0; i < boneCount; i++) + constrained.Items[i].sorted = true; + } + + private void SortTransformConstraint (TransformConstraint constraint) { + SortBone(constraint.target); + + var constrained = constraint.bones; + int boneCount = constrained.Count; + if (constraint.data.local) { + for (int i = 0; i < boneCount; i++) { + Bone child = constrained.Items[i]; + SortBone(child.parent); + if (!updateCache.Contains(child)) updateCacheReset.Add(child); + } + } else { + for (int i = 0; i < boneCount; i++) + SortBone(constrained.Items[i]); + } + + updateCache.Add(constraint); + + for (int i = 0; i < boneCount; i++) + SortReset(constrained.Items[i].children); + for (int i = 0; i < boneCount; i++) + constrained.Items[i].sorted = true; + } + + private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) { + foreach (var entry in skin.Attachments) + if (entry.Key.slotIndex == slotIndex) SortPathConstraintAttachment(entry.Value, slotBone); + } + + private void SortPathConstraintAttachment (Attachment attachment, Bone slotBone) { + if (!(attachment is PathAttachment)) return; + int[] pathBones = ((PathAttachment)attachment).bones; + if (pathBones == null) + SortBone(slotBone); + else { + var bones = this.bones; + for (int i = 0, n = pathBones.Length; i < n;) { + int nn = pathBones[i++]; + nn += i; + while (i < nn) + SortBone(bones.Items[pathBones[i++]]); + } + } + } + + private void SortBone (Bone bone) { + if (bone.sorted) return; + Bone parent = bone.parent; + if (parent != null) SortBone(parent); + bone.sorted = true; + updateCache.Add(bone); + } + + private static void SortReset (ExposedList bones) { + var bonesItems = bones.Items; + for (int i = 0, n = bones.Count; i < n; i++) { + Bone bone = bonesItems[i]; + if (bone.sorted) SortReset(bone.children); + bone.sorted = false; + } + } + + /// Updates the world transform for each bone and applies constraints. + public void UpdateWorldTransform () { + var updateCacheReset = this.updateCacheReset; + var updateCacheResetItems = updateCacheReset.Items; + for (int i = 0, n = updateCacheReset.Count; i < n; i++) { + Bone bone = updateCacheResetItems[i]; + bone.ax = bone.x; + bone.ay = bone.y; + bone.arotation = bone.rotation; + bone.ascaleX = bone.scaleX; + bone.ascaleY = bone.scaleY; + bone.ashearX = bone.shearX; + bone.ashearY = bone.shearY; + bone.appliedValid = true; + } + var updateItems = this.updateCache.Items; + for (int i = 0, n = updateCache.Count; i < n; i++) + updateItems[i].Update(); + } + + /// Sets the bones, constraints, and slots to their setup pose values. + public void SetToSetupPose () { + SetBonesToSetupPose(); + SetSlotsToSetupPose(); + } + + /// Sets the bones and constraints to their setup pose values. + public void SetBonesToSetupPose () { + var bonesItems = this.bones.Items; + for (int i = 0, n = bones.Count; i < n; i++) + bonesItems[i].SetToSetupPose(); + + var ikConstraintsItems = this.ikConstraints.Items; + for (int i = 0, n = ikConstraints.Count; i < n; i++) { + IkConstraint constraint = ikConstraintsItems[i]; + constraint.bendDirection = constraint.data.bendDirection; + constraint.mix = constraint.data.mix; + } + + var transformConstraintsItems = this.transformConstraints.Items; + for (int i = 0, n = transformConstraints.Count; i < n; i++) { + TransformConstraint constraint = transformConstraintsItems[i]; + TransformConstraintData constraintData = constraint.data; + constraint.rotateMix = constraintData.rotateMix; + constraint.translateMix = constraintData.translateMix; + constraint.scaleMix = constraintData.scaleMix; + constraint.shearMix = constraintData.shearMix; + } + + var pathConstraintItems = this.pathConstraints.Items; + for (int i = 0, n = pathConstraints.Count; i < n; i++) { + PathConstraint constraint = pathConstraintItems[i]; + PathConstraintData constraintData = constraint.data; + constraint.position = constraintData.position; + constraint.spacing = constraintData.spacing; + constraint.rotateMix = constraintData.rotateMix; + constraint.translateMix = constraintData.translateMix; + } + } + + public void SetSlotsToSetupPose () { + var slots = this.slots; + var slotsItems = slots.Items; + drawOrder.Clear(); + for (int i = 0, n = slots.Count; i < n; i++) + drawOrder.Add(slotsItems[i]); + + for (int i = 0, n = slots.Count; i < n; i++) + slotsItems[i].SetToSetupPose(); + } + + /// May be null. + public Bone FindBone (string boneName) { + if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); + var bones = this.bones; + var bonesItems = bones.Items; + for (int i = 0, n = bones.Count; i < n; i++) { + Bone bone = bonesItems[i]; + if (bone.data.name == boneName) return bone; + } + return null; + } + + /// -1 if the bone was not found. + public int FindBoneIndex (string boneName) { + if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); + var bones = this.bones; + var bonesItems = bones.Items; + for (int i = 0, n = bones.Count; i < n; i++) + if (bonesItems[i].data.name == boneName) return i; + return -1; + } + + /// May be null. + public Slot FindSlot (string slotName) { + if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); + var slots = this.slots; + var slotsItems = slots.Items; + for (int i = 0, n = slots.Count; i < n; i++) { + Slot slot = slotsItems[i]; + if (slot.data.name == slotName) return slot; + } + return null; + } + + /// -1 if the bone was not found. + public int FindSlotIndex (string slotName) { + if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); + var slots = this.slots; + var slotsItems = slots.Items; + for (int i = 0, n = slots.Count; i < n; i++) + if (slotsItems[i].data.name.Equals(slotName)) return i; + return -1; + } + + /// Sets a skin by name (see SetSkin). + public void SetSkin (string skinName) { + Skin foundSkin = data.FindSkin(skinName); + if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName"); + SetSkin(foundSkin); + } + + /// + /// Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. + /// If there was no old skin, each slot's setup mode attachment is attached from the new skin. + /// After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling + /// . + /// Also, often is called before the next time the + /// skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin. + /// + /// May be null. + public void SetSkin (Skin newSkin) { + if (newSkin != null) { + if (skin != null) + newSkin.AttachAll(this, skin); + else { + ExposedList slots = this.slots; + for (int i = 0, n = slots.Count; i < n; i++) { + Slot slot = slots.Items[i]; + string name = slot.data.attachmentName; + if (name != null) { + Attachment attachment = newSkin.GetAttachment(i, name); + if (attachment != null) slot.Attachment = attachment; + } + } + } + } + skin = newSkin; + } + + /// May be null. + public Attachment GetAttachment (string slotName, string attachmentName) { + return GetAttachment(data.FindSlotIndex(slotName), attachmentName); + } + + /// May be null. + public Attachment GetAttachment (int slotIndex, string attachmentName) { + if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null."); + if (skin != null) { + Attachment attachment = skin.GetAttachment(slotIndex, attachmentName); + if (attachment != null) return attachment; + } + return data.defaultSkin != null ? data.defaultSkin.GetAttachment(slotIndex, attachmentName) : null; + } + + /// May be null. + public void SetAttachment (string slotName, string attachmentName) { + if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); + ExposedList slots = this.slots; + for (int i = 0, n = slots.Count; i < n; i++) { + Slot slot = slots.Items[i]; + if (slot.data.name == slotName) { + Attachment attachment = null; + if (attachmentName != null) { + attachment = GetAttachment(i, attachmentName); + if (attachment == null) throw new Exception("Attachment not found: " + attachmentName + ", for slot: " + slotName); + } + slot.Attachment = attachment; + return; + } + } + throw new Exception("Slot not found: " + slotName); + } + + /// May be null. + public IkConstraint FindIkConstraint (string constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + ExposedList ikConstraints = this.ikConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) { + IkConstraint ikConstraint = ikConstraints.Items[i]; + if (ikConstraint.data.name == constraintName) return ikConstraint; + } + return null; + } + + /// May be null. + public TransformConstraint FindTransformConstraint (string constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + ExposedList transformConstraints = this.transformConstraints; + for (int i = 0, n = transformConstraints.Count; i < n; i++) { + TransformConstraint transformConstraint = transformConstraints.Items[i]; + if (transformConstraint.data.name == constraintName) return transformConstraint; + } + return null; + } + + /// May be null. + public PathConstraint FindPathConstraint (string constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + ExposedList pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.Count; i < n; i++) { + PathConstraint constraint = pathConstraints.Items[i]; + if (constraint.data.name.Equals(constraintName)) return constraint; + } + return null; + } + + public void Update (float delta) { + time += delta; + } + + /// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. + /// The horizontal distance between the skeleton origin and the left side of the AABB. + /// The vertical distance between the skeleton origin and the bottom side of the AABB. + /// The width of the AABB + /// The height of the AABB. + /// Reference to hold a float[]. May be a null reference. This method will assign it a new float[] with the appropriate size as needed. + public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer) { + float[] temp = vertexBuffer; + temp = temp ?? new float[8]; + var drawOrderItems = this.drawOrder.Items; + float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue; + for (int i = 0, n = this.drawOrder.Count; i < n; i++) { + Slot slot = drawOrderItems[i]; + int verticesLength = 0; + float[] vertices = null; + Attachment attachment = slot.attachment; + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + verticesLength = 8; + vertices = temp; + if (vertices.Length < 8) vertices = temp = new float[8]; + regionAttachment.ComputeWorldVertices(slot.bone, temp, 0); + } else { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + MeshAttachment mesh = meshAttachment; + verticesLength = mesh.WorldVerticesLength; + vertices = temp; + if (vertices.Length < verticesLength) vertices = temp = new float[verticesLength]; + mesh.ComputeWorldVertices(slot, 0, verticesLength, temp, 0); + } + } + + if (vertices != null) { + for (int ii = 0; ii < verticesLength; ii += 2) { + float vx = vertices[ii], vy = vertices[ii + 1]; + minX = Math.Min(minX, vx); + minY = Math.Min(minY, vy); + maxX = Math.Max(maxX, vx); + maxY = Math.Max(maxY, vy); + } + } + } + x = minX; + y = minY; + width = maxX - minX; + height = maxY - minY; + vertexBuffer = temp; + } + } +} diff --git a/Assets/Spine/spine-csharp/Skeleton.cs.meta b/Assets/Spine/spine-csharp/Skeleton.cs.meta new file mode 100644 index 0000000..619d2cc --- /dev/null +++ b/Assets/Spine/spine-csharp/Skeleton.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 12ac3c1c7546be24fb9625d3c850619d +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/SkeletonBinary.cs b/Assets/Spine/spine-csharp/SkeletonBinary.cs new file mode 100644 index 0000000..1f009f9 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonBinary.cs @@ -0,0 +1,893 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) +#define IS_UNITY +#endif + +using System; +using System.IO; +using System.Collections.Generic; + +#if WINDOWS_STOREAPP +using System.Threading.Tasks; +using Windows.Storage; +#endif + +namespace Spine { + public class SkeletonBinary { + public const int BONE_ROTATE = 0; + public const int BONE_TRANSLATE = 1; + public const int BONE_SCALE = 2; + public const int BONE_SHEAR = 3; + + public const int SLOT_ATTACHMENT = 0; + public const int SLOT_COLOR = 1; + public const int SLOT_TWO_COLOR = 2; + + public const int PATH_POSITION = 0; + public const int PATH_SPACING = 1; + public const int PATH_MIX = 2; + + public const int CURVE_LINEAR = 0; + public const int CURVE_STEPPED = 1; + public const int CURVE_BEZIER = 2; + + public float Scale { get; set; } + + private AttachmentLoader attachmentLoader; + private byte[] buffer = new byte[32]; + private List linkedMeshes = new List(); + + public SkeletonBinary (params Atlas[] atlasArray) + : this(new AtlasAttachmentLoader(atlasArray)) { + } + + public SkeletonBinary (AttachmentLoader attachmentLoader) { + if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader"); + this.attachmentLoader = attachmentLoader; + Scale = 1; + } + + #if !ISUNITY && WINDOWS_STOREAPP + private async Task ReadFile(string path) { + var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; + using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) { + SkeletonData skeletonData = ReadSkeletonData(input); + skeletonData.Name = Path.GetFileNameWithoutExtension(path); + return skeletonData; + } + } + + public SkeletonData ReadSkeletonData (String path) { + return this.ReadFile(path).Result; + } + #else + public SkeletonData ReadSkeletonData (String path) { + #if WINDOWS_PHONE + using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) { + #else + using (var input = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { + #endif + SkeletonData skeletonData = ReadSkeletonData(input); + skeletonData.name = Path.GetFileNameWithoutExtension(path); + return skeletonData; + } + } + #endif // WINDOWS_STOREAPP + + public static readonly TransformMode[] TransformModeValues = { + TransformMode.Normal, + TransformMode.OnlyTranslation, + TransformMode.NoRotationOrReflection, + TransformMode.NoScale, + TransformMode.NoScaleOrReflection + }; + + /// Returns the version string of binary skeleton data. + public static string GetVersionString (Stream input) { + if (input == null) throw new ArgumentNullException("input"); + + try { + // Hash. + int byteCount = ReadVarint(input, true); + if (byteCount > 1) input.Position += byteCount - 1; + + // Version. + byteCount = ReadVarint(input, true); + if (byteCount > 1) { + byteCount--; + var buffer = new byte[byteCount]; + ReadFully(input, buffer, 0, byteCount); + return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount); + } + + throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.", "input"); + } catch (Exception e) { + throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.\n" + e, "input"); + } + } + + public SkeletonData ReadSkeletonData (Stream input) { + if (input == null) throw new ArgumentNullException("input"); + float scale = Scale; + + var skeletonData = new SkeletonData(); + skeletonData.hash = ReadString(input); + if (skeletonData.hash.Length == 0) skeletonData.hash = null; + skeletonData.version = ReadString(input); + if (skeletonData.version.Length == 0) skeletonData.version = null; + skeletonData.width = ReadFloat(input); + skeletonData.height = ReadFloat(input); + + bool nonessential = ReadBoolean(input); + + if (nonessential) { + skeletonData.fps = ReadFloat(input); + skeletonData.imagesPath = ReadString(input); + if (skeletonData.imagesPath.Length == 0) skeletonData.imagesPath = null; + } + + // Bones. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + String name = ReadString(input); + BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)]; + BoneData data = new BoneData(i, name, parent); + data.rotation = ReadFloat(input); + data.x = ReadFloat(input) * scale; + data.y = ReadFloat(input) * scale; + data.scaleX = ReadFloat(input); + data.scaleY = ReadFloat(input); + data.shearX = ReadFloat(input); + data.shearY = ReadFloat(input); + data.length = ReadFloat(input) * scale; + data.transformMode = TransformModeValues[ReadVarint(input, true)]; + if (nonessential) ReadInt(input); // Skip bone color. + skeletonData.bones.Add(data); + } + + // Slots. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + String slotName = ReadString(input); + BoneData boneData = skeletonData.bones.Items[ReadVarint(input, true)]; + SlotData slotData = new SlotData(i, slotName, boneData); + int color = ReadInt(input); + slotData.r = ((color & 0xff000000) >> 24) / 255f; + slotData.g = ((color & 0x00ff0000) >> 16) / 255f; + slotData.b = ((color & 0x0000ff00) >> 8) / 255f; + slotData.a = ((color & 0x000000ff)) / 255f; + + int darkColor = ReadInt(input); // 0x00rrggbb + if (darkColor != -1) { + slotData.hasSecondColor = true; + slotData.r2 = ((darkColor & 0x00ff0000) >> 16) / 255f; + slotData.g2 = ((darkColor & 0x0000ff00) >> 8) / 255f; + slotData.b2 = ((darkColor & 0x000000ff)) / 255f; + } + + slotData.attachmentName = ReadString(input); + slotData.blendMode = (BlendMode)ReadVarint(input, true); + skeletonData.slots.Add(slotData); + } + + // IK constraints. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + IkConstraintData data = new IkConstraintData(ReadString(input)); + data.order = ReadVarint(input, true); + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) + data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); + data.target = skeletonData.bones.Items[ReadVarint(input, true)]; + data.mix = ReadFloat(input); + data.bendDirection = ReadSByte(input); + skeletonData.ikConstraints.Add(data); + } + + // Transform constraints. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + TransformConstraintData data = new TransformConstraintData(ReadString(input)); + data.order = ReadVarint(input, true); + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) + data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); + data.target = skeletonData.bones.Items[ReadVarint(input, true)]; + data.local = ReadBoolean(input); + data.relative = ReadBoolean(input); + data.offsetRotation = ReadFloat(input); + data.offsetX = ReadFloat(input) * scale; + data.offsetY = ReadFloat(input) * scale; + data.offsetScaleX = ReadFloat(input); + data.offsetScaleY = ReadFloat(input); + data.offsetShearY = ReadFloat(input); + data.rotateMix = ReadFloat(input); + data.translateMix = ReadFloat(input); + data.scaleMix = ReadFloat(input); + data.shearMix = ReadFloat(input); + skeletonData.transformConstraints.Add(data); + } + + // Path constraints + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + PathConstraintData data = new PathConstraintData(ReadString(input)); + data.order = ReadVarint(input, true); + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) + data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); + data.target = skeletonData.slots.Items[ReadVarint(input, true)]; + data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(ReadVarint(input, true)); + data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue(ReadVarint(input, true)); + data.rotateMode = (RotateMode)Enum.GetValues(typeof(RotateMode)).GetValue(ReadVarint(input, true)); + data.offsetRotation = ReadFloat(input); + data.position = ReadFloat(input); + if (data.positionMode == PositionMode.Fixed) data.position *= scale; + data.spacing = ReadFloat(input); + if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; + data.rotateMix = ReadFloat(input); + data.translateMix = ReadFloat(input); + skeletonData.pathConstraints.Add(data); + } + + // Default skin. + Skin defaultSkin = ReadSkin(input, skeletonData, "default", nonessential); + if (defaultSkin != null) { + skeletonData.defaultSkin = defaultSkin; + skeletonData.skins.Add(defaultSkin); + } + + // Skins. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) + skeletonData.skins.Add(ReadSkin(input, skeletonData, ReadString(input), nonessential)); + + // Linked meshes. + for (int i = 0, n = linkedMeshes.Count; i < n; i++) { + SkeletonJson.LinkedMesh linkedMesh = linkedMeshes[i]; + Skin skin = linkedMesh.skin == null ? skeletonData.DefaultSkin : skeletonData.FindSkin(linkedMesh.skin); + if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin); + Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent); + if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent); + linkedMesh.mesh.ParentMesh = (MeshAttachment)parent; + linkedMesh.mesh.UpdateUVs(); + } + linkedMeshes.Clear(); + + // Events. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + EventData data = new EventData(ReadString(input)); + data.Int = ReadVarint(input, false); + data.Float = ReadFloat(input); + data.String = ReadString(input); + skeletonData.events.Add(data); + } + + // Animations. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) + ReadAnimation(ReadString(input), input, skeletonData); + + skeletonData.bones.TrimExcess(); + skeletonData.slots.TrimExcess(); + skeletonData.skins.TrimExcess(); + skeletonData.events.TrimExcess(); + skeletonData.animations.TrimExcess(); + skeletonData.ikConstraints.TrimExcess(); + skeletonData.pathConstraints.TrimExcess(); + return skeletonData; + } + + + /// May be null. + private Skin ReadSkin (Stream input, SkeletonData skeletonData, String skinName, bool nonessential) { + int slotCount = ReadVarint(input, true); + if (slotCount == 0) return null; + Skin skin = new Skin(skinName); + for (int i = 0; i < slotCount; i++) { + int slotIndex = ReadVarint(input, true); + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { + String name = ReadString(input); + Attachment attachment = ReadAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + if (attachment != null) skin.AddAttachment(slotIndex, name, attachment); + } + } + return skin; + } + + private Attachment ReadAttachment (Stream input, SkeletonData skeletonData, Skin skin, int slotIndex, String attachmentName, bool nonessential) { + float scale = Scale; + + String name = ReadString(input); + if (name == null) name = attachmentName; + + AttachmentType type = (AttachmentType)input.ReadByte(); + switch (type) { + case AttachmentType.Region: { + String path = ReadString(input); + float rotation = ReadFloat(input); + float x = ReadFloat(input); + float y = ReadFloat(input); + float scaleX = ReadFloat(input); + float scaleY = ReadFloat(input); + float width = ReadFloat(input); + float height = ReadFloat(input); + int color = ReadInt(input); + + if (path == null) path = name; + RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path); + if (region == null) return null; + region.Path = path; + region.x = x * scale; + region.y = y * scale; + region.scaleX = scaleX; + region.scaleY = scaleY; + region.rotation = rotation; + region.width = width * scale; + region.height = height * scale; + region.r = ((color & 0xff000000) >> 24) / 255f; + region.g = ((color & 0x00ff0000) >> 16) / 255f; + region.b = ((color & 0x0000ff00) >> 8) / 255f; + region.a = ((color & 0x000000ff)) / 255f; + region.UpdateOffset(); + return region; + } + case AttachmentType.Boundingbox: { + int vertexCount = ReadVarint(input, true); + Vertices vertices = ReadVertices(input, vertexCount); + if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning. + + BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name); + if (box == null) return null; + box.worldVerticesLength = vertexCount << 1; + box.vertices = vertices.vertices; + box.bones = vertices.bones; + return box; + } + case AttachmentType.Mesh: { + String path = ReadString(input); + int color = ReadInt(input); + int vertexCount = ReadVarint(input, true); + float[] uvs = ReadFloatArray(input, vertexCount << 1, 1); + int[] triangles = ReadShortArray(input); + Vertices vertices = ReadVertices(input, vertexCount); + int hullLength = ReadVarint(input, true); + int[] edges = null; + float width = 0, height = 0; + if (nonessential) { + edges = ReadShortArray(input); + width = ReadFloat(input); + height = ReadFloat(input); + } + + if (path == null) path = name; + MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path); + if (mesh == null) return null; + mesh.Path = path; + mesh.r = ((color & 0xff000000) >> 24) / 255f; + mesh.g = ((color & 0x00ff0000) >> 16) / 255f; + mesh.b = ((color & 0x0000ff00) >> 8) / 255f; + mesh.a = ((color & 0x000000ff)) / 255f; + mesh.bones = vertices.bones; + mesh.vertices = vertices.vertices; + mesh.WorldVerticesLength = vertexCount << 1; + mesh.triangles = triangles; + mesh.regionUVs = uvs; + mesh.UpdateUVs(); + mesh.HullLength = hullLength << 1; + if (nonessential) { + mesh.Edges = edges; + mesh.Width = width * scale; + mesh.Height = height * scale; + } + return mesh; + } + case AttachmentType.Linkedmesh: { + String path = ReadString(input); + int color = ReadInt(input); + String skinName = ReadString(input); + String parent = ReadString(input); + bool inheritDeform = ReadBoolean(input); + float width = 0, height = 0; + if (nonessential) { + width = ReadFloat(input); + height = ReadFloat(input); + } + + if (path == null) path = name; + MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path); + if (mesh == null) return null; + mesh.Path = path; + mesh.r = ((color & 0xff000000) >> 24) / 255f; + mesh.g = ((color & 0x00ff0000) >> 16) / 255f; + mesh.b = ((color & 0x0000ff00) >> 8) / 255f; + mesh.a = ((color & 0x000000ff)) / 255f; + mesh.inheritDeform = inheritDeform; + if (nonessential) { + mesh.Width = width * scale; + mesh.Height = height * scale; + } + linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent)); + return mesh; + } + case AttachmentType.Path: { + bool closed = ReadBoolean(input); + bool constantSpeed = ReadBoolean(input); + int vertexCount = ReadVarint(input, true); + Vertices vertices = ReadVertices(input, vertexCount); + float[] lengths = new float[vertexCount / 3]; + for (int i = 0, n = lengths.Length; i < n; i++) + lengths[i] = ReadFloat(input) * scale; + if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; + + PathAttachment path = attachmentLoader.NewPathAttachment(skin, name); + if (path == null) return null; + path.closed = closed; + path.constantSpeed = constantSpeed; + path.worldVerticesLength = vertexCount << 1; + path.vertices = vertices.vertices; + path.bones = vertices.bones; + path.lengths = lengths; + return path; + } + case AttachmentType.Point: { + float rotation = ReadFloat(input); + float x = ReadFloat(input); + float y = ReadFloat(input); + if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; + + PointAttachment point = attachmentLoader.NewPointAttachment(skin, name); + if (point == null) return null; + point.x = x * scale; + point.y = y * scale; + point.rotation = rotation; + //if (nonessential) point.color = color; + return point; + } + case AttachmentType.Clipping: { + int endSlotIndex = ReadVarint(input, true); + int vertexCount = ReadVarint(input, true); + Vertices vertices = ReadVertices(input, vertexCount); + if (nonessential) ReadInt(input); + + ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name); + if (clip == null) return null; + clip.EndSlot = skeletonData.slots.Items[endSlotIndex]; + clip.worldVerticesLength = vertexCount << 1; + clip.vertices = vertices.vertices; + clip.bones = vertices.bones; + return clip; + } + } + return null; + } + + private Vertices ReadVertices (Stream input, int vertexCount) { + float scale = Scale; + int verticesLength = vertexCount << 1; + Vertices vertices = new Vertices(); + if(!ReadBoolean(input)) { + vertices.vertices = ReadFloatArray(input, verticesLength, scale); + return vertices; + } + var weights = new ExposedList(verticesLength * 3 * 3); + var bonesArray = new ExposedList(verticesLength * 3); + for (int i = 0; i < vertexCount; i++) { + int boneCount = ReadVarint(input, true); + bonesArray.Add(boneCount); + for (int ii = 0; ii < boneCount; ii++) { + bonesArray.Add(ReadVarint(input, true)); + weights.Add(ReadFloat(input) * scale); + weights.Add(ReadFloat(input) * scale); + weights.Add(ReadFloat(input)); + } + } + + vertices.vertices = weights.ToArray(); + vertices.bones = bonesArray.ToArray(); + return vertices; + } + + private float[] ReadFloatArray (Stream input, int n, float scale) { + float[] array = new float[n]; + if (scale == 1) { + for (int i = 0; i < n; i++) + array[i] = ReadFloat(input); + } else { + for (int i = 0; i < n; i++) + array[i] = ReadFloat(input) * scale; + } + return array; + } + + private int[] ReadShortArray (Stream input) { + int n = ReadVarint(input, true); + int[] array = new int[n]; + for (int i = 0; i < n; i++) + array[i] = (input.ReadByte() << 8) | input.ReadByte(); + return array; + } + + private void ReadAnimation (String name, Stream input, SkeletonData skeletonData) { + var timelines = new ExposedList(); + float scale = Scale; + float duration = 0; + + // Slot timelines. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + int slotIndex = ReadVarint(input, true); + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { + int timelineType = input.ReadByte(); + int frameCount = ReadVarint(input, true); + switch (timelineType) { + case SLOT_ATTACHMENT: { + AttachmentTimeline timeline = new AttachmentTimeline(frameCount); + timeline.slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) + timeline.SetFrame(frameIndex, ReadFloat(input), ReadString(input)); + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[frameCount - 1]); + break; + } + case SLOT_COLOR: { + ColorTimeline timeline = new ColorTimeline(frameCount); + timeline.slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + float time = ReadFloat(input); + int color = ReadInt(input); + float r = ((color & 0xff000000) >> 24) / 255f; + float g = ((color & 0x00ff0000) >> 16) / 255f; + float b = ((color & 0x0000ff00) >> 8) / 255f; + float a = ((color & 0x000000ff)) / 255f; + timeline.SetFrame(frameIndex, time, r, g, b, a); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]); + break; + } + case SLOT_TWO_COLOR: { + TwoColorTimeline timeline = new TwoColorTimeline(frameCount); + timeline.slotIndex = slotIndex; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + float time = ReadFloat(input); + int color = ReadInt(input); + float r = ((color & 0xff000000) >> 24) / 255f; + float g = ((color & 0x00ff0000) >> 16) / 255f; + float b = ((color & 0x0000ff00) >> 8) / 255f; + float a = ((color & 0x000000ff)) / 255f; + int color2 = ReadInt(input); // 0x00rrggbb + float r2 = ((color2 & 0x00ff0000) >> 16) / 255f; + float g2 = ((color2 & 0x0000ff00) >> 8) / 255f; + float b2 = ((color2 & 0x000000ff)) / 255f; + + timeline.SetFrame(frameIndex, time, r, g, b, a, r2, g2, b2); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]); + break; + } + } + } + } + + // Bone timelines. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + int boneIndex = ReadVarint(input, true); + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { + int timelineType = input.ReadByte(); + int frameCount = ReadVarint(input, true); + switch (timelineType) { + case BONE_ROTATE: { + RotateTimeline timeline = new RotateTimeline(frameCount); + timeline.boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input)); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]); + break; + } + case BONE_TRANSLATE: + case BONE_SCALE: + case BONE_SHEAR: { + TranslateTimeline timeline; + float timelineScale = 1; + if (timelineType == BONE_SCALE) + timeline = new ScaleTimeline(frameCount); + else if (timelineType == BONE_SHEAR) + timeline = new ShearTimeline(frameCount); + else { + timeline = new TranslateTimeline(frameCount); + timelineScale = scale; + } + timeline.boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input) * timelineScale, ReadFloat(input) + * timelineScale); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]); + break; + } + } + } + } + + // IK timelines. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + int index = ReadVarint(input, true); + int frameCount = ReadVarint(input, true); + IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount); + timeline.ikConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input)); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(frameCount - 1) * IkConstraintTimeline.ENTRIES]); + } + + // Transform constraint timelines. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + int index = ReadVarint(input, true); + int frameCount = ReadVarint(input, true); + TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount); + timeline.transformConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input)); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TransformConstraintTimeline.ENTRIES]); + } + + // Path constraint timelines. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + int index = ReadVarint(input, true); + PathConstraintData data = skeletonData.pathConstraints.Items[index]; + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { + int timelineType = ReadSByte(input); + int frameCount = ReadVarint(input, true); + switch(timelineType) { + case PATH_POSITION: + case PATH_SPACING: { + PathConstraintPositionTimeline timeline; + float timelineScale = 1; + if (timelineType == PATH_SPACING) { + timeline = new PathConstraintSpacingTimeline(frameCount); + if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; + } else { + timeline = new PathConstraintPositionTimeline(frameCount); + if (data.positionMode == PositionMode.Fixed) timelineScale = scale; + } + timeline.pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input) * timelineScale); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]); + break; + } + case PATH_MIX: { + PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount); + timeline.pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input)); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]); + break; + } + } + } + } + + // Deform timelines. + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + Skin skin = skeletonData.skins.Items[ReadVarint(input, true)]; + for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { + int slotIndex = ReadVarint(input, true); + for (int iii = 0, nnn = ReadVarint(input, true); iii < nnn; iii++) { + VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, ReadString(input)); + bool weighted = attachment.bones != null; + float[] vertices = attachment.vertices; + int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length; + + int frameCount = ReadVarint(input, true); + DeformTimeline timeline = new DeformTimeline(frameCount); + timeline.slotIndex = slotIndex; + timeline.attachment = attachment; + + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + float time = ReadFloat(input); + float[] deform; + int end = ReadVarint(input, true); + if (end == 0) + deform = weighted ? new float[deformLength] : vertices; + else { + deform = new float[deformLength]; + int start = ReadVarint(input, true); + end += start; + if (scale == 1) { + for (int v = start; v < end; v++) + deform[v] = ReadFloat(input); + } else { + for (int v = start; v < end; v++) + deform[v] = ReadFloat(input) * scale; + } + if (!weighted) { + for (int v = 0, vn = deform.Length; v < vn; v++) + deform[v] += vertices[v]; + } + } + + timeline.SetFrame(frameIndex, time, deform); + if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[frameCount - 1]); + } + } + } + + // Draw order timeline. + int drawOrderCount = ReadVarint(input, true); + if (drawOrderCount > 0) { + DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount); + int slotCount = skeletonData.slots.Count; + for (int i = 0; i < drawOrderCount; i++) { + float time = ReadFloat(input); + int offsetCount = ReadVarint(input, true); + int[] drawOrder = new int[slotCount]; + for (int ii = slotCount - 1; ii >= 0; ii--) + drawOrder[ii] = -1; + int[] unchanged = new int[slotCount - offsetCount]; + int originalIndex = 0, unchangedIndex = 0; + for (int ii = 0; ii < offsetCount; ii++) { + int slotIndex = ReadVarint(input, true); + // Collect unchanged items. + while (originalIndex != slotIndex) + unchanged[unchangedIndex++] = originalIndex++; + // Set changed items. + drawOrder[originalIndex + ReadVarint(input, true)] = originalIndex++; + } + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (int ii = slotCount - 1; ii >= 0; ii--) + if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + timeline.SetFrame(i, time, drawOrder); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[drawOrderCount - 1]); + } + + // Event timeline. + int eventCount = ReadVarint(input, true); + if (eventCount > 0) { + EventTimeline timeline = new EventTimeline(eventCount); + for (int i = 0; i < eventCount; i++) { + float time = ReadFloat(input); + EventData eventData = skeletonData.events.Items[ReadVarint(input, true)]; + Event e = new Event(time, eventData); + e.Int = ReadVarint(input, false); + e.Float = ReadFloat(input); + e.String = ReadBoolean(input) ? ReadString(input) : eventData.String; + timeline.SetFrame(i, e); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[eventCount - 1]); + } + + timelines.TrimExcess(); + skeletonData.animations.Add(new Animation(name, timelines, duration)); + } + + private void ReadCurve (Stream input, int frameIndex, CurveTimeline timeline) { + switch (input.ReadByte()) { + case CURVE_STEPPED: + timeline.SetStepped(frameIndex); + break; + case CURVE_BEZIER: + timeline.SetCurve(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input)); + break; + } + } + + private static sbyte ReadSByte (Stream input) { + int value = input.ReadByte(); + if (value == -1) throw new EndOfStreamException(); + return (sbyte)value; + } + + private static bool ReadBoolean (Stream input) { + return input.ReadByte() != 0; + } + + private float ReadFloat (Stream input) { + buffer[3] = (byte)input.ReadByte(); + buffer[2] = (byte)input.ReadByte(); + buffer[1] = (byte)input.ReadByte(); + buffer[0] = (byte)input.ReadByte(); + return BitConverter.ToSingle(buffer, 0); + } + + private static int ReadInt (Stream input) { + return (input.ReadByte() << 24) + (input.ReadByte() << 16) + (input.ReadByte() << 8) + input.ReadByte(); + } + + private static int ReadVarint (Stream input, bool optimizePositive) { + int b = input.ReadByte(); + int result = b & 0x7F; + if ((b & 0x80) != 0) { + b = input.ReadByte(); + result |= (b & 0x7F) << 7; + if ((b & 0x80) != 0) { + b = input.ReadByte(); + result |= (b & 0x7F) << 14; + if ((b & 0x80) != 0) { + b = input.ReadByte(); + result |= (b & 0x7F) << 21; + if ((b & 0x80) != 0) result |= (input.ReadByte() & 0x7F) << 28; + } + } + } + return optimizePositive ? result : ((result >> 1) ^ -(result & 1)); + } + + private string ReadString (Stream input) { + int byteCount = ReadVarint(input, true); + switch (byteCount) { + case 0: + return null; + case 1: + return ""; + } + byteCount--; + byte[] buffer = this.buffer; + if (buffer.Length < byteCount) buffer = new byte[byteCount]; + ReadFully(input, buffer, 0, byteCount); + return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount); + } + + private static void ReadFully (Stream input, byte[] buffer, int offset, int length) { + while (length > 0) { + int count = input.Read(buffer, offset, length); + if (count <= 0) throw new EndOfStreamException(); + offset += count; + length -= count; + } + } + + internal class Vertices { + public int[] bones; + public float[] vertices; + } + } +} diff --git a/Assets/Spine/spine-csharp/SkeletonBinary.cs.meta b/Assets/Spine/spine-csharp/SkeletonBinary.cs.meta new file mode 100644 index 0000000..fd3aabc --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonBinary.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 40d8a8f15082f3844a5c9c8c3ef2047f +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/SkeletonBounds.cs b/Assets/Spine/spine-csharp/SkeletonBounds.cs new file mode 100644 index 0000000..e0096c2 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonBounds.cs @@ -0,0 +1,234 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + + /// + /// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon. + /// The polygon vertices are provided along with convenience methods for doing hit detection. + /// + public class SkeletonBounds { + private ExposedList polygonPool = new ExposedList(); + private float minX, minY, maxX, maxY; + + public ExposedList BoundingBoxes { get; private set; } + public ExposedList Polygons { get; private set; } + public float MinX { get { return minX; } set { minX = value; } } + public float MinY { get { return minY; } set { minY = value; } } + public float MaxX { get { return maxX; } set { maxX = value; } } + public float MaxY { get { return maxY; } set { maxY = value; } } + public float Width { get { return maxX - minX; } } + public float Height { get { return maxY - minY; } } + + public SkeletonBounds () { + BoundingBoxes = new ExposedList(); + Polygons = new ExposedList(); + } + + /// + /// Clears any previous polygons, finds all visible bounding box attachments, + /// and computes the world vertices for each bounding box's polygon. + /// The skeleton. + /// + /// If true, the axis aligned bounding box containing all the polygons is computed. + /// If false, the SkeletonBounds AABB methods will always return true. + /// + public void Update (Skeleton skeleton, bool updateAabb) { + ExposedList boundingBoxes = BoundingBoxes; + ExposedList polygons = Polygons; + ExposedList slots = skeleton.slots; + int slotCount = slots.Count; + + boundingBoxes.Clear(); + for (int i = 0, n = polygons.Count; i < n; i++) + polygonPool.Add(polygons.Items[i]); + polygons.Clear(); + + for (int i = 0; i < slotCount; i++) { + Slot slot = slots.Items[i]; + BoundingBoxAttachment boundingBox = slot.attachment as BoundingBoxAttachment; + if (boundingBox == null) continue; + boundingBoxes.Add(boundingBox); + + Polygon polygon = null; + int poolCount = polygonPool.Count; + if (poolCount > 0) { + polygon = polygonPool.Items[poolCount - 1]; + polygonPool.RemoveAt(poolCount - 1); + } else + polygon = new Polygon(); + polygons.Add(polygon); + + int count = boundingBox.worldVerticesLength; + polygon.Count = count; + if (polygon.Vertices.Length < count) polygon.Vertices = new float[count]; + boundingBox.ComputeWorldVertices(slot, polygon.Vertices); + } + + if (updateAabb) { + AabbCompute(); + } else { + minX = int.MinValue; + minY = int.MinValue; + maxX = int.MaxValue; + maxY = int.MaxValue; + } + } + + private void AabbCompute () { + float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue; + ExposedList polygons = Polygons; + for (int i = 0, n = polygons.Count; i < n; i++) { + Polygon polygon = polygons.Items[i]; + float[] vertices = polygon.Vertices; + for (int ii = 0, nn = polygon.Count; ii < nn; ii += 2) { + float x = vertices[ii]; + float y = vertices[ii + 1]; + minX = Math.Min(minX, x); + minY = Math.Min(minY, y); + maxX = Math.Max(maxX, x); + maxY = Math.Max(maxY, y); + } + } + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + + + /// Returns true if the axis aligned bounding box contains the point. + public bool AabbContainsPoint (float x, float y) { + return x >= minX && x <= maxX && y >= minY && y <= maxY; + } + + /// Returns true if the axis aligned bounding box intersects the line segment. + public bool AabbIntersectsSegment (float x1, float y1, float x2, float y2) { + float minX = this.minX; + float minY = this.minY; + float maxX = this.maxX; + float maxY = this.maxY; + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) + return false; + float m = (y2 - y1) / (x2 - x1); + float y = m * (minX - x1) + y1; + if (y > minY && y < maxY) return true; + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) return true; + float x = (minY - y1) / m + x1; + if (x > minX && x < maxX) return true; + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) return true; + return false; + } + + /// Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. + public bool AabbIntersectsSkeleton (SkeletonBounds bounds) { + return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY; + } + + /// Returns true if the polygon contains the point. + public bool ContainsPoint (Polygon polygon, float x, float y) { + float[] vertices = polygon.Vertices; + int nn = polygon.Count; + + int prevIndex = nn - 2; + bool inside = false; + for (int ii = 0; ii < nn; ii += 2) { + float vertexY = vertices[ii + 1]; + float prevY = vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + float vertexX = vertices[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside; + } + prevIndex = ii; + } + return inside; + } + + /// Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more + /// efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. + public BoundingBoxAttachment ContainsPoint (float x, float y) { + ExposedList polygons = Polygons; + for (int i = 0, n = polygons.Count; i < n; i++) + if (ContainsPoint(polygons.Items[i], x, y)) return BoundingBoxes.Items[i]; + return null; + } + + /// Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually + /// more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. + public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) { + ExposedList polygons = Polygons; + for (int i = 0, n = polygons.Count; i < n; i++) + if (IntersectsSegment(polygons.Items[i], x1, y1, x2, y2)) return BoundingBoxes.Items[i]; + return null; + } + + /// Returns true if the polygon contains the line segment. + public bool IntersectsSegment (Polygon polygon, float x1, float y1, float x2, float y2) { + float[] vertices = polygon.Vertices; + int nn = polygon.Count; + + float width12 = x1 - x2, height12 = y1 - y2; + float det1 = x1 * y2 - y1 * x2; + float x3 = vertices[nn - 2], y3 = vertices[nn - 1]; + for (int ii = 0; ii < nn; ii += 2) { + float x4 = vertices[ii], y4 = vertices[ii + 1]; + float det2 = x3 * y4 - y3 * x4; + float width34 = x3 - x4, height34 = y3 - y4; + float det3 = width12 * height34 - height12 * width34; + float x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + float y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; + } + x3 = x4; + y3 = y4; + } + return false; + } + + public Polygon GetPolygon (BoundingBoxAttachment attachment) { + int index = BoundingBoxes.IndexOf(attachment); + return index == -1 ? null : Polygons.Items[index]; + } + } + + public class Polygon { + public float[] Vertices { get; set; } + public int Count { get; set; } + + public Polygon () { + Vertices = new float[16]; + } + } +} diff --git a/Assets/Spine/spine-csharp/SkeletonBounds.cs.meta b/Assets/Spine/spine-csharp/SkeletonBounds.cs.meta new file mode 100644 index 0000000..db6a2d1 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonBounds.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 087b328a58c93b149bb977eee3a17258 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/SkeletonClipping.cs b/Assets/Spine/spine-csharp/SkeletonClipping.cs new file mode 100644 index 0000000..a11c0c8 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonClipping.cs @@ -0,0 +1,285 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class SkeletonClipping { + internal readonly Triangulator triangulator = new Triangulator(); + internal readonly ExposedList clippingPolygon = new ExposedList(); + internal readonly ExposedList clipOutput = new ExposedList(128); + internal readonly ExposedList clippedVertices = new ExposedList(128); + internal readonly ExposedList clippedTriangles = new ExposedList(128); + internal readonly ExposedList clippedUVs = new ExposedList(128); + internal readonly ExposedList scratch = new ExposedList(); + + internal ClippingAttachment clipAttachment; + internal ExposedList> clippingPolygons; + + public ExposedList ClippedVertices { get { return clippedVertices; } } + public ExposedList ClippedTriangles { get { return clippedTriangles; } } + public ExposedList ClippedUVs { get { return clippedUVs; } } + + public bool IsClipping { get { return clipAttachment != null; } } + + public int ClipStart (Slot slot, ClippingAttachment clip) { + if (clipAttachment != null) return 0; + clipAttachment = clip; + + int n = clip.worldVerticesLength; + float[] vertices = clippingPolygon.Resize(n).Items; + clip.ComputeWorldVertices(slot, 0, n, vertices, 0, 2); + MakeClockwise(clippingPolygon); + clippingPolygons = triangulator.Decompose(clippingPolygon, triangulator.Triangulate(clippingPolygon)); + foreach (var polygon in clippingPolygons) { + MakeClockwise(polygon); + polygon.Add(polygon.Items[0]); + polygon.Add(polygon.Items[1]); + } + return clippingPolygons.Count; + } + + public void ClipEnd (Slot slot) { + if (clipAttachment != null && clipAttachment.endSlot == slot.data) ClipEnd(); + } + + public void ClipEnd () { + if (clipAttachment == null) return; + clipAttachment = null; + clippingPolygons = null; + clippedVertices.Clear(); + clippedTriangles.Clear(); + clippingPolygon.Clear(); + } + + public void ClipTriangles (float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) { + ExposedList clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; + var clippedTriangles = this.clippedTriangles; + var polygons = clippingPolygons.Items; + int polygonsCount = clippingPolygons.Count; + + int index = 0; + clippedVertices.Clear(); + clippedUVs.Clear(); + clippedTriangles.Clear(); + //outer: + for (int i = 0; i < trianglesLength; i += 3) { + int vertexOffset = triangles[i] << 1; + float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 1] << 1; + float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; + + vertexOffset = triangles[i + 2] << 1; + float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; + + for (int p = 0; p < polygonsCount; p++) { + int s = clippedVertices.Count; + if (Clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { + int clipOutputLength = clipOutput.Count; + if (clipOutputLength == 0) continue; + float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + float d = 1 / (d0 * d2 + d1 * (y1 - y3)); + + int clipOutputCount = clipOutputLength >> 1; + float[] clipOutputItems = clipOutput.Items; + float[] clippedVerticesItems = clippedVertices.Resize(s + clipOutputCount * 2).Items; + float[] clippedUVsItems = clippedUVs.Resize(s + clipOutputCount * 2).Items; + for (int ii = 0; ii < clipOutputLength; ii += 2) { + float x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; + clippedVerticesItems[s] = x; + clippedVerticesItems[s + 1] = y; + float c0 = x - x3, c1 = y - y3; + float a = (d0 * c0 + d1 * c1) * d; + float b = (d4 * c0 + d2 * c1) * d; + float c = 1 - a - b; + clippedUVsItems[s] = u1 * a + u2 * b + u3 * c; + clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c; + s += 2; + } + + s = clippedTriangles.Count; + int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3 * (clipOutputCount - 2)).Items; + clipOutputCount--; + for (int ii = 1; ii < clipOutputCount; ii++) { + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = index + ii; + clippedTrianglesItems[s + 2] = index + ii + 1; + s += 3; + } + index += clipOutputCount + 1; + } + else { + float[] clippedVerticesItems = clippedVertices.Resize(s + 3 * 2).Items; + float[] clippedUVsItems = clippedUVs.Resize(s + 3 * 2).Items; + clippedVerticesItems[s] = x1; + clippedVerticesItems[s + 1] = y1; + clippedVerticesItems[s + 2] = x2; + clippedVerticesItems[s + 3] = y2; + clippedVerticesItems[s + 4] = x3; + clippedVerticesItems[s + 5] = y3; + + clippedUVsItems[s] = u1; + clippedUVsItems[s + 1] = v1; + clippedUVsItems[s + 2] = u2; + clippedUVsItems[s + 3] = v2; + clippedUVsItems[s + 4] = u3; + clippedUVsItems[s + 5] = v3; + + s = clippedTriangles.Count; + int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items; + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = index + 1; + clippedTrianglesItems[s + 2] = index + 2; + index += 3; + break; //continue outer; + } + } + } + + } + + /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping + * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ + internal bool Clip (float x1, float y1, float x2, float y2, float x3, float y3, ExposedList clippingArea, ExposedList output) { + var originalOutput = output; + var clipped = false; + + // Avoid copy at the end. + ExposedList input = null; + if (clippingArea.Count % 4 >= 2) { + input = output; + output = scratch; + } else { + input = scratch; + } + + input.Clear(); + input.Add(x1); + input.Add(y1); + input.Add(x2); + input.Add(y2); + input.Add(x3); + input.Add(y3); + input.Add(x1); + input.Add(y1); + output.Clear(); + + float[] clippingVertices = clippingArea.Items; + int clippingVerticesLast = clippingArea.Count - 4; + for (int i = 0; ; i += 2) { + float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; + + float[] inputVertices = input.Items; + int inputVerticesLength = input.Count - 2, outputStart = output.Count; + for (int ii = 0; ii < inputVerticesLength; ii += 2) { + float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; + float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; + bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; + if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { + if (side2) { // v1 inside, v2 inside + output.Add(inputX2); + output.Add(inputY2); + continue; + } + // v1 inside, v2 outside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.Add(edgeX + (edgeX2 - edgeX) * ua); + output.Add(edgeY + (edgeY2 - edgeY) * ua); + } + else if (side2) { // v1 outside, v2 inside + float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.Add(edgeX + (edgeX2 - edgeX) * ua); + output.Add(edgeY + (edgeY2 - edgeY) * ua); + output.Add(inputX2); + output.Add(inputY2); + } + clipped = true; + } + + if (outputStart == output.Count) { // All edges outside. + originalOutput.Clear(); + return true; + } + + output.Add(output.Items[0]); + output.Add(output.Items[1]); + + if (i == clippingVerticesLast) break; + var temp = output; + output = input; + output.Clear(); + input = temp; + } + + if (originalOutput != output) { + originalOutput.Clear(); + for (int i = 0, n = output.Count - 2; i < n; i++) { + originalOutput.Add(output.Items[i]); + } + } else { + originalOutput.Resize(originalOutput.Count - 2); + } + + return clipped; + } + + static void MakeClockwise (ExposedList polygon) { + float[] vertices = polygon.Items; + int verticeslength = polygon.Count; + + float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y; + for (int i = 0, n = verticeslength - 3; i < n; i += 2) { + p1x = vertices[i]; + p1y = vertices[i + 1]; + p2x = vertices[i + 2]; + p2y = vertices[i + 3]; + area += p1x * p2y - p2x * p1y; + } + if (area < 0) return; + + for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { + float x = vertices[i], y = vertices[i + 1]; + int other = lastX - i; + vertices[i] = vertices[other]; + vertices[i + 1] = vertices[other + 1]; + vertices[other] = x; + vertices[other + 1] = y; + } + } + } +} diff --git a/Assets/Spine/spine-csharp/SkeletonClipping.cs.meta b/Assets/Spine/spine-csharp/SkeletonClipping.cs.meta new file mode 100644 index 0000000..73ecd0d --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonClipping.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7db809d277afd0e4a8e8c6b703002ee0 +timeCreated: 1492744746 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/SkeletonData.cs b/Assets/Spine/spine-csharp/SkeletonData.cs new file mode 100644 index 0000000..b2e8310 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonData.cs @@ -0,0 +1,224 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + + /// Stores the setup pose and all of the stateless data for a skeleton. + public class SkeletonData { + internal string name; + internal ExposedList bones = new ExposedList(); // Ordered parents first + internal ExposedList slots = new ExposedList(); // Setup pose draw order. + internal ExposedList skins = new ExposedList(); + internal Skin defaultSkin; + internal ExposedList events = new ExposedList(); + internal ExposedList animations = new ExposedList(); + internal ExposedList ikConstraints = new ExposedList(); + internal ExposedList transformConstraints = new ExposedList(); + internal ExposedList pathConstraints = new ExposedList(); + internal float width, height; + internal string version, hash; + + // Nonessential. + internal float fps; + internal string imagesPath; + + public string Name { get { return name; } set { name = value; } } + + /// The skeleton's bones, sorted parent first. The root bone is always the first bone. + public ExposedList Bones { get { return bones; } } + + public ExposedList Slots { get { return slots; } } + + /// All skins, including the default skin. + public ExposedList Skins { get { return skins; } set { skins = value; } } + + /// + /// The skeleton's default skin. + /// By default this skin contains all attachments that were not in a skin in Spine. + /// + /// May be null. + public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } } + + public ExposedList Events { get { return events; } set { events = value; } } + public ExposedList Animations { get { return animations; } set { animations = value; } } + public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } + public ExposedList TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } } + public ExposedList PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } } + + public float Width { get { return width; } set { width = value; } } + public float Height { get { return height; } set { height = value; } } + /// The Spine version used to export this data, or null. + public string Version { get { return version; } set { version = value; } } + public string Hash { get { return hash; } set { hash = value; } } + public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } } + + /// + /// The dopesheet FPS in Spine. Available only when nonessential data was exported. + public float Fps { get { return fps; } set { fps = value; } } + + // --- Bones. + + /// + /// Finds a bone by comparing each bone's name. + /// It is more efficient to cache the results of this method than to call it multiple times. + /// May be null. + public BoneData FindBone (string boneName) { + if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); + var bones = this.bones; + var bonesItems = bones.Items; + for (int i = 0, n = bones.Count; i < n; i++) { + BoneData bone = bonesItems[i]; + if (bone.name == boneName) return bone; + } + return null; + } + + /// -1 if the bone was not found. + public int FindBoneIndex (string boneName) { + if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); + var bones = this.bones; + var bonesItems = bones.Items; + for (int i = 0, n = bones.Count; i < n; i++) + if (bonesItems[i].name == boneName) return i; + return -1; + } + + // --- Slots. + + /// May be null. + public SlotData FindSlot (string slotName) { + if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); + ExposedList slots = this.slots; + for (int i = 0, n = slots.Count; i < n; i++) { + SlotData slot = slots.Items[i]; + if (slot.name == slotName) return slot; + } + return null; + } + + /// -1 if the slot was not found. + public int FindSlotIndex (string slotName) { + if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); + ExposedList slots = this.slots; + for (int i = 0, n = slots.Count; i < n; i++) + if (slots.Items[i].name == slotName) return i; + return -1; + } + + // --- Skins. + + /// May be null. + public Skin FindSkin (string skinName) { + if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null."); + foreach (Skin skin in skins) + if (skin.name == skinName) return skin; + return null; + } + + // --- Events. + + /// May be null. + public EventData FindEvent (string eventDataName) { + if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null."); + foreach (EventData eventData in events) + if (eventData.name == eventDataName) return eventData; + return null; + } + + // --- Animations. + + /// May be null. + public Animation FindAnimation (string animationName) { + if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null."); + ExposedList animations = this.animations; + for (int i = 0, n = animations.Count; i < n; i++) { + Animation animation = animations.Items[i]; + if (animation.name == animationName) return animation; + } + return null; + } + + // --- IK constraints. + + /// May be null. + public IkConstraintData FindIkConstraint (string constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + ExposedList ikConstraints = this.ikConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) { + IkConstraintData ikConstraint = ikConstraints.Items[i]; + if (ikConstraint.name == constraintName) return ikConstraint; + } + return null; + } + + // --- Transform constraints. + + /// May be null. + public TransformConstraintData FindTransformConstraint (string constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + ExposedList transformConstraints = this.transformConstraints; + for (int i = 0, n = transformConstraints.Count; i < n; i++) { + TransformConstraintData transformConstraint = transformConstraints.Items[i]; + if (transformConstraint.name == constraintName) return transformConstraint; + } + return null; + } + + // --- Path constraints. + + /// May be null. + public PathConstraintData FindPathConstraint (string constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + ExposedList pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.Count; i < n; i++) { + PathConstraintData constraint = pathConstraints.Items[i]; + if (constraint.name.Equals(constraintName)) return constraint; + } + return null; + } + + /// -1 if the path constraint was not found. + public int FindPathConstraintIndex (string pathConstraintName) { + if (pathConstraintName == null) throw new ArgumentNullException("pathConstraintName", "pathConstraintName cannot be null."); + ExposedList pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.Count; i < n; i++) + if (pathConstraints.Items[i].name.Equals(pathConstraintName)) return i; + return -1; + } + + // --- + + override public string ToString () { + return name ?? base.ToString(); + } + } +} diff --git a/Assets/Spine/spine-csharp/SkeletonData.cs.meta b/Assets/Spine/spine-csharp/SkeletonData.cs.meta new file mode 100644 index 0000000..579186e --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2b813f63abbb6d94a80a5c050590a0be +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/SkeletonJson.cs b/Assets/Spine/spine-csharp/SkeletonJson.cs new file mode 100644 index 0000000..5f3fee5 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonJson.cs @@ -0,0 +1,865 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1) +#define IS_UNITY +#endif + +using System; +using System.IO; +using System.Collections.Generic; + +#if WINDOWS_STOREAPP +using System.Threading.Tasks; +using Windows.Storage; +#endif + +namespace Spine { + public class SkeletonJson { + public float Scale { get; set; } + + private AttachmentLoader attachmentLoader; + private List linkedMeshes = new List(); + + public SkeletonJson (params Atlas[] atlasArray) + : this(new AtlasAttachmentLoader(atlasArray)) { + } + + public SkeletonJson (AttachmentLoader attachmentLoader) { + if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null."); + this.attachmentLoader = attachmentLoader; + Scale = 1; + } + + #if !IS_UNITY && WINDOWS_STOREAPP + private async Task ReadFile(string path) { + var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; + var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false); + using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) { + SkeletonData skeletonData = ReadSkeletonData(reader); + skeletonData.Name = Path.GetFileNameWithoutExtension(path); + return skeletonData; + } + } + + public SkeletonData ReadSkeletonData (string path) { + return this.ReadFile(path).Result; + } + #else + public SkeletonData ReadSkeletonData (string path) { + #if WINDOWS_PHONE + using (var reader = new StreamReader(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) { + #else + using (var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))) { + #endif + SkeletonData skeletonData = ReadSkeletonData(reader); + skeletonData.name = Path.GetFileNameWithoutExtension(path); + return skeletonData; + } + } + #endif + + public SkeletonData ReadSkeletonData (TextReader reader) { + if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null."); + + float scale = this.Scale; + var skeletonData = new SkeletonData(); + + var root = Json.Deserialize(reader) as Dictionary; + if (root == null) throw new Exception("Invalid JSON."); + + // Skeleton. + if (root.ContainsKey("skeleton")) { + var skeletonMap = (Dictionary)root["skeleton"]; + skeletonData.hash = (string)skeletonMap["hash"]; + skeletonData.version = (string)skeletonMap["spine"]; + skeletonData.width = GetFloat(skeletonMap, "width", 0); + skeletonData.height = GetFloat(skeletonMap, "height", 0); + skeletonData.fps = GetFloat(skeletonMap, "fps", 0); + skeletonData.imagesPath = GetString(skeletonMap, "images", null); + } + + // Bones. + foreach (Dictionary boneMap in (List)root["bones"]) { + BoneData parent = null; + if (boneMap.ContainsKey("parent")) { + parent = skeletonData.FindBone((string)boneMap["parent"]); + if (parent == null) + throw new Exception("Parent bone not found: " + boneMap["parent"]); + } + var data = new BoneData(skeletonData.Bones.Count, (string)boneMap["name"], parent); + data.length = GetFloat(boneMap, "length", 0) * scale; + data.x = GetFloat(boneMap, "x", 0) * scale; + data.y = GetFloat(boneMap, "y", 0) * scale; + data.rotation = GetFloat(boneMap, "rotation", 0); + data.scaleX = GetFloat(boneMap, "scaleX", 1); + data.scaleY = GetFloat(boneMap, "scaleY", 1); + data.shearX = GetFloat(boneMap, "shearX", 0); + data.shearY = GetFloat(boneMap, "shearY", 0); + + string tm = GetString(boneMap, "transform", TransformMode.Normal.ToString()); + data.transformMode = (TransformMode)Enum.Parse(typeof(TransformMode), tm, true); + + skeletonData.bones.Add(data); + } + + // Slots. + if (root.ContainsKey("slots")) { + foreach (Dictionary slotMap in (List)root["slots"]) { + var slotName = (string)slotMap["name"]; + var boneName = (string)slotMap["bone"]; + BoneData boneData = skeletonData.FindBone(boneName); + if (boneData == null) throw new Exception("Slot bone not found: " + boneName); + var data = new SlotData(skeletonData.Slots.Count, slotName, boneData); + + if (slotMap.ContainsKey("color")) { + string color = (string)slotMap["color"]; + data.r = ToColor(color, 0); + data.g = ToColor(color, 1); + data.b = ToColor(color, 2); + data.a = ToColor(color, 3); + } + + if (slotMap.ContainsKey("dark")) { + var color2 = (string)slotMap["dark"]; + data.r2 = ToColor(color2, 0, 6); // expectedLength = 6. ie. "RRGGBB" + data.g2 = ToColor(color2, 1, 6); + data.b2 = ToColor(color2, 2, 6); + data.hasSecondColor = true; + } + + data.attachmentName = GetString(slotMap, "attachment", null); + if (slotMap.ContainsKey("blend")) + data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (string)slotMap["blend"], true); + else + data.blendMode = BlendMode.Normal; + skeletonData.slots.Add(data); + } + } + + // IK constraints. + if (root.ContainsKey("ik")) { + foreach (Dictionary constraintMap in (List)root["ik"]) { + IkConstraintData data = new IkConstraintData((string)constraintMap["name"]); + data.order = GetInt(constraintMap, "order", 0); + + foreach (string boneName in (List)constraintMap["bones"]) { + BoneData bone = skeletonData.FindBone(boneName); + if (bone == null) throw new Exception("IK constraint bone not found: " + boneName); + data.bones.Add(bone); + } + + string targetName = (string)constraintMap["target"]; + data.target = skeletonData.FindBone(targetName); + if (data.target == null) throw new Exception("Target bone not found: " + targetName); + + data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1; + data.mix = GetFloat(constraintMap, "mix", 1); + + skeletonData.ikConstraints.Add(data); + } + } + + // Transform constraints. + if (root.ContainsKey("transform")) { + foreach (Dictionary constraintMap in (List)root["transform"]) { + TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]); + data.order = GetInt(constraintMap, "order", 0); + + foreach (string boneName in (List)constraintMap["bones"]) { + BoneData bone = skeletonData.FindBone(boneName); + if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName); + data.bones.Add(bone); + } + + string targetName = (string)constraintMap["target"]; + data.target = skeletonData.FindBone(targetName); + if (data.target == null) throw new Exception("Target bone not found: " + targetName); + + data.local = GetBoolean(constraintMap, "local", false); + data.relative = GetBoolean(constraintMap, "relative", false); + + data.offsetRotation = GetFloat(constraintMap, "rotation", 0); + data.offsetX = GetFloat(constraintMap, "x", 0) * scale; + data.offsetY = GetFloat(constraintMap, "y", 0) * scale; + data.offsetScaleX = GetFloat(constraintMap, "scaleX", 0); + data.offsetScaleY = GetFloat(constraintMap, "scaleY", 0); + data.offsetShearY = GetFloat(constraintMap, "shearY", 0); + + data.rotateMix = GetFloat(constraintMap, "rotateMix", 1); + data.translateMix = GetFloat(constraintMap, "translateMix", 1); + data.scaleMix = GetFloat(constraintMap, "scaleMix", 1); + data.shearMix = GetFloat(constraintMap, "shearMix", 1); + + skeletonData.transformConstraints.Add(data); + } + } + + // Path constraints. + if(root.ContainsKey("path")) { + foreach (Dictionary constraintMap in (List)root["path"]) { + PathConstraintData data = new PathConstraintData((string)constraintMap["name"]); + data.order = GetInt(constraintMap, "order", 0); + + foreach (string boneName in (List)constraintMap["bones"]) { + BoneData bone = skeletonData.FindBone(boneName); + if (bone == null) throw new Exception("Path bone not found: " + boneName); + data.bones.Add(bone); + } + + string targetName = (string)constraintMap["target"]; + data.target = skeletonData.FindSlot(targetName); + if (data.target == null) throw new Exception("Target slot not found: " + targetName); + + data.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true); + data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true); + data.rotateMode = (RotateMode)Enum.Parse(typeof(RotateMode), GetString(constraintMap, "rotateMode", "tangent"), true); + data.offsetRotation = GetFloat(constraintMap, "rotation", 0); + data.position = GetFloat(constraintMap, "position", 0); + if (data.positionMode == PositionMode.Fixed) data.position *= scale; + data.spacing = GetFloat(constraintMap, "spacing", 0); + if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale; + data.rotateMix = GetFloat(constraintMap, "rotateMix", 1); + data.translateMix = GetFloat(constraintMap, "translateMix", 1); + + skeletonData.pathConstraints.Add(data); + } + } + + // Skins. + if (root.ContainsKey("skins")) { + foreach (KeyValuePair skinMap in (Dictionary)root["skins"]) { + var skin = new Skin(skinMap.Key); + foreach (KeyValuePair slotEntry in (Dictionary)skinMap.Value) { + int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); + foreach (KeyValuePair entry in ((Dictionary)slotEntry.Value)) { + try { + Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key, skeletonData); + if (attachment != null) skin.AddAttachment(slotIndex, entry.Key, attachment); + } catch (Exception e) { + throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e); + } + } + } + skeletonData.skins.Add(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; + } + } + + // Linked meshes. + for (int i = 0, n = linkedMeshes.Count; i < n; i++) { + LinkedMesh linkedMesh = linkedMeshes[i]; + Skin skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.FindSkin(linkedMesh.skin); + if (skin == null) throw new Exception("Slot not found: " + linkedMesh.skin); + Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent); + if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent); + linkedMesh.mesh.ParentMesh = (MeshAttachment)parent; + linkedMesh.mesh.UpdateUVs(); + } + linkedMeshes.Clear(); + + // Events. + if (root.ContainsKey("events")) { + foreach (KeyValuePair entry in (Dictionary)root["events"]) { + var entryMap = (Dictionary)entry.Value; + var data = new EventData(entry.Key); + data.Int = GetInt(entryMap, "int", 0); + data.Float = GetFloat(entryMap, "float", 0); + data.String = GetString(entryMap, "string", string.Empty); + skeletonData.events.Add(data); + } + } + + // Animations. + if (root.ContainsKey("animations")) { + foreach (KeyValuePair entry in (Dictionary)root["animations"]) { + try { + ReadAnimation((Dictionary)entry.Value, entry.Key, skeletonData); + } catch (Exception e) { + throw new Exception("Error reading animation: " + entry.Key, e); + } + } + } + + skeletonData.bones.TrimExcess(); + skeletonData.slots.TrimExcess(); + skeletonData.skins.TrimExcess(); + skeletonData.events.TrimExcess(); + skeletonData.animations.TrimExcess(); + skeletonData.ikConstraints.TrimExcess(); + return skeletonData; + } + + private Attachment ReadAttachment (Dictionary map, Skin skin, int slotIndex, string name, SkeletonData skeletonData) { + float scale = this.Scale; + name = GetString(map, "name", name); + + var typeName = GetString(map, "type", "region"); + if (typeName == "skinnedmesh") typeName = "weightedmesh"; + if (typeName == "weightedmesh") typeName = "mesh"; + if (typeName == "weightedlinkedmesh") typeName = "linkedmesh"; + var type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName, true); + + string path = GetString(map, "path", name); + + switch (type) { + case AttachmentType.Region: + RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path); + if (region == null) return null; + region.Path = path; + region.x = GetFloat(map, "x", 0) * scale; + region.y = GetFloat(map, "y", 0) * scale; + region.scaleX = GetFloat(map, "scaleX", 1); + region.scaleY = GetFloat(map, "scaleY", 1); + region.rotation = GetFloat(map, "rotation", 0); + region.width = GetFloat(map, "width", 32) * scale; + region.height = GetFloat(map, "height", 32) * scale; + + if (map.ContainsKey("color")) { + var color = (string)map["color"]; + region.r = ToColor(color, 0); + region.g = ToColor(color, 1); + region.b = ToColor(color, 2); + region.a = ToColor(color, 3); + } + + region.UpdateOffset(); + return region; + case AttachmentType.Boundingbox: + BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name); + if (box == null) return null; + ReadVertices(map, box, GetInt(map, "vertexCount", 0) << 1); + return box; + case AttachmentType.Mesh: + case AttachmentType.Linkedmesh: { + MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path); + if (mesh == null) return null; + mesh.Path = path; + + if (map.ContainsKey("color")) { + var color = (string)map["color"]; + mesh.r = ToColor(color, 0); + mesh.g = ToColor(color, 1); + mesh.b = ToColor(color, 2); + mesh.a = ToColor(color, 3); + } + + mesh.Width = GetFloat(map, "width", 0) * scale; + mesh.Height = GetFloat(map, "height", 0) * scale; + + string parent = GetString(map, "parent", null); + if (parent != null) { + mesh.InheritDeform = GetBoolean(map, "deform", true); + linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent)); + return mesh; + } + + float[] uvs = GetFloatArray(map, "uvs", 1); + ReadVertices(map, mesh, uvs.Length); + mesh.triangles = GetIntArray(map, "triangles"); + mesh.regionUVs = uvs; + mesh.UpdateUVs(); + + if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) * 2; + if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges"); + return mesh; + } + case AttachmentType.Path: { + PathAttachment pathAttachment = attachmentLoader.NewPathAttachment(skin, name); + if (pathAttachment == null) return null; + pathAttachment.closed = GetBoolean(map, "closed", false); + pathAttachment.constantSpeed = GetBoolean(map, "constantSpeed", true); + + int vertexCount = GetInt(map, "vertexCount", 0); + ReadVertices(map, pathAttachment, vertexCount << 1); + + // potential BOZO see Java impl + pathAttachment.lengths = GetFloatArray(map, "lengths", scale); + return pathAttachment; + } + case AttachmentType.Point: { + PointAttachment point = attachmentLoader.NewPointAttachment(skin, name); + if (point == null) return null; + point.x = GetFloat(map, "x", 0) * scale; + point.y = GetFloat(map, "y", 0) * scale; + point.rotation = GetFloat(map, "rotation", 0); + + //string color = GetString(map, "color", null); + //if (color != null) point.color = color; + return point; + } + case AttachmentType.Clipping: { + ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name); + if (clip == null) return null; + + string end = GetString(map, "end", null); + if (end != null) { + SlotData slot = skeletonData.FindSlot(end); + if (slot == null) throw new Exception("Clipping end slot not found: " + end); + clip.EndSlot = slot; + } + + ReadVertices(map, clip, GetInt(map, "vertexCount", 0) << 1); + + //string color = GetString(map, "color", null); + // if (color != null) clip.color = color; + return clip; + } + } + return null; + } + + private void ReadVertices (Dictionary map, VertexAttachment attachment, int verticesLength) { + attachment.WorldVerticesLength = verticesLength; + float[] vertices = GetFloatArray(map, "vertices", 1); + float scale = Scale; + if (verticesLength == vertices.Length) { + if (scale != 1) { + for (int i = 0; i < vertices.Length; i++) { + vertices[i] *= scale; + } + } + attachment.vertices = vertices; + return; + } + ExposedList weights = new ExposedList(verticesLength * 3 * 3); + ExposedList bones = new ExposedList(verticesLength * 3); + for (int i = 0, n = vertices.Length; i < n;) { + int boneCount = (int)vertices[i++]; + bones.Add(boneCount); + for (int nn = i + boneCount * 4; i < nn; i += 4) { + bones.Add((int)vertices[i]); + weights.Add(vertices[i + 1] * this.Scale); + weights.Add(vertices[i + 2] * this.Scale); + weights.Add(vertices[i + 3]); + } + } + attachment.bones = bones.ToArray(); + attachment.vertices = weights.ToArray(); + } + + private void ReadAnimation (Dictionary map, string name, SkeletonData skeletonData) { + var scale = this.Scale; + var timelines = new ExposedList(); + float duration = 0; + + // Slot timelines. + if (map.ContainsKey("slots")) { + foreach (KeyValuePair entry in (Dictionary)map["slots"]) { + string slotName = entry.Key; + int slotIndex = skeletonData.FindSlotIndex(slotName); + var timelineMap = (Dictionary)entry.Value; + foreach (KeyValuePair timelineEntry in timelineMap) { + var values = (List)timelineEntry.Value; + var timelineName = (string)timelineEntry.Key; + if (timelineName == "attachment") { + var timeline = new AttachmentTimeline(values.Count); + timeline.slotIndex = slotIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + timeline.SetFrame(frameIndex++, time, (string)valueMap["name"]); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); + + } else if (timelineName == "color") { + var timeline = new ColorTimeline(values.Count); + timeline.slotIndex = slotIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + string c = (string)valueMap["color"]; + timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]); + + } else if (timelineName == "twoColor") { + var timeline = new TwoColorTimeline(values.Count); + timeline.slotIndex = slotIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + string light = (string)valueMap["light"]; + string dark = (string)valueMap["dark"]; + timeline.SetFrame(frameIndex, time, ToColor(light, 0), ToColor(light, 1), ToColor(light, 2), ToColor(light, 3), + ToColor(dark, 0, 6), ToColor(dark, 1, 6), ToColor(dark, 2, 6)); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]); + + } else + throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } + } + } + + // Bone timelines. + if (map.ContainsKey("bones")) { + foreach (KeyValuePair entry in (Dictionary)map["bones"]) { + string boneName = entry.Key; + int boneIndex = skeletonData.FindBoneIndex(boneName); + if (boneIndex == -1) throw new Exception("Bone not found: " + boneName); + var timelineMap = (Dictionary)entry.Value; + foreach (KeyValuePair timelineEntry in timelineMap) { + var values = (List)timelineEntry.Value; + var timelineName = (string)timelineEntry.Key; + if (timelineName == "rotate") { + var timeline = new RotateTimeline(values.Count); + timeline.boneIndex = boneIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + timeline.SetFrame(frameIndex, (float)valueMap["time"], (float)valueMap["angle"]); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * RotateTimeline.ENTRIES]); + + } else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") { + TranslateTimeline timeline; + float timelineScale = 1; + if (timelineName == "scale") + timeline = new ScaleTimeline(values.Count); + else if (timelineName == "shear") + timeline = new ShearTimeline(values.Count); + else { + timeline = new TranslateTimeline(values.Count); + timelineScale = scale; + } + timeline.boneIndex = boneIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + float x = GetFloat(valueMap, "x", 0); + float y = GetFloat(valueMap, "y", 0); + timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TranslateTimeline.ENTRIES]); + + } else + throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } + } + } + + // IK constraint timelines. + if (map.ContainsKey("ik")) { + foreach (KeyValuePair constraintMap in (Dictionary)map["ik"]) { + IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key); + var values = (List)constraintMap.Value; + var timeline = new IkConstraintTimeline(values.Count); + timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint); + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + float mix = GetFloat(valueMap, "mix", 1); + bool bendPositive = GetBoolean(valueMap, "bendPositive", true); + timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * IkConstraintTimeline.ENTRIES]); + } + } + + // Transform constraint timelines. + if (map.ContainsKey("transform")) { + foreach (KeyValuePair constraintMap in (Dictionary)map["transform"]) { + TransformConstraintData constraint = skeletonData.FindTransformConstraint(constraintMap.Key); + var values = (List)constraintMap.Value; + var timeline = new TransformConstraintTimeline(values.Count); + timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint); + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + float rotateMix = GetFloat(valueMap, "rotateMix", 1); + float translateMix = GetFloat(valueMap, "translateMix", 1); + float scaleMix = GetFloat(valueMap, "scaleMix", 1); + float shearMix = GetFloat(valueMap, "shearMix", 1); + timeline.SetFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TransformConstraintTimeline.ENTRIES]); + } + } + + // Path constraint timelines. + if (map.ContainsKey("paths")) { + foreach (KeyValuePair constraintMap in (Dictionary)map["paths"]) { + int index = skeletonData.FindPathConstraintIndex(constraintMap.Key); + if (index == -1) throw new Exception("Path constraint not found: " + constraintMap.Key); + PathConstraintData data = skeletonData.pathConstraints.Items[index]; + var timelineMap = (Dictionary)constraintMap.Value; + foreach (KeyValuePair timelineEntry in timelineMap) { + var values = (List)timelineEntry.Value; + var timelineName = (string)timelineEntry.Key; + if (timelineName == "position" || timelineName == "spacing") { + PathConstraintPositionTimeline timeline; + float timelineScale = 1; + if (timelineName == "spacing") { + timeline = new PathConstraintSpacingTimeline(values.Count); + if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale; + } + else { + timeline = new PathConstraintPositionTimeline(values.Count); + if (data.positionMode == PositionMode.Fixed) timelineScale = scale; + } + timeline.pathConstraintIndex = index; + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + timeline.SetFrame(frameIndex, (float)valueMap["time"], GetFloat(valueMap, timelineName, 0) * timelineScale); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintPositionTimeline.ENTRIES]); + } + else if (timelineName == "mix") { + PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(values.Count); + timeline.pathConstraintIndex = index; + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + timeline.SetFrame(frameIndex, (float)valueMap["time"], GetFloat(valueMap, "rotateMix", 1), GetFloat(valueMap, "translateMix", 1)); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintMixTimeline.ENTRIES]); + } + } + } + } + + // Deform timelines. + if (map.ContainsKey("deform")) { + foreach (KeyValuePair deformMap in (Dictionary)map["deform"]) { + Skin skin = skeletonData.FindSkin(deformMap.Key); + foreach (KeyValuePair slotMap in (Dictionary)deformMap.Value) { + int slotIndex = skeletonData.FindSlotIndex(slotMap.Key); + if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key); + foreach (KeyValuePair timelineMap in (Dictionary)slotMap.Value) { + var values = (List)timelineMap.Value; + VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.Key); + if (attachment == null) throw new Exception("Deform attachment not found: " + timelineMap.Key); + bool weighted = attachment.bones != null; + float[] vertices = attachment.vertices; + int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length; + + var timeline = new DeformTimeline(values.Count); + timeline.slotIndex = slotIndex; + timeline.attachment = attachment; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float[] deform; + if (!valueMap.ContainsKey("vertices")) { + deform = weighted ? new float[deformLength] : vertices; + } else { + deform = new float[deformLength]; + int start = GetInt(valueMap, "offset", 0); + float[] verticesValue = GetFloatArray(valueMap, "vertices", 1); + Array.Copy(verticesValue, 0, deform, start, verticesValue.Length); + if (scale != 1) { + for (int i = start, n = i + verticesValue.Length; i < n; i++) + deform[i] *= scale; + } + + if (!weighted) { + for (int i = 0; i < deformLength; i++) + deform[i] += vertices[i]; + } + } + + timeline.SetFrame(frameIndex, (float)valueMap["time"], deform); + ReadCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); + } + } + } + } + + // Draw order timeline. + if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) { + var values = (List)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"]; + var timeline = new DrawOrderTimeline(values.Count); + int slotCount = skeletonData.slots.Count; + int frameIndex = 0; + foreach (Dictionary drawOrderMap in values) { + int[] drawOrder = null; + if (drawOrderMap.ContainsKey("offsets")) { + drawOrder = new int[slotCount]; + for (int i = slotCount - 1; i >= 0; i--) + drawOrder[i] = -1; + var offsets = (List)drawOrderMap["offsets"]; + int[] unchanged = new int[slotCount - offsets.Count]; + int originalIndex = 0, unchangedIndex = 0; + foreach (Dictionary offsetMap in offsets) { + int slotIndex = skeletonData.FindSlotIndex((string)offsetMap["slot"]); + if (slotIndex == -1) throw new Exception("Slot not found: " + offsetMap["slot"]); + // Collect unchanged items. + while (originalIndex != slotIndex) + unchanged[unchangedIndex++] = originalIndex++; + // Set changed items. + int index = originalIndex + (int)(float)offsetMap["offset"]; + drawOrder[index] = originalIndex++; + } + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (int i = slotCount - 1; i >= 0; i--) + if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; + } + timeline.SetFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); + } + + // Event timeline. + if (map.ContainsKey("events")) { + var eventsMap = (List)map["events"]; + var timeline = new EventTimeline(eventsMap.Count); + int frameIndex = 0; + foreach (Dictionary eventMap in eventsMap) { + EventData eventData = skeletonData.FindEvent((string)eventMap["name"]); + if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]); + var e = new Event((float)eventMap["time"], eventData); + e.Int = GetInt(eventMap, "int", eventData.Int); + e.Float = GetFloat(eventMap, "float", eventData.Float); + e.String = GetString(eventMap, "string", eventData.String); + timeline.SetFrame(frameIndex++, e); + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); + } + + timelines.TrimExcess(); + skeletonData.animations.Add(new Animation(name, timelines, duration)); + } + + static void ReadCurve (Dictionary valueMap, CurveTimeline timeline, int frameIndex) { + if (!valueMap.ContainsKey("curve")) + return; + Object curveObject = valueMap["curve"]; + if (curveObject.Equals("stepped")) + timeline.SetStepped(frameIndex); + else { + var curve = curveObject as List; + if (curve != null) + timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]); + } + } + + internal class LinkedMesh { + internal string parent, skin; + internal int slotIndex; + internal MeshAttachment mesh; + + public LinkedMesh (MeshAttachment mesh, string skin, int slotIndex, string parent) { + this.mesh = mesh; + this.skin = skin; + this.slotIndex = slotIndex; + this.parent = parent; + } + } + + static float[] GetFloatArray(Dictionary map, string name, float scale) { + var list = (List)map[name]; + var values = new float[list.Count]; + if (scale == 1) { + for (int i = 0, n = list.Count; i < n; i++) + values[i] = (float)list[i]; + } else { + for (int i = 0, n = list.Count; i < n; i++) + values[i] = (float)list[i] * scale; + } + return values; + } + + static int[] GetIntArray(Dictionary map, string name) { + var list = (List)map[name]; + var values = new int[list.Count]; + for (int i = 0, n = list.Count; i < n; i++) + values[i] = (int)(float)list[i]; + return values; + } + + static float GetFloat(Dictionary map, string name, float defaultValue) { + if (!map.ContainsKey(name)) + return defaultValue; + return (float)map[name]; + } + + static int GetInt(Dictionary map, string name, int defaultValue) { + if (!map.ContainsKey(name)) + return defaultValue; + return (int)(float)map[name]; + } + + static bool GetBoolean(Dictionary map, string name, bool defaultValue) { + if (!map.ContainsKey(name)) + return defaultValue; + return (bool)map[name]; + } + + static string GetString(Dictionary map, string name, string defaultValue) { + if (!map.ContainsKey(name)) + return defaultValue; + return (string)map[name]; + } + + static float ToColor(string hexString, int colorIndex, int expectedLength = 8) { + if (hexString.Length != expectedLength) + throw new ArgumentException("Color hexidecimal length must be " + expectedLength + ", recieved: " + hexString, "hexString"); + return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255; + } + } +} diff --git a/Assets/Spine/spine-csharp/SkeletonJson.cs.meta b/Assets/Spine/spine-csharp/SkeletonJson.cs.meta new file mode 100644 index 0000000..9188b23 --- /dev/null +++ b/Assets/Spine/spine-csharp/SkeletonJson.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6c4ab7992894bdb44a480981b1953f76 +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Skin.cs b/Assets/Spine/spine-csharp/Skin.cs new file mode 100644 index 0000000..2e754d3 --- /dev/null +++ b/Assets/Spine/spine-csharp/Skin.cs @@ -0,0 +1,125 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Spine { + /// Stores attachments by slot index and attachment name. + /// See SkeletonData , Skeleton , and + /// Runtime skins in the Spine Runtimes Guide. + /// + public class Skin { + internal string name; + private Dictionary attachments = + new Dictionary(AttachmentKeyTupleComparer.Instance); + + public string Name { get { return name; } } + public Dictionary Attachments { get { return attachments; } } + + public Skin (string name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.name = name; + } + + /// Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced. + public void AddAttachment (int slotIndex, string name, Attachment attachment) { + if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null."); + attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment; + } + + /// Returns the attachment for the specified slot index and name, or null. + /// May be null. + public Attachment GetAttachment (int slotIndex, string name) { + Attachment attachment; + attachments.TryGetValue(new AttachmentKeyTuple(slotIndex, name), out attachment); + return attachment; + } + + /// Finds the skin keys for a given slot. The results are added to the passed List(names). + /// The target slotIndex. To find the slot index, use or + /// Found skin key names will be added to this list. + public void FindNamesForSlot (int slotIndex, List names) { + if (names == null) throw new ArgumentNullException("names", "names cannot be null."); + foreach (AttachmentKeyTuple key in attachments.Keys) + if (key.slotIndex == slotIndex) names.Add(key.name); + } + + /// Finds the attachments for a given slot. The results are added to the passed List(Attachment). + /// The target slotIndex. To find the slot index, use or + /// Found Attachments will be added to this list. + public void FindAttachmentsForSlot (int slotIndex, List attachments) { + if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null."); + foreach (KeyValuePair entry in this.attachments) + if (entry.Key.slotIndex == slotIndex) attachments.Add(entry.Value); + } + + override public string ToString () { + return name; + } + + /// Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached. + internal void AttachAll (Skeleton skeleton, Skin oldSkin) { + foreach (KeyValuePair entry in oldSkin.attachments) { + int slotIndex = entry.Key.slotIndex; + Slot slot = skeleton.slots.Items[slotIndex]; + if (slot.Attachment == entry.Value) { + Attachment attachment = GetAttachment(slotIndex, entry.Key.name); + if (attachment != null) slot.Attachment = attachment; + } + } + } + + public struct AttachmentKeyTuple { + public readonly int slotIndex; + public readonly string name; + internal readonly int nameHashCode; + + public AttachmentKeyTuple (int slotIndex, string name) { + this.slotIndex = slotIndex; + this.name = name; + nameHashCode = this.name.GetHashCode(); + } + } + + // Avoids boxing in the dictionary. + class AttachmentKeyTupleComparer : IEqualityComparer { + internal static readonly AttachmentKeyTupleComparer Instance = new AttachmentKeyTupleComparer(); + + bool IEqualityComparer.Equals (AttachmentKeyTuple o1, AttachmentKeyTuple o2) { + return o1.slotIndex == o2.slotIndex && o1.nameHashCode == o2.nameHashCode && string.Equals(o1.name, o2.name, StringComparison.Ordinal); + } + + int IEqualityComparer.GetHashCode (AttachmentKeyTuple o) { + return o.slotIndex; + } + } + } +} diff --git a/Assets/Spine/spine-csharp/Skin.cs.meta b/Assets/Spine/spine-csharp/Skin.cs.meta new file mode 100644 index 0000000..1e9f87e --- /dev/null +++ b/Assets/Spine/spine-csharp/Skin.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7df8caa3a771f464e803316a6b18c909 +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Slot.cs b/Assets/Spine/spine-csharp/Slot.cs new file mode 100644 index 0000000..6151aa9 --- /dev/null +++ b/Assets/Spine/spine-csharp/Slot.cs @@ -0,0 +1,100 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class Slot { + internal SlotData data; + internal Bone bone; + internal float r, g, b, a; + internal float r2, g2, b2; + internal bool hasSecondColor; + internal Attachment attachment; + internal float attachmentTime; + internal ExposedList attachmentVertices = new ExposedList(); + + public SlotData Data { get { return data; } } + public Bone Bone { get { return bone; } } + public Skeleton Skeleton { get { return bone.skeleton; } } + public float R { get { return r; } set { r = value; } } + public float G { get { return g; } set { g = value; } } + public float B { get { return b; } set { b = value; } } + public float A { get { return a; } set { a = value; } } + + public float R2 { get { return r2; } set { r2 = value; } } + public float G2 { get { return g2; } set { g2 = value; } } + public float B2 { get { return b2; } set { b2 = value; } } + public bool HasSecondColor { get { return data.hasSecondColor; } set { data.hasSecondColor = value; } } + + /// May be null. + public Attachment Attachment { + get { return attachment; } + set { + if (attachment == value) return; + attachment = value; + attachmentTime = bone.skeleton.time; + attachmentVertices.Clear(false); + } + } + + public float AttachmentTime { + get { return bone.skeleton.time - attachmentTime; } + set { attachmentTime = bone.skeleton.time - value; } + } + + public ExposedList AttachmentVertices { get { return attachmentVertices; } set { attachmentVertices = value; } } + + public Slot (SlotData data, Bone bone) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null."); + this.data = data; + this.bone = bone; + SetToSetupPose(); + } + + public void SetToSetupPose () { + r = data.r; + g = data.g; + b = data.b; + a = data.a; + if (data.attachmentName == null) + Attachment = null; + else { + attachment = null; + Attachment = bone.skeleton.GetAttachment(data.index, data.attachmentName); + } + } + + override public string ToString () { + return data.name; + } + } +} diff --git a/Assets/Spine/spine-csharp/Slot.cs.meta b/Assets/Spine/spine-csharp/Slot.cs.meta new file mode 100644 index 0000000..4bc86f4 --- /dev/null +++ b/Assets/Spine/spine-csharp/Slot.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6974c4b5c87687140a2417201ea43066 +timeCreated: 1456265154 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/SlotData.cs b/Assets/Spine/spine-csharp/SlotData.cs new file mode 100644 index 0000000..956634d --- /dev/null +++ b/Assets/Spine/spine-csharp/SlotData.cs @@ -0,0 +1,74 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class SlotData { + internal int index; + internal string name; + internal BoneData boneData; + internal float r = 1, g = 1, b = 1, a = 1; + internal float r2 = 0, g2 = 0, b2 = 0; + internal bool hasSecondColor = false; + internal string attachmentName; + internal BlendMode blendMode; + + public int Index { get { return index; } } + public string Name { get { return name; } } + public BoneData BoneData { get { return boneData; } } + public float R { get { return r; } set { r = value; } } + public float G { get { return g; } set { g = value; } } + public float B { get { return b; } set { b = value; } } + public float A { get { return a; } set { a = value; } } + + public float R2 { get { return r2; } set { r2 = value; } } + public float G2 { get { return g2; } set { g2 = value; } } + public float B2 { get { return b2; } set { b2 = value; } } + public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } } + + /// May be null. + public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } } + public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } } + + public SlotData (int index, String name, BoneData boneData) { + if (index < 0) throw new ArgumentException ("index must be >= 0.", "index"); + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + if (boneData == null) throw new ArgumentNullException("boneData", "boneData cannot be null."); + this.index = index; + this.name = name; + this.boneData = boneData; + } + + override public string ToString () { + return name; + } + } +} diff --git a/Assets/Spine/spine-csharp/SlotData.cs.meta b/Assets/Spine/spine-csharp/SlotData.cs.meta new file mode 100644 index 0000000..d203ba7 --- /dev/null +++ b/Assets/Spine/spine-csharp/SlotData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f28cb47bc1e8b434c85e6f69b2c9e15e +timeCreated: 1456265156 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/TransformConstraint.cs b/Assets/Spine/spine-csharp/TransformConstraint.cs new file mode 100644 index 0000000..f220a8a --- /dev/null +++ b/Assets/Spine/spine-csharp/TransformConstraint.cs @@ -0,0 +1,284 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class TransformConstraint : IConstraint { + internal TransformConstraintData data; + internal ExposedList bones; + internal Bone target; + internal float rotateMix, translateMix, scaleMix, shearMix; + + public TransformConstraintData Data { get { return data; } } + public int Order { get { return data.order; } } + public ExposedList Bones { get { return bones; } } + public Bone Target { get { return target; } set { target = value; } } + public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } + public float TranslateMix { get { return translateMix; } set { translateMix = value; } } + public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } } + public float ShearMix { get { return shearMix; } set { shearMix = value; } } + + public TransformConstraint (TransformConstraintData data, Skeleton skeleton) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + this.data = data; + rotateMix = data.rotateMix; + translateMix = data.translateMix; + scaleMix = data.scaleMix; + shearMix = data.shearMix; + + bones = new ExposedList(); + foreach (BoneData boneData in data.bones) + bones.Add (skeleton.FindBone (boneData.name)); + + target = skeleton.FindBone(data.target.name); + } + + public void Apply () { + Update(); + } + + public void Update () { + if (data.local) { + if (data.relative) + ApplyRelativeLocal(); + else + ApplyAbsoluteLocal(); + } else { + if (data.relative) + ApplyRelativeWorld(); + else + ApplyAbsoluteWorld(); + } + } + + void ApplyAbsoluteWorld () { + float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + Bone target = this.target; + float ta = target.a, tb = target.b, tc = target.c, td = target.d; + float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad; + float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; + var bones = this.bones; + for (int i = 0, n = bones.Count; i < n; i++) { + Bone bone = bones.Items[i]; + bool modified = false; + + if (rotateMix != 0) { + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + float r = MathUtils.Atan2(tc, ta) - MathUtils.Atan2(c, a) + offsetRotation; + if (r > MathUtils.PI) + r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; + r *= rotateMix; + float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + modified = true; + } + + if (translateMix != 0) { + float tx, ty; //Vector2 temp = this.temp; + target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY)); + bone.worldX += (tx - bone.worldX) * translateMix; + bone.worldY += (ty - bone.worldY) * translateMix; + modified = true; + } + + if (scaleMix > 0) { + float s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c); + //float ts = (float)Math.sqrt(ta * ta + tc * tc); + if (s > 0.00001f) s = (s + ((float)Math.Sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * scaleMix) / s; + bone.a *= s; + bone.c *= s; + s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d); + //ts = (float)Math.Sqrt(tb * tb + td * td); + if (s > 0.00001f) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * scaleMix) / s; + bone.b *= s; + bone.d *= s; + modified = true; + } + + if (shearMix > 0) { + float b = bone.b, d = bone.d; + float by = MathUtils.Atan2(d, b); + float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta) - (by - MathUtils.Atan2(bone.c, bone.a)); + if (r > MathUtils.PI) + r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; + r = by + (r + offsetShearY) * shearMix; + float s = (float)Math.Sqrt(b * b + d * d); + bone.b = MathUtils.Cos(r) * s; + bone.d = MathUtils.Sin(r) * s; + modified = true; + } + + if (modified) bone.appliedValid = false; + } + } + + void ApplyRelativeWorld () { + float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + Bone target = this.target; + float ta = target.a, tb = target.b, tc = target.c, td = target.d; + float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad; + float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; + var bones = this.bones; + for (int i = 0, n = bones.Count; i < n; i++) { + Bone bone = bones.Items[i]; + bool modified = false; + + if (rotateMix != 0) { + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + float r = MathUtils.Atan2(tc, ta) + offsetRotation; + if (r > MathUtils.PI) + r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; + r *= rotateMix; + float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + modified = true; + } + + if (translateMix != 0) { + float tx, ty; //Vector2 temp = this.temp; + target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY)); + bone.worldX += tx * translateMix; + bone.worldY += ty * translateMix; + modified = true; + } + + if (scaleMix > 0) { + float s = ((float)Math.Sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * scaleMix + 1; + bone.a *= s; + bone.c *= s; + s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * scaleMix + 1; + bone.b *= s; + bone.d *= s; + modified = true; + } + + if (shearMix > 0) { + float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta); + if (r > MathUtils.PI) + r -= MathUtils.PI2; + else if (r < -MathUtils.PI) r += MathUtils.PI2; + float b = bone.b, d = bone.d; + r = MathUtils.Atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix; + float s = (float)Math.Sqrt(b * b + d * d); + bone.b = MathUtils.Cos(r) * s; + bone.d = MathUtils.Sin(r) * s; + modified = true; + } + + if (modified) bone.appliedValid = false; + } + } + + void ApplyAbsoluteLocal () { + float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + Bone target = this.target; + if (!target.appliedValid) target.UpdateAppliedTransform(); + var bonesItems = this.bones.Items; + for (int i = 0, n = this.bones.Count; i < n; i++) { + Bone bone = bonesItems[i]; + if (!bone.appliedValid) bone.UpdateAppliedTransform(); + + float rotation = bone.arotation; + if (rotateMix != 0) { + float r = target.arotation - rotation + data.offsetRotation; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + rotation += r * rotateMix; + } + + float x = bone.ax, y = bone.ay; + if (translateMix != 0) { + x += (target.ax - x + data.offsetX) * translateMix; + y += (target.ay - y + data.offsetY) * translateMix; + } + + float scaleX = bone.ascaleX, scaleY = bone.ascaleY; + if (scaleMix > 0) { + if (scaleX > 0.00001f) scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * scaleMix) / scaleX; + if (scaleY > 0.00001f) scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * scaleMix) / scaleY; + } + + float shearY = bone.ashearY; + if (shearMix > 0) { + float r = target.ashearY - shearY + data.offsetShearY; + r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; + bone.shearY += r * shearMix; + } + + bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); + } + } + + void ApplyRelativeLocal () { + float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + Bone target = this.target; + if (!target.appliedValid) target.UpdateAppliedTransform(); + var bonesItems = this.bones.Items; + for (int i = 0, n = this.bones.Count; i < n; i++) { + Bone bone = bonesItems[i]; + if (!bone.appliedValid) bone.UpdateAppliedTransform(); + + float rotation = bone.arotation; + if (rotateMix != 0) rotation += (target.arotation + data.offsetRotation) * rotateMix; + + float x = bone.ax, y = bone.ay; + if (translateMix != 0) { + x += (target.ax + data.offsetX) * translateMix; + y += (target.ay + data.offsetY) * translateMix; + } + + float scaleX = bone.ascaleX, scaleY = bone.ascaleY; + if (scaleMix > 0) { + if (scaleX > 0.00001f) scaleX *= ((target.ascaleX - 1 + data.offsetScaleX) * scaleMix) + 1; + if (scaleY > 0.00001f) scaleY *= ((target.ascaleY - 1 + data.offsetScaleY) * scaleMix) + 1; + } + + float shearY = bone.ashearY; + if (shearMix > 0) shearY += (target.ashearY + data.offsetShearY) * shearMix; + + bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); + } + } + + override public string ToString () { + return data.name; + } + } +} diff --git a/Assets/Spine/spine-csharp/TransformConstraint.cs.meta b/Assets/Spine/spine-csharp/TransformConstraint.cs.meta new file mode 100644 index 0000000..b3e7ada --- /dev/null +++ b/Assets/Spine/spine-csharp/TransformConstraint.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2e30316a9733f8a4b8b6c362c06dfa11 +timeCreated: 1456265153 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/TransformConstraintData.cs b/Assets/Spine/spine-csharp/TransformConstraintData.cs new file mode 100644 index 0000000..be0dc1c --- /dev/null +++ b/Assets/Spine/spine-csharp/TransformConstraintData.cs @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + public class TransformConstraintData { + internal string name; + internal int order; + internal ExposedList bones = new ExposedList(); + internal BoneData target; + internal float rotateMix, translateMix, scaleMix, shearMix; + internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; + internal bool relative, local; + + public string Name { get { return name; } } + public int Order { get { return order; } set { order = value; } } + public ExposedList Bones { get { return bones; } } + public BoneData Target { get { return target; } set { target = value; } } + public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } + public float TranslateMix { get { return translateMix; } set { translateMix = value; } } + public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } } + public float ShearMix { get { return shearMix; } set { shearMix = value; } } + + public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } } + public float OffsetX { get { return offsetX; } set { offsetX = value; } } + public float OffsetY { get { return offsetY; } set { offsetY = value; } } + public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } } + public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } } + public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } } + + public bool Relative { get { return relative; } set { relative = value; } } + public bool Local { get { return local; } set { local = value; } } + + public TransformConstraintData (string name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.name = name; + } + + override public string ToString () { + return name; + } + } +} diff --git a/Assets/Spine/spine-csharp/TransformConstraintData.cs.meta b/Assets/Spine/spine-csharp/TransformConstraintData.cs.meta new file mode 100644 index 0000000..d8b3f27 --- /dev/null +++ b/Assets/Spine/spine-csharp/TransformConstraintData.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b7edeecc9bd7dd44faabf24f29b119a0 +timeCreated: 1456265155 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-csharp/Triangulator.cs b/Assets/Spine/spine-csharp/Triangulator.cs new file mode 100644 index 0000000..c4bdb04 --- /dev/null +++ b/Assets/Spine/spine-csharp/Triangulator.cs @@ -0,0 +1,278 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; + +namespace Spine { + internal class Triangulator { + private readonly ExposedList> convexPolygons = new ExposedList>(); + private readonly ExposedList> convexPolygonsIndices = new ExposedList>(); + + private readonly ExposedList indicesArray = new ExposedList(); + private readonly ExposedList isConcaveArray = new ExposedList(); + private readonly ExposedList triangles = new ExposedList(); + + private readonly Pool> polygonPool = new Pool>(); + private readonly Pool> polygonIndicesPool = new Pool>(); + + public ExposedList Triangulate (ExposedList verticesArray) { + var vertices = verticesArray.Items; + int vertexCount = verticesArray.Count >> 1; + + var indicesArray = this.indicesArray; + indicesArray.Clear(); + int[] indices = indicesArray.Resize(vertexCount).Items; + for (int i = 0; i < vertexCount; i++) + indices[i] = i; + + var isConcaveArray = this.isConcaveArray; + bool[] isConcave = isConcaveArray.Resize(vertexCount).Items; + for (int i = 0, n = vertexCount; i < n; ++i) + isConcave[i] = IsConcave(i, vertexCount, vertices, indices); + + var triangles = this.triangles; + triangles.Clear(); + triangles.EnsureCapacity(Math.Max(0, vertexCount - 2) << 2); + + while (vertexCount > 3) { + // Find ear tip. + int previous = vertexCount - 1, i = 0, next = 1; + + // outer: + while (true) { + if (!isConcave[i]) { + int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; + float p1x = vertices[p1], p1y = vertices[p1 + 1]; + float p2x = vertices[p2], p2y = vertices[p2 + 1]; + float p3x = vertices[p3], p3y = vertices[p3 + 1]; + for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { + if (!isConcave[ii]) continue; + int v = indices[ii] << 1; + float vx = vertices[v], vy = vertices[v + 1]; + if (PositiveArea(p3x, p3y, p1x, p1y, vx, vy)) { + if (PositiveArea(p1x, p1y, p2x, p2y, vx, vy)) { + if (PositiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto break_outer; // break outer; + } + } + } + break; + } + break_outer: + + if (next == 0) { + do { + if (!isConcave[i]) break; + i--; + } while (i > 0); + break; + } + + previous = i; + i = next; + next = (next + 1) % vertexCount; + } + + // Cut ear tip. + triangles.Add(indices[(vertexCount + i - 1) % vertexCount]); + triangles.Add(indices[i]); + triangles.Add(indices[(i + 1) % vertexCount]); + indicesArray.RemoveAt(i); + isConcaveArray.RemoveAt(i); + vertexCount--; + + int previousIndex = (vertexCount + i - 1) % vertexCount; + int nextIndex = i == vertexCount ? 0 : i; + isConcave[previousIndex] = IsConcave(previousIndex, vertexCount, vertices, indices); + isConcave[nextIndex] = IsConcave(nextIndex, vertexCount, vertices, indices); + } + + if (vertexCount == 3) { + triangles.Add(indices[2]); + triangles.Add(indices[0]); + triangles.Add(indices[1]); + } + + return triangles; + } + + public ExposedList> Decompose (ExposedList verticesArray, ExposedList triangles) { + var vertices = verticesArray.Items; + var convexPolygons = this.convexPolygons; + for (int i = 0, n = convexPolygons.Count; i < n; i++) { + polygonPool.Free(convexPolygons.Items[i]); + } + convexPolygons.Clear(); + + var convexPolygonsIndices = this.convexPolygonsIndices; + for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++) { + polygonIndicesPool.Free(convexPolygonsIndices.Items[i]); + } + convexPolygonsIndices.Clear(); + + var polygonIndices = polygonIndicesPool.Obtain(); + polygonIndices.Clear(); + + var polygon = polygonPool.Obtain(); + polygon.Clear(); + + // Merge subsequent triangles if they form a triangle fan. + int fanBaseIndex = -1, lastWinding = 0; + int[] trianglesItems = triangles.Items; + for (int i = 0, n = triangles.Count; i < n; i += 3) { + int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1; + float x1 = vertices[t1], y1 = vertices[t1 + 1]; + float x2 = vertices[t2], y2 = vertices[t2 + 1]; + float x3 = vertices[t3], y3 = vertices[t3 + 1]; + + // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). + var merged = false; + if (fanBaseIndex == t1) { + int o = polygon.Count - 4; + float[] p = polygon.Items; + int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); + int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]); + if (winding1 == lastWinding && winding2 == lastWinding) { + polygon.Add(x3); + polygon.Add(y3); + polygonIndices.Add(t3); + merged = true; + } + } + + // Otherwise make this triangle the new base. + if (!merged) { + if (polygon.Count > 0) { + convexPolygons.Add(polygon); + convexPolygonsIndices.Add(polygonIndices); + } else { + polygonPool.Free(polygon); + polygonIndicesPool.Free(polygonIndices); + } + polygon = polygonPool.Obtain(); + polygon.Clear(); + polygon.Add(x1); + polygon.Add(y1); + polygon.Add(x2); + polygon.Add(y2); + polygon.Add(x3); + polygon.Add(y3); + polygonIndices = polygonIndicesPool.Obtain(); + polygonIndices.Clear(); + polygonIndices.Add(t1); + polygonIndices.Add(t2); + polygonIndices.Add(t3); + lastWinding = Winding(x1, y1, x2, y2, x3, y3); + fanBaseIndex = t1; + } + } + + if (polygon.Count > 0) { + convexPolygons.Add(polygon); + convexPolygonsIndices.Add(polygonIndices); + } + + // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. + for (int i = 0, n = convexPolygons.Count; i < n; i++) { + polygonIndices = convexPolygonsIndices.Items[i]; + if (polygonIndices.Count == 0) continue; + int firstIndex = polygonIndices.Items[0]; + int lastIndex = polygonIndices.Items[polygonIndices.Count - 1]; + + polygon = convexPolygons.Items[i]; + int o = polygon.Count - 4; + float[] p = polygon.Items; + float prevPrevX = p[o], prevPrevY = p[o + 1]; + float prevX = p[o + 2], prevY = p[o + 3]; + float firstX = p[0], firstY = p[1]; + float secondX = p[2], secondY = p[3]; + int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); + + for (int ii = 0; ii < n; ii++) { + if (ii == i) continue; + var otherIndices = convexPolygonsIndices.Items[ii]; + if (otherIndices.Count != 3) continue; + int otherFirstIndex = otherIndices.Items[0]; + int otherSecondIndex = otherIndices.Items[1]; + int otherLastIndex = otherIndices.Items[2]; + + var otherPoly = convexPolygons.Items[ii]; + float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1]; + + if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue; + int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); + int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY); + if (winding1 == winding && winding2 == winding) { + otherPoly.Clear(); + otherIndices.Clear(); + polygon.Add(x3); + polygon.Add(y3); + polygonIndices.Add(otherLastIndex); + prevPrevX = prevX; + prevPrevY = prevY; + prevX = x3; + prevY = y3; + ii = 0; + } + } + } + + // Remove empty polygons that resulted from the merge step above. + for (int i = convexPolygons.Count - 1; i >= 0; i--) { + polygon = convexPolygons.Items[i]; + if (polygon.Count == 0) { + convexPolygons.RemoveAt(i); + polygonPool.Free(polygon); + polygonIndices = convexPolygonsIndices.Items[i]; + convexPolygonsIndices.RemoveAt(i); + polygonIndicesPool.Free(polygonIndices); + } + } + + return convexPolygons; + } + + static private bool IsConcave (int index, int vertexCount, float[] vertices, int[] indices) { + int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; + int current = indices[index] << 1; + int next = indices[(index + 1) % vertexCount] << 1; + return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], + vertices[next + 1]); + } + + static private bool PositiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; + } + + static private int Winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + float px = p2x - p1x, py = p2y - p1y; + return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; + } + } +} diff --git a/Assets/Spine/spine-csharp/Triangulator.cs.meta b/Assets/Spine/spine-csharp/Triangulator.cs.meta new file mode 100644 index 0000000..cdabaeb --- /dev/null +++ b/Assets/Spine/spine-csharp/Triangulator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 90a8832a1f3c5f846b8773dc0e1c3238 +timeCreated: 1493126637 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity.meta b/Assets/Spine/spine-unity.meta new file mode 100644 index 0000000..f89227f --- /dev/null +++ b/Assets/Spine/spine-unity.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 29a3535756b284a428d35dcd4327185e +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Asset Types.meta b/Assets/Spine/spine-unity/Asset Types.meta new file mode 100644 index 0000000..ade58c0 --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 1dc4b7c23385e8c43ad19d01cbed78ce +folderAsset: yes +timeCreated: 1455489521 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Asset Types/AnimationReferenceAsset.cs b/Assets/Spine/spine-unity/Asset Types/AnimationReferenceAsset.cs new file mode 100644 index 0000000..e7305cd --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/AnimationReferenceAsset.cs @@ -0,0 +1,67 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define AUTOINIT_SPINEREFERENCE + +using UnityEngine; + +namespace Spine.Unity { + [CreateAssetMenu(menuName = "Spine/Animation Reference Asset")] + public class AnimationReferenceAsset : ScriptableObject, IHasSkeletonDataAsset { + const bool QuietSkeletonData = true; + + [SerializeField] protected SkeletonDataAsset skeletonDataAsset; + [SerializeField, SpineAnimation] protected string animationName; + private Animation animation; + + public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } + + public Animation Animation { + get { + #if AUTOINIT_SPINEREFERENCE + if (animation == null) + Initialize(); + #endif + + return animation; + } + } + + public void Initialize () { + if (skeletonDataAsset == null) return; + this.animation = skeletonDataAsset.GetSkeletonData(AnimationReferenceAsset.QuietSkeletonData).FindAnimation(animationName); + if (this.animation == null) Debug.LogWarningFormat("Animation '{0}' not found in SkeletonData : {1}.", animationName, skeletonDataAsset.name); + } + + public static implicit operator Animation (AnimationReferenceAsset asset) { + return asset.Animation; + } + } +} diff --git a/Assets/Spine/spine-unity/Asset Types/AnimationReferenceAsset.cs.meta b/Assets/Spine/spine-unity/Asset Types/AnimationReferenceAsset.cs.meta new file mode 100644 index 0000000..dbfadcd --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/AnimationReferenceAsset.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6e3e95a05e4c9774397eeeb7bdee8ccb +timeCreated: 1523328498 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 52b12ec801461494185a4d3dc66f3d1d, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Asset Types/AtlasAsset.cs b/Assets/Spine/spine-unity/Asset Types/AtlasAsset.cs new file mode 100644 index 0000000..824e500 --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/AtlasAsset.cs @@ -0,0 +1,241 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using Spine; + +namespace Spine.Unity { + /// Loads and stores a Spine atlas and list of materials. + public class AtlasAsset : ScriptableObject { + public TextAsset atlasFile; + public Material[] materials; + protected Atlas atlas; + + public bool IsLoaded { get { return this.atlas != null; } } + + #region Runtime Instantiation + /// + /// Creates a runtime AtlasAsset + public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Material[] materials, bool initialize) { + AtlasAsset atlasAsset = ScriptableObject.CreateInstance(); + atlasAsset.Reset(); + atlasAsset.atlasFile = atlasText; + atlasAsset.materials = materials; + + if (initialize) + atlasAsset.GetAtlas(); + + return atlasAsset; + } + + /// + /// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. + public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Material materialPropertySource, bool initialize) { + // Get atlas page names. + string atlasString = atlasText.text; + atlasString = atlasString.Replace("\r", ""); + string[] atlasLines = atlasString.Split('\n'); + var pages = new List(); + for (int i = 0; i < atlasLines.Length - 1; i++) { + if (atlasLines[i].Trim().Length == 0) + pages.Add(atlasLines[i + 1].Trim().Replace(".png", "")); + } + + // Populate Materials[] by matching texture names with page names. + var materials = new Material[pages.Count]; + for (int i = 0, n = pages.Count; i < n; i++) { + Material mat = null; + + // Search for a match. + string pageName = pages[i]; + for (int j = 0, m = textures.Length; j < m; j++) { + if (string.Equals(pageName, textures[j].name, System.StringComparison.OrdinalIgnoreCase)) { + // Match found. + mat = new Material(materialPropertySource); + mat.mainTexture = textures[j]; + break; + } + } + + if (mat != null) + materials[i] = mat; + else + throw new ArgumentException("Could not find matching atlas page in the texture array."); + } + + // Create AtlasAsset normally + return CreateRuntimeInstance(atlasText, materials, initialize); + } + + /// + /// Creates a runtime AtlasAsset. Only providing the textures is slower because it has to search for atlas page matches. + public static AtlasAsset CreateRuntimeInstance (TextAsset atlasText, Texture2D[] textures, Shader shader, bool initialize) { + if (shader == null) + shader = Shader.Find("Spine/Skeleton"); + + Material materialProperySource = new Material(shader); + var oa = CreateRuntimeInstance(atlasText, textures, materialProperySource, initialize); + + return oa; + } + #endregion + + void Reset () { + Clear(); + } + + public virtual void Clear () { + atlas = null; + } + + /// The atlas or null if it could not be loaded. + public virtual Atlas GetAtlas () { + if (atlasFile == null) { + Debug.LogError("Atlas file not set for atlas asset: " + name, this); + Clear(); + return null; + } + + if (materials == null || materials.Length == 0) { + Debug.LogError("Materials not set for atlas asset: " + name, this); + Clear(); + return null; + } + + if (atlas != null) return atlas; + + try { + atlas = new Atlas(new StringReader(atlasFile.text), "", new MaterialsTextureLoader(this)); + atlas.FlipV(); + return atlas; + } catch (Exception ex) { + Debug.LogError("Error reading atlas file for atlas asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); + return null; + } + } + + public Mesh GenerateMesh (string name, Mesh mesh, out Material material, float scale = 0.01f) { + AtlasRegion region = atlas.FindRegion(name); + material = null; + if (region != null) { + if (mesh == null) { + mesh = new Mesh(); + mesh.name = name; + } + + Vector3[] verts = new Vector3[4]; + Vector2[] uvs = new Vector2[4]; + Color[] colors = { Color.white, Color.white, Color.white, Color.white }; + int[] triangles = { 0, 1, 2, 2, 3, 0 }; + + float left, right, top, bottom; + left = region.width / -2f; + right = left * -1f; + top = region.height / 2f; + bottom = top * -1; + + verts[0] = new Vector3(left, bottom, 0) * scale; + verts[1] = new Vector3(left, top, 0) * scale; + verts[2] = new Vector3(right, top, 0) * scale; + verts[3] = new Vector3(right, bottom, 0) * scale; + float u, v, u2, v2; + u = region.u; + v = region.v; + u2 = region.u2; + v2 = region.v2; + + if (!region.rotate) { + uvs[0] = new Vector2(u, v2); + uvs[1] = new Vector2(u, v); + uvs[2] = new Vector2(u2, v); + uvs[3] = new Vector2(u2, v2); + } else { + uvs[0] = new Vector2(u2, v2); + uvs[1] = new Vector2(u, v2); + uvs[2] = new Vector2(u, v); + uvs[3] = new Vector2(u2, v); + } + + mesh.triangles = new int[0]; + mesh.vertices = verts; + mesh.uv = uvs; + mesh.colors = colors; + mesh.triangles = triangles; + mesh.RecalculateNormals(); + mesh.RecalculateBounds(); + + material = (Material)region.page.rendererObject; + } else { + mesh = null; + } + + return mesh; + } + } + + public class MaterialsTextureLoader : TextureLoader { + AtlasAsset atlasAsset; + + public MaterialsTextureLoader (AtlasAsset atlasAsset) { + this.atlasAsset = atlasAsset; + } + + public void Load (AtlasPage page, string path) { + String name = Path.GetFileNameWithoutExtension(path); + Material material = null; + foreach (Material other in atlasAsset.materials) { + if (other.mainTexture == null) { + Debug.LogError("Material is missing texture: " + other.name, other); + return; + } + if (other.mainTexture.name == name) { + material = other; + break; + } + } + if (material == null) { + Debug.LogError("Material with texture name \"" + name + "\" not found for atlas asset: " + atlasAsset.name, atlasAsset); + return; + } + page.rendererObject = material; + + // Very old atlas files expected the texture's actual size to be used at runtime. + if (page.width == 0 || page.height == 0) { + page.width = material.mainTexture.width; + page.height = material.mainTexture.height; + } + } + + public void Unload (object texture) { } + } +} diff --git a/Assets/Spine/spine-unity/Asset Types/AtlasAsset.cs.meta b/Assets/Spine/spine-unity/Asset Types/AtlasAsset.cs.meta new file mode 100644 index 0000000..e6a4920 --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/AtlasAsset.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a6b194f808b1af6499c93410e504af42 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 3fc714a0dc1cf6b4b959e073fff2844e, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Asset Types/EventDataReferenceAsset.cs b/Assets/Spine/spine-unity/Asset Types/EventDataReferenceAsset.cs new file mode 100644 index 0000000..6cee80b --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/EventDataReferenceAsset.cs @@ -0,0 +1,66 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define AUTOINIT_SPINEREFERENCE + +using UnityEngine; + +namespace Spine.Unity { + [CreateAssetMenu(menuName = "Spine/EventData Reference Asset")] + public class EventDataReferenceAsset : ScriptableObject { + const bool QuietSkeletonData = true; + + [SerializeField] protected SkeletonDataAsset skeletonDataAsset; + [SerializeField, SpineEvent(dataField: "skeletonDataAsset")] protected string eventName; + + EventData eventData; + public EventData EventData { + get { + #if AUTOINIT_SPINEREFERENCE + if (eventData == null) + Initialize(); + #endif + return eventData; + } + } + + public void Initialize () { + if (skeletonDataAsset == null) + return; + this.eventData = skeletonDataAsset.GetSkeletonData(EventDataReferenceAsset.QuietSkeletonData).FindEvent(eventName); + if (this.eventData == null) + Debug.LogWarningFormat("Event Data '{0}' not found in SkeletonData : {1}.", eventName, skeletonDataAsset.name); + } + + public static implicit operator EventData (EventDataReferenceAsset asset) { + return asset.EventData; + } + } +} diff --git a/Assets/Spine/spine-unity/Asset Types/EventDataReferenceAsset.cs.meta b/Assets/Spine/spine-unity/Asset Types/EventDataReferenceAsset.cs.meta new file mode 100644 index 0000000..cb26130 --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/EventDataReferenceAsset.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8e60be42c1473144db0fd3337c25b500 +timeCreated: 1523330891 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: d226a80acc775714aa78b85e16a00e9b, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Asset Types/RegionlessAttachmentLoader.cs b/Assets/Spine/spine-unity/Asset Types/RegionlessAttachmentLoader.cs new file mode 100644 index 0000000..cbfd25c --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/RegionlessAttachmentLoader.cs @@ -0,0 +1,84 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using UnityEngine; + +namespace Spine.Unity { + + public class RegionlessAttachmentLoader : AttachmentLoader { + + static AtlasRegion emptyRegion; + static AtlasRegion EmptyRegion { + get { + if (emptyRegion == null) { + emptyRegion = new AtlasRegion { + name = "Empty AtlasRegion", + page = new AtlasPage { + name = "Empty AtlasPage", + rendererObject = new Material(Shader.Find("Spine/Special/HiddenPass")) { name = "NoRender Material" } + } + }; + } + return emptyRegion; + } + } + + public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { + RegionAttachment attachment = new RegionAttachment(name) { + RendererObject = EmptyRegion + }; + return attachment; + } + + public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { + MeshAttachment attachment = new MeshAttachment(name) { + RendererObject = EmptyRegion + }; + return attachment; + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + return new BoundingBoxAttachment(name); + } + + public PathAttachment NewPathAttachment (Skin skin, string name) { + return new PathAttachment(name); + } + + public PointAttachment NewPointAttachment (Skin skin, string name) { + return new PointAttachment(name); + } + + public ClippingAttachment NewClippingAttachment (Skin skin, string name) { + return new ClippingAttachment(name); + } + } +} diff --git a/Assets/Spine/spine-unity/Asset Types/RegionlessAttachmentLoader.cs.meta b/Assets/Spine/spine-unity/Asset Types/RegionlessAttachmentLoader.cs.meta new file mode 100644 index 0000000..5d7a368 --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/RegionlessAttachmentLoader.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 15f0f78b87720c047a320c5e0e3f91b7 +timeCreated: 1520505662 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Asset Types/SkeletonDataAsset.cs b/Assets/Spine/spine-unity/Asset Types/SkeletonDataAsset.cs new file mode 100644 index 0000000..8891ee3 --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/SkeletonDataAsset.cs @@ -0,0 +1,221 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.IO; +using UnityEngine; +using Spine; + +namespace Spine.Unity { + public class SkeletonDataAsset : ScriptableObject { + #region Inspector + public AtlasAsset[] atlasAssets = new AtlasAsset[0]; + #if SPINE_TK2D + public tk2dSpriteCollectionData spriteCollection; + public float scale = 1f; + #else + public float scale = 0.01f; + #endif + public TextAsset skeletonJSON; + [SpineAnimation(includeNone: false)] + public string[] fromAnimation = new string[0]; + [SpineAnimation(includeNone: false)] + public string[] toAnimation = new string[0]; + public float[] duration = new float[0]; + public float defaultMix; + public RuntimeAnimatorController controller; + + public bool IsLoaded { get { return this.skeletonData != null; } } + + void Reset () { + Clear(); + } + #endregion + + SkeletonData skeletonData; + AnimationStateData stateData; + + #region Runtime Instantiation + /// + /// Creates a runtime SkeletonDataAsset. + public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAsset atlasAsset, bool initialize, float scale = 0.01f) { + return CreateRuntimeInstance(skeletonDataFile, new [] {atlasAsset}, initialize, scale); + } + + /// + /// Creates a runtime SkeletonDataAsset. + public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAsset[] atlasAssets, bool initialize, float scale = 0.01f) { + SkeletonDataAsset skeletonDataAsset = ScriptableObject.CreateInstance(); + skeletonDataAsset.Clear(); + skeletonDataAsset.skeletonJSON = skeletonDataFile; + skeletonDataAsset.atlasAssets = atlasAssets; + skeletonDataAsset.scale = scale; + + if (initialize) + skeletonDataAsset.GetSkeletonData(true); + + return skeletonDataAsset; + } + #endregion + + public void Clear () { + skeletonData = null; + stateData = null; + } + + public SkeletonData GetSkeletonData (bool quiet) { + if (skeletonJSON == null) { + if (!quiet) + Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this); + Clear(); + return null; + } + + // Disabled to support attachmentless/skinless SkeletonData. +// if (atlasAssets == null) { +// atlasAssets = new AtlasAsset[0]; +// if (!quiet) +// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); +// Clear(); +// return null; +// } +// #if !SPINE_TK2D +// if (atlasAssets.Length == 0) { +// Clear(); +// return null; +// } +// #else +// if (atlasAssets.Length == 0 && spriteCollection == null) { +// Clear(); +// return null; +// } +// #endif + + if (skeletonData != null) + return skeletonData; + + AttachmentLoader attachmentLoader; + float skeletonDataScale; + Atlas[] atlasArray = this.GetAtlasArray(); + + #if !SPINE_TK2D + attachmentLoader = (atlasArray.Length == 0) ? (AttachmentLoader)new RegionlessAttachmentLoader() : (AttachmentLoader)new AtlasAttachmentLoader(atlasArray); + skeletonDataScale = scale; + #else + if (spriteCollection != null) { + attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection); + skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale); + } else { + if (atlasArray.Length == 0) { + Reset(); + if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); + return null; + } + attachmentLoader = new AtlasAttachmentLoader(atlasArray); + skeletonDataScale = scale; + } + #endif + + bool isBinary = skeletonJSON.name.ToLower().Contains(".skel"); + SkeletonData loadedSkeletonData; + + try { + if (isBinary) + loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale); + else + loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale); + + } catch (Exception ex) { + if (!quiet) + Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); + return null; + + } + + this.InitializeWithData(loadedSkeletonData); + + return skeletonData; + } + + internal void InitializeWithData (SkeletonData sd) { + this.skeletonData = sd; + this.stateData = new AnimationStateData(skeletonData); + FillStateData(); + } + + internal Atlas[] GetAtlasArray () { + var returnList = new System.Collections.Generic.List(atlasAssets.Length); + for (int i = 0; i < atlasAssets.Length; i++) { + var aa = atlasAssets[i]; + if (aa == null) continue; + var a = aa.GetAtlas(); + if (a == null) continue; + returnList.Add(a); + } + return returnList.ToArray(); + } + + internal static SkeletonData ReadSkeletonData (byte[] bytes, AttachmentLoader attachmentLoader, float scale) { + var input = new MemoryStream(bytes); + var binary = new SkeletonBinary(attachmentLoader) { + Scale = scale + }; + return binary.ReadSkeletonData(input); + } + + internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) { + var input = new StringReader(text); + var json = new SkeletonJson(attachmentLoader) { + Scale = scale + }; + return json.ReadSkeletonData(input); + } + + public void FillStateData () { + if (stateData != null) { + stateData.defaultMix = defaultMix; + + for (int i = 0, n = fromAnimation.Length; i < n; i++) { + if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0) + continue; + stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]); + } + } + } + + public AnimationStateData GetAnimationStateData () { + if (stateData != null) + return stateData; + GetSkeletonData(false); + return stateData; + } + } + +} diff --git a/Assets/Spine/spine-unity/Asset Types/SkeletonDataAsset.cs.meta b/Assets/Spine/spine-unity/Asset Types/SkeletonDataAsset.cs.meta new file mode 100644 index 0000000..b5162ad --- /dev/null +++ b/Assets/Spine/spine-unity/Asset Types/SkeletonDataAsset.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f1b3b4b945939a54ea0b23d3396115fb +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 68defdbc95b30a74a9ad396bfc9a2277, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Components.meta b/Assets/Spine/spine-unity/Components.meta new file mode 100644 index 0000000..c8b4659 --- /dev/null +++ b/Assets/Spine/spine-unity/Components.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 954179821df28404683b8289f05d0c6f +folderAsset: yes +timeCreated: 1518344191 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Components/BoneFollower.cs b/Assets/Spine/spine-unity/Components/BoneFollower.cs new file mode 100644 index 0000000..6ce6f14 --- /dev/null +++ b/Assets/Spine/spine-unity/Components/BoneFollower.cs @@ -0,0 +1,184 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using UnityEngine; + +namespace Spine.Unity { + /// Sets a GameObject's transform to match a bone on a Spine skeleton. + [ExecuteInEditMode] + [AddComponentMenu("Spine/BoneFollower")] + public class BoneFollower : MonoBehaviour { + + #region Inspector + public SkeletonRenderer skeletonRenderer; + public SkeletonRenderer SkeletonRenderer { + get { return skeletonRenderer; } + set { + skeletonRenderer = value; + Initialize(); + } + } + + /// If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly. + [SpineBone(dataField: "skeletonRenderer")] + [SerializeField] public string boneName; + + public bool followZPosition = true; + public bool followBoneRotation = true; + + [Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")] + public bool followSkeletonFlip = true; + + [Tooltip("Follows the target bone's local scale. BoneFollower cannot inherit world/skewed scale because of UnityEngine.Transform property limitations.")] + public bool followLocalScale = false; + + [UnityEngine.Serialization.FormerlySerializedAs("resetOnAwake")] + public bool initializeOnAwake = true; + #endregion + + [NonSerialized] public bool valid; + /// + /// The bone. + /// + [NonSerialized] public Bone bone; + Transform skeletonTransform; + bool skeletonTransformIsParent; + + /// + /// Sets the target bone by its bone name. Returns false if no bone was found. To set the bone by reference, use BoneFollower.bone directly. + public bool SetBone (string name) { + bone = skeletonRenderer.skeleton.FindBone(name); + if (bone == null) { + Debug.LogError("Bone not found: " + name, this); + return false; + } + boneName = name; + return true; + } + + public void Awake () { + if (initializeOnAwake) Initialize(); + } + + public void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) { + Initialize(); + } + + public void Initialize () { + bone = null; + valid = skeletonRenderer != null && skeletonRenderer.valid; + if (!valid) return; + + skeletonTransform = skeletonRenderer.transform; + skeletonRenderer.OnRebuild -= HandleRebuildRenderer; + skeletonRenderer.OnRebuild += HandleRebuildRenderer; + skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent); + + if (!string.IsNullOrEmpty(boneName)) + bone = skeletonRenderer.skeleton.FindBone(boneName); + + #if UNITY_EDITOR + if (Application.isEditor) + LateUpdate(); + #endif + } + + void OnDestroy () { + if (skeletonRenderer != null) + skeletonRenderer.OnRebuild -= HandleRebuildRenderer; + } + + public void LateUpdate () { + if (!valid) { + Initialize(); + return; + } + + #if UNITY_EDITOR + if (!Application.isPlaying) + skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent); + #endif + + if (bone == null) { + if (string.IsNullOrEmpty(boneName)) return; + bone = skeletonRenderer.skeleton.FindBone(boneName); + if (!SetBone(boneName)) return; + } + + Transform thisTransform = this.transform; + if (skeletonTransformIsParent) { + // Recommended setup: Use local transform properties if Spine GameObject is the immediate parent + thisTransform.localPosition = new Vector3(bone.worldX, bone.worldY, followZPosition ? 0f : thisTransform.localPosition.z); + if (followBoneRotation) { + float halfRotation = Mathf.Atan2(bone.c, bone.a) * 0.5f; + if (followLocalScale && bone.scaleX < 0) // Negate rotation from negative scaleX. Don't use negative determinant. local scaleY doesn't factor into used rotation. + halfRotation += Mathf.PI * 0.5f; + + var q = default(Quaternion); + q.z = Mathf.Sin(halfRotation); + q.w = Mathf.Cos(halfRotation); + thisTransform.localRotation = q; + } + } else { + // For special cases: Use transform world properties if transform relationship is complicated + Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY, 0f)); + if (!followZPosition) targetWorldPosition.z = thisTransform.position.z; + + float boneWorldRotation = bone.WorldRotationX; + + Transform transformParent = thisTransform.parent; + if (transformParent != null) { + Matrix4x4 m = transformParent.localToWorldMatrix; + if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative + boneWorldRotation = -boneWorldRotation; + } + + if (followBoneRotation) { + Vector3 worldRotation = skeletonTransform.rotation.eulerAngles; + if (followLocalScale && bone.scaleX < 0) boneWorldRotation += 180f; + #if UNITY_5_6_OR_NEWER + thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + boneWorldRotation)); + #else + thisTransform.position = targetWorldPosition; + thisTransform.rotation = Quaternion.Euler(worldRotation.x, worldRotation.y, worldRotation.z + bone.WorldRotationX); + #endif + } else { + thisTransform.position = targetWorldPosition; + } + } + + Vector3 localScale = followLocalScale ? new Vector3(bone.scaleX, bone.scaleY, 1f) : new Vector3(1f, 1f, 1f); + if (followSkeletonFlip) localScale.y *= bone.skeleton.flipX ^ bone.skeleton.flipY ? -1f : 1f; + thisTransform.localScale = localScale; + } + } + +} diff --git a/Assets/Spine/spine-unity/Components/BoneFollower.cs.meta b/Assets/Spine/spine-unity/Components/BoneFollower.cs.meta new file mode 100644 index 0000000..c5bb78c --- /dev/null +++ b/Assets/Spine/spine-unity/Components/BoneFollower.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a1fd8daaed7b64148a34acb96ba14ce1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Components/PointFollower.cs b/Assets/Spine/spine-unity/Components/PointFollower.cs new file mode 100644 index 0000000..f3702be --- /dev/null +++ b/Assets/Spine/spine-unity/Components/PointFollower.cs @@ -0,0 +1,154 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity { + + [ExecuteInEditMode] + [AddComponentMenu("Spine/Point Follower")] + public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent { + + [SerializeField] public SkeletonRenderer skeletonRenderer; + public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } } + public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } } + + [SpineSlot(dataField:"skeletonRenderer", includeNone: true)] + public string slotName; + + [SpineAttachment(slotField:"slotName", dataField: "skeletonRenderer", fallbackToTextField:true, includeNone: true)] + public string pointAttachmentName; + + public bool followRotation = true; + public bool followSkeletonFlip = true; + public bool followSkeletonZPosition = false; + + Transform skeletonTransform; + bool skeletonTransformIsParent; + PointAttachment point; + Bone bone; + bool valid; + public bool IsValid { get { return valid; } } + + public void Initialize () { + valid = skeletonRenderer != null && skeletonRenderer.valid; + if (!valid) + return; + + UpdateReferences(); + + #if UNITY_EDITOR + if (Application.isEditor) LateUpdate(); + #endif + } + + private void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) { + Initialize(); + } + + void UpdateReferences () { + skeletonTransform = skeletonRenderer.transform; + skeletonRenderer.OnRebuild -= HandleRebuildRenderer; + skeletonRenderer.OnRebuild += HandleRebuildRenderer; + skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent); + + bone = null; + point = null; + if (!string.IsNullOrEmpty(pointAttachmentName)) { + var skeleton = skeletonRenderer.skeleton; + + int slotIndex = skeleton.FindSlotIndex(slotName); + if (slotIndex >= 0) { + var slot = skeleton.slots.Items[slotIndex]; + bone = slot.bone; + point = skeleton.GetAttachment(slotIndex, pointAttachmentName) as PointAttachment; + } + } + } + + public void LateUpdate () { + #if UNITY_EDITOR + if (!Application.isPlaying) skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent); + #endif + + if (point == null) { + if (string.IsNullOrEmpty(pointAttachmentName)) return; + UpdateReferences(); + if (point == null) return; + } + + Vector2 worldPos; + point.ComputeWorldPosition(bone, out worldPos.x, out worldPos.y); + float rotation = point.ComputeWorldRotation(bone); + + Transform thisTransform = this.transform; + if (skeletonTransformIsParent) { + // Recommended setup: Use local transform properties if Spine GameObject is the immediate parent + thisTransform.localPosition = new Vector3(worldPos.x, worldPos.y, followSkeletonZPosition ? 0f : thisTransform.localPosition.z); + if (followRotation) { + float halfRotation = rotation * 0.5f * Mathf.Deg2Rad; + + var q = default(Quaternion); + q.z = Mathf.Sin(halfRotation); + q.w = Mathf.Cos(halfRotation); + thisTransform.localRotation = q; + } + } else { + // For special cases: Use transform world properties if transform relationship is complicated + Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(worldPos.x, worldPos.y, 0f)); + if (!followSkeletonZPosition) + targetWorldPosition.z = thisTransform.position.z; + + Transform transformParent = thisTransform.parent; + if (transformParent != null) { + Matrix4x4 m = transformParent.localToWorldMatrix; + if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative + rotation = -rotation; + } + + if (followRotation) { + Vector3 transformWorldRotation = skeletonTransform.rotation.eulerAngles; + thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(transformWorldRotation.x, transformWorldRotation.y, transformWorldRotation.z + rotation)); + } else { + thisTransform.position = targetWorldPosition; + } + } + + if (followSkeletonFlip) { + Vector3 localScale = thisTransform.localScale; + localScale.y = Mathf.Abs(localScale.y) * (bone.skeleton.flipX ^ bone.skeleton.flipY ? -1f : 1f); + thisTransform.localScale = localScale; + } + } + } +} + diff --git a/Assets/Spine/spine-unity/Components/PointFollower.cs.meta b/Assets/Spine/spine-unity/Components/PointFollower.cs.meta new file mode 100644 index 0000000..1af3893 --- /dev/null +++ b/Assets/Spine/spine-unity/Components/PointFollower.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: daf461e4341180341a648c07e1899528 +timeCreated: 1518094986 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Components/SkeletonAnimation.cs b/Assets/Spine/spine-unity/Components/SkeletonAnimation.cs new file mode 100644 index 0000000..218bd35 --- /dev/null +++ b/Assets/Spine/spine-unity/Components/SkeletonAnimation.cs @@ -0,0 +1,210 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; + +namespace Spine.Unity { + + [ExecuteInEditMode] + [AddComponentMenu("Spine/SkeletonAnimation")] + [HelpURL("http://esotericsoftware.com/spine-unity-documentation#Controlling-Animation")] + public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation, IAnimationStateComponent { + + #region IAnimationStateComponent + /// + /// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it. + /// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start + public Spine.AnimationState state; + /// + /// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it. + /// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start + public Spine.AnimationState AnimationState { get { return this.state; } } + #endregion + + #region Bone Callbacks ISkeletonAnimation + protected event UpdateBonesDelegate _UpdateLocal; + protected event UpdateBonesDelegate _UpdateWorld; + protected event UpdateBonesDelegate _UpdateComplete; + + /// + /// Occurs after the animations are applied and before world space values are resolved. + /// Use this callback when you want to set bone local values. + /// + public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } } + + /// + /// Occurs after the Skeleton's bone world space values are resolved (including all constraints). + /// Using this callback will cause the world space values to be solved an extra time. + /// Use this callback if want to use bone world space values, and also set bone local values. + public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } } + + /// + /// Occurs after the Skeleton's bone world space values are resolved (including all constraints). + /// Use this callback if you want to use bone world space values, but don't intend to modify bone local values. + /// This callback can also be used when setting world position and the bone matrix. + public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } } + #endregion + + #region Serialized state and Beginner API + [SerializeField] + [SpineAnimation] + private string _animationName; + + /// + /// Setting this property sets the animation of the skeleton. If invalid, it will store the animation name for the next time the skeleton is properly initialized. + /// Getting this property gets the name of the currently playing animation. If invalid, it will return the last stored animation name set through this property. + public string AnimationName { + get { + if (!valid) { + return _animationName; + } else { + TrackEntry entry = state.GetCurrent(0); + return entry == null ? null : entry.Animation.Name; + } + } + set { + if (_animationName == value) + return; + _animationName = value; + + if (string.IsNullOrEmpty(value)) { + state.ClearTrack(0); + } else { + TrySetAnimation(value, loop); + } + } + } + + TrackEntry TrySetAnimation (string animationName, bool animationLoop) { + var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(animationName); + if (animationObject != null) + return state.SetAnimation(0, animationObject, animationLoop); + + return null; + } + + /// Whether or not should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected. + public bool loop; + + /// + /// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%. + /// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively. + public float timeScale = 1; + #endregion + + #region Runtime Instantiation + /// Adds and prepares a SkeletonAnimation component to a GameObject at runtime. + /// The newly instantiated SkeletonAnimation + public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) { + return SkeletonRenderer.AddSpineComponent(gameObject, skeletonDataAsset); + } + + /// Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it. + /// The newly instantiated SkeletonAnimation component. + public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset) { + return SkeletonRenderer.NewSpineGameObject(skeletonDataAsset); + } + #endregion + + /// + /// Clears the previously generated mesh, resets the skeleton's pose, and clears all previously active animations. + public override void ClearState () { + base.ClearState(); + if (state != null) state.ClearTracks(); + } + + /// + /// Initialize this component. Attempts to load the SkeletonData and creates the internal Spine objects and buffers. + /// If set to true, force overwrite an already initialized object. + public override void Initialize (bool overwrite) { + if (valid && !overwrite) + return; + + base.Initialize(overwrite); + + if (!valid) + return; + + state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); + + #if UNITY_EDITOR + if (!string.IsNullOrEmpty(_animationName)) { + if (Application.isPlaying) { + TrackEntry startingTrack = TrySetAnimation(_animationName, loop); + if (startingTrack != null) + Update(0); + } else { + // Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above. + var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName); + if (animationObject != null) + animationObject.PoseSkeleton(skeleton, 0f); + } + } + #else + if (!string.IsNullOrEmpty(_animationName)) { + TrackEntry startingTrack = TrySetAnimation(_animationName, loop); + if (startingTrack != null) + Update(0); + } + #endif + } + + void Update () { + Update(Time.deltaTime); + } + + /// Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time. + public void Update (float deltaTime) { + if (!valid) + return; + + deltaTime *= timeScale; + skeleton.Update(deltaTime); + state.Update(deltaTime); + state.Apply(skeleton); + + if (_UpdateLocal != null) + _UpdateLocal(this); + + skeleton.UpdateWorldTransform(); + + if (_UpdateWorld != null) { + _UpdateWorld(this); + skeleton.UpdateWorldTransform(); + } + + if (_UpdateComplete != null) { + _UpdateComplete(this); + } + } + + } + +} diff --git a/Assets/Spine/spine-unity/Components/SkeletonAnimation.cs.meta b/Assets/Spine/spine-unity/Components/SkeletonAnimation.cs.meta new file mode 100644 index 0000000..23ede7b --- /dev/null +++ b/Assets/Spine/spine-unity/Components/SkeletonAnimation.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d247ba06193faa74d9335f5481b2b56c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Components/SkeletonAnimator.cs b/Assets/Spine/spine-unity/Components/SkeletonAnimator.cs new file mode 100644 index 0000000..e6012d8 --- /dev/null +++ b/Assets/Spine/spine-unity/Components/SkeletonAnimator.cs @@ -0,0 +1,463 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections.Generic; + +namespace Spine.Unity { + [RequireComponent(typeof(Animator))] + public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { + + [SerializeField] protected MecanimTranslator translator; + public MecanimTranslator Translator { get { return translator; } } + + #region Bone Callbacks (ISkeletonAnimation) + protected event UpdateBonesDelegate _UpdateLocal; + protected event UpdateBonesDelegate _UpdateWorld; + protected event UpdateBonesDelegate _UpdateComplete; + + /// + /// Occurs after the animations are applied and before world space values are resolved. + /// Use this callback when you want to set bone local values. + public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } } + + /// + /// Occurs after the Skeleton's bone world space values are resolved (including all constraints). + /// Using this callback will cause the world space values to be solved an extra time. + /// Use this callback if want to use bone world space values, and also set bone local values. + public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } } + + /// + /// Occurs after the Skeleton's bone world space values are resolved (including all constraints). + /// Use this callback if you want to use bone world space values, but don't intend to modify bone local values. + /// This callback can also be used when setting world position and the bone matrix. + public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } } + #endregion + + public override void Initialize (bool overwrite) { + if (valid && !overwrite) return; + base.Initialize(overwrite); + if (!valid) return; + + if (translator == null) translator = new MecanimTranslator(); + translator.Initialize(GetComponent(), this.skeletonDataAsset); + } + + public void Update () { + if (!valid) return; + + #if UNITY_EDITOR + if (Application.isPlaying) { + translator.Apply(skeleton); + } else { + var translatorAnimator = translator.Animator; + if (translatorAnimator != null && translatorAnimator.isInitialized) + translator.Apply(skeleton); + } + #else + translator.Apply(skeleton); + #endif + + // UpdateWorldTransform and Bone Callbacks + { + if (_UpdateLocal != null) + _UpdateLocal(this); + + skeleton.UpdateWorldTransform(); + + if (_UpdateWorld != null) { + _UpdateWorld(this); + skeleton.UpdateWorldTransform(); + } + + if (_UpdateComplete != null) + _UpdateComplete(this); + } + } + + [System.Serializable] + public class MecanimTranslator { + #region Inspector + public bool autoReset = true; + public MixMode[] layerMixModes = new MixMode[0]; + #endregion + + public enum MixMode { AlwaysMix, MixNext, SpineStyle } + + readonly Dictionary animationTable = new Dictionary(IntEqualityComparer.Instance); + readonly Dictionary clipNameHashCodeTable = new Dictionary(AnimationClipEqualityComparer.Instance); + readonly List previousAnimations = new List(); + + protected class ClipInfos { + public bool isInterruptionActive = false; + public bool isLastFrameOfInterruption = false; + + public int clipInfoCount = 0; + public int nextClipInfoCount = 0; + public int interruptingClipInfoCount = 0; + public readonly List clipInfos = new List(); + public readonly List nextClipInfos = new List(); + public readonly List interruptingClipInfos = new List(); + + public AnimatorStateInfo stateInfo; + public AnimatorStateInfo nextStateInfo; + public AnimatorStateInfo interruptingStateInfo; + + public float interruptingClipTimeAddition = 0; + } + protected ClipInfos[] layerClipInfos = new ClipInfos[0]; + + Animator animator; + public Animator Animator { get { return this.animator; } } + + public void Initialize (Animator animator, SkeletonDataAsset skeletonDataAsset) { + this.animator = animator; + + previousAnimations.Clear(); + + animationTable.Clear(); + var data = skeletonDataAsset.GetSkeletonData(true); + foreach (var a in data.Animations) + animationTable.Add(a.Name.GetHashCode(), a); + + clipNameHashCodeTable.Clear(); + ClearClipInfosForLayers(); + } + + public void Apply (Skeleton skeleton) { + if (layerMixModes.Length < animator.layerCount) + System.Array.Resize(ref layerMixModes, animator.layerCount); + + InitClipInfosForLayers(); + for (int layer = 0, n = animator.layerCount; layer < n; layer++) { + GetStateUpdatesFromAnimator(layer); + } + + //skeleton.Update(Time.deltaTime); // Doesn't actually do anything, currently. (Spine 3.6). + + // Clear Previous + if (autoReset) { + var previousAnimations = this.previousAnimations; + for (int i = 0, n = previousAnimations.Count; i < n; i++) + previousAnimations[i].SetKeyedItemsToSetupPose(skeleton); + + previousAnimations.Clear(); + for (int layer = 0, n = animator.layerCount; layer < n; layer++) { + float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1. + if (layerWeight <= 0) continue; + + AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer); + + bool hasNext = nextStateInfo.fullPathHash != 0; + + int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount; + IList clipInfo, nextClipInfo, interruptingClipInfo; + bool isInterruptionActive, shallInterpolateWeightTo1; + GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount, + out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1); + + for (int c = 0; c < clipInfoCount; c++) { + var info = clipInfo[c]; + float weight = info.weight * layerWeight; if (weight == 0) continue; + previousAnimations.Add(GetAnimation(info.clip)); + } + + if (hasNext) { + for (int c = 0; c < nextClipInfoCount; c++) { + var info = nextClipInfo[c]; + float weight = info.weight * layerWeight; if (weight == 0) continue; + previousAnimations.Add(GetAnimation(info.clip)); + } + } + + if (isInterruptionActive) { + for (int c = 0; c < interruptingClipInfoCount; c++) + { + var info = interruptingClipInfo[c]; + float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; + float weight = clipWeight * layerWeight; if (weight == 0) continue; + previousAnimations.Add(GetAnimation(info.clip)); + } + } + } + } + + // Apply + for (int layer = 0, n = animator.layerCount; layer < n; layer++) { + float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1. + + bool isInterruptionActive; + AnimatorStateInfo stateInfo; + AnimatorStateInfo nextStateInfo; + AnimatorStateInfo interruptingStateInfo; + float interruptingClipTimeAddition; + GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition); + + bool hasNext = nextStateInfo.fullPathHash != 0; + + int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount; + IList clipInfo, nextClipInfo, interruptingClipInfo; + bool shallInterpolateWeightTo1; + GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount, + out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1); + + MixMode mode = layerMixModes[layer]; + if (mode == MixMode.AlwaysMix) { + // Always use Mix instead of Applying the first non-zero weighted clip. + for (int c = 0; c < clipInfoCount; c++) { + var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, MixPose.Current, MixDirection.In); + } + if (hasNext) { + for (int c = 0; c < nextClipInfoCount; c++) { + var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime , info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, MixPose.Current, MixDirection.In); + } + } + if (isInterruptionActive) { + for (int c = 0; c < interruptingClipInfoCount; c++) + { + var info = interruptingClipInfo[c]; + float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; + float weight = clipWeight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), + interruptingStateInfo.loop, null, weight, MixPose.Current, MixDirection.In); + } + } + } else { // case MixNext || SpineStyle + // Apply first non-zero weighted clip + int c = 0; + for (; c < clipInfoCount; c++) { + var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, 1f, MixPose.Current, MixDirection.In); + break; + } + // Mix the rest + for (; c < clipInfoCount; c++) { + var info = clipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(stateInfo.normalizedTime, info.clip.length, stateInfo.loop, stateInfo.speed < 0), stateInfo.loop, null, weight, MixPose.Current, MixDirection.In); + } + + c = 0; + if (hasNext) { + // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) + if (mode == MixMode.SpineStyle) { + for (; c < nextClipInfoCount; c++) { + var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime , info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, 1f, MixPose.Current, MixDirection.In); + break; + } + } + // Mix the rest + for (; c < nextClipInfoCount; c++) { + var info = nextClipInfo[c]; float weight = info.weight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(nextStateInfo.normalizedTime , info.clip.length, nextStateInfo.speed < 0), nextStateInfo.loop, null, weight, MixPose.Current, MixDirection.In); + } + } + + c = 0; + if (isInterruptionActive) { + // Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights) + if (mode == MixMode.SpineStyle) { + for (; c < interruptingClipInfoCount; c++) { + var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; + float weight = clipWeight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, 1f, MixPose.Current, MixDirection.In); + break; + } + } + // Mix the rest + for (; c < interruptingClipInfoCount; c++) { + var info = interruptingClipInfo[c]; float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight; + float weight = clipWeight * layerWeight; if (weight == 0) continue; + GetAnimation(info.clip).Apply(skeleton, 0, AnimationTime(interruptingStateInfo.normalizedTime + interruptingClipTimeAddition, info.clip.length, interruptingStateInfo.speed < 0), interruptingStateInfo.loop, null, weight, MixPose.Current, MixDirection.In); + } + } + } + } + } + + static float AnimationTime (float normalizedTime, float clipLength, bool loop, bool reversed) { + if (reversed) + normalizedTime = (1-normalizedTime + (int)normalizedTime) + (int)normalizedTime; + float time = normalizedTime * clipLength; + if (loop) return time; + const float EndSnapEpsilon = 1f/30f; // Workaround for end-duration keys not being applied. + return (clipLength - time < EndSnapEpsilon) ? clipLength : time; // return a time snapped to clipLength; + } + + static float AnimationTime (float normalizedTime, float clipLength, bool reversed) { + if (reversed) + normalizedTime = (1-normalizedTime + (int)normalizedTime) + (int)normalizedTime; + + return normalizedTime * clipLength; + } + + void InitClipInfosForLayers() { + if (layerClipInfos.Length < animator.layerCount) { + System.Array.Resize(ref layerClipInfos, animator.layerCount); + for (int layer = 0, n = animator.layerCount; layer < n; ++layer) { + if (layerClipInfos[layer] == null) + layerClipInfos[layer] = new ClipInfos(); + } + } + } + + void ClearClipInfosForLayers() { + for (int layer = 0, n = layerClipInfos.Length; layer < n; ++layer) { + if (layerClipInfos[layer] == null) + layerClipInfos[layer] = new ClipInfos(); + else { + layerClipInfos[layer].isInterruptionActive = false; + layerClipInfos[layer].isLastFrameOfInterruption = false; + layerClipInfos[layer].clipInfos.Clear(); + layerClipInfos[layer].nextClipInfos.Clear(); + layerClipInfos[layer].interruptingClipInfos.Clear(); + } + } + } + + void GetStateUpdatesFromAnimator(int layer) { + + var layerInfos = layerClipInfos[layer]; + int clipInfoCount = animator.GetCurrentAnimatorClipInfoCount(layer); + int nextClipInfoCount = animator.GetNextAnimatorClipInfoCount(layer); + + var clipInfos = layerInfos.clipInfos; + var nextClipInfos = layerInfos.nextClipInfos; + var interruptingClipInfos = layerInfos.interruptingClipInfos; + + layerInfos.isInterruptionActive = (clipInfoCount == 0 && nextClipInfoCount == 0); + + // Note: during interruption, GetCurrentAnimatorClipInfoCount and GetNextAnimatorClipInfoCount + // are returning 0 in calls above. Therefore we keep previous clipInfos and nextClipInfos + // until the interruption is over. + if (layerInfos.isInterruptionActive) { + + // Note: The last frame of a transition interruption + // will have fullPathHash set to 0, therefore we have to use previous + // frame's infos about interruption clips and correct some values + // accordingly (normalizedTime and weight). + var interruptingStateInfo = animator.GetNextAnimatorStateInfo(layer); + layerInfos.isLastFrameOfInterruption = interruptingStateInfo.fullPathHash == 0; + if (!layerInfos.isLastFrameOfInterruption) { + layerInfos.interruptingClipInfoCount = interruptingClipInfos.Count; + + animator.GetNextAnimatorClipInfo(layer, interruptingClipInfos); + float oldTime = layerInfos.interruptingStateInfo.normalizedTime; + float newTime = interruptingStateInfo.normalizedTime; + layerInfos.interruptingClipTimeAddition = newTime - oldTime; + layerInfos.interruptingStateInfo = interruptingStateInfo; + } + } + else { + layerInfos.clipInfoCount = clipInfoCount; + layerInfos.nextClipInfoCount = nextClipInfoCount; + layerInfos.interruptingClipInfoCount = 0; + layerInfos.isLastFrameOfInterruption = false; + + if (clipInfos.Capacity < clipInfoCount) clipInfos.Capacity = clipInfoCount; + if (nextClipInfos.Capacity < nextClipInfoCount) nextClipInfos.Capacity = nextClipInfoCount; + + animator.GetCurrentAnimatorClipInfo(layer, clipInfos); + animator.GetNextAnimatorClipInfo(layer, nextClipInfos); + + layerInfos.stateInfo = animator.GetCurrentAnimatorStateInfo(layer); + layerInfos.nextStateInfo = animator.GetNextAnimatorStateInfo(layer); + } + } + + void GetAnimatorClipInfos ( + int layer, + out bool isInterruptionActive, + out int clipInfoCount, + out int nextClipInfoCount, + out int interruptingClipInfoCount, + out IList clipInfo, + out IList nextClipInfo, + out IList interruptingClipInfo, + out bool shallInterpolateWeightTo1) { + + var layerInfos = layerClipInfos[layer]; + isInterruptionActive = layerInfos.isInterruptionActive; + + clipInfoCount = layerInfos.clipInfoCount; + nextClipInfoCount = layerInfos.nextClipInfoCount; + interruptingClipInfoCount = layerInfos.interruptingClipInfoCount; + + clipInfo = layerInfos.clipInfos; + nextClipInfo = layerInfos.nextClipInfos; + interruptingClipInfo = isInterruptionActive ? layerInfos.interruptingClipInfos : null; + shallInterpolateWeightTo1 = layerInfos.isLastFrameOfInterruption; + } + + void GetAnimatorStateInfos( + int layer, + out bool isInterruptionActive, + out AnimatorStateInfo stateInfo, + out AnimatorStateInfo nextStateInfo, + out AnimatorStateInfo interruptingStateInfo, + out float interruptingClipTimeAddition) { + + var layerInfos = layerClipInfos[layer]; + isInterruptionActive = layerInfos.isInterruptionActive; + + stateInfo = layerInfos.stateInfo; + nextStateInfo = layerInfos.nextStateInfo; + interruptingStateInfo = layerInfos.interruptingStateInfo; + interruptingClipTimeAddition = layerInfos.isLastFrameOfInterruption ? layerInfos.interruptingClipTimeAddition : 0; + } + + Spine.Animation GetAnimation (AnimationClip clip) { + int clipNameHashCode; + if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) { + clipNameHashCode = clip.name.GetHashCode(); + clipNameHashCodeTable.Add(clip, clipNameHashCode); + } + Spine.Animation animation; + animationTable.TryGetValue(clipNameHashCode, out animation); + return animation; + } + + class AnimationClipEqualityComparer : IEqualityComparer { + internal static readonly IEqualityComparer Instance = new AnimationClipEqualityComparer(); + public bool Equals (AnimationClip x, AnimationClip y) { return x.GetInstanceID() == y.GetInstanceID(); } + public int GetHashCode (AnimationClip o) { return o.GetInstanceID(); } + } + + class IntEqualityComparer : IEqualityComparer { + internal static readonly IEqualityComparer Instance = new IntEqualityComparer(); + public bool Equals (int x, int y) { return x == y; } + public int GetHashCode(int o) { return o; } + } + } + + } +} diff --git a/Assets/Spine/spine-unity/Components/SkeletonAnimator.cs.meta b/Assets/Spine/spine-unity/Components/SkeletonAnimator.cs.meta new file mode 100644 index 0000000..ebd50dc --- /dev/null +++ b/Assets/Spine/spine-unity/Components/SkeletonAnimator.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f9db98c60740638449864eb028fbe7ad +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Components/SkeletonRenderer.cs b/Assets/Spine/spine-unity/Components/SkeletonRenderer.cs new file mode 100644 index 0000000..1eb23a4 --- /dev/null +++ b/Assets/Spine/spine-unity/Components/SkeletonRenderer.cs @@ -0,0 +1,331 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define SPINE_OPTIONAL_RENDEROVERRIDE +#define SPINE_OPTIONAL_MATERIALOVERRIDE + +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity { + /// Renders a skeleton. + [ExecuteInEditMode, RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent] + [HelpURL("http://esotericsoftware.com/spine-unity-documentation#Rendering")] + public class SkeletonRenderer : MonoBehaviour, ISkeletonComponent, IHasSkeletonDataAsset { + + public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer); + public event SkeletonRendererDelegate OnRebuild; + + /// Occurs after the vertex data is populated every frame, before the vertices are pushed into the mesh. + public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices; + + public SkeletonDataAsset skeletonDataAsset; + public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } // ISkeletonComponent + public string initialSkinName; + public bool initialFlipX, initialFlipY; + + #region Advanced + // Submesh Separation + [UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")] [SpineSlot] public string[] separatorSlotNames = new string[0]; + [System.NonSerialized] public readonly List separatorSlots = new List(); + + [Range(-0.1f, 0f)] public float zSpacing; + //public bool renderMeshes = true; + public bool useClipping = true; + public bool immutableTriangles = false; + public bool pmaVertexColors = true; + /// Clears the state when this component or its GameObject is disabled. This prevents previous state from being retained when it is enabled again. When pooling your skeleton, setting this to true can be helpful. + public bool clearStateOnDisable = false; + public bool tintBlack = false; + public bool singleSubmesh = false; + + [UnityEngine.Serialization.FormerlySerializedAs("calculateNormals")] + public bool addNormals = false; + public bool calculateTangents = false; + + public bool logErrors = false; + + #if SPINE_OPTIONAL_RENDEROVERRIDE + public bool disableRenderingOnOverride = true; + public delegate void InstructionDelegate (SkeletonRendererInstruction instruction); + event InstructionDelegate generateMeshOverride; + public event InstructionDelegate GenerateMeshOverride { + add { + generateMeshOverride += value; + if (disableRenderingOnOverride && generateMeshOverride != null) { + Initialize(false); + meshRenderer.enabled = false; + } + } + remove { + generateMeshOverride -= value; + if (disableRenderingOnOverride && generateMeshOverride == null) { + Initialize(false); + meshRenderer.enabled = true; + } + } + } + #endif + + #if SPINE_OPTIONAL_MATERIALOVERRIDE + [System.NonSerialized] readonly Dictionary customMaterialOverride = new Dictionary(); + public Dictionary CustomMaterialOverride { get { return customMaterialOverride; } } + #endif + + // Custom Slot Material + [System.NonSerialized] readonly Dictionary customSlotMaterials = new Dictionary(); + public Dictionary CustomSlotMaterials { get { return customSlotMaterials; } } + #endregion + + MeshRenderer meshRenderer; + MeshFilter meshFilter; + + [System.NonSerialized] public bool valid; + [System.NonSerialized] public Skeleton skeleton; + public Skeleton Skeleton { + get { + Initialize(false); + return skeleton; + } + } + + [System.NonSerialized] readonly SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction(); + readonly MeshGenerator meshGenerator = new MeshGenerator(); + [System.NonSerialized] readonly MeshRendererBuffers rendererBuffers = new MeshRendererBuffers(); + + #region Runtime Instantiation + public static T NewSpineGameObject (SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + return SkeletonRenderer.AddSpineComponent(new GameObject("New Spine GameObject"), skeletonDataAsset); + } + + /// Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime. + /// T should be SkeletonRenderer or any of its derived classes. + public static T AddSpineComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) where T : SkeletonRenderer { + var c = gameObject.AddComponent(); + if (skeletonDataAsset != null) { + c.skeletonDataAsset = skeletonDataAsset; + c.Initialize(false); + } + return c; + } + + /// Applies MeshGenerator settings to the SkeletonRenderer and its internal MeshGenerator. + public void SetMeshSettings (MeshGenerator.Settings settings) { + this.calculateTangents = settings.calculateTangents; + this.immutableTriangles = settings.immutableTriangles; + this.pmaVertexColors = settings.pmaVertexColors; + this.tintBlack = settings.tintBlack; + this.useClipping = settings.useClipping; + this.zSpacing = settings.zSpacing; + + this.meshGenerator.settings = settings; + } + #endregion + + public virtual void Awake () { + Initialize(false); + } + + void OnDisable () { + if (clearStateOnDisable && valid) + ClearState(); + } + + void OnDestroy () { + rendererBuffers.Dispose(); + valid = false; + } + + /// + /// Clears the previously generated mesh and resets the skeleton's pose. + public virtual void ClearState () { + meshFilter.sharedMesh = null; + currentInstructions.Clear(); + if (skeleton != null) skeleton.SetToSetupPose(); + } + + public void EnsureMeshGeneratorCapacity (int minimumVertexCount) { + meshGenerator.EnsureVertexCapacity(minimumVertexCount); + } + + /// + /// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers. + /// If set to true, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize. + public virtual void Initialize (bool overwrite) { + if (valid && !overwrite) + return; + + // Clear + { + if (meshFilter != null) + meshFilter.sharedMesh = null; + + meshRenderer = GetComponent(); + if (meshRenderer != null) meshRenderer.sharedMaterial = null; + + currentInstructions.Clear(); + rendererBuffers.Clear(); + meshGenerator.Begin(); + skeleton = null; + valid = false; + } + + if (!skeletonDataAsset) { + if (logErrors) Debug.LogError("Missing SkeletonData asset.", this); + return; + } + + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false); + if (skeletonData == null) return; + valid = true; + + meshFilter = GetComponent(); + meshRenderer = GetComponent(); + rendererBuffers.Initialize(); + + skeleton = new Skeleton(skeletonData) { + flipX = initialFlipX, + flipY = initialFlipY + }; + + if (!string.IsNullOrEmpty(initialSkinName) && !string.Equals(initialSkinName, "default", System.StringComparison.Ordinal)) + skeleton.SetSkin(initialSkinName); + + separatorSlots.Clear(); + for (int i = 0; i < separatorSlotNames.Length; i++) + separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i])); + + LateUpdate(); // Generate mesh for the first frame it exists. + + if (OnRebuild != null) + OnRebuild(this); + } + + /// + /// Generates a new UnityEngine.Mesh from the internal Skeleton. + public virtual void LateUpdate () { + if (!valid) return; + + #if SPINE_OPTIONAL_RENDEROVERRIDE + bool doMeshOverride = generateMeshOverride != null; + if ((!meshRenderer.enabled) && !doMeshOverride) return; + #else + const bool doMeshOverride = false; + if (!meshRenderer.enabled) return; + #endif + var currentInstructions = this.currentInstructions; + var workingSubmeshInstructions = currentInstructions.submeshInstructions; + var currentSmartMesh = rendererBuffers.GetNextMesh(); // Double-buffer for performance. + + bool updateTriangles; + + if (this.singleSubmesh) { + // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================= + MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, skeletonDataAsset.atlasAssets[0].materials[0]); + + // STEP 1.9. Post-process workingInstructions. ================================================================================== + #if SPINE_OPTIONAL_MATERIALOVERRIDE + if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated + MeshGenerator.TryReplaceMaterials(workingSubmeshInstructions, customMaterialOverride); + #endif + + // STEP 2. Update vertex buffer based on verts from the attachments. =========================================================== + meshGenerator.settings = new MeshGenerator.Settings { + pmaVertexColors = this.pmaVertexColors, + zSpacing = this.zSpacing, + useClipping = this.useClipping, + tintBlack = this.tintBlack, + calculateTangents = this.calculateTangents, + addNormals = this.addNormals + }; + meshGenerator.Begin(); + updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed); + if (currentInstructions.hasActiveClipping) { + meshGenerator.AddSubmesh(workingSubmeshInstructions.Items[0], updateTriangles); + } else { + meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles); + } + + } else { + // STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. ============================================= + MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, customSlotMaterials, separatorSlots, doMeshOverride, this.immutableTriangles); + + // STEP 1.9. Post-process workingInstructions. ================================================================================== + #if SPINE_OPTIONAL_MATERIALOVERRIDE + if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated + MeshGenerator.TryReplaceMaterials(workingSubmeshInstructions, customMaterialOverride); + #endif + + #if SPINE_OPTIONAL_RENDEROVERRIDE + if (doMeshOverride) { + this.generateMeshOverride(currentInstructions); + if (disableRenderingOnOverride) return; + } + #endif + + updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed); + + // STEP 2. Update vertex buffer based on verts from the attachments. =========================================================== + meshGenerator.settings = new MeshGenerator.Settings { + pmaVertexColors = this.pmaVertexColors, + zSpacing = this.zSpacing, + useClipping = this.useClipping, + tintBlack = this.tintBlack, + calculateTangents = this.calculateTangents, + addNormals = this.addNormals + }; + meshGenerator.Begin(); + if (currentInstructions.hasActiveClipping) + meshGenerator.BuildMesh(currentInstructions, updateTriangles); + else + meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles); + } + + if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers); + + // STEP 3. Move the mesh data into a UnityEngine.Mesh =========================================================================== + var currentMesh = currentSmartMesh.mesh; + meshGenerator.FillVertexData(currentMesh); + rendererBuffers.UpdateSharedMaterials(workingSubmeshInstructions); + if (updateTriangles) { // Check if the triangles should also be updated. + meshGenerator.FillTriangles(currentMesh); + meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray(); + } else if (rendererBuffers.MaterialsChangedInLastUpdate()) { + meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray(); + } + + meshGenerator.FillLateVertexData(currentMesh); + + // STEP 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. =========== + meshFilter.sharedMesh = currentMesh; + currentSmartMesh.instructionUsed.Set(currentInstructions); + } + } +} diff --git a/Assets/Spine/spine-unity/Components/SkeletonRenderer.cs.meta b/Assets/Spine/spine-unity/Components/SkeletonRenderer.cs.meta new file mode 100644 index 0000000..5230098 --- /dev/null +++ b/Assets/Spine/spine-unity/Components/SkeletonRenderer.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: e075b9a3e08e2f74fbd651c858ab16ed +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor.meta b/Assets/Spine/spine-unity/Editor.meta new file mode 100644 index 0000000..3db0fd1 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: f0e95036e72b08544a9d295dd4366f40 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Editor/AnimationReferenceAssetEditor.cs b/Assets/Spine/spine-unity/Editor/AnimationReferenceAssetEditor.cs new file mode 100644 index 0000000..596efbb --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/AnimationReferenceAssetEditor.cs @@ -0,0 +1,166 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +using System.Reflection; + +namespace Spine.Unity.Editor { + using Editor = UnityEditor.Editor; + + [CustomEditor(typeof(AnimationReferenceAsset))] + public class AnimationReferenceAssetEditor : Editor { + + const string InspectorHelpText = "This is a Spine-Unity Animation Reference Asset. It serializes a reference to a SkeletonDataAsset and an animationName. It does not contain actual animation data. At runtime, it stores a reference to a Spine.Animation.\n\n" + + "You can use this in your AnimationState calls instead of a string animation name or a Spine.Animation reference. Use its implicit conversion into Spine.Animation or its .Animation property.\n\n" + + "Use AnimationReferenceAssets as an alternative to storing strings or finding animations and caching per component. This only does the lookup by string once, and allows you to store and manage animations via asset references."; + + readonly SkeletonInspectorPreview preview = new SkeletonInspectorPreview(); + FieldInfo skeletonDataAssetField = typeof(AnimationReferenceAsset).GetField("skeletonDataAsset", BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo nameField = typeof(AnimationReferenceAsset).GetField("animationName", BindingFlags.NonPublic | BindingFlags.Instance); + + AnimationReferenceAsset ThisAnimationReferenceAsset { get { return target as AnimationReferenceAsset; } } + SkeletonDataAsset ThisSkeletonDataAsset { get { return skeletonDataAssetField.GetValue(ThisAnimationReferenceAsset) as SkeletonDataAsset; } } + string ThisAnimationName { get { return nameField.GetValue(ThisAnimationReferenceAsset) as string; } } + + bool changeNextFrame = false; + SerializedProperty animationNameProperty; + SkeletonDataAsset lastSkeletonDataAsset; + + void OnEnable () { HandleOnEnablePreview(); } + void OnDestroy () { HandleOnDestroyPreview(); } + + public override void OnInspectorGUI () { + animationNameProperty = animationNameProperty ?? serializedObject.FindProperty("animationName"); + string animationName = animationNameProperty.stringValue; + + Animation animation = null; + if (ThisSkeletonDataAsset != null) { + var skeletonData = ThisSkeletonDataAsset.GetSkeletonData(true); + if (skeletonData != null) { + animation = skeletonData.FindAnimation(animationName); + } + } + bool animationNotFound = (animation == null); + + if (changeNextFrame) { + changeNextFrame = false; + + if (ThisSkeletonDataAsset != lastSkeletonDataAsset) { + preview.Clear(); + preview.Initialize(Repaint, ThisSkeletonDataAsset, LastSkinName); + + if (animationNotFound) { + animationNameProperty.stringValue = ""; + preview.ClearAnimationSetupPose(); + } + } + + preview.ClearAnimationSetupPose(); + + if (!string.IsNullOrEmpty(animationNameProperty.stringValue)) + preview.PlayPauseAnimation(animationNameProperty.stringValue, true); + } + lastSkeletonDataAsset = ThisSkeletonDataAsset; + + //EditorGUILayout.HelpBox(AnimationReferenceAssetEditor.InspectorHelpText, MessageType.Info, true); + EditorGUILayout.Space(); + EditorGUI.BeginChangeCheck(); + DrawDefaultInspector(); + if (EditorGUI.EndChangeCheck()) { + changeNextFrame = true; + } + + // Draw extra info below default inspector. + EditorGUILayout.Space(); + if (ThisSkeletonDataAsset == null) { + EditorGUILayout.HelpBox("SkeletonDataAsset is missing.", MessageType.Error); + } else if (string.IsNullOrEmpty(animationName)) { + EditorGUILayout.HelpBox("No animation selected.", MessageType.Warning); + } else if (animationNotFound) { + EditorGUILayout.HelpBox(string.Format("Animation named {0} was not found for this Skeleton.", animationNameProperty.stringValue), MessageType.Warning); + } else { + using (new SpineInspectorUtility.BoxScope()) { + if (!string.Equals(SpineEditorUtilities.GetPathSafeName(animationName), ThisAnimationReferenceAsset.name, System.StringComparison.OrdinalIgnoreCase)) + EditorGUILayout.HelpBox("Animation name value does not match this asset's name. Inspectors using this asset may be misleading.", MessageType.None); + + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(animationName, SpineEditorUtilities.Icons.animation)); + if (animation != null) { + EditorGUILayout.LabelField(string.Format("Timelines: {0}", animation.Timelines.Count)); + EditorGUILayout.LabelField(string.Format("Duration: {0} sec", animation.Duration)); + } + } + } + } + + #region Preview Handlers + string TargetAssetGUID { get { return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(ThisSkeletonDataAsset)); } } + string LastSkinKey { get { return TargetAssetGUID + "_lastSkin"; } } + string LastSkinName { get { return EditorPrefs.GetString(LastSkinKey, ""); } } + + void HandleOnEnablePreview () { + preview.Initialize(this.Repaint, ThisSkeletonDataAsset, LastSkinName); + preview.PlayPauseAnimation(ThisAnimationName, true); + preview.OnSkinChanged -= HandleOnSkinChanged; + preview.OnSkinChanged += HandleOnSkinChanged; + EditorApplication.update -= preview.HandleEditorUpdate; + EditorApplication.update += preview.HandleEditorUpdate; + } + + private void HandleOnSkinChanged (string skinName) { + EditorPrefs.SetString(LastSkinKey, skinName); + preview.PlayPauseAnimation(ThisAnimationName, true); + } + + void HandleOnDestroyPreview () { + EditorApplication.update -= preview.HandleEditorUpdate; + preview.OnDestroy(); + } + + override public bool HasPreviewGUI () { + if (serializedObject.isEditingMultipleObjects) return false; + return ThisSkeletonDataAsset != null && ThisSkeletonDataAsset.GetSkeletonData(true) != null; + } + + override public void OnInteractivePreviewGUI (Rect r, GUIStyle background) { + preview.Initialize(this.Repaint, ThisSkeletonDataAsset); + preview.HandleInteractivePreviewGUI(r, background); + } + + public override GUIContent GetPreviewTitle () { return SpineInspectorUtility.TempContent("Preview"); } + public override void OnPreviewSettings () { preview.HandleDrawSettings(); } + public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) { return preview.GetStaticPreview(width, height); } + #endregion + } + +} diff --git a/Assets/Spine/spine-unity/Editor/AnimationReferenceAssetEditor.cs.meta b/Assets/Spine/spine-unity/Editor/AnimationReferenceAssetEditor.cs.meta new file mode 100644 index 0000000..58c7aaa --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/AnimationReferenceAssetEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9511532e80feed24881a5863f5485446 +timeCreated: 1523316585 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs b/Assets/Spine/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs new file mode 100644 index 0000000..ec76a4a --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; + +namespace Spine.Unity.Editor { + public static class AssetDatabaseAvailabilityDetector { + const string MARKER_RESOURCE_NAME = "SpineAssetDatabaseMarker"; + private static bool _isMarkerLoaded; + + public static bool IsAssetDatabaseAvailable (bool forceCheck = false) { + if (!forceCheck && _isMarkerLoaded) + return true; + + TextAsset markerTextAsset = Resources.Load(MARKER_RESOURCE_NAME); + _isMarkerLoaded = markerTextAsset != null; + if (markerTextAsset != null) { + Resources.UnloadAsset(markerTextAsset); + } + + return _isMarkerLoaded; + } + } +} diff --git a/Assets/Spine/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta b/Assets/Spine/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta new file mode 100644 index 0000000..00b99c1 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/AssetDatabaseAvailabilityDetector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 25086cd81e3158b439761b73d7366c47 +timeCreated: 1444587791 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/AtlasAssetInspector.cs b/Assets/Spine/spine-unity/Editor/AtlasAssetInspector.cs new file mode 100644 index 0000000..7b73493 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/AtlasAssetInspector.cs @@ -0,0 +1,366 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +//#define BAKE_ALL_BUTTON +//#define REGION_BAKING_MESH + +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using Spine; + +namespace Spine.Unity.Editor { + using Event = UnityEngine.Event; + + [CustomEditor(typeof(AtlasAsset)), CanEditMultipleObjects] + public class AtlasAssetInspector : UnityEditor.Editor { + SerializedProperty atlasFile, materials; + AtlasAsset atlasAsset; + + GUIContent spriteSlicesLabel; + GUIContent SpriteSlicesLabel { + get { + if (spriteSlicesLabel == null) { + spriteSlicesLabel = new GUIContent( + "Apply Regions as Texture Sprite Slices", + SpineEditorUtilities.Icons.unity, + "Adds Sprite slices to atlas texture(s). " + + "Updates existing slices if ones with matching names exist. \n\n" + + "If your atlas was exported with Premultiply Alpha, " + + "your SpriteRenderer should use the generated Spine _Material asset (or any Material with a PMA shader) instead of Sprites-Default."); + } + return spriteSlicesLabel; + } + } + + static List GetRegions (Atlas atlas) { + FieldInfo regionsField = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.NonPublic); + return (List)regionsField.GetValue(atlas); + } + + void OnEnable () { + SpineEditorUtilities.ConfirmInitialization(); + atlasFile = serializedObject.FindProperty("atlasFile"); + materials = serializedObject.FindProperty("materials"); + materials.isExpanded = true; + atlasAsset = (AtlasAsset)target; + #if REGION_BAKING_MESH + UpdateBakedList(); + #endif + } + + #if REGION_BAKING_MESH + private List baked; + private List bakedObjects; + + void UpdateBakedList () { + AtlasAsset asset = (AtlasAsset)target; + baked = new List(); + bakedObjects = new List(); + if (atlasFile.objectReferenceValue != null) { + List regions = this.Regions; + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + for (int i = 0; i < regions.Count; i++) { + AtlasRegion region = regions[i]; + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(region) + ".prefab").Replace("\\", "/"); + GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); + baked.Add(prefab != null); + bakedObjects.Add(prefab); + } + } + } + #endif + + override public void OnInspectorGUI () { + if (serializedObject.isEditingMultipleObjects) { + DrawDefaultInspector(); + return; + } + + serializedObject.Update(); + atlasAsset = atlasAsset ?? (AtlasAsset)target; + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(atlasFile); + EditorGUILayout.PropertyField(materials, true); + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + atlasAsset.Clear(); + atlasAsset.GetAtlas(); + } + + if (materials.arraySize == 0) { + EditorGUILayout.HelpBox("No materials", MessageType.Error); + return; + } + + for (int i = 0; i < materials.arraySize; i++) { + SerializedProperty prop = materials.GetArrayElementAtIndex(i); + var material = (Material)prop.objectReferenceValue; + if (material == null) { + EditorGUILayout.HelpBox("Materials cannot be null.", MessageType.Error); + return; + } + } + + EditorGUILayout.Space(); + if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Set Mipmap Bias to " + SpineEditorUtilities.DEFAULT_MIPMAPBIAS))) { + foreach (var m in atlasAsset.materials) { + var texture = m.mainTexture; + texture.mipMapBias = SpineEditorUtilities.DEFAULT_MIPMAPBIAS; + } + Debug.Log("Texture mipmap bias set to " + SpineEditorUtilities.DEFAULT_MIPMAPBIAS); + } + + EditorGUILayout.Space(); + if (atlasFile.objectReferenceValue != null) { + if (SpineInspectorUtility.LargeCenteredButton(SpriteSlicesLabel)) { + var atlas = atlasAsset.GetAtlas(); + foreach (var m in atlasAsset.materials) + UpdateSpriteSlices(m.mainTexture, atlas); + } + } + + EditorGUILayout.Space(); + + #if REGION_BAKING_MESH + if (atlasFile.objectReferenceValue != null) { + Atlas atlas = asset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + EditorGUILayout.LabelField(new GUIContent("Region Baking", SpineEditorUtilities.Icons.unityIcon)); + EditorGUI.indentLevel++; + AtlasPage lastPage = null; + for (int i = 0; i < regions.Count; i++) { + if (lastPage != regions[i].page) { + if (lastPage != null) { + EditorGUILayout.Separator(); + EditorGUILayout.Separator(); + } + lastPage = regions[i].page; + Material mat = ((Material)lastPage.rendererObject); + if (mat != null) { + GUILayout.BeginHorizontal(); + { + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250)); + EditorGUI.EndDisabledGroup(); + } + GUILayout.EndHorizontal(); + + } else { + EditorGUILayout.LabelField(new GUIContent("Page missing material!", SpineEditorUtilities.Icons.warning)); + } + } + GUILayout.BeginHorizontal(); + { + //EditorGUILayout.ToggleLeft(baked[i] ? "" : regions[i].name, baked[i]); + bool result = baked[i] ? EditorGUILayout.ToggleLeft("", baked[i], GUILayout.Width(24)) : EditorGUILayout.ToggleLeft(" " + regions[i].name, baked[i]); + if(baked[i]){ + EditorGUILayout.ObjectField(bakedObjects[i], typeof(GameObject), false, GUILayout.Width(250)); + } + if (result && !baked[i]) { + //bake + baked[i] = true; + bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); + EditorGUIUtility.PingObject(bakedObjects[i]); + } else if (!result && baked[i]) { + //unbake + bool unbakeResult = EditorUtility.DisplayDialog("Delete Baked Region", "Do you want to delete the prefab for " + regions[i].name, "Yes", "Cancel"); + switch (unbakeResult) { + case true: + //delete + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/"); + AssetDatabase.DeleteAsset(bakedPrefabPath); + baked[i] = false; + break; + case false: + //do nothing + break; + } + } + } + GUILayout.EndHorizontal(); + } + EditorGUI.indentLevel--; + + #if BAKE_ALL_BUTTON + // Check state + bool allBaked = true; + bool allUnbaked = true; + for (int i = 0; i < regions.Count; i++) { + allBaked &= baked[i]; + allUnbaked &= !baked[i]; + } + + if (!allBaked && GUILayout.Button("Bake All")) { + for (int i = 0; i < regions.Count; i++) { + if (!baked[i]) { + baked[i] = true; + bakedObjects[i] = SpineEditorUtilities.BakeRegion(atlasAsset, regions[i]); + } + } + + } else if (!allUnbaked && GUILayout.Button("Unbake All")) { + bool unbakeResult = EditorUtility.DisplayDialog("Delete All Baked Regions", "Are you sure you want to unbake all region prefabs? This cannot be undone.", "Yes", "Cancel"); + switch (unbakeResult) { + case true: + //delete + for (int i = 0; i < regions.Count; i++) { + if (baked[i]) { + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeRegionName(regions[i]) + ".prefab").Replace("\\", "/"); + AssetDatabase.DeleteAsset(bakedPrefabPath); + baked[i] = false; + } + } + break; + case false: + //do nothing + break; + } + + } + #endif + + } + #else + if (atlasFile.objectReferenceValue != null) { + EditorGUILayout.LabelField("Atlas Regions", EditorStyles.boldLabel); + int baseIndent = EditorGUI.indentLevel; + + var regions = AtlasAssetInspector.GetRegions(atlasAsset.GetAtlas()); + AtlasPage lastPage = null; + for (int i = 0; i < regions.Count; i++) { + if (lastPage != regions[i].page) { + if (lastPage != null) { + EditorGUILayout.Separator(); + EditorGUILayout.Separator(); + } + lastPage = regions[i].page; + Material mat = ((Material)lastPage.rendererObject); + if (mat != null) { + EditorGUI.indentLevel = baseIndent; + using (new GUILayout.HorizontalScope()) + using (new EditorGUI.DisabledGroupScope(true)) + EditorGUILayout.ObjectField(mat, typeof(Material), false, GUILayout.Width(250)); + EditorGUI.indentLevel = baseIndent + 1; + } else { + EditorGUILayout.HelpBox("Page missing material!", MessageType.Warning); + } + } + + EditorGUILayout.LabelField(new GUIContent(regions[i].name, SpineEditorUtilities.Icons.image)); + } + EditorGUI.indentLevel = baseIndent; + } + #endif + + if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) + atlasAsset.Clear(); + } + + static public void UpdateSpriteSlices (Texture texture, Atlas atlas) { + string texturePath = AssetDatabase.GetAssetPath(texture.GetInstanceID()); + var t = (TextureImporter)TextureImporter.GetAtPath(texturePath); + t.spriteImportMode = SpriteImportMode.Multiple; + var spriteSheet = t.spritesheet; + var sprites = new List(spriteSheet); + + var regions = AtlasAssetInspector.GetRegions(atlas); + char[] FilenameDelimiter = {'.'}; + int updatedCount = 0; + int addedCount = 0; + + foreach (var r in regions) { + string pageName = r.page.name.Split(FilenameDelimiter, StringSplitOptions.RemoveEmptyEntries)[0]; + string textureName = texture.name; + bool pageMatch = string.Equals(pageName, textureName, StringComparison.Ordinal); + +// if (pageMatch) { +// int pw = r.page.width; +// int ph = r.page.height; +// bool mismatchSize = pw != texture.width || pw > t.maxTextureSize || ph != texture.height || ph > t.maxTextureSize; +// if (mismatchSize) +// Debug.LogWarningFormat("Size mismatch found.\nExpected atlas size is {0}x{1}. Texture Import Max Size of texture '{2}'({4}x{5}) is currently set to {3}.", pw, ph, texture.name, t.maxTextureSize, texture.width, texture.height); +// } + + int spriteIndex = pageMatch ? sprites.FindIndex( + (s) => string.Equals(s.name, r.name, StringComparison.Ordinal) + ) : -1; + bool spriteNameMatchExists = spriteIndex >= 0; + + if (pageMatch) { + Rect spriteRect = new Rect(); + + if (r.rotate) { + spriteRect.width = r.height; + spriteRect.height = r.width; + } else { + spriteRect.width = r.width; + spriteRect.height = r.height; + } + spriteRect.x = r.x; + spriteRect.y = r.page.height - spriteRect.height - r.y; + + if (spriteNameMatchExists) { + var s = sprites[spriteIndex]; + s.rect = spriteRect; + sprites[spriteIndex] = s; + updatedCount++; + } else { + sprites.Add(new SpriteMetaData { + name = r.name, + pivot = new Vector2(0.5f, 0.5f), + rect = spriteRect + }); + addedCount++; + } + } + + } + + t.spritesheet = sprites.ToArray(); + EditorUtility.SetDirty(t); + AssetDatabase.ImportAsset(texturePath, ImportAssetOptions.ForceUpdate); + EditorGUIUtility.PingObject(texture); + Debug.Log(string.Format("Applied sprite slices to {2}. {0} added. {1} updated.", addedCount, updatedCount, texture.name)); + } + } + +} diff --git a/Assets/Spine/spine-unity/Editor/AtlasAssetInspector.cs.meta b/Assets/Spine/spine-unity/Editor/AtlasAssetInspector.cs.meta new file mode 100644 index 0000000..2f5962a --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/AtlasAssetInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ca9b3ce36d70a05408e3bdd5e92c7f64 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/BoneFollowerInspector.cs b/Assets/Spine/spine-unity/Editor/BoneFollowerInspector.cs new file mode 100644 index 0000000..f1cb32f --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/BoneFollowerInspector.cs @@ -0,0 +1,208 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEditor; +using UnityEngine; + +namespace Spine.Unity.Editor { + + using Editor = UnityEditor.Editor; + using Event = UnityEngine.Event; + + [CustomEditor(typeof(BoneFollower)), CanEditMultipleObjects] + public class BoneFollowerInspector : Editor { + SerializedProperty boneName, skeletonRenderer, followZPosition, followBoneRotation, followLocalScale, followSkeletonFlip; + BoneFollower targetBoneFollower; + bool needsReset; + + #region Context Menu Item + [MenuItem ("CONTEXT/SkeletonRenderer/Add BoneFollower GameObject")] + static void AddBoneFollowerGameObject (MenuCommand cmd) { + var skeletonRenderer = cmd.context as SkeletonRenderer; + var go = new GameObject("BoneFollower"); + var t = go.transform; + t.SetParent(skeletonRenderer.transform); + t.localPosition = Vector3.zero; + + var f = go.AddComponent(); + f.skeletonRenderer = skeletonRenderer; + + EditorGUIUtility.PingObject(t); + + Undo.RegisterCreatedObjectUndo(go, "Add BoneFollower"); + } + + // Validate + [MenuItem ("CONTEXT/SkeletonRenderer/Add BoneFollower GameObject", true)] + static bool ValidateAddBoneFollowerGameObject (MenuCommand cmd) { + var skeletonRenderer = cmd.context as SkeletonRenderer; + return skeletonRenderer.valid; + } + #endregion + + void OnEnable () { + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); + boneName = serializedObject.FindProperty("boneName"); + followBoneRotation = serializedObject.FindProperty("followBoneRotation"); + followZPosition = serializedObject.FindProperty("followZPosition"); + followLocalScale = serializedObject.FindProperty("followLocalScale"); + followSkeletonFlip = serializedObject.FindProperty("followSkeletonFlip"); + + targetBoneFollower = (BoneFollower)target; + if (targetBoneFollower.SkeletonRenderer != null) + targetBoneFollower.SkeletonRenderer.Initialize(false); + + if (!targetBoneFollower.valid || needsReset) { + targetBoneFollower.Initialize(); + targetBoneFollower.LateUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + } + + public void OnSceneGUI () { + var tbf = target as BoneFollower; + var skeletonRendererComponent = tbf.skeletonRenderer; + if (skeletonRendererComponent == null) return; + + var transform = skeletonRendererComponent.transform; + var skeleton = skeletonRendererComponent.skeleton; + + if (string.IsNullOrEmpty(boneName.stringValue)) { + SpineHandles.DrawBones(transform, skeleton); + SpineHandles.DrawBoneNames(transform, skeleton); + Handles.Label(tbf.transform.position, "No bone selected", EditorStyles.helpBox); + } else { + var targetBone = tbf.bone; + if (targetBone == null) return; + SpineHandles.DrawBoneWireframe(transform, targetBone, SpineHandles.TransformContraintColor); + Handles.Label(targetBone.GetWorldPosition(transform), targetBone.Data.Name, SpineHandles.BoneNameStyle); + } + } + + override public void OnInspectorGUI () { + if (serializedObject.isEditingMultipleObjects) { + if (needsReset) { + needsReset = false; + foreach (var o in targets) { + var bf = (BoneFollower)o; + bf.Initialize(); + bf.LateUpdate(); + } + SceneView.RepaintAll(); + } + + EditorGUI.BeginChangeCheck(); + DrawDefaultInspector(); + needsReset |= EditorGUI.EndChangeCheck(); + return; + } + + if (needsReset && Event.current.type == EventType.Layout) { + targetBoneFollower.Initialize(); + targetBoneFollower.LateUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + serializedObject.Update(); + + // Find Renderer + if (skeletonRenderer.objectReferenceValue == null) { + SkeletonRenderer parentRenderer = targetBoneFollower.GetComponentInParent(); + if (parentRenderer != null && parentRenderer.gameObject != targetBoneFollower.gameObject) { + skeletonRenderer.objectReferenceValue = parentRenderer; + Debug.Log("Inspector automatically assigned BoneFollower.SkeletonRenderer"); + } + } + + EditorGUILayout.PropertyField(skeletonRenderer); + var skeletonRendererReference = skeletonRenderer.objectReferenceValue as SkeletonRenderer; + if (skeletonRendererReference != null) { + if (skeletonRendererReference.gameObject == targetBoneFollower.gameObject) { + skeletonRenderer.objectReferenceValue = null; + EditorUtility.DisplayDialog("Invalid assignment.", "BoneFollower can only follow a skeleton on a separate GameObject.\n\nCreate a new GameObject for your BoneFollower, or choose a SkeletonRenderer from a different GameObject.", "Ok"); + } + } + + if (!targetBoneFollower.valid) { + needsReset = true; + } + + if (targetBoneFollower.valid) { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(boneName); + needsReset |= EditorGUI.EndChangeCheck(); + + EditorGUILayout.PropertyField(followBoneRotation); + EditorGUILayout.PropertyField(followZPosition); + EditorGUILayout.PropertyField(followLocalScale); + EditorGUILayout.PropertyField(followSkeletonFlip); + + BoneFollowerInspector.RecommendRigidbodyButton(targetBoneFollower); + } else { + var boneFollowerSkeletonRenderer = targetBoneFollower.skeletonRenderer; + if (boneFollowerSkeletonRenderer == null) { + EditorGUILayout.HelpBox("SkeletonRenderer is unassigned. Please assign a SkeletonRenderer (SkeletonAnimation or SkeletonAnimator).", MessageType.Warning); + } else { + boneFollowerSkeletonRenderer.Initialize(false); + + if (boneFollowerSkeletonRenderer.skeletonDataAsset == null) + EditorGUILayout.HelpBox("Assigned SkeletonRenderer does not have SkeletonData assigned to it.", MessageType.Warning); + + if (!boneFollowerSkeletonRenderer.valid) + EditorGUILayout.HelpBox("Assigned SkeletonRenderer is invalid. Check target SkeletonRenderer, its SkeletonDataAsset or the console for other errors.", MessageType.Warning); + } + } + + var current = Event.current; + bool wasUndo = (current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed"); + if (wasUndo) + targetBoneFollower.Initialize(); + + serializedObject.ApplyModifiedProperties(); + } + + internal static void RecommendRigidbodyButton (Component component) { + bool hasCollider2D = component.GetComponent() != null || component.GetComponent() != null; + bool hasCollider3D = !hasCollider2D && component.GetComponent(); + bool missingRigidBody = (hasCollider2D && component.GetComponent() == null) || (hasCollider3D && component.GetComponent() == null); + if (missingRigidBody) { + using (new SpineInspectorUtility.BoxScope()) { + EditorGUILayout.HelpBox("Collider detected. Unity recommends adding a Rigidbody to the parent Transforms of any colliders that are intended to be dynamically repositioned and rotated.", MessageType.Warning); + var rbType = hasCollider2D ? typeof(Rigidbody2D) : typeof(Rigidbody); + string rbLabel = string.Format("Add {0}", rbType.Name); + var rbContent = SpineInspectorUtility.TempContent(rbLabel, SpineInspectorUtility.UnityIcon(rbType), "Add a rigidbody to this GameObject to be the Physics body parent of the attached collider."); + if (SpineInspectorUtility.CenteredButton(rbContent)) component.gameObject.AddComponent(rbType); + } + } + } + } + +} diff --git a/Assets/Spine/spine-unity/Editor/BoneFollowerInspector.cs.meta b/Assets/Spine/spine-unity/Editor/BoneFollowerInspector.cs.meta new file mode 100644 index 0000000..0448ccb --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/BoneFollowerInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c71ca35fd6241cb49a0b0756a664fcf7 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI.meta b/Assets/Spine/spine-unity/Editor/GUI.meta new file mode 100644 index 0000000..d730a55 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: bfaea6b7e7f52bc46b8d1c3cb5e9eaa1 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Editor/GUI/AtlasAsset Icon.png b/Assets/Spine/spine-unity/Editor/GUI/AtlasAsset Icon.png new file mode 100644 index 0000000..4792130 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/AtlasAsset Icon.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/AtlasAsset Icon.png.meta b/Assets/Spine/spine-unity/Editor/GUI/AtlasAsset Icon.png.meta new file mode 100644 index 0000000..ef7eb69 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/AtlasAsset Icon.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 3fc714a0dc1cf6b4b959e073fff2844e +timeCreated: 1508165143 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/SkeletonDataAsset Icon.png b/Assets/Spine/spine-unity/Editor/GUI/SkeletonDataAsset Icon.png new file mode 100644 index 0000000..61c0f18 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/SkeletonDataAsset Icon.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/SkeletonDataAsset Icon.png.meta b/Assets/Spine/spine-unity/Editor/GUI/SkeletonDataAsset Icon.png.meta new file mode 100644 index 0000000..22239ca --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/SkeletonDataAsset Icon.png.meta @@ -0,0 +1,46 @@ +fileFormatVersion: 2 +guid: 68defdbc95b30a74a9ad396bfc9a2277 +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-animation.png b/Assets/Spine/spine-unity/Editor/GUI/icon-animation.png new file mode 100644 index 0000000..f44d38e Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-animation.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-animation.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-animation.png.meta new file mode 100644 index 0000000..dcf6806 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-animation.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 52b12ec801461494185a4d3dc66f3d1d +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-animationRoot.png b/Assets/Spine/spine-unity/Editor/GUI/icon-animationRoot.png new file mode 100644 index 0000000..6107546 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-animationRoot.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-animationRoot.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-animationRoot.png.meta new file mode 100644 index 0000000..1bbca1c --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-animationRoot.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 3d1be4ea889f3a14b864352fe49a1bde +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-attachment.png b/Assets/Spine/spine-unity/Editor/GUI/icon-attachment.png new file mode 100644 index 0000000..4e14f98 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-attachment.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-attachment.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-attachment.png.meta new file mode 100644 index 0000000..1316c3d --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-attachment.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 04ae56b3698d3e844844cfcef2f009e7 +timeCreated: 1494928093 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-bone.png b/Assets/Spine/spine-unity/Editor/GUI/icon-bone.png new file mode 100644 index 0000000..ec66a6a Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-bone.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-bone.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-bone.png.meta new file mode 100644 index 0000000..f0cbaea --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-bone.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 8322793223a533a4ca8be6f430256dfc +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-boneNib.png b/Assets/Spine/spine-unity/Editor/GUI/icon-boneNib.png new file mode 100644 index 0000000..87373b8 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-boneNib.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-boneNib.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-boneNib.png.meta new file mode 100644 index 0000000..a0b91be --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-boneNib.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 97a43f11e00735147a9dc3dff6d68191 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-boundingBox.png b/Assets/Spine/spine-unity/Editor/GUI/icon-boundingBox.png new file mode 100644 index 0000000..82beedf Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-boundingBox.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-boundingBox.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-boundingBox.png.meta new file mode 100644 index 0000000..0e5d628 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-boundingBox.png.meta @@ -0,0 +1,47 @@ +fileFormatVersion: 2 +guid: 955aed20030d0504b8a9c6934a5cb47a +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-clipping.png b/Assets/Spine/spine-unity/Editor/GUI/icon-clipping.png new file mode 100644 index 0000000..398137c Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-clipping.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-clipping.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-clipping.png.meta new file mode 100644 index 0000000..9cb5d0c --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-clipping.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: f5fff1b5caee03642ab77c9984b4bb6a +timeCreated: 1497479335 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintIK.png b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintIK.png new file mode 100644 index 0000000..1c93d75 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintIK.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintIK.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintIK.png.meta new file mode 100644 index 0000000..1e11b1d --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintIK.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 02822eb69e09dd947b434ab81e3d938f +timeCreated: 1494878353 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintNib.png b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintNib.png new file mode 100644 index 0000000..175bcb0 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintNib.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintNib.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintNib.png.meta new file mode 100644 index 0000000..a692728 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintNib.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: de1a4f5ad4bdf1a4ea072c4d59ba87d8 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintPath.png b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintPath.png new file mode 100644 index 0000000..9663b85 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintPath.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintPath.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintPath.png.meta new file mode 100644 index 0000000..4459669 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintPath.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: c1aae98dd56b14c4b8c25360000b7e9e +timeCreated: 1494878353 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintTransform.png b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintTransform.png new file mode 100644 index 0000000..2264aaa Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintTransform.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraintTransform.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintTransform.png.meta new file mode 100644 index 0000000..181bb92 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-constraintTransform.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: 4709175437c21f64bab9b061f98a49fc +timeCreated: 1494878353 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraints.png b/Assets/Spine/spine-unity/Editor/GUI/icon-constraints.png new file mode 100644 index 0000000..9c61ee5 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-constraints.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-constraints.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-constraints.png.meta new file mode 100644 index 0000000..306364d --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-constraints.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: ed0736a1eb519ef42b4892d1db2426b3 +timeCreated: 1494878353 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-event.png b/Assets/Spine/spine-unity/Editor/GUI/icon-event.png new file mode 100644 index 0000000..4547f25 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-event.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-event.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-event.png.meta new file mode 100644 index 0000000..0bf19db --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-event.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: d226a80acc775714aa78b85e16a00e9b +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-hingeChain.png b/Assets/Spine/spine-unity/Editor/GUI/icon-hingeChain.png new file mode 100644 index 0000000..a27af1c Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-hingeChain.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-hingeChain.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-hingeChain.png.meta new file mode 100644 index 0000000..e61da1e --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-hingeChain.png.meta @@ -0,0 +1,47 @@ +fileFormatVersion: 2 +guid: 2c2c6d283dcf3654baf40001c982891c +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-image.png b/Assets/Spine/spine-unity/Editor/GUI/icon-image.png new file mode 100644 index 0000000..663d113 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-image.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-image.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-image.png.meta new file mode 100644 index 0000000..421c654 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-image.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 2b3a6f35bbaa8414eb51a344743ee641 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-mesh.png b/Assets/Spine/spine-unity/Editor/GUI/icon-mesh.png new file mode 100644 index 0000000..1035553 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-mesh.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-mesh.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-mesh.png.meta new file mode 100644 index 0000000..2b49d8b --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-mesh.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: a309a2e14638a204091b915126910f45 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-null.png b/Assets/Spine/spine-unity/Editor/GUI/icon-null.png new file mode 100644 index 0000000..9a6d738 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-null.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-null.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-null.png.meta new file mode 100644 index 0000000..29da564 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-null.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: d1de1604dfe4cb64c9d31246a8e43c78 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-path.png b/Assets/Spine/spine-unity/Editor/GUI/icon-path.png new file mode 100644 index 0000000..604ae0d Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-path.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-path.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-path.png.meta new file mode 100644 index 0000000..8aebfb7 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-path.png.meta @@ -0,0 +1,59 @@ +fileFormatVersion: 2 +guid: dbc817a6c9e9c5747b7f6261bf5d1d09 +timeCreated: 1482240904 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 7 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-point.png b/Assets/Spine/spine-unity/Editor/GUI/icon-point.png new file mode 100644 index 0000000..0e5099f Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-point.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-point.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-point.png.meta new file mode 100644 index 0000000..9df5c2a --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-point.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: d7a76922e4dd9fa429da15c018ff127f +timeCreated: 1524196821 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + 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: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-poseBones.png b/Assets/Spine/spine-unity/Editor/GUI/icon-poseBones.png new file mode 100644 index 0000000..102700a Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-poseBones.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-poseBones.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-poseBones.png.meta new file mode 100644 index 0000000..800466b --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-poseBones.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: da6f6d414e43aac46a57cc5a87208db4 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skeleton.png b/Assets/Spine/spine-unity/Editor/GUI/icon-skeleton.png new file mode 100644 index 0000000..b5d5adf Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-skeleton.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skeleton.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-skeleton.png.meta new file mode 100644 index 0000000..9c2a1a7 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-skeleton.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: f2216037084d99d4481810cb521ed96f +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skeletonUtility.png b/Assets/Spine/spine-unity/Editor/GUI/icon-skeletonUtility.png new file mode 100644 index 0000000..ce4d937 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-skeletonUtility.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skeletonUtility.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-skeletonUtility.png.meta new file mode 100644 index 0000000..3a77fd7 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-skeletonUtility.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 5bb0631368b462047869d8788673cb48 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skin.png b/Assets/Spine/spine-unity/Editor/GUI/icon-skin.png new file mode 100644 index 0000000..4dc8e7f Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-skin.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skin.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-skin.png.meta new file mode 100644 index 0000000..f02b09c --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-skin.png.meta @@ -0,0 +1,92 @@ +fileFormatVersion: 2 +guid: bfd9f3d2607e9e44c97384d7575a17dc +timeCreated: 1494878353 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 1 + fadeOut: 0 + borderMipMap: 0 + 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: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 1024 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skinPlaceholder.png b/Assets/Spine/spine-unity/Editor/GUI/icon-skinPlaceholder.png new file mode 100644 index 0000000..17bfef0 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-skinPlaceholder.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skinPlaceholder.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-skinPlaceholder.png.meta new file mode 100644 index 0000000..92955a9 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-skinPlaceholder.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 04c82a4acf7b5244e947f2709ec3a6cf +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skinsRoot.png b/Assets/Spine/spine-unity/Editor/GUI/icon-skinsRoot.png new file mode 100644 index 0000000..31857cf Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-skinsRoot.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-skinsRoot.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-skinsRoot.png.meta new file mode 100644 index 0000000..b8ce214 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-skinsRoot.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 8bd14c7643597a74ba2edc10a5e4c4ed +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-slot.png b/Assets/Spine/spine-unity/Editor/GUI/icon-slot.png new file mode 100644 index 0000000..ca93872 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-slot.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-slot.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-slot.png.meta new file mode 100644 index 0000000..66d52b0 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-slot.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 0338faf3e7d93e2478fcbc022d13e081 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-slotRoot.png b/Assets/Spine/spine-unity/Editor/GUI/icon-slotRoot.png new file mode 100644 index 0000000..dfca413 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-slotRoot.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-slotRoot.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-slotRoot.png.meta new file mode 100644 index 0000000..3d9fdd7 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-slotRoot.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 4a1646cf39026224c85ecba92d7d6948 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-spine.png b/Assets/Spine/spine-unity/Editor/GUI/icon-spine.png new file mode 100644 index 0000000..7fd5473 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-spine.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-spine.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-spine.png.meta new file mode 100644 index 0000000..811e17a --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-spine.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 4e7c964fa5e07024c8bf1debecc3b7c8 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-subMeshRenderer.png b/Assets/Spine/spine-unity/Editor/GUI/icon-subMeshRenderer.png new file mode 100644 index 0000000..ec7c9e6 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-subMeshRenderer.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta new file mode 100644 index 0000000..fa37928 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-subMeshRenderer.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: f31c0c0d608e8ba4f9a1afb032092287 +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-warning.png b/Assets/Spine/spine-unity/Editor/GUI/icon-warning.png new file mode 100644 index 0000000..05e3f4c Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-warning.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-warning.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-warning.png.meta new file mode 100644 index 0000000..87c616f --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-warning.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 754d724c1bd750048852e8cf3d4a05ee +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-weights.png b/Assets/Spine/spine-unity/Editor/GUI/icon-weights.png new file mode 100644 index 0000000..fa8f190 Binary files /dev/null and b/Assets/Spine/spine-unity/Editor/GUI/icon-weights.png differ diff --git a/Assets/Spine/spine-unity/Editor/GUI/icon-weights.png.meta b/Assets/Spine/spine-unity/Editor/GUI/icon-weights.png.meta new file mode 100644 index 0000000..e25807b --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/GUI/icon-weights.png.meta @@ -0,0 +1,53 @@ +fileFormatVersion: 2 +guid: 0b1bcb09fa228d049ba3c9ea6a57e1ee +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 1 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 8 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: .5, y: .5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/Menus.cs b/Assets/Spine/spine-unity/Editor/Menus.cs new file mode 100644 index 0000000..64cfa4f --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/Menus.cs @@ -0,0 +1,84 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System; +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace Spine.Unity.Editor { + public static class Menus { + [MenuItem("Assets/Create/Spine/Atlas Asset")] + static public void CreateAtlas () { + CreateAsset("New Atlas"); + } + + [MenuItem("Assets/Create/Spine/SkeletonData Asset")] + static public void CreateSkeletonData () { + CreateAsset("New SkeletonData"); + } + + static void CreateAsset (String name) where T : ScriptableObject { + var dir = "Assets/"; + var selected = Selection.activeObject; + if (selected != null) { + var assetDir = AssetDatabase.GetAssetPath(selected.GetInstanceID()); + if (assetDir.Length > 0 && Directory.Exists(assetDir)) + dir = assetDir + "/"; + } + ScriptableObject asset = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(asset, dir + name + ".asset"); + AssetDatabase.SaveAssets(); + EditorUtility.FocusProjectWindow(); + Selection.activeObject = asset; + } + + [MenuItem("GameObject/Spine/SkeletonRenderer", false, 10)] + static public void CreateSkeletonRendererGameObject () { + CreateSpineGameObject("New SkeletonRenderer"); + } + + [MenuItem("GameObject/Spine/SkeletonAnimation", false, 10)] + static public void CreateSkeletonAnimationGameObject () { + CreateSpineGameObject("New SkeletonAnimation"); + } + + static void CreateSpineGameObject (string name) where T : MonoBehaviour { + var parentGameObject = Selection.activeObject as GameObject; + var parentTransform = parentGameObject == null ? null : parentGameObject.transform; + + var gameObject = new GameObject(name, typeof(T)); + gameObject.transform.SetParent(parentTransform, false); + EditorUtility.FocusProjectWindow(); + Selection.activeObject = gameObject; + EditorGUIUtility.PingObject(Selection.activeObject); + } + } +} diff --git a/Assets/Spine/spine-unity/Editor/Menus.cs.meta b/Assets/Spine/spine-unity/Editor/Menus.cs.meta new file mode 100644 index 0000000..d33433f --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/Menus.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: cf21125cbd8928844a85a3ad9002693b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/PointFollowerEditor.cs b/Assets/Spine/spine-unity/Editor/PointFollowerEditor.cs new file mode 100644 index 0000000..4baf305 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/PointFollowerEditor.cs @@ -0,0 +1,188 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEditor; +using UnityEngine; + +namespace Spine.Unity.Editor { + + using Editor = UnityEditor.Editor; + using Event = UnityEngine.Event; + + [CustomEditor(typeof(PointFollower)), CanEditMultipleObjects] + public class PointFollowerEditor : Editor { + SerializedProperty slotName, pointAttachmentName, skeletonRenderer, followZPosition, followBoneRotation, followSkeletonFlip; + PointFollower targetPointFollower; + bool needsReset; + + #region Context Menu Item + [MenuItem("CONTEXT/SkeletonRenderer/Add PointFollower GameObject")] + static void AddBoneFollowerGameObject (MenuCommand cmd) { + var skeletonRenderer = cmd.context as SkeletonRenderer; + var go = new GameObject("PointFollower"); + var t = go.transform; + t.SetParent(skeletonRenderer.transform); + t.localPosition = Vector3.zero; + + var f = go.AddComponent(); + f.skeletonRenderer = skeletonRenderer; + + EditorGUIUtility.PingObject(t); + + Undo.RegisterCreatedObjectUndo(go, "Add PointFollower"); + } + + // Validate + [MenuItem("CONTEXT/SkeletonRenderer/Add PointFollower GameObject", true)] + static bool ValidateAddBoneFollowerGameObject (MenuCommand cmd) { + var skeletonRenderer = cmd.context as SkeletonRenderer; + return skeletonRenderer.valid; + } + #endregion + + void OnEnable () { + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); + slotName = serializedObject.FindProperty("slotName"); + pointAttachmentName = serializedObject.FindProperty("pointAttachmentName"); + + targetPointFollower = (PointFollower)target; + if (targetPointFollower.skeletonRenderer != null) + targetPointFollower.skeletonRenderer.Initialize(false); + + if (!targetPointFollower.IsValid || needsReset) { + targetPointFollower.Initialize(); + targetPointFollower.LateUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + } + + public void OnSceneGUI () { + var tbf = target as PointFollower; + var skeletonRendererComponent = tbf.skeletonRenderer; + if (skeletonRendererComponent == null) + return; + + var skeleton = skeletonRendererComponent.skeleton; + var skeletonTransform = skeletonRendererComponent.transform; + + if (string.IsNullOrEmpty(pointAttachmentName.stringValue)) { + // Draw all active PointAttachments in the current skin + var currentSkin = skeleton.Skin; + if (currentSkin != skeleton.Data.DefaultSkin) DrawPointsInSkin(skeleton.Data.DefaultSkin, skeleton, skeletonTransform); + if (currentSkin != null) DrawPointsInSkin(currentSkin, skeleton, skeletonTransform); + } else { + int slotIndex = skeleton.FindSlotIndex(slotName.stringValue); + if (slotIndex >= 0) { + var slot = skeleton.Slots.Items[slotIndex]; + var point = skeleton.GetAttachment(slotIndex, pointAttachmentName.stringValue) as PointAttachment; + if (point != null) { + DrawPointAttachmentWithLabel(point, slot.Bone, skeletonTransform); + } + } + } + } + + static void DrawPointsInSkin (Skin skin, Skeleton skeleton, Transform transform) { + foreach (var skinEntry in skin.Attachments) { + var attachment = skinEntry.Value as PointAttachment; + if (attachment != null) { + var skinKey = skinEntry.Key; + var slot = skeleton.Slots.Items[skinKey.slotIndex]; + DrawPointAttachmentWithLabel(attachment, slot.Bone, transform); + } + } + } + + static void DrawPointAttachmentWithLabel (PointAttachment point, Bone bone, Transform transform) { + Vector3 labelOffset = new Vector3(0f, -0.2f, 0f); + SpineHandles.DrawPointAttachment(bone, point, transform); + Handles.Label(labelOffset + point.GetWorldPosition(bone, transform), point.Name, SpineHandles.PointNameStyle); + } + + override public void OnInspectorGUI () { + if (serializedObject.isEditingMultipleObjects) { + if (needsReset) { + needsReset = false; + foreach (var o in targets) { + var bf = (BoneFollower)o; + bf.Initialize(); + bf.LateUpdate(); + } + SceneView.RepaintAll(); + } + + EditorGUI.BeginChangeCheck(); + DrawDefaultInspector(); + needsReset |= EditorGUI.EndChangeCheck(); + return; + } + + if (needsReset && Event.current.type == EventType.Layout) { + targetPointFollower.Initialize(); + targetPointFollower.LateUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + serializedObject.Update(); + + DrawDefaultInspector(); + + // Find Renderer + if (skeletonRenderer.objectReferenceValue == null) { + SkeletonRenderer parentRenderer = targetPointFollower.GetComponentInParent(); + if (parentRenderer != null && parentRenderer.gameObject != targetPointFollower.gameObject) { + skeletonRenderer.objectReferenceValue = parentRenderer; + Debug.Log("Inspector automatically assigned PointFollower.SkeletonRenderer"); + } + } + + var skeletonRendererReference = skeletonRenderer.objectReferenceValue as SkeletonRenderer; + if (skeletonRendererReference != null) { + if (skeletonRendererReference.gameObject == targetPointFollower.gameObject) { + skeletonRenderer.objectReferenceValue = null; + EditorUtility.DisplayDialog("Invalid assignment.", "PointFollower can only follow a skeleton on a separate GameObject.\n\nCreate a new GameObject for your PointFollower, or choose a SkeletonRenderer from a different GameObject.", "Ok"); + } + } + + if (!targetPointFollower.IsValid) { + needsReset = true; + } + + var current = Event.current; + bool wasUndo = (current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed"); + if (wasUndo) + targetPointFollower.Initialize(); + + serializedObject.ApplyModifiedProperties(); + } + } + +} diff --git a/Assets/Spine/spine-unity/Editor/PointFollowerEditor.cs.meta b/Assets/Spine/spine-unity/Editor/PointFollowerEditor.cs.meta new file mode 100644 index 0000000..2106156 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/PointFollowerEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7c7e838a8ec295a4e9c53602f690f42f +timeCreated: 1518163038 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/Resources.meta b/Assets/Spine/spine-unity/Editor/Resources.meta new file mode 100644 index 0000000..8d1a705 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/Resources.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 24903fdac57ee784b9597fcb751ec22f +folderAsset: yes +timeCreated: 1444565388 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt b/Assets/Spine/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt new file mode 100644 index 0000000..1a3c1a3 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt @@ -0,0 +1 @@ +DO NOT MOVE OR DELETE THIS FILE \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt.meta b/Assets/Spine/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt.meta new file mode 100644 index 0000000..7569884 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/Resources/SpineAssetDatabaseMarker.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 57281c00bdd90ad4392f811f2b9f0da1 +timeCreated: 1444565392 +licenseType: Pro +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonAnimationInspector.cs b/Assets/Spine/spine-unity/Editor/SkeletonAnimationInspector.cs new file mode 100644 index 0000000..b5755dc --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -0,0 +1,143 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEditor; +using UnityEngine; +using Spine; + +namespace Spine.Unity.Editor { + + [CustomEditor(typeof(SkeletonAnimation))] + [CanEditMultipleObjects] + public class SkeletonAnimationInspector : SkeletonRendererInspector { + protected SerializedProperty animationName, loop, timeScale, autoReset; + protected bool wasAnimationNameChanged; + protected bool requireRepaint; + readonly GUIContent LoopLabel = new GUIContent("Loop", "Whether or not .AnimationName should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected."); + readonly GUIContent TimeScaleLabel = new GUIContent("Time Scale", "The rate at which animations progress over time. 1 means normal speed. 0.5 means 50% speed."); + + protected override void OnEnable () { + base.OnEnable(); + animationName = serializedObject.FindProperty("_animationName"); + loop = serializedObject.FindProperty("loop"); + timeScale = serializedObject.FindProperty("timeScale"); + } + + protected override void DrawInspectorGUI (bool multi) { + base.DrawInspectorGUI(multi); + if (!TargetIsValid) return; + bool sameData = SpineInspectorUtility.TargetsUseSameData(serializedObject); + + if (multi) { + foreach (var o in targets) + TrySetAnimation(o, multi); + + EditorGUILayout.Space(); + if (!sameData) { + #if UNITY_5_3_OR_NEWER + EditorGUILayout.DelayedTextField(animationName); + #else + animationName.stringValue = EditorGUILayout.TextField(animationName.displayName, animationName.stringValue); + #endif + } else { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(animationName); + wasAnimationNameChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update. + } + EditorGUILayout.PropertyField(loop); + EditorGUILayout.PropertyField(timeScale); + foreach (var o in targets) { + var component = o as SkeletonAnimation; + component.timeScale = Mathf.Max(component.timeScale, 0); + } + } else { + TrySetAnimation(target, multi); + + EditorGUILayout.Space(); + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(animationName); + wasAnimationNameChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update. + EditorGUILayout.PropertyField(loop, LoopLabel); + EditorGUILayout.PropertyField(timeScale, TimeScaleLabel); + var component = (SkeletonAnimation)target; + component.timeScale = Mathf.Max(component.timeScale, 0); + EditorGUILayout.Space(); + } + + if (!isInspectingPrefab) { + if (requireRepaint) { + SceneView.RepaintAll(); + requireRepaint = false; + } + } + } + + protected void TrySetAnimation (Object o, bool multi) { + var skeletonAnimation = o as SkeletonAnimation; + if (skeletonAnimation == null) return; + if (!skeletonAnimation.valid) + return; + + if (!isInspectingPrefab) { + if (wasAnimationNameChanged) { + if (!Application.isPlaying) { + if (skeletonAnimation.state != null) skeletonAnimation.state.ClearTrack(0); + skeletonAnimation.skeleton.SetToSetupPose(); + } + + Spine.Animation animationToUse = skeletonAnimation.skeleton.Data.FindAnimation(animationName.stringValue); + + if (!Application.isPlaying) { + if (animationToUse != null) animationToUse.PoseSkeleton(skeletonAnimation.Skeleton, 0f); + skeletonAnimation.Update(0); + skeletonAnimation.LateUpdate(); + requireRepaint = true; + } else { + if (animationToUse != null) + skeletonAnimation.state.SetAnimation(0, animationToUse, loop.boolValue); + else + skeletonAnimation.state.ClearTrack(0); + } + + wasAnimationNameChanged = false; + } + + // Reflect animationName serialized property in the inspector even if SetAnimation API was used. + if (!multi && Application.isPlaying) { + TrackEntry current = skeletonAnimation.state.GetCurrent(0); + if (current != null) { + if (skeletonAnimation.AnimationName != animationName.stringValue) + animationName.stringValue = current.Animation.Name; + } + } + } + } + } +} diff --git a/Assets/Spine/spine-unity/Editor/SkeletonAnimationInspector.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonAnimationInspector.cs.meta new file mode 100644 index 0000000..1ca7941 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonAnimationInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 39fbfef61034ca045b5aa80088e1e8a4 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonAnimatorInspector.cs b/Assets/Spine/spine-unity/Editor/SkeletonAnimatorInspector.cs new file mode 100644 index 0000000..d33a3b3 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonAnimatorInspector.cs @@ -0,0 +1,51 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEditor; + +namespace Spine.Unity.Editor { + [CustomEditor(typeof(SkeletonAnimator))] + [CanEditMultipleObjects] + public class SkeletonAnimatorInspector : SkeletonRendererInspector { + protected SerializedProperty mecanimTranslator; + + protected override void OnEnable () { + base.OnEnable(); + mecanimTranslator = serializedObject.FindProperty("translator"); + } + + protected override void DrawInspectorGUI (bool multi) { + base.DrawInspectorGUI(multi); + EditorGUILayout.PropertyField(mecanimTranslator, true); + } + } +} diff --git a/Assets/Spine/spine-unity/Editor/SkeletonAnimatorInspector.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonAnimatorInspector.cs.meta new file mode 100644 index 0000000..9ef306e --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonAnimatorInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6a9ca5213a3a4614c9a9f2e60909bc33 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonBaker.cs b/Assets/Spine/spine-unity/Editor/SkeletonBaker.cs new file mode 100644 index 0000000..e2c2a67 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonBaker.cs @@ -0,0 +1,1426 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +#define SPINE_SKELETON_ANIMATOR + +using UnityEngine; +using UnityEditor; +using UnityEditorInternal; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.IO; +using Spine; + +namespace Spine.Unity.Editor { + + /// + /// [SUPPORTS] + /// Linear, Constant, and Bezier Curves* + /// Inverse Kinematics* + /// Inherit Rotation + /// Translate Timeline + /// Rotate Timeline + /// Scale Timeline** + /// Event Timeline*** + /// Attachment Timeline + /// + /// RegionAttachment + /// MeshAttachment (optionally Skinned) + /// + /// [LIMITATIONS] + /// *Bezier Curves are baked into the animation at 60fps and are not realtime. Use bakeIncrement constant to adjust key density if desired. + /// *Inverse Kinematics is baked into the animation at 60fps and are not realtime. Use bakeIncrement constant to adjust key density if desired. + /// ***Events may only fire 1 type of data per event in Unity safely so priority to String data if present in Spine key, otherwise a Float is sent whether the Spine key was Int or Float with priority given to Int. + /// + /// [DOES NOT SUPPORT] + /// FFD (Unity does not provide access to BlendShapes with code) + /// Color Keys (Maybe one day when Unity supports full FBX standard and provides access with code) + /// Draw Order Keyframes + /// + public static class SkeletonBaker { + + #region SkeletonAnimator's Mecanim Clips + #if SPINE_SKELETON_ANIMATOR + public static void GenerateMecanimAnimationClips (SkeletonDataAsset skeletonDataAsset) { + var data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) { + Debug.LogError("SkeletonData loading failed!", skeletonDataAsset); + return; + } + + string dataPath = AssetDatabase.GetAssetPath(skeletonDataAsset); + string controllerPath = dataPath.Replace(SpineEditorUtilities.SkeletonDataSuffix, "_Controller").Replace(".asset", ".controller"); + UnityEditor.Animations.AnimatorController controller; + if (skeletonDataAsset.controller != null) { + controller = (UnityEditor.Animations.AnimatorController)skeletonDataAsset.controller; + controllerPath = AssetDatabase.GetAssetPath(controller); + } else { + if (File.Exists(controllerPath)) { + if (EditorUtility.DisplayDialog("Controller Overwrite Warning", "Unknown Controller already exists at: " + controllerPath, "Update", "Overwrite")) { + controller = (UnityEditor.Animations.AnimatorController)AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController)); + } else { + controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); + } + } else { + controller = (UnityEditor.Animations.AnimatorController)UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); + } + + } + + skeletonDataAsset.controller = controller; + EditorUtility.SetDirty(skeletonDataAsset); + + UnityEngine.Object[] objs = AssetDatabase.LoadAllAssetsAtPath(controllerPath); + + var unityAnimationClipTable = new Dictionary(); + var spineAnimationTable = new Dictionary(); + + foreach (var o in objs) { + //Debug.LogFormat("({0}){1} : {3} + {2} + {4}", o.GetType(), o.name, o.hideFlags, o.GetInstanceID(), o.GetHashCode()); + // There is a bug in Unity 5.3.3 (and likely before) that creates + // a duplicate AnimationClip when you duplicate a Mecanim Animator State. + // These duplicates seem to be identifiable by their HideFlags, so we'll exclude them. + if (o is AnimationClip) { + var clip = o as AnimationClip; + if (!clip.HasFlag(HideFlags.HideInHierarchy)) { + if (unityAnimationClipTable.ContainsKey(clip.name)) { + Debug.LogWarningFormat("Duplicate AnimationClips were found named {0}", clip.name); + } + unityAnimationClipTable.Add(clip.name, clip); + } + } + } + + foreach (var animations in data.Animations) { + string animationName = animations.Name; // Review for unsafe names. Requires runtime implementation too. + spineAnimationTable.Add(animationName, animations); + + if (unityAnimationClipTable.ContainsKey(animationName) == false) { + AnimationClip newClip = new AnimationClip { + name = animationName + }; + //AssetDatabase.CreateAsset(newClip, Path.GetDirectoryName(dataPath) + "/" + animationName + ".asset"); + AssetDatabase.AddObjectToAsset(newClip, controller); + unityAnimationClipTable.Add(animationName, newClip); + } + + AnimationClip clip = unityAnimationClipTable[animationName]; + clip.SetCurve("", typeof(GameObject), "dummy", AnimationCurve.Linear(0, 0, animations.Duration, 0)); + var settings = AnimationUtility.GetAnimationClipSettings(clip); + settings.stopTime = animations.Duration; + SetAnimationSettings(clip, settings); + + AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]); + foreach (Timeline t in animations.Timelines) { + if (t is EventTimeline) + ParseEventTimeline((EventTimeline)t, clip, SendMessageOptions.DontRequireReceiver); + } + + EditorUtility.SetDirty(clip); + unityAnimationClipTable.Remove(animationName); + } + + foreach (var clip in unityAnimationClipTable.Values) { + AnimationClip.DestroyImmediate(clip, true); + } + + AssetDatabase.Refresh(); + AssetDatabase.SaveAssets(); + } + + static bool HasFlag (this UnityEngine.Object o, HideFlags flagToCheck) { + return (o.hideFlags & flagToCheck) == flagToCheck; + } + #endif + #endregion + + #region Prefab and AnimationClip Baking + /// + /// Interval between key sampling for Bezier curves, IK controlled bones, and Inherit Rotation effected bones. + /// + const float BakeIncrement = 1 / 60f; + + public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, ExposedList skins, string outputPath = "", bool bakeAnimations = true, bool bakeIK = true, SendMessageOptions eventOptions = SendMessageOptions.DontRequireReceiver) { + if (skeletonDataAsset == null || skeletonDataAsset.GetSkeletonData(true) == null) { + Debug.LogError("Could not export Spine Skeleton because SkeletonDataAsset is null or invalid!"); + return; + } + + if (outputPath == "") { + outputPath = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(skeletonDataAsset)) + "/Baked"; + System.IO.Directory.CreateDirectory(outputPath); + } + + var skeletonData = skeletonDataAsset.GetSkeletonData(true); + bool hasAnimations = bakeAnimations && skeletonData.Animations.Count > 0; + UnityEditor.Animations.AnimatorController controller = null; + if (hasAnimations) { + string controllerPath = outputPath + "/" + skeletonDataAsset.skeletonJSON.name + " Controller.controller"; + bool newAnimContainer = false; + + var runtimeController = AssetDatabase.LoadAssetAtPath(controllerPath, typeof(RuntimeAnimatorController)); + + if (runtimeController != null) { + controller = (UnityEditor.Animations.AnimatorController)runtimeController; + } else { + controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(controllerPath); + newAnimContainer = true; + } + + var existingClipTable = new Dictionary(); + var unusedClipNames = new List(); + Object[] animObjs = AssetDatabase.LoadAllAssetsAtPath(controllerPath); + + foreach (Object o in animObjs) { + if (o is AnimationClip) { + var clip = (AnimationClip)o; + existingClipTable.Add(clip.name, clip); + unusedClipNames.Add(clip.name); + } + } + + Dictionary> slotLookup = new Dictionary>(); + + int skinCount = skins.Count; + + for (int s = 0; s < skeletonData.Slots.Count; s++) { + List attachmentNames = new List(); + for (int i = 0; i < skinCount; i++) { + var skin = skins.Items[i]; + List temp = new List(); + skin.FindNamesForSlot(s, temp); + foreach (string str in temp) { + if (!attachmentNames.Contains(str)) + attachmentNames.Add(str); + } + } + slotLookup.Add(s, attachmentNames); + } + + foreach (var anim in skeletonData.Animations) { + + AnimationClip clip = null; + if (existingClipTable.ContainsKey(anim.Name)) { + clip = existingClipTable[anim.Name]; + } + + clip = ExtractAnimation(anim.Name, skeletonData, slotLookup, bakeIK, eventOptions, clip); + + if (unusedClipNames.Contains(clip.name)) { + unusedClipNames.Remove(clip.name); + } else { + AssetDatabase.AddObjectToAsset(clip, controller); + controller.AddMotion(clip); + } + } + + if (newAnimContainer) { + EditorUtility.SetDirty(controller); + AssetDatabase.SaveAssets(); + AssetDatabase.ImportAsset(controllerPath, ImportAssetOptions.ForceUpdate); + AssetDatabase.Refresh(); + } else { + + foreach (string str in unusedClipNames) { + AnimationClip.DestroyImmediate(existingClipTable[str], true); + } + + EditorUtility.SetDirty(controller); + AssetDatabase.SaveAssets(); + AssetDatabase.ImportAsset(controllerPath, ImportAssetOptions.ForceUpdate); + AssetDatabase.Refresh(); + } + } + + foreach (var skin in skins) { + bool newPrefab = false; + + string prefabPath = outputPath + "/" + skeletonDataAsset.skeletonJSON.name + " (" + skin.Name + ").prefab"; + + + Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); + if (prefab == null) { + prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); + newPrefab = true; + } + + Dictionary meshTable = new Dictionary(); + List unusedMeshNames = new List(); + Object[] assets = AssetDatabase.LoadAllAssetsAtPath(prefabPath); + foreach (var obj in assets) { + if (obj is Mesh) { + meshTable.Add(obj.name, (Mesh)obj); + unusedMeshNames.Add(obj.name); + } + } + + GameObject prefabRoot = new GameObject("root"); + + Dictionary slotTable = new Dictionary(); + Dictionary boneTable = new Dictionary(); + List boneList = new List(); + + //create bones + for (int i = 0; i < skeletonData.Bones.Count; i++) { + var boneData = skeletonData.Bones.Items[i]; + Transform boneTransform = new GameObject(boneData.Name).transform; + boneTransform.parent = prefabRoot.transform; + boneTable.Add(boneTransform.name, boneTransform); + boneList.Add(boneTransform); + } + + for (int i = 0; i < skeletonData.Bones.Count; i++) { + + var boneData = skeletonData.Bones.Items[i]; + Transform boneTransform = boneTable[boneData.Name]; + Transform parentTransform = null; + if (i > 0) + parentTransform = boneTable[boneData.Parent.Name]; + else + parentTransform = boneTransform.parent; + + boneTransform.parent = parentTransform; + boneTransform.localPosition = new Vector3(boneData.X, boneData.Y, 0); + var tm = boneData.TransformMode; + if (tm.InheritsRotation()) + boneTransform.localRotation = Quaternion.Euler(0, 0, boneData.Rotation); + else + boneTransform.rotation = Quaternion.Euler(0, 0, boneData.Rotation); + + if (tm.InheritsScale()) + boneTransform.localScale = new Vector3(boneData.ScaleX, boneData.ScaleY, 1); + } + + //create slots and attachments + for (int i = 0; i < skeletonData.Slots.Count; i++) { + var slotData = skeletonData.Slots.Items[i]; + Transform slotTransform = new GameObject(slotData.Name).transform; + slotTransform.parent = prefabRoot.transform; + slotTable.Add(slotData.Name, slotTransform); + + List attachments = new List(); + List attachmentNames = new List(); + + skin.FindAttachmentsForSlot(i, attachments); + skin.FindNamesForSlot(i, attachmentNames); + + if (skin != skeletonData.DefaultSkin) { + skeletonData.DefaultSkin.FindAttachmentsForSlot(i, attachments); + skeletonData.DefaultSkin.FindNamesForSlot(i, attachmentNames); + } + + for (int a = 0; a < attachments.Count; a++) { + var attachment = attachments[a]; + string attachmentName = attachmentNames[a]; + string attachmentMeshName = "[" + slotData.Name + "] " + attachmentName; + Vector3 offset = Vector3.zero; + float rotation = 0; + Mesh mesh = null; + Material material = null; + bool isWeightedMesh = false; + + if (meshTable.ContainsKey(attachmentMeshName)) + mesh = meshTable[attachmentMeshName]; + if (attachment is RegionAttachment) { + var regionAttachment = (RegionAttachment)attachment; + offset.x = regionAttachment.X; + offset.y = regionAttachment.Y; + rotation = regionAttachment.Rotation; + mesh = ExtractRegionAttachment(attachmentMeshName, regionAttachment, mesh); + material = attachment.GetMaterial(); + unusedMeshNames.Remove(attachmentMeshName); + if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) + AssetDatabase.AddObjectToAsset(mesh, prefab); + } else if (attachment is MeshAttachment) { + var meshAttachment = (MeshAttachment)attachment; + isWeightedMesh = (meshAttachment.Bones != null); + offset.x = 0; + offset.y = 0; + rotation = 0; + + if (isWeightedMesh) + mesh = ExtractWeightedMeshAttachment(attachmentMeshName, meshAttachment, i, skeletonData, boneList, mesh); + else + mesh = ExtractMeshAttachment(attachmentMeshName, meshAttachment, mesh); + + material = attachment.GetMaterial(); + unusedMeshNames.Remove(attachmentMeshName); + if (newPrefab || meshTable.ContainsKey(attachmentMeshName) == false) + AssetDatabase.AddObjectToAsset(mesh, prefab); + } else + continue; + + Transform attachmentTransform = new GameObject(attachmentName).transform; + + attachmentTransform.parent = slotTransform; + attachmentTransform.localPosition = offset; + attachmentTransform.localRotation = Quaternion.Euler(0, 0, rotation); + + if (isWeightedMesh) { + attachmentTransform.position = Vector3.zero; + attachmentTransform.rotation = Quaternion.identity; + var skinnedMeshRenderer = attachmentTransform.gameObject.AddComponent(); + skinnedMeshRenderer.rootBone = boneList[0]; + skinnedMeshRenderer.bones = boneList.ToArray(); + skinnedMeshRenderer.sharedMesh = mesh; + } else { + attachmentTransform.gameObject.AddComponent().sharedMesh = mesh; + attachmentTransform.gameObject.AddComponent(); + } + + attachmentTransform.GetComponent().sharedMaterial = material; + attachmentTransform.GetComponent().sortingOrder = i; + + if (attachmentName != slotData.AttachmentName) + attachmentTransform.gameObject.SetActive(false); + } + + } + + foreach (var slotData in skeletonData.Slots) { + Transform slotTransform = slotTable[slotData.Name]; + slotTransform.parent = boneTable[slotData.BoneData.Name]; + slotTransform.localPosition = Vector3.zero; + slotTransform.localRotation = Quaternion.identity; + slotTransform.localScale = Vector3.one; + } + + if (hasAnimations) { + var animator = prefabRoot.AddComponent(); + animator.applyRootMotion = false; + animator.runtimeAnimatorController = (RuntimeAnimatorController)controller; + EditorGUIUtility.PingObject(controller); + } + + + if (newPrefab) { + PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ConnectToPrefab); + } else { + + foreach (string str in unusedMeshNames) { + Mesh.DestroyImmediate(meshTable[str], true); + } + + PrefabUtility.ReplacePrefab(prefabRoot, prefab, ReplacePrefabOptions.ReplaceNameBased); + } + + EditorGUIUtility.PingObject(prefab); + + AssetDatabase.Refresh(); + AssetDatabase.SaveAssets(); + + GameObject.DestroyImmediate(prefabRoot); + } + + } + + #region Attachment Baking + static Bone DummyBone; + static Slot DummySlot; + + internal static Bone GetDummyBone () { + if (DummyBone != null) + return DummyBone; + + SkeletonData skelData = new SkeletonData(); + BoneData data = new BoneData(0, "temp", null) { + ScaleX = 1, + ScaleY = 1, + Length = 100 + }; + + skelData.Bones.Add(data); + + Skeleton skeleton = new Skeleton(skelData); + + Bone bone = new Bone(data, skeleton, null); + bone.UpdateWorldTransform(); + + DummyBone = bone; + + return DummyBone; + } + + internal static Slot GetDummySlot () { + if (DummySlot != null) + return DummySlot; + + Bone bone = GetDummyBone(); + + SlotData data = new SlotData(0, "temp", bone.Data); + Slot slot = new Slot(data, bone); + DummySlot = slot; + return DummySlot; + } + + internal static Mesh ExtractRegionAttachment (string name, RegionAttachment attachment, Mesh mesh = null, bool centered = true) { + var bone = GetDummyBone(); + + if (centered) { + bone.X = -attachment.X; + bone.Y = -attachment.Y; + } + + bone.UpdateWorldTransform(); + + Vector2[] uvs = ExtractUV(attachment.UVs); + float[] floatVerts = new float[8]; + attachment.ComputeWorldVertices(bone, floatVerts, 0); + Vector3[] verts = ExtractVerts(floatVerts); + + //unrotate verts now that they're centered + if (centered) { + for (int i = 0; i < verts.Length; i++) + verts[i] = Quaternion.Euler(0, 0, -attachment.Rotation) * verts[i]; + } + + int[] triangles = { 1, 3, 0, 2, 3, 1 }; + Color color = attachment.GetColor(); + + if (mesh == null) + mesh = new Mesh(); + + mesh.triangles = new int[0]; + + mesh.vertices = verts; + mesh.uv = uvs; + mesh.triangles = triangles; + mesh.colors = new [] { color, color, color, color }; + mesh.RecalculateBounds(); + mesh.RecalculateNormals(); + mesh.name = name; + + return mesh; + } + + internal static Mesh ExtractMeshAttachment (string name, MeshAttachment attachment, Mesh mesh = null) { + var slot = GetDummySlot(); + + slot.Bone.X = 0; + slot.Bone.Y = 0; + slot.Bone.UpdateWorldTransform(); + + Vector2[] uvs = ExtractUV(attachment.UVs); + float[] floatVerts = new float[attachment.WorldVerticesLength]; + attachment.ComputeWorldVertices(slot, floatVerts); + Vector3[] verts = ExtractVerts(floatVerts); + + int[] triangles = attachment.Triangles; + Color color = attachment.GetColor(); + + if (mesh == null) + mesh = new Mesh(); + + mesh.triangles = new int[0]; + + mesh.vertices = verts; + mesh.uv = uvs; + mesh.triangles = triangles; + Color[] colors = new Color[verts.Length]; + for (int i = 0; i < verts.Length; i++) + colors[i] = color; + + mesh.colors = colors; + mesh.RecalculateBounds(); + mesh.RecalculateNormals(); + mesh.name = name; + + return mesh; + } + + public class BoneWeightContainer { + public struct Pair { + public Transform bone; + public float weight; + + public Pair (Transform bone, float weight) { + this.bone = bone; + this.weight = weight; + } + } + + public List bones; + public List weights; + public List pairs; + + + public BoneWeightContainer () { + this.bones = new List(); + this.weights = new List(); + this.pairs = new List(); + } + + public void Add (Transform transform, float weight) { + bones.Add(transform); + weights.Add(weight); + + pairs.Add(new Pair(transform, weight)); + } + } + + internal static Mesh ExtractWeightedMeshAttachment (string name, MeshAttachment attachment, int slotIndex, SkeletonData skeletonData, List boneList, Mesh mesh = null) { + if (!attachment.IsWeighted()) + throw new System.ArgumentException("Mesh is not weighted.", "attachment"); + + Skeleton skeleton = new Skeleton(skeletonData); + skeleton.UpdateWorldTransform(); + + float[] floatVerts = new float[attachment.WorldVerticesLength]; + attachment.ComputeWorldVertices(skeleton.Slots.Items[slotIndex], floatVerts); + + Vector2[] uvs = ExtractUV(attachment.UVs); + Vector3[] verts = ExtractVerts(floatVerts); + + int[] triangles = attachment.Triangles; + Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A); + + mesh = mesh ?? new Mesh(); + + mesh.triangles = new int[0]; + + mesh.vertices = verts; + mesh.uv = uvs; + mesh.triangles = triangles; + Color[] colors = new Color[verts.Length]; + for (int i = 0; i < verts.Length; i++) + colors[i] = color; + + mesh.colors = colors; + mesh.name = name; + mesh.RecalculateNormals(); + mesh.RecalculateBounds(); + + // Handle weights and binding + var weightTable = new Dictionary(); + var warningBuilder = new System.Text.StringBuilder(); + + int[] bones = attachment.Bones; + float[] weights = attachment.Vertices; + for (int w = 0, v = 0, b = 0, n = bones.Length; v < n; w += 2) { + + int nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + Transform boneTransform = boneList[bones[v]]; + int vIndex = w / 2; + BoneWeightContainer container; + if (weightTable.ContainsKey(vIndex)) + container = weightTable[vIndex]; + else { + container = new BoneWeightContainer(); + weightTable.Add(vIndex, container); + } + + float weight = weights[b + 2]; + container.Add(boneTransform, weight); + } + } + + BoneWeight[] boneWeights = new BoneWeight[weightTable.Count]; + + for (int i = 0; i < weightTable.Count; i++) { + BoneWeight bw = new BoneWeight(); + var container = weightTable[i]; + + var pairs = container.pairs.OrderByDescending(pair => pair.weight).ToList(); + + for (int b = 0; b < pairs.Count; b++) { + if (b > 3) { + if (warningBuilder.Length == 0) + warningBuilder.Insert(0, "[Weighted Mesh: " + name + "]\r\nUnity only supports 4 weight influences per vertex! The 4 strongest influences will be used.\r\n"); + + warningBuilder.AppendFormat("{0} ignored on vertex {1}!\r\n", pairs[b].bone.name, i); + continue; + } + + int boneIndex = boneList.IndexOf(pairs[b].bone); + float weight = pairs[b].weight; + + switch (b) { + case 0: + bw.boneIndex0 = boneIndex; + bw.weight0 = weight; + break; + case 1: + bw.boneIndex1 = boneIndex; + bw.weight1 = weight; + break; + case 2: + bw.boneIndex2 = boneIndex; + bw.weight2 = weight; + break; + case 3: + bw.boneIndex3 = boneIndex; + bw.weight3 = weight; + break; + } + } + + boneWeights[i] = bw; + } + + Matrix4x4[] bindPoses = new Matrix4x4[boneList.Count]; + for (int i = 0; i < boneList.Count; i++) { + bindPoses[i] = boneList[i].worldToLocalMatrix; + } + + mesh.boneWeights = boneWeights; + mesh.bindposes = bindPoses; + + string warningString = warningBuilder.ToString(); + if (warningString.Length > 0) + Debug.LogWarning(warningString); + + + return mesh; + } + + internal static Vector2[] ExtractUV (float[] floats) { + Vector2[] arr = new Vector2[floats.Length / 2]; + + for (int i = 0; i < floats.Length; i += 2) { + arr[i / 2] = new Vector2(floats[i], floats[i + 1]); + } + + return arr; + } + + internal static Vector3[] ExtractVerts (float[] floats) { + Vector3[] arr = new Vector3[floats.Length / 2]; + + for (int i = 0; i < floats.Length; i += 2) { + arr[i / 2] = new Vector3(floats[i], floats[i + 1], 0);// *scale; + } + + return arr; + } + #endregion + + #region Animation Baking + static AnimationClip ExtractAnimation (string name, SkeletonData skeletonData, Dictionary> slotLookup, bool bakeIK, SendMessageOptions eventOptions, AnimationClip clip = null) { + var animation = skeletonData.FindAnimation(name); + + var timelines = animation.Timelines; + + if (clip == null) { + clip = new AnimationClip(); + } else { + clip.ClearCurves(); + AnimationUtility.SetAnimationEvents(clip, new AnimationEvent[0]); + } + + clip.name = name; + + Skeleton skeleton = new Skeleton(skeletonData); + + List ignoreRotateTimelineIndexes = new List(); + + if (bakeIK) { + foreach (IkConstraint i in skeleton.IkConstraints) { + foreach (Bone b in i.Bones) { + int index = skeleton.FindBoneIndex(b.Data.Name); + ignoreRotateTimelineIndexes.Add(index); + BakeBoneConstraints(b, animation, clip); + } + } + } + + foreach (Bone b in skeleton.Bones) { + if (!b.Data.TransformMode.InheritsRotation()) { + int index = skeleton.FindBoneIndex(b.Data.Name); + + if (ignoreRotateTimelineIndexes.Contains(index) == false) { + ignoreRotateTimelineIndexes.Add(index); + BakeBoneConstraints(b, animation, clip); + } + } + } + + foreach (Timeline t in timelines) { + skeleton.SetToSetupPose(); + + if (t is ScaleTimeline) { + ParseScaleTimeline(skeleton, (ScaleTimeline)t, clip); + } else if (t is TranslateTimeline) { + ParseTranslateTimeline(skeleton, (TranslateTimeline)t, clip); + } else if (t is RotateTimeline) { + //bypass any rotation keys if they're going to get baked anyway to prevent localEulerAngles vs Baked collision + if (ignoreRotateTimelineIndexes.Contains(((RotateTimeline)t).BoneIndex) == false) + ParseRotateTimeline(skeleton, (RotateTimeline)t, clip); + } else if (t is AttachmentTimeline) { + ParseAttachmentTimeline(skeleton, (AttachmentTimeline)t, slotLookup, clip); + } else if (t is EventTimeline) { + ParseEventTimeline((EventTimeline)t, clip, eventOptions); + } + + } + + var settings = AnimationUtility.GetAnimationClipSettings(clip); + settings.loopTime = true; + settings.stopTime = Mathf.Max(clip.length, 0.001f); + + SetAnimationSettings(clip, settings); + + clip.EnsureQuaternionContinuity(); + + EditorUtility.SetDirty(clip); + + return clip; + } + + static int BinarySearch (float[] values, float target) { + int low = 0; + int high = values.Length - 2; + if (high == 0) return 1; + int current = (int)((uint)high >> 1); + while (true) { + if (values[(current + 1)] <= target) + low = current + 1; + else + high = current; + + if (low == high) return (low + 1); + current = (int)((uint)(low + high) >> 1); + } + } + + static void BakeBoneConstraints (Bone bone, Spine.Animation animation, AnimationClip clip) { + Skeleton skeleton = bone.Skeleton; + bool inheritRotation = bone.Data.TransformMode.InheritsRotation(); + + animation.PoseSkeleton(skeleton, 0); + skeleton.UpdateWorldTransform(); + float duration = animation.Duration; + + AnimationCurve curve = new AnimationCurve(); + + List keys = new List(); + + float rotation = bone.AppliedRotation; + if (!inheritRotation) + rotation = GetUninheritedAppliedRotation(bone); + + keys.Add(new Keyframe(0, rotation, 0, 0)); + + int listIndex = 1; + + float r = rotation; + + int steps = Mathf.CeilToInt(duration / BakeIncrement); + + float currentTime = 0; + float angle = rotation; + + for (int i = 1; i <= steps; i++) { + currentTime += BakeIncrement; + if (i == steps) + currentTime = duration; + + animation.PoseSkeleton(skeleton, currentTime, true); + skeleton.UpdateWorldTransform(); + + int pIndex = listIndex - 1; + + Keyframe pk = keys[pIndex]; + + pk = keys[pIndex]; + + rotation = inheritRotation ? bone.AppliedRotation : GetUninheritedAppliedRotation(bone); + + angle += Mathf.DeltaAngle(angle, rotation); + + r = angle; + + float rOut = (r - pk.value) / (currentTime - pk.time); + + pk.outTangent = rOut; + + keys.Add(new Keyframe(currentTime, r, rOut, 0)); + + keys[pIndex] = pk; + + listIndex++; + } + + curve = EnsureCurveKeyCount(new AnimationCurve(keys.ToArray())); + + string path = GetPath(bone.Data); + string propertyName = "localEulerAnglesBaked"; + + EditorCurveBinding xBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".x"); + AnimationUtility.SetEditorCurve(clip, xBind, new AnimationCurve()); + EditorCurveBinding yBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".y"); + AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve()); + EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z"); + AnimationUtility.SetEditorCurve(clip, zBind, curve); + } + + static void ParseTranslateTimeline (Skeleton skeleton, TranslateTimeline timeline, AnimationClip clip) { + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; + + AnimationCurve xCurve = new AnimationCurve(); + AnimationCurve yCurve = new AnimationCurve(); + AnimationCurve zCurve = new AnimationCurve(); + + float endTime = timeline.Frames[(timeline.FrameCount * 3) - 3]; + + float currentTime = timeline.Frames[0]; + + List xKeys = new List(); + List yKeys = new List(); + + xKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[1] + boneData.X, 0, 0)); + yKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[2] + boneData.Y, 0, 0)); + + int listIndex = 1; + int frameIndex = 1; + int f = 3; + float[] frames = timeline.Frames; + skeleton.SetToSetupPose(); + float lastTime = 0; + while (currentTime < endTime) { + int pIndex = listIndex - 1; + + float curveType = timeline.GetCurveType(frameIndex - 1); + if (curveType == 0) { + //linear + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] + boneData.X; + float y = frames[f + 2] + boneData.Y; + + float xOut = (x - px.value) / (time - px.time); + float yOut = (y - py.value) / (time - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + lastTime = time; + listIndex++; + } else if (curveType == 1) { + //stepped + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] + boneData.X; + float y = frames[f + 2] + boneData.Y; + + float xOut = float.PositiveInfinity; + float yOut = float.PositiveInfinity; + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + lastTime = time; + listIndex++; + } else if (curveType == 2) { + + //bezier + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + + int steps = Mathf.FloorToInt((time - px.time) / BakeIncrement); + + for (int i = 1; i <= steps; i++) { + currentTime += BakeIncrement; + if (i == steps) + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + px = xKeys[listIndex - 1]; + py = yKeys[listIndex - 1]; + + float xOut = (bone.X - px.value) / (currentTime - px.time); + float yOut = (bone.Y - py.value) / (currentTime - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(currentTime, bone.X, xOut, 0)); + yKeys.Add(new Keyframe(currentTime, bone.Y, yOut, 0)); + + xKeys[listIndex - 1] = px; + yKeys[listIndex - 1] = py; + + listIndex++; + lastTime = currentTime; + } + } + + frameIndex++; + f += 3; + } + + xCurve = EnsureCurveKeyCount(new AnimationCurve(xKeys.ToArray())); + yCurve = EnsureCurveKeyCount(new AnimationCurve(yKeys.ToArray())); + + + + string path = GetPath(boneData); + const string propertyName = "localPosition"; + + clip.SetCurve(path, typeof(Transform), propertyName + ".x", xCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".y", yCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".z", zCurve); + } + + static void ParseScaleTimeline (Skeleton skeleton, ScaleTimeline timeline, AnimationClip clip) { + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; + + AnimationCurve xCurve = new AnimationCurve(); + AnimationCurve yCurve = new AnimationCurve(); + AnimationCurve zCurve = new AnimationCurve(); + + float endTime = timeline.Frames[(timeline.FrameCount * 3) - 3]; + + float currentTime = timeline.Frames[0]; + + List xKeys = new List(); + List yKeys = new List(); + + xKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[1] * boneData.ScaleX, 0, 0)); + yKeys.Add(new Keyframe(timeline.Frames[0], timeline.Frames[2] * boneData.ScaleY, 0, 0)); + + int listIndex = 1; + int frameIndex = 1; + int f = 3; + float[] frames = timeline.Frames; + skeleton.SetToSetupPose(); + float lastTime = 0; + while (currentTime < endTime) { + int pIndex = listIndex - 1; + float curveType = timeline.GetCurveType(frameIndex - 1); + + if (curveType == 0) { + //linear + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] * boneData.ScaleX; + float y = frames[f + 2] * boneData.ScaleY; + + float xOut = (x - px.value) / (time - px.time); + float yOut = (y - py.value) / (time - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + lastTime = time; + listIndex++; + } else if (curveType == 1) { + //stepped + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + float x = frames[f + 1] * boneData.ScaleX; + float y = frames[f + 2] * boneData.ScaleY; + + float xOut = float.PositiveInfinity; + float yOut = float.PositiveInfinity; + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(time, x, xOut, 0)); + yKeys.Add(new Keyframe(time, y, yOut, 0)); + + xKeys[pIndex] = px; + yKeys[pIndex] = py; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + lastTime = time; + listIndex++; + } else if (curveType == 2) { + //bezier + Keyframe px = xKeys[pIndex]; + Keyframe py = yKeys[pIndex]; + + float time = frames[f]; + + int steps = Mathf.FloorToInt((time - px.time) / BakeIncrement); + + for (int i = 1; i <= steps; i++) { + currentTime += BakeIncrement; + if (i == steps) + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + px = xKeys[listIndex - 1]; + py = yKeys[listIndex - 1]; + + float xOut = (bone.ScaleX - px.value) / (currentTime - px.time); + float yOut = (bone.ScaleY - py.value) / (currentTime - py.time); + + px.outTangent = xOut; + py.outTangent = yOut; + + xKeys.Add(new Keyframe(currentTime, bone.ScaleX, xOut, 0)); + yKeys.Add(new Keyframe(currentTime, bone.ScaleY, yOut, 0)); + + xKeys[listIndex - 1] = px; + yKeys[listIndex - 1] = py; + + listIndex++; + lastTime = currentTime; + } + } + + frameIndex++; + f += 3; + } + + xCurve = EnsureCurveKeyCount(new AnimationCurve(xKeys.ToArray())); + yCurve = EnsureCurveKeyCount(new AnimationCurve(yKeys.ToArray())); + + string path = GetPath(boneData); + string propertyName = "localScale"; + + clip.SetCurve(path, typeof(Transform), propertyName + ".x", xCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".y", yCurve); + clip.SetCurve(path, typeof(Transform), propertyName + ".z", zCurve); + } + + static void ParseRotateTimeline (Skeleton skeleton, RotateTimeline timeline, AnimationClip clip) { + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; + + AnimationCurve curve = new AnimationCurve(); + + float endTime = timeline.Frames[(timeline.FrameCount * 2) - 2]; + + float currentTime = timeline.Frames[0]; + + List keys = new List(); + + float rotation = timeline.Frames[1] + boneData.Rotation; + + keys.Add(new Keyframe(timeline.Frames[0], rotation, 0, 0)); + + int listIndex = 1; + int frameIndex = 1; + int f = 2; + float[] frames = timeline.Frames; + skeleton.SetToSetupPose(); + float lastTime = 0; + float angle = rotation; + while (currentTime < endTime) { + int pIndex = listIndex - 1; + float curveType = timeline.GetCurveType(frameIndex - 1); + + if (curveType == 0) { + //linear + Keyframe pk = keys[pIndex]; + + float time = frames[f]; + + rotation = frames[f + 1] + boneData.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + float r = angle; + + float rOut = (r - pk.value) / (time - pk.time); + + pk.outTangent = rOut; + + keys.Add(new Keyframe(time, r, rOut, 0)); + + keys[pIndex] = pk; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + lastTime = time; + listIndex++; + } else if (curveType == 1) { + //stepped + + Keyframe pk = keys[pIndex]; + + float time = frames[f]; + + rotation = frames[f + 1] + boneData.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + float r = angle; + + float rOut = float.PositiveInfinity; + + pk.outTangent = rOut; + + keys.Add(new Keyframe(time, r, rOut, 0)); + + keys[pIndex] = pk; + + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + + lastTime = time; + listIndex++; + } else if (curveType == 2) { + //bezier + Keyframe pk = keys[pIndex]; + + float time = frames[f]; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + skeleton.UpdateWorldTransform(); + + rotation = frames[f + 1] + boneData.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + float r = angle; + + int steps = Mathf.FloorToInt((time - pk.time) / BakeIncrement); + + for (int i = 1; i <= steps; i++) { + currentTime += BakeIncrement; + if (i == steps) + currentTime = time; + + timeline.Apply(skeleton, lastTime, currentTime, null, 1, MixPose.Setup, MixDirection.In); + skeleton.UpdateWorldTransform(); + pk = keys[listIndex - 1]; + + rotation = bone.Rotation; + angle += Mathf.DeltaAngle(angle, rotation); + r = angle; + + float rOut = (r - pk.value) / (currentTime - pk.time); + + pk.outTangent = rOut; + + keys.Add(new Keyframe(currentTime, r, rOut, 0)); + + keys[listIndex - 1] = pk; + + listIndex++; + lastTime = currentTime; + } + } + + frameIndex++; + f += 2; + } + + curve = EnsureCurveKeyCount(new AnimationCurve(keys.ToArray())); + + string path = GetPath(boneData); + const string propertyName = "localEulerAnglesBaked"; + + EditorCurveBinding xBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".x"); + AnimationUtility.SetEditorCurve(clip, xBind, new AnimationCurve()); + EditorCurveBinding yBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".y"); + AnimationUtility.SetEditorCurve(clip, yBind, new AnimationCurve()); + EditorCurveBinding zBind = EditorCurveBinding.FloatCurve(path, typeof(Transform), propertyName + ".z"); + AnimationUtility.SetEditorCurve(clip, zBind, curve); + } + + static void ParseEventTimeline (EventTimeline timeline, AnimationClip clip, SendMessageOptions eventOptions) { + + float[] frames = timeline.Frames; + var events = timeline.Events; + + List animEvents = new List(); + for (int i = 0; i < frames.Length; i++) { + var ev = events[i]; + + AnimationEvent ae = new AnimationEvent(); + //MITCH: left todo: Deal with Mecanim's zero-time missed event + ae.time = frames[i]; + ae.functionName = ev.Data.Name; + ae.messageOptions = eventOptions; + + if (!string.IsNullOrEmpty(ev.String)) { + ae.stringParameter = ev.String; + } else { + if (ev.Int == 0 && ev.Float == 0) { + //do nothing, raw function + } else { + if (ev.Int != 0) + ae.floatParameter = (float)ev.Int; + else + ae.floatParameter = ev.Float; + } + + } + + animEvents.Add(ae); + } + + AnimationUtility.SetAnimationEvents(clip, animEvents.ToArray()); + } + + static void ParseAttachmentTimeline (Skeleton skeleton, AttachmentTimeline timeline, Dictionary> slotLookup, AnimationClip clip) { + var attachmentNames = slotLookup[timeline.SlotIndex]; + + string bonePath = GetPath(skeleton.Slots.Items[timeline.SlotIndex].Bone.Data); + string slotPath = bonePath + "/" + skeleton.Slots.Items[timeline.SlotIndex].Data.Name; + + Dictionary curveTable = new Dictionary(); + + foreach (string str in attachmentNames) { + curveTable.Add(str, new AnimationCurve()); + } + + float[] frames = timeline.Frames; + + if (frames[0] != 0) { + string startingName = skeleton.Slots.Items[timeline.SlotIndex].Data.AttachmentName; + foreach (var pair in curveTable) { + if (startingName == "" || startingName == null) { + pair.Value.AddKey(new Keyframe(0, 0, float.PositiveInfinity, float.PositiveInfinity)); + } else { + if (pair.Key == startingName) { + pair.Value.AddKey(new Keyframe(0, 1, float.PositiveInfinity, float.PositiveInfinity)); + } else { + pair.Value.AddKey(new Keyframe(0, 0, float.PositiveInfinity, float.PositiveInfinity)); + } + } + } + } + + float currentTime = timeline.Frames[0]; + float endTime = frames[frames.Length - 1]; + int f = 0; + while (currentTime < endTime) { + float time = frames[f]; + + int frameIndex = (time >= frames[frames.Length - 1] ? frames.Length : BinarySearch(frames, time)) - 1; + + string name = timeline.AttachmentNames[frameIndex]; + foreach (var pair in curveTable) { + if (name == "") { + pair.Value.AddKey(new Keyframe(time, 0, float.PositiveInfinity, float.PositiveInfinity)); + } else { + if (pair.Key == name) { + pair.Value.AddKey(new Keyframe(time, 1, float.PositiveInfinity, float.PositiveInfinity)); + } else { + pair.Value.AddKey(new Keyframe(time, 0, float.PositiveInfinity, float.PositiveInfinity)); + } + } + } + + currentTime = time; + f += 1; + } + + foreach (var pair in curveTable) { + string path = slotPath + "/" + pair.Key; + string prop = "m_IsActive"; + + clip.SetCurve(path, typeof(GameObject), prop, pair.Value); + } + } + + static AnimationCurve EnsureCurveKeyCount (AnimationCurve curve) { + if (curve.length == 1) + curve.AddKey(curve.keys[0].time + 0.25f, curve.keys[0].value); + + return curve; + } + + static float GetUninheritedAppliedRotation (Bone b) { + Bone parent = b.Parent; + float angle = b.AppliedRotation; + + while (parent != null) { + angle -= parent.AppliedRotation; + parent = parent.Parent; + } + + return angle; + } + #endregion + #endregion + + static string GetPath (BoneData b) { + return GetPathRecurse(b).Substring(1); + } + + static string GetPathRecurse (BoneData b) { + if (b == null) return ""; + return GetPathRecurse(b.Parent) + "/" + b.Name; + } + + static void SetAnimationSettings (AnimationClip clip, AnimationClipSettings settings) { + AnimationUtility.SetAnimationClipSettings(clip, settings); + } + + + } + +} + diff --git a/Assets/Spine/spine-unity/Editor/SkeletonBaker.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonBaker.cs.meta new file mode 100644 index 0000000..37b13c9 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonBaker.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 687d9be457ea4eb44bf09c35c95ee5cd +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonBakingWindow.cs b/Assets/Spine/spine-unity/Editor/SkeletonBakingWindow.cs new file mode 100644 index 0000000..0cadad2 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonBakingWindow.cs @@ -0,0 +1,145 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +namespace Spine.Unity.Editor { + + using Editor = UnityEditor.Editor; + using Icons = SpineEditorUtilities.Icons; + + public class SkeletonBakingWindow : EditorWindow { + const bool IsUtilityWindow = true; + + [MenuItem("CONTEXT/SkeletonDataAsset/Skeleton Baking", false, 5000)] + public static void Init (MenuCommand command) { + var window = EditorWindow.GetWindow(IsUtilityWindow); + window.minSize = new Vector2(330f, 530f); + window.maxSize = new Vector2(600f, 1000f); + window.titleContent = new GUIContent("Skeleton Baking", Icons.spine); + window.skeletonDataAsset = command.context as SkeletonDataAsset; + window.Show(); + } + + public SkeletonDataAsset skeletonDataAsset; + [SpineSkin(dataField:"skeletonDataAsset")] + public string skinToBake = "default"; + + // Settings + bool bakeAnimations = false; + bool bakeIK = true; + SendMessageOptions bakeEventOptions; + + SerializedObject so; + Skin bakeSkin; + + + void DataAssetChanged () { + bakeSkin = null; + } + + void OnGUI () { + so = so ?? new SerializedObject(this); + + EditorGUIUtility.wideMode = true; + EditorGUILayout.LabelField("Spine Skeleton Prefab Baking", EditorStyles.boldLabel); + + const string BakingWarningMessage = "\nSkeleton baking is not the primary use case for Spine skeletons." + + "\nUse baking if you have specialized uses, such as simplified skeletons with movement driven by physics." + + + "\n\nBaked Skeletons do not support the following:" + + "\n\tDisabled rotation or scale inheritance" + + "\n\tLocal Shear" + + "\n\tAll Constraint types" + + "\n\tWeighted mesh verts with more than 4 bound bones" + + + "\n\nBaked Animations do not support the following:" + + "\n\tMesh Deform Keys" + + "\n\tColor Keys" + + "\n\tDraw Order Keys" + + + "\n\nAnimation Curves are sampled at 60fps and are not realtime." + + "\nConstraint animations are also baked into animation curves." + + "\nSee SkeletonBaker.cs comments for full details.\n"; + + EditorGUILayout.HelpBox(BakingWarningMessage, MessageType.Info, true); + + EditorGUI.BeginChangeCheck(); + var skeletonDataAssetProperty = so.FindProperty("skeletonDataAsset"); + EditorGUILayout.PropertyField(skeletonDataAssetProperty, SpineInspectorUtility.TempContent("SkeletonDataAsset", Icons.spine)); + if (EditorGUI.EndChangeCheck()) { + so.ApplyModifiedProperties(); + DataAssetChanged(); + } + EditorGUILayout.Space(); + + if (skeletonDataAsset == null) return; + var skeletonData = skeletonDataAsset.GetSkeletonData(false); + if (skeletonData == null) return; + bool hasExtraSkins = skeletonData.Skins.Count > 1; + + using (new SpineInspectorUtility.BoxScope(false)) { + EditorGUILayout.LabelField(skeletonDataAsset.name, EditorStyles.boldLabel); + using (new SpineInspectorUtility.IndentScope()) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bones: " + skeletonData.Bones.Count, Icons.bone)); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Slots: " + skeletonData.Slots.Count, Icons.slotRoot)); + + if (hasExtraSkins) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins: " + skeletonData.Skins.Count, Icons.skinsRoot)); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Current skin attachments: " + (bakeSkin == null ? 0 : bakeSkin.Attachments.Count), Icons.skinPlaceholder)); + } else if (skeletonData.Skins.Count == 1) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins: 1 (only default Skin)", Icons.skinsRoot)); + } + + int totalAttachments = 0; + foreach (var s in skeletonData.Skins) + totalAttachments += s.Attachments.Count; + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Total Attachments: " + totalAttachments, Icons.genericAttachment)); + } + } + using (new SpineInspectorUtility.BoxScope(false)) { + EditorGUILayout.LabelField("Animations", EditorStyles.boldLabel); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Animations: " + skeletonData.Animations.Count, Icons.animation)); + + using (new SpineInspectorUtility.IndentScope()) { + bakeAnimations = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bake Animations", Icons.animationRoot), bakeAnimations); + using (new EditorGUI.DisabledScope(!bakeAnimations)) { + using (new SpineInspectorUtility.IndentScope()) { + bakeIK = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bake IK", Icons.constraintIK), bakeIK); + bakeEventOptions = (SendMessageOptions)EditorGUILayout.EnumPopup(SpineInspectorUtility.TempContent("Event Options", Icons.userEvent), bakeEventOptions); + } + } + } + } + EditorGUILayout.Space(); + + if (!string.IsNullOrEmpty(skinToBake) && UnityEngine.Event.current.type == EventType.Repaint) + bakeSkin = skeletonData.FindSkin(skinToBake) ?? skeletonData.DefaultSkin; + + var prefabIcon = EditorGUIUtility.FindTexture("PrefabModel Icon"); + + if (hasExtraSkins) { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(so.FindProperty("skinToBake")); + if (EditorGUI.EndChangeCheck()) { + so.ApplyModifiedProperties(); + Repaint(); + } + + if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent(string.Format("Bake Skeleton with Skin ({0})", (bakeSkin == null ? "default" : bakeSkin.Name)), prefabIcon))) { + SkeletonBaker.BakeToPrefab(skeletonDataAsset, new ExposedList(new[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); + } + + if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent(string.Format("Bake All ({0} skins)", skeletonData.Skins.Count), prefabIcon))) { + SkeletonBaker.BakeToPrefab(skeletonDataAsset, skeletonData.Skins, "", bakeAnimations, bakeIK, bakeEventOptions); + } + } else { + if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Bake Skeleton", prefabIcon))) { + SkeletonBaker.BakeToPrefab(skeletonDataAsset, new ExposedList(new[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); + } + + } + + } + } +} diff --git a/Assets/Spine/spine-unity/Editor/SkeletonBakingWindow.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonBakingWindow.cs.meta new file mode 100644 index 0000000..4e1aa1d --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonBakingWindow.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 868b0caae5b3e65408ece1ab400c4a99 +timeCreated: 1495203966 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonDataAssetInspector.cs b/Assets/Spine/spine-unity/Editor/SkeletonDataAssetInspector.cs new file mode 100644 index 0000000..682adbb --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -0,0 +1,1156 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define SPINE_SKELETON_ANIMATOR + +using System; +using System.Reflection; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +using Spine; + +namespace Spine.Unity.Editor { + using Event = UnityEngine.Event; + using Icons = SpineEditorUtilities.Icons; + using Animation = Spine.Animation; + + [CustomEditor(typeof(SkeletonDataAsset)), CanEditMultipleObjects] + public class SkeletonDataAssetInspector : UnityEditor.Editor { + internal static bool showAnimationStateData = true; + internal static bool showAnimationList = true; + internal static bool showSlotList = false; + internal static bool showAttachments = false; + + SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix; + #if SPINE_TK2D + SerializedProperty spriteCollection; + #endif + + #if SPINE_SKELETON_ANIMATOR + static bool isMecanimExpanded = false; + SerializedProperty controller; + #endif + + SkeletonDataAsset targetSkeletonDataAsset; + SkeletonData targetSkeletonData; + + readonly List warnings = new List(); + readonly SkeletonInspectorPreview preview = new SkeletonInspectorPreview(); + + GUIStyle activePlayButtonStyle, idlePlayButtonStyle; + readonly GUIContent DefaultMixLabel = new GUIContent("Default Mix Duration", "Sets 'SkeletonDataAsset.defaultMix' in the asset and 'AnimationState.data.defaultMix' at runtime load time."); + + string TargetAssetGUID { get { return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(targetSkeletonDataAsset)); } } + string LastSkinKey { get { return TargetAssetGUID + "_lastSkin"; } } + string LastSkinName { get { return EditorPrefs.GetString(LastSkinKey, ""); } } + + void OnEnable () { + InitializeEditor(); + } + + void OnDestroy () { + HandleOnDestroyPreview(); + AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; + EditorApplication.update -= preview.HandleEditorUpdate; + } + + private void OnDomainUnload (object sender, EventArgs e) { + OnDestroy(); + } + + void InitializeEditor () { + SpineEditorUtilities.ConfirmInitialization(); + targetSkeletonDataAsset = (SkeletonDataAsset)target; + + bool newAtlasAssets = atlasAssets == null; + if (newAtlasAssets) atlasAssets = serializedObject.FindProperty("atlasAssets"); + skeletonJSON = serializedObject.FindProperty("skeletonJSON"); + scale = serializedObject.FindProperty("scale"); + fromAnimation = serializedObject.FindProperty("fromAnimation"); + toAnimation = serializedObject.FindProperty("toAnimation"); + duration = serializedObject.FindProperty("duration"); + defaultMix = serializedObject.FindProperty("defaultMix"); + + #if SPINE_SKELETON_ANIMATOR + controller = serializedObject.FindProperty("controller"); + #endif + + #if SPINE_TK2D + if (newAtlasAssets) atlasAssets.isExpanded = false; + spriteCollection = serializedObject.FindProperty("spriteCollection"); + #else + // Analysis disable once ConvertIfToOrExpression + if (newAtlasAssets) atlasAssets.isExpanded = true; +#endif + + // This handles the case where the managed editor assembly is unloaded before recompilation when code changes. + AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; + AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; + + EditorApplication.update -= preview.HandleEditorUpdate; + EditorApplication.update += preview.HandleEditorUpdate; + preview.OnSkinChanged -= HandlePreviewSkinChanged; + preview.OnSkinChanged += HandlePreviewSkinChanged; + + PopulateWarnings(); + if (targetSkeletonDataAsset.skeletonJSON == null) { + targetSkeletonData = null; + return; + } + + targetSkeletonData = warnings.Count == 0 ? targetSkeletonDataAsset.GetSkeletonData(false) : null; + + if (targetSkeletonData != null && warnings.Count <= 0) { + preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); + } + + } + + void Clear () { + preview.Clear(); + targetSkeletonDataAsset.Clear(); + targetSkeletonData = null; + } + + override public void OnInspectorGUI () { + // Multi-Editing + if (serializedObject.isEditingMultipleObjects) { + OnInspectorGUIMulti(); + return; + } + + { // Lazy initialization because accessing EditorStyles values in OnEnable during a recompile causes UnityEditor to throw null exceptions. (Unity 5.3.5) + idlePlayButtonStyle = idlePlayButtonStyle ?? new GUIStyle(EditorStyles.miniButton); + if (activePlayButtonStyle == null) { + activePlayButtonStyle = new GUIStyle(idlePlayButtonStyle); + activePlayButtonStyle.normal.textColor = Color.red; + } + } + + serializedObject.Update(); + + // Header + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel); + if (targetSkeletonData != null) EditorGUILayout.LabelField("(Drag and Drop to instantiate.)", EditorStyles.miniLabel); + + // Main Serialized Fields + using (var changeCheck = new EditorGUI.ChangeCheckScope()) { + using (new SpineInspectorUtility.BoxScope()) + DrawSkeletonDataFields(); + + using (new SpineInspectorUtility.BoxScope()) { + DrawAtlasAssetsFields(); + HandleAtlasAssetsNulls(); + } + + if (changeCheck.changed) { + if (serializedObject.ApplyModifiedProperties()) { + this.Clear(); + this.InitializeEditor(); + return; + } + } + } + + // Unity Quirk: Some code depends on valid preview. If preview is initialized elsewhere, this can cause contents to change between Layout and Repaint events, causing GUILayout control count errors. + if (warnings.Count <= 0) + preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); + + if (targetSkeletonData != null) { + GUILayout.Space(20f); + + using (new SpineInspectorUtility.BoxScope(false)) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Mix Settings", Icons.animationRoot), EditorStyles.boldLabel); + DrawAnimationStateInfo(); + EditorGUILayout.Space(); + } + + EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel); + DrawAnimationList(); + if (targetSkeletonData.Animations.Count > 0) { + const string AnimationReferenceButtonText = "Create Animation Reference Assets"; + const string AnimationReferenceTooltipText = "AnimationReferenceAsset acts as Unity asset for a reference to a Spine.Animation. This can be used in inspectors.\n\nIt serializes a reference to a SkeletonDataAsset and an animationName.\n\nAt runtime, a reference to its Spine.Animation is loaded and cached into the object to be used as needed. This skips the need to find and cache animation references in individual MonoBehaviours."; + if (GUILayout.Button(SpineInspectorUtility.TempContent(AnimationReferenceButtonText, Icons.animationRoot, AnimationReferenceTooltipText), GUILayout.Width(250), GUILayout.Height(26))) { + CreateAnimationReferenceAssets(); + } + } + EditorGUILayout.Space(); + DrawSlotList(); + EditorGUILayout.Space(); + + DrawUnityTools(); + + } else { + #if !SPINE_TK2D + // Draw Reimport Button + using (new EditorGUI.DisabledGroupScope(skeletonJSON.objectReferenceValue == null)) { + if (GUILayout.Button(SpineInspectorUtility.TempContent("Attempt Reimport", Icons.warning))) + DoReimport(); + } + #else + EditorGUILayout.HelpBox("Couldn't load SkeletonData.", MessageType.Error); + #endif + + DrawWarningList(); + } + + if (!Application.isPlaying) + serializedObject.ApplyModifiedProperties(); + } + + void CreateAnimationReferenceAssets () { + const string AssetFolderName = "ReferenceAssets"; + string parentFolder = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(targetSkeletonDataAsset)); + string dataPath = parentFolder + "/" + AssetFolderName; + if (!AssetDatabase.IsValidFolder(dataPath)) { + AssetDatabase.CreateFolder(parentFolder, AssetFolderName); + } + + FieldInfo nameField = typeof(AnimationReferenceAsset).GetField("animationName", BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo skeletonDataAssetField = typeof(AnimationReferenceAsset).GetField("skeletonDataAsset", BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var animation in targetSkeletonData.Animations) { + string assetPath = string.Format("{0}/{1}.asset", dataPath, SpineEditorUtilities.GetPathSafeName(animation.Name)); + AnimationReferenceAsset existingAsset = AssetDatabase.LoadAssetAtPath(assetPath); + if (existingAsset == null) { + AnimationReferenceAsset newAsset = ScriptableObject.CreateInstance(); + skeletonDataAssetField.SetValue(newAsset, targetSkeletonDataAsset); + nameField.SetValue(newAsset, animation.Name); + AssetDatabase.CreateAsset(newAsset, assetPath); + } + } + + var folderObject = AssetDatabase.LoadAssetAtPath(dataPath, typeof(UnityEngine.Object)); + if (folderObject != null) { + Selection.activeObject = folderObject; + EditorGUIUtility.PingObject(folderObject); + } + } + + void OnInspectorGUIMulti () { + + // Skeleton data file field. + using (new SpineInspectorUtility.BoxScope()) { + EditorGUILayout.LabelField("SkeletonData", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine)); + EditorGUILayout.PropertyField(scale); + } + + // Texture source field. + using (new SpineInspectorUtility.BoxScope()) { + EditorGUILayout.LabelField("Atlas", EditorStyles.boldLabel); + #if !SPINE_TK2D + EditorGUILayout.PropertyField(atlasAssets, true); + #else + using (new EditorGUI.DisabledGroupScope(spriteCollection.objectReferenceValue != null)) { + EditorGUILayout.PropertyField(atlasAssets, true); + } + EditorGUILayout.LabelField("spine-tk2d", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(spriteCollection, true); + #endif + } + + // Mix settings. + using (new SpineInspectorUtility.BoxScope()) { + EditorGUILayout.LabelField("Mix Settings", EditorStyles.boldLabel); + SpineInspectorUtility.PropertyFieldWideLabel(defaultMix, DefaultMixLabel, 160); + EditorGUILayout.Space(); + } + + } + + void DrawSkeletonDataFields () { + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.LabelField("SkeletonData", EditorStyles.boldLabel); + if (targetSkeletonData != null) { + var sd = targetSkeletonData; + string m = string.Format("{8} - {0} {1}\nBones: {2}\nConstraints: \n {5} IK \n {6} Path \n {7} Transform\n\nSlots: {3}\nSkins: {4}\n\nAnimations: {9}", + sd.Version, string.IsNullOrEmpty(sd.Version) ? "" : "export ", sd.Bones.Count, sd.Slots.Count, sd.Skins.Count, sd.IkConstraints.Count, sd.PathConstraints.Count, sd.TransformConstraints.Count, skeletonJSON.objectReferenceValue.name, sd.Animations.Count); + EditorGUILayout.LabelField(GUIContent.none, new GUIContent(Icons.info, m), GUILayout.Width(30f)); + } + } + EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine)); + EditorGUILayout.PropertyField(scale); + } + + void DrawAtlasAssetsFields () { + EditorGUILayout.LabelField("Atlas", EditorStyles.boldLabel); + #if !SPINE_TK2D + EditorGUILayout.PropertyField(atlasAssets, true); + #else + using (new EditorGUI.DisabledGroupScope(spriteCollection.objectReferenceValue != null)) { + EditorGUILayout.PropertyField(atlasAssets, true); + } + EditorGUILayout.LabelField("spine-tk2d", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(spriteCollection, true); + #endif + + if (atlasAssets.arraySize == 0) + EditorGUILayout.HelpBox("AtlasAssets array is empty. Skeleton's attachments will load without being mapped to images.", MessageType.Info); + } + + void HandleAtlasAssetsNulls () { + bool hasNulls = false; + foreach (var a in targetSkeletonDataAsset.atlasAssets) { + if (a == null) { + hasNulls = true; + break; + } + } + if (hasNulls) { + if (targetSkeletonDataAsset.atlasAssets.Length == 1) { + EditorGUILayout.HelpBox("Atlas array cannot have null entries!", MessageType.None); + } + else { + EditorGUILayout.HelpBox("Atlas array should not have null entries!", MessageType.Error); + if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Remove null entries"))) { + var trimmedAtlasAssets = new List(); + foreach (var a in targetSkeletonDataAsset.atlasAssets) { + if (a != null) + trimmedAtlasAssets.Add(a); + } + targetSkeletonDataAsset.atlasAssets = trimmedAtlasAssets.ToArray(); + serializedObject.Update(); + } + } + } + } + + void DrawAnimationStateInfo () { + using (new SpineInspectorUtility.IndentScope()) + showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data"); + + if (!showAnimationStateData) + return; + + using (var cc = new EditorGUI.ChangeCheckScope()) { + using (new SpineInspectorUtility.IndentScope()) + SpineInspectorUtility.PropertyFieldWideLabel(defaultMix, DefaultMixLabel, 160); + + // Do not use EditorGUIUtility.indentLevel. It will add spaces on every field. + for (int i = 0; i < fromAnimation.arraySize; i++) { + SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i); + SerializedProperty to = toAnimation.GetArrayElementAtIndex(i); + SerializedProperty durationProp = duration.GetArrayElementAtIndex(i); + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.Space(16f); + EditorGUILayout.PropertyField(from, GUIContent.none); + EditorGUILayout.PropertyField(to, GUIContent.none); + durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue, GUILayout.MinWidth(25f), GUILayout.MaxWidth(60f)); + if (GUILayout.Button("Delete", EditorStyles.miniButton)) { + duration.DeleteArrayElementAtIndex(i); + toAnimation.DeleteArrayElementAtIndex(i); + fromAnimation.DeleteArrayElementAtIndex(i); + } + } + } + + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.Space(); + if (GUILayout.Button("Add Mix")) { + duration.arraySize++; + toAnimation.arraySize++; + fromAnimation.arraySize++; + } + EditorGUILayout.Space(); + } + + if (cc.changed) { + targetSkeletonDataAsset.FillStateData(); + EditorUtility.SetDirty(targetSkeletonDataAsset); + serializedObject.ApplyModifiedProperties(); + } + } + } + + void DrawAnimationList () { + showAnimationList = EditorGUILayout.Foldout(showAnimationList, SpineInspectorUtility.TempContent(string.Format("Animations [{0}]", targetSkeletonData.Animations.Count), Icons.animationRoot)); + if (!showAnimationList) + return; + + bool isPreviewWindowOpen = preview.IsValid; + + if (isPreviewWindowOpen) { + if (GUILayout.Button(SpineInspectorUtility.TempContent("Setup Pose", Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) { + preview.ClearAnimationSetupPose(); + preview.RefreshOnNextUpdate(); + } + } else { + EditorGUILayout.HelpBox("Animations can be previewed if you expand the Preview window below.", MessageType.Info); + } + + EditorGUILayout.LabelField("Name", " Duration"); + //bool nonessential = targetSkeletonData.ImagesPath != null; // Currently the only way to determine if skeleton data has nonessential data. (Spine 3.6) + //float fps = targetSkeletonData.Fps; + //if (nonessential && fps == 0) fps = 30; + + var activeTrack = preview.ActiveTrack; + foreach (Animation animation in targetSkeletonData.Animations) { + using (new GUILayout.HorizontalScope()) { + if (isPreviewWindowOpen) { + bool active = activeTrack != null && activeTrack.Animation == animation; + //bool sameAndPlaying = active && activeTrack.TimeScale > 0f; + if (GUILayout.Button("\u25BA", active ? activePlayButtonStyle : idlePlayButtonStyle, GUILayout.Width(24))) { + preview.PlayPauseAnimation(animation.Name, true); + activeTrack = preview.ActiveTrack; + } + } else { + GUILayout.Label("-", GUILayout.Width(24)); + } + //string frameCountString = (fps > 0) ? ("(" + (Mathf.RoundToInt(animation.Duration * fps)) + ")").PadLeft(12, ' ') : string.Empty; + //EditorGUILayout.LabelField(new GUIContent(animation.Name, Icons.animation), SpineInspectorUtility.TempContent(animation.Duration.ToString("f3") + "s" + frameCountString)); + EditorGUILayout.LabelField(new GUIContent(animation.Name, Icons.animation), SpineInspectorUtility.TempContent(animation.Duration.ToString("f3") + "s")); + } + } + } + + void DrawSlotList () { + showSlotList = EditorGUILayout.Foldout(showSlotList, SpineInspectorUtility.TempContent("Slots", Icons.slotRoot)); + + if (!showSlotList) return; + if (!preview.IsValid) return; + + EditorGUI.indentLevel++; + showAttachments = EditorGUILayout.ToggleLeft("Show Attachments", showAttachments); + var slotAttachments = new List(); + var slotAttachmentNames = new List(); + var defaultSkinAttachmentNames = new List(); + var defaultSkin = targetSkeletonData.Skins.Items[0]; + Skin skin = preview.Skeleton.Skin ?? defaultSkin; + var slotsItems = preview.Skeleton.Slots.Items; + + for (int i = preview.Skeleton.Slots.Count - 1; i >= 0; i--) { + Slot slot = slotsItems[i]; + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot)); + if (showAttachments) { + + EditorGUI.indentLevel++; + slotAttachments.Clear(); + slotAttachmentNames.Clear(); + defaultSkinAttachmentNames.Clear(); + + skin.FindNamesForSlot(i, slotAttachmentNames); + skin.FindAttachmentsForSlot(i, slotAttachments); + + if (skin != defaultSkin) { + defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames); + defaultSkin.FindNamesForSlot(i, slotAttachmentNames); + defaultSkin.FindAttachmentsForSlot(i, slotAttachments); + } else { + defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames); + } + + for (int a = 0; a < slotAttachments.Count; a++) { + Attachment attachment = slotAttachments[a]; + string attachmentName = slotAttachmentNames[a]; + Texture2D icon = Icons.GetAttachmentIcon(attachment); + bool initialState = slot.Attachment == attachment; + bool toggled = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachmentName, icon), slot.Attachment == attachment); + + if (!defaultSkinAttachmentNames.Contains(attachmentName)) { + Rect skinPlaceHolderIconRect = GUILayoutUtility.GetLastRect(); + skinPlaceHolderIconRect.width = Icons.skinPlaceholder.width; + skinPlaceHolderIconRect.height = Icons.skinPlaceholder.height; + GUI.DrawTexture(skinPlaceHolderIconRect, Icons.skinPlaceholder); + } + + if (toggled != initialState) { + slot.Attachment = toggled ? attachment : null; + preview.RefreshOnNextUpdate(); + } + } + EditorGUI.indentLevel--; + } + } + EditorGUI.indentLevel--; + } + + void DrawUnityTools () { + #if SPINE_SKELETON_ANIMATOR + using (new SpineInspectorUtility.BoxScope()) { + isMecanimExpanded = EditorGUILayout.Foldout(isMecanimExpanded, SpineInspectorUtility.TempContent("SkeletonAnimator", SpineInspectorUtility.UnityIcon())); + if (isMecanimExpanded) { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(controller, SpineInspectorUtility.TempContent("Controller", SpineInspectorUtility.UnityIcon())); + if (controller.objectReferenceValue == null) { + + // Generate Mecanim Controller Button + using (new GUILayout.HorizontalScope()) { + GUILayout.Space(EditorGUIUtility.labelWidth); + if (GUILayout.Button(SpineInspectorUtility.TempContent("Generate Mecanim Controller"), GUILayout.Height(20))) + SkeletonBaker.GenerateMecanimAnimationClips(targetSkeletonDataAsset); + } + EditorGUILayout.HelpBox("SkeletonAnimator is the Mecanim alternative to SkeletonAnimation.\nIt is not required.", MessageType.Info); + + } else { + + // Update AnimationClips button. + using (new GUILayout.HorizontalScope()) { + GUILayout.Space(EditorGUIUtility.labelWidth); + if (GUILayout.Button(SpineInspectorUtility.TempContent("Force Update AnimationClips"), GUILayout.Height(20))) + SkeletonBaker.GenerateMecanimAnimationClips(targetSkeletonDataAsset); + } + + } + EditorGUI.indentLevel--; + } + } + #endif + } + + void DrawWarningList () { + foreach (string line in warnings) + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(line, Icons.warning)); + } + + void PopulateWarnings () { + warnings.Clear(); + + if (skeletonJSON.objectReferenceValue == null) { + warnings.Add("Missing Skeleton JSON"); + } else { + var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue; + if (!SpineEditorUtilities.SkeletonDataFileValidator.IsSpineData(fieldValue)) { + warnings.Add("Skeleton data file is not a valid JSON or binary file."); + } else { + #if SPINE_TK2D + bool searchForSpineAtlasAssets = true; + bool isSpriteCollectionNull = spriteCollection.objectReferenceValue == null; + if (!isSpriteCollectionNull) searchForSpineAtlasAssets = false; + #else + // Analysis disable once ConvertToConstant.Local + bool searchForSpineAtlasAssets = true; + #endif + + if (searchForSpineAtlasAssets) { + bool detectedNullAtlasEntry = false; + var atlasList = new List(); + var actualAtlasAssets = targetSkeletonDataAsset.atlasAssets; + + for (int i = 0; i < actualAtlasAssets.Length; i++) { + if (targetSkeletonDataAsset.atlasAssets[i] == null) { + detectedNullAtlasEntry = true; + break; + } else { + atlasList.Add(actualAtlasAssets[i].GetAtlas()); + } + } + + if (detectedNullAtlasEntry) { + warnings.Add("AtlasAsset elements should not be null."); + } else { + List missingPaths = null; + if (atlasAssets.arraySize > 0) { + missingPaths = SpineEditorUtilities.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue)); + + foreach (var atlas in atlasList) { + for (int i = 0; i < missingPaths.Count; i++) { + if (atlas.FindRegion(missingPaths[i]) != null) { + missingPaths.RemoveAt(i); + i--; + } + } + } + + #if SPINE_TK2D + if (missingPaths.Count > 0) + warnings.Add("Missing regions. SkeletonDataAsset requires tk2DSpriteCollectionData or Spine AtlasAssets."); + #endif + } + + if (missingPaths != null) { + foreach (string str in missingPaths) + warnings.Add("Missing Region: '" + str + "'"); + } + + } + } + + } + } + } + + void DoReimport () { + SpineEditorUtilities.ImportSpineContent(new [] { AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue) }, true); + preview.Clear(); + InitializeEditor(); + EditorUtility.SetDirty(targetSkeletonDataAsset); + } + + void HandlePreviewSkinChanged (string skinName) { + EditorPrefs.SetString(LastSkinKey, skinName); + } + + #region Preview Handlers + void HandleOnDestroyPreview () { + EditorApplication.update -= preview.HandleEditorUpdate; + preview.OnDestroy(); + } + + override public bool HasPreviewGUI () { + if (serializedObject.isEditingMultipleObjects) + return false; + + for (int i = 0; i < atlasAssets.arraySize; i++) { + var prop = atlasAssets.GetArrayElementAtIndex(i); + if (prop.objectReferenceValue == null) + return false; + } + + return skeletonJSON.objectReferenceValue != null; + } + + override public void OnInteractivePreviewGUI (Rect r, GUIStyle background) { + if (warnings.Count <= 0) { + preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); + preview.HandleInteractivePreviewGUI(r, background); + } + } + + override public GUIContent GetPreviewTitle () { return SpineInspectorUtility.TempContent("Preview"); } + public override void OnPreviewSettings () { preview.HandleDrawSettings(); } + public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) { return preview.GetStaticPreview(width, height); } + #endregion + } + + internal class SkeletonInspectorPreview { + Color OriginColor = new Color(0.3f, 0.3f, 0.3f, 1); + static readonly int SliderHash = "Slider".GetHashCode(); + + SkeletonDataAsset skeletonDataAsset; + SkeletonData skeletonData; + + SkeletonAnimation skeletonAnimation; + GameObject previewGameObject; + internal bool requiresRefresh; + + #if !(UNITY_2017_4 || UNITY_2018) + float animationLastTime; + #endif + + static float CurrentTime { get { return (float)EditorApplication.timeSinceStartup; } } + + Action Repaint; + public event Action OnSkinChanged; + + Texture previewTexture; + PreviewRenderUtility previewRenderUtility; + Camera PreviewUtilityCamera { + get { + if (previewRenderUtility == null) return null; + #if UNITY_2017_1_OR_NEWER + return previewRenderUtility.camera; + #else + return previewRenderUtility.m_Camera; + #endif + } + } + + static Vector3 lastCameraPositionGoal; + static float lastCameraOrthoGoal; + float cameraOrthoGoal = 1; + Vector3 cameraPositionGoal = new Vector3(0, 0, -10); + double cameraAdjustEndFrame = 0; + + List currentAnimationEvents = new List(); + List currentAnimationEventTimes = new List(); + List currentAnimationEventTooltips = new List(); + + public bool IsValid { get { return skeletonAnimation != null && skeletonAnimation.valid; } } + + public Skeleton Skeleton { get { return IsValid ? skeletonAnimation.Skeleton : null; } } + + public float TimeScale { + get { return IsValid ? skeletonAnimation.timeScale : 1f; } + set { if (IsValid) skeletonAnimation.timeScale = value; } + } + + public bool IsPlayingAnimation { get { + if (!IsValid) return false; + var currentTrack = skeletonAnimation.AnimationState.GetCurrent(0); + return currentTrack != null && currentTrack.TimeScale > 0; + } + } + + public TrackEntry ActiveTrack { get { return IsValid ? skeletonAnimation.AnimationState.GetCurrent(0) : null; } } + + public Vector3 PreviewCameraPosition { + get { return PreviewUtilityCamera.transform.position; } + set { PreviewUtilityCamera.transform.position = value; } + } + + public void HandleDrawSettings () { + const float SliderWidth = 150; + const float SliderSnap = 0.25f; + const float SliderMin = 0f; + const float SliderMax = 2f; + + if (IsValid) { + float timeScale = GUILayout.HorizontalSlider(TimeScale, SliderMin, SliderMax, GUILayout.MaxWidth(SliderWidth)); + timeScale = Mathf.RoundToInt(timeScale / SliderSnap) * SliderSnap; + TimeScale = timeScale; + } + } + + public void HandleEditorUpdate () { + AdjustCamera(); + if (IsPlayingAnimation) { + RefreshOnNextUpdate(); + Repaint(); + } else if (requiresRefresh) { + Repaint(); + } + } + + public void Initialize (Action repaintCallback, SkeletonDataAsset skeletonDataAsset, string skinName = "") { + if (skeletonDataAsset == null) return; + if (skeletonDataAsset.GetSkeletonData(false) == null) { + DestroyPreviewGameObject(); + return; + } + + this.Repaint = repaintCallback; + this.skeletonDataAsset = skeletonDataAsset; + this.skeletonData = skeletonDataAsset.GetSkeletonData(false); + + if (skeletonData == null) { + DestroyPreviewGameObject(); + return; + } + + if (previewRenderUtility == null) { + previewRenderUtility = new PreviewRenderUtility(true); + #if !(UNITY_2017_4 || UNITY_2018) + animationLastTime = CurrentTime; + #endif + + const int PreviewLayer = 30; + const int PreviewCameraCullingMask = 1 << PreviewLayer; + + { + var c = this.PreviewUtilityCamera; + c.orthographic = true; + c.cullingMask = PreviewCameraCullingMask; + c.nearClipPlane = 0.01f; + c.farClipPlane = 1000f; + c.orthographicSize = lastCameraOrthoGoal; + c.transform.position = lastCameraPositionGoal; + } + + DestroyPreviewGameObject(); + + if (previewGameObject == null) { + try { + previewGameObject = SpineEditorUtilities.InstantiateSkeletonAnimation(skeletonDataAsset, skinName).gameObject; + + if (previewGameObject != null) { + previewGameObject.hideFlags = HideFlags.HideAndDontSave; + previewGameObject.layer = PreviewLayer; + skeletonAnimation = previewGameObject.GetComponent(); + skeletonAnimation.initialSkinName = skinName; + skeletonAnimation.LateUpdate(); + previewGameObject.GetComponent().enabled = false; + + #if UNITY_2017_4 || UNITY_2018 + previewRenderUtility.AddSingleGO(previewGameObject); + #endif + } + + if (this.ActiveTrack != null) cameraAdjustEndFrame = EditorApplication.timeSinceStartup + skeletonAnimation.AnimationState.GetCurrent(0).Alpha; + AdjustCameraGoals(); + } catch { + DestroyPreviewGameObject(); + } + + RefreshOnNextUpdate(); + } + } + } + + public void HandleInteractivePreviewGUI (Rect r, GUIStyle background) { + if (Event.current.type == EventType.Repaint) { + if (requiresRefresh) { + previewRenderUtility.BeginPreview(r, background); + DoRenderPreview(true); + previewTexture = previewRenderUtility.EndPreview(); + requiresRefresh = false; + } + if (previewTexture != null) + GUI.DrawTexture(r, previewTexture, ScaleMode.StretchToFill, false); + } + + DrawSkinToolbar(r); + //DrawSetupPoseButton(r); + DrawTimeBar(r); + HandleMouseScroll(r); + } + + public Texture2D GetStaticPreview (int width, int height) { + var c = this.PreviewUtilityCamera; + if (c == null) + return null; + + RefreshOnNextUpdate(); + AdjustCameraGoals(); + c.orthographicSize = cameraOrthoGoal / 2; + c.transform.position = cameraPositionGoal; + previewRenderUtility.BeginStaticPreview(new Rect(0, 0, width, height)); + DoRenderPreview(false); + var tex = previewRenderUtility.EndStaticPreview(); + + return tex; + } + + public void DoRenderPreview (bool drawHandles) { + if (this.PreviewUtilityCamera.activeTexture == null || this.PreviewUtilityCamera.targetTexture == null) + return; + + GameObject go = previewGameObject; + if (requiresRefresh && go != null) { + var renderer = go.GetComponent(); + renderer.enabled = true; + + + if (!EditorApplication.isPlaying) { + #if !(UNITY_2017_4 || UNITY_2018) + float current = CurrentTime; + float deltaTime = (current - animationLastTime); + skeletonAnimation.Update(deltaTime); + animationLastTime = current; + #endif + skeletonAnimation.LateUpdate(); + } + + var thisPreviewUtilityCamera = this.PreviewUtilityCamera; + + if (drawHandles) { + Handles.SetCamera(thisPreviewUtilityCamera); + Handles.color = OriginColor; + + // Draw Cross + float scale = skeletonDataAsset.scale; + float cl = 1000 * scale; + Handles.DrawLine(new Vector3(-cl, 0), new Vector3(cl, 0)); + Handles.DrawLine(new Vector3(0, cl), new Vector3(0, -cl)); + } + + thisPreviewUtilityCamera.Render(); + + if (drawHandles) { + Handles.SetCamera(thisPreviewUtilityCamera); + SpineHandles.DrawBoundingBoxes(skeletonAnimation.transform, skeletonAnimation.skeleton); + if (SkeletonDataAssetInspector.showAttachments) + SpineHandles.DrawPaths(skeletonAnimation.transform, skeletonAnimation.skeleton); + } + + renderer.enabled = false; + } + } + + public void AdjustCamera () { + if (previewRenderUtility == null) + return; + + if (CurrentTime < cameraAdjustEndFrame) + AdjustCameraGoals(); + + lastCameraPositionGoal = cameraPositionGoal; + lastCameraOrthoGoal = cameraOrthoGoal; + + var c = this.PreviewUtilityCamera; + float orthoSet = Mathf.Lerp(c.orthographicSize, cameraOrthoGoal, 0.1f); + + c.orthographicSize = orthoSet; + + float dist = Vector3.Distance(c.transform.position, cameraPositionGoal); + if (dist > 0f) { + Vector3 pos = Vector3.Lerp(c.transform.position, cameraPositionGoal, 0.1f); + pos.x = 0; + c.transform.position = pos; + c.transform.rotation = Quaternion.identity; + RefreshOnNextUpdate(); + } + } + + void AdjustCameraGoals () { + if (previewGameObject == null) return; + + Bounds bounds = previewGameObject.GetComponent().bounds; + cameraOrthoGoal = bounds.size.y; + cameraPositionGoal = bounds.center + new Vector3(0, 0, -10f); + } + + void HandleMouseScroll (Rect position) { + Event current = Event.current; + int controlID = GUIUtility.GetControlID(SliderHash, FocusType.Passive); + switch (current.GetTypeForControl(controlID)) { + case EventType.ScrollWheel: + if (position.Contains(current.mousePosition)) { + cameraOrthoGoal += current.delta.y * 0.06f; + cameraOrthoGoal = Mathf.Max(0.01f, cameraOrthoGoal); + GUIUtility.hotControl = controlID; + current.Use(); + } + break; + } + } + + public void RefreshOnNextUpdate () { + requiresRefresh = true; + } + + public void ClearAnimationSetupPose () { + if (skeletonAnimation == null) { + Debug.LogWarning("Animation was stopped but preview doesn't exist. It's possible that the Preview Panel is closed."); + } + + skeletonAnimation.AnimationState.ClearTracks(); + skeletonAnimation.Skeleton.SetToSetupPose(); + } + + public void PlayPauseAnimation (string animationName, bool loop) { + if (skeletonData == null) return; + + if (skeletonAnimation == null) { + //Debug.LogWarning("Animation was stopped but preview doesn't exist. It's possible that the Preview Panel is closed."); + return; + } + + if (!skeletonAnimation.valid) return; + + if (string.IsNullOrEmpty(animationName)) { + skeletonAnimation.Skeleton.SetToSetupPose(); + skeletonAnimation.AnimationState.ClearTracks(); + return; + } + + var targetAnimation = skeletonData.FindAnimation(animationName); + if (targetAnimation != null) { + var currentTrack = this.ActiveTrack; + bool isEmpty = (currentTrack == null); + bool isNewAnimation = isEmpty || currentTrack.Animation != targetAnimation; + + var skeleton = skeletonAnimation.Skeleton; + var animationState = skeletonAnimation.AnimationState; + + if (isEmpty) { + skeleton.SetToSetupPose(); + animationState.SetAnimation(0, targetAnimation, loop); + } else { + bool sameAnimation = (currentTrack.Animation == targetAnimation); + if (sameAnimation) { + currentTrack.TimeScale = (currentTrack.TimeScale == 0) ? 1f : 0f; // pause/play + } else { + currentTrack.TimeScale = 1f; + animationState.SetAnimation(0, targetAnimation, loop); + } + } + + if (isNewAnimation) { + currentAnimationEvents.Clear(); + currentAnimationEventTimes.Clear(); + foreach (Timeline timeline in targetAnimation.Timelines) { + var eventTimeline = timeline as EventTimeline; + if (eventTimeline != null) { + for (int i = 0; i < eventTimeline.Events.Length; i++) { + currentAnimationEvents.Add(eventTimeline.Events[i]); + currentAnimationEventTimes.Add(eventTimeline.Frames[i]); + } + } + } + } + } else { + Debug.LogFormat("The Spine.Animation named '{0}' was not found for this Skeleton.", animationName); + } + + } + + void DrawSkinToolbar (Rect r) { + if (!this.IsValid) return; + + var skeleton = this.Skeleton; + string label = (skeleton.Skin != null) ? skeleton.Skin.Name : "default"; + + Rect popRect = new Rect(r); + popRect.y += 32; + popRect.x += 4; + popRect.height = 24; + popRect.width = 40; + EditorGUI.DropShadowLabel(popRect, SpineInspectorUtility.TempContent("Skin")); + + popRect.y += 11; + popRect.width = 150; + popRect.x += 44; + + if (GUI.Button(popRect, SpineInspectorUtility.TempContent(label, Icons.skin), EditorStyles.popup)) { + DrawSkinDropdown(); + } + } + + void DrawSetupPoseButton (Rect r) { + if (!this.IsValid) + return; + + var skeleton = this.Skeleton; + + Rect popRect = new Rect(r); + popRect.y += 64; + popRect.x += 4; + popRect.height = 24; + popRect.width = 40; + + //popRect.y += 11; + popRect.width = 150; + //popRect.x += 44; + + if (GUI.Button(popRect, SpineInspectorUtility.TempContent("Reset to SetupPose", Icons.skeleton))) { + ClearAnimationSetupPose(); + RefreshOnNextUpdate(); + } + } + + void DrawSkinDropdown () { + var menu = new GenericMenu(); + foreach (Skin s in skeletonData.Skins) + menu.AddItem(new GUIContent(s.Name, Icons.skin), skeletonAnimation.skeleton.Skin == s, HandleSkinDropdownSelection, s); + + menu.ShowAsContext(); + } + + void HandleSkinDropdownSelection (object o) { + Skin skin = (Skin)o; + skeletonAnimation.initialSkinName = skin.Name; + skeletonAnimation.Initialize(true); + RefreshOnNextUpdate(); + if (OnSkinChanged != null) OnSkinChanged(skin.Name); + } + + void DrawTimeBar (Rect r) { + if (skeletonAnimation == null) + return; + + Rect barRect = new Rect(r); + barRect.height = 32; + barRect.x += 4; + barRect.width -= 4; + + GUI.Box(barRect, ""); + + Rect lineRect = new Rect(barRect); + float lineRectWidth = lineRect.width; + TrackEntry t = skeletonAnimation.AnimationState.GetCurrent(0); + + if (t != null) { + int loopCount = (int)(t.TrackTime / t.TrackEnd); + float currentTime = t.TrackTime - (t.TrackEnd * loopCount); + float normalizedTime = currentTime / t.Animation.Duration; + float wrappedTime = normalizedTime % 1f; + + lineRect.x = barRect.x + (lineRectWidth * wrappedTime) - 0.5f; + lineRect.width = 2; + + GUI.color = Color.red; + GUI.DrawTexture(lineRect, EditorGUIUtility.whiteTexture); + GUI.color = Color.white; + + currentAnimationEventTooltips = currentAnimationEventTooltips ?? new List(); + currentAnimationEventTooltips.Clear(); + for (int i = 0; i < currentAnimationEvents.Count; i++) { + float eventTime = currentAnimationEventTimes[i]; + var userEventIcon = Icons.userEvent; + var evRect = new Rect(barRect) { + x = Mathf.Max(((eventTime / t.Animation.Duration) * lineRectWidth) - (userEventIcon.width / 2), barRect.x), + y = barRect.y + userEventIcon.height, + width = userEventIcon.width, + height = userEventIcon.height + }; + GUI.DrawTexture(evRect, userEventIcon); + + Event ev = Event.current; + if (ev.type == EventType.Repaint) { + if (evRect.Contains(ev.mousePosition)) { + string eventName = currentAnimationEvents[i].Data.Name; + Rect tooltipRect = new Rect(evRect) { + width = EditorStyles.helpBox.CalcSize(new GUIContent(eventName)).x + }; + tooltipRect.y -= 4; + tooltipRect.y -= tooltipRect.height * currentAnimationEventTooltips.Count; // Avoid several overlapping tooltips. + tooltipRect.x += 4; + + // Handle tooltip overflowing to the right. + float rightEdgeOverflow = (tooltipRect.x + tooltipRect.width) - (barRect.x + barRect.width); + if (rightEdgeOverflow > 0) + tooltipRect.x -= rightEdgeOverflow; + + currentAnimationEventTooltips.Add(new SpineEventTooltip { rect = tooltipRect, text = eventName }); + } + } + } + + // Draw tooltips. + for (int i = 0; i < currentAnimationEventTooltips.Count; i++) { + GUI.Label(currentAnimationEventTooltips[i].rect, currentAnimationEventTooltips[i].text, EditorStyles.helpBox); + GUI.tooltip = currentAnimationEventTooltips[i].text; + } + } + } + + public void OnDestroy () { + DisposePreviewRenderUtility(); + DestroyPreviewGameObject(); + } + + public void Clear () { + DisposePreviewRenderUtility(); + DestroyPreviewGameObject(); + } + + void DisposePreviewRenderUtility () { + if (previewRenderUtility != null) { + previewRenderUtility.Cleanup(); + previewRenderUtility = null; + } + } + + void DestroyPreviewGameObject () { + if (previewGameObject != null) { + GameObject.DestroyImmediate(previewGameObject); + previewGameObject = null; + } + } + + internal struct SpineEventTooltip { + public Rect rect; + public string text; + } + } + +} diff --git a/Assets/Spine/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta new file mode 100644 index 0000000..fba64b6 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonDataAssetInspector.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01cbef8f24d105f4bafa9668d669e040 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonDebugWindow.cs b/Assets/Spine/spine-unity/Editor/SkeletonDebugWindow.cs new file mode 100644 index 0000000..cee6ecf --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonDebugWindow.cs @@ -0,0 +1,558 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// With contributions from: Mitch Thompson + +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.AnimatedValues; + +namespace Spine.Unity.Editor { + using Editor = UnityEditor.Editor; + using Icons = SpineEditorUtilities.Icons; + + public class SkeletonDebugWindow : EditorWindow { + + const bool IsUtilityWindow = true; + internal static bool showBoneNames, showPaths = true, showShapes = true, showConstraints = true; + + [MenuItem("CONTEXT/SkeletonRenderer/Open Skeleton Debug Window", false, 5000)] + public static void Init () { + var window = EditorWindow.GetWindow(IsUtilityWindow); + window.minSize = new Vector2(330f, 360f); + window.maxSize = new Vector2(600f, 4000f); + window.titleContent = new GUIContent("Skeleton Debug", Icons.spine); + window.Show(); + window.OnSelectionChange(); + } + + + static AnimBool showSkeleton = new AnimBool(true); + static AnimBool showSlotsTree = new AnimBool(false); + static AnimBool showConstraintsTree = new AnimBool(false); + static AnimBool showDrawOrderTree = new AnimBool(false); + static AnimBool showEventDataTree = new AnimBool(false); + static AnimBool showDataTree = new AnimBool(false); + static AnimBool showInspectBoneTree = new AnimBool(false); + + Vector2 scrollPos; + + GUIContent SlotsRootLabel, SkeletonRootLabel; + GUIStyle BoldFoldoutStyle; + + public SkeletonRenderer skeletonRenderer; + Skeleton skeleton; + Skin activeSkin; + bool isPrefab; + + SerializedProperty bpo; + Bone bone; + + [SpineBone(dataField:"skeletonRenderer")] + public string boneName; + + readonly Dictionary> attachmentTable = new Dictionary>(); + + static bool staticLostValues = true; + + void OnSceneGUI (SceneView sceneView) { + if (skeleton == null || skeletonRenderer == null || !skeletonRenderer.valid || isPrefab) + return; + + var transform = skeletonRenderer.transform; + if (showPaths) SpineHandles.DrawPaths(transform, skeleton); + if (showConstraints) SpineHandles.DrawConstraints(transform, skeleton); + if (showBoneNames) SpineHandles.DrawBoneNames(transform, skeleton); + if (showShapes) SpineHandles.DrawBoundingBoxes(transform, skeleton); + + if (bone != null) { + SpineHandles.DrawBone(skeletonRenderer.transform, bone, 1.5f, Color.cyan); + Handles.Label(bone.GetWorldPosition(skeletonRenderer.transform) + (Vector3.down * 0.15f), bone.Data.Name, SpineHandles.BoneNameStyle); + } + } + + void OnSelectionChange () { + SceneView.onSceneGUIDelegate -= this.OnSceneGUI; + SceneView.onSceneGUIDelegate += this.OnSceneGUI; + + bool noSkeletonRenderer = false; + + var selectedObject = Selection.activeGameObject; + if (selectedObject == null) { + noSkeletonRenderer = true; + } else { + var selectedSkeletonRenderer = selectedObject.GetComponent(); + if (selectedSkeletonRenderer == null) { + noSkeletonRenderer = true; + } else if (skeletonRenderer != selectedSkeletonRenderer) { + + bone = null; + if (skeletonRenderer != null && skeletonRenderer.SkeletonDataAsset != selectedSkeletonRenderer.SkeletonDataAsset) + boneName = null; + + skeletonRenderer = selectedSkeletonRenderer; + skeletonRenderer.Initialize(false); + skeletonRenderer.LateUpdate(); + skeleton = skeletonRenderer.skeleton; + isPrefab |= PrefabUtility.GetPrefabType(selectedObject) == PrefabType.Prefab; + UpdateAttachments(); + } + } + + if (noSkeletonRenderer) Clear(); + Repaint(); + } + + void Clear () { + skeletonRenderer = null; + skeleton = null; + attachmentTable.Clear(); + isPrefab = false; + boneName = string.Empty; + bone = null; + SceneView.onSceneGUIDelegate -= this.OnSceneGUI; + } + + void OnDestroy () { + Clear(); + } + + static void FalseDropDown (string label, string stringValue, Texture2D icon = null, bool disabledGroup = false) { + if (disabledGroup) EditorGUI.BeginDisabledGroup(true); + var pos = EditorGUILayout.GetControlRect(true); + pos = EditorGUI.PrefixLabel(pos, SpineInspectorUtility.TempContent(label)); + GUI.Button(pos, SpineInspectorUtility.TempContent(stringValue, icon), EditorStyles.popup); + if (disabledGroup) EditorGUI.EndDisabledGroup(); + } + + // Window GUI + void OnGUI () { + bool requireRepaint = false; + + if (staticLostValues) { + Clear(); + OnSelectionChange(); + staticLostValues = false; + requireRepaint = true; + } + + if (SlotsRootLabel == null) { + SlotsRootLabel = new GUIContent("Slots", Icons.slotRoot); + SkeletonRootLabel = new GUIContent("Skeleton", Icons.skeleton); + BoldFoldoutStyle = new GUIStyle(EditorStyles.foldout); + BoldFoldoutStyle.fontStyle = FontStyle.Bold; + BoldFoldoutStyle.stretchWidth = true; + BoldFoldoutStyle.fixedWidth = 0; + } + + + EditorGUILayout.Space(); + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.ObjectField(SpineInspectorUtility.TempContent("Debug Selection", Icons.spine), skeletonRenderer, typeof(SkeletonRenderer), true); + EditorGUI.EndDisabledGroup(); + + if (skeleton == null || skeletonRenderer == null) { + EditorGUILayout.HelpBox("No SkeletonRenderer Spine GameObject selected.", MessageType.Info); + return; + } + + if (isPrefab) { + EditorGUILayout.HelpBox("SkeletonDebug only debugs Spine GameObjects in the scene.", MessageType.Warning); + return; + } + + if (!skeletonRenderer.valid) { + EditorGUILayout.HelpBox("Spine Component is invalid. Check SkeletonData Asset.", MessageType.Error); + return; + } + + if (activeSkin != skeleton.Skin) + UpdateAttachments(); + + scrollPos = EditorGUILayout.BeginScrollView(scrollPos); + + using (new SpineInspectorUtility.BoxScope(false)) { + if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetToSetupPose()"))) { + skeleton.SetToSetupPose(); + requireRepaint = true; + } + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.LabelField("Scene View", EditorStyles.boldLabel); + using (new SpineInspectorUtility.LabelWidthScope()) { + showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames); + showPaths = EditorGUILayout.Toggle("Show Paths", showPaths); + showShapes = EditorGUILayout.Toggle("Show Shapes", showShapes); + showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints); + } + requireRepaint |= EditorGUI.EndChangeCheck(); + + + // Skeleton + showSkeleton.target = EditorGUILayout.Foldout(showSkeleton.target, SkeletonRootLabel, BoldFoldoutStyle); + if (showSkeleton.faded > 0) { + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.FadeGroupScope(showSkeleton.faded)) { + EditorGUI.BeginChangeCheck(); + + EditorGUI.BeginDisabledGroup(true); + FalseDropDown(".Skin", skeleton.Skin != null ? skeletonRenderer.Skeleton.Skin.Name : "", Icons.skin); + EditorGUI.EndDisabledGroup(); + + // Flip + EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(160f)); + EditorGUILayout.LabelField("Flip", GUILayout.MaxWidth(EditorGUIUtility.labelWidth - 20f)); + skeleton.FlipX = EditorGUILayout.ToggleLeft(".FlipX", skeleton.FlipX, GUILayout.MaxWidth(70f)); + skeleton.FlipY = EditorGUILayout.ToggleLeft(".FlipY", skeleton.FlipY, GUILayout.MaxWidth(70f)); + GUILayout.EndHorizontal(); + + // Color + skeleton.SetColor(EditorGUILayout.ColorField(".R .G .B .A", skeleton.GetColor())); + + requireRepaint |= EditorGUI.EndChangeCheck(); + } + } + } + + // Bone + showInspectBoneTree.target = EditorGUILayout.Foldout(showInspectBoneTree.target, SpineInspectorUtility.TempContent("Bone", Icons.bone), BoldFoldoutStyle); + if (showInspectBoneTree.faded > 0) { + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.FadeGroupScope(showInspectBoneTree.faded)) { + showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames); + if (bpo == null) bpo = new SerializedObject(this).FindProperty("boneName"); + EditorGUILayout.PropertyField(bpo, SpineInspectorUtility.TempContent("Bone")); + if (!string.IsNullOrEmpty(bpo.stringValue)) { + if (bone == null || bone.Data.Name != bpo.stringValue) { + bone = skeleton.FindBone(bpo.stringValue); + } + + if (bone != null) { + using (new EditorGUI.DisabledGroupScope(true)) { + var wm = EditorGUIUtility.wideMode; + EditorGUIUtility.wideMode = true; + EditorGUILayout.Slider("Local Rotation", ViewRound(bone.Rotation), -180f, 180f); + EditorGUILayout.Vector2Field("Local Position", RoundVector2(bone.X, bone.Y)); + EditorGUILayout.Vector2Field("Local Scale", RoundVector2(bone.ScaleX, bone.ScaleY)); + EditorGUILayout.Vector2Field("Local Shear", RoundVector2(bone.ShearX, bone.ShearY)); + + EditorGUILayout.Space(); + + var boneParent = bone.Parent; + if (boneParent != null) FalseDropDown("Parent", boneParent.Data.Name, Icons.bone); + + const string RoundFormat = "0.##"; + var lw = EditorGUIUtility.labelWidth; + var fw = EditorGUIUtility.fieldWidth; + EditorGUIUtility.labelWidth *= 0.25f; + EditorGUIUtility.fieldWidth *= 0.5f; + EditorGUILayout.LabelField("LocalToWorld"); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.Space(); + EditorGUILayout.TextField(".A", bone.A.ToString(RoundFormat)); + EditorGUILayout.TextField(".B", bone.B.ToString(RoundFormat)); + EditorGUILayout.EndHorizontal(); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.Space(); + EditorGUILayout.TextField(".C", bone.C.ToString(RoundFormat)); + EditorGUILayout.TextField(".D", bone.D.ToString(RoundFormat)); + EditorGUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = lw * 0.5f; + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUILayout.TextField(".WorldX", bone.WorldX.ToString(RoundFormat)); + EditorGUILayout.TextField(".WorldY", bone.WorldY.ToString(RoundFormat)); + EditorGUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = lw; + EditorGUIUtility.fieldWidth = fw; + EditorGUIUtility.wideMode = wm; + + } + } + requireRepaint = true; + } else { + bone = null; + } + } + } + } + + // Slots + int preSlotsIndent = EditorGUI.indentLevel; + showSlotsTree.target = EditorGUILayout.Foldout(showSlotsTree.target, SlotsRootLabel, BoldFoldoutStyle); + if (showSlotsTree.faded > 0) { + using (new EditorGUILayout.FadeGroupScope(showSlotsTree.faded)) { + if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetSlotsToSetupPose()"))) { + skeleton.SetSlotsToSetupPose(); + requireRepaint = true; + } + + int baseIndent = EditorGUI.indentLevel; + foreach (KeyValuePair> pair in attachmentTable) { + Slot slot = pair.Key; + + using (new EditorGUILayout.HorizontalScope()) { + EditorGUI.indentLevel = baseIndent + 1; + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false)); + EditorGUI.BeginChangeCheck(); + Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60)); + if (EditorGUI.EndChangeCheck()) { + slot.SetColor(c); + requireRepaint = true; + } + } + + foreach (var attachment in pair.Value) { + GUI.contentColor = slot.Attachment == attachment ? Color.white : Color.grey; + EditorGUI.indentLevel = baseIndent + 2; + var icon = Icons.GetAttachmentIcon(attachment); + bool isAttached = (attachment == slot.Attachment); + bool swap = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachment.Name, icon), attachment == slot.Attachment); + if (isAttached != swap) { + slot.Attachment = isAttached ? null : attachment; + requireRepaint = true; + } + GUI.contentColor = Color.white; + } + } + } + } + EditorGUI.indentLevel = preSlotsIndent; + + // Constraints + const string NoneText = ""; + showConstraintsTree.target = EditorGUILayout.Foldout(showConstraintsTree.target, SpineInspectorUtility.TempContent("Constraints", Icons.constraintRoot), BoldFoldoutStyle); + if (showConstraintsTree.faded > 0) { + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.FadeGroupScope(showConstraintsTree.faded)) { + const float MixMin = 0f; + const float MixMax = 1f; + EditorGUI.BeginChangeCheck(); + showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints); + requireRepaint |= EditorGUI.EndChangeCheck(); + + EditorGUILayout.Space(); + + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("IK Constraints ({0})", skeleton.IkConstraints.Count), Icons.constraintIK), EditorStyles.boldLabel); + using (new SpineInspectorUtility.IndentScope()) { + if (skeleton.IkConstraints.Count > 0) { + foreach (var c in skeleton.IkConstraints) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintIK)); + FalseDropDown("Goal", c.Data.Target.Name, Icons.bone, true); + + EditorGUI.BeginChangeCheck(); + c.Mix = EditorGUILayout.Slider("Mix", c.Mix, MixMin, MixMax); + c.BendDirection = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bend Clockwise", tooltip: "IkConstraint.BendDirection == 1 if clockwise; -1 if counterclockwise."), c.BendDirection > 0) ? 1 : -1; + if (EditorGUI.EndChangeCheck()) requireRepaint = true; + + EditorGUILayout.Space(); + } + + } else { + EditorGUILayout.LabelField(NoneText); + } + } + + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Transform Constraints ({0})", skeleton.TransformConstraints.Count), Icons.constraintTransform), EditorStyles.boldLabel); + using (new SpineInspectorUtility.IndentScope()) { + if (skeleton.TransformConstraints.Count > 0) { + foreach (var c in skeleton.TransformConstraints) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintTransform)); + EditorGUI.BeginDisabledGroup(true); + FalseDropDown("Goal", c.Data.Target.Name, Icons.bone); + EditorGUI.EndDisabledGroup(); + + EditorGUI.BeginChangeCheck(); + c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax); + c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax); + c.ScaleMix = EditorGUILayout.Slider("ScaleMix", c.ScaleMix, MixMin, MixMax); + c.ShearMix = EditorGUILayout.Slider("ShearMix", c.ShearMix, MixMin, MixMax); + if (EditorGUI.EndChangeCheck()) requireRepaint = true; + + EditorGUILayout.Space(); + } + } else { + EditorGUILayout.LabelField(NoneText); + } + } + + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Path Constraints ({0})", skeleton.PathConstraints.Count), Icons.constraintPath), EditorStyles.boldLabel); + + EditorGUI.BeginChangeCheck(); + showPaths = EditorGUILayout.Toggle("Show Paths", showPaths); + requireRepaint |= EditorGUI.EndChangeCheck(); + + using (new SpineInspectorUtility.IndentScope()) { + if (skeleton.PathConstraints.Count > 0) { + foreach (var c in skeleton.PathConstraints) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintPath)); + EditorGUI.BeginDisabledGroup(true); + FalseDropDown("Path Slot", c.Data.Target.Name, Icons.slot); + var activeAttachment = c.Target.Attachment; + FalseDropDown("Active Path", activeAttachment != null ? activeAttachment.Name : "", activeAttachment is PathAttachment ? Icons.path : null); + EditorGUILayout.LabelField("PositionMode." + c.Data.PositionMode); + EditorGUILayout.LabelField("SpacingMode." + c.Data.SpacingMode); + EditorGUILayout.LabelField("RotateMode." + c.Data.RotateMode); + EditorGUI.EndDisabledGroup(); + + EditorGUI.BeginChangeCheck(); + c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax); + c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax); + c.Position = EditorGUILayout.FloatField("Position", c.Position); + c.Spacing = EditorGUILayout.FloatField("Spacing", c.Spacing); + if (EditorGUI.EndChangeCheck()) requireRepaint = true; + + EditorGUILayout.Space(); + } + + } else { + EditorGUILayout.LabelField(NoneText); + } + } + } + } + } + + showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, SpineInspectorUtility.TempContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle); + if (showDrawOrderTree.faded > 0) { + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.FadeGroupScope(showDrawOrderTree.faded)) { + + const string SeparatorString = "------------- v SEPARATOR v -------------"; + + if (Application.isPlaying) { + foreach (var slot in skeleton.DrawOrder) { + if (skeletonRenderer.separatorSlots.Contains(slot)) EditorGUILayout.LabelField(SeparatorString); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false)); + } + } else { + foreach (var slot in skeleton.DrawOrder) { + var slotNames = skeletonRenderer.separatorSlotNames; + for (int i = 0, n = slotNames.Length; i < n; i++) { + if (string.Equals(slotNames[i], slot.Data.Name, System.StringComparison.Ordinal)) { + EditorGUILayout.LabelField(SeparatorString); + break; + } + } + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false)); + } + } + + } + } + } + + showEventDataTree.target = EditorGUILayout.Foldout(showEventDataTree.target, SpineInspectorUtility.TempContent("Events", Icons.userEvent), BoldFoldoutStyle); + if (showEventDataTree.faded > 0) { + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.FadeGroupScope(showEventDataTree.faded)) { + if (skeleton.Data.Events.Count > 0) { + foreach (var e in skeleton.Data.Events) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(e.Name, Icons.userEvent)); + } + } else { + EditorGUILayout.LabelField(NoneText); + } + } + } + } + + showDataTree.target = EditorGUILayout.Foldout(showDataTree.target, SpineInspectorUtility.TempContent("Data Counts", Icons.spine), BoldFoldoutStyle); + if (showDataTree.faded > 0) { + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.FadeGroupScope(showDataTree.faded)) { + using (new SpineInspectorUtility.LabelWidthScope()) { + var skeletonData = skeleton.Data; + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bones", Icons.bone, "Skeleton.Data.Bones"), new GUIContent(skeletonData.Bones.Count.ToString())); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Slots", Icons.slotRoot, "Skeleton.Data.Slots"), new GUIContent(skeletonData.Slots.Count.ToString())); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins", Icons.skinsRoot, "Skeleton.Data.Skins"), new GUIContent(skeletonData.Skins.Count.ToString())); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Events", Icons.userEvent, "Skeleton.Data.Events"), new GUIContent(skeletonData.Events.Count.ToString())); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("IK Constraints", Icons.constraintIK, "Skeleton.Data.IkConstraints"), new GUIContent(skeletonData.IkConstraints.Count.ToString())); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Transform Constraints", Icons.constraintTransform, "Skeleton.Data.TransformConstraints"), new GUIContent(skeletonData.TransformConstraints.Count.ToString())); + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Path Constraints", Icons.constraintPath, "Skeleton.Data.PathConstraints"), new GUIContent(skeletonData.PathConstraints.Count.ToString())); + } + } + } + } + + if (IsAnimating(showSlotsTree, showSkeleton, showConstraintsTree, showDrawOrderTree, showEventDataTree, showInspectBoneTree, showDataTree)) + Repaint(); + } + + if (requireRepaint) { + skeletonRenderer.LateUpdate(); + Repaint(); + SceneView.RepaintAll(); + } + + EditorGUILayout.EndScrollView(); + } + + static float ViewRound (float x) { + const float Factor = 100f; + const float Divisor = 1f/Factor; + return Mathf.Round(x * Factor) * Divisor; + } + + static Vector2 RoundVector2 (float x, float y) { + const float Factor = 100f; + const float Divisor = 1f/Factor; + return new Vector2(Mathf.Round(x * Factor) * Divisor, Mathf.Round(y * Factor) * Divisor); + } + + static bool IsAnimating (params AnimBool[] animBools) { + foreach (var a in animBools) + if (a.isAnimating) return true; + return false; + } + + void UpdateAttachments () { + //skeleton = skeletonRenderer.skeleton; + Skin defaultSkin = skeleton.Data.DefaultSkin; + Skin skin = skeleton.Skin ?? defaultSkin; + bool notDefaultSkin = skin != defaultSkin; + + attachmentTable.Clear(); + for (int i = skeleton.Slots.Count - 1; i >= 0; i--) { + var attachments = new List(); + attachmentTable.Add(skeleton.Slots.Items[i], attachments); + skin.FindAttachmentsForSlot(i, attachments); // Add skin attachments. + if (notDefaultSkin) defaultSkin.FindAttachmentsForSlot(i, attachments); // Add default skin attachments. + } + + activeSkin = skeleton.Skin; + } + } +} diff --git a/Assets/Spine/spine-unity/Editor/SkeletonDebugWindow.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonDebugWindow.cs.meta new file mode 100644 index 0000000..d3697a0 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonDebugWindow.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7093e73ff3cf6c543ac5865980070b49 +timeCreated: 1494837950 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SkeletonRendererInspector.cs b/Assets/Spine/spine-unity/Editor/SkeletonRendererInspector.cs new file mode 100644 index 0000000..7faef12 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonRendererInspector.cs @@ -0,0 +1,398 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define NO_PREFAB_MESH + +using UnityEditor; +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity.Editor { + using Event = UnityEngine.Event; + using Icons = SpineEditorUtilities.Icons; + + [CustomEditor(typeof(SkeletonRenderer))] + [CanEditMultipleObjects] + public class SkeletonRendererInspector : UnityEditor.Editor { + public static bool advancedFoldout; + + protected SerializedProperty skeletonDataAsset, initialSkinName; + protected SerializedProperty initialFlipX, initialFlipY; + protected SerializedProperty singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles; + protected SerializedProperty normals, tangents, meshes, zSpacing, pmaVertexColors, tintBlack; // MeshGenerator settings + protected SpineInspectorUtility.SerializedSortingProperties sortingProperties; + protected bool isInspectingPrefab; + + protected GUIContent SkeletonDataAssetLabel, SkeletonUtilityButtonContent; + protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, MeshesLabel, ImmubleTrianglesLabel, TintBlackLabel, SingleSubmeshLabel; + protected GUIContent NormalsLabel, TangentsLabel; + const string ReloadButtonLabel = "Reload"; + + protected bool TargetIsValid { + get { + if (serializedObject.isEditingMultipleObjects) { + foreach (var o in targets) { + var component = (SkeletonRenderer)o; + if (!component.valid) + return false; + } + return true; + } else { + var component = (SkeletonRenderer)target; + return component.valid; + } + } + } + + protected virtual void OnEnable () { + isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab); + + SpineEditorUtilities.ConfirmInitialization(); + + // Labels + SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine); + SkeletonUtilityButtonContent = new GUIContent("Add Skeleton Utility", Icons.skeletonUtility); + MeshesLabel = new GUIContent("Render MeshAttachments", "Disable to optimize rendering for skeletons that don't use Mesh Attachments"); + ImmubleTrianglesLabel = new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"); + PMAVertexColorsLabel = new GUIContent("PMA Vertex Colors", "Use this if you are using the default Spine/Skeleton shader or any premultiply-alpha shader."); + ClearStateOnDisableLabel = new GUIContent("Clear State On Disable", "Use this if you are pooling or enabling/disabling your Spine GameObject."); + ZSpacingLabel = new GUIContent("Z Spacing", "A value other than 0 adds a space between each rendered attachment to prevent Z Fighting when using shaders that read or write to the depth buffer. Large values may cause unwanted parallax and spaces depending on camera setup."); + NormalsLabel = new GUIContent("Add Normals", "Use this if your shader requires vertex normals. A more efficient solution for 2D setups is to modify the shader to assume a single normal value for the whole mesh."); + TangentsLabel = new GUIContent("Solve Tangents", "Calculates the tangents per frame. Use this if you are using lit shaders (usually with normal maps) that require vertex tangents."); + TintBlackLabel = new GUIContent("Tint Black (!)", "Adds black tint vertex data to the mesh as UV2 and UV3. Black tinting requires that the shader interpret UV2 and UV3 as black tint colors for this effect to work. You may also use the default [Spine/Skeleton Tint Black] shader.\n\nIf you only need to tint the whole skeleton and not individual parts, the [Spine/Skeleton Tint] shader is recommended for better efficiency and changing/animating the _Black material property via MaterialPropertyBlock."); + SingleSubmeshLabel = new GUIContent("Use Single Submesh", "Simplifies submesh determination by assuming you are only using one Material and need only one submesh. This is will disable render separation and custom slot materials."); + + var so = this.serializedObject; + skeletonDataAsset = so.FindProperty("skeletonDataAsset"); + initialSkinName = so.FindProperty("initialSkinName"); + initialFlipX = so.FindProperty("initialFlipX"); + initialFlipY = so.FindProperty("initialFlipY"); + normals = so.FindProperty("addNormals"); + tangents = so.FindProperty("calculateTangents"); + meshes = so.FindProperty("renderMeshes"); + immutableTriangles = so.FindProperty("immutableTriangles"); + pmaVertexColors = so.FindProperty("pmaVertexColors"); + clearStateOnDisable = so.FindProperty("clearStateOnDisable"); + tintBlack = so.FindProperty("tintBlack"); + singleSubmesh = so.FindProperty("singleSubmesh"); + + separatorSlotNames = so.FindProperty("separatorSlotNames"); + separatorSlotNames.isExpanded = true; + + zSpacing = so.FindProperty("zSpacing"); + + SerializedObject rso = SpineInspectorUtility.GetRenderersSerializedObject(serializedObject); + sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(rso); + } + + public static void ReapplySeparatorSlotNames (SkeletonRenderer skeletonRenderer) { + if (!skeletonRenderer.valid) return; + + var separatorSlots = skeletonRenderer.separatorSlots; + var separatorSlotNames = skeletonRenderer.separatorSlotNames; + var skeleton = skeletonRenderer.skeleton; + + separatorSlots.Clear(); + for (int i = 0, n = separatorSlotNames.Length; i < n; i++) { + var slot = skeleton.FindSlot(separatorSlotNames[i]); + if (slot != null) { + separatorSlots.Add(slot); + } else { + Debug.LogWarning(separatorSlotNames[i] + " is not a slot in " + skeletonRenderer.skeletonDataAsset.skeletonJSON.name); + } + } + } + + GUIContent[] skins; + ExposedList loadedSkinList; + + protected virtual void DrawInspectorGUI (bool multi) { + bool valid = TargetIsValid; + var reloadWidth = GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonLabel)).x + 20); + var reloadButtonStyle = EditorStyles.miniButtonRight; + + if (multi) { + using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) { + SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel); + if (GUILayout.Button(ReloadButtonLabel, reloadButtonStyle, reloadWidth)) { + foreach (var c in targets) { + var component = c as SkeletonRenderer; + if (component.skeletonDataAsset != null) { + foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) { + if (aa != null) + aa.Clear(); + } + component.skeletonDataAsset.Clear(); + } + component.Initialize(true); + } + } + } + + foreach (var c in targets) { + var component = c as SkeletonRenderer; + if (!component.valid) { + if (Event.current.type == EventType.Layout) { + component.Initialize(true); + component.LateUpdate(); + } + if (!component.valid) + continue; + } + + #if NO_PREFAB_MESH + if (isInspectingPrefab) { + MeshFilter meshFilter = component.GetComponent(); + if (meshFilter != null && meshFilter.sharedMesh != null) + meshFilter.sharedMesh = null; + } + #endif + } + + if (valid) + EditorGUILayout.PropertyField(initialSkinName); + + } else { + var component = (SkeletonRenderer)target; + + if (!component.valid && Event.current.type == EventType.Layout) { + component.Initialize(true); + component.LateUpdate(); + } + + using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) { + SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel); + if (component.valid) { + if (GUILayout.Button(ReloadButtonLabel, reloadButtonStyle, reloadWidth)) { + if (component.skeletonDataAsset != null) { + foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) { + if (aa != null) + aa.Clear(); + } + component.skeletonDataAsset.Clear(); + } + component.Initialize(true); + } + } + } + + if (component.skeletonDataAsset == null) { + EditorGUILayout.HelpBox("Skeleton Data Asset required", MessageType.Warning); + return; + } + + #if NO_PREFAB_MESH + if (isInspectingPrefab) { + MeshFilter meshFilter = component.GetComponent(); + if (meshFilter != null && meshFilter.sharedMesh != null) + meshFilter.sharedMesh = null; + } + #endif + + // Initial skin name. + if (component.valid) { + var skeletonDataSkins = component.skeleton.Data.Skins; + int skinCount = skeletonDataSkins.Count; + if (loadedSkinList != skeletonDataSkins) { + skins = new GUIContent[skinCount]; + loadedSkinList = skeletonDataSkins; + for (int i = 0; i < skins.Length; i++) { + string skinNameString = skeletonDataSkins.Items[i].Name; + skins[i] = new GUIContent(skinNameString, Icons.skin); + } + } + + int skinIndex = 0; + for (int i = 0; i < skins.Length; i++) { + string skinNameString = skeletonDataSkins.Items[i].Name; + if (skinNameString == initialSkinName.stringValue) + skinIndex = i; + } + + skinIndex = EditorGUILayout.Popup(SpineInspectorUtility.TempContent("Initial Skin"), skinIndex, skins); + if (skins.Length > 0) // Support attachmentless/skinless SkeletonData. + initialSkinName.stringValue = skins[skinIndex].text; + } + } + + EditorGUILayout.Space(); + + // Sorting Layers + SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true); + + if (!TargetIsValid) return; + + // More Render Options... + using (new SpineInspectorUtility.BoxScope()) { + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5)); + advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced"); + if (advancedFoldout) { + EditorGUILayout.Space(); + if (GUILayout.Button("Debug", EditorStyles.miniButton, GUILayout.Width(65f))) + SkeletonDebugWindow.Init(); + } else { + EditorGUILayout.Space(); + } + EditorGUILayout.EndHorizontal(); + + if (advancedFoldout) { + + using (new SpineInspectorUtility.IndentScope()) { + using (new EditorGUILayout.HorizontalScope()) { + SpineInspectorUtility.ToggleLeftLayout(initialFlipX); + SpineInspectorUtility.ToggleLeftLayout(initialFlipY); + EditorGUILayout.Space(); + } + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Renderer Settings", EditorStyles.boldLabel); + using (new SpineInspectorUtility.LabelWidthScope()) { + // Optimization options + if (singleSubmesh != null) EditorGUILayout.PropertyField(singleSubmesh, SingleSubmeshLabel); + //if (meshes != null) EditorGUILayout.PropertyField(meshes, MeshesLabel); + if (immutableTriangles != null) EditorGUILayout.PropertyField(immutableTriangles, ImmubleTrianglesLabel); + EditorGUILayout.PropertyField(clearStateOnDisable, ClearStateOnDisableLabel); + EditorGUILayout.Space(); + } + + SeparatorsField(separatorSlotNames); + EditorGUILayout.Space(); + + // Render options + const float MinZSpacing = -0.1f; + const float MaxZSpacing = 0f; + EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing, ZSpacingLabel); + EditorGUILayout.Space(); + + using (new SpineInspectorUtility.LabelWidthScope()) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Vertex Data", SpineInspectorUtility.UnityIcon()), EditorStyles.boldLabel); + if (pmaVertexColors != null) EditorGUILayout.PropertyField(pmaVertexColors, PMAVertexColorsLabel); + EditorGUILayout.PropertyField(tintBlack, TintBlackLabel); + + // Optional fields. May be disabled in SkeletonRenderer. + if (normals != null) EditorGUILayout.PropertyField(normals, NormalsLabel); + if (tangents != null) EditorGUILayout.PropertyField(tangents, TangentsLabel); + } + + EditorGUILayout.Space(); + + if (TargetIsValid && !isInspectingPrefab) { + if (multi) { + // Support multi-edit SkeletonUtility button. + // EditorGUILayout.Space(); + // bool addSkeletonUtility = GUILayout.Button(buttonContent, GUILayout.Height(30)); + // foreach (var t in targets) { + // var component = t as Component; + // if (addSkeletonUtility && component.GetComponent() == null) + // component.gameObject.AddComponent(); + // } + } else { + var component = (Component)target; + if (component.GetComponent() == null) { + if (SpineInspectorUtility.CenteredButton(SkeletonUtilityButtonContent, 21, true, 200f)) + component.gameObject.AddComponent(); + } + } + } + + EditorGUILayout.Space(); + } + } + + if (EditorGUI.EndChangeCheck()) + SceneView.RepaintAll(); + } + } + + public static void SeparatorsField (SerializedProperty separatorSlotNames) { + bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects; + bool hasTerminalSlot = false; + if (!multi) { + var sr = separatorSlotNames.serializedObject.targetObject as ISkeletonComponent; + var skeleton = sr.Skeleton; + int lastSlot = skeleton.Slots.Count - 1; + if (skeleton != null) { + for (int i = 0, n = separatorSlotNames.arraySize; i < n; i++) { + int index = skeleton.FindSlotIndex(separatorSlotNames.GetArrayElementAtIndex(i).stringValue); + if (index == 0 || index == lastSlot) { + hasTerminalSlot = true; + break; + } + } + } + } + + string terminalSlotWarning = hasTerminalSlot ? " (!)" : ""; + + using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { + const string SeparatorsDescription = "Stored names of slots where the Skeleton's render will be split into different batches. This is used by separate components that split the render into different MeshRenderers or GameObjects."; + if (separatorSlotNames.isExpanded) { + EditorGUILayout.PropertyField(separatorSlotNames, SpineInspectorUtility.TempContent(separatorSlotNames.displayName + terminalSlotWarning, Icons.slotRoot, SeparatorsDescription), true); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("+", GUILayout.MaxWidth(28f), GUILayout.MaxHeight(15f))) { + separatorSlotNames.arraySize++; + } + GUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + } else + EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format("{0} [{1}]", terminalSlotWarning, separatorSlotNames.arraySize), SeparatorsDescription), true); + } + } + + public void OnSceneGUI () { + var skeletonRenderer = (SkeletonRenderer)target; + var skeleton = skeletonRenderer.skeleton; + var transform = skeletonRenderer.transform; + if (skeleton == null) return; + + SpineHandles.DrawBones(transform, skeleton); + } + + override public void OnInspectorGUI () { + //serializedObject.Update(); + bool multi = serializedObject.isEditingMultipleObjects; + DrawInspectorGUI(multi); + if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) { + if (!Application.isPlaying) { + if (multi) + foreach (var o in targets) + ((SkeletonRenderer)o).Initialize(true); + else + ((SkeletonRenderer)target).Initialize(true); + } + } + } + + } +} diff --git a/Assets/Spine/spine-unity/Editor/SkeletonRendererInspector.cs.meta b/Assets/Spine/spine-unity/Editor/SkeletonRendererInspector.cs.meta new file mode 100644 index 0000000..d7791c6 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SkeletonRendererInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d0fc5db9788bce4418ad3252d43faa8a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SpineAttributeDrawers.cs b/Assets/Spine/spine-unity/Editor/SpineAttributeDrawers.cs new file mode 100644 index 0000000..20b6ff6 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SpineAttributeDrawers.cs @@ -0,0 +1,557 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using UnityEditor; +using System; +using System.Collections.Generic; +using System.Reflection; +using Spine; + +namespace Spine.Unity.Editor { + public struct SpineDrawerValuePair { + public string str; + public SerializedProperty property; + + public SpineDrawerValuePair (string val, SerializedProperty property) { + this.str = val; + this.property = property; + } + } + + public abstract class SpineTreeItemDrawerBase : PropertyDrawer where T:SpineAttributeBase { + protected SkeletonDataAsset skeletonDataAsset; + internal const string NoneString = ""; + + // Analysis disable once StaticFieldInGenericType + static GUIContent noneLabel; + static GUIContent NoneLabel (Texture2D image = null) { + if (noneLabel == null) + noneLabel = new GUIContent(NoneString); + noneLabel.image = image; + return noneLabel; + } + + protected T TargetAttribute { get { return (T)attribute; } } + protected SerializedProperty SerializedProperty { get; private set; } + + protected abstract Texture2D Icon { get; } + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + SerializedProperty = property; + + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + SerializedProperty dataField = property.FindBaseOrSiblingProperty(TargetAttribute.dataField); + + if (dataField != null) { + var objectReferenceValue = dataField.objectReferenceValue; + if (objectReferenceValue is SkeletonDataAsset) { + skeletonDataAsset = (SkeletonDataAsset)objectReferenceValue; + } else if (objectReferenceValue is IHasSkeletonDataAsset) { + var hasSkeletonDataAsset = (IHasSkeletonDataAsset)objectReferenceValue; + if (hasSkeletonDataAsset != null) + skeletonDataAsset = hasSkeletonDataAsset.SkeletonDataAsset; + } else if (objectReferenceValue != null) { + EditorGUI.LabelField(position, "ERROR:", "Invalid reference type"); + return; + } + + } else { + var targetObject = property.serializedObject.targetObject; + + IHasSkeletonDataAsset hasSkeletonDataAsset = targetObject as IHasSkeletonDataAsset; + if (hasSkeletonDataAsset == null) { + var component = targetObject as Component; + if (component != null) + hasSkeletonDataAsset = component.GetComponentInChildren(typeof(IHasSkeletonDataAsset)) as IHasSkeletonDataAsset; + } + + if (hasSkeletonDataAsset != null) + skeletonDataAsset = hasSkeletonDataAsset.SkeletonDataAsset; + } + + if (skeletonDataAsset == null) { + if (TargetAttribute.fallbackToTextField) { + EditorGUI.PropertyField(position, property); //EditorGUI.TextField(position, label, property.stringValue); + } else { + EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset"); + } + + skeletonDataAsset = property.serializedObject.targetObject as SkeletonDataAsset; + if (skeletonDataAsset == null) return; + } + + position = EditorGUI.PrefixLabel(position, label); + + var image = Icon; + var propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue; + if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : SpineInspectorUtility.TempContent(propertyStringValue, image), EditorStyles.popup)) + Selector(property); + } + + public ISkeletonComponent GetTargetSkeletonComponent (SerializedProperty property) { + var dataField = property.FindBaseOrSiblingProperty(TargetAttribute.dataField); + + if (dataField != null) { + var skeletonComponent = dataField.objectReferenceValue as ISkeletonComponent; + if (dataField.objectReferenceValue != null && skeletonComponent != null) // note the overloaded UnityEngine.Object == null check. Do not simplify. + return skeletonComponent; + } else { + var component = property.serializedObject.targetObject as Component; + if (component != null) + return component.GetComponentInChildren(typeof(ISkeletonComponent)) as ISkeletonComponent; + } + + return null; + } + + protected virtual void Selector (SerializedProperty property) { + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) return; + + var menu = new GenericMenu(); + PopulateMenu(menu, property, this.TargetAttribute, data); + menu.ShowAsContext(); + } + + protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data); + + protected virtual void HandleSelect (object menuItemObject) { + var clickedItem = (SpineDrawerValuePair)menuItemObject; + clickedItem.property.stringValue = clickedItem.str; + clickedItem.property.serializedObject.ApplyModifiedProperties(); + } + + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { + return 18; + } + + } + + [CustomPropertyDrawer(typeof(SpineSlot))] + public class SpineSlotDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.slot; } } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) { + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < data.Slots.Count; i++) { + string name = data.Slots.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) { + + if (targetAttribute.containsBoundingBoxes) { + int slotIndex = i; + var attachments = new List(); + foreach (var skin in data.Skins) + skin.FindAttachmentsForSlot(slotIndex, attachments); + + bool hasBoundingBox = false; + foreach (var attachment in attachments) { + var bbAttachment = attachment as BoundingBoxAttachment; + if (bbAttachment != null) { + string menuLabel = bbAttachment.IsWeighted() ? name + " (!)" : name; + menu.AddItem(new GUIContent(menuLabel), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + hasBoundingBox = true; + break; + } + } + + if (!hasBoundingBox) + menu.AddDisabledItem(new GUIContent(name)); + + } else { + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + } + + } + } + + } + + [CustomPropertyDrawer(typeof(SpineSkin))] + public class SpineSkinDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.skin; } } + + public static void GetSkinMenuItems (SkeletonData data, List animationNames, List menuItems, bool includeNone = true) { + if (data == null) return; + + var skins = data.Skins; + + animationNames.Clear(); + menuItems.Clear(); + + var icon = SpineEditorUtilities.Icons.skin; + + if (includeNone) { + animationNames.Add(""); + menuItems.Add(new GUIContent(NoneString, icon)); + } + + foreach (var s in skins) { + var skinName = s.Name; + animationNames.Add(skinName); + menuItems.Add(new GUIContent(skinName, icon)); + } + } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) { + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + for (int i = 0; i < data.Skins.Count; i++) { + string name = data.Skins.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineAnimation))] + public class SpineAnimationDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.animation; } } + + public static void GetAnimationMenuItems (SkeletonData data, List animationNames, List menuItems, bool includeNone = true) { + if (data == null) return; + + var animations = data.Animations; + + animationNames.Clear(); + menuItems.Clear(); + + if (includeNone) { + animationNames.Add(""); + menuItems.Add(new GUIContent(NoneString, SpineEditorUtilities.Icons.animation)); + } + + foreach (var a in animations) { + var animationName = a.Name; + animationNames.Add(animationName); + menuItems.Add(new GUIContent(animationName, SpineEditorUtilities.Icons.animation)); + } + } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) { + var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < animations.Count; i++) { + string name = animations.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineEvent))] + public class SpineEventNameDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.userEvent; } } + + public static void GetEventMenuItems (SkeletonData data, List eventNames, List menuItems, bool includeNone = true) { + if (data == null) return; + + var animations = data.Events; + + eventNames.Clear(); + menuItems.Clear(); + + if (includeNone) { + eventNames.Add(""); + menuItems.Add(new GUIContent(NoneString, SpineEditorUtilities.Icons.userEvent)); + } + + foreach (var a in animations) { + var animationName = a.Name; + eventNames.Add(animationName); + menuItems.Add(new GUIContent(animationName, SpineEditorUtilities.Icons.userEvent)); + } + } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) { + var events = skeletonDataAsset.GetSkeletonData(false).Events; + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < events.Count; i++) { + string name = events.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineIkConstraint))] + public class SpineIkConstraintDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.constraintIK; } } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineIkConstraint targetAttribute, SkeletonData data) { + var constraints = skeletonDataAsset.GetSkeletonData(false).IkConstraints; + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < constraints.Count; i++) { + string name = constraints.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineTransformConstraint))] + public class SpineTransformConstraintDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.constraintTransform; } } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineTransformConstraint targetAttribute, SkeletonData data) { + var constraints = skeletonDataAsset.GetSkeletonData(false).TransformConstraints; + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < constraints.Count; i++) { + string name = constraints.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + } + + [CustomPropertyDrawer(typeof(SpinePathConstraint))] + public class SpinePathConstraintDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.constraintPath; } } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpinePathConstraint targetAttribute, SkeletonData data) { + var constraints = skeletonDataAsset.GetSkeletonData(false).PathConstraints; + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < constraints.Count; i++) { + string name = constraints.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + } + + [CustomPropertyDrawer(typeof(SpineAttachment))] + public class SpineAttachmentDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.genericAttachment; } } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) { + ISkeletonComponent skeletonComponent = GetTargetSkeletonComponent(property); + var validSkins = new List(); + + if (skeletonComponent != null && targetAttribute.currentSkinOnly) { + Skin currentSkin = null; + + var skinProperty = property.FindBaseOrSiblingProperty(targetAttribute.skinField); + if (skinProperty != null) currentSkin = skeletonComponent.Skeleton.Data.FindSkin(skinProperty.stringValue); + + currentSkin = currentSkin ?? skeletonComponent.Skeleton.Skin; + if (currentSkin != null) + validSkins.Add(currentSkin); + else + validSkins.Add(data.Skins.Items[0]); + + } else { + foreach (Skin skin in data.Skins) + if (skin != null) validSkins.Add(skin); + } + + var attachmentNames = new List(); + var placeholderNames = new List(); + string prefix = ""; + + if (skeletonComponent != null && targetAttribute.currentSkinOnly) + menu.AddDisabledItem(new GUIContent((skeletonComponent as Component).gameObject.name + " (Skeleton)")); + else + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + + menu.AddSeparator(""); + if (TargetAttribute.includeNone) { + const string NullAttachmentName = ""; + menu.AddItem(new GUIContent("Null"), property.stringValue == NullAttachmentName, HandleSelect, new SpineDrawerValuePair(NullAttachmentName, property)); + menu.AddSeparator(""); + } + + Skin defaultSkin = data.Skins.Items[0]; + var slotProperty = property.FindBaseOrSiblingProperty(TargetAttribute.slotField); + + string slotMatch = ""; + if (slotProperty != null) { + if (slotProperty.propertyType == SerializedPropertyType.String) + slotMatch = slotProperty.stringValue.ToLower(); + } + + foreach (Skin skin in validSkins) { + string skinPrefix = skin.Name + "/"; + + if (validSkins.Count > 1) + prefix = skinPrefix; + + for (int i = 0; i < data.Slots.Count; i++) { + if (slotMatch.Length > 0 && !(data.Slots.Items[i].Name.Equals(slotMatch, StringComparison.OrdinalIgnoreCase))) + continue; + + attachmentNames.Clear(); + placeholderNames.Clear(); + + skin.FindNamesForSlot(i, attachmentNames); + if (skin != defaultSkin) { + defaultSkin.FindNamesForSlot(i, attachmentNames); + skin.FindNamesForSlot(i, placeholderNames); + } + + for (int a = 0; a < attachmentNames.Count; a++) { + string attachmentPath = attachmentNames[a]; + string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath; + string name = attachmentNames[a]; + + if (targetAttribute.returnAttachmentPath) + name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath; + + if (targetAttribute.placeholdersOnly && !placeholderNames.Contains(attachmentPath)) { + menu.AddDisabledItem(new GUIContent(menuPath)); + } else { + menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + } + } + + } + + [CustomPropertyDrawer(typeof(SpineBone))] + public class SpineBoneDrawer : SpineTreeItemDrawerBase { + + protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.bone; } } + + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) { + menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); + menu.AddSeparator(""); + + if (TargetAttribute.includeNone) + menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); + + for (int i = 0; i < data.Bones.Count; i++) { + string name = data.Bones.Items[i].Name; + if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal)) + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + } + + } + + [CustomPropertyDrawer(typeof(SpineAtlasRegion))] + public class SpineAtlasRegionDrawer : PropertyDrawer { + SerializedProperty atlasProp; + + protected SpineAtlasRegion TargetAttribute { get { return (SpineAtlasRegion)attribute; } } + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + if (property.propertyType != SerializedPropertyType.String) { + EditorGUI.LabelField(position, "ERROR:", "May only apply to type string"); + return; + } + + string atlasAssetFieldName = TargetAttribute.atlasAssetField; + if (string.IsNullOrEmpty(atlasAssetFieldName)) + atlasAssetFieldName = "atlasAsset"; + + atlasProp = property.FindBaseOrSiblingProperty(atlasAssetFieldName); + + if (atlasProp == null) { + EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!"); + return; + } else if (atlasProp.objectReferenceValue == null) { + EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!"); + return; + } else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) { + EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!"); + } + + position = EditorGUI.PrefixLabel(position, label); + + if (GUI.Button(position, property.stringValue, EditorStyles.popup)) + Selector(property); + + } + + void Selector (SerializedProperty property) { + GenericMenu menu = new GenericMenu(); + AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue; + Atlas atlas = atlasAsset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + + for (int i = 0; i < regions.Count; i++) { + string name = regions[i].name; + menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); + } + + menu.ShowAsContext(); + } + + static void HandleSelect (object val) { + var pair = (SpineDrawerValuePair)val; + pair.property.stringValue = pair.str; + pair.property.serializedObject.ApplyModifiedProperties(); + } + + } + +} diff --git a/Assets/Spine/spine-unity/Editor/SpineAttributeDrawers.cs.meta b/Assets/Spine/spine-unity/Editor/SpineAttributeDrawers.cs.meta new file mode 100644 index 0000000..901111a --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SpineAttributeDrawers.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f2de282d583d4a641bf1c349f0a3eef9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Editor/SpineEditorUtilities.cs b/Assets/Spine/spine-unity/Editor/SpineEditorUtilities.cs new file mode 100644 index 0000000..28ed6bd --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SpineEditorUtilities.cs @@ -0,0 +1,2029 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#pragma warning disable 0219 + +// Original contribution by: Mitch Thompson + +#define SPINE_SKELETONANIMATOR +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Linq; +using System.Reflection; +using Spine; + +namespace Spine.Unity.Editor { + using EventType = UnityEngine.EventType; + + // Analysis disable once ConvertToStaticType + [InitializeOnLoad] + public class SpineEditorUtilities : AssetPostprocessor { + + public static class Icons { + public static Texture2D skeleton; + public static Texture2D nullBone; + public static Texture2D bone; + public static Texture2D poseBones; + public static Texture2D boneNib; + public static Texture2D slot; + public static Texture2D slotRoot; + public static Texture2D skinPlaceholder; + public static Texture2D image; + public static Texture2D genericAttachment; + public static Texture2D boundingBox; + public static Texture2D point; + public static Texture2D mesh; + public static Texture2D weights; + public static Texture2D path; + public static Texture2D clipping; + public static Texture2D skin; + public static Texture2D skinsRoot; + public static Texture2D animation; + public static Texture2D animationRoot; + public static Texture2D spine; + public static Texture2D userEvent; + public static Texture2D constraintNib; + public static Texture2D constraintRoot; + public static Texture2D constraintTransform; + public static Texture2D constraintPath; + public static Texture2D constraintIK; + public static Texture2D warning; + public static Texture2D skeletonUtility; + public static Texture2D hingeChain; + public static Texture2D subMeshRenderer; + public static Texture2D skeletonDataAssetIcon; + public static Texture2D info; + public static Texture2D unity; +// public static Texture2D controllerIcon; + + static Texture2D LoadIcon (string filename) { + return (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/" + filename); + } + + public static void Initialize () { + skeleton = LoadIcon("icon-skeleton.png"); + nullBone = LoadIcon("icon-null.png"); + bone = LoadIcon("icon-bone.png"); + poseBones = LoadIcon("icon-poseBones.png"); + boneNib = LoadIcon("icon-boneNib.png"); + slot = LoadIcon("icon-slot.png"); + slotRoot = LoadIcon("icon-slotRoot.png"); + skinPlaceholder = LoadIcon("icon-skinPlaceholder.png"); + + genericAttachment = LoadIcon("icon-attachment.png"); + image = LoadIcon("icon-image.png"); + boundingBox = LoadIcon("icon-boundingBox.png"); + point = LoadIcon("icon-point.png"); + mesh = LoadIcon("icon-mesh.png"); + weights = LoadIcon("icon-weights.png"); + path = LoadIcon("icon-path.png"); + clipping = LoadIcon("icon-clipping.png"); + + skin = LoadIcon("icon-skin.png"); + skinsRoot = LoadIcon("icon-skinsRoot.png"); + animation = LoadIcon("icon-animation.png"); + animationRoot = LoadIcon("icon-animationRoot.png"); + spine = LoadIcon("icon-spine.png"); + userEvent = LoadIcon("icon-event.png"); + constraintNib = LoadIcon("icon-constraintNib.png"); + + constraintRoot = LoadIcon("icon-constraints.png"); + constraintTransform = LoadIcon("icon-constraintTransform.png"); + constraintPath = LoadIcon("icon-constraintPath.png"); + constraintIK = LoadIcon("icon-constraintIK.png"); + + warning = LoadIcon("icon-warning.png"); + skeletonUtility = LoadIcon("icon-skeletonUtility.png"); + hingeChain = LoadIcon("icon-hingeChain.png"); + subMeshRenderer = LoadIcon("icon-subMeshRenderer.png"); + + skeletonDataAssetIcon = LoadIcon("SkeletonDataAsset Icon.png"); + + info = EditorGUIUtility.FindTexture("console.infoicon.sml"); + unity = EditorGUIUtility.FindTexture("SceneAsset Icon"); +// controllerIcon = EditorGUIUtility.FindTexture("AnimatorController Icon"); + } + + public static Texture2D GetAttachmentIcon (Attachment attachment) { + // Analysis disable once CanBeReplacedWithTryCastAndCheckForNull + if (attachment is RegionAttachment) + return Icons.image; + else if (attachment is MeshAttachment) + return ((MeshAttachment)attachment).IsWeighted() ? Icons.weights : Icons.mesh; + else if (attachment is BoundingBoxAttachment) + return Icons.boundingBox; + else if (attachment is PointAttachment) + return Icons.point; + else if (attachment is PathAttachment) + return Icons.path; + else if (attachment is ClippingAttachment) + return Icons.clipping; + else + return Icons.warning; + } + } + + public static string editorPath = ""; + public static string editorGUIPath = ""; + public static bool initialized; + + /// HACK: This list keeps the asset reference temporarily during importing. + /// + /// In cases of very large projects/sufficient RAM pressure, when AssetDatabase.SaveAssets is called, + /// Unity can mistakenly unload assets whose references are only on the stack. + /// This leads to MissingReferenceException and other errors. + static readonly List protectFromStackGarbageCollection = new List(); + static HashSet assetsImportedInWrongState = new HashSet(); + + #if SPINE_TK2D + const float DEFAULT_DEFAULT_SCALE = 1f; + #else + const float DEFAULT_DEFAULT_SCALE = 0.01f; + #endif + const string DEFAULT_SCALE_KEY = "SPINE_DEFAULT_SCALE"; + public static float defaultScale = DEFAULT_DEFAULT_SCALE; + + const float DEFAULT_DEFAULT_MIX = 0.2f; + const string DEFAULT_MIX_KEY = "SPINE_DEFAULT_MIX"; + public static float defaultMix = DEFAULT_DEFAULT_MIX; + + const string DEFAULT_DEFAULT_SHADER = "Spine/Skeleton"; + const string DEFAULT_SHADER_KEY = "SPINE_DEFAULT_SHADER"; + public static string defaultShader = DEFAULT_DEFAULT_SHADER; + + const float DEFAULT_DEFAULT_ZSPACING = 0f; + const string DEFAULT_ZSPACING_KEY = "SPINE_DEFAULT_ZSPACING"; + public static float defaultZSpacing = DEFAULT_DEFAULT_ZSPACING; + + const bool DEFAULT_SHOW_HIERARCHY_ICONS = true; + const string SHOW_HIERARCHY_ICONS_KEY = "SPINE_SHOW_HIERARCHY_ICONS"; + public static bool showHierarchyIcons = DEFAULT_SHOW_HIERARCHY_ICONS; + + const bool DEFAULT_SET_TEXTUREIMPORTER_SETTINGS = true; + const string SET_TEXTUREIMPORTER_SETTINGS_KEY = "SPINE_SET_TEXTUREIMPORTER_SETTINGS"; + public static bool setTextureImporterSettings = DEFAULT_SET_TEXTUREIMPORTER_SETTINGS; + + internal const float DEFAULT_MIPMAPBIAS = -0.5f; + + public const float DEFAULT_SCENE_ICONS_SCALE = 1f; + public const string SCENE_ICONS_SCALE_KEY = "SPINE_SCENE_ICONS_SCALE"; + + #region Initialization + static SpineEditorUtilities () { + Initialize(); + } + + static void LoadPreferences () { + defaultMix = EditorPrefs.GetFloat(DEFAULT_MIX_KEY, DEFAULT_DEFAULT_MIX); + defaultScale = EditorPrefs.GetFloat(DEFAULT_SCALE_KEY, DEFAULT_DEFAULT_SCALE); + defaultZSpacing = EditorPrefs.GetFloat(DEFAULT_ZSPACING_KEY, DEFAULT_DEFAULT_ZSPACING); + defaultShader = EditorPrefs.GetString(DEFAULT_SHADER_KEY, DEFAULT_DEFAULT_SHADER); + showHierarchyIcons = EditorPrefs.GetBool(SHOW_HIERARCHY_ICONS_KEY, DEFAULT_SHOW_HIERARCHY_ICONS); + setTextureImporterSettings = EditorPrefs.GetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, DEFAULT_SET_TEXTUREIMPORTER_SETTINGS); + SpineHandles.handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, DEFAULT_SCENE_ICONS_SCALE); + preferencesLoaded = true; + } + + static void Initialize () { + LoadPreferences(); + + DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath); + FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories); + editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets")); + editorGUIPath = editorPath + "/GUI"; + + Icons.Initialize(); + + // Drag and Drop + SceneView.onSceneGUIDelegate -= SceneViewDragAndDrop; + SceneView.onSceneGUIDelegate += SceneViewDragAndDrop; + EditorApplication.hierarchyWindowItemOnGUI -= SpineEditorHierarchyHandler.HierarchyDragAndDrop; + EditorApplication.hierarchyWindowItemOnGUI += SpineEditorHierarchyHandler.HierarchyDragAndDrop; + + // Hierarchy Icons + #if UNITY_2017_2_OR_NEWER + EditorApplication.playModeStateChanged -= SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged; + EditorApplication.playModeStateChanged += SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged; + SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode); + #else + EditorApplication.playmodeStateChanged -= SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged; + EditorApplication.playmodeStateChanged += SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged; + SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged(); + #endif + + initialized = true; + } + + public static void ConfirmInitialization () { + if (!initialized || Icons.skeleton == null) + Initialize(); + } + #endregion + + #region Spine Preferences and Defaults + static bool preferencesLoaded = false; + + [PreferenceItem("Spine")] + static void PreferencesGUI () { + if (!preferencesLoaded) + LoadPreferences(); + + EditorGUI.BeginChangeCheck(); + showHierarchyIcons = EditorGUILayout.Toggle(new GUIContent("Show Hierarchy Icons", "Show relevant icons on GameObjects with Spine Components on them. Disable this if you have large, complex scenes."), showHierarchyIcons); + if (EditorGUI.EndChangeCheck()) { + EditorPrefs.SetBool(SHOW_HIERARCHY_ICONS_KEY, showHierarchyIcons); + #if UNITY_2017_2_OR_NEWER + SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode); + #else + SpineEditorHierarchyHandler.HierarchyIconsOnPlaymodeStateChanged(); + #endif + } + + EditorGUILayout.Separator(); + + EditorGUILayout.LabelField("Auto-Import Settings", EditorStyles.boldLabel); + + EditorGUI.BeginChangeCheck(); + defaultMix = EditorGUILayout.FloatField("Default Mix", defaultMix); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetFloat(DEFAULT_MIX_KEY, defaultMix); + + EditorGUI.BeginChangeCheck(); + defaultScale = EditorGUILayout.FloatField("Default SkeletonData Scale", defaultScale); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetFloat(DEFAULT_SCALE_KEY, defaultScale); + + EditorGUI.BeginChangeCheck(); + var shader = (EditorGUILayout.ObjectField("Default Shader", Shader.Find(defaultShader), typeof(Shader), false) as Shader); + defaultShader = shader != null ? shader.name : DEFAULT_DEFAULT_SHADER; + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetString(DEFAULT_SHADER_KEY, defaultShader); + + EditorGUI.BeginChangeCheck(); + setTextureImporterSettings = EditorGUILayout.Toggle(new GUIContent("Apply Atlas Texture Settings", "Apply the recommended settings for Texture Importers."), setTextureImporterSettings); + if (EditorGUI.EndChangeCheck()) { + EditorPrefs.SetBool(SET_TEXTUREIMPORTER_SETTINGS_KEY, setTextureImporterSettings); + } + + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Editor Instantiation", EditorStyles.boldLabel); + EditorGUI.BeginChangeCheck(); + defaultZSpacing = EditorGUILayout.Slider("Default Slot Z-Spacing", defaultZSpacing, -0.1f, 0f); + if (EditorGUI.EndChangeCheck()) + EditorPrefs.SetFloat(DEFAULT_ZSPACING_KEY, defaultZSpacing); + + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Handles and Gizmos", EditorStyles.boldLabel); + EditorGUI.BeginChangeCheck(); + SpineHandles.handleScale = EditorGUILayout.Slider("Editor Bone Scale", SpineHandles.handleScale, 0.01f, 2f); + SpineHandles.handleScale = Mathf.Max(0.01f, SpineHandles.handleScale); + if (EditorGUI.EndChangeCheck()) { + EditorPrefs.SetFloat(SCENE_ICONS_SCALE_KEY, SpineHandles.handleScale); + SceneView.RepaintAll(); + } + + + GUILayout.Space(20); + EditorGUILayout.LabelField("3rd Party Settings", EditorStyles.boldLabel); + using (new GUILayout.HorizontalScope()) { + EditorGUILayout.PrefixLabel("Define TK2D"); + if (GUILayout.Button("Enable", GUILayout.Width(64))) + SpineTK2DEditorUtility.EnableTK2D(); + if (GUILayout.Button("Disable", GUILayout.Width(64))) + SpineTK2DEditorUtility.DisableTK2D(); + } + } + #endregion + + #region Drag and Drop Instantiation + public delegate Component InstantiateDelegate (SkeletonDataAsset skeletonDataAsset); + + public struct SpawnMenuData { + public Vector3 spawnPoint; + public SkeletonDataAsset skeletonDataAsset; + public InstantiateDelegate instantiateDelegate; + public bool isUI; + } + + public class SkeletonComponentSpawnType { + public string menuLabel; + public InstantiateDelegate instantiateDelegate; + public bool isUI; + } + + internal static readonly List additionalSpawnTypes = new List(); + + static void SceneViewDragAndDrop (SceneView sceneview) { + var current = UnityEngine.Event.current; + var references = DragAndDrop.objectReferences; + if (current.type == EventType.Layout) return; + + // Allow drag and drop of one SkeletonDataAsset. + if (references.Length == 1) { + var skeletonDataAsset = references[0] as SkeletonDataAsset; + if (skeletonDataAsset != null) { + var mousePos = current.mousePosition; + + bool invalidSkeletonData = skeletonDataAsset.GetSkeletonData(true) == null; + if (invalidSkeletonData) { + DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; + Handles.BeginGUI(); + GUI.Label(new Rect(mousePos + new Vector2(20f, 20f), new Vector2(400f, 40f)), new GUIContent(string.Format("{0} is invalid.\nCannot create new Spine GameObject.", skeletonDataAsset.name), SpineEditorUtilities.Icons.warning)); + Handles.EndGUI(); + return; + } else { + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + Handles.BeginGUI(); + GUI.Label(new Rect(mousePos + new Vector2(20f, 20f), new Vector2(400f, 20f)), new GUIContent(string.Format("Create Spine GameObject ({0})", skeletonDataAsset.skeletonJSON.name), SpineEditorUtilities.Icons.skeletonDataAssetIcon)); + Handles.EndGUI(); + + if (current.type == EventType.DragPerform) { + RectTransform rectTransform = (Selection.activeGameObject == null) ? null : Selection.activeGameObject.GetComponent(); + Plane plane = (rectTransform == null) ? new Plane(Vector3.back, Vector3.zero) : new Plane(-rectTransform.forward, rectTransform.position); + Vector3 spawnPoint = MousePointToWorldPoint2D(mousePos, sceneview.camera, plane); + ShowInstantiateContextMenu(skeletonDataAsset, spawnPoint); + DragAndDrop.AcceptDrag(); + current.Use(); + } + } + } + } + } + + public static void ShowInstantiateContextMenu (SkeletonDataAsset skeletonDataAsset, Vector3 spawnPoint) { + var menu = new GenericMenu(); + + // SkeletonAnimation + menu.AddItem(new GUIContent("SkeletonAnimation"), false, HandleSkeletonComponentDrop, new SpawnMenuData { + skeletonDataAsset = skeletonDataAsset, + spawnPoint = spawnPoint, + instantiateDelegate = (data) => InstantiateSkeletonAnimation(data), + isUI = false + }); + + // SkeletonGraphic + var skeletonGraphicInspectorType = System.Type.GetType("Spine.Unity.Editor.SkeletonGraphicInspector"); + if (skeletonGraphicInspectorType != null) { + var graphicInstantiateDelegate = skeletonGraphicInspectorType.GetMethod("SpawnSkeletonGraphicFromDrop", BindingFlags.Static | BindingFlags.Public); + if (graphicInstantiateDelegate != null) + menu.AddItem(new GUIContent("SkeletonGraphic (UI)"), false, HandleSkeletonComponentDrop, new SpawnMenuData { + skeletonDataAsset = skeletonDataAsset, + spawnPoint = spawnPoint, + instantiateDelegate = System.Delegate.CreateDelegate(typeof(InstantiateDelegate), graphicInstantiateDelegate) as InstantiateDelegate, + isUI = true + }); + } + + #if SPINE_SKELETONANIMATOR + menu.AddSeparator(""); + // SkeletonAnimator + menu.AddItem(new GUIContent("SkeletonAnimator"), false, HandleSkeletonComponentDrop, new SpawnMenuData { + skeletonDataAsset = skeletonDataAsset, + spawnPoint = spawnPoint, + instantiateDelegate = (data) => InstantiateSkeletonAnimator(data) + }); + #endif + + menu.ShowAsContext(); + } + + public static void HandleSkeletonComponentDrop (object spawnMenuData) { + var data = (SpawnMenuData)spawnMenuData; + + if (data.skeletonDataAsset.GetSkeletonData(true) == null) { + EditorUtility.DisplayDialog("Invalid SkeletonDataAsset", "Unable to create Spine GameObject.\n\nPlease check your SkeletonDataAsset.", "Ok"); + return; + } + + bool isUI = data.isUI; + + Component newSkeletonComponent = data.instantiateDelegate.Invoke(data.skeletonDataAsset); + GameObject newGameObject = newSkeletonComponent.gameObject; + Transform newTransform = newGameObject.transform; + + var activeGameObject = Selection.activeGameObject; + if (isUI && activeGameObject != null) + newTransform.SetParent(activeGameObject.transform, false); + + newTransform.position = isUI ? data.spawnPoint : RoundVector(data.spawnPoint, 2); + + if (isUI && (activeGameObject == null || activeGameObject.GetComponent() == null)) + Debug.Log("Created a UI Skeleton GameObject not under a RectTransform. It may not be visible until you parent it to a canvas."); + + if (!isUI && activeGameObject != null && activeGameObject.transform.localScale != Vector3.one) + Debug.Log("New Spine GameObject was parented to a scaled Transform. It may not be the intended size."); + + Selection.activeGameObject = newGameObject; + //EditorGUIUtility.PingObject(newGameObject); // Doesn't work when setting activeGameObject. + Undo.RegisterCreatedObjectUndo(newGameObject, "Create Spine GameObject"); + } + + /// + /// Rounds off vector components to a number of decimal digits. + /// + public static Vector3 RoundVector (Vector3 vector, int digits) { + vector.x = (float)System.Math.Round(vector.x, digits); + vector.y = (float)System.Math.Round(vector.y, digits); + vector.z = (float)System.Math.Round(vector.z, digits); + return vector; + } + + /// + /// Converts a mouse point to a world point on a plane. + /// + static Vector3 MousePointToWorldPoint2D (Vector2 mousePosition, Camera camera, Plane plane) { + var screenPos = new Vector3(mousePosition.x, camera.pixelHeight - mousePosition.y, 0f); + var ray = camera.ScreenPointToRay(screenPos); + float distance; + bool hit = plane.Raycast(ray, out distance); + return ray.GetPoint(distance); + } + #endregion + + #region Hierarchy + static class SpineEditorHierarchyHandler { + static Dictionary skeletonRendererTable = new Dictionary(); + static Dictionary skeletonUtilityBoneTable = new Dictionary(); + static Dictionary boundingBoxFollowerTable = new Dictionary(); + + #if UNITY_2017_2_OR_NEWER + internal static void HierarchyIconsOnPlaymodeStateChanged (PlayModeStateChange stateChange) { + #else + internal static void HierarchyIconsOnPlaymodeStateChanged () { + #endif + skeletonRendererTable.Clear(); + skeletonUtilityBoneTable.Clear(); + boundingBoxFollowerTable.Clear(); + + #if UNITY_2018 + EditorApplication.hierarchyChanged -= HierarchyIconsOnChanged; + #else + EditorApplication.hierarchyWindowChanged -= HierarchyIconsOnChanged; + #endif + EditorApplication.hierarchyWindowItemOnGUI -= HierarchyIconsOnGUI; + + if (!Application.isPlaying && showHierarchyIcons) { + #if UNITY_2018 + EditorApplication.hierarchyChanged += HierarchyIconsOnChanged; + #else + EditorApplication.hierarchyWindowChanged += HierarchyIconsOnChanged; + #endif + EditorApplication.hierarchyWindowItemOnGUI += HierarchyIconsOnGUI; + HierarchyIconsOnChanged(); + } + } + + internal static void HierarchyIconsOnChanged () { + skeletonRendererTable.Clear(); + skeletonUtilityBoneTable.Clear(); + boundingBoxFollowerTable.Clear(); + + SkeletonRenderer[] arr = Object.FindObjectsOfType(); + foreach (SkeletonRenderer r in arr) + skeletonRendererTable[r.gameObject.GetInstanceID()] = r.gameObject; + + SkeletonUtilityBone[] boneArr = Object.FindObjectsOfType(); + foreach (SkeletonUtilityBone b in boneArr) + skeletonUtilityBoneTable[b.gameObject.GetInstanceID()] = b; + + BoundingBoxFollower[] bbfArr = Object.FindObjectsOfType(); + foreach (BoundingBoxFollower bbf in bbfArr) + boundingBoxFollowerTable[bbf.gameObject.GetInstanceID()] = bbf; + } + + internal static void HierarchyIconsOnGUI (int instanceId, Rect selectionRect) { + Rect r = new Rect(selectionRect); + if (skeletonRendererTable.ContainsKey(instanceId)) { + r.x = r.width - 15; + r.width = 15; + GUI.Label(r, Icons.spine); + } else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) { + r.x -= 26; + if (skeletonUtilityBoneTable[instanceId] != null) { + if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0) + r.x += 13; + r.y += 2; + r.width = 13; + r.height = 13; + if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) + GUI.DrawTexture(r, Icons.bone); + else + GUI.DrawTexture(r, Icons.poseBones); + } + } else if (boundingBoxFollowerTable.ContainsKey(instanceId)) { + r.x -= 26; + if (boundingBoxFollowerTable[instanceId] != null) { + if (boundingBoxFollowerTable[instanceId].transform.childCount == 0) + r.x += 13; + r.y += 2; + r.width = 13; + r.height = 13; + GUI.DrawTexture(r, Icons.boundingBox); + } + } + } + + internal static void HierarchyDragAndDrop (int instanceId, Rect selectionRect) { + // HACK: Uses EditorApplication.hierarchyWindowItemOnGUI. + // Only works when there is at least one item in the scene. + var current = UnityEngine.Event.current; + var eventType = current.type; + bool isDraggingEvent = eventType == EventType.DragUpdated; + bool isDropEvent = eventType == EventType.DragPerform; + if (isDraggingEvent || isDropEvent) { + var mouseOverWindow = EditorWindow.mouseOverWindow; + if (mouseOverWindow != null) { + + // One, existing, valid SkeletonDataAsset + var references = DragAndDrop.objectReferences; + if (references.Length == 1) { + var skeletonDataAsset = references[0] as SkeletonDataAsset; + if (skeletonDataAsset != null && skeletonDataAsset.GetSkeletonData(true) != null) { + + // Allow drag-and-dropping anywhere in the Hierarchy Window. + // HACK: string-compare because we can't get its type via reflection. + const string HierarchyWindow = "UnityEditor.SceneHierarchyWindow"; + if (HierarchyWindow.Equals(mouseOverWindow.GetType().ToString(), System.StringComparison.Ordinal)) { + if (isDraggingEvent) { + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + current.Use(); + } else if (isDropEvent) { + ShowInstantiateContextMenu(skeletonDataAsset, Vector3.zero); + DragAndDrop.AcceptDrag(); + current.Use(); + return; + } + } + + } + } + } + } + + } + } + #endregion + + #region Auto-Import Entry Point + static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) { + if (imported.Length == 0) + return; + + // In case user used "Assets -> Reimport All", during the import process, + // asset database is not initialized until some point. During that period, + // all attempts to load any assets using API (i.e. AssetDatabase.LoadAssetAtPath) + // will return null, and as result, assets won't be loaded even if they actually exists, + // which may lead to numerous importing errors. + // This situation also happens if Library folder is deleted from the project, which is a pretty + // common case, since when using version control systems, the Library folder must be excluded. + // + // So to avoid this, in case asset database is not available, we delay loading the assets + // until next time. + // + // Unity *always* reimports some internal assets after the process is done, so this method + // is always called once again in a state when asset database is available. + // + // Checking whether AssetDatabase is initialized is done by attempting to load + // a known "marker" asset that should always be available. Failing to load this asset + // means that AssetDatabase is not initialized. + assetsImportedInWrongState.UnionWith(imported); + if (AssetDatabaseAvailabilityDetector.IsAssetDatabaseAvailable()) { + string[] combinedAssets = assetsImportedInWrongState.ToArray(); + assetsImportedInWrongState.Clear(); + ImportSpineContent(combinedAssets); + } + } + + public static void ImportSpineContent (string[] imported, bool reimport = false) { + var atlasPaths = new List(); + var imagePaths = new List(); + var skeletonPaths = new List(); + + foreach (string str in imported) { + string extension = Path.GetExtension(str).ToLower(); + switch (extension) { + case ".txt": + if (str.EndsWith(".atlas.txt", System.StringComparison.Ordinal)) + atlasPaths.Add(str); + break; + case ".png": + case ".jpg": + imagePaths.Add(str); + break; + case ".json": + var jsonAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)); + if (jsonAsset != null && SkeletonDataFileValidator.IsSpineData(jsonAsset)) + skeletonPaths.Add(str); + break; + case ".bytes": + if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) { + if (SkeletonDataFileValidator.IsSpineData((TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset)))) + skeletonPaths.Add(str); + } + break; + } + } + + // Import atlases first. + var atlases = new List(); + foreach (string ap in atlasPaths) { + TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset)); + AtlasAsset atlas = IngestSpineAtlas(atlasText); + atlases.Add(atlas); + } + + // Import skeletons and match them with atlases. + bool abortSkeletonImport = false; + foreach (string sp in skeletonPaths) { + if (!reimport && SkeletonDataFileValidator.CheckForValidSkeletonData(sp)) { + ReloadSkeletonData(sp); + continue; + } + + string dir = Path.GetDirectoryName(sp); + + #if SPINE_TK2D + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, null); + #else + var localAtlases = FindAtlasesAtPath(dir); + var requiredPaths = GetRequiredAtlasRegions(sp); + var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + if (atlasMatch != null || requiredPaths.Count == 0) { + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); + } else { + bool resolved = false; + while (!resolved) { + + string filename = Path.GetFileNameWithoutExtension(sp); + int result = EditorUtility.DisplayDialogComplex( + string.Format("AtlasAsset for \"{0}\"", filename), + string.Format("Could not automatically set the AtlasAsset for \"{0}\". You may set it manually.", filename), + "Choose AtlasAssets...", "Skip this", "Stop importing all" + ); + + switch (result) { + case -1: + //Debug.Log("Select Atlas"); + AtlasAsset selectedAtlas = GetAtlasDialog(Path.GetDirectoryName(sp)); + if (selectedAtlas != null) { + localAtlases.Clear(); + localAtlases.Add(selectedAtlas); + atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); + if (atlasMatch != null) { + resolved = true; + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch); + } + } + break; + case 0: // Choose AtlasAssets... + var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(sp), Path.GetFileNameWithoutExtension(sp)); + if (atlasList != null) + IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasList.ToArray()); + + resolved = true; + break; + case 1: // Skip + Debug.Log("Skipped importing: " + Path.GetFileName(sp)); + resolved = true; + break; + case 2: // Stop importing all + abortSkeletonImport = true; + resolved = true; + break; + } + } + } + + if (abortSkeletonImport) + break; + #endif + } + // Any post processing of images + } + + static void ReloadSkeletonData (string skeletonJSONPath) { + string dir = Path.GetDirectoryName(skeletonJSONPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var f in files) { + string localPath = dir + "/" + f.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + var skeletonDataAsset = obj as SkeletonDataAsset; + if (skeletonDataAsset != null) { + if (skeletonDataAsset.skeletonJSON == textAsset) { + if (Selection.activeObject == skeletonDataAsset) + Selection.activeObject = null; + + Debug.LogFormat("Changes to '{0}' detected. Clearing SkeletonDataAsset: {1}", skeletonJSONPath, localPath); + skeletonDataAsset.Clear(); + + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(skeletonDataAsset)); + string lastHash = EditorPrefs.GetString(guid + "_hash"); + + // For some weird reason sometimes Unity loses the internal Object pointer, + // and as a result, all comparisons with null returns true. + // But the C# wrapper is still alive, so we can "restore" the object + // by reloading it from its Instance ID. + AtlasAsset[] skeletonDataAtlasAssets = skeletonDataAsset.atlasAssets; + if (skeletonDataAtlasAssets != null) { + for (int i = 0; i < skeletonDataAtlasAssets.Length; i++) { + if (!ReferenceEquals(null, skeletonDataAtlasAssets[i]) && + skeletonDataAtlasAssets[i].Equals(null) && + skeletonDataAtlasAssets[i].GetInstanceID() != 0 + ) { + skeletonDataAtlasAssets[i] = EditorUtility.InstanceIDToObject(skeletonDataAtlasAssets[i].GetInstanceID()) as AtlasAsset; + } + } + } + + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true); + string currentHash = skeletonData != null ? skeletonData.Hash : null; + + #if SPINE_SKELETONANIMATOR + if (currentHash == null || lastHash != currentHash) + UpdateMecanimClips(skeletonDataAsset); + #endif + + // if (currentHash == null || lastHash != currentHash) + // Do any upkeep on synchronized assets + + if (currentHash != null) + EditorPrefs.SetString(guid + "_hash", currentHash); + } + } + } + } + #endregion + + #region Match SkeletonData with Atlases + static readonly AttachmentType[] AtlasTypes = { AttachmentType.Region, AttachmentType.Linkedmesh, AttachmentType.Mesh }; + + static List MultiAtlasDialog (List requiredPaths, string initialDirectory, string filename = "") { + List atlasAssets = new List(); + bool resolved = false; + string lastAtlasPath = initialDirectory; + while (!resolved) { + + // Build dialog box message. + var missingRegions = new List(requiredPaths); + var dialogText = new StringBuilder(); + { + dialogText.AppendLine(string.Format("SkeletonDataAsset for \"{0}\"", filename)); + dialogText.AppendLine("has missing regions."); + dialogText.AppendLine(); + dialogText.AppendLine("Current Atlases:"); + + if (atlasAssets.Count == 0) + dialogText.AppendLine("\t--none--"); + + for (int i = 0; i < atlasAssets.Count; i++) + dialogText.AppendLine("\t" + atlasAssets[i].name); + + dialogText.AppendLine(); + dialogText.AppendLine("Missing Regions:"); + + foreach (var atlasAsset in atlasAssets) { + var atlas = atlasAsset.GetAtlas(); + for (int i = 0; i < missingRegions.Count; i++) { + if (atlas.FindRegion(missingRegions[i]) != null) { + missingRegions.RemoveAt(i); + i--; + } + } + } + + int n = missingRegions.Count; + if (n == 0) break; + + const int MaxListLength = 15; + for (int i = 0; (i < n && i < MaxListLength); i++) + dialogText.AppendLine("\t" + missingRegions[i]); + + if (n > MaxListLength) dialogText.AppendLine(string.Format("\t... {0} more...", n - MaxListLength)); + } + + // Show dialog box. + int result = EditorUtility.DisplayDialogComplex( + "SkeletonDataAsset has missing Atlas.", + dialogText.ToString(), + "Browse...", "Import anyway", "Cancel" + ); + + switch (result) { + case 0: // Browse... + AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath); + if (selectedAtlasAsset != null) { + var atlas = selectedAtlasAsset.GetAtlas(); + bool hasValidRegion = false; + foreach (string str in missingRegions) { + if (atlas.FindRegion(str) != null) { + hasValidRegion = true; + break; + } + } + atlasAssets.Add(selectedAtlasAsset); + } + break; + case 1: // Import anyway + resolved = true; + break; + case 2: // Cancel + atlasAssets = null; + resolved = true; + break; + } + } + + return atlasAssets; + } + + static AtlasAsset GetAtlasDialog (string dirPath) { + string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset"); + if (path == "") return null; // Canceled or closed by user. + + int subLen = Application.dataPath.Length - 6; + string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/"); + + Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); + + if (obj == null || obj.GetType() != typeof(AtlasAsset)) + return null; + + return (AtlasAsset)obj; + } + + static void AddRequiredAtlasRegionsFromBinary (string skeletonDataPath, List requiredPaths) { + SkeletonBinary binary = new SkeletonBinary(new AtlasRequirementLoader(requiredPaths)); + TextAsset data = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset)); + MemoryStream input = new MemoryStream(data.bytes); + binary.ReadSkeletonData(input); + binary = null; + } + + public static List GetRequiredAtlasRegions (string skeletonDataPath) { + List requiredPaths = new List(); + + if (skeletonDataPath.Contains(".skel")) { + AddRequiredAtlasRegionsFromBinary(skeletonDataPath, requiredPaths); + return requiredPaths; + } + + TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonDataPath, typeof(TextAsset)); + + StringReader reader = new StringReader(spineJson.text); + var root = Json.Deserialize(reader) as Dictionary; + + if (!root.ContainsKey("skins")) + return requiredPaths; + + foreach (KeyValuePair entry in (Dictionary)root["skins"]) { + foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { + + foreach (KeyValuePair attachmentEntry in ((Dictionary)slotEntry.Value)) { + var data = ((Dictionary)attachmentEntry.Value); + + // Ignore non-atlas-requiring types. + if (data.ContainsKey("type")) { + AttachmentType attachmentType; + string typeString = (string)data["type"]; + try { + attachmentType = (AttachmentType)System.Enum.Parse(typeof(AttachmentType), typeString, true); + } catch (System.ArgumentException e) { + // For more info, visit: http://esotericsoftware.com/forum/Spine-editor-and-runtime-version-management-6534 + Debug.LogWarning(string.Format("Unidentified Attachment type: \"{0}\". Skeleton may have been exported from an incompatible Spine version.", typeString)); + throw e; + } + + if (!AtlasTypes.Contains(attachmentType)) + continue; + } + + if (data.ContainsKey("path")) + requiredPaths.Add((string)data["path"]); + else if (data.ContainsKey("name")) + requiredPaths.Add((string)data["name"]); + else + requiredPaths.Add(attachmentEntry.Key); + } + } + } + + return requiredPaths; + } + + static AtlasAsset GetMatchingAtlas (List requiredPaths, List atlasAssets) { + AtlasAsset atlasAssetMatch = null; + + foreach (AtlasAsset a in atlasAssets) { + Atlas atlas = a.GetAtlas(); + bool failed = false; + foreach (string regionPath in requiredPaths) { + if (atlas.FindRegion(regionPath) == null) { + failed = true; + break; + } + } + + if (!failed) { + atlasAssetMatch = a; + break; + } + } + + return atlasAssetMatch; + } + + public class AtlasRequirementLoader : AttachmentLoader { + List requirementList; + + public AtlasRequirementLoader (List requirementList) { + this.requirementList = requirementList; + } + + public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { + requirementList.Add(path); + return new RegionAttachment(name); + } + + public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { + requirementList.Add(path); + return new MeshAttachment(name); + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + return new BoundingBoxAttachment(name); + } + + public PathAttachment NewPathAttachment (Skin skin, string name) { + return new PathAttachment(name); + } + + public PointAttachment NewPointAttachment (Skin skin, string name) { + return new PointAttachment(name); + } + + public ClippingAttachment NewClippingAttachment (Skin skin, string name) { + return new ClippingAttachment(name); + } + } + #endregion + + #region Import Atlases + static List FindAtlasesAtPath (string path) { + List arr = new List(); + DirectoryInfo dir = new DirectoryInfo(path); + FileInfo[] assetInfoArr = dir.GetFiles("*.asset"); + + int subLen = Application.dataPath.Length - 6; + foreach (var f in assetInfoArr) { + string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/"); + Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset)); + if (obj != null) + arr.Add(obj as AtlasAsset); + } + + return arr; + } + + static AtlasAsset IngestSpineAtlas (TextAsset atlasText) { + if (atlasText == null) { + Debug.LogWarning("Atlas source cannot be null!"); + return null; + } + + string primaryName = Path.GetFileNameWithoutExtension(atlasText.name).Replace(".atlas", ""); + string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(atlasText)); + + string atlasPath = assetPath + "/" + primaryName + "_Atlas.asset"; + + AtlasAsset atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); + + List vestigialMaterials = new List(); + + if (atlasAsset == null) + atlasAsset = AtlasAsset.CreateInstance(); + else { + foreach (Material m in atlasAsset.materials) + vestigialMaterials.Add(m); + } + + protectFromStackGarbageCollection.Add(atlasAsset); + atlasAsset.atlasFile = atlasText; + + //strip CR + string atlasStr = atlasText.text; + atlasStr = atlasStr.Replace("\r", ""); + + string[] atlasLines = atlasStr.Split('\n'); + List pageFiles = new List(); + for (int i = 0; i < atlasLines.Length - 1; i++) { + if (atlasLines[i].Trim().Length == 0) + pageFiles.Add(atlasLines[i + 1].Trim()); + } + + var populatingMaterials = new List(pageFiles.Count);//atlasAsset.materials = new Material[pageFiles.Count]; + + for (int i = 0; i < pageFiles.Count; i++) { + string texturePath = assetPath + "/" + pageFiles[i]; + Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)); + + if (setTextureImporterSettings) { + TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath); + if (texImporter == null) { + Debug.LogWarning(string.Format("{0} ::: Texture asset \"{1}\" not found. Skipping. Please check your atlas file for renamed files.", atlasAsset.name, texturePath)); + continue; + } + + texImporter.textureCompression = TextureImporterCompression.Uncompressed; + texImporter.alphaSource = TextureImporterAlphaSource.FromInput; + texImporter.mipmapEnabled = false; + texImporter.alphaIsTransparency = false; // Prevent the texture importer from applying bleed to the transparent parts for PMA. + texImporter.spriteImportMode = SpriteImportMode.None; + texImporter.maxTextureSize = 2048; + + EditorUtility.SetDirty(texImporter); + AssetDatabase.ImportAsset(texturePath); + AssetDatabase.SaveAssets(); + } + + string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]); + + //because this looks silly + if (pageName == primaryName && pageFiles.Count == 1) + pageName = "Material"; + + string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat"; + Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)); + + if (mat == null) { + mat = new Material(Shader.Find(defaultShader)); + AssetDatabase.CreateAsset(mat, materialPath); + } else { + vestigialMaterials.Remove(mat); + } + + mat.mainTexture = texture; + EditorUtility.SetDirty(mat); + AssetDatabase.SaveAssets(); + + populatingMaterials.Add(mat); //atlasAsset.materials[i] = mat; + } + + atlasAsset.materials = populatingMaterials.ToArray(); + + for (int i = 0; i < vestigialMaterials.Count; i++) + AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(vestigialMaterials[i])); + + if (AssetDatabase.GetAssetPath(atlasAsset) == "") + AssetDatabase.CreateAsset(atlasAsset, atlasPath); + else + atlasAsset.Clear(); + + EditorUtility.SetDirty(atlasAsset); + AssetDatabase.SaveAssets(); + + if (pageFiles.Count != atlasAsset.materials.Length) + Debug.LogWarning(string.Format("{0} ::: Not all atlas pages were imported. If you rename your image files, please make sure you also edit the filenames specified in the atlas file.", atlasAsset.name)); + else + Debug.Log(string.Format("{0} ::: Imported with {1} material", atlasAsset.name, atlasAsset.materials.Length)); + + // Iterate regions and bake marked. + Atlas atlas = atlasAsset.GetAtlas(); + FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic); + List regions = (List)field.GetValue(atlas); + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + + bool hasBakedRegions = false; + for (int i = 0; i < regions.Count; i++) { + AtlasRegion region = regions[i]; + string bakedPrefabPath = Path.Combine(bakedDirPath, SpineEditorUtilities.GetPathSafeName(region.name) + ".prefab").Replace("\\", "/"); + GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); + if (prefab != null) { + BakeRegion(atlasAsset, region, false); + hasBakedRegions = true; + } + } + + if (hasBakedRegions) { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + protectFromStackGarbageCollection.Remove(atlasAsset); + return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset)); + } + #endregion + + #region Bake Atlas Region + public static GameObject BakeRegion (AtlasAsset atlasAsset, AtlasRegion region, bool autoSave = true) { + Atlas atlas = atlasAsset.GetAtlas(); + string atlasAssetPath = AssetDatabase.GetAssetPath(atlasAsset); + string atlasAssetDirPath = Path.GetDirectoryName(atlasAssetPath); + string bakedDirPath = Path.Combine(atlasAssetDirPath, atlasAsset.name); + string bakedPrefabPath = Path.Combine(bakedDirPath, GetPathSafeName(region.name) + ".prefab").Replace("\\", "/"); + + GameObject prefab = (GameObject)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(GameObject)); + GameObject root; + Mesh mesh; + bool isNewPrefab = false; + + if (!Directory.Exists(bakedDirPath)) + Directory.CreateDirectory(bakedDirPath); + + if (prefab == null) { + root = new GameObject("temp", typeof(MeshFilter), typeof(MeshRenderer)); + prefab = PrefabUtility.CreatePrefab(bakedPrefabPath, root); + isNewPrefab = true; + Object.DestroyImmediate(root); + } + + mesh = (Mesh)AssetDatabase.LoadAssetAtPath(bakedPrefabPath, typeof(Mesh)); + + Material mat = null; + mesh = atlasAsset.GenerateMesh(region.name, mesh, out mat); + if (isNewPrefab) { + AssetDatabase.AddObjectToAsset(mesh, prefab); + prefab.GetComponent().sharedMesh = mesh; + } + + EditorUtility.SetDirty(mesh); + EditorUtility.SetDirty(prefab); + + if (autoSave) { + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + + prefab.GetComponent().sharedMaterial = mat; + + return prefab; + } + #endregion + + #region Import SkeletonData (json or binary) + public const string SkeletonDataSuffix = "_SkeletonData"; + static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAsset[] atlasAssets) { + string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); + string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); + string filePath = assetPath + "/" + primaryName + SkeletonDataSuffix + ".asset"; + + #if SPINE_TK2D + if (spineJson != null) { + SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); + if (skeletonDataAsset == null) { + skeletonDataAsset = SkeletonDataAsset.CreateInstance(); + skeletonDataAsset.skeletonJSON = spineJson; + skeletonDataAsset.fromAnimation = new string[0]; + skeletonDataAsset.toAnimation = new string[0]; + skeletonDataAsset.duration = new float[0]; + skeletonDataAsset.defaultMix = defaultMix; + skeletonDataAsset.scale = defaultScale; + + AssetDatabase.CreateAsset(skeletonDataAsset, filePath); + AssetDatabase.SaveAssets(); + } else { + skeletonDataAsset.Clear(); + skeletonDataAsset.GetSkeletonData(true); + } + + return skeletonDataAsset; + } else { + EditorUtility.DisplayDialog("Error!", "Tried to ingest null Spine data.", "OK"); + return null; + } + + #else + if (spineJson != null && atlasAssets != null) { + SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); + if (skeletonDataAsset == null) { + skeletonDataAsset = ScriptableObject.CreateInstance(); { + skeletonDataAsset.atlasAssets = atlasAssets; + skeletonDataAsset.skeletonJSON = spineJson; + skeletonDataAsset.defaultMix = defaultMix; + skeletonDataAsset.scale = defaultScale; + } + + AssetDatabase.CreateAsset(skeletonDataAsset, filePath); + AssetDatabase.SaveAssets(); + } else { + skeletonDataAsset.atlasAssets = atlasAssets; + skeletonDataAsset.Clear(); + skeletonDataAsset.GetSkeletonData(true); + } + + return skeletonDataAsset; + } else { + EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK"); + return null; + } + #endif + } + #endregion + + #region SkeletonDataFileValidator + internal static class SkeletonDataFileValidator { + static int[][] compatibleBinaryVersions = { new[] { 3, 6, 0 }, new[] { 3, 5, 0 } }; + static int[][] compatibleJsonVersions = { new[] { 3, 6, 0 }, new[] { 3, 7, 0 }, new[] { 3, 5, 0 } }; + //static bool isFixVersionRequired = false; + + public static bool CheckForValidSkeletonData (string skeletonJSONPath) { + string dir = Path.GetDirectoryName(skeletonJSONPath); + TextAsset textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(skeletonJSONPath, typeof(TextAsset)); + DirectoryInfo dirInfo = new DirectoryInfo(dir); + FileInfo[] files = dirInfo.GetFiles("*.asset"); + + foreach (var path in files) { + string localPath = dir + "/" + path.Name; + var obj = AssetDatabase.LoadAssetAtPath(localPath, typeof(Object)); + var skeletonDataAsset = obj as SkeletonDataAsset; + if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON == textAsset) + return true; + } + + return false; + } + + public static bool IsSpineData (TextAsset asset) { + if (asset == null) + return false; + + bool isSpineData = false; + string rawVersion = null; + + int[][] compatibleVersions; + if (asset.name.Contains(".skel")) { + try { + rawVersion = SkeletonBinary.GetVersionString(new MemoryStream(asset.bytes)); + isSpineData = !(string.IsNullOrEmpty(rawVersion)); + compatibleVersions = compatibleBinaryVersions; + } catch (System.Exception e) { + Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e); + return false; + } + } else { + object obj = Json.Deserialize(new StringReader(asset.text)); + if (obj == null) { + Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name); + return false; + } + + var root = obj as Dictionary; + if (root == null) { + Debug.LogError("Parser returned an incorrect type."); + return false; + } + + isSpineData = root.ContainsKey("skeleton"); + if (isSpineData) { + var skeletonInfo = (Dictionary)root["skeleton"]; + object jv; + skeletonInfo.TryGetValue("spine", out jv); + rawVersion = jv as string; + } + + compatibleVersions = compatibleJsonVersions; + } + + // Version warning + if (isSpineData) { + string primaryRuntimeVersionDebugString = compatibleVersions[0][0] + "." + compatibleVersions[0][1]; + + if (string.IsNullOrEmpty(rawVersion)) { + Debug.LogWarningFormat("Skeleton '{0}' has no version information. It may be incompatible with your runtime version: spine-unity v{1}", asset.name, primaryRuntimeVersionDebugString); + } else { + string[] versionSplit = rawVersion.Split('.'); + bool match = false; + foreach (var version in compatibleVersions) { + bool primaryMatch = version[0] == int.Parse(versionSplit[0]); + bool secondaryMatch = version[1] == int.Parse(versionSplit[1]); + + // if (isFixVersionRequired) secondaryMatch &= version[2] <= int.Parse(jsonVersionSplit[2]); + + if (primaryMatch && secondaryMatch) { + match = true; + break; + } + } + + if (!match) + Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-unity v{2}", asset.name, rawVersion, primaryRuntimeVersionDebugString); + } + } + + return isSpineData; + } + } + + #endregion + + #region SkeletonAnimation Menu + public static void IngestAdvancedRenderSettings (SkeletonRenderer skeletonRenderer) { + const string PMAShaderQuery = "Spine/Skeleton"; + const string TintBlackShaderQuery = "Tint Black"; + + if (skeletonRenderer == null) return; + var skeletonDataAsset = skeletonRenderer.skeletonDataAsset; + if (skeletonDataAsset == null) return; + + bool pmaVertexColors = false; + bool tintBlack = false; + foreach (AtlasAsset atlasAsset in skeletonDataAsset.atlasAssets) { + if (!pmaVertexColors) { + foreach (Material m in atlasAsset.materials) { + if (m.shader.name.Contains(PMAShaderQuery)) { + pmaVertexColors = true; + break; + } + } + } + + if (!tintBlack) { + foreach (Material m in atlasAsset.materials) { + if (m.shader.name.Contains(TintBlackShaderQuery)) { + tintBlack = true; + break; + } + } + } + } + + skeletonRenderer.pmaVertexColors = pmaVertexColors; + skeletonRenderer.tintBlack = tintBlack; + } + + public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, string skinName, bool destroyInvalid = true) { + var skeletonData = skeletonDataAsset.GetSkeletonData(true); + var skin = skeletonData != null ? skeletonData.FindSkin(skinName) : null; + return InstantiateSkeletonAnimation(skeletonDataAsset, skin, destroyInvalid); + } + + public static SkeletonAnimation InstantiateSkeletonAnimation (SkeletonDataAsset skeletonDataAsset, Skin skin = null, bool destroyInvalid = true) { + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + + if (data == null) { + for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + data = skeletonDataAsset.GetSkeletonData(false); + } + + if (data == null) { + Debug.LogWarning("InstantiateSkeletonAnimation tried to instantiate a skeleton from an invalid SkeletonDataAsset."); + return null; + } + + string spineGameObjectName = string.Format("Spine GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); + GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation)); + SkeletonAnimation newSkeletonAnimation = go.GetComponent(); + newSkeletonAnimation.skeletonDataAsset = skeletonDataAsset; + IngestAdvancedRenderSettings(newSkeletonAnimation); + + try { + newSkeletonAnimation.Initialize(false); + } catch (System.Exception e) { + if (destroyInvalid) { + Debug.LogWarning("Editor-instantiated SkeletonAnimation threw an Exception. Destroying GameObject to prevent orphaned GameObject."); + GameObject.DestroyImmediate(go); + } + Debug.Log(e); + } + + // Set Defaults + bool noSkins = data.DefaultSkin == null && (data.Skins == null || data.Skins.Count == 0); // Support attachmentless/skinless SkeletonData. + skin = skin ?? data.DefaultSkin ?? (noSkins ? null : data.Skins.Items[0]); + if (skin != null) { + newSkeletonAnimation.initialSkinName = skin.Name; + newSkeletonAnimation.skeleton.SetSkin(skin); + } + + newSkeletonAnimation.zSpacing = defaultZSpacing; + + newSkeletonAnimation.skeleton.Update(0); + newSkeletonAnimation.state.Update(0); + newSkeletonAnimation.state.Apply(newSkeletonAnimation.skeleton); + newSkeletonAnimation.skeleton.UpdateWorldTransform(); + + return newSkeletonAnimation; + } + #endregion + + #region SkeletonAnimator + #if SPINE_SKELETONANIMATOR + static void UpdateMecanimClips (SkeletonDataAsset skeletonDataAsset) { + if (skeletonDataAsset.controller == null) + return; + + SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset); + } + + public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, string skinName) { + return InstantiateSkeletonAnimator(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); + } + + public static SkeletonAnimator InstantiateSkeletonAnimator (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + string spineGameObjectName = string.Format("Spine Mecanim GameObject ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); + GameObject go = new GameObject(spineGameObjectName, typeof(MeshFilter), typeof(MeshRenderer), typeof(Animator), typeof(SkeletonAnimator)); + + if (skeletonDataAsset.controller == null) { + SkeletonBaker.GenerateMecanimAnimationClips(skeletonDataAsset); + Debug.Log(string.Format("Mecanim controller was automatically generated and assigned for {0}", skeletonDataAsset.name)); + } + + go.GetComponent().runtimeAnimatorController = skeletonDataAsset.controller; + + SkeletonAnimator anim = go.GetComponent(); + anim.skeletonDataAsset = skeletonDataAsset; + IngestAdvancedRenderSettings(anim); + + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + if (data == null) { + for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + data = skeletonDataAsset.GetSkeletonData(true); + } + + // Set defaults + skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0]; + anim.zSpacing = defaultZSpacing; + + anim.Initialize(false); + anim.skeleton.SetSkin(skin); + anim.initialSkinName = skin.Name; + + anim.skeleton.Update(0); + anim.skeleton.UpdateWorldTransform(); + anim.LateUpdate(); + + return anim; + } +#endif + #endregion + + #region SpineTK2DEditorUtility + internal static class SpineTK2DEditorUtility { + const string SPINE_TK2D_DEFINE = "SPINE_TK2D"; + + static bool IsInvalidGroup (BuildTargetGroup group) { + int gi = (int)group; + return + gi == 15 || gi == 16 + || + group == BuildTargetGroup.Unknown; + } + + internal static void EnableTK2D () { + bool added = false; + foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { + if (IsInvalidGroup(group)) + continue; + + string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); + if (!defines.Contains(SPINE_TK2D_DEFINE)) { + added = true; + if (defines.EndsWith(";", System.StringComparison.Ordinal)) + defines = defines + SPINE_TK2D_DEFINE; + else + defines = defines + ";" + SPINE_TK2D_DEFINE; + + PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines); + } + } + + if (added) { + Debug.LogWarning("Setting Scripting Define Symbol " + SPINE_TK2D_DEFINE); + } else { + Debug.LogWarning("Already Set Scripting Define Symbol " + SPINE_TK2D_DEFINE); + } + } + + + internal static void DisableTK2D () { + bool removed = false; + foreach (BuildTargetGroup group in System.Enum.GetValues(typeof(BuildTargetGroup))) { + if (IsInvalidGroup(group)) + continue; + + string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); + if (defines.Contains(SPINE_TK2D_DEFINE)) { + removed = true; + if (defines.Contains(SPINE_TK2D_DEFINE + ";")) + defines = defines.Replace(SPINE_TK2D_DEFINE + ";", ""); + else + defines = defines.Replace(SPINE_TK2D_DEFINE, ""); + + PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines); + } + } + + if (removed) { + Debug.LogWarning("Removing Scripting Define Symbol " + SPINE_TK2D_DEFINE); + } else { + Debug.LogWarning("Already Removed Scripting Define Symbol " + SPINE_TK2D_DEFINE); + } + } + } + #endregion + + public static string GetPathSafeName (string name) { + foreach (char c in System.IO.Path.GetInvalidFileNameChars()) { // Doesn't handle more obscure file name limitations. + name = name.Replace(c, '_'); + } + return name; + } + } + + public static class SpineHandles { + internal static float handleScale = 1f; + public static Color BoneColor { get { return new Color(0.8f, 0.8f, 0.8f, 0.4f); } } + public static Color PathColor { get { return new Color(254/255f, 127/255f, 0); } } + public static Color TransformContraintColor { get { return new Color(170/255f, 226/255f, 35/255f); } } + public static Color IkColor { get { return new Color(228/255f,90/255f,43/255f); } } + public static Color PointColor { get { return new Color(1f, 1f, 0f, 1f); } } + + static Vector3[] _boneMeshVerts = { + new Vector3(0, 0, 0), + new Vector3(0.1f, 0.1f, 0), + new Vector3(1, 0, 0), + new Vector3(0.1f, -0.1f, 0) + }; + static Mesh _boneMesh; + public static Mesh BoneMesh { + get { + if (_boneMesh == null) { + _boneMesh = new Mesh { + vertices = _boneMeshVerts, + uv = new Vector2[4], + triangles = new [] { 0, 1, 2, 2, 3, 0 } + }; + _boneMesh.RecalculateBounds(); + _boneMesh.RecalculateNormals(); + } + return _boneMesh; + } + } + + static Mesh _arrowheadMesh; + public static Mesh ArrowheadMesh { + get { + if (_arrowheadMesh == null) { + _arrowheadMesh = new Mesh { + vertices = new [] { + new Vector3(0, 0), + new Vector3(-0.1f, 0.05f), + new Vector3(-0.1f, -0.05f) + }, + uv = new Vector2[3], + triangles = new [] { 0, 1, 2 } + }; + _arrowheadMesh.RecalculateBounds(); + _arrowheadMesh.RecalculateNormals(); + } + return _arrowheadMesh; + } + } + + static Material _boneMaterial; + static Material BoneMaterial { + get { + if (_boneMaterial == null) { + _boneMaterial = new Material(Shader.Find("Hidden/Spine/Bones")); + _boneMaterial.SetColor("_Color", SpineHandles.BoneColor); + } + + return _boneMaterial; + } + } + public static Material GetBoneMaterial () { + BoneMaterial.SetColor("_Color", SpineHandles.BoneColor); + return BoneMaterial; + } + + public static Material GetBoneMaterial (Color color) { + BoneMaterial.SetColor("_Color", color); + return BoneMaterial; + } + + static Material _ikMaterial; + public static Material IKMaterial { + get { + if (_ikMaterial == null) { + _ikMaterial = new Material(Shader.Find("Hidden/Spine/Bones")); + _ikMaterial.SetColor("_Color", SpineHandles.IkColor); + } + return _ikMaterial; + } + } + + static GUIStyle _boneNameStyle; + public static GUIStyle BoneNameStyle { + get { + if (_boneNameStyle == null) { + _boneNameStyle = new GUIStyle(EditorStyles.whiteMiniLabel) { + alignment = TextAnchor.MiddleCenter, + stretchWidth = true, + padding = new RectOffset(0, 0, 0, 0), + contentOffset = new Vector2(-5f, 0f) + }; + } + return _boneNameStyle; + } + } + + static GUIStyle _pathNameStyle; + public static GUIStyle PathNameStyle { + get { + if (_pathNameStyle == null) { + _pathNameStyle = new GUIStyle(SpineHandles.BoneNameStyle); + _pathNameStyle.normal.textColor = SpineHandles.PathColor; + } + return _pathNameStyle; + } + } + + static GUIStyle _pointNameStyle; + public static GUIStyle PointNameStyle { + get { + if (_pointNameStyle == null) { + _pointNameStyle = new GUIStyle(SpineHandles.BoneNameStyle); + _pointNameStyle.normal.textColor = SpineHandles.PointColor; + } + return _pointNameStyle; + } + } + + public static void DrawBoneNames (Transform transform, Skeleton skeleton, float positionScale = 1f) { + GUIStyle style = BoneNameStyle; + foreach (Bone b in skeleton.Bones) { + var pos = new Vector3(b.WorldX * positionScale, b.WorldY * positionScale, 0) + (new Vector3(b.A, b.C) * (b.Data.Length * 0.5f)); + pos = transform.TransformPoint(pos); + Handles.Label(pos, b.Data.Name, style); + } + } + + public static void DrawBones (Transform transform, Skeleton skeleton, float positionScale = 1f) { + float boneScale = 1.8f; // Draw the root bone largest; + DrawCrosshairs2D(skeleton.Bones.Items[0].GetWorldPosition(transform), 0.08f, positionScale); + + foreach (Bone b in skeleton.Bones) { + DrawBone(transform, b, boneScale, positionScale); + boneScale = 1f; + } + } + + static Vector3[] _boneWireBuffer = new Vector3[5]; + static Vector3[] GetBoneWireBuffer (Matrix4x4 m) { + for (int i = 0, n = _boneMeshVerts.Length; i < n; i++) + _boneWireBuffer[i] = m.MultiplyPoint(_boneMeshVerts[i]); + + _boneWireBuffer[4] = _boneWireBuffer[0]; // closed polygon. + return _boneWireBuffer; + } + public static void DrawBoneWireframe (Transform transform, Bone b, Color color, float skeletonRenderScale = 1f) { + Handles.color = color; + var pos = new Vector3(b.WorldX * skeletonRenderScale, b.WorldY * skeletonRenderScale, 0); + float length = b.Data.Length; + + if (length > 0) { + Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX); + Vector3 scale = Vector3.one * length * b.WorldScaleX * skeletonRenderScale; + const float my = 1.5f; + scale.y *= (SpineHandles.handleScale + 1) * 0.5f; + scale.y = Mathf.Clamp(scale.x, -my * skeletonRenderScale, my * skeletonRenderScale); + Handles.DrawPolyLine(GetBoneWireBuffer(transform.localToWorldMatrix * Matrix4x4.TRS(pos, rot, scale))); + var wp = transform.TransformPoint(pos); + DrawBoneCircle(wp, color, transform.forward, skeletonRenderScale); + } else { + var wp = transform.TransformPoint(pos); + DrawBoneCircle(wp, color, transform.forward, skeletonRenderScale); + } + } + + public static void DrawBone (Transform transform, Bone b, float boneScale, float skeletonRenderScale = 1f) { + var pos = new Vector3(b.WorldX * skeletonRenderScale, b.WorldY * skeletonRenderScale, 0); + float length = b.Data.Length; + if (length > 0) { + Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX); + Vector3 scale = Vector3.one * length * b.WorldScaleX * skeletonRenderScale; + const float my = 1.5f; + scale.y *= (SpineHandles.handleScale + 1f) * 0.5f; + scale.y = Mathf.Clamp(scale.x, -my * skeletonRenderScale, my * skeletonRenderScale); + SpineHandles.GetBoneMaterial().SetPass(0); + Graphics.DrawMeshNow(SpineHandles.BoneMesh, transform.localToWorldMatrix * Matrix4x4.TRS(pos, rot, scale)); + } else { + var wp = transform.TransformPoint(pos); + DrawBoneCircle(wp, SpineHandles.BoneColor, transform.forward, boneScale * skeletonRenderScale); + } + } + + public static void DrawBone (Transform transform, Bone b, float boneScale, Color color, float skeletonRenderScale = 1f) { + var pos = new Vector3(b.WorldX * skeletonRenderScale, b.WorldY * skeletonRenderScale, 0); + float length = b.Data.Length; + if (length > 0) { + Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX); + Vector3 scale = Vector3.one * length * b.WorldScaleX; + const float my = 1.5f; + scale.y *= (SpineHandles.handleScale + 1f) * 0.5f; + scale.y = Mathf.Clamp(scale.x, -my, my); + SpineHandles.GetBoneMaterial(color).SetPass(0); + Graphics.DrawMeshNow(SpineHandles.BoneMesh, transform.localToWorldMatrix * Matrix4x4.TRS(pos, rot, scale)); + } else { + var wp = transform.TransformPoint(pos); + DrawBoneCircle(wp, color, transform.forward, boneScale * skeletonRenderScale); + } + } + + public static void DrawPaths (Transform transform, Skeleton skeleton) { + foreach (Slot s in skeleton.DrawOrder) { + var p = s.Attachment as PathAttachment; + if (p != null) SpineHandles.DrawPath(s, p, transform, true); + } + } + + static float[] pathVertexBuffer; + public static void DrawPath (Slot s, PathAttachment p, Transform t, bool includeName) { + int worldVerticesLength = p.WorldVerticesLength; + + if (pathVertexBuffer == null || pathVertexBuffer.Length < worldVerticesLength) + pathVertexBuffer = new float[worldVerticesLength]; + + float[] pv = pathVertexBuffer; + p.ComputeWorldVertices(s, pv); + + var ocolor = Handles.color; + Handles.color = SpineHandles.PathColor; + + Matrix4x4 m = t.localToWorldMatrix; + const int step = 6; + int n = worldVerticesLength - step; + Vector3 p0, p1, p2, p3; + for (int i = 2; i < n; i += step) { + p0 = m.MultiplyPoint(new Vector3(pv[i], pv[i+1])); + p1 = m.MultiplyPoint(new Vector3(pv[i+2], pv[i+3])); + p2 = m.MultiplyPoint(new Vector3(pv[i+4], pv[i+5])); + p3 = m.MultiplyPoint(new Vector3(pv[i+6], pv[i+7])); + DrawCubicBezier(p0, p1, p2, p3); + } + + n += step; + if (p.Closed) { + p0 = m.MultiplyPoint(new Vector3(pv[n - 4], pv[n - 3])); + p1 = m.MultiplyPoint(new Vector3(pv[n - 2], pv[n - 1])); + p2 = m.MultiplyPoint(new Vector3(pv[0], pv[1])); + p3 = m.MultiplyPoint(new Vector3(pv[2], pv[3])); + DrawCubicBezier(p0, p1, p2, p3); + } + + const float endCapSize = 0.05f; + Vector3 firstPoint = m.MultiplyPoint(new Vector3(pv[2], pv[3])); + SpineHandles.DrawDot(firstPoint, endCapSize); + + //if (!p.Closed) SpineHandles.DrawDot(m.MultiplyPoint(new Vector3(pv[n - 4], pv[n - 3])), endCapSize); + if (includeName) Handles.Label(firstPoint + new Vector3(0,0.1f), p.Name, PathNameStyle); + + Handles.color = ocolor; + } + + public static void DrawDot (Vector3 position, float size) { + Handles.DotHandleCap(0, position, Quaternion.identity, size * HandleUtility.GetHandleSize(position), EventType.Ignore); //Handles.DotCap(0, position, Quaternion.identity, size * HandleUtility.GetHandleSize(position)); + } + + public static void DrawBoundingBoxes (Transform transform, Skeleton skeleton) { + foreach (var slot in skeleton.Slots) { + var bba = slot.Attachment as BoundingBoxAttachment; + if (bba != null) SpineHandles.DrawBoundingBox(slot, bba, transform); + } + } + + public static void DrawBoundingBox (Slot slot, BoundingBoxAttachment box, Transform t) { + if (box.Vertices.Length <= 2) return; // Handle cases where user creates a BoundingBoxAttachment but doesn't actually define it. + + var worldVerts = new float[box.WorldVerticesLength]; + box.ComputeWorldVertices(slot, worldVerts); + + Handles.color = Color.green; + Vector3 lastVert = Vector3.zero; + Vector3 vert = Vector3.zero; + Vector3 firstVert = t.TransformPoint(new Vector3(worldVerts[0], worldVerts[1], 0)); + for (int i = 0; i < worldVerts.Length; i += 2) { + vert.x = worldVerts[i]; + vert.y = worldVerts[i + 1]; + vert.z = 0; + + vert = t.TransformPoint(vert); + + if (i > 0) + Handles.DrawLine(lastVert, vert); + + lastVert = vert; + } + + Handles.DrawLine(lastVert, firstVert); + } + + public static void DrawPointAttachment (Bone bone, PointAttachment pointAttachment, Transform skeletonTransform) { + if (bone == null) return; + if (pointAttachment == null) return; + + Vector2 localPos; + pointAttachment.ComputeWorldPosition(bone, out localPos.x, out localPos.y); + float localRotation = pointAttachment.ComputeWorldRotation(bone); + Matrix4x4 m = Matrix4x4.TRS(localPos, Quaternion.Euler(0, 0, localRotation), Vector3.one) * Matrix4x4.TRS(Vector3.right * 0.25f, Quaternion.identity, Vector3.one); + + DrawBoneCircle(skeletonTransform.TransformPoint(localPos), SpineHandles.PointColor, Vector3.back, 1.3f); + DrawArrowhead(skeletonTransform.localToWorldMatrix * m); + } + + public static void DrawConstraints (Transform transform, Skeleton skeleton, float skeletonRenderScale = 1f) { + Vector3 targetPos; + Vector3 pos; + bool active; + Color handleColor; + const float Thickness = 4f; + Vector3 normal = transform.forward; + + // Transform Constraints + handleColor = SpineHandles.TransformContraintColor; + foreach (var tc in skeleton.TransformConstraints) { + var targetBone = tc.Target; + targetPos = targetBone.GetWorldPosition(transform, skeletonRenderScale); + + if (tc.TranslateMix > 0) { + if (tc.TranslateMix != 1f) { + Handles.color = handleColor; + foreach (var b in tc.Bones) { + pos = b.GetWorldPosition(transform, skeletonRenderScale); + Handles.DrawDottedLine(targetPos, pos, Thickness); + } + } + SpineHandles.DrawBoneCircle(targetPos, handleColor, normal, 1.3f * skeletonRenderScale); + Handles.color = handleColor; + SpineHandles.DrawCrosshairs(targetPos, 0.2f, targetBone.A, targetBone.B, targetBone.C, targetBone.D, transform, skeletonRenderScale); + } + } + + // IK Constraints + handleColor = SpineHandles.IkColor; + foreach (var ikc in skeleton.IkConstraints) { + Bone targetBone = ikc.Target; + targetPos = targetBone.GetWorldPosition(transform, skeletonRenderScale); + var bones = ikc.Bones; + active = ikc.Mix > 0; + if (active) { + pos = bones.Items[0].GetWorldPosition(transform, skeletonRenderScale); + switch (bones.Count) { + case 1: { + Handles.color = handleColor; + Handles.DrawLine(targetPos, pos); + SpineHandles.DrawBoneCircle(targetPos, handleColor, normal); + var m = bones.Items[0].GetMatrix4x4(); + m.m03 = targetBone.WorldX * skeletonRenderScale; + m.m13 = targetBone.WorldY * skeletonRenderScale; + SpineHandles.DrawArrowhead(transform.localToWorldMatrix * m); + break; + } + case 2: { + Bone childBone = bones.Items[1]; + Vector3 child = childBone.GetWorldPosition(transform, skeletonRenderScale); + Handles.color = handleColor; + Handles.DrawLine(child, pos); + Handles.DrawLine(targetPos, child); + SpineHandles.DrawBoneCircle(pos, handleColor, normal, 0.5f); + SpineHandles.DrawBoneCircle(child, handleColor, normal, 0.5f); + SpineHandles.DrawBoneCircle(targetPos, handleColor, normal); + var m = childBone.GetMatrix4x4(); + m.m03 = targetBone.WorldX * skeletonRenderScale; + m.m13 = targetBone.WorldY * skeletonRenderScale; + SpineHandles.DrawArrowhead(transform.localToWorldMatrix * m); + break; + } + } + } + //Handles.Label(targetPos, ikc.Data.Name, SpineHandles.BoneNameStyle); + } + + // Path Constraints + handleColor = SpineHandles.PathColor; + foreach (var pc in skeleton.PathConstraints) { + active = pc.TranslateMix > 0; + if (active) + foreach (var b in pc.Bones) + SpineHandles.DrawBoneCircle(b.GetWorldPosition(transform, skeletonRenderScale), handleColor, normal, 1f * skeletonRenderScale); + } + } + + static void DrawCrosshairs2D (Vector3 position, float scale, float skeletonRenderScale = 1f) { + scale *= SpineHandles.handleScale * skeletonRenderScale; + Handles.DrawLine(position + new Vector3(-scale, 0), position + new Vector3(scale, 0)); + Handles.DrawLine(position + new Vector3(0, -scale), position + new Vector3(0, scale)); + } + + static void DrawCrosshairs (Vector3 position, float scale, float a, float b, float c, float d, Transform transform, float skeletonRenderScale = 1f) { + scale *= SpineHandles.handleScale * skeletonRenderScale; + + var xOffset = (Vector3)(new Vector2(a, c).normalized * scale); + var yOffset = (Vector3)(new Vector2(b, d).normalized * scale); + xOffset = transform.TransformDirection(xOffset); + yOffset = transform.TransformDirection(yOffset); + + Handles.DrawLine(position + xOffset, position - xOffset); + Handles.DrawLine(position + yOffset, position - yOffset); + } + + static void DrawArrowhead2D (Vector3 pos, float localRotation, float scale = 1f) { + scale *= SpineHandles.handleScale; + + SpineHandles.IKMaterial.SetPass(0); + Graphics.DrawMeshNow(SpineHandles.ArrowheadMesh, Matrix4x4.TRS(pos, Quaternion.Euler(0, 0, localRotation), new Vector3(scale, scale, scale))); + } + + static void DrawArrowhead (Vector3 pos, Quaternion worldQuaternion) { + Graphics.DrawMeshNow(SpineHandles.ArrowheadMesh, pos, worldQuaternion, 0); + } + + static void DrawArrowhead (Matrix4x4 m) { + float s = SpineHandles.handleScale; + m.m00 *= s; + m.m01 *= s; + m.m02 *= s; + m.m10 *= s; + m.m11 *= s; + m.m12 *= s; + m.m20 *= s; + m.m21 *= s; + m.m22 *= s; + + SpineHandles.IKMaterial.SetPass(0); + Graphics.DrawMeshNow(SpineHandles.ArrowheadMesh, m); + } + + static void DrawBoneCircle (Vector3 pos, Color outlineColor, Vector3 normal, float scale = 1f) { + scale *= SpineHandles.handleScale; + + Color o = Handles.color; + Handles.color = outlineColor; + float firstScale = 0.08f * scale; + Handles.DrawSolidDisc(pos, normal, firstScale); + const float Thickness = 0.03f; + float secondScale = firstScale - (Thickness * SpineHandles.handleScale * scale); + + if (secondScale > 0f) { + Handles.color = new Color(0.3f, 0.3f, 0.3f, 0.5f); + Handles.DrawSolidDisc(pos, normal, secondScale); + } + + Handles.color = o; + } + + internal static void DrawCubicBezier (Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) { + Handles.DrawBezier(p0, p3, p1, p2, Handles.color, Texture2D.whiteTexture, 2f); + // const float dotSize = 0.01f; + // Quaternion q = Quaternion.identity; + // Handles.DotCap(0, p0, q, dotSize); + // Handles.DotCap(0, p1, q, dotSize); + // Handles.DotCap(0, p2, q, dotSize); + // Handles.DotCap(0, p3, q, dotSize); + // Handles.DrawLine(p0, p1); + // Handles.DrawLine(p3, p2); + } + } + +} diff --git a/Assets/Spine/spine-unity/Editor/SpineEditorUtilities.cs.meta b/Assets/Spine/spine-unity/Editor/SpineEditorUtilities.cs.meta new file mode 100644 index 0000000..544e477 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SpineEditorUtilities.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f834d5cd806ec4645915ac315edbdc60 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/Editor/SpineInspectorUtility.cs b/Assets/Spine/spine-unity/Editor/SpineInspectorUtility.cs new file mode 100644 index 0000000..312f6f8 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SpineInspectorUtility.cs @@ -0,0 +1,379 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.Reflection; + +namespace Spine.Unity.Editor { + public static class SpineInspectorUtility { + + public static string Pluralize (int n, string singular, string plural) { + return n + " " + (n == 1 ? singular : plural); + } + + public static string PluralThenS (int n) { + return n == 1 ? "" : "s"; + } + + public static string EmDash { + get { return "\u2014"; } + } + + static GUIContent tempContent; + internal static GUIContent TempContent (string text, Texture2D image = null, string tooltip = null) { + if (tempContent == null) tempContent = new GUIContent(); + tempContent.text = text; + tempContent.image = image; + tempContent.tooltip = tooltip; + return tempContent; + } + + public static void PropertyFieldWideLabel (SerializedProperty property, GUIContent label = null, float minimumLabelWidth = 150) { + EditorGUIUtility.labelWidth = minimumLabelWidth; + EditorGUILayout.PropertyField(property, label ?? TempContent(property.displayName, null, property.tooltip)); + EditorGUIUtility.labelWidth = 0; // Resets to default + } + + public static void PropertyFieldFitLabel (SerializedProperty property, GUIContent label = null, float extraSpace = 5f) { + label = label ?? TempContent(property.displayName, null, property.tooltip); + float width = GUI.skin.label.CalcSize(TempContent(label.text)).x + extraSpace; + if (label.image != null) + width += EditorGUIUtility.singleLineHeight; + PropertyFieldWideLabel(property, label, width); + } + + /// Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty) + public static void ToggleLeftLayout (SerializedProperty property, GUIContent label = null, float width = 120f) { + if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip); + + if (property.hasMultipleDifferentValues) { + bool previousShowMixedValue = EditorGUI.showMixedValue; + EditorGUI.showMixedValue = true; + + bool clicked = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width)); + if (clicked) property.boolValue = true; // Set all values to true when clicked. + + EditorGUI.showMixedValue = previousShowMixedValue; + } else { + property.boolValue = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width)); + } + } + + /// Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty) + public static void ToggleLeft (Rect rect, SerializedProperty property, GUIContent label = null) { + if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip); + + if (property.hasMultipleDifferentValues) { + bool previousShowMixedValue = EditorGUI.showMixedValue; + EditorGUI.showMixedValue = true; + + bool clicked = EditorGUI.ToggleLeft(rect, label, property.boolValue); + if (clicked) property.boolValue = true; // Set all values to true when clicked. + + EditorGUI.showMixedValue = previousShowMixedValue; + } else { + property.boolValue = EditorGUI.ToggleLeft(rect, label, property.boolValue); + } + } + + public static bool UndoRedoPerformed (UnityEngine.Event current) { + return current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed"; + } + + public static Texture2D UnityIcon() { + return EditorGUIUtility.ObjectContent(null, typeof(T)).image as Texture2D; + } + + public static Texture2D UnityIcon(System.Type type) { + return EditorGUIUtility.ObjectContent(null, type).image as Texture2D; + } + + #region SerializedProperty Helpers + public static SerializedProperty FindBaseOrSiblingProperty (this SerializedProperty property, string propertyName) { + if (string.IsNullOrEmpty(propertyName)) return null; + + SerializedProperty relativeProperty = property.serializedObject.FindProperty(propertyName); // baseProperty + + // If base property is not found, look for the sibling property. + if (relativeProperty == null) { + string propertyPath = property.propertyPath; + int localPathLength = property.name.Length; + + string newPropertyPath = propertyPath.Remove(propertyPath.Length - localPathLength, localPathLength) + propertyName; + relativeProperty = property.serializedObject.FindProperty(newPropertyPath); + + // If a direct sibling property was not found, try to find the sibling of the array. + if (relativeProperty == null && property.isArray) { + int propertyPathLength = propertyPath.Length; + + int dotCount = 0; + const int SiblingOfListDotCount = 3; + for (int i = 1; i < propertyPathLength; i++) { + if (propertyPath[propertyPathLength - i] == '.') { + dotCount++; + if (dotCount >= SiblingOfListDotCount) { + localPathLength = i - 1; + break; + } + } + } + + newPropertyPath = propertyPath.Remove(propertyPath.Length - localPathLength, localPathLength) + propertyName; + relativeProperty = property.serializedObject.FindProperty(newPropertyPath); + } + } + + return relativeProperty; + } + #endregion + + #region Layout Scopes + static GUIStyle grayMiniLabel; + public static GUIStyle GrayMiniLabel { + get { + if (grayMiniLabel == null) { + grayMiniLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel); + grayMiniLabel.alignment = TextAnchor.UpperLeft; + } + return grayMiniLabel; + } + } + + public class LabelWidthScope : System.IDisposable { + public LabelWidthScope (float minimumLabelWidth = 190f) { + EditorGUIUtility.labelWidth = minimumLabelWidth; + } + + public void Dispose () { + EditorGUIUtility.labelWidth = 0f; + } + } + + public class IndentScope : System.IDisposable { + public IndentScope () { EditorGUI.indentLevel++; } + public void Dispose () { EditorGUI.indentLevel--; } + } + + public class BoxScope : System.IDisposable { + readonly bool indent; + + static GUIStyle boxScopeStyle; + public static GUIStyle BoxScopeStyle { + get { + if (boxScopeStyle == null) { + boxScopeStyle = new GUIStyle(EditorStyles.helpBox); + RectOffset p = boxScopeStyle.padding; // RectOffset is a class + p.right += 6; + p.top += 1; + p.left += 3; + } + + return boxScopeStyle; + } + } + + public BoxScope (bool indent = true) { + this.indent = indent; + EditorGUILayout.BeginVertical(BoxScopeStyle); + if (indent) EditorGUI.indentLevel++; + } + + public void Dispose () { + if (indent) EditorGUI.indentLevel--; + EditorGUILayout.EndVertical(); + } + } + #endregion + + #region Button + const float CenterButtonMaxWidth = 270f; + const float CenterButtonHeight = 35f; + static GUIStyle spineButtonStyle; + static GUIStyle SpineButtonStyle { + get { + if (spineButtonStyle == null) { + spineButtonStyle = new GUIStyle(GUI.skin.button); + spineButtonStyle.name = "Spine Button"; + spineButtonStyle.padding = new RectOffset(10, 10, 10, 10); + } + return spineButtonStyle; + } + } + + public static bool LargeCenteredButton (string label, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) { + if (sideSpace) { + bool clicked; + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.Space(); + clicked = GUILayout.Button(label, SpineButtonStyle, GUILayout.MaxWidth(maxWidth), GUILayout.Height(CenterButtonHeight)); + EditorGUILayout.Space(); + } + EditorGUILayout.Space(); + return clicked; + } else { + return GUILayout.Button(label, GUILayout.MaxWidth(CenterButtonMaxWidth), GUILayout.Height(CenterButtonHeight)); + } + } + + public static bool LargeCenteredButton (GUIContent content, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) { + if (sideSpace) { + bool clicked; + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.Space(); + clicked = GUILayout.Button(content, SpineButtonStyle, GUILayout.MaxWidth(maxWidth), GUILayout.Height(CenterButtonHeight)); + EditorGUILayout.Space(); + } + EditorGUILayout.Space(); + return clicked; + } else { + return GUILayout.Button(content, GUILayout.MaxWidth(CenterButtonMaxWidth), GUILayout.Height(CenterButtonHeight)); + } + } + + public static bool CenteredButton (GUIContent content, float height = 20f, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) { + if (sideSpace) { + bool clicked; + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.Space(); + clicked = GUILayout.Button(content, GUILayout.MaxWidth(maxWidth), GUILayout.Height(height)); + EditorGUILayout.Space(); + } + EditorGUILayout.Space(); + return clicked; + } else { + return GUILayout.Button(content, GUILayout.MaxWidth(maxWidth), GUILayout.Height(height)); + } + } + #endregion + + #region Multi-Editing Helpers + public static bool TargetsUseSameData (SerializedObject so) { + if (so.isEditingMultipleObjects) { + int n = so.targetObjects.Length; + var first = so.targetObjects[0] as IHasSkeletonDataAsset; + for (int i = 1; i < n; i++) { + var sr = so.targetObjects[i] as IHasSkeletonDataAsset; + if (sr != null && sr.SkeletonDataAsset != first.SkeletonDataAsset) + return false; + } + } + return true; + } + + public static SerializedObject GetRenderersSerializedObject (SerializedObject serializedObject) { + if (serializedObject.isEditingMultipleObjects) { + var renderers = new List(); + foreach (var o in serializedObject.targetObjects) { + var component = o as Component; + if (component != null) { + var renderer = component.GetComponent(); + if (renderer != null) + renderers.Add(renderer); + } + } + return new SerializedObject(renderers.ToArray()); + } else { + var component = serializedObject.targetObject as Component; + if (component != null) { + var renderer = component.GetComponent(); + if (renderer != null) + return new SerializedObject(renderer); + } + } + + return null; + } + #endregion + + #region Sorting Layer Field Helpers + static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer", "MeshRenderer.sortingLayerID"); + static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer", "MeshRenderer.sortingOrder"); + + static MethodInfo m_SortingLayerFieldMethod; + static MethodInfo SortingLayerFieldMethod { + get { + if (m_SortingLayerFieldMethod == null) + m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new [] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null); + + return m_SortingLayerFieldMethod; + } + } + + public struct SerializedSortingProperties { + public SerializedObject renderer; + public SerializedProperty sortingLayerID; + public SerializedProperty sortingOrder; + + public SerializedSortingProperties (Renderer r) : this(new SerializedObject(r)) {} + public SerializedSortingProperties (Object[] renderers) : this(new SerializedObject(renderers)) {} + + /// + /// Initializes a new instance of the + /// struct. + /// + /// SerializedObject of the renderer. Use + /// to easily generate this. + public SerializedSortingProperties (SerializedObject rendererSerializedObject) { + renderer = rendererSerializedObject; + sortingLayerID = renderer.FindProperty("m_SortingLayerID"); + sortingOrder = renderer.FindProperty("m_SortingOrder"); + } + + public void ApplyModifiedProperties () { + renderer.ApplyModifiedProperties(); + + // SetDirty + if (renderer.isEditingMultipleObjects) + foreach (var o in renderer.targetObjects) + EditorUtility.SetDirty(o); + else + EditorUtility.SetDirty(renderer.targetObject); + } + } + + public static void SortingPropertyFields (SerializedSortingProperties prop, bool applyModifiedProperties) { + if (applyModifiedProperties) + EditorGUI.BeginChangeCheck(); + + if (SpineInspectorUtility.SortingLayerFieldMethod != null && prop.sortingLayerID != null) + SpineInspectorUtility.SortingLayerFieldMethod.Invoke(null, new object[] { SortingLayerLabel, prop.sortingLayerID, EditorStyles.popup } ); + else + EditorGUILayout.PropertyField(prop.sortingLayerID); + + EditorGUILayout.PropertyField(prop.sortingOrder, OrderInLayerLabel); + + if (applyModifiedProperties && EditorGUI.EndChangeCheck()) + prop.ApplyModifiedProperties(); + } + #endregion + } +} diff --git a/Assets/Spine/spine-unity/Editor/SpineInspectorUtility.cs.meta b/Assets/Spine/spine-unity/Editor/SpineInspectorUtility.cs.meta new file mode 100644 index 0000000..3219ca0 --- /dev/null +++ b/Assets/Spine/spine-unity/Editor/SpineInspectorUtility.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 663715b5714e2db499192c8d91ef1f86 +timeCreated: 1457404957 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/ISkeletonAnimation.cs b/Assets/Spine/spine-unity/ISkeletonAnimation.cs new file mode 100644 index 0000000..d1ab2ff --- /dev/null +++ b/Assets/Spine/spine-unity/ISkeletonAnimation.cs @@ -0,0 +1,75 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine.Unity { + public delegate void UpdateBonesDelegate (ISkeletonAnimation animated); + + /// A Spine-Unity Component that animates a Skeleton but not necessarily with a Spine.AnimationState. + public interface ISkeletonAnimation { + event UpdateBonesDelegate UpdateLocal; + event UpdateBonesDelegate UpdateWorld; + event UpdateBonesDelegate UpdateComplete; + + //void LateUpdate (); + Skeleton Skeleton { get; } + } + + /// Holds a reference to a SkeletonDataAsset. + public interface IHasSkeletonDataAsset { + /// Gets the SkeletonDataAsset of the Spine Component. + SkeletonDataAsset SkeletonDataAsset { get; } + } + + /// A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset. + public interface ISkeletonComponent { + /// Gets the SkeletonDataAsset of the Spine Component. + //[System.Obsolete] + SkeletonDataAsset SkeletonDataAsset { get; } + + /// Gets the Spine.Skeleton instance of the Spine Component. This is equivalent to SkeletonRenderer's .skeleton. + Skeleton Skeleton { get; } + } + + /// A Spine-Unity Component that uses a Spine.AnimationState to animate its skeleton. + public interface IAnimationStateComponent { + /// Gets the Spine.AnimationState of the animated Spine Component. This is equivalent to SkeletonAnimation.state. + AnimationState AnimationState { get; } + } + + /// A Spine-Unity Component that holds a reference to a SkeletonRenderer. + public interface IHasSkeletonRenderer { + SkeletonRenderer SkeletonRenderer { get; } + } + + /// A Spine-Unity Component that holds a reference to an ISkeletonComponent. + public interface IHasSkeletonComponent { + ISkeletonComponent SkeletonComponent { get; } + } +} diff --git a/Assets/Spine/spine-unity/ISkeletonAnimation.cs.meta b/Assets/Spine/spine-unity/ISkeletonAnimation.cs.meta new file mode 100644 index 0000000..68e421f --- /dev/null +++ b/Assets/Spine/spine-unity/ISkeletonAnimation.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a7b480b941568134891f411137bfbf55 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation.meta b/Assets/Spine/spine-unity/Mesh Generation.meta new file mode 100644 index 0000000..ae63350 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: c5d065c4fe677ad4495a852580ec32fa +folderAsset: yes +timeCreated: 1455493477 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/DoubleBuffered.cs b/Assets/Spine/spine-unity/Mesh Generation/DoubleBuffered.cs new file mode 100644 index 0000000..c4dea80 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/DoubleBuffered.cs @@ -0,0 +1,46 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +namespace Spine.Unity { + public class DoubleBuffered where T : new() { + readonly T a = new T(); + readonly T b = new T(); + bool usingA; + + public T GetCurrent () { + return usingA ? a : b; + } + + public T GetNext () { + usingA = !usingA; + return usingA ? a : b; + } + } +} diff --git a/Assets/Spine/spine-unity/Mesh Generation/DoubleBuffered.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/DoubleBuffered.cs.meta new file mode 100644 index 0000000..2e570e4 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/DoubleBuffered.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 08b76da7751523448a87e528c48a5399 +timeCreated: 1457396939 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/SpineMesh.cs b/Assets/Spine/spine-unity/Mesh Generation/SpineMesh.cs new file mode 100644 index 0000000..e17812e --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/SpineMesh.cs @@ -0,0 +1,1546 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Not for optimization. Do not disable. +#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame. +//#define SPINE_DEBUG + +using UnityEngine; +using System; +using System.Collections.Generic; + +namespace Spine.Unity { + public static class SpineMesh { + internal const HideFlags MeshHideflags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor; + + /// Factory method for creating a new mesh for use in Spine components. This can be called in field initializers. + public static Mesh NewSkeletonMesh () { + var m = new Mesh(); + m.MarkDynamic(); + m.name = "Skeleton Mesh"; + m.hideFlags = SpineMesh.MeshHideflags; + return m; + } + } + + /// Instructions for how to generate a mesh or submesh: "Render this skeleton's slots: start slot, up to but not including endSlot, using this material." + public struct SubmeshInstruction { + public Skeleton skeleton; + public int startSlot; + public int endSlot; + public Material material; + + public bool forceSeparate; + public int preActiveClippingSlotSource; + + #if SPINE_TRIANGLECHECK + // Cached values because they are determined in the process of generating instructions, + // but could otherwise be pulled from accessing attachments, checking materials and counting tris and verts. + public int rawTriangleCount; + public int rawVertexCount; + public int rawFirstVertexIndex; + public bool hasClipping; + #endif + + /// The number of slots in this SubmeshInstruction's range. Not necessarily the number of attachments. + public int SlotCount { get { return endSlot - startSlot; } } + + public override string ToString () { + return + string.Format("[SubmeshInstruction: slots {0} to {1}. (Material){2}. preActiveClippingSlotSource:{3}]", + startSlot, + endSlot - 1, + material == null ? "" : material.name, + preActiveClippingSlotSource + ); + } + } + + public delegate void MeshGeneratorDelegate (MeshGeneratorBuffers buffers); + + public struct MeshGeneratorBuffers { + /// The vertex count that will actually be used for the mesh. The Lengths of the buffer arrays may be larger than this number. + public int vertexCount; + + /// Vertex positions. To be used for UnityEngine.Mesh.vertices. + public Vector3[] vertexBuffer; + + /// Vertex UVs. To be used for UnityEngine.Mesh.uvs. + public Vector2[] uvBuffer; + + /// Vertex colors. To be used for UnityEngine.Mesh.colors32. + public Color32[] colorBuffer; + + /// The Spine rendering component's MeshGenerator. + public MeshGenerator meshGenerator; + } + + [System.Serializable] + public class MeshGenerator { + public Settings settings = Settings.Default; + + [System.Serializable] + public struct Settings { + public bool useClipping; + [Space] + [Range(-0.1f, 0f)] public float zSpacing; + [Space] + [Header("Vertex Data")] + public bool pmaVertexColors; + public bool tintBlack; + public bool calculateTangents; + public bool addNormals; + public bool immutableTriangles; + + static public Settings Default { + get { + return new Settings { + pmaVertexColors = true, + zSpacing = 0f, + useClipping = true, + tintBlack = false, + calculateTangents = false, + //renderMeshes = true, + addNormals = false, + immutableTriangles = false + }; + } + } + } + + const float BoundsMinDefault = float.PositiveInfinity; + const float BoundsMaxDefault = float.NegativeInfinity; + + [NonSerialized] readonly ExposedList vertexBuffer = new ExposedList(4); + [NonSerialized] readonly ExposedList uvBuffer = new ExposedList(4); + [NonSerialized] readonly ExposedList colorBuffer = new ExposedList(4); + [NonSerialized] readonly ExposedList> submeshes = new ExposedList> { new ExposedList(6) }; // start with 1 submesh. + + [NonSerialized] Vector2 meshBoundsMin, meshBoundsMax; + [NonSerialized] float meshBoundsThickness; + [NonSerialized] int submeshIndex = 0; + + [NonSerialized] SkeletonClipping clipper = new SkeletonClipping(); + [NonSerialized] float[] tempVerts = new float[8]; + [NonSerialized] int[] regionTriangles = { 0, 1, 2, 2, 3, 0 }; + + #region Optional Buffers + [NonSerialized] Vector3[] normals; + [NonSerialized] Vector4[] tangents; + [NonSerialized] Vector2[] tempTanBuffer; + [NonSerialized] ExposedList uv2; + [NonSerialized] ExposedList uv3; + #endregion + + public MeshGenerator () { + submeshes.TrimExcess(); + } + + public int VertexCount { get { return vertexBuffer.Count; } } + + public MeshGeneratorBuffers Buffers { + get { + return new MeshGeneratorBuffers { + vertexCount = this.VertexCount, + vertexBuffer = this.vertexBuffer.Items, + uvBuffer = this.uvBuffer.Items, + colorBuffer = this.colorBuffer.Items, + meshGenerator = this + }; + } + } + + #region Step 1 : Generate Instructions + public static void GenerateSingleSubmeshInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, Material material) { + ExposedList drawOrder = skeleton.drawOrder; + int drawOrderCount = drawOrder.Count; + + // Clear last state of attachments and submeshes + instructionOutput.Clear(); // submeshInstructions.Clear(); attachments.Clear(); + var workingSubmeshInstructions = instructionOutput.submeshInstructions; + workingSubmeshInstructions.Resize(1); + + #if SPINE_TRIANGLECHECK + instructionOutput.attachments.Resize(drawOrderCount); + var workingAttachmentsItems = instructionOutput.attachments.Items; + int totalRawVertexCount = 0; + #endif + + var current = new SubmeshInstruction { + skeleton = skeleton, + preActiveClippingSlotSource = -1, + startSlot = 0, + #if SPINE_TRIANGLECHECK + rawFirstVertexIndex = 0, + #endif + material = material, + forceSeparate = false, + endSlot = drawOrderCount + }; + + #if SPINE_TRIANGLECHECK + bool skeletonHasClipping = false; + var drawOrderItems = drawOrder.Items; + for (int i = 0; i < drawOrderCount; i++) { + Slot slot = drawOrderItems[i]; + Attachment attachment = slot.attachment; + + workingAttachmentsItems[i] = attachment; + int attachmentTriangleCount; + int attachmentVertexCount; + + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + attachmentVertexCount = 4; + attachmentTriangleCount = 6; + } else { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + attachmentVertexCount = meshAttachment.worldVerticesLength >> 1; + attachmentTriangleCount = meshAttachment.triangles.Length; + } else { + var clippingAttachment = attachment as ClippingAttachment; + if (clippingAttachment != null) { + current.hasClipping = true; + skeletonHasClipping = true; + } + attachmentVertexCount = 0; + attachmentTriangleCount = 0; + } + } + current.rawTriangleCount += attachmentTriangleCount; + current.rawVertexCount += attachmentVertexCount; + totalRawVertexCount += attachmentVertexCount; + + } + + instructionOutput.hasActiveClipping = skeletonHasClipping; + instructionOutput.rawVertexCount = totalRawVertexCount; + #endif + + workingSubmeshInstructions.Items[0] = current; + } + + public static void GenerateSkeletonRendererInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, Dictionary customSlotMaterials, List separatorSlots, bool generateMeshOverride, bool immutableTriangles = false) { +// if (skeleton == null) throw new ArgumentNullException("skeleton"); +// if (instructionOutput == null) throw new ArgumentNullException("instructionOutput"); + + ExposedList drawOrder = skeleton.drawOrder; + int drawOrderCount = drawOrder.Count; + + // Clear last state of attachments and submeshes + instructionOutput.Clear(); // submeshInstructions.Clear(); attachments.Clear(); + var workingSubmeshInstructions = instructionOutput.submeshInstructions; + #if SPINE_TRIANGLECHECK + instructionOutput.attachments.Resize(drawOrderCount); + var workingAttachmentsItems = instructionOutput.attachments.Items; + int totalRawVertexCount = 0; + bool skeletonHasClipping = false; + #endif + + var current = new SubmeshInstruction { + skeleton = skeleton, + preActiveClippingSlotSource = -1 + }; + + #if !SPINE_TK2D + bool isCustomSlotMaterialsPopulated = customSlotMaterials != null && customSlotMaterials.Count > 0; + #endif + + int separatorCount = separatorSlots == null ? 0 : separatorSlots.Count; + bool hasSeparators = separatorCount > 0; + + int clippingAttachmentSource = -1; + int lastPreActiveClipping = -1; // The index of the last slot that had an active ClippingAttachment. + SlotData clippingEndSlot = null; + int submeshIndex = 0; + var drawOrderItems = drawOrder.Items; + for (int i = 0; i < drawOrderCount; i++) { + Slot slot = drawOrderItems[i]; + Attachment attachment = slot.attachment; + #if SPINE_TRIANGLECHECK + workingAttachmentsItems[i] = attachment; + int attachmentVertexCount = 0, attachmentTriangleCount = 0; + #endif + + object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object. + bool noRender = false; // Using this allows empty slots as separators, and keeps separated parts more stable despite slots being reordered + + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + rendererObject = regionAttachment.RendererObject; + #if SPINE_TRIANGLECHECK + attachmentVertexCount = 4; + attachmentTriangleCount = 6; + #endif + } else { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + rendererObject = meshAttachment.RendererObject; + #if SPINE_TRIANGLECHECK + attachmentVertexCount = meshAttachment.worldVerticesLength >> 1; + attachmentTriangleCount = meshAttachment.triangles.Length; + #endif + } else { + #if SPINE_TRIANGLECHECK + var clippingAttachment = attachment as ClippingAttachment; + if (clippingAttachment != null) { + clippingEndSlot = clippingAttachment.endSlot; + clippingAttachmentSource = i; + current.hasClipping = true; + skeletonHasClipping = true; + } + #endif + noRender = true; + } + } + + if (clippingEndSlot != null && slot.data == clippingEndSlot && i != clippingAttachmentSource) { + clippingEndSlot = null; + clippingAttachmentSource = -1; + } + + // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator) + // Slot with a separator/new material will become the starting slot of the next new instruction. + if (hasSeparators) { //current.forceSeparate = hasSeparators && separatorSlots.Contains(slot); + current.forceSeparate = false; + for (int s = 0; s < separatorCount; s++) { + if (Slot.ReferenceEquals(slot, separatorSlots[s])) { + current.forceSeparate = true; + break; + } + } + } + + if (noRender) { + if (current.forceSeparate && generateMeshOverride) { // && current.rawVertexCount > 0) { + { // Add + current.endSlot = i; + current.preActiveClippingSlotSource = lastPreActiveClipping; + + workingSubmeshInstructions.Resize(submeshIndex + 1); + workingSubmeshInstructions.Items[submeshIndex] = current; + + submeshIndex++; + } + + current.startSlot = i; + lastPreActiveClipping = clippingAttachmentSource; + #if SPINE_TRIANGLECHECK + current.rawTriangleCount = 0; + current.rawVertexCount = 0; + current.rawFirstVertexIndex = totalRawVertexCount; + current.hasClipping = clippingAttachmentSource >= 0; + #endif + } + } else { + #if !SPINE_TK2D + Material material; + if (isCustomSlotMaterialsPopulated) { + if (!customSlotMaterials.TryGetValue(slot, out material)) + material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + } else { + material = (Material)((AtlasRegion)rendererObject).page.rendererObject; + } + #else + Material material = (rendererObject is Material) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; + #endif + + if (current.forceSeparate || (current.rawVertexCount > 0 && !System.Object.ReferenceEquals(current.material, material))) { // Material changed. Add the previous submesh. + { // Add + current.endSlot = i; + current.preActiveClippingSlotSource = lastPreActiveClipping; + + workingSubmeshInstructions.Resize(submeshIndex + 1); + workingSubmeshInstructions.Items[submeshIndex] = current; + submeshIndex++; + } + current.startSlot = i; + lastPreActiveClipping = clippingAttachmentSource; + #if SPINE_TRIANGLECHECK + current.rawTriangleCount = 0; + current.rawVertexCount = 0; + current.rawFirstVertexIndex = totalRawVertexCount; + current.hasClipping = clippingAttachmentSource >= 0; + #endif + } + + // Update state for the next Attachment. + current.material = material; + #if SPINE_TRIANGLECHECK + current.rawTriangleCount += attachmentTriangleCount; + current.rawVertexCount += attachmentVertexCount; + current.rawFirstVertexIndex = totalRawVertexCount; + totalRawVertexCount += attachmentVertexCount; + #endif + } + } + + if (current.rawVertexCount > 0) { + { // Add last or only submesh. + current.endSlot = drawOrderCount; + current.preActiveClippingSlotSource = lastPreActiveClipping; + current.forceSeparate = false; + + workingSubmeshInstructions.Resize(submeshIndex + 1); + workingSubmeshInstructions.Items[submeshIndex] = current; + //submeshIndex++; + } + } + + #if SPINE_TRIANGLECHECK + instructionOutput.hasActiveClipping = skeletonHasClipping; + instructionOutput.rawVertexCount = totalRawVertexCount; + #endif + instructionOutput.immutableTriangles = immutableTriangles; + } + + public static void TryReplaceMaterials (ExposedList workingSubmeshInstructions, Dictionary customMaterialOverride) { + // Material overrides are done here so they can be applied per submesh instead of per slot + // but they will still be passed through the GenerateMeshOverride delegate, + // and will still go through the normal material match check step in STEP 3. + var wsii = workingSubmeshInstructions.Items; + for (int i = 0; i < workingSubmeshInstructions.Count; i++) { + var m = wsii[i].material; + Material mo; + if (customMaterialOverride.TryGetValue(m, out mo)) + wsii[i].material = mo; + } + } + #endregion + + #region Step 2 : Populate vertex data and triangle index buffers. + public void Begin () { + vertexBuffer.Clear(false); + colorBuffer.Clear(false); + uvBuffer.Clear(false); + clipper.ClipEnd(); + + { + meshBoundsMin.x = BoundsMinDefault; + meshBoundsMin.y = BoundsMinDefault; + meshBoundsMax.x = BoundsMaxDefault; + meshBoundsMax.y = BoundsMaxDefault; + meshBoundsThickness = 0f; + } + + submeshIndex = 0; + submeshes.Count = 1; + //submeshes.Items[0].Clear(false); + } + + public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) { + var settings = this.settings; + + int newCount = submeshIndex + 1; + if (submeshes.Items.Length < newCount) + submeshes.Resize(newCount); + submeshes.Count = newCount; + var submesh = submeshes.Items[submeshIndex]; + if (submesh == null) + submeshes.Items[submeshIndex] = submesh = new ExposedList(); + + submesh.Clear(false); + + var skeleton = instruction.skeleton; + var drawOrderItems = skeleton.drawOrder.Items; + + Color32 color = default(Color32); + float skeletonA = skeleton.a * 255, skeletonR = skeleton.r, skeletonG = skeleton.g, skeletonB = skeleton.b; + Vector2 meshBoundsMin = this.meshBoundsMin, meshBoundsMax = this.meshBoundsMax; + + // Settings + float zSpacing = settings.zSpacing; + bool pmaVertexColors = settings.pmaVertexColors; + bool tintBlack = settings.tintBlack; + #if SPINE_TRIANGLECHECK + bool useClipping = settings.useClipping && instruction.hasClipping; + #else + bool useClipping = settings.useClipping; + #endif + + if (useClipping) { + if (instruction.preActiveClippingSlotSource >= 0) { + var slot = drawOrderItems[instruction.preActiveClippingSlotSource]; + clipper.ClipStart(slot, slot.attachment as ClippingAttachment); + } + } + + for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) { + var slot = drawOrderItems[slotIndex]; + var attachment = slot.attachment; + float z = zSpacing * slotIndex; + + var workingVerts = this.tempVerts; + float[] uvs; + int[] attachmentTriangleIndices; + int attachmentVertexCount; + int attachmentIndexCount; + + Color c = default(Color); + + // Identify and prepare values. + var region = attachment as RegionAttachment; + if (region != null) { + region.ComputeWorldVertices(slot.bone, workingVerts, 0); + uvs = region.uvs; + attachmentTriangleIndices = regionTriangles; + c.r = region.r; c.g = region.g; c.b = region.b; c.a = region.a; + attachmentVertexCount = 4; + attachmentIndexCount = 6; + } else { + var mesh = attachment as MeshAttachment; + if (mesh != null) { + int meshVerticesLength = mesh.worldVerticesLength; + if (workingVerts.Length < meshVerticesLength) { + workingVerts = new float[meshVerticesLength]; + this.tempVerts = workingVerts; + } + mesh.ComputeWorldVertices(slot, 0, meshVerticesLength, workingVerts, 0); //meshAttachment.ComputeWorldVertices(slot, tempVerts); + uvs = mesh.uvs; + attachmentTriangleIndices = mesh.triangles; + c.r = mesh.r; c.g = mesh.g; c.b = mesh.b; c.a = mesh.a; + attachmentVertexCount = meshVerticesLength >> 1; // meshVertexCount / 2; + attachmentIndexCount = mesh.triangles.Length; + } else { + if (useClipping) { + var clippingAttachment = attachment as ClippingAttachment; + if (clippingAttachment != null) { + clipper.ClipStart(slot, clippingAttachment); + continue; + } + } + + // If not any renderable attachment. + clipper.ClipEnd(slot); + continue; + } + } + + if (pmaVertexColors) { + color.a = (byte)(skeletonA * slot.a * c.a); + color.r = (byte)(skeletonR * slot.r * c.r * color.a); + color.g = (byte)(skeletonG * slot.g * c.g * color.a); + color.b = (byte)(skeletonB * slot.b * c.b * color.a); + if (slot.data.blendMode == BlendMode.Additive) color.a = 0; + } else { + color.a = (byte)(skeletonA * slot.a * c.a); + color.r = (byte)(skeletonR * slot.r * c.r * 255); + color.g = (byte)(skeletonG * slot.g * c.g * 255); + color.b = (byte)(skeletonB * slot.b * c.b * 255); + } + + if (useClipping && clipper.IsClipping) { + clipper.ClipTriangles(workingVerts, attachmentVertexCount << 1, attachmentTriangleIndices, attachmentIndexCount, uvs); + workingVerts = clipper.clippedVertices.Items; + attachmentVertexCount = clipper.clippedVertices.Count >> 1; + attachmentTriangleIndices = clipper.clippedTriangles.Items; + attachmentIndexCount = clipper.clippedTriangles.Count; + uvs = clipper.clippedUVs.Items; + } + + // Actually add slot/attachment data into buffers. + if (attachmentVertexCount != 0 && attachmentIndexCount != 0) { + if (tintBlack) + AddAttachmentTintBlack(slot.r2, slot.g2, slot.b2, attachmentVertexCount); + + //AddAttachment(workingVerts, uvs, color, attachmentTriangleIndices, attachmentVertexCount, attachmentIndexCount, ref meshBoundsMin, ref meshBoundsMax, z); + int ovc = vertexBuffer.Count; + // Add data to vertex buffers + { + int newVertexCount = ovc + attachmentVertexCount; + if (newVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize() + Array.Resize(ref vertexBuffer.Items, newVertexCount); + Array.Resize(ref uvBuffer.Items, newVertexCount); + Array.Resize(ref colorBuffer.Items, newVertexCount); + } + vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount; + } + + var vbi = vertexBuffer.Items; + var ubi = uvBuffer.Items; + var cbi = colorBuffer.Items; + if (ovc == 0) { + for (int i = 0; i < attachmentVertexCount; i++) { + int vi = ovc + i; + int i2 = i << 1; // i * 2 + float x = workingVerts[i2]; + float y = workingVerts[i2 + 1]; + + vbi[vi].x = x; + vbi[vi].y = y; + vbi[vi].z = z; + ubi[vi].x = uvs[i2]; + ubi[vi].y = uvs[i2 + 1]; + cbi[vi] = color; + + // Calculate bounds. + if (x < meshBoundsMin.x) meshBoundsMin.x = x; + if (x > meshBoundsMax.x) meshBoundsMax.x = x; + if (y < meshBoundsMin.y) meshBoundsMin.y = y; + if (y > meshBoundsMax.y) meshBoundsMax.y = y; + } + } else { + for (int i = 0; i < attachmentVertexCount; i++) { + int vi = ovc + i; + int i2 = i << 1; // i * 2 + float x = workingVerts[i2]; + float y = workingVerts[i2 + 1]; + + vbi[vi].x = x; + vbi[vi].y = y; + vbi[vi].z = z; + ubi[vi].x = uvs[i2]; + ubi[vi].y = uvs[i2 + 1]; + cbi[vi] = color; + + // Calculate bounds. + if (x < meshBoundsMin.x) meshBoundsMin.x = x; + else if (x > meshBoundsMax.x) meshBoundsMax.x = x; + if (y < meshBoundsMin.y) meshBoundsMin.y = y; + else if (y > meshBoundsMax.y) meshBoundsMax.y = y; + } + } + + + // Add data to triangle buffer + if (updateTriangles) { + int oldTriangleCount = submesh.Count; + { //submesh.Resize(oldTriangleCount + attachmentIndexCount); + int newTriangleCount = oldTriangleCount + attachmentIndexCount; + if (newTriangleCount > submesh.Items.Length) Array.Resize(ref submesh.Items, newTriangleCount); + submesh.Count = newTriangleCount; + } + var submeshItems = submesh.Items; + for (int i = 0; i < attachmentIndexCount; i++) + submeshItems[oldTriangleCount + i] = attachmentTriangleIndices[i] + ovc; + } + } + + clipper.ClipEnd(slot); + } + clipper.ClipEnd(); + + this.meshBoundsMin = meshBoundsMin; + this.meshBoundsMax = meshBoundsMax; + meshBoundsThickness = instruction.endSlot * zSpacing; + + // Trim or zero submesh triangles. + var currentSubmeshItems = submesh.Items; + for (int i = submesh.Count, n = currentSubmeshItems.Length; i < n; i++) + currentSubmeshItems[i] = 0; + + submeshIndex++; // Next AddSubmesh will use a new submeshIndex value. + } + + public void BuildMesh (SkeletonRendererInstruction instruction, bool updateTriangles) { + var wsii = instruction.submeshInstructions.Items; + for (int i = 0, n = instruction.submeshInstructions.Count; i < n; i++) + this.AddSubmesh(wsii[i], updateTriangles); + } + + // Use this faster method when no clipping is involved. + public void BuildMeshWithArrays (SkeletonRendererInstruction instruction, bool updateTriangles) { + var settings = this.settings; + int totalVertexCount = instruction.rawVertexCount; + + // Add data to vertex buffers + { + if (totalVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize() + Array.Resize(ref vertexBuffer.Items, totalVertexCount); + Array.Resize(ref uvBuffer.Items, totalVertexCount); + Array.Resize(ref colorBuffer.Items, totalVertexCount); + } + vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = totalVertexCount; + } + + // Populate Verts + Color32 color = default(Color32); + + int vertexIndex = 0; + var tempVerts = this.tempVerts; + Vector3 bmin = this.meshBoundsMin; + Vector3 bmax = this.meshBoundsMax; + + var vbi = vertexBuffer.Items; + var ubi = uvBuffer.Items; + var cbi = colorBuffer.Items; + int lastSlotIndex = 0; + + // drawOrder[endSlot] is excluded + for (int si = 0, n = instruction.submeshInstructions.Count; si < n; si++) { + var submesh = instruction.submeshInstructions.Items[si]; + var skeleton = submesh.skeleton; + var skeletonDrawOrderItems = skeleton.drawOrder.Items; + float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; + + int endSlot = submesh.endSlot; + int startSlot = submesh.startSlot; + lastSlotIndex = endSlot; + + if (settings.tintBlack) { + Vector2 rg, b2; + int vi = vertexIndex; + b2.y = 1f; + + { + if (uv2 == null) { + uv2 = new ExposedList(); + uv3 = new ExposedList(); + } + if (totalVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize() + Array.Resize(ref uv2.Items, totalVertexCount); + Array.Resize(ref uv3.Items, totalVertexCount); + } + uv2.Count = uv3.Count = totalVertexCount; + } + + var uv2i = uv2.Items; + var uv3i = uv3.Items; + + for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) { + var slot = skeletonDrawOrderItems[slotIndex]; + var attachment = slot.attachment; + + rg.x = slot.r2; //r + rg.y = slot.g2; //g + b2.x = slot.b2; //b + + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + uv2i[vi] = rg; uv2i[vi + 1] = rg; uv2i[vi + 2] = rg; uv2i[vi + 3] = rg; + uv3i[vi] = b2; uv3i[vi + 1] = b2; uv3i[vi + 2] = b2; uv3i[vi + 3] = b2; + vi += 4; + } else { //} if (settings.renderMeshes) { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + int meshVertexCount = meshAttachment.worldVerticesLength; + for (int iii = 0; iii < meshVertexCount; iii += 2) { + uv2i[vi] = rg; + uv3i[vi] = b2; + vi++; + } + } + } + } + } + + for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) { + var slot = skeletonDrawOrderItems[slotIndex]; + var attachment = slot.attachment; + float z = slotIndex * settings.zSpacing; + + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + regionAttachment.ComputeWorldVertices(slot.bone, tempVerts, 0); + + float x1 = tempVerts[RegionAttachment.BLX], y1 = tempVerts[RegionAttachment.BLY]; + float x2 = tempVerts[RegionAttachment.ULX], y2 = tempVerts[RegionAttachment.ULY]; + float x3 = tempVerts[RegionAttachment.URX], y3 = tempVerts[RegionAttachment.URY]; + float x4 = tempVerts[RegionAttachment.BRX], y4 = tempVerts[RegionAttachment.BRY]; + vbi[vertexIndex].x = x1; vbi[vertexIndex].y = y1; vbi[vertexIndex].z = z; + vbi[vertexIndex + 1].x = x4; vbi[vertexIndex + 1].y = y4; vbi[vertexIndex + 1].z = z; + vbi[vertexIndex + 2].x = x2; vbi[vertexIndex + 2].y = y2; vbi[vertexIndex + 2].z = z; + vbi[vertexIndex + 3].x = x3; vbi[vertexIndex + 3].y = y3; vbi[vertexIndex + 3].z = z; + + if (settings.pmaVertexColors) { + color.a = (byte)(a * slot.a * regionAttachment.a); + color.r = (byte)(r * slot.r * regionAttachment.r * color.a); + color.g = (byte)(g * slot.g * regionAttachment.g * color.a); + color.b = (byte)(b * slot.b * regionAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.Additive) color.a = 0; + } else { + color.a = (byte)(a * slot.a * regionAttachment.a); + color.r = (byte)(r * slot.r * regionAttachment.r * 255); + color.g = (byte)(g * slot.g * regionAttachment.g * 255); + color.b = (byte)(b * slot.b * regionAttachment.b * 255); + } + + cbi[vertexIndex] = color; cbi[vertexIndex + 1] = color; cbi[vertexIndex + 2] = color; cbi[vertexIndex + 3] = color; + + float[] regionUVs = regionAttachment.uvs; + ubi[vertexIndex].x = regionUVs[RegionAttachment.BLX]; ubi[vertexIndex].y = regionUVs[RegionAttachment.BLY]; + ubi[vertexIndex + 1].x = regionUVs[RegionAttachment.BRX]; ubi[vertexIndex + 1].y = regionUVs[RegionAttachment.BRY]; + ubi[vertexIndex + 2].x = regionUVs[RegionAttachment.ULX]; ubi[vertexIndex + 2].y = regionUVs[RegionAttachment.ULY]; + ubi[vertexIndex + 3].x = regionUVs[RegionAttachment.URX]; ubi[vertexIndex + 3].y = regionUVs[RegionAttachment.URY]; + + if (x1 < bmin.x) bmin.x = x1; // Potential first attachment bounds initialization. Initial min should not block initial max. Same for Y below. + if (x1 > bmax.x) bmax.x = x1; + if (x2 < bmin.x) bmin.x = x2; + else if (x2 > bmax.x) bmax.x = x2; + if (x3 < bmin.x) bmin.x = x3; + else if (x3 > bmax.x) bmax.x = x3; + if (x4 < bmin.x) bmin.x = x4; + else if (x4 > bmax.x) bmax.x = x4; + + if (y1 < bmin.y) bmin.y = y1; + if (y1 > bmax.y) bmax.y = y1; + if (y2 < bmin.y) bmin.y = y2; + else if (y2 > bmax.y) bmax.y = y2; + if (y3 < bmin.y) bmin.y = y3; + else if (y3 > bmax.y) bmax.y = y3; + if (y4 < bmin.y) bmin.y = y4; + else if (y4 > bmax.y) bmax.y = y4; + + vertexIndex += 4; + } else { //if (settings.renderMeshes) { + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + int meshVertexCount = meshAttachment.worldVerticesLength; + if (tempVerts.Length < meshVertexCount) this.tempVerts = tempVerts = new float[meshVertexCount]; + meshAttachment.ComputeWorldVertices(slot, tempVerts); + + if (settings.pmaVertexColors) { + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * color.a); + color.g = (byte)(g * slot.g * meshAttachment.g * color.a); + color.b = (byte)(b * slot.b * meshAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.Additive) color.a = 0; + } else { + color.a = (byte)(a * slot.a * meshAttachment.a); + color.r = (byte)(r * slot.r * meshAttachment.r * 255); + color.g = (byte)(g * slot.g * meshAttachment.g * 255); + color.b = (byte)(b * slot.b * meshAttachment.b * 255); + } + + float[] attachmentUVs = meshAttachment.uvs; + + // Potential first attachment bounds initialization. See conditions in RegionAttachment logic. + if (vertexIndex == 0) { + // Initial min should not block initial max. + // vi == vertexIndex does not always mean the bounds are fresh. It could be a submesh. Do not nuke old values by omitting the check. + // Should know that this is the first attachment in the submesh. slotIndex == startSlot could be an empty slot. + float fx = tempVerts[0], fy = tempVerts[1]; + if (fx < bmin.x) bmin.x = fx; + if (fx > bmax.x) bmax.x = fx; + if (fy < bmin.y) bmin.y = fy; + if (fy > bmax.y) bmax.y = fy; + } + + for (int iii = 0; iii < meshVertexCount; iii += 2) { + float x = tempVerts[iii], y = tempVerts[iii + 1]; + vbi[vertexIndex].x = x; vbi[vertexIndex].y = y; vbi[vertexIndex].z = z; + cbi[vertexIndex] = color; ubi[vertexIndex].x = attachmentUVs[iii]; ubi[vertexIndex].y = attachmentUVs[iii + 1]; + + if (x < bmin.x) bmin.x = x; + else if (x > bmax.x) bmax.x = x; + + if (y < bmin.y) bmin.y = y; + else if (y > bmax.y) bmax.y = y; + + vertexIndex++; + } + } + } + } + } + + this.meshBoundsMin = bmin; + this.meshBoundsMax = bmax; + this.meshBoundsThickness = lastSlotIndex * settings.zSpacing; + + int submeshInstructionCount = instruction.submeshInstructions.Count; + submeshes.Count = submeshInstructionCount; + + // Add triangles + if (updateTriangles) { + // Match submesh buffers count with submeshInstruction count. + if (this.submeshes.Items.Length < submeshInstructionCount) { + this.submeshes.Resize(submeshInstructionCount); + for (int i = 0, n = submeshInstructionCount; i < n; i++) { + var submeshBuffer = this.submeshes.Items[i]; + if (submeshBuffer == null) + this.submeshes.Items[i] = new ExposedList(); + else + submeshBuffer.Clear(false); + } + } + + var submeshInstructionsItems = instruction.submeshInstructions.Items; // This relies on the resize above. + + // Fill the buffers. + int attachmentFirstVertex = 0; + for (int smbi = 0; smbi < submeshInstructionCount; smbi++) { + var submeshInstruction = submeshInstructionsItems[smbi]; + var currentSubmeshBuffer = this.submeshes.Items[smbi]; + { //submesh.Resize(submesh.rawTriangleCount); + int newTriangleCount = submeshInstruction.rawTriangleCount; + if (newTriangleCount > currentSubmeshBuffer.Items.Length) + Array.Resize(ref currentSubmeshBuffer.Items, newTriangleCount); + else if (newTriangleCount < currentSubmeshBuffer.Items.Length) { + // Zero the extra. + var sbi = currentSubmeshBuffer.Items; + for (int ei = newTriangleCount, nn = sbi.Length; ei < nn; ei++) + sbi[ei] = 0; + } + currentSubmeshBuffer.Count = newTriangleCount; + } + + var tris = currentSubmeshBuffer.Items; + int triangleIndex = 0; + var skeleton = submeshInstruction.skeleton; + var skeletonDrawOrderItems = skeleton.drawOrder.Items; + for (int a = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; a < endSlot; a++) { + var attachment = skeletonDrawOrderItems[a].attachment; + if (attachment is RegionAttachment) { + tris[triangleIndex] = attachmentFirstVertex; + tris[triangleIndex + 1] = attachmentFirstVertex + 2; + tris[triangleIndex + 2] = attachmentFirstVertex + 1; + tris[triangleIndex + 3] = attachmentFirstVertex + 2; + tris[triangleIndex + 4] = attachmentFirstVertex + 3; + tris[triangleIndex + 5] = attachmentFirstVertex + 1; + triangleIndex += 6; + attachmentFirstVertex += 4; + continue; + } + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { + int[] attachmentTriangles = meshAttachment.triangles; + for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++) + tris[triangleIndex] = attachmentFirstVertex + attachmentTriangles[ii]; + attachmentFirstVertex += meshAttachment.worldVerticesLength >> 1; // length/2; + } + } + } + } + } + + public void ScaleVertexData (float scale) { + var vbi = vertexBuffer.Items; + for (int i = 0, n = vertexBuffer.Count; i < n; i++) { + vbi[i] *= scale; // vbi[i].x *= scale; vbi[i].y *= scale; + } + + meshBoundsMin *= scale; + meshBoundsMax *= scale; + meshBoundsThickness *= scale; + } + + void AddAttachmentTintBlack (float r2, float g2, float b2, int vertexCount) { + var rg = new Vector2(r2, g2); + var bo = new Vector2(b2, 1f); + + int ovc = vertexBuffer.Count; + int newVertexCount = ovc + vertexCount; + { + if (uv2 == null) { + uv2 = new ExposedList(); + uv3 = new ExposedList(); + } + if (newVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize() + Array.Resize(ref uv2.Items, newVertexCount); + Array.Resize(ref uv3.Items, newVertexCount); + } + uv2.Count = uv3.Count = newVertexCount; + } + + var uv2i = uv2.Items; + var uv3i = uv3.Items; + for (int i = 0; i < vertexCount; i++) { + uv2i[ovc + i] = rg; + uv3i[ovc + i] = bo; + } + } + #endregion + + #region Step 3 : Transfer vertex and triangle data to UnityEngine.Mesh + public void FillVertexData (Mesh mesh) { + var vbi = vertexBuffer.Items; + var ubi = uvBuffer.Items; + var cbi = colorBuffer.Items; + + // Zero the extra. + { + int listCount = vertexBuffer.Count; + int arrayLength = vertexBuffer.Items.Length; + var vector3zero = Vector3.zero; + for (int i = listCount; i < arrayLength; i++) + vbi[i] = vector3zero; + } + + // Set the vertex buffer. + { + mesh.vertices = vbi; + mesh.uv = ubi; + mesh.colors32 = cbi; + + if (float.IsInfinity(meshBoundsMin.x)) { // meshBoundsMin.x == BoundsMinDefault // == doesn't work on float Infinity constants. + mesh.bounds = new Bounds(); + } else { + //mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax); + Vector2 halfSize = (meshBoundsMax - meshBoundsMin) * 0.5f; + mesh.bounds = new Bounds { + center = (Vector3)(meshBoundsMin + halfSize), + extents = new Vector3(halfSize.x, halfSize.y, meshBoundsThickness * 0.5f) + }; + } + } + + { + int vertexCount = this.vertexBuffer.Count; + if (settings.addNormals) { + int oldLength = 0; + + if (normals == null) + normals = new Vector3[vertexCount]; + else + oldLength = normals.Length; + + if (oldLength < vertexCount) { + Array.Resize(ref this.normals, vertexCount); + var localNormals = this.normals; + for (int i = oldLength; i < vertexCount; i++) localNormals[i] = Vector3.back; + } + mesh.normals = this.normals; + } + + if (settings.tintBlack) { + if (uv2 != null) { + mesh.uv2 = this.uv2.Items; + mesh.uv3 = this.uv3.Items; + } + } + } + } + + public void FillLateVertexData (Mesh mesh) { + if (settings.calculateTangents) { + int vertexCount = this.vertexBuffer.Count; + var sbi = submeshes.Items; + int submeshCount = submeshes.Count; + var vbi = vertexBuffer.Items; + var ubi = uvBuffer.Items; + + MeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertexCount); + for (int i = 0; i < submeshCount; i++) { + var submesh = sbi[i].Items; + int triangleCount = sbi[i].Count; + MeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh, triangleCount, vbi, ubi, vertexCount); + } + MeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount); + mesh.tangents = this.tangents; + } + } + + public void FillTriangles (Mesh mesh) { + int submeshCount = submeshes.Count; + var submeshesItems = submeshes.Items; + mesh.subMeshCount = submeshCount; + + for (int i = 0; i < submeshCount; i++) + mesh.SetTriangles(submeshesItems[i].Items, i, false); + } + + public void FillTrianglesSingle (Mesh mesh) { + mesh.SetTriangles(submeshes.Items[0].Items, 0, false); + } + #endregion + + public void EnsureVertexCapacity (int minimumVertexCount, bool inlcudeTintBlack = false, bool includeTangents = false, bool includeNormals = false) { + if (minimumVertexCount > vertexBuffer.Items.Length) { + Array.Resize(ref vertexBuffer.Items, minimumVertexCount); + Array.Resize(ref uvBuffer.Items, minimumVertexCount); + Array.Resize(ref colorBuffer.Items, minimumVertexCount); + + if (inlcudeTintBlack) { + if (uv2 == null) { + uv2 = new ExposedList(minimumVertexCount); + uv3 = new ExposedList(minimumVertexCount); + } + uv2.Resize(minimumVertexCount); + uv3.Resize(minimumVertexCount); + } + + if (includeNormals) { + if (normals == null) + normals = new Vector3[minimumVertexCount]; + else + Array.Resize(ref normals, minimumVertexCount); + + } + + if (includeTangents) { + if (tangents == null) + tangents = new Vector4[minimumVertexCount]; + else + Array.Resize(ref tangents, minimumVertexCount); + } + } + //vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = minimumVertexCount; + } + + public void TrimExcess () { + vertexBuffer.TrimExcess(); + uvBuffer.TrimExcess(); + colorBuffer.TrimExcess(); + + if (uv2 != null) uv2.TrimExcess(); + if (uv3 != null) uv3.TrimExcess(); + + int count = vertexBuffer.Count; + if (normals != null) Array.Resize(ref normals, count); + if (tangents != null) Array.Resize(ref tangents, count); + } + + #region TangentSolver2D + // Thanks to contributions from forum user ToddRivers + + /// Step 1 of solving tangents. Ensure you have buffers of the correct size. + /// Eventual Vector4[] tangent buffer to assign to Mesh.tangents. + /// Temporary Vector2 buffer for calculating directions. + /// Number of vertices that require tangents (or the size of the vertex array) + internal static void SolveTangents2DEnsureSize (ref Vector4[] tangentBuffer, ref Vector2[] tempTanBuffer, int vertexCount) { + if (tangentBuffer == null || tangentBuffer.Length < vertexCount) + tangentBuffer = new Vector4[vertexCount]; + + if (tempTanBuffer == null || tempTanBuffer.Length < vertexCount * 2) + tempTanBuffer = new Vector2[vertexCount * 2]; // two arrays in one. + } + + /// Step 2 of solving tangents. Fills (part of) a temporary tangent-solution buffer based on the vertices and uvs defined by a submesh's triangle buffer. Only needs to be called once for single-submesh meshes. + /// A temporary Vector3[] for calculating tangents. + /// The mesh's current vertex position buffer. + /// The mesh's current triangles buffer. + /// The mesh's current uvs buffer. + /// Number of vertices that require tangents (or the size of the vertex array) + /// The number of triangle indexes in the triangle array to be used. + internal static void SolveTangents2DTriangles (Vector2[] tempTanBuffer, int[] triangles, int triangleCount, Vector3[] vertices, Vector2[] uvs, int vertexCount) { + Vector2 sdir; + Vector2 tdir; + for (int t = 0; t < triangleCount; t += 3) { + int i1 = triangles[t + 0]; + int i2 = triangles[t + 1]; + int i3 = triangles[t + 2]; + + Vector3 v1 = vertices[i1]; + Vector3 v2 = vertices[i2]; + Vector3 v3 = vertices[i3]; + + Vector2 w1 = uvs[i1]; + Vector2 w2 = uvs[i2]; + Vector2 w3 = uvs[i3]; + + float x1 = v2.x - v1.x; + float x2 = v3.x - v1.x; + float y1 = v2.y - v1.y; + float y2 = v3.y - v1.y; + + float s1 = w2.x - w1.x; + float s2 = w3.x - w1.x; + float t1 = w2.y - w1.y; + float t2 = w3.y - w1.y; + + float div = s1 * t2 - s2 * t1; + float r = (div == 0f) ? 0f : 1f / div; + + sdir.x = (t2 * x1 - t1 * x2) * r; + sdir.y = (t2 * y1 - t1 * y2) * r; + tempTanBuffer[i1] = tempTanBuffer[i2] = tempTanBuffer[i3] = sdir; + + tdir.x = (s1 * x2 - s2 * x1) * r; + tdir.y = (s1 * y2 - s2 * y1) * r; + tempTanBuffer[vertexCount + i1] = tempTanBuffer[vertexCount + i2] = tempTanBuffer[vertexCount + i3] = tdir; + } + } + + /// Step 3 of solving tangents. Fills a Vector4[] tangents array according to values calculated in step 2. + /// A Vector4[] that will eventually be used to set Mesh.tangents + /// A temporary Vector3[] for calculating tangents. + /// Number of vertices that require tangents (or the size of the vertex array) + internal static void SolveTangents2DBuffer (Vector4[] tangents, Vector2[] tempTanBuffer, int vertexCount) { + Vector4 tangent; + tangent.z = 0; + for (int i = 0; i < vertexCount; ++i) { + Vector2 t = tempTanBuffer[i]; + + // t.Normalize() (aggressively inlined). Even better if offloaded to GPU via vertex shader. + float magnitude = Mathf.Sqrt(t.x * t.x + t.y * t.y); + if (magnitude > 1E-05) { + float reciprocalMagnitude = 1f/magnitude; + t.x *= reciprocalMagnitude; + t.y *= reciprocalMagnitude; + } + + Vector2 t2 = tempTanBuffer[vertexCount + i]; + tangent.x = t.x; + tangent.y = t.y; + //tangent.z = 0; + tangent.w = (t.y * t2.x > t.x * t2.y) ? 1 : -1; // 2D direction calculation. Used for binormals. + tangents[i] = tangent; + } + } + #endregion + + #region AttachmentRendering + static List AttachmentVerts = new List(); + static List AttachmentUVs = new List(); + static List AttachmentColors32 = new List(); + static List AttachmentIndices = new List(); + + /// + /// Fills mesh vertex data to render a RegionAttachment. + public static void FillMeshLocal (Mesh mesh, RegionAttachment regionAttachment) { + if (mesh == null) return; + if (regionAttachment == null) return; + + AttachmentVerts.Clear(); + var offsets = regionAttachment.Offset; + AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BLX], offsets[RegionAttachment.BLY])); + AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.ULX], offsets[RegionAttachment.ULY])); + AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.URX], offsets[RegionAttachment.URY])); + AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BRX], offsets[RegionAttachment.BRY])); + + AttachmentUVs.Clear(); + var uvs = regionAttachment.UVs; + AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.ULX], uvs[RegionAttachment.ULY])); + AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.URX], uvs[RegionAttachment.URY])); + AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BRX], uvs[RegionAttachment.BRY])); + AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BLX], uvs[RegionAttachment.BLY])); + + AttachmentColors32.Clear(); + Color32 c = (Color32)(new Color(regionAttachment.r, regionAttachment.g, regionAttachment.b, regionAttachment.a)); + for (int i = 0; i < 4; i++) + AttachmentColors32.Add(c); + + AttachmentIndices.Clear(); + AttachmentIndices.AddRange(new[] { 0, 2, 1, 0, 3, 2 }); + + mesh.Clear(); + mesh.name = regionAttachment.Name; + mesh.SetVertices(AttachmentVerts); + mesh.SetUVs(0, AttachmentUVs); + mesh.SetColors(AttachmentColors32); + mesh.SetTriangles(AttachmentIndices, 0); + mesh.RecalculateBounds(); + + AttachmentVerts.Clear(); + AttachmentUVs.Clear(); + AttachmentColors32.Clear(); + AttachmentIndices.Clear(); + } + + public static void FillMeshLocal (Mesh mesh, MeshAttachment meshAttachment, SkeletonData skeletonData) { + if (mesh == null) return; + if (meshAttachment == null) return; + int vertexCount = meshAttachment.WorldVerticesLength / 2; + + AttachmentVerts.Clear(); + if (meshAttachment.IsWeighted()) { + int count = meshAttachment.WorldVerticesLength; + int[] meshAttachmentBones = meshAttachment.bones; + int v = 0; + + float[] vertices = meshAttachment.vertices; + for (int w = 0, b = 0; w < count; w += 2) { + float wx = 0, wy = 0; + int n = meshAttachmentBones[v++]; + n += v; + for (; v < n; v++, b += 3) { + BoneMatrix bm = BoneMatrix.CalculateSetupWorld(skeletonData.bones.Items[meshAttachmentBones[v]]); + float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + wx += (vx * bm.a + vy * bm.b + bm.x) * weight; + wy += (vx * bm.c + vy * bm.d + bm.y) * weight; + } + AttachmentVerts.Add(new Vector3(wx, wy)); + } + } else { + var localVerts = meshAttachment.Vertices; + Vector3 pos = default(Vector3); + for (int i = 0; i < vertexCount; i++) { + int ii = i * 2; + pos.x = localVerts[ii]; + pos.y = localVerts[ii + 1]; + AttachmentVerts.Add(pos); + } + } + + var uvs = meshAttachment.uvs; + Vector2 uv = default(Vector2); + Color32 c = (Color32)(new Color(meshAttachment.r, meshAttachment.g, meshAttachment.b, meshAttachment.a)); + AttachmentUVs.Clear(); + AttachmentColors32.Clear(); + for (int i = 0; i < vertexCount; i++) { + int ii = i * 2; + uv.x = uvs[ii]; + uv.y = uvs[ii + 1]; + AttachmentUVs.Add(uv); + + AttachmentColors32.Add(c); + } + + AttachmentIndices.Clear(); + AttachmentIndices.AddRange(meshAttachment.triangles); + + mesh.Clear(); + mesh.name = meshAttachment.Name; + mesh.SetVertices(AttachmentVerts); + mesh.SetUVs(0, AttachmentUVs); + mesh.SetColors(AttachmentColors32); + mesh.SetTriangles(AttachmentIndices, 0); + mesh.RecalculateBounds(); + + AttachmentVerts.Clear(); + AttachmentUVs.Clear(); + AttachmentColors32.Clear(); + AttachmentIndices.Clear(); + } + + + #endregion + } + + public class MeshRendererBuffers : IDisposable { + DoubleBuffered doubleBufferedMesh; + internal readonly ExposedList submeshMaterials = new ExposedList(); + internal Material[] sharedMaterials = new Material[0]; + + public void Initialize () { + if (doubleBufferedMesh != null) { + doubleBufferedMesh.GetNext().Clear(); + doubleBufferedMesh.GetNext().Clear(); + submeshMaterials.Clear(); + } else { + doubleBufferedMesh = new DoubleBuffered(); + } + } + + public Material[] GetUpdatedSharedMaterialsArray () { + if (submeshMaterials.Count == sharedMaterials.Length) + submeshMaterials.CopyTo(sharedMaterials); + else + sharedMaterials = submeshMaterials.ToArray(); + + return sharedMaterials; + } + + public bool MaterialsChangedInLastUpdate () { + int newSubmeshMaterials = submeshMaterials.Count; + var sharedMaterials = this.sharedMaterials; + if (newSubmeshMaterials != sharedMaterials.Length) return true; + + var submeshMaterialsItems = submeshMaterials.Items; + for (int i = 0; i < newSubmeshMaterials; i++) + if (!Material.ReferenceEquals(submeshMaterialsItems[i], sharedMaterials[i])) return true; //if (submeshMaterialsItems[i].GetInstanceID() != sharedMaterials[i].GetInstanceID()) return true; + + return false; + } + + public void UpdateSharedMaterials (ExposedList instructions) { + int newSize = instructions.Count; + { //submeshMaterials.Resize(instructions.Count); + if (newSize > submeshMaterials.Items.Length) + Array.Resize(ref submeshMaterials.Items, newSize); + submeshMaterials.Count = newSize; + } + + var submeshMaterialsItems = submeshMaterials.Items; + var instructionsItems = instructions.Items; + for (int i = 0; i < newSize; i++) + submeshMaterialsItems[i] = instructionsItems[i].material; + } + + public SmartMesh GetNextMesh () { + return doubleBufferedMesh.GetNext(); + } + + public void Clear () { + sharedMaterials = new Material[0]; + submeshMaterials.Clear(); + } + + public void Dispose () { + if (doubleBufferedMesh == null) return; + doubleBufferedMesh.GetNext().Dispose(); + doubleBufferedMesh.GetNext().Dispose(); + doubleBufferedMesh = null; + } + + ///This is a Mesh that also stores the instructions SkeletonRenderer generated for it. + public class SmartMesh : IDisposable { + public Mesh mesh = SpineMesh.NewSkeletonMesh(); + public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction(); + + public void Clear () { + mesh.Clear(); + instructionUsed.Clear(); + } + + public void Dispose () { + if (mesh != null) { + #if UNITY_EDITOR + if (Application.isEditor && !Application.isPlaying) + UnityEngine.Object.DestroyImmediate(mesh); + else + UnityEngine.Object.Destroy(mesh); + #else + UnityEngine.Object.Destroy(mesh); + #endif + } + mesh = null; + } + } + } + + public class SkeletonRendererInstruction { + public bool immutableTriangles; + public readonly ExposedList submeshInstructions = new ExposedList(); + + #if SPINE_TRIANGLECHECK + public bool hasActiveClipping; + public int rawVertexCount = -1; + public readonly ExposedList attachments = new ExposedList(); + #endif + + public void Clear () { + #if SPINE_TRIANGLECHECK + this.attachments.Clear(false); + rawVertexCount = -1; + hasActiveClipping = false; + #endif + this.submeshInstructions.Clear(false); + } + + public void Dispose () { + attachments.Clear(true); + } + + public void SetWithSubset (ExposedList instructions, int startSubmesh, int endSubmesh) { + #if SPINE_TRIANGLECHECK + int runningVertexCount = 0; + #endif + + var submeshes = this.submeshInstructions; + submeshes.Clear(false); + int submeshCount = endSubmesh - startSubmesh; + submeshes.Resize(submeshCount); + var submeshesItems = submeshes.Items; + var instructionsItems = instructions.Items; + for (int i = 0; i < submeshCount; i++) { + var instruction = instructionsItems[startSubmesh + i]; + submeshesItems[i] = instruction; + #if SPINE_TRIANGLECHECK + this.hasActiveClipping |= instruction.hasClipping; + submeshesItems[i].rawFirstVertexIndex = runningVertexCount; // Ensure current instructions have correct cached values. + runningVertexCount += instruction.rawVertexCount; // vertexCount will also be used for the rest of this method. + #endif + } + #if SPINE_TRIANGLECHECK + this.rawVertexCount = runningVertexCount; + + // assumption: instructions are contiguous. start and end are valid within instructions. + + int startSlot = instructionsItems[startSubmesh].startSlot; + int endSlot = instructionsItems[endSubmesh - 1].endSlot; + attachments.Clear(false); + int attachmentCount = endSlot - startSlot; + attachments.Resize(attachmentCount); + var attachmentsItems = attachments.Items; + + var drawOrder = instructionsItems[0].skeleton.drawOrder.Items; + for (int i = 0; i < attachmentCount; i++) + attachmentsItems[i] = drawOrder[startSlot + i].attachment; + #endif + } + + public void Set (SkeletonRendererInstruction other) { + this.immutableTriangles = other.immutableTriangles; + + #if SPINE_TRIANGLECHECK + this.hasActiveClipping = other.hasActiveClipping; + this.rawVertexCount = other.rawVertexCount; + this.attachments.Clear(false); + this.attachments.GrowIfNeeded(other.attachments.Capacity); + this.attachments.Count = other.attachments.Count; + other.attachments.CopyTo(this.attachments.Items); + #endif + + this.submeshInstructions.Clear(false); + this.submeshInstructions.GrowIfNeeded(other.submeshInstructions.Capacity); + this.submeshInstructions.Count = other.submeshInstructions.Count; + other.submeshInstructions.CopyTo(this.submeshInstructions.Items); + } + + public static bool GeometryNotEqual (SkeletonRendererInstruction a, SkeletonRendererInstruction b) { + #if SPINE_TRIANGLECHECK + #if UNITY_EDITOR + if (!Application.isPlaying) + return true; + #endif + + if (a.hasActiveClipping || b.hasActiveClipping) return true; // Triangles are unpredictable when clipping is active. + + // Everything below assumes the raw vertex and triangle counts were used. (ie, no clipping was done) + if (a.rawVertexCount != b.rawVertexCount) return true; + + if (a.immutableTriangles != b.immutableTriangles) return true; + + int attachmentCountB = b.attachments.Count; + if (a.attachments.Count != attachmentCountB) return true; // Bounds check for the looped storedAttachments count below. + + // Submesh count changed + int submeshCountA = a.submeshInstructions.Count; + int submeshCountB = b.submeshInstructions.Count; + if (submeshCountA != submeshCountB) return true; + + // Submesh Instruction mismatch + var submeshInstructionsItemsA = a.submeshInstructions.Items; + var submeshInstructionsItemsB = b.submeshInstructions.Items; + + var attachmentsA = a.attachments.Items; + var attachmentsB = b.attachments.Items; + for (int i = 0; i < attachmentCountB; i++) + if (!System.Object.ReferenceEquals(attachmentsA[i], attachmentsB[i])) return true; + + for (int i = 0; i < submeshCountB; i++) { + var submeshA = submeshInstructionsItemsA[i]; + var submeshB = submeshInstructionsItemsB[i]; + + if (!( + submeshA.rawVertexCount == submeshB.rawVertexCount && + submeshA.startSlot == submeshB.startSlot && + submeshA.endSlot == submeshB.endSlot + && submeshA.rawTriangleCount == submeshB.rawTriangleCount && + submeshA.rawFirstVertexIndex == submeshB.rawFirstVertexIndex + )) + return true; + } + + return false; + #else + // In normal immutable triangle use, immutableTriangles will be initially false, forcing the smartmesh to update the first time but never again after that, unless there was an immutableTriangles flag mismatch.. + if (a.immutableTriangles || b.immutableTriangles) + return (a.immutableTriangles != b.immutableTriangles); + + return true; + #endif + } + } + +} diff --git a/Assets/Spine/spine-unity/Mesh Generation/SpineMesh.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/SpineMesh.cs.meta new file mode 100644 index 0000000..b30649a --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/SpineMesh.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f834c8746034db645a52a9506ff1de89 +timeCreated: 1455416715 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused.meta new file mode 100644 index 0000000..ebee0d3 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 0cb4e560224445d44affd8e57c1ae8b1 +folderAsset: yes +timeCreated: 1495071888 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysMeshGenerator.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysMeshGenerator.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysMeshGenerator.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysMeshGenerator.cs.meta new file mode 100644 index 0000000..e2048bb --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ed047fa8737dedb4081579983c193db5 +timeCreated: 1458124926 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSimpleMeshGenerator.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSimpleMeshGenerator.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSimpleMeshGenerator.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSimpleMeshGenerator.cs.meta new file mode 100644 index 0000000..72f63e4 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSimpleMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 296d4c2077fb3bc4fa9a5211c0f95011 +timeCreated: 1455406990 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshSetMeshGenerator.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshSetMeshGenerator.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshSetMeshGenerator.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshSetMeshGenerator.cs.meta new file mode 100644 index 0000000..88a0168 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshSetMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 11ba077d7d984814db31d054192be532 +timeCreated: 1458047178 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshedMeshGenerator.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshedMeshGenerator.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshedMeshGenerator.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshedMeshGenerator.cs.meta new file mode 100644 index 0000000..81ce284 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/ArraysSubmeshedMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 412898b02d5caaf489b3ffb29e4ae1c0 +timeCreated: 1455407003 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/DoubleBufferedMesh.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/DoubleBufferedMesh.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/DoubleBufferedMesh.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/DoubleBufferedMesh.cs.meta new file mode 100644 index 0000000..8114723 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/DoubleBufferedMesh.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7191a70dcf4959f43961fa78e05484a0 +timeCreated: 1455140057 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ISimpleMeshGenerator.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/ISimpleMeshGenerator.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ISimpleMeshGenerator.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/ISimpleMeshGenerator.cs.meta new file mode 100644 index 0000000..41c9e5a --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/ISimpleMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0af0615ce8e0e054ea224e3e03ab31aa +timeCreated: 1455406790 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ISubmeshedMeshGenerator.cs b/Assets/Spine/spine-unity/Mesh Generation/Unused/ISubmeshedMeshGenerator.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Spine/spine-unity/Mesh Generation/Unused/ISubmeshedMeshGenerator.cs.meta b/Assets/Spine/spine-unity/Mesh Generation/Unused/ISubmeshedMeshGenerator.cs.meta new file mode 100644 index 0000000..85782f9 --- /dev/null +++ b/Assets/Spine/spine-unity/Mesh Generation/Unused/ISubmeshedMeshGenerator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7492a8ff24ea1884f92307ab1121e451 +timeCreated: 1458046464 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules.meta b/Assets/Spine/spine-unity/Modules.meta new file mode 100644 index 0000000..2dc106c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a7935399edad9a14bb5708cf59c94b67 +folderAsset: yes +timeCreated: 1455489260 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/AttachmentTools.meta b/Assets/Spine/spine-unity/Modules/AttachmentTools.meta new file mode 100644 index 0000000..fee2e8c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/AttachmentTools.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f601b524e4da4704a9e0f6b7e05d6ca6 +folderAsset: yes +timeCreated: 1478437840 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/Assets/Spine/spine-unity/Modules/AttachmentTools/AttachmentTools.cs new file mode 100644 index 0000000..cc2f14e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/AttachmentTools/AttachmentTools.cs @@ -0,0 +1,1135 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections.Generic; + +namespace Spine.Unity.Modules.AttachmentTools { + public static class AttachmentRegionExtensions { + #region GetRegion + /// + /// Tries to get the region (image) of a renderable attachment. If the attachment is not renderable, it returns null. + public static AtlasRegion GetRegion (this Attachment attachment) { + var renderableAttachment = attachment as IHasRendererObject; + if (renderableAttachment != null) + return renderableAttachment.RendererObject as AtlasRegion; + + return null; + } + + /// Gets the region (image) of a RegionAttachment + public static AtlasRegion GetRegion (this RegionAttachment regionAttachment) { + return regionAttachment.RendererObject as AtlasRegion; + } + + /// Gets the region (image) of a MeshAttachment + public static AtlasRegion GetRegion (this MeshAttachment meshAttachment) { + return meshAttachment.RendererObject as AtlasRegion; + } + #endregion + #region SetRegion + /// + /// Tries to set the region (image) of a renderable attachment. If the attachment is not renderable, nothing is applied. + public static void SetRegion (this Attachment attachment, AtlasRegion region, bool updateOffset = true) { + var regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) + regionAttachment.SetRegion(region, updateOffset); + + var meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) + meshAttachment.SetRegion(region, updateOffset); + } + + /// Sets the region (image) of a RegionAttachment + public static void SetRegion (this RegionAttachment attachment, AtlasRegion region, bool updateOffset = true) { + if (region == null) throw new System.ArgumentNullException("region"); + + // (AtlasAttachmentLoader.cs) + attachment.RendererObject = region; + attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + + if (updateOffset) attachment.UpdateOffset(); + } + + /// Sets the region (image) of a MeshAttachment + public static void SetRegion (this MeshAttachment attachment, AtlasRegion region, bool updateUVs = true) { + if (region == null) throw new System.ArgumentNullException("region"); + + // (AtlasAttachmentLoader.cs) + attachment.RendererObject = region; + attachment.RegionU = region.u; + attachment.RegionV = region.v; + attachment.RegionU2 = region.u2; + attachment.RegionV2 = region.v2; + attachment.RegionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + + if (updateUVs) attachment.UpdateUVs(); + } + #endregion + + #region Runtime RegionAttachments + /// + /// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses a new AtlasPage with the Material provided./// + public static RegionAttachment ToRegionAttachment (this Sprite sprite, Material material, float rotation = 0f) { + return sprite.ToRegionAttachment(material.ToSpineAtlasPage(), rotation); + } + + /// + /// Creates a RegionAttachment based on a sprite. This method creates a real, usable AtlasRegion. That AtlasRegion uses the AtlasPage provided./// + public static RegionAttachment ToRegionAttachment (this Sprite sprite, AtlasPage page, float rotation = 0f) { + if (sprite == null) throw new System.ArgumentNullException("sprite"); + if (page == null) throw new System.ArgumentNullException("page"); + var region = sprite.ToAtlasRegion(page); + var unitsPerPixel = 1f / sprite.pixelsPerUnit; + return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation); + } + + /// + /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate texture of the Sprite's texture data. Returns a RegionAttachment that uses it. Use this if you plan to use a premultiply alpha shader such as "Spine/Skeleton" + public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Shader shader, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, Material materialPropertySource = null, float rotation = 0f) { + if (sprite == null) throw new System.ArgumentNullException("sprite"); + if (shader == null) throw new System.ArgumentNullException("shader"); + var region = sprite.ToAtlasRegionPMAClone(shader, textureFormat, mipmaps, materialPropertySource); + var unitsPerPixel = 1f / sprite.pixelsPerUnit; + return region.ToRegionAttachment(sprite.name, unitsPerPixel, rotation); + } + + public static RegionAttachment ToRegionAttachmentPMAClone (this Sprite sprite, Material materialPropertySource, TextureFormat textureFormat = AtlasUtilities.SpineTextureFormat, bool mipmaps = AtlasUtilities.UseMipMaps, float rotation = 0f) { + return sprite.ToRegionAttachmentPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource, rotation); + } + + /// + /// Creates a new RegionAttachment from a given AtlasRegion. + public static RegionAttachment ToRegionAttachment (this AtlasRegion region, string attachmentName, float scale = 0.01f, float rotation = 0f) { + if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName can't be null or empty.", "attachmentName"); + if (region == null) throw new System.ArgumentNullException("region"); + + // (AtlasAttachmentLoader.cs) + var attachment = new RegionAttachment(attachmentName); + + attachment.RendererObject = region; + attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + + attachment.Path = region.name; + attachment.scaleX = 1; + attachment.scaleY = 1; + attachment.rotation = rotation; + + attachment.r = 1; + attachment.g = 1; + attachment.b = 1; + attachment.a = 1; + + // pass OriginalWidth and OriginalHeight because UpdateOffset uses it in its calculation. + attachment.width = attachment.regionOriginalWidth * scale; + attachment.height = attachment.regionOriginalHeight * scale; + + attachment.SetColor(Color.white); + attachment.UpdateOffset(); + return attachment; + } + + /// Sets the scale. Call regionAttachment.UpdateOffset to apply the change. + public static void SetScale (this RegionAttachment regionAttachment, Vector2 scale) { + regionAttachment.scaleX = scale.x; + regionAttachment.scaleY = scale.y; + } + + /// Sets the scale. Call regionAttachment.UpdateOffset to apply the change. + public static void SetScale (this RegionAttachment regionAttachment, float x, float y) { + regionAttachment.scaleX = x; + regionAttachment.scaleY = y; + } + + /// Sets the position offset. Call regionAttachment.UpdateOffset to apply the change. + public static void SetPositionOffset (this RegionAttachment regionAttachment, Vector2 offset) { + regionAttachment.x = offset.x; + regionAttachment.y = offset.y; + } + + /// Sets the position offset. Call regionAttachment.UpdateOffset to apply the change. + public static void SetPositionOffset (this RegionAttachment regionAttachment, float x, float y) { + regionAttachment.x = x; + regionAttachment.y = y; + } + + /// Sets the rotation. Call regionAttachment.UpdateOffset to apply the change. + public static void SetRotation (this RegionAttachment regionAttachment, float rotation) { + regionAttachment.rotation = rotation; + } + #endregion + } + + public static class AtlasUtilities { + internal const TextureFormat SpineTextureFormat = TextureFormat.RGBA32; + internal const float DefaultMipmapBias = -0.5f; + internal const bool UseMipMaps = false; + internal const float DefaultScale = 0.01f; + + const int NonrenderingRegion = -1; + + public static AtlasRegion ToAtlasRegion (this Texture2D t, Material materialPropertySource, float scale = DefaultScale) { + return t.ToAtlasRegion(materialPropertySource.shader, scale, materialPropertySource); + } + + public static AtlasRegion ToAtlasRegion (this Texture2D t, Shader shader, float scale = DefaultScale, Material materialPropertySource = null) { + var material = new Material(shader); + if (materialPropertySource != null) { + material.CopyPropertiesFromMaterial(materialPropertySource); + material.shaderKeywords = materialPropertySource.shaderKeywords; + } + + material.mainTexture = t; + var page = material.ToSpineAtlasPage(); + + float width = t.width; + float height = t.height; + + var region = new AtlasRegion(); + region.name = t.name; + region.index = -1; + region.rotate = false; + + // World space units + Vector2 boundsMin = Vector2.zero, boundsMax = new Vector2(width, height) * scale; + + // Texture space/pixel units + region.width = (int)width; + region.originalWidth = (int)width; + region.height = (int)height; + region.originalHeight = (int)height; + region.offsetX = width * (0.5f - InverseLerp(boundsMin.x, boundsMax.x, 0)); + region.offsetY = height * (0.5f - InverseLerp(boundsMin.y, boundsMax.y, 0)); + + // Use the full area of the texture. + region.u = 0; + region.v = 1; + region.u2 = 1; + region.v2 = 0; + region.x = 0; + region.y = 0; + + region.page = page; + + return region; + } + + /// + /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate of the Sprite's texture data. + public static AtlasRegion ToAtlasRegionPMAClone (this Texture2D t, Material materialPropertySource, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { + return t.ToAtlasRegionPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource); + } + + /// + /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate of the Sprite's texture data. + public static AtlasRegion ToAtlasRegionPMAClone (this Texture2D t, Shader shader, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null) { + var material = new Material(shader); + if (materialPropertySource != null) { + material.CopyPropertiesFromMaterial(materialPropertySource); + material.shaderKeywords = materialPropertySource.shaderKeywords; + } + var newTexture = t.GetClone(false, textureFormat, mipmaps); + newTexture.ApplyPMA(true); + + newTexture.name = t.name + "-pma-"; + material.name = t.name + shader.name; + + material.mainTexture = newTexture; + var page = material.ToSpineAtlasPage(); + + var region = newTexture.ToAtlasRegion(shader); + region.page = page; + + return region; + } + + /// + /// Creates a new Spine.AtlasPage from a UnityEngine.Material. If the material has a preassigned texture, the page width and height will be set. + public static AtlasPage ToSpineAtlasPage (this Material m) { + var newPage = new AtlasPage { + rendererObject = m, + name = m.name + }; + + var t = m.mainTexture; + if (t != null) { + newPage.width = t.width; + newPage.height = t.height; + } + + return newPage; + } + + /// + /// Creates a Spine.AtlasRegion from a UnityEngine.Sprite. + public static AtlasRegion ToAtlasRegion (this Sprite s, AtlasPage page) { + if (page == null) throw new System.ArgumentNullException("page", "page cannot be null. AtlasPage determines which texture region belongs and how it should be rendered. You can use material.ToSpineAtlasPage() to get a shareable AtlasPage from a Material, or use the sprite.ToAtlasRegion(material) overload."); + var region = s.ToAtlasRegion(); + region.page = page; + return region; + } + + /// + /// Creates a Spine.AtlasRegion from a UnityEngine.Sprite. This creates a new AtlasPage object for every AtlasRegion you create. You can centralize Material control by creating a shared atlas page using Material.ToSpineAtlasPage and using the sprite.ToAtlasRegion(AtlasPage) overload. + public static AtlasRegion ToAtlasRegion (this Sprite s, Material material) { + var region = s.ToAtlasRegion(); + region.page = material.ToSpineAtlasPage(); + return region; + } + + /// + /// Creates a Spine.AtlasRegion that uses a premultiplied alpha duplicate of the Sprite's texture data. + public static AtlasRegion ToAtlasRegionPMAClone (this Sprite s, Shader shader, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null) { + var material = new Material(shader); + if (materialPropertySource != null) { + material.CopyPropertiesFromMaterial(materialPropertySource); + material.shaderKeywords = materialPropertySource.shaderKeywords; + } + + var tex = s.ToTexture(false, textureFormat, mipmaps); + tex.ApplyPMA(true); + + tex.name = s.name + "-pma-"; + material.name = tex.name + shader.name; + + material.mainTexture = tex; + var page = material.ToSpineAtlasPage(); + + var region = s.ToAtlasRegion(true); + region.page = page; + + return region; + } + + public static AtlasRegion ToAtlasRegionPMAClone (this Sprite s, Material materialPropertySource, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { + return s.ToAtlasRegionPMAClone(materialPropertySource.shader, textureFormat, mipmaps, materialPropertySource); + } + + internal static AtlasRegion ToAtlasRegion (this Sprite s, bool isolatedTexture = false) { + var region = new AtlasRegion(); + region.name = s.name; + region.index = -1; + region.rotate = s.packed && s.packingRotation != SpritePackingRotation.None; + + // World space units + Bounds bounds = s.bounds; + Vector2 boundsMin = bounds.min, boundsMax = bounds.max; + + // Texture space/pixel units + Rect spineRect = s.rect.SpineUnityFlipRect(s.texture.height); + region.width = (int)spineRect.width; + region.originalWidth = (int)spineRect.width; + region.height = (int)spineRect.height; + region.originalHeight = (int)spineRect.height; + region.offsetX = spineRect.width * (0.5f - InverseLerp(boundsMin.x, boundsMax.x, 0)); + region.offsetY = spineRect.height * (0.5f - InverseLerp(boundsMin.y, boundsMax.y, 0)); + + if (isolatedTexture) { + region.u = 0; + region.v = 1; + region.u2 = 1; + region.v2 = 0; + region.x = 0; + region.y = 0; + } else { + Texture2D tex = s.texture; + Rect uvRect = TextureRectToUVRect(s.textureRect, tex.width, tex.height); + region.u = uvRect.xMin; + region.v = uvRect.yMax; + region.u2 = uvRect.xMax; + region.v2 = uvRect.yMin; + region.x = (int)spineRect.x; + region.y = (int)spineRect.y; + } + + return region; + } + + #region Runtime Repacking + /// + /// Fills the outputAttachments list with new attachment objects based on the attachments in sourceAttachments, but mapped to a new single texture using the same material. + /// The list of attachments to be repacked. + /// The List(Attachment) to populate with the newly created Attachment objects. + /// + /// May be null. If no Material property source is provided, no special + public static void GetRepackedAttachments (List sourceAttachments, List outputAttachments, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, string newAssetName = "Repacked Attachments", bool clearCache = false, bool useOriginalNonrenderables = true) { + if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments"); + if (outputAttachments == null) throw new System.ArgumentNullException("outputAttachments"); + + // Use these to detect and use shared regions. + var existingRegions = new Dictionary(); + var regionIndexes = new List(); + var texturesToPack = new List(); + var originalRegions = new List(); + + outputAttachments.Clear(); + outputAttachments.AddRange(sourceAttachments); + + int newRegionIndex = 0; + for (int i = 0, n = sourceAttachments.Count; i < n; i++) { + var originalAttachment = sourceAttachments[i]; + + if (IsRenderable(originalAttachment)) { + var newAttachment = originalAttachment.GetClone(true); + var region = newAttachment.GetRegion(); + int existingIndex; + if (existingRegions.TryGetValue(region, out existingIndex)) { + regionIndexes.Add(existingIndex); // Store the region index for the eventual new attachment. + } else { + originalRegions.Add(region); + texturesToPack.Add(region.ToTexture()); // Add the texture to the PackTextures argument + existingRegions.Add(region, newRegionIndex); // Add the region to the dictionary of known regions + regionIndexes.Add(newRegionIndex); // Store the region index for the eventual new attachment. + newRegionIndex++; + } + + outputAttachments[i] = newAttachment; + } else { + outputAttachments[i] = useOriginalNonrenderables ? originalAttachment : originalAttachment.GetClone(true); + regionIndexes.Add(NonrenderingRegion); // Output attachments pairs with regionIndexes list 1:1. Pad with a sentinel if the attachment doesn't have a region. + } + } + + // Fill a new texture with the collected attachment textures. + var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); + newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias; + newTexture.anisoLevel = texturesToPack[0].anisoLevel; + newTexture.name = newAssetName; + var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); + + // Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments + Shader shader = materialPropertySource == null ? Shader.Find("Spine/Skeleton") : materialPropertySource.shader; + var newMaterial = new Material(shader); + if (materialPropertySource != null) { + newMaterial.CopyPropertiesFromMaterial(materialPropertySource); + newMaterial.shaderKeywords = materialPropertySource.shaderKeywords; + } + + newMaterial.name = newAssetName; + newMaterial.mainTexture = newTexture; + var page = newMaterial.ToSpineAtlasPage(); + page.name = newAssetName; + + var repackedRegions = new List(); + for (int i = 0, n = originalRegions.Count; i < n; i++) { + var oldRegion = originalRegions[i]; + var newRegion = UVRectToAtlasRegion(rects[i], oldRegion.name, page, oldRegion.offsetX, oldRegion.offsetY, oldRegion.rotate); + repackedRegions.Add(newRegion); + } + + // Map the cloned attachments to the repacked atlas. + for (int i = 0, n = outputAttachments.Count; i < n; i++) { + var a = outputAttachments[i]; + if (IsRenderable(a)) + a.SetRegion(repackedRegions[regionIndexes[i]]); + } + + // Clean up. + if (clearCache) + AtlasUtilities.ClearCache(); + + outputTexture = newTexture; + outputMaterial = newMaterial; + } + + /// + /// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas comprised of all the regions from the original skin. + /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. + public static Skin GetRepackedSkin (this Skin o, string newName, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, bool useOriginalNonrenderables = true) { + return GetRepackedSkin(o, newName, materialPropertySource.shader, out outputMaterial, out outputTexture, maxAtlasSize, padding, textureFormat, mipmaps, materialPropertySource, useOriginalNonrenderables); + } + + /// + /// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas comprised of all the regions from the original skin. + /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. + public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null, bool clearCache = false, bool useOriginalNonrenderables = true) { + if (o == null) throw new System.NullReferenceException("Skin was null"); + var skinAttachments = o.Attachments; + var newSkin = new Skin(newName); + + // Use these to detect and use shared regions. + var existingRegions = new Dictionary(); + var regionIndexes = new List(); + + // Collect all textures from the attachments of the original skin. + var repackedAttachments = new List(); + var texturesToPack = new List(); + var originalRegions = new List(); + int newRegionIndex = 0; + foreach (var skinEntry in skinAttachments) { + var originalKey = skinEntry.Key; + var originalAttachment = skinEntry.Value; + + Attachment newAttachment; + if (IsRenderable(originalAttachment)) { + newAttachment = originalAttachment.GetClone(true); + var region = newAttachment.GetRegion(); + int existingIndex; + if (existingRegions.TryGetValue(region, out existingIndex)) { + regionIndexes.Add(existingIndex); // Store the region index for the eventual new attachment. + } else { + originalRegions.Add(region); + texturesToPack.Add(region.ToTexture()); // Add the texture to the PackTextures argument + existingRegions.Add(region, newRegionIndex); // Add the region to the dictionary of known regions + regionIndexes.Add(newRegionIndex); // Store the region index for the eventual new attachment. + newRegionIndex++; + } + + repackedAttachments.Add(newAttachment); + newSkin.AddAttachment(originalKey.slotIndex, originalKey.name, newAttachment); + } else { + newSkin.AddAttachment(originalKey.slotIndex, originalKey.name, useOriginalNonrenderables ? originalAttachment : originalAttachment.GetClone(true)); + } + } + + // Fill a new texture with the collected attachment textures. + var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); + newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias; + newTexture.anisoLevel = texturesToPack[0].anisoLevel; + newTexture.name = newName; + var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); + + // Rehydrate the repacked textures as a Material, Spine atlas and Spine.AtlasAttachments + var newMaterial = new Material(shader); + if (materialPropertySource != null) { + newMaterial.CopyPropertiesFromMaterial(materialPropertySource); + newMaterial.shaderKeywords = materialPropertySource.shaderKeywords; + } + + newMaterial.name = newName; + newMaterial.mainTexture = newTexture; + var page = newMaterial.ToSpineAtlasPage(); + page.name = newName; + + var repackedRegions = new List(); + for (int i = 0, n = originalRegions.Count; i < n; i++) { + var oldRegion = originalRegions[i]; + var newRegion = UVRectToAtlasRegion(rects[i], oldRegion.name, page, oldRegion.offsetX, oldRegion.offsetY, oldRegion.rotate); + repackedRegions.Add(newRegion); + } + + // Map the cloned attachments to the repacked atlas. + for (int i = 0, n = repackedAttachments.Count; i < n; i++) { + var a = repackedAttachments[i]; + if (IsRenderable(a)) + a.SetRegion(repackedRegions[regionIndexes[i]]); + } + + // Clean up. + if (clearCache) + AtlasUtilities.ClearCache(); + + outputTexture = newTexture; + outputMaterial = newMaterial; + return newSkin; + } + + public static Sprite ToSprite (this AtlasRegion ar, float pixelsPerUnit = 100) { + return Sprite.Create(ar.GetMainTexture(), ar.GetUnityRect(), new Vector2(0.5f, 0.5f), pixelsPerUnit); + } + + static Dictionary CachedRegionTextures = new Dictionary(); + static List CachedRegionTexturesList = new List(); + + public static void ClearCache () { + foreach (var t in CachedRegionTexturesList) { + UnityEngine.Object.Destroy(t); + } + CachedRegionTextures.Clear(); + CachedRegionTexturesList.Clear(); + } + + /// Creates a new Texture2D object based on an AtlasRegion. + /// If applyImmediately is true, Texture2D.Apply is called immediately after the Texture2D is filled with data. + public static Texture2D ToTexture (this AtlasRegion ar, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { + Texture2D output; + + CachedRegionTextures.TryGetValue(ar, out output); + if (output == null) { + Texture2D sourceTexture = ar.GetMainTexture(); + Rect r = ar.GetUnityRect(sourceTexture.height); + int width = (int)r.width; + int height = (int)r.height; + output = new Texture2D(width, height, textureFormat, mipmaps); + output.name = ar.name; + Color[] pixelBuffer = sourceTexture.GetPixels((int)r.x, (int)r.y, width, height); + output.SetPixels(pixelBuffer); + CachedRegionTextures.Add(ar, output); + CachedRegionTexturesList.Add(output); + + if (applyImmediately) + output.Apply(); + } + + return output; + } + + static Texture2D ToTexture (this Sprite s, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { + var spriteTexture = s.texture; + var r = s.textureRect; + var spritePixels = spriteTexture.GetPixels((int)r.x, (int)r.y, (int)r.width, (int)r.height); + var newTexture = new Texture2D((int)r.width, (int)r.height, textureFormat, mipmaps); + newTexture.SetPixels(spritePixels); + + if (applyImmediately) + newTexture.Apply(); + + return newTexture; + } + + static Texture2D GetClone (this Texture2D t, bool applyImmediately = true, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { + var spritePixels = t.GetPixels(0, 0, (int)t.width, (int)t.height); + var newTexture = new Texture2D((int)t.width, (int)t.height, textureFormat, mipmaps); + newTexture.SetPixels(spritePixels); + + if (applyImmediately) + newTexture.Apply(); + + return newTexture; + } + + static bool IsRenderable (Attachment a) { + return a is IHasRendererObject; + } + + /// + /// Get a rect with flipped Y so that a Spine atlas rect gets converted to a Unity Sprite rect and vice versa. + static Rect SpineUnityFlipRect (this Rect rect, int textureHeight) { + rect.y = textureHeight - rect.y - rect.height; + return rect; + } + + /// + /// Gets the Rect of an AtlasRegion according to Unity texture coordinates (x-right, y-up). + /// This overload relies on region.page.height being correctly set. + static Rect GetUnityRect (this AtlasRegion region) { + return region.GetSpineAtlasRect().SpineUnityFlipRect(region.page.height); + } + + /// + /// Gets the Rect of an AtlasRegion according to Unity texture coordinates (x-right, y-up). + static Rect GetUnityRect (this AtlasRegion region, int textureHeight) { + return region.GetSpineAtlasRect().SpineUnityFlipRect(textureHeight); + } + + /// + /// Returns a Rect of the AtlasRegion according to Spine texture coordinates. (x-right, y-down) + static Rect GetSpineAtlasRect (this AtlasRegion region, bool includeRotate = true) { + if (includeRotate && region.rotate) + return new Rect(region.x, region.y, region.height, region.width); + else + return new Rect(region.x, region.y, region.width, region.height); + } + + /// + /// Denormalize a uvRect into a texture-space Rect. + static Rect UVRectToTextureRect (Rect uvRect, int texWidth, int texHeight) { + uvRect.x *= texWidth; + uvRect.width *= texWidth; + uvRect.y *= texHeight; + uvRect.height *= texHeight; + return uvRect; + } + + /// + /// Normalize a texture Rect into UV coordinates. + static Rect TextureRectToUVRect (Rect textureRect, int texWidth, int texHeight) { + textureRect.x = Mathf.InverseLerp(0, texWidth, textureRect.x); + textureRect.y = Mathf.InverseLerp(0, texHeight, textureRect.y); + textureRect.width = Mathf.InverseLerp(0, texWidth, textureRect.width); + textureRect.height = Mathf.InverseLerp(0, texHeight, textureRect.height); + return textureRect; + } + + /// + /// Creates a new Spine AtlasRegion according to a Unity UV Rect (x-right, y-up, uv-normalized). + static AtlasRegion UVRectToAtlasRegion (Rect uvRect, string name, AtlasPage page, float offsetX, float offsetY, bool rotate) { + var tr = UVRectToTextureRect(uvRect, page.width, page.height); + var rr = tr.SpineUnityFlipRect(page.height); + + int x = (int)rr.x, y = (int)rr.y; + int w, h; + if (rotate) { + w = (int)rr.height; + h = (int)rr.width; + } else { + w = (int)rr.width; + h = (int)rr.height; + } + + return new AtlasRegion { + page = page, + name = name, + + u = uvRect.xMin, + u2 = uvRect.xMax, + v = uvRect.yMax, + v2 = uvRect.yMin, + + index = -1, + + width = w, + originalWidth = w, + height = h, + originalHeight = h, + offsetX = offsetX, + offsetY = offsetY, + x = x, + y = y, + + rotate = rotate + }; + } + + /// + /// Convenience method for getting the main texture of the material of the page of the region. + static Texture2D GetMainTexture (this AtlasRegion region) { + var material = (region.page.rendererObject as Material); + return material.mainTexture as Texture2D; + } + + static void ApplyPMA (this Texture2D texture, bool applyImmediately = true) { + var pixels = texture.GetPixels(); + for (int i = 0, n = pixels.Length; i < n; i++) { + Color p = pixels[i]; + float a = p.a; + p.r = p.r * a; + p.g = p.g * a; + p.b = p.b * a; + pixels[i] = p; + } + texture.SetPixels(pixels); + if (applyImmediately) + texture.Apply(); + } + #endregion + + static float InverseLerp (float a, float b, float value) { + return (value - a) / (b - a); + } + } + + public static class SkinUtilities { + + #region Skeleton Skin Extensions + /// + /// Convenience method for duplicating a skeleton's current active skin so changes to it will not affect other skeleton instances. . + public static Skin UnshareSkin (this Skeleton skeleton, bool includeDefaultSkin, bool unshareAttachments, AnimationState state = null) { + // 1. Copy the current skin and set the skeleton's skin to the new one. + var newSkin = skeleton.GetClonedSkin("cloned skin", includeDefaultSkin, unshareAttachments, true); + skeleton.SetSkin(newSkin); + + // 2. Apply correct attachments: skeleton.SetToSetupPose + animationState.Apply + if (state != null) { + skeleton.SetToSetupPose(); + state.Apply(skeleton); + } + + // 3. Return unshared skin. + return newSkin; + } + + public static Skin GetClonedSkin (this Skeleton skeleton, string newSkinName, bool includeDefaultSkin = false, bool cloneAttachments = false, bool cloneMeshesAsLinked = true) { + var newSkin = new Skin(newSkinName); // may have null name. Harmless. + var defaultSkin = skeleton.data.DefaultSkin; + var activeSkin = skeleton.skin; + + if (includeDefaultSkin) + defaultSkin.CopyTo(newSkin, true, cloneAttachments, cloneMeshesAsLinked); + + if (activeSkin != null) + activeSkin.CopyTo(newSkin, true, cloneAttachments, cloneMeshesAsLinked); + + return newSkin; + } + #endregion + + /// + /// Gets a shallow copy of the skin. The cloned skin's attachments are shared with the original skin. + public static Skin GetClone (this Skin original) { + var newSkin = new Skin(original.name + " clone"); + var newSkinAttachments = newSkin.Attachments; + + foreach (var a in original.Attachments) + newSkinAttachments[a.Key] = a.Value; + + return newSkin; + } + + /// Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced. + public static void SetAttachment (this Skin skin, string slotName, string keyName, Attachment attachment, Skeleton skeleton) { + int slotIndex = skeleton.FindSlotIndex(slotName); + if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null."); + if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName"); + skin.AddAttachment(slotIndex, keyName, attachment); + } + + /// Gets an attachment from the skin for the specified slot index and name. + public static Attachment GetAttachment (this Skin skin, string slotName, string keyName, Skeleton skeleton) { + int slotIndex = skeleton.FindSlotIndex(slotName); + if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null."); + if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName"); + return skin.GetAttachment(slotIndex, keyName); + } + + /// Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced. + public static void SetAttachment (this Skin skin, int slotIndex, string keyName, Attachment attachment) { + skin.AddAttachment(slotIndex, keyName, attachment); + } + + /// Removes the attachment. Returns true if the element is successfully found and removed; otherwise, false. + public static bool RemoveAttachment (this Skin skin, string slotName, string keyName, Skeleton skeleton) { + int slotIndex = skeleton.FindSlotIndex(slotName); + if (skeleton == null) throw new System.ArgumentNullException("skeleton", "skeleton cannot be null."); + if (slotIndex == -1) throw new System.ArgumentException(string.Format("Slot '{0}' does not exist in skeleton.", slotName), "slotName"); + return skin.RemoveAttachment(slotIndex, keyName); + } + + /// Removes the attachment. Returns true if the element is successfully found and removed; otherwise, false. + public static bool RemoveAttachment (this Skin skin, int slotIndex, string keyName) { + return skin.Attachments.Remove(new Skin.AttachmentKeyTuple(slotIndex, keyName)); + } + + public static void Clear (this Skin skin) { + skin.Attachments.Clear(); + } + + public static void Append (this Skin destination, Skin source) { + source.CopyTo(destination, true, false); + } + + public static void CopyTo (this Skin source, Skin destination, bool overwrite, bool cloneAttachments, bool cloneMeshesAsLinked = true) { + var sourceAttachments = source.Attachments; + var destinationAttachments = destination.Attachments; + + if (cloneAttachments) { + if (overwrite) { + foreach (var e in sourceAttachments) + destinationAttachments[e.Key] = e.Value.GetClone(cloneMeshesAsLinked); + } else { + foreach (var e in sourceAttachments) { + if (destinationAttachments.ContainsKey(e.Key)) continue; + destinationAttachments.Add(e.Key, e.Value.GetClone(cloneMeshesAsLinked)); + } + } + } else { + if (overwrite) { + foreach (var e in sourceAttachments) + destinationAttachments[e.Key] = e.Value; + } else { + foreach (var e in sourceAttachments) { + if (destinationAttachments.ContainsKey(e.Key)) continue; + destinationAttachments.Add(e.Key, e.Value); + } + } + } + } + + + } + + public static class AttachmentCloneExtensions { + /// + /// Clones the attachment. + public static Attachment GetClone (this Attachment o, bool cloneMeshesAsLinked) { + var regionAttachment = o as RegionAttachment; + if (regionAttachment != null) + return regionAttachment.GetClone(); + + var meshAttachment = o as MeshAttachment; + if (meshAttachment != null) + return cloneMeshesAsLinked ? meshAttachment.GetLinkedClone() : meshAttachment.GetClone(); + + var boundingBoxAttachment = o as BoundingBoxAttachment; + if (boundingBoxAttachment != null) + return boundingBoxAttachment.GetClone(); + + var pathAttachment = o as PathAttachment; + if (pathAttachment != null) + return pathAttachment.GetClone(); + + var pointAttachment = o as PointAttachment; + if (pointAttachment != null) + return pointAttachment.GetClone(); + + var clippingAttachment = o as ClippingAttachment; + if (clippingAttachment != null) + return clippingAttachment.GetClone(); + + return null; + } + + public static RegionAttachment GetClone (this RegionAttachment o) { + return new RegionAttachment(o.Name + "clone") { + x = o.x, + y = o.y, + rotation = o.rotation, + scaleX = o.scaleX, + scaleY = o.scaleY, + width = o.width, + height = o.height, + + r = o.r, + g = o.g, + b = o.b, + a = o.a, + + Path = o.Path, + RendererObject = o.RendererObject, + regionOffsetX = o.regionOffsetX, + regionOffsetY = o.regionOffsetY, + regionWidth = o.regionWidth, + regionHeight = o.regionHeight, + regionOriginalWidth = o.regionOriginalWidth, + regionOriginalHeight = o.regionOriginalHeight, + uvs = o.uvs.Clone() as float[], + offset = o.offset.Clone() as float[] + }; + } + + public static ClippingAttachment GetClone (this ClippingAttachment o) { + var ca = new ClippingAttachment(o.Name) { + endSlot = o.endSlot + }; + CloneVertexAttachment(o, ca); + return ca; + } + + public static PointAttachment GetClone (this PointAttachment o) { + var pa = new PointAttachment(o.Name) { + rotation = o.rotation, + x = o.x, + y = o.y + }; + return pa; + } + + public static BoundingBoxAttachment GetClone (this BoundingBoxAttachment o) { + var ba = new BoundingBoxAttachment(o.Name); + CloneVertexAttachment(o, ba); + return ba; + } + + public static MeshAttachment GetLinkedClone (this MeshAttachment o, bool inheritDeform = true) { + return o.GetLinkedMesh(o.Name, o.RendererObject as AtlasRegion, inheritDeform, copyOriginalProperties: true); + } + + /// + /// Returns a clone of the MeshAttachment. This will cause Deform animations to stop working unless you explicity set the .parentMesh to the original. + public static MeshAttachment GetClone (this MeshAttachment o) { + var ma = new MeshAttachment(o.Name) { + r = o.r, + g = o.g, + b = o.b, + a = o.a, + + inheritDeform = o.inheritDeform, + + Path = o.Path, + RendererObject = o.RendererObject, + + regionOffsetX = o.regionOffsetX, + regionOffsetY = o.regionOffsetY, + regionWidth = o.regionWidth, + regionHeight = o.regionHeight, + regionOriginalWidth = o.regionOriginalWidth, + regionOriginalHeight = o.regionOriginalHeight, + RegionU = o.RegionU, + RegionV = o.RegionV, + RegionU2 = o.RegionU2, + RegionV2 = o.RegionV2, + RegionRotate = o.RegionRotate, + uvs = o.uvs.Clone() as float[] + }; + + // Linked mesh + if (o.ParentMesh != null) { + // bones, vertices, worldVerticesLength, regionUVs, triangles, HullLength, Edges, Width, Height + ma.ParentMesh = o.ParentMesh; + } else { + CloneVertexAttachment(o, ma); // bones, vertices, worldVerticesLength + ma.regionUVs = o.regionUVs.Clone() as float[]; + ma.triangles = o.triangles.Clone() as int[]; + ma.hulllength = o.hulllength; + + // Nonessential. + ma.Edges = (o.Edges == null) ? null : o.Edges.Clone() as int[]; // Allow absence of Edges array when nonessential data is not exported. + ma.Width = o.Width; + ma.Height = o.Height; + } + + return ma; + } + + public static PathAttachment GetClone (this PathAttachment o) { + var newPathAttachment = new PathAttachment(o.Name) { + lengths = o.lengths.Clone() as float[], + closed = o.closed, + constantSpeed = o.constantSpeed + }; + CloneVertexAttachment(o, newPathAttachment); + + return newPathAttachment; + } + + static void CloneVertexAttachment (VertexAttachment src, VertexAttachment dest) { + dest.worldVerticesLength = src.worldVerticesLength; + if (src.bones != null) + dest.bones = src.bones.Clone() as int[]; + + if (src.vertices != null) + dest.vertices = src.vertices.Clone() as float[]; + } + + + #region Runtime Linked MeshAttachments + /// + /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to the AtlasRegion provided. + public static MeshAttachment GetLinkedMesh (this MeshAttachment o, string newLinkedMeshName, AtlasRegion region, bool inheritDeform = true, bool copyOriginalProperties = false) { + //if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName cannot be null or empty", "attachmentName"); + if (region == null) throw new System.ArgumentNullException("region"); + + // If parentMesh is a linked mesh, create a link to its parent. Preserves Deform animations. + if (o.ParentMesh != null) + o = o.ParentMesh; + + // 1. NewMeshAttachment (AtlasAttachmentLoader.cs) + var mesh = new MeshAttachment(newLinkedMeshName); + mesh.SetRegion(region, false); + + // 2. (SkeletonJson.cs::ReadAttachment. case: LinkedMesh) + mesh.Path = newLinkedMeshName; + if (copyOriginalProperties) { + mesh.r = o.r; + mesh.g = o.g; + mesh.b = o.b; + mesh.a = o.a; + } else { + mesh.r = 1f; + mesh.g = 1f; + mesh.b = 1f; + mesh.a = 1f; + } + //mesh.ParentMesh property call below sets mesh.Width and mesh.Height + + // 3. Link mesh with parent. (SkeletonJson.cs) + mesh.inheritDeform = inheritDeform; + mesh.ParentMesh = o; + mesh.UpdateUVs(); + + return mesh; + } + + /// + /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader. + /// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool) + public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Shader shader, bool inheritDeform = true, Material materialPropertySource = null) { + var m = new Material(shader); + if (materialPropertySource != null) { + m.CopyPropertiesFromMaterial(materialPropertySource); + m.shaderKeywords = materialPropertySource.shaderKeywords; + } + return o.GetLinkedMesh(sprite.name, sprite.ToAtlasRegion(), inheritDeform); + } + + /// + /// Returns a new linked mesh linked to this MeshAttachment. It will be mapped to an AtlasRegion generated from a Sprite. The AtlasRegion will be mapped to a new Material based on the shader. + /// For better caching and batching, use GetLinkedMesh(string, AtlasRegion, bool) + public static MeshAttachment GetLinkedMesh (this MeshAttachment o, Sprite sprite, Material materialPropertySource, bool inheritDeform = true) { + return o.GetLinkedMesh(sprite, materialPropertySource.shader, inheritDeform, materialPropertySource); + } + #endregion + + #region RemappedClone Convenience Methods + /// + /// Gets a clone of the attachment remapped with a sprite image. + /// The remapped clone. + /// The original attachment. + /// The sprite whose texture to use. + /// The source material used to copy the shader and material properties from. + /// If true, a premultiply alpha clone of the original texture will be created. + /// If true MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment. + /// If true the size of the original attachment will be followed, instead of using the Sprite size. + public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial, bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false) { + var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture }); + return o.GetRemappedClone(atlasRegion, cloneMeshAsLinked, useOriginalRegionSize, 1f/sprite.pixelsPerUnit); + } + + /// + /// Gets a clone of the attachment remapped with an atlasRegion image. + /// The remapped clone. + /// The original attachment. + /// Atlas region. + /// If true MeshAttachments will be cloned as linked meshes and will inherit animation from the original attachment. + /// If true the size of the original attachment will be followed, instead of using the Sprite size. + /// Unity units per pixel scale used to scale the atlas region size when not using the original region size. + public static Attachment GetRemappedClone (this Attachment o, AtlasRegion atlasRegion, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, float scale = 0.01f) { + var regionAttachment = o as RegionAttachment; + if (regionAttachment != null) { + RegionAttachment newAttachment = regionAttachment.GetClone(); + newAttachment.SetRegion(atlasRegion, false); + if (!useOriginalRegionSize) { + newAttachment.width = atlasRegion.width * scale; + newAttachment.height = atlasRegion.height * scale; + } + newAttachment.UpdateOffset(); + return newAttachment; + } else { + var meshAttachment = o as MeshAttachment; + if (meshAttachment != null) { + MeshAttachment newAttachment = cloneMeshAsLinked ? meshAttachment.GetLinkedClone(cloneMeshAsLinked) : meshAttachment.GetClone(); + newAttachment.SetRegion(atlasRegion); + return newAttachment; + } + } + + return o.GetClone(true); // Non-renderable Attachments will return as normal cloned attachments. + } + #endregion + } +} diff --git a/Assets/Spine/spine-unity/Modules/AttachmentTools/AttachmentTools.cs.meta b/Assets/Spine/spine-unity/Modules/AttachmentTools/AttachmentTools.cs.meta new file mode 100644 index 0000000..68ad280 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/AttachmentTools/AttachmentTools.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8dd46dbf979fcb7459246cd37aad09ef +timeCreated: 1478437807 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/BoundingBoxFollower.meta b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower.meta new file mode 100644 index 0000000..56d0730 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 86b1ae3ec8681c646aea49654969b73c +folderAsset: yes +timeCreated: 1455492474 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs new file mode 100644 index 0000000..54df52e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs @@ -0,0 +1,240 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections.Generic; + +namespace Spine.Unity { + + [ExecuteInEditMode] + public class BoundingBoxFollower : MonoBehaviour { + internal static bool DebugMessages = true; + + #region Inspector + public SkeletonRenderer skeletonRenderer; + [SpineSlot(dataField: "skeletonRenderer", containsBoundingBoxes: true)] + public string slotName; + public bool isTrigger; + public bool clearStateOnDisable = true; + #endregion + + Slot slot; + BoundingBoxAttachment currentAttachment; + string currentAttachmentName; + PolygonCollider2D currentCollider; + + public readonly Dictionary colliderTable = new Dictionary(); + public readonly Dictionary nameTable = new Dictionary(); + + public Slot Slot { get { return slot; } } + public BoundingBoxAttachment CurrentAttachment { get { return currentAttachment; } } + public string CurrentAttachmentName { get { return currentAttachmentName; } } + public PolygonCollider2D CurrentCollider { get { return currentCollider; } } + public bool IsTrigger { get { return isTrigger; } } + + void Start () { + Initialize(); + } + + void OnEnable () { + if (skeletonRenderer != null) { + skeletonRenderer.OnRebuild -= HandleRebuild; + skeletonRenderer.OnRebuild += HandleRebuild; + } + + Initialize(); + } + + void HandleRebuild (SkeletonRenderer sr) { + //if (BoundingBoxFollower.DebugMessages) Debug.Log("Skeleton was rebuilt. Repopulating BoundingBoxFollower."); + Initialize(); + } + + /// + /// Initialize and instantiate the BoundingBoxFollower colliders. This is method checks if the BoundingBoxFollower has already been initialized for the skeleton instance and slotName and prevents overwriting unless it detects a new setup. + public void Initialize (bool overwrite = false) { + if (skeletonRenderer == null) + return; + + skeletonRenderer.Initialize(false); + + if (string.IsNullOrEmpty(slotName)) + return; + + // Don't reinitialize if the setup did not change. + if (!overwrite + && + colliderTable.Count > 0 && slot != null // Slot is set and colliders already populated. + && + skeletonRenderer.skeleton == slot.Skeleton // Skeleton object did not change. + && + slotName == slot.data.name // Slot object did not change. + ) + return; + + DisposeColliders(); + + var skeleton = skeletonRenderer.skeleton; + slot = skeleton.FindSlot(slotName); + int slotIndex = skeleton.FindSlotIndex(slotName); + + if (slot == null) { + if (BoundingBoxFollower.DebugMessages) + Debug.LogWarning(string.Format("Slot '{0}' not found for BoundingBoxFollower on '{1}'. (Previous colliders were disposed.)", slotName, this.gameObject.name)); + return; + } + + if (this.gameObject.activeInHierarchy) { + foreach (var skin in skeleton.Data.Skins) + AddSkin(skin, slotIndex); + + if (skeleton.skin != null) + AddSkin(skeleton.skin, slotIndex); + } + + if (BoundingBoxFollower.DebugMessages) { + bool valid = colliderTable.Count != 0; + if (!valid) { + if (this.gameObject.activeInHierarchy) + Debug.LogWarning("Bounding Box Follower not valid! Slot [" + slotName + "] does not contain any Bounding Box Attachments!"); + else + Debug.LogWarning("Bounding Box Follower tried to rebuild as a prefab."); + } + } + } + + void AddSkin (Skin skin, int slotIndex) { + if (skin == null) return; + var attachmentNames = new List(); + skin.FindNamesForSlot(slotIndex, attachmentNames); + + foreach (var skinKey in attachmentNames) { + var attachment = skin.GetAttachment(slotIndex, skinKey); + var boundingBoxAttachment = attachment as BoundingBoxAttachment; + + if (BoundingBoxFollower.DebugMessages && attachment != null && boundingBoxAttachment == null) + Debug.Log("BoundingBoxFollower tried to follow a slot that contains non-boundingbox attachments: " + slotName); + + if (boundingBoxAttachment != null) { + if (!colliderTable.ContainsKey(boundingBoxAttachment)) { + var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(boundingBoxAttachment, slot, gameObject, isTrigger); + bbCollider.enabled = false; + bbCollider.hideFlags = HideFlags.NotEditable; + bbCollider.isTrigger = IsTrigger; + colliderTable.Add(boundingBoxAttachment, bbCollider); + nameTable.Add(boundingBoxAttachment, skinKey); + } + } + } + } + + void OnDisable () { + if (clearStateOnDisable) + ClearState(); + } + + public void ClearState () { + if (colliderTable != null) + foreach (var col in colliderTable.Values) + col.enabled = false; + + currentAttachment = null; + currentAttachmentName = null; + currentCollider = null; + } + + void DisposeColliders () { + var colliders = GetComponents(); + if (colliders.Length == 0) return; + + if (Application.isEditor) { + if (Application.isPlaying) { + foreach (var c in colliders) { + if (c != null) + Destroy(c); + } + } else { + foreach (var c in colliders) + if (c != null) + DestroyImmediate(c); + } + } else { + foreach (PolygonCollider2D c in colliders) + if (c != null) + Destroy(c); + } + + slot = null; + currentAttachment = null; + currentAttachmentName = null; + currentCollider = null; + colliderTable.Clear(); + nameTable.Clear(); + } + + void LateUpdate () { + if (slot != null && slot.Attachment != currentAttachment) + MatchAttachment(slot.Attachment); + } + + /// Sets the current collider to match attachment. + /// If the attachment is not a bounding box, it will be treated as null. + void MatchAttachment (Attachment attachment) { + var bbAttachment = attachment as BoundingBoxAttachment; + + if (BoundingBoxFollower.DebugMessages && attachment != null && bbAttachment == null) + Debug.LogWarning("BoundingBoxFollower tried to match a non-boundingbox attachment. It will treat it as null."); + + if (currentCollider != null) + currentCollider.enabled = false; + + if (bbAttachment == null) { + currentCollider = null; + currentAttachment = null; + currentAttachmentName = null; + } else { + PolygonCollider2D foundCollider; + colliderTable.TryGetValue(bbAttachment, out foundCollider); + if (foundCollider != null) { + currentCollider = foundCollider; + currentCollider.enabled = true; + currentAttachment = bbAttachment; + currentAttachmentName = nameTable[bbAttachment]; + } else { + currentCollider = null; + currentAttachment = bbAttachment; + currentAttachmentName = null; + if (BoundingBoxFollower.DebugMessages) Debug.LogFormat("Collider for BoundingBoxAttachment named '{0}' was not initialized. It is possibly from a new skin. currentAttachmentName will be null. You may need to call BoundingBoxFollower.Initialize(overwrite: true);", bbAttachment.Name); + } + } + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs.meta b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs.meta new file mode 100644 index 0000000..d81fd14 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/BoundingBoxFollower.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0317ee9ba6e1b1e49a030268e026d372 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor.meta b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor.meta new file mode 100644 index 0000000..76ca150 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b7f013ad20f0af34e913e21b44466777 +folderAsset: yes +timeCreated: 1455492480 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs new file mode 100644 index 0000000..94f1e35 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs @@ -0,0 +1,206 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; + +namespace Spine.Unity.Editor { + using Event = UnityEngine.Event; + using Icons = SpineEditorUtilities.Icons; + + [CustomEditor(typeof(BoundingBoxFollower))] + public class BoundingBoxFollowerInspector : UnityEditor.Editor { + SerializedProperty skeletonRenderer, slotName, isTrigger, clearStateOnDisable; + BoundingBoxFollower follower; + bool rebuildRequired = false; + bool addBoneFollower = false; + bool sceneRepaintRequired = false; + bool debugIsExpanded; + + GUIContent addBoneFollowerLabel; + GUIContent AddBoneFollowerLabel { + get { + if (addBoneFollowerLabel == null) addBoneFollowerLabel = new GUIContent("Add Bone Follower", Icons.bone); + return addBoneFollowerLabel; + } + } + + void OnEnable () { + skeletonRenderer = serializedObject.FindProperty("skeletonRenderer"); + slotName = serializedObject.FindProperty("slotName"); + isTrigger = serializedObject.FindProperty("isTrigger"); + clearStateOnDisable = serializedObject.FindProperty("clearStateOnDisable"); + follower = (BoundingBoxFollower)target; + } + + public override void OnInspectorGUI () { + bool isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab); + + // Try to auto-assign SkeletonRenderer field. + if (skeletonRenderer.objectReferenceValue == null) { + var foundSkeletonRenderer = follower.GetComponentInParent(); + if (foundSkeletonRenderer != null) + Debug.Log("BoundingBoxFollower automatically assigned: " + foundSkeletonRenderer.gameObject.name); + else if (Event.current.type == EventType.Repaint) + Debug.Log("No Spine GameObject detected. Make sure to set this GameObject as a child of the Spine GameObject; or set BoundingBoxFollower's 'Skeleton Renderer' field in the inspector."); + + skeletonRenderer.objectReferenceValue = foundSkeletonRenderer; + serializedObject.ApplyModifiedProperties(); + } + + var skeletonRendererValue = skeletonRenderer.objectReferenceValue as SkeletonRenderer; + if (skeletonRendererValue != null && skeletonRendererValue.gameObject == follower.gameObject) { + using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { + EditorGUILayout.HelpBox("It's ideal to add BoundingBoxFollower to a separate child GameObject of the Spine GameObject.", MessageType.Warning); + + if (GUILayout.Button(new GUIContent("Move BoundingBoxFollower to new GameObject", Icons.boundingBox), GUILayout.Height(50f))) { + AddBoundingBoxFollowerChild(skeletonRendererValue, follower); + DestroyImmediate(follower); + return; + } + } + EditorGUILayout.Space(); + } + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(skeletonRenderer); + EditorGUILayout.PropertyField(slotName, new GUIContent("Slot")); + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + if (!isInspectingPrefab) + rebuildRequired = true; + } + + using (new SpineInspectorUtility.LabelWidthScope(150f)) { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(isTrigger); + bool triggerChanged = EditorGUI.EndChangeCheck(); + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(clearStateOnDisable, new GUIContent(clearStateOnDisable.displayName, "Enable this if you are pooling your Spine GameObject")); + bool clearStateChanged = EditorGUI.EndChangeCheck(); + + if (clearStateChanged || triggerChanged) { + serializedObject.ApplyModifiedProperties(); + if (triggerChanged) + foreach (var col in follower.colliderTable.Values) + col.isTrigger = isTrigger.boolValue; + } + } + + if (isInspectingPrefab) { + follower.colliderTable.Clear(); + follower.nameTable.Clear(); + EditorGUILayout.HelpBox("BoundingBoxAttachments cannot be previewed in prefabs.", MessageType.Info); + + // How do you prevent components from being saved into the prefab? No such HideFlag. DontSaveInEditor | DontSaveInBuild does not work. DestroyImmediate does not work. + var collider = follower.GetComponent(); + if (collider != null) Debug.LogWarning("Found BoundingBoxFollower collider components in prefab. These are disposed and regenerated at runtime."); + + } else { + using (new SpineInspectorUtility.BoxScope()) { + if (debugIsExpanded = EditorGUILayout.Foldout(debugIsExpanded, "Debug Colliders")) { + EditorGUI.indentLevel++; + EditorGUILayout.LabelField(string.Format("Attachment Names ({0} PolygonCollider2D)", follower.colliderTable.Count)); + EditorGUI.BeginChangeCheck(); + foreach (var kp in follower.nameTable) { + string attachmentName = kp.Value; + var collider = follower.colliderTable[kp.Key]; + bool isPlaceholder = attachmentName != kp.Key.Name; + collider.enabled = EditorGUILayout.ToggleLeft(new GUIContent(!isPlaceholder ? attachmentName : string.Format("{0} [{1}]", attachmentName, kp.Key.Name), isPlaceholder ? Icons.skinPlaceholder : Icons.boundingBox), collider.enabled); + } + sceneRepaintRequired |= EditorGUI.EndChangeCheck(); + EditorGUI.indentLevel--; + } + } + + } + + bool hasBoneFollower = follower.GetComponent() != null; + if (!hasBoneFollower) { + bool buttonDisabled = follower.Slot == null; + using (new EditorGUI.DisabledGroupScope(buttonDisabled)) { + addBoneFollower |= SpineInspectorUtility.LargeCenteredButton(AddBoneFollowerLabel, true); + EditorGUILayout.Space(); + } + } + + + if (Event.current.type == EventType.Repaint) { + if (addBoneFollower) { + var boneFollower = follower.gameObject.AddComponent(); + boneFollower.skeletonRenderer = skeletonRendererValue; + boneFollower.SetBone(follower.Slot.Data.BoneData.Name); + addBoneFollower = false; + } + + if (sceneRepaintRequired) { + SceneView.RepaintAll(); + sceneRepaintRequired = false; + } + + if (rebuildRequired) { + follower.Initialize(); + rebuildRequired = false; + } + } + } + + #region Menus + [MenuItem("CONTEXT/SkeletonRenderer/Add BoundingBoxFollower GameObject")] + static void AddBoundingBoxFollowerChild (MenuCommand command) { + var go = AddBoundingBoxFollowerChild((SkeletonRenderer)command.context); + Undo.RegisterCreatedObjectUndo(go, "Add BoundingBoxFollower"); + } + #endregion + + static GameObject AddBoundingBoxFollowerChild (SkeletonRenderer sr, BoundingBoxFollower original = null) { + var go = new GameObject("BoundingBoxFollower"); + go.transform.SetParent(sr.transform, false); + var newFollower = go.AddComponent(); + + if (original != null) { + newFollower.slotName = original.slotName; + newFollower.isTrigger = original.isTrigger; + newFollower.clearStateOnDisable = original.clearStateOnDisable; + } + + newFollower.skeletonRenderer = sr; + newFollower.Initialize(); + + + Selection.activeGameObject = go; + EditorGUIUtility.PingObject(go); + return go; + } + + } + +} diff --git a/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs.meta b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs.meta new file mode 100644 index 0000000..e42ba77 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/BoundingBoxFollower/Editor/BoundingBoxFollowerInspector.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 670a3cefa3853bd48b5da53a424fd542 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials.meta b/Assets/Spine/spine-unity/Modules/CustomMaterials.meta new file mode 100644 index 0000000..16b542c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a7236dbdc6a4e5a4989483dac97aee0b +folderAsset: yes +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor.meta b/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor.meta new file mode 100644 index 0000000..6b6e2f6 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 99abd7478ddde384cbf86f2ecd396900 +folderAsset: yes +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs b/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs new file mode 100644 index 0000000..e102ad8 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs @@ -0,0 +1,166 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define SPINE_OPTIONAL_MATERIALOVERRIDE + +// Contributed by: Lost Polygon + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using Spine.Unity.Modules; + +namespace Spine.Unity.Editor { + + // This script is not intended for use with code. See the readme.txt file in SkeletonRendererCustomMaterials folder to learn more. + [CustomEditor(typeof(SkeletonRendererCustomMaterials))] + public class SkeletonRendererCustomMaterialsInspector : UnityEditor.Editor { + List componentCustomMaterialOverrides, _customMaterialOverridesPrev; + List componentCustomSlotMaterials, _customSlotMaterialsPrev; + SkeletonRendererCustomMaterials component; + + const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic; + MethodInfo RemoveCustomMaterialOverrides, RemoveCustomSlotMaterials, SetCustomMaterialOverrides, SetCustomSlotMaterials; + + #region SkeletonRenderer context menu + [MenuItem("CONTEXT/SkeletonRenderer/Add Basic Serialized Custom Materials")] + static void AddSkeletonRendererCustomMaterials (MenuCommand menuCommand) { + var skeletonRenderer = (SkeletonRenderer)menuCommand.context; + var newComponent = skeletonRenderer.gameObject.AddComponent(); + Undo.RegisterCreatedObjectUndo(newComponent, "Add Basic Serialized Custom Materials"); + } + + [MenuItem("CONTEXT/SkeletonRenderer/Add Basic Serialized Custom Materials", true)] + static bool AddSkeletonRendererCustomMaterials_Validate (MenuCommand menuCommand) { + var skeletonRenderer = (SkeletonRenderer)menuCommand.context; + return (skeletonRenderer.GetComponent() == null); + } + #endregion + + void OnEnable () { + Type cm = typeof(SkeletonRendererCustomMaterials); + RemoveCustomMaterialOverrides = cm.GetMethod("RemoveCustomMaterialOverrides", PrivateInstance); + RemoveCustomSlotMaterials = cm.GetMethod("RemoveCustomSlotMaterials", PrivateInstance); + SetCustomMaterialOverrides = cm.GetMethod("SetCustomMaterialOverrides", PrivateInstance); + SetCustomSlotMaterials = cm.GetMethod("SetCustomSlotMaterials", PrivateInstance); + } + + public override void OnInspectorGUI () { + component = (SkeletonRendererCustomMaterials)target; + var skeletonRenderer = component.skeletonRenderer; + + // Draw the default inspector + DrawDefaultInspector(); + + if (serializedObject.isEditingMultipleObjects) + return; + + if (componentCustomMaterialOverrides == null) { + Type cm = typeof(SkeletonRendererCustomMaterials); + componentCustomMaterialOverrides = cm.GetField("customMaterialOverrides", PrivateInstance).GetValue(component) as List; + componentCustomSlotMaterials = cm.GetField("customSlotMaterials", PrivateInstance).GetValue(component) as List; + if (componentCustomMaterialOverrides == null) { + Debug.Log("Reflection failed."); + return; + } + } + + // Fill with current values at start + if (_customMaterialOverridesPrev == null || _customSlotMaterialsPrev == null) { + _customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides); + _customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials); + } + + // Compare new values with saved. If change is detected: + // store new values, restore old values, remove overrides, restore new values, restore overrides. + + // 1. Store new values + var customMaterialOverridesNew = CopyList(componentCustomMaterialOverrides); + var customSlotMaterialsNew = CopyList(componentCustomSlotMaterials); + + // Detect changes + if (!_customMaterialOverridesPrev.SequenceEqual(customMaterialOverridesNew) || + !_customSlotMaterialsPrev.SequenceEqual(customSlotMaterialsNew)) { + // 2. Restore old values + componentCustomMaterialOverrides.Clear(); + componentCustomSlotMaterials.Clear(); + componentCustomMaterialOverrides.AddRange(_customMaterialOverridesPrev); + componentCustomSlotMaterials.AddRange(_customSlotMaterialsPrev); + + // 3. Remove overrides + RemoveCustomMaterials(); + + // 4. Restore new values + componentCustomMaterialOverrides.Clear(); + componentCustomSlotMaterials.Clear(); + componentCustomMaterialOverrides.AddRange(customMaterialOverridesNew); + componentCustomSlotMaterials.AddRange(customSlotMaterialsNew); + + // 5. Restore overrides + SetCustomMaterials(); + + if (skeletonRenderer != null) + skeletonRenderer.LateUpdate(); + } + + _customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides); + _customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials); + + if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Clear and Reapply Changes", tooltip: "Removes all non-serialized overrides in the SkeletonRenderer and reapplies the overrides on this component."))) { + if (skeletonRenderer != null) { + #if SPINE_OPTIONAL_MATERIALOVERRIDE + skeletonRenderer.CustomMaterialOverride.Clear(); + #endif + skeletonRenderer.CustomSlotMaterials.Clear(); + RemoveCustomMaterials(); + SetCustomMaterials(); + skeletonRenderer.LateUpdate(); + } + } + } + + void RemoveCustomMaterials () { + RemoveCustomMaterialOverrides.Invoke(component, null); + RemoveCustomSlotMaterials.Invoke(component, null); + } + + void SetCustomMaterials () { + SetCustomMaterialOverrides.Invoke(component, null); + SetCustomSlotMaterials.Invoke(component, null); + } + + static List CopyList (List list) { + return list.GetRange(0, list.Count); + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs.meta b/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs.meta new file mode 100644 index 0000000..1e37a56 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: e70f7f2a241d6d34aafd6a4a52a368d0 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.cs b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.cs new file mode 100644 index 0000000..a8d8a71 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.cs @@ -0,0 +1,204 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define SPINE_OPTIONAL_MATERIALOVERRIDE + +// Contributed by: Lost Polygon + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity.Modules { + [ExecuteInEditMode] + public class SkeletonRendererCustomMaterials : MonoBehaviour { + + #region Inspector + public SkeletonRenderer skeletonRenderer; + [SerializeField] protected List customSlotMaterials = new List(); + [SerializeField] protected List customMaterialOverrides = new List(); + + #if UNITY_EDITOR + void Reset () { + skeletonRenderer = GetComponent(); + + // Populate atlas list + if (skeletonRenderer != null && skeletonRenderer.skeletonDataAsset != null) { + AtlasAsset[] atlasAssets = skeletonRenderer.skeletonDataAsset.atlasAssets; + + var initialAtlasMaterialOverrides = new List(); + foreach (AtlasAsset atlasAsset in atlasAssets) { + foreach (Material atlasMaterial in atlasAsset.materials) { + var atlasMaterialOverride = new AtlasMaterialOverride(); + atlasMaterialOverride.overrideDisabled = true; + atlasMaterialOverride.originalMaterial = atlasMaterial; + + initialAtlasMaterialOverrides.Add(atlasMaterialOverride); + } + } + + customMaterialOverrides = initialAtlasMaterialOverrides; + } + } + #endif + #endregion + + void SetCustomSlotMaterials () { + if (skeletonRenderer == null) { + Debug.LogError("skeletonRenderer == null"); + return; + } + + for (int i = 0; i < customSlotMaterials.Count; i++) { + SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i]; + if (slotMaterialOverride.overrideDisabled || string.IsNullOrEmpty(slotMaterialOverride.slotName)) + continue; + + Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName); + skeletonRenderer.CustomSlotMaterials[slotObject] = slotMaterialOverride.material; + } + } + + void RemoveCustomSlotMaterials () { + if (skeletonRenderer == null) { + Debug.LogError("skeletonRenderer == null"); + return; + } + + for (int i = 0; i < customSlotMaterials.Count; i++) { + SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i]; + if (string.IsNullOrEmpty(slotMaterialOverride.slotName)) + continue; + + Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName); + + Material currentMaterial; + if (!skeletonRenderer.CustomSlotMaterials.TryGetValue(slotObject, out currentMaterial)) + continue; + + // Do not revert the material if it was changed by something else + if (currentMaterial != slotMaterialOverride.material) + continue; + + skeletonRenderer.CustomSlotMaterials.Remove(slotObject); + } + } + + void SetCustomMaterialOverrides () { + if (skeletonRenderer == null) { + Debug.LogError("skeletonRenderer == null"); + return; + } + + #if SPINE_OPTIONAL_MATERIALOVERRIDE + for (int i = 0; i < customMaterialOverrides.Count; i++) { + AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i]; + if (atlasMaterialOverride.overrideDisabled) + continue; + + skeletonRenderer.CustomMaterialOverride[atlasMaterialOverride.originalMaterial] = atlasMaterialOverride.replacementMaterial; + } + #endif + } + + void RemoveCustomMaterialOverrides () { + if (skeletonRenderer == null) { + Debug.LogError("skeletonRenderer == null"); + return; + } + + #if SPINE_OPTIONAL_MATERIALOVERRIDE + for (int i = 0; i < customMaterialOverrides.Count; i++) { + AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i]; + Material currentMaterial; + + if (!skeletonRenderer.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalMaterial, out currentMaterial)) + continue; + + // Do not revert the material if it was changed by something else + if (currentMaterial != atlasMaterialOverride.replacementMaterial) + continue; + + skeletonRenderer.CustomMaterialOverride.Remove(atlasMaterialOverride.originalMaterial); + } + #endif + } + + // OnEnable applies the overrides at runtime, and when the editor loads. + void OnEnable () { + if (skeletonRenderer == null) + skeletonRenderer = GetComponent(); + + if (skeletonRenderer == null) { + Debug.LogError("skeletonRenderer == null"); + return; + } + + skeletonRenderer.Initialize(false); + SetCustomMaterialOverrides(); + SetCustomSlotMaterials(); + } + + // OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed. + void OnDisable () { + if (skeletonRenderer == null) { + Debug.LogError("skeletonRenderer == null"); + return; + } + + RemoveCustomMaterialOverrides(); + RemoveCustomSlotMaterials(); + } + + [Serializable] + public struct SlotMaterialOverride : IEquatable { + public bool overrideDisabled; + + [SpineSlot] + public string slotName; + public Material material; + + public bool Equals (SlotMaterialOverride other) { + return overrideDisabled == other.overrideDisabled && slotName == other.slotName && material == other.material; + } + } + + [Serializable] + public struct AtlasMaterialOverride : IEquatable { + public bool overrideDisabled; + public Material originalMaterial; + public Material replacementMaterial; + + public bool Equals (AtlasMaterialOverride other) { + return overrideDisabled == other.overrideDisabled && originalMaterial == other.originalMaterial && replacementMaterial == other.replacementMaterial; + } + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.cs.meta b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.cs.meta new file mode 100644 index 0000000..eb50faf --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 26947ae098a8447408d80c0c86e35b48 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.txt b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.txt new file mode 100644 index 0000000..9170775 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.txt @@ -0,0 +1,11 @@ +SkeletonRendererCustomMaterials by LostPolygon +=============================== +This is a basic serialization and inspector implementation for custom material overrides for SkeletonRenderer and its derived classes (SkeletonAnimation, SkeletonAnimator). + +## How to use +Right-click on your SkeletonRenderer and select "Add Basic Serialized Custom Materials". This will add and initialize the SkeletonRendererCustomMaterials to the same object. + +You can use this to store material override settings for SkeletonRenderer instances/prefabs so they will be applied automatically when your scene starts or when the prefab is instantiated. + +This script is not intended for use with code. +To dynamically set materials for your SkeletonRenderer through code, you can directly access `SkeletonRenderer.CustomMaterialOverride` for material array overrides and `SkeletonRenderer.CustomSlotMaterials` for slot material overrides. \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.txt.meta b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.txt.meta new file mode 100644 index 0000000..a979106 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/CustomMaterials/SkeletonRendererCustomMaterials.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d4db6c367e463c4cb5566afc490163c +timeCreated: 1460572571 +licenseType: Free +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ghost.meta b/Assets/Spine/spine-unity/Modules/Ghost.meta new file mode 100644 index 0000000..cc48589 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 13193c9d213765f4c85f4c1faa615711 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Modules/Ghost/Shaders.meta b/Assets/Spine/spine-unity/Modules/Ghost/Shaders.meta new file mode 100644 index 0000000..0e0bd21 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/Shaders.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: a0cee0de78cef7440ae0b5aac39ae971 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Modules/Ghost/Shaders/Spine-Special-Skeleton-Ghost.shader b/Assets/Spine/spine-unity/Modules/Ghost/Shaders/Spine-Special-Skeleton-Ghost.shader new file mode 100644 index 0000000..235d76c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/Shaders/Spine-Special-Skeleton-Ghost.shader @@ -0,0 +1,59 @@ +// - Unlit + no shadow +// - Premultiplied Alpha Blending (One OneMinusSrcAlpha) +// - Double-sided, no depth + +Shader "Spine/Special/SkeletonGhost" { + Properties { + _Color ("Main Color", Color) = (1,1,1,1) + [NoScaleOffset] _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {} + _TextureFade ("Texture Fade Out", Range(0,1)) = 0 + } + SubShader { + Tags { + "Queue"="Transparent" + "IgnoreProjector"="False" + "RenderType"="Transparent" + } + Fog { Mode Off } + Blend One OneMinusSrcAlpha + ZWrite Off + Cull Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + fixed4 _Color; + fixed _TextureFade; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 color : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 color : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + o.color = v.color; + return o; + } + + fixed4 frag (VertexOutput i) : COLOR { + fixed4 tc = tex2D(_MainTex, i.uv); + tc = fixed4(max(_TextureFade, tc.r), max(_TextureFade, tc.g), max(_TextureFade, tc.b), tc.a); + return tc * ((i.color * _Color) * tc.a); + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Ghost/Shaders/Spine-Special-Skeleton-Ghost.shader.meta b/Assets/Spine/spine-unity/Modules/Ghost/Shaders/Spine-Special-Skeleton-Ghost.shader.meta new file mode 100644 index 0000000..d24d9a3 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/Shaders/Spine-Special-Skeleton-Ghost.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3873d4699ee8a4b4da8fa6b8c229b94d +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhost.cs b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhost.cs new file mode 100644 index 0000000..535b14f --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhost.cs @@ -0,0 +1,186 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using System.Collections.Generic; + +namespace Spine.Unity.Modules { + + [RequireComponent(typeof(SkeletonRenderer))] + public class SkeletonGhost : MonoBehaviour { + // Internal Settings + const HideFlags GhostHideFlags = HideFlags.HideInHierarchy; + const string GhostingShaderName = "Spine/Special/SkeletonGhost"; + + public bool ghostingEnabled = true; + public float spawnRate = 0.05f; + public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive. + [Tooltip("Remember to set color alpha to 0 if Additive is true")] + public bool additive = true; + public int maximumGhosts = 10; + public float fadeSpeed = 10; + public Shader ghostShader; + [Tooltip("0 is Color and Alpha, 1 is Alpha only.")] + [Range(0, 1)] + public float textureFade = 1; + + [Header("Sorting")] + public bool sortWithDistanceOnly; + public float zOffset = 0f; + + float nextSpawnTime; + SkeletonGhostRenderer[] pool; + int poolIndex = 0; + SkeletonRenderer skeletonRenderer; + MeshRenderer meshRenderer; + MeshFilter meshFilter; + + readonly Dictionary materialTable = new Dictionary(); + + void Start () { + if (ghostShader == null) + ghostShader = Shader.Find(GhostingShaderName); + + skeletonRenderer = GetComponent(); + meshFilter = GetComponent(); + meshRenderer = GetComponent(); + nextSpawnTime = Time.time + spawnRate; + pool = new SkeletonGhostRenderer[maximumGhosts]; + for (int i = 0; i < maximumGhosts; i++) { + GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer)); + pool[i] = go.GetComponent(); + go.SetActive(false); + go.hideFlags = GhostHideFlags; + } + + var skeletonAnimation = skeletonRenderer as Spine.Unity.IAnimationStateComponent; + if (skeletonAnimation != null) skeletonAnimation.AnimationState.Event += OnEvent; + } + + //SkeletonAnimation + /* + * Int Value: 0 sets ghostingEnabled to false, 1 sets ghostingEnabled to true + * Float Value: Values greater than 0 set the spawnRate equal the float value + * String Value: Pass RGBA hex color values in to set the color property. IE: "A0FF8BFF" + */ + void OnEvent (Spine.TrackEntry trackEntry, Spine.Event e) { + if (e.Data.Name.Equals("Ghosting", System.StringComparison.Ordinal)) { + ghostingEnabled = e.Int > 0; + if (e.Float > 0) + spawnRate = e.Float; + + if (!string.IsNullOrEmpty(e.stringValue)) + this.color = HexToColor(e.String); + } + } + + //SkeletonAnimator + //SkeletonAnimator or Mecanim based animations only support toggling ghostingEnabled. Be sure not to set anything other than the Int param in Spine or String will take priority. + void Ghosting (float val) { + ghostingEnabled = val > 0; + } + + void Update () { + if (!ghostingEnabled) + return; + + if (Time.time >= nextSpawnTime) { + GameObject go = pool[poolIndex].gameObject; + + Material[] materials = meshRenderer.sharedMaterials; + for (int i = 0; i < materials.Length; i++) { + var originalMat = materials[i]; + Material ghostMat; + if (!materialTable.ContainsKey(originalMat)) { + ghostMat = new Material(originalMat); + ghostMat.shader = ghostShader; + ghostMat.color = Color.white; + if (ghostMat.HasProperty("_TextureFade")) + ghostMat.SetFloat("_TextureFade", textureFade); + materialTable.Add(originalMat, ghostMat); + } else { + ghostMat = materialTable[originalMat]; + } + + materials[i] = ghostMat; + } + + var goTransform = go.transform; + goTransform.parent = transform; + + pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1); + + goTransform.localPosition = new Vector3(0f, 0f, zOffset); + goTransform.localRotation = Quaternion.identity; + goTransform.localScale = Vector3.one; + + goTransform.parent = null; + + poolIndex++; + + if (poolIndex == pool.Length) + poolIndex = 0; + + nextSpawnTime = Time.time + spawnRate; + } + } + + void OnDestroy () { + if (pool != null) { + for (int i = 0; i < maximumGhosts; i++) + if (pool[i] != null) pool[i].Cleanup(); + } + + foreach (var mat in materialTable.Values) + Destroy(mat); + } + + //based on UnifyWiki http://wiki.unity3d.com/index.php?title=HexConverter + static Color32 HexToColor (string hex) { + const System.Globalization.NumberStyles HexStyle = System.Globalization.NumberStyles.HexNumber; + + if (hex.Length < 6) + return Color.magenta; + + hex = hex.Replace("#", ""); + byte r = byte.Parse(hex.Substring(0, 2), HexStyle); + byte g = byte.Parse(hex.Substring(2, 2), HexStyle); + byte b = byte.Parse(hex.Substring(4, 2), HexStyle); + byte a = 0xFF; + if (hex.Length == 8) + a = byte.Parse(hex.Substring(6, 2), HexStyle); + + return new Color32(r, g, b, a); + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhost.cs.meta b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhost.cs.meta new file mode 100644 index 0000000..32cd44c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhost.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 02f2fa991881c6d419500ccc40ad443f +timeCreated: 1431858330 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: + - ghostShader: {fileID: 4800000, guid: 3873d4699ee8a4b4da8fa6b8c229b94d, type: 3} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs new file mode 100644 index 0000000..e9f86c8 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs @@ -0,0 +1,133 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using System.Collections; + +namespace Spine.Unity.Modules { + public class SkeletonGhostRenderer : MonoBehaviour { + + public float fadeSpeed = 10; + + Color32[] colors; + Color32 black = new Color32(0, 0, 0, 0); + MeshFilter meshFilter; + MeshRenderer meshRenderer; + + void Awake () { + meshRenderer = gameObject.AddComponent(); + meshFilter = gameObject.AddComponent(); + } + + public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingLayerID, int sortingOrder) { + StopAllCoroutines(); + + gameObject.SetActive(true); + meshRenderer.sharedMaterials = materials; + meshRenderer.sortingLayerID = sortingLayerID; + meshRenderer.sortingOrder = sortingOrder; + meshFilter.sharedMesh = Instantiate(mesh); + colors = meshFilter.sharedMesh.colors32; + + if ((color.a + color.r + color.g + color.b) > 0) { + for (int i = 0; i < colors.Length; i++) + colors[i] = color; + } + + fadeSpeed = speed; + + if (additive) + StartCoroutine(FadeAdditive()); + else + StartCoroutine(Fade()); + } + + IEnumerator Fade () { + Color32 c; + for (int t = 0; t < 500; t++) { + bool breakout = true; + for (int i = 0; i < colors.Length; i++) { + c = colors[i]; + if (c.a > 0) + breakout = false; + + colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed); + } + meshFilter.sharedMesh.colors32 = colors; + + if (breakout) + break; + + yield return null; + } + + Destroy(meshFilter.sharedMesh); + gameObject.SetActive(false); + } + + IEnumerator FadeAdditive () { + Color32 c; + Color32 black = this.black; + + for (int t = 0; t < 500; t++) { + + bool breakout = true; + for (int i = 0; i < colors.Length; i++) { + c = colors[i]; + black.a = c.a; + if (c.r > 0 || c.g > 0 || c.b > 0) + breakout = false; + + colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed); + } + + meshFilter.sharedMesh.colors32 = colors; + + if (breakout) + break; + yield return null; + } + + Destroy(meshFilter.sharedMesh); + + gameObject.SetActive(false); + } + + public void Cleanup () { + if (meshFilter != null && meshFilter.sharedMesh != null) + Destroy(meshFilter.sharedMesh); + + Destroy(gameObject); + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs.meta b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs.meta new file mode 100644 index 0000000..2f9fb1a --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 58e3a9b80754b7545a1dff4d8475b51f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll.meta b/Assets/Spine/spine-unity/Modules/Ragdoll.meta new file mode 100644 index 0000000..cb90625 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 90af663b37d994841b7ac03ae30fe2a9 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/Editor.meta b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor.meta new file mode 100644 index 0000000..6cac463 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 7220dc1e8d545e849a2eb63e8633349b +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs new file mode 100644 index 0000000..9a75edb --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs @@ -0,0 +1,38 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using UnityEditor; + +namespace Spine.Unity.Modules { + public class SkeletonRagdoll2DInspector {} +} diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta new file mode 100644 index 0000000..0848020 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdoll2DInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b6dd0b99faf3aeb4d803eb9989cb369c +timeCreated: 1431741936 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs new file mode 100644 index 0000000..c374f82 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs @@ -0,0 +1,47 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using UnityEditor; + +namespace Spine.Unity.Modules { + + public class SkeletonRagdollInspector : UnityEditor.Editor { + [CustomPropertyDrawer(typeof(SkeletonRagdoll.LayerFieldAttribute))] + public class LayerFieldPropertyDrawer : PropertyDrawer { + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + property.intValue = EditorGUI.LayerField(position, label, property.intValue); + } + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta new file mode 100644 index 0000000..7c478ae --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/Editor/SkeletonRagdollInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c95a670c56447c644a0f062e4cdd448e +timeCreated: 1431740230 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs new file mode 100644 index 0000000..d43fb19 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs @@ -0,0 +1,405 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Spine.Unity.Modules { + [RequireComponent(typeof(SkeletonRenderer))] + public class SkeletonRagdoll : MonoBehaviour { + static Transform parentSpaceHelper; + + #region Inspector + [Header("Hierarchy")] + [SpineBone] + public string startingBoneName = ""; + [SpineBone] + public List stopBoneNames = new List(); + + [Header("Parameters")] + public bool applyOnStart; + [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] + public bool disableIK = true; + public bool disableOtherConstraints = false; + [Space(18)] + [Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")] + public bool pinStartBone; + [Tooltip("Enable Collision between adjacent ragdoll elements (IE: Neck and Head)")] + public bool enableJointCollision; + public bool useGravity = true; + [Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")] + public float thickness = 0.125f; + [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] + public float rotationLimit = 20; + public float rootMass = 20; + [Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")] + [Range(0.01f, 1f)] + public float massFalloffFactor = 0.4f; + [Tooltip("The layer assigned to all of the rigidbody parts.")] + public int colliderLayer = 0; + [Range(0, 1)] + public float mix = 1; + #endregion + + ISkeletonAnimation targetSkeletonComponent; + Skeleton skeleton; + Dictionary boneTable = new Dictionary(); + Transform ragdollRoot; + public Rigidbody RootRigidbody { get; private set; } + public Bone StartingBone { get; private set; } + Vector3 rootOffset; + public Vector3 RootOffset { get { return this.rootOffset; } } + bool isActive; + public bool IsActive { get { return this.isActive; } } + + IEnumerator Start () { + if (parentSpaceHelper == null) { + parentSpaceHelper = (new GameObject("Parent Space Helper")).transform; + parentSpaceHelper.hideFlags = HideFlags.HideInHierarchy; + } + + targetSkeletonComponent = GetComponent() as ISkeletonAnimation; + if (targetSkeletonComponent == null) Debug.LogError("Attached Spine component does not implement ISkeletonAnimation. This script is not compatible."); + skeleton = targetSkeletonComponent.Skeleton; + + if (applyOnStart) { + yield return null; + Apply(); + } + } + + #region API + public Rigidbody[] RigidbodyArray { + get { + if (!isActive) + return new Rigidbody[0]; + + var rigidBodies = new Rigidbody[boneTable.Count]; + int i = 0; + foreach (Transform t in boneTable.Values) { + rigidBodies[i] = t.GetComponent(); + i++; + } + + return rigidBodies; + } + } + + public Vector3 EstimatedSkeletonPosition { + get { return RootRigidbody.position - rootOffset; } + } + + /// Instantiates the ragdoll simulation and applies its transforms to the skeleton. + public void Apply () { + isActive = true; + mix = 1; + + StartingBone = skeleton.FindBone(startingBoneName); + RecursivelyCreateBoneProxies(StartingBone); + + RootRigidbody = boneTable[StartingBone].GetComponent(); + RootRigidbody.isKinematic = pinStartBone; + RootRigidbody.mass = rootMass; + var boneColliders = new List(); + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + Transform parentTransform; + boneColliders.Add(t.GetComponent()); + if (b == StartingBone) { + ragdollRoot = new GameObject("RagdollRoot").transform; + ragdollRoot.SetParent(transform, false); + if (b == skeleton.RootBone) { // RagdollRoot is skeleton root. + ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b)); + } else { + ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent)); + } + parentTransform = ragdollRoot; + rootOffset = t.position - transform.position; + } else { + parentTransform = boneTable[b.Parent]; + } + + // Add joint and attach to parent. + var rbParent = parentTransform.GetComponent(); + if (rbParent != null) { + var joint = t.gameObject.AddComponent(); + joint.connectedBody = rbParent; + Vector3 localPos = parentTransform.InverseTransformPoint(t.position); + localPos.x *= 1; + joint.connectedAnchor = localPos; + joint.axis = Vector3.forward; + + joint.GetComponent().mass = joint.connectedBody.mass * massFalloffFactor; + joint.limits = new JointLimits { + min = -rotationLimit, + max = rotationLimit, + }; + joint.useLimits = true; + joint.enableCollision = enableJointCollision; + } + } + + // Ignore collisions among bones. + for (int x = 0; x < boneColliders.Count; x++) { + for (int y = 0; y < boneColliders.Count; y++) { + if (x == y) continue; + Physics.IgnoreCollision(boneColliders[x], boneColliders[y]); + } + } + + // Destroy existing override-mode SkeletonUtilityBones. + var utilityBones = GetComponentsInChildren(); + if (utilityBones.Length > 0) { + var destroyedUtilityBoneNames = new List(); + foreach (var ub in utilityBones) { + if (ub.mode == SkeletonUtilityBone.Mode.Override) { + destroyedUtilityBoneNames.Add(ub.gameObject.name); + Destroy(ub.gameObject); + } + } + if (destroyedUtilityBoneNames.Count > 0) { + string msg = "Destroyed Utility Bones: "; + for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) { + msg += destroyedUtilityBoneNames[i]; + if (i != destroyedUtilityBoneNames.Count - 1) { + msg += ","; + } + } + Debug.LogWarning(msg); + } + } + + // Disable skeleton constraints. + if (disableIK) { + var ikConstraints = skeleton.IkConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) + ikConstraints.Items[i].mix = 0; + } + + if (disableOtherConstraints) { + var transformConstraints = skeleton.transformConstraints; + for (int i = 0, n = transformConstraints.Count; i < n; i++) { + transformConstraints.Items[i].rotateMix = 0; + transformConstraints.Items[i].scaleMix = 0; + transformConstraints.Items[i].shearMix = 0; + transformConstraints.Items[i].translateMix = 0; + } + + var pathConstraints = skeleton.pathConstraints; + for (int i = 0, n = pathConstraints.Count; i < n; i++) { + pathConstraints.Items[i].rotateMix = 0; + pathConstraints.Items[i].translateMix = 0; + } + } + + targetSkeletonComponent.UpdateWorld += UpdateSpineSkeleton; + } + + /// Transitions the mix value from the current value to a target value. + public Coroutine SmoothMix (float target, float duration) { + return StartCoroutine(SmoothMixCoroutine(target, duration)); + } + + IEnumerator SmoothMixCoroutine (float target, float duration) { + float startTime = Time.time; + float startMix = mix; + while (mix > 0) { + skeleton.SetBonesToSetupPose(); + mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); + yield return null; + } + } + + /// Set the transform world position while preserving the ragdoll parts world position. + public void SetSkeletonPosition (Vector3 worldPosition) { + if (!isActive) { + Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!"); + return; + } + + Vector3 offset = worldPosition - transform.position; + transform.position = worldPosition; + foreach (Transform t in boneTable.Values) + t.position -= offset; + + UpdateSpineSkeleton(null); + skeleton.UpdateWorldTransform(); + } + + /// Removes the ragdoll instance and effect from the animated skeleton. + public void Remove () { + isActive = false; + foreach (var t in boneTable.Values) + Destroy(t.gameObject); + + Destroy(ragdollRoot.gameObject); + + boneTable.Clear(); + targetSkeletonComponent.UpdateWorld -= UpdateSpineSkeleton; + } + + public Rigidbody GetRigidbody (string boneName) { + var bone = skeleton.FindBone(boneName); + return (bone != null && boneTable.ContainsKey(bone)) ? boneTable[bone].GetComponent() : null; + } + #endregion + + void RecursivelyCreateBoneProxies (Bone b) { + string boneName = b.data.name; + if (stopBoneNames.Contains(boneName)) + return; + + var boneGameObject = new GameObject(boneName); + boneGameObject.layer = colliderLayer; + Transform t = boneGameObject.transform; + boneTable.Add(b, t); + + t.parent = transform; + t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.shearX); + t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1); + + // MITCH: You left "todo: proper ragdoll branching" + var colliders = AttachBoundingBoxRagdollColliders(b); + if (colliders.Count == 0) { + float length = b.Data.Length; + if (length == 0) { + var ball = boneGameObject.AddComponent(); + ball.radius = thickness * 0.5f; + } else { + var box = boneGameObject.AddComponent(); + box.size = new Vector3(length, thickness, thickness); + box.center = new Vector3(length * 0.5f, 0); + } + } + var rb = boneGameObject.AddComponent(); + rb.constraints = RigidbodyConstraints.FreezePositionZ; + + foreach (Bone child in b.Children) + RecursivelyCreateBoneProxies(child); + } + + void UpdateSpineSkeleton (ISkeletonAnimation skeletonRenderer) { + bool flipX = skeleton.flipX; + bool flipY = skeleton.flipY; + bool flipXOR = flipX ^ flipY; + bool flipOR = flipX || flipY; + + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + bool isStartingBone = b == StartingBone; + Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[b.Parent]; + Vector3 parentTransformWorldPosition = parentTransform.position; + Quaternion parentTransformWorldRotation = parentTransform.rotation; + + parentSpaceHelper.position = parentTransformWorldPosition; + parentSpaceHelper.rotation = parentTransformWorldRotation; + parentSpaceHelper.localScale = parentTransform.localScale; + + Vector3 boneWorldPosition = t.position; + Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right); + + Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition); + float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; + + if (flipOR) { + if (isStartingBone) { + if (flipX) boneLocalPosition.x *= -1f; + if (flipY) boneLocalPosition.y *= -1f; + + boneLocalRotation = boneLocalRotation * (flipXOR ? -1f : 1f); + if (flipX) boneLocalRotation += 180; + } else { + if (flipXOR) { + boneLocalRotation *= -1f; + boneLocalPosition.y *= -1f; // wtf?? + } + } + } + + b.x = Mathf.Lerp(b.x, boneLocalPosition.x, mix); + b.y = Mathf.Lerp(b.y, boneLocalPosition.y, mix); + b.rotation = Mathf.Lerp(b.rotation, boneLocalRotation, mix); + //b.AppliedRotation = Mathf.Lerp(b.AppliedRotation, boneLocalRotation, mix); + } + } + + List AttachBoundingBoxRagdollColliders (Bone b) { + const string AttachmentNameMarker = "ragdoll"; + var colliders = new List(); + + Transform t = boneTable[b]; + GameObject go = t.gameObject; + var skin = skeleton.Skin ?? skeleton.Data.DefaultSkin; + + var attachments = new List(); + foreach (Slot s in skeleton.Slots) { + if (s.Bone == b) { + skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); + foreach (var a in attachments) { + var bbAttachment = a as BoundingBoxAttachment; + if (bbAttachment != null) { + if (!a.Name.ToLower().Contains(AttachmentNameMarker)) + continue; + + var bbCollider = go.AddComponent(); + var bounds = SkeletonUtility.GetBoundingBoxBounds(bbAttachment, thickness); + bbCollider.center = bounds.center; + bbCollider.size = bounds.size; + colliders.Add(bbCollider); + } + } + } + } + + return colliders; + } + + static float GetPropagatedRotation (Bone b) { + Bone parent = b.Parent; + float a = b.AppliedRotation; + while (parent != null) { + a += parent.AppliedRotation; + parent = parent.parent; + } + return a; + } + + public class LayerFieldAttribute : PropertyAttribute {} + } + +} diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs.meta b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs.meta new file mode 100644 index 0000000..f19d680 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 373527d2bf3351348b9fcc499ce9ea23 +timeCreated: 1430552693 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs new file mode 100644 index 0000000..0e9c0ab --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs @@ -0,0 +1,410 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace Spine.Unity.Modules { + [RequireComponent(typeof(SkeletonRenderer))] + public class SkeletonRagdoll2D : MonoBehaviour { + static Transform parentSpaceHelper; + + #region Inspector + [Header("Hierarchy")] + [SpineBone] + public string startingBoneName = ""; + [SpineBone] + public List stopBoneNames = new List(); + + [Header("Parameters")] + public bool applyOnStart; + [Tooltip("Warning! You will have to re-enable and tune mix values manually if attempting to remove the ragdoll system.")] + public bool disableIK = true; + public bool disableOtherConstraints = false; + [Space] + [Tooltip("Set RootRigidbody IsKinematic to true when Apply is called.")] + public bool pinStartBone; + public float gravityScale = 1; + [Tooltip("If no BoundingBox Attachment is attached to a bone, this becomes the default Width or Radius of a Bone's ragdoll Rigidbody")] + public float thickness = 0.125f; + [Tooltip("Default rotational limit value. Min is negative this value, Max is this value.")] + public float rotationLimit = 20; + public float rootMass = 20; + [Tooltip("If your ragdoll seems unstable or uneffected by limits, try lowering this value.")] + [Range(0.01f, 1f)] + public float massFalloffFactor = 0.4f; + [Tooltip("The layer assigned to all of the rigidbody parts.")] + [SkeletonRagdoll.LayerField] + public int colliderLayer = 0; + [Range(0, 1)] + public float mix = 1; + #endregion + + ISkeletonAnimation targetSkeletonComponent; + Skeleton skeleton; + Dictionary boneTable = new Dictionary(); + Transform ragdollRoot; + public Rigidbody2D RootRigidbody { get; private set; } + public Bone StartingBone { get; private set; } + Vector2 rootOffset; + public Vector3 RootOffset { get { return this.rootOffset; } } + bool isActive; + public bool IsActive { get { return this.isActive; } } + + IEnumerator Start () { + if (parentSpaceHelper == null) { + parentSpaceHelper = (new GameObject("Parent Space Helper")).transform; + } + + targetSkeletonComponent = GetComponent() as ISkeletonAnimation; + if (targetSkeletonComponent == null) Debug.LogError("Attached Spine component does not implement ISkeletonAnimation. This script is not compatible."); + skeleton = targetSkeletonComponent.Skeleton; + + if (applyOnStart) { + yield return null; + Apply(); + } + } + + #region API + public Rigidbody2D[] RigidbodyArray { + get { + if (!isActive) + return new Rigidbody2D[0]; + + var rigidBodies = new Rigidbody2D[boneTable.Count]; + int i = 0; + foreach (Transform t in boneTable.Values) { + rigidBodies[i] = t.GetComponent(); + i++; + } + + return rigidBodies; + } + } + + public Vector3 EstimatedSkeletonPosition { + get { return this.RootRigidbody.position - rootOffset; } + } + + /// Instantiates the ragdoll simulation and applies its transforms to the skeleton. + public void Apply () { + isActive = true; + mix = 1; + + Bone startingBone = this.StartingBone = skeleton.FindBone(startingBoneName); + RecursivelyCreateBoneProxies(startingBone); + + RootRigidbody = boneTable[startingBone].GetComponent(); + RootRigidbody.isKinematic = pinStartBone; + RootRigidbody.mass = rootMass; + var boneColliders = new List(); + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + Transform parentTransform; + boneColliders.Add(t.GetComponent()); + if (b == startingBone) { + ragdollRoot = new GameObject("RagdollRoot").transform; + ragdollRoot.SetParent(transform, false); + if (b == skeleton.RootBone) { // RagdollRoot is skeleton root. + ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b)); + } else { + ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0); + ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent)); + } + parentTransform = ragdollRoot; + rootOffset = t.position - transform.position; + } else { + parentTransform = boneTable[b.Parent]; + } + + // Add joint and attach to parent. + var rbParent = parentTransform.GetComponent(); + if (rbParent != null) { + var joint = t.gameObject.AddComponent(); + joint.connectedBody = rbParent; + Vector3 localPos = parentTransform.InverseTransformPoint(t.position); + joint.connectedAnchor = localPos; + + joint.GetComponent().mass = joint.connectedBody.mass * massFalloffFactor; + joint.limits = new JointAngleLimits2D { + min = -rotationLimit, + max = rotationLimit + }; + joint.useLimits = true; + } + } + + // Ignore collisions among bones. + for (int x = 0; x < boneColliders.Count; x++) { + for (int y = 0; y < boneColliders.Count; y++) { + if (x == y) continue; + Physics2D.IgnoreCollision(boneColliders[x], boneColliders[y]); + } + } + + // Destroy existing override-mode SkeletonUtility bones. + var utilityBones = GetComponentsInChildren(); + if (utilityBones.Length > 0) { + var destroyedUtilityBoneNames = new List(); + foreach (var ub in utilityBones) { + if (ub.mode == SkeletonUtilityBone.Mode.Override) { + destroyedUtilityBoneNames.Add(ub.gameObject.name); + Destroy(ub.gameObject); + } + } + if (destroyedUtilityBoneNames.Count > 0) { + string msg = "Destroyed Utility Bones: "; + for (int i = 0; i < destroyedUtilityBoneNames.Count; i++) { + msg += destroyedUtilityBoneNames[i]; + if (i != destroyedUtilityBoneNames.Count - 1) { + msg += ","; + } + } + Debug.LogWarning(msg); + } + } + + // Disable skeleton constraints. + if (disableIK) { + var ikConstraints = skeleton.IkConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) + ikConstraints.Items[i].mix = 0; + } + + if (disableOtherConstraints) { + var transformConstraints = skeleton.transformConstraints; + for (int i = 0, n = transformConstraints.Count; i < n; i++) { + transformConstraints.Items[i].rotateMix = 0; + transformConstraints.Items[i].scaleMix = 0; + transformConstraints.Items[i].shearMix = 0; + transformConstraints.Items[i].translateMix = 0; + } + + var pathConstraints = skeleton.pathConstraints; + for (int i = 0, n = pathConstraints.Count; i < n; i++) { + pathConstraints.Items[i].rotateMix = 0; + pathConstraints.Items[i].translateMix = 0; + } + } + + targetSkeletonComponent.UpdateWorld += UpdateSpineSkeleton; + } + + /// Transitions the mix value from the current value to a target value. + public Coroutine SmoothMix (float target, float duration) { + return StartCoroutine(SmoothMixCoroutine(target, duration)); + } + + IEnumerator SmoothMixCoroutine (float target, float duration) { + float startTime = Time.time; + float startMix = mix; + while (mix > 0) { + skeleton.SetBonesToSetupPose(); + mix = Mathf.SmoothStep(startMix, target, (Time.time - startTime) / duration); + yield return null; + } + } + + /// Set the transform world position while preserving the ragdoll parts world position. + public void SetSkeletonPosition (Vector3 worldPosition) { + if (!isActive) { + Debug.LogWarning("Can't call SetSkeletonPosition while Ragdoll is not active!"); + return; + } + + Vector3 offset = worldPosition - transform.position; + transform.position = worldPosition; + foreach (Transform t in boneTable.Values) + t.position -= offset; + + UpdateSpineSkeleton(null); + skeleton.UpdateWorldTransform(); + } + + /// Removes the ragdoll instance and effect from the animated skeleton. + public void Remove () { + isActive = false; + foreach (var t in boneTable.Values) + Destroy(t.gameObject); + + Destroy(ragdollRoot.gameObject); + boneTable.Clear(); + targetSkeletonComponent.UpdateWorld -= UpdateSpineSkeleton; + } + + public Rigidbody2D GetRigidbody (string boneName) { + var bone = skeleton.FindBone(boneName); + return (bone != null && boneTable.ContainsKey(bone)) ? boneTable[bone].GetComponent() : null; + } + #endregion + + /// Generates the ragdoll simulation's Transform and joint setup. + void RecursivelyCreateBoneProxies (Bone b) { + string boneName = b.data.name; + if (stopBoneNames.Contains(boneName)) + return; + + var boneGameObject = new GameObject(boneName); + boneGameObject.layer = this.colliderLayer; + Transform t = boneGameObject.transform; + boneTable.Add(b, t); + + t.parent = transform; + t.localPosition = new Vector3(b.WorldX, b.WorldY, 0); + t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.shearX); + t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 0); + + // MITCH: You left "todo: proper ragdoll branching" + var colliders = AttachBoundingBoxRagdollColliders(b, boneGameObject, skeleton, this.gravityScale); + if (colliders.Count == 0) { + float length = b.data.length; + if (length == 0) { + var circle = boneGameObject.AddComponent(); + circle.radius = thickness * 0.5f; + } else { + var box = boneGameObject.AddComponent(); + box.size = new Vector2(length, thickness); + box.offset = new Vector2(length * 0.5f, 0); // box.center in UNITY_4 + } + } + + var rb = boneGameObject.GetComponent(); + if (rb == null) rb = boneGameObject.AddComponent(); + rb.gravityScale = this.gravityScale; + + foreach (Bone child in b.Children) + RecursivelyCreateBoneProxies(child); + } + + /// Performed every skeleton animation update to translate Unity Transforms positions into Spine bone transforms. + void UpdateSpineSkeleton (ISkeletonAnimation animatedSkeleton) { + bool flipX = skeleton.flipX; + bool flipY = skeleton.flipY; + bool flipXOR = flipX ^ flipY; + bool flipOR = flipX || flipY; + var startingBone = this.StartingBone; + + foreach (var pair in boneTable) { + var b = pair.Key; + var t = pair.Value; + bool isStartingBone = (b == startingBone); + Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[b.Parent]; + Vector3 parentTransformWorldPosition = parentTransform.position; + Quaternion parentTransformWorldRotation = parentTransform.rotation; + + parentSpaceHelper.position = parentTransformWorldPosition; + parentSpaceHelper.rotation = parentTransformWorldRotation; + parentSpaceHelper.localScale = parentTransform.localScale; + + Vector3 boneWorldPosition = t.position; + Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right); + + Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition); + float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg; + if (flipOR) { + if (isStartingBone) { + if (flipX) boneLocalPosition.x *= -1f; + if (flipY) boneLocalPosition.y *= -1f; + + boneLocalRotation = boneLocalRotation * (flipXOR ? -1f : 1f); + if (flipX) boneLocalRotation += 180; + } else { + if (flipXOR) { + boneLocalRotation *= -1f; + boneLocalPosition.y *= -1f; // wtf?? + } + } + } + + b.x = Mathf.Lerp(b.x, boneLocalPosition.x, mix); + b.y = Mathf.Lerp(b.y, boneLocalPosition.y, mix); + b.rotation = Mathf.Lerp(b.rotation, boneLocalRotation, mix); + //b.AppliedRotation = Mathf.Lerp(b.AppliedRotation, boneLocalRotation, mix); + } + } + + static List AttachBoundingBoxRagdollColliders (Bone b, GameObject go, Skeleton skeleton, float gravityScale) { + const string AttachmentNameMarker = "ragdoll"; + var colliders = new List(); + var skin = skeleton.Skin ?? skeleton.Data.DefaultSkin; + + var attachments = new List(); + foreach (Slot s in skeleton.Slots) { + if (s.bone == b) { + skin.FindAttachmentsForSlot(skeleton.Slots.IndexOf(s), attachments); + foreach (var a in attachments) { + var bbAttachment = a as BoundingBoxAttachment; + if (bbAttachment != null) { + if (!a.Name.ToLower().Contains(AttachmentNameMarker)) + continue; + + var bbCollider = SkeletonUtility.AddBoundingBoxAsComponent(bbAttachment, s, go, isTrigger: false, isKinematic: false, gravityScale: gravityScale); + colliders.Add(bbCollider); + } + } + } + } + + return colliders; + } + + static float GetPropagatedRotation (Bone b) { + Bone parent = b.Parent; + float a = b.AppliedRotation; + while (parent != null) { + a += parent.AppliedRotation; + parent = parent.parent; + } + return a; + } + + static Vector3 FlipScale (bool flipX, bool flipY) { + return new Vector3(flipX ? -1f : 1f, flipY ? -1f : 1f, 1f); + } + + #if UNITY_EDITOR + void OnDrawGizmosSelected () { + if (isActive) { + Gizmos.DrawWireSphere(transform.position, thickness * 1.2f); + Vector3 newTransformPos = RootRigidbody.position - rootOffset; + Gizmos.DrawLine(transform.position, newTransformPos); + Gizmos.DrawWireSphere(newTransformPos, thickness * 1.2f); + } + } + #endif + } + +} diff --git a/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs.meta b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs.meta new file mode 100644 index 0000000..ed29795 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Ragdoll/SkeletonRagdoll2D.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e74a49a26242a214d9084fde00bfe3ab +timeCreated: 1431497383 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders.meta b/Assets/Spine/spine-unity/Modules/Shaders.meta new file mode 100644 index 0000000..6cf9868 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3e6a65e2576c5b74dabb05c3d3fc2ae4 +folderAsset: yes +timeCreated: 1479258132 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader new file mode 100644 index 0000000..262b90e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader @@ -0,0 +1,57 @@ +// - Unlit + no shadow +// - Premultiplied Alpha Blending (One OneMinusSrcAlpha) +// - Double-sided, no depth + +Shader "Spine/Skeleton Fill" { + Properties { + _FillColor ("FillColor", Color) = (1,1,1,1) + _FillPhase ("FillPhase", Range(0, 1)) = 0 + [NoScaleOffset]_MainTex ("MainTex", 2D) = "white" {} + } + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } + Blend One OneMinusSrcAlpha + Cull Off + ZWrite Off + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + float4 _FillColor; + float _FillPhase; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o = (VertexOutput)0; + o.uv = v.uv; + o.vertexColor = v.vertexColor; + o.pos = UnityObjectToClipPos(v.vertex); // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 rawColor = tex2D(_MainTex,i.uv); + float finalAlpha = (rawColor.a * i.vertexColor.a); + float3 finalColor = lerp((rawColor.rgb * i.vertexColor.rgb), (_FillColor.rgb * finalAlpha), _FillPhase); // make sure to PMA _FillColor. + return fixed4(finalColor, finalAlpha); + } + ENDCG + } + } + FallBack "Diffuse" +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader.meta new file mode 100644 index 0000000..c71933e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 45495790b394f894a967dbf44489b57b +timeCreated: 1492385797 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader new file mode 100644 index 0000000..79bacb5 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader @@ -0,0 +1,100 @@ +// Spine/Skeleton Tint +// - Two color tint +// - unlit +// - Premultiplied alpha blending +// - No depth, no backface culling, no fog. + +Shader "Spine/Skeleton Tint" { + Properties { + _Color ("Tint Color", Color) = (1,1,1,1) + _Black ("Black Point", Color) = (0,0,0,0) + [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {} + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } + + Fog { Mode Off } + Cull Off + ZWrite Off + Blend One OneMinusSrcAlpha + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + float4 _Color; + float4 _Black; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o; + o.pos = UnityObjectToClipPos(v.vertex); // replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + o.uv = v.uv; + o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 texColor = tex2D(_MainTex, i.uv); + return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * _Black.rgb * texColor.a*_Color.a*i.vertexColor.a), 0); + } + ENDCG + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + ZWrite On + ZTest LEqual + + Fog { Mode Off } + Cull Off + Lighting Off + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + sampler2D _MainTex; + fixed _Cutoff; + + struct VertexOutput { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + VertexOutput vert (appdata_base v) { + VertexOutput o; + o.uv = v.texcoord; + TRANSFER_SHADOW_CASTER(o) + return o; + } + + float4 frag (VertexOutput i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader.meta new file mode 100644 index 0000000..1efeec6 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 522f03282fd79be47b306e2ef4b593fd +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite.meta new file mode 100644 index 0000000..f997503 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a831a8ed72a588a48b2fb892e7f37371 +folderAsset: yes +timeCreated: 1479419399 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes.meta new file mode 100644 index 0000000..009f684 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 4b6fb48f295cd8248a7566315212a3c2 +folderAsset: yes +timeCreated: 1494092464 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderMaths.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderMaths.cginc new file mode 100644 index 0000000..d79c013 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderMaths.cginc @@ -0,0 +1,70 @@ +#ifndef SHADER_MATHS_INCLUDED +#define SHADER_MATHS_INCLUDED + +#include "UnityCG.cginc" + +//////////////////////////////////////// +// Maths functions +// + +inline half3 safeNormalize(half3 inVec) +{ + half dp3 = max(0.001f, dot(inVec, inVec)); + return inVec * rsqrt(dp3); +} + +inline float dotClamped(float3 a, float3 b) +{ + #if (SHADER_TARGET < 30 || defined(SHADER_API_PS3)) + return saturate(dot(a, b)); + #else + return max(0.0h, dot(a, b)); + #endif +} + +inline float oneDividedBy(float value) +{ + //Catches NANs + float sign_value = sign(value); + float sign_value_squared = sign_value*sign_value; + return sign_value_squared / ( value + sign_value_squared - 1.0); +} + +inline half pow5 (half x) +{ + return x*x*x*x*x; +} + +inline float4 quat_from_axis_angle(float3 axis, float angleRadians) +{ + float4 qr; + float half_angle = (angleRadians * 0.5); + qr.x = axis.x * sin(half_angle); + qr.y = axis.y * sin(half_angle); + qr.z = axis.z * sin(half_angle); + qr.w = cos(half_angle); + return qr; +} + +inline float3 rotate_vertex_position(float3 position, float3 axis, float angleRadians) +{ + float4 q = quat_from_axis_angle(axis, angleRadians); + float3 v = position.xyz; + return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); +} + +float3 EncodeFloatRGB(float value) +{ + const float max24int = 256*256*256-1; + float3 decomp = floor( value * float3( max24int/(256*256), max24int/256, max24int ) ) / 255.0; + decomp.z -= decomp.y * 256.0; + decomp.y -= decomp.x * 256.0; + return decomp; +} + +float DecodeFloatRGB(float3 decomp) +{ + return dot( decomp.xyz, float3( 255.0/256, 255.0/(256*256), 255.0/(256*256*256) ) ); +} + +#endif // SHADER_MATHS_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderMaths.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderMaths.cginc.meta new file mode 100644 index 0000000..348f3ac --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderMaths.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e1de23de2025abe4a84ff2edd3f24491 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderShared.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderShared.cginc new file mode 100644 index 0000000..75284d2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderShared.cginc @@ -0,0 +1,474 @@ +// Upgrade NOTE: upgraded instancing buffer 'PerDrawSprite' to new syntax. + +#ifndef SHADER_SHARED_INCLUDED +#define SHADER_SHARED_INCLUDED + +#include "UnityCG.cginc" + +#ifdef UNITY_INSTANCING_ENABLED + + UNITY_INSTANCING_BUFFER_START(PerDrawSprite) + // SpriteRenderer.Color while Non-Batched/Instanced. + fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE]; + // this could be smaller but that's how bit each entry is regardless of type + float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE]; + UNITY_INSTANCING_BUFFER_END(PerDrawSprite) + + #define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID] + #define _Flip unity_SpriteFlipArray[unity_InstanceID] + +#endif // instancing + +CBUFFER_START(UnityPerDrawSprite) +#ifndef UNITY_INSTANCING_ENABLED + fixed4 _RendererColor; + float4 _Flip; +#endif + float _EnableExternalAlpha; +CBUFFER_END + +//////////////////////////////////////// +// Space functions +// + +inline float4 calculateWorldPos(float4 vertex) +{ + return mul(unity_ObjectToWorld, vertex); +} + +inline float4 calculateLocalPos(float4 vertex) +{ +#ifdef UNITY_INSTANCING_ENABLED + vertex.xy *= _Flip.xy; +#endif + + float4 pos = UnityObjectToClipPos(vertex); + +#ifdef PIXELSNAP_ON + pos = UnityPixelSnap(pos); +#endif + + return pos; +} + +inline half3 calculateWorldNormal(float3 normal) +{ + return UnityObjectToWorldNormal(normal); +} + +//////////////////////////////////////// +// Normal map functions +// + +#if defined(_NORMALMAP) + +uniform sampler2D _BumpMap; +uniform half _BumpScale; + +half3 UnpackScaleNormal(half4 packednormal, half bumpScale) +{ + #if defined(UNITY_NO_DXT5nm) + return packednormal.xyz * 2 - 1; + #else + half3 normal; + normal.xy = (packednormal.wy * 2 - 1); + #if (SHADER_TARGET >= 30) + // SM2.0: instruction count limitation + // SM2.0: normal scaler is not supported + normal.xy *= bumpScale; + #endif + normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy))); + return normal; + #endif +} + + +inline half3 calculateWorldTangent(float4 tangent) +{ + return UnityObjectToWorldDir(tangent); +} + +inline half3 calculateWorldBinormal(half3 normalWorld, half3 tangentWorld, float tangentSign) +{ + //When calculating the binormal we have to flip it when the mesh is scaled negatively. + //Normally this would just be unity_WorldTransformParams.w but this isn't set correctly by Unity for its SpriteRenderer meshes so get from objectToWorld matrix scale instead. + half worldTransformSign = sign(unity_ObjectToWorld[0][0] * unity_ObjectToWorld[1][1] * unity_ObjectToWorld[2][2]); + half sign = tangentSign * worldTransformSign; + return cross(normalWorld, tangentWorld) * sign; +} + +inline half3 calculateNormalFromBumpMap(float2 texUV, half3 tangentWorld, half3 binormalWorld, half3 normalWorld) +{ + half3 localNormal = UnpackScaleNormal(tex2D(_BumpMap, texUV), _BumpScale); + half3x3 rotation = half3x3(tangentWorld, binormalWorld, normalWorld); + half3 normal = normalize(mul(localNormal, rotation)); + return normal; +} + +#endif // _NORMALMAP + +//////////////////////////////////////// +// Blending functions +// + +inline fixed4 calculateLitPixel(fixed4 texureColor, fixed4 color, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHABLEND_ON) + //Normal Alpha + finalPixel.a = texureColor.a * color.a; + finalPixel.rgb = texureColor.rgb * color.rgb * (lighting * finalPixel.a); +#elif defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor * color; + finalPixel.rgb *= lighting * color.a; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = texureColor * color; + finalPixel.rgb *= lighting; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * color.rgb * lighting * 2.0f; + finalPixel.a = color.a * texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f * color; + finalPixel.rgb *= lighting * color.a; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = texureColor * color; + finalPixel.rgb *= lighting * finalPixel.a; +#else + //Opaque + finalPixel.a = 1; + finalPixel.rgb = texureColor.rgb * color.rgb * lighting; +#endif + + return finalPixel; +} + +inline fixed4 calculateLitPixel(fixed4 texureColor, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHABLEND_ON) + //Normal Alpha + finalPixel.a = texureColor.a; + finalPixel.rgb = texureColor.rgb * (lighting * finalPixel.a); +#elif defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor; + finalPixel.rgb *= lighting; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = texureColor; + finalPixel.rgb *= lighting; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * lighting * 2.0f; + finalPixel.a = texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f; + finalPixel.rgb *= lighting; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = texureColor; + finalPixel.rgb *= lighting * finalPixel.a; +#else + //Opaque + finalPixel.a = 1; + finalPixel.rgb = texureColor.rgb * lighting; +#endif + + return finalPixel; +} + +inline fixed4 calculateAdditiveLitPixel(fixed4 texureColor, fixed4 color, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHABLEND_ON) || defined(_MULTIPLYBLEND) || defined(_MULTIPLYBLEND_X2) || defined(_ADDITIVEBLEND) || defined(_ADDITIVEBLEND_SOFT) + //Normal Alpha, Additive and Multiply modes + finalPixel.rgb = (texureColor.rgb * lighting * color.rgb) * (texureColor.a * color.a); + finalPixel.a = 1.0; +#elif defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel.rgb = texureColor.rgb * lighting * color.rgb * color.a; + finalPixel.a = 1.0; +#else + //Opaque + finalPixel.rgb = texureColor.rgb * lighting * color.rgb; + finalPixel.a = 1.0; +#endif + + return finalPixel; +} + +inline fixed4 calculateAdditiveLitPixel(fixed4 texureColor, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHABLEND_ON) || defined(_MULTIPLYBLEND) || defined(_MULTIPLYBLEND_X2) || defined(_ADDITIVEBLEND) || defined(_ADDITIVEBLEND_SOFT) + //Normal Alpha, Additive and Multiply modes + finalPixel.rgb = (texureColor.rgb * lighting) * texureColor.a; + finalPixel.a = 1.0; +#else + //Pre multiplied alpha and Opaque + finalPixel.rgb = texureColor.rgb * lighting; + finalPixel.a = 1.0; +#endif + + return finalPixel; +} + +inline fixed4 calculatePixel(fixed4 texureColor, fixed4 color) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHABLEND_ON) + //Normal Alpha + finalPixel.a = texureColor.a * color.a; + finalPixel.rgb = (texureColor.rgb * color.rgb) * finalPixel.a; +#elif defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor * color; + finalPixel.rgb *= color.a; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = color * texureColor; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * color.rgb * 2.0f; + finalPixel.a = color.a * texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f * color; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = color * texureColor; + finalPixel.rgb *= finalPixel.a; +#else + //Opaque + finalPixel.a = 1; + finalPixel.rgb = texureColor.rgb * color.rgb; +#endif + + return finalPixel; +} + +inline fixed4 calculatePixel(fixed4 texureColor) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHABLEND_ON) + //Normal Alpha + finalPixel.a = texureColor.a; + finalPixel.rgb = texureColor.rgb * finalPixel.a; +#elif defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = texureColor; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * 2.0f; + finalPixel.a = texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = texureColor; + finalPixel.rgb *= finalPixel.a; +#else + //Opaque + finalPixel.a = 1; + finalPixel.rgb = texureColor.rgb; +#endif + + return finalPixel; +} + +//////////////////////////////////////// +// Alpha Clipping +// + +#if defined(_ALPHA_CLIP) + +uniform fixed _Cutoff; + +#define ALPHA_CLIP(pixel, color) clip((pixel.a * color.a) - _Cutoff); + +#else + +#define ALPHA_CLIP(pixel, color) + +#endif + +//////////////////////////////////////// +// Color functions +// + +uniform fixed4 _Color; + +inline fixed4 calculateVertexColor(fixed4 color) +{ + return color * _Color; +} + +#if defined(_COLOR_ADJUST) + +uniform float _Hue; +uniform float _Saturation; +uniform float _Brightness; +uniform fixed4 _OverlayColor; + +float3 rgb2hsv(float3 c) +{ + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +float3 hsv2rgb(float3 c) +{ + c = float3(c.x, clamp(c.yz, 0.0, 1.0)); + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +inline fixed4 adjustColor(fixed4 color) +{ + float3 hsv = rgb2hsv(color.rgb); + + hsv.x += _Hue; + hsv.y *= _Saturation; + hsv.z *= _Brightness; + + color.rgb = hsv2rgb(hsv); + + return color; +} + +#define COLORISE(pixel) pixel.rgb = lerp(pixel.rgb, _OverlayColor.rgb, _OverlayColor.a * pixel.a); +#define COLORISE_ADDITIVE(pixel) pixel.rgb = ((1.0-_OverlayColor.a) * pixel.rgb); + +#else // !_COLOR_ADJUST + +#define COLORISE(pixel) +#define COLORISE_ADDITIVE(pixel) + +#endif // !_COLOR_ADJUST + +//////////////////////////////////////// +// Fog +// + +#if defined(_FOG) && (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)) + +inline fixed4 applyFog(fixed4 pixel, float1 fogCoord) +{ +#if defined(_ADDITIVEBLEND) || defined(_ADDITIVEBLEND_SOFT) + //In additive mode blend from clear to black based on luminance + float luminance = pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11; + fixed4 fogColor = lerp(fixed4(0,0,0,0), fixed4(0,0,0,1), luminance); +#elif defined(_MULTIPLYBLEND) + //In multiplied mode fade to white based on inverse luminance + float luminance = pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11; + fixed4 fogColor = lerp(fixed4(1,1,1,1), fixed4(0,0,0,0), luminance); +#elif defined(_MULTIPLYBLEND_X2) + //In multipliedx2 mode fade to grey based on inverse luminance + float luminance = pixel.r * 0.3 + pixel.g * 0.59 + pixel.b * 0.11; + fixed4 fogColor = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), fixed4(0,0,0,0), luminance); +#elif defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON) + //In alpha blended modes blend to fog color based on pixel alpha + fixed4 fogColor = lerp(fixed4(0,0,0,0), unity_FogColor, pixel.a); +#else + //In opaque mode just return fog color; + fixed4 fogColor = unity_FogColor; +#endif + + UNITY_APPLY_FOG_COLOR(fogCoord, pixel, fogColor); + + return pixel; +} + +#define APPLY_FOG(pixel, input) pixel = applyFog(pixel, input.fogCoord); + +#define APPLY_FOG_ADDITIVE(pixel, input) \ + UNITY_APPLY_FOG_COLOR(input.fogCoord, pixel.rgb, fixed4(0,0,0,0)); // fog towards black in additive pass + +#else + +#define APPLY_FOG(pixel, input) +#define APPLY_FOG_ADDITIVE(pixel, input) + +#endif + +//////////////////////////////////////// +// Texture functions +// + +uniform sampler2D _MainTex; + +#if ETC1_EXTERNAL_ALPHA +//External alpha texture for ETC1 compression +uniform sampler2D _AlphaTex; +#endif //ETC1_EXTERNAL_ALPHA + +#if _TEXTURE_BLEND +uniform sampler2D _BlendTex; +uniform float _BlendAmount; + +inline fixed4 calculateBlendedTexturePixel(float2 texcoord) +{ + return (1.0-_BlendAmount) * tex2D(_MainTex, texcoord) + _BlendAmount * tex2D(_BlendTex, texcoord); +} +#endif // _TEXTURE_BLEND + +inline fixed4 calculateTexturePixel(float2 texcoord) +{ + fixed4 pixel; + +#if _TEXTURE_BLEND + pixel = calculateBlendedTexturePixel(texcoord); +#else + pixel = tex2D(_MainTex, texcoord); +#endif // !_TEXTURE_BLEND + +#if ETC1_EXTERNAL_ALPHA + fixed4 alpha = tex2D (_AlphaTex, texcoord); + pixel.a = lerp (pixel.a, alpha.r, _EnableExternalAlpha); +#endif + +#if defined(_COLOR_ADJUST) + pixel = adjustColor(pixel); +#endif // _COLOR_ADJUST + + return pixel; +} + +uniform fixed4 _MainTex_ST; + +inline float2 calculateTextureCoord(float4 texcoord) +{ + return TRANSFORM_TEX(texcoord, _MainTex); +} + +#endif // SHADER_SHARED_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderShared.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderShared.cginc.meta new file mode 100644 index 0000000..7e1ddf1 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/ShaderShared.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: c18c5cab567666f4d8c5b2bd4e61390b +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteLighting.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteLighting.cginc new file mode 100644 index 0000000..018ca93 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteLighting.cginc @@ -0,0 +1,200 @@ +#ifndef SPRITE_LIGHTING_INCLUDED +#define SPRITE_LIGHTING_INCLUDED + +//Check for using mesh normals +#if !defined(_FIXED_NORMALS_VIEWSPACE) && !defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE) && !defined(_FIXED_NORMALS_MODELSPACE) && !defined(_FIXED_NORMALS_MODELSPACE_BACKFACE) +#define MESH_NORMALS +#endif + +//Check for fixing backfacing tangents +#if defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE) || defined(_FIXED_NORMALS_MODELSPACE_BACKFACE) +#define FIXED_NORMALS_BACKFACE_RENDERING +#endif + +//////////////////////////////////////// +// Vertex structs +// + +struct VertexInput +{ + float4 vertex : POSITION; + float4 texcoord : TEXCOORD0; + float4 color : COLOR; +#if defined(MESH_NORMALS) + float3 normal : NORMAL; +#endif // _FIXED_NORMALS +#if defined(_NORMALMAP) + float4 tangent : TANGENT; +#endif // _NORMALMAP + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +//////////////////////////////////////// +// Normal functions +// + +uniform float4 _FixedNormal = float4(0, 0, 1, 1); + +inline float3 getFixedNormal() +{ + return _FixedNormal.xyz; +} + +inline float calculateBackfacingSign(float3 worldPos) +{ + //If we're using fixed normals and mesh is facing away from camera, flip tangentSign + //Unity uses a left handed coordinate system so camera always looks down the negative z axis + float3 cameraForward = float3(0,0,-1); + float3 meshWorldForward = mul((float3x3)unity_ObjectToWorld, cameraForward); + float3 toCamera = _WorldSpaceCameraPos - worldPos; + return sign(dot(toCamera, meshWorldForward)); +} + +inline half3 calculateSpriteWorldNormal(VertexInput vertex, float backFaceSign) +{ +#if defined(MESH_NORMALS) + + return calculateWorldNormal(vertex.normal); + +#else // !MESH_NORMALS + + float3 normal = getFixedNormal(); + +#if defined(_FIXED_NORMALS_VIEWSPACE) || defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE) + //View space fixed normal + //Rotate fixed normal by inverse view matrix to convert the fixed normal into world space + float3x3 invView = transpose((float3x3)UNITY_MATRIX_V); + return normalize(mul(invView, normal)); +#else + //Model space fixed normal. +#if defined(FIXED_NORMALS_BACKFACE_RENDERING) + //If back face rendering is enabled and the sprite is facing away from the camera (ie we're rendering the backface) then need to flip the normal + normal *= backFaceSign; +#endif + return calculateWorldNormal(normal); +#endif + +#endif // !MESH_NORMALS +} + +inline half3 calculateSpriteViewNormal(VertexInput vertex, float backFaceSign) +{ +#if defined(MESH_NORMALS) + + return normalize(mul((float3x3)UNITY_MATRIX_IT_MV, vertex.normal)); + +#else // !MESH_NORMALS + + float3 normal = getFixedNormal(); + +#if defined(_FIXED_NORMALS_VIEWSPACE) || defined(_FIXED_NORMALS_VIEWSPACE_BACKFACE) + //View space fixed normal + return normal; +#else + //Model space fixed normal +#if defined(FIXED_NORMALS_BACKFACE_RENDERING) + //If back face rendering is enabled and the sprite is facing away from the camera (ie we're rendering the backface) then need to flip the normal + normal *= backFaceSign; +#endif + return normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal)); +#endif + +#endif // !MESH_NORMALS +} + +//////////////////////////////////////// +// Normal map functions +// + +#if defined(_NORMALMAP) + +inline half3 calculateSpriteWorldBinormal(VertexInput vertex, half3 normalWorld, half3 tangentWorld, float backFaceSign) +{ + float tangentSign = vertex.tangent.w; + +#if defined(FIXED_NORMALS_BACKFACE_RENDERING) + tangentSign *= backFaceSign; +#endif + + return calculateWorldBinormal(normalWorld, tangentWorld, tangentSign); +} + +#endif // _NORMALMAP + +#if defined(_DIFFUSE_RAMP) + + +//////////////////////////////////////// +// Diffuse ramp functions +// + +//Disable for softer, more traditional diffuse ramping +#define HARD_DIFFUSE_RAMP + +uniform sampler2D _DiffuseRamp; + +inline fixed3 calculateDiffuseRamp(float ramp) +{ + return tex2D(_DiffuseRamp, float2(ramp, ramp)).rgb; +} + +inline fixed3 calculateRampedDiffuse(fixed3 lightColor, float attenuation, float angleDot) +{ + float d = angleDot * 0.5 + 0.5; +#if defined(HARD_DIFFUSE_RAMP) + half3 ramp = calculateDiffuseRamp(d * attenuation * 2); + return lightColor * ramp; +#else + half3 ramp = calculateDiffuseRamp(d); + return lightColor * ramp * (attenuation * 2); +#endif +} +#endif // _DIFFUSE_RAMP + +//////////////////////////////////////// +// Rim Lighting functions +// + +#ifdef _RIM_LIGHTING + +uniform float _RimPower; +uniform fixed4 _RimColor; + +inline fixed3 applyRimLighting(fixed3 posWorld, fixed3 normalWorld, fixed4 pixel) : SV_Target +{ + fixed3 viewDir = normalize(_WorldSpaceCameraPos - posWorld); + float invDot = 1.0 - saturate(dot(normalWorld, viewDir)); + float rimPower = pow(invDot, _RimPower); + float rim = saturate(rimPower * _RimColor.a); + +#if defined(_DIFFUSE_RAMP) + rim = calculateDiffuseRamp(rim).r; +#endif + + return lerp(pixel.rgb, _RimColor.xyz * pixel.a, rim); +} + +#endif //_RIM_LIGHTING + +//////////////////////////////////////// +// Emission functions +// + +#ifdef _EMISSION + +uniform sampler2D _EmissionMap; +uniform fixed4 _EmissionColor; +uniform float _EmissionPower; + + +#define APPLY_EMISSION(diffuse, uv) diffuse += tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb * _EmissionPower; +#define APPLY_EMISSION_SPECULAR(pixel, uv) pixel.rgb += (tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb * _EmissionPower) * pixel.a; + +#else //!_EMISSION + +#define APPLY_EMISSION(diffuse, uv) +#define APPLY_EMISSION_SPECULAR(pixel, uv) + +#endif //!_EMISSION + +#endif // SPRITE_LIGHTING_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteLighting.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteLighting.cginc.meta new file mode 100644 index 0000000..b25bff3 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteLighting.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 0cfb891658099ca4bb0c9544c08e60f9 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpritePixelLighting.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpritePixelLighting.cginc new file mode 100644 index 0000000..8c763f4 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpritePixelLighting.cginc @@ -0,0 +1,252 @@ +#ifndef SPRITE_PIXEL_LIGHTING_INCLUDED +#define SPRITE_PIXEL_LIGHTING_INCLUDED + +#include "ShaderShared.cginc" +#include "SpriteLighting.cginc" +#include "SpriteSpecular.cginc" +#include "AutoLight.cginc" + +//////////////////////////////////////// +// Defines +// + +//////////////////////////////////////// +// Vertex output struct +// + +#if defined(_NORMALMAP) + #define _VERTEX_LIGHTING_INDEX TEXCOORD5 + #define _LIGHT_COORD_INDEX_0 6 + #define _LIGHT_COORD_INDEX_1 7 + #define _FOG_COORD_INDEX 8 +#else + #define _VERTEX_LIGHTING_INDEX TEXCOORD3 + #define _LIGHT_COORD_INDEX_0 4 + #define _LIGHT_COORD_INDEX_1 5 + #define _FOG_COORD_INDEX 6 +#endif // _NORMALMAP + +struct VertexOutput +{ + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; + float4 posWorld : TEXCOORD1; + half3 normalWorld : TEXCOORD2; +#if defined(_NORMALMAP) + half3 tangentWorld : TEXCOORD3; + half3 binormalWorld : TEXCOORD4; +#endif // _NORMALMAP + fixed3 vertexLighting : _VERTEX_LIGHTING_INDEX; + LIGHTING_COORDS(_LIGHT_COORD_INDEX_0, _LIGHT_COORD_INDEX_1) +#if defined(_FOG) + UNITY_FOG_COORDS(_FOG_COORD_INDEX) +#endif // _FOG + + UNITY_VERTEX_OUTPUT_STEREO +}; + +//////////////////////////////////////// +// Light calculations +// + +uniform fixed4 _LightColor0; + +inline fixed3 calculateLightDiffuse(VertexOutput input, float3 normalWorld, inout fixed4 albedo) +{ + //For directional lights _WorldSpaceLightPos0.w is set to zero + float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w); + + float attenuation = LIGHT_ATTENUATION(input); + float angleDot = max(0, dot(normalWorld, lightWorldDirection)); + +#if defined(_DIFFUSE_RAMP) + fixed3 lightDiffuse = calculateRampedDiffuse(_LightColor0.rgb, attenuation, angleDot); +#else + fixed3 lightDiffuse = _LightColor0.rgb * (attenuation * angleDot); +#endif // _DIFFUSE_RAMP + + return lightDiffuse; +} + +inline float3 calculateNormalWorld(VertexOutput input) +{ +#if defined(_NORMALMAP) + return calculateNormalFromBumpMap(input.texcoord, input.tangentWorld, input.binormalWorld, input.normalWorld); +#else + return input.normalWorld; +#endif +} + +fixed3 calculateVertexLighting(float3 posWorld, float3 normalWorld) +{ + fixed3 vertexLighting = fixed3(0,0,0); + +#ifdef VERTEXLIGHT_ON + //Get approximated illumination from non-important point lights + vertexLighting = Shade4PointLights ( unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, + unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, + unity_4LightAtten0, posWorld, normalWorld) * 0.5; +#endif + + return vertexLighting; +} + +fixed3 calculateAmbientLight(half3 normalWorld) +{ +#if defined(_SPHERICAL_HARMONICS) + fixed3 ambient = ShadeSH9(half4(normalWorld, 1.0)); +#else + fixed3 ambient = unity_AmbientSky.rgb; +#endif + return ambient; +} + +#if defined(SPECULAR) + +fixed4 calculateSpecularLight(SpecularCommonData s, float3 viewDir, float3 normal, float3 lightDir, float3 lightColor, half3 ambient) +{ + SpecularLightData data = calculatePhysicsBasedSpecularLight (s.specColor, s.oneMinusReflectivity, s.smoothness, normal, viewDir, lightDir, lightColor, ambient, unity_IndirectSpecColor.rgb); + fixed4 pixel = calculateLitPixel(fixed4(s.diffColor, s.alpha), data.lighting); + pixel.rgb += data.specular * s.alpha; + return pixel; +} + +fixed4 calculateSpecularLightAdditive(SpecularCommonData s, float3 viewDir, float3 normal, float3 lightDir, float3 lightColor) +{ + SpecularLightData data = calculatePhysicsBasedSpecularLight (s.specColor, s.oneMinusReflectivity, s.smoothness, normal, viewDir, lightDir, lightColor, half3(0,0,0), half3(0,0,0)); + fixed4 pixel = calculateAdditiveLitPixel(fixed4(s.diffColor, s.alpha), data.lighting); + pixel.rgb += data.specular * s.alpha; + return pixel; +} + +#endif //SPECULAR + +//////////////////////////////////////// +// Vertex program +// + +VertexOutput vert(VertexInput v) +{ + VertexOutput output; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + output.pos = calculateLocalPos(v.vertex); + output.color = calculateVertexColor(v.color); + output.texcoord = calculateTextureCoord(v.texcoord); + output.posWorld = calculateWorldPos(v.vertex); + + float backFaceSign = 1; +#if defined(FIXED_NORMALS_BACKFACE_RENDERING) + backFaceSign = calculateBackfacingSign(output.posWorld.xyz); +#endif + + output.normalWorld = calculateSpriteWorldNormal(v, backFaceSign); + output.vertexLighting = calculateVertexLighting(output.posWorld, output.normalWorld); + +#if defined(_NORMALMAP) + output.tangentWorld = calculateWorldTangent(v.tangent); + output.binormalWorld = calculateSpriteWorldBinormal(v, output.normalWorld, output.tangentWorld, backFaceSign); +#endif + + TRANSFER_VERTEX_TO_FRAGMENT(output) + +#if defined(_FOG) + UNITY_TRANSFER_FOG(output,output.pos); +#endif // _FOG + + return output; +} + +//////////////////////////////////////// +// Fragment programs +// + +fixed4 fragBase(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord); + ALPHA_CLIP(texureColor, input.color) + + //Get normal direction + fixed3 normalWorld = calculateNormalWorld(input); + + //Get Ambient diffuse + fixed3 ambient = calculateAmbientLight(normalWorld); + + +#if defined(SPECULAR) + + //For directional lights _WorldSpaceLightPos0.w is set to zero + float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w); + float attenuation = LIGHT_ATTENUATION(input); + + //Returns pixel lit by light, texture color should inlcluded alpha + half3 viewDir = normalize(_WorldSpaceCameraPos - input.posWorld.xyz); + fixed4 pixel = calculateSpecularLight(getSpecularData(input.texcoord.xy, texureColor, input.color), viewDir, normalWorld, lightWorldDirection, _LightColor0.rgb * attenuation, ambient + input.vertexLighting); + + APPLY_EMISSION_SPECULAR(pixel, input.texcoord) + +#else + + //Get primary pixel light diffuse + fixed3 diffuse = calculateLightDiffuse(input, normalWorld, texureColor); + + //Combine along with vertex lighting for the base lighting pass + fixed3 lighting = ambient + diffuse + input.vertexLighting; + + APPLY_EMISSION(lighting, input.texcoord) + + fixed4 pixel = calculateLitPixel(texureColor, input.color, lighting); + +#endif + +#if defined(_RIM_LIGHTING) + pixel.rgb = applyRimLighting(input.posWorld, normalWorld, pixel); +#endif + + COLORISE(pixel) + APPLY_FOG(pixel, input) + + return pixel; +} + +fixed4 fragAdd(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord); + +#if defined(_COLOR_ADJUST) + texureColor = adjustColor(texureColor); +#endif // _COLOR_ADJUST + + ALPHA_CLIP(texureColor, input.color) + + //Get normal direction + fixed3 normalWorld = calculateNormalWorld(input); + +#if defined(SPECULAR) + + //For directional lights _WorldSpaceLightPos0.w is set to zero + float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w); + float attenuation = LIGHT_ATTENUATION(input); + + half3 viewDir = normalize(_WorldSpaceCameraPos - input.posWorld.xyz); + fixed4 pixel = calculateSpecularLightAdditive(getSpecularData(input.texcoord.xy, texureColor, input.color), viewDir, normalWorld, lightWorldDirection, _LightColor0.rgb * attenuation); + +#else + + //Get light diffuse + fixed3 lighting = calculateLightDiffuse(input, normalWorld, texureColor); + fixed4 pixel = calculateAdditiveLitPixel(texureColor, input.color, lighting); + +#endif + + COLORISE_ADDITIVE(pixel) + APPLY_FOG_ADDITIVE(pixel, input) + + return pixel; +} + + +#endif // SPRITE_PIXEL_LIGHTING_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpritePixelLighting.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpritePixelLighting.cginc.meta new file mode 100644 index 0000000..73da285 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpritePixelLighting.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7ffc57e05c42ec748838bea0a3aff9f9 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteShadows.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteShadows.cginc new file mode 100644 index 0000000..a24df47 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteShadows.cginc @@ -0,0 +1,49 @@ +#ifndef SPRITE_SHADOWS_INCLUDED +#define SPRITE_SHADOWS_INCLUDED + +#include "ShaderShared.cginc" + +//////////////////////////////////////// +// Vertex structs +// + +struct vertexInput +{ + float4 vertex : POSITION; + float4 texcoord : TEXCOORD0; +}; + +struct vertexOutput +{ + V2F_SHADOW_CASTER; + float2 texcoord : TEXCOORD1; +}; + +//////////////////////////////////////// +// Vertex program +// + +vertexOutput vert(vertexInput v) +{ + vertexOutput o; + TRANSFER_SHADOW_CASTER(o) + o.texcoord = calculateTextureCoord(v.texcoord); + return o; +} + +//////////////////////////////////////// +// Fragment program +// + + +uniform fixed _ShadowAlphaCutoff; + +fixed4 frag(vertexOutput IN) : COLOR +{ + fixed4 texureColor = calculateTexturePixel(IN.texcoord); + clip(texureColor.a - _ShadowAlphaCutoff); + + SHADOW_CASTER_FRAGMENT(IN) +} + +#endif // SPRITE_SHADOWS_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteShadows.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteShadows.cginc.meta new file mode 100644 index 0000000..09089fb --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteShadows.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b7dbdfb1f55ee26459284220ad6d5bc4 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteSpecular.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteSpecular.cginc new file mode 100644 index 0000000..e96d566 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteSpecular.cginc @@ -0,0 +1,246 @@ +#ifndef SPRITE_SPECULAR_INCLUDED +#define SPRITE_SPECULAR_INCLUDED + +#include "ShaderMaths.cginc" + +//////////////////////////////////////// +// Specular functions +// + +#if defined(_SPECULAR) || defined(_SPECULAR_GLOSSMAP) + +#define SPECULAR + + +//ALL THESE FUNCTIONS ARE TAKEN AND ADAPTED FROM UNITY'S OWN PHYSICS BASED STANDARD SHADER + +uniform float _Metallic; +uniform float _Glossiness; +uniform float _GlossMapScale; +uniform sampler2D _MetallicGlossMap; + +struct SpecularLightData +{ + half3 lighting; + half3 specular; +}; + +struct SpecularCommonData +{ + half3 diffColor, specColor; + // Note: smoothness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level. + // Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots. + half oneMinusReflectivity, smoothness; + half alpha; +}; + +inline half2 getMetallicGloss(float2 uv) +{ + half2 mg; + +#ifdef _SPECULAR_GLOSSMAP + mg = tex2D(_MetallicGlossMap, uv).ra; + mg.g *= _GlossMapScale; +#else + mg.r = _Metallic; + mg.g = _Glossiness; +#endif + + return mg; +} + +inline half getOneMinusReflectivityFromMetallic(half metallic) +{ + // We'll need oneMinusReflectivity, so + // 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic) + // store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then + // 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) = + // = alpha - metallic * alpha + half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a; + return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec; +} + +inline SpecularCommonData getSpecularData(float2 uv, half4 texureColor, fixed4 color) +{ + half2 metallicGloss = getMetallicGloss(uv); + half metallic = metallicGloss.x; + half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m. + + fixed4 albedo = calculatePixel(texureColor, color); + + half3 specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic); + half oneMinusReflectivity = getOneMinusReflectivityFromMetallic(metallic); + half3 diffColor = albedo * oneMinusReflectivity; + + SpecularCommonData o = (SpecularCommonData)0; + o.diffColor = diffColor; + o.specColor = specColor; + o.oneMinusReflectivity = oneMinusReflectivity; + o.smoothness = smoothness; + +#if defined(_ALPHAPREMULTIPLY_ON) && (SHADER_TARGET >= 30) + // Reflectivity 'removes' from the rest of components, including Transparency + // outAlpha = 1-(1-alpha)*(1-reflectivity) = 1-(oneMinusReflectivity - alpha*oneMinusReflectivity) = + // = 1-oneMinusReflectivity + alpha*oneMinusReflectivity + //o.alpha = 1-oneMinusReflectivity + albedo.a*oneMinusReflectivity; + o.alpha = albedo.a; +#else + o.alpha = albedo.a; +#endif + + return o; +} +inline half SmoothnessToPerceptualRoughness(half smoothness) +{ + return (1 - smoothness); +} + +inline half PerceptualRoughnessToRoughness(half perceptualRoughness) +{ + return perceptualRoughness * perceptualRoughness; +} + +// Ref: http://jcgt.org/published/0003/02/03/paper.pdf +inline half SmithJointGGXVisibilityTerm (half NdotL, half NdotV, half roughness) +{ +#if 0 + // Original formulation: + // lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f; + // lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f; + // G = 1 / (1 + lambda_v + lambda_l); + + // Reorder code to be more optimal + half a = roughness; + half a2 = a * a; + + half lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2); + half lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2); + + // Simplify visibility term: (2.0f * NdotL * NdotV) / ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f)); + return 0.5f / (lambdaV + lambdaL + 1e-5f); // This function is not intended to be running on Mobile, + // therefore epsilon is smaller than can be represented by half +#else + // Approximation of the above formulation (simplify the sqrt, not mathematically correct but close enough) + half a = roughness; + half lambdaV = NdotL * (NdotV * (1 - a) + a); + half lambdaL = NdotV * (NdotL * (1 - a) + a); + + return 0.5f / (lambdaV + lambdaL + 1e-5f); +#endif +} + +inline half GGXTerm (half NdotH, half roughness) +{ + half a2 = roughness * roughness; + half d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad + return UNITY_INV_PI * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile, + // therefore epsilon is smaller than what can be represented by half +} + +inline half3 FresnelTerm (half3 F0, half cosA) +{ + half t = pow5 (1 - cosA); // ala Schlick interpoliation + return F0 + (1-F0) * t; +} + +inline half3 FresnelLerp (half3 F0, half F90, half cosA) +{ + half t = pow5 (1 - cosA); // ala Schlick interpoliation + return lerp (F0, F90, t); +} + +// Note: Disney diffuse must be multiply by diffuseAlbedo / PI. This is done outside of this function. +inline half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness) +{ + half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness; + // Two schlick fresnel term + half lightScatter = (1 + (fd90 - 1) * pow5(1 - NdotL)); + half viewScatter = (1 + (fd90 - 1) * pow5(1 - NdotV)); + + return lightScatter * viewScatter; +} + +// Main Physically Based BRDF +// Derived from Disney work and based on Torrance-Sparrow micro-facet model +// +// BRDF = kD / pi + kS * (D * V * F) / 4 +// I = BRDF * NdotL +// +// * NDF (depending on UNITY_BRDF_GGX): +// a) Normalized BlinnPhong +// b) GGX +// * Smith for Visiblity term +// * Schlick approximation for Fresnel +SpecularLightData calculatePhysicsBasedSpecularLight(half3 specColor, half oneMinusReflectivity, half smoothness, half3 normal, half3 viewDir, half3 lightdir, half3 lightColor, half3 indirectDiffuse, half3 indirectSpecular) +{ + half perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness); + half3 halfDir = safeNormalize (lightdir + viewDir); + +// NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping +// In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts. +// but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too). +// Following define allow to control this. Set it to 0 if ALU is critical on your platform. +// This correction is interesting for GGX with SmithJoint visibility function because artifacts are more visible in this case due to highlight edge of rough surface +// Edit: Disable this code by default for now as it is not compatible with two sided lighting used in SpeedTree. +#define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0 + +#if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV + // The amount we shift the normal toward the view vector is defined by the dot product. + half shiftAmount = dot(normal, viewDir); + normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal; + // A re-normalization should be applied here but as the shift is small we don't do it to save ALU. + //normal = normalize(normal); + + half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here +#else + half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact +#endif + + half nl = saturate(dot(normal, lightdir)); + half nh = saturate(dot(normal, halfDir)); + + half lv = saturate(dot(lightdir, viewDir)); + half lh = saturate(dot(lightdir, halfDir)); + + // Diffuse term + half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl; + + // Specular term + // HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm! + // BUT 1) that will make shader look significantly darker than Legacy ones + // and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH + half roughness = PerceptualRoughnessToRoughness(perceptualRoughness); + half V = SmithJointGGXVisibilityTerm (nl, nv, roughness); + half D = GGXTerm (nh, roughness); + + half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later + +# ifdef UNITY_COLORSPACE_GAMMA + specularTerm = sqrt(max(1e-4h, specularTerm)); +# endif + + // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value + specularTerm = max(0, specularTerm * nl); + + // surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1) + half surfaceReduction; +# ifdef UNITY_COLORSPACE_GAMMA + surfaceReduction = 1.0 - 0.28f * roughness * perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1] +# else + surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1] +# endif + + // To provide true Lambert lighting, we need to be able to kill specular completely. + specularTerm *= any(specColor) ? 1.0 : 0.0; + + half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity)); + + SpecularLightData outData = (SpecularLightData)0; + outData.lighting = indirectDiffuse + lightColor * diffuseTerm; + outData.specular = (specularTerm * lightColor * FresnelTerm (specColor, lh)) + (surfaceReduction * indirectSpecular * FresnelLerp (specColor, grazingTerm, nv)); + return outData; +} + +#endif // _SPECULAR && _SPECULAR_GLOSSMAP + +#endif // SPRITE_SPECULAR_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteSpecular.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteSpecular.cginc.meta new file mode 100644 index 0000000..a9fdc4f --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteSpecular.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f195336fc94457241a37a0aa85923681 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc new file mode 100644 index 0000000..4385ffc --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc @@ -0,0 +1,72 @@ +#ifndef SPRITE_UNLIT_INCLUDED +#define SPRITE_UNLIT_INCLUDED + +#include "ShaderShared.cginc" + +//////////////////////////////////////// +// Vertex structs +// + +struct VertexInput +{ + float4 vertex : POSITION; + float4 texcoord : TEXCOORD0; + fixed4 color : COLOR; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; + +struct VertexOutput +{ + float4 pos : SV_POSITION; + float2 texcoord : TEXCOORD0; + fixed4 color : COLOR; +#if defined(_FOG) + UNITY_FOG_COORDS(1) +#endif // _FOG + + UNITY_VERTEX_OUTPUT_STEREO +}; + +//////////////////////////////////////// +// Vertex program +// + +VertexOutput vert(VertexInput input) +{ + VertexOutput output; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + output.pos = calculateLocalPos(input.vertex); + output.texcoord = calculateTextureCoord(input.texcoord); + output.color = calculateVertexColor(input.color); + +#if defined(_FOG) + UNITY_TRANSFER_FOG(output,output.pos); +#endif // _FOG + + return output; +} + +//////////////////////////////////////// +// Fragment program +// + + + + +fixed4 frag(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord.xy); + ALPHA_CLIP(texureColor, input.color) + + fixed4 pixel = calculatePixel(texureColor, input.color); + + COLORISE(pixel) + APPLY_FOG(pixel, input) + + return pixel; +} + +#endif // SPRITE_UNLIT_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc.meta new file mode 100644 index 0000000..1e8d974 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteUnlit.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 072e7b07ec7fb1346a9dcd3bcbbb7111 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteVertexLighting.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteVertexLighting.cginc new file mode 100644 index 0000000..0ffa277 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteVertexLighting.cginc @@ -0,0 +1,474 @@ +#ifndef SPRITE_VERTEX_LIGHTING_INCLUDED +#define SPRITE_VERTEX_LIGHTING_INCLUDED + +#include "ShaderShared.cginc" +#include "SpriteLighting.cginc" +#include "SpriteSpecular.cginc" + +//////////////////////////////////////// +// Defines +// + +//Define to use spot lights (more expensive) +#define SPOT_LIGHTS + +//Have to process lighting per pixel if using normal maps or a diffuse ramp or rim lighting or specular +#if defined(_NORMALMAP) || defined(_DIFFUSE_RAMP) || defined(_RIM_LIGHTING) || defined(SPECULAR) +#define PER_PIXEL_LIGHTING +#endif + +//Turn off bump mapping and diffuse ramping on older shader models as they dont support needed number of outputs +#if defined(PER_PIXEL_LIGHTING) && (SHADER_TARGET < 30) + #undef PER_PIXEL_LIGHTING + #undef _NORMALMAP + #undef _DIFFUSE_RAMP + #undef _RIM_LIGHTING +#endif + +//In D3D9 only have a max of 9 TEXCOORD so can't have diffuse ramping or fog or rim lighting if processing lights per pixel +#if defined(SHADER_API_D3D9) && defined(PER_PIXEL_LIGHTING) + #if defined(_NORMALMAP) + #undef _DIFFUSE_RAMP + #undef _FOG + #undef _RIM_LIGHTING + #elif defined(_DIFFUSE_RAMP) + #undef _FOG + #undef _RIM_LIGHTING + #elif defined(_RIM_LIGHTING) + #undef _FOG + #undef _DIFFUSE_RAMP + #else + #undef _DIFFUSE_RAMP + #undef _RIM_LIGHTING + #endif +#endif + +#if defined(PER_PIXEL_LIGHTING) + #if defined(_NORMALMAP) && defined(_DIFFUSE_RAMP) + #define ATTENUATIONS TEXCOORD9 + #if defined(_RIM_LIGHTING) + #define _POS_WORLD_INDEX TEXCOORD10 + #define _FOG_COORD_INDEX 11 + #else + #define _FOG_COORD_INDEX 10 + #endif + #elif defined(_NORMALMAP) != defined(_DIFFUSE_RAMP) + #define ATTENUATIONS TEXCOORD8 + #if defined(_RIM_LIGHTING) + #define _POS_WORLD_INDEX TEXCOORD9 + #define _FOG_COORD_INDEX 10 + #else + #define _FOG_COORD_INDEX 9 + #endif + #else //!_DIFFUSE_RAMP && !_NORMALMAP + #if defined(_RIM_LIGHTING) + #define _POS_WORLD_INDEX TEXCOORD8 + #define _FOG_COORD_INDEX 9 + #else + #define _FOG_COORD_INDEX 8 + #endif + #endif +#else //!PER_PIXEL_LIGHTING + #define _FOG_COORD_INDEX 2 +#endif + +//////////////////////////////////////// +// Vertex output struct +// + +struct VertexOutput +{ + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float3 texcoord : TEXCOORD0; + +#if defined(PER_PIXEL_LIGHTING) + + half4 VertexLightInfo0 : TEXCOORD1; + half4 VertexLightInfo1 : TEXCOORD2; + half4 VertexLightInfo2 : TEXCOORD3; + half4 VertexLightInfo3 : TEXCOORD4; + half4 VertexLightInfo4 : TEXCOORD5; + + #if defined(_NORMALMAP) + half4 normalWorld : TEXCOORD6; + half4 tangentWorld : TEXCOORD7; + half4 binormalWorld : TEXCOORD8; + #else + half3 normalWorld : TEXCOORD6; + half3 VertexLightInfo5 : TEXCOORD7; + #endif + #if defined(_DIFFUSE_RAMP) + half4 LightAttenuations : ATTENUATIONS; + #endif + #if defined(_RIM_LIGHTING) + float4 posWorld : _POS_WORLD_INDEX; + #endif + +#else //!PER_PIXEL_LIGHTING + + half3 FullLighting : TEXCOORD1; + +#endif // !PER_PIXEL_LIGHTING + +#if defined(_FOG) + UNITY_FOG_COORDS(_FOG_COORD_INDEX) +#endif // _FOG + + UNITY_VERTEX_OUTPUT_STEREO +}; + +//////////////////////////////////////// +// Light calculations +// + +struct VertexLightInfo +{ + half3 lightDirection; + fixed3 lightColor; + +#if defined(_DIFFUSE_RAMP) + float attenuation; +#endif // _DIFFUSE_RAMP +}; + +inline VertexLightInfo getVertexLightAttenuatedInfo(int index, float3 viewPos) +{ + VertexLightInfo lightInfo; + + //For directional lights unity_LightPosition.w is set to zero + lightInfo.lightDirection = unity_LightPosition[index].xyz - viewPos.xyz * unity_LightPosition[index].w; + float lengthSq = dot(lightInfo.lightDirection, lightInfo.lightDirection); + + // don't produce NaNs if some vertex position overlaps with the light + lengthSq = max(lengthSq, 0.000001); + + lightInfo.lightDirection *= rsqrt(lengthSq); + + float attenuation = 1.0 / (1.0 + lengthSq * unity_LightAtten[index].z); + +#if defined(SPOT_LIGHTS) + //Spot light attenuation - for non-spot lights unity_LightAtten.x is set to -1 and y is set to 1 + { + float rho = max (0, dot(lightInfo.lightDirection, unity_SpotDirection[index].xyz)); + float spotAtt = (rho - unity_LightAtten[index].x) * unity_LightAtten[index].y; + attenuation *= saturate(spotAtt); + } +#endif // SPOT_LIGHTS + + //If using a diffuse ramp texture then need to pass through the lights attenuation, otherwise premultiply the light color with it +#if defined(_DIFFUSE_RAMP) + lightInfo.lightColor = unity_LightColor[index].rgb; + lightInfo.attenuation = attenuation; +#else + lightInfo.lightColor = unity_LightColor[index].rgb * attenuation; +#endif // _DIFFUSE_RAMP + + return lightInfo; +} + +fixed3 calculateAmbientLight(half3 normalWorld) +{ +#if defined(_SPHERICAL_HARMONICS) + + //Magic constants used to tweak ambient to approximate pixel shader spherical harmonics + static const fixed3 worldUp = fixed3(0,1,0); + static const float skyGroundDotMul = 2.5; + static const float minEquatorMix = 0.5; + static const float equatorColorBlur = 0.33; + + float upDot = dot(normalWorld, worldUp); + + //Fade between a flat lerp from sky to ground and a 3 way lerp based on how bright the equator light is. + //This simulates how directional lights get blurred using spherical harmonics + + //Work out color from ground and sky, ignoring equator + float adjustedDot = upDot * skyGroundDotMul; + fixed3 skyGroundColor = lerp(unity_AmbientGround, unity_AmbientSky, saturate((adjustedDot + 1.0) * 0.5)); + + //Work out equator lights brightness + float equatorBright = saturate(dot(unity_AmbientEquator.rgb, unity_AmbientEquator.rgb)); + + //Blur equator color with sky and ground colors based on how bright it is. + fixed3 equatorBlurredColor = lerp(unity_AmbientEquator, saturate(unity_AmbientEquator + unity_AmbientGround + unity_AmbientSky), equatorBright * equatorColorBlur); + + //Work out 3 way lerp inc equator light + fixed3 equatorColor = lerp(equatorBlurredColor, unity_AmbientGround, -upDot) * step(upDot, 0) + lerp(equatorBlurredColor, unity_AmbientSky, upDot) * step(0, upDot); + + //Mix the two colors together based on how bright the equator light is + return lerp(skyGroundColor, equatorColor, saturate(equatorBright + minEquatorMix)); + +#else // !_SPHERICAL_HARMONICS + + //Flat ambient is just the sky color + return unity_AmbientSky.rgb; + +#endif // !_SPHERICAL_HARMONICS +} + +//////////////////////////////////////// +// Light Packing Functions +// + +#if defined(_DIFFUSE_RAMP) + +inline fixed3 calculateLightDiffuse(fixed3 lightColor, half3 viewNormal, half3 lightViewDir, float attenuation) +{ + float angleDot = max(0, dot(viewNormal, lightViewDir)); + fixed3 lightDiffuse = calculateRampedDiffuse(lightColor, attenuation, angleDot); + return lightDiffuse; +} + +#else + +inline fixed3 calculateLightDiffuse(fixed3 attenuatedLightColor, half3 viewNormal, half3 lightViewDir) +{ + float angleDot = max(0, dot(viewNormal, lightViewDir)); + fixed3 lightDiffuse = attenuatedLightColor * angleDot; + + return lightDiffuse; +} + +#endif // _NORMALMAP + + +#if defined(PER_PIXEL_LIGHTING) + +#define VERTEX_LIGHT_0_DIR VertexLightInfo0.xyz +#define VERTEX_LIGHT_0_R VertexLightInfo4.x +#define VERTEX_LIGHT_0_G VertexLightInfo4.y +#define VERTEX_LIGHT_0_B VertexLightInfo4.z + +#define VERTEX_LIGHT_1_DIR VertexLightInfo1.xyz +#define VERTEX_LIGHT_1_R VertexLightInfo0.w +#define VERTEX_LIGHT_1_G VertexLightInfo1.w +#define VERTEX_LIGHT_1_B VertexLightInfo2.w + +#define VERTEX_LIGHT_2_DIR VertexLightInfo2.xyz +#define VERTEX_LIGHT_2_R VertexLightInfo3.w +#define VERTEX_LIGHT_2_G VertexLightInfo4.w +#define VERTEX_LIGHT_2_B texcoord.z + +#define VERTEX_LIGHT_3_DIR VertexLightInfo3.xyz + +#if defined(_NORMALMAP) + #define VERTEX_LIGHT_3_R normalWorld.w + #define VERTEX_LIGHT_3_G tangentWorld.w + #define VERTEX_LIGHT_3_B binormalWorld.w +#else + #define VERTEX_LIGHT_3_R VertexLightInfo5.x + #define VERTEX_LIGHT_3_G VertexLightInfo5.y + #define VERTEX_LIGHT_3_B VertexLightInfo5.z +#endif + +#if defined(_DIFFUSE_RAMP) + + #define LIGHT_DIFFUSE_ATTEN_0 LightAttenuations.x + #define LIGHT_DIFFUSE_ATTEN_1 LightAttenuations.y + #define LIGHT_DIFFUSE_ATTEN_2 LightAttenuations.z + #define LIGHT_DIFFUSE_ATTEN_3 LightAttenuations.w + + #define PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo) \ + { \ + output.LIGHT_DIFFUSE_ATTEN_##index = lightInfo.attenuation; \ + } + + #define ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \ + { \ + diffuse += calculateLightDiffuse(lightColor, viewNormal, lightViewDir, input.LIGHT_DIFFUSE_ATTEN_##index); \ + } +#else + #define PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo) + #define ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \ + { \ + diffuse += calculateLightDiffuse(lightColor, viewNormal, lightViewDir); \ + } +#endif + +#define PACK_VERTEX_LIGHT(index, output, viewPos) \ + { \ + VertexLightInfo lightInfo = getVertexLightAttenuatedInfo(index, viewPos); \ + output.VERTEX_LIGHT_##index##_DIR = lightInfo.lightDirection; \ + output.VERTEX_LIGHT_##index##_R = lightInfo.lightColor.r; \ + output.VERTEX_LIGHT_##index##_G = lightInfo.lightColor.g; \ + output.VERTEX_LIGHT_##index##_B = lightInfo.lightColor.b; \ + PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo); \ + } + +#define ADD_VERTEX_LIGHT(index, input, viewNormal, diffuse) \ + { \ + half3 lightViewDir = input.VERTEX_LIGHT_##index##_DIR; \ + fixed3 lightColor = fixed3(input.VERTEX_LIGHT_##index##_R, input.VERTEX_LIGHT_##index##_G, input.VERTEX_LIGHT_##index##_B); \ + ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \ + } + +#if defined(SPECULAR) + +#define ADD_VERTEX_LIGHT_SPEC(index, input, viewNormal, specData, combinedLightData, indirectDiffuse, indirectSpecular) \ + { \ + half3 lightViewDir = input.VERTEX_LIGHT_##index##_DIR; \ + fixed3 lightColor = fixed3(input.VERTEX_LIGHT_##index##_R, input.VERTEX_LIGHT_##index##_G, input.VERTEX_LIGHT_##index##_B); \ + SpecularLightData lightData = calculatePhysicsBasedSpecularLight(specData.specColor, specData.oneMinusReflectivity, specData.smoothness, viewNormal, fixed3(0,0,1), lightViewDir, lightColor, indirectDiffuse, indirectSpecular); \ + combinedLightData.lighting += lightData.lighting; \ + combinedLightData.specular += lightData.specular; \ + } + +#endif + +#else //!PER_PIXEL_LIGHTING + +//////////////////////////////////////// +// Vertex Only Functions +// + +inline fixed3 calculateLightDiffuse(int index, float3 viewPos, half3 viewNormal) +{ + VertexLightInfo lightInfo = getVertexLightAttenuatedInfo(index, viewPos); + float angleDot = max(0, dot(viewNormal, lightInfo.lightDirection)); + return lightInfo.lightColor * angleDot; +} + +#endif // !PER_PIXEL_LIGHTING + +//////////////////////////////////////// +// Vertex program +// + +VertexOutput vert(VertexInput input) +{ + VertexOutput output; + + UNITY_SETUP_INSTANCE_ID(input); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); + + output.pos = calculateLocalPos(input.vertex); + output.color = calculateVertexColor(input.color); + output.texcoord = float3(calculateTextureCoord(input.texcoord), 0); + + float3 viewPos = UnityObjectToViewPos(input.vertex); //float3 viewPos = mul(UNITY_MATRIX_MV, input.vertex); // +#if defined(FIXED_NORMALS_BACKFACE_RENDERING) || defined(_RIM_LIGHTING) + float4 powWorld = calculateWorldPos(input.vertex); +#endif + + float backFaceSign = 1; +#if defined(FIXED_NORMALS_BACKFACE_RENDERING) + backFaceSign = calculateBackfacingSign(powWorld.xyz); +#endif + +#if defined(PER_PIXEL_LIGHTING) + + #if defined(_RIM_LIGHTING) + output.posWorld = powWorld; + #endif + + PACK_VERTEX_LIGHT(0, output, viewPos) + PACK_VERTEX_LIGHT(1, output, viewPos) + PACK_VERTEX_LIGHT(2, output, viewPos) + PACK_VERTEX_LIGHT(3, output, viewPos) + + output.normalWorld.xyz = calculateSpriteWorldNormal(input, backFaceSign); + + #if defined(_NORMALMAP) + output.tangentWorld.xyz = calculateWorldTangent(input.tangent); + output.binormalWorld.xyz = calculateSpriteWorldBinormal(input, output.normalWorld, output.tangentWorld, backFaceSign); + #endif + +#else // !PER_PIXEL_LIGHTING + + //Just pack full lighting + float3 viewNormal = calculateSpriteViewNormal(input, backFaceSign); + //Get Ambient diffuse + float3 normalWorld = calculateSpriteWorldNormal(input, backFaceSign); + fixed3 ambient = calculateAmbientLight(normalWorld); + + fixed3 diffuse = calculateLightDiffuse(0, viewPos, viewNormal); + diffuse += calculateLightDiffuse(1, viewPos, viewNormal); + diffuse += calculateLightDiffuse(2, viewPos, viewNormal); + diffuse += calculateLightDiffuse(3, viewPos, viewNormal); + + output.FullLighting = ambient + diffuse; + +#endif // !PER_PIXEL_LIGHTING + +#if defined(_FOG) + UNITY_TRANSFER_FOG(output, output.pos); +#endif // _FOG + + return output; +} + +//////////////////////////////////////// +// Fragment program +// + +fixed4 frag(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord.xy); + ALPHA_CLIP(texureColor, input.color) + +#if defined(PER_PIXEL_LIGHTING) + + #if defined(_NORMALMAP) + half3 normalWorld = calculateNormalFromBumpMap(input.texcoord.xy, input.tangentWorld.xyz, input.binormalWorld.xyz, input.normalWorld.xyz); + #else + half3 normalWorld = input.normalWorld.xyz; + #endif + + //Get Ambient diffuse + fixed3 ambient = calculateAmbientLight(normalWorld); + + half3 normalView = normalize(mul((float3x3)UNITY_MATRIX_V, normalWorld)); + +#if defined(SPECULAR) + + SpecularCommonData specData = getSpecularData(input.texcoord.xy, texureColor, input.color); + + SpecularLightData combinedLightData = (SpecularLightData)0; + ADD_VERTEX_LIGHT_SPEC(0, input, normalView, specData, combinedLightData, ambient, unity_IndirectSpecColor.rgb) + ADD_VERTEX_LIGHT_SPEC(1, input, normalView, specData, combinedLightData, fixed3(0,0,0), fixed3(0,0,0)) + ADD_VERTEX_LIGHT_SPEC(2, input, normalView, specData, combinedLightData, fixed3(0,0,0), fixed3(0,0,0)) + ADD_VERTEX_LIGHT_SPEC(3, input, normalView, specData, combinedLightData, fixed3(0,0,0), fixed3(0,0,0)) + + fixed4 pixel = calculateLitPixel(fixed4(specData.diffColor, specData.alpha), combinedLightData.lighting); + pixel.rgb += combinedLightData.specular * specData.alpha; + + APPLY_EMISSION_SPECULAR(pixel, input.texcoord) + +#else + + //Find vertex light diffuse + fixed3 diffuse = fixed3(0,0,0); + + //Add each vertex light to diffuse + ADD_VERTEX_LIGHT(0, input, normalView, diffuse) + ADD_VERTEX_LIGHT(1, input, normalView, diffuse) + ADD_VERTEX_LIGHT(2, input, normalView, diffuse) + ADD_VERTEX_LIGHT(3, input, normalView, diffuse) + + fixed3 lighting = ambient + diffuse; + + APPLY_EMISSION(lighting, input.texcoord.xy) + + fixed4 pixel = calculateLitPixel(texureColor, input.color, lighting); + +#endif + +#if defined(_RIM_LIGHTING) + pixel.rgb = applyRimLighting(input.posWorld, normalWorld, pixel); +#endif + +#else // !PER_PIXEL_LIGHTING + + APPLY_EMISSION(input.FullLighting, input.texcoord.xy) + + fixed4 pixel = calculateLitPixel(texureColor, input.color, input.FullLighting); + +#endif // !PER_PIXEL_LIGHTING + + COLORISE(pixel) + APPLY_FOG(pixel, input) + + return pixel; +} + +#endif // SPRITE_VERTEX_LIGHTING_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteVertexLighting.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteVertexLighting.cginc.meta new file mode 100644 index 0000000..5987054 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CGIncludes/SpriteVertexLighting.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: c739dcf9dbcab944898d0b796e11afb9 +timeCreated: 1494092582 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthNormalsTexture.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthNormalsTexture.shader new file mode 100644 index 0000000..0c80ebc --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthNormalsTexture.shader @@ -0,0 +1,522 @@ +Shader "Hidden/Sprite-CameraDepthNormalsTexture" { + +// Use this shader to render a DepthNormals texture for a camera with correct sprite normals (using camera.RenderWithShader with replacement tag "RenderType") + +Properties { + _MainTex ("", 2D) = "white" {} + _Cutoff ("", Float) = 0.5 + _Color ("", Color) = (1,1,1,1) +} + +SubShader { + Tags { "RenderType"="Sprite" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderShared.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _FixedNormal; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="SpriteViewSpaceFixedNormal" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/SpriteLighting.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.nz.xyz = getFixedNormal(); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="SpriteModelSpaceFixedNormal" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/SpriteLighting.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + float3 worldPos = mul(unity_ObjectToWorld, v.vertex); + float3 normal = getFixedNormal(); +//Only do this if backface is enabled :/ + normal *= calculateBackfacingSign(worldPos.xyz); +// + o.nz.xyz = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal)); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="Opaque" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +struct v2f { + float4 pos : SV_POSITION; + float4 nz : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TransparentCutout" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _MainTex_ST; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +uniform fixed4 _Color; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a*_Color.a - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeBark" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertBark(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag( v2f i ) : SV_Target { + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeLeaf" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertLeaf(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag( v2f i ) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float4 nz : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" } + Pass { + Cull Back +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + Pass { + Cull Front +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = -COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + +} + +SubShader { + Tags { "RenderType"="TreeBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert (appdata_tree_billboard v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv.x = v.texcoord.x; + o.uv.y = v.texcoord.y > 0; + o.nz.xyz = float3(0,0,1); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - 0.001 ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="GrassBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassBillboardVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="Grass" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +Fallback Off +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthNormalsTexture.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthNormalsTexture.shader.meta new file mode 100644 index 0000000..ba10b83 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthNormalsTexture.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 4794ea6b2d07cc546ba97a809b5f9ada +timeCreated: 1494092583 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthTexture.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthTexture.shader new file mode 100644 index 0000000..3a7f60d --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthTexture.shader @@ -0,0 +1,518 @@ +Shader "Hidden/Sprite-CameraDepthTexture" { + +// Use this shader to render a Depth texture for a camera with soft edged Sprites (using camera.RenderWithShader with replacement tag "RenderType") +// Note the depth is encoded into the pixels RGB not the full RGBA (alpha is needed for blending) + +Properties { + _MainTex ("", 2D) = "white" {} + _Cutoff ("", Float) = 0.5 + _Color ("", Color) = (1,1,1,1) +} + +SubShader { + Tags { "RenderType"="Sprite" } + Pass { + Cull Off + Blend SrcAlpha OneMinusSrcAlpha +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/ShaderMaths.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), alpha); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="SpriteViewSpaceFixedNormal" } + Pass { + Cull Off + Blend SrcAlpha OneMinusSrcAlpha +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/ShaderMaths.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), alpha); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="SpriteModelSpaceFixedNormal" } + Pass { + Cull Off + Blend SrcAlpha OneMinusSrcAlpha +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/ShaderMaths.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), alpha); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="Opaque" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +struct v2f { + float4 pos : SV_POSITION; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.depth = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TransparentCutout" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _MainTex_ST; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +uniform fixed4 _Color; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a*_Color.a - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeBark" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertBark(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.depth = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag( v2f i ) : SV_Target { + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeLeaf" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertLeaf(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag( v2f i ) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.depth = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" } + Pass { + Cull Back +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } + Pass { + Cull Front +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } + +} + +SubShader { + Tags { "RenderType"="TreeBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert (appdata_tree_billboard v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv.x = v.texcoord.x; + o.uv.y = v.texcoord.y > 0; + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - 0.001 ); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="GrassBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassBillboardVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="Grass" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderMaths.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float depth : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord; + o.depth = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return fixed4(EncodeFloatRGB (i.depth), 1); +} +ENDCG + } +} + +Fallback Off +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthTexture.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthTexture.shader.meta new file mode 100644 index 0000000..abf6acb --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraDepthTexture.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f768a57e040cc48489ad8c7392a31154 +timeCreated: 1494092586 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraNormalsTexture.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraNormalsTexture.shader new file mode 100644 index 0000000..1cf6a77 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraNormalsTexture.shader @@ -0,0 +1,522 @@ +Shader "Hidden/Sprite-CameraNormalsTexture" { + +// Use this shader to render a Normals texture for a camera with correct sprite normals (using camera.RenderWithShader with replacement tag "RenderType") + +Properties { + _MainTex ("", 2D) = "white" {} + _Cutoff ("", Float) = 0.5 + _Color ("", Color) = (1,1,1,1) +} + +SubShader { + Tags { "RenderType"="Sprite" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderShared.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _FixedNormal; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return i.nz; +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="SpriteViewSpaceFixedNormal" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/SpriteLighting.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + o.nz.xyz = getFixedNormal(); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return i.nz; +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="SpriteModelSpaceFixedNormal" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "CGIncludes/ShaderShared.cginc" +#include "CGIncludes/SpriteLighting.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = calculateTextureCoord(v.texcoord); + float3 worldPos = mul(unity_ObjectToWorld, v.vertex); + float3 normal = getFixedNormal(); +//Only do this if backface is enabled :/ + normal *= calculateBackfacingSign(worldPos.xyz); +// + o.nz.xyz = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, normal)); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = calculateTexturePixel(i.uv ); + float alpha = texcol.a*_Color.a; + clip( alpha - _Cutoff ); + return i.nz; +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="Opaque" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +struct v2f { + float4 pos : SV_POSITION; + float4 nz : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TransparentCutout" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _MainTex_ST; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +uniform fixed4 _Color; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a*_Color.a - _Cutoff ); + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeBark" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertBark(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag( v2f i ) : SV_Target { + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeLeaf" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertLeaf(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag( v2f i ) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float4 nz : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" } + Pass { + Cull Back +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return i.nz; +} +ENDCG + } + Pass { + Cull Front +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = -COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - _Cutoff ); + return i.nz; +} +ENDCG + } + +} + +SubShader { + Tags { "RenderType"="TreeBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert (appdata_tree_billboard v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv.x = v.texcoord.x; + o.uv.y = v.texcoord.y > 0; + o.nz.xyz = float3(0,0,1); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - 0.001 ); + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="GrassBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassBillboardVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return i.nz; +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="Grass" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return i.nz; +} +ENDCG + } +} + +Fallback Off +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraNormalsTexture.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraNormalsTexture.shader.meta new file mode 100644 index 0000000..88d7505 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/CameraNormalsTexture.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 537141eca02c6df4bb8b4f77567e9de2 +timeCreated: 1494092584 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor.meta new file mode 100644 index 0000000..c34b558 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7bebbafa671002646b3a7267b32a0d60 +folderAsset: yes +timeCreated: 1479419399 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor/SpineSpriteShaderGUI.cs b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor/SpineSpriteShaderGUI.cs new file mode 100644 index 0000000..9a982d5 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor/SpineSpriteShaderGUI.cs @@ -0,0 +1,973 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; + +using SpineInspectorUtility = Spine.Unity.Editor.SpineInspectorUtility; + +public class SpineSpriteShaderGUI : ShaderGUI { + static readonly string kShaderVertexLit = "Spine/Sprite/Vertex Lit"; + static readonly string kShaderPixelLit = "Spine/Sprite/Pixel Lit"; + static readonly string kShaderUnlit = "Spine/Sprite/Unlit"; + static readonly int kSolidQueue = 2000; + static readonly int kAlphaTestQueue = 2450; + static readonly int kTransparentQueue = 3000; + + private enum eBlendMode { + PreMultipliedAlpha, + StandardAlpha, + Opaque, + Additive, + SoftAdditive, + Multiply, + Multiplyx2, + }; + + private enum eLightMode { + VertexLit, + PixelLit, + Unlit, + }; + + private enum eCulling { + Off = 0, + Front = 1, + Back = 2, + }; + + private enum eNormalsMode { + MeshNormals = -1, + FixedNormalsViewSpace = 0, + FixedNormalsModelSpace = 1, + }; + + MaterialEditor _materialEditor; + + MaterialProperty _mainTexture = null; + MaterialProperty _color = null; + + MaterialProperty _pixelSnap = null; + + MaterialProperty _writeToDepth = null; + MaterialProperty _depthAlphaCutoff = null; + MaterialProperty _shadowAlphaCutoff = null; + MaterialProperty _renderQueue = null; + MaterialProperty _culling = null; + MaterialProperty _customRenderQueue = null; + + MaterialProperty _overlayColor = null; + MaterialProperty _hue = null; + MaterialProperty _saturation = null; + MaterialProperty _brightness = null; + + MaterialProperty _rimPower = null; + MaterialProperty _rimColor = null; + + MaterialProperty _bumpMap = null; + MaterialProperty _bumpScale = null; + MaterialProperty _diffuseRamp = null; + MaterialProperty _fixedNormal = null; + + MaterialProperty _blendTexture = null; + MaterialProperty _blendTextureLerp = null; + + MaterialProperty _emissionMap = null; + MaterialProperty _emissionColor = null; + MaterialProperty _emissionPower = null; + + MaterialProperty _metallic = null; + MaterialProperty _metallicGlossMap = null; + MaterialProperty _smoothness = null; + MaterialProperty _smoothnessScale = null; + + static GUIContent _albedoText = new GUIContent("Albedo", "Albedo (RGB) and Transparency (A)"); + static GUIContent _altAlbedoText = new GUIContent("Secondary Albedo", "When a secondary albedo texture is set the albedo will be a blended mix of the two textures based on the blend value."); + static GUIContent _metallicMapText = new GUIContent("Metallic", "Metallic (R) and Smoothness (A)"); + static GUIContent _smoothnessText = new GUIContent("Smoothness", "Smoothness value"); + static GUIContent _smoothnessScaleText = new GUIContent("Smoothness", "Smoothness scale factor"); + static GUIContent _normalMapText = new GUIContent("Normal Map", "Normal Map"); + static GUIContent _emissionText = new GUIContent("Emission", "Emission (RGB)"); + static GUIContent _emissionPowerText = new GUIContent("Emission Power"); + static GUIContent _emissionToggleText = new GUIContent("Emission", "Enable Emission."); + static GUIContent _diffuseRampText = new GUIContent("Diffuse Ramp", "A black and white gradient can be used to create a 'Toon Shading' effect."); + static GUIContent _depthText = new GUIContent("Write to Depth", "Write to Depth Buffer by clipping alpha."); + static GUIContent _depthAlphaCutoffText = new GUIContent("Depth Alpha Cutoff", "Threshold for depth write alpha cutoff"); + static GUIContent _shadowAlphaCutoffText = new GUIContent("Shadow Alpha Cutoff", "Threshold for shadow alpha cutoff"); + static GUIContent _fixedNormalText = new GUIContent("Fixed Normals", "If this is ticked instead of requiring mesh normals a Fixed Normal will be used instead (it's quicker and can result in better looking lighting effects on 2d objects)."); + static GUIContent _fixedNormalDirectionText = new GUIContent("Fixed Normal Direction", "Should normally be (0,0,1) if in view-space or (0,0,-1) if in model-space."); + static GUIContent _adjustBackfaceTangentText = new GUIContent("Adjust Back-face Tangents", "Tick only if you are going to rotate the sprite to face away from the camera, the tangents will be flipped when this is the case to make lighting correct."); + static GUIContent _sphericalHarmonicsText = new GUIContent("Spherical Harmonics", "Enable to use spherical harmonics to calculate ambient light / light probes. In vertex-lit mode this will be approximated from scenes ambient trilight settings."); + static GUIContent _lightingModeText = new GUIContent("Lighting Mode", "Lighting Mode"); + static GUIContent[] _lightingModeOptions = { + new GUIContent("Vertex Lit"), + new GUIContent("Pixel Lit"), + new GUIContent("Unlit") + }; + static GUIContent _blendModeText = new GUIContent("Blend Mode", "Blend Mode"); + static GUIContent[] _blendModeOptions = { + new GUIContent("Pre-Multiplied Alpha"), + new GUIContent("Standard Alpha"), + new GUIContent("Opaque"), + new GUIContent("Additive"), + new GUIContent("Soft Additive"), + new GUIContent("Multiply"), + new GUIContent("Multiply x2") + }; + static GUIContent _rendererQueueText = new GUIContent("Renderer Queue"); + static GUIContent _cullingModeText = new GUIContent("Culling Mode"); + static GUIContent[] _cullingModeOptions = { new GUIContent("Off"), new GUIContent("Front"), new GUIContent("Back") }; + static GUIContent _pixelSnapText = new GUIContent("Pixel Snap"); + //static GUIContent _customRenderTypetagsText = new GUIContent("Use Custom RenderType tags"); + static GUIContent _fixedNormalSpaceText = new GUIContent("Fixed Normal Space"); + static GUIContent[] _fixedNormalSpaceOptions = { new GUIContent("View-Space"), new GUIContent("Model-Space") }; + static GUIContent _rimLightingToggleText = new GUIContent("Rim Lighting", "Enable Rim Lighting."); + static GUIContent _rimColorText = new GUIContent("Rim Color"); + static GUIContent _rimPowerText = new GUIContent("Rim Power"); + static GUIContent _specularToggleText = new GUIContent("Specular", "Enable Specular."); + static GUIContent _colorAdjustmentToggleText = new GUIContent("Color Adjustment", "Enable material color adjustment."); + static GUIContent _colorAdjustmentColorText = new GUIContent("Overlay Color"); + static GUIContent _colorAdjustmentHueText = new GUIContent("Hue"); + static GUIContent _colorAdjustmentSaturationText = new GUIContent("Saturation"); + static GUIContent _colorAdjustmentBrightnessText = new GUIContent("Brightness"); + static GUIContent _fogToggleText = new GUIContent("Fog", "Enable Fog rendering on this renderer."); + static GUIContent _meshRequiresTangentsText = new GUIContent("Note: Material requires a mesh with tangents."); + static GUIContent _meshRequiresNormalsText = new GUIContent("Note: Material requires a mesh with normals."); + static GUIContent _meshRequiresNormalsAndTangentsText = new GUIContent("Note: Material requires a mesh with Normals and Tangents."); + + const string _primaryMapsText = "Main Maps"; + const string _depthLabelText = "Depth"; + const string _shadowsText = "Shadows"; + const string _customRenderType = "Use Custom RenderType"; + + #region ShaderGUI + + public override void OnGUI (MaterialEditor materialEditor, MaterialProperty[] properties) { + FindProperties(properties); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly + _materialEditor = materialEditor; + ShaderPropertiesGUI(); + } + + public override void AssignNewShaderToMaterial (Material material, Shader oldShader, Shader newShader) { + base.AssignNewShaderToMaterial(material, oldShader, newShader); + + //If not originally a sprite shader set default keywords + if (oldShader.name != kShaderVertexLit && oldShader.name != kShaderPixelLit && oldShader.name != kShaderUnlit) { + SetDefaultSpriteKeywords(material, newShader); + } + + SetMaterialKeywords(material); + } + + #endregion + + #region Virtual Interface + + protected virtual void FindProperties (MaterialProperty[] props) { + _mainTexture = FindProperty("_MainTex", props); + _color = FindProperty("_Color", props); + + _pixelSnap = FindProperty("PixelSnap", props); + + _writeToDepth = FindProperty("_ZWrite", props); + _depthAlphaCutoff = FindProperty("_Cutoff", props); + _shadowAlphaCutoff = FindProperty("_ShadowAlphaCutoff", props); + _renderQueue = FindProperty("_RenderQueue", props); + _culling = FindProperty("_Cull", props); + _customRenderQueue = FindProperty("_CustomRenderQueue", props); + + _bumpMap = FindProperty("_BumpMap", props, false); + _bumpScale = FindProperty("_BumpScale", props, false); + _diffuseRamp = FindProperty("_DiffuseRamp", props, false); + _fixedNormal = FindProperty("_FixedNormal", props, false); + _blendTexture = FindProperty("_BlendTex", props, false); + _blendTextureLerp = FindProperty("_BlendAmount", props, false); + + _overlayColor = FindProperty("_OverlayColor", props, false); + _hue = FindProperty("_Hue", props, false); + _saturation = FindProperty("_Saturation", props, false); + _brightness = FindProperty("_Brightness", props, false); + + _rimPower = FindProperty("_RimPower", props, false); + _rimColor = FindProperty("_RimColor", props, false); + + _emissionMap = FindProperty("_EmissionMap", props, false); + _emissionColor = FindProperty("_EmissionColor", props, false); + _emissionPower = FindProperty("_EmissionPower", props, false); + + _metallic = FindProperty("_Metallic", props, false); + _metallicGlossMap = FindProperty("_MetallicGlossMap", props, false); + _smoothness = FindProperty("_Glossiness", props, false); + _smoothnessScale = FindProperty("_GlossMapScale", props, false); + } + + static bool BoldToggleField (GUIContent label, bool value) { + FontStyle origFontStyle = EditorStyles.label.fontStyle; + EditorStyles.label.fontStyle = FontStyle.Bold; + value = EditorGUILayout.Toggle(label, value, EditorStyles.toggle); + EditorStyles.label.fontStyle = origFontStyle; + return value; + } + + protected virtual void ShaderPropertiesGUI () { + // Use default labelWidth + EditorGUIUtility.labelWidth = 0f; + + RenderMeshInfoBox(); + + // Detect any changes to the material + bool dataChanged = RenderModes(); + + GUILayout.Label(_primaryMapsText, EditorStyles.boldLabel); + { + dataChanged |= RenderTextureProperties(); + } + + GUILayout.Label(_depthLabelText, EditorStyles.boldLabel); + { + dataChanged |= RenderDepthProperties(); + } + + GUILayout.Label(_shadowsText, EditorStyles.boldLabel); + { + dataChanged |= RenderShadowsProperties(); + } + + if (_metallic != null) { + dataChanged |= RenderSpecularProperties(); + } + + if (_emissionMap != null && _emissionColor != null) { + dataChanged |= RenderEmissionProperties(); + } + + if (_fixedNormal != null) { + dataChanged |= RenderNormalsProperties(); + } + + if (_fixedNormal != null) { + dataChanged |= RenderSphericalHarmonicsProperties(); + } + + { + dataChanged |= RenderFogProperties(); + } + + { + dataChanged |= RenderColorProperties(); + } + + if (_rimColor != null) { + dataChanged |= RenderRimLightingProperties(); + } + + if (dataChanged) { + MaterialChanged(_materialEditor); + } + } + + protected virtual bool RenderModes () { + bool dataChanged = false; + + //Lighting Mode + { + EditorGUI.BeginChangeCheck(); + + eLightMode lightMode = GetMaterialLightMode((Material)_materialEditor.target); + EditorGUI.showMixedValue = false; + foreach (Material material in _materialEditor.targets) { + if (lightMode != GetMaterialLightMode(material)) { + EditorGUI.showMixedValue = true; + break; + } + } + + lightMode = (eLightMode)EditorGUILayout.Popup(_lightingModeText, (int)lightMode, _lightingModeOptions); + if (EditorGUI.EndChangeCheck()) { + foreach (Material material in _materialEditor.targets) { + switch (lightMode) { + case eLightMode.VertexLit: + if (material.shader.name != kShaderVertexLit) + _materialEditor.SetShader(Shader.Find(kShaderVertexLit), false); + break; + case eLightMode.PixelLit: + if (material.shader.name != kShaderPixelLit) + _materialEditor.SetShader(Shader.Find(kShaderPixelLit), false); + break; + case eLightMode.Unlit: + if (material.shader.name != kShaderUnlit) + _materialEditor.SetShader(Shader.Find(kShaderUnlit), false); + break; + } + } + + dataChanged = true; + } + } + + //Blend Mode + { + eBlendMode blendMode = GetMaterialBlendMode((Material)_materialEditor.target); + EditorGUI.showMixedValue = false; + foreach (Material material in _materialEditor.targets) { + if (blendMode != GetMaterialBlendMode(material)) { + EditorGUI.showMixedValue = true; + break; + } + } + + EditorGUI.BeginChangeCheck(); + blendMode = (eBlendMode)EditorGUILayout.Popup(_blendModeText, (int)blendMode, _blendModeOptions); + if (EditorGUI.EndChangeCheck()) { + foreach (Material mat in _materialEditor.targets) { + SetBlendMode(mat, blendMode); + } + + dataChanged = true; + } + } + + // GUILayout.Label(Styles.advancedText, EditorStyles.boldLabel); + // m_MaterialEditor.RenderQueueField(); + // m_MaterialEditor.EnableInstancingField(); + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = _renderQueue.hasMixedValue; + int renderQueue = EditorGUILayout.IntSlider(_rendererQueueText, (int)_renderQueue.floatValue, 0, 49); + if (EditorGUI.EndChangeCheck()) { + SetInt("_RenderQueue", renderQueue); + dataChanged = true; + } + + EditorGUI.BeginChangeCheck(); + var culling = (eCulling)Mathf.RoundToInt(_culling.floatValue); + EditorGUI.showMixedValue = _culling.hasMixedValue; + culling = (eCulling)EditorGUILayout.Popup(_cullingModeText, (int)culling, _cullingModeOptions); + if (EditorGUI.EndChangeCheck()) { + SetInt("_Cull", (int)culling); + dataChanged = true; + } + + EditorGUI.showMixedValue = false; + + EditorGUI.BeginChangeCheck(); + _materialEditor.ShaderProperty(_pixelSnap, _pixelSnapText); + dataChanged |= EditorGUI.EndChangeCheck(); + + return dataChanged; + } + + protected virtual bool RenderTextureProperties () { + bool dataChanged = false; + + EditorGUI.BeginChangeCheck(); + + _materialEditor.TexturePropertySingleLine(_albedoText, _mainTexture, _color); + + if (_bumpMap != null) + _materialEditor.TexturePropertySingleLine(_normalMapText, _bumpMap, _bumpMap.textureValue != null ? _bumpScale : null); + + if (_diffuseRamp != null) + _materialEditor.TexturePropertySingleLine(_diffuseRampText, _diffuseRamp); + + dataChanged |= EditorGUI.EndChangeCheck(); + + if (_blendTexture != null) { + EditorGUI.BeginChangeCheck(); + _materialEditor.TexturePropertySingleLine(_altAlbedoText, _blendTexture, _blendTextureLerp); + if (EditorGUI.EndChangeCheck()) { + SetKeyword(_materialEditor, "_TEXTURE_BLEND", _blendTexture != null); + dataChanged = true; + } + } + + EditorGUI.BeginChangeCheck(); + _materialEditor.TextureScaleOffsetProperty(_mainTexture); + dataChanged |= EditorGUI.EndChangeCheck(); + + EditorGUI.showMixedValue = false; + + return dataChanged; + } + + protected virtual bool RenderDepthProperties () { + bool dataChanged = false; + + EditorGUI.BeginChangeCheck(); + + bool mixedValue = _writeToDepth.hasMixedValue; + EditorGUI.showMixedValue = mixedValue; + bool writeTodepth = EditorGUILayout.Toggle(_depthText, _writeToDepth.floatValue != 0.0f); + + if (EditorGUI.EndChangeCheck()) { + SetInt("_ZWrite", writeTodepth ? 1 : 0); + _depthAlphaCutoff.floatValue = writeTodepth ? 0.5f : 0.0f; + mixedValue = false; + dataChanged = true; + } + + if (writeTodepth && !mixedValue && GetMaterialBlendMode((Material)_materialEditor.target) != eBlendMode.Opaque) { + EditorGUI.BeginChangeCheck(); + _materialEditor.RangeProperty(_depthAlphaCutoff, _depthAlphaCutoffText.text); + dataChanged |= EditorGUI.EndChangeCheck(); + } + + { + bool useCustomRenderType = _customRenderQueue.floatValue > 0.0f; + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = _customRenderQueue.hasMixedValue; + useCustomRenderType = EditorGUILayout.Toggle(_customRenderType, useCustomRenderType); + if (EditorGUI.EndChangeCheck()) { + dataChanged = true; + + _customRenderQueue.floatValue = useCustomRenderType ? 1.0f : 0.0f; + + foreach (Material material in _materialEditor.targets) { + eBlendMode blendMode = GetMaterialBlendMode(material); + + switch (blendMode) { + case eBlendMode.Opaque: + { + SetRenderType(material, "Opaque", useCustomRenderType); + } + break; + default: + { + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + SetRenderType(material, zWrite ? "TransparentCutout" : "Transparent", useCustomRenderType); + } + break; + } + } + } + } + + EditorGUI.showMixedValue = false; + + return dataChanged; + } + + protected virtual bool RenderNormalsProperties () { + bool dataChanged = false; + + eNormalsMode normalsMode = GetMaterialNormalsMode((Material)_materialEditor.target); + bool mixedNormalsMode = false; + foreach (Material material in _materialEditor.targets) { + if (normalsMode != GetMaterialNormalsMode(material)) { + mixedNormalsMode = true; + break; + } + } + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = mixedNormalsMode; + bool fixedNormals = BoldToggleField(_fixedNormalText, normalsMode != eNormalsMode.MeshNormals); + + if (EditorGUI.EndChangeCheck()) { + normalsMode = fixedNormals ? eNormalsMode.FixedNormalsViewSpace : eNormalsMode.MeshNormals; + SetNormalsMode(_materialEditor, normalsMode, false); + _fixedNormal.vectorValue = new Vector4(0.0f, 0.0f, normalsMode == eNormalsMode.FixedNormalsViewSpace ? 1.0f : -1.0f, 1.0f); + mixedNormalsMode = false; + dataChanged = true; + } + + if (fixedNormals) { + //Show drop down for normals space + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = mixedNormalsMode; + normalsMode = (eNormalsMode)EditorGUILayout.Popup(_fixedNormalSpaceText, (int)normalsMode, _fixedNormalSpaceOptions); + if (EditorGUI.EndChangeCheck()) { + SetNormalsMode((Material)_materialEditor.target, normalsMode, GetMaterialFixedNormalsBackfaceRenderingOn((Material)_materialEditor.target)); + + foreach (Material material in _materialEditor.targets) { + SetNormalsMode(material, normalsMode, GetMaterialFixedNormalsBackfaceRenderingOn(material)); + } + + //Reset fixed normal to default (Vector3.forward for model-space, -Vector3.forward for view-space). + _fixedNormal.vectorValue = new Vector4(0.0f, 0.0f, normalsMode == eNormalsMode.FixedNormalsViewSpace ? 1.0f : -1.0f, 1.0f); + + mixedNormalsMode = false; + dataChanged = true; + } + + //Show fixed normal + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = _fixedNormal.hasMixedValue; + Vector3 normal = EditorGUILayout.Vector3Field(_fixedNormalDirectionText, _fixedNormal.vectorValue); + if (EditorGUI.EndChangeCheck()) { + _fixedNormal.vectorValue = new Vector4(normal.x, normal.y, normal.z, 1.0f); + dataChanged = true; + } + + //Show adjust for back face rendering + { + bool fixBackFaceRendering = GetMaterialFixedNormalsBackfaceRenderingOn((Material)_materialEditor.target); + bool mixedBackFaceRendering = false; + foreach (Material material in _materialEditor.targets) { + if (fixBackFaceRendering != GetMaterialFixedNormalsBackfaceRenderingOn(material)) { + mixedBackFaceRendering = true; + break; + } + } + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = mixedBackFaceRendering; + bool backRendering = EditorGUILayout.Toggle(_adjustBackfaceTangentText, fixBackFaceRendering); + + if (EditorGUI.EndChangeCheck()) { + SetNormalsMode(_materialEditor, normalsMode, backRendering); + dataChanged = true; + } + } + + } + + EditorGUI.showMixedValue = false; + + return dataChanged; + } + + protected virtual bool RenderShadowsProperties () { + EditorGUI.BeginChangeCheck(); + _materialEditor.RangeProperty(_shadowAlphaCutoff, _shadowAlphaCutoffText.text); + return EditorGUI.EndChangeCheck(); + } + + protected virtual bool RenderSphericalHarmonicsProperties () { + EditorGUI.BeginChangeCheck(); + bool mixedValue; + bool enabled = IsKeywordEnabled(_materialEditor, "_SPHERICAL_HARMONICS", out mixedValue); + EditorGUI.showMixedValue = mixedValue; + enabled = BoldToggleField(_sphericalHarmonicsText, enabled); + EditorGUI.showMixedValue = false; + + if (EditorGUI.EndChangeCheck()) { + SetKeyword(_materialEditor, "_SPHERICAL_HARMONICS", enabled); + return true; + } + + return false; + } + + protected virtual bool RenderFogProperties () { + EditorGUI.BeginChangeCheck(); + bool mixedValue; + bool fog = IsKeywordEnabled(_materialEditor, "_FOG", out mixedValue); + EditorGUI.showMixedValue = mixedValue; + fog = BoldToggleField(_fogToggleText, fog); + EditorGUI.showMixedValue = false; + + if (EditorGUI.EndChangeCheck()) { + SetKeyword(_materialEditor, "_FOG", fog); + return true; + } + + return false; + } + + protected virtual bool RenderColorProperties () { + bool dataChanged = false; + + EditorGUI.BeginChangeCheck(); + bool mixedValue; + bool colorAdjust = IsKeywordEnabled(_materialEditor, "_COLOR_ADJUST", out mixedValue); + EditorGUI.showMixedValue = mixedValue; + colorAdjust = BoldToggleField(_colorAdjustmentToggleText, colorAdjust); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) { + SetKeyword(_materialEditor, "_COLOR_ADJUST", colorAdjust); + mixedValue = false; + dataChanged = true; + } + + if (colorAdjust && !mixedValue) { + EditorGUI.BeginChangeCheck(); + _materialEditor.ColorProperty(_overlayColor, _colorAdjustmentColorText.text); + _materialEditor.RangeProperty(_hue, _colorAdjustmentHueText.text); + _materialEditor.RangeProperty(_saturation, _colorAdjustmentSaturationText.text); + _materialEditor.RangeProperty(_brightness, _colorAdjustmentBrightnessText.text); + dataChanged |= EditorGUI.EndChangeCheck(); + } + + return dataChanged; + } + + protected virtual bool RenderSpecularProperties () { + bool dataChanged = false; + + bool mixedSpecularValue; + bool specular = IsKeywordEnabled(_materialEditor, "_SPECULAR", out mixedSpecularValue); + bool mixedSpecularGlossMapValue; + bool specularGlossMap = IsKeywordEnabled(_materialEditor, "_SPECULAR_GLOSSMAP", out mixedSpecularGlossMapValue); + bool mixedValue = mixedSpecularValue || mixedSpecularGlossMapValue; + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = mixedValue; + bool specularEnabled = BoldToggleField(_specularToggleText, specular || specularGlossMap); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) { + foreach (Material material in _materialEditor.targets) { + bool hasGlossMap = material.GetTexture("_MetallicGlossMap") != null; + SetKeyword(material, "_SPECULAR", specularEnabled && !hasGlossMap); + SetKeyword(material, "_SPECULAR_GLOSSMAP", specularEnabled && hasGlossMap); + } + + mixedValue = false; + dataChanged = true; + } + + if (specularEnabled && !mixedValue) { + EditorGUI.BeginChangeCheck(); + bool hasGlossMap = _metallicGlossMap.textureValue != null; + _materialEditor.TexturePropertySingleLine(_metallicMapText, _metallicGlossMap, hasGlossMap ? null : _metallic); + if (EditorGUI.EndChangeCheck()) { + hasGlossMap = _metallicGlossMap.textureValue != null; + SetKeyword(_materialEditor, "_SPECULAR", !hasGlossMap); + SetKeyword(_materialEditor, "_SPECULAR_GLOSSMAP", hasGlossMap); + + dataChanged = true; + } + + const int indentation = 2; + _materialEditor.ShaderProperty(hasGlossMap ? _smoothnessScale : _smoothness, hasGlossMap ? _smoothnessScaleText : _smoothnessText, indentation); + } + + return dataChanged; + } + + protected virtual bool RenderEmissionProperties () { + bool dataChanged = false; + + bool mixedValue; + bool emission = IsKeywordEnabled(_materialEditor, "_EMISSION", out mixedValue); + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = mixedValue; + emission = BoldToggleField(_emissionToggleText, emission); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) { + SetKeyword(_materialEditor, "_EMISSION", emission); + mixedValue = false; + dataChanged = true; + } + + if (emission && !mixedValue) { + EditorGUI.BeginChangeCheck(); + +#if UNITY_2018 + _materialEditor.TexturePropertyWithHDRColor(_emissionText, _emissionMap, _emissionColor, true); +#else + _materialEditor.TexturePropertyWithHDRColor(_emissionText, _emissionMap, _emissionColor, new ColorPickerHDRConfig(0, 1, 0.01010101f, 3), true); +#endif + _materialEditor.FloatProperty(_emissionPower, _emissionPowerText.text); + dataChanged |= EditorGUI.EndChangeCheck(); + } + + return dataChanged; + } + + protected virtual bool RenderRimLightingProperties () { + bool dataChanged = false; + + bool mixedValue; + bool rimLighting = IsKeywordEnabled(_materialEditor, "_RIM_LIGHTING", out mixedValue); + + EditorGUI.BeginChangeCheck(); + EditorGUI.showMixedValue = mixedValue; + rimLighting = BoldToggleField(_rimLightingToggleText, rimLighting); + EditorGUI.showMixedValue = false; + if (EditorGUI.EndChangeCheck()) { + SetKeyword(_materialEditor, "_RIM_LIGHTING", rimLighting); + mixedValue = false; + dataChanged = true; + } + + if (rimLighting && !mixedValue) { + EditorGUI.BeginChangeCheck(); + _materialEditor.ColorProperty(_rimColor, _rimColorText.text); + _materialEditor.FloatProperty(_rimPower, _rimPowerText.text); + dataChanged |= EditorGUI.EndChangeCheck(); + } + + return dataChanged; + } + +#endregion + +#region Private Functions + + void RenderMeshInfoBox () { + var material = (Material)_materialEditor.target; + bool requiresNormals = _fixedNormal != null && GetMaterialNormalsMode(material) == eNormalsMode.MeshNormals; + bool requiresTangents = material.HasProperty("_BumpMap") && material.GetTexture("_BumpMap") != null; + + if (requiresNormals || requiresTangents) { + GUILayout.Label(requiresNormals && requiresTangents ? _meshRequiresNormalsAndTangentsText : requiresNormals ? _meshRequiresNormalsText : _meshRequiresTangentsText, GUI.skin.GetStyle("helpBox")); + } + } + + void SetInt (string propertyName, int value) { + foreach (Material material in _materialEditor.targets) { + material.SetInt(propertyName, value); + } + } + + void SetDefaultSpriteKeywords (Material material, Shader shader) { + //Disable emission by default (is set on by default in standard shader) + SetKeyword(material, "_EMISSION", false); + //Start with preMultiply alpha by default + SetBlendMode(material, eBlendMode.PreMultipliedAlpha); + //Start with mesh normals by default + SetNormalsMode(material, eNormalsMode.MeshNormals, false); + if (_fixedNormal != null) + _fixedNormal.vectorValue = new Vector4(0.0f, 0.0f, 1.0f, 1.0f); + //Start with spherical harmonics disabled? + SetKeyword(material, "_SPHERICAL_HARMONICS", false); + //Start with specular disabled + SetKeyword(material, "_SPECULAR", false); + SetKeyword(material, "_SPECULAR_GLOSSMAP", false); + //Start with Culling disabled + material.SetInt("_Cull", (int)eCulling.Off); + //Start with Z writing disabled + material.SetInt("_ZWrite", 0); + } + + //Z write is on then + + static void SetRenderType (Material material, string renderType, bool useCustomRenderQueue) { + //Want a check box to say if should use Sprite render queue (for custom writing depth and normals) + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + + if (useCustomRenderQueue) { + //If sprite has fixed normals then assign custom render type so we can write its correct normal with soft edges + eNormalsMode normalsMode = GetMaterialNormalsMode(material); + + switch (normalsMode) { + case eNormalsMode.FixedNormalsViewSpace: + renderType = "SpriteViewSpaceFixedNormal"; + break; + case eNormalsMode.FixedNormalsModelSpace: + renderType = "SpriteModelSpaceFixedNormal"; + break; + case eNormalsMode.MeshNormals: + { + //If sprite doesn't write to depth assign custom render type so we can write its depth with soft edges + if (!zWrite) { + renderType = "Sprite"; + } + } + break; + } + } + + //If we don't write to depth set tag so custom shaders can write to depth themselves + material.SetOverrideTag("AlphaDepth", zWrite ? "False" : "True"); + + material.SetOverrideTag("RenderType", renderType); + } + + static void SetMaterialKeywords (Material material) { + eBlendMode blendMode = GetMaterialBlendMode(material); + SetBlendMode(material, blendMode); + + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + bool clipAlpha = zWrite && blendMode != eBlendMode.Opaque && material.GetFloat("_Cutoff") > 0.0f; + SetKeyword(material, "_ALPHA_CLIP", clipAlpha); + + bool normalMap = material.HasProperty("_BumpMap") && material.GetTexture("_BumpMap") != null; + SetKeyword(material, "_NORMALMAP", normalMap); + + bool diffuseRamp = material.HasProperty("_DiffuseRamp") && material.GetTexture("_DiffuseRamp") != null; + SetKeyword(material, "_DIFFUSE_RAMP", diffuseRamp); + + bool blendTexture = material.HasProperty("_BlendTex") && material.GetTexture("_BlendTex") != null; + SetKeyword(material, "_TEXTURE_BLEND", blendTexture); + } + + static void MaterialChanged (MaterialEditor materialEditor) { + foreach (Material material in materialEditor.targets) + SetMaterialKeywords(material); + } + + static void SetKeyword (MaterialEditor m, string keyword, bool state) { + foreach (Material material in m.targets) { + SetKeyword(material, keyword, state); + } + } + + static void SetKeyword (Material m, string keyword, bool state) { + if (state) + m.EnableKeyword(keyword); + else + m.DisableKeyword(keyword); + } + + static bool IsKeywordEnabled (MaterialEditor editor, string keyword, out bool mixedValue) { + bool keywordEnabled = ((Material)editor.target).IsKeywordEnabled(keyword); + mixedValue = false; + + foreach (Material material in editor.targets) { + if (material.IsKeywordEnabled(keyword) != keywordEnabled) { + mixedValue = true; + break; + } + } + + return keywordEnabled; + } + + static eLightMode GetMaterialLightMode (Material material) { + if (material.shader.name == kShaderPixelLit) { + return eLightMode.PixelLit; + } else if (material.shader.name == kShaderUnlit) { + return eLightMode.Unlit; + } else { + return eLightMode.VertexLit; + } + } + + static eBlendMode GetMaterialBlendMode (Material material) { + if (material.IsKeywordEnabled("_ALPHABLEND_ON")) + return eBlendMode.StandardAlpha; + if (material.IsKeywordEnabled("_ALPHAPREMULTIPLY_ON")) + return eBlendMode.PreMultipliedAlpha; + if (material.IsKeywordEnabled("_MULTIPLYBLEND")) + return eBlendMode.Multiply; + if (material.IsKeywordEnabled("_MULTIPLYBLEND_X2")) + return eBlendMode.Multiplyx2; + if (material.IsKeywordEnabled("_ADDITIVEBLEND")) + return eBlendMode.Additive; + if (material.IsKeywordEnabled("_ADDITIVEBLEND_SOFT")) + return eBlendMode.SoftAdditive; + + return eBlendMode.Opaque; + } + + static void SetBlendMode (Material material, eBlendMode blendMode) { + SetKeyword(material, "_ALPHABLEND_ON", blendMode == eBlendMode.StandardAlpha); + SetKeyword(material, "_ALPHAPREMULTIPLY_ON", blendMode == eBlendMode.PreMultipliedAlpha); + SetKeyword(material, "_MULTIPLYBLEND", blendMode == eBlendMode.Multiply); + SetKeyword(material, "_MULTIPLYBLEND_X2", blendMode == eBlendMode.Multiplyx2); + SetKeyword(material, "_ADDITIVEBLEND", blendMode == eBlendMode.Additive); + SetKeyword(material, "_ADDITIVEBLEND_SOFT", blendMode == eBlendMode.SoftAdditive); + + int renderQueue; + bool useCustomRenderQueue = material.GetFloat("_CustomRenderQueue") > 0.0f; + + switch (blendMode) { + case eBlendMode.Opaque: + { + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); + SetRenderType(material, "Opaque", useCustomRenderQueue); + renderQueue = kSolidQueue; + } + break; + case eBlendMode.Additive: + { + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One); + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + SetRenderType(material, zWrite ? "TransparentCutout" : "Transparent", useCustomRenderQueue); + renderQueue = zWrite ? kAlphaTestQueue : kTransparentQueue; + } + break; + case eBlendMode.SoftAdditive: + { + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcColor); + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + SetRenderType(material, zWrite ? "TransparentCutout" : "Transparent", useCustomRenderQueue); + renderQueue = zWrite ? kAlphaTestQueue : kTransparentQueue; + } + break; + case eBlendMode.Multiply: + { + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.Zero); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.SrcColor); + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + SetRenderType(material, zWrite ? "TransparentCutout" : "Transparent", useCustomRenderQueue); + renderQueue = zWrite ? kAlphaTestQueue : kTransparentQueue; + } + break; + case eBlendMode.Multiplyx2: + { + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.DstColor); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.SrcColor); + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + SetRenderType(material, zWrite ? "TransparentCutout" : "Transparent", useCustomRenderQueue); + renderQueue = zWrite ? kAlphaTestQueue : kTransparentQueue; + } + break; + default: + { + material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); + material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + bool zWrite = material.GetFloat("_ZWrite") > 0.0f; + SetRenderType(material, zWrite ? "TransparentCutout" : "Transparent", useCustomRenderQueue); + renderQueue = zWrite ? kAlphaTestQueue : kTransparentQueue; + } + break; + } + + material.renderQueue = renderQueue + material.GetInt("_RenderQueue"); + material.SetOverrideTag("IgnoreProjector", blendMode == eBlendMode.Opaque ? "False" : "True"); + } + + static eNormalsMode GetMaterialNormalsMode (Material material) { + if (material.IsKeywordEnabled("_FIXED_NORMALS_VIEWSPACE") || material.IsKeywordEnabled("_FIXED_NORMALS_VIEWSPACE_BACKFACE")) + return eNormalsMode.FixedNormalsViewSpace; + if (material.IsKeywordEnabled("_FIXED_NORMALS_MODELSPACE") || material.IsKeywordEnabled("_FIXED_NORMALS_MODELSPACE_BACKFACE")) + return eNormalsMode.FixedNormalsModelSpace; + + return eNormalsMode.MeshNormals; + } + + static void SetNormalsMode (MaterialEditor materialEditor, eNormalsMode normalsMode, bool allowBackFaceRendering) { + SetNormalsMode((Material)materialEditor.target, normalsMode, allowBackFaceRendering); + + foreach (Material material in materialEditor.targets) { + SetNormalsMode(material, normalsMode, allowBackFaceRendering); + } + } + + static void SetNormalsMode (Material material, eNormalsMode normalsMode, bool allowBackFaceRendering) { + SetKeyword(material, "_FIXED_NORMALS_VIEWSPACE", normalsMode == eNormalsMode.FixedNormalsViewSpace && !allowBackFaceRendering); + SetKeyword(material, "_FIXED_NORMALS_VIEWSPACE_BACKFACE", normalsMode == eNormalsMode.FixedNormalsViewSpace && allowBackFaceRendering); + SetKeyword(material, "_FIXED_NORMALS_MODELSPACE", normalsMode == eNormalsMode.FixedNormalsModelSpace && !allowBackFaceRendering); + SetKeyword(material, "_FIXED_NORMALS_MODELSPACE_BACKFACE", normalsMode == eNormalsMode.FixedNormalsModelSpace && allowBackFaceRendering); + } + + static bool GetMaterialFixedNormalsBackfaceRenderingOn (Material material) { + return material.IsKeywordEnabled("_FIXED_NORMALS_VIEWSPACE_BACKFACE") || material.IsKeywordEnabled("_FIXED_NORMALS_MODELSPACE_BACKFACE"); + } + +#endregion +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor/SpineSpriteShaderGUI.cs.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor/SpineSpriteShaderGUI.cs.meta new file mode 100644 index 0000000..2215e75 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/Editor/SpineSpriteShaderGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: aef90b4c481362e42891bb46de344c1c +timeCreated: 1479458475 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/README.md b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/README.md new file mode 100644 index 0000000..15d7899 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/README.md @@ -0,0 +1,46 @@ +Contributed by ToddRivers + +# Unity Sprite Shaders +An Uber Shader specialised for rendering Sprites in Unity. +Even though it's designed for Sprites it can be used for a whole range of uses. It supports a wide range of optional shader features that won't effect performance unless they are used. +It also supports per-pixel effects such as normal maps and diffuse ramping whilst using Vertex Lit rendering. + +### Lighting +The shaders support lighting using both Forward Rendering and Vertex Lit Rendering. +Forward rendering is more accurate but is slower and crucially means the sprite has to write to depth using alpha clipping to avoid overdraw. +Vertex lit means all lighting can be done in one pass meaning full alpha can be used. + +### Normal Mapping +Normals maps are supported in both lighting modes (in Vertex Lit rendering data for normal mapping is packed into texture channels and then processed per pixel). + +### Blend Modes +Easily switch between blend modes including pre-multiplied alpha, additive, multiply etc. + +### Rim Lighting +Camera-space rim lighting is supported in both lighting modes. + +### Diffuse Ramp +A ramp texture is optionally supported for toon shading effects. + +### Shadows +Shadows are supported using alpha clipping. + +### Gradient based Ambient lighting +Both lighting modes support using a gradient for ambient light. In Vertex Lit mode the Spherical Harmonics is approximated from the ground, equator and sky colors. + +### Emission Map +An optional emission map is supported. + +### Camera Space Normals +As sprites are 2d their normals will always be constant. The shaders allow you to define a fixed normal in camera space rather than pass through mesh normals. +This not only saves vertex throughput but means lighting looks less 'flat' for rendering sprites with a perspective camera. + +### Color Adjustment +The shaders allow optional adjustment of hue / saturation and brightness as well as applying a solid color overlay effect for flashing a sprite to a solid color (eg. for damage effects). + +### Fog +Fog is optionally supported + + +## To Use +On your object's material click the drop down for shader and select Spine\Sprite\Pixel Lit, Vertex Lit or Unlit. diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/README.md.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/README.md.meta new file mode 100644 index 0000000..78cb908 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/README.md.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cecd0ea162097a94c89a97af6baf0a66 +timeCreated: 1479457854 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/ShaderShared.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/ShaderShared.cginc new file mode 100644 index 0000000..65d6f27 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/ShaderShared.cginc @@ -0,0 +1,363 @@ +#ifndef SHADER_SHARED_INCLUDED +#define SHADER_SHARED_INCLUDED + +#include "UnityCG.cginc" + +//////////////////////////////////////// +// Space functions +// + +inline float4 calculateWorldPos(float4 vertex) +{ + return mul(unity_ObjectToWorld, vertex); +} + +inline float4 calculateLocalPos(float4 vertex) +{ + return UnityObjectToClipPos(vertex); +} + +inline half3 calculateWorldNormal(float3 normal) +{ + return UnityObjectToWorldNormal(normal); +} + +//////////////////////////////////////// +// Maths functions +// + +inline float dotClamped(float3 a, float3 b) +{ + #if (SHADER_TARGET < 30 || defined(SHADER_API_PS3)) + return saturate(dot(a, b)); + #else + return max(0.0h, dot(a, b)); + #endif +} + +inline float oneDividedBy(float value) +{ + //Catches NANs + float sign_value = sign(value); + float sign_value_squared = sign_value*sign_value; + return sign_value_squared / ( value + sign_value_squared - 1.0); +} + +inline float4 quat_from_axis_angle(float3 axis, float angleRadians) +{ + float4 qr; + float half_angle = (angleRadians * 0.5); + qr.x = axis.x * sin(half_angle); + qr.y = axis.y * sin(half_angle); + qr.z = axis.z * sin(half_angle); + qr.w = cos(half_angle); + return qr; +} + +inline float3 rotate_vertex_position(float3 position, float3 axis, float angleRadians) +{ + float4 q = quat_from_axis_angle(axis, angleRadians); + float3 v = position.xyz; + return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); +} + +//////////////////////////////////////// +// Normal map functions +// + +#if defined(_NORMALMAP) + +uniform sampler2D _BumpMap; + +inline half3 calculateWorldTangent(float4 tangent) +{ + return UnityObjectToWorldDir(tangent); +} + +inline half3 calculateWorldBinormal(half3 normalWorld, half3 tangentWorld, float tangentW) +{ + // For odd-negative scale transforms we need to flip the binormal + return cross(normalWorld, tangentWorld.xyz) * tangentW * unity_WorldTransformParams.w; +} + +inline half3 calculateNormalFromBumpMap(float2 texUV, half3 tangentWorld, half3 binormalWorld, half3 normalWorld) +{ + half3 localNormal = UnpackNormal(tex2D(_BumpMap, texUV)); + half3x3 rotation = half3x3(tangentWorld, binormalWorld, normalWorld); + half3 normal = normalize(mul(localNormal, rotation)); + return normal; +} + +#endif // _NORMALMAP + +//////////////////////////////////////// +// Blending functions +// + +inline fixed4 calculateLitPixel(fixed4 texureColor, fixed4 color, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor * color; + finalPixel.rgb *= lighting * color.a; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = color * texureColor; + finalPixel.rgb *= lighting; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * color.rgb * lighting * 2.0f; + finalPixel.a = color.a * texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f * color; + finalPixel.rgb *= lighting * color.a; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = texureColor * color; + finalPixel.rgb *= lighting * finalPixel.a; +#else + finalPixel.a = texureColor.a * color.a; + finalPixel.rgb = texureColor.rgb * color.rgb * (lighting * finalPixel.a); +#endif + + return finalPixel; +} + +inline fixed4 calculateLitPixel(fixed4 texureColor, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor; + finalPixel.rgb *= lighting; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = texureColor; + finalPixel.rgb *= lighting; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * lighting * 2.0f; + finalPixel.a = texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f; + finalPixel.rgb *= lighting; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = texureColor; + finalPixel.rgb *= lighting * finalPixel.a; +#else + finalPixel.a = texureColor.a; + finalPixel.rgb = texureColor.rgb * (lighting * finalPixel.a); +#endif + + return finalPixel; +} + +inline fixed4 calculateAdditiveLitPixel(fixed4 texureColor, fixed4 color, fixed3 lighting) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel.rgb = texureColor.rgb * lighting * color.rgb * color.a; + finalPixel.a = 1.0; +#else + //All other alpha + finalPixel.rgb = (texureColor.rgb * lighting * color.rgb) * (texureColor.a * color.a); + finalPixel.a = 1.0; +#endif + + return finalPixel; +} + +inline fixed4 calculatePixel(fixed4 texureColor, fixed4 color) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor * color; + finalPixel.rgb *= color.a; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = color * texureColor; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * color.rgb * 2.0f; + finalPixel.a = color.a * texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f * color; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = color * texureColor; + finalPixel.rgb *= finalPixel.a; +#else + //Standard alpha + finalPixel.a = texureColor.a * color.a; + finalPixel.rgb = (texureColor.rgb * color.rgb) * finalPixel.a; +#endif + + return finalPixel; +} + +inline fixed4 calculatePixel(fixed4 texureColor) : SV_Target +{ + fixed4 finalPixel; + +#if defined(_ALPHAPREMULTIPLY_ON) + //Pre multiplied alpha + finalPixel = texureColor; +#elif defined(_MULTIPLYBLEND) + //Multiply + finalPixel = texureColor; + finalPixel = lerp(fixed4(1,1,1,1), finalPixel, finalPixel.a); +#elif defined(_MULTIPLYBLEND_X2) + //Multiply x2 + finalPixel.rgb = texureColor.rgb * 2.0f; + finalPixel.a = texureColor.a; + finalPixel = lerp(fixed4(0.5f,0.5f,0.5f,0.5f), finalPixel, finalPixel.a); +#elif defined(_ADDITIVEBLEND) + //Additive + finalPixel = texureColor * 2.0f; +#elif defined(_ADDITIVEBLEND_SOFT) + //Additive soft + finalPixel = texureColor; + finalPixel.rgb *= finalPixel.a; +#else + //Standard alpha + finalPixel.a = texureColor.a; + finalPixel.rgb = texureColor.rgb * finalPixel.a; +#endif + + return finalPixel; +} + +//////////////////////////////////////// +// Alpha Clipping +// + +#if defined(_ALPHA_CLIP) + +uniform fixed _Cutoff; + +#define ALPHA_CLIP(pixel, color) clip((pixel.a * color.a) - _Cutoff); + +#else + +#define ALPHA_CLIP(pixel, color) + +#endif + +//////////////////////////////////////// +// Color functions +// + +uniform fixed4 _Color; + +inline fixed4 calculateVertexColor(fixed4 color) +{ + return color * _Color; +} + +#if defined(_COLOR_ADJUST) + +uniform float _Hue; +uniform float _Saturation; +uniform float _Brightness; +uniform fixed4 _OverlayColor; + +float3 rgb2hsv(float3 c) +{ + float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g)); + float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +float3 hsv2rgb(float3 c) +{ + c = float3(c.x, clamp(c.yz, 0.0, 1.0)); + float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +inline fixed4 adjustColor(fixed4 color) +{ + float3 hsv = rgb2hsv(color.rgb); + + hsv.x += _Hue; + hsv.y *= _Saturation; + hsv.z *= _Brightness; + + color.rgb = hsv2rgb(hsv); + + return color; +} + +#define COLORISE(pixel) pixel.rgb = lerp(pixel.rgb, _OverlayColor.rgb, _OverlayColor.a * pixel.a); +#define COLORISE_ADDITIVE(pixel) pixel.rgb = ((1.0-_OverlayColor.a) * pixel.rgb); + +#else // !_COLOR_ADJUST + +#define COLORISE(pixel) +#define COLORISE_ADDITIVE(pixel) + +#endif // !_COLOR_ADJUST + +//////////////////////////////////////// +// Texture functions +// + +uniform sampler2D _MainTex; + +#if _TEXTURE_BLEND +uniform sampler2D _BlendTex; +uniform float _BlendAmount; + +fixed4 calculateBlendedTexturePixel(float2 texcoord) +{ + return (1.0-_BlendAmount) * tex2D(_MainTex, texcoord) + _BlendAmount * tex2D(_BlendTex, texcoord); +} +#endif // _TEXTURE_BLEND + +inline fixed4 calculateTexturePixel(float2 texcoord) +{ + fixed4 pixel; + +#if _TEXTURE_BLEND + pixel = calculateBlendedTexturePixel(texcoord); +#else + pixel = tex2D(_MainTex, texcoord); +#endif // !_TEXTURE_BLEND + +#if defined(_COLOR_ADJUST) + pixel = adjustColor(pixel); +#endif // _COLOR_ADJUST + + return pixel; +} + +uniform fixed4 _MainTex_ST; + +inline float2 calculateTextureCoord(float4 texcoord) +{ + return TRANSFORM_TEX(texcoord, _MainTex); +} + +#endif // SHADER_SHARED_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/ShaderShared.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/ShaderShared.cginc.meta new file mode 100644 index 0000000..ad2f18c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/ShaderShared.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ce9f78731d2f39c49a8688633f53a524 +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteDepthNormalsTexture.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteDepthNormalsTexture.shader new file mode 100644 index 0000000..4d834a2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteDepthNormalsTexture.shader @@ -0,0 +1,446 @@ +Shader "Hidden/Internal-SpriteDepthNormalsTexture" { + +// Use this shader to render a DepthNormals texture for a camera with correct sprite normals (using camera.RenderWithShader) + +Properties { + _MainTex ("", 2D) = "white" {} + _Cutoff ("", Float) = 0.5 + _Color ("", Color) = (1,1,1,1) +} + +SubShader { + Tags { "RenderType"="Sprite" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _MainTex_ST; +uniform float4 _FixedNormal; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + o.nz.xyz = _FixedNormal.xyz; +#if UNITY_REVERSED_Z + o.nz.z = -o.nz.z; +#endif + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +uniform fixed4 _Color; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a*_Color.a - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + } + +SubShader { + Tags { "RenderType"="Opaque" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +struct v2f { + float4 pos : SV_POSITION; + float4 nz : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TransparentCutout" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +uniform float4 _MainTex_ST; +v2f vert( appdata_base v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +uniform fixed4 _Color; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a*_Color.a - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeBark" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertBark(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag( v2f i ) : SV_Target { + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeLeaf" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "UnityBuiltin3xTreeLibrary.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert( appdata_full v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TreeVertLeaf(v); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag( v2f i ) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeOpaque" "DisableBatching"="True" } + Pass { +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float4 nz : TEXCOORD0; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +fixed4 frag(v2f i) : SV_Target { + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="TreeTransparentCutout" "DisableBatching"="True" } + Pass { + Cull Back +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + half alpha = tex2D(_MainTex, i.uv).a; + + clip (alpha - _Cutoff); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + Pass { + Cull Front +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +struct appdata { + float4 vertex : POSITION; + float3 normal : NORMAL; + fixed4 color : COLOR; + float4 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID +}; +v2f vert( appdata v ) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainAnimateTree(v.vertex, v.color.w); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = -COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } + +} + +SubShader { + Tags { "RenderType"="TreeBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; +v2f vert (appdata_tree_billboard v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y); + o.pos = UnityObjectToClipPos(v.vertex); + o.uv.x = v.texcoord.x; + o.uv.y = v.texcoord.y > 0; + o.nz.xyz = float3(0,0,1); + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + clip( texcol.a - 0.001 ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="GrassBillboard" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" + +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassBillboardVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord.xy; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +SubShader { + Tags { "RenderType"="Grass" } + Pass { + Cull Off +CGPROGRAM +#pragma target 3.0 +#pragma vertex vert +#pragma fragment frag +#include "UnityCG.cginc" +#include "TerrainEngine.cginc" +struct v2f { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 uv : TEXCOORD0; + float4 nz : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert (appdata_full v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + WavingGrassVert (v); + o.color = v.color; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord; + o.nz.xyz = COMPUTE_VIEW_NORMAL; + o.nz.w = COMPUTE_DEPTH_01; + return o; +} +uniform sampler2D _MainTex; +uniform fixed _Cutoff; +fixed4 frag(v2f i) : SV_Target { + fixed4 texcol = tex2D( _MainTex, i.uv ); + fixed alpha = texcol.a * i.color.a; + clip( alpha - _Cutoff ); + return EncodeDepthNormal (i.nz.w, i.nz.xyz); +} +ENDCG + } +} + +Fallback Off +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteDepthNormalsTexture.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteDepthNormalsTexture.shader.meta new file mode 100644 index 0000000..7290467 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteDepthNormalsTexture.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: abbda12fddbb0b048a842a3835470d30 +timeCreated: 1480325971 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteLighting.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteLighting.cginc new file mode 100644 index 0000000..45a8b01 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteLighting.cginc @@ -0,0 +1,162 @@ +#ifndef SPRITE_LIGHTING_INCLUDED +#define SPRITE_LIGHTING_INCLUDED + +//Check for using mesh normals +#if !defined(_FIXED_NORMALS) && !defined(_FIXED_NORMALS_BACK_RENDERING) +#define MESH_NORMALS +#endif // _FIXED_NORMALS || _FIXED_NORMALS_BACK_RENDERING + +//////////////////////////////////////// +// Vertex structs +// + +struct VertexInput +{ + float4 vertex : POSITION; + float4 texcoord : TEXCOORD0; + float4 color : COLOR; +#if defined(MESH_NORMALS) + float3 normal : NORMAL; +#endif // MESH_NORMALS +#if defined(_NORMALMAP) + float4 tangent : TANGENT; +#endif // _NORMALMAP + +}; + +//////////////////////////////////////// +// Normal functions +// + +//Fixed Normal defined in view space +uniform float4 _FixedNormal = float4(0, 0, -1, 1); + +inline half3 calculateSpriteWorldNormal(VertexInput vertex) +{ +#if defined(MESH_NORMALS) + return calculateWorldNormal(vertex.normal); +#else //MESH_NORMALS + //Rotate fixed normal by inverse camera matrix to convert the fixed normal into world space + float3x3 invView = transpose((float3x3)UNITY_MATRIX_VP); + float3 normal = _FixedNormal.xyz; +#if UNITY_REVERSED_Z + normal.z = -normal.z; +#endif + return normalize(mul(invView, normal)); +#endif // !MESH_NORMALS +} + +inline half3 calculateSpriteViewNormal(VertexInput vertex) +{ +#if defined(MESH_NORMALS) + return normalize(mul((float3x3)UNITY_MATRIX_IT_MV, vertex.normal)); +#else // !MESH_NORMALS + float3 normal = _FixedNormal.xyz; +#if UNITY_REVERSED_Z + normal.z = -normal.z; +#endif + return normal; +#endif // !MESH_NORMALS +} + +//////////////////////////////////////// +// Normal map functions +// + +#if defined(_NORMALMAP) + +inline half3 calculateSpriteWorldBinormal(half3 normalWorld, half3 tangentWorld, float tangentW) +{ +#if defined(_FIXED_NORMALS_BACK_RENDERING) + //If we're using fixed normals and sprite is facing away from camera, flip tangentW + float3 zAxis = float3(0.0, 0.0, 1.0); + float3 modelForward = mul((float3x3)unity_ObjectToWorld, zAxis); + float3 cameraForward = mul((float3x3)UNITY_MATRIX_VP, zAxis); + float directionDot = dot(modelForward, cameraForward); + //Don't worry if directionDot is zero, sprite will be side on to camera so invisible meaning it doesnt matter that tangentW will be zero too + tangentW *= sign(directionDot); +#endif // _FIXED_NORMALS_BACK_RENDERING + + return calculateWorldBinormal(normalWorld, tangentWorld, tangentW); +} + +#endif // _NORMALMAP + +#if defined(_DIFFUSE_RAMP) + + +//////////////////////////////////////// +// Diffuse ramp functions +// + +//Disable for softer, more traditional diffuse ramping +#define HARD_DIFFUSE_RAMP + +uniform sampler2D _DiffuseRamp; + +inline fixed3 calculateDiffuseRamp(float ramp) +{ + return tex2D(_DiffuseRamp, float2(ramp, ramp)).rgb; +} + +inline fixed3 calculateRampedDiffuse(fixed3 lightColor, float attenuation, float angleDot) +{ + float d = angleDot * 0.5 + 0.5; +#if defined(HARD_DIFFUSE_RAMP) + half3 ramp = calculateDiffuseRamp(d * attenuation * 2); + return lightColor * ramp; +#else + half3 ramp = calculateDiffuseRamp(d); + return lightColor * ramp * (attenuation * 2); +#endif +} +#endif // _DIFFUSE_RAMP + +//////////////////////////////////////// +// Rim Lighting functions +// + +#ifdef _RIM_LIGHTING + +uniform float _RimPower; +uniform fixed4 _RimColor; + +inline fixed3 applyRimLighting(fixed3 posWorld, fixed3 normalWorld, fixed4 pixel) : SV_Target +{ + fixed3 viewDir = normalize(_WorldSpaceCameraPos - posWorld); + float invDot = 1.0 - saturate(dot(normalWorld, viewDir)); + float rimPower = pow(invDot, _RimPower); + float rim = saturate(rimPower * _RimColor.a); + +#if defined(_DIFFUSE_RAMP) + rim = calculateDiffuseRamp(rim).r; +#endif + + return lerp(pixel.rgb, _RimColor.xyz * pixel.a, rim); +} + +#endif //_RIM_LIGHTING + +//////////////////////////////////////// +// Emission functions +// + +#ifdef _EMISSION + +uniform sampler2D _EmissionMap; +uniform fixed4 _EmissionColor; +uniform float _EmissionPower; + + +#define APPLY_EMISSION(diffuse, uv) \ + { \ + diffuse += tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb * _EmissionPower; \ + } + +#else //!_EMISSION + +#define APPLY_EMISSION(diffuse, uv) + +#endif //!_EMISSION + +#endif // SPRITE_LIGHTING_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteLighting.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteLighting.cginc.meta new file mode 100644 index 0000000..279520a --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteLighting.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 252dba02e84702448a0838ced241467d +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritePixelLighting.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritePixelLighting.cginc new file mode 100644 index 0000000..f08b59a --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritePixelLighting.cginc @@ -0,0 +1,198 @@ +#ifndef SPRITE_PIXEL_LIGHTING_INCLUDED +#define SPRITE_PIXEL_LIGHTING_INCLUDED + +#include "ShaderShared.cginc" +#include "SpriteLighting.cginc" +#include "AutoLight.cginc" + +//////////////////////////////////////// +// Defines +// + +//////////////////////////////////////// +// Vertex output struct +// + +#if defined(_NORMALMAP) + #define _VERTEX_LIGHTING_INDEX TEXCOORD5 + #define _LIGHT_COORD_INDEX_0 6 + #define _LIGHT_COORD_INDEX_1 7 + #define _FOG_COORD_INDEX 8 +#else + #define _VERTEX_LIGHTING_INDEX TEXCOORD3 + #define _LIGHT_COORD_INDEX_0 4 + #define _LIGHT_COORD_INDEX_1 5 + #define _FOG_COORD_INDEX 6 +#endif // _NORMALMAP + +struct VertexOutput +{ + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; + float4 posWorld : TEXCOORD1; + half3 normalWorld : TEXCOORD2; +#if defined(_NORMALMAP) + half3 tangentWorld : TEXCOORD3; + half3 binormalWorld : TEXCOORD4; +#endif // _NORMALMAP + fixed3 vertexLighting : _VERTEX_LIGHTING_INDEX; + LIGHTING_COORDS(_LIGHT_COORD_INDEX_0, _LIGHT_COORD_INDEX_1) +#if defined(_FOG) + UNITY_FOG_COORDS(_FOG_COORD_INDEX) +#endif // _FOG +}; + +//////////////////////////////////////// +// Light calculations +// + +uniform fixed4 _LightColor0; + +inline fixed3 calculateLightDiffuse(VertexOutput input, float3 normalWorld) +{ + //For directional lights _WorldSpaceLightPos0.w is set to zero + float3 lightWorldDirection = normalize(_WorldSpaceLightPos0.xyz - input.posWorld.xyz * _WorldSpaceLightPos0.w); + + float attenuation = LIGHT_ATTENUATION(input); + float angleDot = max(0, dot(normalWorld, lightWorldDirection)); + +#if defined(_DIFFUSE_RAMP) + fixed3 lightDiffuse = calculateRampedDiffuse(_LightColor0.rgb, attenuation, angleDot); +#else + fixed3 lightDiffuse = _LightColor0.rgb * (attenuation * angleDot); +#endif // _DIFFUSE_RAMP + + return lightDiffuse; +} + +inline float3 calculateNormalWorld(VertexOutput input) +{ +#if defined(_NORMALMAP) + return calculateNormalFromBumpMap(input.texcoord, input.tangentWorld, input.binormalWorld, input.normalWorld); +#else + return input.normalWorld; +#endif +} + +fixed3 calculateVertexLighting(float3 posWorld, float3 normalWorld) +{ + fixed3 vertexLighting = fixed3(0,0,0); + +#ifdef VERTEXLIGHT_ON + //Get approximated illumination from non-important point lights + vertexLighting = Shade4PointLights ( unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, + unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, + unity_4LightAtten0, posWorld, normalWorld) * 0.5; +#endif + + return vertexLighting; +} + +fixed3 calculateAmbientLight(half3 normalWorld) +{ +#if defined(_SPHERICAL_HARMONICS) + fixed3 ambient = ShadeSH9(half4(normalWorld, 1.0)) * 0.75f; +#else + fixed3 ambient = unity_AmbientSky.rgb * 0.75; +#endif + return ambient; +} + +//////////////////////////////////////// +// Vertex program +// + +VertexOutput vert(VertexInput v) +{ + VertexOutput output; + + output.pos = calculateLocalPos(v.vertex); + output.color = calculateVertexColor(v.color); + output.texcoord = calculateTextureCoord(v.texcoord); + output.posWorld = calculateWorldPos(v.vertex); + output.normalWorld = calculateSpriteWorldNormal(v); + output.vertexLighting = calculateVertexLighting(output.posWorld, output.normalWorld); + +#if defined(_NORMALMAP) + output.tangentWorld = calculateWorldTangent(v.tangent); + output.binormalWorld = calculateSpriteWorldBinormal(output.normalWorld, output.tangentWorld, v.tangent.w); +#endif + + TRANSFER_VERTEX_TO_FRAGMENT(output) + +#if defined(_FOG) + UNITY_TRANSFER_FOG(output,output.pos); +#endif // _FOG + + return output; +} + +//////////////////////////////////////// +// Fragment programs +// + +fixed4 fragBase(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord); + ALPHA_CLIP(texureColor, input.color) + + //Get normal direction + fixed3 normalWorld = calculateNormalWorld(input); + + //Get Ambient diffuse + fixed3 ambient = calculateAmbientLight(normalWorld); + + //Get primary pixel light diffuse + fixed3 diffuse = calculateLightDiffuse(input, normalWorld); + + //Combine along with vertex lighting for the base lighting pass + fixed3 lighting = ambient + diffuse + input.vertexLighting; + + APPLY_EMISSION(lighting, input.texcoord) + + fixed4 pixel = calculateLitPixel(texureColor, input.color, lighting); + +#if defined(_RIM_LIGHTING) + pixel.rgb = applyRimLighting(input.posWorld, normalWorld, pixel); +#endif + + COLORISE(pixel) + +#if defined(_FOG) + fixed4 fogColor = lerp(fixed4(0,0,0,0), unity_FogColor, pixel.a); + UNITY_APPLY_FOG_COLOR(input.fogCoord, pixel, fogColor); +#endif // _FOG + + return pixel; +} + +fixed4 fragAdd(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord); + +#if defined(_COLOR_ADJUST) + texureColor = adjustColor(texureColor); +#endif // _COLOR_ADJUST + + ALPHA_CLIP(texureColor, input.color) + + //Get normal direction + fixed3 normalWorld = calculateNormalWorld(input); + + //Get light diffuse + fixed3 lighting = calculateLightDiffuse(input, normalWorld); + + fixed4 pixel = calculateAdditiveLitPixel(texureColor, input.color, lighting); + + COLORISE_ADDITIVE(pixel) + +#if defined(_FOG) + UNITY_APPLY_FOG_COLOR(input.fogCoord, pixel.rgb, fixed4(0,0,0,0)); // fog towards black in additive pass +#endif // _FOG + + return pixel; +} + + +#endif // SPRITE_PIXEL_LIGHTING_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritePixelLighting.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritePixelLighting.cginc.meta new file mode 100644 index 0000000..2759558 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritePixelLighting.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 38bcbfc10385d424c9cbac5b5b9ec1af +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteShadows.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteShadows.cginc new file mode 100644 index 0000000..6b88c19 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteShadows.cginc @@ -0,0 +1,52 @@ +#ifndef SPRITE_SHADOWS_INCLUDED +#define SPRITE_SHADOWS_INCLUDED + +#include "UnityCG.cginc" + +//////////////////////////////////////// +// Vertex structs +// + +struct vertexInput +{ + float4 vertex : POSITION; + float4 texcoord : TEXCOORD0; +}; + +struct vertexOutput +{ + V2F_SHADOW_CASTER; + float4 texcoord : TEXCOORD1; +}; + +//////////////////////////////////////// +// Vertex program +// + +uniform sampler2D _MainTex; +uniform fixed4 _MainTex_ST; + +vertexOutput vert(vertexInput v) +{ + vertexOutput o; + TRANSFER_SHADOW_CASTER(o) + o.texcoord = float4(TRANSFORM_TEX(v.texcoord, _MainTex), 0, 0); + return o; +} + +//////////////////////////////////////// +// Fragment program +// + + +uniform fixed _ShadowAlphaCutoff; + +fixed4 frag(vertexOutput IN) : COLOR +{ + fixed4 texureColor = tex2D(_MainTex, IN.texcoord.xy); + clip(texureColor.a - _ShadowAlphaCutoff); + + SHADOW_CASTER_FRAGMENT(IN) +} + +#endif // SPRITE_SHADOWS_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteShadows.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteShadows.cginc.meta new file mode 100644 index 0000000..610e02e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteShadows.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 0f58b811a44f1b44cb9aac3ddfe24f37 +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteUnlit.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteUnlit.cginc new file mode 100644 index 0000000..b58a509 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteUnlit.cginc @@ -0,0 +1,67 @@ +#ifndef SPRITE_UNLIT_INCLUDED +#define SPRITE_UNLIT_INCLUDED + +#include "ShaderShared.cginc" + +//////////////////////////////////////// +// Vertex structs +// + +struct VertexInput +{ + float4 vertex : POSITION; + float4 texcoord : TEXCOORD0; + fixed4 color : COLOR; +}; + +struct VertexOutput +{ + float4 pos : SV_POSITION; + float2 texcoord : TEXCOORD0; + fixed4 color : COLOR; +#if defined(_FOG) + UNITY_FOG_COORDS(1) +#endif // _FOG +}; + +//////////////////////////////////////// +// Vertex program +// + +VertexOutput vert(VertexInput input) +{ + VertexOutput output; + + output.pos = calculateLocalPos(input.vertex); + output.texcoord = calculateTextureCoord(input.texcoord); + output.color = calculateVertexColor(input.color); + +#if defined(_FOG) + UNITY_TRANSFER_FOG(output,output.pos); +#endif // _FOG + + return output; +} + +//////////////////////////////////////// +// Fragment program +// + +fixed4 frag(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord.xy); + ALPHA_CLIP(texureColor, input.color) + + fixed4 pixel = calculatePixel(texureColor, input.color); + + COLORISE(pixel) + +#if defined(_FOG) + fixed4 fogColor = lerp(fixed4(0,0,0,0), unity_FogColor, pixel.a); + UNITY_APPLY_FOG_COLOR(input.fogCoord, pixel, fogColor); +#endif // _FOG + + return pixel; +} + +#endif // SPRITE_UNLIT_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteUnlit.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteUnlit.cginc.meta new file mode 100644 index 0000000..684bd24 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteUnlit.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e67832d6dd9dd914ca946fa7fcaa4a9e +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteVertexLighting.cginc b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteVertexLighting.cginc new file mode 100644 index 0000000..aae3b27 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteVertexLighting.cginc @@ -0,0 +1,430 @@ +#ifndef SPRITE_VERTEX_LIGHTING_INCLUDED +#define SPRITE_VERTEX_LIGHTING_INCLUDED + +#include "ShaderShared.cginc" +#include "SpriteLighting.cginc" +#include "UnityStandardUtils.cginc" + +//////////////////////////////////////// +// Defines +// + +//Define to use spot lights (more expensive) +#define SPOT_LIGHTS + +//Have to process lighting per pixel if using normal maps or a diffuse ramp or rim lighting +#if defined(_NORMALMAP) || defined(_DIFFUSE_RAMP) || defined(_RIM_LIGHTING) +#define PER_PIXEL_LIGHTING +#endif + +//Turn off bump mapping and diffuse ramping on older shader models as they dont support needed number of outputs +#if defined(PER_PIXEL_LIGHTING) && (SHADER_TARGET < 30) + #undef PER_PIXEL_LIGHTING + #undef _NORMALMAP + #undef _DIFFUSE_RAMP + #undef _RIM_LIGHTING +#endif + +//In D3D9 only have a max of 9 TEXCOORD so can't have diffuse ramping or fog or rim lighting if processing lights per pixel +#if defined(SHADER_API_D3D9) && defined(PER_PIXEL_LIGHTING) + #if defined(_NORMALMAP) + #undef _DIFFUSE_RAMP + #undef _FOG + #undef _RIM_LIGHTING + #elif defined(_DIFFUSE_RAMP) + #undef _FOG + #undef _RIM_LIGHTING + #elif defined(_RIM_LIGHTING) + #undef _FOG + #undef _DIFFUSE_RAMP + #else + #undef _DIFFUSE_RAMP + #undef _RIM_LIGHTING + #endif +#endif + +#if defined(PER_PIXEL_LIGHTING) + #if defined(_NORMALMAP) && defined(_DIFFUSE_RAMP) + #define ATTENUATIONS TEXCOORD9 + #if defined(_RIM_LIGHTING) + #define _POS_WORLD_INDEX TEXCOORD10 + #define _FOG_COORD_INDEX 11 + #else + #define _FOG_COORD_INDEX 10 + #endif + #elif defined(_NORMALMAP) != defined(_DIFFUSE_RAMP) + #define ATTENUATIONS TEXCOORD8 + #if defined(_RIM_LIGHTING) + #define _POS_WORLD_INDEX TEXCOORD9 + #define _FOG_COORD_INDEX 10 + #else + #define _FOG_COORD_INDEX 9 + #endif + #else //!_DIFFUSE_RAMP && !_NORMALMAP + #if defined(_RIM_LIGHTING) + #define _POS_WORLD_INDEX TEXCOORD8 + #define _FOG_COORD_INDEX 9 + #else + #define _FOG_COORD_INDEX 8 + #endif + #endif +#else //!PER_PIXEL_LIGHTING + #define _FOG_COORD_INDEX 2 +#endif + +//////////////////////////////////////// +// Vertex output struct +// + +struct VertexOutput +{ + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float3 texcoord : TEXCOORD0; + +#if defined(PER_PIXEL_LIGHTING) + + half4 VertexLightInfo0 : TEXCOORD1; + half4 VertexLightInfo1 : TEXCOORD2; + half4 VertexLightInfo2 : TEXCOORD3; + half4 VertexLightInfo3 : TEXCOORD4; + half4 VertexLightInfo4 : TEXCOORD5; + + #if defined(_NORMALMAP) + half4 normalWorld : TEXCOORD6; + half4 tangentWorld : TEXCOORD7; + half4 binormalWorld : TEXCOORD8; + #else + half3 normalWorld : TEXCOORD6; + half3 VertexLightInfo5 : TEXCOORD7; + #endif + #if defined(_DIFFUSE_RAMP) + half4 LightAttenuations : ATTENUATIONS; + #endif + #if defined(_RIM_LIGHTING) + float4 posWorld : _POS_WORLD_INDEX; + #endif + +#else //!PER_PIXEL_LIGHTING + + half3 FullLighting : TEXCOORD1; + +#endif // !PER_PIXEL_LIGHTING + +#if defined(_FOG) + UNITY_FOG_COORDS(_FOG_COORD_INDEX) +#endif // _FOG +}; + +//////////////////////////////////////// +// Light calculations +// + +struct VertexLightInfo +{ + half3 lightDirection; + fixed3 lightColor; + +#if defined(_DIFFUSE_RAMP) + float attenuation; +#endif // _DIFFUSE_RAMP +}; + +inline VertexLightInfo getVertexLightAttenuatedInfo(int index, float3 viewPos) +{ + VertexLightInfo lightInfo; + + //For directional lights unity_LightPosition.w is set to zero + lightInfo.lightDirection = unity_LightPosition[index].xyz - viewPos.xyz * unity_LightPosition[index].w; + float lengthSq = dot(lightInfo.lightDirection, lightInfo.lightDirection); + + // don't produce NaNs if some vertex position overlaps with the light + lengthSq = max(lengthSq, 0.000001); + + lightInfo.lightDirection *= rsqrt(lengthSq); + + float attenuation = 1.0 / (1.0 + lengthSq * unity_LightAtten[index].z); + +#if defined(SPOT_LIGHTS) + //Spot light attenuation - for non-spot lights unity_LightAtten.x is set to -1 and y is set to 1 + { + float rho = max (0, dot(lightInfo.lightDirection, unity_SpotDirection[index].xyz)); + float spotAtt = (rho - unity_LightAtten[index].x) * unity_LightAtten[index].y; + attenuation *= saturate(spotAtt); + } +#endif // SPOT_LIGHTS + + //If using a diffuse ramp texture then need to pass through the lights attenuation, otherwise premultiply the light color with it +#if defined(_DIFFUSE_RAMP) + lightInfo.lightColor = unity_LightColor[index].rgb; + lightInfo.attenuation = attenuation; +#else + lightInfo.lightColor = unity_LightColor[index].rgb * attenuation; +#endif // _DIFFUSE_RAMP + + return lightInfo; +} + +fixed3 calculateAmbientLight(half3 normalWorld) +{ +#if defined(_SPHERICAL_HARMONICS) + + //Magic constants used to tweak ambient to approximate pixel shader spherical harmonics + static const fixed3 worldUp = fixed3(0,1,0); + static const float skyGroundDotMul = 2.5; + static const float minEquatorMix = 0.5; + static const float equatorColorBlur = 0.33; + + float upDot = dot(normalWorld, worldUp); + + //Fade between a flat lerp from sky to ground and a 3 way lerp based on how bright the equator light is. + //This simulates how directional lights get blurred using spherical harmonics + + //Work out color from ground and sky, ignoring equator + float adjustedDot = upDot * skyGroundDotMul; + fixed3 skyGroundColor = lerp(unity_AmbientGround, unity_AmbientSky, saturate((adjustedDot + 1.0) * 0.5)); + + //Work out equator lights brightness + float equatorBright = saturate(dot(unity_AmbientEquator.rgb, unity_AmbientEquator.rgb)); + + //Blur equator color with sky and ground colors based on how bright it is. + fixed3 equatorBlurredColor = lerp(unity_AmbientEquator, saturate(unity_AmbientEquator + unity_AmbientGround + unity_AmbientSky), equatorBright * equatorColorBlur); + + //Work out 3 way lerp inc equator light + fixed3 equatorColor = lerp(equatorBlurredColor, unity_AmbientGround, -upDot) * step(upDot, 0) + lerp(equatorBlurredColor, unity_AmbientSky, upDot) * step(0, upDot); + + //Mix the two colors together based on how bright the equator light is + return lerp(skyGroundColor, equatorColor, saturate(equatorBright + minEquatorMix)) * 0.75; + +#else // !_SPHERICAL_HARMONICS + + //Flat ambient is just the sky color + return unity_AmbientSky.rgb * 0.75; + +#endif // !_SPHERICAL_HARMONICS +} + +//////////////////////////////////////// +// Light Packing Functions +// + +#if defined(_DIFFUSE_RAMP) + +inline fixed3 calculateLightDiffuse(fixed3 lightColor, half3 viewNormal, half3 lightViewDir, float attenuation) +{ + float angleDot = max(0, dot(viewNormal, lightViewDir)); + return calculateRampedDiffuse(lightColor, attenuation, angleDot); +} + +#else + +inline fixed3 calculateLightDiffuse(fixed3 attenuatedLightColor, half3 viewNormal, half3 lightViewDir) +{ + float angleDot = max(0, dot(viewNormal, lightViewDir)); + return attenuatedLightColor * angleDot; +} + +#endif // _NORMALMAP + + +#if defined(PER_PIXEL_LIGHTING) + +#define VERTEX_LIGHT_0_DIR VertexLightInfo0.xyz +#define VERTEX_LIGHT_0_R VertexLightInfo4.x +#define VERTEX_LIGHT_0_G VertexLightInfo4.y +#define VERTEX_LIGHT_0_B VertexLightInfo4.z + +#define VERTEX_LIGHT_1_DIR VertexLightInfo1.xyz +#define VERTEX_LIGHT_1_R VertexLightInfo0.w +#define VERTEX_LIGHT_1_G VertexLightInfo1.w +#define VERTEX_LIGHT_1_B VertexLightInfo2.w + +#define VERTEX_LIGHT_2_DIR VertexLightInfo2.xyz +#define VERTEX_LIGHT_2_R VertexLightInfo3.w +#define VERTEX_LIGHT_2_G VertexLightInfo4.w +#define VERTEX_LIGHT_2_B texcoord.z + +#define VERTEX_LIGHT_3_DIR VertexLightInfo3.xyz + +#if defined(_NORMALMAP) + #define VERTEX_LIGHT_3_R normalWorld.w + #define VERTEX_LIGHT_3_G tangentWorld.w + #define VERTEX_LIGHT_3_B binormalWorld.w +#else + #define VERTEX_LIGHT_3_R VertexLightInfo5.x + #define VERTEX_LIGHT_3_G VertexLightInfo5.y + #define VERTEX_LIGHT_3_B VertexLightInfo5.z +#endif + +#if defined(_DIFFUSE_RAMP) + + #define LIGHT_DIFFUSE_ATTEN_0 LightAttenuations.x + #define LIGHT_DIFFUSE_ATTEN_1 LightAttenuations.y + #define LIGHT_DIFFUSE_ATTEN_2 LightAttenuations.z + #define LIGHT_DIFFUSE_ATTEN_3 LightAttenuations.w + + #define PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo) \ + { \ + output.LIGHT_DIFFUSE_ATTEN_##index = lightInfo.attenuation; \ + } + + #define ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \ + { \ + diffuse += calculateLightDiffuse(lightColor, viewNormal, lightViewDir, input.LIGHT_DIFFUSE_ATTEN_##index); \ + } +#else + #define PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo) + #define ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \ + { \ + diffuse += calculateLightDiffuse(lightColor, viewNormal, lightViewDir); \ + } +#endif + +#define PACK_VERTEX_LIGHT(index, output, viewPos) \ + { \ + VertexLightInfo lightInfo = getVertexLightAttenuatedInfo(index, viewPos); \ + output.VERTEX_LIGHT_##index##_DIR = lightInfo.lightDirection; \ + output.VERTEX_LIGHT_##index##_R = lightInfo.lightColor.r; \ + output.VERTEX_LIGHT_##index##_G = lightInfo.lightColor.g; \ + output.VERTEX_LIGHT_##index##_B = lightInfo.lightColor.b; \ + PACK_VERTEX_LIGHT_DIFFUSE(index, output, lightInfo); \ + } + +#define ADD_VERTEX_LIGHT(index, input, viewNormal, diffuse) \ + { \ + half3 lightViewDir = input.VERTEX_LIGHT_##index##_DIR; \ + fixed3 lightColor = fixed3(input.VERTEX_LIGHT_##index##_R, input.VERTEX_LIGHT_##index##_G, input.VERTEX_LIGHT_##index##_B); \ + ADD_VERTEX_LIGHT_DIFFUSE(index, diffuse, input, lightColor, viewNormal, lightViewDir) \ + } + +#else //!PER_PIXEL_LIGHTING + +//////////////////////////////////////// +// Vertex Only Functions +// + +inline fixed3 calculateLightDiffuse(int index, float3 viewPos, half3 viewNormal) +{ + VertexLightInfo lightInfo = getVertexLightAttenuatedInfo(index, viewPos); + float angleDot = max(0, dot(viewNormal, lightInfo.lightDirection)); + return lightInfo.lightColor * angleDot; +} + +#endif // !PER_PIXEL_LIGHTING + +//////////////////////////////////////// +// Vertex program +// + +VertexOutput vert(VertexInput input) +{ + VertexOutput output; + + output.pos = calculateLocalPos(input.vertex); + output.color = calculateVertexColor(input.color); + output.texcoord = float3(calculateTextureCoord(input.texcoord), 0); + + float3 viewPos = UnityObjectToViewPos(input.vertex); + +#if defined(PER_PIXEL_LIGHTING) + + #if defined(_RIM_LIGHTING) + output.posWorld = calculateWorldPos(input.vertex); + #endif + + PACK_VERTEX_LIGHT(0, output, viewPos) + PACK_VERTEX_LIGHT(1, output, viewPos) + PACK_VERTEX_LIGHT(2, output, viewPos) + PACK_VERTEX_LIGHT(3, output, viewPos) + + output.normalWorld.xyz = calculateSpriteWorldNormal(input); + + #if defined(_NORMALMAP) + output.tangentWorld.xyz = calculateWorldTangent(input.tangent); + output.binormalWorld.xyz = calculateSpriteWorldBinormal(output.normalWorld, output.tangentWorld, input.tangent.w); + #endif + +#else // !PER_PIXEL_LIGHTING + + //Just pack full lighting + float3 viewNormal = calculateSpriteViewNormal(input); + + //Get Ambient diffuse + float3 normalWorld = calculateSpriteWorldNormal(input); + fixed3 ambient = calculateAmbientLight(normalWorld); + + fixed3 diffuse = calculateLightDiffuse(0, viewPos, viewNormal); + diffuse += calculateLightDiffuse(1, viewPos, viewNormal); + diffuse += calculateLightDiffuse(2, viewPos, viewNormal); + diffuse += calculateLightDiffuse(3, viewPos, viewNormal); + + output.FullLighting = ambient + diffuse; + +#endif // !PER_PIXEL_LIGHTING + +#if defined(_FOG) + UNITY_TRANSFER_FOG(output, output.pos); +#endif // _FOG + + return output; +} + +//////////////////////////////////////// +// Fragment program +// + +fixed4 frag(VertexOutput input) : SV_Target +{ + fixed4 texureColor = calculateTexturePixel(input.texcoord.xy); + ALPHA_CLIP(texureColor, input.color) + +#if defined(PER_PIXEL_LIGHTING) + + #if defined(_NORMALMAP) + half3 normalWorld = calculateNormalFromBumpMap(input.texcoord.xy, input.tangentWorld.xyz, input.binormalWorld.xyz, input.normalWorld.xyz); + #else + half3 normalWorld = input.normalWorld.xyz; + #endif + + //Get Ambient diffuse + fixed3 ambient = calculateAmbientLight(normalWorld); + + //Find vertex light diffuse + fixed3 diffuse = fixed3(0,0,0); + + //Add each vertex light to diffuse + half3 normalView = normalize(mul((float3x3)UNITY_MATRIX_V, normalWorld)); + ADD_VERTEX_LIGHT(0, input, normalView, diffuse) + ADD_VERTEX_LIGHT(1, input, normalView, diffuse) + ADD_VERTEX_LIGHT(2, input, normalView, diffuse) + ADD_VERTEX_LIGHT(3, input, normalView, diffuse) + + fixed3 lighting = ambient + diffuse; + + APPLY_EMISSION(lighting, input.texcoord.xy) + + fixed4 pixel = calculateLitPixel(texureColor, input.color, lighting); + +#if defined(_RIM_LIGHTING) + pixel.rgb = applyRimLighting(input.posWorld, normalWorld, pixel); +#endif + +#else // !PER_PIXEL_LIGHTING + + APPLY_EMISSION(input.FullLighting, input.texcoord.xy) + + fixed4 pixel = calculateLitPixel(texureColor, input.color, input.FullLighting); + +#endif // !PER_PIXEL_LIGHTING + + COLORISE(pixel) + +#if defined(_FOG) + fixed4 fogColor = lerp(fixed4(0,0,0,0), unity_FogColor, pixel.a); + UNITY_APPLY_FOG_COLOR(input.fogCoord, pixel, fogColor); +#endif // _FOG + + return pixel; +} + +#endif // SPRITE_VERTEX_LIGHTING_INCLUDED \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteVertexLighting.cginc.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteVertexLighting.cginc.meta new file mode 100644 index 0000000..f206cca --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpriteVertexLighting.cginc.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b256f9f092407414392d98f339173a2d +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesPixelLit.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesPixelLit.shader new file mode 100644 index 0000000..fca34de --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesPixelLit.shader @@ -0,0 +1,153 @@ +Shader "Spine/Sprite/Pixel Lit" +{ + Properties + { + _MainTex ("Main Texture", 2D) = "white" {} + _Color ("Color", Color) = (1,1,1,1) + + _BumpScale("Scale", Float) = 1.0 + _BumpMap ("Normal Map", 2D) = "bump" {} + + [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 + [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} + [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 + + _EmissionColor("Color", Color) = (0,0,0,0) + _EmissionMap("Emission", 2D) = "white" {} + _EmissionPower("Emission Power", Float) = 2.0 + + _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5 + _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0 + [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 + _MetallicGlossMap("Metallic", 2D) = "white" {} + + _DiffuseRamp ("Diffuse Ramp Texture", 2D) = "gray" {} + + _FixedNormal ("Fixed Normal", Vector) = (0,0,1,1) + _ZWrite ("Depth Write", Float) = 1.0 + _Cutoff ("Depth alpha cutoff", Range(0,1)) = 0.5 + _ShadowAlphaCutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + _CustomRenderQueue ("Custom Render Queue", Float) = 0.0 + + _OverlayColor ("Overlay Color", Color) = (0,0,0,0) + _Hue("Hue", Range(-0.5,0.5)) = 0.0 + _Saturation("Saturation", Range(0,2)) = 1.0 + _Brightness("Brightness", Range(0,2)) = 1.0 + + _RimPower("Rim Power", Float) = 2.0 + _RimColor ("Rim Color", Color) = (1,1,1,1) + + _BlendTex ("Blend Texture", 2D) = "white" {} + _BlendAmount ("Blend", Range(0,1)) = 0.0 + + [HideInInspector] _SrcBlend ("__src", Float) = 1.0 + [HideInInspector] _DstBlend ("__dst", Float) = 0.0 + [HideInInspector] _RenderQueue ("__queue", Float) = 0.0 + [HideInInspector] _Cull ("__cull", Float) = 0.0 + } + + SubShader + { + Tags { "Queue"="Transparent" "RenderType"="Sprite" "AlphaDepth"="False" "CanUseSpriteAtlas"="True" "IgnoreProjector"="True" } + LOD 200 + + Pass + { + Name "FORWARD" + Tags { "LightMode" = "ForwardBase" } + Blend [_SrcBlend] [_DstBlend] + ZWrite [_ZWrite] + ZTest LEqual + Cull [_Cull] + + CGPROGRAM + #pragma target 3.0 + + #pragma shader_feature _ _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON _ADDITIVEBLEND _ADDITIVEBLEND_SOFT _MULTIPLYBLEND _MULTIPLYBLEND_X2 + #pragma shader_feature _ _FIXED_NORMALS_VIEWSPACE _FIXED_NORMALS_VIEWSPACE_BACKFACE _FIXED_NORMALS_MODELSPACE _FIXED_NORMALS_MODELSPACE_BACKFACE + #pragma shader_feature _ _SPECULAR _SPECULAR_GLOSSMAP + #pragma shader_feature _NORMALMAP + #pragma shader_feature _ALPHA_CLIP + #pragma shader_feature _EMISSION + #pragma shader_feature _RIM_LIGHTING + #pragma shader_feature _DIFFUSE_RAMP + #pragma shader_feature _COLOR_ADJUST + #pragma shader_feature _TEXTURE_BLEND + #pragma shader_feature _SPHERICAL_HARMONICS + #pragma shader_feature _FOG + + #pragma multi_compile_fwdbase + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_fog + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment fragBase + + #include "CGIncludes/SpritePixelLighting.cginc" + ENDCG + } + Pass + { + Name "FORWARD_DELTA" + Tags { "LightMode" = "ForwardAdd" } + Blend [_SrcBlend] One + ZWrite Off + ZTest LEqual + Cull [_Cull] + + CGPROGRAM + #pragma target 3.0 + + #pragma shader_feature _ _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON _ADDITIVEBLEND _ADDITIVEBLEND_SOFT _MULTIPLYBLEND _MULTIPLYBLEND_X2 + #pragma shader_feature _ _FIXED_NORMALS_VIEWSPACE _FIXED_NORMALS_VIEWSPACE_BACKFACE _FIXED_NORMALS_MODELSPACE _FIXED_NORMALS_MODELSPACE_BACKFACE + #pragma shader_feature _ _SPECULAR _SPECULAR_GLOSSMAP + #pragma shader_feature _NORMALMAP + #pragma shader_feature _ALPHA_CLIP + #pragma shader_feature _DIFFUSE_RAMP + #pragma shader_feature _COLOR_ADJUST + #pragma shader_feature _TEXTURE_BLEND + #pragma shader_feature _FOG + + #pragma multi_compile_fwdadd_fullshadows + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_fog + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment fragAdd + + #include "CGIncludes/SpritePixelLighting.cginc" + ENDCG + } + Pass + { + Name "ShadowCaster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + Fog { Mode Off } + ZWrite On + ZTest LEqual + Cull Off + Lighting Off + + CGPROGRAM + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_shadowcaster + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment frag + + #include "CGIncludes/SpriteShadows.cginc" + ENDCG + } + } + + FallBack "Spine/Sprite/Unlit" + CustomEditor "SpineSpriteShaderGUI" +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesPixelLit.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesPixelLit.shader.meta new file mode 100644 index 0000000..c5a0947 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesPixelLit.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6f7a5a97a82637f478494bc40ea8c8a2 +timeCreated: 1479457857 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesUnlit.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesUnlit.shader new file mode 100644 index 0000000..807a4c2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesUnlit.shader @@ -0,0 +1,90 @@ +Shader "Spine/Sprite/Unlit" +{ + Properties + { + _MainTex ("Main Texture", 2D) = "white" {} + _Color ("Color", Color) = (1,1,1,1) + + [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 + [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} + [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 + + _ZWrite ("Depth Write", Float) = 0.0 + _Cutoff ("Depth alpha cutoff", Range(0,1)) = 0.0 + _ShadowAlphaCutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + _CustomRenderQueue ("Custom Render Queue", Float) = 0.0 + + _OverlayColor ("Overlay Color", Color) = (0,0,0,0) + _Hue("Hue", Range(-0.5,0.5)) = 0.0 + _Saturation("Saturation", Range(0,2)) = 1.0 + _Brightness("Brightness", Range(0,2)) = 1.0 + + _BlendTex ("Blend Texture", 2D) = "white" {} + _BlendAmount ("Blend", Range(0,1)) = 0.0 + + [HideInInspector] _SrcBlend ("__src", Float) = 1.0 + [HideInInspector] _DstBlend ("__dst", Float) = 0.0 + [HideInInspector] _RenderQueue ("__queue", Float) = 0.0 + [HideInInspector] _Cull ("__cull", Float) = 0.0 + } + + SubShader + { + Tags { "Queue"="Transparent" "RenderType"="Sprite" "AlphaDepth"="False" "CanUseSpriteAtlas"="True" "IgnoreProjector"="True" } + LOD 100 + + Pass + { + Blend [_SrcBlend] [_DstBlend] + Lighting Off + ZWrite [_ZWrite] + ZTest LEqual + Cull [_Cull] + Lighting Off + + CGPROGRAM + #pragma shader_feature _ _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON _ADDITIVEBLEND _ADDITIVEBLEND_SOFT _MULTIPLYBLEND _MULTIPLYBLEND_X2 + #pragma shader_feature _ALPHA_CLIP + #pragma shader_feature _TEXTURE_BLEND + #pragma shader_feature _COLOR_ADJUST + #pragma shader_feature _FOG + + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_fog + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment frag + + #include "CGIncludes/SpriteUnlit.cginc" + ENDCG + } + Pass + { + Name "ShadowCaster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + Fog { Mode Off } + ZWrite On + ZTest LEqual + Cull Off + Lighting Off + + CGPROGRAM + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_shadowcaster + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment frag + + #include "CGIncludes/SpriteShadows.cginc" + ENDCG + } + } + + CustomEditor "SpineSpriteShaderGUI" +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesUnlit.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesUnlit.shader.meta new file mode 100644 index 0000000..0304ebb --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesUnlit.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 64005298b9a80bb4899eabd5140dc4a8 +timeCreated: 1479457857 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesVertexLit.shader b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesVertexLit.shader new file mode 100644 index 0000000..7137b0d --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesVertexLit.shader @@ -0,0 +1,120 @@ +Shader "Spine/Sprite/Vertex Lit" +{ + Properties + { + _MainTex ("Main Texture", 2D) = "white" {} + _Color ("Color", Color) = (1,1,1,1) + + _BumpScale("Scale", Float) = 1.0 + _BumpMap ("Normal Map", 2D) = "bump" {} + + [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 + [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {} + [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0 + + _EmissionColor("Color", Color) = (0,0,0,0) + _EmissionMap("Emission", 2D) = "white" {} + _EmissionPower("Emission Power", Float) = 2.0 + + _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5 + _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0 + [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 + _MetallicGlossMap("Metallic", 2D) = "white" {} + + _DiffuseRamp ("Diffuse Ramp Texture", 2D) = "gray" {} + + _FixedNormal ("Fixed Normal", Vector) = (0,0,1,1) + _ZWrite ("Depth Write", Float) = 0.0 + _Cutoff ("Depth alpha cutoff", Range(0,1)) = 0.0 + _ShadowAlphaCutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + _CustomRenderQueue ("Custom Render Queue", Float) = 0.0 + + _OverlayColor ("Overlay Color", Color) = (0,0,0,0) + _Hue("Hue", Range(-0.5,0.5)) = 0.0 + _Saturation("Saturation", Range(0,2)) = 1.0 + _Brightness("Brightness", Range(0,2)) = 1.0 + + _RimPower("Rim Power", Float) = 2.0 + _RimColor ("Rim Color", Color) = (1,1,1,1) + + _BlendTex ("Blend Texture", 2D) = "white" {} + _BlendAmount ("Blend", Range(0,1)) = 0.0 + + [HideInInspector] _SrcBlend ("__src", Float) = 1.0 + [HideInInspector] _DstBlend ("__dst", Float) = 0.0 + [HideInInspector] _RenderQueue ("__queue", Float) = 0.0 + [HideInInspector] _Cull ("__cull", Float) = 0.0 + } + + SubShader + { + Tags { "Queue"="Transparent" "RenderType"="Sprite" "AlphaDepth"="False" "CanUseSpriteAtlas"="True" "IgnoreProjector"="True" } + LOD 150 + + Pass + { + Name "Vertex" + Tags { "LightMode" = "Vertex" } + Blend [_SrcBlend] [_DstBlend] + ZWrite [_ZWrite] + ZTest LEqual + Cull [_Cull] + Lighting On + + CGPROGRAM + #pragma target 3.0 + + #pragma shader_feature _ _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON _ADDITIVEBLEND _ADDITIVEBLEND_SOFT _MULTIPLYBLEND _MULTIPLYBLEND_X2 + #pragma shader_feature _ _FIXED_NORMALS_VIEWSPACE _FIXED_NORMALS_VIEWSPACE_BACKFACE _FIXED_NORMALS_MODELSPACE _FIXED_NORMALS_MODELSPACE_BACKFACE + #pragma shader_feature _ _SPECULAR _SPECULAR_GLOSSMAP + #pragma shader_feature _NORMALMAP + #pragma shader_feature _ALPHA_CLIP + #pragma shader_feature _EMISSION + #pragma shader_feature _DIFFUSE_RAMP + #pragma shader_feature _COLOR_ADJUST + #pragma shader_feature _RIM_LIGHTING + #pragma shader_feature _TEXTURE_BLEND + #pragma shader_feature _SPHERICAL_HARMONICS + #pragma shader_feature _FOG + + + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_fog + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment frag + + #include "CGIncludes/SpriteVertexLighting.cginc" + ENDCG + } + Pass + { + Name "ShadowCaster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + Fog { Mode Off } + ZWrite On + ZTest LEqual + Cull Off + Lighting Off + + CGPROGRAM + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_shadowcaster + #pragma multi_compile _ PIXELSNAP_ON + #pragma multi_compile _ ETC1_EXTERNAL_ALPHA + + #pragma vertex vert + #pragma fragment frag + + #include "CGIncludes/SpriteShadows.cginc" + ENDCG + } + } + + FallBack "Spine/Sprite/Unlit" + CustomEditor "SpineSpriteShaderGUI" +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesVertexLit.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesVertexLit.shader.meta new file mode 100644 index 0000000..34d232e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Sprite/SpritesVertexLit.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2ce511398fb980f41b7d316c51534590 +timeCreated: 1479457856 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha.meta b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha.meta new file mode 100644 index 0000000..e423168 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d6f7e10049c6d6348ae5c92ccb3825e0 +folderAsset: yes +timeCreated: 1521392482 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Fill.shader b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Fill.shader new file mode 100644 index 0000000..a4c0eba --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Fill.shader @@ -0,0 +1,56 @@ +// - Unlit + no shadow +// - Double-sided, no depth + +Shader "Spine/Straight Alpha/Skeleton Fill" { + Properties { + _FillColor ("FillColor", Color) = (1,1,1,1) + _FillPhase ("FillPhase", Range(0, 1)) = 0 + [NoScaleOffset]_MainTex ("MainTex", 2D) = "white" {} + } + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } + Blend One OneMinusSrcAlpha + Cull Off + ZWrite Off + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + float4 _FillColor; + float _FillPhase; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o = (VertexOutput)0; + o.uv = v.uv; + o.vertexColor = v.vertexColor; + o.pos = UnityObjectToClipPos(v.vertex); + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 rawColor = tex2D(_MainTex,i.uv); + float finalAlpha = (rawColor.a * i.vertexColor.a); + float3 finalColor = lerp((rawColor.rgb * i.vertexColor.rgb * rawColor.a), (_FillColor.rgb * finalAlpha), _FillPhase); + return fixed4(finalColor, finalAlpha); + } + ENDCG + } + } + FallBack "Diffuse" +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Fill.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Fill.shader.meta new file mode 100644 index 0000000..38c4a7c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Fill.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ec3c686f972ccf5459c2b55555e6635f +timeCreated: 1492385797 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Tint.shader b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Tint.shader new file mode 100644 index 0000000..85d89cf --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Tint.shader @@ -0,0 +1,99 @@ +// - Two color tint +// - unlit +// - No depth, no backface culling, no fog. + +Shader "Spine/Straight Alpha/Skeleton Tint" { + Properties { + _Color ("Tint Color", Color) = (1,1,1,1) + _Black ("Black Point", Color) = (0,0,0,0) + [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {} + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } + + Fog { Mode Off } + Cull Off + ZWrite Off + Blend One OneMinusSrcAlpha + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + float4 _Color; + float4 _Black; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o; + o.pos = UnityObjectToClipPos(v.vertex); // replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + o.uv = v.uv; + o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 texColor = tex2D(_MainTex, i.uv); + texColor = float4(texColor.rgb * texColor.a, texColor.a); + return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * _Black.rgb * texColor.a*_Color.a*i.vertexColor.a), 0); + } + ENDCG + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + ZWrite On + ZTest LEqual + + Fog { Mode Off } + Cull Off + Lighting Off + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + sampler2D _MainTex; + fixed _Cutoff; + + struct VertexOutput { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + VertexOutput vert (appdata_base v) { + VertexOutput o; + o.uv = v.texcoord; + TRANSFER_SHADOW_CASTER(o) + return o; + } + + float4 frag (VertexOutput i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Tint.shader.meta b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Tint.shader.meta new file mode 100644 index 0000000..e2b0cbe --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Shaders/Straight Alpha/Spine-Straight-Skeleton-Tint.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c9a84b3418d033a4f9749511a6ac36d6 +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic.meta new file mode 100644 index 0000000..dfa68ee --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6338355ec521d6848bb8ef1e10272b3e +folderAsset: yes +timeCreated: 1455493504 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/BoneFollowerGraphic.cs b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/BoneFollowerGraphic.cs new file mode 100644 index 0000000..f399e9b --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/BoneFollowerGraphic.cs @@ -0,0 +1,131 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity { + [ExecuteInEditMode] + [DisallowMultipleComponent] + [AddComponentMenu("Spine/UI/BoneFollowerGraphic")] + public class BoneFollowerGraphic : MonoBehaviour { + public SkeletonGraphic skeletonGraphic; + public SkeletonGraphic SkeletonGraphic { + get { return skeletonGraphic; } + set { + skeletonGraphic = value; + Initialize(); + } + } + + public bool initializeOnAwake = true; + + /// If a bone isn't set in code, boneName is used to find the bone at the beginning. For runtime switching by name, use SetBoneByName. You can also set the BoneFollower.bone field directly. + [SpineBone(dataField: "skeletonGraphic")] + [SerializeField] public string boneName; + + public bool followBoneRotation = true; + [Tooltip("Follows the skeleton's flip state by controlling this Transform's local scale.")] + public bool followSkeletonFlip = true; + [Tooltip("Follows the target bone's local scale. BoneFollower cannot inherit world/skewed scale because of UnityEngine.Transform property limitations.")] + public bool followLocalScale = false; + public bool followZPosition = true; + + [System.NonSerialized] public Bone bone; + + Transform skeletonTransform; + bool skeletonTransformIsParent; + + [System.NonSerialized] public bool valid; + + /// + /// Sets the target bone by its bone name. Returns false if no bone was found. + public bool SetBone (string name) { + bone = skeletonGraphic.Skeleton.FindBone(name); + if (bone == null) { + Debug.LogError("Bone not found: " + name, this); + return false; + } + boneName = name; + return true; + } + + public void Awake () { + if (initializeOnAwake) Initialize(); + } + + public void Initialize () { + bone = null; + valid = skeletonGraphic != null && skeletonGraphic.IsValid; + if (!valid) return; + + skeletonTransform = skeletonGraphic.transform; +// skeletonGraphic.OnRebuild -= HandleRebuildRenderer; +// skeletonGraphic.OnRebuild += HandleRebuildRenderer; + skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent); + + if (!string.IsNullOrEmpty(boneName)) + bone = skeletonGraphic.Skeleton.FindBone(boneName); + + #if UNITY_EDITOR + if (Application.isEditor) { + LateUpdate(); + } + #endif + } + + public void LateUpdate () { + if (!valid) { + Initialize(); + return; + } + + #if UNITY_EDITOR + if (!Application.isPlaying) + skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent); + #endif + + if (bone == null) { + if (string.IsNullOrEmpty(boneName)) return; + bone = skeletonGraphic.Skeleton.FindBone(boneName); + if (!SetBone(boneName)) return; + } + + var thisTransform = this.transform as RectTransform; + if (thisTransform == null) return; + + var canvas = skeletonGraphic.canvas; + if (canvas == null) canvas = skeletonGraphic.GetComponentInParent(); + float scale = canvas.referencePixelsPerUnit; + + if (skeletonTransformIsParent) { + // Recommended setup: Use local transform properties if Spine GameObject is the immediate parent + thisTransform.localPosition = new Vector3(bone.worldX * scale, bone.worldY * scale, followZPosition ? 0f : thisTransform.localPosition.z); + if (followBoneRotation) thisTransform.localRotation = bone.GetQuaternion(); + } else { + // For special cases: Use transform world properties if transform relationship is complicated + Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(bone.worldX * scale, bone.worldY * scale, 0f)); + if (!followZPosition) targetWorldPosition.z = thisTransform.position.z; + + float boneWorldRotation = bone.WorldRotationX; + + Transform transformParent = thisTransform.parent; + if (transformParent != null) { + Matrix4x4 m = transformParent.localToWorldMatrix; + if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative + boneWorldRotation = -boneWorldRotation; + } + + if (followBoneRotation) { + Vector3 worldRotation = skeletonTransform.rotation.eulerAngles; + thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(worldRotation.x, worldRotation.y, skeletonTransform.rotation.eulerAngles.z + boneWorldRotation)); + } else { + thisTransform.position = targetWorldPosition; + } + } + + Vector3 localScale = followLocalScale ? new Vector3(bone.scaleX, bone.scaleY, 1f) : new Vector3(1f, 1f, 1f); + if (followSkeletonFlip) localScale.y *= bone.skeleton.flipX ^ bone.skeleton.flipY ? -1f : 1f; + thisTransform.localScale = localScale; + } + + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/BoneFollowerGraphic.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/BoneFollowerGraphic.cs.meta new file mode 100644 index 0000000..b227966 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/BoneFollowerGraphic.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b42a195b47491d34b9bcbc40898bcb29 +timeCreated: 1499211965 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor.meta new file mode 100644 index 0000000..12c3774 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 78bba472871bc624f8930d51dea6dd3d +folderAsset: yes +timeCreated: 1455570938 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/BoneFollowerGraphicInspector.cs b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/BoneFollowerGraphicInspector.cs new file mode 100644 index 0000000..1d74f0b --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/BoneFollowerGraphicInspector.cs @@ -0,0 +1,172 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +using Spine.Unity; + +namespace Spine.Unity.Editor { + + using Editor = UnityEditor.Editor; + using Event = UnityEngine.Event; + + [CustomEditor(typeof(BoneFollowerGraphic)), CanEditMultipleObjects] + public class BoneFollowerGraphicInspector : Editor { + + SerializedProperty boneName, skeletonGraphic, followZPosition, followBoneRotation, followLocalScale, followSkeletonFlip; + BoneFollowerGraphic targetBoneFollower; + bool needsReset; + + #region Context Menu Item + [MenuItem ("CONTEXT/SkeletonGraphic/Add BoneFollower GameObject")] + static void AddBoneFollowerGameObject (MenuCommand cmd) { + var skeletonGraphic = cmd.context as SkeletonGraphic; + var go = new GameObject("BoneFollower", typeof(RectTransform)); + var t = go.transform; + t.SetParent(skeletonGraphic.transform); + t.localPosition = Vector3.zero; + + var f = go.AddComponent(); + f.skeletonGraphic = skeletonGraphic; + f.SetBone(skeletonGraphic.Skeleton.RootBone.Data.Name); + + EditorGUIUtility.PingObject(t); + + Undo.RegisterCreatedObjectUndo(go, "Add BoneFollowerGraphic"); + } + + // Validate + [MenuItem ("CONTEXT/SkeletonGraphic/Add BoneFollower GameObject", true)] + static bool ValidateAddBoneFollowerGameObject (MenuCommand cmd) { + var skeletonGraphic = cmd.context as SkeletonGraphic; + return skeletonGraphic.IsValid; + } + #endregion + + void OnEnable () { + skeletonGraphic = serializedObject.FindProperty("skeletonGraphic"); + boneName = serializedObject.FindProperty("boneName"); + followBoneRotation = serializedObject.FindProperty("followBoneRotation"); + followZPosition = serializedObject.FindProperty("followZPosition"); + followLocalScale = serializedObject.FindProperty("followLocalScale"); + followSkeletonFlip = serializedObject.FindProperty("followSkeletonFlip"); + + targetBoneFollower = (BoneFollowerGraphic)target; + if (targetBoneFollower.SkeletonGraphic != null) + targetBoneFollower.SkeletonGraphic.Initialize(false); + + if (!targetBoneFollower.valid || needsReset) { + targetBoneFollower.Initialize(); + targetBoneFollower.LateUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + } + + public void OnSceneGUI () { + var tbf = target as BoneFollowerGraphic; + var skeletonGraphicComponent = tbf.SkeletonGraphic; + if (skeletonGraphicComponent == null) return; + + var transform = skeletonGraphicComponent.transform; + var skeleton = skeletonGraphicComponent.Skeleton; + var canvas = skeletonGraphicComponent.canvas; + float positionScale = canvas == null ? 1f : skeletonGraphicComponent.canvas.referencePixelsPerUnit; + + if (string.IsNullOrEmpty(boneName.stringValue)) { + SpineHandles.DrawBones(transform, skeleton, positionScale); + SpineHandles.DrawBoneNames(transform, skeleton, positionScale); + Handles.Label(tbf.transform.position, "No bone selected", EditorStyles.helpBox); + } else { + var targetBone = tbf.bone; + if (targetBone == null) return; + + SpineHandles.DrawBoneWireframe(transform, targetBone, SpineHandles.TransformContraintColor, positionScale); + Handles.Label(targetBone.GetWorldPosition(transform, positionScale), targetBone.Data.Name, SpineHandles.BoneNameStyle); + } + } + + override public void OnInspectorGUI () { + if (serializedObject.isEditingMultipleObjects) { + if (needsReset) { + needsReset = false; + foreach (var o in targets) { + var bf = (BoneFollower)o; + bf.Initialize(); + bf.LateUpdate(); + } + SceneView.RepaintAll(); + } + + EditorGUI.BeginChangeCheck(); + DrawDefaultInspector(); + needsReset |= EditorGUI.EndChangeCheck(); + return; + } + + if (needsReset && Event.current.type == EventType.Layout) { + targetBoneFollower.Initialize(); + targetBoneFollower.LateUpdate(); + needsReset = false; + SceneView.RepaintAll(); + } + serializedObject.Update(); + + // Find Renderer + if (skeletonGraphic.objectReferenceValue == null) { + SkeletonGraphic parentRenderer = targetBoneFollower.GetComponentInParent(); + if (parentRenderer != null && parentRenderer.gameObject != targetBoneFollower.gameObject) { + skeletonGraphic.objectReferenceValue = parentRenderer; + Debug.Log("Inspector automatically assigned BoneFollowerGraphic.SkeletonGraphic"); + } + } + + EditorGUILayout.PropertyField(skeletonGraphic); + var skeletonGraphicComponent = skeletonGraphic.objectReferenceValue as SkeletonGraphic; + if (skeletonGraphicComponent != null) { + if (skeletonGraphicComponent.gameObject == targetBoneFollower.gameObject) { + skeletonGraphic.objectReferenceValue = null; + EditorUtility.DisplayDialog("Invalid assignment.", "BoneFollowerGraphic can only follow a skeleton on a separate GameObject.\n\nCreate a new GameObject for your BoneFollower, or choose a SkeletonGraphic from a different GameObject.", "Ok"); + } + } + + if (!targetBoneFollower.valid) { + needsReset = true; + } + + if (targetBoneFollower.valid) { + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(boneName); + needsReset |= EditorGUI.EndChangeCheck(); + + EditorGUILayout.PropertyField(followBoneRotation); + EditorGUILayout.PropertyField(followZPosition); + EditorGUILayout.PropertyField(followLocalScale); + EditorGUILayout.PropertyField(followSkeletonFlip); + + //BoneFollowerInspector.RecommendRigidbodyButton(targetBoneFollower); + } else { + var boneFollowerSkeletonGraphic = targetBoneFollower.skeletonGraphic; + if (boneFollowerSkeletonGraphic == null) { + EditorGUILayout.HelpBox("SkeletonGraphic is unassigned. Please assign a SkeletonRenderer (SkeletonAnimation or SkeletonAnimator).", MessageType.Warning); + } else { + boneFollowerSkeletonGraphic.Initialize(false); + + if (boneFollowerSkeletonGraphic.skeletonDataAsset == null) + EditorGUILayout.HelpBox("Assigned SkeletonGraphic does not have SkeletonData assigned to it.", MessageType.Warning); + + if (!boneFollowerSkeletonGraphic.IsValid) + EditorGUILayout.HelpBox("Assigned SkeletonGraphic is invalid. Check target SkeletonGraphic, its SkeletonDataAsset or the console for other errors.", MessageType.Warning); + } + } + + var current = Event.current; + bool wasUndo = (current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed"); + if (wasUndo) + targetBoneFollower.Initialize(); + + serializedObject.ApplyModifiedProperties(); + } + + } +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/BoneFollowerGraphicInspector.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/BoneFollowerGraphicInspector.cs.meta new file mode 100644 index 0000000..b3d60a4 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/BoneFollowerGraphicInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: da44a8561fd243c43a1f77bda36de0eb +timeCreated: 1499279157 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs new file mode 100644 index 0000000..10687c3 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs @@ -0,0 +1,229 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; +using Spine; + +namespace Spine.Unity.Editor { + + [InitializeOnLoad] + [CustomEditor(typeof(SkeletonGraphic))] + [CanEditMultipleObjects] + public class SkeletonGraphicInspector : UnityEditor.Editor { + SerializedProperty material, color; + SerializedProperty skeletonDataAsset, initialSkinName; + SerializedProperty startingAnimation, startingLoop, timeScale, freeze, unscaledTime, tintBlack; + SerializedProperty initialFlipX, initialFlipY; + SerializedProperty meshGeneratorSettings; + SerializedProperty raycastTarget; + + SkeletonGraphic thisSkeletonGraphic; + + void OnEnable () { + var so = this.serializedObject; + thisSkeletonGraphic = target as SkeletonGraphic; + + // MaskableGraphic + material = so.FindProperty("m_Material"); + color = so.FindProperty("m_Color"); + raycastTarget = so.FindProperty("m_RaycastTarget"); + + // SkeletonRenderer + skeletonDataAsset = so.FindProperty("skeletonDataAsset"); + initialSkinName = so.FindProperty("initialSkinName"); + + initialFlipX = so.FindProperty("initialFlipX"); + initialFlipY = so.FindProperty("initialFlipY"); + + // SkeletonAnimation + startingAnimation = so.FindProperty("startingAnimation"); + startingLoop = so.FindProperty("startingLoop"); + timeScale = so.FindProperty("timeScale"); + unscaledTime = so.FindProperty("unscaledTime"); + freeze = so.FindProperty("freeze"); + + meshGeneratorSettings = so.FindProperty("meshGenerator").FindPropertyRelative("settings"); + meshGeneratorSettings.isExpanded = SkeletonRendererInspector.advancedFoldout; + } + + public override void OnInspectorGUI () { + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.PropertyField(skeletonDataAsset); + EditorGUILayout.PropertyField(material); + EditorGUILayout.PropertyField(color); + + if (thisSkeletonGraphic.skeletonDataAsset == null) { + EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info); + serializedObject.ApplyModifiedProperties(); + serializedObject.Update(); + return; + } + using (new SpineInspectorUtility.BoxScope()) { + EditorGUILayout.PropertyField(meshGeneratorSettings, SpineInspectorUtility.TempContent("Advanced..."), includeChildren: true); + SkeletonRendererInspector.advancedFoldout = meshGeneratorSettings.isExpanded; + } + + EditorGUILayout.Space(); + EditorGUILayout.PropertyField(initialSkinName); + { + var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight); + EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent("Initial Flip")); + rect.x += EditorGUIUtility.labelWidth; + rect.width = 30f; + SpineInspectorUtility.ToggleLeft(rect, initialFlipX, SpineInspectorUtility.TempContent("X", tooltip: "initialFlipX")); + rect.x += 35f; + SpineInspectorUtility.ToggleLeft(rect, initialFlipY, SpineInspectorUtility.TempContent("Y", tooltip: "initialFlipY")); + } + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(startingAnimation); + EditorGUILayout.PropertyField(startingLoop); + EditorGUILayout.PropertyField(timeScale); + EditorGUILayout.PropertyField(unscaledTime, SpineInspectorUtility.TempContent(unscaledTime.displayName, tooltip: "If checked, this will use Time.unscaledDeltaTime to make this update independent of game Time.timeScale. Instance SkeletonGraphic.timeScale will still be applied.")); + EditorGUILayout.Space(); + EditorGUILayout.PropertyField(freeze); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("UI", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(raycastTarget); + + bool wasChanged = EditorGUI.EndChangeCheck(); + + if (wasChanged) + serializedObject.ApplyModifiedProperties(); + } + + #region Menus + [MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")] + static void MatchRectTransformWithBounds (MenuCommand command) { + var skeletonGraphic = (SkeletonGraphic)command.context; + Mesh mesh = skeletonGraphic.GetLastMesh(); + if (mesh == null) { + Debug.Log("Mesh was not previously generated."); + return; + } + + if (mesh.vertexCount == 0) { + skeletonGraphic.rectTransform.sizeDelta = new Vector2(50f, 50f); + skeletonGraphic.rectTransform.pivot = new Vector2(0.5f, 0.5f); + return; + } + + mesh.RecalculateBounds(); + var bounds = mesh.bounds; + var size = bounds.size; + var center = bounds.center; + var p = new Vector2( + 0.5f - (center.x / size.x), + 0.5f - (center.y / size.y) + ); + + skeletonGraphic.rectTransform.sizeDelta = size; + skeletonGraphic.rectTransform.pivot = p; + } + + [MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 15)] + static public void SkeletonGraphicCreateMenuItem () { + var parentGameObject = Selection.activeObject as GameObject; + var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent(); + + if (parentTransform == null) + Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas"); + + var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic"); + gameObject.transform.SetParent(parentTransform, false); + EditorUtility.FocusProjectWindow(); + Selection.activeObject = gameObject; + EditorGUIUtility.PingObject(Selection.activeObject); + } + + // SpineEditorUtilities.InstantiateDelegate. Used by drag and drop. + public static Component SpawnSkeletonGraphicFromDrop (SkeletonDataAsset data) { + return InstantiateSkeletonGraphic(data); + } + + public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) { + return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName)); + } + + public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) { + string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", "")); + var go = NewSkeletonGraphicGameObject(spineGameObjectName); + var graphic = go.GetComponent(); + graphic.skeletonDataAsset = skeletonDataAsset; + + SkeletonData data = skeletonDataAsset.GetSkeletonData(true); + + if (data == null) { + for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) { + string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]); + skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset)); + } + + data = skeletonDataAsset.GetSkeletonData(true); + } + + skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0]; + graphic.MeshGenerator.settings.zSpacing = SpineEditorUtilities.defaultZSpacing; + + graphic.Initialize(false); + if (skin != null) graphic.Skeleton.SetSkin(skin); + graphic.initialSkinName = skin.Name; + graphic.Skeleton.UpdateWorldTransform(); + graphic.UpdateMesh(); + + return graphic; + } + + static GameObject NewSkeletonGraphicGameObject (string gameObjectName) { + var go = new GameObject(gameObjectName, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic)); + var graphic = go.GetComponent(); + graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial; + return go; + } + + public static Material DefaultSkeletonGraphicMaterial { + get { + var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material"); + if (guids.Length <= 0) return null; + + var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]); + if (string.IsNullOrEmpty(firstAssetPath)) return null; + + var firstMaterial = AssetDatabase.LoadAssetAtPath(firstAssetPath); + return firstMaterial; + } + } + + #endregion + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta new file mode 100644 index 0000000..5fced2e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0d81cc76b52fcdf499b2db252a317726 +timeCreated: 1455570945 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders.meta new file mode 100644 index 0000000..da6d0a2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 43897010c7e77c54897cb98c1ddf84f1 +folderAsset: yes +timeCreated: 1455128695 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicDefault.mat b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicDefault.mat new file mode 100644 index 0000000..08d7764 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicDefault.mat @@ -0,0 +1,87 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: SkeletonGraphicDefault + m_Shader: {fileID: 4800000, guid: fa95b0fb6983c0f40a152e6f9aa82bfb, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 5 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _AlphaTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + 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} + - _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: 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} + - _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_Floats: + - PixelSnap: 0 + - _BumpScale: 1 + - _ColorMask: 15 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EnableExternalAlpha: 0 + - _Glossiness: 0.5 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SrcBlend: 1 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _UVSec: 0 + - _UseUIAlphaClip: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _Flip: {r: 1, g: 1, b: 1, a: 1} + - _RendererColor: {r: 1, g: 1, b: 1, a: 1} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicDefault.mat.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicDefault.mat.meta new file mode 100644 index 0000000..aaa80ff --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicDefault.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b66cf7a186d13054989b33a5c90044e4 +timeCreated: 1455140322 +licenseType: Free +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicTintBlack.mat b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicTintBlack.mat new file mode 100644 index 0000000..d78d415 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicTintBlack.mat @@ -0,0 +1,79 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: SkeletonGraphicTintBlack + m_Shader: {fileID: 4800000, guid: f64c7bc238bb2c246b8ca1912b2b6b9c, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 5 + m_EnableInstancingVariants: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + 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} + - _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: 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} + - _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_Floats: + - _BumpScale: 1 + - _ColorMask: 15 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _Glossiness: 0.5 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SrcBlend: 1 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _UVSec: 0 + - _UseUIAlphaClip: 0 + - _ZWrite: 1 + m_Colors: + - _Black: {r: 0, g: 0, b: 0, a: 0} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicTintBlack.mat.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicTintBlack.mat.meta new file mode 100644 index 0000000..0d83b23 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/SkeletonGraphicTintBlack.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cfcea0e11aa80bb4b8d05790b905fc31 +timeCreated: 1455140322 +licenseType: Free +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader new file mode 100644 index 0000000..eb02354 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader @@ -0,0 +1,118 @@ +// This is a premultiply-alpha adaptation of the built-in Unity shader "UI/Default" to allow Unity UI stencil masking. + +Shader "Spine/SkeletonGraphic Tint Black (Premultiply Alpha)" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + _Color ("Tint", Color) = (1,1,1,1) + _Black ("Black Point", Color) = (0,0,0,0) + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Fog { Mode Off } + Blend One OneMinusSrcAlpha + ColorMask [_ColorMask] + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + #include "UnityUI.cginc" + + #pragma multi_compile __ UNITY_UI_ALPHACLIP + + struct VertexInput { + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct VertexOutput { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + half2 texcoord : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float4 worldPosition : TEXCOORD3; + UNITY_VERTEX_OUTPUT_STEREO + }; + + fixed4 _Color; + fixed4 _Black; + fixed4 _TextureSampleAdd; + float4 _ClipRect; + + VertexOutput vert (VertexInput IN) { + VertexOutput OUT; + + UNITY_SETUP_INSTANCE_ID(IN); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); + + OUT.worldPosition = IN.vertex; + OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); + OUT.texcoord = IN.texcoord; + + OUT.color = IN.color * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + OUT.uv1 = IN.uv1; + OUT.uv2 = IN.uv2; + return OUT; + } + + sampler2D _MainTex; + + fixed4 frag (VertexOutput IN) : SV_Target + { + half4 texColor = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd); + + texColor.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); + + #ifdef UNITY_UI_ALPHACLIP + clip (texColor.a - 0.001); + #endif + + return (texColor * IN.color) + float4(((1-texColor.rgb) * (_Black.rgb + float3(IN.uv1.r, IN.uv1.g, IN.uv2.r)) * texColor.a * _Color.a * IN.color.a), 0); + } + ENDCG + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader.meta new file mode 100644 index 0000000..5193a26 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f64c7bc238bb2c246b8ca1912b2b6b9c +timeCreated: 1455080068 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic.shader b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic.shader new file mode 100644 index 0000000..fa25e7b --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic.shader @@ -0,0 +1,115 @@ +// This is a premultiply-alpha adaptation of the built-in Unity shader "UI/Default" in Unity 5.6.2 to allow Unity UI stencil masking. + +Shader "Spine/SkeletonGraphic (Premultiply Alpha)" +{ + Properties + { + [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} + _Color ("Tint", Color) = (1,1,1,1) + + _StencilComp ("Stencil Comparison", Float) = 8 + _Stencil ("Stencil ID", Float) = 0 + _StencilOp ("Stencil Operation", Float) = 0 + _StencilWriteMask ("Stencil Write Mask", Float) = 255 + _StencilReadMask ("Stencil Read Mask", Float) = 255 + + _ColorMask ("Color Mask", Float) = 15 + + [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 + } + + SubShader + { + Tags + { + "Queue"="Transparent" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + "CanUseSpriteAtlas"="True" + } + + Stencil + { + Ref [_Stencil] + Comp [_StencilComp] + Pass [_StencilOp] + ReadMask [_StencilReadMask] + WriteMask [_StencilWriteMask] + } + + Cull Off + Lighting Off + ZWrite Off + ZTest [unity_GUIZTestMode] + Fog { Mode Off } + Blend One OneMinusSrcAlpha + ColorMask [_ColorMask] + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma target 2.0 + + #include "UnityCG.cginc" + #include "UnityUI.cginc" + + #pragma multi_compile __ UNITY_UI_ALPHACLIP + + struct VertexInput { + float4 vertex : POSITION; + float4 color : COLOR; + float2 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct VertexOutput { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + half2 texcoord : TEXCOORD0; + float4 worldPosition : TEXCOORD1; + UNITY_VERTEX_OUTPUT_STEREO + }; + + fixed4 _Color; + fixed4 _TextureSampleAdd; + float4 _ClipRect; + + VertexOutput vert (VertexInput IN) { + VertexOutput OUT; + + UNITY_SETUP_INSTANCE_ID(IN); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); + + OUT.worldPosition = IN.vertex; + OUT.vertex = UnityObjectToClipPos(OUT.worldPosition); + OUT.texcoord = IN.texcoord; + + #ifdef UNITY_HALF_TEXEL_OFFSET + OUT.vertex.xy += (_ScreenParams.zw-1.0) * float2(-1,1); + #endif + + OUT.color = IN.color * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + return OUT; + } + + sampler2D _MainTex; + + fixed4 frag (VertexOutput IN) : SV_Target + { + half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; + + color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); + + #ifdef UNITY_UI_ALPHACLIP + clip (color.a - 0.001); + #endif + + return color; + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic.shader.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic.shader.meta new file mode 100644 index 0000000..167123f --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: fa95b0fb6983c0f40a152e6f9aa82bfb +timeCreated: 1455080068 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs new file mode 100644 index 0000000..d6db696 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs @@ -0,0 +1,299 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEngine.UI; +using Spine; + +namespace Spine.Unity { + [ExecuteInEditMode, RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent] + [AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")] + public class SkeletonGraphic : MaskableGraphic, ISkeletonComponent, IAnimationStateComponent, ISkeletonAnimation, IHasSkeletonDataAsset { + + #region Inspector + public SkeletonDataAsset skeletonDataAsset; + public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } + + [SpineSkin(dataField:"skeletonDataAsset")] + public string initialSkinName = "default"; + public bool initialFlipX, initialFlipY; + + [SpineAnimation(dataField:"skeletonDataAsset")] + public string startingAnimation; + public bool startingLoop; + public float timeScale = 1f; + public bool freeze; + public bool unscaledTime; + + #if UNITY_EDITOR + protected override void OnValidate () { + // This handles Scene View preview. + base.OnValidate (); + if (this.IsValid) { + if (skeletonDataAsset == null) { + Clear(); + startingAnimation = ""; + } else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) { + Clear(); + Initialize(true); + startingAnimation = ""; + if (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].materials.Length > 1) + Debug.LogError("Unity UI does not support multiple textures per Renderer. Your skeleton will not be rendered correctly. Recommend using SkeletonAnimation instead. This requires the use of a Screen space camera canvas."); + } else { + if (freeze) return; + + if (!string.IsNullOrEmpty(initialSkinName)) { + var skin = skeleton.data.FindSkin(initialSkinName); + if (skin != null) { + if (skin == skeleton.data.defaultSkin) + skeleton.SetSkin((Skin)null); + else + skeleton.SetSkin(skin); + } + + } + + // Only provide visual feedback to inspector changes in Unity Editor Edit mode. + if (!Application.isPlaying) { + skeleton.flipX = this.initialFlipX; + skeleton.flipY = this.initialFlipY; + + skeleton.SetToSetupPose(); + if (!string.IsNullOrEmpty(startingAnimation)) + skeleton.PoseWithAnimation(startingAnimation, 0f, false); + } + + } + } else { + if (skeletonDataAsset != null) + Initialize(true); + } + } + + protected override void Reset () { + base.Reset(); + if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)")) + Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material."); + } + #endif + #endregion + + #region Runtime Instantiation + public static SkeletonGraphic NewSkeletonGraphicGameObject (SkeletonDataAsset skeletonDataAsset, Transform parent) { + SkeletonGraphic sg = SkeletonGraphic.AddSkeletonGraphicComponent(new GameObject("New Spine GameObject"), skeletonDataAsset); + if (parent != null) sg.transform.SetParent(parent, false); + return sg; + } + + public static SkeletonGraphic AddSkeletonGraphicComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset) { + var c = gameObject.AddComponent(); + if (skeletonDataAsset != null) { + c.skeletonDataAsset = skeletonDataAsset; + c.Initialize(false); + } + return c; + } + #endregion + + #region Internals + // This is used by the UI system to determine what to put in the MaterialPropertyBlock. + Texture overrideTexture; + public Texture OverrideTexture { + get { return overrideTexture; } + set { + overrideTexture = value; + canvasRenderer.SetTexture(this.mainTexture); // Refresh canvasRenderer's texture. Make sure it handles null. + } + } + public override Texture mainTexture { + get { + // Fail loudly when incorrectly set up. + if (overrideTexture != null) return overrideTexture; + return skeletonDataAsset == null ? null : skeletonDataAsset.atlasAssets[0].materials[0].mainTexture; + } + } + + protected override void Awake () { + base.Awake (); + if (!this.IsValid) { + Initialize(false); + Rebuild(CanvasUpdate.PreRender); + } + } + + public override void Rebuild (CanvasUpdate update) { + base.Rebuild(update); + if (canvasRenderer.cull) return; + if (update == CanvasUpdate.PreRender) UpdateMesh(); + } + + public virtual void Update () { + if (freeze) return; + Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime); + } + + public virtual void Update (float deltaTime) { + if (!this.IsValid) return; + + deltaTime *= timeScale; + skeleton.Update(deltaTime); + state.Update(deltaTime); + state.Apply(skeleton); + + if (UpdateLocal != null) UpdateLocal(this); + + skeleton.UpdateWorldTransform(); + + if (UpdateWorld != null) { + UpdateWorld(this); + skeleton.UpdateWorldTransform(); + } + + if (UpdateComplete != null) UpdateComplete(this); + } + + public void LateUpdate () { + if (freeze) return; + //this.SetVerticesDirty(); // Which is better? + UpdateMesh(); + } + #endregion + + #region API + protected Skeleton skeleton; + public Skeleton Skeleton { get { return skeleton; } internal set { skeleton = value; } } + public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } } + public bool IsValid { get { return skeleton != null; } } + + protected Spine.AnimationState state; + public Spine.AnimationState AnimationState { get { return state; } } + + [SerializeField] protected Spine.Unity.MeshGenerator meshGenerator = new MeshGenerator(); + public Spine.Unity.MeshGenerator MeshGenerator { get { return this.meshGenerator; } } + DoubleBuffered meshBuffers; + SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction(); + + public Mesh GetLastMesh () { + return meshBuffers.GetCurrent().mesh; + } + + public event UpdateBonesDelegate UpdateLocal; + public event UpdateBonesDelegate UpdateWorld; + public event UpdateBonesDelegate UpdateComplete; + + /// Occurs after the vertex data populated every frame, before the vertices are pushed into the mesh. + public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices; + + public void Clear () { + skeleton = null; + canvasRenderer.Clear(); + } + + public void Initialize (bool overwrite) { + if (this.IsValid && !overwrite) return; + + // Make sure none of the stuff is null + if (this.skeletonDataAsset == null) return; + var skeletonData = this.skeletonDataAsset.GetSkeletonData(false); + if (skeletonData == null) return; + + if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].materials.Length <= 0) return; + + this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData()); + if (state == null) { + Clear(); + return; + } + + this.skeleton = new Skeleton(skeletonData) { + flipX = this.initialFlipX, + flipY = this.initialFlipY + }; + + meshBuffers = new DoubleBuffered(); + canvasRenderer.SetTexture(this.mainTexture); // Needed for overwriting initializations. + + // Set the initial Skin and Animation + if (!string.IsNullOrEmpty(initialSkinName)) + skeleton.SetSkin(initialSkinName); + + #if UNITY_EDITOR + if (!string.IsNullOrEmpty(startingAnimation)) { + if (Application.isPlaying) { + state.SetAnimation(0, startingAnimation, startingLoop); + } else { + // Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above. + var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation); + if (animationObject != null) + animationObject.PoseSkeleton(skeleton, 0); + } + Update(0); + } + #else + if (!string.IsNullOrEmpty(startingAnimation)) { + state.SetAnimation(0, startingAnimation, startingLoop); + Update(0); + } + #endif + } + + public void UpdateMesh () { + if (!this.IsValid) return; + + skeleton.SetColor(this.color); + var smartMesh = meshBuffers.GetNext(); + var currentInstructions = this.currentInstructions; + + MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, this.material); + bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed); + + meshGenerator.Begin(); + if (currentInstructions.hasActiveClipping) { + meshGenerator.AddSubmesh(currentInstructions.submeshInstructions.Items[0], updateTriangles); + } else { + meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles); + } + + if (canvas != null) meshGenerator.ScaleVertexData(canvas.referencePixelsPerUnit); + if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers); + + var mesh = smartMesh.mesh; + meshGenerator.FillVertexData(mesh); + if (updateTriangles) meshGenerator.FillTrianglesSingle(mesh); + meshGenerator.FillLateVertexData(mesh); + + canvasRenderer.SetMesh(mesh); + smartMesh.instructionUsed.Set(currentInstructions); + + //this.UpdateMaterial(); // TODO: This allocates memory. + } + #endregion + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta new file mode 100644 index 0000000..e44ab78 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: d85b887af7e6c3f45a2e2d2920d641bc +timeCreated: 1455576193 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: + - m_Material: {fileID: 2100000, guid: b66cf7a186d13054989b33a5c90044e4, type: 2} + - skeletonDataAsset: {instanceID: 0} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs new file mode 100644 index 0000000..8fa8996 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs @@ -0,0 +1,101 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity.Modules { + public class SkeletonGraphicMirror : MonoBehaviour { + + public SkeletonRenderer source; + public bool mirrorOnStart = true; + public bool restoreOnDisable = true; + SkeletonGraphic skeletonGraphic; + + Skeleton originalSkeleton; + bool originalFreeze; + Texture2D overrideTexture; + + private void Awake () { + skeletonGraphic = GetComponent(); + } + + void Start () { + if (mirrorOnStart) StartMirroring(); + } + + void LateUpdate () { + skeletonGraphic.UpdateMesh(); + } + + void OnDisable () { + if (restoreOnDisable) RestoreIndependentSkeleton(); + } + + /// Freeze the SkeletonGraphic on this GameObject, and use the source as the Skeleton to be rendered by the SkeletonGraphic. + public void StartMirroring () { + if (source == null) return; + if (skeletonGraphic == null) return; + + skeletonGraphic.startingAnimation = string.Empty; + + if (originalSkeleton == null) { + originalSkeleton = skeletonGraphic.Skeleton; + originalFreeze = skeletonGraphic.freeze; + } + + skeletonGraphic.Skeleton = source.skeleton; + skeletonGraphic.freeze = true; + if (overrideTexture != null) + skeletonGraphic.OverrideTexture = overrideTexture; + } + + /// Use a new texture for the SkeletonGraphic. Use this if your source skeleton uses a repacked atlas. + public void UpdateTexture (Texture2D newOverrideTexture) { + overrideTexture = newOverrideTexture; + if (newOverrideTexture != null) + skeletonGraphic.OverrideTexture = overrideTexture; + } + + /// Stops mirroring the source SkeletonRenderer and allows the SkeletonGraphic to become an independent Skeleton component again. + public void RestoreIndependentSkeleton () { + if (originalSkeleton == null) + return; + + skeletonGraphic.Skeleton = originalSkeleton; + skeletonGraphic.freeze = originalFreeze; + skeletonGraphic.OverrideTexture = null; + + originalSkeleton = null; + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs.meta new file mode 100644 index 0000000..3e0f6bc --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: dbeb0b9949e46754eb0e0b61021b4f1c +timeCreated: 1532024358 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator.meta new file mode 100644 index 0000000..72398a4 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3a361f5ac799a5149b340f9e20da27d1 +folderAsset: yes +timeCreated: 1457405502 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor.meta new file mode 100644 index 0000000..1d2ca48 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 211465c9f045fd142abe552a6ffdc799 +folderAsset: yes +timeCreated: 1457405813 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs new file mode 100644 index 0000000..81eda3f --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs @@ -0,0 +1,56 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; +using Spine.Unity.Editor; + +namespace Spine.Unity.Modules { + [CustomEditor(typeof(SkeletonPartsRenderer))] + public class SkeletonRenderPartInspector : UnityEditor.Editor { + SpineInspectorUtility.SerializedSortingProperties sortingProperties; + + void OnEnable () { + sortingProperties = new SpineInspectorUtility.SerializedSortingProperties((target as Component).GetComponent()); + } + + public override void OnInspectorGUI () { + SpineInspectorUtility.SortingPropertyFields(sortingProperties, true); + EditorGUILayout.Space(); + if (SpineInspectorUtility.LargeCenteredButton(new GUIContent("Select SkeletonRenderer", SpineEditorUtilities.Icons.spine))) { + var thisSkeletonPartsRenderer = target as SkeletonPartsRenderer; + var srs = thisSkeletonPartsRenderer.GetComponentInParent(); + if (srs != null && srs.partsRenderers.Contains(thisSkeletonPartsRenderer) && srs.SkeletonRenderer != null) + Selection.activeGameObject = srs.SkeletonRenderer.gameObject; + } + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs.meta new file mode 100644 index 0000000..a97d662 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 30e43037bf4433645ad70266f34c1c8b +timeCreated: 1458051036 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs new file mode 100644 index 0000000..856e531 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs @@ -0,0 +1,291 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; + +using System.Collections.Generic; + +using Spine.Unity; +using Spine.Unity.Editor; + +namespace Spine.Unity.Modules { + + [CustomEditor(typeof(SkeletonRenderSeparator))] + public class SkeletonRenderSeparatorInspector : UnityEditor.Editor { + SkeletonRenderSeparator component; + + // Properties + SerializedProperty skeletonRenderer_, copyPropertyBlock_, copyMeshRendererFlags_, partsRenderers_; + static bool partsRenderersExpanded = false; + + // For separator field. + SerializedObject skeletonRendererSerializedObject; + SerializedProperty separatorNamesProp; + static bool skeletonRendererExpanded = true; + bool slotsReapplyRequired = false; + + void OnEnable () { + if (component == null) + component = target as SkeletonRenderSeparator; + + skeletonRenderer_ = serializedObject.FindProperty("skeletonRenderer"); + copyPropertyBlock_ = serializedObject.FindProperty("copyPropertyBlock"); + copyMeshRendererFlags_ = serializedObject.FindProperty("copyMeshRendererFlags"); + + var partsRenderers = component.partsRenderers; + partsRenderers_ = serializedObject.FindProperty("partsRenderers"); + partsRenderers_.isExpanded = partsRenderersExpanded || // last state + partsRenderers.Contains(null) || // null items found + partsRenderers.Count < 1 || // no parts renderers + (skeletonRenderer_.objectReferenceValue != null && SkeletonRendererSeparatorCount + 1 > partsRenderers.Count); // not enough parts renderers + } + + int SkeletonRendererSeparatorCount { + get { + if (Application.isPlaying) + return component.SkeletonRenderer.separatorSlots.Count; + else + return separatorNamesProp == null ? 0 : separatorNamesProp.arraySize; + } + } + + public override void OnInspectorGUI () { + var componentRenderers = component.partsRenderers; + int totalParts; + + using (new SpineInspectorUtility.LabelWidthScope()) { + bool componentEnabled = component.enabled; + bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled); + if (checkBox != componentEnabled) + component.enabled = checkBox; + if (component.SkeletonRenderer.disableRenderingOnOverride && !component.enabled) + EditorGUILayout.HelpBox("By default, SkeletonRenderer's MeshRenderer is disabled while the SkeletonRenderSeparator takes over rendering. It is re-enabled when SkeletonRenderSeparator is disabled.", MessageType.Info); + + EditorGUILayout.PropertyField(copyPropertyBlock_); + EditorGUILayout.PropertyField(copyMeshRendererFlags_); + } + + // SkeletonRenderer Box + using (new SpineInspectorUtility.BoxScope(false)) { + // Fancy SkeletonRenderer foldout reference field + { + EditorGUI.indentLevel++; + EditorGUI.BeginChangeCheck(); + var foldoutSkeletonRendererRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); + EditorGUI.PropertyField(foldoutSkeletonRendererRect, skeletonRenderer_); + if (EditorGUI.EndChangeCheck()) + serializedObject.ApplyModifiedProperties(); + if (component.SkeletonRenderer != null) { + skeletonRendererExpanded = EditorGUI.Foldout(foldoutSkeletonRendererRect, skeletonRendererExpanded, ""); + } + EditorGUI.indentLevel--; + } + + int separatorCount = 0; + EditorGUI.BeginChangeCheck(); + if (component.SkeletonRenderer != null) { + // Separators from SkeletonRenderer + { + bool skeletonRendererMismatch = skeletonRendererSerializedObject != null && skeletonRendererSerializedObject.targetObject != component.SkeletonRenderer; + if (separatorNamesProp == null || skeletonRendererMismatch) { + if (component.SkeletonRenderer != null) { + skeletonRendererSerializedObject = new SerializedObject(component.SkeletonRenderer); + separatorNamesProp = skeletonRendererSerializedObject.FindProperty("separatorSlotNames"); + separatorNamesProp.isExpanded = true; + } + } + + if (separatorNamesProp != null) { + if (skeletonRendererExpanded) { + EditorGUI.indentLevel++; + SkeletonRendererInspector.SeparatorsField(separatorNamesProp); + EditorGUI.indentLevel--; + } + separatorCount = this.SkeletonRendererSeparatorCount; + } + } + + if (SkeletonRendererSeparatorCount == 0) { + EditorGUILayout.HelpBox("Separators are empty. Change the size to 1 and choose a slot if you want the render to be separated.", MessageType.Info); + } + } + + if (EditorGUI.EndChangeCheck()) { + skeletonRendererSerializedObject.ApplyModifiedProperties(); + + if (!Application.isPlaying) + slotsReapplyRequired = true; + } + + + totalParts = separatorCount + 1; + var counterStyle = skeletonRendererExpanded ? EditorStyles.label : EditorStyles.miniLabel; + EditorGUILayout.LabelField(string.Format("{0}: separates into {1}.", SpineInspectorUtility.Pluralize(separatorCount, "separator", "separators"), SpineInspectorUtility.Pluralize(totalParts, "part", "parts") ), counterStyle); + } + + // Parts renderers + using (new SpineInspectorUtility.BoxScope(false)) { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(this.partsRenderers_, true); + EditorGUI.indentLevel--; + + // Null items warning + bool nullItemsFound = componentRenderers.Contains(null); + if (nullItemsFound) + EditorGUILayout.HelpBox("Some items in the parts renderers list are null and may cause problems.\n\nYou can right-click on that element and choose 'Delete Array Element' to remove it.", MessageType.Warning); + + // (Button) Match Separators count + if (separatorNamesProp != null) { + int currentRenderers = 0; + foreach (var r in componentRenderers) { + if (r != null) + currentRenderers++; + } + int extraRenderersNeeded = totalParts - currentRenderers; + + if (component.enabled && component.SkeletonRenderer != null && extraRenderersNeeded > 0) { + EditorGUILayout.HelpBox(string.Format("Insufficient parts renderers. Some parts will not be rendered."), MessageType.Warning); + string addMissingLabel = string.Format("Add the missing renderer{1} ({0}) ", extraRenderersNeeded, SpineInspectorUtility.PluralThenS(extraRenderersNeeded)); + if (GUILayout.Button(addMissingLabel, GUILayout.Height(40f))) { + AddPartsRenderer(extraRenderersNeeded); + DetectOrphanedPartsRenderers(component); + } + } + } + + if (partsRenderers_.isExpanded != partsRenderersExpanded) partsRenderersExpanded = partsRenderers_.isExpanded; + if (partsRenderers_.isExpanded) { + using (new EditorGUILayout.HorizontalScope()) { + // (Button) Destroy Renderers button + if (componentRenderers.Count > 0) { + if (GUILayout.Button("Clear Parts Renderers")) { + // Do you really want to destroy all? + Undo.RegisterCompleteObjectUndo(component, "Clear Parts Renderers"); + if (EditorUtility.DisplayDialog("Destroy Renderers", "Do you really want to destroy all the Parts Renderer GameObjects in the list?", "Destroy", "Cancel")) { + foreach (var r in componentRenderers) { + if (r != null) + Undo.DestroyObjectImmediate(r.gameObject); + } + componentRenderers.Clear(); + // Do you also want to destroy orphans? (You monster.) + DetectOrphanedPartsRenderers(component); + } + } + } + + // (Button) Add Part Renderer button + if (GUILayout.Button("Add Parts Renderer")) + AddPartsRenderer(1); + } + } + } + + serializedObject.ApplyModifiedProperties(); + + if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) { + SkeletonRendererInspector.ReapplySeparatorSlotNames(component.SkeletonRenderer); + component.SkeletonRenderer.LateUpdate(); + SceneView.RepaintAll(); + slotsReapplyRequired = false; + } + } + + public void AddPartsRenderer (int count) { + var componentRenderers = component.partsRenderers; + bool emptyFound = componentRenderers.Contains(null); + if (emptyFound) { + bool userClearEntries = EditorUtility.DisplayDialog("Empty entries found", "Null entries found. Do you want to remove null entries before adding the new renderer? ", "Clear Empty Entries", "Don't Clear"); + if (userClearEntries) componentRenderers.RemoveAll(x => x == null); + } + + Undo.RegisterCompleteObjectUndo(component, "Add Parts Renderers"); + for (int i = 0; i < count; i++) { + int index = componentRenderers.Count; + var smr = SkeletonPartsRenderer.NewPartsRendererGameObject(component.transform, index.ToString()); + Undo.RegisterCreatedObjectUndo(smr.gameObject, "New Parts Renderer GameObject."); + componentRenderers.Add(smr); + + // increment renderer sorting order. + if (index == 0) continue; + var prev = componentRenderers[index - 1]; if (prev == null) continue; + + var prevMeshRenderer = prev.GetComponent(); + var currentMeshRenderer = smr.GetComponent(); + if (prevMeshRenderer == null || currentMeshRenderer == null) continue; + + int prevSortingLayer = prevMeshRenderer.sortingLayerID; + int prevSortingOrder = prevMeshRenderer.sortingOrder; + currentMeshRenderer.sortingLayerID = prevSortingLayer; + currentMeshRenderer.sortingOrder = prevSortingOrder + SkeletonRenderSeparator.DefaultSortingOrderIncrement; + } + + } + + /// Detects orphaned parts renderers and offers to delete them. + public void DetectOrphanedPartsRenderers (SkeletonRenderSeparator component) { + var children = component.GetComponentsInChildren(); + + var orphans = new System.Collections.Generic.List(); + foreach (var r in children) { + if (!component.partsRenderers.Contains(r)) + orphans.Add(r); + } + + if (orphans.Count > 0) { + if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Unassigned renderers were found. Do you want to delete them? (These may belong to another Render Separator in the same hierarchy. If you don't have another Render Separator component in the children of this GameObject, it's likely safe to delete. Warning: This operation cannot be undone.)", "Delete", "Cancel")) { + foreach (var o in orphans) { + Undo.DestroyObjectImmediate(o.gameObject); + } + } + } + } + + #region SkeletonRenderer Context Menu Item + [MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator")] + static void AddRenderSeparatorComponent (MenuCommand cmd) { + var skeletonRenderer = cmd.context as SkeletonRenderer; + var newComponent = skeletonRenderer.gameObject.AddComponent(); + + Undo.RegisterCreatedObjectUndo(newComponent, "Add SkeletonRenderSeparator"); + } + + // Validate + [MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator", true)] + static bool ValidateAddRenderSeparatorComponent (MenuCommand cmd) { + var skeletonRenderer = cmd.context as SkeletonRenderer; + var separator = skeletonRenderer.GetComponent(); + bool separatorNotOnObject = separator == null; + return separatorNotOnObject; + } + #endregion + + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs.meta new file mode 100644 index 0000000..7e8e658 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d2a5062cfe5dd4344831cda4723128af +timeCreated: 1458067064 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs new file mode 100644 index 0000000..df61f15 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs @@ -0,0 +1,139 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; + +namespace Spine.Unity.Modules { + [RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))] + public class SkeletonPartsRenderer : MonoBehaviour { + + #region Properties + MeshGenerator meshGenerator; + public MeshGenerator MeshGenerator { + get { + LazyIntialize(); + return meshGenerator; + } + } + + MeshRenderer meshRenderer; + public MeshRenderer MeshRenderer { + get { + LazyIntialize(); + return meshRenderer; + } + } + + MeshFilter meshFilter; + public MeshFilter MeshFilter { + get { + LazyIntialize(); + return meshFilter; + } + } + #endregion + + MeshRendererBuffers buffers; + SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction(); + + + void LazyIntialize () { + if (buffers == null) { + buffers = new MeshRendererBuffers(); + buffers.Initialize(); + + if (meshGenerator != null) return; + meshGenerator = new MeshGenerator(); + meshFilter = GetComponent(); + meshRenderer = GetComponent(); + currentInstructions.Clear(); + } + } + + public void ClearMesh () { + LazyIntialize(); + meshFilter.sharedMesh = null; + } + + public void RenderParts (ExposedList instructions, int startSubmesh, int endSubmesh) { + LazyIntialize(); + + // STEP 1: Create instruction + var smartMesh = buffers.GetNextMesh(); + currentInstructions.SetWithSubset(instructions, startSubmesh, endSubmesh); + bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed); + + // STEP 2: Generate mesh buffers. + var currentInstructionsSubmeshesItems = currentInstructions.submeshInstructions.Items; + meshGenerator.Begin(); + if (currentInstructions.hasActiveClipping) { + for (int i = 0; i < currentInstructions.submeshInstructions.Count; i++) + meshGenerator.AddSubmesh(currentInstructionsSubmeshesItems[i], updateTriangles); + } else { + meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles); + } + + buffers.UpdateSharedMaterials(currentInstructions.submeshInstructions); + + // STEP 3: modify mesh. + var mesh = smartMesh.mesh; + + if (meshGenerator.VertexCount <= 0) { // Clear an empty mesh + updateTriangles = false; + mesh.Clear(); + } else { + meshGenerator.FillVertexData(mesh); + if (updateTriangles) { + meshGenerator.FillTriangles(mesh); + meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray(); + } else if (buffers.MaterialsChangedInLastUpdate()) { + meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray(); + } + } + meshGenerator.FillLateVertexData(mesh); + + meshFilter.sharedMesh = mesh; + smartMesh.instructionUsed.Set(currentInstructions); + } + + public void SetPropertyBlock (MaterialPropertyBlock block) { + LazyIntialize(); + meshRenderer.SetPropertyBlock(block); + } + + public static SkeletonPartsRenderer NewPartsRendererGameObject (Transform parent, string name) { + var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer)); + go.transform.SetParent(parent, false); + var returnComponent = go.AddComponent(); + + return returnComponent; + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs.meta new file mode 100644 index 0000000..9359388 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonPartsRenderer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1c0b968d1e7333b499e347acb644f1c1 +timeCreated: 1458045480 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs new file mode 100644 index 0000000..6a3e3d2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs @@ -0,0 +1,235 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#define SPINE_OPTIONAL_RENDEROVERRIDE + +using UnityEngine; +using System.Collections.Generic; +using Spine.Unity; + +namespace Spine.Unity.Modules { + + [ExecuteInEditMode] + [HelpURL("https://github.com/pharan/spine-unity-docs/blob/master/SkeletonRenderSeparator.md")] + public class SkeletonRenderSeparator : MonoBehaviour { + public const int DefaultSortingOrderIncrement = 5; + + #region Inspector + [SerializeField] + protected SkeletonRenderer skeletonRenderer; + public SkeletonRenderer SkeletonRenderer { + get { return skeletonRenderer; } + set { + #if SPINE_OPTIONAL_RENDEROVERRIDE + if (skeletonRenderer != null) + skeletonRenderer.GenerateMeshOverride -= HandleRender; + #endif + + skeletonRenderer = value; + this.enabled = false; // Disable if nulled. + } + } + + MeshRenderer mainMeshRenderer; + public bool copyPropertyBlock = true; + [Tooltip("Copies MeshRenderer flags into each parts renderer")] + public bool copyMeshRendererFlags = true; + public List partsRenderers = new List(); + + #if UNITY_EDITOR + void Reset () { + if (skeletonRenderer == null) + skeletonRenderer = GetComponent(); + } + #endif + #endregion + + #region Runtime Instantiation + /// Adds a SkeletonRenderSeparator and child SkeletonPartsRenderer GameObjects to a given SkeletonRenderer. + /// The to skeleton renderer. + /// The target SkeletonRenderer or SkeletonAnimation. + /// Sorting layer to be used for the parts renderers. + /// Number of additional SkeletonPartsRenderers on top of the ones determined by counting the number of separator slots. + /// The integer to increment the sorting order per SkeletonPartsRenderer to separate them. + /// The sorting order value of the first SkeletonPartsRenderer. + /// If set to true, a minimum number of SkeletonPartsRenderer GameObjects (determined by separatorSlots.Count + 1) will be added. + public static SkeletonRenderSeparator AddToSkeletonRenderer (SkeletonRenderer skeletonRenderer, int sortingLayerID = 0, int extraPartsRenderers = 0, int sortingOrderIncrement = DefaultSortingOrderIncrement, int baseSortingOrder = 0, bool addMinimumPartsRenderers = true) { + if (skeletonRenderer == null) { + Debug.Log("Tried to add SkeletonRenderSeparator to a null SkeletonRenderer reference."); + return null; + } + + var srs = skeletonRenderer.gameObject.AddComponent(); + srs.skeletonRenderer = skeletonRenderer; + + skeletonRenderer.Initialize(false); + int count = extraPartsRenderers; + if (addMinimumPartsRenderers) + count = extraPartsRenderers + skeletonRenderer.separatorSlots.Count + 1; + + var skeletonRendererTransform = skeletonRenderer.transform; + var componentRenderers = srs.partsRenderers; + + for (int i = 0; i < count; i++) { + var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRendererTransform, i.ToString()); + var mr = spr.MeshRenderer; + mr.sortingLayerID = sortingLayerID; + mr.sortingOrder = baseSortingOrder + (i * sortingOrderIncrement); + componentRenderers.Add(spr); + } + + return srs; + } + + /// Add a child SkeletonPartsRenderer GameObject to this SkeletonRenderSeparator. + public void AddPartsRenderer (int sortingOrderIncrement = DefaultSortingOrderIncrement) { + int sortingLayerID = 0; + int sortingOrder = 0; + if (partsRenderers.Count > 0) { + var previous = partsRenderers[partsRenderers.Count - 1]; + var previousMeshRenderer = previous.MeshRenderer; + sortingLayerID = previousMeshRenderer.sortingLayerID; + sortingOrder = previousMeshRenderer.sortingOrder + sortingOrderIncrement; + } + + var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRenderer.transform, partsRenderers.Count.ToString()); + partsRenderers.Add(spr); + + var mr = spr.MeshRenderer; + mr.sortingLayerID = sortingLayerID; + mr.sortingOrder = sortingOrder; + } + #endregion + + void OnEnable () { + if (skeletonRenderer == null) return; + if (copiedBlock == null) copiedBlock = new MaterialPropertyBlock(); + mainMeshRenderer = skeletonRenderer.GetComponent(); + + #if SPINE_OPTIONAL_RENDEROVERRIDE + skeletonRenderer.GenerateMeshOverride -= HandleRender; + skeletonRenderer.GenerateMeshOverride += HandleRender; + #endif + + if (copyMeshRendererFlags) { + var lightProbeUsage = mainMeshRenderer.lightProbeUsage; + bool receiveShadows = mainMeshRenderer.receiveShadows; + var reflectionProbeUsage = mainMeshRenderer.reflectionProbeUsage; + var shadowCastingMode = mainMeshRenderer.shadowCastingMode; + var motionVectorGenerationMode = mainMeshRenderer.motionVectorGenerationMode; + var probeAnchor = mainMeshRenderer.probeAnchor; + + for (int i = 0; i < partsRenderers.Count; i++) { + var currentRenderer = partsRenderers[i]; + if (currentRenderer == null) continue; // skip null items. + + var mr = currentRenderer.MeshRenderer; + mr.lightProbeUsage = lightProbeUsage; + mr.receiveShadows = receiveShadows; + mr.reflectionProbeUsage = reflectionProbeUsage; + mr.shadowCastingMode = shadowCastingMode; + mr.motionVectorGenerationMode = motionVectorGenerationMode; + mr.probeAnchor = probeAnchor; + } + } + } + + void OnDisable () { + if (skeletonRenderer == null) return; + #if SPINE_OPTIONAL_RENDEROVERRIDE + skeletonRenderer.GenerateMeshOverride -= HandleRender; + #endif + + #if UNITY_EDITOR + skeletonRenderer.LateUpdate(); + #endif + + foreach (var s in partsRenderers) + s.ClearMesh(); + } + + MaterialPropertyBlock copiedBlock; + + void HandleRender (SkeletonRendererInstruction instruction) { + int rendererCount = partsRenderers.Count; + if (rendererCount <= 0) return; + + if (copyPropertyBlock) + mainMeshRenderer.GetPropertyBlock(copiedBlock); + + var settings = new MeshGenerator.Settings { + addNormals = skeletonRenderer.addNormals, + calculateTangents = skeletonRenderer.calculateTangents, + immutableTriangles = false, // parts cannot do immutable triangles. + pmaVertexColors = skeletonRenderer.pmaVertexColors, + //renderMeshes = skeletonRenderer.renderMeshes, + tintBlack = skeletonRenderer.tintBlack, + useClipping = true, + zSpacing = skeletonRenderer.zSpacing + }; + + var submeshInstructions = instruction.submeshInstructions; + var submeshInstructionsItems = submeshInstructions.Items; + int lastSubmeshInstruction = submeshInstructions.Count - 1; + + int rendererIndex = 0; + var currentRenderer = partsRenderers[rendererIndex]; + for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) { + if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) { + // Apply properties + var meshGenerator = currentRenderer.MeshGenerator; + meshGenerator.settings = settings; + + if (copyPropertyBlock) + currentRenderer.SetPropertyBlock(copiedBlock); + + // Render + currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1); + + start = si + 1; + rendererIndex++; + if (rendererIndex < rendererCount) { + currentRenderer = partsRenderers[rendererIndex]; + } else { + // Not enough renderers. Skip the rest of the instructions. + break; + } + } + } + + // Clear extra renderers if they exist. + for (; rendererIndex < rendererCount; rendererIndex++) { + partsRenderers[rendererIndex].ClearMesh(); + } + + } + + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs.meta new file mode 100644 index 0000000..0c344f7 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5c70a5b35f6ff2541aed8e8346b7e4d5 +timeCreated: 1457405791 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.txt b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.txt new file mode 100644 index 0000000..c42f929 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.txt @@ -0,0 +1,6 @@ +SkeletonRenderSeparator +======================= + +Dependencies: +- SkeletonPartsRenderer uses the `ArraysMeshGenerator` class in `Spine.Unity.MeshGeneration` +- It requires `SPINE_OPTIONAL_RENDEROVERRIDE` to be #defined in `SkeletonRenderer.cs`. diff --git a/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.txt.meta b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.txt.meta new file mode 100644 index 0000000..5dd3c99 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f0e413eeb00eabc46bde6dbd7aaaa76c +timeCreated: 1469110129 +licenseType: Free +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules.meta b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules.meta new file mode 100644 index 0000000..4f84211 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d81fbd54cb5cab844900eaa11c48a907 +folderAsset: yes +timeCreated: 1455489575 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs new file mode 100644 index 0000000..9b2effb --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs @@ -0,0 +1,87 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections; + +namespace Spine.Unity.Modules { + public class SkeletonUtilityEyeConstraint : SkeletonUtilityConstraint { + public Transform[] eyes; + public float radius = 0.5f; + public Transform target; + public Vector3 targetPosition; + public float speed = 10; + Vector3[] origins; + Vector3 centerPoint; + + protected override void OnEnable () { + if (!Application.isPlaying) + return; + + base.OnEnable(); + + Bounds centerBounds = new Bounds(eyes[0].localPosition, Vector3.zero); + origins = new Vector3[eyes.Length]; + for (int i = 0; i < eyes.Length; i++) { + origins[i] = eyes[i].localPosition; + centerBounds.Encapsulate(origins[i]); + } + + centerPoint = centerBounds.center; + } + + protected override void OnDisable () { + if (!Application.isPlaying) + return; + + base.OnDisable(); + } + + public override void DoUpdate () { + + if (target != null) + targetPosition = target.position; + + Vector3 goal = targetPosition; + + Vector3 center = transform.TransformPoint(centerPoint); + Vector3 dir = goal - center; + + if (dir.magnitude > 1) + dir.Normalize(); + + for (int i = 0; i < eyes.Length; i++) { + center = transform.TransformPoint(origins[i]); + eyes[i].position = Vector3.MoveTowards(eyes[i].position, center + (dir * radius), speed * Time.deltaTime); + } + + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs.meta new file mode 100644 index 0000000..18e153c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityEyeConstraint.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0d994c65b6daec64f80ae2ae04e9d999 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs new file mode 100644 index 0000000..d8ab190 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs @@ -0,0 +1,129 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; + +namespace Spine.Unity.Modules { + [RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode] + public class SkeletonUtilityGroundConstraint : SkeletonUtilityConstraint { + + [Tooltip("LayerMask for what objects to raycast against")] + public LayerMask groundMask; + [Tooltip("Use 2D")] + public bool use2D = false; + [Tooltip("Uses SphereCast for 3D mode and CircleCast for 2D mode")] + public bool useRadius = false; + [Tooltip("The Radius")] + public float castRadius = 0.1f; + [Tooltip("How high above the target bone to begin casting from")] + public float castDistance = 5f; + [Tooltip("X-Axis adjustment")] + public float castOffset = 0; + [Tooltip("Y-Axis adjustment")] + public float groundOffset = 0; + [Tooltip("How fast the target IK position adjusts to the ground. Use smaller values to prevent snapping")] + public float adjustSpeed = 5; + + Vector3 rayOrigin; + Vector3 rayDir = new Vector3(0, -1, 0); + float hitY; + float lastHitY; + + protected override void OnEnable () { + base.OnEnable(); + lastHitY = transform.position.y; + } + + public override void DoUpdate () { + rayOrigin = transform.position + new Vector3(castOffset, castDistance, 0); + + hitY = float.MinValue; + if (use2D) { + RaycastHit2D hit; + + if (useRadius) + hit = Physics2D.CircleCast(rayOrigin, castRadius, rayDir, castDistance + groundOffset, groundMask); + else + hit = Physics2D.Raycast(rayOrigin, rayDir, castDistance + groundOffset, groundMask); + + if (hit.collider != null) { + hitY = hit.point.y + groundOffset; + if (Application.isPlaying) + hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime); + } else { + if (Application.isPlaying) + hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime); + } + } else { + RaycastHit hit; + bool validHit = false; + + if (useRadius) + validHit = Physics.SphereCast(rayOrigin, castRadius, rayDir, out hit, castDistance + groundOffset, groundMask); + else + validHit = Physics.Raycast(rayOrigin, rayDir, out hit, castDistance + groundOffset, groundMask); + + if (validHit) { + hitY = hit.point.y + groundOffset; + if (Application.isPlaying) + hitY = Mathf.MoveTowards(lastHitY, hitY, adjustSpeed * Time.deltaTime); + + } else { + if (Application.isPlaying) + hitY = Mathf.MoveTowards(lastHitY, transform.position.y, adjustSpeed * Time.deltaTime); + } + } + + Vector3 v = transform.position; + v.y = Mathf.Clamp(v.y, Mathf.Min(lastHitY, hitY), float.MaxValue); + transform.position = v; + + utilBone.bone.X = transform.localPosition.x; + utilBone.bone.Y = transform.localPosition.y; + + lastHitY = hitY; + } + + void OnDrawGizmos () { + Vector3 hitEnd = rayOrigin + (rayDir * Mathf.Min(castDistance, rayOrigin.y - hitY)); + Vector3 clearEnd = rayOrigin + (rayDir * castDistance); + Gizmos.DrawLine(rayOrigin, hitEnd); + + if (useRadius) { + Gizmos.DrawLine(new Vector3(hitEnd.x - castRadius, hitEnd.y - groundOffset, hitEnd.z), new Vector3(hitEnd.x + castRadius, hitEnd.y - groundOffset, hitEnd.z)); + Gizmos.DrawLine(new Vector3(clearEnd.x - castRadius, clearEnd.y, clearEnd.z), new Vector3(clearEnd.x + castRadius, clearEnd.y, clearEnd.z)); + } + + Gizmos.color = Color.red; + Gizmos.DrawLine(hitEnd, clearEnd); + } + } + +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs.meta new file mode 100644 index 0000000..e5474ed --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityGroundConstraint.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3662334b99de5fe4396ab24e30c4fd12 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs new file mode 100644 index 0000000..37ba9bc --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs @@ -0,0 +1,138 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections.Generic; + +namespace Spine.Unity.Modules { + + // SkeletonUtilityKinematicShadow allows hinge chains to inherit a velocity interpreted from changes in parent transform position or from unrelated rigidbodies. + // Note: Uncheck "useRootTransformIfNull + public class SkeletonUtilityKinematicShadow : MonoBehaviour { + #region Inspector + [Tooltip("If checked, the hinge chain can inherit your root transform's velocity or position/rotation changes.")] + public bool detachedShadow = false; + public Transform parent; + public bool hideShadow = true; + public PhysicsSystem physicsSystem = PhysicsSystem.Physics3D; + #endregion + + GameObject shadowRoot; + readonly List shadowTable = new List(); + struct TransformPair { + public Transform dest, src; + } + + public enum PhysicsSystem { + Physics2D, + Physics3D + }; + + void Start () { + // Duplicate this gameObject as the "shadow" with a different parent. + shadowRoot = Instantiate(this.gameObject); + Destroy(shadowRoot.GetComponent()); + + // Prepare shadow gameObject's properties. + var shadowRootTransform = shadowRoot.transform; + shadowRootTransform.position = transform.position; + shadowRootTransform.rotation = transform.rotation; + + Vector3 scaleRef = transform.TransformPoint(Vector3.right); + float scale = Vector3.Distance(transform.position, scaleRef); + shadowRootTransform.localScale = Vector3.one; + + if (!detachedShadow) { + // Do not change to null coalescing operator (??). Unity overloads null checks for UnityEngine.Objects but not the ?? operator. + if (parent == null) + shadowRootTransform.parent = transform.root; + else + shadowRootTransform.parent = parent; + } + + if (hideShadow) + shadowRoot.hideFlags = HideFlags.HideInHierarchy; + + var shadowJoints = shadowRoot.GetComponentsInChildren(); + foreach (Joint j in shadowJoints) + j.connectedAnchor *= scale; + + // Build list of bone pairs (matches shadow transforms with bone transforms) + var bones = GetComponentsInChildren(); + var shadowBones = shadowRoot.GetComponentsInChildren(); + foreach (var b in bones) { + if (b.gameObject == this.gameObject) + continue; + + System.Type checkType = (physicsSystem == PhysicsSystem.Physics2D) ? typeof(Rigidbody2D) : typeof(Rigidbody); + foreach (var sb in shadowBones) { + if (sb.GetComponent(checkType) != null && sb.boneName == b.boneName) { + shadowTable.Add(new TransformPair { + dest = b.transform, + src = sb.transform + }); + break; + } + } + + } + + // Destroy conflicting and unneeded components + DestroyComponents(shadowBones); + + DestroyComponents(GetComponentsInChildren()); + DestroyComponents(GetComponentsInChildren()); + DestroyComponents(GetComponentsInChildren()); + } + + static void DestroyComponents (Component[] components) { + for (int i = 0, n = components.Length; i < n; i++) + Destroy(components[i]); + } + + void FixedUpdate () { + if (physicsSystem == PhysicsSystem.Physics2D) { + var shadowRootRigidbody = shadowRoot.GetComponent(); + shadowRootRigidbody.MovePosition(transform.position); + shadowRootRigidbody.MoveRotation(transform.rotation.eulerAngles.z); + } else { + var shadowRootRigidbody = shadowRoot.GetComponent(); + shadowRootRigidbody.MovePosition(transform.position); + shadowRootRigidbody.MoveRotation(transform.rotation); + } + + for (int i = 0, n = shadowTable.Count; i < n; i++) { + var pair = shadowTable[i]; + pair.dest.localPosition = pair.src.localPosition; + pair.dest.localRotation = pair.src.localRotation; + } + } + } +} diff --git a/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs.meta b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs.meta new file mode 100644 index 0000000..2b071f3 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SkeletonUtility Modules/SkeletonUtilityKinematicShadow.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: cfeac06b8a6aa1645813700e3e4c0863 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes.meta new file mode 100644 index 0000000..c3ef039 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: dfdd78a071ca1a04bb64c6cc41e14aa0 +folderAsset: yes +timeCreated: 1496447038 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor.meta new file mode 100644 index 0000000..80a6cfc --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 1ad4318c20ec5674a9f4d7f786afd681 +folderAsset: yes +timeCreated: 1496449217 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor/SlotBlendModesEditor.cs b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor/SlotBlendModesEditor.cs new file mode 100644 index 0000000..199ca48 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor/SlotBlendModesEditor.cs @@ -0,0 +1,47 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using UnityEditor; +using Spine.Unity.Modules; + +namespace Spine.Unity.Editor { + using Editor = UnityEditor.Editor; + + public class SlotBlendModesEditor : Editor { + + [MenuItem("CONTEXT/SkeletonRenderer/Add Slot Blend Modes Component")] + static void AddSlotBlendModesComponent (MenuCommand command) { + var skeletonRenderer = (SkeletonRenderer)command.context; + skeletonRenderer.gameObject.AddComponent(); + } + } +} + diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor/SlotBlendModesEditor.cs.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor/SlotBlendModesEditor.cs.meta new file mode 100644 index 0000000..21d0e26 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Editor/SlotBlendModesEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cbec7dc66dca80a419477536c23b7a0d +timeCreated: 1496449255 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAMultiply.mat b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAMultiply.mat new file mode 100644 index 0000000..28c90c1 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAMultiply.mat @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: SkeletonPMAMultiply + m_Shader: {fileID: 4800000, guid: 8bdcdc7ee298e594a9c20c61d25c33b6, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_CustomRenderQueue: -1 + stringTagMap: {} + m_SavedProperties: + serializedVersion: 2 + m_TexEnvs: + - first: + name: + second: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - first: + name: _MainTex + second: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - first: + name: + second: 0 + - first: + name: _Cutoff + second: 0.1 + m_Colors: + - first: + name: + second: {r: 0, g: 2.018574, b: 1e-45, a: 0.000007110106} + - first: + name: _Color + second: {r: 1, g: 1, b: 1, a: 1} diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAMultiply.mat.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAMultiply.mat.meta new file mode 100644 index 0000000..c8c8038 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAMultiply.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 53bf0ab317d032d418cf1252d68f51df +timeCreated: 1496447909 +licenseType: Free +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAScreen.mat b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAScreen.mat new file mode 100644 index 0000000..601f987 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAScreen.mat @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: SkeletonPMAScreen + m_Shader: {fileID: 4800000, guid: 4e8caa36c07aacf4ab270da00784e4d9, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_CustomRenderQueue: -1 + stringTagMap: {} + m_SavedProperties: + serializedVersion: 2 + m_TexEnvs: + - first: + name: + second: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - first: + name: _MainTex + second: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - first: + name: + second: 0 + - first: + name: _Cutoff + second: 0.1 + m_Colors: + - first: + name: + second: {r: 0, g: 2.018574, b: 1e-45, a: 0.000007121922} + - first: + name: _Color + second: {r: 1, g: 1, b: 1, a: 1} diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAScreen.mat.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAScreen.mat.meta new file mode 100644 index 0000000..6a1a749 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SkeletonPMAScreen.mat.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 73f0f46d3177c614baf0fa48d646a9be +timeCreated: 1496447909 +licenseType: Free +NativeFormatImporter: + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs new file mode 100644 index 0000000..44177f0 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs @@ -0,0 +1,152 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections.Generic; +using UnityEngine; + +namespace Spine.Unity.Modules { + + [DisallowMultipleComponent] + public class SlotBlendModes : MonoBehaviour { + + #region Internal Material Dictionary + public struct MaterialTexturePair { + public Texture2D texture2D; + public Material material; + } + + static Dictionary materialTable; + internal static Dictionary MaterialTable { + get { + if (materialTable == null) materialTable = new Dictionary(); + return materialTable; + } + } + + internal static Material GetMaterialFor (Material materialSource, Texture2D texture) { + if (materialSource == null || texture == null) return null; + + var mt = SlotBlendModes.MaterialTable; + Material m; + var key = new MaterialTexturePair { material = materialSource, texture2D = texture }; + if (!mt.TryGetValue(key, out m)) { + m = new Material(materialSource); + m.name = "(Clone)" + texture.name + "-" + materialSource.name; + m.mainTexture = texture; + mt[key] = m; + } + + return m; + } + #endregion + + #region Inspector + public Material multiplyMaterialSource; + public Material screenMaterialSource; + + Texture2D texture; + #endregion + + public bool Applied { get; private set; } + + void Start () { + if (!Applied) Apply(); + } + + void OnDestroy () { + if (Applied) Remove(); + } + + public void Apply () { + GetTexture(); + if (texture == null) return; + + var skeletonRenderer = GetComponent(); + if (skeletonRenderer == null) return; + + var slotMaterials = skeletonRenderer.CustomSlotMaterials; + + foreach (var s in skeletonRenderer.Skeleton.Slots) { + switch (s.data.blendMode) { + case BlendMode.Multiply: + if (multiplyMaterialSource != null) slotMaterials[s] = GetMaterialFor(multiplyMaterialSource, texture); + break; + case BlendMode.Screen: + if (screenMaterialSource != null) slotMaterials[s] = GetMaterialFor(screenMaterialSource, texture); + break; + } + } + + Applied = true; + skeletonRenderer.LateUpdate(); + } + + public void Remove () { + GetTexture(); + if (texture == null) return; + + var skeletonRenderer = GetComponent(); + if (skeletonRenderer == null) return; + + var slotMaterials = skeletonRenderer.CustomSlotMaterials; + + foreach (var s in skeletonRenderer.Skeleton.Slots) { + Material m = null; + + switch (s.data.blendMode) { + case BlendMode.Multiply: + if (slotMaterials.TryGetValue(s, out m) && Material.ReferenceEquals(m, GetMaterialFor(multiplyMaterialSource, texture))) + slotMaterials.Remove(s); + break; + case BlendMode.Screen: + if (slotMaterials.TryGetValue(s, out m) && Material.ReferenceEquals(m, GetMaterialFor(screenMaterialSource, texture))) + slotMaterials.Remove(s); + break; + } + } + + Applied = false; + if (skeletonRenderer.valid) skeletonRenderer.LateUpdate(); + } + + public void GetTexture () { + if (texture == null) { + var sr = GetComponent(); if (sr == null) return; + var sda = sr.skeletonDataAsset; if (sda == null) return; + var aa = sda.atlasAssets[0]; if (aa == null) return; + var am = aa.materials[0]; if (am == null) return; + texture = am.mainTexture as Texture2D; + } + } + + + } +} + diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs.meta new file mode 100644 index 0000000..6750f11 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/SlotBlendModes.cs.meta @@ -0,0 +1,16 @@ +fileFormatVersion: 2 +guid: f1f8243645ba2e74aa3564bd956eed89 +timeCreated: 1496794038 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: + - multiplyMaterialSource: {fileID: 2100000, guid: 53bf0ab317d032d418cf1252d68f51df, + type: 2} + - screenMaterialSource: {fileID: 2100000, guid: 73f0f46d3177c614baf0fa48d646a9be, + type: 2} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Multiply.shader b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Multiply.shader new file mode 100644 index 0000000..26126be --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Multiply.shader @@ -0,0 +1,101 @@ +// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + +// Spine/Skeleton PMA Multiply +// - single color multiply tint +// - unlit +// - Premultiplied alpha Multiply blending +// - No depth, no backface culling, no fog. +// - ShadowCaster pass + +Shader "Spine/Skeleton PMA Multiply" { + Properties { + _Color ("Tint Color", Color) = (1,1,1,1) + [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {} + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } + LOD 100 + + Fog { Mode Off } + Cull Off + ZWrite Off + Blend DstColor OneMinusSrcAlpha + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + uniform sampler2D _MainTex; + uniform float4 _Color; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 texColor = tex2D(_MainTex, i.uv); + return (texColor * i.vertexColor); + } + ENDCG + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + ZWrite On + ZTest LEqual + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + struct v2f { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + uniform float4 _MainTex_ST; + + v2f vert (appdata_base v) { + v2f o; + TRANSFER_SHADOW_CASTER(o) + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + return o; + } + + uniform sampler2D _MainTex; + uniform fixed _Cutoff; + + float4 frag (v2f i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Multiply.shader.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Multiply.shader.meta new file mode 100644 index 0000000..f311988 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Multiply.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8bdcdc7ee298e594a9c20c61d25c33b6 +timeCreated: 1496446742 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Screen.shader b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Screen.shader new file mode 100644 index 0000000..d973970 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Screen.shader @@ -0,0 +1,99 @@ +// Spine/Skeleton PMA Multiply +// - single color multiply tint +// - unlit +// - Premultiplied alpha Multiply blending +// - No depth, no backface culling, no fog. +// - ShadowCaster pass + +Shader "Spine/Skeleton PMA Screen" { + Properties { + _Color ("Tint Color", Color) = (1,1,1,1) + [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {} + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } + LOD 100 + + Fog { Mode Off } + Cull Off + ZWrite Off + Blend One OneMinusSrcColor + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + uniform sampler2D _MainTex; + uniform float4 _Color; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 texColor = tex2D(_MainTex, i.uv); + return (texColor * i.vertexColor); + } + ENDCG + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + ZWrite On + ZTest LEqual + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + struct v2f { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + uniform float4 _MainTex_ST; + + v2f vert (appdata_base v) { + v2f o; + TRANSFER_SHADOW_CASTER(o) + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + return o; + } + + uniform sampler2D _MainTex; + uniform fixed _Cutoff; + + float4 frag (v2f i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Screen.shader.meta b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Screen.shader.meta new file mode 100644 index 0000000..173ff6a --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/SlotBlendModes/Spine-Skeleton-PMA-Screen.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 4e8caa36c07aacf4ab270da00784e4d9 +timeCreated: 1496448787 +licenseType: Free +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/TK2D.meta b/Assets/Spine/spine-unity/Modules/TK2D.meta new file mode 100644 index 0000000..bd32abb --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/TK2D.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e70d3026a0242e5418232b2015be29f7 +folderAsset: yes +timeCreated: 1456509301 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs b/Assets/Spine/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs new file mode 100644 index 0000000..842c438 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs @@ -0,0 +1,157 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if SPINE_TK2D +using System; +using UnityEngine; +using Spine; + +// MITCH: handle TPackerCW flip mode (probably not swap uv horizontaly) +namespace Spine.Unity.TK2D { + public class SpriteCollectionAttachmentLoader : AttachmentLoader { + private tk2dSpriteCollectionData sprites; + private float u, v, u2, v2; + private bool regionRotated; + private float regionOriginalWidth, regionOriginalHeight; + private float regionWidth, regionHeight; + private float regionOffsetX, regionOffsetY; + private Material material; + + public SpriteCollectionAttachmentLoader (tk2dSpriteCollectionData sprites) { + if (sprites == null) + throw new ArgumentNullException("sprites cannot be null."); + this.sprites = sprites; + } + + private void ProcessSpriteDefinition (String name) { + // Strip folder names. + int index = name.LastIndexOfAny(new char[] {'/', '\\'}); + if (index != -1) + name = name.Substring(index + 1); + + tk2dSpriteDefinition def = sprites.inst.GetSpriteDefinition(name); + + if (def == null) { + Debug.Log("Sprite not found in atlas: " + name, sprites); + throw new Exception("Sprite not found in atlas: " + name); + } + if (def.complexGeometry) + throw new NotImplementedException("Complex geometry is not supported: " + name); + if (def.flipped == tk2dSpriteDefinition.FlipMode.TPackerCW) + throw new NotImplementedException("Only 2D Toolkit atlases are supported: " + name); + + Vector2 minTexCoords = Vector2.one, maxTexCoords = Vector2.zero; + for (int i = 0; i < def.uvs.Length; ++i) { + Vector2 uv = def.uvs[i]; + minTexCoords = Vector2.Min(minTexCoords, uv); + maxTexCoords = Vector2.Max(maxTexCoords, uv); + } + regionRotated = def.flipped == tk2dSpriteDefinition.FlipMode.Tk2d; + if (regionRotated) { + float temp = minTexCoords.x; + minTexCoords.x = maxTexCoords.x; + maxTexCoords.x = temp; + } + u = minTexCoords.x; + v = maxTexCoords.y; + u2 = maxTexCoords.x; + v2 = minTexCoords.y; + + regionOriginalWidth = (int)(def.untrimmedBoundsData[1].x / def.texelSize.x); + regionOriginalHeight = (int)(def.untrimmedBoundsData[1].y / def.texelSize.y); + + regionWidth = (int)(def.boundsData[1].x / def.texelSize.x); + regionHeight = (int)(def.boundsData[1].y / def.texelSize.y); + + float x0 = def.untrimmedBoundsData[0].x - def.untrimmedBoundsData[1].x / 2; + float x1 = def.boundsData[0].x - def.boundsData[1].x / 2; + regionOffsetX = (int)((x1 - x0) / def.texelSize.x); + + float y0 = def.untrimmedBoundsData[0].y - def.untrimmedBoundsData[1].y / 2; + float y1 = def.boundsData[0].y - def.boundsData[1].y / 2; + regionOffsetY = (int)((y1 - y0) / def.texelSize.y); + + material = def.materialInst; + } + + public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); + + RegionAttachment region = new RegionAttachment(name); + region.Path = path; + region.RendererObject = material; + region.SetUVs(u, v, u2, v2, regionRotated); + region.RegionOriginalWidth = regionOriginalWidth; + region.RegionOriginalHeight = regionOriginalHeight; + region.RegionWidth = regionWidth; + region.RegionHeight = regionHeight; + region.RegionOffsetX = regionOffsetX; + region.RegionOffsetY = regionOffsetY; + return region; + } + + public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) { + ProcessSpriteDefinition(path); + + MeshAttachment mesh = new MeshAttachment(name); + mesh.Path = path; + mesh.RendererObject = material; + mesh.RegionU = u; + mesh.RegionV = v; + mesh.RegionU2 = u2; + mesh.RegionV2 = v2; + mesh.RegionRotate = regionRotated; + mesh.RegionOriginalWidth = regionOriginalWidth; + mesh.RegionOriginalHeight = regionOriginalHeight; + mesh.RegionWidth = regionWidth; + mesh.RegionHeight = regionHeight; + mesh.RegionOffsetX = regionOffsetX; + mesh.RegionOffsetY = regionOffsetY; + return mesh; + } + + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) { + return new BoundingBoxAttachment(name); + } + + public PathAttachment NewPathAttachment (Skin skin, string name) { + return new PathAttachment(name); + } + + public PointAttachment NewPointAttachment (Skin skin, string name) { + return new PointAttachment(name); + } + + public ClippingAttachment NewClippingAttachment (Skin skin, string name) { + return new ClippingAttachment(name); + } + } +} +#endif diff --git a/Assets/Spine/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs.meta b/Assets/Spine/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs.meta new file mode 100644 index 0000000..671d5c7 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/TK2D/SpriteCollectionAttachmentLoader.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 03238e4a73953c045a6cb289162532f3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/Modules/Timeline.meta b/Assets/Spine/spine-unity/Modules/Timeline.meta new file mode 100644 index 0000000..9dd425c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 9192f99585e00e2468a2e2592cfce807 +folderAsset: yes +timeCreated: 1505434717 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation.meta b/Assets/Spine/spine-unity/Modules/Timeline/Documentation.meta new file mode 100644 index 0000000..d6cf5f6 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/Documentation.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a363511f60be37a4199a1a4ad0188b40 +folderAsset: yes +timeCreated: 1511505394 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/README.md b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/README.md new file mode 100644 index 0000000..94d4c61 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/README.md @@ -0,0 +1,80 @@ +This Documentation is for the Spine Timeline features. +If this documentation contains mistakes or doesn't cover some questions, please feel to comment below, open an issue or post in the official [Spine-Unity forums](http://esotericsoftware.com/forum/viewforum.php?f=3). + +# Spine-Unity Timeline Playables + +![](add-menu.png) + +## Spine Animation Track +Controls the skeleton pose a given Spine component with animations. + +**Status:** +- Currently only SkeletonAnimation (via SkeletonAnimationPlayableHandle) +- Mixing has some significant bugs. It should work fine if you don't include mixing, or if all your animations have perfectly matching dopesheet properties. + +**To use:** +1. Add `SkeletonAnimationPlayableHandle` component to your SkeletonAnimation GameObject. +2. With an existing Unity Playable Direction, and in the Unity Timeline window, right-click on an empty space on the left and choose **Spine.Unity.Playables** > **Spine Animation Track**. +3. Drag the SkeletonAnimation GameObject onto the empty reference property of the new Spine Skeleton Flip Track. +4. Right-click on the row in an empty space in the Timeline dopesheet and choose **Add Spine Animation Clip Clip**. +5. Adjust the start and end times of the new clip, name it appropriately at the top of the Inspector. +6. Click on the clip inspector's SkeletonDataAsset field and choose your target skeleton's SkeletonDataAsset. This will enable the animation name dropdown to appear. +7. Choose the appropriate animation name, loop, and mix settings. +8. For easier readability, rename your clip to the animation name or something descriptive. + +**Track Behavior** +- Currently buggy +- + + +## Spine Skeleton Flip Track +![](skeleton-flip-clip-inspector.png) +Controls skeleton flip a given Spine component. + +**Status:** +- Currently only SkeletonAnimation (via SkeletonAnimationPlayableHandle) + +**To use:** +1. Add `SkeletonAnimationPlayableHandle` component to your SkeletonAnimation GameObject. +2. With an existing Unity Playable Director, and in the Unity Timeline window, right-click on an empty space on the left and choose **Spine.Unity.Playables** > **Spine Skeleton Flip Track**. +3. Drag the SkeletonAnimation GameObject onto the empty reference property of the new Spine Skeleton Flip Track. +4. Right-click on the row in an empty space in the Timeline dopesheet and choose **Add Spine Skeleton Flip Clip Clip**. +5. Adjust the start and end times of the new clip, name it appropriately at the top of the Inspector, and choose the desired FlipX and FlipY values. + +**Track Behavior** +- The specified skeleton flip values will be applied for every frame within the duration of each track. +- At the end of the timeline, the track will revert the skeleton flip to the flip values it captures when it starts playing that timeline. + +## Spine AnimationState Track +![](animationstate-clip-inspector.png) +Sets Animations on the target SkeletonAnimation's AnimationState (via SetAnimation). + +**Status:** +- Currently only SkeletonAnimation (directly) + +**To use:** +1. With an existing Unity Playable Director, and in the Unity Timeline window, right-click on an empty space on the left and choose **Spine.Unity.Playables** > **Spine Animation State Track**. +2. Drag the SkeletonAnimation GameObject onto the empty reference property of the new Spine AnimationState Track. +3. Right-click on the row in an empty space in the Timeline dopesheet and choose **Add Spine Animation State Clip Clip**. +4. Adjust the start and end times of the new clip, name it appropriately at the top of the Inspector. +5. Click on the clip inspector's SkeletonDataAsset field and choose your target skeleton's SkeletonDataAsset. This will enable the animation name dropdown to appear. +6. Choose the appropriate animation name, loop, and mix settings. +- For easier readability, rename your clip to the animation name or something descriptive. +- To avoid having to do steps 4-6 repeatedly, use the Duplicate function (`CTRL`/`CMD` + `D`) + +**Track Behavior** +- `AnimationState.SetAnimation` will be called at the beginning of every clip based on the animationName. +- Clip durations don't matter. Animations won't be cleared where there is no active clip at certain slices of time. +- **EMPTY ANIMATION**: If a clip has no name specified, it will call SetEmptyAnimation instead. +- **ERROR HANDLING**: If the animation with the provided animationName is not found, it will do nothing (the previous animation will continue playing normally). +- Animations playing before the timeline starts playing will not be interrupted until the first clip starts playing. +- At the end of the last clip and at the end of the timeline, nothing happens. This means the effect of the last clip's SetAnimation call will persist until you give other commands to that AnimationState. +- If "custom duration" is unchecked, it will do a normal lookup of the AnimationState data's specified transition-pair mix setting, or the default mix. +- Edit mode preview mixing may look different from Play Mode mixing. Please check in actual Play Mode to see the real results. + +## Known Issues +Spine Timeline support is currently experimental and has some known issues and inconveniences. +- The Console logs an incorrect/harmless error `DrivenPropertyManager has failed to register property "m_Script" of object "Spine GameObject (spineboy-pro)" with driver "" because the property doesn't exist.`. This is a known issue on Unity's end. See more here: https://forum.unity.com/threads/default-playables-text-switcher-track-error.502903/ +- These Spine Tracks (like other custom Unity Timeline Playable types) do not have labels on them. Unity currently doesn't have API to specify their labels yet. +- Each track clip currently requires you to specify a reference to SkeletonData so its inspector can show you a convenient list of animation names. This is because track clips are agnostic of its track and target component/track binding, and provides no way of automatically finding it while in the editor. The clips will still function correctly without the SkeletonDataAsset references; you just won't get the dropdown of animation names in the editor. +- Each track clip cannot be automatically named based on the chosen animationName. The Timeline object editors currently doesn't provide access to the clip names to do this automatically. \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/README.md.meta b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/README.md.meta new file mode 100644 index 0000000..d0bfc37 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/README.md.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 970f0962a0a79bf42bdedfc9ed439f56 +timeCreated: 1511505255 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/add-menu.png b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/add-menu.png new file mode 100644 index 0000000..b8e383f Binary files /dev/null and b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/add-menu.png differ diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/add-menu.png.meta b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/add-menu.png.meta new file mode 100644 index 0000000..b5247a7 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/add-menu.png.meta @@ -0,0 +1,112 @@ +fileFormatVersion: 2 +guid: ab296920a52d8204ea35ed4385481be3 +timeCreated: 1511505440 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + 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 + 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} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Windows Store Apps + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/animationstate-clip-inspector.png b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/animationstate-clip-inspector.png new file mode 100644 index 0000000..f5b5702 Binary files /dev/null and b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/animationstate-clip-inspector.png differ diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/animationstate-clip-inspector.png.meta b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/animationstate-clip-inspector.png.meta new file mode 100644 index 0000000..d43a3b7 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/animationstate-clip-inspector.png.meta @@ -0,0 +1,112 @@ +fileFormatVersion: 2 +guid: ca162358b7b05034cae4e25bfb8b98f8 +timeCreated: 1511508127 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + 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 + 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} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Windows Store Apps + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/skeleton-flip-clip-inspector.png b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/skeleton-flip-clip-inspector.png new file mode 100644 index 0000000..d13e746 Binary files /dev/null and b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/skeleton-flip-clip-inspector.png differ diff --git a/Assets/Spine/spine-unity/Modules/Timeline/Documentation/skeleton-flip-clip-inspector.png.meta b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/skeleton-flip-clip-inspector.png.meta new file mode 100644 index 0000000..599bc95 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/Documentation/skeleton-flip-clip-inspector.png.meta @@ -0,0 +1,112 @@ +fileFormatVersion: 2 +guid: 80e301d4353d83640bb50224021a8379 +timeCreated: 1511506555 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + 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 + 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} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Windows Store Apps + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component.meta b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component.meta new file mode 100644 index 0000000..083282b --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: ff79ab0c89530f24d8f60162bd4c1009 +folderAsset: yes +timeCreated: 1507311562 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SkeletonAnimationPlayableHandle.cs b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SkeletonAnimationPlayableHandle.cs new file mode 100644 index 0000000..e1c1fd5 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SkeletonAnimationPlayableHandle.cs @@ -0,0 +1,114 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +//using UnityEngine.Playables; + +using Spine; +using Spine.Unity; +using Spine.Unity.Playables; + +namespace Spine.Unity.Playables { + + [AddComponentMenu("Spine/Playables/SkeletonAnimation Playable Handle (Playables)")] + public class SkeletonAnimationPlayableHandle : SpinePlayableHandleBase { + #region Inspector + public SkeletonAnimation skeletonAnimation; + //public float fadeOutDuration = 0.5f; + + #if UNITY_EDITOR + void OnValidate () { + if (this.skeletonAnimation == null) + skeletonAnimation = GetComponent(); + } + #endif + + #endregion + + //readonly HashSet frameAppliedProperties = new HashSet(); + + public override Skeleton Skeleton { get { return skeletonAnimation.Skeleton; } } + public override SkeletonData SkeletonData { get { return skeletonAnimation.Skeleton.data; } } + + #if UNITY_2017 || UNITY_2018 + void Awake () { + if (skeletonAnimation == null) + skeletonAnimation = GetComponent(); + + //frameAppliedProperties.Clear(); + } + + //Skeleton skeleton; + //int frameTrackCount = 0; + //int frameCurrentInputs = 0; + //bool firstCleared = false; + //int lastApplyFrame = 0; + //public override void ProcessFrame (Playable playable, FrameData info, SpineAnimationMixerBehaviour mixer) { + // if (skeletonAnimation == null) return; + // if (skeleton == null) skeleton = skeletonAnimation.Skeleton; + + // // New frame. + // if (lastApplyFrame != Time.frameCount) { + // if (frameTrackCount > 0) + // frameAppliedProperties.Clear(); + + // frameCurrentInputs = 0; + // frameTrackCount = 0; + // } + // lastApplyFrame = Time.frameCount; + + // int currentInputs = mixer.ApplyPlayableFrame(playable, skeleton, frameAppliedProperties, frameTrackCount); + // frameCurrentInputs += currentInputs; + + // // EXPERIMENTAL: Handle overriding SkeletonAnimation.AnimationState. + // if (frameCurrentInputs > 0) { + // var state = skeletonAnimation.AnimationState; + + // if (!firstCleared) { + // firstCleared = true; + // for (int i = 0; i < 4; i++) { + // if (state.GetCurrent(i) != null) state.SetEmptyAnimation(i, fadeOutDuration); + // } + // } + + // // Update again whenever an animation is playing in the AnimationState. Quite wasteful. + // //if (state.GetCurrent(0) != null) { + // skeleton.UpdateWorldTransform(); + // //} + // } + + // frameTrackCount++; + //} +#endif + } + +} diff --git a/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SkeletonAnimationPlayableHandle.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SkeletonAnimationPlayableHandle.cs.meta new file mode 100644 index 0000000..8075d1e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SkeletonAnimationPlayableHandle.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bd5f4cbc0a51cc24b86e5f70151bc0c3 +timeCreated: 1507311603 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SpinePlayableHandleBase.cs b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SpinePlayableHandleBase.cs new file mode 100644 index 0000000..bea55c3 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SpinePlayableHandleBase.cs @@ -0,0 +1,64 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +//using UnityEngine.Playables; + +namespace Spine.Unity.Playables { + + public delegate void SpineEventDelegate (Spine.Event e); + + /// Base class for Spine Playable Handle components, commonly for integrating with UnityEngine Timeline. + public abstract class SpinePlayableHandleBase : MonoBehaviour { + + /// Gets the SkeletonData of the targeted Spine component. + public abstract SkeletonData SkeletonData { get; } + + public abstract Skeleton Skeleton { get; } + + /// MixerBehaviour ProcessFrame method handler. + /// Returns true if a playable was applied previously + //public abstract void ProcessFrame (Playable playable, FrameData info, SpineAnimationMixerBehaviour mixer); + + /// Subscribe to this to handle user events played by the Unity playable + public event SpineEventDelegate AnimationEvents; + + public virtual void HandleEvents (ExposedList eventBuffer) { + if (eventBuffer == null || AnimationEvents == null) return; + for (int i = 0, n = eventBuffer.Count; i < n; i++) + AnimationEvents.Invoke(eventBuffer.Items[i]); + } + + } +} + diff --git a/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SpinePlayableHandleBase.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SpinePlayableHandleBase.cs.meta new file mode 100644 index 0000000..cb575e7 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/PlayableHandle Component/SpinePlayableHandleBase.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2dfbb56974b17984ca2534bc8185f665 +timeCreated: 1507311422 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState.meta new file mode 100644 index 0000000..a5b1d9d --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 47adfa0aa094be548be25e178c1435d2 +folderAsset: yes +timeCreated: 1510816616 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor.meta new file mode 100644 index 0000000..198f690 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 54bc1978049774f4aa13bfd014a5773a +folderAsset: yes +timeCreated: 1510816616 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor/SpineAnimationStateDrawer.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor/SpineAnimationStateDrawer.cs new file mode 100644 index 0000000..3ea81ae --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor/SpineAnimationStateDrawer.cs @@ -0,0 +1,49 @@ +using UnityEditor; +using UnityEngine; +using Spine; +using Spine.Unity; +using Spine.Unity.Playables; + +//[CustomPropertyDrawer(typeof(SpineAnimationStateBehaviour))] +public class SpineAnimationStateDrawer : PropertyDrawer { + /* + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) { + const int fieldCount = 8; + return fieldCount * EditorGUIUtility.singleLineHeight; + } + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { + SerializedProperty skeletonDataAssetProp = property.FindPropertyRelative("skeletonDataAsset"); + SerializedProperty animationNameProp = property.FindPropertyRelative("animationName"); + SerializedProperty loopProp = property.FindPropertyRelative("loop"); + SerializedProperty eventProp = property.FindPropertyRelative("eventThreshold"); + SerializedProperty attachmentProp = property.FindPropertyRelative("attachmentThreshold"); + SerializedProperty drawOrderProp = property.FindPropertyRelative("drawOrderThreshold"); + + Rect singleFieldRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); + EditorGUI.PropertyField(singleFieldRect, skeletonDataAssetProp); + + float lineHeightWithSpacing = EditorGUIUtility.singleLineHeight + 2f; + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, animationNameProp); + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, loopProp); + + singleFieldRect.y += lineHeightWithSpacing * 0.5f; + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.LabelField(singleFieldRect, "Mixing Settings", EditorStyles.boldLabel); + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, eventProp); + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, attachmentProp); + + singleFieldRect.y += lineHeightWithSpacing; + EditorGUI.PropertyField(singleFieldRect, drawOrderProp); + } + */ +} diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor/SpineAnimationStateDrawer.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor/SpineAnimationStateDrawer.cs.meta new file mode 100644 index 0000000..fddb862 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/Editor/SpineAnimationStateDrawer.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: cba97e3d11d6d4d428fcfe1a7be16db0 +timeCreated: 1510816616 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateBehaviour.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateBehaviour.cs new file mode 100644 index 0000000..86c0ee1 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateBehaviour.cs @@ -0,0 +1,64 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if UNITY_2017 || UNITY_2018 +using System; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; +using Spine; +using Spine.Unity; +using System.Collections.Generic; + +namespace Spine.Unity.Playables { + + using Animation = Spine.Animation; + + [Serializable] + public class SpineAnimationStateBehaviour : PlayableBehaviour { + public AnimationReferenceAsset animationReference; + public bool loop; + + [Header("Mix Properties")] + public bool customDuration = false; + public float mixDuration = 0.1f; + + [Range(0, 1f)] + public float attachmentThreshold = 0.5f; + + [Range(0, 1f)] + public float eventThreshold = 0.5f; + + [Range(0, 1f)] + public float drawOrderThreshold = 0.5f; + } + +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateBehaviour.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateBehaviour.cs.meta new file mode 100644 index 0000000..d468103 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateBehaviour.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 82bd6e7ec1121b5428c447b7bd16b042 +timeCreated: 1510816616 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateClip.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateClip.cs new file mode 100644 index 0000000..0feea80 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateClip.cs @@ -0,0 +1,52 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if UNITY_2017 || UNITY_2018 +using System; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +namespace Spine.Unity.Playables { + [Serializable] + public class SpineAnimationStateClip : PlayableAsset, ITimelineClipAsset { + public SpineAnimationStateBehaviour template = new SpineAnimationStateBehaviour(); + + public ClipCaps clipCaps { get { return ClipCaps.None; } } + + public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { + var playable = ScriptPlayable.Create(graph, template); + playable.GetBehaviour(); //SpineAnimationStateBehaviour clone = playable.GetBehaviour(); + return playable; + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateClip.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateClip.cs.meta new file mode 100644 index 0000000..d9a7bf4 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateClip.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 585e20c924ba86a44926c850aa8e1d84 +timeCreated: 1510816616 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs new file mode 100644 index 0000000..05371ac --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs @@ -0,0 +1,171 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + + #define SPINE_EDITMODEPOSE + +#if UNITY_2017 || UNITY_2018 +using System; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +namespace Spine.Unity.Playables { + public class SpineAnimationStateMixerBehaviour : PlayableBehaviour { + + float[] lastInputWeights; + + // NOTE: This function is called at runtime and edit time. Keep that in mind when setting the values of properties. + public override void ProcessFrame (Playable playable, FrameData info, object playerData) { + var spineComponent = playerData as SkeletonAnimation; + if (spineComponent == null) return; + + var skeleton = spineComponent.Skeleton; + var state = spineComponent.AnimationState; + + if (!Application.isPlaying) { + #if SPINE_EDITMODEPOSE + PreviewEditModePose(playable, spineComponent); + #endif + return; + } + + int inputCount = playable.GetInputCount(); + + // Ensure correct buffer size. + if (this.lastInputWeights == null || this.lastInputWeights.Length < inputCount) { + this.lastInputWeights = new float[inputCount]; + + for (int i = 0; i < inputCount; i++) + this.lastInputWeights[i] = default(float); + } + var lastInputWeights = this.lastInputWeights; + + // Check all clips. If a clip that was weight 0 turned into weight 1, call SetAnimation. + for (int i = 0; i < inputCount; i++) { + float lastInputWeight = lastInputWeights[i]; + float inputWeight = playable.GetInputWeight(i); + bool trackStarted = inputWeight > lastInputWeight; + lastInputWeights[i] = inputWeight; + + if (trackStarted) { + ScriptPlayable inputPlayable = (ScriptPlayable)playable.GetInput(i); + SpineAnimationStateBehaviour clipData = inputPlayable.GetBehaviour(); + + if (clipData.animationReference == null) { + float mixDuration = clipData.customDuration ? clipData.mixDuration : state.Data.DefaultMix; + state.SetEmptyAnimation(0, mixDuration); + } else { + if (clipData.animationReference.Animation != null) { + Spine.TrackEntry trackEntry = state.SetAnimation(0, clipData.animationReference.Animation, clipData.loop); + + //trackEntry.TrackTime = (float)inputPlayable.GetTime(); // More accurate time-start? + trackEntry.EventThreshold = clipData.eventThreshold; + trackEntry.DrawOrderThreshold = clipData.drawOrderThreshold; + trackEntry.AttachmentThreshold = clipData.attachmentThreshold; + + if (clipData.customDuration) + trackEntry.MixDuration = clipData.mixDuration; + } + //else Debug.LogWarningFormat("Animation named '{0}' not found", clipData.animationName); + } + + // Ensure that the first frame ends with an updated mesh. + spineComponent.Update(0); + spineComponent.LateUpdate(); + } + } + } + + #if SPINE_EDITMODEPOSE + public void PreviewEditModePose (Playable playable, SkeletonAnimation spineComponent) { + if (Application.isPlaying) return; + if (spineComponent == null) return; + + int inputCount = playable.GetInputCount(); + int lastOneWeight = -1; + + for (int i = 0; i < inputCount; i++) { + float inputWeight = playable.GetInputWeight(i); + if (inputWeight >= 1) lastOneWeight = i; + } + + if (lastOneWeight != -1) { + ScriptPlayable inputPlayableClip = (ScriptPlayable)playable.GetInput(lastOneWeight); + SpineAnimationStateBehaviour clipData = inputPlayableClip.GetBehaviour(); + + var skeleton = spineComponent.Skeleton; + + bool skeletonDataMismatch = clipData.animationReference != null && spineComponent.SkeletonDataAsset.GetSkeletonData(true) != clipData.animationReference.SkeletonDataAsset.GetSkeletonData(true); + if (skeletonDataMismatch) { + Debug.LogWarningFormat("SpineAnimationStateMixerBehaviour tried to apply an animation for the wrong skeleton. Expected {0}. Was {1}", spineComponent.SkeletonDataAsset, clipData.animationReference.SkeletonDataAsset); + } + + // Getting the from-animation here because it's required to get the mix information from AnimationStateData. + Animation fromAnimation = null; + float fromClipTime = 0; + bool fromClipLoop = false; + if (lastOneWeight != 0 && inputCount > 1) { + var fromClip = (ScriptPlayable)playable.GetInput(lastOneWeight - 1); + var fromClipData = fromClip.GetBehaviour(); + fromAnimation = fromClipData.animationReference.Animation; + fromClipTime = (float)fromClip.GetTime(); + fromClipLoop = fromClipData.loop; + } + + Animation toAnimation = clipData.animationReference.Animation; + float toClipTime = (float)inputPlayableClip.GetTime(); + float mixDuration = clipData.mixDuration; + + if (!clipData.customDuration && fromAnimation != null) { + mixDuration = spineComponent.AnimationState.Data.GetMix(fromAnimation, toAnimation); + } + + // Approximate what AnimationState might do at runtime. + if (fromAnimation != null && mixDuration > 0 && toClipTime < mixDuration) { + skeleton.SetToSetupPose(); + float fauxFromAlpha = (1f - toClipTime/mixDuration); + fauxFromAlpha = fauxFromAlpha > 0.5f ? 1f : fauxFromAlpha * 2f; // fake value, but reduce dip. + fromAnimation.Apply(skeleton, 0, fromClipTime, fromClipLoop, null, fauxFromAlpha, MixPose.Setup, MixDirection.Out); //fromAnimation.PoseSkeleton(skeleton, fromClipTime, fromClipLoop); + toAnimation.Apply(skeleton, 0, toClipTime, clipData.loop, null, toClipTime/mixDuration, MixPose.Current, MixDirection.In); + } else { + skeleton.SetToSetupPose(); + toAnimation.PoseSkeleton(skeleton, toClipTime, clipData.loop); + } + + } + // Do nothing outside of the first clip and the last clip. + + } + #endif + + } + +} +#endif diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs.meta new file mode 100644 index 0000000..ad4f2ea --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: f61d2f4ac1c4ad044bf4ae6e63b0eca8 +timeCreated: 1510816616 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateTrack.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateTrack.cs new file mode 100644 index 0000000..3bce9be --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateTrack.cs @@ -0,0 +1,46 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if UNITY_2017 || UNITY_2018 +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +namespace Spine.Unity.Playables { + [TrackColor(0.9960785f, 0.2509804f, 0.003921569f)] + [TrackClipType(typeof(SpineAnimationStateClip))] + [TrackBindingType(typeof(SkeletonAnimation))] + public class SpineAnimationStateTrack : TrackAsset { + public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) { + return ScriptPlayable.Create (graph, inputCount); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateTrack.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateTrack.cs.meta new file mode 100644 index 0000000..379c2a2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineAnimationState/SpineAnimationStateTrack.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: dfabb056a779ae34b9ebd7de425c868b +timeCreated: 1510816616 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip.meta new file mode 100644 index 0000000..39ab9b4 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3099d730dd2a9e349b3d918960cb42fa +folderAsset: yes +timeCreated: 1500876410 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor.meta new file mode 100644 index 0000000..b27c726 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: fc5fac7eface2ba4fbb6146017e41192 +folderAsset: yes +timeCreated: 1500876410 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor/SpineSkeletonFlipDrawer.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor/SpineSkeletonFlipDrawer.cs new file mode 100644 index 0000000..7af9f30 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor/SpineSkeletonFlipDrawer.cs @@ -0,0 +1,27 @@ +#if UNITY_2017 || UNITY_2018 +using UnityEditor; +using UnityEngine; +using UnityEngine.Playables; + +[CustomPropertyDrawer(typeof(SpineSkeletonFlipBehaviour))] +public class SpineSkeletonFlipDrawer : PropertyDrawer +{ + public override float GetPropertyHeight (SerializedProperty property, GUIContent label) + { + int fieldCount = 1; + return fieldCount * EditorGUIUtility.singleLineHeight; + } + + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) + { + SerializedProperty flipXProp = property.FindPropertyRelative("flipX"); + SerializedProperty flipYProp = property.FindPropertyRelative("flipY"); + + Rect singleFieldRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); + EditorGUI.PropertyField(singleFieldRect, flipXProp); + + singleFieldRect.y += EditorGUIUtility.singleLineHeight; + EditorGUI.PropertyField(singleFieldRect, flipYProp); + } +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor/SpineSkeletonFlipDrawer.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor/SpineSkeletonFlipDrawer.cs.meta new file mode 100644 index 0000000..d7949d2 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/Editor/SpineSkeletonFlipDrawer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b8a0a5d3de45a5e48ae96aa414860d3c +timeCreated: 1500876411 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipBehaviour.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipBehaviour.cs new file mode 100644 index 0000000..b4dcb89 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipBehaviour.cs @@ -0,0 +1,11 @@ +#if UNITY_2017 || UNITY_2018 +using System; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +[Serializable] +public class SpineSkeletonFlipBehaviour : PlayableBehaviour { + public bool flipX, flipY; +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipBehaviour.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipBehaviour.cs.meta new file mode 100644 index 0000000..efa92e5 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipBehaviour.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3740378c8a8da8042991fbbe4b4a2094 +timeCreated: 1500876411 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipClip.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipClip.cs new file mode 100644 index 0000000..43ec42c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipClip.cs @@ -0,0 +1,50 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if UNITY_2017 || UNITY_2018 +using System; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +[Serializable] +public class SpineSkeletonFlipClip : PlayableAsset, ITimelineClipAsset { + public SpineSkeletonFlipBehaviour template = new SpineSkeletonFlipBehaviour(); + + public ClipCaps clipCaps { + get { return ClipCaps.None; } + } + + public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { + var playable = ScriptPlayable.Create(graph, template); + return playable; + } +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipClip.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipClip.cs.meta new file mode 100644 index 0000000..cfc9eee --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipClip.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0a29ac408b701f84c93882fb506759d1 +timeCreated: 1500876411 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipMixerBehaviour.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipMixerBehaviour.cs new file mode 100644 index 0000000..2330aae --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipMixerBehaviour.cs @@ -0,0 +1,102 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if UNITY_2017 || UNITY_2018 + using System; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; + +using Spine.Unity; + +namespace Spine.Unity.Playables { + public class SpineSkeletonFlipMixerBehaviour : PlayableBehaviour { + bool defaultFlipX, defaultFlipY; + + SpinePlayableHandleBase playableHandle; + bool m_FirstFrameHappened; + + public override void ProcessFrame (Playable playable, FrameData info, object playerData) { + playableHandle = playerData as SpinePlayableHandleBase; + + if (playableHandle == null) + return; + + var skeleton = playableHandle.Skeleton; + + if (!m_FirstFrameHappened) { + defaultFlipX = skeleton.flipX; + defaultFlipY = skeleton.flipY; + m_FirstFrameHappened = true; + } + + int inputCount = playable.GetInputCount(); + + float totalWeight = 0f; + float greatestWeight = 0f; + int currentInputs = 0; + + for (int i = 0; i < inputCount; i++) { + float inputWeight = playable.GetInputWeight(i); + ScriptPlayable inputPlayable = (ScriptPlayable)playable.GetInput(i); + SpineSkeletonFlipBehaviour input = inputPlayable.GetBehaviour(); + + totalWeight += inputWeight; + + if (inputWeight > greatestWeight) { + skeleton.flipX = input.flipX; + skeleton.flipY = input.flipY; + greatestWeight = inputWeight; + } + + if (!Mathf.Approximately(inputWeight, 0f)) + currentInputs++; + } + + if (currentInputs != 1 && 1f - totalWeight > greatestWeight) { + skeleton.flipX = defaultFlipX; + skeleton.flipY = defaultFlipY; + } + } + + public override void OnGraphStop (Playable playable) { + m_FirstFrameHappened = false; + + if (playableHandle == null) + return; + + var skeleton = playableHandle.Skeleton; + skeleton.flipX = defaultFlipX; + skeleton.flipY = defaultFlipY; + } + } + +} +#endif diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipMixerBehaviour.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipMixerBehaviour.cs.meta new file mode 100644 index 0000000..737c386 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipMixerBehaviour.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f25dcf1fb34ab8643ac8734e57a9540f +timeCreated: 1500876411 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipTrack.cs b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipTrack.cs new file mode 100644 index 0000000..86d24d9 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipTrack.cs @@ -0,0 +1,68 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#if UNITY_2017 || UNITY_2018 + using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Timeline; +using System.Collections.Generic; + +using Spine.Unity; + +namespace Spine.Unity.Playables { + + [TrackColor(0.855f, 0.8623f, 0.87f)] + [TrackClipType(typeof(SpineSkeletonFlipClip))] + [TrackBindingType(typeof(SpinePlayableHandleBase))] + public class SpineSkeletonFlipTrack : TrackAsset { + public override Playable CreateTrackMixer (PlayableGraph graph, GameObject go, int inputCount) { + return ScriptPlayable.Create(graph, inputCount); + } + + public override void GatherProperties (PlayableDirector director, IPropertyCollector driver) { +#if UNITY_EDITOR + SpinePlayableHandleBase trackBinding = director.GetGenericBinding(this) as SpinePlayableHandleBase; + if (trackBinding == null) + return; + + var serializedObject = new UnityEditor.SerializedObject(trackBinding); + var iterator = serializedObject.GetIterator(); + while (iterator.NextVisible(true)) { + if (iterator.hasVisibleChildren) + continue; + + driver.AddFromName(trackBinding.gameObject, iterator.propertyPath); + } +#endif + base.GatherProperties(director, driver); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipTrack.cs.meta b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipTrack.cs.meta new file mode 100644 index 0000000..b819b34 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/Timeline/SpineSkeletonFlip/SpineSkeletonFlipTrack.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 82ac803eb1878814bba380a18b6b5d9d +timeCreated: 1500876411 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions.meta b/Assets/Spine/spine-unity/Modules/YieldInstructions.meta new file mode 100644 index 0000000..903af3e --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 15ac4befbee15d845ac289de3ab6d3d4 +folderAsset: yes +timeCreated: 1455486167 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs new file mode 100644 index 0000000..13f219c --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs @@ -0,0 +1,86 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections; +using Spine; + +namespace Spine.Unity { + /// + /// Use this as a condition-blocking yield instruction for Unity Coroutines. + /// The routine will pause until the AnimationState.TrackEntry fires its Complete event. + public class WaitForSpineAnimationComplete : IEnumerator { + + bool m_WasFired = false; + + public WaitForSpineAnimationComplete (Spine.TrackEntry trackEntry) { + SafeSubscribe(trackEntry); + } + + void HandleComplete (TrackEntry trackEntry) { + m_WasFired = true; + } + + void SafeSubscribe (Spine.TrackEntry trackEntry) { + if (trackEntry == null) { + // Break immediately if trackEntry is null. + Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately."); + m_WasFired = true; + } else { + trackEntry.Complete += HandleComplete; + } + } + + #region Reuse + /// + /// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure. + /// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete. + public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry) { + SafeSubscribe(trackEntry); + return this; + } + #endregion + + #region IEnumerator + bool IEnumerator.MoveNext () { + if (m_WasFired) { + ((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse + return false; + } + + return true; + } + void IEnumerator.Reset () { m_WasFired = false; } + object IEnumerator.Current { get { return null; } } + #endregion + + } + +} diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs.meta b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs.meta new file mode 100644 index 0000000..0aabc3b --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineAnimationComplete.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a807dd9fb79db3545b6c2859a2bbfc0b +timeCreated: 1449704018 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs new file mode 100644 index 0000000..38a9e65 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs @@ -0,0 +1,160 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections; +using Spine; + +namespace Spine.Unity { + /// + /// Use this as a condition-blocking yield instruction for Unity Coroutines. + /// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference. + public class WaitForSpineEvent : IEnumerator { + + Spine.EventData m_TargetEvent; + string m_EventName; + Spine.AnimationState m_AnimationState; + + bool m_WasFired = false; + bool m_unsubscribeAfterFiring = false; + + #region Constructors + void Subscribe (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribe) { + if (state == null) { + Debug.LogWarning("AnimationState argument was null. Coroutine will continue immediately."); + m_WasFired = true; + return; + } else if (eventDataReference == null) { + Debug.LogWarning("eventDataReference argument was null. Coroutine will continue immediately."); + m_WasFired = true; + return; + } + + m_AnimationState = state; + m_TargetEvent = eventDataReference; + state.Event += HandleAnimationStateEvent; + + m_unsubscribeAfterFiring = unsubscribe; + + } + + void SubscribeByName (Spine.AnimationState state, string eventName, bool unsubscribe) { + if (state == null) { + Debug.LogWarning("AnimationState argument was null. Coroutine will continue immediately."); + m_WasFired = true; + return; + } else if (string.IsNullOrEmpty(eventName)) { + Debug.LogWarning("eventName argument was null. Coroutine will continue immediately."); + m_WasFired = true; + return; + } + + m_AnimationState = state; + m_EventName = eventName; + state.Event += HandleAnimationStateEventByName; + + m_unsubscribeAfterFiring = unsubscribe; + } + + public WaitForSpineEvent (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { + Subscribe(state, eventDataReference, unsubscribeAfterFiring); + } + + public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { + // If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine. + Subscribe(skeletonAnimation.state, eventDataReference, unsubscribeAfterFiring); + } + + public WaitForSpineEvent (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) { + SubscribeByName(state, eventName, unsubscribeAfterFiring); + } + + public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, string eventName, bool unsubscribeAfterFiring = true) { + // If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine. + SubscribeByName(skeletonAnimation.state, eventName, unsubscribeAfterFiring); + } + #endregion + + #region Event Handlers + void HandleAnimationStateEventByName (Spine.TrackEntry trackEntry, Spine.Event e) { + m_WasFired |= (e.Data.Name == m_EventName); // Check event name string match. + if (m_WasFired && m_unsubscribeAfterFiring) + m_AnimationState.Event -= HandleAnimationStateEventByName; // Unsubscribe after correct event fires. + } + + void HandleAnimationStateEvent (Spine.TrackEntry trackEntry, Spine.Event e) { + m_WasFired |= (e.Data == m_TargetEvent); // Check event data reference match. + if (m_WasFired && m_unsubscribeAfterFiring) + m_AnimationState.Event -= HandleAnimationStateEvent; // Usubscribe after correct event fires. + } + #endregion + + #region Reuse + /// + /// By default, WaitForSpineEvent will unsubscribe from the event immediately after it fires a correct matching event. + /// If you want to reuse this WaitForSpineEvent instance on the same event, you can set this to false. + public bool WillUnsubscribeAfterFiring { get { return m_unsubscribeAfterFiring; } set { m_unsubscribeAfterFiring = value; } } + + public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) { + ((IEnumerator)this).Reset(); + Clear(state); + Subscribe(state, eventDataReference, unsubscribeAfterFiring); + + return this; + } + + public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) { + ((IEnumerator)this).Reset(); + Clear(state); + SubscribeByName(state, eventName, unsubscribeAfterFiring); + + return this; + } + + void Clear (Spine.AnimationState state) { + state.Event -= HandleAnimationStateEvent; + state.Event -= HandleAnimationStateEventByName; + } + #endregion + + #region IEnumerator + bool IEnumerator.MoveNext () { + if (m_WasFired) { + ((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse + return false; + } + + return true; + } + void IEnumerator.Reset () { m_WasFired = false; } + object IEnumerator.Current { get { return null; } } + #endregion + } +} diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs.meta b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs.meta new file mode 100644 index 0000000..72bbef7 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineEvent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fc166d883db083e469872998172f2d38 +timeCreated: 1449701857 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineTrackEntryEnd.cs b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineTrackEntryEnd.cs new file mode 100644 index 0000000..7c5ef94 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineTrackEntryEnd.cs @@ -0,0 +1,86 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; +using System.Collections; +using Spine; + +namespace Spine.Unity { + /// + /// Use this as a condition-blocking yield instruction for Unity Coroutines. + /// The routine will pause until the AnimationState.TrackEntry fires its End event. + public class WaitForSpineTrackEntryEnd : IEnumerator { + + bool m_WasFired = false; + + public WaitForSpineTrackEntryEnd (Spine.TrackEntry trackEntry) { + SafeSubscribe(trackEntry); + } + + void HandleEnd (TrackEntry trackEntry) { + m_WasFired = true; + } + + void SafeSubscribe (Spine.TrackEntry trackEntry) { + if (trackEntry == null) { + // Break immediately if trackEntry is null. + Debug.LogWarning("TrackEntry was null. Coroutine will continue immediately."); + m_WasFired = true; + } else { + trackEntry.End += HandleEnd; + } + } + + #region Reuse + /// + /// One optimization high-frequency YieldInstruction returns is to cache instances to minimize GC pressure. + /// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationEnd. + public WaitForSpineTrackEntryEnd NowWaitFor (Spine.TrackEntry trackEntry) { + SafeSubscribe(trackEntry); + return this; + } + #endregion + + #region IEnumerator + bool IEnumerator.MoveNext () { + if (m_WasFired) { + ((IEnumerator)this).Reset(); // auto-reset for YieldInstruction reuse + return false; + } + + return true; + } + void IEnumerator.Reset () { m_WasFired = false; } + object IEnumerator.Current { get { return null; } } + #endregion + + } + +} diff --git a/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineTrackEntryEnd.cs.meta b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineTrackEntryEnd.cs.meta new file mode 100644 index 0000000..afd6031 --- /dev/null +++ b/Assets/Spine/spine-unity/Modules/YieldInstructions/WaitForSpineTrackEntryEnd.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8036c6c2897d2764db92f632d2aef568 +timeCreated: 1480672707 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders.meta b/Assets/Spine/spine-unity/Shaders.meta new file mode 100644 index 0000000..96c6a80 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: ef8189a68a74bec4eba582e65fb98dbd +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader new file mode 100644 index 0000000..7eeca1e --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader @@ -0,0 +1,106 @@ +// Spine/Skeleton Tint Black +// - Two color tint +// - UV2 and UV3 as Black Tint color. +// - Final black tint is (UV black data and _Black/"Black Point") +// - unlit +// - Premultiplied alpha blending +// - No depth, no backface culling, no fog. + +Shader "Spine/Skeleton Tint Black" { + Properties { + _Color ("Tint Color", Color) = (1,1,1,1) + _Black ("Black Point", Color) = (0,0,0,0) + [NoScaleOffset] _MainTex ("MainTex", 2D) = "black" {} + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } + LOD 100 + + Fog { Mode Off } + Cull Off + ZWrite Off + Blend One OneMinusSrcAlpha + Lighting Off + + Pass { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + sampler2D _MainTex; + float4 _Color; + float4 _Black; + + struct VertexInput { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float4 vertexColor : COLOR; + }; + + struct VertexOutput { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float4 vertexColor : COLOR; + }; + + VertexOutput vert (VertexInput v) { + VertexOutput o; + o.pos = UnityObjectToClipPos(v.vertex); // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + o.uv = v.uv; + o.vertexColor = v.vertexColor * float4(_Color.rgb * _Color.a, _Color.a); // Combine a PMA version of _Color with vertexColor. + o.uv1 = v.uv1; + o.uv2 = v.uv2; + return o; + } + + float4 frag (VertexOutput i) : COLOR { + float4 texColor = tex2D(_MainTex, i.uv); + return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * (_Black.rgb + float3(i.uv1.r, i.uv1.g, i.uv2.r)) * texColor.a*_Color.a*i.vertexColor.a), 0); + } + ENDCG + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + ZWrite On + ZTest LEqual + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + sampler2D _MainTex; + fixed _Cutoff; + + struct v2f { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + v2f vert (appdata_base v) { + v2f o; + TRANSFER_SHADOW_CASTER(o) + o.uv = v.texcoord; + return o; + } + + float4 frag (v2f i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } +} diff --git a/Assets/Spine/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader.meta b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader.meta new file mode 100644 index 0000000..2801fe0 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: deee23ab4aa38564ead2ac05e112c169 +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders/Spine-Skeleton.shader b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton.shader new file mode 100644 index 0000000..7b5c00d --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton.shader @@ -0,0 +1,80 @@ +Shader "Spine/Skeleton" { + Properties { + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + [NoScaleOffset] _MainTex ("Main Texture", 2D) = "black" {} + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane"} + + Fog { Mode Off } + Cull Off + ZWrite Off + Blend One OneMinusSrcAlpha + Lighting Off + + Pass { + Fog { Mode Off } + ColorMaterial AmbientAndDiffuse + SetTexture [_MainTex] { + Combine texture * primary + } + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + ZWrite On + ZTest LEqual + + Fog { Mode Off } + Cull Off + Lighting Off + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + sampler2D _MainTex; + fixed _Cutoff; + + struct v2f { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + v2f vert (appdata_base v) { + v2f o; + TRANSFER_SHADOW_CASTER(o) + o.uv = v.texcoord; + return o; + } + + float4 frag (v2f i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } + + Cull Off + ZWrite Off + Blend One OneMinusSrcAlpha + Lighting Off + + Pass { + ColorMaterial AmbientAndDiffuse + SetTexture [_MainTex] { + Combine texture * primary DOUBLE, texture * primary + } + } + } +} diff --git a/Assets/Spine/spine-unity/Shaders/Spine-Skeleton.shader.meta b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton.shader.meta new file mode 100644 index 0000000..06c4b0c --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Spine-Skeleton.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1e8a610c9e01c3648bac42585e5fc676 +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders/Spine-SkeletonLit.shader b/Assets/Spine/spine-unity/Shaders/Spine-SkeletonLit.shader new file mode 100644 index 0000000..8b193c2 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Spine-SkeletonLit.shader @@ -0,0 +1,189 @@ +// - Vertex Lit + ShadowCaster +// - Premultiplied Alpha Blending (One OneMinusSrcAlpha) +// - Double-sided, no depth + +Shader "Spine/Skeleton Lit" { + Properties { + _Cutoff ("Shadow alpha cutoff", Range(0,1)) = 0.1 + [NoScaleOffset] _MainTex ("Main Texture", 2D) = "black" {} + } + + SubShader { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } + LOD 100 + + Cull Off + ZWrite Off + Blend One OneMinusSrcAlpha + +// Pass { +// Tags { "LightMode"="Vertex" } +// ColorMaterial AmbientAndDiffuse +// Lighting On +// SetTexture [_MainTex] { +// Combine texture * primary DOUBLE, texture * primary +// } +// } + + Pass { + Tags { + "LIGHTMODE"="Vertex" + "QUEUE"="Transparent" + "IGNOREPROJECTOR"="true" + "RenderType"="Transparent" + } + + ZWrite Off + Cull Off + Blend One OneMinusSrcAlpha + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma target 2.0 + #include "UnityCG.cginc" + + // ES2.0/WebGL/3DS can not do loops with non-constant-expression iteration counts :( + #if defined(SHADER_API_GLES) + #define LIGHT_LOOP_LIMIT 8 + #elif defined(SHADER_API_N3DS) + #define LIGHT_LOOP_LIMIT 4 + #else + #define LIGHT_LOOP_LIMIT unity_VertexLightParams.x + #endif + + #pragma multi_compile __ POINT SPOT + + half3 computeLighting (int idx, half3 dirToLight, half3 eyeNormal, half3 viewDir, half4 diffuseColor, half atten) { + half NdotL = max(dot(eyeNormal, dirToLight), 0.0); + // diffuse + half3 color = NdotL * diffuseColor.rgb * unity_LightColor[idx].rgb; + return color * atten; + } + + half3 computeOneLight (int idx, float3 eyePosition, half3 eyeNormal, half3 viewDir, half4 diffuseColor) { + float3 dirToLight = unity_LightPosition[idx].xyz; + half att = 1.0; + + #if defined(POINT) || defined(SPOT) + dirToLight -= eyePosition * unity_LightPosition[idx].w; + + // distance attenuation + float distSqr = dot(dirToLight, dirToLight); + att /= (1.0 + unity_LightAtten[idx].z * distSqr); + if (unity_LightPosition[idx].w != 0 && distSqr > unity_LightAtten[idx].w) att = 0.0; // set to 0 if outside of range + distSqr = max(distSqr, 0.000001); // don't produce NaNs if some vertex position overlaps with the light + dirToLight *= rsqrt(distSqr); + #if defined(SPOT) + + // spot angle attenuation + half rho = max(dot(dirToLight, unity_SpotDirection[idx].xyz), 0.0); + half spotAtt = (rho - unity_LightAtten[idx].x) * unity_LightAtten[idx].y; + att *= saturate(spotAtt); + #endif + #endif + + att *= 0.5; // passed in light colors are 2x brighter than what used to be in FFP + return min (computeLighting (idx, dirToLight, eyeNormal, viewDir, diffuseColor, att), 1.0); + } + + int4 unity_VertexLightParams; // x: light count, y: zero, z: one (y/z needed by d3d9 vs loop instruction) + + struct appdata { + float3 pos : POSITION; + float3 normal : NORMAL; + half4 color : COLOR; + float3 uv0 : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct VertexOutput { + fixed4 color : COLOR0; + float2 uv0 : TEXCOORD0; + float4 pos : SV_POSITION; + UNITY_VERTEX_OUTPUT_STEREO + }; + + VertexOutput vert (appdata v) { + VertexOutput o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + + half4 color = v.color; + float3 eyePos = UnityObjectToViewPos(float4(v.pos, 1)).xyz; //mul(UNITY_MATRIX_MV, float4(v.pos,1)).xyz; + half3 fixedNormal = half3(0,0,-1); + half3 eyeNormal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, fixedNormal)); + //half3 eyeNormal = half3(0,0,1); + half3 viewDir = 0.0; + + // Lights + half3 lcolor = half4(0,0,0,1).rgb + color.rgb * glstate_lightmodel_ambient.rgb; + for (int il = 0; il < LIGHT_LOOP_LIMIT; ++il) { + lcolor += computeOneLight(il, eyePos, eyeNormal, viewDir, color); + } + + color.rgb = lcolor.rgb; + o.color = saturate(color); + o.uv0 = v.uv0; + o.pos = UnityObjectToClipPos(v.pos); + return o; + } + + sampler2D _MainTex; + + fixed4 frag (VertexOutput i) : SV_Target { + fixed4 tex = tex2D(_MainTex, i.uv0); + + fixed4 col; + col.rgb = tex * i.color; + col *= 2; + col.a = tex.a * i.color.a; + return col; + } + ENDCG + + } + + Pass { + Name "Caster" + Tags { "LightMode"="ShadowCaster" } + Offset 1, 1 + + Fog { Mode Off } + ZWrite On + ZTest LEqual + Cull Off + Lighting Off + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma multi_compile_shadowcaster + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + struct v2f { + V2F_SHADOW_CASTER; + float2 uv : TEXCOORD1; + }; + + uniform float4 _MainTex_ST; + + v2f vert (appdata_base v) { + v2f o; + TRANSFER_SHADOW_CASTER(o) + o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); + return o; + } + + uniform sampler2D _MainTex; + uniform fixed _Cutoff; + + float4 frag (v2f i) : COLOR { + fixed4 texcol = tex2D(_MainTex, i.uv); + clip(texcol.a - _Cutoff); + SHADOW_CASTER_FRAGMENT(i) + } + ENDCG + } + } +} \ No newline at end of file diff --git a/Assets/Spine/spine-unity/Shaders/Spine-SkeletonLit.shader.meta b/Assets/Spine/spine-unity/Shaders/Spine-SkeletonLit.shader.meta new file mode 100644 index 0000000..1d0a386 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Spine-SkeletonLit.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bd83c75f51f5e23498ae22ffcdfe92c3 +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders/Utility.meta b/Assets/Spine/spine-unity/Shaders/Utility.meta new file mode 100644 index 0000000..188c117 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: bc59776133d26dc469c8ba66bdc647e4 +folderAsset: yes +timeCreated: 1492387122 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders/Utility/Hidden-Spine-Bones.shader b/Assets/Spine/spine-unity/Shaders/Utility/Hidden-Spine-Bones.shader new file mode 100644 index 0000000..ee2045d --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility/Hidden-Spine-Bones.shader @@ -0,0 +1,66 @@ +Shader "Hidden/Spine/Bones" { +Properties { + _Color ("Color", Color) = (0.5,0.5,0.5,0.5) + _MainTex ("Particle Texture", 2D) = "white" {} +} + +Category { + Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } + Blend SrcAlpha OneMinusSrcAlpha + AlphaTest Greater .01 + ColorMask RGB + + Lighting Off Cull Off ZTest Always ZWrite Off Fog { Mode Off } + + SubShader { + Pass { + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag +// #pragma multi_compile_particles + + #include "UnityCG.cginc" + + sampler2D _MainTex; + fixed4 _Color; + + struct appdata_t { + float4 vertex : POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; + }; + + struct v2f { + float4 vertex : SV_POSITION; + fixed4 color : COLOR; + float2 texcoord : TEXCOORD0; +// #ifdef SOFTPARTICLES_ON +// float4 projPos : TEXCOORD1; +// #endif + }; + + float4 _MainTex_ST; + + v2f vert (appdata_t v) { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); +// #ifdef SOFTPARTICLES_ON +// o.projPos = ComputeScreenPos (o.vertex); +// COMPUTE_EYEDEPTH(o.projPos.z); +// #endif + o.color = v.color; + o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex); + return o; + } + + sampler2D_float _CameraDepthTexture; + + fixed4 frag (v2f i) : SV_Target { + return i.color * _Color * tex2D(_MainTex, i.texcoord); + } + ENDCG + } + } +} +} diff --git a/Assets/Spine/spine-unity/Shaders/Utility/Hidden-Spine-Bones.shader.meta b/Assets/Spine/spine-unity/Shaders/Utility/Hidden-Spine-Bones.shader.meta new file mode 100644 index 0000000..17117f3 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility/Hidden-Spine-Bones.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 66988de88a15abd4e8846c6805485f57 +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.mat b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.mat new file mode 100644 index 0000000..c7bc5ad --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.mat @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: HiddenPass + m_Shader: {fileID: 4800000, guid: 913475501bf19374c84390868a9d6d3d, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 5 + m_CustomRenderQueue: -1 + stringTagMap: {} + m_SavedProperties: + serializedVersion: 2 + m_TexEnvs: + - first: + name: _MainTex + second: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - first: + name: _Cutoff + second: 0.1 + - first: + name: _InvFade + second: 1 + m_Colors: + - first: + name: _Color + second: {r: 1, g: 1, b: 1, a: 1} + - first: + name: _TintColor + second: {r: 0.5, g: 0.5, b: 0.5, a: 0} diff --git a/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.mat.meta b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.mat.meta new file mode 100644 index 0000000..f9a2a31 --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.mat.meta @@ -0,0 +1,6 @@ +fileFormatVersion: 2 +guid: 43227e5adadc6f24bb4bf74b92a56fb4 +NativeFormatImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.shader b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.shader new file mode 100644 index 0000000..c06650a --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.shader @@ -0,0 +1,12 @@ +Shader "Spine/Special/HiddenPass" { + SubShader + { + Tags {"Queue" = "Geometry-1" } + Lighting Off + Pass + { + ZWrite Off + ColorMask 0 + } + } +} diff --git a/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.shader.meta b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.shader.meta new file mode 100644 index 0000000..01ca4ed --- /dev/null +++ b/Assets/Spine/spine-unity/Shaders/Utility/HiddenPass.shader.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 913475501bf19374c84390868a9d6d3d +ShaderImporter: + defaultTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/SkeletonExtensions.cs b/Assets/Spine/spine-unity/SkeletonExtensions.cs new file mode 100644 index 0000000..8412098 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonExtensions.cs @@ -0,0 +1,617 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; + +namespace Spine.Unity { + public static class SkeletonExtensions { + + #region Colors + const float ByteToFloat = 1f / 255f; + public static Color GetColor (this Skeleton s) { return new Color(s.r, s.g, s.b, s.a); } + public static Color GetColor (this RegionAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static Color GetColor (this MeshAttachment a) { return new Color(a.r, a.g, a.b, a.a); } + public static Color GetColor (this Slot s) { return new Color(s.r, s.g, s.b, s.a); } + public static Color GetColorTintBlack (this Slot s) { return new Color(s.r2, s.g2, s.b2, 1f); } + + public static void SetColor (this Skeleton skeleton, Color color) { + skeleton.A = color.a; + skeleton.R = color.r; + skeleton.G = color.g; + skeleton.B = color.b; + } + + public static void SetColor (this Skeleton skeleton, Color32 color) { + skeleton.A = color.a * ByteToFloat; + skeleton.R = color.r * ByteToFloat; + skeleton.G = color.g * ByteToFloat; + skeleton.B = color.b * ByteToFloat; + } + + public static void SetColor (this Slot slot, Color color) { + slot.A = color.a; + slot.R = color.r; + slot.G = color.g; + slot.B = color.b; + } + + public static void SetColor (this Slot slot, Color32 color) { + slot.A = color.a * ByteToFloat; + slot.R = color.r * ByteToFloat; + slot.G = color.g * ByteToFloat; + slot.B = color.b * ByteToFloat; + } + + public static void SetColor (this RegionAttachment attachment, Color color) { + attachment.A = color.a; + attachment.R = color.r; + attachment.G = color.g; + attachment.B = color.b; + } + + public static void SetColor (this RegionAttachment attachment, Color32 color) { + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; + } + + public static void SetColor (this MeshAttachment attachment, Color color) { + attachment.A = color.a; + attachment.R = color.r; + attachment.G = color.g; + attachment.B = color.b; + } + + public static void SetColor (this MeshAttachment attachment, Color32 color) { + attachment.A = color.a * ByteToFloat; + attachment.R = color.r * ByteToFloat; + attachment.G = color.g * ByteToFloat; + attachment.B = color.b * ByteToFloat; + } + #endregion + + #region Bone + /// Sets the bone's (local) X and Y according to a Vector2 + public static void SetPosition (this Bone bone, Vector2 position) { + bone.X = position.x; + bone.Y = position.y; + } + + /// Sets the bone's (local) X and Y according to a Vector3. The z component is ignored. + public static void SetPosition (this Bone bone, Vector3 position) { + bone.X = position.x; + bone.Y = position.y; + } + + /// Gets the bone's local X and Y as a Vector2. + public static Vector2 GetLocalPosition (this Bone bone) { + return new Vector2(bone.x, bone.y); + } + + /// Gets the position of the bone in Skeleton-space. + public static Vector2 GetSkeletonSpacePosition (this Bone bone) { + return new Vector2(bone.worldX, bone.worldY); + } + + /// Gets a local offset from the bone and converts it into Skeleton-space. + public static Vector2 GetSkeletonSpacePosition (this Bone bone, Vector2 boneLocal) { + Vector2 o; + bone.LocalToWorld(boneLocal.x, boneLocal.y, out o.x, out o.y); + return o; + } + + /// Gets the bone's Unity World position using its Spine GameObject Transform. UpdateWorldTransform needs to have been called for this to return the correct, updated value. + public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform) { + return spineGameObjectTransform.TransformPoint(new Vector3(bone.worldX, bone.worldY)); + } + + public static Vector3 GetWorldPosition (this Bone bone, UnityEngine.Transform spineGameObjectTransform, float positionScale) { + return spineGameObjectTransform.TransformPoint(new Vector3(bone.worldX * positionScale, bone.worldY * positionScale)); + } + + /// Gets a skeleton space UnityEngine.Quaternion representation of bone.WorldRotationX. + public static Quaternion GetQuaternion (this Bone bone) { + var halfRotation = Mathf.Atan2(bone.c, bone.a) * 0.5f; + return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation)); + } + + /// Gets a bone-local space UnityEngine.Quaternion representation of bone.rotation. + public static Quaternion GetLocalQuaternion (this Bone bone) { + var halfRotation = bone.rotation * Mathf.Deg2Rad * 0.5f; + return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation)); + } + + /// Gets the internal bone matrix as a Unity bonespace-to-skeletonspace transformation matrix. + public static Matrix4x4 GetMatrix4x4 (this Bone bone) { + return new Matrix4x4 { + m00 = bone.a, m01 = bone.b, m03 = bone.worldX, + m10 = bone.c, m11 = bone.d, m13 = bone.worldY, + m33 = 1 + }; + } + + /// Calculates a 2x2 Transformation Matrix that can convert a skeleton-space position to a bone-local position. + public static void GetWorldToLocalMatrix (this Bone bone, out float ia, out float ib, out float ic, out float id) { + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + float invDet = 1 / (a * d - b * c); + ia = invDet * d; + ib = invDet * -b; + ic = invDet * -c; + id = invDet * a; + } + + /// UnityEngine.Vector2 override of Bone.WorldToLocal. This converts a skeleton-space position into a bone local position. + public static Vector2 WorldToLocal (this Bone bone, Vector2 worldPosition) { + Vector2 o; + bone.WorldToLocal(worldPosition.x, worldPosition.y, out o.x, out o.y); + return o; + } + + /// Sets the skeleton-space position of a bone. + /// The local position in its parent bone space, or in skeleton space if it is the root bone. + public static Vector2 SetPositionSkeletonSpace (this Bone bone, Vector2 skeletonSpacePosition) { + if (bone.parent == null) { // root bone + bone.SetPosition(skeletonSpacePosition); + return skeletonSpacePosition; + } else { + var parent = bone.parent; + Vector2 parentLocal = parent.WorldToLocal(skeletonSpacePosition); + bone.SetPosition(parentLocal); + return parentLocal; + } + } + #endregion + + #region Attachments + public static Material GetMaterial (this Attachment a) { + object rendererObject = null; + var renderableAttachment = a as IHasRendererObject; + if (renderableAttachment != null) { + rendererObject = renderableAttachment.RendererObject; + } + + if (rendererObject == null) + return null; + + #if SPINE_TK2D + return (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; + #else + return (Material)((AtlasRegion)rendererObject).page.rendererObject; + #endif + } + + /// Fills a Vector2 buffer with local vertices. + /// The VertexAttachment + /// Slot where the attachment belongs. + /// Correctly-sized buffer. Use attachment's .WorldVerticesLength to get the correct size. If null, a new Vector2[] of the correct size will be allocated. + public static Vector2[] GetLocalVertices (this VertexAttachment va, Slot slot, Vector2[] buffer) { + int floatsCount = va.worldVerticesLength; + int bufferTargetSize = floatsCount >> 1; + buffer = buffer ?? new Vector2[bufferTargetSize]; + if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", va.Name, floatsCount), "buffer"); + + if (va.bones == null) { + var localVerts = va.vertices; + for (int i = 0; i < bufferTargetSize; i++) { + int j = i * 2; + buffer[i] = new Vector2(localVerts[j], localVerts[j+1]); + } + } else { + var floats = new float[floatsCount]; + va.ComputeWorldVertices(slot, floats); + + Bone sb = slot.bone; + float ia, ib, ic, id, bwx = sb.worldX, bwy = sb.worldY; + sb.GetWorldToLocalMatrix(out ia, out ib, out ic, out id); + + for (int i = 0; i < bufferTargetSize; i++) { + int j = i * 2; + float x = floats[j] - bwx, y = floats[j+1] - bwy; + buffer[i] = new Vector2(x * ia + y * ib, x * ic + y * id); + } + } + + return buffer; + } + + /// Calculates world vertices and fills a Vector2 buffer. + /// The VertexAttachment + /// Slot where the attachment belongs. + /// Correctly-sized buffer. Use attachment's .WorldVerticesLength to get the correct size. If null, a new Vector2[] of the correct size will be allocated. + public static Vector2[] GetWorldVertices (this VertexAttachment a, Slot slot, Vector2[] buffer) { + int worldVertsLength = a.worldVerticesLength; + int bufferTargetSize = worldVertsLength >> 1; + buffer = buffer ?? new Vector2[bufferTargetSize]; + if (buffer.Length < bufferTargetSize) throw new System.ArgumentException(string.Format("Vector2 buffer too small. {0} requires an array of size {1}. Use the attachment's .WorldVerticesLength to get the correct size.", a.Name, worldVertsLength), "buffer"); + + var floats = new float[worldVertsLength]; + a.ComputeWorldVertices(slot, floats); + + for (int i = 0, n = worldVertsLength >> 1; i < n; i++) { + int j = i * 2; + buffer[i] = new Vector2(floats[j], floats[j + 1]); + } + + return buffer; + } + + /// Gets the PointAttachment's Unity World position using its Spine GameObject Transform. + public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) { + Vector3 skeletonSpacePosition; + skeletonSpacePosition.z = 0; + attachment.ComputeWorldPosition(slot.bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y); + return spineGameObjectTransform.TransformPoint(skeletonSpacePosition); + } + + /// Gets the PointAttachment's Unity World position using its Spine GameObject Transform. + public static Vector3 GetWorldPosition (this PointAttachment attachment, Bone bone, Transform spineGameObjectTransform) { + Vector3 skeletonSpacePosition; + skeletonSpacePosition.z = 0; + attachment.ComputeWorldPosition(bone, out skeletonSpacePosition.x, out skeletonSpacePosition.y); + return spineGameObjectTransform.TransformPoint(skeletonSpacePosition); + } + #endregion + } +} + +namespace Spine { + using System; + using System.Collections.Generic; + + public struct BoneMatrix { + public float a, b, c, d, x, y; + + /// Recursively calculates a worldspace bone matrix based on BoneData. + public static BoneMatrix CalculateSetupWorld (BoneData boneData) { + if (boneData == null) + return default(BoneMatrix); + + // End condition: isRootBone + if (boneData.parent == null) + return GetInheritedInternal(boneData, default(BoneMatrix)); + + BoneMatrix result = CalculateSetupWorld(boneData.parent); + return GetInheritedInternal(boneData, result); + } + + static BoneMatrix GetInheritedInternal (BoneData boneData, BoneMatrix parentMatrix) { + var parent = boneData.parent; + if (parent == null) return new BoneMatrix(boneData); // isRootBone + + float pa = parentMatrix.a, pb = parentMatrix.b, pc = parentMatrix.c, pd = parentMatrix.d; + BoneMatrix result = default(BoneMatrix); + result.x = pa * boneData.x + pb * boneData.y + parentMatrix.x; + result.y = pc * boneData.x + pd * boneData.y + parentMatrix.y; + + switch (boneData.transformMode) { + case TransformMode.Normal: { + float rotationY = boneData.rotation + 90 + boneData.shearY; + float la = MathUtils.CosDeg(boneData.rotation + boneData.shearX) * boneData.scaleX; + float lb = MathUtils.CosDeg(rotationY) * boneData.scaleY; + float lc = MathUtils.SinDeg(boneData.rotation + boneData.shearX) * boneData.scaleX; + float ld = MathUtils.SinDeg(rotationY) * boneData.scaleY; + result.a = pa * la + pb * lc; + result.b = pa * lb + pb * ld; + result.c = pc * la + pd * lc; + result.d = pc * lb + pd * ld; + break; + } + case TransformMode.OnlyTranslation: { + float rotationY = boneData.rotation + 90 + boneData.shearY; + result.a = MathUtils.CosDeg(boneData.rotation + boneData.shearX) * boneData.scaleX; + result.b = MathUtils.CosDeg(rotationY) * boneData.scaleY; + result.c = MathUtils.SinDeg(boneData.rotation + boneData.shearX) * boneData.scaleX; + result.d = MathUtils.SinDeg(rotationY) * boneData.scaleY; + break; + } + case TransformMode.NoRotationOrReflection: { + float s = pa * pa + pc * pc, prx; + if (s > 0.0001f) { + s = Math.Abs(pa * pd - pb * pc) / s; + pb = pc * s; + pd = pa * s; + prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg; + } else { + pa = 0; + pc = 0; + prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg; + } + float rx = boneData.rotation + boneData.shearX - prx; + float ry = boneData.rotation + boneData.shearY - prx + 90; + float la = MathUtils.CosDeg(rx) * boneData.scaleX; + float lb = MathUtils.CosDeg(ry) * boneData.scaleY; + float lc = MathUtils.SinDeg(rx) * boneData.scaleX; + float ld = MathUtils.SinDeg(ry) * boneData.scaleY; + result.a = pa * la - pb * lc; + result.b = pa * lb - pb * ld; + result.c = pc * la + pd * lc; + result.d = pc * lb + pd * ld; + break; + } + case TransformMode.NoScale: + case TransformMode.NoScaleOrReflection: { + float cos = MathUtils.CosDeg(boneData.rotation), sin = MathUtils.SinDeg(boneData.rotation); + float za = pa * cos + pb * sin; + float zc = pc * cos + pd * sin; + float s = (float)Math.Sqrt(za * za + zc * zc); + if (s > 0.00001f) + s = 1 / s; + za *= s; + zc *= s; + s = (float)Math.Sqrt(za * za + zc * zc); + float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za); + float zb = MathUtils.Cos(r) * s; + float zd = MathUtils.Sin(r) * s; + float la = MathUtils.CosDeg(boneData.shearX) * boneData.scaleX; + float lb = MathUtils.CosDeg(90 + boneData.shearY) * boneData.scaleY; + float lc = MathUtils.SinDeg(boneData.shearX) * boneData.scaleX; + float ld = MathUtils.SinDeg(90 + boneData.shearY) * boneData.scaleY; + if (boneData.transformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : false) { + zb = -zb; + zd = -zd; + } + result.a = za * la + zb * lc; + result.b = za * lb + zb * ld; + result.c = zc * la + zd * lc; + result.d = zc * lb + zd * ld; + break; + } + } + + return result; + } + + /// Constructor for a local bone matrix based on Setup Pose BoneData. + public BoneMatrix (BoneData boneData) { + float rotationY = boneData.rotation + 90 + boneData.shearY; + float rotationX = boneData.rotation + boneData.shearX; + + a = MathUtils.CosDeg(rotationX) * boneData.scaleX; + c = MathUtils.SinDeg(rotationX) * boneData.scaleX; + b = MathUtils.CosDeg(rotationY) * boneData.scaleY; + d = MathUtils.SinDeg(rotationY) * boneData.scaleY; + x = boneData.x; + y = boneData.y; + } + + /// Constructor for a local bone matrix based on a bone instance's current pose. + public BoneMatrix (Bone bone) { + float rotationY = bone.rotation + 90 + bone.shearY; + float rotationX = bone.rotation + bone.shearX; + + a = MathUtils.CosDeg(rotationX) * bone.scaleX; + c = MathUtils.SinDeg(rotationX) * bone.scaleX; + b = MathUtils.CosDeg(rotationY) * bone.scaleY; + d = MathUtils.SinDeg(rotationY) * bone.scaleY; + x = bone.x; + y = bone.y; + } + + public BoneMatrix TransformMatrix (BoneMatrix local) { + return new BoneMatrix { + a = this.a * local.a + this.b * local.c, + b = this.a * local.b + this.b * local.d, + c = this.c * local.a + this.d * local.c, + d = this.c * local.b + this.d * local.d, + x = this.a * local.x + this.b * local.y + this.x, + y = this.c * local.x + this.d * local.y + this.y + }; + } + } + + public static class SkeletonExtensions { + public static bool IsWeighted (this VertexAttachment va) { + return va.bones != null && va.bones.Length > 0; + } + + public static bool IsRenderable (this Attachment a) { + return a is IHasRendererObject; + } + + #region Transform Modes + public static bool InheritsRotation (this TransformMode mode) { + const int RotationBit = 0; + return ((int)mode & (1U << RotationBit)) == 0; + } + + public static bool InheritsScale (this TransformMode mode) { + const int ScaleBit = 1; + return ((int)mode & (1U << ScaleBit)) == 0; + } + #endregion + + #region Posing + internal static void SetPropertyToSetupPose (this Skeleton skeleton, int propertyID) { + int tt = propertyID >> 24; + var timelineType = (TimelineType)tt; + int i = propertyID - (tt << 24); + + Bone bone; + IkConstraint ikc; + PathConstraint pc; + + switch (timelineType) { + // Bone + case TimelineType.Rotate: + bone = skeleton.bones.Items[i]; + bone.rotation = bone.data.rotation; + break; + case TimelineType.Translate: + bone = skeleton.bones.Items[i]; + bone.x = bone.data.x; + bone.y = bone.data.y; + break; + case TimelineType.Scale: + bone = skeleton.bones.Items[i]; + bone.scaleX = bone.data.scaleX; + bone.scaleY = bone.data.scaleY; + break; + case TimelineType.Shear: + bone = skeleton.bones.Items[i]; + bone.shearX = bone.data.shearX; + bone.shearY = bone.data.shearY; + break; + + // Slot + case TimelineType.Attachment: + skeleton.SetSlotAttachmentToSetupPose(i); + break; + case TimelineType.Color: + skeleton.slots.Items[i].SetColorToSetupPose(); + break; + case TimelineType.TwoColor: + skeleton.slots.Items[i].SetColorToSetupPose(); + break; + case TimelineType.Deform: + skeleton.slots.Items[i].attachmentVertices.Clear(); + break; + + // Skeleton + case TimelineType.DrawOrder: + skeleton.SetDrawOrderToSetupPose(); + break; + + // IK Constraint + case TimelineType.IkConstraint: + ikc = skeleton.ikConstraints.Items[i]; + ikc.mix = ikc.data.mix; + ikc.bendDirection = ikc.data.bendDirection; + break; + + // TransformConstraint + case TimelineType.TransformConstraint: + var tc = skeleton.transformConstraints.Items[i]; + var tcData = tc.data; + tc.rotateMix = tcData.rotateMix; + tc.translateMix = tcData.translateMix; + tc.scaleMix = tcData.scaleMix; + tc.shearMix = tcData.shearMix; + break; + + // Path Constraint + case TimelineType.PathConstraintPosition: + pc = skeleton.pathConstraints.Items[i]; + pc.position = pc.data.position; + break; + case TimelineType.PathConstraintSpacing: + pc = skeleton.pathConstraints.Items[i]; + pc.spacing = pc.data.spacing; + break; + case TimelineType.PathConstraintMix: + pc = skeleton.pathConstraints.Items[i]; + pc.rotateMix = pc.data.rotateMix; + pc.translateMix = pc.data.translateMix; + break; + } + } + + /// Resets the DrawOrder to the Setup Pose's draw order + public static void SetDrawOrderToSetupPose (this Skeleton skeleton) { + var slotsItems = skeleton.slots.Items; + int n = skeleton.slots.Count; + + var drawOrder = skeleton.drawOrder; + drawOrder.Clear(false); + drawOrder.GrowIfNeeded(n); + System.Array.Copy(slotsItems, drawOrder.Items, n); + } + + /// Resets the color of a slot to Setup Pose value. + public static void SetColorToSetupPose (this Slot slot) { + slot.r = slot.data.r; + slot.g = slot.data.g; + slot.b = slot.data.b; + slot.a = slot.data.a; + slot.r2 = slot.data.r2; + slot.g2 = slot.data.g2; + slot.b2 = slot.data.b2; + } + + /// Sets a slot's attachment to setup pose. If you have the slotIndex, Skeleton.SetSlotAttachmentToSetupPose is faster. + public static void SetAttachmentToSetupPose (this Slot slot) { + var slotData = slot.data; + slot.Attachment = slot.bone.skeleton.GetAttachment(slotData.name, slotData.attachmentName); + } + + /// Resets the attachment of slot at a given slotIndex to setup pose. This is faster than Slot.SetAttachmentToSetupPose. + public static void SetSlotAttachmentToSetupPose (this Skeleton skeleton, int slotIndex) { + var slot = skeleton.slots.Items[slotIndex]; + var attachmentName = slot.data.attachmentName; + if (string.IsNullOrEmpty(attachmentName)) { + slot.Attachment = null; + } else { + var attachment = skeleton.GetAttachment(slotIndex, attachmentName); + slot.Attachment = attachment; + } + } + + /// + /// Shortcut for posing a skeleton at a specific time. Time is in seconds. (frameNumber / 30f) will give you seconds. + /// If you need to do this often, you should get the Animation object yourself using skeleton.data.FindAnimation. and call Apply on that. + /// The skeleton to pose. + /// The name of the animation to use. + /// The time of the pose within the animation. + /// Wraps the time around if it is longer than the duration of the animation. + public static void PoseWithAnimation (this Skeleton skeleton, string animationName, float time, bool loop = false) { + // Fail loud when skeleton.data is null. + Spine.Animation animation = skeleton.data.FindAnimation(animationName); + if (animation == null) return; + animation.Apply(skeleton, 0, time, loop, null, 1f, MixPose.Setup, MixDirection.In); + } + + /// Pose a skeleton according to a given time in an animation. + public static void PoseSkeleton (this Animation animation, Skeleton skeleton, float time, bool loop = false) { + animation.Apply(skeleton, 0, time, loop, null, 1f, MixPose.Setup, MixDirection.In); + } + + /// Resets Skeleton parts to Setup Pose according to a Spine.Animation's keyed items. + public static void SetKeyedItemsToSetupPose (this Animation animation, Skeleton skeleton) { + animation.Apply(skeleton, 0, 0, false, null, 0, MixPose.Setup, MixDirection.Out); + } + + + #endregion + + #region Skins + /// + public static void FindNamesForSlot (this Skin skin, string slotName, SkeletonData skeletonData, List results) { + int slotIndex = skeletonData.FindSlotIndex(slotName); + skin.FindNamesForSlot(slotIndex, results); + } + + /// + public static void FindAttachmentsForSlot (this Skin skin, string slotName, SkeletonData skeletonData, List results) { + int slotIndex = skeletonData.FindSlotIndex(slotName); + skin.FindAttachmentsForSlot(slotIndex, results); + } + #endregion + } +} diff --git a/Assets/Spine/spine-unity/SkeletonExtensions.cs.meta b/Assets/Spine/spine-unity/SkeletonExtensions.cs.meta new file mode 100644 index 0000000..427cdd1 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonExtensions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea85c8f6a91a6ab45881b0dbdaabb7d0 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Spine/spine-unity/SkeletonUtility.meta b/Assets/Spine/spine-unity/SkeletonUtility.meta new file mode 100644 index 0000000..d690c94 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: f6e0caaafe294de48af468a6a9321473 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/SkeletonUtility/Editor.meta b/Assets/Spine/spine-unity/SkeletonUtility/Editor.meta new file mode 100644 index 0000000..386e1e9 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/Editor.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: a751a9d1e3e26d64d997b66a781df8e9 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs new file mode 100644 index 0000000..de58f12 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs @@ -0,0 +1,388 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +//#define HINGECHAIN2D +// Contributed by: Mitch Thompson + + +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using Spine; + +namespace Spine.Unity.Editor { + using Icons = SpineEditorUtilities.Icons; + + [CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects] + public class SkeletonUtilityBoneInspector : UnityEditor.Editor { + SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, parentReference; + + //multi selected flags + bool containsFollows, containsOverrides, multiObject; + + //single selected helpers + SkeletonUtilityBone utilityBone; + SkeletonUtility skeletonUtility; + bool canCreateHingeChain = false; + + Dictionary> boundingBoxTable = new Dictionary>(); + //string currentSkinName = ""; + + void OnEnable () { + mode = this.serializedObject.FindProperty("mode"); + boneName = this.serializedObject.FindProperty("boneName"); + zPosition = this.serializedObject.FindProperty("zPosition"); + position = this.serializedObject.FindProperty("position"); + rotation = this.serializedObject.FindProperty("rotation"); + scale = this.serializedObject.FindProperty("scale"); + overrideAlpha = this.serializedObject.FindProperty("overrideAlpha"); + parentReference = this.serializedObject.FindProperty("parentReference"); + EvaluateFlags(); + + if (!utilityBone.valid && skeletonUtility != null && skeletonUtility.skeletonRenderer != null) + skeletonUtility.skeletonRenderer.Initialize(false); + + canCreateHingeChain = CanCreateHingeChain(); + boundingBoxTable.Clear(); + + if (multiObject) return; + if (utilityBone.bone == null) return; + + var skeleton = utilityBone.bone.Skeleton; + int slotCount = skeleton.Slots.Count; + Skin skin = skeleton.Skin; + if (skeleton.Skin == null) + skin = skeleton.Data.DefaultSkin; + + //currentSkinName = skin.Name; + for(int i = 0; i < slotCount; i++){ + Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i]; + if (slot.Bone == utilityBone.bone) { + var slotAttachments = new List(); + skin.FindAttachmentsForSlot(skeleton.FindSlotIndex(slot.Data.Name), slotAttachments); + var boundingBoxes = new List(); + foreach (var att in slotAttachments) { + var boundingBoxAttachment = att as BoundingBoxAttachment; + if (boundingBoxAttachment != null) + boundingBoxes.Add(boundingBoxAttachment); + } + + if (boundingBoxes.Count > 0) + boundingBoxTable.Add(slot, boundingBoxes); + } + } + + } + + void EvaluateFlags () { + utilityBone = (SkeletonUtilityBone)target; + skeletonUtility = utilityBone.skeletonUtility; + + if (Selection.objects.Length == 1) { + containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow; + containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override; + } else { + int boneCount = 0; + foreach (Object o in Selection.objects) { + var go = o as GameObject; + if (go != null) { + SkeletonUtilityBone sub = go.GetComponent(); + if (sub != null) { + boneCount++; + containsFollows |= (sub.mode == SkeletonUtilityBone.Mode.Follow); + containsOverrides |= (sub.mode == SkeletonUtilityBone.Mode.Override); + } + } + } + + multiObject |= (boneCount > 1); + } + } + + public override void OnInspectorGUI () { + serializedObject.Update(); + + EditorGUI.BeginChangeCheck(); + EditorGUILayout.PropertyField(mode); + if (EditorGUI.EndChangeCheck()) { + containsOverrides = mode.enumValueIndex == 1; + containsFollows = mode.enumValueIndex == 0; + } + + using (new EditorGUI.DisabledGroupScope(multiObject)) { + string str = boneName.stringValue; + if (str == "") + str = ""; + if (multiObject) + str = ""; + + using (new GUILayout.HorizontalScope()) { + EditorGUILayout.PrefixLabel("Bone"); + if (GUILayout.Button(str, EditorStyles.popup)) { + BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).skeletonUtility.skeletonRenderer.skeleton.Bones, "", TargetBoneSelected); + } + } + } + + EditorGUILayout.PropertyField(zPosition); + EditorGUILayout.PropertyField(position); + EditorGUILayout.PropertyField(rotation); + EditorGUILayout.PropertyField(scale); + + using (new EditorGUI.DisabledGroupScope(containsFollows)) { + EditorGUILayout.PropertyField(overrideAlpha); + EditorGUILayout.PropertyField(parentReference); + } + + EditorGUILayout.Space(); + + using (new GUILayout.HorizontalScope()) { + EditorGUILayout.Space(); + using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0)) { + if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Child", Icons.bone), GUILayout.MinWidth(120), GUILayout.Height(24))) + BoneSelectorContextMenu("", utilityBone.bone.Children, "", SpawnChildBoneSelected); + } + using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides)) { + if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Override", Icons.poseBones), GUILayout.MinWidth(120), GUILayout.Height(24))) + SpawnOverride(); + } + EditorGUILayout.Space(); + } + EditorGUILayout.Space(); + using (new GUILayout.HorizontalScope()) { + EditorGUILayout.Space(); + using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || !canCreateHingeChain)) { + if (GUILayout.Button(SpineInspectorUtility.TempContent("Create Hinge Chain", Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24))) + CreateHingeChain(); + } + EditorGUILayout.Space(); + } + + using (new EditorGUI.DisabledGroupScope(multiObject || boundingBoxTable.Count == 0)) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bounding Boxes", Icons.boundingBox), EditorStyles.boldLabel); + + foreach (var entry in boundingBoxTable){ + Slot slot = entry.Key; + var boundingBoxes = entry.Value; + + EditorGUI.indentLevel++; + EditorGUILayout.LabelField(slot.Data.Name); + EditorGUI.indentLevel++; + { + foreach (var box in boundingBoxes) { + using (new GUILayout.HorizontalScope()) { + GUILayout.Space(30); + string buttonLabel = box.IsWeighted() ? box.Name + " (!)" : box.Name; + if (GUILayout.Button(buttonLabel, GUILayout.Width(200))) { + utilityBone.bone.Skeleton.UpdateWorldTransform(); + var bbTransform = utilityBone.transform.Find("[BoundingBox]" + box.Name); // Use FindChild in older versions of Unity. + if (bbTransform != null) { + var originalCollider = bbTransform.GetComponent(); + if (originalCollider != null) + SkeletonUtility.SetColliderPointsLocal(originalCollider, slot, box); + else + SkeletonUtility.AddBoundingBoxAsComponent(box, slot, bbTransform.gameObject); + } else { + var newPolygonCollider = SkeletonUtility.AddBoundingBoxGameObject(null, box, slot, utilityBone.transform); + bbTransform = newPolygonCollider.transform; + } + EditorGUIUtility.PingObject(bbTransform); + } + } + + } + } + EditorGUI.indentLevel--; + EditorGUI.indentLevel--; + } + } + + BoneFollowerInspector.RecommendRigidbodyButton(utilityBone); + + serializedObject.ApplyModifiedProperties(); + } + + static void BoneSelectorContextMenu (string current, ExposedList bones, string topValue, GenericMenu.MenuFunction2 callback) { + var menu = new GenericMenu(); + + if (topValue != "") + menu.AddItem(new GUIContent(topValue), current == topValue, callback, null); + + for (int i = 0; i < bones.Count; i++) + menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]); + + menu.ShowAsContext(); + } + + void TargetBoneSelected (object obj) { + if (obj == null) { + boneName.stringValue = ""; + serializedObject.ApplyModifiedProperties(); + } else { + var bone = (Bone)obj; + boneName.stringValue = bone.Data.Name; + serializedObject.ApplyModifiedProperties(); + utilityBone.Reset(); + } + } + + void SpawnChildBoneSelected (object obj) { + if (obj == null) { + // Add recursively + foreach (var bone in utilityBone.bone.Children) { + GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); + SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren(); + foreach (SkeletonUtilityBone utilBone in newUtilityBones) + SkeletonUtilityInspector.AttachIcon(utilBone); + } + } else { + var bone = (Bone)obj; + GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale); + SkeletonUtilityInspector.AttachIcon(go.GetComponent()); + Selection.activeGameObject = go; + EditorGUIUtility.PingObject(go); + } + } + + void SpawnOverride () { + GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale); + go.name = go.name + " [Override]"; + SkeletonUtilityInspector.AttachIcon(go.GetComponent()); + Selection.activeGameObject = go; + EditorGUIUtility.PingObject(go); + } + + +#if HINGECHAIN2D + bool CanCreateHingeChain () { + if (utilityBone == null) return false; + if (utilityBone.GetComponent() != null) return false; + if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0) return false; + var rigidbodies = utilityBone.GetComponentsInChildren(); + return rigidbodies.Length <= 0; + } + + void CreateHingeChain () { + var utilBoneArr = utilityBone.GetComponentsInChildren(); + + foreach (var utilBone in utilBoneArr) { + if (utilBone.GetComponent() == null) { + if (utilBone.bone.Data.Length == 0) { + var sphere = utilBone.gameObject.AddComponent(); + sphere.radius = 0.1f; + } else { + float length = utilBone.bone.Data.Length; + var box = utilBone.gameObject.AddComponent(); + box.size = new Vector3(length, length / 3f, 0.2f); + box.offset = new Vector3(length / 2f, 0, 0); + } + } + + utilBone.gameObject.AddComponent(); + } + + utilityBone.GetComponent().isKinematic = true; + + foreach (var utilBone in utilBoneArr) { + if (utilBone == utilityBone) + continue; + + utilBone.mode = SkeletonUtilityBone.Mode.Override; + + var joint = utilBone.gameObject.AddComponent(); + joint.connectedBody = utilBone.transform.parent.GetComponent(); + joint.useLimits = true; + joint.limits = new JointAngleLimits2D { + min = -20, + max = 20 + }; + utilBone.GetComponent().mass = utilBone.transform.parent.GetComponent().mass * 0.75f; + } + } +#else + bool CanCreateHingeChain () { + if (utilityBone == null) + return false; + if (utilityBone.GetComponent() != null) + return false; + if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0) + return false; + + var rigidbodies = utilityBone.GetComponentsInChildren(); + + return rigidbodies.Length <= 0; + } + + void CreateHingeChain () { + var utilBoneArr = utilityBone.GetComponentsInChildren(); + + foreach (var utilBone in utilBoneArr) { + AttachRigidbody(utilBone); + } + + utilityBone.GetComponent().isKinematic = true; + + foreach (var utilBone in utilBoneArr) { + if (utilBone == utilityBone) + continue; + + utilBone.mode = SkeletonUtilityBone.Mode.Override; + + HingeJoint joint = utilBone.gameObject.AddComponent(); + joint.axis = Vector3.forward; + joint.connectedBody = utilBone.transform.parent.GetComponent(); + joint.useLimits = true; + joint.limits = new JointLimits { + min = -20, + max = 20 + }; + utilBone.GetComponent().mass = utilBone.transform.parent.GetComponent().mass * 0.75f; + } + } + + static void AttachRigidbody (SkeletonUtilityBone utilBone) { + if (utilBone.GetComponent() == null) { + if (utilBone.bone.Data.Length == 0) { + SphereCollider sphere = utilBone.gameObject.AddComponent(); + sphere.radius = 0.1f; + } else { + float length = utilBone.bone.Data.Length; + BoxCollider box = utilBone.gameObject.AddComponent(); + box.size = new Vector3(length, length / 3f, 0.2f); + box.center = new Vector3(length / 2f, 0, 0); + } + } + + utilBone.gameObject.AddComponent(); + } +#endif + } + +} diff --git a/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs.meta b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs.meta new file mode 100644 index 0000000..81de4d6 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b3ae20b4bcc31f645afd6f5b64f82473 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs new file mode 100644 index 0000000..218c753 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs @@ -0,0 +1,154 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using UnityEditor; +using UnityEditor.AnimatedValues; +using System.Collections.Generic; +using Spine; +using System.Reflection; + +namespace Spine.Unity.Editor { + using Icons = SpineEditorUtilities.Icons; + + [CustomEditor(typeof(SkeletonUtility))] + public class SkeletonUtilityInspector : UnityEditor.Editor { + + SkeletonUtility skeletonUtility; + Skeleton skeleton; + SkeletonRenderer skeletonRenderer; + bool isPrefab; + + GUIContent SpawnHierarchyButtonLabel = new GUIContent("Spawn Hierarchy", Icons.skeleton); + + void OnEnable () { + skeletonUtility = (SkeletonUtility)target; + skeletonRenderer = skeletonUtility.GetComponent(); + skeleton = skeletonRenderer.Skeleton; + + if (skeleton == null) { + skeletonRenderer.Initialize(false); + skeletonRenderer.LateUpdate(); + skeleton = skeletonRenderer.skeleton; + } + + if (!skeletonRenderer.valid) return; + + isPrefab |= PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab; + } + + public override void OnInspectorGUI () { + if (isPrefab) { + GUILayout.Label(new GUIContent("Cannot edit Prefabs", Icons.warning)); + return; + } + + if (!skeletonRenderer.valid) { + GUILayout.Label(new GUIContent("Spine Component invalid. Check Skeleton Data Asset.", Icons.warning)); + return; + } + + skeletonUtility.boneRoot = (Transform)EditorGUILayout.ObjectField("Bone Root", skeletonUtility.boneRoot, typeof(Transform), true); + + bool hasRootBone = skeletonUtility.boneRoot != null; + using (new EditorGUI.DisabledGroupScope(hasRootBone)) { + if (SpineInspectorUtility.LargeCenteredButton(SpawnHierarchyButtonLabel)) + SpawnHierarchyContextMenu(); + } + + if (hasRootBone) { + if (SpineInspectorUtility.CenteredButton(new GUIContent("Remove Hierarchy"))) { + Undo.RegisterCompleteObjectUndo(skeletonUtility, "Remove Hierarchy"); + Undo.DestroyObjectImmediate(skeletonUtility.boneRoot.gameObject); + skeletonUtility.boneRoot = null; + } + } + } + + void SpawnHierarchyContextMenu () { + GenericMenu menu = new GenericMenu(); + + menu.AddItem(new GUIContent("Follow"), false, SpawnFollowHierarchy); + menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly); + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Override"), false, SpawnOverrideHierarchy); + menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly); + + menu.ShowAsContext(); + } + + public static void AttachIcon (SkeletonUtilityBone utilityBone) { + Skeleton skeleton = utilityBone.skeletonUtility.skeletonRenderer.skeleton; + Texture2D icon = utilityBone.bone.Data.Length == 0 ? Icons.nullBone : Icons.boneNib; + + foreach (IkConstraint c in skeleton.IkConstraints) + if (c.Target == utilityBone.bone) { + icon = Icons.constraintNib; + break; + } + + typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] { + utilityBone.gameObject, + icon + }); + } + + static void AttachIconsToChildren (Transform root) { + if (root != null) { + var utilityBones = root.GetComponentsInChildren(); + foreach (var utilBone in utilityBones) + AttachIcon(utilBone); + } + } + + void SpawnFollowHierarchy () { + Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + + void SpawnFollowHierarchyRootOnly () { + Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + + void SpawnOverrideHierarchy () { + Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + + void SpawnOverrideHierarchyRootOnly () { + Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true); + AttachIconsToChildren(skeletonUtility.boneRoot); + } + } + +} diff --git a/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs.meta b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs.meta new file mode 100644 index 0000000..44e13b1 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a5b90df955eb8c2429ac67c8b2de6c5c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtility.cs b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtility.cs new file mode 100644 index 0000000..8280a05 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtility.cs @@ -0,0 +1,366 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using System.Collections.Generic; +using Spine; + +namespace Spine.Unity { + [RequireComponent(typeof(ISkeletonAnimation))] + [ExecuteInEditMode] + public class SkeletonUtility : MonoBehaviour { + + #region BoundingBoxAttachment + public static PolygonCollider2D AddBoundingBoxGameObject (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) { + Skin skin = string.IsNullOrEmpty(skinName) ? skeleton.data.defaultSkin : skeleton.data.FindSkin(skinName); + if (skin == null) { + Debug.LogError("Skin " + skinName + " not found!"); + return null; + } + + var attachment = skin.GetAttachment(skeleton.FindSlotIndex(slotName), attachmentName); + if (attachment == null) { + Debug.LogFormat("Attachment in slot '{0}' named '{1}' not found in skin '{2}'.", slotName, attachmentName, skin.name); + return null; + } + + var box = attachment as BoundingBoxAttachment; + if (box != null) { + var slot = skeleton.FindSlot(slotName); + return AddBoundingBoxGameObject(box.Name, box, slot, parent, isTrigger); + } else { + Debug.LogFormat("Attachment '{0}' was not a Bounding Box.", attachmentName); + return null; + } + } + + public static PolygonCollider2D AddBoundingBoxGameObject (string name, BoundingBoxAttachment box, Slot slot, Transform parent, bool isTrigger = true) { + var go = new GameObject("[BoundingBox]" + (string.IsNullOrEmpty(name) ? box.Name : name)); + var got = go.transform; + got.parent = parent; + got.localPosition = Vector3.zero; + got.localRotation = Quaternion.identity; + got.localScale = Vector3.one; + return AddBoundingBoxAsComponent(box, slot, go, isTrigger); + } + + public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment box, Slot slot, GameObject gameObject, bool isTrigger = true, bool isKinematic = true, float gravityScale = 0f) { + if (box == null) return null; + + if (slot.bone != slot.Skeleton.RootBone) { + var rb = gameObject.GetComponent(); + if (rb == null) { + rb = gameObject.AddComponent(); + rb.isKinematic = isKinematic; + rb.gravityScale = gravityScale; + } + } + + var collider = gameObject.AddComponent(); + collider.isTrigger = isTrigger; + SetColliderPointsLocal(collider, slot, box); + return collider; + } + + public static void SetColliderPointsLocal (PolygonCollider2D collider, Slot slot, BoundingBoxAttachment box) { + if (box == null) return; + if (box.IsWeighted()) Debug.LogWarning("UnityEngine.PolygonCollider2D does not support weighted or animated points. Collider points will not be animated and may have incorrect orientation. If you want to use it as a collider, please remove weights and animations from the bounding box in Spine editor."); + var verts = box.GetLocalVertices(slot, null); + collider.SetPath(0, verts); + } + + public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) { + float[] floats = boundingBox.Vertices; + int floatCount = floats.Length; + + Bounds bounds = new Bounds(); + bounds.center = new Vector3(floats[0], floats[1], 0); + for (int i = 2; i < floatCount; i += 2) + bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0)); + + Vector3 size = bounds.size; + size.z = depth; + bounds.size = size; + + return bounds; + } + #endregion + + public delegate void SkeletonUtilityDelegate (); + public event SkeletonUtilityDelegate OnReset; + public Transform boneRoot; + + void Update () { + var skeleton = skeletonRenderer.skeleton; + if (boneRoot != null && skeleton != null) { + Vector3 flipScale = Vector3.one; + if (skeleton.FlipX) + flipScale.x = -1; + + if (skeleton.FlipY) + flipScale.y = -1; + + boneRoot.localScale = flipScale; + } + } + + [HideInInspector] + public SkeletonRenderer skeletonRenderer; + [HideInInspector] + public ISkeletonAnimation skeletonAnimation; + [System.NonSerialized] + public List utilityBones = new List(); + [System.NonSerialized] + public List utilityConstraints = new List(); + + protected bool hasTransformBones; + protected bool hasUtilityConstraints; + protected bool needToReprocessBones; + + void OnEnable () { + if (skeletonRenderer == null) { + skeletonRenderer = GetComponent(); + } + + if (skeletonAnimation == null) { + skeletonAnimation = GetComponent(); + if (skeletonAnimation == null) + skeletonAnimation = GetComponent(); + } + + skeletonRenderer.OnRebuild -= HandleRendererReset; + skeletonRenderer.OnRebuild += HandleRendererReset; + + if (skeletonAnimation != null) { + skeletonAnimation.UpdateLocal -= UpdateLocal; + skeletonAnimation.UpdateLocal += UpdateLocal; + } + + CollectBones(); + } + + void Start () { + //recollect because order of operations failure when switching between game mode and edit mode... + CollectBones(); + } + + void OnDisable () { + skeletonRenderer.OnRebuild -= HandleRendererReset; + + if (skeletonAnimation != null) { + skeletonAnimation.UpdateLocal -= UpdateLocal; + skeletonAnimation.UpdateWorld -= UpdateWorld; + skeletonAnimation.UpdateComplete -= UpdateComplete; + } + } + + void HandleRendererReset (SkeletonRenderer r) { + if (OnReset != null) + OnReset(); + + CollectBones(); + } + + public void RegisterBone (SkeletonUtilityBone bone) { + if (utilityBones.Contains(bone)) + return; + else { + utilityBones.Add(bone); + needToReprocessBones = true; + } + } + + public void UnregisterBone (SkeletonUtilityBone bone) { + utilityBones.Remove(bone); + } + + public void RegisterConstraint (SkeletonUtilityConstraint constraint) { + if (utilityConstraints.Contains(constraint)) + return; + else { + utilityConstraints.Add(constraint); + needToReprocessBones = true; + } + } + + public void UnregisterConstraint (SkeletonUtilityConstraint constraint) { + utilityConstraints.Remove(constraint); + } + + public void CollectBones () { + var skeleton = skeletonRenderer.skeleton; + if (skeleton == null) return; + + if (boneRoot != null) { + var constraintTargets = new List(); + var ikConstraints = skeleton.IkConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) + constraintTargets.Add(ikConstraints.Items[i].target); + + var transformConstraints = skeleton.TransformConstraints; + for (int i = 0, n = transformConstraints.Count; i < n; i++) + constraintTargets.Add(transformConstraints.Items[i].target); + + var utilityBones = this.utilityBones; + for (int i = 0, n = utilityBones.Count; i < n; i++) { + var b = utilityBones[i]; + if (b.bone == null) continue; + hasTransformBones |= (b.mode == SkeletonUtilityBone.Mode.Override); + hasUtilityConstraints |= constraintTargets.Contains(b.bone); + } + + hasUtilityConstraints |= utilityConstraints.Count > 0; + + if (skeletonAnimation != null) { + skeletonAnimation.UpdateWorld -= UpdateWorld; + skeletonAnimation.UpdateComplete -= UpdateComplete; + + if (hasTransformBones || hasUtilityConstraints) + skeletonAnimation.UpdateWorld += UpdateWorld; + + if (hasUtilityConstraints) + skeletonAnimation.UpdateComplete += UpdateComplete; + } + + needToReprocessBones = false; + } else { + utilityBones.Clear(); + utilityConstraints.Clear(); + } + } + + void UpdateLocal (ISkeletonAnimation anim) { + if (needToReprocessBones) + CollectBones(); + + var utilityBones = this.utilityBones; + if (utilityBones == null) return; + for (int i = 0, n = utilityBones.Count; i < n; i++) + utilityBones[i].transformLerpComplete = false; + + UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Local); + } + + void UpdateWorld (ISkeletonAnimation anim) { + UpdateAllBones(SkeletonUtilityBone.UpdatePhase.World); + for (int i = 0, n = utilityConstraints.Count; i < n; i++) + utilityConstraints[i].DoUpdate(); + } + + void UpdateComplete (ISkeletonAnimation anim) { + UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Complete); + } + + void UpdateAllBones (SkeletonUtilityBone.UpdatePhase phase) { + if (boneRoot == null) + CollectBones(); + + var utilityBones = this.utilityBones; + if (utilityBones == null) return; + for (int i = 0, n = utilityBones.Count; i < n; i++) + utilityBones[i].DoUpdate(phase); + } + + public Transform GetBoneRoot () { + if (boneRoot != null) + return boneRoot; + + boneRoot = new GameObject("SkeletonUtility-Root").transform; + boneRoot.parent = transform; + boneRoot.localPosition = Vector3.zero; + boneRoot.localRotation = Quaternion.identity; + boneRoot.localScale = Vector3.one; + + return boneRoot; + } + + public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GetBoneRoot(); + Skeleton skeleton = this.skeletonRenderer.skeleton; + + GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca); + CollectBones(); + return go; + } + + public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GetBoneRoot(); + Skeleton skeleton = this.skeletonRenderer.skeleton; + GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca); + CollectBones(); + return go; + } + + public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca); + + ExposedList childrenBones = bone.Children; + for (int i = 0, n = childrenBones.Count; i < n; i++) { + Bone child = childrenBones.Items[i]; + SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca); + } + + return go; + } + + public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { + GameObject go = new GameObject(bone.Data.Name); + go.transform.parent = parent; + + SkeletonUtilityBone b = go.AddComponent(); + b.skeletonUtility = this; + b.position = pos; + b.rotation = rot; + b.scale = sca; + b.mode = mode; + b.zPosition = true; + b.Reset(); + b.bone = bone; + b.boneName = bone.Data.Name; + b.valid = true; + + if (mode == SkeletonUtilityBone.Mode.Override) { + if (rot) + go.transform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation); + + if (pos) + go.transform.localPosition = new Vector3(b.bone.X, b.bone.Y, 0); + + go.transform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0); + } + + return go; + } + + } + +} diff --git a/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtility.cs.meta b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtility.cs.meta new file mode 100644 index 0000000..29f2fd7 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtility.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7f726fb798ad621458c431cb9966d91d +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs new file mode 100644 index 0000000..42cee5b --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs @@ -0,0 +1,235 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using Spine; + +namespace Spine.Unity { + /// Sets a GameObject's transform to match a bone on a Spine skeleton. + [ExecuteInEditMode] + [AddComponentMenu("Spine/SkeletonUtilityBone")] + public class SkeletonUtilityBone : MonoBehaviour { + public enum Mode { + Follow, + Override + } + + public enum UpdatePhase { + Local, + World, + Complete + } + + #region Inspector + /// If a bone isn't set, boneName is used to find the bone. + public string boneName; + public Transform parentReference; + public Mode mode; + public bool position, rotation, scale, zPosition = true; + [Range(0f, 1f)] + public float overrideAlpha = 1; + #endregion + + [System.NonSerialized] public SkeletonUtility skeletonUtility; + [System.NonSerialized] public Bone bone; + [System.NonSerialized] public bool transformLerpComplete; + [System.NonSerialized] public bool valid; + Transform cachedTransform; + Transform skeletonTransform; + bool incompatibleTransformMode; + public bool IncompatibleTransformMode { get { return incompatibleTransformMode; } } + + public void Reset () { + bone = null; + cachedTransform = transform; + valid = skeletonUtility != null && skeletonUtility.skeletonRenderer != null && skeletonUtility.skeletonRenderer.valid; + if (!valid) + return; + skeletonTransform = skeletonUtility.transform; + skeletonUtility.OnReset -= HandleOnReset; + skeletonUtility.OnReset += HandleOnReset; + DoUpdate(UpdatePhase.Local); + } + + void OnEnable () { + skeletonUtility = transform.GetComponentInParent(); + + if (skeletonUtility == null) + return; + + skeletonUtility.RegisterBone(this); + skeletonUtility.OnReset += HandleOnReset; + } + + void HandleOnReset () { + Reset(); + } + + void OnDisable () { + if (skeletonUtility != null) { + skeletonUtility.OnReset -= HandleOnReset; + skeletonUtility.UnregisterBone(this); + } + } + + public void DoUpdate (UpdatePhase phase) { + if (!valid) { + Reset(); + return; + } + + var skeleton = skeletonUtility.skeletonRenderer.skeleton; + + if (bone == null) { + if (string.IsNullOrEmpty(boneName)) return; + bone = skeleton.FindBone(boneName); + if (bone == null) { + Debug.LogError("Bone not found: " + boneName, this); + return; + } + } + + var thisTransform = cachedTransform; + float skeletonFlipRotation = (skeleton.flipX ^ skeleton.flipY) ? -1f : 1f; + if (mode == Mode.Follow) { + switch (phase) { + case UpdatePhase.Local: + if (position) + thisTransform.localPosition = new Vector3(bone.x, bone.y, 0); + + if (rotation) { + if (bone.data.transformMode.InheritsRotation()) { + thisTransform.localRotation = Quaternion.Euler(0, 0, bone.rotation); + } else { + Vector3 euler = skeletonTransform.rotation.eulerAngles; + thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation)); + } + } + + if (scale) { + thisTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, 1f); + incompatibleTransformMode = BoneTransformModeIncompatible(bone); + } + break; + case UpdatePhase.World: + case UpdatePhase.Complete: + // Use Applied transform values (ax, ay, AppliedRotation, ascale) if world values were modified by constraints. + if (!bone.appliedValid) + bone.UpdateAppliedTransform(); + + if (position) + thisTransform.localPosition = new Vector3(bone.ax, bone.ay, 0); + + if (rotation) { + if (bone.data.transformMode.InheritsRotation()) { + thisTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation); + } else { + Vector3 euler = skeletonTransform.rotation.eulerAngles; + thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation)); + } + } + + if (scale) { + thisTransform.localScale = new Vector3(bone.ascaleX, bone.ascaleY, 1f); + incompatibleTransformMode = BoneTransformModeIncompatible(bone); + } + break; + } + + } else if (mode == Mode.Override) { + if (transformLerpComplete) + return; + + if (parentReference == null) { + if (position) { + Vector3 clp = thisTransform.localPosition; + bone.x = Mathf.Lerp(bone.x, clp.x, overrideAlpha); + bone.y = Mathf.Lerp(bone.y, clp.y, overrideAlpha); + } + + if (rotation) { + float angle = Mathf.LerpAngle(bone.Rotation, thisTransform.localRotation.eulerAngles.z, overrideAlpha); + bone.Rotation = angle; + bone.AppliedRotation = angle; + } + + if (scale) { + Vector3 cls = thisTransform.localScale; + bone.scaleX = Mathf.Lerp(bone.scaleX, cls.x, overrideAlpha); + bone.scaleY = Mathf.Lerp(bone.scaleY, cls.y, overrideAlpha); + } + + } else { + if (transformLerpComplete) + return; + + if (position) { + Vector3 pos = parentReference.InverseTransformPoint(thisTransform.position); + bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha); + bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha); + } + + if (rotation) { + float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(Vector3.forward, parentReference.InverseTransformDirection(thisTransform.up)).eulerAngles.z, overrideAlpha); + bone.Rotation = angle; + bone.AppliedRotation = angle; + } + + if (scale) { + Vector3 cls = thisTransform.localScale; + bone.scaleX = Mathf.Lerp(bone.scaleX, cls.x, overrideAlpha); + bone.scaleY = Mathf.Lerp(bone.scaleY, cls.y, overrideAlpha); + } + + incompatibleTransformMode = BoneTransformModeIncompatible(bone); + } + + transformLerpComplete = true; + } + } + + public static bool BoneTransformModeIncompatible (Bone bone) { + return !bone.data.transformMode.InheritsScale(); + } + + public void AddBoundingBox (string skinName, string slotName, string attachmentName) { + SkeletonUtility.AddBoundingBoxGameObject(bone.skeleton, skinName, slotName, attachmentName, transform); + } + + #if UNITY_EDITOR + void OnDrawGizmos () { + if (IncompatibleTransformMode) + Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning"); + } + #endif + } +} diff --git a/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs.meta b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs.meta new file mode 100644 index 0000000..51537b4 --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityBone.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b238dfcde8209044b97d23f62bcaadf6 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs new file mode 100644 index 0000000..0caefad --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs @@ -0,0 +1,54 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; + +namespace Spine.Unity { + [RequireComponent(typeof(SkeletonUtilityBone)), ExecuteInEditMode] + public abstract class SkeletonUtilityConstraint : MonoBehaviour { + + protected SkeletonUtilityBone utilBone; + protected SkeletonUtility skeletonUtility; + + protected virtual void OnEnable () { + utilBone = GetComponent(); + skeletonUtility = transform.GetComponentInParent(); + skeletonUtility.RegisterConstraint(this); + } + + protected virtual void OnDisable () { + skeletonUtility.UnregisterConstraint(this); + } + + public abstract void DoUpdate (); + } +} diff --git a/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs.meta b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs.meta new file mode 100644 index 0000000..b78abdc --- /dev/null +++ b/Assets/Spine/spine-unity/SkeletonUtility/SkeletonUtilityConstraint.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 522dbfcc6c916df4396f14f35048d185 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/SpineAttributes.cs b/Assets/Spine/spine-unity/SpineAttributes.cs new file mode 100644 index 0000000..5f3d7ed --- /dev/null +++ b/Assets/Spine/spine-unity/SpineAttributes.cs @@ -0,0 +1,291 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +// Contributed by: Mitch Thompson + +using UnityEngine; +using System; +using System.Collections; + +namespace Spine.Unity { + + [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] + public abstract class SpineAttributeBase : PropertyAttribute { + public string dataField = ""; + public string startsWith = ""; + public bool includeNone = true; + public bool fallbackToTextField = false; + } + + public class SpineSlot : SpineAttributeBase { + public bool containsBoundingBoxes = false; + + /// + /// Smart popup menu for Spine Slots + /// + /// Filters popup results to elements that begin with supplied string. + /// Disables popup results that don't contain bounding box attachments when true. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineSlot (string startsWith = "", string dataField = "", bool containsBoundingBoxes = false, bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.containsBoundingBoxes = containsBoundingBoxes; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + + public class SpineEvent : SpineAttributeBase { + /// + /// Smart popup menu for Spine Events (Spine.EventData) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback. + /// + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + public SpineEvent (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + + public class SpineIkConstraint : SpineAttributeBase { + /// + /// Smart popup menu for Spine IK Constraints (Spine.IkConstraint) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback. + /// + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + public SpineIkConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + + public class SpinePathConstraint : SpineAttributeBase { + /// + /// Smart popup menu for Spine Events (Spine.PathConstraint) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent(SkeletonRenderer)() will be called as a fallback. + /// + public SpinePathConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + + public class SpineTransformConstraint : SpineAttributeBase { + /// + /// Smart popup menu for Spine Transform Constraints (Spine.TransformConstraint) + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives). + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineTransformConstraint (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + + public class SpineSkin : SpineAttributeBase { + /// + /// Smart popup menu for Spine Skins + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineSkin (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + public class SpineAnimation : SpineAttributeBase { + /// + /// Smart popup menu for Spine Animations + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineAnimation (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + } + + public class SpineAttachment : SpineAttributeBase { + public bool returnAttachmentPath = false; + public bool currentSkinOnly = false; + public bool placeholdersOnly = false; + public string skinField = ""; + public string slotField = ""; + + /// + /// Smart popup menu for Spine Attachments + /// + /// Filters popup results to only include the current Skin. Only valid when a SkeletonRenderer is the data source. + /// Returns a fully qualified path for an Attachment in the format "Skin/Slot/AttachmentName". This path format is only used by the SpineAttachment helper methods like SpineAttachment.GetAttachment and .GetHierarchy. Do not use full path anywhere else in Spine's system. + /// Filters popup results to exclude attachments that are not children of Skin Placeholders + /// If specified, a locally scoped field with the name supplied by in slotField will be used to limit the popup results to children of a named slot + /// If specified, a locally scoped field with the name supplied by in skinField will be used to limit the popup results to entries of the named skin + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "", string skinField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.currentSkinOnly = currentSkinOnly; + this.returnAttachmentPath = returnAttachmentPath; + this.placeholdersOnly = placeholdersOnly; + this.slotField = slotField; + this.dataField = dataField; + this.skinField = skinField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + + public static SpineAttachment.Hierarchy GetHierarchy (string fullPath) { + return new SpineAttachment.Hierarchy(fullPath); + } + + public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) { + var hierarchy = SpineAttachment.GetHierarchy(attachmentPath); + return string.IsNullOrEmpty(hierarchy.name) ? null : skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name); + } + + public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) { + return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true)); + } + + /// + /// A struct that represents 3 strings that help identify and locate an attachment in a skeleton. + public struct Hierarchy { + public string skin; + public string slot; + public string name; + + public Hierarchy (string fullPath) { + string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries); + if (chunks.Length == 0) { + skin = ""; + slot = ""; + name = ""; + return; + } + else if (chunks.Length < 2) { + throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]"); + } + skin = chunks[0]; + slot = chunks[1]; + name = ""; + for (int i = 2; i < chunks.Length; i++) { + name += chunks[i]; + } + } + } + } + + public class SpineBone : SpineAttributeBase { + /// + /// Smart popup menu for Spine Bones + /// + /// Filters popup results to elements that begin with supplied string. + /// If true, the dropdown list will include a "none" option which stored as an empty string. + /// If true, and an animation list source can't be found, the field will fall back to a normal text field. If false, it will show an error. + /// If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results. + /// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives) + /// If left empty and the script the attribute is applied to is derived from Component, GetComponent() will be called as a fallback. + /// + public SpineBone (string startsWith = "", string dataField = "", bool includeNone = true, bool fallbackToTextField = false) { + this.startsWith = startsWith; + this.dataField = dataField; + this.includeNone = includeNone; + this.fallbackToTextField = fallbackToTextField; + } + + public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) { + return renderer.skeleton == null ? null : renderer.skeleton.FindBone(boneName); + } + + public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) { + var data = skeletonDataAsset.GetSkeletonData(true); + return data.FindBone(boneName); + } + } + + public class SpineAtlasRegion : PropertyAttribute { + public string atlasAssetField; + + public SpineAtlasRegion (string atlasAssetField = "") { + this.atlasAssetField = atlasAssetField; + } + } + +} diff --git a/Assets/Spine/spine-unity/SpineAttributes.cs.meta b/Assets/Spine/spine-unity/SpineAttributes.cs.meta new file mode 100644 index 0000000..00a5090 --- /dev/null +++ b/Assets/Spine/spine-unity/SpineAttributes.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ce216f51ebc1d3f40929f4e58d1c65e5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Spine/spine-unity/version.txt b/Assets/Spine/spine-unity/version.txt new file mode 100644 index 0000000..2c1099d --- /dev/null +++ b/Assets/Spine/spine-unity/version.txt @@ -0,0 +1 @@ +This Spine-Unity runtime works with data exported from Spine Editor version: 3.6.xx \ No newline at end of file diff --git a/Assets/Spine/spine-unity/version.txt.meta b/Assets/Spine/spine-unity/version.txt.meta new file mode 100644 index 0000000..d34c5b7 --- /dev/null +++ b/Assets/Spine/spine-unity/version.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 80c06a67282e71043a4b1fad3e0c5654 +timeCreated: 1485965987 +licenseType: Free +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets.meta b/Assets/StreamingAssets.meta new file mode 100644 index 0000000..ef64aa8 --- /dev/null +++ b/Assets/StreamingAssets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cb22e05b7b722ba4d9583046f354696d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/config.ini b/Assets/StreamingAssets/config.ini new file mode 100644 index 0000000..4ad8c39 --- /dev/null +++ b/Assets/StreamingAssets/config.ini @@ -0,0 +1 @@ +[placeholder] \ No newline at end of file diff --git a/Assets/StreamingAssets/config.ini.meta b/Assets/StreamingAssets/config.ini.meta new file mode 100644 index 0000000..cc611c1 --- /dev/null +++ b/Assets/StreamingAssets/config.ini.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9dfa86ca3d6820a49922a7096b6961e1 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin.meta b/Assets/StreamingAssets/skin.meta new file mode 100644 index 0000000..b43efd4 --- /dev/null +++ b/Assets/StreamingAssets/skin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3dc8a35d596d44d4e941ea994b24c387 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0001_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0001_01.png new file mode 100644 index 0000000..3048a26 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0001_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0001_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0001_01.png.meta new file mode 100644 index 0000000..184f4d2 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0001_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: f1868a473512c4442adf5f8ca5343160 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 65588d901ebaee24bbd33c4ac0604100 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0001_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0001_02.png new file mode 100644 index 0000000..978fcde Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0001_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0001_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0001_02.png.meta new file mode 100644 index 0000000..e074c83 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0001_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 9e466b03342ad29408202ff3d587a76c +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 6c730f215a8658c47808a3e67d325bb8 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0001_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0001_03.png new file mode 100644 index 0000000..63c715d Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0001_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0001_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0001_03.png.meta new file mode 100644 index 0000000..f6e32a1 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0001_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 99f32d658dc572a49af7f8b06edc2c4e +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: a8d15ac99b3a5a54787f85fe28f64f5c + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0002_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0002_01.png new file mode 100644 index 0000000..5d7c759 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0002_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0002_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0002_01.png.meta new file mode 100644 index 0000000..d487e44 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0002_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: d784c322a20589d4f8ccbf8a7547ab92 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: bfd5ed0d818e5894fac3f0b860fcbe29 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0002_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0002_02.png new file mode 100644 index 0000000..93905ae Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0002_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0002_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0002_02.png.meta new file mode 100644 index 0000000..ff384f6 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0002_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 038bc0958c4c6014abe1d2710cc4af8f +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 79624955d4af88c429b3f693083797bc + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0002_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0002_03.png new file mode 100644 index 0000000..6ef9c31 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0002_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0002_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0002_03.png.meta new file mode 100644 index 0000000..eea055c --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0002_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 5b6c7a35211e3e54bbe52ab386abbbc2 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 17372b378b7f8d340812d767f05d2b07 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0003_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0003_01.png new file mode 100644 index 0000000..17eca4d Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0003_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0003_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0003_01.png.meta new file mode 100644 index 0000000..338080e --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0003_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 88d5a0634a5d8d641b2226337646e5cc +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: ef4c7f9a9cfd43245a32975eb30b4e44 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0003_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0003_02.png new file mode 100644 index 0000000..ca22f2a Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0003_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0003_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0003_02.png.meta new file mode 100644 index 0000000..ebd3c4b --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0003_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 0b111ebdd052a1e4da0f768a043db91c +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 1164ab56d6b0efb4c8357154fa876343 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0003_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0003_03.png new file mode 100644 index 0000000..2a30639 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0003_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0003_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0003_03.png.meta new file mode 100644 index 0000000..3791f26 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0003_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 73534efffdc2c3d4fb43110634117e91 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: c2b9477af8a2c8d45bdf8b0f614ce4cf + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0004_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0004_01.png new file mode 100644 index 0000000..60a8f28 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0004_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0004_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0004_01.png.meta new file mode 100644 index 0000000..698d821 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0004_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 90ad67e2ef026fc418f245170293264f +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 83758c3e6becffd4c8fe0d36ae2547e7 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0004_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0004_02.png new file mode 100644 index 0000000..c0025e0 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0004_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0004_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0004_02.png.meta new file mode 100644 index 0000000..d1daeda --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0004_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 0e2f1b7fdbd0e0c45ad263ac90424712 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 0c2d0ee7b646d454c828a262cc6aced6 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0004_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0004_03.png new file mode 100644 index 0000000..223258b Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0004_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0004_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0004_03.png.meta new file mode 100644 index 0000000..2b95dc0 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0004_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: c2759ba98bed72f4b8360af9c4ac7f9b +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 03015d03c4d4f87478d08ccd2ada567d + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0005_01.png new file mode 100644 index 0000000..e3b6c17 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0005_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0005_01.png.meta new file mode 100644 index 0000000..e6a4f7c --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0005_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 1832cd99847058e4bb789caf288b5777 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: cef41d4a5233d594580508c91cdbd2a3 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0005_02.png new file mode 100644 index 0000000..152e6cd Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0005_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0005_02.png.meta new file mode 100644 index 0000000..58ffe73 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0005_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 62021b363e48f18419fdbd690cd3eb05 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: c132f08727c6126498b965211c8bcd84 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0005_03.png new file mode 100644 index 0000000..eb24d7c Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0005_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0005_03.png.meta new file mode 100644 index 0000000..bfd9fd7 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0005_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 0ffd2482ba53e6646a28018a1bdf9981 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 192c2b9de507eb6448cda498929160bb + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_04.png b/Assets/StreamingAssets/skin/EnemySpine_01_0005_04.png new file mode 100644 index 0000000..edade04 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0005_04.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_04.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0005_04.png.meta new file mode 100644 index 0000000..eb13bd7 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0005_04.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 66b7a384587f3054b849ed7587cd00a8 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 9458939364682664e8e25fcc234eb7f9 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_05.png b/Assets/StreamingAssets/skin/EnemySpine_01_0005_05.png new file mode 100644 index 0000000..3047b2f Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0005_05.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0005_05.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0005_05.png.meta new file mode 100644 index 0000000..dc21c00 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0005_05.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 4ab62e72ca652c749bc306974b54acbc +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 3affcb8f6ab6c08448d10850bfc3fab4 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0006_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0006_01.png new file mode 100644 index 0000000..ad7f7b3 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0006_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0006_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0006_01.png.meta new file mode 100644 index 0000000..63061b8 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0006_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 3be82c4195041c442a18346a66d6eab3 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 1e0024fa295300f469e1c924e8e53fb3 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0006_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0006_02.png new file mode 100644 index 0000000..eb524e5 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0006_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0006_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0006_02.png.meta new file mode 100644 index 0000000..1e68942 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0006_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: c92790bc0f2303745a55c2482ede0cfd +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: f002f680ef8d0a74cb71f192989b620e + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0006_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0006_03.png new file mode 100644 index 0000000..1cb127e Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0006_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0006_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0006_03.png.meta new file mode 100644 index 0000000..0c720bc --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0006_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 732653180fbc83f41959f7a3971f4bcc +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: c60618d625c1cf442882829a83dfa32d + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0007_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0007_01.png new file mode 100644 index 0000000..0e47ee1 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0007_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0007_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0007_01.png.meta new file mode 100644 index 0000000..4f6c0f9 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0007_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 02b9f0980d745f443b4bc88d05092910 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 2f1442b31e9cdfb4eae7276de9178db9 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0007_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0007_02.png new file mode 100644 index 0000000..6c80edd Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0007_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0007_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0007_02.png.meta new file mode 100644 index 0000000..a2fe900 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0007_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 24509ca83ef43b94197e7cd721b8d7f0 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 7cae77f33fad93f458ec4a5198f9709c + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0007_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0007_03.png new file mode 100644 index 0000000..353f4cf Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0007_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0007_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0007_03.png.meta new file mode 100644 index 0000000..fd9334c --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0007_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 55f331981d067cf4d9be50501e99b921 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 3e7681f2a54a2cf43a27f525ae976c8d + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0008_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0008_01.png new file mode 100644 index 0000000..d2ff653 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0008_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0008_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0008_01.png.meta new file mode 100644 index 0000000..b373ff7 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0008_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 3eefdf903c450d943b6ee3a030454c0e +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 527cb6aac45585d4d946d069758bf0f8 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0008_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0008_02.png new file mode 100644 index 0000000..5c5a61f Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0008_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0008_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0008_02.png.meta new file mode 100644 index 0000000..ae339b0 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0008_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 2d20fa011041cc14e88c9c6630fb1037 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 6b96ab26ad362704e95cb6b86ceb4ca7 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0008_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0008_03.png new file mode 100644 index 0000000..2b2ddf1 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0008_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0008_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0008_03.png.meta new file mode 100644 index 0000000..3d58420 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0008_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: e31a53d35f703a048a89d3d638137eb4 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 21c995ef09d1b0b44abb941bbcafc027 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0009_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0009_01.png new file mode 100644 index 0000000..e00a8b1 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0009_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0009_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0009_01.png.meta new file mode 100644 index 0000000..b7ebe30 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0009_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: ecbc924e8556a5c4cb9caf2356f6f5b0 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 0fdba7810da79564e854a3e46b5f7019 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0009_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0009_02.png new file mode 100644 index 0000000..dc7b0a8 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0009_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0009_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0009_02.png.meta new file mode 100644 index 0000000..a06c834 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0009_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 6cd1c6f6a6b14474382ed30125e42293 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: c760dc45a4734574299f4c2a4c2d10d5 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0009_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0009_03.png new file mode 100644 index 0000000..7f7f2d7 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0009_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0009_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0009_03.png.meta new file mode 100644 index 0000000..f9ebd6d --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0009_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: ea9600bcd7ae64e43aa25a9a0b19a686 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: bca046fb1d3025e48974a3a185123914 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0010_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0010_01.png new file mode 100644 index 0000000..6859092 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0010_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0010_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0010_01.png.meta new file mode 100644 index 0000000..96aabe9 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0010_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 93990af9a361d6443835d1d967dfb2b6 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: bf8ee054974cc5a48b6965e35417aba5 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0010_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0010_02.png new file mode 100644 index 0000000..1e3d8a0 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0010_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0010_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0010_02.png.meta new file mode 100644 index 0000000..2a58411 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0010_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: e13697621b5203747bde05fcdae619fb +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 062d80cdf1a1fef47bce871b055f1afe + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0010_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0010_03.png new file mode 100644 index 0000000..20ab6af Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0010_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0010_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0010_03.png.meta new file mode 100644 index 0000000..9e618f7 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0010_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 1e758c48c9ba189428eb773a6cf2d9c9 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: b2d1b9ad2f404df4b930f3490f5d8625 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0011_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0011_01.png new file mode 100644 index 0000000..5d288da Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0011_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0011_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0011_01.png.meta new file mode 100644 index 0000000..0aac238 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0011_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 9489b6b6cab07dc4bb5ad22a89e7c86e +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: c5db781480823fa4dae7674b63510bc1 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0011_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0011_02.png new file mode 100644 index 0000000..a90df1e Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0011_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0011_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0011_02.png.meta new file mode 100644 index 0000000..bdf7941 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0011_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 2de8c2857c95a6b4c8760777801fe75c +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: ad3aff0f5fc26d24998a52872f4d34ea + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0011_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0011_03.png new file mode 100644 index 0000000..5d3dc61 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0011_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0011_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0011_03.png.meta new file mode 100644 index 0000000..d7e9452 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0011_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: eb24d457e11144c4b8df72e54dcdc794 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: de4a74e133876f845b9982605cce01b8 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0012_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0012_01.png new file mode 100644 index 0000000..28f9705 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0012_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0012_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0012_01.png.meta new file mode 100644 index 0000000..ab69009 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0012_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 7eb7190a2b752aa4094e34d5366cc0c7 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 3bb2cbc2477cc9647af338eafe78b10f + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_01.png b/Assets/StreamingAssets/skin/EnemySpine_01_0015_01.png new file mode 100644 index 0000000..5c0560b Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0015_01.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_01.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0015_01.png.meta new file mode 100644 index 0000000..e63c14d --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0015_01.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 352b491893c43dd408e3a54050c62239 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: b28b2ec48a531304a9b546a0e9ea6627 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0015_02.png new file mode 100644 index 0000000..ab4b9fa Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0015_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0015_02.png.meta new file mode 100644 index 0000000..a03479e --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0015_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: f2648b1fc9ffe274b8cc376a19d551e1 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: de3d6036ea4028041b585f8c8c3475c4 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_03.png b/Assets/StreamingAssets/skin/EnemySpine_01_0015_03.png new file mode 100644 index 0000000..817cf52 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0015_03.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_03.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0015_03.png.meta new file mode 100644 index 0000000..0e8dd5c --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0015_03.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 83374f4f87ed9d84286647ce14d41288 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 58bdf56dba3057142a0febf79d56e52a + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_04.png b/Assets/StreamingAssets/skin/EnemySpine_01_0015_04.png new file mode 100644 index 0000000..97adc15 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0015_04.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_04.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0015_04.png.meta new file mode 100644 index 0000000..84ee922 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0015_04.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: d7bec61a5a0e7c84da284e72fe067ce3 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 24a2fd0e18d7524469a045a0052ebd63 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_05.png b/Assets/StreamingAssets/skin/EnemySpine_01_0015_05.png new file mode 100644 index 0000000..3d2c660 Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0015_05.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0015_05.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0015_05.png.meta new file mode 100644 index 0000000..4423687 --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0015_05.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: a0ecb85c8965b234396761afd94a88e5 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: a336d482342ced441975d4a2e6eddc5e + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0016_02.png b/Assets/StreamingAssets/skin/EnemySpine_01_0016_02.png new file mode 100644 index 0000000..307f95f Binary files /dev/null and b/Assets/StreamingAssets/skin/EnemySpine_01_0016_02.png differ diff --git a/Assets/StreamingAssets/skin/EnemySpine_01_0016_02.png.meta b/Assets/StreamingAssets/skin/EnemySpine_01_0016_02.png.meta new file mode 100644 index 0000000..604beef --- /dev/null +++ b/Assets/StreamingAssets/skin/EnemySpine_01_0016_02.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 6d9f14878d84a8c48bf89159db788393 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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: 8 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 0d2f0965885bd0a4e86565da4776ca67 + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/code.meta b/Assets/code.meta new file mode 100644 index 0000000..483c6b0 --- /dev/null +++ b/Assets/code.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 95c39fd69047c6247a6a56af722ccffa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/code/Mouse.cs b/Assets/code/Mouse.cs new file mode 100644 index 0000000..9c41c00 --- /dev/null +++ b/Assets/code/Mouse.cs @@ -0,0 +1,126 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine; + +public class Mouse : MonoBehaviour +{ + //Windows接口 + [DllImport("user32.dll")] + public static extern short GetAsyncKeyState(int vKey); + private const int VK_LBUTTON = 0x01; //鼠标左键 + private const int VK_RBUTTON = 0x02; //鼠标右键 + + + private Vector3 lastMousePosition = Vector3.zero; + private bool isLMouseDown = false; + private bool isRMouseDown = false; + private bool removeTransYet = false; + private bool notLClickOnObj = false; + private bool notRClickOnObj = false; + private static float previousLClickTime; + + void Update() + { + if (isMouseOnObj()) + { + if (!removeTransYet) + { + removeTransYet = true; + Window.removeMouseTransparent(); + } + } + else + { + if (removeTransYet) + { + removeTransYet = false; + Window.setMouseTransparent(); + } + } + + if (GetAsyncKeyState(VK_LBUTTON) != 0) + { + if (!isMouseOnObj()) + { + notLClickOnObj = true; + } + else if (!notLClickOnObj && !isLMouseDown) + { + isLMouseDown = true; + if (Time.time - previousLClickTime < 0.5 && Move.hugeAmount > 1) + { + GameObject thisHuge = GameObject.Find(this.name); + Destroy(thisHuge); + Move.hugeAmount--; + Window.setMouseTransparent(); + return; + } + this.GetComponent().changeState(Move.State.STOP); + this.GetComponent().changeSide(Move.Side.CENTER); + previousLClickTime = Time.time; + } + } + else + { + notLClickOnObj = false; + if (isLMouseDown) + { + isLMouseDown = false; + lastMousePosition = Vector3.zero; + if (!this.GetComponent().checkOnWall()) + { + this.GetComponent().changeState(Move.State.STOP); + this.GetComponent().changeSide(Move.Side.BOTTOM); + } + } + } + + if (isLMouseDown) + { + if (lastMousePosition != Vector3.zero) + { + Vector3 offset = Camera.main.ScreenToWorldPoint(Input.mousePosition) - lastMousePosition; + this.transform.position += offset; + } + lastMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition); + } + + + if (GetAsyncKeyState(VK_RBUTTON) != 0) + { + if (!isMouseOnObj()) + { + notRClickOnObj = true; + } + else if (!notRClickOnObj && !isRMouseDown) + { + isRMouseDown = true; + if (Move.previousCloneTime + 1 < Time.time) + { + this.GetComponent().spawnHuge(); + } + + } + } + else + { + notRClickOnObj = false; + if (isRMouseDown) + { + isRMouseDown = false; + } + } + } + + + public bool isMouseOnObj() + { + RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero); + if (hit.collider != null && hit.collider.name == this.name) + { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Assets/code/Mouse.cs.meta b/Assets/code/Mouse.cs.meta new file mode 100644 index 0000000..a5722bf --- /dev/null +++ b/Assets/code/Mouse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee19b8dc16b484546b9ddbf1869a9c7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/code/Move.cs b/Assets/code/Move.cs new file mode 100644 index 0000000..edc398c --- /dev/null +++ b/Assets/code/Move.cs @@ -0,0 +1,285 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using Spine; +using Spine.Unity; +using System.IO; + +public class Move : MonoBehaviour +{ + private SkeletonAnimation hugeAnimation; + private float currentSpeed; + private float xSpeed; + private float ySpeed; + private float nextChangeTime; + private float nextSpwanTime; + private State currentState; + private Side currentSide; + private bool canTurn = false; + + public static int hugeAmount = 1; + public static int hugeNo = 1; + public static float previousCloneTime; + public static string hugeSkinDirPath; + public static string[] hugeSkinPaths; + public static int hugeSkinNum; + + private const float moveSpeed = 0.3f; + private const float xMinLimit = -15.0f; + private const float xMaxLimit = 15.0f; + private const float yMinLimit = -8.0f; + private const float yMaxLimit = 8.0f; + private const float edgeWidth = 0.3f; + private const float minMoveTime = 1.0f; + private const float maxMoveTime = 8.0f; + private const float minStopTime = 6.0f; + private const float maxStopTime = 13.0f; + private const float minSpwanTime = 120.0f; + private const float maxSpwanTime = 300.0f; + private const float spawnRate = 0.1f; + private const int amountLimit = 10; + + public enum State + { + STOP, + MOVE_LEFT, + MOVE_RIGHT + } + + public enum Side + { + BOTTOM, + TOP, + LEFT, + RIGHT, + CENTER + } + + + void Awake() + { + //INIParser ini = new INIParser(); + //ini.Open(Application.streamingAssetsPath + "/config.ini"); + //ini.Close(); + hugeSkinDirPath = Application.streamingAssetsPath + "/skin/"; + hugeSkinPaths = Directory.GetFiles(hugeSkinDirPath, "*.png"); + hugeSkinNum = hugeSkinPaths.Length; + + hugeAnimation = this.GetComponentInChildren(); + xSpeed = ySpeed = 0; + nextChangeTime = Time.time + Random.Range(minStopTime, maxStopTime); + nextSpwanTime = Time.time + Random.Range(minSpwanTime, maxSpwanTime); + changeState(State.STOP); + changeSide(Side.BOTTOM); + if (hugeNo != 1) + { + Invoke("changeHugeSkin", 0.5f); + } + } + + + public void changeState(State state) + { + switch (state) + { + case State.STOP: + currentSpeed = 0.0f; + hugeAnimation.AnimationName = "Wait_A02"; + break; + case State.MOVE_LEFT: + currentSpeed = -moveSpeed; + hugeAnimation.AnimationName = "ActionStart_A"; + break; + case State.MOVE_RIGHT: + currentSpeed = moveSpeed; + hugeAnimation.AnimationName = "ActionClose_A"; + break; + } + currentState = state; + updateSpeed(); + } + + public void changeSide(Side side) + { + xSpeed = ySpeed = 0; + this.GetComponent().gravityScale = 0; + switch (side) + { + case Side.BOTTOM: + this.GetComponent().gravityScale = 1; + this.transform.rotation = Quaternion.Euler(0, 0, 0); + break; + case Side.TOP: + this.transform.rotation = Quaternion.Euler(0, 0, 180); + this.transform.position = new Vector3(this.transform.position.x, yMaxLimit, 0); + break; + case Side.LEFT: + this.transform.rotation = Quaternion.Euler(0, 0, 270); + this.transform.position = new Vector3(xMinLimit, this.transform.position.y, 0); + break; + case Side.RIGHT: + this.transform.rotation = Quaternion.Euler(0, 0, 90); + this.transform.position = new Vector3(xMaxLimit, this.transform.position.y, 0); + break; + case Side.CENTER: + this.transform.rotation = Quaternion.Euler(0, 0, 0); + break; + } + currentSide = side; + updateSpeed(); + } + + public void updateSpeed() + { + xSpeed = ySpeed = 0; + switch (currentSide) + { + case Side.BOTTOM: + xSpeed = currentSpeed; + break; + case Side.TOP: + xSpeed = currentSpeed * -1; + break; + case Side.LEFT: + ySpeed = currentSpeed * -1; + break; + case Side.RIGHT: + ySpeed = currentSpeed; + break; + case Side.CENTER: + break; + } + } + + public bool checkOnWall() + { + if (this.transform.position.x < xMinLimit + edgeWidth && currentSide != Side.LEFT) + { + changeSide(Side.LEFT); + return true; + } + if (this.transform.position.x > xMaxLimit - edgeWidth && currentSide != Side.RIGHT) + { + changeSide(Side.RIGHT); + return true; + } + if (this.transform.position.y > yMaxLimit - edgeWidth && currentSide != Side.TOP) + { + changeSide(Side.TOP); + return true; + } + return false; + } + + public void checkTurn() + { + if (this.transform.position.x < xMinLimit + edgeWidth && this.transform.position.y > yMaxLimit - edgeWidth) + { + changeSide(currentSide == Side.LEFT ? Side.TOP : Side.LEFT); + canTurn = false; + return; + } + if (this.transform.position.x > xMaxLimit - edgeWidth && this.transform.position.y > yMaxLimit - edgeWidth) + { + changeSide(currentSide == Side.RIGHT ? Side.TOP : Side.RIGHT); + canTurn = false; + return; + } + if (this.transform.position.x < xMinLimit + edgeWidth && this.transform.position.y < yMinLimit + edgeWidth) + { + changeSide(currentSide == Side.LEFT ? Side.BOTTOM : Side.LEFT); + canTurn = false; + return; + } + if (this.transform.position.x > xMaxLimit - edgeWidth && this.transform.position.y < yMinLimit + edgeWidth) + { + changeSide(currentSide == Side.RIGHT ? Side.BOTTOM : Side.RIGHT); + canTurn = false; + return; + } + } + + bool checkDirection() + { + if (currentSide == Side.BOTTOM && this.transform.rotation.z > 0.2 || this.transform.rotation.z < -0.2) + { + return false; + } + return true; + } + + public void spawnHuge() + { + hugeNo++; + hugeAmount++; + GameObject thisHuge = GameObject.Find(this.name); + GameObject newHuge = Instantiate(thisHuge); + newHuge.name = "huge" + hugeNo.ToString(); + previousCloneTime = Time.time; + + } + + public void changeHugeSkin() + { + Texture2D tex = new Texture2D(0,0); + tex.LoadImage(File.ReadAllBytes(hugeSkinPaths[Random.Range(0, hugeSkinNum - 1)])); + Material mat = new Material(Shader.Find("Sprites/Default")); + mat.SetTexture("_MainTex", tex); + GetComponentInChildren().material = mat; + } + + + void Update() + { + if (Input.GetMouseButtonDown(0)) + { + //changeHugeSkin(); + } + + if (Time.time >= nextChangeTime) + { + if (currentState == State.STOP && currentSide != Side.CENTER && checkDirection()) + { + if (Random.Range(0.0f, 1.0f) >= 0.5f) + { + changeState(State.MOVE_LEFT); + } + else + { + changeState(State.MOVE_RIGHT); + } + nextChangeTime += Random.Range(minMoveTime, maxMoveTime); + canTurn = true; + } + else + { + changeState(State.STOP); + nextChangeTime += Random.Range(minStopTime, maxStopTime); + } + + } + + if (Time.time >= nextSpwanTime) + { + if (Random.Range(0.0f, 1.0f) <= spawnRate && hugeAmount <= amountLimit) + { + spawnHuge(); + } + nextSpwanTime += Random.Range(minSpwanTime, maxSpwanTime); + } + + + if (currentSide == Side.LEFT || currentSide == Side.RIGHT || currentSide == Side.TOP) + { + changeSide(currentSide); + } + + if (canTurn) + { + checkTurn(); + } + + + this.transform.position += new Vector3(xSpeed * Time.deltaTime, ySpeed * Time.deltaTime, 0); + } +} diff --git a/Assets/code/Move.cs.meta b/Assets/code/Move.cs.meta new file mode 100644 index 0000000..c6ec4c8 --- /dev/null +++ b/Assets/code/Move.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27cd781cc90caf84a8c2dcda96a6baaf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/code/Window.cs b/Assets/code/Window.cs new file mode 100644 index 0000000..e41ec56 --- /dev/null +++ b/Assets/code/Window.cs @@ -0,0 +1,84 @@ +using UnityEngine; +using System; +using System.Runtime.InteropServices; + +public class Window : MonoBehaviour +{ + private int currentX; + private int currentY; + #region Win函数常量 + private struct MARGINS + { + public int cxLeftWidth; + public int cxRightWidth; + public int cyTopHeight; + public int cyBottomHeight; + } + [DllImport("user32.dll")] + private static extern IntPtr GetActiveWindow(); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags); + + [DllImport("user32.dll")] + static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, int bAlpha, int dwFlags); + + [DllImport("Dwmapi.dll")] + static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins); + [DllImport("user32.dll")] + private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong); + //private const int WS_POPUP = 0x800000; + private const int GWL_EXSTYLE = -20; + private const int GWL_STYLE = -16; + private const int WS_EX_LAYERED = 0x00080000; + private const int WS_BORDER = 0x00800000; + private const int WS_CAPTION = 0x00C00000; + private const int SWP_SHOWWINDOW = 0x0040; + private const int LWA_COLORKEY = 0x00000001; + private const int LWA_ALPHA = 0x00000002; + private const int WS_EX_TRANSPARENT = 0x00000020; + #endregion + + public static IntPtr hwnd; + void Awake() + { +#if UNITY_EDITOR + //print("unity内运行程序"); +#else + hwnd = FindWindow(null, "Huge Pet"); + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_TRANSPARENT | WS_EX_LAYERED); + SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_BORDER & ~WS_CAPTION); + currentX = 0; + currentY = 0; + SetWindowPos(hwnd, -1, currentX, currentY, Screen.currentResolution.width, Screen.currentResolution.height, SWP_SHOWWINDOW); + var margins = new MARGINS() { cxLeftWidth = -1 }; + DwmExtendFrameIntoClientArea(hwnd, ref margins); +#endif + } + + public static void setMouseTransparent() + { +#if UNITY_EDITOR + //print("unity内运行程序"); +#else + SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_LAYERED); +#endif + } + + public static void removeMouseTransparent() + { +#if UNITY_EDITOR + //print("unity内运行程序"); +#else + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_TRANSPARENT & ~WS_EX_LAYERED); +#endif + } +} diff --git a/Assets/code/Window.cs.meta b/Assets/code/Window.cs.meta new file mode 100644 index 0000000..2b22536 --- /dev/null +++ b/Assets/code/Window.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9edbed9fcddfca44c80abfb98a5659da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..8ba0375 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,40 @@ +{ + "dependencies": { + "com.unity.ads": "2.0.8", + "com.unity.analytics": "3.2.2", + "com.unity.collab-proxy": "1.2.15", + "com.unity.package-manager-ui": "2.0.13", + "com.unity.purchasing": "2.2.1", + "com.unity.textmeshpro": "1.4.1", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..4f31e74 --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..b3c263d --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,30 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 8 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0 + m_ClothInterCollisionStiffness: 0 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..0147887 --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: [] + m_configObjects: {} diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..8d9e83b --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_ExternalVersionControlSupport: Visible Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 1 + m_SpritePackerMode: 4 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd + m_ProjectGenerationRootNamespace: + m_UserGeneratedProjectSuffix: + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..ce1e58e --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..17c8f53 --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/ProjectSettings/NetworkManager.asset b/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000..5dc6a83 --- /dev/null +++ b/ProjectSettings/NetworkManager.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!149 &1 +NetworkManager: + m_ObjectHideFlags: 0 + m_DebugLevel: 0 + m_Sendrate: 15 + m_AssetToPrefab: {} diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..47880b1 --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 1 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..7992168 --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,13 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + m_DefaultList: + - type: + m_NativeTypeID: 20 + m_ManagedTypePPtr: {fileID: 0} + m_ManagedTypeFallback: + defaultPresets: + - m_Preset: {fileID: 2655988077585873504, guid: bfcfc320427f8224bbb7a96f3d3aebad, + type: 2} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..ced2b9b --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,650 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 18 + productGUID: 6d1454d602deccc4981b635813f437c9 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: Huge Pet + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 0 + m_ShowUnitySplashLogo: 0 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 0 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + displayResolutionDialog: 0 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 0 + androidBlitType: 0 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 0 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + vulkanEnableSetSRGBWrite: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 0.1 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + isWsaHolographicRemotingEnabled: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 1 + oculus: + sharedDepthBuffer: 1 + dashSupport: 1 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 0 + enable360StereoCapture: 0 + protectGraphicsMemory: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: + Standalone: com.Company.ProductName + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 1 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 9.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 9.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPhone58inPortraitSplashScreen: {fileID: 0} + iPhone58inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 5f34be1353de5cf4398729fda238591b + templatePackageId: com.unity.template.2d@1.3.0 + templateDefaultScene: Assets/Scenes/SampleScene.unity + AndroidTargetArchitectures: 5 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: + - m_BuildTarget: Standalone + m_Icons: + - serializedVersion: 2 + m_Icon: {fileID: 0} + m_Width: 1024 + m_Height: 1024 + m_Kind: 0 + - serializedVersion: 2 + m_Icon: {fileID: 2800000, guid: 47122c817d8857b4fb0b2b7d336c1823, type: 3} + m_Width: 512 + m_Height: 512 + m_Kind: 0 + - serializedVersion: 2 + m_Icon: {fileID: 0} + m_Width: 256 + m_Height: 256 + m_Kind: 0 + - serializedVersion: 2 + m_Icon: {fileID: 0} + m_Width: 128 + m_Height: 128 + m_Kind: 0 + - serializedVersion: 2 + m_Icon: {fileID: 0} + m_Width: 48 + m_Height: 48 + m_Kind: 0 + - serializedVersion: 2 + m_Icon: {fileID: 0} + m_Width: 32 + m_Height: 32 + m_Kind: 0 + - serializedVersion: 2 + m_Icon: {fileID: 0} + m_Width: 16 + m_Height: 16 + m_Kind: 0 + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: [] + m_BuildTargetVRSettings: [] + m_BuildTargetEnableVuforiaSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 3 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 1 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: {} + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: Template_2D + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: Template_2D + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + metroCompilationOverrides: 1 + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + xboxOneScriptCompiler: 1 + XboxOneOverrideIdentityName: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: + UNet: 1 + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_PrivateKeyPath: + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + facebookSdkVersion: 7.9.4 + facebookAppId: + facebookCookies: 1 + facebookLogging: 1 + facebookStatus: 1 + facebookXfbml: 0 + facebookFrictionlessRequests: 1 + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 0 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..ff8802c --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 2018.4.36f1 diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..4ae8c38 --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,191 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 3 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 0 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 0 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Standalone: 5 + Tizen: 2 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..1c92a78 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..06bcc6d --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.1 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/UPRSettings.asset b/ProjectSettings/UPRSettings.asset new file mode 100644 index 0000000..36aeda8 --- /dev/null +++ b/ProjectSettings/UPRSettings.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!487778994 &1 +UPRManager: + m_openweb: 1 + m_screenshotEnable: 1 + m_objectProfilerTicker: 0 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..4e8ef8d --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity.cn + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..6e0eaca --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05