- Edited
try to convert CCtexture2d to Attachment
I really have problem with this issue. what am I wrong?
-(RegionAttachment*) regionWithTexture2d:(CCTexture2D*)tex2d name:(NSString*)_name{
CCTextureAtlas *tex = [CCTextureAtlas textureAtlasWithTexture:tex2d capacity:4];
AtlasPage *page = AtlasPage_create([_name UTF8String]);
page->rendererObject = tex;
page->width = tex2d.contentSizeInPixels.width;
page->height = tex2d.contentSizeInPixels.height;
AtlasRegion *reg = AtlasRegion_create();
reg->page = page;
RegionAttachment *region = RegionAttachment_create([_name UTF8String]);
region->rendererObject = reg;
CGSize size = tex2d.contentSizeInPixels;
float u = region->x / size.width;
float u2 = (region->x + region->width) / size.width;
float v = region->y / size.height;
float v2 = (region->y + region->height) / size.height;
RegionAttachment_setUVs(region,u,v,u2,v2,0);
region->regionOffsetX = 0; // zero if not doing whitespace stripping
region->regionOffsetY = 0;
region->regionWidth = tex2d.contentSizeInPixels.width;
region->regionHeight = tex2d.contentSizeInPixels.height;
region->regionOriginalWidth = region->regionWidth; // same if not doing whitespace stripping
region->regionOriginalHeight = region->regionHeight;
return region;
}
when I put the Attachment into the slot it always show notthing. How can I fix this?
Sorry for the delayed response.
Your code is good, it does what spine-cocos2d-iphone.m (renderer part) and AtlasAttachmentLoader (atlas part) do, but it misses what SkeletonJson does (loading part). Look just after spAttachmentLoader_newAttachment is called in SkeletonJson and you'll see it further configures the new attachment using data from the JSON. You need to add code like this to what you already have, then it should work:
region->x = 0;
region->y = 0;
region->scaleX = 1;
region->scaleY = 1;
region->rotation = 0;
region->width = 64;
region->height = 64;
spRegionAttachment_updateOffset(region);
We could include something like this in the spine-cocos2d-iphone API, I just haven't had time to do so. If you'd like to share what you end up using, that'd be great.
Thank Nate,
So this mean the code should look like this?
-(RegionAttachment*) regionWithTexture2d:(CCTexture2D*)tex2d name:(NSString*)_name{
CCTextureAtlas *tex = [CCTextureAtlas textureAtlasWithTexture:tex2d capacity:1];
AtlasPage *page = AtlasPage_create([_name UTF8String]);
page->rendererObject = tex;
page->width = tex2d.contentSizeInPixels.width;
page->height = tex2d.contentSizeInPixels.height;
AtlasRegion *reg = AtlasRegion_create();
reg->page = page;
RegionAttachment *region = RegionAttachment_create([_name UTF8String]);
region->rendererObject = reg;
CGSize size = tex2d.contentSizeInPixels;
float u = region->x / size.width;
float u2 = (region->x + region->width) / size.width;
float v = region->y / size.height;
float v2 = (region->y + region->height) / size.height;
RegionAttachment_setUVs(region,u,v,u2,v2,0);
region->regionOffsetX = 0; // zero if not doing whitespace stripping
region->regionOffsetY = 0;
region->regionWidth = size.width;
region->regionHeight = size.height;
region->regionOriginalWidth = region->regionWidth; // same if not doing whitespace stripping
region->regionOriginalHeight = region->regionHeight;
region->x = 0;
region->y = 0;
region->scaleX = 1;
region->scaleY = 1;
region->rotation = 0;
region->width = size.width;
region->height = size.height;
spRegionAttachment_updateOffset(region);
return region;
}
but after I try to attach it into the skeleton it alway error
CCSprite *sp = [CCSprite spriteWithFile:@"Icon.png"];
CCTexture2D *tex = [sp texture];
RegionAttachment *reg = [self regionWithTexture2d:tex name:@"head"];
Slot *s = [spineNode findSlot:@"head"];
Slot_setAttachment(s,(Attachment*)reg);
I quit confuse about Attachment and RegionAttachment.
Seems ok. What error do you get?
It said about the CCTexture2D capacity have problems. I'll capture the log tomorrow (I did this on other mac).
It error in CCSkeleton.h on line 176
if (textureAtlas.capacity == textureAtlas.totalQuads) {
[textureAtlas drawQuads];
[textureAtlas removeAllQuads];
if (![textureAtlas resizeCapacity:textureAtlas.capacity * 2]) return;
}
if (textureAtlas.capacity == textureAtlas.totalQuads)
Still not sure what the error was. Try to set the capacity to 128 when you create the CCTextureAtlas.
I have send the source code for you, Nate.
Please try it.
Your CCTextureAtlas problem was because you weren't retaining the instance, so it gets disposed by the pool.
Your next problem was that you UVs were 0,0,0,0. You were using:
float u = region->x / size.width;
float u2 = (region->x + region->width) / size.width;
float v = region->y / size.height;
float v2 = (region->y + region->height) / size.height;
But you haven't set region x/y/width/height yet, so you get zeros and nothing displays.
Here is the fixed method:
-(RegionAttachment*) regionWithTexture2d:(CCTexture2D*)tex2d name:(NSString*)_name{
CCTextureAtlas *tex = [[CCTextureAtlas alloc] initWithTexture:tex2d capacity:128];
AtlasPage *page = AtlasPage_create([_name UTF8String]);
page->rendererObject = tex;
page->width = tex2d.contentSizeInPixels.width;
page->height = tex2d.contentSizeInPixels.height;
AtlasRegion *reg = AtlasRegion_create();
reg->page = page;
RegionAttachment *region = RegionAttachment_create([_name UTF8String]);
region->rendererObject = reg;
CGSize size = tex2d.contentSizeInPixels;
float u = 0;
float u2 = 1;
float v = 0;
float v2 = 1;
RegionAttachment_setUVs(region,u,v,u2,v2,0);
region->regionOffsetX = 0; // zero if not doing whitespace stripping
region->regionOffsetY = 0;
region->regionWidth = size.width;
region->regionHeight = size.height;
region->regionOriginalWidth = region->regionWidth; // same if not doing whitespace stripping
region->regionOriginalHeight = region->regionHeight;
region->x = 0;
region->y = 0;
region->scaleX = 1;
region->scaleY = 1;
region->rotation = 0;
region->width = size.width;
region->height = size.height;
spRegionAttachment_updateOffset(region);
return region;
}
Result is an icon instead of a head:
Image removed due to the lack of support for HTTPS. | Show Anyway
YES! & Thank you Nate!!
You really save my life!
change these lines to make it compatible with retina screen and rotate back into correct position
region->rotation = -90;
region->width = size.width/CC_CONTENT_SCALE_FACTOR();
region->height = size.height/CC_CONTENT_SCALE_FACTOR();
This is my final code.
-(RegionAttachment*) regionWithTexture2d:(CCTexture2D*)tex2d name:(NSString*)_name{
CCTextureAtlas *tex = [[CCTextureAtlas alloc] initWithTexture:tex2d capacity:1];
AtlasPage *page = AtlasPage_create([_name UTF8String]);
page->rendererObject = tex;
page->width = tex2d.contentSizeInPixels.width;
page->height = tex2d.contentSizeInPixels.height;
AtlasRegion *reg = AtlasRegion_create();
reg->page = page;
RegionAttachment *region = RegionAttachment_create([_name UTF8String]);
region->rendererObject = reg;
CGSize size = tex2d.contentSizeInPixels;
float u = 0;
float u2 = 1;
float v = 0;
float v2 = 1;
RegionAttachment_setUVs(region,u,v,u2,v2,0);
region->regionOffsetX = 0; // zero if not doing whitespace stripping
region->regionOffsetY = 0;
region->regionWidth = size.width;
region->regionHeight = size.height;
region->regionOriginalWidth = region->regionWidth; // same if not doing whitespace stripping
region->regionOriginalHeight = region->regionHeight;
region->x = 0;
region->y = 0;
region->scaleX = 1/CC_CONTENT_SCALE_FACTOR();
region->scaleY = 1/CC_CONTENT_SCALE_FACTOR();
region->rotation = -90;
region->width = size.width;
region->height = size.height;
spRegionAttachment_updateOffset(region);
return region;
}
these are my CCSkeletonAnimation.h and CCSkeletonAnimation.m
CCSkeletonAnimation.h
/******************************************************************************
* Spine Runtime Software License - Version 1.1
*
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms in whole or in part, with
* or without modification, are permitted provided that the following conditions
* are met:
*
* 1. A Spine Essential, Professional, Enterprise, or Education License must
* be purchased from Esoteric Software and the license must remain valid:
* http://esotericsoftware.com/
* 2. Redistributions of source code must retain this license, which is the
* above copyright notice, this declaration of conditions and the following
* disclaimer.
* 3. Redistributions in binary form must reproduce this license, which is the
* above copyright notice, this declaration of conditions and the following
* disclaimer, in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#define SPINE_SHORT_NAMES
#import <spine/spine.h>
#import <spine/CCSkeleton.h>
#import "cocos2d.h"
@class CCSkeletonAnimation;
@protocol CCSkeletonAnimationDelegate <NSObject>
@optional
- (void) animationDidStart:(CCSkeletonAnimation*)animation track:(int)trackIndex;
- (void) animationWillEnd:(CCSkeletonAnimation*)animation track:(int)trackIndex;
- (void) animationDidTriggerEvent:(CCSkeletonAnimation*)animation track:(int)trackIndex event:(Event*)event;
- (void) animationDidComplete:(CCSkeletonAnimation*)animation track:(int)trackIndex loopCount:(int)loopCount;
@end
/** Draws an animated skeleton, providing an AnimationState for applying one or more animations and queuing animations to be
* played later. */
@interface CCSkeletonAnimation : CCSkeleton {
AnimationState* _state;
bool _ownsAnimationStateData;
id<CCSkeletonAnimationDelegate> _delegate;
bool _delegateStart, _delegateEnd, _delegateEvent, _delegateComplete;
}
+ (id) skeletonWithData:(SkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData;
+ (id) skeletonWithFile:(NSString*)skeletonDataFile atlas:(Atlas*)atlas scale:(float)scale;
+ (id) skeletonWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale;
- (id) initWithData:(SkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData;
- (id) initWithFile:(NSString*)skeletonDataFile atlas:(Atlas*)atlas scale:(float)scale;
- (id) initWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale;
- (void) setAnimationStateData:(AnimationStateData*)stateData;
- (void) setMixFrom:(NSString*)fromAnimation to:(NSString*)toAnimation duration:(float)duration;
- (void) setDelegate:(id<CCSkeletonAnimationDelegate>)delegate;
- (TrackEntry*) setAnimationForTrack:(int)trackIndex name:(NSString*)name loop:(bool)loop;
- (TrackEntry*) addAnimationForTrack:(int)trackIndex name:(NSString*)name loop:(bool)loop afterDelay:(int)delay;
- (TrackEntry*) getCurrentForTrack:(int)trackIndex;
- (void) clearTracks;
- (void) clearTrack:(int)trackIndex;
- (void) onAnimationStateEvent:(int)trackIndex type:(EventType)type event:(Event*)event loopCount:(int)loopCount;
- (bool) setCCTexture2D:(CCTexture2D*)texture intoSlot:(NSString*)slotName;
- (bool) setCCSprite:(CCSprite*)sprite intoSlot:(NSString*)slotName;
@property (nonatomic, readonly) AnimationState* state;
@end
#define AnimationStateToString(TYPE) [NSString stringWithFormat:@"%s",AnimationState_getCurrent(TYPE.state,trackIndex)->animation->name]//animation to NSString
CCSkeletonAnimation.m
/******************************************************************************
* Spine Runtime Software License - Version 1.1
*
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms in whole or in part, with
* or without modification, are permitted provided that the following conditions
* are met:
*
* 1. A Spine Essential, Professional, Enterprise, or Education License must
* be purchased from Esoteric Software and the license must remain valid:
* http://esotericsoftware.com/
* 2. Redistributions of source code must retain this license, which is the
* above copyright notice, this declaration of conditions and the following
* disclaimer.
* 3. Redistributions in binary form must reproduce this license, which is the
* above copyright notice, this declaration of conditions and the following
* disclaimer, in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#import <spine/CCSkeletonAnimation.h>
#import <spine/spine-cocos2d-iphone.h>
static void callback (AnimationState* state, int trackIndex, EventType type, Event* event, int loopCount) {
[(CCSkeletonAnimation*)state->context onAnimationStateEvent:trackIndex type:type event:event loopCount:loopCount];
}
@interface CCSkeletonAnimation (Private)
- (void) initialize;
- (RegionAttachment*) regionWithTexture2d:(CCTexture2D*)tex2d name:(NSString*)_name;
@end
@implementation CCSkeletonAnimation
@synthesize state = _state;
+ (id) skeletonWithData:(SkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData {
return [[[self alloc] initWithData:skeletonData ownsSkeletonData:ownsSkeletonData] autorelease];
}
+ (id) skeletonWithFile:(NSString*)skeletonDataFile atlas:(Atlas*)atlas scale:(float)scale {
return [[[self alloc] initWithFile:skeletonDataFile atlas:atlas scale:scale] autorelease];
}
+ (id) skeletonWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale {
return [[[self alloc] initWithFile:skeletonDataFile atlasFile:atlasFile scale:scale] autorelease];
}
- (void) initialize {
_ownsAnimationStateData = true;
_state = AnimationState_create(AnimationStateData_create(_skeleton->data));
_state->context = self;
_state->listener = callback;
}
- (id) initWithData:(SkeletonData*)skeletonData ownsSkeletonData:(bool)ownsSkeletonData {
self = [super initWithData:skeletonData ownsSkeletonData:ownsSkeletonData];
if (!self) return nil;
[self initialize];
return self;
}
- (id) initWithFile:(NSString*)skeletonDataFile atlas:(Atlas*)atlas scale:(float)scale {
self = [super initWithFile:skeletonDataFile atlas:atlas scale:scale];
if (!self) return nil;
[self initialize];
return self;
}
- (id) initWithFile:(NSString*)skeletonDataFile atlasFile:(NSString*)atlasFile scale:(float)scale {
self = [super initWithFile:skeletonDataFile atlasFile:atlasFile scale:scale];
if (!self) return nil;
[self initialize];
return self;
}
- (void) dealloc {
if (_ownsAnimationStateData) AnimationStateData_dispose(_state->data);
AnimationState_dispose(_state);
[super dealloc];
}
- (void) update:(ccTime)deltaTime {
[super update:deltaTime];
deltaTime *= _timeScale;
AnimationState_update(_state, deltaTime);
AnimationState_apply(_state, _skeleton);
Skeleton_updateWorldTransform(_skeleton);
}
- (void) setAnimationStateData:(AnimationStateData*)stateData {
NSAssert(stateData, @"stateData cannot be null.");
if (_ownsAnimationStateData) AnimationStateData_dispose(_state->data);
AnimationState_dispose(_state);
_ownsAnimationStateData = false;
_state = AnimationState_create(stateData);
_state->context = self;
_state->listener = callback;
}
- (void) setMixFrom:(NSString*)fromAnimation to:(NSString*)toAnimation duration:(float)duration {
AnimationStateData_setMixByName(_state->data, [fromAnimation UTF8String], [toAnimation UTF8String], duration);
}
- (void) setDelegate:(id<CCSkeletonAnimationDelegate>)delegate {
_delegate = delegate;
_delegateStart = [delegate respondsToSelector:@selector(animationDidStart:track:)];
_delegateEnd = [delegate respondsToSelector:@selector(animationWillEnd:track:)];
_delegateEvent = [delegate respondsToSelector:@selector(animationDidTriggerEvent:track:event:)];
_delegateComplete = [delegate respondsToSelector:@selector(animationDidComplete:track:loopCount:)];
}
- (TrackEntry*) setAnimationForTrack:(int)trackIndex name:(NSString*)name loop:(bool)loop {
Animation* animation = SkeletonData_findAnimation(_skeleton->data, [name UTF8String]);
if (!animation) {
CCLOG(@"Spine: Animation not found: %@", name);
return 0;
}
return AnimationState_setAnimation(_state, trackIndex, animation, loop);
}
- (TrackEntry*) addAnimationForTrack:(int)trackIndex name:(NSString*)name loop:(bool)loop afterDelay:(int)delay {
Animation* animation = SkeletonData_findAnimation(_skeleton->data, [name UTF8String]);
if (!animation) {
CCLOG(@"Spine: Animation not found: %@", name);
return 0;
}
return AnimationState_addAnimation(_state, trackIndex, animation, loop, delay);
}
- (TrackEntry*) getCurrentForTrack:(int)trackIndex {
return AnimationState_getCurrent(_state, trackIndex);
}
- (void) clearTracks {
AnimationState_clearTracks(_state);
}
- (void) clearTrack:(int)trackIndex {
AnimationState_clearTrack(_state, trackIndex);
}
- (void) onAnimationStateEvent:(int)trackIndex type:(EventType)type event:(Event*)event loopCount:(int)loopCount {
if (!_delegate) return;
switch (type) {
case ANIMATION_START:
if (_delegateStart) [_delegate animationDidStart:self track:trackIndex];
break;
case ANIMATION_END:
if (_delegateEnd) [_delegate animationWillEnd:self track:trackIndex];
break;
case ANIMATION_COMPLETE:
if (_delegateComplete) [_delegate animationDidComplete:self track:trackIndex loopCount:loopCount];
break;
case ANIMATION_EVENT:
if (_delegateEvent) [_delegate animationDidTriggerEvent:self track:trackIndex event:event];
break;
}
}
- (bool) setCCTexture2D:(CCTexture2D*)texture intoSlot:(NSString*)slotName{
RegionAttachment *reg = [self regionWithTexture2d:texture
name:slotName];
Slot *slot = [self findSlot:slotName];
if ((slot!=nil)&&(reg!=nil)) {
Slot_setAttachment(slot,(Attachment*)reg);
return true;
}else{
return false;
}
return false;
}
- (bool) setCCSprite:(CCSprite*)sprite intoSlot:(NSString*)slotName{
//render for get all CCSprite setting
sprite.position = ccp(sprite.contentSize.width,sprite.contentSize.height);
sprite.anchorPoint = ccp(0,0); //set sprite to origin
sprite.rotation = 180; //the rendered texture will flip
sprite.flipX = YES; // flip back
CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:sprite.contentSize.width
height:sprite.contentSize.height];
[rt beginWithClear:0 g:0 b:0 a:0]; //start with clear
[sprite visit]; //visit for draw
[rt end];//end
return [self setCCTexture2D:rt.sprite.texture intoSlot:slotName]; //return
}
-(RegionAttachment*) regionWithTexture2d:(CCTexture2D*)tex2d name:(NSString*)_name{
CCTextureAtlas *tex = [[CCTextureAtlas alloc] initWithTexture:tex2d capacity:1];
AtlasPage *page = AtlasPage_create([_name UTF8String]);
page->rendererObject = tex;
page->width = tex2d.contentSizeInPixels.width;
page->height = tex2d.contentSizeInPixels.height;
AtlasRegion *reg = AtlasRegion_create();
reg->page = page;
RegionAttachment *region = RegionAttachment_create([_name UTF8String]);
region->rendererObject = reg;
CGSize size = tex2d.contentSizeInPixels;
float u = 0;
float u2 = 1;
float v = 0;
float v2 = 1;
RegionAttachment_setUVs(region,u,v,u2,v2,0);
Slot *slot = [self findSlot:_name];
RegionAttachment* attachment = (RegionAttachment*)slot->attachment;
region->regionOffsetX = 0;
region->regionOffsetY = 0;
region->regionWidth = size.width;
region->regionHeight = size.height;
region->regionOriginalWidth = region->regionWidth; // same if not doing whitespace stripping
region->regionOriginalHeight = region->regionHeight;
region->x = attachment->x;
region->y = attachment->y;
region->scaleX = self.nodeScale;
region->scaleY = self.nodeScale;
region->rotation = attachment->rotation;
region->width = size.width;
region->height = size.height;
spRegionAttachment_updateOffset(region);
return region;
}
@end
now we can change the part on-fly by this
CGSize wins = [[CCDirector sharedDirector]winSize];
Atlas *spineBoyAtlas = Atlas_readAtlasFile([@"spineboy.atlas" UTF8String]);
CCSkeletonAnimation *spineNode = [CCSkeletonAnimation skeletonWithFile:@"spineboy.json" atlas:spineBoyAtlas scale:1.0f];
[spineNode setMixFrom:@"walk" to:@"jump" duration:0.2f];
[spineNode setMixFrom:@"jump" to:@"walk" duration:0.4f];
[spineNode setDelegate:self];
[spineNode addAnimationForTrack:0 name:@"walk" loop:YES afterDelay:0];
[spineNode addAnimationForTrack:0 name:@"jump" loop:YES afterDelay:4];
[spineNode setPosition:ccp(wins.width*0.5f, wins.height*0.25f)];
[self addChild:spineNode z:1 tag:100];
//change
CCSprite *sprite = [CCSprite spriteWithFile:@"Icon.png"];
//can change color or other CCSprite properties
sprite.color = ccc3(100, 200, 300);
[spineNode setCCSprite:sprite intoSlot:@"neck"];
Thank you.
Glad it is working!
Hi,
First let me say this is awesome and will be very useful to me
thanks for sharing it!
I've ported it to Cocos2d-x and integrated it into my project. Unfortunately I'm seeing some weird image offset issues on the new attachment. In the picture below, the right half shows what the hair graphic should look like. The left half shows the graphic after being attached. If you look very closely about halfway up the image is shifted 2 pixels to the left.
The skeleton has several animations. The one shown here is "stand." When "walk" is animated, the image offsets/jaggies increase.
This seems to be related to either the uvs or the rotation of the newly created region. Right now I'm using:
region->rotation = attachment->rotation;
If I change rotation to -90 (as suggested in the previous post) I get a slightly different result. The "stand" animation looks even worse (see below) but the "walk" animation looks perfect!
region->rotation = -90;
I've tried copying the uvs directly from the current attachment to the new region, but that was way off.
Any idea what's going on? I'd appreciate suggestions on what to try next!
Thanks for your time,
Shoog
It looks like a filtering artifact. Since you want your images to move around, it's impossible to draw your images aligned to screen pixels, so there is always going to be some filtering. Make sure you are using linear and not nearest neighbor filtering and likely it'll smooth it out enough that it isn't an issue.
Filtering is a good suggestion. I added this to regionWithTexture2d(). These settings matches the configuration of the character's original Atlas. Given that with the original attachment renders without these artifacts, it seems reasonable that this should work.
page->format = ATLAS_RGBA8888;
page->magFilter = ATLAS_LINEAR;
page->minFilter = ATLAS_LINEAR;
Unfortunately, the results are identical to before
same artifacts. I don't see anything else inconsistent in the AtlasPage. Can you think of anything in the AtlasRegion that might impact this?
Those fields on the page are read from the atlas and are intended to be applied by whatever loads the atlas. I don't know that all atlas loaders respect them though. You'll want to set the filtering to linear on the CCTexture2D. cocos2d's API is a bit strange here, but it looks like you need to call setAntiAliasTexParameters on CCTexture2D (it takes no arguments).
You're correct
CCTexture2D setAntiAliasTexParameters() did the trick. For anyone following along, the change is to add this to the top of regionWithTexture2d and I also needed to default rotation to -90.0:
RegionAttachment* CCSkeletonAnimation::regionWithTexture2d(CCTexture2D* tex2d, CCString* name)
{
tex2d->setAntiAliasTexParameters();
...
region->rotation = -90.0;
Thanks for your help, Nate. Much appreciated!
In new spine version look like it will error on this line.
AtlasPage *page = AtlasPage_create([_name UTF8String]);
any idea?