1 module spine.skeleton; 2 3 import spine.atlas; 4 import spine.color; 5 import std..string: toStringz; 6 import std.exception: enforce; 7 8 class SkeletonData 9 { 10 package spSkeletonData* sp_skeletonData; 11 12 this(string filename, Atlas atlas) 13 { 14 spSkeletonJson* json = spSkeletonJson_create(atlas.atlas); 15 assert(json); 16 17 sp_skeletonData = spSkeletonJson_readSkeletonDataFile(json, filename.toStringz); 18 enforce(sp_skeletonData, "file '"~filename~"' isn't opened"); 19 20 spSkeletonJson_dispose(json); 21 } 22 23 ~this() 24 { 25 spSkeletonData_dispose(sp_skeletonData); 26 } 27 28 const (spSkeletonData)* getSpSkeletonData() const 29 { 30 return sp_skeletonData; 31 } 32 33 spSkin* findSkin(string name) 34 { 35 return spSkeletonData_findSkin(sp_skeletonData, name.toStringz); 36 } 37 38 void defaultSkin(spSkin* s) 39 { 40 sp_skeletonData.defaultSkin = s; 41 } 42 43 int findBoneIndex(string boneName) const 44 { 45 int idx = spSkeletonData_findBoneIndex(sp_skeletonData, boneName.toStringz); 46 47 enforce(idx >= 0, "Bone not found"); 48 49 return idx; 50 } 51 52 int findSlotIndex(string slotName) const 53 { 54 int idx = spSkeletonData_findSlotIndex(sp_skeletonData, slotName.toStringz); 55 56 enforce(idx >= 0, "Slot not found"); 57 58 return idx; 59 } 60 61 package spSlotData* findSlotByIndex(int idx) 62 { 63 assert(idx >= 0); 64 assert(idx < sp_skeletonData.slotsCount); 65 66 return sp_skeletonData.slots[idx]; 67 } 68 } 69 70 class Skeleton 71 { 72 private SkeletonData skeletonData; 73 package spSkeleton* sp_skeleton; 74 75 /// Useful for custom implementations of Skeleton.draw() 76 protected const (spSkeleton)* sp_skeleton_protected() const 77 { 78 return sp_skeleton; 79 } 80 81 this(SkeletonData sd) 82 { 83 skeletonData = sd; 84 sp_skeleton = spSkeleton_create(skeletonData.sp_skeletonData); 85 86 assert(sp_skeleton); 87 } 88 89 ~this() 90 { 91 spSkeleton_dispose (sp_skeleton); 92 } 93 94 spSkeleton* getSpSkeleton() 95 { 96 return sp_skeleton; 97 } 98 99 const(SkeletonData) getSkeletonData() const 100 { 101 return skeletonData; 102 } 103 104 void update(float deltaTime) 105 { 106 spSkeleton_update(sp_skeleton, deltaTime); 107 } 108 109 void updateWorldTransform() 110 { 111 spSkeleton_updateWorldTransform(sp_skeleton); 112 } 113 114 void setToSetupPose() 115 { 116 spSkeleton_setToSetupPose(sp_skeleton); 117 } 118 119 bool flipX(bool b){ sp_skeleton.flipX = b; return b; } 120 bool flipY(bool b){ sp_skeleton.flipY = b; return b; } 121 122 bool flipX() const { return sp_skeleton.flipX != 0; } 123 bool flipY() const { return sp_skeleton.flipY != 0; } 124 125 void x(float x){ sp_skeleton.x = x; } 126 void y(float y){ sp_skeleton.y = y; } 127 128 float x() const { return sp_skeleton.x; } 129 float y() const { return sp_skeleton.y; } 130 131 spBone* getRootBone() 132 { 133 return sp_skeleton.root; 134 } 135 136 spBone* getBoneByIndex(int idx) 137 { 138 assert(idx >= 0); 139 assert(idx < sp_skeleton.bonesCount); 140 141 return sp_skeleton.bones[idx]; 142 } 143 144 spBone* getBoneByIndex(size_t idx) 145 { 146 import std.conv: to; 147 148 return getBoneByIndex(idx.to!int); 149 } 150 151 spSlot* getSlotByIndex(int idx) 152 { 153 assert(idx >= 0); 154 assert(idx < sp_skeleton.slotsCount); 155 156 return sp_skeleton.slots[idx]; 157 } 158 159 /// @param attachmentName May be null 160 package spAttachment* getAttachmentForSlotIndex(int slotIdx, string attachmentName) 161 { 162 spAttachment* ret = spSkeleton_getAttachmentForSlotIndex(sp_skeleton, slotIdx, attachmentName.toStringz); 163 164 enforce(ret !is null, "Slot or attachment is not found"); 165 166 return ret; 167 } 168 169 /// @param attachmentName May be null 170 void setAttachment(string slotName, string attachmentName) 171 { 172 auto ret = spSkeleton_setAttachment(sp_skeleton, slotName.toStringz, attachmentName.toStringz); 173 174 enforce(ret != 0, "Slot or attachment is not found"); 175 } 176 177 void skin(spSkin* skin) 178 { 179 spSkeleton_setSkin(sp_skeleton, skin); 180 } 181 182 spBone* findBoneByAttachment(ATT)(in ATT* anyAttachment) 183 //~ if(is(ATT == spAttachment) || is(ATT == spBoundingBoxAttachment)) 184 { 185 auto att = cast(spAttachment*) anyAttachment; 186 187 foreach(i; 0 .. sp_skeleton.slotsCount) 188 if(sp_skeleton.slots[i].attachment == att) 189 return sp_skeleton.slots[i].bone; 190 191 return null; 192 } 193 } 194 195 package extern(C): 196 197 enum spTransformMode 198 { 199 NORMAL, 200 ONLYTRANSLATION, 201 NOROTATIONORREFLECTION, 202 NOSCALE, 203 NOSCALEORREFLECTION 204 }; 205 206 struct spBoneData 207 { 208 const int index; 209 const (char*) name; 210 const (spBoneData*) parent; 211 float length=0; 212 float x=0, y=0, rotation=0, scaleX=0, scaleY=0, shearX=0, shearY=0; 213 spTransformMode transformMode = spTransformMode.NORMAL; 214 215 string toString() const 216 { 217 import std.conv: to; 218 219 return 220 "length="~length.to!string~"\n"~ 221 "rotation="~rotation.to!string~"\n"~ 222 "scaleX="~scaleX.to!string~"\n"~ 223 "scaleY="~scaleY.to!string~"\n"~ 224 "shearX="~shearX.to!string~"\n"~ 225 "shearY="~shearY.to!string~"\n"~ 226 "x="~x.to!string~"\n"~ 227 "y="~y.to!string; 228 } 229 } 230 231 public struct spBone 232 { 233 const(spBoneData)* data; 234 const(spSkeleton)* skeleton; 235 spBone* parent; 236 int childrenCount; 237 const(spBone)** children; 238 float x=0, y=0, rotation=0, scaleX=0, scaleY=0, shearX=0, shearY=0; 239 float ax=0, ay=0, arotation=0, ascaleX=0, ascaleY=0, ashearX=0, ashearY=0; 240 int /*bool*/ appliedValid; 241 242 float a=0, b=0, worldX=0; 243 float c=0, d=0, worldY=0; 244 245 int/*bool*/ sorted; 246 247 float worldRotation() const 248 { 249 float ret = 0; 250 const(spBone)* curr = &this; 251 252 do 253 { 254 ret += curr.rotation; 255 curr = curr.parent; 256 } 257 while(curr !is null); 258 259 return ret; 260 } 261 262 /// Converts a skeleton-space position into a bone local position 263 void worldToLocal(float worldX, float worldY, out float localX, out float localY) 264 { 265 spBone_worldToLocal(&this, worldX, worldY, &localX, &localY); 266 } 267 268 void updateAppliedTransform() 269 { 270 spBone_updateAppliedTransform(&this); 271 } 272 273 string toString() const 274 { 275 import std.conv: to; 276 277 return 278 "x="~x.to!string~"\n"~ 279 "y="~y.to!string~"\n"~ 280 "ax="~ax.to!string~"\n"~ 281 "ay="~ay.to!string~"\n"~ 282 "shearX="~shearX.to!string~"\n"~ 283 "shearY="~shearY.to!string~"\n"~ 284 "rotation="~rotation.to!string~"\n"~ 285 "arotation="~arotation.to!string~"\n"~ 286 "a="~a.to!string~"\n"~ 287 "b="~b.to!string~"\n"~ 288 "c="~c.to!string~"\n"~ 289 "d="~d.to!string~"\n"~ 290 "worldX="~worldX.to!string~"\n"~ 291 "worldY="~worldY.to!string; 292 } 293 } 294 295 struct spSkin; 296 struct spEventData; 297 struct spAnimation; 298 struct spIkConstraint; 299 struct spIkConstraintData; 300 struct spTransformConstraint; 301 struct spTransformConstraintData; 302 struct spPathConstraint; 303 struct spPathConstraintData; 304 305 struct spSkeletonData 306 { 307 const (char)* __version; 308 const (char)* hash; 309 float width, height; 310 311 int bonesCount; 312 spBoneData** bones; 313 314 int slotsCount; 315 spSlotData** slots; 316 317 int skinsCount; 318 spSkin** skins; 319 spSkin* defaultSkin; 320 321 int eventsCount; 322 spEventData** events; 323 324 int animationsCount; 325 spAnimation** animations; 326 327 int ikConstraintsCount; 328 spIkConstraintData** ikConstraints; 329 330 int transformConstraintsCount; 331 spTransformConstraintData** transformConstraints; 332 333 int pathConstraintsCount; 334 spPathConstraintData** pathConstraints; 335 } 336 337 public enum spAttachmentType 338 { 339 REGION, 340 BOUNDING_BOX, 341 MESH, 342 LINKED_MESH, 343 PATH, 344 ATTACHMENT_POINT, 345 ATTACHMENT_CLIPPING, 346 SKELETON = 1000 /// Unofficial type 347 } 348 349 struct spAttachment 350 { 351 const(char)* name; 352 spAttachmentType type = spAttachmentType.REGION; 353 void* vtable; 354 void* attachmentLoader; 355 } 356 357 enum spBlendMode 358 { 359 NORMAL, 360 ADDITIVE, 361 MULTIPLY, 362 SCREEN 363 } 364 365 struct spSlotData 366 { 367 const int index; 368 const(char*) name; 369 const(spBoneData*) boneData; 370 const(char*) attachmentName; 371 spColor color; 372 spColor* darkColor; 373 spBlendMode blendMode = spBlendMode.NORMAL; 374 } 375 376 public struct spSlot 377 { 378 const(spSlotData)* data; 379 spBone* bone; 380 spColor color; 381 spColor* darkColor; 382 const(spAttachment)* attachment; 383 384 int attachmentVerticesCapacity; 385 int attachmentVerticesCount; 386 float* attachmentVertices; 387 } 388 389 struct spSkeleton 390 { 391 const spSkeletonData* data; 392 393 int bonesCount; 394 spBone** bones; 395 spBone* root; 396 397 int slotsCount; 398 spSlot** slots; 399 spSlot** drawOrder; 400 401 int ikConstraintsCount; 402 spIkConstraint** ikConstraints; 403 404 int transformConstraintsCount; 405 spTransformConstraint** transformConstraints; 406 407 int pathConstraintsCount; 408 spPathConstraint** pathConstraints; 409 410 const spSkin* skin; 411 spColor color; 412 float time=0; 413 int/*bool*/flipX=0, flipY=0; 414 float x=0, y=0; 415 } 416 417 void spSkeleton_update (spSkeleton* self, float deltaTime); 418 419 void spSkeleton_updateWorldTransform (const(spSkeleton)* self); 420 421 private: 422 423 struct spSkeletonJson; 424 425 spSkeletonJson* spSkeletonJson_create(spAtlas* atlas); 426 void spSkeletonJson_dispose(spSkeletonJson* json); 427 428 spSkeletonData* spSkeletonJson_readSkeletonDataFile(spSkeletonJson*, const(char)* path); 429 void spSkeletonData_dispose (spSkeletonData* self); 430 spSkin* spSkeletonData_findSkin (const(spSkeletonData)* self, const(char)* skinName); 431 int spSkeletonData_findBoneIndex (const(spSkeletonData)* self, const(char)* boneName); 432 int spSkeletonData_findSlotIndex (const(spSkeletonData)* self, const(char)* slotName); 433 434 spSkeleton* spSkeleton_create (spSkeletonData* data); 435 void spSkeleton_dispose (spSkeleton* self); 436 437 void spSkeleton_setToSetupPose (const(spSkeleton)* self); 438 void spSkeleton_setSkin (spSkeleton* self, spSkin* skin); 439 spAttachment* spSkeleton_getAttachmentForSlotIndex (const(spSkeleton)* self, int slotIndex, const(char)* attachmentName); 440 int spSkeleton_setAttachment (spSkeleton* self, const(char)* slotName, const(char)* attachmentName); 441 442 void spBone_worldToLocal (spBone* self, float worldX, float worldY, float* localX, float* localY); 443 444 void spBone_updateAppliedTransform (spBone* self);