============================================================
博文原创,转载请声明出处
电子咖啡(原id蓝岩)
============================================================
闲来无事,开始研究opengl在cocos2d中图像渲染时候的底层用法,及除了CCScene,CCSprint普通接口以外,底层opengl图形纹理渲染部分的代码。github有不少很棒的demo,拿过来研究一下。在这里感谢开源社区的所有贡献者。
水果忍者刀划痕效果,github地址:https://github.com/hiepnd/CCBlade
该例子主要用一下两个文件:
CCBlade.m//划痕效果的顶点存储,opengl的渲染
TouchTrailLayer.m //继承CCLayer,接收touch的begin、end点,传递给CCBlade,进行动画显示
下面主要介绍CCBlade。由于代码较多,难度不一,因此我将我的理解注释和测试代码写入了文件中,你可以直接阅读代码,里面关键的地方我已经写上了注释。有助于理解,当然,我的理解肯定有错误,还请多多指正,共同进步。
效果图:
CCBlade.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#define USE_STL_LIST 0
inline float fangle(CGPoint vect);
inline float lagrange1(CGPoint p1, CGPoint p2, float x);
inline void CGPointSet(CGPoint *v, float x, float y);
inline void f1(CGPoint p1, CGPoint p2, float d, CGPoint *o1, CGPoint *o2);
@interface CCBlade : CCNode {
//轨迹所有点array
NSMutableArray *path;
unsigned int pointLimit;
int count;
//opengl中存储vertices & coordinate数组
CGPoint *vertices;
CGPoint *coordinates;
BOOL reset;
CCTexture2D *_texture;
float width;
BOOL _finish;
BOOL _willPop;
}
@property (readonly)unsigned int pointLimit;
@property(retain) CCTexture2D *texture;
@property(nonatomic) float width;
@property (nonatomic, assign) BOOL autoDim;
+ (id) bladeWithMaximumPoint:(int) limit;
- (id) initWithMaximumPoint:(int) limit;
- (void) push:(CGPoint) v;
- (void) pop:(int) n;
- (void) clear;
- (void) reset;
- (void) dim:(BOOL) dim;
- (void) finish;
@end
CCBlade.m
#import "CCBlade.h"
//计算旋转角度
inline float fangle(CGPoint vect){
//相同,不旋转
if (vect.x == 0.0 && vect.y == 0.0) {
return 0;
}
//y不同,旋转90度
if (vect.x == 0.0) {
return vect.y > 0 ? M_PI/2 : -M_PI/2;
}
//x逆向
if (vect.y == 0.0 && vect.x < 0) {
return -M_PI;
}
//出去上面的特殊情况,剩下的atan计算角度
float angle = atan(vect.y / vect.x);
return vect.x < 0 ? angle + M_PI : angle;
}
//p1,p2 原点
//o1,o2 目标点
//d 宽度
inline void f1(CGPoint p1, CGPoint p2, float d, CGPoint *o1, CGPoint *o2){
float l = ccpDistance(p1, p2);
//计算转角角度
float angle = fangle(ccpSub(p2, p1));
//将p1点的width两端进行旋转
//这里只取p1,是因为下一次p2会迭代为p1进行计算
*o1 = ccpRotateByAngle(ccp(p1.x + l,p1.y + d), p1, angle);
*o2 = ccpRotateByAngle(ccp(p1.x + l,p1.y - d), p1, angle);
}
inline float lagrange1(CGPoint p1, CGPoint p2, float x){
return (x-p1.x)/(p2.x - p1.x)*p2.y + (x-p2.x)/(p1.x - p2.x)*p1.y ;
}
inline void CGPointSet(CGPoint *v, float x, float y){
v->x = x;
v->y = y;
}
@implementation CCBlade
@synthesize texture = _texture;
@synthesize pointLimit;
@synthesize width;
@synthesize autoDim;
+ (id) bladeWithMaximumPoint:(int) limit{
return [[[self alloc] initWithMaximumPoint:limit] autorelease];
}
- (id) initWithMaximumPoint:(int) limit{
self = [super init];
pointLimit = limit;
self.width = 5;
vertices = (CGPoint *)calloc(2*limit+5, sizeof(vertices[0]));
coordinates = (CGPoint *)calloc(2*limit+5, sizeof(coordinates[0]));
CGPointSet(coordinates+0, 0.00, 0.5);
reset = NO;
path = [[NSMutableArray alloc] init];
return self;
}
- (void) dealloc{
[_texture release];
free(vertices);
free(coordinates);
[path release];
[super dealloc];
}
//move (populater) vertices
- (void) populateVertices{
vertices[0] = [[path objectAtIndex:0] CGPointValue];
//get the header point ,then iterate it
CGPoint pre = vertices[0];
unsigned int i = 0;
/**
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glVertexPointer(2, GL_FLOAT, 0, vertices);
*/
//get second point ,then iterate it
CGPoint it = [[path objectAtIndex:1] CGPointValue];
float dd = width / [path count];
//as "pre"&"it" cost 2 item ,so "while" is "n-2"
// printf("%s:%d sizeof CGPoing:%ld\n",__FUNCTION__,__LINE__,sizeof(CGPoint));
while (i < [path count] - 2){
//change the width (height) of the blade
//"width - i * dd",dd不变 i 越大,越宽
f1(pre, it, width - i * dd , vertices+2*i+1, vertices+2*i+2);
// f1(pre, it, 7, vertices+2*i+1, vertices+2*i+2);
//all coordinates are same value , never change value
CGPointSet(coordinates+2*i+1, .5, 1.0);
CGPointSet(coordinates+2*i+2, .5, 0.0);
i++;
pre = it;
it = [[path objectAtIndex:i+1] CGPointValue];
}
CGPointSet(coordinates+1, 0.25, 1.0);
CGPointSet(coordinates+2, 0.25, 0.0);
vertices[2*[path count]-3] = it;
CGPointSet(coordinates+2*[path count]-3, 0.75, 0.5);
}
//移动点坐标,未用
- (void) shift{
int index = 2 * pointLimit - 1;
for (int i = index; i > 3; i -= 2) {
vertices[i] = vertices[i-2];
vertices[i-1] = vertices[i-3];
}
}
- (void) setWidth:(float)width_{
width = width_ * CC_CONTENT_SCALE_FACTOR();
}
#define DISTANCE_TO_INTERPOLATE 10 //允许的线条最大长度
//insert a point at the header of the path
- (void) push:(CGPoint) v{
_willPop = NO;
if (reset) {
return;
}
//retina screen
if (CC_CONTENT_SCALE_FACTOR() != 1.0f) {
v = ccpMult(v, CC_CONTENT_SCALE_FACTOR());
}
if ([path count] == 0) {
[path insertObject:[NSValue valueWithCGPoint:v] atIndex:0];
return;
}
CGPoint first = [[path objectAtIndex:0] CGPointValue];
if (ccpDistance(v, first) < DISTANCE_TO_INTERPOLATE) {
//如果小于最大长度,则insert at the header
[path insertObject:[NSValue valueWithCGPoint:v] atIndex:0];
if ([path count] > pointLimit) {
[path removeLastObject];
}
}else{
//移除最后的vertices
int num = ccpDistance(v, first) / DISTANCE_TO_INTERPOLATE;
CGPoint iv = ccpMult(ccpSub(v, first), (float)1./(num + 1));
for (int i = 1; i <= num + 1; i++) {
[path insertObject:[NSValue valueWithCGPoint:ccpAdd(first, ccpMult(iv, i))] atIndex:0];
}
while ([path count] > pointLimit) {
[path removeLastObject];
}
}
[self populateVertices];
}
//移除最后点
- (void) pop:(int) n{
while ([path count] > 0 && n > 0) {
[path removeLastObject];
n--;
}
if ([path count] > 2) {
[self populateVertices];
}
}
- (void) clear{
[path removeAllObjects];
reset = NO;
if (_finish)
[self removeFromParentAndCleanup:YES];
}
- (void) reset{
reset = TRUE;
}
- (void) dim:(BOOL) dim{
reset = dim;
}
- (void) draw{
if ((reset && [path count] > 0) || (self.autoDim && _willPop)) {
[self pop:1];
if ([path count] < 3) {
[self clear];
}
}
if ([path count] < 3) {
return;
}
_willPop = YES;
glDisableClientState(GL_COLOR_ARRAY);
NSAssert(_texture, @"NO TEXTURE SET");
/**
f1(pre, it, width - i * dd , vertices+2*i+1, vertices+2*i+2);
// f1(pre, it, 7, vertices+2*i+1, vertices+2*i+2);
//all coordinates are same value , never change value
CGPointSet(coordinates+2*i+1, .5, 1.0);
CGPointSet(coordinates+2*i+2, .5, 0.0);
*/
glBindTexture(GL_TEXTURE_2D, _texture.name);
// glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glVertexPointer(2, GL_FLOAT, 0, vertices);
// glTexCoordPointer(2, GL_FLOAT, 0, vertices);
// glVertexPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 2*[path count]-2);
//不是blade,是fans
// glDrawArrays(GL_TRIANGLE_FAN, 0, 2*[path count]-2);
glEnableClientState(GL_COLOR_ARRAY);
}
- (void) finish
{
_finish = YES;
}