summary refs log tree commit diff stats
path: root/libs/cocos2d/CCProgressTimer.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCProgressTimer.m')
-rwxr-xr-xlibs/cocos2d/CCProgressTimer.m493
1 files changed, 493 insertions, 0 deletions
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 @@
1/*
2 * cocos2d for iPhone: http://www.cocos2d-iphone.org
3 *
4 * Copyright (c) 2010 Lam Pham
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 */
25
26#import "CCProgressTimer.h"
27
28#import "ccMacros.h"
29#import "CCTextureCache.h"
30#import "Support/CGPointExtension.h"
31
32
33
34#define kProgressTextureCoordsCount 4
35// kProgressTextureCoords holds points {0,0} {0,1} {1,1} {1,0} we can represent it as bits
36const char kProgressTextureCoords = 0x1e;
37
38@interface CCProgressTimer (Internal)
39
40-(void)updateProgress;
41-(void)updateBar;
42-(void)updateRadial;
43-(void)updateColor;
44-(CGPoint)boundaryTexCoord:(char)index;
45@end
46
47
48@implementation CCProgressTimer
49@synthesize percentage = percentage_;
50@synthesize sprite = sprite_;
51@synthesize type = type_;
52
53+(id)progressWithFile:(NSString*) filename
54{
55 return [[[self alloc]initWithFile:filename] autorelease];
56}
57-(id)initWithFile:(NSString*) filename
58{
59 return [self initWithTexture:[[CCTextureCache sharedTextureCache] addImage: filename]];
60}
61
62+(id)progressWithTexture:(CCTexture2D*) texture
63{
64 return [[[self alloc]initWithTexture:texture] autorelease];
65}
66-(id)initWithTexture:(CCTexture2D*) texture
67{
68 if(( self = [super init] )){
69 self.sprite = [CCSprite spriteWithTexture:texture];
70 percentage_ = 0.f;
71 vertexData_ = NULL;
72 vertexDataCount_ = 0;
73 self.anchorPoint = ccp(.5f,.5f);
74 self.contentSize = sprite_.contentSize;
75 self.type = kCCProgressTimerTypeRadialCCW;
76 }
77 return self;
78}
79-(void)dealloc
80{
81 if(vertexData_)
82 free(vertexData_);
83
84 [sprite_ release];
85 [super dealloc];
86}
87
88-(void)setPercentage:(float) percentage
89{
90 if(percentage_ != percentage) {
91 percentage_ = clampf( percentage, 0, 100);
92 [self updateProgress];
93 }
94}
95-(void)setSprite:(CCSprite *)newSprite
96{
97 if(sprite_ != newSprite){
98 [sprite_ release];
99 sprite_ = [newSprite retain];
100
101 // Everytime we set a new sprite, we free the current vertex data
102 if(vertexData_){
103 free(vertexData_);
104 vertexData_ = NULL;
105 vertexDataCount_ = 0;
106 }
107 }
108}
109-(void)setType:(CCProgressTimerType)newType
110{
111 if (newType != type_) {
112
113 // release all previous information
114 if(vertexData_){
115 free(vertexData_);
116 vertexData_ = NULL;
117 vertexDataCount_ = 0;
118 }
119 type_ = newType;
120 }
121}
122@end
123
124@implementation CCProgressTimer(Internal)
125
126///
127// @returns the vertex position from the texture coordinate
128///
129-(ccVertex2F)vertexFromTexCoord:(CGPoint) texCoord
130{
131 CGPoint tmp;
132 ccVertex2F ret;
133 if (sprite_.texture) {
134 CCTexture2D *texture = [sprite_ texture];
135 CGSize texSize = [texture contentSizeInPixels];
136 tmp = ccp(texSize.width * texCoord.x/texture.maxS,
137 texSize.height * (1 - (texCoord.y/texture.maxT)));
138 } else
139 tmp = CGPointZero;
140
141 ret.x = tmp.x;
142 ret.y = tmp.y;
143 return ret;
144}
145
146-(void)updateColor
147{
148 GLubyte op = sprite_.opacity;
149 ccColor3B c3b = sprite_.color;
150
151 ccColor4B color = { c3b.r, c3b.g, c3b.b, op };
152 if([sprite_.texture hasPremultipliedAlpha]){
153 color.r *= op/255;
154 color.g *= op/255;
155 color.b *= op/255;
156 }
157
158 if(vertexData_){
159 for (int i=0; i < vertexDataCount_; ++i) {
160 vertexData_[i].colors = color;
161 }
162 }
163}
164
165-(void)updateProgress
166{
167 switch (type_) {
168 case kCCProgressTimerTypeRadialCW:
169 case kCCProgressTimerTypeRadialCCW:
170 [self updateRadial];
171 break;
172 case kCCProgressTimerTypeHorizontalBarLR:
173 case kCCProgressTimerTypeHorizontalBarRL:
174 case kCCProgressTimerTypeVerticalBarBT:
175 case kCCProgressTimerTypeVerticalBarTB:
176 [self updateBar];
177 break;
178 default:
179 break;
180 }
181}
182
183///
184// Update does the work of mapping the texture onto the triangles
185// It now doesn't occur the cost of free/alloc data every update cycle.
186// It also only changes the percentage point but no other points if they have not
187// been modified.
188//
189// It now deals with flipped texture. If you run into this problem, just use the
190// sprite property and enable the methods flipX, flipY.
191///
192-(void)updateRadial
193{
194 // Texture Max is the actual max coordinates to deal with non-power of 2 textures
195 CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT);
196
197 // Grab the midpoint
198 CGPoint midpoint = ccpCompMult(self.anchorPoint, tMax);
199
200 float alpha = percentage_ / 100.f;
201
202 // Otherwise we can get the angle from the alpha
203 float angle = 2.f*((float)M_PI) * ( type_ == kCCProgressTimerTypeRadialCW? alpha : 1.f - alpha);
204
205 // We find the vector to do a hit detection based on the percentage
206 // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
207 // from that by the progress angle around the midpoint pivot
208 CGPoint topMid = ccp(midpoint.x, 0.f);
209 CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle);
210
211
212 int index = 0;
213 CGPoint hit = CGPointZero;
214
215 if (alpha == 0.f) {
216 // More efficient since we don't always need to check intersection
217 // If the alpha is zero then the hit point is top mid and the index is 0.
218 hit = topMid;
219 index = 0;
220 } else if (alpha == 1.f) {
221 // More efficient since we don't always need to check intersection
222 // If the alpha is one then the hit point is top mid and the index is 4.
223 hit = topMid;
224 index = 4;
225 } else {
226 // We run a for loop checking the edges of the texture to find the
227 // intersection point
228 // We loop through five points since the top is split in half
229
230 float min_t = FLT_MAX;
231
232 for (int i = 0; i <= kProgressTextureCoordsCount; ++i) {
233 int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount;
234
235 CGPoint edgePtA = ccpCompMult([self boundaryTexCoord:i % kProgressTextureCoordsCount],tMax);
236 CGPoint edgePtB = ccpCompMult([self boundaryTexCoord:pIndex],tMax);
237
238 // Remember that the top edge is split in half for the 12 o'clock position
239 // Let's deal with that here by finding the correct endpoints
240 if(i == 0){
241 edgePtB = ccpLerp(edgePtA,edgePtB,.5f);
242 } else if(i == 4){
243 edgePtA = ccpLerp(edgePtA,edgePtB,.5f);
244 }
245
246 // s and t are returned by ccpLineIntersect
247 float s = 0, t = 0;
248 if(ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t))
249 {
250
251 // Since our hit test is on rays we have to deal with the top edge
252 // being in split in half so we have to test as a segment
253 if ((i == 0 || i == 4)) {
254 // s represents the point between edgePtA--edgePtB
255 if (!(0.f <= s && s <= 1.f)) {
256 continue;
257 }
258 }
259 // As long as our t isn't negative we are at least finding a
260 // correct hitpoint from midpoint to percentagePt.
261 if (t >= 0.f) {
262 // Because the percentage line and all the texture edges are
263 // rays we should only account for the shortest intersection
264 if (t < min_t) {
265 min_t = t;
266 index = i;
267 }
268 }
269 }
270 }
271
272 // Now that we have the minimum magnitude we can use that to find our intersection
273 hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t));
274
275 }
276
277
278 // The size of the vertex data is the index from the hitpoint
279 // the 3 is for the midpoint, 12 o'clock point and hitpoint position.
280
281 BOOL sameIndexCount = YES;
282 if(vertexDataCount_ != index + 3){
283 sameIndexCount = NO;
284 if(vertexData_){
285 free(vertexData_);
286 vertexData_ = NULL;
287 vertexDataCount_ = 0;
288 }
289 }
290
291
292 if(!vertexData_) {
293 vertexDataCount_ = index + 3;
294 vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F));
295 NSAssert( vertexData_, @"CCProgressTimer. Not enough memory");
296
297 [self updateColor];
298 }
299
300 if (!sameIndexCount) {
301
302 // First we populate the array with the midpoint, then all
303 // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
304 vertexData_[0].texCoords = (ccTex2F){midpoint.x, midpoint.y};
305 vertexData_[0].vertices = [self vertexFromTexCoord:midpoint];
306
307 vertexData_[1].texCoords = (ccTex2F){midpoint.x, 0.f};
308 vertexData_[1].vertices = [self vertexFromTexCoord:ccp(midpoint.x, 0.f)];
309
310 for(int i = 0; i < index; ++i){
311 CGPoint texCoords = ccpCompMult([self boundaryTexCoord:i], tMax);
312
313 vertexData_[i+2].texCoords = (ccTex2F){texCoords.x, texCoords.y};
314 vertexData_[i+2].vertices = [self vertexFromTexCoord:texCoords];
315 }
316
317 // Flip the texture coordinates if set
318 if (sprite_.flipY || sprite_.flipX) {
319 for(int i = 0; i < vertexDataCount_ - 1; ++i){
320 if (sprite_.flipX) {
321 vertexData_[i].texCoords.u = tMax.x - vertexData_[i].texCoords.u;
322 }
323 if(sprite_.flipY){
324 vertexData_[i].texCoords.v = tMax.y - vertexData_[i].texCoords.v;
325 }
326 }
327 }
328 }
329
330 // hitpoint will go last
331 vertexData_[vertexDataCount_ - 1].texCoords = (ccTex2F){hit.x, hit.y};
332 vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromTexCoord:hit];
333
334 if (sprite_.flipY || sprite_.flipX) {
335 if (sprite_.flipX) {
336 vertexData_[vertexDataCount_ - 1].texCoords.u = tMax.x - vertexData_[vertexDataCount_ - 1].texCoords.u;
337 }
338 if(sprite_.flipY){
339 vertexData_[vertexDataCount_ - 1].texCoords.v = tMax.y - vertexData_[vertexDataCount_ - 1].texCoords.v;
340 }
341 }
342}
343
344///
345// Update does the work of mapping the texture onto the triangles for the bar
346// It now doesn't occur the cost of free/alloc data every update cycle.
347// It also only changes the percentage point but no other points if they have not
348// been modified.
349//
350// It now deals with flipped texture. If you run into this problem, just use the
351// sprite property and enable the methods flipX, flipY.
352///
353-(void)updateBar
354{
355
356 float alpha = percentage_ / 100.f;
357
358 CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT);
359
360 unsigned char vIndexes[2] = {0,0};
361 unsigned char index = 0;
362
363 // We know vertex data is always equal to the 4 corners
364 // If we don't have vertex data then we create it here and populate
365 // the side of the bar vertices that won't ever change.
366 if (!vertexData_) {
367 vertexDataCount_ = kProgressTextureCoordsCount;
368 vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F));
369 NSAssert( vertexData_, @"CCProgressTimer. Not enough memory");
370
371 if(type_ == kCCProgressTimerTypeHorizontalBarLR){
372 vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0,0};
373 vertexData_[vIndexes[1] = 1].texCoords = (ccTex2F){0, tMax.y};
374 }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) {
375 vertexData_[vIndexes[0] = 2].texCoords = (ccTex2F){tMax.x, tMax.y};
376 vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, 0.f};
377 }else if (type_ == kCCProgressTimerTypeVerticalBarBT) {
378 vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y};
379 vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y};
380 }else if (type_ == kCCProgressTimerTypeVerticalBarTB) {
381 vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, 0};
382 vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, 0};
383 }
384
385 index = vIndexes[0];
386 vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
387
388 index = vIndexes[1];
389 vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
390
391 if (sprite_.flipY || sprite_.flipX) {
392 if (sprite_.flipX) {
393 index = vIndexes[0];
394 vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
395 index = vIndexes[1];
396 vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
397 }
398 if(sprite_.flipY){
399 index = vIndexes[0];
400 vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
401 index = vIndexes[1];
402 vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
403 }
404 }
405
406 [self updateColor];
407 }
408
409 if(type_ == kCCProgressTimerTypeHorizontalBarLR){
410 vertexData_[vIndexes[0] = 3].texCoords = (ccTex2F){tMax.x*alpha, tMax.y};
411 vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x*alpha, 0};
412 }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) {
413 vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){tMax.x*(1.f - alpha), 0};
414 vertexData_[vIndexes[1] = 0].texCoords = (ccTex2F){tMax.x*(1.f - alpha), tMax.y};
415 }else if (type_ == kCCProgressTimerTypeVerticalBarBT) {
416 vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, tMax.y*(1.f - alpha)};
417 vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, tMax.y*(1.f - alpha)};
418 }else if (type_ == kCCProgressTimerTypeVerticalBarTB) {
419 vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y*alpha};
420 vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y*alpha};
421 }
422
423 index = vIndexes[0];
424 vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
425 index = vIndexes[1];
426 vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)];
427
428 if (sprite_.flipY || sprite_.flipX) {
429 if (sprite_.flipX) {
430 index = vIndexes[0];
431 vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
432 index = vIndexes[1];
433 vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u;
434 }
435 if(sprite_.flipY){
436 index = vIndexes[0];
437 vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
438 index = vIndexes[1];
439 vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v;
440 }
441 }
442
443}
444
445-(CGPoint)boundaryTexCoord:(char)index
446{
447 if (index < kProgressTextureCoordsCount) {
448 switch (type_) {
449 case kCCProgressTimerTypeRadialCW:
450 return ccp((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1);
451 case kCCProgressTimerTypeRadialCCW:
452 return ccp((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1);
453 default:
454 break;
455 }
456 }
457 return CGPointZero;
458}
459
460-(void)draw
461{
462 [super draw];
463
464 if(!vertexData_)return;
465 if(!sprite_)return;
466 ccBlendFunc blendFunc = sprite_.blendFunc;
467 BOOL newBlend = blendFunc.src != CC_BLEND_SRC || blendFunc.dst != CC_BLEND_DST;
468 if( newBlend )
469 glBlendFunc( blendFunc.src, blendFunc.dst );
470
471 /// ========================================================================
472 // Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData
473 // Everything above me and below me is copied from CCTextureNode's draw
474 glBindTexture(GL_TEXTURE_2D, sprite_.texture.name);
475 glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].vertices);
476 glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].texCoords);
477 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &vertexData_[0].colors);
478 if(type_ == kCCProgressTimerTypeRadialCCW || type_ == kCCProgressTimerTypeRadialCW){
479 glDrawArrays(GL_TRIANGLE_FAN, 0, vertexDataCount_);
480 } else if (type_ == kCCProgressTimerTypeHorizontalBarLR ||
481 type_ == kCCProgressTimerTypeHorizontalBarRL ||
482 type_ == kCCProgressTimerTypeVerticalBarBT ||
483 type_ == kCCProgressTimerTypeVerticalBarTB) {
484 glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_);
485 }
486 //glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_);
487 /// ========================================================================
488
489 if( newBlend )
490 glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
491}
492
493@end