commit
092217ca73
@ -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
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c11a242431890cd439b0b70ce5cd4cf9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f856848c84c89de45b4c4caa95df966a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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
|
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c235d8d59cefbcf40b382573b3b3a148
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
After Width: | Height: | Size: 224 KiB |
@ -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:
|
Binary file not shown.
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8832d7fa69024664cb8a4a2944e048aa
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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}
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e77f14b977183584d96496d29fdecf2c
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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}
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9b8fad0533e1ea64ca4ca2273a08d2c8
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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}
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8f60a4f2675cf5d4d9f93d67a100ebb3
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
After Width: | Height: | Size: 174 KiB |
@ -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:
|
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5b3816b9db8dcc34c9dbb4c4c30d2b66
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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}
|
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 73a8e2058e32f95459116a81c34f16d2
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 30918bcaadaaecc42bc215ff52f75b21
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1488288531
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a60dd41ef13d98647b9f963089feb7b0
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1456265151
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb7099b9c6ce91740b7041dabb0752c2
|
||||||
|
timeCreated: 1456265156
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ec2f0e7143c8a174994595883f4b1e33
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
|
||||||
|
/// <summary>Stores mix (crossfade) durations to be applied when AnimationState animations are changed.</summary>
|
||||||
|
public class AnimationStateData {
|
||||||
|
internal SkeletonData skeletonData;
|
||||||
|
readonly Dictionary<AnimationPair, float> animationToMixTime = new Dictionary<AnimationPair, float>(AnimationPairComparer.Instance);
|
||||||
|
internal float defaultMix;
|
||||||
|
|
||||||
|
/// <summary>The SkeletonData to look up animations when they are specified by name.</summary>
|
||||||
|
public SkeletonData SkeletonData { get { return skeletonData; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mix duration to use when no mix duration has been specifically defined between two animations.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets a mix duration by animation names.</summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets a mix duration when changing from the specified animation to the other.
|
||||||
|
/// See TrackEntry.MixDuration.</summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mix duration to use when changing from the specified animation to the other,
|
||||||
|
/// or the DefaultMix if no mix duration has been set.
|
||||||
|
/// </summary>
|
||||||
|
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<AnimationPair> {
|
||||||
|
public static readonly AnimationPairComparer Instance = new AnimationPairComparer();
|
||||||
|
|
||||||
|
bool IEqualityComparer<AnimationPair>.Equals (AnimationPair x, AnimationPair y) {
|
||||||
|
return ReferenceEquals(x.a1, y.a1) && ReferenceEquals(x.a2, y.a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IEqualityComparer<AnimationPair>.GetHashCode (AnimationPair obj) {
|
||||||
|
// from Tuple.CombineHashCodes // return (((h1 << 5) + h1) ^ h2);
|
||||||
|
int h1 = obj.a1.GetHashCode();
|
||||||
|
return (((h1 << 5) + h1) ^ obj.a2.GetHashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e03d60c517d9b974db35b9fd144a1d09
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<AtlasRegion> {
|
||||||
|
readonly List<AtlasPage> pages = new List<AtlasPage>();
|
||||||
|
List<AtlasRegion> regions = new List<AtlasRegion>();
|
||||||
|
TextureLoader textureLoader;
|
||||||
|
|
||||||
|
#region IEnumerable implementation
|
||||||
|
public IEnumerator<AtlasRegion> 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<AtlasPage> pages, List<AtlasRegion> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the number of tuple values read (1, 2 or 4).</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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.</summary>
|
||||||
|
/// <returns>The region, or null.</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 60626307629cc034bafd42c53a901fff
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2afe1c6b912aac54abb5925ca4ac52c2
|
||||||
|
folderAsset: yes
|
||||||
|
timeCreated: 1456265152
|
||||||
|
licenseType: Free
|
||||||
|
DefaultImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An AttachmentLoader that configures attachments using texture regions from an Atlas.
|
||||||
|
/// See <a href='http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data'>Loading Skeleton Data</a> in the Spine Runtimes Guide.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e6ff30e27c28344bad3e67d308c94cd
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 05b56321b2ddd8145a888746bc6ab917
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <return>May be null to not load any attachment.</return>
|
||||||
|
RegionAttachment NewRegionAttachment (Skin skin, string name, string path);
|
||||||
|
|
||||||
|
/// <return>May be null to not load any attachment.</return>
|
||||||
|
MeshAttachment NewMeshAttachment (Skin skin, string name, string path);
|
||||||
|
|
||||||
|
/// <return>May be null to not load any attachment.</return>
|
||||||
|
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name);
|
||||||
|
|
||||||
|
/// <returns>May be null to not load any attachment</returns>
|
||||||
|
PathAttachment NewPathAttachment (Skin skin, string name);
|
||||||
|
|
||||||
|
PointAttachment NewPointAttachment (Skin skin, string name);
|
||||||
|
|
||||||
|
ClippingAttachment NewClippingAttachment (Skin skin, string name);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 95466a4f5a30dca4aa69e8ee7df8ae85
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d6b1941960a9f6f47be3e865554d8695
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Attachment that has a polygon for bounds checking.</summary>
|
||||||
|
public class BoundingBoxAttachment : VertexAttachment {
|
||||||
|
public BoundingBoxAttachment (string name)
|
||||||
|
: base(name) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cd8ad8fc0f5bce448ba26d096ab32e85
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3380954b107f38b4c85a4cdfeceace42
|
||||||
|
timeCreated: 1492744746
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Attachment that displays a texture region using a mesh.</summary>
|
||||||
|
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; } }
|
||||||
|
/// <summary>The UV pair for each vertex, normalized within the entire texture. <seealso cref="MeshAttachment.UpdateUVs"/></summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b7f7514a003143844b6d01ecc93ed4d5
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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;
|
||||||
|
|
||||||
|
/// <summary>The length in the setup pose from the start of the path to the end of each curve.</summary>
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c77d9bf384a1e9f41966464e7e3b4870
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4fdde4cc4df0952468946f4f913dcb36
|
||||||
|
timeCreated: 1485603478
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Attachment that displays a texture region.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Transforms the attachment's four vertices to world coordinates.</summary>
|
||||||
|
/// <param name="bone">The parent bone.</param>
|
||||||
|
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
|
||||||
|
/// <param name="offset">The worldVertices index to begin writing values.</param>
|
||||||
|
/// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 89cefdd024734a941952a05d2b5dff71
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>>An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices.</summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>Gets a unique ID for this attachment.</summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Transforms local vertices to world coordinates.</summary>
|
||||||
|
/// <param name="start">The index of the first <see cref="Vertices"/> value to transform. Each vertex has 2 values, x and y.</param>
|
||||||
|
/// <param name="count">The number of world vertex values to output. Must be less than or equal to <see cref="WorldVerticesLength"/> - start.</param>
|
||||||
|
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to <paramref name="offset"/> + <paramref name="count"/>.</param>
|
||||||
|
/// <param name="offset">The <paramref name="worldVertices"/> index to begin writing values.</param>
|
||||||
|
/// <param name="stride">The number of <paramref name="worldVertices"/> entries between the value pairs written.</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if a deform originally applied to the specified attachment should be applied to this attachment.</summary>
|
||||||
|
virtual public bool ApplyDeform (VertexAttachment sourceAttachment) {
|
||||||
|
return this == sourceAttachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b40cfb462a8b774891e1604e5360d32
|
||||||
|
timeCreated: 1466772712
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b08ef68b8e39f40498ef24ef12cca281
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>
|
||||||
|
/// Stores a bone's current pose.
|
||||||
|
/// <para>
|
||||||
|
/// 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.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public class Bone : IUpdatable {
|
||||||
|
static public bool yDown;
|
||||||
|
|
||||||
|
internal BoneData data;
|
||||||
|
internal Skeleton skeleton;
|
||||||
|
internal Bone parent;
|
||||||
|
internal ExposedList<Bone> children = new ExposedList<Bone>();
|
||||||
|
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<Bone> Children { get { return children; } }
|
||||||
|
/// <summary>The local X translation.</summary>
|
||||||
|
public float X { get { return x; } set { x = value; } }
|
||||||
|
/// <summary>The local Y translation.</summary>
|
||||||
|
public float Y { get { return y; } set { y = value; } }
|
||||||
|
/// <summary>The local rotation.</summary>
|
||||||
|
public float Rotation { get { return rotation; } set { rotation = value; } }
|
||||||
|
|
||||||
|
/// <summary>The local scaleX.</summary>
|
||||||
|
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||||
|
|
||||||
|
/// <summary>The local scaleY.</summary>
|
||||||
|
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||||
|
|
||||||
|
/// <summary>The local shearX.</summary>
|
||||||
|
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||||
|
|
||||||
|
/// <summary>The local shearY.</summary>
|
||||||
|
public float ShearY { get { return shearY; } set { shearY = value; } }
|
||||||
|
|
||||||
|
/// <summary>The rotation, as calculated by any constraints.</summary>
|
||||||
|
public float AppliedRotation { get { return arotation; } set { arotation = value; } }
|
||||||
|
|
||||||
|
/// <summary>The applied local x translation.</summary>
|
||||||
|
public float AX { get { return ax; } set { ax = value; } }
|
||||||
|
|
||||||
|
/// <summary>The applied local y translation.</summary>
|
||||||
|
public float AY { get { return ay; } set { ay = value; } }
|
||||||
|
|
||||||
|
/// <summary>The applied local scaleX.</summary>
|
||||||
|
public float AScaleX { get { return ascaleX; } set { ascaleX = value; } }
|
||||||
|
|
||||||
|
/// <summary>The applied local scaleY.</summary>
|
||||||
|
public float AScaleY { get { return ascaleY; } set { ascaleY = value; } }
|
||||||
|
|
||||||
|
/// <summary>The applied local shearX.</summary>
|
||||||
|
public float AShearX { get { return ashearX; } set { ashearX = value; } }
|
||||||
|
|
||||||
|
/// <summary>The applied local shearY.</summary>
|
||||||
|
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; } }
|
||||||
|
|
||||||
|
/// <summary>Returns the magnitide (always positive) of the world scale X.</summary>
|
||||||
|
public float WorldScaleX { get { return (float)Math.Sqrt(a * a + c * c); } }
|
||||||
|
/// <summary>Returns the magnitide (always positive) of the world scale Y.</summary>
|
||||||
|
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
|
||||||
|
|
||||||
|
/// <param name="parent">May be null.</param>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Same as <see cref="UpdateWorldTransform"/>. This method exists for Bone to implement <see cref="Spine.IUpdatable"/>.</summary>
|
||||||
|
public void Update () {
|
||||||
|
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Computes the world transform using the parent bone and this bone's local transform.</summary>
|
||||||
|
public void UpdateWorldTransform () {
|
||||||
|
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Computes the world transform using the parent bone and the specified local transform.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates the world transform the specified amount and sets isAppliedValid to false.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="degrees">Degrees.</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed00e3a4b386a964fb0f1c7ffd5544e5
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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;
|
||||||
|
|
||||||
|
/// <summary>The index of the bone in Skeleton.Bones</summary>
|
||||||
|
public int Index { get { return index; } }
|
||||||
|
|
||||||
|
/// <summary>The name of the bone, which is unique within the skeleton.</summary>
|
||||||
|
public string Name { get { return name; } }
|
||||||
|
|
||||||
|
/// <summary>May be null.</summary>
|
||||||
|
public BoneData Parent { get { return parent; } }
|
||||||
|
|
||||||
|
public float Length { get { return length; } set { length = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local X translation.</summary>
|
||||||
|
public float X { get { return x; } set { x = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local Y translation.</summary>
|
||||||
|
public float Y { get { return y; } set { y = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local rotation.</summary>
|
||||||
|
public float Rotation { get { return rotation; } set { rotation = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local scaleX.</summary>
|
||||||
|
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local scaleY.</summary>
|
||||||
|
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local shearX.</summary>
|
||||||
|
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||||
|
|
||||||
|
/// <summary>Local shearY.</summary>
|
||||||
|
public float ShearY { get { return shearY; } set { shearY = value; } }
|
||||||
|
|
||||||
|
/// <summary>The transform mode for how parent world transforms affect this bone.</summary>
|
||||||
|
public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } }
|
||||||
|
|
||||||
|
/// <param name="parent">May be null.</param>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2cf831005966832449a5de742752e578
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Stores the current pose values for an Event.</summary>
|
||||||
|
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; } }
|
||||||
|
/// <summary>The animation time this event was keyed.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dec0d9d780605944eb4514125ab6350b
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Stores the setup pose values for an Event.</summary>
|
||||||
|
public class EventData {
|
||||||
|
internal string name;
|
||||||
|
|
||||||
|
/// <summary>The name of the event, which is unique within the skeleton.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 37bbfb9fb268a644ba75052961a42b81
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 89690af94a880744989712505f2957b1
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1193bcbef8900304db4c4ae8c750f617
|
||||||
|
timeCreated: 1474766505
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 ();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44a51df5672fe4249b6763960587a017
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<Bone> bones = new ExposedList<Bone>();
|
||||||
|
internal Bone target;
|
||||||
|
internal float mix;
|
||||||
|
internal int bendDirection;
|
||||||
|
|
||||||
|
public IkConstraintData Data { get { return data; } }
|
||||||
|
public int Order { get { return data.order; } }
|
||||||
|
public ExposedList<Bone> 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<Bone>(data.bones.Count);
|
||||||
|
foreach (BoneData boneData in data.bones)
|
||||||
|
bones.Add(skeleton.FindBone(boneData.name));
|
||||||
|
target = skeleton.FindBone(data.target.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Applies the constraint to the constrained bones.</summary>
|
||||||
|
public void Apply () {
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update () {
|
||||||
|
Bone target = this.target;
|
||||||
|
ExposedList<Bone> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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.</summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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.</summary>
|
||||||
|
/// <param name="child">A direct descendant of the parent bone.</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 916f8e6534860cc40824adfc2916baa7
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Stores the setup pose for an IkConstraint.</summary>
|
||||||
|
public class IkConstraintData {
|
||||||
|
internal string name;
|
||||||
|
internal int order;
|
||||||
|
internal List<BoneData> bones = new List<BoneData>();
|
||||||
|
internal BoneData target;
|
||||||
|
internal int bendDirection = 1;
|
||||||
|
internal float mix = 1;
|
||||||
|
|
||||||
|
/// <summary>The IK constraint's name, which is unique within the skeleton.</summary>
|
||||||
|
public string Name {
|
||||||
|
get { return name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Order {
|
||||||
|
get { return order; }
|
||||||
|
set { order = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The bones that are constrained by this IK Constraint.</summary>
|
||||||
|
public List<BoneData> Bones {
|
||||||
|
get { return bones; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The bone that is the IK target.</summary>
|
||||||
|
public BoneData Target {
|
||||||
|
get { return target; }
|
||||||
|
set { target = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 94ad1e9256073264785f806086a000ba
|
||||||
|
timeCreated: 1456265155
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<string, object> ParseObject()
|
||||||
|
{
|
||||||
|
var table = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// {
|
||||||
|
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<object> ParseArray()
|
||||||
|
{
|
||||||
|
var array = new List<object>();
|
||||||
|
|
||||||
|
// [
|
||||||
|
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>(T value)
|
||||||
|
{
|
||||||
|
if (lexer.hasError)
|
||||||
|
TriggerError("Lexical error ocurred");
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 371f40ecc08b2eb4cbec49585d41e2c3
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the sine in radians from a lookup table.</summary>
|
||||||
|
static public float Sin (float radians) {
|
||||||
|
return sin[(int)(radians * RadToIndex) & SIN_MASK];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the cosine in radians from a lookup table.</summary>
|
||||||
|
static public float Cos (float radians) {
|
||||||
|
return sin[(int)((radians + PI / 2) * RadToIndex) & SIN_MASK];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the sine in radians from a lookup table.</summary>
|
||||||
|
static public float SinDeg (float degrees) {
|
||||||
|
return sin[(int)(degrees * DegToIndex) & SIN_MASK];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the cosine in radians from a lookup table.</summary>
|
||||||
|
static public float CosDeg (float degrees) {
|
||||||
|
return sin[(int)((degrees + 90) * DegToIndex) & SIN_MASK];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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).</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 03b653e54c5403b4191f5003d64c6e18
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<Bone> bones;
|
||||||
|
internal Slot target;
|
||||||
|
internal float position, spacing, rotateMix, translateMix;
|
||||||
|
|
||||||
|
internal ExposedList<float> spaces = new ExposedList<float>(), positions = new ExposedList<float>();
|
||||||
|
internal ExposedList<float> world = new ExposedList<float>(), curves = new ExposedList<float>(), lengths = new ExposedList<float>();
|
||||||
|
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<Bone> 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<Bone>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Applies the constraint to the constrained bones.</summary>
|
||||||
|
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<float> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 731d05fbc2874c74984813ce4c5bb8df
|
||||||
|
timeCreated: 1467213650
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<BoneData> bones = new ExposedList<BoneData>();
|
||||||
|
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<BoneData> 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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9d836858269be96428428fb6764dfc3a
|
||||||
|
timeCreated: 1467213651
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<Bone> bones;
|
||||||
|
internal ExposedList<Slot> slots;
|
||||||
|
internal ExposedList<Slot> drawOrder;
|
||||||
|
internal ExposedList<IkConstraint> ikConstraints;
|
||||||
|
internal ExposedList<TransformConstraint> transformConstraints;
|
||||||
|
internal ExposedList<PathConstraint> pathConstraints;
|
||||||
|
internal ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
|
||||||
|
internal ExposedList<Bone> updateCacheReset = new ExposedList<Bone>();
|
||||||
|
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<Bone> Bones { get { return bones; } }
|
||||||
|
public ExposedList<IUpdatable> UpdateCacheList { get { return updateCache; } }
|
||||||
|
public ExposedList<Slot> Slots { get { return slots; } }
|
||||||
|
public ExposedList<Slot> DrawOrder { get { return drawOrder; } }
|
||||||
|
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
|
||||||
|
public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
|
||||||
|
public ExposedList<TransformConstraint> 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<Bone>(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<Slot>(data.slots.Count);
|
||||||
|
drawOrder = new ExposedList<Slot>(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<IkConstraint>(data.ikConstraints.Count);
|
||||||
|
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
|
||||||
|
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
|
||||||
|
|
||||||
|
transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
|
||||||
|
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
|
||||||
|
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
|
||||||
|
|
||||||
|
pathConstraints = new ExposedList<PathConstraint> (data.pathConstraints.Count);
|
||||||
|
foreach (PathConstraintData pathConstraintData in data.pathConstraints)
|
||||||
|
pathConstraints.Add(new PathConstraint(pathConstraintData, this));
|
||||||
|
|
||||||
|
UpdateCache();
|
||||||
|
UpdateWorldTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added
|
||||||
|
/// or removed.</summary>
|
||||||
|
public void UpdateCache () {
|
||||||
|
ExposedList<IUpdatable> updateCache = this.updateCache;
|
||||||
|
updateCache.Clear();
|
||||||
|
this.updateCacheReset.Clear();
|
||||||
|
|
||||||
|
ExposedList<Bone> bones = this.bones;
|
||||||
|
for (int i = 0, n = bones.Count; i < n; i++)
|
||||||
|
bones.Items[i].sorted = false;
|
||||||
|
|
||||||
|
ExposedList<IkConstraint> 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<Bone> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Updates the world transform for each bone and applies constraints.</summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets the bones, constraints, and slots to their setup pose values.</summary>
|
||||||
|
public void SetToSetupPose () {
|
||||||
|
SetBonesToSetupPose();
|
||||||
|
SetSlotsToSetupPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>-1 if the bone was not found.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>-1 if the bone was not found.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets a skin by name (see SetSkin).</summary>
|
||||||
|
public void SetSkin (string skinName) {
|
||||||
|
Skin foundSkin = data.FindSkin(skinName);
|
||||||
|
if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
|
||||||
|
SetSkin(foundSkin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>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.</para>
|
||||||
|
/// <para>After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
|
||||||
|
/// <see cref="Skeleton.SetSlotsToSetupPose()"/>.
|
||||||
|
/// Also, often <see cref="AnimationState.Apply(Skeleton)"/> 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.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newSkin">May be null.</param>
|
||||||
|
public void SetSkin (Skin newSkin) {
|
||||||
|
if (newSkin != null) {
|
||||||
|
if (skin != null)
|
||||||
|
newSkin.AttachAll(this, skin);
|
||||||
|
else {
|
||||||
|
ExposedList<Slot> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public Attachment GetAttachment (string slotName, string attachmentName) {
|
||||||
|
return GetAttachment(data.FindSlotIndex(slotName), attachmentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="attachmentName">May be null.</param>
|
||||||
|
public void SetAttachment (string slotName, string attachmentName) {
|
||||||
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
|
ExposedList<Slot> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public IkConstraint FindIkConstraint (string constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<IkConstraint> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public TransformConstraint FindTransformConstraint (string constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<TransformConstraint> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public PathConstraint FindPathConstraint (string constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<PathConstraint> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.</summary>
|
||||||
|
/// <param name="x">The horizontal distance between the skeleton origin and the left side of the AABB.</param>
|
||||||
|
/// <param name="y">The vertical distance between the skeleton origin and the bottom side of the AABB.</param>
|
||||||
|
/// <param name="width">The width of the AABB</param>
|
||||||
|
/// <param name="height">The height of the AABB.</param>
|
||||||
|
/// <param name="vertexBuffer">Reference to hold a float[]. May be a null reference. This method will assign it a new float[] with the appropriate size as needed.</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 12ac3c1c7546be24fb9625d3c850619d
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<SkeletonJson.LinkedMesh> linkedMeshes = new List<SkeletonJson.LinkedMesh>();
|
||||||
|
|
||||||
|
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<SkeletonData> 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
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>Returns the version string of binary skeleton data.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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<float>(verticesLength * 3 * 3);
|
||||||
|
var bonesArray = new ExposedList<int>(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<Timeline>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 40d8a8f15082f3844a5c9c8c3ef2047f
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public class SkeletonBounds {
|
||||||
|
private ExposedList<Polygon> polygonPool = new ExposedList<Polygon>();
|
||||||
|
private float minX, minY, maxX, maxY;
|
||||||
|
|
||||||
|
public ExposedList<BoundingBoxAttachment> BoundingBoxes { get; private set; }
|
||||||
|
public ExposedList<Polygon> 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<BoundingBoxAttachment>();
|
||||||
|
Polygons = new ExposedList<Polygon>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears any previous polygons, finds all visible bounding box attachments,
|
||||||
|
/// and computes the world vertices for each bounding box's polygon.</summary>
|
||||||
|
/// <param name="skeleton">The skeleton.</param>
|
||||||
|
/// <param name="updateAabb">
|
||||||
|
/// If true, the axis aligned bounding box containing all the polygons is computed.
|
||||||
|
/// If false, the SkeletonBounds AABB methods will always return true.
|
||||||
|
/// </param>
|
||||||
|
public void Update (Skeleton skeleton, bool updateAabb) {
|
||||||
|
ExposedList<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
|
||||||
|
ExposedList<Polygon> polygons = Polygons;
|
||||||
|
ExposedList<Slot> 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<Polygon> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns true if the axis aligned bounding box contains the point.</summary>
|
||||||
|
public bool AabbContainsPoint (float x, float y) {
|
||||||
|
return x >= minX && x <= maxX && y >= minY && y <= maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if the axis aligned bounding box intersects the line segment.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds.</summary>
|
||||||
|
public bool AabbIntersectsSkeleton (SkeletonBounds bounds) {
|
||||||
|
return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if the polygon contains the point.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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.</summary>
|
||||||
|
public BoundingBoxAttachment ContainsPoint (float x, float y) {
|
||||||
|
ExposedList<Polygon> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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.</summary>
|
||||||
|
public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
|
||||||
|
ExposedList<Polygon> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if the polygon contains the line segment.</summary>
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 087b328a58c93b149bb977eee3a17258
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7db809d277afd0e4a8e8c6b703002ee0
|
||||||
|
timeCreated: 1492744746
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
|
||||||
|
/// <summary>Stores the setup pose and all of the stateless data for a skeleton.</summary>
|
||||||
|
public class SkeletonData {
|
||||||
|
internal string name;
|
||||||
|
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(); // Ordered parents first
|
||||||
|
internal ExposedList<SlotData> slots = new ExposedList<SlotData>(); // Setup pose draw order.
|
||||||
|
internal ExposedList<Skin> skins = new ExposedList<Skin>();
|
||||||
|
internal Skin defaultSkin;
|
||||||
|
internal ExposedList<EventData> events = new ExposedList<EventData>();
|
||||||
|
internal ExposedList<Animation> animations = new ExposedList<Animation>();
|
||||||
|
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
|
||||||
|
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
|
||||||
|
internal ExposedList<PathConstraintData> pathConstraints = new ExposedList<PathConstraintData>();
|
||||||
|
internal float width, height;
|
||||||
|
internal string version, hash;
|
||||||
|
|
||||||
|
// Nonessential.
|
||||||
|
internal float fps;
|
||||||
|
internal string imagesPath;
|
||||||
|
|
||||||
|
public string Name { get { return name; } set { name = value; } }
|
||||||
|
|
||||||
|
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
|
||||||
|
public ExposedList<BoneData> Bones { get { return bones; } }
|
||||||
|
|
||||||
|
public ExposedList<SlotData> Slots { get { return slots; } }
|
||||||
|
|
||||||
|
/// <summary>All skins, including the default skin.</summary>
|
||||||
|
public ExposedList<Skin> Skins { get { return skins; } set { skins = value; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The skeleton's default skin.
|
||||||
|
/// By default this skin contains all attachments that were not in a skin in Spine.
|
||||||
|
/// </summary>
|
||||||
|
/// <return>May be null.</return>
|
||||||
|
public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } }
|
||||||
|
|
||||||
|
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
|
||||||
|
public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
|
||||||
|
public ExposedList<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
|
||||||
|
public ExposedList<TransformConstraintData> TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } }
|
||||||
|
public ExposedList<PathConstraintData> 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; } }
|
||||||
|
/// <summary>The Spine version used to export this data, or null.</summary>
|
||||||
|
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; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The dopesheet FPS in Spine. Available only when nonessential data was exported.</summary>
|
||||||
|
public float Fps { get { return fps; } set { fps = value; } }
|
||||||
|
|
||||||
|
// --- Bones.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.</summary>
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>-1 if the bone was not found.</returns>
|
||||||
|
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.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public SlotData FindSlot (string slotName) {
|
||||||
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
|
ExposedList<SlotData> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>-1 if the slot was not found.</returns>
|
||||||
|
public int FindSlotIndex (string slotName) {
|
||||||
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
|
ExposedList<SlotData> slots = this.slots;
|
||||||
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
|
if (slots.Items[i].name == slotName) return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Skins.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
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.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public Animation FindAnimation (string animationName) {
|
||||||
|
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
|
||||||
|
ExposedList<Animation> 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.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public IkConstraintData FindIkConstraint (string constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<IkConstraintData> 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.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public TransformConstraintData FindTransformConstraint (string constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<TransformConstraintData> 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.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public PathConstraintData FindPathConstraint (string constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<PathConstraintData> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>-1 if the path constraint was not found.</returns>
|
||||||
|
public int FindPathConstraintIndex (string pathConstraintName) {
|
||||||
|
if (pathConstraintName == null) throw new ArgumentNullException("pathConstraintName", "pathConstraintName cannot be null.");
|
||||||
|
ExposedList<PathConstraintData> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b813f63abbb6d94a80a5c050590a0be
|
||||||
|
timeCreated: 1456265153
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
|
||||||
|
|
||||||
|
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<SkeletonData> 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<string, Object>;
|
||||||
|
if (root == null) throw new Exception("Invalid JSON.");
|
||||||
|
|
||||||
|
// Skeleton.
|
||||||
|
if (root.ContainsKey("skeleton")) {
|
||||||
|
var skeletonMap = (Dictionary<string, Object>)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<string, Object> boneMap in (List<Object>)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<string, Object> slotMap in (List<Object>)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<string, Object> constraintMap in (List<Object>)root["ik"]) {
|
||||||
|
IkConstraintData data = new IkConstraintData((string)constraintMap["name"]);
|
||||||
|
data.order = GetInt(constraintMap, "order", 0);
|
||||||
|
|
||||||
|
foreach (string boneName in (List<Object>)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<string, Object> constraintMap in (List<Object>)root["transform"]) {
|
||||||
|
TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]);
|
||||||
|
data.order = GetInt(constraintMap, "order", 0);
|
||||||
|
|
||||||
|
foreach (string boneName in (List<Object>)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<string, Object> constraintMap in (List<Object>)root["path"]) {
|
||||||
|
PathConstraintData data = new PathConstraintData((string)constraintMap["name"]);
|
||||||
|
data.order = GetInt(constraintMap, "order", 0);
|
||||||
|
|
||||||
|
foreach (string boneName in (List<Object>)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<string, Object> skinMap in (Dictionary<string, Object>)root["skins"]) {
|
||||||
|
var skin = new Skin(skinMap.Key);
|
||||||
|
foreach (KeyValuePair<string, Object> slotEntry in (Dictionary<string, Object>)skinMap.Value) {
|
||||||
|
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
||||||
|
foreach (KeyValuePair<string, Object> entry in ((Dictionary<string, Object>)slotEntry.Value)) {
|
||||||
|
try {
|
||||||
|
Attachment attachment = ReadAttachment((Dictionary<string, Object>)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<string, Object> entry in (Dictionary<string, Object>)root["events"]) {
|
||||||
|
var entryMap = (Dictionary<string, Object>)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<string, Object> entry in (Dictionary<string, Object>)root["animations"]) {
|
||||||
|
try {
|
||||||
|
ReadAnimation((Dictionary<string, Object>)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<string, Object> 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<string, Object> 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<float> weights = new ExposedList<float>(verticesLength * 3 * 3);
|
||||||
|
ExposedList<int> bones = new ExposedList<int>(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<string, Object> map, string name, SkeletonData skeletonData) {
|
||||||
|
var scale = this.Scale;
|
||||||
|
var timelines = new ExposedList<Timeline>();
|
||||||
|
float duration = 0;
|
||||||
|
|
||||||
|
// Slot timelines.
|
||||||
|
if (map.ContainsKey("slots")) {
|
||||||
|
foreach (KeyValuePair<string, Object> entry in (Dictionary<string, Object>)map["slots"]) {
|
||||||
|
string slotName = entry.Key;
|
||||||
|
int slotIndex = skeletonData.FindSlotIndex(slotName);
|
||||||
|
var timelineMap = (Dictionary<string, Object>)entry.Value;
|
||||||
|
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
||||||
|
var values = (List<Object>)timelineEntry.Value;
|
||||||
|
var timelineName = (string)timelineEntry.Key;
|
||||||
|
if (timelineName == "attachment") {
|
||||||
|
var timeline = new AttachmentTimeline(values.Count);
|
||||||
|
timeline.slotIndex = slotIndex;
|
||||||
|
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<string, Object> 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<string, Object> 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<string, Object> entry in (Dictionary<string, Object>)map["bones"]) {
|
||||||
|
string boneName = entry.Key;
|
||||||
|
int boneIndex = skeletonData.FindBoneIndex(boneName);
|
||||||
|
if (boneIndex == -1) throw new Exception("Bone not found: " + boneName);
|
||||||
|
var timelineMap = (Dictionary<string, Object>)entry.Value;
|
||||||
|
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
||||||
|
var values = (List<Object>)timelineEntry.Value;
|
||||||
|
var timelineName = (string)timelineEntry.Key;
|
||||||
|
if (timelineName == "rotate") {
|
||||||
|
var timeline = new RotateTimeline(values.Count);
|
||||||
|
timeline.boneIndex = boneIndex;
|
||||||
|
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<string, Object> 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<string, Object> constraintMap in (Dictionary<string, Object>)map["ik"]) {
|
||||||
|
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
|
||||||
|
var values = (List<Object>)constraintMap.Value;
|
||||||
|
var timeline = new IkConstraintTimeline(values.Count);
|
||||||
|
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<string, Object> constraintMap in (Dictionary<string, Object>)map["transform"]) {
|
||||||
|
TransformConstraintData constraint = skeletonData.FindTransformConstraint(constraintMap.Key);
|
||||||
|
var values = (List<Object>)constraintMap.Value;
|
||||||
|
var timeline = new TransformConstraintTimeline(values.Count);
|
||||||
|
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<string, Object> constraintMap in (Dictionary<string, Object>)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<string, Object>)constraintMap.Value;
|
||||||
|
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
||||||
|
var values = (List<Object>)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<string, Object> 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<string, Object> 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<string, Object> deformMap in (Dictionary<string, Object>)map["deform"]) {
|
||||||
|
Skin skin = skeletonData.FindSkin(deformMap.Key);
|
||||||
|
foreach (KeyValuePair<string, Object> slotMap in (Dictionary<string, Object>)deformMap.Value) {
|
||||||
|
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
|
||||||
|
if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key);
|
||||||
|
foreach (KeyValuePair<string, Object> timelineMap in (Dictionary<string, Object>)slotMap.Value) {
|
||||||
|
var values = (List<Object>)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<string, Object> 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<Object>)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"];
|
||||||
|
var timeline = new DrawOrderTimeline(values.Count);
|
||||||
|
int slotCount = skeletonData.slots.Count;
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<Object>)drawOrderMap["offsets"];
|
||||||
|
int[] unchanged = new int[slotCount - offsets.Count];
|
||||||
|
int originalIndex = 0, unchangedIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<Object>)map["events"];
|
||||||
|
var timeline = new EventTimeline(eventsMap.Count);
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<string, Object> 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<string, Object> 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<Object>;
|
||||||
|
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<string, Object> map, string name, float scale) {
|
||||||
|
var list = (List<Object>)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<string, Object> map, string name) {
|
||||||
|
var list = (List<Object>)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<string, Object> map, string name, float defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (float)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetInt(Dictionary<string, Object> map, string name, int defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (int)(float)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetBoolean(Dictionary<string, Object> map, string name, bool defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (bool)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetString(Dictionary<string, Object> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6c4ab7992894bdb44a480981b1953f76
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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 {
|
||||||
|
/// <summary>Stores attachments by slot index and attachment name.
|
||||||
|
/// <para>See SkeletonData <see cref="Spine.SkeletonData.DefaultSkin"/>, Skeleton <see cref="Spine.Skeleton.Skin"/>, and
|
||||||
|
/// <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide.</para>
|
||||||
|
/// </summary>
|
||||||
|
public class Skin {
|
||||||
|
internal string name;
|
||||||
|
private Dictionary<AttachmentKeyTuple, Attachment> attachments =
|
||||||
|
new Dictionary<AttachmentKeyTuple, Attachment>(AttachmentKeyTupleComparer.Instance);
|
||||||
|
|
||||||
|
public string Name { get { return name; } }
|
||||||
|
public Dictionary<AttachmentKeyTuple, Attachment> Attachments { get { return attachments; } }
|
||||||
|
|
||||||
|
public Skin (string name) {
|
||||||
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>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.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns the attachment for the specified slot index and name, or null.</summary>
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public Attachment GetAttachment (int slotIndex, string name) {
|
||||||
|
Attachment attachment;
|
||||||
|
attachments.TryGetValue(new AttachmentKeyTuple(slotIndex, name), out attachment);
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds the skin keys for a given slot. The results are added to the passed List(names).</summary>
|
||||||
|
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
|
||||||
|
/// <param name="names">Found skin key names will be added to this list.</param>
|
||||||
|
public void FindNamesForSlot (int slotIndex, List<string> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds the attachments for a given slot. The results are added to the passed List(Attachment).</summary>
|
||||||
|
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
|
||||||
|
/// <param name="attachments">Found Attachments will be added to this list.</param>
|
||||||
|
public void FindAttachmentsForSlot (int slotIndex, List<Attachment> attachments) {
|
||||||
|
if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null.");
|
||||||
|
foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in this.attachments)
|
||||||
|
if (entry.Key.slotIndex == slotIndex) attachments.Add(entry.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
override public string ToString () {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.</summary>
|
||||||
|
internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
|
||||||
|
foreach (KeyValuePair<AttachmentKeyTuple, Attachment> 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<AttachmentKeyTuple> {
|
||||||
|
internal static readonly AttachmentKeyTupleComparer Instance = new AttachmentKeyTupleComparer();
|
||||||
|
|
||||||
|
bool IEqualityComparer<AttachmentKeyTuple>.Equals (AttachmentKeyTuple o1, AttachmentKeyTuple o2) {
|
||||||
|
return o1.slotIndex == o2.slotIndex && o1.nameHashCode == o2.nameHashCode && string.Equals(o1.name, o2.name, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IEqualityComparer<AttachmentKeyTuple>.GetHashCode (AttachmentKeyTuple o) {
|
||||||
|
return o.slotIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7df8caa3a771f464e803316a6b18c909
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<float> attachmentVertices = new ExposedList<float>();
|
||||||
|
|
||||||
|
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; } }
|
||||||
|
|
||||||
|
/// <summary>May be null.</summary>
|
||||||
|
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<float> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6974c4b5c87687140a2417201ea43066
|
||||||
|
timeCreated: 1456265154
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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; } }
|
||||||
|
|
||||||
|
/// <summary>May be null.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f28cb47bc1e8b434c85e6f69b2c9e15e
|
||||||
|
timeCreated: 1456265156
|
||||||
|
licenseType: Free
|
||||||
|
MonoImporter:
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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<Bone> 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<Bone> 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<Bone>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue