How do I implement AABB ray cast hit checking on the iPhone - iphone

Basically, I draw a 3D cube, I can spin it around but I want to be able to touch it and know where on my cube's surface the user touched.
I'm using for setting up, generating and spinning. Its based on the Molecules code and NeHe tutorial #5.
Any help, links, tutorials and code would be greatly appreciated. I have lots of development experience but nothing much in the way of openGL and 3d.
//
// GLViewController.h
// NeHe Lesson 05
//
// Created by Jeff LaMarche on 12/12/08.
// Copyright Jeff LaMarche Consulting 2008. All rights reserved.
//
#import "GLViewController.h"
#import "GLView.h"
#implementation GLViewController
- (void)drawBox
{
static const GLfloat cubeVertices[] = {
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f,-1.0f, 1.0f,
-1.0f,-1.0f, 1.0f,
-1.0f, 1.0f,-1.0f,
1.0f, 1.0f,-1.0f,
1.0f,-1.0f,-1.0f,
-1.0f,-1.0f,-1.0f
};
static const GLubyte cubeNumberOfIndices = 36;
const GLubyte cubeVertexFaces[] = {
0, 1, 5, // Half of top face
0, 5, 4, // Other half of top face
4, 6, 5, // Half of front face
4, 6, 7, // Other half of front face
0, 1, 2, // Half of back face
0, 3, 2, // Other half of back face
1, 2, 5, // Half of right face
2, 5, 6, // Other half of right face
0, 3, 4, // Half of left face
7, 4, 3, // Other half of left face
3, 6, 2, // Half of bottom face
6, 7, 3, // Other half of bottom face
};
const GLubyte cubeFaceColors[] = {
0, 255, 0, 255,
255, 125, 0, 255,
255, 0, 0, 255,
255, 255, 0, 255,
0, 0, 255, 255,
255, 0, 255, 255
};
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
int colorIndex = 0;
for(int i = 0; i < cubeNumberOfIndices; i += 3)
{
glColor4ub(cubeFaceColors[colorIndex], cubeFaceColors[colorIndex+1], cubeFaceColors[colorIndex+2], cubeFaceColors[colorIndex+3]);
int face = (i / 3.0);
if (face%2 != 0.0)
colorIndex+=4;
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, &cubeVertexFaces[i]);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
//move this to a data model later!
- (GLfixed)floatToFixed:(GLfloat)aValue;
{
return (GLfixed) (aValue * 65536.0f);
}
- (void)drawViewByRotatingAroundX:(float)xRotation rotatingAroundY:(float)yRotation scaling:(float)scaleFactor translationInX:(float)xTranslation translationInY:(float)yTranslation view:(GLView*)view;
{
glMatrixMode(GL_MODELVIEW);
GLfixed currentModelViewMatrix[16] = { 45146, 47441, 2485, 0,
-25149, 26775,-54274, 0,
-40303, 36435, 36650, 0,
0, 0, 0, 65536 };
/*
GLfixed currentModelViewMatrix[16] = { 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 65536 };
*/
//glLoadIdentity();
//glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -10.0f, 4.0f);
// Reset rotation system
if (isFirstDrawing)
{
//glLoadIdentity();
glMultMatrixx(currentModelViewMatrix);
[self configureLighting];
isFirstDrawing = NO;
}
// Scale the view to fit current multitouch scaling
GLfixed fixedPointScaleFactor = [self floatToFixed:scaleFactor];
glScalex(fixedPointScaleFactor, fixedPointScaleFactor, fixedPointScaleFactor);
// Perform incremental rotation based on current angles in X and Y
glGetFixedv(GL_MODELVIEW_MATRIX, currentModelViewMatrix);
GLfloat totalRotation = sqrt(xRotation*xRotation + yRotation*yRotation);
glRotatex([self floatToFixed:totalRotation],
(GLfixed)((xRotation/totalRotation) * (GLfloat)currentModelViewMatrix[1] + (yRotation/totalRotation) * (GLfloat)currentModelViewMatrix[0]),
(GLfixed)((xRotation/totalRotation) * (GLfloat)currentModelViewMatrix[5] + (yRotation/totalRotation) * (GLfloat)currentModelViewMatrix[4]),
(GLfixed)((xRotation/totalRotation) * (GLfloat)currentModelViewMatrix[9] + (yRotation/totalRotation) * (GLfloat)currentModelViewMatrix[8])
);
// Translate the model by the accumulated amount
glGetFixedv(GL_MODELVIEW_MATRIX, currentModelViewMatrix);
float currentScaleFactor = sqrt(pow((GLfloat)currentModelViewMatrix[0] / 65536.0f, 2.0f) + pow((GLfloat)currentModelViewMatrix[1] / 65536.0f, 2.0f) + pow((GLfloat)currentModelViewMatrix[2] / 65536.0f, 2.0f));
xTranslation = xTranslation / (currentScaleFactor * currentScaleFactor);
yTranslation = yTranslation / (currentScaleFactor * currentScaleFactor);
// Grab the current model matrix, and use the (0,4,8) components to figure the eye's X axis in the model coordinate system, translate along that
glTranslatef(xTranslation * (GLfloat)currentModelViewMatrix[0] / 65536.0f, xTranslation * (GLfloat)currentModelViewMatrix[4] / 65536.0f, xTranslation * (GLfloat)currentModelViewMatrix[8] / 65536.0f);
// Grab the current model matrix, and use the (1,5,9) components to figure the eye's Y axis in the model coordinate system, translate along that
glTranslatef(yTranslation * (GLfloat)currentModelViewMatrix[1] / 65536.0f, yTranslation * (GLfloat)currentModelViewMatrix[5] / 65536.0f, yTranslation * (GLfloat)currentModelViewMatrix[9] / 65536.0f);
// Black background, with depth buffer enabled
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self drawBox];
}
- (void)configureLighting;
{
const GLfixed lightAmbient[] = {13107, 13107, 13107, 65535};
const GLfixed lightDiffuse[] = {65535, 65535, 65535, 65535};
const GLfixed matAmbient[] = {65535, 65535, 65535, 65535};
const GLfixed matDiffuse[] = {65535, 65535, 65535, 65535};
const GLfixed lightPosition[] = {30535, -30535, 0, 0};
const GLfixed lightShininess = 20;
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glMaterialxv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmbient);
glMaterialxv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiffuse);
glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, lightShininess);
glLightxv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
glLightxv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
glLightxv(GL_LIGHT0, GL_POSITION, lightPosition);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_NORMALIZE);
}
-(void)setupView:(GLView*)view
{
const GLfloat zNear = 0.1,
zFar = 1000.0,
fieldOfView = 60.0;
GLfloat size;
glMatrixMode(GL_PROJECTION);
glEnable(GL_DEPTH_TEST);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = view.bounds;
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
(rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glScissor(0, 0, rect.size.width, rect.size.height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glTranslatef(0.0f, 0.0f, -6.0f);
isFirstDrawing = YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)dealloc
{
[super dealloc];
}
#end

In order to implement Ray cast hit checking, you should check these sources:
http://www.mvps.org/directx/articles/rayproj.htm
http://bookofhook.com/mousepick.pdf
http://eigenclass.blogspot.com/2008/10/opengl-es-picking-using-ray-boundingbox.html
Basically, first, create a 3D ray from a 2D touch. Then use that ray to check for intersection with objects in your world. You should create the matrix inverse of your current matrix, and from the inverse matrix you can create start and end position using your near and far clip plane. and then when calculating the near and far points you should apply the projection settings.
BTW: In my project, my point recognition is based on color unique pixel comparison rather than ray cast hit check. It is much easier to implement hit check with just finding unique colors. Only a suggestion, hope it helps :)
cheers,
Guvener

Related

Shaders iOS, OSX

I have problem with OpenGL's shaders.
#ifdef GL_ES
precision lowp float;
#endif
varying vec4 v_fragmentColor;
void main()
{
gl_FragColor = v_fragmentColor;
}
and the second one
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_MVPMatrix;
#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
#else
varying vec4 v_fragmentColor;
#endif
void main()
{
gl_Position = u_MVPMatrix * a_position;
v_fragmentColor = a_color;
}
Firs I make 2d projection
- (void)makeProjection2D
{
float scaleFactor = 1.0;
CGSize size = self.bounds.size;
glViewport(0, 0, size.width, size.height);
kmGLMatrixMode(KM_GL_PROJECTION);
kmGLLoadIdentity();
kmMat4 orthoMatrix;
kmMat4OrthographicProjection(&orthoMatrix, 0, size.width / scaleFactor, 0, size.height / scaleFactor, -size.width, size.width);
kmGLMultMatrix( &orthoMatrix );
kmGLMatrixMode(KM_GL_MODELVIEW);
kmGLLoadIdentity();
}
Next step is shaders creation.
Folowed by OpenGL initialization.
GLint dim[2] = {1025, 769};
CGLSetParameter([[self openGLContext] CGLContextObj], kCGLCPSurfaceBackingSize, dim);
CGLEnable([[self openGLContext] CGLContextObj], kCGLCESurfaceBackingSize);
// Setup OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
glGetError();
// Setup the view port in Pixels
glOrtho(0, frame.size.width, 0, frame.size.height, -1, 1);
glViewport(0, 0, frame.size.width, frame.size.height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnableClientState(GL_VERTEX_ARRAY);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
self.pointSize = pointSizeForDrawing;
GLint zeroOpacity = 0;
[[self openGLContext] setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity];
If I try to draw domething on the screen like four big pixels
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, framebuffer);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPointSize(100.0);
glColor3b(1.0, 1.0, 1.0);
GLfloat i[8] = {44.0, 44.0, 100.0, 100.0, 300.0, 300.0, 500.0, 500.0};
glVertexPointer(2, GL_FLOAT, 0, i);
glDrawArrays(GL_POINTS, 0, 4);
glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, framebuffer );
glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, 0 );
glViewport(0, 0, self.bounds.size.width, self.bounds.size.height);
glBlitFramebufferEXT( 0, 0, self.bounds.size.width, self.bounds.size.height, 0, 0, self.bounds.size.width, self.bounds.size.height, GL_COLOR_BUFFER_BIT, GL_NEAREST );
glSwapAPPLE();
Only one is visible, if I remove making 2d projection and shaders then all 4 are visible.
The code, which uses the shaders works on iOS, but not on OSX, any idea why? To initialize shaders I use CCGLProgram from cocos2d If you need more info, please let me know.

