main.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated April 5, 2025. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2025, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. * or otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. #include <SFML/Graphics.hpp>
  30. #include <iostream>
  31. #include <spine/Debug.h>
  32. #include <spine/Log.h>
  33. #include <spine/spine-sfml.h>
  34. using namespace std;
  35. using namespace spine;
  36. #include <memory>
  37. template<typename T, typename... Args>
  38. unique_ptr<T> make_unique_test(Args &&...args) {
  39. return unique_ptr<T>(new T(std::forward<Args>(args)...));
  40. }
  41. void callback(AnimationState *state, EventType type, TrackEntry *entry, Event *event) {
  42. SP_UNUSED(state);
  43. const String &animationName = (entry && entry->getAnimation()) ? entry->getAnimation()->getName() : String("");
  44. switch (type) {
  45. case EventType_Start:
  46. printf("%d start: %s\n", entry->getTrackIndex(), animationName.buffer());
  47. break;
  48. case EventType_Interrupt:
  49. printf("%d interrupt: %s\n", entry->getTrackIndex(), animationName.buffer());
  50. break;
  51. case EventType_End:
  52. printf("%d end: %s\n", entry->getTrackIndex(), animationName.buffer());
  53. break;
  54. case EventType_Complete:
  55. printf("%d complete: %s\n", entry->getTrackIndex(), animationName.buffer());
  56. break;
  57. case EventType_Dispose:
  58. printf("%d dispose: %s\n", entry->getTrackIndex(), animationName.buffer());
  59. break;
  60. case EventType_Event:
  61. printf("%d event: %s, %s: %d, %f, %s %f %f\n", entry->getTrackIndex(), animationName.buffer(), event->getData().getName().buffer(), event->getIntValue(), event->getFloatValue(),
  62. event->getStringValue().buffer(), event->getVolume(), event->getBalance());
  63. break;
  64. }
  65. fflush(stdout);
  66. }
  67. shared_ptr<SkeletonData> readSkeletonJsonData(const String &filename, Atlas *atlas, float scale) {
  68. SkeletonJson json(atlas);
  69. json.setScale(scale);
  70. auto skeletonData = json.readSkeletonDataFile(filename);
  71. if (!skeletonData) {
  72. printf("%s\n", json.getError().buffer());
  73. exit(0);
  74. }
  75. return shared_ptr<SkeletonData>(skeletonData);
  76. }
  77. shared_ptr<SkeletonData> readSkeletonBinaryData(const char *filename, Atlas *atlas, float scale) {
  78. SkeletonBinary binary(atlas);
  79. binary.setScale(scale);
  80. auto skeletonData = binary.readSkeletonDataFile(filename);
  81. if (!skeletonData) {
  82. printf("%s\n", binary.getError().buffer());
  83. exit(0);
  84. }
  85. return shared_ptr<SkeletonData>(skeletonData);
  86. }
  87. void testcase(void func(SkeletonData *skeletonData, Atlas *atlas),
  88. const char *jsonName, const char *binaryName, const char *atlasName,
  89. float scale) {
  90. SP_UNUSED(jsonName);
  91. SFMLTextureLoader textureLoader;
  92. auto atlas = make_unique_test<Atlas>(atlasName, &textureLoader);
  93. auto skeletonData = readSkeletonJsonData(jsonName, atlas.get(), scale);
  94. func(skeletonData.get(), atlas.get());//
  95. skeletonData = readSkeletonBinaryData(binaryName, atlas.get(), scale);
  96. func(skeletonData.get(), atlas.get());
  97. }
  98. void spineboy(SkeletonData *skeletonData, Atlas *atlas) {
  99. SP_UNUSED(atlas);
  100. SkeletonBounds bounds;
  101. // Configure mixing.
  102. AnimationStateData stateData(skeletonData);
  103. stateData.setMix("walk", "jump", 0.2f);
  104. stateData.setMix("jump", "run", 0.2f);
  105. SkeletonDrawable drawable(skeletonData, &stateData);
  106. drawable.timeScale = 1;
  107. drawable.setUsePremultipliedAlpha(true);
  108. Skeleton *skeleton = drawable.skeleton;
  109. skeleton->setToSetupPose();
  110. skeleton->setPosition(320, 590);
  111. skeleton->updateWorldTransform(Physics_Update);
  112. Slot *headSlot = skeleton->findSlot("head");
  113. drawable.state->setListener(callback);
  114. drawable.state->addAnimation(0, "walk", true, 0);
  115. drawable.state->addAnimation(0, "jump", false, 3);
  116. drawable.state->addAnimation(0, "run", true, 0);
  117. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - spineboy");
  118. window.setFramerateLimit(60);
  119. sf::Event event;
  120. sf::Clock deltaClock;
  121. while (window.isOpen()) {
  122. while (window.pollEvent(event))
  123. if (event.type == sf::Event::Closed) window.close();
  124. float delta = deltaClock.getElapsedTime().asSeconds();
  125. deltaClock.restart();
  126. bounds.update(*skeleton, true);
  127. sf::Vector2i position = sf::Mouse::getPosition(window);
  128. if (bounds.containsPoint((float) position.x, (float) position.y)) {
  129. headSlot->getColor().g = 0;
  130. headSlot->getColor().b = 0;
  131. } else {
  132. headSlot->getColor().g = 1;
  133. headSlot->getColor().b = 1;
  134. }
  135. drawable.update(delta);
  136. window.clear();
  137. window.draw(drawable);
  138. window.display();
  139. }
  140. }
  141. void ikDemo(SkeletonData *skeletonData, Atlas *atlas) {
  142. SP_UNUSED(atlas);
  143. SkeletonBounds bounds;
  144. // Create the SkeletonDrawable and position it
  145. AnimationStateData stateData(skeletonData);
  146. SkeletonDrawable drawable(skeletonData, &stateData);
  147. drawable.timeScale = 1;
  148. drawable.setUsePremultipliedAlpha(true);
  149. drawable.skeleton->setPosition(320, 590);
  150. // Queue the "walk" animation on the first track.
  151. drawable.state->setAnimation(0, "walk", true);
  152. // Queue the "aim" animation on a higher track.
  153. // It consists of a single frame that positions
  154. // the back arm and gun such that they point at
  155. // the "crosshair" bone. By setting this
  156. // animation on a higher track, it overrides
  157. // any changes to the back arm and gun made
  158. // by the walk animation, allowing us to
  159. // mix the two. The mouse position following
  160. // is performed in the render() method below.
  161. drawable.state->setAnimation(1, "aim", true);
  162. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - IK Demo");
  163. window.setFramerateLimit(60);
  164. sf::Event event;
  165. sf::Clock deltaClock;
  166. while (window.isOpen()) {
  167. while (window.pollEvent(event))
  168. if (event.type == sf::Event::Closed) window.close();
  169. float delta = deltaClock.getElapsedTime().asSeconds();
  170. deltaClock.restart();
  171. // Update and apply the animations to the skeleton,
  172. // then calculate the world transforms of every bone.
  173. // This is needed so we can call Bone#worldToLocal()
  174. // later.
  175. drawable.update(delta);
  176. // Position the "crosshair" bone at the mouse
  177. // location. We do this before calling
  178. // skeleton.updateWorldTransform() below, so
  179. // our change is incorporated before the IK
  180. // constraint is applied.
  181. //
  182. // When setting the crosshair bone position
  183. // to the mouse position, we need to translate
  184. // from "mouse space" to "local bone space". Note that the local
  185. // bone space is calculated using the bone's parent
  186. // worldToLocal() function!
  187. sf::Vector2i mouseCoords = sf::Mouse::getPosition(window);
  188. float boneCoordsX = 0, boneCoordsY = 0;
  189. Bone *crosshair = drawable.skeleton->findBone("crosshair");// Should be cached.
  190. crosshair->getParent()->worldToLocal(mouseCoords.x, mouseCoords.y, boneCoordsX, boneCoordsY);
  191. crosshair->setX(boneCoordsX);
  192. crosshair->setY(boneCoordsY);
  193. // Calculate final world transform with the
  194. // crosshair bone set to the mouse cursor
  195. // position.
  196. drawable.skeleton->updateWorldTransform(Physics_Update);
  197. window.clear();
  198. window.draw(drawable);
  199. window.display();
  200. }
  201. }
  202. void goblins(SkeletonData *skeletonData, Atlas *atlas) {
  203. SP_UNUSED(atlas);
  204. SkeletonDrawable drawable(skeletonData);
  205. drawable.timeScale = 1;
  206. drawable.setUsePremultipliedAlpha(true);
  207. Skeleton *skeleton = drawable.skeleton;
  208. skeleton->setSkin("goblingirl");
  209. skeleton->setSlotsToSetupPose();
  210. skeleton->setPosition(320, 590);
  211. skeleton->updateWorldTransform(Physics_Update);
  212. drawable.state->setAnimation(0, "walk", true);
  213. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - goblins");
  214. window.setFramerateLimit(60);
  215. sf::Event event;
  216. sf::Clock deltaClock;
  217. while (window.isOpen()) {
  218. while (window.pollEvent(event))
  219. if (event.type == sf::Event::Closed) window.close();
  220. float delta = deltaClock.getElapsedTime().asSeconds();
  221. deltaClock.restart();
  222. drawable.update(delta);
  223. window.clear();
  224. window.draw(drawable);
  225. window.display();
  226. }
  227. }
  228. void raptor(SkeletonData *skeletonData, Atlas *atlas) {
  229. SP_UNUSED(atlas);
  230. SkeletonDrawable drawable(skeletonData);
  231. drawable.timeScale = 1;
  232. drawable.setUsePremultipliedAlpha(true);
  233. PowInterpolation pow2(2);
  234. PowOutInterpolation powOut2(2);
  235. Skeleton *skeleton = drawable.skeleton;
  236. skeleton->setPosition(320, 590);
  237. skeleton->updateWorldTransform(Physics_Update);
  238. drawable.state->setAnimation(0, "walk", true);
  239. drawable.state->addAnimation(1, "gun-grab", false, 2);
  240. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - raptor");
  241. window.setFramerateLimit(60);
  242. sf::Event event;
  243. sf::Clock deltaClock;
  244. while (window.isOpen()) {
  245. while (window.pollEvent(event))
  246. if (event.type == sf::Event::Closed) window.close();
  247. float delta = deltaClock.getElapsedTime().asSeconds();
  248. deltaClock.restart();
  249. drawable.update(delta);
  250. window.clear();
  251. window.draw(drawable);
  252. window.display();
  253. }
  254. }
  255. void tank(SkeletonData *skeletonData, Atlas *atlas) {
  256. SP_UNUSED(atlas);
  257. SkeletonDrawable drawable(skeletonData);
  258. drawable.timeScale = 1;
  259. drawable.setUsePremultipliedAlpha(true);
  260. Skeleton *skeleton = drawable.skeleton;
  261. skeleton->setPosition(500, 590);
  262. skeleton->updateWorldTransform(Physics_Update);
  263. drawable.state->setAnimation(0, "drive", true);
  264. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - tank");
  265. window.setFramerateLimit(60);
  266. sf::Event event;
  267. sf::Clock deltaClock;
  268. while (window.isOpen()) {
  269. while (window.pollEvent(event))
  270. if (event.type == sf::Event::Closed) window.close();
  271. float delta = deltaClock.getElapsedTime().asSeconds();
  272. deltaClock.restart();
  273. drawable.update(delta);
  274. window.clear();
  275. window.draw(drawable);
  276. window.display();
  277. }
  278. }
  279. void vine(SkeletonData *skeletonData, Atlas *atlas) {
  280. SP_UNUSED(atlas);
  281. SkeletonDrawable drawable(skeletonData);
  282. drawable.timeScale = 1;
  283. drawable.setUsePremultipliedAlpha(true);
  284. Skeleton *skeleton = drawable.skeleton;
  285. skeleton->setPosition(320, 590);
  286. skeleton->updateWorldTransform(Physics_Update);
  287. drawable.state->setAnimation(0, "grow", true);
  288. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - vine");
  289. window.setFramerateLimit(60);
  290. sf::Event event;
  291. sf::Clock deltaClock;
  292. while (window.isOpen()) {
  293. while (window.pollEvent(event))
  294. if (event.type == sf::Event::Closed) window.close();
  295. float delta = deltaClock.getElapsedTime().asSeconds();
  296. deltaClock.restart();
  297. drawable.update(delta);
  298. window.clear();
  299. window.draw(drawable);
  300. window.display();
  301. }
  302. }
  303. void stretchyman(SkeletonData *skeletonData, Atlas *atlas) {
  304. SP_UNUSED(atlas);
  305. SkeletonDrawable drawable(skeletonData);
  306. drawable.timeScale = 1;
  307. drawable.setUsePremultipliedAlpha(true);
  308. Skeleton *skeleton = drawable.skeleton;
  309. skeleton->setPosition(100, 590);
  310. skeleton->updateWorldTransform(Physics_Update);
  311. drawable.state->setAnimation(0, "sneak", true);
  312. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - Streatchyman");
  313. window.setFramerateLimit(60);
  314. sf::Event event;
  315. sf::Clock deltaClock;
  316. while (window.isOpen()) {
  317. while (window.pollEvent(event))
  318. if (event.type == sf::Event::Closed) window.close();
  319. float delta = deltaClock.getElapsedTime().asSeconds();
  320. deltaClock.restart();
  321. drawable.update(delta);
  322. window.clear();
  323. window.draw(drawable);
  324. window.display();
  325. }
  326. }
  327. void stretchymanStrechyIk(SkeletonData *skeletonData, Atlas *atlas) {
  328. SP_UNUSED(atlas);
  329. SkeletonDrawable *drawable = new SkeletonDrawable(skeletonData);
  330. drawable->timeScale = 1;
  331. drawable->setUsePremultipliedAlpha(true);
  332. Skeleton *skeleton = drawable->skeleton;
  333. skeleton->setPosition(100, 590);
  334. skeleton->updateWorldTransform(Physics_Update);
  335. drawable->state->setAnimation(0, "sneak", true);
  336. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - Streatchyman Stretchy IK");
  337. window.setFramerateLimit(60);
  338. sf::Event event;
  339. sf::Clock deltaClock;
  340. while (window.isOpen()) {
  341. while (window.pollEvent(event))
  342. if (event.type == sf::Event::Closed) window.close();
  343. float delta = deltaClock.getElapsedTime().asSeconds();
  344. deltaClock.restart();
  345. drawable->update(delta);
  346. window.clear();
  347. window.draw(*drawable);
  348. window.display();
  349. }
  350. delete drawable;
  351. }
  352. void coin(SkeletonData *skeletonData, Atlas *atlas) {
  353. SP_UNUSED(atlas);
  354. SkeletonDrawable drawable(skeletonData);
  355. drawable.timeScale = 1;
  356. drawable.setUsePremultipliedAlpha(true);
  357. Skeleton *skeleton = drawable.skeleton;
  358. skeleton->setPosition(320, 320);
  359. skeleton->updateWorldTransform(Physics_Update);
  360. drawable.state->setAnimation(0, "animation", true);
  361. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - coin");
  362. window.setFramerateLimit(60);
  363. sf::Event event;
  364. sf::Clock deltaClock;
  365. while (window.isOpen()) {
  366. while (window.pollEvent(event)) {
  367. if (event.type == sf::Event::Closed) window.close();
  368. }
  369. float delta = deltaClock.getElapsedTime().asSeconds();
  370. deltaClock.restart();
  371. drawable.update(delta);
  372. window.clear();
  373. window.draw(drawable);
  374. window.display();
  375. }
  376. }
  377. void dragon(SkeletonData *skeletonData, Atlas *atlas) {
  378. SP_UNUSED(atlas);
  379. SkeletonDrawable drawable(skeletonData);
  380. drawable.timeScale = 1;
  381. drawable.setUsePremultipliedAlpha(true);
  382. Skeleton *skeleton = drawable.skeleton;
  383. skeleton->setPosition(320, 320);
  384. skeleton->updateWorldTransform(Physics_Update);
  385. drawable.state->setAnimation(0, "flying", true);
  386. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - dragon");
  387. window.setFramerateLimit(60);
  388. sf::Event event;
  389. sf::Clock deltaClock;
  390. while (window.isOpen()) {
  391. while (window.pollEvent(event)) {
  392. if (event.type == sf::Event::Closed) window.close();
  393. }
  394. float delta = deltaClock.getElapsedTime().asSeconds();
  395. deltaClock.restart();
  396. drawable.update(delta);
  397. window.clear();
  398. window.draw(drawable);
  399. window.display();
  400. }
  401. }
  402. void owl(SkeletonData *skeletonData, Atlas *atlas) {
  403. SP_UNUSED(atlas);
  404. SkeletonDrawable drawable(skeletonData);
  405. drawable.timeScale = 1;
  406. drawable.setUsePremultipliedAlpha(true);
  407. Skeleton *skeleton = drawable.skeleton;
  408. skeleton->setPosition(320, 400);
  409. skeleton->updateWorldTransform(Physics_Update);
  410. drawable.state->setAnimation(0, "idle", true);
  411. drawable.state->setAnimation(1, "blink", true);
  412. TrackEntry *left = drawable.state->setAnimation(2, "left", true);
  413. TrackEntry *right = drawable.state->setAnimation(3, "right", true);
  414. TrackEntry *up = drawable.state->setAnimation(4, "up", true);
  415. TrackEntry *down = drawable.state->setAnimation(5, "down", true);
  416. left->setAlpha(0);
  417. left->setMixBlend(MixBlend_Add);
  418. right->setAlpha(0);
  419. right->setMixBlend(MixBlend_Add);
  420. up->setAlpha(0);
  421. up->setMixBlend(MixBlend_Add);
  422. down->setAlpha(0);
  423. down->setMixBlend(MixBlend_Add);
  424. // drawable.state->setAnimation(5, "blink", true);
  425. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - owl");
  426. window.setFramerateLimit(60);
  427. sf::Event event;
  428. sf::Clock deltaClock;
  429. while (window.isOpen()) {
  430. while (window.pollEvent(event)) {
  431. if (event.type == sf::Event::Closed) window.close();
  432. if (event.type == sf::Event::MouseMoved) {
  433. float x = event.mouseMove.x / 640.0f;
  434. left->setAlpha((MathUtil::max(x, 0.5f) - 0.5f) * 2);
  435. right->setAlpha((0.5f - MathUtil::min(x, 0.5f)) * 2);
  436. float y = event.mouseMove.y / 640.0f;
  437. down->setAlpha((MathUtil::max(y, 0.5f) - 0.5f) * 2);
  438. up->setAlpha((0.5f - MathUtil::min(y, 0.5f)) * 2);
  439. }
  440. }
  441. float delta = deltaClock.getElapsedTime().asSeconds();
  442. deltaClock.restart();
  443. drawable.skeleton->setToSetupPose();
  444. drawable.update(delta);
  445. window.clear();
  446. window.draw(drawable);
  447. window.display();
  448. }
  449. }
  450. void mixAndMatch(SkeletonData *skeletonData, Atlas *atlas) {
  451. SP_UNUSED(atlas);
  452. SkeletonDrawable drawable(skeletonData);
  453. drawable.timeScale = 1;
  454. drawable.setUsePremultipliedAlpha(true);
  455. Skeleton *skeleton = drawable.skeleton;
  456. Skin *skin = new Skin("mix-and-match");
  457. skin->addSkin(skeletonData->findSkin("full-skins/girl"));
  458. skin->addSkin(skeletonData->findSkin("accessories/cape-blue"));
  459. skeleton->setSkin(skin);
  460. skeleton->setToSetupPose();
  461. skeleton->setPosition(320, 590);
  462. skeleton->updateWorldTransform(Physics_Update);
  463. drawable.state->setAnimation(0, "dance", true);
  464. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - goblins");
  465. window.setFramerateLimit(60);
  466. sf::Event event;
  467. sf::Clock deltaClock;
  468. bool isCapeEquipped = true;
  469. while (window.isOpen()) {
  470. while (window.pollEvent(event)) {
  471. if (event.type == sf::Event::Closed) window.close();
  472. if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) {
  473. skeleton->setSkin(nullptr);
  474. delete skin;
  475. isCapeEquipped = !isCapeEquipped;
  476. skin = new Skin("mix-and-match");
  477. skin->addSkin(skeletonData->findSkin("full-skins/girl"));
  478. if (isCapeEquipped) skin->addSkin(skeletonData->findSkin("accessories/cape-blue"));
  479. skeleton->setSkin(skin);
  480. skeleton->setSlotsToSetupPose();
  481. }
  482. }
  483. float delta = deltaClock.getElapsedTime().asSeconds();
  484. deltaClock.restart();
  485. drawable.update(delta);
  486. window.clear();
  487. window.draw(drawable);
  488. window.display();
  489. }
  490. delete skin;
  491. }
  492. void celestialCircus(SkeletonData *skeletonData, Atlas *atlas) {
  493. SP_UNUSED(atlas);
  494. SkeletonDrawable drawable(skeletonData);
  495. drawable.timeScale = 1;
  496. drawable.setUsePremultipliedAlpha(true);
  497. Skeleton *skeleton = drawable.skeleton;
  498. skeleton->setPosition(320, 480);
  499. skeleton->setScaleX(0.2);
  500. skeleton->setScaleY(0.2);
  501. skeleton->updateWorldTransform(Physics_Update);
  502. drawable.state->setAnimation(0, "swing", true);
  503. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - celestial circus");
  504. window.setFramerateLimit(60);
  505. sf::Event event;
  506. sf::Clock deltaClock;
  507. while (window.isOpen()) {
  508. while (window.pollEvent(event)) {
  509. if (event.type == sf::Event::Closed) window.close();
  510. }
  511. float delta = deltaClock.getElapsedTime().asSeconds();
  512. deltaClock.restart();
  513. drawable.update(delta);
  514. window.clear();
  515. window.draw(drawable);
  516. window.display();
  517. }
  518. }
  519. void sack(SkeletonData *skeletonData, Atlas *atlas) {
  520. SP_UNUSED(atlas);
  521. SkeletonDrawable drawable(skeletonData);
  522. drawable.timeScale = 1;
  523. drawable.setUsePremultipliedAlpha(true);
  524. Skeleton *skeleton = drawable.skeleton;
  525. skeleton->setPosition(320, 480);
  526. skeleton->updateWorldTransform(Physics_Update);
  527. drawable.state->setAnimation(0, "walk", true);
  528. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - sack");
  529. window.setFramerateLimit(60);
  530. sf::Event event;
  531. sf::Clock deltaClock;
  532. while (window.isOpen()) {
  533. while (window.pollEvent(event)) {
  534. if (event.type == sf::Event::Closed) window.close();
  535. }
  536. float delta = deltaClock.getElapsedTime().asSeconds();
  537. deltaClock.restart();
  538. drawable.update(delta);
  539. window.clear();
  540. window.draw(drawable);
  541. window.display();
  542. }
  543. }
  544. void snowglobe(SkeletonData *skeletonData, Atlas *atlas) {
  545. SP_UNUSED(atlas);
  546. SkeletonDrawable drawable(skeletonData);
  547. drawable.timeScale = 1;
  548. drawable.setUsePremultipliedAlpha(true);
  549. Skeleton *skeleton = drawable.skeleton;
  550. skeleton->setPosition(320, 480);
  551. skeleton->updateWorldTransform(Physics_Update);
  552. drawable.state->setAnimation(0, "shake", true);
  553. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - snowglobe");
  554. window.setFramerateLimit(60);
  555. sf::Event event;
  556. sf::Clock deltaClock;
  557. while (window.isOpen()) {
  558. while (window.pollEvent(event)) {
  559. if (event.type == sf::Event::Closed) window.close();
  560. }
  561. float delta = deltaClock.getElapsedTime().asSeconds();
  562. deltaClock.restart();
  563. drawable.update(delta);
  564. window.clear();
  565. window.draw(drawable);
  566. window.display();
  567. }
  568. }
  569. void cloudpot(SkeletonData *skeletonData, Atlas *atlas) {
  570. SP_UNUSED(atlas);
  571. SkeletonDrawable drawable(skeletonData);
  572. drawable.timeScale = 1;
  573. drawable.setUsePremultipliedAlpha(true);
  574. Skeleton *skeleton = drawable.skeleton;
  575. skeleton->setPosition(320, 480);
  576. skeleton->updateWorldTransform(Physics_Update);
  577. drawable.state->setAnimation(0, "playing-in-the-rain", true);
  578. sf::RenderWindow window(sf::VideoMode(640, 640), "Spine SFML - cloudpot");
  579. window.setFramerateLimit(60);
  580. sf::Event event;
  581. sf::Clock deltaClock;
  582. while (window.isOpen()) {
  583. while (window.pollEvent(event)) {
  584. if (event.type == sf::Event::Closed) window.close();
  585. }
  586. float delta = deltaClock.getElapsedTime().asSeconds();
  587. deltaClock.restart();
  588. drawable.update(delta);
  589. window.clear();
  590. window.draw(drawable);
  591. window.display();
  592. }
  593. }
  594. /**
  595. * Used for debugging purposes during runtime development
  596. */
  597. void test(SkeletonData *skeletonData, Atlas *atlas) {
  598. SP_UNUSED(atlas);
  599. Skeleton skeleton(skeletonData);
  600. AnimationStateData animationStateData(skeletonData);
  601. AnimationState animationState(&animationStateData);
  602. animationState.setAnimation(0, "idle", true);
  603. float d = 3;
  604. for (int i = 0; i < 1; i++) {
  605. animationState.update(d);
  606. animationState.apply(skeleton);
  607. skeleton.updateWorldTransform(Physics_Update);
  608. d += 0.1f;
  609. }
  610. }
  611. DebugExtension dbgExtension(SpineExtension::getInstance());
  612. extern spine::SkeletonRenderer *skeletonRenderer;
  613. int main() {
  614. SpineExtension::setInstance(&dbgExtension);
  615. testcase(celestialCircus, "data/celestial-circus-pro.json", "data/celestial-circus-pro.skel", "data/celestial-circus-pma.atlas", 1);
  616. testcase(mixAndMatch, "data/mix-and-match-pro.json", "data/mix-and-match-pro.skel", "data/mix-and-match-pma.atlas", 0.5f);
  617. testcase(cloudpot, "data/cloud-pot.json", "data/cloud-pot.skel", "data/cloud-pot-pma.atlas", 0.2);
  618. testcase(sack, "data/sack-pro.json", "data/sack-pro.skel", "data/sack-pma.atlas", 0.2f);
  619. testcase(snowglobe, "data/snowglobe-pro.json", "data/snowglobe-pro.skel", "data/snowglobe-pma.atlas", 0.2f);
  620. testcase(dragon, "data/dragon-ess.json", "data/dragon-ess.skel", "data/dragon-pma.atlas", 0.6f);
  621. testcase(spineboy, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.62f);
  622. testcase(ikDemo, "data/spineboy-pro.json", "data/spineboy-pro.skel", "data/spineboy-pma.atlas", 0.6f);
  623. testcase(vine, "data/vine-pro.json", "data/vine-pro.skel", "data/vine-pma.atlas", 0.5f);
  624. testcase(owl, "data/owl-pro.json", "data/owl-pro.skel", "data/owl-pma.atlas", 0.5f);
  625. testcase(coin, "data/coin-pro.json", "data/coin-pro.skel", "data/coin-pma.atlas", 0.5f);
  626. testcase(raptor, "data/raptor-pro.json", "data/raptor-pro.skel", "data/raptor-pma.atlas", 0.5f);
  627. testcase(tank, "data/tank-pro.json", "data/tank-pro.skel", "data/tank-pma.atlas", 0.2f);
  628. testcase(goblins, "data/goblins-pro.json", "data/goblins-pro.skel", "data/goblins-pma.atlas", 1.4f);
  629. testcase(stretchyman, "data/stretchyman-pro.json", "data/stretchyman-pro.skel", "data/stretchyman-pma.atlas", 0.6f);
  630. delete skeletonRenderer;
  631. dbgExtension.reportLeaks();
  632. return 0;
  633. }