SOLVED! thanks to Jonathan in the comments below, and Andy Zupko moments before on IM. 🙂 You guys rock.
I usually try to share solutions on this blog, but sometimes I just feel dumb and need some help. I’ve searched all over for this one, but I know some wicked smart people read this blog, so maybe they can help me.
So I am rendering some points in OpenGL ES on the iPhone and want to rotate them with touches. Swipe left/right to rotate on y axis, up/down on x axis. Simple. If you open up XCode and build a project from the OpenGL ES Application template, and then make the following changes, you’ll have what I have…
1. in EAGL.h, add these vars:
[c] CGFloat dx, dy;
CGPoint startPoint;
[/c]
2. in EAGL.m, change the drawView method to the following:
[c]- (void)drawView {
// Replace the implementation of this method to do your own custom drawing
const GLfloat squareVertices[] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f
};
const GLubyte squareColors[] = {
0, 0, 0, 255,
0, 0, 0, 255,
0, 0, 0, 255,
0, 0, 0, 255,
0, 0, 0, 255,
0, 0, 0, 255,
0, 0, 0, 255,
0, 0, 0, 255,
};
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glRotatef(dx, 1.0f, 0.0f, 0.0f);
glRotatef(dy, 0.0f, 1.0f, 0.0f);
dx = dy = 0.0;
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glVertexPointer(3, GL_FLOAT, 0, squareVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
glEnableClientState(GL_COLOR_ARRAY);
glPointSize(4.0f);
glDrawArrays(GL_POINTS, 0, 8);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}[/c]
This adds a few points to make a cube, colors all the points black, and renders them as points. And rotates them around based on the dx, dy values.
3. Add these two methods to EAGL.m:
[c]- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
startPoint = [touch locationInView:self];
}
– (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
dx = point.y – startPoint.y;
dy = point.x – startPoint.x;
startPoint = point;
}[/c]
This just calculates dx, dy as the distance the touch moved.
Now this all works fine for starters. Swipe left/right or up/down and the cube moves correctly. But rotate it down 90 degrees and then swipe horizontally and the thing rotates in the z axis. I know, I know, it’s still rotating on the local y axis, but that’s not what I want. I just want it so that no matter what the orientation, when I drag up and down, it looks like I’m dragging it that way, and same for left/right. I’ve hit a brick wall with this one. Some combination of push/pop matrix? mult matrix? Do I need to use quaternions? Am I just screwed? My brain is locked up.
Oh, and yes, I know how glRotatef works – rotate n degrees around a vector defined by the last three params. I’ve tackled it that way too, to no avail. I think I’ve exhausted every bright idea I’ve had on this one.
Does this help (done with Papervision3D)?
http://pv3d.org/2008/11/29/free-rotation-using-quaternions/
John, so quaternions may be the solution, as I’ve expected…
Anyone know a OpenGL ES compatible quaternion class?
humm..
you dont’t have to change some calc using the accelerometer data?
bruno, who said anything about accelerator?
Finding the axis to rotate around based on how you’ve used your mouse is really the key. For example:
//the cross of mouse plane and the xy plane
var rotationAxis:Number3D = Number3D.cross(new Number3D(velocityX, velocityY, velocityZ), new Number3D(0, 0, 1));
Then you can just create a matrix from that axis and multiply your object’s matrix with that axis matrix.
Starting to add this in my framework… Not implemented yet but I’ll use this site!!
http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/quaternions/
No need to understand Quaternions.. just copy paste 😀
Keith,
You don’t really need quaternions for this problem, although they are much better for dealing with rotations, that isn’t the issue. The biggest breakthrough I have had with understanding matrices is to think about them as a change of basis – or change of a coordinate system. You are changing the direction that X, Y, and Z point when you do a rotation.
Further operations involving the matrix operate in the new coordinate space or the original coordinate space, depending on the handedness of your matrices. In the case of OpenGL, the new operations you are performing when the user touches the screen are *closer* in proximity to the drawing. In OpenGL you can imagine them as being applied *before* the previous matrix manipulations. This means that the rotations you are applying are in the object’s coordinate system, not the world coordinate system.
To fix your code, you simply need to perform these operations in world space rather than object space. This means you need to swap the order of the rotations. In this case, perhaps the easiest solution is just to copy the current matrix, clear it, apply your rotations, then apply the old matrix.
I tested this code and it has the desired behavior, I believe. Just replace your existing block of code that manipulates the GL_MODELVIEW matrix with this:
glMatrixMode(GL_MODELVIEW);
GLfloat m[16];
glGetFloatv(GL_MODELVIEW_MATRIX, m);
glLoadIdentity();
glRotatef(dx, 1.0f, 0.0f, 0.0f);
glRotatef(dy, 0.0f, 1.0f, 0.0f);
glMultMatrixf(m);
dx = dy = 0.0;
-Jonathan Branam
Jonathan. thanks a LOT. Although Andy Zupko beat you by about 20 minutes, with the exact same solution! 🙂