diff --git a/Extensions/CCEffectLine/CCEffectLine.frag b/Extensions/CCEffectLine/CCEffectLine.frag new file mode 100644 index 0000000..476c345 --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLine.frag @@ -0,0 +1,45 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2014-2015 Lars Birkemose + * + * 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. + */ + +// ----------------------------------------------------------------------- + +uniform float u_textureMixEnabled; +uniform highp float u_textureMix; + +void main() +{ + if (u_textureMixEnabled != 0.0) + { + vec4 tex1 = texture2D(cc_MainTexture, cc_FragTexCoord1); + vec4 tex2 = texture2D(cc_MainTexture, cc_FragTexCoord2); + gl_FragColor = cc_FragColor * mix(tex2, tex1, u_textureMix); + } + else + { + gl_FragColor = cc_FragColor * texture2D(cc_MainTexture, cc_FragTexCoord1); + } +} + +// ----------------------------------------------------------------------- + diff --git a/Extensions/CCEffectLine/CCEffectLine.h b/Extensions/CCEffectLine/CCEffectLine.h new file mode 100644 index 0000000..324a628 --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLine.h @@ -0,0 +1,126 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2014-2015 Lars Birkemose + * + * 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. + */ + +// TODO Add documentation + +#import +#import "cocos2d.h" + +// ----------------------------------------------------------------------- + +typedef NS_ENUM(NSInteger, CCEffectLineMode) +{ + CCEffectLineModePointToPoint, // line is drawn point to point as defined + CCEffectLineModeFreeHand, // free hand drawing + CCEffectLineModeStraight, // line is drawn as a straight line between start and end +}; + +typedef NS_ENUM(NSInteger, CCEffectLineWidth) +{ + CCEffectLineWidthSimple, // line width is constant + CCEffectLineWidthSpeed, // line width is dependent on drawing speed + CCEffectLineWidthBarrel, // line width is barrel shaped +}; + +typedef NS_ENUM(NSInteger, CCEffectLineTexture) +{ + CCEffectLineTextureSimple, // simple texturing + CCEffectLineTextureBlendSinus, // textures are blended according to sinus shape + CCEffectLineTextureBlendLinear, // textures are blended linear + CCEffectLineTextureRandom, // textures are shown random +}; + +typedef NS_ENUM(NSInteger, CCEffectLineAnimation) +{ + CCEffectLineAnimationNone, // no texture animation + CCEffectLineAnimationScroll, // texture scrolls + CCEffectLineAnimationRandom, // texture is animated randomly + CCEffectLineAnimationClamped, // texture will be shown only once +}; + +typedef NS_ENUM(NSInteger, CCEffectLineDebug) +{ + CCEffectLineDebugNone, // no debug drawing + CCEffectLineDebugSegments, // line is drawn as a series of segments + CCEffectLineDebugBoth, // both normal texture and segments are drawn +}; + +// ----------------------------------------------------------------------- + +@interface CCEffectLine : CCSprite + +// ----------------------------------------------------------------------- + +// control properties +@property (nonatomic, assign) CCEffectLineMode lineMode; // line type +@property (nonatomic, assign) float life; // life span of the line +@property (nonatomic, assign) BOOL autoRemove; // autoremove when expired +@property (nonatomic, assign) BOOL smooth; // a smooth line will be generated +@property (nonatomic, assign) float speedMultiplyer; // speed multiplyer +@property (nonatomic, assign) float granularity; // granularity of the line (0->1) +@property (nonatomic, assign) BOOL drawLineStart; // draw line start +@property (nonatomic, assign) BOOL drawLineEnd; // draw line end +@property (nonatomic, assign) CGPoint wind; // wind factor +@property (nonatomic, assign) CGPoint gravity; // gravity factor +@property (nonatomic, assign) GLKVector4 colorStart; // start color +@property (nonatomic, assign) GLKVector4 colorEnd; // end color +@property (nonatomic, assign) BOOL locked; // locked while adding +@property (nonatomic, assign) CCEffectLineWidth widthMode; // width type +@property (nonatomic, assign) float widthStart; +@property (nonatomic, assign) float widthEnd; +@property (nonatomic, assign) CCEffectLineTexture textureMix; // texture mixing +@property (nonatomic, assign) CCEffectLineAnimation textureAnimation; // texture animation +@property (nonatomic, assign) NSUInteger textureCount; // textures in texture (number of lines, default 8) +@property (nonatomic, assign) NSUInteger textureIndex; // current texture index +@property (nonatomic, assign) float textureScale; // scale the texture +@property (nonatomic, assign) float textureScroll; // scroll speed for texture animation +@property (nonatomic, assign) float textureMixTime; // time to mix to next texture +@property (nonatomic, assign) float textureGain; // defines gain of texture (normally just leave) +@property (nonatomic, assign) CCEffectLineDebug debugMode; // draw debug mode + +@property (nonatomic, readonly) float contentScale; +@property (nonatomic, readonly) BOOL expired; // all segments have been removed +@property (nonatomic, readonly) float length; // return length of line +@property (nonatomic, readonly) float drawSpeed; // drawing speed + + +// ----------------------------------------------------------------------- + ++ (instancetype)lineWithDictionary:(NSDictionary *)dict; +- (instancetype)initWithDictionary:(NSDictionary *)dict; + ++ (instancetype)lineWithImageNamed:(NSString *)imageName; +- (instancetype)initWithImageNamed:(NSString *)imageName; + +- (void)start:(CGPoint)pos timestamp:(NSTimeInterval)timestamp; +- (BOOL)add:(CGPoint)pos timestamp:(NSTimeInterval)timestamp; +- (void)end:(CGPoint)pos timestamp:(NSTimeInterval)timestamp; +- (void)clear; + +- (void)clearTextures; +- (void)addTextures:(NSArray *)list; + +// ----------------------------------------------------------------------- + +@end diff --git a/Extensions/CCEffectLine/CCEffectLine.m b/Extensions/CCEffectLine/CCEffectLine.m new file mode 100644 index 0000000..aa17407 --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLine.m @@ -0,0 +1,917 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2014-2015 Lars Birkemose + * + * 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 "CCEffectLine.h" +#import "CCEffectLineSegment.h" +#import "CCTexture_Private.h" + +// ----------------------------------------------------------------------- +// some defaults + +const float CCEffectLineWidthStart = 32.0f; +const float CCEffectLineWidthEnd = 32.0f; +const float CCEffectLineSpeedDivider = 1000.0f; +const float CCEffectLineSpeedInterpolation = 0.25f; +const float CCEffectLineGranularity = 1.0f; +const float CCEffectLineBezierSegments = 1.0f; +const float CCEffectLineLifeDefault = 1.0f; +const NSUInteger CCEffectLineTextureCount = 8; + +// ----------------------------------------------------------------------- + +GLKVector4 GLKVector4FromString(NSString *data) +{ + + NSArray* array; + float result[4] = {1.0f, 1.0f, 1.0, 1.0}; + + data = [data stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"{ }"]]; + array = [data componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]]; + + for (int index = 0; index < 4; index++) + { + if (array.count > index) + { + NSString *component = [NSString stringWithString:[array objectAtIndex:index]]; + if (![component isEqualToString:@""]) result[index] = [component floatValue]; + } + } + // done + return (GLKVector4){result[0], result[1], result[2], result[3]}; +} + +// ----------------------------------------------------------------------- + +@implementation CCEffectLine +{ + // list of line points + NSMutableArray *_pointList; // list of points created + BOOL _temporarySegment; // last segment is temporary + CGPoint _startPosition; // line startposition + CGPoint _lastPosition; // internally used last position + CGPoint _endPosition; // line endposition + CGPoint _lastDirection; + + NSTimeInterval _lastTime; + + NSMutableArray *_textureList; // textures to blend through + NSUInteger _textureIndex; // current texture index in list + NSUInteger _texture1; + NSUInteger _texture2; // two current textures + float _textureOffset; + float _textureTime; + BOOL _widthCheck; // line width should be checked + BOOL _lineLocked; // line can not age + float _speedWidth; + + // debug draw node + CCDrawNode *_debugDraw; +} + +// ----------------------------------------------------------------------- + ++ (instancetype)lineWithDictionary:(NSDictionary *)dict +{ + return [[self alloc] initWithDictionary:dict]; +} + +// ----------------------------------------------------------------------- + ++ (instancetype)lineWithImageNamed:(NSString *)imageName +{ + return [[self alloc] initWithImageNamed:imageName]; +} + +// ----------------------------------------------------------------------- + +- (instancetype)initWithDictionary:(NSDictionary *)dict +{ + self = [self initWithImageNamed:[dict objectForKey:@"image"]]; + + // load setup from dictionary + id data; + + data = [dict objectForKey:@"name"]; + if (data) self.name = data; + + data = [dict objectForKey:@"lineMode"]; + if (data) self.lineMode = [data integerValue]; + + data = [dict objectForKey:@"widthMode"]; + if (data) self.widthMode = [data integerValue]; + + data = [dict objectForKey:@"textureList"]; + if (data) [self addTextures:data]; + + data = [dict objectForKey:@"textureMix"]; + if (data) self.textureMix = [data integerValue]; + + data = [dict objectForKey:@"textureAnimation"]; + if (data) self.textureAnimation = [data integerValue]; + + data = [dict objectForKey:@"textureScroll"]; + if (data) self.textureScroll = [data floatValue]; + + data = [dict objectForKey:@"textureMixTime"]; + if (data) self.textureMixTime = [data floatValue]; + + data = [dict objectForKey:@"textureCount"]; + if (data) self.textureCount = [data integerValue]; + + data = [dict objectForKey:@"textureIndex"]; + if (data) self.textureIndex = [data integerValue]; + + data = [dict objectForKey:@"textureScale"]; + if (data) self.textureScale = [data floatValue]; + + data = [dict objectForKey:@"life"]; + if (data) self.life = [data floatValue]; + + data = [dict objectForKey:@"autoRemove"]; + if (data) self.autoRemove = [data integerValue]; + + data = [dict objectForKey:@"smooth"]; + if (data) self.smooth = [data integerValue]; + + data = [dict objectForKey:@"speedMultiplyer"]; + if (data) self.speedMultiplyer = [data floatValue]; + + data = [dict objectForKey:@"granularity"]; + if (data) self.granularity = [data floatValue]; + + data = [dict objectForKey:@"drawLineStart"]; + if (data) self.drawLineStart = [data integerValue]; + + data = [dict objectForKey:@"locked"]; + if (data) self.locked = [data integerValue]; + + data = [dict objectForKey:@"drawLineEnd"]; + if (data) self.drawLineEnd = [data integerValue]; + + data = [dict objectForKey:@"widthStart"]; + if (data) self.widthStart = [data floatValue]; + + data = [dict objectForKey:@"widthEnd"]; + if (data) self.widthEnd = [data floatValue]; + + data = [dict objectForKey:@"width"]; + if (data) + { + self.widthStart = [data floatValue]; + self.widthEnd = [data floatValue]; + } + + data = [dict objectForKey:@"wind"]; + if (data) _wind = CGPointFromString(data); + + data = [dict objectForKey:@"gravity"]; + if (data) _gravity = CGPointFromString(data); + + data = [dict objectForKey:@"colorStart"]; + if (data) _colorStart = GLKVector4FromString(data); + + data = [dict objectForKey:@"colorEnd"]; + if (data) _colorEnd = GLKVector4FromString(data); + + // done + return self; +} + +// ----------------------------------------------------------------------- + +- (instancetype)initWithImageNamed:(NSString *)imageName +{ + self = [super initWithImageNamed:imageName]; + self.anchorPoint = CGPointZero; + + // create point list + _pointList = [NSMutableArray array]; + + // reset properties to defaults + _life = CCEffectLineLifeDefault; + _autoRemove = NO; + _smooth = NO; + _speedMultiplyer = 1.0f; + _granularity = CCEffectLineGranularity; + _lineMode = CCEffectLineModePointToPoint; + _widthMode = CCEffectLineWidthSimple; + _drawLineStart = YES; + _drawLineEnd = YES; + _widthStart = CCEffectLineWidthStart; + _widthEnd = CCEffectLineWidthEnd; + _lineLocked = NO; + _debugMode = CCEffectLineDebugNone; + _colorStart = (GLKVector4){1.0, 1.0, 1.0, 1.0}; + _colorEnd = (GLKVector4){1.0, 1.0, 1.0, 0.0}; + _wind = CGPointZero; + _gravity = CGPointZero; + _locked = NO; + + // texture properties + _textureList = [NSMutableArray array]; + [self clearTextures]; + _textureIndex = 0; + _textureCount = CCEffectLineTextureCount; + _textureScale = 1.0f; + _textureScroll = 1.0f; + _textureOffset = 0.0f; + _textureMixTime = 1.0f; + + // reset helper properties + _lastTime = 0; + _lastPosition = CGPointZero; + _contentScale = [CCDirector sharedDirector].contentScaleFactor; + _textureGain = 1.0f / [CCDirector sharedDirector].viewSize.width; + _expired = NO; + _widthCheck = NO; + _drawSpeed = 0; + _speedWidth = CCEffectLineWidthStart; + + // Not currently added to Cocos2D + self.blendMode = [CCBlendMode blendModeWithOptions: + @{ + CCBlendFuncSrcColor: @(GL_SRC_ALPHA), + CCBlendFuncDstColor: @(GL_ONE), + }]; + + // used for debug draw + _debugDraw = [CCDrawNode node]; + [self addChild:_debugDraw]; + + // load custom shader + NSString *vertexPath = [[CCFileUtils sharedFileUtils] fullPathForFilename:@"CCEffectLine.vert"]; + NSString *vertexSource = [NSString stringWithContentsOfFile:vertexPath encoding:NSUTF8StringEncoding error:nil]; + + NSString *fragmentPath = [[CCFileUtils sharedFileUtils] fullPathForFilename:@"CCEffectLine.frag"]; + NSString *fragmentSource = [NSString stringWithContentsOfFile:fragmentPath encoding:NSUTF8StringEncoding error:nil]; + + self.shader = [[CCShader alloc] initWithVertexShaderSource:vertexSource fragmentShaderSource:fragmentSource]; + + // done + return self; +} + +// ----------------------------------------------------------------------- + +- (void)addEffectLineSegmentWithDictionary:(CGPoint)pos + direction:(CGPoint)direction + textureOffset:(float)offset + startAge:(float)startAge +{ + float widthStart; + float widthEnd; + + // adjust direction of last point, according to new point + if (_pointList.count > 1) + { + CCEffectLineSegment *p0 = [_pointList objectAtIndex:_pointList.count - 2]; + CCEffectLineSegment *p1 = [_pointList objectAtIndex:_pointList.count - 1]; + + CGPoint d0 = ccpNormalize(ccpSub(p1.position, p0.position)); + CGPoint d1 = ccpNormalize(ccpSub(pos, p1.position)); + + p1.direction = ccpAdd(d0, d1); + } + + // check for speed mode + if (_widthMode == CCEffectLineWidthSpeed) + { + // calculate new speed width + float newWidth = _widthStart + ((_widthEnd - _widthStart) * _drawSpeed / 10.0); + _speedWidth += ((newWidth - _speedWidth) * 0.01); + if (_widthEnd > _widthStart) + { + _speedWidth = clampf(_speedWidth, _widthStart, _widthEnd); + } + else + { + _speedWidth = clampf(_speedWidth, _widthEnd, _widthStart); + + } + + widthStart = _speedWidth; + widthEnd = widthStart; + } + else + { + widthStart = _widthStart; + widthEnd = _widthEnd; + } + + // if line is locked, no age information + // create the point + [_pointList addObject:[CCEffectLineSegment segmentWithDictionary: + @{ + // basic setups + @"position" : [NSValue valueWithCGPoint:pos], + @"direction" : [NSValue valueWithCGPoint:direction], + @"wind" : [NSValue valueWithCGPoint:_wind], + @"gravity" : [NSValue valueWithCGPoint:_gravity], + @"life" : @(_life), + @"startAge" : @(startAge), + @"textureOffset" : @(offset), + @"widthStart" : @(widthStart), + @"widthEnd" : @(widthEnd), + @"colorStart" : [NSValue valueWithGLKVector4:_colorStart], + @"colorEnd" : [NSValue valueWithGLKVector4:_colorEnd], + }]]; +} + +- (void)addEffectLineSegment:(CGPoint)pos + direction:(CGPoint)direction + textureOffset:(float)offset + startAge:(float)startAge +{ + + CCEffectLineSegment *lastPoint = [_pointList lastObject]; + if ((_textureAnimation == CCEffectLineAnimationClamped) && (lastPoint) && (lastPoint.textureOffset >= 1.0)) + return; + + // if only one segment added previously + if (_pointList.count == 1) + { + // make sure first point points towards second + lastPoint.direction = ccpNormalize(ccpSub(pos, lastPoint.position)); + + // if smooth line start, check if next point is too far away + if ((_drawLineStart) && (_lineMode != CCEffectLineModeFreeHand)) + { + float distance = ccpDistance(lastPoint.position, pos); + if (distance > _widthStart * 1.2) + { + // add new point + CGPoint pos = ccpAdd(lastPoint.position, ccpMult(lastPoint.direction, _widthStart)); + [self addEffectLineSegmentWithDictionary:pos direction:direction textureOffset:offset startAge:startAge]; + } + } + } + + // add new point + [self addEffectLineSegmentWithDictionary:pos direction:direction textureOffset:offset startAge:startAge]; + + _widthCheck = YES; +} + +// ----------------------------------------------------------------------- + +- (void)start:(CGPoint)pos timestamp:(NSTimeInterval)timestamp +{ + // check if line has already been started + NSAssert(_pointList.count == 0, @"Line already started"); + + // start line + _startPosition = pos; + _lastPosition = pos; + _lastTime = timestamp; + _temporarySegment = NO; + _textureTime = 0.0f; + _lineLocked = _locked; + _speedWidth = _widthStart; +} + +// ----------------------------------------------------------------------- + +- (BOOL)add:(CGPoint)pos timestamp:(NSTimeInterval)timestamp +{ + NSAssert(_lastTime != 0, @"Line has not been started"); + + // calculate speed + float newSpeed = ccpDistance(pos, _lastPosition) / (timestamp - _lastTime) / CCEffectLineSpeedDivider; + _drawSpeed += (newSpeed - _drawSpeed) * CCEffectLineSpeedInterpolation; + + if (_lineMode == CCEffectLineModeStraight) + { + // calculate a new line from start to end + [self calculateStraightLine:_startPosition end:pos]; + return YES; + } + + // line, following the points added + // some pre-calculations + CGPoint direction = ccpSub(pos, _lastPosition); + float distance = ccpDistance(_lastPosition, pos); + + // if no point has been previously added, create start point, and create a last direction identical to the current + if (_pointList.count == 0) + { + float textureStart = (_textureAnimation == CCEffectLineAnimationClamped) ? 0 : CCRANDOM_0_1(); + // create line + [self addEffectLineSegment:_lastPosition + direction:direction + textureOffset:textureStart + startAge:0]; + _lastDirection = direction; + } + + // test if temporary end segment, and remove it + if ((_lineMode == CCEffectLineModeFreeHand) && _temporarySegment) + { + // remove temporary segment + [_pointList removeLastObject]; + _temporarySegment = NO; + } + + // get start and end pos of smooth line + CCEffectLineSegment *lastPoint = [_pointList lastObject]; + CGPoint lastPos = lastPoint.position; + float textureOffset = lastPoint.textureOffset; + + // check if criterias for creating a line segment is met + if (distance <= (1.0f / _granularity * _widthStart / _contentScale)) + { + if (_lineMode == CCEffectLineModeFreeHand) + { + // create a temporary line segment + float distance = ccpDistance(lastPos, pos); + textureOffset += distance * _textureGain / _textureScale; + + // add a simple straight line segment + [self addEffectLineSegment:pos + direction:direction + textureOffset:textureOffset + startAge:0]; + _temporarySegment = YES; + } + return NO; + } + + // calculate number of segments + NSInteger numberOfSegments = 1 + (distance / ((1.0f / _granularity) * _widthStart / (float)CCEffectLineBezierSegments)); + + // check if smooth + if ((_smooth) && (numberOfSegments > 1) && (_pointList.count >= 2)) + { + // add a smooth piece of line segment + float step = 1.0 / (float)numberOfSegments; + float progress = step; + + CGPoint startPos = lastPos; + CGPoint endPos = ccpMidpoint(_lastPosition, pos); + + // current start end endpos + CGPoint currentStart = startPos; + CGPoint currentEnd; + + // time interval for each segment + // used to add a sleep timing, so that it appears like the segments are created liniear, and not in one chunk + float timeInterval = (timestamp - _lastTime) / numberOfSegments; + + for (NSInteger count = 0; count < numberOfSegments; count ++) + { + // inverse progress + float invProgress = 1.0 - progress; + + // calculate new end position + CGPoint p0 = ccpMult(startPos, invProgress * invProgress); + CGPoint p1 = ccpMult(_lastPosition, 2.0f * progress * invProgress); + CGPoint p2 = ccpMult(endPos, progress * progress); + currentEnd = ccpAdd(ccpAdd(p0, p1), p2); + + // calculate new texture offset + CGPoint newPos = ccpMidpoint(currentStart, currentEnd); + float distance = ccpDistance(lastPos, newPos); + textureOffset += distance * _textureGain / _textureScale; + + // create line + // NOTE + // the age of the last line is used as offset, because touch timing and line update might be out of sync. + // As the timings are very accurate, an extra call update before the touch is handled, will result in inaccurate decay of the line + [self addEffectLineSegment:newPos + direction:ccpSub(currentEnd, currentStart) + textureOffset:textureOffset + startAge:lastPoint.age - (timeInterval * count)]; + + // update + lastPos = newPos; + currentStart = currentEnd; + progress += step; + } + } + else + { + // TODO calculate new texture offset + float distance = ccpDistance(lastPoint.position, ccpMidpoint(_lastPosition, pos)); + textureOffset = lastPoint.textureOffset + (distance * _textureGain / _textureScale); + + // add a simple straight line segment + [self addEffectLineSegment:ccpMidpoint(_lastPosition, pos) + direction:direction + textureOffset:textureOffset + startAge:lastPoint.age]; + } + + // update last data + _lastPosition = pos; + _lastDirection = direction; + _lastTime = timestamp; + + // done + return YES; +} + +// ----------------------------------------------------------------------- + +- (void)end:(CGPoint)pos timestamp:(NSTimeInterval)timestamp +{ + NSAssert(_lastTime != 0, @"Line has not been started"); + + _lineLocked = NO; + + // straight lines just expire when ended + if (_lineMode == CCEffectLineModeStraight) return; + + if ((_lineMode == CCEffectLineModeFreeHand) && _temporarySegment) + { + // remove temporary segment + [_pointList removeLastObject]; + _temporarySegment = NO; + } + + // get start and end pos of smooth line + CCEffectLineSegment *lastPoint = [_pointList lastObject]; + + // TODO calculate new texture offset + float distance = ccpDistance(lastPoint.position, pos); + float textureOffset = lastPoint.textureOffset; + + // check line end + if ((_drawLineEnd) && (distance > lastPoint.width * 1.2)) + { + CGPoint direction = ccpNormalize(ccpSub(pos, lastPoint.position)); + float length = distance - lastPoint.width; + textureOffset += length * _textureGain / _textureScale; + // add new point + [self addEffectLineSegment:ccpAdd(lastPoint.position, ccpMult(direction, length)) + direction:direction + textureOffset:textureOffset + startAge:lastPoint.age]; + } + + // add final line segment + [self addEffectLineSegment:pos + direction:ccpSub(pos, lastPoint.position) + textureOffset:textureOffset + (ccpDistance(lastPoint.position, pos) * _textureGain / _textureScale) + startAge:lastPoint.age]; +} + +// ----------------------------------------------------------------------- + +- (void)clear +{ + [_pointList removeAllObjects]; + _expired = YES; +} + +// ----------------------------------------------------------------------- + +- (void)calculateStraightLine:(CGPoint)start end:(CGPoint)end +{ + // clear list + [_pointList removeAllObjects]; + + // preset some stuff + float distance = ccpDistance(start, end); + CGPoint direction = ccpSub(end, start); + NSInteger segments = 1 + (distance / _widthStart * _granularity); + direction = ccpMult(direction, 1.0f / (float)segments); + float textureOffset = 0; + distance = ccpLength(direction); + _widthCheck = YES; + + for (NSInteger count = 0; count <= segments; count ++) + { + // create the line + [ self addEffectLineSegmentWithDictionary:start direction:direction textureOffset:textureOffset startAge:0]; + // update data for next segment + textureOffset += (distance * _textureGain / _textureScale); + start = ccpAdd(start, direction); + + } +} + +// ----------------------------------------------------------------------- +// check various inconsistens states regarding points + +- (void)validatePoints:(CCTime)dt +{ + // only two or more points can be checked + if (_pointList.count <= 2) return; + + // check that no points are crossing other points + for (NSInteger index = 0; index < _pointList.count - 1; index ++) + { + float s, t; + CCEffectLineSegment *p0 = [_pointList objectAtIndex:index]; + CCEffectLineSegment *p1 = [_pointList objectAtIndex:index + 1]; + + CGPoint pos; + + if (ccpLineIntersect(p0.posA, p0.posB, p1.posA, p1.posB, &s, &t) && (s > 0) && (s < 1)) + { + if (s < 0.5) + { + pos = ccpMidpoint(p0.posA, p1.posA); + [p0 setPosA:pos]; + [p1 setPosA:pos]; + } + else + { + pos = ccpMidpoint(p0.posB, p1.posB); + [p0 setPosB:pos]; + [p1 setPosB:pos]; + } + } + } + + // check for barrel line + if ((_widthMode == CCEffectLineWidthBarrel) && (_widthCheck)) + { + float lineLength = self.length; + float currentLength = 0; + CCEffectLineSegment *last = [_pointList firstObject]; + for (CCEffectLineSegment *point in _pointList) + { + currentLength += ccpDistance(point.position, last.position); + last = point; + float progress = currentLength / lineLength; + point.width = _widthStart + (sinf(progress * M_PI) * (_widthEnd - _widthStart)); + } + _widthCheck = NO; + } + + // other checks ? + + + + + +} + +// ----------------------------------------------------------------------- + +- (void)clearTextures +{ + [_textureList removeAllObjects]; + _texture1 = 0; + _texture2 = 0; +} + +// ----------------------------------------------------------------------- + +- (void)addTextures:(NSArray *)list +{ + [_textureList addObjectsFromArray:list]; +} + +- (void)setTextureCount:(NSUInteger)textureCount +{ + NSAssert(textureCount > 0, @"Texture count must be greater than 0"); + _textureCount = textureCount; + _textureIndex = _textureIndex % _textureCount; + for (NSNumber *number in _textureList) + { + NSInteger texture = [number integerValue]; + NSAssert((texture >= 0) && (texture < _textureCount), @"Invalid texture"); + } +} + +- (void)setGranularity:(float)granularity +{ + _granularity = clampf(granularity, 0.1f, 2.0f); +} + +- (void)setTextureScale:(float)textureScale +{ + _textureScale = clampf(textureScale, 0.01f, 100.0f); +} + +- (void)setLineMode:(CCEffectLineMode)lineMode +{ + NSAssert((lineMode >= CCEffectLineModePointToPoint) && (lineMode <= CCEffectLineModeStraight), @"Invalid line type"); + _lineMode = lineMode; +} + +- (void)setWidthMode:(CCEffectLineWidth)widthMode +{ + NSAssert((widthMode >= CCEffectLineWidthSimple) && (widthMode <= CCEffectLineWidthBarrel), @"Invalid width type"); + _widthMode = widthMode; +} + +- (float)length +{ + float result = 0; + + CCEffectLineSegment *last = nil; + for (CCEffectLineSegment *next in _pointList) + { + if (last) + { + result += ccpDistance(last.position, next.position); + } + last = next; + } + return result; +} + +// ----------------------------------------------------------------------- + +- (void)update:(CCTime)dt +{ + // Update all line segments + // TODO: implement different lines modes ... + dt *= _speedMultiplyer; + + // set up texture mixing + // if not enough textures, force mix mode none (maintain mix mode setting) + CCEffectLineTexture mixMode = _textureMix; + if (_textureList.count < 2) mixMode = CCEffectLineTextureSimple; + // some default values and calculations + float mix = 1.0f; + _textureTime += dt; + // set mode + switch (mixMode) { + case CCEffectLineTextureSimple: + _texture1 = _textureIndex; + _texture2 = _textureIndex; + break; + + case CCEffectLineTextureBlendSinus: + case CCEffectLineTextureBlendLinear: + if (_textureTime >= _textureMixTime) + { + _textureIndex = (_textureIndex + 1) % _textureList.count; + _textureTime -= _textureMixTime; + } + _texture1 = [[_textureList objectAtIndex:_textureIndex] integerValue]; + _texture2 = [[_textureList objectAtIndex:(_textureIndex + 1) % _textureList.count] integerValue]; + float mixProgress = clampf(_textureTime / _textureMixTime, 0.0f, 1.0f); + if (_textureMix == CCEffectLineTextureBlendSinus) mix = (sinf(M_PI_2 + (M_PI * mixProgress)) + 1.0) * 0.5; + else if (_textureMix == CCEffectLineTextureBlendLinear) mix = 1.0 - mixProgress; + break; + + case CCEffectLineTextureRandom: + if (_textureTime >= _textureMixTime) + { + _textureTime -= _textureMixTime; + _texture1 = [[_textureList objectAtIndex:arc4random() % _textureList.count] integerValue]; + _texture2 = _texture1; + } + break; + } + // set the texture uniforms + self.shaderUniforms[@"u_textureMix"] = [NSNumber numberWithFloat:mix]; + self.shaderUniforms[@"u_textureMixEnabled"] = [NSNumber numberWithBool:_texture1 != _texture2]; + + // set texture scrolling + _textureOffset -= (_textureScroll * dt); + + // textures done + + // update point segment and remove when expires + for (NSInteger index = _pointList.count - 1; index >= 0; index --) + { + CCEffectLineSegment *point = [_pointList objectAtIndex:index]; + [point update:(_lineLocked) ? 0.0f : dt]; + + // if (point.hasChanged) changed = YES; + if (point.expired) + { + [_pointList removeObjectAtIndex:index]; + _expired = (_pointList.count == 0); + } + } + // check if any points has changed + /* if (changed) */ [self validatePoints:dt]; + // + if (_expired && _autoRemove) + { + [self removeFromParentAndCleanup:YES]; + } +} + +// ----------------------------------------------------------------------- + +- (void)draw:(CCRenderer *)renderer transform:(const GLKMatrix4 *)transform +{ + [_debugDraw clear]; + + if (_debugMode != CCEffectLineDebugNone) + { + // show line segments with lines representing width + + // draw line + for (CCEffectLineSegment *point in _pointList) + { + CGPoint p = ccpLerp(point.posA, point.posB, 0.25); + [_debugDraw drawSegmentFrom:point.posA to:p radius:1 color:[CCColor redColor]]; + [_debugDraw drawSegmentFrom:p to:point.posB radius:1 color:[CCColor whiteColor]]; + } + } + + if (_debugMode == CCEffectLineDebugSegments) return; + if (_pointList.count < 2) return; + + // set texture parameters to repeat texture + ccTexParams params = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT}; + if (_textureAnimation == CCEffectLineAnimationClamped) params = (ccTexParams){GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE}; + [self.texture setTexParameters:¶ms]; + + // calculate vertice- and triangle count + NSUInteger triangleCount = 2 * (_pointList.count - 1); + NSUInteger vertexCount = 2 * _pointList.count; + + CCRenderBuffer buffer = [renderer enqueueTriangles:triangleCount andVertexes:vertexCount withState:self.renderState globalSortOrder:0]; + CCVertex vertex; + + CCEffectLineSegment *lastSegment = nil; + CCEffectLineSegment *currentSegment = nil; + int vertexIndex = 0; + int triangleIndex = 0; + + float interval = 1.0f / (float)_textureCount; + CGPoint offset1 = ccp(0, interval * (float)_texture1); + CGPoint offset2 = ccp(0, interval * (float)_texture2); + + // set texture animation + switch (_textureAnimation) { + case CCEffectLineAnimationNone: + default: + break; + + case CCEffectLineAnimationRandom: + offset1.x = CCRANDOM_0_1(); + offset2.x = CCRANDOM_0_1(); + break; + + case CCEffectLineAnimationScroll: + offset1.x = _textureOffset; + offset2.x = _textureOffset; + break; + } + + // create line + // TODO remove empty triangles + for (NSInteger index = 0; index < _pointList.count; index ++) + { + currentSegment = [_pointList objectAtIndex:index]; + GLKVector4 color = currentSegment.color; + + if ((index == 0) && (_drawLineStart)) color.a = 0.0; + if ((index == _pointList.count - 1) && (_drawLineEnd)) color.a = 0.0; + + vertex.position = (GLKVector4){currentSegment.posA.x, currentSegment.posA.y, 0, 1}; + vertex.color = color; + + // + vertex.texCoord1 = (GLKVector2){currentSegment.textureOffset + offset1.x, offset1.y}; + vertex.texCoord2 = (GLKVector2){currentSegment.textureOffset + offset2.x, offset2.y}; + + CCRenderBufferSetVertex(buffer, vertexIndex, CCVertexApplyTransform(vertex, transform)); + vertexIndex ++; + + vertex.position = (GLKVector4){currentSegment.posB.x, currentSegment.posB.y, 0, 1}; + vertex.color = color; + // + vertex.texCoord1 = (GLKVector2){currentSegment.textureOffset + offset1.x, offset1.y + interval}; + vertex.texCoord2 = (GLKVector2){currentSegment.textureOffset + offset2.x, offset2.y + interval}; + + CCRenderBufferSetVertex(buffer, vertexIndex, CCVertexApplyTransform(vertex, transform)); + vertexIndex ++; + + if (lastSegment != nil) + { + CCRenderBufferSetTriangle(buffer, triangleIndex, vertexIndex - 3, vertexIndex - 4, vertexIndex - 2); + triangleIndex ++; + CCRenderBufferSetTriangle(buffer, triangleIndex, vertexIndex - 3, vertexIndex - 2, vertexIndex - 1); + triangleIndex ++; + } + + lastSegment = currentSegment; + } + +} + +// ----------------------------------------------------------------------- + +@end diff --git a/Extensions/CCEffectLine/CCEffectLine.vert b/Extensions/CCEffectLine/CCEffectLine.vert new file mode 100644 index 0000000..284239d --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLine.vert @@ -0,0 +1,37 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2014-2015 Lars Birkemose + * + * 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. + */ + +// ----------------------------------------------------------------------- + +void main() +{ + cc_FragTexCoord1 = cc_TexCoord1; + cc_FragTexCoord2 = cc_TexCoord2; + + cc_FragColor = clamp(cc_Color, 0.0, 1.0); + + gl_Position = cc_Position; +} + +// ----------------------------------------------------------------------- diff --git a/Extensions/CCEffectLine/CCEffectLineFactory.h b/Extensions/CCEffectLine/CCEffectLineFactory.h new file mode 100644 index 0000000..39cc55f --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLineFactory.h @@ -0,0 +1,56 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012-2014 Lars Birkemose + * + * 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. + */ + +// Note! +// The CCEffectLineFactory is a helper class, mainly intended for template usage + +// Default "effects.png" textures are +// 0 : Dark trail 0 +// 1 : Dark smoke trail 1 +// 2 : Simple (slightly blurred) line +// 3 : Smoke dragon +// 4 : Electricity +// 5 : White smoke trail 0 +// 6 : White smoke trail 1 +// 7 : Thin smoke + +#import +#import "cocos2d.h" + +// ----------------------------------------------------------------------- + +@interface CCEffectLineFactory : NSObject + +// ----------------------------------------------------------------------- + +@property (nonatomic, readonly) NSUInteger count; + +- (NSDictionary *)lineFromIndex:(NSUInteger)index; +- (NSDictionary *)lineFromName:(NSString *)name; + +- (NSString *)nameFromIndex:(NSUInteger)index; + +// ----------------------------------------------------------------------- + +@end diff --git a/Extensions/CCEffectLine/CCEffectLineFactory.m b/Extensions/CCEffectLine/CCEffectLineFactory.m new file mode 100644 index 0000000..ea1d6be --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLineFactory.m @@ -0,0 +1,363 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012-2014 Lars Birkemose + * + * 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 "CCEffectLineFactory.h" +#import "CCEffectLine.h" + +// ----------------------------------------------------------------------- + +@implementation CCEffectLineFactory +{ + NSMutableArray *_lines; +} + +// ----------------------------------------------------------------------- + +- (instancetype)init +{ + self = [super init]; + + _lines = [NSMutableArray array]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Simple Line", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModePointToPoint), + @"widthMode" : @(CCEffectLineWidthSimple), + @"width" : @(10.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(2), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationNone), + @"textureScroll" : @(0.00f), + @"textureMixTime" : @(1.00f), + @"textureScale" : @(1.0), + // texture mixing + @"life" : @(1.0f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(0.40f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{0.3, 1.0, 0.3, 1.0}", + @"colorEnd" : @"{0.0, 0.5, 0.0, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Free Hand Drawing", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModePointToPoint), + @"widthMode" : @(CCEffectLineWidthSpeed), + @"widthStart" : @(2.0), + @"widthEnd" : @(50.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(2), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationNone), + @"textureScroll" : @(0.00f), + @"textureMixTime" : @(1.00f), + @"textureScale" : @(1.0), + // texture mixing + @"life" : @(1.0f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(0.40f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 0.3, 1.0}", + @"colorEnd" : @"{0.5, 0.5, 0.0, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Vertical Smoke", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModePointToPoint), + @"widthMode" : @(CCEffectLineWidthSimple), + @"widthStart" : @(50.0), + @"widthEnd" : @(200.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(7), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationScroll), + @"textureScroll" : @(0.030f), + @"textureMixTime" : @(0.50f), + @"textureScale" : @(1.25), + // texture mixing + @"life" : @(3.00f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(1.00f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 1.0, 1.0}", + @"colorEnd" : @"{0.5, 0.5, 0.5, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Horizontal Fog", + @"extra" : @"Vertical Smoke", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModePointToPoint), + @"widthMode" : @(CCEffectLineWidthSimple), + @"widthStart" : @(50.0), + @"widthEnd" : @(200.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(7), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationScroll), + @"textureScroll" : @(-0.015f), + @"textureMixTime" : @(0.50f), + @"textureScale" : @(1.25), + // texture mixing + @"life" : @(3.00f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(1.00f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 1.0, 1.0}", + @"colorEnd" : @"{0.5, 0.5, 0.5, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Sword Effect", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModeFreeHand), + @"widthMode" : @(CCEffectLineWidthSimple), + @"width" : @(200.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(2), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationNone), + @"textureScroll" : @(0.00f), + @"textureMixTime" : @(3.00f), + @"textureScale" : @(1.00), + // texture mixing + @"locked" : @(YES), + @"life" : @(0.15f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(1.00f), + @"granularity" : @(1.50f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(NO), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 1.0, 0.8}", + @"colorEnd" : @"{1.0, 1.0, 1.0, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Bullet Tracer", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModePointToPoint), + @"widthMode" : @(CCEffectLineWidthSimple), + @"widthStart" : @(30.0), + @"widthEnd" : @(50.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(0), + @"textureList" : @[@(0), @(1)], + @"textureMix" : @(CCEffectLineTextureBlendLinear), + @"textureAnimation" : @(CCEffectLineAnimationScroll), + @"textureScroll" : @(0.00f), + @"textureMixTime" : @(0.50f), + @"textureScale" : @(0.50), + // texture mixing + @"life" : @(1.00f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(1.00f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 1.0, 1.0}", + @"colorEnd" : @"{0.5, 0.5, 0.5, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Rocket Smoke Trail", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModeFreeHand), + @"widthMode" : @(CCEffectLineWidthBarrel), + @"widthStart" : @(20.0), + @"widthEnd" : @(100.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(0), + @"textureList" : @[@(5), @(6)], + @"textureMix" : @(CCEffectLineTextureBlendLinear), + @"textureAnimation" : @(CCEffectLineAnimationScroll), + @"textureScroll" : @(-0.05f), + @"textureMixTime" : @(3.00f), + @"textureScale" : @(0.50), + // texture mixing + @"life" : @(3.00f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(1.00f), + @"granularity" : @(0.25f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 1.0, 1.0}", + @"colorEnd" : @"{1.0, 1.0, 1.0, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Electricity", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModeStraight), + @"widthMode" : @(CCEffectLineWidthBarrel), + @"widthStart" : @(10.0), + @"widthEnd" : @(150), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(4), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationRandom), + @"textureScroll" : @(0.00f), + @"textureMixTime" : @(1.00f), + @"textureScale" : @(0.75), + // texture mixing + @"locked" : @(YES), + @"life" : @(0.05f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(0.40f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{0, 0}", + @"gravity" : @"{0, 0}", + @"colorStart" : @"{1.0, 1.0, 1.0, 1.0}", + @"colorEnd" : @"{1.0, 0.0, 0.0, 0.0}", + }]; + + [_lines addObject: + @{ + // basic setups + @"name" : @"Smoke Dragon", + @"image" : @"effects.png", + @"lineMode" : @(CCEffectLineModePointToPoint), + @"widthMode" : @(CCEffectLineWidthSimple), + @"widthStart" : @(100.0), + @"widthEnd" : @(200.0), + // textures used + @"textureCount" : @(8), + @"textureIndex" : @(3), + @"textureList" : @[], + @"textureMix" : @(CCEffectLineTextureSimple), + @"textureAnimation" : @(CCEffectLineAnimationClamped), + @"textureScroll" : @(0.00f), + @"textureMixTime" : @(1.00f), + @"textureScale" : @(1.0), + // texture mixing + @"life" : @(2.0f), + @"autoRemove" : @(YES), + @"smooth" : @(YES), + @"speedMultiplyer" : @(1.0f), + @"granularity" : @(1.0f), + @"drawLineStart" : @(YES), + @"drawLineEnd" : @(YES), + @"wind" : @"{50, 0}", + @"gravity" : @"{0, 50}", + @"colorStart" : @"{1.0, 1.0, 1.0, 1.0}", + @"colorEnd" : @"{0.5, 0.5, 0.0, 0.0}", + }]; + + return self; +} + +- (NSUInteger)count +{ + return _lines.count; +} + +- (NSDictionary *)lineFromIndex:(NSUInteger)index +{ + NSAssert(index < self.count, @"Invalid index"); + return [_lines objectAtIndex:index]; +} + +- (NSDictionary *)lineFromName:(NSString *)name +{ + for (NSDictionary *dict in _lines) + { + if ([name isEqualToString:[dict objectForKey:@"name"]]) return dict; + } + return nil; +} + +- (NSString *)nameFromIndex:(NSUInteger)index +{ + NSAssert(index < self.count, @"Invalid index"); + return [[_lines objectAtIndex:index] objectForKey:@"name"]; +} + +// ----------------------------------------------------------------------- + +@end diff --git a/Extensions/CCEffectLine/CCEffectLineSegment.h b/Extensions/CCEffectLine/CCEffectLineSegment.h new file mode 100644 index 0000000..675111a --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLineSegment.h @@ -0,0 +1,112 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2014-2015 Lars Birkemose + * + * 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 +#import "cocos2d.h" + +// ----------------------------------------------------------------------- + +@interface NSValue(CCEffectLineSegment) + +- (GLKVector4)GLKVector4Value; + +@end + +// ----------------------------------------------------------------------- +// Holding a single line point +// - 2 points created from a point, a direction and a width + +@interface CCEffectLineSegment : NSObject + +// ----------------------------------------------------------------------- + +@property (nonatomic, readonly) float life; +@property (nonatomic, readonly) float age; +@property (nonatomic, readonly) float progress; +@property (nonatomic, readonly) BOOL expired; +@property (nonatomic, readonly) CGPoint position; +@property (nonatomic, readonly) CGPoint posA; +@property (nonatomic, readonly) CGPoint posB; +@property (nonatomic, readonly) float widthStart; +@property (nonatomic, readonly) float widthEnd; +@property (nonatomic, readonly) GLKVector4 color; +@property (nonatomic, readonly) GLKVector4 colorStart; +@property (nonatomic, readonly) GLKVector4 colorEnd; + +@property (nonatomic, assign) float textureOffset; +@property (nonatomic, assign) CGPoint direction; +@property (nonatomic, assign) float width; +@property (nonatomic, assign) BOOL hasChanged; +@property (nonatomic, assign) CGPoint wind; +@property (nonatomic, assign) CGPoint gravity; + +// ----------------------------------------------------------------------- + ++ (instancetype)segmentWithDictionary:(NSDictionary *)settings; +- (instancetype)initWithDictionary:(NSDictionary *)settings; + +- (void)setPosA:(CGPoint)pos; +- (void)setPosB:(CGPoint)pos; + +- (void)update:(CCTime)delta; + +// ----------------------------------------------------------------------- + +@end + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Extensions/CCEffectLine/CCEffectLineSegment.m b/Extensions/CCEffectLine/CCEffectLineSegment.m new file mode 100644 index 0000000..12b60bc --- /dev/null +++ b/Extensions/CCEffectLine/CCEffectLineSegment.m @@ -0,0 +1,195 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2014-2015 Lars Birkemose + * + * 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 "CCEffectLineSegment.h" + +@implementation NSValue(CCEffectLineSegment) + +- (GLKVector4)GLKVector4Value +{ + GLKVector4 result; + [self getValue:&result]; + return result; +} + +@end + +// ----------------------------------------------------------------------- + +@implementation CCEffectLineSegment +{ + CGPoint _sumGravity; +} + +// ----------------------------------------------------------------------- + ++ (instancetype)segmentWithDictionary:(NSDictionary *)settings +{ + return [[self alloc] initWithDictionary:settings]; +} + +- (instancetype)initWithDictionary:(NSDictionary *)settings +{ + self = [super init]; + + // load from dictionary + _position = [[settings objectForKey:@"position"] CGPointValue]; + _direction = [[settings objectForKey:@"direction"] CGPointValue]; + _wind = [[settings objectForKey:@"wind"] CGPointValue]; + _gravity = [[settings objectForKey:@"gravity"] CGPointValue]; + _life = [[settings objectForKey:@"life"] floatValue]; + _age = [[settings objectForKey:@"startAge"] floatValue]; + _textureOffset = [[settings objectForKey:@"textureOffset"] floatValue]; + _widthStart = [[settings objectForKey:@"widthStart"] floatValue]; + _widthEnd = [[settings objectForKey:@"widthEnd"] floatValue]; + _colorStart = [[settings objectForKey:@"colorStart"] GLKVector4Value]; + _colorEnd = [[settings objectForKey:@"colorEnd"] GLKVector4Value]; + _color = _colorStart; + _sumGravity = CGPointZero; + + // reset internal stuff + _progress = 0; + _hasChanged = YES; + _width = _widthStart; + + // pre-calculate internal stuff + _direction = ccpNormalize(_direction); + CGPoint perp = ccpMult(ccpPerp(_direction), _width * 0.5); + _posA = ccpSub(_position, perp); + _posB = ccpAdd(_position, perp); + + return self; +} + +// ----------------------------------------------------------------------- + +- (void)update:(CCTime)delta +{ + _age += delta; + _progress = (_life > 0) ? clampf(_age / _life, 0.0, 1.0) : 0.0f; + + _color.r = _colorStart.r + (_colorEnd.r - _colorStart.r ) * _progress; + _color.g = _colorStart.g + (_colorEnd.g - _colorStart.g ) * _progress; + _color.b = _colorStart.b + (_colorEnd.b - _colorStart.b ) * _progress; + _color.a = _colorStart.a + (_colorEnd.a - _colorStart.a ) * _progress; + + float lastWidth = _width; + float newWidth = _widthStart + ((_widthEnd - _widthStart) * _progress); + if (fabs(lastWidth - newWidth) > 1.0) + { + _width = newWidth; + CGPoint perp = ccpMult(ccpPerp(_direction), _width * 0.5); + _posA = ccpSub(_position, perp); + _posB = ccpAdd(_position, perp); + } + CGPoint change; + // add gravity + _sumGravity = ccpAdd(_sumGravity, ccpMult(_gravity, delta)); + change = ccpMult(_sumGravity, delta); + // add wind + change = ccpAdd(change, ccpMult(_wind, delta)); + _position = ccpAdd(_position, change); + _posA = ccpAdd(_posA, change); + _posB = ccpAdd(_posB, change); +} + +// ----------------------------------------------------------------------- + +- (void)setPosA:(CGPoint)pos +{ + _posA = pos; + _position = ccpMidpoint(_posA, _posB); + _direction = ccpNormalize(ccpPerp(ccpSub(_posA, _posB))); +} + +- (void)setPosB:(CGPoint)pos +{ + _posB = pos; + _position = ccpMidpoint(_posA, _posB); + _direction = ccpNormalize(ccpPerp(ccpSub(_posA, _posB))); +} + +// ----------------------------------------------------------------------- + +- (BOOL)expired +{ + return (_age > _life); +} + +- (void)setDirection:(CGPoint)direction +{ + _direction = ccpNormalize(direction); + CGPoint perp = ccpMult(ccpPerp(ccpNormalize(_direction)), _width * 0.5); + _posA = ccpSub(_position, perp); + _posB = ccpAdd(_position, perp); +} + +- (void)setWidth:(float)width +{ + NSAssert(width > 0, @"Width must be greater than 0"); + _width = width; + _widthStart = _width; + _widthEnd = _width; + CGPoint perp = ccpMult(ccpPerp(_direction), _width * 0.5); + _posA = ccpSub(_position, perp); + _posB = ccpAdd(_position, perp); +} + +// ----------------------------------------------------------------------- + +@end + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Extensions/CCEffectLine/README.md b/Extensions/CCEffectLine/README.md new file mode 100644 index 0000000..19d87fc --- /dev/null +++ b/Extensions/CCEffectLine/README.md @@ -0,0 +1,6 @@ +CCEffectLine +============ + +Type of class : Descendant of CCSprite +Uses extension : [NONE] + diff --git a/Extensions/CCEffectLine/effects.png b/Extensions/CCEffectLine/effects.png new file mode 100644 index 0000000..198e733 Binary files /dev/null and b/Extensions/CCEffectLine/effects.png differ diff --git a/cocos2d-iphone-ext.xcodeproj/project.pbxproj b/cocos2d-iphone-ext.xcodeproj/project.pbxproj index f10f334..dc696a7 100644 --- a/cocos2d-iphone-ext.xcodeproj/project.pbxproj +++ b/cocos2d-iphone-ext.xcodeproj/project.pbxproj @@ -15,6 +15,14 @@ 0E57EA4119C6D5D4008CBC9E /* pebbles.png in Resources */ = {isa = PBXBuildFile; fileRef = 0E57EA4019C6D5D4008CBC9E /* pebbles.png */; }; 0E99598B19C73B0500A02D2B /* rippleNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E99598A19C73B0500A02D2B /* rippleNode.m */; }; 0E9A092A199CDDD10049E409 /* CCLayerSmoothLineTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E9A0929199CDDD10049E409 /* CCLayerSmoothLineTest.m */; }; + 0EB2EE2B1A6E992B00700971 /* CCEffectLine.frag in Resources */ = {isa = PBXBuildFile; fileRef = 0EB2EE211A6E992A00700971 /* CCEffectLine.frag */; }; + 0EB2EE2C1A6E992B00700971 /* CCEffectLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EB2EE231A6E992B00700971 /* CCEffectLine.m */; }; + 0EB2EE2D1A6E992B00700971 /* CCEffectLine.vert in Resources */ = {isa = PBXBuildFile; fileRef = 0EB2EE241A6E992B00700971 /* CCEffectLine.vert */; }; + 0EB2EE2E1A6E992B00700971 /* CCEffectLineFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EB2EE261A6E992B00700971 /* CCEffectLineFactory.m */; }; + 0EB2EE2F1A6E992B00700971 /* CCEffectLineSegment.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EB2EE281A6E992B00700971 /* CCEffectLineSegment.m */; }; + 0EB2EE301A6E992B00700971 /* effects.png in Resources */ = {isa = PBXBuildFile; fileRef = 0EB2EE291A6E992B00700971 /* effects.png */; }; + 0EB2EE391A6E997400700971 /* CCEffectLineTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EB2EE381A6E997400700971 /* CCEffectLineTest.m */; }; + 0EB2EE3C1A6E9A2600700971 /* effectLineNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0EB2EE3B1A6E9A2600700971 /* effectLineNode.m */; }; 0EBBCFB21A2E1FF600493FA4 /* libcocos2d.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EBBCFAB1A2E1F6A00493FA4 /* libcocos2d.a */; }; 0ECEB48819D29EB100BDBB9E /* clothNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ECEB48719D29EB100BDBB9E /* clothNode.m */; }; 0ED73BB51997951A00C83507 /* CCSpineCurve.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ED73BB41997951A00C83507 /* CCSpineCurve.m */; }; @@ -156,6 +164,19 @@ 0E99598919C73B0500A02D2B /* rippleNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rippleNode.h; path = "Classes/Tests/Test helper classes/rippleNode.h"; sourceTree = ""; }; 0E99598A19C73B0500A02D2B /* rippleNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = rippleNode.m; path = "Classes/Tests/Test helper classes/rippleNode.m"; sourceTree = ""; }; 0E9A0929199CDDD10049E409 /* CCLayerSmoothLineTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCLayerSmoothLineTest.m; path = Classes/Tests/CCLayerSmoothLineTest.m; sourceTree = ""; }; + 0EB2EE211A6E992A00700971 /* CCEffectLine.frag */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CCEffectLine.frag; path = Extensions/CCEffectLine/CCEffectLine.frag; sourceTree = SOURCE_ROOT; }; + 0EB2EE221A6E992B00700971 /* CCEffectLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCEffectLine.h; path = Extensions/CCEffectLine/CCEffectLine.h; sourceTree = SOURCE_ROOT; }; + 0EB2EE231A6E992B00700971 /* CCEffectLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCEffectLine.m; path = Extensions/CCEffectLine/CCEffectLine.m; sourceTree = SOURCE_ROOT; }; + 0EB2EE241A6E992B00700971 /* CCEffectLine.vert */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CCEffectLine.vert; path = Extensions/CCEffectLine/CCEffectLine.vert; sourceTree = SOURCE_ROOT; }; + 0EB2EE251A6E992B00700971 /* CCEffectLineFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCEffectLineFactory.h; path = Extensions/CCEffectLine/CCEffectLineFactory.h; sourceTree = SOURCE_ROOT; }; + 0EB2EE261A6E992B00700971 /* CCEffectLineFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCEffectLineFactory.m; path = Extensions/CCEffectLine/CCEffectLineFactory.m; sourceTree = SOURCE_ROOT; }; + 0EB2EE271A6E992B00700971 /* CCEffectLineSegment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CCEffectLineSegment.h; path = Extensions/CCEffectLine/CCEffectLineSegment.h; sourceTree = SOURCE_ROOT; }; + 0EB2EE281A6E992B00700971 /* CCEffectLineSegment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCEffectLineSegment.m; path = Extensions/CCEffectLine/CCEffectLineSegment.m; sourceTree = SOURCE_ROOT; }; + 0EB2EE291A6E992B00700971 /* effects.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = effects.png; path = Extensions/CCEffectLine/effects.png; sourceTree = SOURCE_ROOT; }; + 0EB2EE2A1A6E992B00700971 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = Extensions/CCEffectLine/README.md; sourceTree = SOURCE_ROOT; }; + 0EB2EE381A6E997400700971 /* CCEffectLineTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CCEffectLineTest.m; path = Classes/Tests/CCEffectLineTest.m; sourceTree = ""; }; + 0EB2EE3A1A6E9A2600700971 /* effectLineNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = effectLineNode.h; path = "Classes/Tests/Test helper classes/effectLineNode.h"; sourceTree = ""; }; + 0EB2EE3B1A6E9A2600700971 /* effectLineNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = effectLineNode.m; path = "Classes/Tests/Test helper classes/effectLineNode.m"; sourceTree = ""; }; 0EBBCF9D1A2E1F6900493FA4 /* cocos2d.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = cocos2d.xcodeproj; path = Externals/cocos2d.xcodeproj; sourceTree = ""; }; 0ECEB48619D29EB100BDBB9E /* clothNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = clothNode.h; path = "Classes/Tests/Test helper classes/clothNode.h"; sourceTree = ""; }; 0ECEB48719D29EB100BDBB9E /* clothNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = clothNode.m; path = "Classes/Tests/Test helper classes/clothNode.m"; sourceTree = ""; }; @@ -334,6 +355,23 @@ path = Resources/Tests; sourceTree = ""; }; + 0EB2EE151A6E8DFC00700971 /* CCEffectLine */ = { + isa = PBXGroup; + children = ( + 0EB2EE2A1A6E992B00700971 /* README.md */, + 0EB2EE271A6E992B00700971 /* CCEffectLineSegment.h */, + 0EB2EE281A6E992B00700971 /* CCEffectLineSegment.m */, + 0EB2EE221A6E992B00700971 /* CCEffectLine.h */, + 0EB2EE231A6E992B00700971 /* CCEffectLine.m */, + 0EB2EE241A6E992B00700971 /* CCEffectLine.vert */, + 0EB2EE211A6E992A00700971 /* CCEffectLine.frag */, + 0EB2EE251A6E992B00700971 /* CCEffectLineFactory.h */, + 0EB2EE261A6E992B00700971 /* CCEffectLineFactory.m */, + 0EB2EE291A6E992B00700971 /* effects.png */, + ); + name = CCEffectLine; + sourceTree = ""; + }; 0EBBCF9E1A2E1F6900493FA4 /* Products */ = { isa = PBXGroup; children = ( @@ -518,6 +556,7 @@ A66C5801193A0BF0009D2BD4 /* Tests */ = { isa = PBXGroup; children = ( + 0EB2EE381A6E997400700971 /* CCEffectLineTest.m */, A6994F82194A2FAC00B97481 /* CCSpineTest.m */, A6994F83194A2FAC00B97481 /* CCTransformationNodeTest.m */, A6994F80194A2F3D00B97481 /* CCCropNodeTest.m */, @@ -532,6 +571,7 @@ A66C5808193A0C24009D2BD4 /* Extensions */ = { isa = PBXGroup; children = ( + 0EB2EE151A6E8DFC00700971 /* CCEffectLine */, 0E57EA3619C6CF83008CBC9E /* CCSpriteGrid */, 0EF602F9199CDC4A007AEBD9 /* CCLayerSmoothLine */, 0E21D303198A45F800E84AB1 /* CCSpriteMultiTouch */, @@ -627,6 +667,8 @@ A6994F7E194A2E2800B97481 /* Test helper classes */ = { isa = PBXGroup; children = ( + 0EB2EE3A1A6E9A2600700971 /* effectLineNode.h */, + 0EB2EE3B1A6E9A2600700971 /* effectLineNode.m */, A6994F86194A2FBB00B97481 /* cardNode.h */, A6994F87194A2FBB00B97481 /* cardNode.m */, A6994F88194A2FBB00B97481 /* spineNode.h */, @@ -748,6 +790,7 @@ buildActionMask = 2147483647; files = ( A6270E7E193757E400196BD3 /* Default@2x.png in Resources */, + 0EB2EE301A6E992B00700971 /* effects.png in Resources */, A6270E91193757E400196BD3 /* Icon-120.png in Resources */, A62712A3193769A900196BD3 /* sound.caf in Resources */, A627129C193769A900196BD3 /* Interface-hd.png in Resources */, @@ -776,6 +819,7 @@ A62712AA193769A900196BD3 /* Sprites.plist in Resources */, A62712AB193769A900196BD3 /* Sprites.png in Resources */, A62712AF193769A900196BD3 /* TilesAtlassed-ipad.png in Resources */, + 0EB2EE2B1A6E992B00700971 /* CCEffectLine.frag in Resources */, A62712A6193769A900196BD3 /* Sprites-ipad.plist in Resources */, A627129A193769A900196BD3 /* din.png in Resources */, A62712A4193769A900196BD3 /* Sprites-hd.plist in Resources */, @@ -793,6 +837,7 @@ A6270EA0193757E400196BD3 /* iTunesArtwork in Resources */, A6270E9B193757E400196BD3 /* Icon-Spotlight-iOS7.png in Resources */, A6270E8F193757E400196BD3 /* Icon-76@2x.png in Resources */, + 0EB2EE2D1A6E992B00700971 /* CCEffectLine.vert in Resources */, A62712A8193769A900196BD3 /* Sprites-ipadhd.plist in Resources */, A627129D193769A900196BD3 /* Interface-ipad.plist in Resources */, A6270E9D193757E400196BD3 /* Icon-Spotlight-iOS7@2x.png in Resources */, @@ -815,6 +860,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0EB2EE2C1A6E992B00700971 /* CCEffectLine.m in Sources */, + 0EB2EE2E1A6E992B00700971 /* CCEffectLineFactory.m in Sources */, 0E21D30F198A637900E84AB1 /* CCSpriteMultiTouchTest.m in Sources */, 0ED73BB51997951A00C83507 /* CCSpineCurve.m in Sources */, A6994F85194A2FAC00B97481 /* CCTransformationNodeTest.m in Sources */, @@ -841,9 +888,12 @@ A66C5836193A0C87009D2BD4 /* CCSpineSkin.m in Sources */, 0E9A092A199CDDD10049E409 /* CCLayerSmoothLineTest.m in Sources */, 0ECEB48819D29EB100BDBB9E /* clothNode.m in Sources */, + 0EB2EE391A6E997400700971 /* CCEffectLineTest.m in Sources */, A6271107193757E600196BD3 /* main.m in Sources */, 0E21D30C198A484600E84AB1 /* CCSpriteMultiTouch.m in Sources */, + 0EB2EE3C1A6E9A2600700971 /* effectLineNode.m in Sources */, A66C5837193A0C87009D2BD4 /* CCSpineSprite.m in Sources */, + 0EB2EE2F1A6E992B00700971 /* CCEffectLineSegment.m in Sources */, A66C580E193A0C69009D2BD4 /* CCNodeTag.m in Sources */, A627110B193757E600196BD3 /* AppDelegate.m in Sources */, A66C5834193A0C87009D2BD4 /* CCSpineSample.m in Sources */, diff --git a/cocos2d-iphone-ext.xcodeproj/project.xcworkspace/xcuserdata/Birkemose.xcuserdatad/UserInterfaceState.xcuserstate b/cocos2d-iphone-ext.xcodeproj/project.xcworkspace/xcuserdata/Birkemose.xcuserdatad/UserInterfaceState.xcuserstate index c14f97b..467e329 100644 Binary files a/cocos2d-iphone-ext.xcodeproj/project.xcworkspace/xcuserdata/Birkemose.xcuserdatad/UserInterfaceState.xcuserstate and b/cocos2d-iphone-ext.xcodeproj/project.xcworkspace/xcuserdata/Birkemose.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/cocos2d-iphone-ext/Classes/Tests/CCEffectLineTest.m b/cocos2d-iphone-ext/Classes/Tests/CCEffectLineTest.m new file mode 100644 index 0000000..aa6c0f2 --- /dev/null +++ b/cocos2d-iphone-ext/Classes/Tests/CCEffectLineTest.m @@ -0,0 +1,65 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * Copyright (c) 2013-2015 Cocos2D Authors + * + * 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 "TestBase.h" +#import "effectLineNode.h" + +//---------------------------------------------------------------------- + +@interface CCEffectLineTest : TestBase + +@end + +//---------------------------------------------------------------------- + +@implementation CCEffectLineTest + +- (NSArray *)testConstructors +{ + return [NSArray arrayWithObjects: + @"simpleLine", + @"freeHand", + @"smokeSimple", + @"fog", + @"swordEffect", + @"bulletTrail", + @"rocketTrail", + @"electricity", + @"smokeDragon", + nil]; +} + +- (void)simpleLine { [self.contentNode addChild:[effectLineNode nodeWithLine:0]]; } +- (void)freeHand { [self.contentNode addChild:[effectLineNode nodeWithLine:1]]; } +- (void)smokeSimple { [self.contentNode addChild:[effectLineNode nodeWithLine:2]]; } +- (void)fog { [self.contentNode addChild:[effectLineNode nodeWithLine:3]]; } +- (void)swordEffect { [self.contentNode addChild:[effectLineNode nodeWithLine:4]]; } +- (void)bulletTrail { [self.contentNode addChild:[effectLineNode nodeWithLine:5]]; } +- (void)rocketTrail { [self.contentNode addChild:[effectLineNode nodeWithLine:6]]; } +- (void)electricity { [self.contentNode addChild:[effectLineNode nodeWithLine:7]]; } +- (void)smokeDragon { [self.contentNode addChild:[effectLineNode nodeWithLine:8]]; } + +@end diff --git a/cocos2d-iphone-ext/Classes/Tests/Test helper classes/effectLineNode.h b/cocos2d-iphone-ext/Classes/Tests/Test helper classes/effectLineNode.h new file mode 100644 index 0000000..bbb594d --- /dev/null +++ b/cocos2d-iphone-ext/Classes/Tests/Test helper classes/effectLineNode.h @@ -0,0 +1,41 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * Copyright (c) 2013-2015 Cocos2D Authors + * + * 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 +#import "cocos2d.h" + +// ----------------------------------------------------------------- + +@interface effectLineNode : CCNode + +// ----------------------------------------------------------------- + ++ (instancetype)nodeWithLine:(NSUInteger)line; +- (instancetype)initWithLine:(NSUInteger)line; + +// ----------------------------------------------------------------- + +@end diff --git a/cocos2d-iphone-ext/Classes/Tests/Test helper classes/effectLineNode.m b/cocos2d-iphone-ext/Classes/Tests/Test helper classes/effectLineNode.m new file mode 100644 index 0000000..70204c1 --- /dev/null +++ b/cocos2d-iphone-ext/Classes/Tests/Test helper classes/effectLineNode.m @@ -0,0 +1,139 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * Copyright (c) 2013-2015 Cocos2D Authors + * + * 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 "effectLineNode.h" +#import "CCEffectLineSegment.h" +#import "CCEffectLine.h" +#import "CCEffectLineFactory.h" + +// ----------------------------------------------------------------- + +@implementation effectLineNode +{ + CCNode *_lineContainer; + CCEffectLine *_currentLine; + CCEffectLine *_extraLine; // for double line effects + + CCEffectLineFactory *_lineFactory; + NSUInteger _lineIndex; +} + +// ----------------------------------------------------------------- + ++ (instancetype)nodeWithLine:(NSUInteger)line +{ + return [[self alloc] initWithLine:line]; +} + +// ----------------------------------------------------------------- + +- (instancetype)initWithLine:(NSUInteger)line +{ + self = [super init]; + + // initialise + self.contentSize = [CCDirector sharedDirector].viewSize; + self.userInteractionEnabled = YES; + + // line container + _lineContainer = [CCNode node]; + [self addChild:_lineContainer]; + _currentLine = nil; + _extraLine = nil; + + // line factory + _lineFactory = [CCEffectLineFactory new]; + _lineIndex = line; + + // show line type + CCLabelTTF *label = [CCLabelTTF labelWithString:[_lineFactory nameFromIndex:_lineIndex] fontName:@"Arial" fontSize:32]; + label.positionType = CCPositionTypeNormalized; + label.position = ccp(0.5, 0.9); + [self addChild:label]; + + // done + return self; +} + +// ----------------------------------------------------------------- + +- (void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event +{ + // start a new line + NSDictionary *dict = [_lineFactory lineFromIndex:_lineIndex]; + + // check for extra line + // NOTE + // the "extra" key is only added to the dictionaries, to allow for the demo app tp be able to create two simultaneous lines + // the line itself, does not use this entry for anything + if ([dict objectForKey:@"extra"]) + _extraLine = [CCEffectLine lineWithDictionary:[_lineFactory lineFromName:[dict objectForKey:@"extra"]]]; + + _currentLine = [CCEffectLine lineWithDictionary:dict]; + + [_lineContainer addChild:_currentLine]; + if (_extraLine) [_lineContainer addChild:_extraLine]; + + // start the line + [_currentLine start:touch.locationInWorld timestamp:touch.timestamp]; + if (_extraLine) [_extraLine start:touch.locationInWorld timestamp:touch.timestamp]; +} + +// ----------------------------------------------------------------- + +- (void)touchMoved:(CCTouch *)touch withEvent:(CCTouchEvent *)event +{ + [_currentLine add:touch.locationInWorld timestamp:touch.timestamp]; + if (_extraLine) [_extraLine add:touch.locationInWorld timestamp:touch.timestamp]; +} + +// ----------------------------------------------------------------- + +- (void)touchEnded:(CCTouch *)touch withEvent:(CCTouchEvent *)event +{ + [_currentLine end:touch.locationInWorld timestamp:touch.timestamp]; + if (_extraLine) [_extraLine end:touch.locationInWorld timestamp:touch.timestamp]; + + _currentLine = nil; + _extraLine = nil; +} + +// ----------------------------------------------------------------- + +@end + + + + + + + + + + + + +