/*
* cocos2d for iPhone: http://www.cocos2d-iphone.org
*
* Copyright (c) 2009 On-Core
*
* 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 <Availability.h>
#import "ccMacros.h"
#import "CCGrid.h"
#import "CCTexture2D.h"
#import "CCDirector.h"
#import "CCGrabber.h"
#import "Platforms/CCGL.h"
#import "Support/CGPointExtension.h"
#import "Support/ccUtils.h"
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
#import "Platforms/iOS/CCDirectorIOS.h"
#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
#pragma mark -
#pragma mark CCGridBase
@implementation CCGridBase
@synthesize reuseGrid = reuseGrid_;
@synthesize texture = texture_;
@synthesize grabber = grabber_;
@synthesize gridSize = gridSize_;
@synthesize step = step_;
+(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped
{
return [[[self alloc] initWithSize:gridSize texture:texture flippedTexture:flipped] autorelease];
}
+(id) gridWithSize:(ccGridSize)gridSize
{
return [[(CCGridBase*)[self alloc] initWithSize:gridSize] autorelease];
}
-(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped
{
if( (self=[super init]) ) {
active_ = NO;
reuseGrid_ = 0;
gridSize_ = gridSize;
self.texture = texture;
isTextureFlipped_ = flipped;
CGSize texSize = [texture_ contentSizeInPixels];
step_.x = texSize.width / gridSize_.x;
step_.y = texSize.height / gridSize_.y;
grabber_ = [[CCGrabber alloc] init];
[grabber_ grab:texture_];
[self calculateVertexPoints];
}
return self;
}
-(id)initWithSize:(ccGridSize)gSize
{
CCDirector *director = [CCDirector sharedDirector];
CGSize s = [director winSizeInPixels];
unsigned long POTWide = ccNextPOT(s.width);
unsigned long POTHigh = ccNextPOT(s.height);
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
EAGLView *glview = [[CCDirector sharedDirector] openGLView];
NSString *pixelFormat = [glview pixelFormat];
CCTexture2DPixelFormat format = [pixelFormat isEqualToString: kEAGLColorFormatRGB565] ? kCCTexture2DPixelFormat_RGB565 : kCCTexture2DPixelFormat_RGBA8888;
#else
CCTexture2DPixelFormat format = kCCTexture2DPixelFormat_RGBA8888;
#endif
void *data = calloc((int)(POTWide * POTHigh * 4), 1);
if( ! data ) {
CCLOG(@"cocos2d: CCGrid: not enough memory");
[self release];
return nil;
}
CCTexture2D *texture = [[CCTexture2D alloc] initWithData:data pixelFormat:format pixelsWide:POTWide pixelsHigh:POTHigh contentSize:s];
free( data );
if( ! texture ) {
CCLOG(@"cocos2d: CCGrid: error creating texture");
[self release];
return nil;
}
self = [self initWithSize:gSize texture:texture flippedTexture:NO];
[texture release];
return self;
}
- (NSString*) description
{
return [NSString stringWithFormat:@"<%@ = %08X | Dimensions = %ix%i>", [self class], self, gridSize_.x, gridSize_.y];
}
- (void) dealloc
{
CCLOGINFO(@"cocos2d: deallocing %@", self);
[self setActive: NO];
[texture_ release];
[grabber_ release];
[super dealloc];
}
// properties
-(BOOL) active
{
return active_;
}
-(void) setActive:(BOOL)active
{
active_ = active;
if( ! active ) {
CCDirector *director = [CCDirector sharedDirector];
ccDirectorProjection proj = [director projection];
[director setProjection:proj];
}
}
-(BOOL) isTextureFlipped
{
return isTextureFlipped_;
}
-(void) setIsTextureFlipped:(BOOL)flipped
{
if( isTextureFlipped_ != flipped ) {
isTextureFlipped_ = flipped;
[self calculateVertexPoints];
}
}
// This routine can be merged with Director
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
-(void)applyLandscape
{
CCDirector *director = [CCDirector sharedDirector];
CGSize winSize = [director displaySizeInPixels];
float w = winSize.width / 2;
float h = winSize.height / 2;
ccDeviceOrientation orientation = [director deviceOrientation];
switch (orientation) {
case CCDeviceOrientationLandscapeLeft:
glTranslatef(w,h,0);
glRotatef(-90,0,0,1);
glTranslatef(-h,-w,0);
break;
case CCDeviceOrientationLandscapeRight:
glTranslatef(w,h,0);
glRotatef(90,0,0,1);
glTranslatef(-h,-w,0);
break;
case CCDeviceOrientationPortraitUpsideDown:
glTranslatef(w,h,0);
glRotatef(180,0,0,1);
glTranslatef(-w,-h,0);
break;
default:
break;
}
}
#endif
-(void)set2DProjection
{
CGSize winSize = [[CCDirector sharedDirector] winSizeInPixels];
glLoadIdentity();
glViewport(0, 0, winSize.width, winSize.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
ccglOrtho(0, winSize.width, 0, winSize.height, -1024, 1024);
glMatrixMode(GL_MODELVIEW);
}
// This routine can be merged with Director
-(void)set3DProjection
{
CCDirector *director = [CCDirector sharedDirector];
CGSize winSize = [director displaySizeInPixels];
glViewport(0, 0, winSize.width, winSize.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, (GLfloat)winSize.width/winSize.height, 0.5f, 1500.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt( winSize.width/2, winSize.height/2, [director getZEye],
winSize.width/2, winSize.height/2, 0,
0.0f, 1.0f, 0.0f
);
}
-(void)beforeDraw
{
[self set2DProjection];
[grabber_ beforeRender:texture_];
}
-(void)afterDraw:(CCNode *)target
{
[grabber_ afterRender:texture_];
[self set3DProjection];
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
[self applyLandscape];
#endif
if( target.camera.dirty ) {
CGPoint offset = [target anchorPointInPixels];
//
// XXX: Camera should be applied in the AnchorPoint
//
ccglTranslate(offset.x, offset.y, 0);
[target.camera locate];
ccglTranslate(-offset.x, -offset.y, 0);
}
glBindTexture(GL_TEXTURE_2D, texture_.name);
[self blit];
}
-(void)blit
{
[NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
}
-(void)reuse
{
[NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
}
-(void)calculateVertexPoints
{
[NSException raise:@"GridBase" format:@"Abstract class needs implementation"];
}
@end
////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CCGrid3D
@implementation CCGrid3D
-(void)dealloc
{
free(texCoordinates);
free(vertices);
free(indices);
free(originalVertices);
[super dealloc];
}
-(void)blit
{
NSInteger n = gridSize_.x * gridSize_.y;
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
// Unneeded states: GL_COLOR_ARRAY
glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates);
glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
// restore GL default state
glEnableClientState(GL_COLOR_ARRAY);
}
-(void)calculateVertexPoints
{
float width = (float)texture_.pixelsWide;
float height = (float)texture_.pixelsHigh;
float imageH = texture_.contentSizeInPixels.height;
int x, y, i;
vertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
originalVertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
texCoordinates = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(CGPoint));
indices = malloc(gridSize_.x*gridSize_.y*sizeof(GLushort)*6);
float *vertArray = (float*)vertices;
float *texArray = (float*)texCoordinates;
GLushort *idxArray = (GLushort *)indices;
for( x = 0; x < gridSize_.x; x++ )
{
for( y = 0; y < gridSize_.y; y++ )
{
NSInteger idx = (y * gridSize_.x) + x;
float x1 = x * step_.x;
float x2 = x1 + step_.x;
float y1 = y * step_.y;
float y2 = y1 + step_.y;
GLushort a = x * (gridSize_.y+1) + y;
GLushort b = (x+1) * (gridSize_.y+1) + y;
GLushort c = (x+1) * (gridSize_.y+1) + (y+1);
GLushort d = x * (gridSize_.y+1) + (y+1);
GLushort tempidx[6] = { a, b, d, b, c, d };
memcpy(&idxArray[6*idx], tempidx, 6*sizeof(GLushort));
int l1[4] = { a*3, b*3, c*3, d*3 };
ccVertex3F e = {x1,y1,0};
ccVertex3F f = {x2,y1,0};
ccVertex3F g = {x2,y2,0};
ccVertex3F h = {x1,y2,0};
ccVertex3F l2[4] = { e, f, g, h };
int tex1[4] = { a*2, b*2, c*2, d*2 };
CGPoint tex2[4] = { ccp(x1, y1), ccp(x2, y1), ccp(x2, y2), ccp(x1, y2) };
for( i = 0; i < 4; i++ )
{
vertArray[ l1[i] ] = l2[i].x;
vertArray[ l1[i] + 1 ] = l2[i].y;
vertArray[ l1[i] + 2 ] = l2[i].z;
texArray[ tex1[i] ] = tex2[i].x / width;
if( isTextureFlipped_ )
texArray[ tex1[i] + 1 ] = (imageH - tex2[i].y) / height;
else
texArray[ tex1[i] + 1 ] = tex2[i].y / height;
}
}
}
memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
}
-(ccVertex3F)vertex:(ccGridSize)pos
{
NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
float *vertArray = (float *)vertices;
ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] };
return vert;
}
-(ccVertex3F)originalVertex:(ccGridSize)pos
{
NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
float *vertArray = (float *)originalVertices;
ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] };
return vert;
}
-(void)setVertex:(ccGridSize)pos vertex:(ccVertex3F)vertex
{
NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3;
float *vertArray = (float *)vertices;
vertArray[index] = vertex.x;
vertArray[index+1] = vertex.y;
vertArray[index+2] = vertex.z;
}
-(void)reuse
{
if ( reuseGrid_ > 0 )
{
memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F));
reuseGrid_--;
}
}
@end
////////////////////////////////////////////////////////////
#pragma mark -
#pragma mark CCTiledGrid3D
@implementation CCTiledGrid3D
-(void)dealloc
{
free(texCoordinates);
free(vertices);
free(indices);
free(originalVertices);
[super dealloc];
}
-(void)blit
{
NSInteger n = gridSize_.x * gridSize_.y;
// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY
// Unneeded states: GL_COLOR_ARRAY
glDisableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates);
glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices);
// restore default GL state
glEnableClientState(GL_COLOR_ARRAY);
}
-(void)calculateVertexPoints
{
float width = (float)texture_.pixelsWide;
float height = (float)texture_.pixelsHigh;
float imageH = texture_.contentSizeInPixels.height;
NSInteger numQuads = gridSize_.x * gridSize_.y;
vertices = malloc(numQuads*12*sizeof(GLfloat));
originalVertices = malloc(numQuads*12*sizeof(GLfloat));
texCoordinates = malloc(numQuads*8*sizeof(GLfloat));
indices = malloc(numQuads*6*sizeof(GLushort));
float *vertArray = (float*)vertices;
float *texArray = (float*)texCoordinates;
GLushort *idxArray = (GLushort *)indices;
int x, y;
for( x = 0; x < gridSize_.x; x++ )
{
for( y = 0; y < gridSize_.y; y++ )
{
float x1 = x * step_.x;
float x2 = x1 + step_.x;
float y1 = y * step_.y;
float y2 = y1 + step_.y;
*vertArray++ = x1;
*vertArray++ = y1;
*vertArray++ = 0;
*vertArray++ = x2;
*vertArray++ = y1;
*vertArray++ = 0;
*vertArray++ = x1;
*vertArray++ = y2;
*vertArray++ = 0;
*vertArray++ = x2;
*vertArray++ = y2;
*vertArray++ = 0;
float newY1 = y1;
float newY2 = y2;
if( isTextureFlipped_ ) {
newY1 = imageH - y1;
newY2 = imageH - y2;
}
*texArray++ = x1 / width;
*texArray++ = newY1 / height;
*texArray++ = x2 / width;
*texArray++ = newY1 / height;
*texArray++ = x1 / width;
*texArray++ = newY2 / height;
*texArray++ = x2 / width;
*texArray++ = newY2 / height;
}
}
for( x = 0; x < numQuads; x++)
{
idxArray[x*6+0] = x*4+0;
idxArray[x*6+1] = x*4+1;
idxArray[x*6+2] = x*4+2;
idxArray[x*6+3] = x*4+1;
idxArray[x*6+4] = x*4+2;
idxArray[x*6+5] = x*4+3;
}
memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat));
}
-(void)setTile:(ccGridSize)pos coords:(ccQuad3)coords
{
NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
float *vertArray = (float*)vertices;
memcpy(&vertArray[idx], &coords, sizeof(ccQuad3));
}
-(ccQuad3)originalTile:(ccGridSize)pos
{
NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
float *vertArray = (float*)originalVertices;
ccQuad3 ret;
memcpy(&ret, &vertArray[idx], sizeof(ccQuad3));
return ret;
}
-(ccQuad3)tile:(ccGridSize)pos
{
NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3;
float *vertArray = (float*)vertices;
ccQuad3 ret;
memcpy(&ret, &vertArray[idx], sizeof(ccQuad3));
return ret;
}
-(void)reuse
{
if ( reuseGrid_ > 0 )
{
NSInteger numQuads = gridSize_.x * gridSize_.y;
memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat));
reuseGrid_--;
}
}
@end