SkeletonObject.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  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 _SKELETON_OBJECT_H_
  23. #include "2d/sceneobject/SkeletonObject.h"
  24. #endif
  25. #include "spine/extension.h"
  26. // Script bindings.
  27. #include "2d/sceneobject/SkeletonObject_ScriptBinding.h"
  28. //-----------------------------------------------------------------------------
  29. /*void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) {
  30. }
  31. void _spAtlasPage_disposeTexture (spAtlasPage* self) {
  32. }
  33. char* _spUtil_readFile (const char* path, int* length) {
  34. return _readFile(path, length);
  35. }*/
  36. //-----------------------------------------------------------------------------
  37. IMPLEMENT_CONOBJECT(SkeletonObject);
  38. //------------------------------------------------------------------------------
  39. SkeletonObject::SkeletonObject() : mPreTickTime( 0.0f ),
  40. mPostTickTime( 0.0f ),
  41. mTimeScale(1),
  42. mLastFrameTime(0),
  43. mTotalAnimationTime(0),
  44. mSkeleton(NULL),
  45. mState(NULL),
  46. mAnimationCycle(false),
  47. mAnimationFinished(true),
  48. mAnimationDuration(0.0),
  49. mFlipX(false),
  50. mFlipY(false)
  51. {
  52. mCurrentAnimation = StringTable->insert("");
  53. mSkeletonScale.SetZero();
  54. mSkeletonOffset.SetZero();
  55. }
  56. //------------------------------------------------------------------------------
  57. SkeletonObject::~SkeletonObject()
  58. {
  59. if (mSkeleton) {
  60. spSkeleton_dispose(mSkeleton);
  61. mSkeleton = NULL;
  62. }
  63. if (mState) {
  64. spAnimationState_dispose(mState);
  65. mState = NULL;
  66. }
  67. }
  68. //------------------------------------------------------------------------------
  69. void SkeletonObject::initPersistFields()
  70. {
  71. // Call parent.
  72. Parent::initPersistFields();
  73. addProtectedField("Asset", TypeSkeletonAssetPtr, Offset(mSkeletonAsset, SkeletonObject), &setSkeletonAsset, &getSkeletonAsset, &writeSkeletonAsset, "The skeleton asset ID used for the skeleton.");
  74. addProtectedField("AnimationName", TypeString, Offset(mCurrentAnimation, SkeletonObject), &setAnimationName, &getAnimationName, &writeAnimationName, "The animation name to play.");
  75. addProtectedField("Skin", TypeString, Offset(mCurrentSkin, SkeletonObject), &setCurrentSkin, &getCurrentSkin, &writeCurrentSkin, "The skin to use.");
  76. addProtectedField("RootBoneScale", TypeVector2, NULL, &setRootBoneScale, &getRootBoneScale, &writeRootBoneScale, "Scaling of the skeleton's root bone");
  77. addProtectedField("RootBoneOffset", TypeVector2, NULL, &setRootBoneOffset, &getRootBoneOffset, &writeRootBoneOffset, "X/Y offset of the skeleton's root bone");
  78. addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, SkeletonObject), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "Whether the animation loops or not");
  79. addField("FlipX", TypeBool, Offset(mFlipX, SkeletonObject), &writeFlipX, "");
  80. addField("FlipY", TypeBool, Offset(mFlipY, SkeletonObject), &writeFlipY, "");
  81. }
  82. //-----------------------------------------------------------------------------
  83. void SkeletonObject::preIntegrate( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
  84. {
  85. // Note tick times.
  86. mPreTickTime = mPostTickTime;
  87. mPostTickTime = totalTime;
  88. // Update composition at pre-tick time.
  89. updateComposition( mPreTickTime );
  90. // Are the spatials dirty?
  91. if ( getSpatialDirty() )
  92. {
  93. // Yes, so update the world transform.
  94. setBatchTransform( getRenderTransform() );
  95. }
  96. // Are the render extents dirty?
  97. if ( getLocalExtentsDirty() )
  98. {
  99. // Yes, so set size as local extents.
  100. setSize( getLocalExtents() );
  101. }
  102. // Call parent.
  103. Parent::preIntegrate( totalTime, elapsedTime, pDebugStats );
  104. }
  105. //-----------------------------------------------------------------------------
  106. void SkeletonObject::integrateObject( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
  107. {
  108. // Call Parent.
  109. Parent::integrateObject( totalTime, elapsedTime, pDebugStats );
  110. // Finish if the spatials are NOT dirty.
  111. if ( !getSpatialDirty() )
  112. return;
  113. // Update the batch world transform.
  114. setBatchTransform( getRenderTransform() );
  115. }
  116. //-----------------------------------------------------------------------------
  117. void SkeletonObject::interpolateObject( const F32 timeDelta )
  118. {
  119. // Call parent.
  120. Parent::interpolateObject( timeDelta );
  121. // Update composition time (interpolated).
  122. updateComposition( (timeDelta * mPreTickTime) + ((1.0f-timeDelta) * mPostTickTime) );
  123. // Finish if the spatials are NOT dirty.
  124. if ( !getSpatialDirty() )
  125. return;
  126. // Update the batch world transform.
  127. setBatchTransform( getRenderTransform() );
  128. }
  129. //------------------------------------------------------------------------------
  130. void SkeletonObject::copyTo(SimObject* object)
  131. {
  132. // Call to parent.
  133. Parent::copyTo(object);
  134. // Fetch object.
  135. SkeletonObject* pComposite = dynamic_cast<SkeletonObject*>(object);
  136. // Sanity!
  137. AssertFatal(pComposite != NULL, "SkeletonObject::copyTo() - Object is not the correct type.");
  138. // Copy state.
  139. pComposite->setSkeletonAsset( getSkeletonAsset() );
  140. pComposite->setAnimationName( getAnimationName(), getAnimationCycle() );
  141. pComposite->setCurrentSkin( getCurrentSkin() );
  142. pComposite->setRootBoneScale( getRootBoneScale() );
  143. pComposite->setRootBoneOffset( getRootBoneOffset() );
  144. }
  145. //-----------------------------------------------------------------------------
  146. void SkeletonObject::scenePrepareRender( const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue )
  147. {
  148. // Prepare render.
  149. SpriteBatch::prepareRender( this, pSceneRenderState, pSceneRenderQueue );
  150. }
  151. //-----------------------------------------------------------------------------
  152. void SkeletonObject::sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
  153. {
  154. // Render.
  155. SpriteBatch::render( pSceneRenderState, pSceneRenderRequest, pBatchRenderer );
  156. }
  157. //-----------------------------------------------------------------------------
  158. bool SkeletonObject::setSkeletonAsset( const char* pSkeletonAssetId )
  159. {
  160. // Sanity!
  161. AssertFatal( pSkeletonAssetId != NULL, "Cannot use a NULL asset Id." );
  162. // Fetch the asset Id.
  163. mSkeletonAsset = pSkeletonAssetId;
  164. // Generate composition.
  165. generateComposition();
  166. return true;
  167. }
  168. //-----------------------------------------------------------------------------
  169. bool SkeletonObject::setAnimationName( const char* pAnimation, const bool isLooping )
  170. {
  171. // Make sure an asset was loaded.
  172. if (mSkeletonAsset.isNull())
  173. return false;
  174. // Set the animation.
  175. mCurrentAnimation = StringTable->insert(pAnimation);
  176. mAnimationCycle = isLooping;
  177. // Generate composition.
  178. generateComposition();
  179. return true;
  180. }
  181. //-----------------------------------------------------------------------------
  182. bool SkeletonObject::setMix( const char* pFromAnimation, const char* pToAnimation, float time)
  183. {
  184. if (mSkeletonAsset.isNull())
  185. {
  186. Con::warnf("SkeletonObject::setMix() - Cannot mix. No asset assigned");
  187. return false;
  188. }
  189. // Check for valid animation state data
  190. AssertFatal( mSkeletonAsset->mStateData != NULL, "SkeletonObject::setMix() - Animation state data invalid" );
  191. // Check to see if the "from animation" is valid
  192. spAnimation* from = spSkeletonData_findAnimation(mSkeleton->data, pFromAnimation);
  193. if (!from)
  194. {
  195. Con::warnf("SkeletonObject::setMix() - Animation %s does not exist.", pFromAnimation);
  196. return false;
  197. }
  198. // Check to see if the "to animation" is valid
  199. spAnimation* to = spSkeletonData_findAnimation(mSkeleton->data, pToAnimation);
  200. if (!to)
  201. {
  202. Con::warnf("SkeletonObject::setMix() - Animation %s does not exist.", pToAnimation);
  203. return false;
  204. }
  205. // Check to see if a valid mix time was passsed
  206. if (time < 0.0f)
  207. {
  208. Con::warnf("SkeletonObject::setMix() - Invalid time set, %f", time);
  209. return false;
  210. }
  211. spAnimationStateData_setMixByName(mSkeletonAsset->mStateData, pFromAnimation, pToAnimation, time);
  212. return true;
  213. }
  214. //-----------------------------------------------------------------------------
  215. bool SkeletonObject::setCurrentSkin( const char* pSkin )
  216. {
  217. if (mSkeletonAsset.isNull() || !mSkeleton)
  218. {
  219. Con::errorf("SkeletonObject::setCurrentSkin() - Skeleton Asset was null or skeleton was not built");
  220. return false;
  221. }
  222. S32 result = spSkeleton_setSkinByName(mSkeleton, pSkin);
  223. if (result)
  224. {
  225. spSkeleton_setSlotsToSetupPose(mSkeleton);
  226. return true;
  227. }
  228. else
  229. {
  230. Con::errorf("SkeletonObject::setCurrentSkin() - Skin %s not found", pSkin);
  231. return false;
  232. }
  233. }
  234. //-----------------------------------------------------------------------------
  235. void SkeletonObject::setRootBoneScale(const Vector2& scale)
  236. {
  237. mSkeletonScale = scale;
  238. if (!mSkeleton)
  239. return;
  240. if (mSkeletonScale.notZero())
  241. {
  242. spBone* rootBone = mSkeleton->root;
  243. rootBone->scaleX = mSkeletonScale.x;
  244. rootBone->scaleY = mSkeletonScale.y;
  245. }
  246. }
  247. //-----------------------------------------------------------------------------
  248. void SkeletonObject::setRootBoneOffset(const Vector2& offset)
  249. {
  250. mSkeletonOffset = offset;
  251. if (!mSkeleton)
  252. return;
  253. if (mSkeletonOffset.notZero())
  254. {
  255. spBone* rootBone = mSkeleton->root;
  256. rootBone->x = mSkeletonOffset.x;
  257. rootBone->y = mSkeletonOffset.y;
  258. }
  259. }
  260. //-----------------------------------------------------------------------------
  261. void SkeletonObject::generateComposition( void )
  262. {
  263. // Clear existing visualization
  264. clearSprites();
  265. mSkeletonSprites.clear();
  266. // Finish if skeleton asset isn't available.
  267. if ( mSkeletonAsset.isNull() )
  268. return;
  269. // Generate visualization.
  270. if ((*mSkeletonAsset).mImageAsset.isNull())
  271. {
  272. Con::warnf( "SkeletonObject::generateComposition() - Image asset was NULL, so nothing can be added to the composition.");
  273. return;
  274. }
  275. if (!mSkeleton)
  276. mSkeleton = spSkeleton_create(mSkeletonAsset->mSkeletonData);
  277. if (!mState)
  278. mState = spAnimationState_create(mSkeletonAsset->mStateData);
  279. if (mCurrentAnimation != StringTable->EmptyString)
  280. {
  281. spAnimationState_setAnimationByName(mState, 0, mCurrentAnimation, mAnimationCycle);
  282. mAnimationDuration = mState->tracks[0]->animation->duration;
  283. mAnimationFinished = false;
  284. mTotalAnimationTime = mLastFrameTime + mAnimationDuration;
  285. }
  286. if (mSkeletonScale.notZero())
  287. {
  288. spBone* rootBone = mSkeleton->root;
  289. rootBone->scaleX = mSkeletonScale.x;
  290. rootBone->scaleY = mSkeletonScale.y;
  291. }
  292. if (mSkeletonOffset.notZero())
  293. {
  294. spBone* rootBone = mSkeleton->root;
  295. rootBone->x = mSkeletonOffset.x;
  296. rootBone->y = mSkeletonOffset.y;
  297. }
  298. }
  299. //-----------------------------------------------------------------------------
  300. void SkeletonObject::updateComposition( const F32 time )
  301. {
  302. // Update position/orientation/state of visualization
  303. float delta = (time - mLastFrameTime) * mTimeScale;
  304. mLastFrameTime = time;
  305. spSkeleton_update(mSkeleton, delta);
  306. if (!mAnimationFinished)
  307. {
  308. spAnimationState_update(mState, delta);
  309. spAnimationState_apply(mState, mSkeleton);
  310. }
  311. spSkeleton_updateWorldTransform(mSkeleton);
  312. // Get the ImageAsset used by the sprites
  313. StringTableEntry assetId = (*mSkeletonAsset).mImageAsset.getAssetId();
  314. clearSprites();
  315. Vector2 vertices[4];
  316. F32 vertexPositions[8];
  317. for (int i = 0; i < mSkeleton->slotCount; ++i)
  318. {
  319. spSlot* slot = mSkeleton->slots[i];
  320. spAttachment* attachment = slot->attachment;
  321. if (!attachment || attachment->type != ATTACHMENT_REGION)
  322. continue;
  323. spRegionAttachment* regionAttachment = (spRegionAttachment*)attachment;
  324. spRegionAttachment_computeWorldVertices(regionAttachment, slot->skeleton->x, slot->skeleton->y, slot->bone, vertexPositions);
  325. SpriteBatchItem* pSprite = SpriteBatch::createSprite();
  326. pSprite->setDepth(mSceneLayerDepth);
  327. pSprite->setSrcBlendFactor(mSrcBlendFactor);
  328. pSprite->setDstBlendFactor(mDstBlendFactor);
  329. mSkeleton->r = mBlendColor.red;
  330. mSkeleton->g = mBlendColor.green;
  331. mSkeleton->b = mBlendColor.blue;
  332. mSkeleton->a = mBlendColor.alpha;
  333. F32 alpha = mSkeleton->a * slot->a;
  334. pSprite->setBlendColor(ColorF(
  335. mSkeleton->r * slot->r * alpha,
  336. mSkeleton->g * slot->g * alpha,
  337. mSkeleton->b * slot->b * alpha,
  338. alpha
  339. ));
  340. mSkeleton->flipX = getFlipX();
  341. mSkeleton->flipY = getFlipY();
  342. vertices[0].x = vertexPositions[VERTEX_X1];
  343. vertices[0].y = vertexPositions[VERTEX_Y1];
  344. vertices[1].x = vertexPositions[VERTEX_X4];
  345. vertices[1].y = vertexPositions[VERTEX_Y4];
  346. vertices[2].x = vertexPositions[VERTEX_X3];
  347. vertices[2].y = vertexPositions[VERTEX_Y3];
  348. vertices[3].x = vertexPositions[VERTEX_X2];
  349. vertices[3].y = vertexPositions[VERTEX_Y2];
  350. pSprite->setExplicitVertices(vertices);
  351. pSprite->setImage(assetId);
  352. pSprite->setNamedImageFrame(attachment->name);
  353. }
  354. if (mLastFrameTime >= mTotalAnimationTime)
  355. mAnimationFinished = true;
  356. if (mAnimationFinished && !mAnimationCycle)
  357. {
  358. onAnimationFinished();
  359. }
  360. else
  361. {
  362. mAnimationFinished = false;
  363. }
  364. }
  365. void SkeletonObject::onAnimationFinished()
  366. {
  367. // Do script callback.
  368. Con::executef( this, 2, "onAnimationFinished", mCurrentAnimation );
  369. }