From 9cd57b731ab1c666d4a1cb725538fdc137763d12 Mon Sep 17 00:00:00 2001 From: Starla Insigna Date: Sat, 30 Jul 2011 11:19:14 -0400 Subject: Initial commit (version 0.2.1) --- libs/cocos2d/CCRibbon.m | 383 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100755 libs/cocos2d/CCRibbon.m (limited to 'libs/cocos2d/CCRibbon.m') diff --git a/libs/cocos2d/CCRibbon.m b/libs/cocos2d/CCRibbon.m new file mode 100755 index 0000000..2d9acaa --- /dev/null +++ b/libs/cocos2d/CCRibbon.m @@ -0,0 +1,383 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008, 2009 Jason Booth + * + * 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. + */ + +/* + * A ribbon is a dynamically generated list of polygons drawn as a single or series + * of triangle strips. The primary use of Ribbon is as the drawing class of Motion Streak, + * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt + * and pass in the parameters for the next location in the ribbon. The system will automatically + * generate new polygons, texture them accourding to your texture width, etc, etc. + * + * Ribbon data is stored in a RibbonSegment class. This class statically allocates enough verticies and + * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate + * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly + * allocating new memory and prefer a more static method. However, since there is no way to determine + * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible. + * + */ + + +#import "CCRibbon.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" +#import "ccMacros.h" + +// +// Ribbon +// +@implementation CCRibbon +@synthesize blendFunc=blendFunc_; +@synthesize color=color_; +@synthesize textureLength = textureLength_; + ++(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade +{ + self = [[[self alloc] initWithWidth:w image:path length:l color:color fade:fade] autorelease]; + return self; +} + +-(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade +{ + self = [super init]; + if (self) + { + + segments_ = [[NSMutableArray alloc] init]; + deletedSegments_ = [[NSMutableArray alloc] init]; + + /* 1 initial segment */ + CCRibbonSegment* seg = [[CCRibbonSegment alloc] init]; + [segments_ addObject:seg]; + [seg release]; + + textureLength_ = l; + + color_ = color; + fadeTime_ = fade; + lastLocation_ = CGPointZero; + lastWidth_ = w/2; + texVPos_ = 0.0f; + + curTime_ = 0; + pastFirstPoint_ = NO; + + /* XXX: + Ribbon, by default uses this blend function, which might not be correct + if you are using premultiplied alpha images, + but 99% you might want to use this blending function regarding of the texture + */ + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + + self.texture = [[CCTextureCache sharedTextureCache] addImage:path]; + + /* default texture parameter */ + ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; + [texture_ setTexParameters:¶ms]; + } + return self; +} + +-(void)dealloc +{ + [segments_ release]; + [deletedSegments_ release]; + [texture_ release]; + [super dealloc]; +} + +// rotates a point around 0, 0 +-(CGPoint)rotatePoint:(CGPoint)vec rotation:(float)a +{ + float xtemp = (vec.x * cosf(a)) - (vec.y * sinf(a)); + vec.y = (vec.x * sinf(a)) + (vec.y * cosf(a)); + vec.x = xtemp; + return vec; +} + +-(void)update:(ccTime)delta +{ + curTime_+= delta; + delta_ = delta; +} + +-(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2 +{ + CGPoint vp = ccpPerp(ccpSub(l1, l2)); + CGPoint vx = ccpSub(p, l1); + return ccpDot(vx, vp); +} + +// adds a new segment to the ribbon +-(void)addPointAt:(CGPoint)location width:(float)w +{ + location.x *= CC_CONTENT_SCALE_FACTOR(); + location.y *= CC_CONTENT_SCALE_FACTOR(); + + w = w*0.5f; + // if this is the first point added, cache it and return + if (!pastFirstPoint_) + { + lastWidth_ = w; + lastLocation_ = location; + pastFirstPoint_ = YES; + return; + } + + CGPoint sub = ccpSub(lastLocation_, location); + float r = ccpToAngle(sub) + (float)M_PI_2; + CGPoint p1 = ccpAdd([self rotatePoint:ccp(-w, 0) rotation:r], location); + CGPoint p2 = ccpAdd([self rotatePoint:ccp(w, 0) rotation:r], location); + float len = sqrtf(powf(lastLocation_.x - location.x, 2) + powf(lastLocation_.y - location.y, 2)); + float tend = texVPos_ + len/textureLength_; + CCRibbonSegment* seg; + // grab last segment + seg = [segments_ lastObject]; + // lets kill old segments + for (CCRibbonSegment* seg2 in segments_) + { + if (seg2 != seg && seg2->finished) + { + [deletedSegments_ addObject:seg2]; + } + } + [segments_ removeObjectsInArray:deletedSegments_]; + // is the segment full? + if (seg->end >= 50) + [segments_ removeObjectsInArray:deletedSegments_]; + // grab last segment and append to it if it's not full + seg = [segments_ lastObject]; + // is the segment full? + if (seg->end >= 50) + { + CCRibbonSegment* newSeg; + // grab it from the cache if we can + if ([deletedSegments_ count] > 0) + { + newSeg = [deletedSegments_ objectAtIndex:0]; + [newSeg retain]; // will be released later + [deletedSegments_ removeObject:newSeg]; + [newSeg reset]; + } + else + { + newSeg = [[CCRibbonSegment alloc] init]; // will be released later + } + + newSeg->creationTime[0] = seg->creationTime[seg->end - 1]; + int v = (seg->end-1)*6; + int c = (seg->end-1)*4; + newSeg->verts[0] = seg->verts[v]; + newSeg->verts[1] = seg->verts[v+1]; + newSeg->verts[2] = seg->verts[v+2]; + newSeg->verts[3] = seg->verts[v+3]; + newSeg->verts[4] = seg->verts[v+4]; + newSeg->verts[5] = seg->verts[v+5]; + + newSeg->coords[0] = seg->coords[c]; + newSeg->coords[1] = seg->coords[c+1]; + newSeg->coords[2] = seg->coords[c+2]; + newSeg->coords[3] = seg->coords[c+3]; + newSeg->end++; + seg = newSeg; + [segments_ addObject:seg]; + [newSeg release]; // it was retained before + + } + if (seg->end == 0) + { + // first edge has to get rotation from the first real polygon + CGPoint lp1 = ccpAdd([self rotatePoint:ccp(-lastWidth_, 0) rotation:r], lastLocation_); + CGPoint lp2 = ccpAdd([self rotatePoint:ccp(+lastWidth_, 0) rotation:r], lastLocation_); + seg->creationTime[0] = curTime_ - delta_; + seg->verts[0] = lp1.x; + seg->verts[1] = lp1.y; + seg->verts[2] = 0.0f; + seg->verts[3] = lp2.x; + seg->verts[4] = lp2.y; + seg->verts[5] = 0.0f; + seg->coords[0] = 0.0f; + seg->coords[1] = texVPos_; + seg->coords[2] = 1.0f; + seg->coords[3] = texVPos_; + seg->end++; + } + + int v = seg->end*6; + int c = seg->end*4; + // add new vertex + seg->creationTime[seg->end] = curTime_; + seg->verts[v] = p1.x; + seg->verts[v+1] = p1.y; + seg->verts[v+2] = 0.0f; + seg->verts[v+3] = p2.x; + seg->verts[v+4] = p2.y; + seg->verts[v+5] = 0.0f; + + + seg->coords[c] = 0.0f; + seg->coords[c+1] = tend; + seg->coords[c+2] = 1.0f; + seg->coords[c+3] = tend; + + texVPos_ = tend; + lastLocation_ = location; + lastPoint1_ = p1; + lastPoint2_ = p2; + lastWidth_ = w; + seg->end++; +} + +-(void) draw +{ + [super draw]; + + if ([segments_ count] > 0) + { + // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY + // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY + // Unneeded states: GL_COLOR_ARRAY + glDisableClientState(GL_COLOR_ARRAY); + + glBindTexture(GL_TEXTURE_2D, [texture_ name]); + + BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc_.src, blendFunc_.dst ); + + for (CCRibbonSegment* seg in segments_) + [seg draw:curTime_ fadeTime:fadeTime_ color:color_]; + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + // restore default GL state + glEnableClientState( GL_COLOR_ARRAY ); + } +} + +#pragma mark Ribbon - CocosNodeTexture protocol +-(void) setTexture:(CCTexture2D*) texture +{ + [texture_ release]; + texture_ = [texture retain]; + [self setContentSizeInPixels: texture.contentSizeInPixels]; + /* XXX Don't update blending function in Ribbons */ +} + +-(CCTexture2D*) texture +{ + return texture_; +} + +@end + + +#pragma mark - +#pragma mark RibbonSegment + +@implementation CCRibbonSegment + +-(id)init +{ + self = [super init]; + if (self) + { + [self reset]; + } + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | end = %i, begin = %i>", [self class], self, end, begin]; +} + +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [super dealloc]; +} + +-(void)reset +{ + end = 0; + begin = 0; + finished = NO; +} + +-(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color +{ + GLubyte r = color.r; + GLubyte g = color.g; + GLubyte b = color.b; + GLubyte a = color.a; + + if (begin < 50) + { + // the motion streak class will call update and cause time to change, thus, if curTime_ != 0 + // we have to generate alpha for the ribbon each frame. + if (curTime == 0) + { + // no alpha over time, so just set the color + glColor4ub(r,g,b,a); + } + else + { + // generate alpha/color for each point + glEnableClientState(GL_COLOR_ARRAY); + uint i = begin; + for (; i < end; ++i) + { + int idx = i*8; + colors[idx] = r; + colors[idx+1] = g; + colors[idx+2] = b; + colors[idx+4] = r; + colors[idx+5] = g; + colors[idx+6] = b; + float alive = ((curTime - creationTime[i]) / fadeTime); + if (alive > 1) + { + begin++; + colors[idx+3] = 0; + colors[idx+7] = 0; + } + else + { + colors[idx+3] = (GLubyte)(255.f - (alive * 255.f)); + colors[idx+7] = colors[idx+3]; + } + } + glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[begin*8]); + } + glVertexPointer(3, GL_FLOAT, 0, &verts[begin*6]); + glTexCoordPointer(2, GL_FLOAT, 0, &coords[begin*4]); + glDrawArrays(GL_TRIANGLE_STRIP, 0, (end - begin) * 2); + } + else + finished = YES; +} +@end + -- cgit 1.4.1