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);