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/CCProgressTimer.m | 493 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100755 libs/cocos2d/CCProgressTimer.m (limited to 'libs/cocos2d/CCProgressTimer.m') diff --git a/libs/cocos2d/CCProgressTimer.m b/libs/cocos2d/CCProgressTimer.m new file mode 100755 index 0000000..4e697b2 --- /dev/null +++ b/libs/cocos2d/CCProgressTimer.m @@ -0,0 +1,493 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2010 Lam Pham + * + * 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. + * + */ + +#import "CCProgressTimer.h" + +#import "ccMacros.h" +#import "CCTextureCache.h" +#import "Support/CGPointExtension.h" + + + +#define kProgressTextureCoordsCount 4 +// kProgressTextureCoords holds points {0,0} {0,1} {1,1} {1,0} we can represent it as bits +const char kProgressTextureCoords = 0x1e; + +@interface CCProgressTimer (Internal) + +-(void)updateProgress; +-(void)updateBar; +-(void)updateRadial; +-(void)updateColor; +-(CGPoint)boundaryTexCoord:(char)index; +@end + + +@implementation CCProgressTimer +@synthesize percentage = percentage_; +@synthesize sprite = sprite_; +@synthesize type = type_; + ++(id)progressWithFile:(NSString*) filename +{ + return [[[self alloc]initWithFile:filename] autorelease]; +} +-(id)initWithFile:(NSString*) filename +{ + return [self initWithTexture:[[CCTextureCache sharedTextureCache] addImage: filename]]; +} + ++(id)progressWithTexture:(CCTexture2D*) texture +{ + return [[[self alloc]initWithTexture:texture] autorelease]; +} +-(id)initWithTexture:(CCTexture2D*) texture +{ + if(( self = [super init] )){ + self.sprite = [CCSprite spriteWithTexture:texture]; + percentage_ = 0.f; + vertexData_ = NULL; + vertexDataCount_ = 0; + self.anchorPoint = ccp(.5f,.5f); + self.contentSize = sprite_.contentSize; + self.type = kCCProgressTimerTypeRadialCCW; + } + return self; +} +-(void)dealloc +{ + if(vertexData_) + free(vertexData_); + + [sprite_ release]; + [super dealloc]; +} + +-(void)setPercentage:(float) percentage +{ + if(percentage_ != percentage) { + percentage_ = clampf( percentage, 0, 100); + [self updateProgress]; + } +} +-(void)setSprite:(CCSprite *)newSprite +{ + if(sprite_ != newSprite){ + [sprite_ release]; + sprite_ = [newSprite retain]; + + // Everytime we set a new sprite, we free the current vertex data + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + } +} +-(void)setType:(CCProgressTimerType)newType +{ + if (newType != type_) { + + // release all previous information + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + type_ = newType; + } +} +@end + +@implementation CCProgressTimer(Internal) + +/// +// @returns the vertex position from the texture coordinate +/// +-(ccVertex2F)vertexFromTexCoord:(CGPoint) texCoord +{ + CGPoint tmp; + ccVertex2F ret; + if (sprite_.texture) { + CCTexture2D *texture = [sprite_ texture]; + CGSize texSize = [texture contentSizeInPixels]; + tmp = ccp(texSize.width * texCoord.x/texture.maxS, + texSize.height * (1 - (texCoord.y/texture.maxT))); + } else + tmp = CGPointZero; + + ret.x = tmp.x; + ret.y = tmp.y; + return ret; +} + +-(void)updateColor +{ + GLubyte op = sprite_.opacity; + ccColor3B c3b = sprite_.color; + + ccColor4B color = { c3b.r, c3b.g, c3b.b, op }; + if([sprite_.texture hasPremultipliedAlpha]){ + color.r *= op/255; + color.g *= op/255; + color.b *= op/255; + } + + if(vertexData_){ + for (int i=0; i < vertexDataCount_; ++i) { + vertexData_[i].colors = color; + } + } +} + +-(void)updateProgress +{ + switch (type_) { + case kCCProgressTimerTypeRadialCW: + case kCCProgressTimerTypeRadialCCW: + [self updateRadial]; + break; + case kCCProgressTimerTypeHorizontalBarLR: + case kCCProgressTimerTypeHorizontalBarRL: + case kCCProgressTimerTypeVerticalBarBT: + case kCCProgressTimerTypeVerticalBarTB: + [self updateBar]; + break; + default: + break; + } +} + +/// +// Update does the work of mapping the texture onto the triangles +// It now doesn't occur the cost of free/alloc data every update cycle. +// It also only changes the percentage point but no other points if they have not +// been modified. +// +// It now deals with flipped texture. If you run into this problem, just use the +// sprite property and enable the methods flipX, flipY. +/// +-(void)updateRadial +{ + // Texture Max is the actual max coordinates to deal with non-power of 2 textures + CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT); + + // Grab the midpoint + CGPoint midpoint = ccpCompMult(self.anchorPoint, tMax); + + float alpha = percentage_ / 100.f; + + // Otherwise we can get the angle from the alpha + float angle = 2.f*((float)M_PI) * ( type_ == kCCProgressTimerTypeRadialCW? alpha : 1.f - alpha); + + // We find the vector to do a hit detection based on the percentage + // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate + // from that by the progress angle around the midpoint pivot + CGPoint topMid = ccp(midpoint.x, 0.f); + CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle); + + + int index = 0; + CGPoint hit = CGPointZero; + + if (alpha == 0.f) { + // More efficient since we don't always need to check intersection + // If the alpha is zero then the hit point is top mid and the index is 0. + hit = topMid; + index = 0; + } else if (alpha == 1.f) { + // More efficient since we don't always need to check intersection + // If the alpha is one then the hit point is top mid and the index is 4. + hit = topMid; + index = 4; + } else { + // We run a for loop checking the edges of the texture to find the + // intersection point + // We loop through five points since the top is split in half + + float min_t = FLT_MAX; + + for (int i = 0; i <= kProgressTextureCoordsCount; ++i) { + int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount; + + CGPoint edgePtA = ccpCompMult([self boundaryTexCoord:i % kProgressTextureCoordsCount],tMax); + CGPoint edgePtB = ccpCompMult([self boundaryTexCoord:pIndex],tMax); + + // Remember that the top edge is split in half for the 12 o'clock position + // Let's deal with that here by finding the correct endpoints + if(i == 0){ + edgePtB = ccpLerp(edgePtA,edgePtB,.5f); + } else if(i == 4){ + edgePtA = ccpLerp(edgePtA,edgePtB,.5f); + } + + // s and t are returned by ccpLineIntersect + float s = 0, t = 0; + if(ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t)) + { + + // Since our hit test is on rays we have to deal with the top edge + // being in split in half so we have to test as a segment + if ((i == 0 || i == 4)) { + // s represents the point between edgePtA--edgePtB + if (!(0.f <= s && s <= 1.f)) { + continue; + } + } + // As long as our t isn't negative we are at least finding a + // correct hitpoint from midpoint to percentagePt. + if (t >= 0.f) { + // Because the percentage line and all the texture edges are + // rays we should only account for the shortest intersection + if (t < min_t) { + min_t = t; + index = i; + } + } + } + } + + // Now that we have the minimum magnitude we can use that to find our intersection + hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t)); + + } + + + // The size of the vertex data is the index from the hitpoint + // the 3 is for the midpoint, 12 o'clock point and hitpoint position. + + BOOL sameIndexCount = YES; + if(vertexDataCount_ != index + 3){ + sameIndexCount = NO; + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + } + + + if(!vertexData_) { + vertexDataCount_ = index + 3; + vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); + NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); + + [self updateColor]; + } + + if (!sameIndexCount) { + + // First we populate the array with the midpoint, then all + // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint + vertexData_[0].texCoords = (ccTex2F){midpoint.x, midpoint.y}; + vertexData_[0].vertices = [self vertexFromTexCoord:midpoint]; + + vertexData_[1].texCoords = (ccTex2F){midpoint.x, 0.f}; + vertexData_[1].vertices = [self vertexFromTexCoord:ccp(midpoint.x, 0.f)]; + + for(int i = 0; i < index; ++i){ + CGPoint texCoords = ccpCompMult([self boundaryTexCoord:i], tMax); + + vertexData_[i+2].texCoords = (ccTex2F){texCoords.x, texCoords.y}; + vertexData_[i+2].vertices = [self vertexFromTexCoord:texCoords]; + } + + // Flip the texture coordinates if set + if (sprite_.flipY || sprite_.flipX) { + for(int i = 0; i < vertexDataCount_ - 1; ++i){ + if (sprite_.flipX) { + vertexData_[i].texCoords.u = tMax.x - vertexData_[i].texCoords.u; + } + if(sprite_.flipY){ + vertexData_[i].texCoords.v = tMax.y - vertexData_[i].texCoords.v; + } + } + } + } + + // hitpoint will go last + vertexData_[vertexDataCount_ - 1].texCoords = (ccTex2F){hit.x, hit.y}; + vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromTexCoord:hit]; + + if (sprite_.flipY || sprite_.flipX) { + if (sprite_.flipX) { + vertexData_[vertexDataCount_ - 1].texCoords.u = tMax.x - vertexData_[vertexDataCount_ - 1].texCoords.u; + } + if(sprite_.flipY){ + vertexData_[vertexDataCount_ - 1].texCoords.v = tMax.y - vertexData_[vertexDataCount_ - 1].texCoords.v; + } + } +} + +/// +// Update does the work of mapping the texture onto the triangles for the bar +// It now doesn't occur the cost of free/alloc data every update cycle. +// It also only changes the percentage point but no other points if they have not +// been modified. +// +// It now deals with flipped texture. If you run into this problem, just use the +// sprite property and enable the methods flipX, flipY. +/// +-(void)updateBar +{ + + float alpha = percentage_ / 100.f; + + CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT); + + unsigned char vIndexes[2] = {0,0}; + unsigned char index = 0; + + // We know vertex data is always equal to the 4 corners + // If we don't have vertex data then we create it here and populate + // the side of the bar vertices that won't ever change. + if (!vertexData_) { + vertexDataCount_ = kProgressTextureCoordsCount; + vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); + NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); + + if(type_ == kCCProgressTimerTypeHorizontalBarLR){ + vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0,0}; + vertexData_[vIndexes[1] = 1].texCoords = (ccTex2F){0, tMax.y}; + }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) { + vertexData_[vIndexes[0] = 2].texCoords = (ccTex2F){tMax.x, tMax.y}; + vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, 0.f}; + }else if (type_ == kCCProgressTimerTypeVerticalBarBT) { + vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y}; + vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y}; + }else if (type_ == kCCProgressTimerTypeVerticalBarTB) { + vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, 0}; + vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, 0}; + } + + index = vIndexes[0]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + + index = vIndexes[1]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + + if (sprite_.flipY || sprite_.flipX) { + if (sprite_.flipX) { + index = vIndexes[0]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + index = vIndexes[1]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + } + if(sprite_.flipY){ + index = vIndexes[0]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + index = vIndexes[1]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + } + } + + [self updateColor]; + } + + if(type_ == kCCProgressTimerTypeHorizontalBarLR){ + vertexData_[vIndexes[0] = 3].texCoords = (ccTex2F){tMax.x*alpha, tMax.y}; + vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x*alpha, 0}; + }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) { + vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){tMax.x*(1.f - alpha), 0}; + vertexData_[vIndexes[1] = 0].texCoords = (ccTex2F){tMax.x*(1.f - alpha), tMax.y}; + }else if (type_ == kCCProgressTimerTypeVerticalBarBT) { + vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, tMax.y*(1.f - alpha)}; + vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, tMax.y*(1.f - alpha)}; + }else if (type_ == kCCProgressTimerTypeVerticalBarTB) { + vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y*alpha}; + vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y*alpha}; + } + + index = vIndexes[0]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + index = vIndexes[1]; + vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; + + if (sprite_.flipY || sprite_.flipX) { + if (sprite_.flipX) { + index = vIndexes[0]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + index = vIndexes[1]; + vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + } + if(sprite_.flipY){ + index = vIndexes[0]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + index = vIndexes[1]; + vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + } + } + +} + +-(CGPoint)boundaryTexCoord:(char)index +{ + if (index < kProgressTextureCoordsCount) { + switch (type_) { + case kCCProgressTimerTypeRadialCW: + return ccp((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1); + case kCCProgressTimerTypeRadialCCW: + return ccp((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1); + default: + break; + } + } + return CGPointZero; +} + +-(void)draw +{ + [super draw]; + + if(!vertexData_)return; + if(!sprite_)return; + ccBlendFunc blendFunc = sprite_.blendFunc; + BOOL newBlend = blendFunc.src != CC_BLEND_SRC || blendFunc.dst != CC_BLEND_DST; + if( newBlend ) + glBlendFunc( blendFunc.src, blendFunc.dst ); + + /// ======================================================================== + // Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData + // Everything above me and below me is copied from CCTextureNode's draw + glBindTexture(GL_TEXTURE_2D, sprite_.texture.name); + glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].vertices); + glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].texCoords); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &vertexData_[0].colors); + if(type_ == kCCProgressTimerTypeRadialCCW || type_ == kCCProgressTimerTypeRadialCW){ + glDrawArrays(GL_TRIANGLE_FAN, 0, vertexDataCount_); + } else if (type_ == kCCProgressTimerTypeHorizontalBarLR || + type_ == kCCProgressTimerTypeHorizontalBarRL || + type_ == kCCProgressTimerTypeVerticalBarBT || + type_ == kCCProgressTimerTypeVerticalBarTB) { + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_); + } + //glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_); + /// ======================================================================== + + if( newBlend ) + glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); +} + +@end -- cgit 1.4.1