How to change the perspective on a 3D cube in OpenGL ES?

I draw a cube with OpenGL ES and I want to have an above perspective on it.But I don't know what should I modify...Maybe you can help.
- (void)render:(CADisplayLink*)displayLink {
//-(void) render{
glClearColor(255.0, 255.0/255.0, 255.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
//projection
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:8];
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
// _currentScaling += displayLink.duration * 1.0;
[modelView populateFromTranslation:CC3VectorMake(sin(30), _currentScaling , -7)];
[modelView rotateBy:CC3VectorMake(0, -15, 0)];
// [modelView scaleByY:_currentScaling];
if(_currentScaling > 2 ){
[displayLink invalidate];
_running = 1;
// scale = 0;
}
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
// 1
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
// 2
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
// 3
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),
GL_UNSIGNED_BYTE, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
I recently started looking into OpenGL myself and found this same example. I believe what you are looking for is to change this line to change the perspective:
[modelView rotateBy:CC3VectorMake(0, -15, 0)];
and specify the degrees of rotation you want for the x,y,z axes.

Draw Square with OpenGL ES for iOS

I am trying to draw a rectangle using the GLPaint example project provided by apple. I have tried modifying the vertices but cannot get a rectangle to appear on the screen. The finger painting works perfectly. Am I missing something in my renderRect method?
- (void)renderRect {
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// Replace the implementation of this method to do your own custom drawing.
static const GLfloat squareVertices[] = {
-0.5f, -0.33f,
0.5f, -0.33f,
-0.5f, 0.33f,
0.5f, 0.33f,
};
static float transY = 0.0f;
glTranslatef(0.0f, (GLfloat)(sinf(transY)/2.0f), 0.0f);
// Render the vertex array
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
The rest of the project is set up stock to allow drawing on the screen but just for reference these are the gl settings that are set.
// Set the view's scale factor
self.contentScaleFactor = 1.0;
// Setup OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
CGFloat scale = self.contentScaleFactor;
// Setup the view port in Pixels
glOrthof(0, frame.size.width * scale, 0, frame.size.height * scale, -1, 1);
glViewport(0, 0, frame.size.width * scale, frame.size.height * scale);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_BLEND);
// Set a blending function appropriate for premultiplied alpha pixel data
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POINT_SPRITE_OES);
glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
glPointSize(width / brushScale);
static const GLfloat squareVertices[] = {
30.0f, 300.0f,//-0.5f, -0.33f,
280.0f, 300.0f,//0.5f, -0.33f,
30.0f, 170.0f,//-0.5f, 0.33f,
280.0f, 170.0f,//0.5f, 0.33f,
};
That's definitely too much. OpenGL has normalized screen coords in range [-1..1]. So you have to convert device coords to normalized ones.
Issues are:
(1) the following code:
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
CGFloat scale = self.contentScaleFactor;
// Setup the view port in Pixels
glOrthof(0, frame.size.width * scale, 0, frame.size.height * scale, -1, 1);
glViewport(0, 0, frame.size.width * scale, frame.size.height * scale);
Establishes that the on-screen coordinates range from (0, 0) in the lower left to frame.size in the upper right. In other words, one OpenGL unit is one iPhone point. So your array of:
static const GLfloat squareVertices[] = {
-0.5f, -0.33f,
0.5f, -0.33f,
-0.5f, 0.33f,
0.5f, 0.33f,
};
Is less than 1 pixel in size.
(2) you have the following in the setup:
brushImage = [UIImage imageNamed:#"Particle.png"].CGImage;
/* ...brushImage eventually becomes the current texture... */
glEnable(GL_TEXTURE_2D);
You subsequently fail to supply texture coordinates for your quad. Probably you want to disable GL_TEXTURE_2D.
So the following:
static const GLfloat squareVertices[] = {
0.0f, 0.0f,
0.0, 10.0f,
90.0, 0.0f,
90.0f, 10.0f,
};
glDisable(GL_TEXTURE_2D);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
// Render the vertex array
glVertexPointer(2, GL_FLOAT, 0, squareVertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Will produce a white quad 90 points wide and 10 points tlal in the lower left of the screen.

OpenGL sprites are trembling slightly at rendering

I made a class which manage sprites in an iphone application by using opengl es.
rendering works well but when the sprite sheet is large (more than 512x512px) and the sprite has a part which doesn't move, this part seems trembling slightly (like if antialiasing was computed differently for each frame).
Is it normal or is it a bug in my code ?
Here is the code :
//set bounds of the cell
int t_x1 = position.x;
int t_y1 = MAX_SPRITE_HEIGHT - position.y;
int t_x2 = t_x1 + width;
int t_y2 = t_y1 - height;
//set vertices position
GLshort verticesBuffer[4*3];
verticesBuffer[0] = t_x1;
verticesBuffer[1] = t_y1;
verticesBuffer[2] = depth;
verticesBuffer[3] = t_x1;
verticesBuffer[4] = t_y2;
verticesBuffer[5] = depth;
verticesBuffer[6] = t_x2;
verticesBuffer[7] = t_y1;
verticesBuffer[8] = depth;
verticesBuffer[9] = t_x2;
verticesBuffer[10] = t_y2;
verticesBuffer[11] = depth;
//set texture coordinates
GLfloat texCoordBuffer[2*4]
texCoordBuffer[0] = a_cellLayer.origin.x / CGImageGetWidth(texture.textureImage);
texCoordBuffer[1] = a_cellLayer.origin.y / CGImageGetHeight(texture.textureImage);
texCoordBuffer[2] = texCoordBuffer[0];
texCoordBuffer[3] = texCoordBuffer[1] + (float)(t_y1-t_y2) / CGImageGetHeight(texture.textureImage);
texCoordBuffer[4] = texCoordBuffer[6];
texCoordBuffer[5] = texCoordBuffer[1];
texCoordBuffer[6] = texCoordBuffer[0] + (float)(t_x2-t_x1) / CGImageGetWidth(texture.textureImage);
texCoordBuffer[7] = texCoordBuffer[3];
//set texture
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, [texture getTexture]);
//set vertices
glVertexPointer(3, GL_SHORT, 0, verticesBuffer);
glEnableClientState(GL_VERTEX_ARRAY);
//apply texture
glTexCoordPointer(2, GL_FLOAT, 0, texCoordBuffer);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//render
glColor4f(1.0, 1.0, 1.0, 1.0); // R V B Alpha
glNormal3f(0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4*nbOfXSudivisions*nbOfYSudivisions);
I tried to change the type of my texture coordinates with GL_FIXED but it doesn't change anything
Does anybody have an idea ?
I don't use depth buffer and I've set the projection matrix like that :
const GLfloat zNear = 100, zFar = 1000, fieldOfView = 22.5;
glMatrixMode(GL_PROJECTION);
GLfloat size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = oGLView.bounds;
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
(rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//set perspective
gluLookAt(PAGE_WIDTH/2, PAGE_HEIGHT/2, PAGE_WIDTH/2.0f/(tanf(DEGREES_TO_RADIANS(fieldOfView/2.0f))),
PAGE_WIDTH/2, PAGE_HEIGHT/2, 0,
0, 1, 0);

How do I texture map a cube in OpenGL ES?

Having a lot of trouble getting texture maps to work in openGL ES (iphone).
Here's what I've done:
built an array of vertexes
built an array of faces that reference the indices of the array of vertexes for each face
built an array of colors so I can be sure I know which vertex on the cube is which.
All of this following Jeff Lamarche's tutorials. Getting the objects rendering and moving is not a problem.
Now I'm trying to get the cube (actually a tile, narrower in Z that X or Y) to stick a texture on two opposite faces (the others can come later). I have been able to get one face to work, but I am not getting workable results on any other face.
What is the most systematic way to texture map an object in OpenGL ES, and can anyone see where the errors in my code are?
#import "GLViewController.h"
#import "ConstantsAndMacros.h"
#import "OpenGLCommon.h"
#import "Cube.h"
#implementation GLViewController
#synthesize initDone;
#synthesize tileArray;
#synthesize tileRows;
#synthesize tileCols;
#synthesize cubes;
#synthesize gridOffsetX;
#synthesize gridOffsetY;
#synthesize gridOffsetZ;
#synthesize tileSpacing;
- (void)drawView:(UIView *)theView
{
static GLfloat rot = 0.0;
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// This is the same result as using Vertex3D, just faster to type and
// can be made const this way
static const Vertex3D vertices[]= {
{1.0f, -1.0f, 0.2f},
{1.0f, -1.0f, -0.2f},
{1.0f, 1.0f, -0.2f},
{1.0f, 1.0f, 0.2f},
{-1.0f, -1.0f, 0.2f},
{-1.0f, -1.0f, -0.2f},
{-1.0f, 1.0f, -0.2f},
{-1.0f, 1.0f, 0.2f}
};
static const Color3D colors[] = {
{1.0, 0.0, 0.0, 20.0},
{1.0, 1.0, 1.0, 20.0},
{1.0, 1.0, 1.0, 20.0},
{0.0, 0.0, 1.0, 20.0},
{0.0, 1.0, 0.0, 20.0},
{1.0, 1.0, 1.0, 20.0},
{1.0, 1.0, 1.0, 20.0},
{1.0, 1.0, 1.0, 20.0},
};
static const GLubyte cubeFaces[] = {
0, 1, 3,
2, 3, 1,
0, 3, 4,
3, 4, 7, // first main face
2, 1, 6, // second main face
1, 6, 5,
5, 6, 7,
5, 4, 7,
7, 6, 3,
6, 3, 2,
4, 0, 5,
1, 0, 5,
};
static const Vector3D normals[] = {
{0.200000, -0.400000, 0.000000},
{0.400000, -0.200000, -0.400000},
{0.333333, 0.333333, -0.333333},
{0.400000, 0.400000, -0.200000},
{-0.333333, -0.333333, 0.333333},
{-0.400000, -0.400000, -0.200000},
{-0.200000, 0.400000, -0.400000},
{-0.400000, 0.200000, 0.000000},
};
static const GLfloat texCoords[] = {
0.0, 0.0, // texture face
1.0, 1.0,
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
0.0, 0.0, // texture face
1.0, 1.0,
1.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0,
0.0, 0.0, // texture face
1.0, 1.0,
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
0.0, 0.0, // texture face
1.0, 1.0,
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
0.0, 0.0, // texture face
1.0, 1.0,
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
0.0, 0.0, //
1.0, 1.0,
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
};
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glLoadIdentity();
glClearColor(0.7, 0.7, 0.7, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);
glNormalPointer(GL_FLOAT, 0, normals);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
NSMutableArray *tempRow;
Cube *tempCube;
for (int i = 1; i <= cubes.tileRows; i++)
{
tempRow = [cubes rowAtIndex:i-1];
for (int j = 1; j <= cubes.tileCols; j++)
{
tempCube = [tempRow objectAtIndex:j-1];
glLoadIdentity();
glTranslatef(gridOffsetX + (tileSpacing * (GLfloat)i), gridOffsetY + (tileSpacing * (GLfloat)j), gridOffsetZ);
glRotatef(rot, 1.0, 0.0, 0);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, cubeFaces);
}
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
static NSTimeInterval lastDrawTime;
if (lastDrawTime)
{
NSTimeInterval timeSinceLastDraw = [NSDate timeIntervalSinceReferenceDate] - lastDrawTime;
rot+=30 * timeSinceLastDraw;
}
//NSLog(#"rot is %f", rot);
lastDrawTime = [NSDate timeIntervalSinceReferenceDate];
}
-(void)setupView:(GLView*)view
{
initDone = NO;
tileRows = 5;
tileCols = 7;
gridOffsetX = 5.2f;
gridOffsetY = 6.9f;
gridOffsetZ = -14.0;
tileSpacing = -2.15f;
cubes = [[Cubes alloc] initWithRowCount:tileRows colCount: tileCols ];
const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 50.0;
GLfloat size;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
CGRect rect = view.bounds;
// glOrthof(-5.0, // Left
// 5.0, // Right
// -5.0 / (rect.size.width / rect.size.height), // Bottom
// 5.0 / (rect.size.width / rect.size.height), // Top
// 0.01, // Near
// 10000.0); // Far
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size /
(rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_COLOR_MATERIAL);
// Enable lighting
glEnable(GL_LIGHTING);
// Turn the first light on
glEnable(GL_LIGHT0);
// Define the ambient component of the first light
const GLfloat light0Ambient[] = {0.5, 0.5, 0.5, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, light0Ambient);
// Define the diffuse component of the first light
const GLfloat light0Diffuse[] = {0.7, 0.7, 0.7, 1.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
// Define the specular component and shininess of the first light
const GLfloat light0Specular[] = {0.7, 0.7, 0.7, 1.0};
const GLfloat light0Shininess = 0.4;
glLightfv(GL_LIGHT0, GL_SPECULAR, light0Specular);
// Define the position of the first light
const GLfloat light0Position[] = {0.0, 10.0, 10.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
// Define a direction vector for the light, this one points right down the Z axis
const GLfloat light0Direction[] = {0.0, 0.0, -1.0};
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light0Direction);
// Define a cutoff angle. This defines a 90° field of vision, since the cutoff
// is number of degrees to each side of an imaginary line drawn from the light's
// position along the vector supplied in GL_SPOT_DIRECTION above
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_SRC_COLOR);
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
NSString *path = [[NSBundle mainBundle] pathForResource:#"a-tile-64" ofType:#"png"];
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:texData];
if (image == nil)
NSLog(#"Do real error checking here");
GLuint width = CGImageGetWidth(image.CGImage);
GLuint height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
void *imageData = malloc( height * width * 4 );
CGContextRef context = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context, CGRectMake( 0, 0, width, height ) );
CGContextTranslateCTM( context, 0, height - height );
CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
CGContextRelease(context);
free(imageData);
[image release];
[texData release];
glLoadIdentity();
};
- (void)dealloc
{
[tileArray release];
[cubes release];
[super dealloc];
}
#end
I also kick-started OpenGL ES using Jeff's tutorials.
I would suggest simplifying what you're trying to do. For example:
Forget the Normals
Forget the Colors
Forget the indices
Create a structure that binds the vertices to their attributes
Jeff provides a useful TexturedVertexData3D Struct that does this. You don't have to fill in the normal part if you don't want to.
Then, set up your strides appropriately:
glVertexPointer(3, GL_FLOAT, sizeof(TexturedVertexData3D), &vertices[0]);
glTexCoordPointer(2, GL_FLOAT, sizeof(TexturedVertexData3D), &vertices[0].texCoords);
And use glDrawArrays to draw your object:
glDrawArrays(GL_TRIANGLES, 0, nVertices);
Once you have this working, go ahead and add the normals and colors into the TexturedVertexData3D struct, and set your texture and color pointers appropriately. Then test again, or post an update if things don't work.
At this point you can start to think about how to use indices. Indices don't really make sense until you are rendering thousands of vertices. But, when the time comes, you can get a nice performance increase by using them.
I've been watching this question as I'm learning OpenGL at the moment and will be moving on to OpenGL ES. As nobody has answered yet, I'll give you my thoughts, don't consider this an expert opinion though.
Something to consider is that as it stands, your Vertex, Color & Normal arrays contains 8 'items', however your TexCoord array has 36 'items'. I'm pretty sure when you use glDrawElements with a list of indices, it uses those indices to pick items from ALL of the activated arrays. So the last 28 items of your TexCoord array will never be used, they will be picked out according to the indices specified by cubeFaces. In the tutorial you linked, there are four items in all the arrays, which works nicely for a single face of an object.
However this is a bit of an issue with using indices for 3D objects, because although several vertices are re-used in a cube, their texture co-ordinates will not necessarily be the same for the different triangles they are used for. In fact neither will their normals, so that may be another issue with your code when it comes to lighting the object.
I don't know what the best solution to this is, which is why I'm interested in any other answers to this question... drawing the cube vertex by vertex is an option. I'm also wondering whether it's possible to draw each face of the cube separately, and change the TexCoord array each time. Or perhaps there is some easier or standard way of doing this kind of thing that I am not yet aware of!