SpineObject.cc 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _SPINE_OBJECT_H_
  23. #include "2d/sceneobject/SpineObject.h"
  24. #endif
  25. // Script bindings.
  26. #include "2d/sceneobject/SpineObject_ScriptBinding.h"
  27. //------------------------------------------------------------------------------
  28. void spineAnimationEventCallbackHandler(spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) {
  29. SpineObject *targetSpineObject = (SpineObject *)state->userData;
  30. if (!targetSpineObject) {
  31. Con::warnf("spineAnimationEventCallbackHandler - Event ('%s') received with no spine object in spAnimationState->userData. Discarding event.",
  32. event->data->name);
  33. }
  34. const char* animationName = StringTable->insert(entry && entry->animation ? entry->animation->name : 0, true);
  35. switch (type) {
  36. case SP_ANIMATION_START:
  37. Con::executef(targetSpineObject, 3, "onAnimationStart", animationName, Con::getIntArg(entry->trackIndex));
  38. break;
  39. case SP_ANIMATION_INTERRUPT:
  40. Con::executef(targetSpineObject, 3, "onAnimationInterrupt", animationName, Con::getIntArg(entry->trackIndex));
  41. break;
  42. case SP_ANIMATION_END:
  43. Con::executef(targetSpineObject, 3, "onAnimationEnd", animationName, Con::getIntArg(entry->trackIndex));
  44. break;
  45. case SP_ANIMATION_COMPLETE:
  46. Con::executef(targetSpineObject, 3, "onAnimationComplete", animationName, Con::getIntArg(entry->trackIndex));
  47. break;
  48. case SP_ANIMATION_DISPOSE:
  49. Con::executef(targetSpineObject, 3, "onAnimationDispose", animationName, Con::getIntArg(entry->trackIndex));
  50. break;
  51. case SP_ANIMATION_EVENT:
  52. Con::executef(targetSpineObject, 10
  53. , "onAnimationEvent"
  54. , animationName
  55. , Con::getIntArg(entry->trackIndex)
  56. , event->data->name ? event->data->name : ""
  57. , Con::getIntArg(event->intValue)
  58. , Con::getFloatArg(event->floatValue)
  59. , event->stringValue ? event->stringValue : ""
  60. , Con::getFloatArg(event->time)
  61. , Con::getFloatArg(event->volume)
  62. , Con::getFloatArg(event->balance));
  63. break;
  64. }
  65. }
  66. //------------------------------------------------------------------------------
  67. IMPLEMENT_CONOBJECT(SpineObject);
  68. //------------------------------------------------------------------------------
  69. // Spine vertex effect support
  70. static EnumTable::Enums vertexEffectLookup[] =
  71. {
  72. { SpineObject::NONE, "None" },
  73. { SpineObject::JITTER, "Jitter" },
  74. { SpineObject::SWIRL, "Swirl" }
  75. };
  76. static EnumTable VertexEffectTable(3, &vertexEffectLookup[0]);
  77. //------------------------------------------------------------------------------
  78. SpineObject::VertexEffect SpineObject::getVertexEffectTypeEnum(const char* label)
  79. {
  80. // Search for Mnemonic.
  81. for (U32 i = 0; i < (sizeof(vertexEffectLookup) / sizeof(EnumTable::Enums)); i++)
  82. if (dStricmp(vertexEffectLookup[i].label, label) == 0)
  83. return((SpineObject::VertexEffect)vertexEffectLookup[i].index);
  84. // Warn.
  85. Con::warnf("SpineObject::getVertexEffectTypeEnum() - Invalid vertex effect type '%s'.", label);
  86. return VertexEffect::INVALID_VERTEX_EFFECT;
  87. }
  88. //-----------------------------------------------------------------------------
  89. const char* SpineObject::getVertexEffectTypeDescription(const VertexEffect vertexEffectType)
  90. {
  91. // Search for Mnemonic.
  92. for (U32 i = 0; i < (sizeof(vertexEffectLookup) / sizeof(EnumTable::Enums)); i++)
  93. {
  94. if (vertexEffectLookup[i].index == (S32)vertexEffectType)
  95. return vertexEffectLookup[i].label;
  96. }
  97. // Warn.
  98. Con::warnf("SpineObject::getVertexEffectTypeDescription() - Invalid vertex effect type.");
  99. return StringTable->EmptyString;
  100. }
  101. //-----------------------------------------------------------------------------
  102. void SpineObject::initPersistFields()
  103. {
  104. // Call parent.
  105. Parent::initPersistFields();
  106. addProtectedField("Asset", TypeSpineAssetPtr, Offset(mSpineAsset, SpineObject), &setSpineAsset, &defaultProtectedGetFn, &writeSpineAsset, "The spine asset ID used for the spine.");
  107. addProtectedField("Skin", TypeString, 0, &setSkin, &getSkinName, &writeCurrentSkin, "The skin to use.");
  108. addProtectedField("Scale", TypeVector2, 0, &setScale, &getScale, &writeScale, "Scaling of the skeleton geometry.");
  109. addProtectedField("AnimationData", TypeString, 0, &setAnimationData, &getAnimationData, &writeAnimationData, "String encoding of the running animations. It's a tilde separated list of animation entries. Within each entry, each attribute is separated by a semicolon. The attributes are, in this order: 1) Name - String: Name of animation as defined in Spine. 2) Track - Integer: Track the animation is running on. 3) Is Looping - Boolean: 1 or 0. 4) Mix Duration - Float: Can be set to -1.0 to request default mix duration.");
  110. addProtectedField("TimeScale", TypeF32, 0, &setTimeScale, &getTimeScale, &writeTimeScale, "Time scale (animation speed) adjustment factor.");
  111. addProtectedField("FlipX", TypeBool, Offset(mFlipX, SpineObject), &setFlipX, &defaultProtectedGetFn, &writeFlipX, "Whether to invert the image horizontally.");
  112. addProtectedField("FlipY", TypeBool, Offset(mFlipY, SpineObject), &setFlipY, &defaultProtectedGetFn, &writeFlipY, "Whether image should be inverted vertically.");
  113. addGroup("Vertex Effects");
  114. addProtectedField("ActiveEffect", TypeEnum, Offset(mActiveEffect, SpineObject), &setActiveEffectType, &defaultProtectedGetFn, &writeActiveEffectType, 1, &VertexEffectTable, "The name of the vertex effect assigned, or None.");
  115. addProtectedField("JitterX", TypeF32, 0, &setJitterX, &getJitterX, &writeJitterEffectValues, "A 'Jitter' vertex special effect setting. Note: Play around with the various settings. They can be modified on the fly to vary the effect the displayed item.");
  116. addProtectedField("JitterY", TypeF32, 0, &setJitterY, &getJitterY, &writeJitterEffectValues, "A 'Jitter' vertex special effect setting.");
  117. addProtectedField("SwirlX", TypeF32, 0, &setSwirlX, &getSwirlX, &writeSwirlEffectValues, "A 'Swirl' vertex special effect setting.");
  118. addProtectedField("SwirlY", TypeF32, 0, &setSwirlY, &getSwirlY, &writeSwirlEffectValues, "A 'Swirl' vertex special effect setting.");
  119. addProtectedField("SwirlRadius", TypeF32, 0, &setSwirlRadius, &getSwirlRadius, &writeSwirlEffectValues, "A 'Swirl' vertex special effect setting.");
  120. addProtectedField("SwirlAngle", TypeF32, 0, &setSwirlAngle, &getSwirlAngle, &writeSwirlEffectValues, "A 'Swirl' vertex special effect setting.");
  121. endGroup("Vertex Effects");
  122. addProtectedField("EventCallbacksEnabled", TypeBool, 0, &setEventCallbacksEnabled, &defaultProtectedGetFn, &writeEventCallbacksEnabled, "Whether the SpineObject should receive spine animation event callbacks.");
  123. addProtectedField("CollisionData", TypeString, 0, &setCollisionData, &getCollisionData, &writeCollisionData, "String encoding of the Spine object's collision boxes. It's a tilde separated list of collision proxy definitions. Within each entry, each attribute is separated by a semicolon. The attributes are, in this order: 1) Attachment Name - String: Name of attachment that box belongs to. 2) Slot Name - String: Slot that owns the attachment. 3) Skin Name - String: Skin that defines the attachment. 4) Width Sizer - Float: Factor useful to tweak the width of the box. 5) Height Sizer - Float: Factor useful to tweak the height of the box. 6) SimObject Name - String: Name assigned to the collision proxy instance. May be NULL if no name was assigned.");
  124. }
  125. //------------------------------------------------------------------------------
  126. void SpineObject::resetState() {
  127. mPreTickTime = 0.0f;
  128. mPostTickTime = 0.0f;
  129. mLastFrameTime = 0.0f;
  130. mFlipX = mFlipY = false;
  131. mSkeleton.reset();
  132. mAnimationState.reset();
  133. mSkeletonClipping.reset();
  134. mSkeletonBounds.reset();
  135. mAutoCenterOffset.SetZero();
  136. // Don't want b2DynamicTree supported search (for culling reasons) being performed
  137. // on our couple-o-sprites lashup.
  138. setBatchCulling(false);
  139. mVertexEffect = NULL;
  140. mActiveEffect = VertexEffect::NONE;
  141. mPriorRootBoneWorldX = 0.0f;
  142. mPriorRootBoneWorldY = 0.0f;
  143. mPriorFlipX = mFlipX;
  144. mPriorFlipY = mFlipY;
  145. mCollisionProxies.clear();
  146. }
  147. //------------------------------------------------------------------------------
  148. // NOTE: Update this if new state members are added to the object.
  149. void SpineObject::copyTo(SimObject* object)
  150. {
  151. // Call to parent.
  152. Parent::copyTo(object);
  153. // Fetch object.
  154. SpineObject* pSpine = dynamic_cast<SpineObject*>(object);
  155. // Sanity!
  156. AssertFatal(pSpine != NULL, "SpineObject::copyTo() - Object is not the correct type.");
  157. // Copy state.
  158. pSpine->setSpineAsset(getSpineAsset());
  159. pSpine->setFlipX(getFlipX());
  160. pSpine->setFlipY(getFlipY());
  161. pSpine->setPosition(getPosition());
  162. pSpine->setAngle(getAngle());
  163. pSpine->setAnimation(getAnimationName(), getIsLooping());
  164. pSpine->setSkin(getSkinName());
  165. pSpine->setScale(getScale());
  166. pSpine->mAutoCenterOffset = mAutoCenterOffset;
  167. copyCollisionShapes(pSpine);
  168. }
  169. //-----------------------------------------------------------------------------
  170. bool SpineObject::setSpineAsset(const char* pSpineAssetId)
  171. {
  172. // Sanity!
  173. if (pSpineAssetId == NULL)
  174. {
  175. Con::errorf("SpineObject::setSpineAsset() - Cannot use a NULL asset Id.");
  176. return false;
  177. }
  178. // Fetch the asset.
  179. AssetPtr<SpineAsset> temp = StringTable->insert(pSpineAssetId, true);
  180. if (temp->mImageAsset.isNull())
  181. {
  182. Con::errorf("SpineObject::setSpineAsset() - Image asset is undefined.");
  183. return false;
  184. }
  185. // Clear away existing.
  186. clearSprites();
  187. resetState();
  188. // Reflect asset definition
  189. mSpineAsset = std::move(temp);
  190. mSkeleton = skeleton_ptr(spSkeleton_create(mSpineAsset->mSkeletonData));
  191. mAnimationState = animationState_ptr(spAnimationState_create(mSpineAsset->mAnimationStateData));
  192. mSkeletonBounds = skeletonBounds_ptr(spSkeletonBounds_create());
  193. mSkeletonClipping = skeletonClipping_ptr(spSkeletonClipping_create());
  194. spSkeleton_setToSetupPose(mSkeleton.get());
  195. // Needed by the events system to route callbacks.
  196. mAnimationState->userData = this;
  197. // Prepare for animation.
  198. updateSpine(0.0f);
  199. return true;
  200. }
  201. //-----------------------------------------------------------------------------
  202. void SpineObject::setFlip(const bool flipX, const bool flipY) {
  203. mFlipX = flipX;
  204. mSkeleton->scaleX = flipX
  205. ? mSkeleton->scaleX < 0 ? mSkeleton->scaleX : -mSkeleton->scaleX
  206. : mSkeleton->scaleX >= 0 ? mSkeleton->scaleX : -mSkeleton->scaleX;
  207. mFlipY = flipY;
  208. mSkeleton->scaleY = flipY
  209. ? mSkeleton->scaleY < 0 ? mSkeleton->scaleY : -mSkeleton->scaleY
  210. : mSkeleton->scaleY >= 0 ? mSkeleton->scaleY : -mSkeleton->scaleY;
  211. }
  212. //-----------------------------------------------------------------------------
  213. F32 SpineObject::setTimeScale(const F32 timeScale) {
  214. if (!mAnimationState)
  215. return 0.0f;
  216. F32 previousValue = mAnimationState->timeScale;
  217. mAnimationState->timeScale = timeScale;
  218. return previousValue;
  219. }
  220. //-----------------------------------------------------------------------------
  221. bool SpineObject::setAnimation(const char* pName, const int track, const bool shouldLoop, const F32 mixDuration)
  222. {
  223. if (!mAnimationState)
  224. return false;
  225. // Check to see if the requested animation is valid
  226. auto animation = spSkeletonData_findAnimation(mSkeleton->data, StringTable->insert(pName, true));
  227. if (!animation)
  228. {
  229. Con::warnf("SpineObject::setAnimation() - Animation '%s' does not exist.", StringTable->insert(pName, true));
  230. return false;
  231. }
  232. // Set the animation.
  233. spTrackEntry* entry = spAnimationState_setAnimation(mAnimationState.get(), track, animation, shouldLoop);
  234. if (mixDuration < 0.0f) {
  235. // Use default mix duration
  236. return entry != NULL;
  237. }
  238. else {
  239. // Use given mix duration.
  240. entry->mixDuration = mixDuration;
  241. return true;
  242. }
  243. }
  244. //-----------------------------------------------------------------------------
  245. bool SpineObject::setEmptyAnimation(const int track, const F32 mixDuration) {
  246. if (!mAnimationState)
  247. return false;
  248. return spAnimationState_setEmptyAnimation(mAnimationState.get(), track, mixDuration) != NULL;
  249. }
  250. //-----------------------------------------------------------------------------
  251. bool SpineObject::queueAnimation(const char* pName, const int track, const bool shouldLoop, const F32 mixDuration, const F32 delay)
  252. {
  253. if (!mAnimationState)
  254. return false;
  255. // Check to see if the requested animation is valid
  256. auto animation = spSkeletonData_findAnimation(mSkeleton->data, StringTable->insert(pName, true));
  257. if (!animation)
  258. {
  259. Con::warnf("SpineObject::queueAnimation() - Animation '%s' does not exist.", StringTable->insert(pName, true));
  260. return false;
  261. }
  262. // Set the animation.
  263. spTrackEntry* entry = spAnimationState_addAnimation(mAnimationState.get(), track, animation, shouldLoop, delay);
  264. if (mixDuration < 0.0f) {
  265. // Use default mix duration
  266. return entry != NULL;
  267. }
  268. else {
  269. // Use given mix duration.
  270. entry->mixDuration = mixDuration;
  271. return true;
  272. }
  273. }
  274. //-----------------------------------------------------------------------------
  275. bool SpineObject::queueEmptyAnimation(const int track, const F32 mixDuration, const F32 delay) {
  276. if (!mAnimationState)
  277. return false;
  278. return spAnimationState_addEmptyAnimation(mAnimationState.get(), track, mixDuration, delay) != NULL;
  279. }
  280. //-----------------------------------------------------------------------------
  281. void SpineObject::clearAnimations(const int track, const bool mixToSetupPose, const F32 mixDuration) {
  282. if (!mAnimationState)
  283. return;
  284. spAnimationState_clearTrack(mAnimationState.get(), track);
  285. if (mixToSetupPose)
  286. setEmptyAnimation(track, mixDuration);
  287. }
  288. //-----------------------------------------------------------------------------
  289. void SpineObject::clearAllAnimations(const bool mixToSetupPose, const F32 mixDuration) {
  290. if (!mAnimationState)
  291. return;
  292. spAnimationState_clearTracks(mAnimationState.get());
  293. if (mixToSetupPose)
  294. spAnimationState_setEmptyAnimations(mAnimationState.get(), mixDuration);
  295. }
  296. //-----------------------------------------------------------------------------
  297. StringTableEntry SpineObject::getAnimationName(const int track) const {
  298. if (!mAnimationState)
  299. return StringTable->EmptyString;
  300. spTrackEntry* entry = spAnimationState_getCurrent(mAnimationState.get(), track);
  301. if (!entry)
  302. return StringTable->EmptyString;
  303. return StringTable->insert(entry->animation->name, true);
  304. }
  305. //-----------------------------------------------------------------------------
  306. bool SpineObject::getIsLooping(const int track) const {
  307. if (!mAnimationState)
  308. return StringTable->EmptyString;
  309. spTrackEntry* entry = spAnimationState_getCurrent(mAnimationState.get(), track);
  310. if (!entry)
  311. return StringTable->EmptyString;
  312. return entry->loop;
  313. }
  314. //-----------------------------------------------------------------------------
  315. bool SpineObject::setMix(const char* pFromAnimation, const char* pToAnimation, const F32 time)
  316. {
  317. if (mSpineAsset.isNull())
  318. {
  319. Con::warnf("SpineObject::setMix() - Cannot mix. No asset assigned");
  320. return false;
  321. }
  322. // Check for valid animation state data
  323. AssertFatal(mSpineAsset->mAnimationStateData != NULL, "SpineObject::setMix() - Animation state data invalid");
  324. // Check to see if the "from animation" is valid
  325. auto from = spSkeletonData_findAnimation(mSkeleton->data, StringTable->insert(pFromAnimation, true));
  326. if (!from)
  327. {
  328. Con::warnf("SpineObject::setMix() - Animation '%s' does not exist.", StringTable->insert(pFromAnimation, true));
  329. return false;
  330. }
  331. // Check to see if the "to animation" is valid
  332. auto to = spSkeletonData_findAnimation(mSkeleton->data, StringTable->insert(pToAnimation, true));
  333. if (!to)
  334. {
  335. Con::warnf("SpineObject::setMix() - Animation '%s' does not exist.", StringTable->insert(pToAnimation, true));
  336. return false;
  337. }
  338. // Check to see if a valid mix time was passsed
  339. if (time < 0.0f)
  340. {
  341. Con::warnf("SpineObject::setMix() - Invalid time set, '%f'", time);
  342. return false;
  343. }
  344. spAnimationStateData_setMix(mSpineAsset->mAnimationStateData, from, to, time);
  345. return true;
  346. }
  347. //-----------------------------------------------------------------------------
  348. bool SpineObject::setSkin(const char* pSkin)
  349. {
  350. if (mSpineAsset.isNull() || !mSkeleton)
  351. {
  352. Con::errorf("SpineObject::setSkin() - Spine asset was null or skeleton was not built.");
  353. return false;
  354. }
  355. auto to = spSkeletonData_findSkin(mSkeleton->data, StringTable->insert(pSkin, true));
  356. if (!to)
  357. {
  358. Con::warnf("SpineObject::setSkin() - Skin '%s' does not exist.", StringTable->insert(pSkin, true));
  359. return false;
  360. }
  361. spSkeleton_setSkin(mSkeleton.get(), to);
  362. spSkeleton_setSlotsToSetupPose(mSkeleton.get());
  363. spAnimationState_apply(mAnimationState.get(), mSkeleton.get());
  364. return true;
  365. }
  366. //-----------------------------------------------------------------------------
  367. inline StringTableEntry SpineObject::getSkinName(void) const {
  368. if (!mAnimationState || !mSkeleton)
  369. return StringTable->EmptyString;
  370. spSkin* pSkin = mSkeleton->skin;
  371. if (!pSkin) {
  372. AssertFatal(mSkeleton->data->defaultSkin, avar("SpineObject::getSkinName() - Skin name is undefined in '%s'. Is file valid?", mSpineAsset->mSpineFile));
  373. // Using default skin.
  374. return StringTable->insert(mSkeleton->data->defaultSkin->name, true);
  375. }
  376. else {
  377. return StringTable->insert(pSkin->name, true);
  378. }
  379. }
  380. //-----------------------------------------------------------------------------
  381. void SpineObject::setScale(const Vector2& scale)
  382. {
  383. if (!mSkeleton)
  384. return;
  385. // Set scale, but keep orientation. This is used by spine to flip the character
  386. // as well as set its size.
  387. mSkeleton->scaleX = mSkeleton->scaleX < 0.0f ? -scale.x : scale.x;
  388. mSkeleton->scaleY = mSkeleton->scaleY < 0.0f ? -scale.y : scale.y;
  389. }
  390. //------------------------------------------------------------------------------
  391. void SpineObject::setActiveEffect(const VertexEffect requestedEffect) {
  392. if (mActiveEffect == requestedEffect)
  393. return;
  394. switch (requestedEffect) {
  395. case VertexEffect::JITTER:
  396. if (!mJitterControl) {
  397. mJitterControl = jitterEffect_ptr(spJitterVertexEffect_create(0, 0));
  398. }
  399. mVertexEffect = &mJitterControl->super;
  400. mActiveEffect = VertexEffect::JITTER;
  401. break;
  402. case VertexEffect::SWIRL:
  403. if (!mSwirlControl) {
  404. mSwirlControl = swirlEffect_ptr(spSwirlVertexEffect_create(0));
  405. }
  406. mVertexEffect = &mSwirlControl->super;
  407. mActiveEffect = VertexEffect::SWIRL;
  408. break;
  409. default:
  410. Con::warnf("SpineObject::setActiveEffect - Unrecognized vertex special effect requested: '%s'.",
  411. getVertexEffectTypeDescription(requestedEffect));
  412. }
  413. }
  414. //------------------------------------------------------------------------------
  415. void SpineObject::enableJitter(const F32 x, const F32 y) {
  416. if (mJitterControl) {
  417. mJitterControl->jitterX = x;
  418. mJitterControl->jitterY = y;
  419. }
  420. else {
  421. mJitterControl = jitterEffect_ptr(spJitterVertexEffect_create(x, y));
  422. }
  423. mVertexEffect = &mJitterControl->super;
  424. mActiveEffect = VertexEffect::JITTER;
  425. }
  426. //------------------------------------------------------------------------------
  427. void SpineObject::disableJitter() {
  428. if (mActiveEffect == VertexEffect::JITTER) {
  429. mVertexEffect = NULL;
  430. mActiveEffect = VertexEffect::NONE;
  431. }
  432. }
  433. //------------------------------------------------------------------------------
  434. void SpineObject::enableSwirl(const F32 radius) {
  435. if (mSwirlControl) {
  436. mSwirlControl->radius = radius;
  437. }
  438. else {
  439. mSwirlControl = swirlEffect_ptr(spSwirlVertexEffect_create(radius));
  440. }
  441. mVertexEffect = &mSwirlControl->super;
  442. mActiveEffect = VertexEffect::SWIRL;
  443. }
  444. //------------------------------------------------------------------------------
  445. void SpineObject::disableSwirl() {
  446. if (mActiveEffect == VertexEffect::SWIRL) {
  447. mVertexEffect = NULL;
  448. mActiveEffect = VertexEffect::NONE;
  449. }
  450. }
  451. //------------------------------------------------------------------------------
  452. void SpineObject::enableEventCallbacks(void) {
  453. // No animation state
  454. if (!mAnimationState)
  455. return;
  456. // Already listening
  457. if (mAnimationState->listener)
  458. return;
  459. mAnimationState->listener = spineAnimationEventCallbackHandler;
  460. }
  461. //-----------------------------------------------------------------------------
  462. void SpineObject::disableEventCallbacks(void) {
  463. // No animation state
  464. if (!mAnimationState)
  465. return;
  466. // Not listening anyway.
  467. if (!mAnimationState->listener)
  468. return;
  469. mAnimationState->listener = NULL;
  470. }
  471. //------------------------------------------------------------------------------
  472. void SpineObject::preIntegrate(const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats)
  473. {
  474. Parent::preIntegrate(totalTime, elapsedTime, pDebugStats);
  475. // Note tick times.
  476. mPreTickTime = mPostTickTime;
  477. mPostTickTime = totalTime;
  478. // Update at pre-tick time.
  479. updateSpine(mPreTickTime);
  480. prepareSpineForRender();
  481. }
  482. //-----------------------------------------------------------------------------
  483. void SpineObject::interpolateObject(const F32 timeDelta)
  484. {
  485. Parent::interpolateObject(timeDelta);
  486. // Update time (interpolated).
  487. F32 timeInterp = (timeDelta * mPreTickTime) + ((1.0f - timeDelta) * mPostTickTime);
  488. updateSpine(timeInterp);
  489. prepareSpineForRender();
  490. }
  491. //-----------------------------------------------------------------------------
  492. void SpineObject::scenePrepareRender(const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue)
  493. {
  494. // Set batch transform to identity because the skeleton is responsible for
  495. // its geometry's position
  496. setBatchTransform(B2_IDENTITY_TRANSFORM);
  497. SpriteBatch::prepareRender(this, pSceneRenderState, pSceneRenderQueue);
  498. }
  499. //-----------------------------------------------------------------------------
  500. void SpineObject::sceneRender(const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer)
  501. {
  502. // Render.
  503. SpriteBatch::render(pSceneRenderState, pSceneRenderRequest, pBatchRenderer);
  504. }
  505. //-----------------------------------------------------------------------------
  506. void SpineObject::updateSpine(const F32 time)
  507. {
  508. // Update the skeleton's position/rotation.
  509. Vector2 p = getPosition();
  510. mSkeleton->x = p.x - mAutoCenterOffset.x;
  511. mSkeleton->y = p.y - mAutoCenterOffset.y;
  512. mSkeleton->root->rotation = setPerFlipState(mRadToDeg(getAngle()));
  513. // Advance time
  514. F32 delta = (time - mLastFrameTime);
  515. mLastFrameTime = time;
  516. spSkeleton_update(mSkeleton.get(), delta);
  517. spAnimationState_update(mAnimationState.get(), delta);
  518. spAnimationState_apply(mAnimationState.get(), mSkeleton.get());
  519. spSkeleton_updateWorldTransform(mSkeleton.get());
  520. }
  521. //-----------------------------------------------------------------------------
  522. void SpineObject::prepareSpineForRender()
  523. {
  524. // Early out if skeleton is invisible
  525. if (mSkeleton->color.a == 0) {
  526. for (const auto& i : mCollisionProxies) {
  527. i.value->deActivate();
  528. }
  529. return;
  530. }
  531. if (mVertexEffect)
  532. mVertexEffect->begin(mVertexEffect, mSkeleton.get());
  533. // Get the ImageAsset used by the sprites
  534. StringTableEntry assetId = mSpineAsset->mImageAsset.getAssetId();
  535. clearSprites();
  536. b2AABB spriteAABB;
  537. U16 quadIndices[6] = { 0, 1, 2, 2, 3, 0 };
  538. FrameTemp<F32> VertexBuffer(BatchRender::maxVertexCount);
  539. vector<Vector2> pointSoup;
  540. for (int i = 0; i < mSkeleton->slotsCount; ++i)
  541. {
  542. auto slot = mSkeleton->drawOrder[i];
  543. if (!slot)
  544. continue;
  545. auto attachment = slot->attachment;
  546. if (!attachment)
  547. continue;
  548. SpineCollisionProxy* pCollisionProxy = NULL;
  549. auto itr = mCollisionProxies.find(attachment);
  550. if (itr != mCollisionProxies.end()) {
  551. pCollisionProxy = itr->value;
  552. }
  553. if (slot->color.a == 0 || !slot->bone->active) {
  554. spSkeletonClipping_clipEnd(mSkeletonClipping.get(), slot);
  555. if (pCollisionProxy && pCollisionProxy->mActive) {
  556. pCollisionProxy->deActivate();
  557. }
  558. continue;
  559. }
  560. F32 *uvs = 0;
  561. U16 *indices = 0;
  562. int indicesCount = 0;
  563. F32 *vertices = VertexBuffer; // It will be redirected to a different buffer if clipping is performed.
  564. int verticesCount = 0;
  565. spColor* attachmentColor = NULL;
  566. string attachmentName;
  567. if (attachment->type == SP_ATTACHMENT_REGION) {
  568. auto regionAttachment = (spRegionAttachment*)attachment;
  569. attachmentName = StringTable->insert(regionAttachment->path ? regionAttachment->path : attachment->name, true);
  570. attachmentColor = &regionAttachment->color;
  571. // Is slot invisible?
  572. if (attachmentColor->a == 0) {
  573. spSkeletonClipping_clipEnd(mSkeletonClipping.get(), slot);
  574. if (pCollisionProxy && pCollisionProxy->mActive) {
  575. pCollisionProxy->deActivate();
  576. }
  577. continue;
  578. }
  579. if (pCollisionProxy && !pCollisionProxy->mActive) {
  580. pCollisionProxy->activate();
  581. }
  582. auto currentRegion = (spAtlasRegion *)regionAttachment->rendererObject;
  583. spRegionAttachment_updateOffset(regionAttachment);
  584. spRegionAttachment_setUVs(regionAttachment, currentRegion->u, currentRegion->v, currentRegion->u2, currentRegion->v2, currentRegion->rotate);
  585. spRegionAttachment_computeWorldVertices(regionAttachment, slot->bone, vertices, 0, 2);
  586. verticesCount = 4;
  587. uvs = regionAttachment->uvs;
  588. indices = quadIndices;
  589. indicesCount = 6;
  590. }
  591. else if (attachment->type == SP_ATTACHMENT_MESH) {
  592. auto meshAttachment = (spMeshAttachment*)attachment;
  593. attachmentName = StringTable->insert(meshAttachment->path ? meshAttachment->path : attachment->name, true);
  594. attachmentColor = &meshAttachment->color;
  595. // Is slot invisible?
  596. if (attachmentColor->a == 0) {
  597. spSkeletonClipping_clipEnd(mSkeletonClipping.get(), slot);
  598. if (pCollisionProxy && pCollisionProxy->mActive) {
  599. pCollisionProxy->deActivate();
  600. }
  601. continue;
  602. }
  603. // Vertex buffer overrun?
  604. if (meshAttachment->super.worldVerticesLength > BatchRender::maxVertexCount) {
  605. Con::warnf("Mesh attachment '%s' exceeded vertex buffer size. Mesh count was %d. Buffer size is %d.\n",
  606. attachmentName, meshAttachment->super.worldVerticesLength, BatchRender::maxVertexCount);
  607. continue;
  608. }
  609. if (pCollisionProxy && !pCollisionProxy->mActive) {
  610. pCollisionProxy->activate();
  611. }
  612. // Compute updated render info.
  613. spVertexAttachment_computeWorldVertices(&meshAttachment->super, slot, 0, meshAttachment->super.worldVerticesLength, vertices, 0, 2);
  614. verticesCount = meshAttachment->super.worldVerticesLength >> 1;
  615. uvs = meshAttachment->uvs;
  616. indices = meshAttachment->triangles;
  617. indicesCount = meshAttachment->trianglesCount;
  618. }
  619. else if (attachment->type == SP_ATTACHMENT_CLIPPING) {
  620. auto clippingAttachment = (spClippingAttachment*)attachment;
  621. spSkeletonClipping_clipStart(mSkeletonClipping.get(), slot, clippingAttachment);
  622. continue;
  623. }
  624. else continue;
  625. // Perform clipping if active
  626. if (spSkeletonClipping_isClipping(mSkeletonClipping.get())) {
  627. spSkeletonClipping_clipTriangles(mSkeletonClipping.get(), vertices, verticesCount << 1, indices, indicesCount, uvs, 2);
  628. vertices = mSkeletonClipping->clippedVertices->items;
  629. verticesCount = mSkeletonClipping->clippedVertices->size >> 1;
  630. uvs = mSkeletonClipping->clippedUVs->items;
  631. indices = mSkeletonClipping->clippedTriangles->items;
  632. indicesCount = mSkeletonClipping->clippedTriangles->size;
  633. }
  634. if (!verticesCount) {
  635. // No geometry to display.
  636. if (pCollisionProxy && pCollisionProxy->mActive) {
  637. pCollisionProxy->deActivate();
  638. }
  639. continue;
  640. }
  641. if (!mSpineAsset->getPreMultipliedAlpha()) {
  642. // Not using Premultiplied Alpha
  643. switch (slot->data->blendMode) {
  644. case SP_BLEND_MODE_ADDITIVE:
  645. setSrcBlendFactor(GL_SRC_ALPHA);
  646. setDstBlendFactor(GL_ONE);
  647. break;
  648. case SP_BLEND_MODE_MULTIPLY:
  649. setSrcBlendFactor(GL_DST_COLOR);
  650. setDstBlendFactor(GL_ONE_MINUS_SRC_ALPHA);
  651. break;
  652. case SP_BLEND_MODE_SCREEN:
  653. setSrcBlendFactor(GL_ONE);
  654. setDstBlendFactor(GL_ONE_MINUS_SRC_COLOR);
  655. break;
  656. case SP_BLEND_MODE_NORMAL:
  657. default:
  658. setSrcBlendFactor(GL_SRC_ALPHA);
  659. setDstBlendFactor(GL_ONE_MINUS_SRC_ALPHA);
  660. break;
  661. }
  662. }
  663. else {
  664. // Setup for Premultiplied Alpha
  665. switch (slot->data->blendMode) {
  666. case SP_BLEND_MODE_ADDITIVE:
  667. setSrcBlendFactor(GL_ONE);
  668. setDstBlendFactor(GL_ONE);
  669. break;
  670. case SP_BLEND_MODE_MULTIPLY:
  671. setSrcBlendFactor(GL_DST_COLOR);
  672. setDstBlendFactor(GL_ONE_MINUS_SRC_ALPHA);
  673. break;
  674. case SP_BLEND_MODE_SCREEN:
  675. setSrcBlendFactor(GL_ONE);
  676. setDstBlendFactor(GL_ONE_MINUS_SRC_COLOR);
  677. break;
  678. case SP_BLEND_MODE_NORMAL:
  679. default:
  680. setSrcBlendFactor(GL_ONE);
  681. setDstBlendFactor(GL_ONE_MINUS_SRC_ALPHA);
  682. break;
  683. }
  684. }
  685. // Define sprite carrier object. NOTE: This isn't a Sprite. It's a SpriteBatchItem, which is completely different
  686. // than a Sprite. It's more like a render request headed for the batch renderer.
  687. SpriteBatchItem* pSprite = SpriteBatch::createSprite();
  688. pSprite->setDepth(mSceneLayerDepth);
  689. pSprite->setSrcBlendFactor(mSrcBlendFactor);
  690. pSprite->setDstBlendFactor(mDstBlendFactor);
  691. F32 r = mSkeleton->color.r * slot->color.r * attachmentColor->r;
  692. F32 g = mSkeleton->color.g * slot->color.g * attachmentColor->g;
  693. F32 b = mSkeleton->color.b * slot->color.b * attachmentColor->b;
  694. F32 a = mSkeleton->color.a * slot->color.a * attachmentColor->a;
  695. spColor light;
  696. light.r = r; light.g = g; light.b = b; light.a = a;
  697. SpriteBatchItem::drawData *pDrawData = pSprite->getDrawData();
  698. pDrawData->size(indicesCount);
  699. spriteAABB.lowerBound.x = spriteAABB.upperBound.x = vertices[0];
  700. spriteAABB.lowerBound.y = spriteAABB.upperBound.y = vertices[1];
  701. if (mVertexEffect != NULL) {
  702. vector<F32> vertexEffectUVs;
  703. vector<spColor> vertexEffectColors;
  704. for (int j = 0; j < verticesCount; j++) {
  705. spColor vertexColor = light;
  706. spColor dark;
  707. dark.r = dark.g = dark.b = dark.a = 0;
  708. int index = j << 1;
  709. float x = vertices[index];
  710. float y = vertices[index + 1];
  711. float u = uvs[index];
  712. float v = uvs[index + 1];
  713. mVertexEffect->transform(mVertexEffect, &x, &y, &u, &v, &vertexColor, &dark);
  714. vertices[index] = x;
  715. vertices[index + 1] = y;
  716. vertexEffectUVs.push_back(u);
  717. vertexEffectUVs.push_back(v);
  718. vertexEffectColors.push_back(vertexColor);
  719. }
  720. for (int j = 0; j < indicesCount; ++j) {
  721. int index = indices[j] << 1;
  722. F32 x = vertices[index];
  723. F32 y = vertices[index + 1];
  724. spColor vertexColor = vertexEffectColors[index >> 1];
  725. pDrawData->vertexArray[j].Set(x, y);
  726. pDrawData->textureArray[j].Set(vertexEffectUVs[index], vertexEffectUVs[index + 1]);
  727. pDrawData->colorArray[j].red = vertexColor.r;
  728. pDrawData->colorArray[j].green = vertexColor.g;
  729. pDrawData->colorArray[j].blue = vertexColor.b;
  730. pDrawData->colorArray[j].alpha = vertexColor.a;
  731. spriteAABB.lowerBound.x = x < spriteAABB.lowerBound.x ? x : spriteAABB.lowerBound.x;
  732. spriteAABB.lowerBound.y = y < spriteAABB.lowerBound.y ? y : spriteAABB.lowerBound.y;
  733. spriteAABB.upperBound.x = x > spriteAABB.upperBound.x ? x : spriteAABB.upperBound.x;
  734. spriteAABB.upperBound.y = y > spriteAABB.upperBound.y ? y : spriteAABB.upperBound.y;
  735. }
  736. }
  737. else {
  738. for (int j = 0; j < indicesCount; ++j) {
  739. int index = indices[j] << 1;
  740. F32 x = vertices[index];
  741. F32 y = vertices[index + 1];
  742. pDrawData->vertexArray[j].Set(vertices[index], vertices[index + 1]);
  743. pDrawData->textureArray[j].Set(uvs[index], uvs[index + 1]);
  744. pDrawData->colorArray[j].red = r;
  745. pDrawData->colorArray[j].green = g;
  746. pDrawData->colorArray[j].blue = b;
  747. pDrawData->colorArray[j].alpha = a;
  748. spriteAABB.lowerBound.x = x < spriteAABB.lowerBound.x ? x : spriteAABB.lowerBound.x;
  749. spriteAABB.lowerBound.y = y < spriteAABB.lowerBound.y ? y : spriteAABB.lowerBound.y;
  750. spriteAABB.upperBound.x = x > spriteAABB.upperBound.x ? x : spriteAABB.upperBound.x;
  751. spriteAABB.upperBound.y = y > spriteAABB.upperBound.y ? y : spriteAABB.upperBound.y;
  752. }
  753. }
  754. // Save the sprite's aabb on the sprite.
  755. F32 ev[]{
  756. spriteAABB.upperBound.x, spriteAABB.upperBound.y, //LL
  757. spriteAABB.lowerBound.x, spriteAABB.upperBound.y, //LR
  758. spriteAABB.lowerBound.x, spriteAABB.lowerBound.y, //UR
  759. spriteAABB.upperBound.x, spriteAABB.lowerBound.y }; //UL
  760. pSprite->setExplicitVertices(ev);
  761. pSprite->setTriangleRun(true);
  762. pSprite->setImage(assetId, attachmentName.c_str());
  763. spSkeletonClipping_clipEnd(mSkeletonClipping.get(), slot);
  764. if (pCollisionProxy) {
  765. // Center collision box on attachment.
  766. pCollisionProxy->setPosition(
  767. Vector2((spriteAABB.lowerBound.x + spriteAABB.upperBound.x) * 0.5f,
  768. (spriteAABB.lowerBound.y + spriteAABB.upperBound.y) * 0.5f)
  769. );
  770. pCollisionProxy->setAngle(
  771. mDegToRad(spBone_localToWorldRotation(slot->bone, slot->bone->rotation + pCollisionProxy->mRotation))
  772. );
  773. }
  774. // Capture this sprite's vertices for OOBB calculation later.
  775. pointSoup.insert(pointSoup.end(), pDrawData->vertexArray.begin(), pDrawData->vertexArray.end());
  776. }
  777. spSkeletonClipping_clipEnd2(mSkeletonClipping.get());
  778. if (mVertexEffect)
  779. mVertexEffect->end(mVertexEffect);
  780. calculateSpineOOBB(pointSoup);
  781. }
  782. //-----------------------------------------------------------------------------
  783. // This spins through all of the spine object's drawn points and calculates its OOBB.
  784. // It then updates the location of the SceneObject to center it over the spine object.
  785. void SpineObject::calculateSpineOOBB(const vector<Vector2> pointSoup) {
  786. if (!pointSoup.size()) {
  787. setSize(Vector2(0.0f, 0.0f));
  788. setLocalExtentsDirty();
  789. updateLocalExtents();
  790. return;
  791. }
  792. // First step is to get the AABB.
  793. b2AABB oobb = getLocalAABB();
  794. if (getAngle() == 0.0f) {
  795. // The OOBB coincides with the AABB so we are done.
  796. setSize(oobb.upperBound - oobb.lowerBound);
  797. }
  798. else {
  799. // Second step is to rotate the soup about its center to axis align it and
  800. // grab its AABB.
  801. b2Rot rotation(-getAngle());
  802. b2Transform t(oobb.GetCenter(), rotation);
  803. b2Vec2 pp = CoreMath::mRotateAboutArbitraryPoint(t, pointSoup[0]);
  804. oobb.lowerBound.Set(pp.x, pp.y);
  805. oobb.upperBound.Set(pp.x, pp.y);
  806. for (const auto& p : pointSoup) {
  807. pp = CoreMath::mRotateAboutArbitraryPoint(t, p);
  808. oobb.lowerBound.x = pp.x < oobb.lowerBound.x ? pp.x : oobb.lowerBound.x;
  809. oobb.lowerBound.y = pp.y < oobb.lowerBound.y ? pp.y : oobb.lowerBound.y;
  810. oobb.upperBound.x = pp.x > oobb.upperBound.x ? pp.x : oobb.upperBound.x;
  811. oobb.upperBound.y = pp.y > oobb.upperBound.y ? pp.y : oobb.upperBound.y;
  812. }
  813. // We now have the updated AABB. This is what the SceneObject needs, so
  814. // pass it in.
  815. updateLocalExtents(&oobb);
  816. setSize(oobb.upperBound - oobb.lowerBound);
  817. // Third step is to complete calculation of the OOBB by aligning it back to
  818. // the spine object.
  819. t.q.Set(getAngle());
  820. oobb.lowerBound = CoreMath::mRotateAboutArbitraryPoint(t, oobb.lowerBound);
  821. oobb.upperBound = CoreMath::mRotateAboutArbitraryPoint(t, oobb.upperBound);
  822. }
  823. // Reposition the SceneObject so that it is centered over the spine object.
  824. Vector2 actualCenter = oobb.GetCenter();
  825. Vector2 sceneObjectCenter = getPosition();
  826. Vector2 delta = actualCenter - sceneObjectCenter;
  827. // Nullify the reposition of the SceneObject by incorporating its inverse
  828. // into the spine object's position.
  829. mAutoCenterOffset.x += delta.x;
  830. mAutoCenterOffset.y += delta.y;
  831. // Handle special case of the spine object being 'flipped' in either axis. When
  832. // that happens, we want to keep the location of the root bone at roughly the
  833. // same position on screen as it was prior to the flip. This helps to reduce
  834. // a jarring reposition of the root bone.
  835. bool doFlipRecurse = false;
  836. if (mPriorFlipX == mFlipX) {
  837. mPriorRootBoneWorldX = mSkeleton->root->worldX;
  838. }
  839. else {
  840. // Compensate for the flip in X (about the y axis). This is an approximation
  841. // that will fail if the velocity of the spine object is large. If that
  842. // becomes a problem, incorporate the velocity delta into the 'prior world x'
  843. // to have the compensated position reflect the underlying movement.
  844. mPriorFlipX = mFlipX;
  845. doFlipRecurse = true;
  846. delta.x += mPriorRootBoneWorldX - mSkeleton->root->worldX;
  847. }
  848. if (mPriorFlipY == mFlipY) {
  849. mPriorRootBoneWorldY = mSkeleton->root->worldY;
  850. }
  851. else {
  852. mPriorFlipY = mFlipY;
  853. doFlipRecurse = true;
  854. delta.y += mPriorRootBoneWorldY - mSkeleton->root->worldY;
  855. }
  856. setPosition(sceneObjectCenter + delta);
  857. if (doFlipRecurse) {
  858. // Trash the current render data and recalculate it. This avoids a flash
  859. // artifact due to possibly large changes coming from the flip.
  860. this->clearSprites();
  861. updateSpine(mLastFrameTime);
  862. prepareSpineForRender();
  863. }
  864. }
  865. //-----------------------------------------------------------------------------
  866. const SpineCollisionProxy* SpineObject::getCollisionProxy(
  867. const char* anAttachmentName,
  868. const char* aSlotName,
  869. const char* aSkinName,
  870. const F32 sizerWidth,
  871. const F32 sizerHeight,
  872. const char* anObjectName) {
  873. // First, locate the requested attachment.
  874. spAttachment* pAttachment = NULL;
  875. auto attachmentName = StringTable->insert(anAttachmentName, true);
  876. auto slotName = StringTable->insert(aSlotName, true);
  877. auto pSlotData = spSkeletonData_findSlot(mSkeleton->data, slotName);
  878. if (!pSlotData) {
  879. Con::warnf("SpineObject::getCollisionProxy - Slot not found: '%s'.", slotName);
  880. return NULL;
  881. }
  882. auto skinName = StringTable->insert(aSkinName, true);
  883. if (skinName == StringTable->EmptyString || dStricmp(skinName, "default") == 0) {
  884. // Look in the currently active skin and then the default skin if needed.
  885. pAttachment = spSkeleton_getAttachmentForSlotIndex(mSkeleton.get(), pSlotData->index, attachmentName);
  886. if (!pAttachment) {
  887. Con::warnf("SpineObject::getCollisionProxy - Attachment not found: '%s'.", attachmentName);
  888. return NULL;
  889. }
  890. }
  891. else {
  892. // Attachment must exist in the skin name given or else it's an error.
  893. auto pSkin = spSkeletonData_findSkin(mSkeleton->data, skinName);
  894. if (!pSkin) {
  895. Con::warnf("SpineObject::getCollisionProxy - Skin not found: '%s'.", skinName);
  896. return NULL;
  897. }
  898. pAttachment = spSkin_getAttachment(pSkin, pSlotData->index, attachmentName);
  899. if (!pAttachment) {
  900. Con::warnf("SpineObject::getCollisionProxy - Attachment not found in requested skin.\nskin: '%s'. attachment: '%s'.", skinName, attachmentName);
  901. return NULL;
  902. }
  903. }
  904. // Check that the attachment is a supported type.
  905. if (pAttachment->type != spAttachmentType::SP_ATTACHMENT_REGION && pAttachment->type != spAttachmentType::SP_ATTACHMENT_MESH) {
  906. Con::warnf("SpineObject::getCollisionProxy - Attachment requested is of an unsupported type. Only Region and Mesh attachments are supported.");
  907. return NULL;
  908. }
  909. // objectName is not considered here. Multiple proxies for the same attachment is
  910. // not supported.
  911. auto itr = mCollisionProxies.find(pAttachment);
  912. if (itr != mCollisionProxies.end()) {
  913. return itr->value;
  914. }
  915. auto pBone = spSkeleton_findBone(mSkeleton.get(), pSlotData->boneData->name);
  916. if (!pBone) {
  917. Con::warnf("SpineObject::getCollisionProxy - Unable to find bone required by slot data: '%s'.", pSlotData->boneData->name);
  918. return NULL;
  919. }
  920. // Create new proxy object.
  921. const char *objectName = anObjectName ? StringTable->insert(anObjectName, true) : NULL;
  922. auto pProxy = new SpineCollisionProxy(attachmentName, slotName, skinName, sizerWidth, sizerHeight, objectName);
  923. // Add to the internal collection
  924. mCollisionProxies.insert(pAttachment, pProxy);
  925. // Initialize the proxy
  926. F32 width, height = 0.0f;
  927. if (pAttachment->type == SP_ATTACHMENT_REGION) {
  928. auto regionAttachment = (spRegionAttachment*)pAttachment;
  929. width = regionAttachment->width * regionAttachment->scaleX * spBone_getWorldScaleX(pBone) * sizerWidth;
  930. height = regionAttachment->height * regionAttachment->scaleY * spBone_getWorldScaleY(pBone) * sizerHeight;
  931. pProxy->mRotation = regionAttachment->rotation;
  932. }
  933. else {
  934. auto meshAttachment = (spMeshAttachment*)pAttachment;
  935. width = meshAttachment->width * spBone_getWorldScaleX(pBone) * sizerWidth;
  936. height = meshAttachment->height * spBone_getWorldScaleY(pBone) * sizerHeight;
  937. auto currentRegion = (spAtlasRegion *)meshAttachment->rendererObject;
  938. pProxy->mRotation = currentRegion->rotate ? 90.0f : 0.0f;
  939. }
  940. // Set box location to a galaxy far far away...
  941. // NOTE: The box is auto-centered over the attachment at render.
  942. pProxy->setPosition(Vector2(5000, CoreMath::mGetRandomF(-5000.0, 5000.0)));
  943. // Set layer and group membership to that of the owning spine object.
  944. pProxy->setSceneGroup(getSceneGroup());
  945. pProxy->setSceneLayer(getSceneLayer());
  946. // Register the proxy object. This adds it to the runtime environment.
  947. if (objectName) {
  948. if (!pProxy->registerObject(objectName)) {
  949. return NULL;
  950. }
  951. }
  952. else {
  953. if (!pProxy->registerObject()) {
  954. return NULL;
  955. }
  956. }
  957. // Create collision fixture and enable collision callback support.
  958. auto fixtureIndex = pProxy->createPolygonBoxCollisionShape(width * abs(mSkeleton->scaleX), height * abs(mSkeleton->scaleY));
  959. pProxy->setCollisionShapeIsSensor(fixtureIndex, true);
  960. pProxy->setCollisionCallback(true);
  961. pProxy->setSleepingAllowed(false);
  962. // Disable collision box from colliding against other collision boxes in the spine object's scene group.
  963. // The intent is to stop a character's own collision boxes from setting each other off. Different characters
  964. // should be in different groups. That's the idea. Maybe, should just let all the contacts go through and let
  965. // them be handled/ignored on the script side.
  966. auto groupMask = pProxy->getCollisionGroupMask();
  967. groupMask &= ~(1 << getSceneGroup()); // Clears bit in mask corresponding to the spine object's group.
  968. pProxy->setCollisionGroupMask(groupMask);
  969. // Deactivate. If should be active, it will be made so at render time.
  970. pProxy->deActivate();
  971. // If Spine object is already in the scene, add the proxy too.
  972. if (getScene()) {
  973. getScene()->addToScene(pProxy);
  974. }
  975. return pProxy;
  976. }
  977. //-----------------------------------------------------------------------------
  978. bool SpineObject::deleteCollisionProxy(const char *proxyId) {
  979. AssertFatal(proxyId != NULL, "SpineObject::deleteCollisionProxy() - Recieved NULL collision proxy object id.");
  980. SimObjectId idToDelete = dAtoi(proxyId);
  981. // Locate the requested object
  982. for (const auto& i : mCollisionProxies) {
  983. if (i.value->getId() == idToDelete) {
  984. // Delete it
  985. getScene()->removeFromScene(i.value);
  986. i.value->deleteObject();
  987. mCollisionProxies.erase(i.key);
  988. return true;
  989. }
  990. }
  991. Con::warnf("SpineObject::deleteCollisionProxy() - Unable to locate proxy with given id: '%s'.", proxyId);
  992. return false;
  993. }
  994. //-----------------------------------------------------------------------------
  995. bool SpineObject::writeAnimationData(void) const {
  996. // According to the API, a track entry might be null. So we need to make sure there is at least
  997. // one valid entry before returning true.
  998. for (auto i = 0; i < mAnimationState->tracksCount; ++i) {
  999. if (mAnimationState->tracks[i] != NULL) {
  1000. return true;
  1001. }
  1002. }
  1003. return false;
  1004. }
  1005. //-----------------------------------------------------------------------------
  1006. const char *SpineObject::getAnimationData(void) const {
  1007. string result;
  1008. for (auto i = 0; i < mAnimationState->tracksCount; ++i) {
  1009. spTrackEntry *track = mAnimationState->tracks[i];
  1010. if (track) {
  1011. // Encode the track entry.
  1012. result
  1013. .append(track->animation->name).append(";")
  1014. .append(Con::getIntArg(track->trackIndex)).append(";")
  1015. .append(Con::getBoolArg(track->loop)).append(";")
  1016. .append(Con::getFloatArg(track->mixDuration)).append(";~");
  1017. }
  1018. }
  1019. // Could throw it in the StringTable, but don't want to pollute that with this crud.
  1020. unique_ptr<char> retVal = unique_ptr<char>(new char[result.size() + 1]);
  1021. dStrcpy(retVal.get(), result.c_str());
  1022. return retVal.get();
  1023. }
  1024. //-----------------------------------------------------------------------------
  1025. void SpineObject::setAnimationData(const char *animationData) {
  1026. if (!animationData) {
  1027. Con::warnf("SpineObject::setAnimationData() - Ignoring empty animation data string.");
  1028. return;
  1029. }
  1030. // Break into list of entries.
  1031. vector<char *> entries;
  1032. char *entry = dStrtok(const_cast<char *>(animationData), "~");
  1033. while (entry) {
  1034. entries.push_back(entry);
  1035. entry = dStrtok(NULL, "~");
  1036. }
  1037. // Process each entry
  1038. for (auto entry : entries) {
  1039. const char *name = dStrtok(entry, ";");
  1040. int track = dAtoi(dStrtok(NULL, ";"));
  1041. bool loop = dAtob(dStrtok(NULL, ";"));
  1042. F32 mixDuration = dAtof(dStrtok(NULL, ";"));
  1043. setAnimation(name, track, loop, mixDuration);
  1044. }
  1045. }
  1046. //-----------------------------------------------------------------------------
  1047. bool SpineObject::writeCollisionData(void) const {
  1048. return mCollisionProxies.size() > 0;
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. const char *SpineObject::getCollisionData(void) const {
  1052. string result;
  1053. for (auto pair : mCollisionProxies) {
  1054. auto proxy = pair.value;
  1055. // Encode the proxy.
  1056. result
  1057. .append(proxy->mAttachmentName).append(";")
  1058. .append(proxy->mSlotName).append(";")
  1059. .append(proxy->mSkinName).append(";")
  1060. .append(Con::getFloatArg(proxy->mWidthSizer)).append(";")
  1061. .append(Con::getFloatArg(proxy->mHeightSizer)).append(";");
  1062. if (proxy->mObjectName) {
  1063. result.append(proxy->mObjectName).append(";~");
  1064. }
  1065. else {
  1066. result.append("~");
  1067. }
  1068. }
  1069. unique_ptr<char> retVal = unique_ptr<char>(new char[result.size() + 1]);
  1070. dStrcpy(retVal.get(), result.c_str());
  1071. return retVal.get();
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. void SpineObject::setCollisionData(const char *collisionData) {
  1075. if (!collisionData) {
  1076. Con::warnf("SpineObject::setCollisionData() - Ignoring empty collision setup string.");
  1077. return;
  1078. }
  1079. // Break into list of entries.
  1080. vector<char *> entries;
  1081. char *entry = dStrtok(const_cast<char *>(collisionData), "~");
  1082. while (entry) {
  1083. entries.push_back(entry);
  1084. entry = dStrtok(NULL, "~");
  1085. }
  1086. // Process each entry
  1087. for (auto entry : entries) {
  1088. const char *attachmentName = dStrtok(entry, ";");
  1089. const char *slotName = dStrtok(NULL, ";");
  1090. const char *skinName = dStrtok(NULL, ";");
  1091. F32 wSizer = dAtof(dStrtok(NULL, ";"));
  1092. F32 hSizer = dAtof(dStrtok(NULL, ";"));
  1093. const char *objectName = dStrtok(NULL, ";");
  1094. getCollisionProxy(attachmentName, slotName, skinName, wSizer, hSizer, objectName);
  1095. }
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. void SpineObject::OnRegisterScene(Scene *scene) {
  1099. Parent::OnRegisterScene(scene);
  1100. for (auto i : mCollisionProxies) {
  1101. scene->addToScene(i.value);
  1102. }
  1103. }
  1104. //-----------------------------------------------------------------------------
  1105. void SpineObject::OnUnregisterScene(Scene *scene) {
  1106. Parent::OnUnregisterScene(scene);
  1107. for (auto i : mCollisionProxies) {
  1108. if(i.value->getScene())
  1109. i.value->getScene()->removeFromScene(i.value);
  1110. }
  1111. }