| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2013 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #ifndef _SKELETON_OBJECT_H_
- #include "2d/sceneobject/SkeletonObject.h"
- #endif
- #include "spine/extension.h"
- // Script bindings.
- #include "2d/sceneobject/SkeletonObject_ScriptBinding.h"
- //-----------------------------------------------------------------------------
- /*void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) {
- }
- void _spAtlasPage_disposeTexture (spAtlasPage* self) {
- }
- char* _spUtil_readFile (const char* path, int* length) {
- return _readFile(path, length);
- }*/
- //-----------------------------------------------------------------------------
- IMPLEMENT_CONOBJECT(SkeletonObject);
- //------------------------------------------------------------------------------
- SkeletonObject::SkeletonObject() : mPreTickTime( 0.0f ),
- mPostTickTime( 0.0f ),
- mTimeScale(1),
- mLastFrameTime(0),
- mTotalAnimationTime(0),
- mSkeleton(NULL),
- mState(NULL),
- mAnimationCycle(false),
- mAnimationFinished(true),
- mAnimationDuration(0.0),
- mFlipX(false),
- mFlipY(false)
- {
- mCurrentAnimation = StringTable->insert("");
- mSkeletonScale.SetZero();
- mSkeletonOffset.SetZero();
- }
- //------------------------------------------------------------------------------
- SkeletonObject::~SkeletonObject()
- {
- if (mSkeleton) {
- spSkeleton_dispose(mSkeleton);
- mSkeleton = NULL;
- }
- if (mState) {
- spAnimationState_dispose(mState);
- mState = NULL;
- }
- }
- //------------------------------------------------------------------------------
- void SkeletonObject::initPersistFields()
- {
- // Call parent.
- Parent::initPersistFields();
-
- addProtectedField("Asset", TypeSkeletonAssetPtr, Offset(mSkeletonAsset, SkeletonObject), &setSkeletonAsset, &getSkeletonAsset, &writeSkeletonAsset, "The skeleton asset ID used for the skeleton.");
- addProtectedField("AnimationName", TypeString, Offset(mCurrentAnimation, SkeletonObject), &setAnimationName, &getAnimationName, &writeAnimationName, "The animation name to play.");
- addProtectedField("Skin", TypeString, Offset(mCurrentSkin, SkeletonObject), &setCurrentSkin, &getCurrentSkin, &writeCurrentSkin, "The skin to use.");
- addProtectedField("RootBoneScale", TypeVector2, 0, &setRootBoneScale, &getRootBoneScale, &writeRootBoneScale, "Scaling of the skeleton's root bone");
- addProtectedField("RootBoneOffset", TypeVector2, 0, &setRootBoneOffset, &getRootBoneOffset, &writeRootBoneOffset, "X/Y offset of the skeleton's root bone");
- addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, SkeletonObject), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "Whether the animation loops or not");
- addField("FlipX", TypeBool, Offset(mFlipX, SkeletonObject), &writeFlipX, "");
- addField("FlipY", TypeBool, Offset(mFlipY, SkeletonObject), &writeFlipY, "");
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::preIntegrate( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
- {
- // Note tick times.
- mPreTickTime = mPostTickTime;
- mPostTickTime = totalTime;
-
- // Update composition at pre-tick time.
- updateComposition( mPreTickTime );
-
- // Are the spatials dirty?
- if ( getSpatialDirty() )
- {
- // Yes, so update the world transform.
- setBatchTransform( getRenderTransform() );
- }
-
- // Are the render extents dirty?
- if ( getLocalExtentsDirty() )
- {
- // Yes, so set size as local extents.
- setSize( getLocalExtents() );
- }
-
- // Call parent.
- Parent::preIntegrate( totalTime, elapsedTime, pDebugStats );
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::integrateObject( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
- {
- // Call Parent.
- Parent::integrateObject( totalTime, elapsedTime, pDebugStats );
-
- // Finish if the spatials are NOT dirty.
- if ( !getSpatialDirty() )
- return;
-
- // Update the batch world transform.
- setBatchTransform( getRenderTransform() );
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::interpolateObject( const F32 timeDelta )
- {
- // Call parent.
- Parent::interpolateObject( timeDelta );
-
- // Update composition time (interpolated).
- updateComposition( (timeDelta * mPreTickTime) + ((1.0f-timeDelta) * mPostTickTime) );
-
- // Finish if the spatials are NOT dirty.
- if ( !getSpatialDirty() )
- return;
-
- // Update the batch world transform.
- setBatchTransform( getRenderTransform() );
- }
- //------------------------------------------------------------------------------
- void SkeletonObject::copyTo(SimObject* object)
- {
- // Call to parent.
- Parent::copyTo(object);
-
- // Fetch object.
- SkeletonObject* pComposite = dynamic_cast<SkeletonObject*>(object);
-
- // Sanity!
- AssertFatal(pComposite != NULL, "SkeletonObject::copyTo() - Object is not the correct type.");
-
- // Copy state.
- pComposite->setSkeletonAsset( getSkeletonAsset() );
- pComposite->setAnimationName( getAnimationName(), getAnimationCycle() );
- pComposite->setCurrentSkin( getCurrentSkin() );
- pComposite->setRootBoneScale( getRootBoneScale() );
- pComposite->setRootBoneOffset( getRootBoneOffset() );
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::scenePrepareRender( const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue )
- {
- // Prepare render.
- SpriteBatch::prepareRender( this, pSceneRenderState, pSceneRenderQueue );
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
- {
- // Render.
- SpriteBatch::render( pSceneRenderState, pSceneRenderRequest, pBatchRenderer );
-
- }
- //-----------------------------------------------------------------------------
- bool SkeletonObject::setSkeletonAsset( const char* pSkeletonAssetId )
- {
- // Sanity!
- AssertFatal( pSkeletonAssetId != NULL, "Cannot use a NULL asset Id." );
-
- // Fetch the asset Id.
- mSkeletonAsset = pSkeletonAssetId;
-
- // Generate composition.
- generateComposition();
-
- return true;
- }
- //-----------------------------------------------------------------------------
- bool SkeletonObject::setAnimationName( const char* pAnimation, const bool isLooping )
- {
- // Make sure an asset was loaded.
- if (mSkeletonAsset.isNull())
- return false;
-
- // Set the animation.
- mCurrentAnimation = StringTable->insert(pAnimation);
-
- mAnimationCycle = isLooping;
-
- // Generate composition.
- generateComposition();
-
- return true;
- }
- //-----------------------------------------------------------------------------
- bool SkeletonObject::setMix( const char* pFromAnimation, const char* pToAnimation, float time)
- {
- if (mSkeletonAsset.isNull())
- {
- Con::warnf("SkeletonObject::setMix() - Cannot mix. No asset assigned");
- return false;
- }
-
- // Check for valid animation state data
- AssertFatal( mSkeletonAsset->mStateData != NULL, "SkeletonObject::setMix() - Animation state data invalid" );
-
- // Check to see if the "from animation" is valid
- spAnimation* from = spSkeletonData_findAnimation(mSkeleton->data, pFromAnimation);
-
- if (!from)
- {
- Con::warnf("SkeletonObject::setMix() - Animation %s does not exist.", pFromAnimation);
- return false;
- }
-
- // Check to see if the "to animation" is valid
- spAnimation* to = spSkeletonData_findAnimation(mSkeleton->data, pToAnimation);
-
- if (!to)
- {
- Con::warnf("SkeletonObject::setMix() - Animation %s does not exist.", pToAnimation);
- return false;
- }
-
- // Check to see if a valid mix time was passsed
- if (time < 0.0f)
- {
- Con::warnf("SkeletonObject::setMix() - Invalid time set, %f", time);
- return false;
- }
-
- spAnimationStateData_setMixByName(mSkeletonAsset->mStateData, pFromAnimation, pToAnimation, time);
- return true;
- }
- //-----------------------------------------------------------------------------
- bool SkeletonObject::setCurrentSkin( const char* pSkin )
- {
- if (mSkeletonAsset.isNull() || !mSkeleton)
- {
- Con::errorf("SkeletonObject::setCurrentSkin() - Skeleton Asset was null or skeleton was not built");
- return false;
- }
-
- S32 result = spSkeleton_setSkinByName(mSkeleton, pSkin);
-
- if (result)
- {
- spSkeleton_setSlotsToSetupPose(mSkeleton);
- return true;
- }
- else
- {
- Con::errorf("SkeletonObject::setCurrentSkin() - Skin %s not found", pSkin);
- return false;
- }
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::setRootBoneScale(const Vector2& scale)
- {
- mSkeletonScale = scale;
-
- if (!mSkeleton)
- return;
-
- if (mSkeletonScale.notZero())
- {
- spBone* rootBone = mSkeleton->root;
- rootBone->scaleX = mSkeletonScale.x;
- rootBone->scaleY = mSkeletonScale.y;
- }
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::setRootBoneOffset(const Vector2& offset)
- {
- mSkeletonOffset = offset;
-
- if (!mSkeleton)
- return;
-
- if (mSkeletonOffset.notZero())
- {
- spBone* rootBone = mSkeleton->root;
- rootBone->x = mSkeletonOffset.x;
- rootBone->y = mSkeletonOffset.y;
- }
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::generateComposition( void )
- {
- // Clear existing visualization
- clearSprites();
- mSkeletonSprites.clear();
-
- // Finish if skeleton asset isn't available.
- if ( mSkeletonAsset.isNull() )
- return;
-
- // Generate visualization.
- if ((*mSkeletonAsset).mImageAsset.isNull())
- {
- Con::warnf( "SkeletonObject::generateComposition() - Image asset was NULL, so nothing can be added to the composition.");
- return;
- }
-
- if (!mSkeleton)
- mSkeleton = spSkeleton_create(mSkeletonAsset->mSkeletonData);
-
- if (!mState)
- mState = spAnimationState_create(mSkeletonAsset->mStateData);
-
- if (mCurrentAnimation != StringTable->EmptyString)
- {
- spAnimationState_setAnimationByName(mState, 0, mCurrentAnimation, mAnimationCycle);
- mAnimationDuration = mState->tracks[0]->animation->duration;
- mAnimationFinished = false;
- mTotalAnimationTime = mLastFrameTime + mAnimationDuration;
- }
-
- if (mSkeletonScale.notZero())
- {
- spBone* rootBone = mSkeleton->root;
- rootBone->scaleX = mSkeletonScale.x;
- rootBone->scaleY = mSkeletonScale.y;
- }
-
- if (mSkeletonOffset.notZero())
- {
- spBone* rootBone = mSkeleton->root;
- rootBone->x = mSkeletonOffset.x;
- rootBone->y = mSkeletonOffset.y;
- }
- }
- //-----------------------------------------------------------------------------
- void SkeletonObject::updateComposition( const F32 time )
- {
- // Update position/orientation/state of visualization
- float delta = (time - mLastFrameTime) * mTimeScale;
- mLastFrameTime = time;
-
- spSkeleton_update(mSkeleton, delta);
-
- if (!mAnimationFinished)
- {
- spAnimationState_update(mState, delta);
- spAnimationState_apply(mState, mSkeleton);
- }
-
- spSkeleton_updateWorldTransform(mSkeleton);
-
- // Get the ImageAsset used by the sprites
- StringTableEntry assetId = (*mSkeletonAsset).mImageAsset.getAssetId();
-
- clearSprites();
-
- Vector2 vertices[4];
-
- F32 vertexPositions[8];
- for (int i = 0; i < mSkeleton->slotCount; ++i)
- {
- spSlot* slot = mSkeleton->slots[i];
- spAttachment* attachment = slot->attachment;
-
- if (!attachment || attachment->type != ATTACHMENT_REGION)
- continue;
-
- spRegionAttachment* regionAttachment = (spRegionAttachment*)attachment;
- spRegionAttachment_computeWorldVertices(regionAttachment, slot->skeleton->x, slot->skeleton->y, slot->bone, vertexPositions);
-
- SpriteBatchItem* pSprite = SpriteBatch::createSprite();
- pSprite->setDepth(mSceneLayerDepth);
-
- pSprite->setSrcBlendFactor(mSrcBlendFactor);
- pSprite->setDstBlendFactor(mDstBlendFactor);
-
- mSkeleton->r = mBlendColor.red;
- mSkeleton->g = mBlendColor.green;
- mSkeleton->b = mBlendColor.blue;
- mSkeleton->a = mBlendColor.alpha;
-
- F32 alpha = mSkeleton->a * slot->a;
- pSprite->setBlendColor(ColorF(
- mSkeleton->r * slot->r * alpha,
- mSkeleton->g * slot->g * alpha,
- mSkeleton->b * slot->b * alpha,
- alpha
- ));
-
- mSkeleton->flipX = getFlipX();
- mSkeleton->flipY = getFlipY();
-
- vertices[0].x = vertexPositions[VERTEX_X1];
- vertices[0].y = vertexPositions[VERTEX_Y1];
- vertices[1].x = vertexPositions[VERTEX_X4];
- vertices[1].y = vertexPositions[VERTEX_Y4];
- vertices[2].x = vertexPositions[VERTEX_X3];
- vertices[2].y = vertexPositions[VERTEX_Y3];
- vertices[3].x = vertexPositions[VERTEX_X2];
- vertices[3].y = vertexPositions[VERTEX_Y2];
- pSprite->setExplicitVertices(vertices);
-
- pSprite->setImage(assetId);
- pSprite->setNamedImageFrame(attachment->name);
- }
-
- if (mLastFrameTime >= mTotalAnimationTime)
- mAnimationFinished = true;
-
- if (mAnimationFinished && !mAnimationCycle)
- {
- onAnimationFinished();
- }
- else
- {
- mAnimationFinished = false;
- }
- }
- void SkeletonObject::onAnimationFinished()
- {
- // Do script callback.
- Con::executef( this, 2, "onAnimationFinished", mCurrentAnimation );
- }
|