sokol_spine_test.c 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  1. //------------------------------------------------------------------------------
  2. // sokol_spine_test.c
  3. //------------------------------------------------------------------------------
  4. #include "sokol_gfx.h"
  5. #define SOKOL_SPINE_IMPL
  6. #include "spine/spine.h"
  7. #include "sokol_spine.h"
  8. #include "utest.h"
  9. #define T(b) EXPECT_TRUE(b)
  10. static sspine_log_item last_logitem = SSPINE_LOGITEM_OK;
  11. static void log_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) {
  12. (void)tag; (void)log_level; (void)message; (void)line_nr; (void)filename; (void)user_data;
  13. last_logitem = log_item;
  14. }
  15. static void init(void) {
  16. last_logitem = SSPINE_LOGITEM_OK;
  17. sg_setup(&(sg_desc){0});
  18. sspine_setup(&(sspine_desc){ .logger = { .func = log_func } });
  19. }
  20. static void init_with_desc(const sspine_desc* desc) {
  21. last_logitem = SSPINE_LOGITEM_OK;
  22. sspine_desc desc1 = *desc;
  23. desc1.logger.func = log_func;
  24. sg_setup(&(sg_desc){0});
  25. sspine_setup(&desc1);
  26. }
  27. static void shutdown(void) {
  28. sspine_shutdown();
  29. sg_shutdown();
  30. }
  31. // NOTE: this guarantees that the data is zero terminated because the loaded data
  32. // might either be binary or text (the zero sentinel is NOT counted in the returned size)
  33. static sspine_range load_data(const char* path) {
  34. assert(path);
  35. FILE* fp = fopen(path, "rb");
  36. assert(fp);
  37. fseek(fp, 0, SEEK_END);
  38. const size_t size = (size_t)ftell(fp);
  39. fseek(fp, 0, SEEK_SET);
  40. // room for terminating zero
  41. const size_t alloc_size = size + 1;
  42. uint8_t* ptr = (uint8_t*)malloc(alloc_size);
  43. memset(ptr, 0, alloc_size);
  44. // NOTE: GCC warns if result of fread() is ignored
  45. size_t num_bytes = fread(ptr, size, 1, fp);
  46. (void)num_bytes;
  47. fclose(fp);
  48. return (sspine_range) { .ptr = ptr, .size = size };
  49. }
  50. static void free_data(sspine_range r) {
  51. free((void*)r.ptr);
  52. }
  53. static sspine_atlas create_atlas(void) {
  54. sspine_range atlas_data = load_data("spineboy.atlas");
  55. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){
  56. .data = atlas_data
  57. });
  58. free_data(atlas_data);
  59. return atlas;
  60. }
  61. static sspine_skeleton create_skeleton_json(sspine_atlas atlas) {
  62. sspine_range skeleton_json_data = load_data("spineboy-pro.json");
  63. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
  64. .atlas = atlas,
  65. .json_data = (const char*)skeleton_json_data.ptr
  66. });
  67. free_data(skeleton_json_data);
  68. return skeleton;
  69. }
  70. static sspine_skeleton create_skeleton_binary(sspine_atlas atlas) {
  71. sspine_range skeleton_binary_data = load_data("spineboy-pro.skel");
  72. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
  73. .atlas = atlas,
  74. .binary_data = skeleton_binary_data
  75. });
  76. free_data(skeleton_binary_data);
  77. return skeleton;
  78. }
  79. static sspine_skeleton create_skeleton(void) {
  80. return create_skeleton_json(create_atlas());
  81. }
  82. static sspine_instance create_instance(void) {
  83. return sspine_make_instance(&(sspine_instance_desc){
  84. .skeleton = create_skeleton(),
  85. });
  86. }
  87. UTEST(sokol_spine, default_init_shutdown) {
  88. // FIXME!
  89. T(true);
  90. }
  91. UTEST(sokol_spine, atlas_pool_exhausted) {
  92. init_with_desc(&(sspine_desc){
  93. .atlas_pool_size = 4,
  94. });
  95. for (int i = 0; i < 4; i++) {
  96. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
  97. T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED);
  98. T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
  99. }
  100. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
  101. T(SSPINE_INVALID_ID == atlas.id);
  102. T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_INVALID);
  103. T(last_logitem == SSPINE_LOGITEM_ATLAS_POOL_EXHAUSTED);
  104. shutdown();
  105. }
  106. UTEST(sokol_spine, make_destroy_atlas_ok) {
  107. init();
  108. sspine_atlas atlas = create_atlas();
  109. T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_VALID);
  110. T(sspine_atlas_valid(atlas));
  111. sspine_destroy_atlas(atlas);
  112. T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_INVALID);
  113. T(!sspine_atlas_valid(atlas))
  114. shutdown();
  115. }
  116. UTEST(sokol_spine, make_atlas_fail_no_data) {
  117. init();
  118. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
  119. T(atlas.id != SSPINE_INVALID_ID);
  120. T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
  121. T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED);
  122. T(!sspine_atlas_valid(atlas));
  123. shutdown();
  124. }
  125. // an invalid atlas must return zero number of images
  126. UTEST(sokol_spine, failed_atlas_no_images) {
  127. init();
  128. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
  129. T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
  130. T(atlas.id != SSPINE_INVALID_ID);
  131. T(!sspine_atlas_valid(atlas));
  132. T(sspine_num_images(atlas) == 0);
  133. shutdown();
  134. }
  135. // NOTE: spine-c doesn't detect wrong/corrupt atlas file data, so we can't test for that
  136. UTEST(sokol_spine, image_valid) {
  137. init();
  138. sspine_atlas atlas = create_atlas();
  139. T(sspine_image_valid(sspine_image_by_index(atlas, 0)));
  140. T(!sspine_image_valid(sspine_image_by_index(atlas, 1)));
  141. T(!sspine_image_valid(sspine_image_by_index(atlas, -1)));
  142. sspine_destroy_atlas(atlas);
  143. T(!sspine_image_valid(sspine_image_by_index(atlas, 0)));
  144. shutdown();
  145. }
  146. UTEST(sokol_spine, atlas_image_info) {
  147. init();
  148. sspine_atlas atlas = create_atlas();
  149. T(sspine_atlas_valid(atlas));
  150. T(sspine_num_images(atlas) == 1);
  151. const sspine_image_info img_info = sspine_get_image_info(sspine_image_by_index(atlas, 0));
  152. T(img_info.valid);
  153. T(img_info.sgimage.id != SG_INVALID_ID);
  154. T(sg_query_image_state(img_info.sgimage) == SG_RESOURCESTATE_ALLOC);
  155. T(strcmp(img_info.filename.cstr, "spineboy.png") == 0);
  156. T(img_info.min_filter == SG_FILTER_LINEAR);
  157. T(img_info.mag_filter == SG_FILTER_LINEAR);
  158. T(img_info.wrap_u == SG_WRAP_CLAMP_TO_EDGE);
  159. T(img_info.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
  160. T(img_info.width == 1024);
  161. T(img_info.height == 256);
  162. T(img_info.premul_alpha == false);
  163. shutdown();
  164. }
  165. UTEST(sokol_spine, atlas_with_overrides) {
  166. init();
  167. sspine_range atlas_data = load_data("spineboy.atlas");
  168. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){
  169. .data = atlas_data,
  170. .override = {
  171. .min_filter = SG_FILTER_NEAREST,
  172. .mag_filter = SG_FILTER_NEAREST,
  173. .mipmap_filter = SG_FILTER_LINEAR,
  174. .wrap_u = SG_WRAP_REPEAT,
  175. .wrap_v = SG_WRAP_CLAMP_TO_EDGE,
  176. .premul_alpha_enabled = true,
  177. }
  178. });
  179. T(sspine_atlas_valid(atlas));
  180. T(sspine_num_images(atlas) == 1);
  181. const sspine_image_info img_info = sspine_get_image_info(sspine_image_by_index(atlas, 0));
  182. T(img_info.valid);
  183. T(img_info.sgimage.id != SG_INVALID_ID);
  184. T(sg_query_image_state(img_info.sgimage) == SG_RESOURCESTATE_ALLOC);
  185. T(strcmp(img_info.filename.cstr, "spineboy.png") == 0);
  186. T(img_info.min_filter == SG_FILTER_NEAREST);
  187. T(img_info.mag_filter == SG_FILTER_NEAREST);
  188. T(img_info.mipmap_filter == SG_FILTER_LINEAR);
  189. T(img_info.wrap_u == SG_WRAP_REPEAT);
  190. T(img_info.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
  191. T(img_info.width == 1024);
  192. T(img_info.height == 256);
  193. T(img_info.premul_alpha == true);
  194. shutdown();
  195. }
  196. UTEST(sokol_spine, skeleton_pool_exhausted) {
  197. init_with_desc(&(sspine_desc){
  198. .skeleton_pool_size = 4
  199. });
  200. for (int i = 0; i < 4; i++) {
  201. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0});
  202. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
  203. T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA);
  204. }
  205. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0});
  206. T(SSPINE_INVALID_ID == skeleton.id);
  207. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
  208. T(last_logitem == SSPINE_LOGITEM_SKELETON_POOL_EXHAUSTED);
  209. shutdown();
  210. }
  211. UTEST(sokol_spine, make_destroy_skeleton_json_ok) {
  212. init();
  213. sspine_skeleton skeleton = create_skeleton_json(create_atlas());
  214. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_VALID);
  215. T(sspine_skeleton_valid(skeleton));
  216. sspine_destroy_skeleton(skeleton);
  217. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
  218. T(!sspine_skeleton_valid(skeleton));
  219. shutdown();
  220. }
  221. UTEST(sokol_spine, make_destroy_skeleton_binary_ok) {
  222. init();
  223. sspine_skeleton skeleton = create_skeleton_binary(create_atlas());
  224. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_VALID);
  225. T(sspine_skeleton_valid(skeleton));
  226. sspine_destroy_skeleton(skeleton);
  227. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
  228. T(!sspine_skeleton_valid(skeleton));
  229. shutdown();
  230. }
  231. UTEST(sokol_spine, make_skeleton_fail_no_data) {
  232. init();
  233. sspine_atlas atlas = create_atlas();
  234. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
  235. .atlas = atlas
  236. });
  237. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
  238. T(!sspine_skeleton_valid(skeleton));
  239. T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA);
  240. shutdown();
  241. }
  242. UTEST(sokol_spine, make_skeleton_fail_no_atlas) {
  243. init();
  244. sspine_range skeleton_json_data = load_data("spineboy-pro.json");
  245. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
  246. .json_data = (const char*)skeleton_json_data.ptr
  247. });
  248. free_data(skeleton_json_data);
  249. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
  250. T(!sspine_skeleton_valid(skeleton));
  251. T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_ATLAS);
  252. shutdown();
  253. }
  254. UTEST(sokol_spine, make_skeleton_fail_with_failed_atlas) {
  255. init();
  256. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){0});
  257. T(last_logitem == SSPINE_LOGITEM_ATLAS_DESC_NO_DATA);
  258. T(sspine_get_atlas_resource_state(atlas) == SSPINE_RESOURCESTATE_FAILED);
  259. sspine_skeleton skeleton = create_skeleton_json(atlas);
  260. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
  261. T(!sspine_skeleton_valid(skeleton));
  262. T(last_logitem == SSPINE_LOGITEM_SKELETON_ATLAS_NOT_VALID);
  263. shutdown();
  264. }
  265. UTEST(sokol_spine, make_skeleton_json_fail_corrupt_data) {
  266. init();
  267. sspine_atlas atlas = create_atlas();
  268. const char* invalid_json_data = "This is not valid JSON!";
  269. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
  270. .atlas = atlas,
  271. .json_data = (const char*)invalid_json_data,
  272. });
  273. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
  274. T(last_logitem == SSPINE_LOGITEM_CREATE_SKELETON_DATA_FROM_JSON_FAILED);
  275. sspine_destroy_skeleton(skeleton);
  276. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
  277. shutdown();
  278. }
  279. // FIXME: this crashes the spine-c runtime
  280. /*
  281. UTEST(sokol_spine, make_skeleton_binary_fail_corrupt_data) {
  282. init();
  283. sspine_atlas atlas = create_atlas();
  284. uint8_t invalid_binary_data[] = { 0x23, 0x63, 0x11, 0xFF };
  285. sspine_skeleton skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){
  286. .atlas = atlas,
  287. .binary_data = { .ptr = invalid_binary_data, .size = sizeof(invalid_binary_data) }
  288. });
  289. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_FAILED);
  290. sspine_destroy_skeleton(skeleton);
  291. T(sspine_get_skeleton_resource_state(skeleton) == SSPINE_RESOURCESTATE_INVALID);
  292. shutdown();
  293. }
  294. */
  295. UTEST(sokol_spine, instance_pool_exhausted) {
  296. init_with_desc(&(sspine_desc){
  297. .instance_pool_size = 4
  298. });
  299. for (int i = 0; i < 4; i++) {
  300. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0});
  301. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
  302. T(last_logitem == SSPINE_LOGITEM_INSTANCE_DESC_NO_SKELETON);
  303. }
  304. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0});
  305. T(SSPINE_INVALID_ID == instance.id);
  306. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID);
  307. T(last_logitem == SSPINE_LOGITEM_INSTANCE_POOL_EXHAUSTED);
  308. shutdown();
  309. }
  310. UTEST(sokol_spine, make_destroy_instance_ok) {
  311. init();
  312. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
  313. .skeleton = create_skeleton_json(create_atlas())
  314. });
  315. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_VALID);
  316. T(sspine_instance_valid(instance));
  317. sspine_destroy_instance(instance);
  318. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID);
  319. T(!sspine_instance_valid(instance));
  320. shutdown();
  321. }
  322. UTEST(sokol_spine, make_instance_fail_no_skeleton) {
  323. init();
  324. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){0});
  325. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
  326. T(last_logitem == SSPINE_LOGITEM_INSTANCE_DESC_NO_SKELETON);
  327. sspine_destroy_instance(instance);
  328. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_INVALID);
  329. shutdown();
  330. }
  331. UTEST(sokol_spine, make_instance_fail_with_failed_skeleton) {
  332. init();
  333. sspine_skeleton failed_skeleton = sspine_make_skeleton(&(sspine_skeleton_desc){0});
  334. T(last_logitem == SSPINE_LOGITEM_SKELETON_DESC_NO_DATA);
  335. T(sspine_get_skeleton_resource_state(failed_skeleton) == SSPINE_RESOURCESTATE_FAILED);
  336. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
  337. .skeleton = failed_skeleton
  338. });
  339. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
  340. T(last_logitem == SSPINE_LOGITEM_INSTANCE_SKELETON_NOT_VALID);
  341. shutdown();
  342. }
  343. UTEST(sokol_spine, make_instance_fail_with_destroyed_atlas) {
  344. init();
  345. sspine_atlas atlas = create_atlas();
  346. T(sspine_atlas_valid(atlas));
  347. sspine_skeleton skeleton = create_skeleton_json(atlas);
  348. T(sspine_skeleton_valid(skeleton));
  349. sspine_destroy_atlas(atlas);
  350. T(!sspine_atlas_valid(atlas));
  351. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
  352. .skeleton = skeleton
  353. });
  354. T(sspine_get_instance_resource_state(instance) == SSPINE_RESOURCESTATE_FAILED);
  355. T(last_logitem == SSPINE_LOGITEM_INSTANCE_ATLAS_NOT_VALID);
  356. shutdown();
  357. }
  358. UTEST(sokol_spine, get_skeleton_atlas) {
  359. init();
  360. sspine_atlas atlas = create_atlas();
  361. sspine_skeleton skeleton = create_skeleton_json(atlas);
  362. T(sspine_get_skeleton_atlas(skeleton).id == atlas.id);
  363. sspine_destroy_skeleton(skeleton);
  364. T(sspine_get_skeleton_atlas(skeleton).id == SSPINE_INVALID_ID);
  365. shutdown();
  366. }
  367. UTEST(sokol_spine, get_instance_skeleton) {
  368. init();
  369. sspine_atlas atlas = create_atlas();
  370. sspine_skeleton skeleton = create_skeleton_json(atlas);
  371. sspine_instance instance = sspine_make_instance(&(sspine_instance_desc){
  372. .skeleton = skeleton
  373. });
  374. T(sspine_get_instance_skeleton(instance).id == skeleton.id);
  375. sspine_destroy_instance(instance);
  376. T(sspine_get_instance_skeleton(instance).id == SSPINE_INVALID_ID);
  377. shutdown();
  378. }
  379. UTEST(sokol_spine, set_get_position) {
  380. init();
  381. sspine_instance instance = create_instance();
  382. sspine_set_position(instance, (sspine_vec2){ .x=1.0f, .y=2.0f });
  383. const sspine_vec2 pos = sspine_get_position(instance);
  384. T(pos.x == 1.0f);
  385. T(pos.y == 2.0f);
  386. shutdown();
  387. }
  388. UTEST(sokol_spine, set_get_position_destroyed_instance) {
  389. init();
  390. sspine_instance instance = create_instance();
  391. sspine_set_position(instance, (sspine_vec2){ .x=1.0f, .y=2.0f });
  392. sspine_destroy_instance(instance);
  393. const sspine_vec2 pos = sspine_get_position(instance);
  394. T(pos.x == 0.0f);
  395. T(pos.y == 0.0f);
  396. shutdown();
  397. }
  398. UTEST(sokol_spine, set_get_scale) {
  399. init();
  400. sspine_instance instance = create_instance();
  401. sspine_set_scale(instance, (sspine_vec2){ .x=2.0f, .y=3.0f });
  402. const sspine_vec2 scale = sspine_get_scale(instance);
  403. T(scale.x == 2.0f);
  404. T(scale.y == 3.0f);
  405. shutdown();
  406. }
  407. UTEST(sokol_spine, set_get_scale_destroyed_instance) {
  408. init();
  409. sspine_instance instance = create_instance();
  410. sspine_set_scale(instance, (sspine_vec2){ .x=2.0f, .y=3.0f });
  411. sspine_destroy_instance(instance);
  412. const sspine_vec2 scale = sspine_get_scale(instance);
  413. T(scale.x == 0.0f);
  414. T(scale.y == 0.0f);
  415. shutdown();
  416. }
  417. UTEST(sokol_spine, set_get_color) {
  418. init();
  419. sspine_instance instance = create_instance();
  420. sspine_set_color(instance, (sspine_color) { .r=1.0f, .g=2.0f, .b=3.0f, .a=4.0f });
  421. const sspine_color color = sspine_get_color(instance);
  422. T(color.r == 1.0f);
  423. T(color.g == 2.0f);
  424. T(color.b == 3.0f);
  425. T(color.a == 4.0f);
  426. shutdown();
  427. }
  428. UTEST(sokol_spine, set_get_color_destroyed_instance) {
  429. init();
  430. sspine_instance instance = create_instance();
  431. sspine_set_color(instance, (sspine_color) { .r=1.0f, .g=2.0f, .b=3.0f, .a=4.0f });
  432. sspine_destroy_instance(instance);
  433. const sspine_color color = sspine_get_color(instance);
  434. T(color.r == 0.0f);
  435. T(color.g == 0.0f);
  436. T(color.b == 0.0f);
  437. T(color.a == 0.0f);
  438. shutdown();
  439. }
  440. UTEST(sokol_spine, anim_by_name) {
  441. init();
  442. sspine_skeleton skeleton = create_skeleton();
  443. sspine_anim a0 = sspine_anim_by_name(skeleton, "hoverboard");
  444. T((a0.skeleton_id == skeleton.id) && (a0.index == 2));
  445. sspine_anim a1 = sspine_anim_by_name(skeleton, "bla");
  446. T((a1.skeleton_id == 0) && (a1.index == 0));
  447. shutdown();
  448. }
  449. UTEST(sokol_spine, anim_by_name_destroyed_instance) {
  450. init();
  451. sspine_skeleton skeleton = create_skeleton();
  452. sspine_destroy_skeleton(skeleton);
  453. sspine_anim a0 = sspine_anim_by_name(skeleton, "hoverboard");
  454. T((a0.skeleton_id == 0) && (a0.index == 0));
  455. shutdown();
  456. }
  457. UTEST(sokol_spine, anim_valid) {
  458. init();
  459. sspine_skeleton skeleton = create_skeleton();
  460. T(sspine_anim_valid(sspine_anim_by_index(skeleton, 0)));
  461. T(sspine_anim_valid(sspine_anim_by_index(skeleton, 10)));
  462. T(!sspine_anim_valid(sspine_anim_by_index(skeleton, -1)));
  463. T(!sspine_anim_valid(sspine_anim_by_index(skeleton, 11)));
  464. sspine_destroy_skeleton(skeleton);
  465. T(!sspine_anim_valid(sspine_anim_by_index(skeleton, 0)));
  466. shutdown();
  467. }
  468. UTEST(sokol_spine, anim_equal) {
  469. init();
  470. T(sspine_anim_equal((sspine_anim){ 1, 2 }, (sspine_anim){ 1, 2 }));
  471. T(!sspine_anim_equal((sspine_anim){ 2, 2 }, (sspine_anim){ 1, 2 }));
  472. T(!sspine_anim_equal((sspine_anim){ 1, 3 }, (sspine_anim){ 1, 2 }));
  473. shutdown();
  474. }
  475. UTEST(sokol_spine, num_anims) {
  476. init();
  477. sspine_skeleton skeleton = create_skeleton();
  478. T(sspine_num_anims(skeleton) == 11);
  479. sspine_destroy_skeleton(skeleton);
  480. T(sspine_num_anims(skeleton) == 0);
  481. shutdown();
  482. }
  483. UTEST(sokol_spine, get_anim_info) {
  484. init();
  485. sspine_skeleton skeleton = create_skeleton();
  486. sspine_anim anim = sspine_anim_by_name(skeleton, "hoverboard");
  487. const sspine_anim_info info = sspine_get_anim_info(anim);
  488. T(info.valid);
  489. T(info.index == 2);
  490. T(strcmp(info.name.cstr, "hoverboard") == 0);
  491. T(info.duration == 1.0f);
  492. shutdown();
  493. }
  494. UTEST(sokol_spine, get_anim_info_destroyed_skeleton) {
  495. init();
  496. sspine_skeleton skeleton = create_skeleton();
  497. sspine_anim anim = sspine_anim_by_name(skeleton, "hoverboard");
  498. sspine_destroy_skeleton(skeleton);
  499. const sspine_anim_info info = sspine_get_anim_info(anim);
  500. T(!info.valid);
  501. shutdown();
  502. }
  503. UTEST(sokol_spine, get_anim_info_invalid_index) {
  504. init();
  505. sspine_skeleton skeleton = create_skeleton();
  506. const sspine_anim_info i0 = sspine_get_anim_info(sspine_anim_by_index(skeleton, -1));
  507. T(!i0.valid);
  508. T(!i0.name.valid);
  509. const sspine_anim_info i1 = sspine_get_anim_info(sspine_anim_by_index(skeleton, 1234));
  510. T(!i1.valid);
  511. T(!i1.name.valid);
  512. shutdown();
  513. }
  514. UTEST(sokol_spine, atlas_page_valid) {
  515. init();
  516. sspine_atlas atlas = create_atlas();
  517. T(sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 0)));
  518. T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, -1)));
  519. T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 1)));
  520. sspine_destroy_atlas(atlas);
  521. T(!sspine_atlas_page_valid(sspine_atlas_page_by_index(atlas, 0)));
  522. shutdown();
  523. }
  524. UTEST(sokol_spine, num_atlas_pages) {
  525. init();
  526. sspine_atlas atlas = create_atlas();
  527. T(sspine_num_atlas_pages(atlas) == 1);
  528. sspine_destroy_atlas(atlas);
  529. T(sspine_num_atlas_pages(atlas) == 0);
  530. shutdown();
  531. }
  532. UTEST(sokol_spine, get_atlas_page_info) {
  533. init();
  534. sspine_atlas atlas = create_atlas();
  535. const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0));
  536. T(info.valid);
  537. T(info.atlas.id == atlas.id);
  538. T(info.image.valid);
  539. T(info.image.sgimage.id != SG_INVALID_ID);
  540. T(sg_query_image_state(info.image.sgimage) == SG_RESOURCESTATE_ALLOC);
  541. T(strcmp(info.image.filename.cstr, "spineboy.png") == 0);
  542. T(info.image.min_filter == SG_FILTER_LINEAR);
  543. T(info.image.mag_filter == SG_FILTER_LINEAR);
  544. T(info.image.wrap_u == SG_WRAP_CLAMP_TO_EDGE);
  545. T(info.image.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
  546. T(info.image.width == 1024);
  547. T(info.image.height == 256);
  548. T(info.image.premul_alpha == false);
  549. T(info.overrides.min_filter == _SG_FILTER_DEFAULT);
  550. T(info.overrides.mag_filter == _SG_FILTER_DEFAULT);
  551. T(info.overrides.wrap_u == _SG_WRAP_DEFAULT);
  552. T(info.overrides.wrap_v == _SG_WRAP_DEFAULT);
  553. T(!info.overrides.premul_alpha_enabled);
  554. T(!info.overrides.premul_alpha_disabled);
  555. shutdown();
  556. }
  557. UTEST(sokol_spine, get_atlas_page_info_destroyed_atlas) {
  558. init();
  559. sspine_atlas atlas = create_atlas();
  560. sspine_destroy_atlas(atlas);
  561. const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0));
  562. T(!info.valid);
  563. T(info.atlas.id == SSPINE_INVALID_ID);
  564. shutdown();
  565. }
  566. UTEST(sokol_spine, get_atlas_page_info_invalid_index) {
  567. init();
  568. sspine_atlas atlas = create_atlas();
  569. sspine_destroy_atlas(atlas);
  570. const sspine_atlas_page_info i0 = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, -1));
  571. T(!i0.valid);
  572. T(i0.atlas.id == SSPINE_INVALID_ID);
  573. const sspine_atlas_page_info i1 = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 1234));
  574. T(!i0.valid);
  575. T(i1.atlas.id == SSPINE_INVALID_ID);
  576. shutdown();
  577. }
  578. UTEST(sokol_spine, atlas_get_atlas_page_info_with_overrides) {
  579. init();
  580. sspine_range atlas_data = load_data("spineboy.atlas");
  581. sspine_atlas atlas = sspine_make_atlas(&(sspine_atlas_desc){
  582. .data = atlas_data,
  583. .override = {
  584. .min_filter = SG_FILTER_NEAREST,
  585. .mag_filter = SG_FILTER_NEAREST,
  586. .mipmap_filter = SG_FILTER_NEAREST,
  587. .wrap_u = SG_WRAP_REPEAT,
  588. .wrap_v = SG_WRAP_CLAMP_TO_EDGE,
  589. .premul_alpha_enabled = true,
  590. }
  591. });
  592. const sspine_atlas_page_info info = sspine_get_atlas_page_info(sspine_atlas_page_by_index(atlas, 0));
  593. T(info.valid);
  594. T(info.atlas.id == atlas.id);
  595. T(info.image.valid);
  596. T(info.image.sgimage.id != SG_INVALID_ID);
  597. T(sg_query_image_state(info.image.sgimage) == SG_RESOURCESTATE_ALLOC);
  598. T(strcmp(info.image.filename.cstr, "spineboy.png") == 0);
  599. T(info.image.min_filter == SG_FILTER_LINEAR);
  600. T(info.image.mag_filter == SG_FILTER_LINEAR);
  601. T(info.image.mipmap_filter == SG_FILTER_NEAREST);
  602. T(info.image.wrap_u == SG_WRAP_CLAMP_TO_EDGE);
  603. T(info.image.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
  604. T(info.image.width == 1024);
  605. T(info.image.height == 256);
  606. T(info.image.premul_alpha == true); // FIXME: hmm, this is actually inconsistent
  607. T(info.overrides.min_filter == SG_FILTER_NEAREST);
  608. T(info.overrides.mag_filter == SG_FILTER_NEAREST);
  609. T(info.overrides.mipmap_filter == SG_FILTER_NEAREST);
  610. T(info.overrides.wrap_u == SG_WRAP_REPEAT);
  611. T(info.overrides.wrap_v == SG_WRAP_CLAMP_TO_EDGE);
  612. T(info.overrides.premul_alpha_enabled);
  613. T(!info.overrides.premul_alpha_disabled);
  614. shutdown();
  615. }
  616. UTEST(sokol_spine, bone_by_name) {
  617. init();
  618. sspine_skeleton skeleton = create_skeleton();
  619. sspine_bone b0 = sspine_bone_by_name(skeleton, "crosshair");
  620. T((b0.skeleton_id == skeleton.id) && (b0.index == 2));
  621. sspine_bone b1 = sspine_bone_by_name(skeleton, "blablub");
  622. T((b1.skeleton_id == 0) && (b1.index == 0));
  623. shutdown();
  624. }
  625. UTEST(sokol_spine, bone_by_name_destroyed_skeleton) {
  626. init();
  627. sspine_skeleton skeleton = create_skeleton();
  628. sspine_destroy_skeleton(skeleton);
  629. sspine_bone b0 = sspine_bone_by_name(skeleton, "crosshair");
  630. T((b0.skeleton_id == 0) && (b0.index == 0));
  631. shutdown();
  632. }
  633. UTEST(sokol_spine, bone_valid) {
  634. init();
  635. sspine_skeleton skeleton = create_skeleton();
  636. T(sspine_bone_valid(sspine_bone_by_index(skeleton, 0)));
  637. T(sspine_bone_valid(sspine_bone_by_index(skeleton, 66)));
  638. T(!sspine_bone_valid(sspine_bone_by_index(skeleton, -1)));
  639. T(!sspine_bone_valid(sspine_bone_by_index(skeleton, 67)));
  640. sspine_destroy_skeleton(skeleton);
  641. T(!sspine_bone_valid(sspine_bone_by_index(skeleton, 0)));
  642. shutdown();
  643. }
  644. UTEST(sokol_spine, bone_equal) {
  645. init();
  646. T(sspine_bone_equal((sspine_bone){ 1, 2 }, (sspine_bone){ 1, 2 }));
  647. T(!sspine_bone_equal((sspine_bone){ 2, 2 }, (sspine_bone){ 1, 2 }));
  648. T(!sspine_bone_equal((sspine_bone){ 1, 3 }, (sspine_bone){ 1, 2 }));
  649. shutdown();
  650. }
  651. UTEST(sokol_spine, num_bones) {
  652. init();
  653. sspine_skeleton skeleton = create_skeleton();
  654. T(sspine_num_bones(skeleton) == 67);
  655. sspine_destroy_skeleton(skeleton);
  656. T(sspine_num_bones(skeleton) == 0);
  657. shutdown();
  658. }
  659. UTEST(sokol_spine, get_bone_info_root) {
  660. init();
  661. sspine_skeleton skeleton = create_skeleton();
  662. const sspine_bone_info info = sspine_get_bone_info(sspine_bone_by_name(skeleton, "root"));
  663. T(info.valid);
  664. T(info.index == 0);
  665. T((info.parent_bone.skeleton_id == 0) && (info.parent_bone.index == 0));
  666. T(strcmp(info.name.cstr, "root") == 0);
  667. T(info.length == 0.0f);
  668. T(info.pose.position.x == 0.0f);
  669. T(info.pose.position.y == 0.0f);
  670. T(info.pose.rotation == 0.05f);
  671. T(info.pose.scale.x == 1.0f);
  672. T(info.pose.scale.y == 1.0f);
  673. T(info.pose.shear.x == 0.0f);
  674. T(info.pose.shear.y == 0.0f);
  675. shutdown();
  676. }
  677. UTEST(sokol_spine, get_bone_info_parent_bone) {
  678. init();
  679. sspine_skeleton skeleton = create_skeleton();
  680. const sspine_bone_info info = sspine_get_bone_info(sspine_bone_by_name(skeleton, "rear-shin"));
  681. T(info.valid);
  682. T(info.index == 7);
  683. T((info.parent_bone.skeleton_id == skeleton.id) && (info.parent_bone.index == 6));
  684. shutdown();
  685. }
  686. UTEST(sokol_spine, get_bone_info_destroyed_skeleton) {
  687. init();
  688. sspine_skeleton skeleton = create_skeleton();
  689. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  690. sspine_destroy_skeleton(skeleton);
  691. const sspine_bone_info info = sspine_get_bone_info(bone);
  692. T(!info.valid);
  693. T(!info.name.valid);
  694. shutdown();
  695. }
  696. UTEST(sokol_spine, get_bone_info_invalid_index) {
  697. init();
  698. sspine_skeleton skeleton = create_skeleton();
  699. const sspine_bone_info i0 = sspine_get_bone_info(sspine_bone_by_index(skeleton, -1));
  700. T(!i0.valid);
  701. T(!i0.name.valid);
  702. const sspine_bone_info i1 = sspine_get_bone_info(sspine_bone_by_index(skeleton, 1234));
  703. T(!i1.valid);
  704. T(!i1.name.valid);
  705. shutdown();
  706. }
  707. UTEST(sokol_spine, set_get_bone_transform) {
  708. init();
  709. sspine_instance instance = create_instance();
  710. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  711. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  712. sspine_set_bone_transform(instance, bone, &(sspine_bone_transform){
  713. .position = { 1.0f, 2.0f },
  714. .rotation = 3.0f,
  715. .scale = { 4.0f, 5.0f },
  716. .shear = { 6.0f, 7.0f }
  717. });
  718. const sspine_bone_transform tform = sspine_get_bone_transform(instance, bone);
  719. T(tform.position.x == 1.0f);
  720. T(tform.position.y == 2.0f);
  721. T(tform.rotation == 3.0f);
  722. T(tform.scale.x == 4.0f);
  723. T(tform.scale.y == 5.0f);
  724. T(tform.shear.x == 6.0f);
  725. T(tform.shear.y == 7.0f);
  726. shutdown();
  727. }
  728. UTEST(sokol_spine, set_get_bone_transform_destroyed_instance) {
  729. init();
  730. sspine_instance instance = create_instance();
  731. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  732. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  733. sspine_destroy_instance(instance);
  734. sspine_set_bone_transform(instance, bone, &(sspine_bone_transform){
  735. .position = { 1.0f, 2.0f },
  736. .rotation = 3.0f,
  737. .scale = { 4.0f, 5.0f },
  738. .shear = { 6.0f, 7.0f }
  739. });
  740. const sspine_bone_transform tform = sspine_get_bone_transform(instance, bone);
  741. T(tform.position.x == 0.0f);
  742. T(tform.position.y == 0.0f);
  743. T(tform.rotation == 0.0f);
  744. T(tform.scale.x == 0.0f);
  745. T(tform.scale.y == 0.0f);
  746. T(tform.shear.x == 0.0f);
  747. T(tform.shear.y == 0.0f);
  748. shutdown();
  749. }
  750. UTEST(sokol_spine, set_get_bone_position) {
  751. init();
  752. sspine_instance instance = create_instance();
  753. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  754. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  755. sspine_set_bone_position(instance, bone, (sspine_vec2){ 1.0f, 2.0f });
  756. const sspine_vec2 p0 = sspine_get_bone_position(instance, bone);
  757. T(p0.x == 1.0f);
  758. T(p0.y == 2.0f);
  759. sspine_destroy_instance(instance);
  760. const sspine_vec2 p1 = sspine_get_bone_position(instance, bone);
  761. T(p1.x == 0.0f);
  762. T(p1.y == 0.0f);
  763. shutdown();
  764. }
  765. UTEST(sokol_spine, set_get_bone_rotation) {
  766. init();
  767. sspine_instance instance = create_instance();
  768. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  769. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  770. sspine_set_bone_rotation(instance, bone, 5.0f);
  771. T(sspine_get_bone_rotation(instance, bone) == 5.0f);
  772. sspine_destroy_instance(instance);
  773. T(sspine_get_bone_rotation(instance, bone) == 0.0f);
  774. shutdown();
  775. }
  776. UTEST(sokol_spine, set_get_bone_scale) {
  777. init();
  778. sspine_instance instance = create_instance();
  779. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  780. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  781. sspine_set_bone_scale(instance, bone, (sspine_vec2){ 1.0f, 2.0f });
  782. const sspine_vec2 s0 = sspine_get_bone_scale(instance, bone);
  783. T(s0.x == 1.0f);
  784. T(s0.y == 2.0f);
  785. sspine_destroy_instance(instance);
  786. const sspine_vec2 s1 = sspine_get_bone_scale(instance, bone);
  787. T(s1.x == 0.0f);
  788. T(s1.y == 0.0f);
  789. shutdown();
  790. }
  791. UTEST(sokol_spine, set_get_bone_shear) {
  792. init();
  793. sspine_instance instance = create_instance();
  794. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  795. sspine_bone bone = sspine_bone_by_name(skeleton, "root");
  796. sspine_set_bone_shear(instance, bone, (sspine_vec2){ 1.0f, 2.0f });
  797. const sspine_vec2 s0 = sspine_get_bone_shear(instance, bone);
  798. T(s0.x == 1.0f);
  799. T(s0.y == 2.0f);
  800. sspine_destroy_instance(instance);
  801. const sspine_vec2 s1 = sspine_get_bone_shear(instance, bone);
  802. T(s1.x == 0.0f);
  803. T(s1.y == 0.0f);
  804. shutdown();
  805. }
  806. UTEST(sokol_spine, slot_by_name) {
  807. init();
  808. sspine_skeleton skeleton = create_skeleton();
  809. sspine_slot s0 = sspine_slot_by_name(skeleton, "portal-streaks1");
  810. T((s0.skeleton_id == skeleton.id) && (s0.index == 3));
  811. sspine_slot s1 = sspine_slot_by_name(skeleton, "blablub");
  812. T((s1.skeleton_id == 0) && (s1.index == 0));
  813. shutdown();
  814. }
  815. UTEST(sokol_spine, slot_by_name_destroyed_skeleton) {
  816. init();
  817. sspine_skeleton skeleton = create_skeleton();
  818. sspine_destroy_skeleton(skeleton);
  819. sspine_slot s0 = sspine_slot_by_name(skeleton, "portal-streaks1");
  820. T((s0.skeleton_id == 0) && (s0.index == 0));
  821. shutdown();
  822. }
  823. UTEST(sokol_spine, num_slots) {
  824. init();
  825. sspine_skeleton skeleton = create_skeleton();
  826. T(sspine_num_slots(skeleton) == 52);
  827. sspine_destroy_skeleton(skeleton);
  828. T(sspine_num_slots(skeleton) == 0);
  829. shutdown();
  830. }
  831. UTEST(sokol_spine, slot_valid) {
  832. init();
  833. sspine_skeleton skeleton = create_skeleton();
  834. T(sspine_slot_valid(sspine_slot_by_index(skeleton, 0)));
  835. T(sspine_slot_valid(sspine_slot_by_index(skeleton, 51)));
  836. T(!sspine_slot_valid(sspine_slot_by_index(skeleton, -1)));
  837. T(!sspine_slot_valid(sspine_slot_by_index(skeleton, 52)));
  838. sspine_destroy_skeleton(skeleton);
  839. T(!sspine_slot_valid(sspine_slot_by_index(skeleton, 0)));
  840. shutdown();
  841. }
  842. UTEST(sokol_spine, slot_equal) {
  843. init();
  844. T(sspine_slot_equal((sspine_slot){ 1, 2 }, (sspine_slot){ 1, 2 }));
  845. T(!sspine_slot_equal((sspine_slot){ 2, 2 }, (sspine_slot){ 1, 2 }));
  846. T(!sspine_slot_equal((sspine_slot){ 1, 3 }, (sspine_slot){ 1, 2 }));
  847. shutdown();
  848. }
  849. UTEST(sokol_spine, get_slot_info) {
  850. init();
  851. sspine_skeleton skeleton = create_skeleton();
  852. const sspine_slot_info info = sspine_get_slot_info(sspine_slot_by_name(skeleton, "portal-streaks1"));
  853. T(info.valid);
  854. T(info.index == 3);
  855. T(strcmp(info.name.cstr, "portal-streaks1") == 0);
  856. T(!info.attachment_name.valid);
  857. T((info.bone.skeleton_id == skeleton.id) && (info.bone.index == 62));
  858. T(info.color.r == 1.0f);
  859. T(info.color.g == 1.0f);
  860. T(info.color.b == 1.0f);
  861. T(info.color.a == 1.0f);
  862. shutdown();
  863. }
  864. UTEST(sokol_spine, get_slot_info_destroyed_skeleton) {
  865. init();
  866. sspine_skeleton skeleton = create_skeleton();
  867. sspine_slot slot = sspine_slot_by_name(skeleton, "portal-streaks1");
  868. sspine_destroy_skeleton(skeleton);
  869. const sspine_slot_info info = sspine_get_slot_info(slot);
  870. T(!info.valid);
  871. T(!info.name.valid);
  872. shutdown();
  873. }
  874. UTEST(sokol_spine, get_slot_info_invalid_index) {
  875. init();
  876. sspine_skeleton skeleton = create_skeleton();
  877. const sspine_slot_info i0 = sspine_get_slot_info(sspine_slot_by_index(skeleton, -1));
  878. T(!i0.valid);
  879. T(!i0.name.valid);
  880. const sspine_slot_info i1 = sspine_get_slot_info(sspine_slot_by_index(skeleton, 1234));
  881. T(!i1.valid);
  882. T(!i1.name.valid);
  883. shutdown();
  884. }
  885. UTEST(sokol_spine, set_get_slot_color) {
  886. init();
  887. sspine_instance instance = create_instance();
  888. sspine_skeleton skeleton = sspine_get_instance_skeleton(instance);
  889. sspine_slot slot = sspine_slot_by_name(skeleton, "portal-streaks1");
  890. sspine_set_slot_color(instance, slot, (sspine_color){ 1.0f, 2.0f, 3.0f, 4.0f });
  891. const sspine_color color = sspine_get_slot_color(instance, slot);
  892. T(color.r == 1.0f);
  893. T(color.g == 2.0f);
  894. T(color.b == 3.0f);
  895. T(color.a == 4.0f);
  896. const sspine_slot_info info = sspine_get_slot_info(slot);
  897. T(info.color.r == 1.0f);
  898. T(info.color.g == 1.0f);
  899. T(info.color.b == 1.0f);
  900. T(info.color.a == 1.0f);
  901. shutdown();
  902. }
  903. UTEST(sokol_spine, event_by_name) {
  904. init();
  905. sspine_skeleton skeleton = create_skeleton();
  906. sspine_event e0 = sspine_event_by_name(skeleton, "footstep");
  907. T((e0.skeleton_id == skeleton.id) && (e0.index == 0));
  908. sspine_event e1 = sspine_event_by_name(skeleton, "bla");
  909. T((e1.skeleton_id == 0) && (e1.index == 0));
  910. shutdown();
  911. }
  912. UTEST(sokol_spine, event_by_name_destroyed_skeleton) {
  913. init();
  914. sspine_skeleton skeleton = create_skeleton();
  915. sspine_destroy_skeleton(skeleton);
  916. sspine_event e0 = sspine_event_by_name(skeleton, "footstep");
  917. T((e0.skeleton_id == 0) && (e0.index == 0));
  918. shutdown();
  919. }
  920. UTEST(sokol_spine, event_valid) {
  921. init();
  922. sspine_skeleton skeleton = create_skeleton();
  923. T(sspine_event_valid(sspine_event_by_index(skeleton, 0)));
  924. T(!sspine_event_valid(sspine_event_by_index(skeleton, 1)));
  925. T(!sspine_event_valid(sspine_event_by_index(skeleton, -1)));
  926. sspine_destroy_skeleton(skeleton);
  927. T(!sspine_event_valid(sspine_event_by_index(skeleton, 0)));
  928. shutdown();
  929. }
  930. UTEST(sokol_spine, event_equal) {
  931. init();
  932. T(sspine_event_equal((sspine_event){ 1, 2 }, (sspine_event){ 1, 2 }));
  933. T(!sspine_event_equal((sspine_event){ 2, 2 }, (sspine_event){ 1, 2 }));
  934. T(!sspine_event_equal((sspine_event){ 1, 3 }, (sspine_event){ 1, 2 }));
  935. shutdown();
  936. }
  937. UTEST(sokol_spine, num_events) {
  938. init();
  939. sspine_skeleton skeleton = create_skeleton();
  940. T(sspine_num_events(skeleton) == 1);
  941. sspine_destroy_skeleton(skeleton);
  942. T(sspine_num_events(skeleton) == 0);
  943. shutdown();
  944. }
  945. UTEST(sokol_spine, get_event_info) {
  946. init();
  947. sspine_skeleton skeleton = create_skeleton();
  948. const sspine_event_info info = sspine_get_event_info(sspine_event_by_index(skeleton, 0));
  949. T(info.valid);
  950. T(0 == strcmp(info.name.cstr, "footstep"));
  951. T(0 == info.index);
  952. T(0 == info.int_value);
  953. T(0.0f == info.float_value);
  954. T(!info.string_value.valid);
  955. T(!info.audio_path.valid);
  956. T(0.0f == info.volume);
  957. T(0.0f == info.balance);
  958. shutdown();
  959. }
  960. UTEST(sokol_spine, get_event_info_destroyed_skeleton) {
  961. init();
  962. sspine_skeleton skeleton = create_skeleton();
  963. sspine_destroy_skeleton(skeleton);
  964. const sspine_event_info info = sspine_get_event_info(sspine_event_by_index(skeleton, 0));
  965. T(!info.valid);
  966. T(!info.name.valid);
  967. shutdown();
  968. }
  969. UTEST(sokol_spine, iktarget_by_name) {
  970. init();
  971. sspine_skeleton skeleton = create_skeleton();
  972. sspine_iktarget ik0 = sspine_iktarget_by_name(skeleton, "board-ik");
  973. T((ik0.skeleton_id == skeleton.id) && (ik0.index == 2));
  974. sspine_iktarget ik1 = sspine_iktarget_by_name(skeleton, "bla");
  975. T((ik1.skeleton_id == 0) && (ik1.index == 0));
  976. shutdown();
  977. }
  978. UTEST(sokol_spine, iktarget_by_name_destroyed_skeleton) {
  979. init();
  980. sspine_skeleton skeleton = create_skeleton();
  981. sspine_destroy_skeleton(skeleton);
  982. sspine_iktarget ik0 = sspine_iktarget_by_name(skeleton, "board-ik");
  983. T((ik0.skeleton_id == 0) && (ik0.index == 0));
  984. shutdown();
  985. }
  986. UTEST(sokol_spine, iktarget_valid) {
  987. init();
  988. sspine_skeleton skeleton = create_skeleton();
  989. T(sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 0)));
  990. T(sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 6)));
  991. T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, -1)));
  992. T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 7)));
  993. sspine_destroy_skeleton(skeleton);
  994. T(!sspine_iktarget_valid(sspine_iktarget_by_index(skeleton, 0)));
  995. shutdown();
  996. }
  997. UTEST(sokol_spine, iktarget_equal) {
  998. init();
  999. T(sspine_iktarget_equal((sspine_iktarget){ 1, 2 }, (sspine_iktarget){ 1, 2 }));
  1000. T(!sspine_iktarget_equal((sspine_iktarget){ 2, 2 }, (sspine_iktarget){ 1, 2 }));
  1001. T(!sspine_iktarget_equal((sspine_iktarget){ 1, 3 }, (sspine_iktarget){ 1, 2 }));
  1002. shutdown();
  1003. }
  1004. UTEST(sokol_spine, num_iktargets) {
  1005. init();
  1006. sspine_skeleton skeleton = create_skeleton();
  1007. T(sspine_num_iktargets(skeleton) == 7);
  1008. sspine_destroy_skeleton(skeleton);
  1009. T(sspine_num_iktargets(skeleton) == 0);
  1010. shutdown();
  1011. }
  1012. UTEST(sokol_spine, get_iktarget_info) {
  1013. init();
  1014. sspine_skeleton skeleton = create_skeleton();
  1015. const sspine_iktarget_info info = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 1));
  1016. T(info.valid);
  1017. T(1 == info.index);
  1018. T(0 == strcmp(info.name.cstr, "aim-torso-ik"));
  1019. T((info.target_bone.skeleton_id == skeleton.id) && (info.target_bone.index == 2));
  1020. shutdown();
  1021. }
  1022. UTEST(sokol_spine, get_iktarget_info_destroyed_skeleton) {
  1023. init();
  1024. sspine_skeleton skeleton = create_skeleton();
  1025. sspine_destroy_skeleton(skeleton);
  1026. const sspine_iktarget_info info = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 1));
  1027. T(!info.valid);
  1028. T(!info.name.valid);
  1029. shutdown();
  1030. }
  1031. UTEST(sokol_spine, get_iktarget_info_out_of_bounds) {
  1032. init();
  1033. sspine_skeleton skeleton = create_skeleton();
  1034. sspine_destroy_skeleton(skeleton);
  1035. const sspine_iktarget_info info0 = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, -1));
  1036. T(!info0.name.valid);
  1037. const sspine_iktarget_info info1 = sspine_get_iktarget_info(sspine_iktarget_by_index(skeleton, 7));
  1038. T(!info1.name.valid);
  1039. shutdown();
  1040. }
  1041. UTEST(sokol_spine, skin_by_name) {
  1042. init();
  1043. sspine_skeleton skeleton = create_skeleton();
  1044. sspine_skin s0 = sspine_skin_by_name(skeleton, "default");
  1045. T((s0.skeleton_id == skeleton.id) && (s0.index == 0));
  1046. sspine_skin s1 = sspine_skin_by_name(skeleton, "bla");
  1047. T((s1.skeleton_id == 0) && (s1.index == 0));
  1048. sspine_destroy_skeleton(skeleton);
  1049. sspine_skin s2 = sspine_skin_by_name(skeleton, "default");
  1050. T((s2.skeleton_id == 0) && (s2.index == 0));
  1051. shutdown();
  1052. }
  1053. UTEST(sokol_spine, skin_valid) {
  1054. init();
  1055. sspine_skeleton skeleton = create_skeleton();
  1056. T(sspine_skin_valid(sspine_skin_by_index(skeleton, 0)));
  1057. T(!sspine_skin_valid(sspine_skin_by_index(skeleton, -1)));
  1058. T(!sspine_skin_valid(sspine_skin_by_index(skeleton, 1)));
  1059. sspine_destroy_skeleton(skeleton);
  1060. T(!sspine_skin_valid(sspine_skin_by_index(skeleton, 0)));
  1061. shutdown();
  1062. }
  1063. UTEST(sokol_spine, skin_equal) {
  1064. init();
  1065. T(sspine_skin_equal((sspine_skin){ 1, 2 }, (sspine_skin){ 1, 2 }));
  1066. T(!sspine_skin_equal((sspine_skin){ 2, 2 }, (sspine_skin){ 1, 2 }));
  1067. T(!sspine_skin_equal((sspine_skin){ 1, 3 }, (sspine_skin){ 1, 2 }));
  1068. shutdown();
  1069. }
  1070. UTEST(sokol_spine, num_skins) {
  1071. init();
  1072. sspine_skeleton skeleton = create_skeleton();
  1073. T(sspine_num_skins(skeleton) == 1);
  1074. sspine_destroy_skeleton(skeleton);
  1075. T(sspine_num_skins(skeleton) == 0);
  1076. shutdown();
  1077. }
  1078. UTEST(sokol_spine, get_skin_info) {
  1079. init();
  1080. sspine_skeleton skeleton = create_skeleton();
  1081. const sspine_skin_info info = sspine_get_skin_info(sspine_skin_by_index(skeleton, 0));
  1082. T(info.valid);
  1083. T(0 == info.index);
  1084. T(0 == strcmp(info.name.cstr, "default"));
  1085. shutdown();
  1086. }
  1087. UTEST(sokol_spine, get_skin_info_destroyed_skeleton) {
  1088. init();
  1089. sspine_skeleton skeleton = create_skeleton();
  1090. sspine_destroy_skeleton(skeleton);
  1091. const sspine_skin_info info = sspine_get_skin_info(sspine_skin_by_index(skeleton, 0));
  1092. T(!info.valid);
  1093. T(!info.name.valid);
  1094. shutdown();
  1095. }