[SOLVED] Loading LibGDX / Spine Atlas

Hello guys,

I’m currently using spine in my project, and everything is going fine, except for one issue:
Instead of using TexturePacker, I’m trying to use Spine’s texture packer, as I have it’s license. The problem is that Spine generates a LibGDX Atlas format (plain text), which I can’t directly use with any Cocos2D sprite loading functions, as they require a plist. Since spine have the Atlas loader implemented, I tried to use it to load my atlas file, but it fails, with the debugger pointing to a fault address error…
First, let me post my code for the atlas loading:

// Load animations
    Constants::screechAnimation = CCAnimation::create();
    CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();
    Atlas* atlas = Atlas_readAtlasFile(s_Powerup_FX_Screech_Atlas);
    AtlasRegion* region = atlas->regions;

    while(region) {
        CCSpriteFrame* sf = CCSpriteFrame::create(s_Powerup_FX_Screech_Image,
            CCRectMake(region->x, region->y, region->width, region->height),
            (bool)region->rotate,
            CCPointMake(region->offsetX, region->offsetY),
            CCSizeMake(region->originalWidth, region->originalHeight));
        cache->addSpriteFrame(sf, region->name);
        Constants::screechAnimation->addSpriteFrame(sf);
        region = region->next;
    }

    Constants::screechAnimation->retain();
    Constants::screechAnimation->setDelayPerUnit(0.8f);
    CCAnimationCache::sharedAnimationCache()->addAnimation(Constants::screechAnimation, s_Powerup_FX_ScreechAnimationName);

I’ve looked at spine runtime sources and found that these functions needs to be implemented for atlas loading to work, and are the cause of the faulty memory adress:

/*
 * Functions that must be implemented:
 */

void _AtlasPage_createTexture (AtlasPage* self, const char* path);
void _AtlasPage_disposeTexture (AtlasPage* self);
char* _Util_readFile (const char* path, int* length);

As they are needed in Atlas.cpp

But I’ve found them implemented in spine-cocos2dx.cpp:

void _AtlasPage_createTexture (AtlasPage* self, const char* path) {
    CCTexture2D* texture = CCTextureCache::sharedTextureCache()->addImage(path);
    CCTextureAtlas* textureAtlas = CCTextureAtlas::createWithTexture(texture, 4);
    textureAtlas->retain();
    self->rendererObject = textureAtlas;
    // Using getContentSize to make it supports the strategy of loading resources in cocos2d-x.
    // self->width = texture->getPixelsWide();
    // self->height = texture->getPixelsHigh();
    self->width = texture->getContentSize().width;
    self->height = texture->getContentSize().height;
}

void _AtlasPage_disposeTexture (AtlasPage* self) {
    ((CCTextureAtlas*)self->rendererObject)->release();
}

char* _Util_readFile (const char* path, int* length) {
    unsigned long size;
    char* data = reinterpret_cast(CCFileUtils::sharedFileUtils()->getFileData(
        CCFileUtils::sharedFileUtils()->fullPathForFilename(path).c_str(), "r", &size));
    *length = size;
    return data;
}

And, of couse, i’m importing spine-cocos2dx.h and compiling the whole extensions. So, any idea why this code is not being executed?

Thanks for your attention!

PS: The main reason for using spine texture packer is that i’ll be able to use options that are blocked in TexturePacker for free users, as trim, rotate and other , and have already paid for spine, I don’t want to pay for a feature spine already has…

— Edit:
Oh well, it turns out I was loading with the wrong filename…. Anyway, if someone needs to know how to use Spine’s Atlas loader, here it is :stuck_out_tongue: (i’ve edited my code as it was wrong)

Did you not have any issues with sprite rotation? I’ve noticed Cocos2d plist format rotates clockwise, libGDX on the other had handled counter-clockwise.

Here is a optional implementation that will handle both cocos2d plist and libGDX atlas format and use the correct 90 degree rotation:
https://github.com/jllust/cocos2d-x/commit/741d26cc764cae42b66cc4485cdda6327a75cc8f

I haven’t tried sprite rotation yet, but it’s nice to know there’s a solution already, thanks!

Hey Jason, I noticed that you’ve forgotten to implement the removeSpriteFramesFromAtlas function, which is also very handy, I can’t push it right now to your repo, but here it is:
CCSpriteFrameCache.h

@119
private:
    /** Removes multiple Sprite Frames from CCDictionary.
    * @since v0.99.5
    */
    void removeSpriteFramesFromDictionary(CCDictionary* dictionary);

    /** Removes multiple Sprite Frames from Atlas.
    */
    void removeSpriteFramesFromAtlas(Atlas* atlas);

CCSpriteFrameCache.cpp

@386
void CCSpriteFrameCache::removeSpriteFramesFromFile(const char* plist)
{
    std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(plist);
    size_t extPos = string(plist).find_last_of("."); 
    int cmp = strcmp(&plist[extPos], ".atlas"); 

    if ( !cmp ) { 
        Atlas* atlas = Atlas_readAtlasFile(fullPath.c_str()); 
        removeSpriteFramesFromAtlas(atlas);
        Atlas_dispose(atlas); 
    } else {
        CCDictionary* dict = CCDictionary::createWithContentsOfFileThreadSafe(fullPath.c_str());

        removeSpriteFramesFromDictionary((CCDictionary*)dict);

        // remove it from the cache
        set::iterator ret = m_pLoadedFileNames->find(plist);
        if (ret != m_pLoadedFileNames->end())
        {
            m_pLoadedFileNames->erase(ret);
        }

        dict->release();
    }
}
void CCSpriteFrameCache::removeSpriteFramesFromAtlas(Atlas* atlas)
{
    CCArray* keysToRemove = CCArray::create();
    AtlasRegion* region = atlas->regions; 
    do { 
        std::string spriteFrameName = region->name; 
        CCSpriteFrame* spriteFrame = (CCSpriteFrame*)m_pSpriteFrames->objectForKey(spriteFrameName); 
        if (spriteFrame) 
        { 
            keysToRemove->addObject(CCString::create(spriteFrameName)); 
        } 
        spriteFrame->release(); 
    } while ((region = region->next));

    m_pSpriteFrames->removeObjectsForKeys(keysToRemove);
}

Thanks, posted the commit.
https://github.com/jllust/cocos2d-x/commit/414c8416cd79569abdcaeae4c62437bc38ec6ec4

There’s just a small change needed for this code load an Atlas with multiple pages at addSpriteFramesWithAtlas:

@88
        // create frame 
        spriteFrame = new CCSpriteFrame(); 
        CCTextureAtlas* texAtlas = static_cast(region->page->rendererObject); 

        spriteFrame->initWithTexture(texAtlas->getTexture(), 
            CCRectMake(region->x, region->y, region->width, region->height), 
            region->rotate*-1, 
            CCPointMake(region->offsetX, region->offsetY), 
            CCSizeMake((float)region->originalWidth, (float)region->originalHeight) 
        ); 
        // add sprite frame 

You can also remove the CCTexture2DpobTexture* as it won’t be needed anymore ;D