I have a sphere created using the Rajawali3D OpenGL ES library, with the camera placed inside the sphere at (0, 0, 0). The user can rotate this sphere on swipe.
I want to get the 3D co-ordinates of the spot the user touches on the sphere
Currently I am using the Unproject method to get the near and far planes, calculate vector direction and find the intersection point in the sphere.Here is the code
mNearPos4 = new double[4];
mFarPos4 = new double[4];
mNearPos = new Vector3();
mFarPos = new Vector3();
mNewPos = new Vector3();
// near plane
GLU.gluUnProject(x, getViewportHeight() - y, 0,
mViewMatrix.getDoubleValues(), 0,
mProjectionMatrix.getDoubleValues(), 0, mViewport, 0,
mNearPos4, 0);
// far plane
GLU.gluUnProject(x, getViewportHeight() - y, 1.0f,
mViewMatrix.getDoubleValues(), 0,
mProjectionMatrix.getDoubleValues(), 0, mViewport, 0,
mFarPos4, 0);
// transform 4D to 3D
mNearPos.setAll(mNearPos4[0] / mNearPos4[3], mNearPos4[1]
/ mNearPos4[3], mNearPos4[2] / mNearPos4[3]);
mFarPos.setAll(mFarPos4[0] / mFarPos4[3],
mFarPos4[1] / mFarPos4[3], mFarPos4[2] / mFarPos4[3]);
Vector3 dir = new Vector3(mFarPos.x - mNearPos.x, mFarPos.y - mNearPos.y, mFarPos.z - mNearPos.z);
dir.normalize();
// compute the intersection with the sphere centered at (0, 0, 0)
double a = Math.pow(dir.x, 2) + Math.pow(dir.y, 2) + Math.pow(dir.z, 2);
double b = 2 * (dir.x * (mNearPos.x) + dir.y * (mNearPos.y) + dir.z * (mNearPos.z));
double c = Math.pow(mNearPos.x, 2) + Math.pow(mNearPos.y, 2) + Math.pow(mNearPos.z, 2) - radSquare;
double D = Math.pow(b, 2) - 4 * a * c;
// need only smaller root since the camera is within
// mNewPos is used as the position of the point
mNewPos.setAll((mNearPos.x + dir.x * t), (mNearPos.y + dir.y * t), mNearPos.z);
The problem is that i am getting the same range of co-ordinates when i rotate the sphere. For example, If i get the co-ordinates (a, b, c) on one side of the sphere, i get the same on the opposite side of the sphere.
How do i solve this problem and get the correct co-ordinates for all sides?
I am using Rajawali 1.0.232 snapshot
SOLVED: The problem was that i was saving the camera's projection and view matrices in variables.
So when a call was made to unproject() to convert the 2D point to 3D, it was taking the old values and hence the point was not getting plotted correctly.
So a solution would be to get the camera's view and projection matrices on demand without caching them.
mViewport = new int[]{0, 0, getViewportWidth(), getViewportHeight()};
Vector3 position3D = new Vector3();
mapToSphere(event.getX(), event.getY(), position3D, mViewport,
mCam.getViewMatrix(), mCam.getProjectionMatrix());
where the mapSphere() function does the unproject function as follows
public static void mapToSphere(float x, float y, Vector3 position, int[] viewport,
Matrix4 viewMatrix, Matrix4 projectionMatrix) {
//please refer for explanation in case of openGL
//http://myweb.lmu.edu/dondi/share/cg/unproject-explained.pdf
double[] tempPosition = new double[4];
GLU.gluUnProject(x, viewport[3] - y, 0.7f,
viewMatrix.getDoubleValues(), 0,
projectionMatrix.getDoubleValues(), 0, viewport, 0,
tempPosition, 0);
// the co-ordinates are stored in tempPosition as 4d (x, y, z, w)
// convert to 3D by dividing x, y, z by w
// the minus (-) for the z co-ordinate worked for me
position.setAll(tempPosition[0] / tempPosition[3], tempPosition[1]
/ tempPosition[3], -tempPosition[2] / tempPosition[3]);
}
I'm using OpenGL ES 2.0 for Android. I'm translating and rotating a model using the touch screen. My translations are only in the (x, y) plane, and my rotation is only about the z-axis. Imagine looking directly down at a map on a table and moving to various coordinates on the map, and being able to rotate the map around the point you are looking at.
The problem is that after I rotate, my subsequent translations are to longer matched to the motions of the pointer on the screen, the axes are different.
Everything I've tried gives me one of two behaviors One is equivalent to:
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, Xposition, Yposition, 0.0f);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
This allows me to translate as expected (up/down on the screen moves the model up and down, left/right moves model left and right), regardless of rotation. The problem is that the rotation is about the center of the object, and I need the rotation to be about the point that I am looking at, which is different than the center of the object.
The other behavior I can get is equivalent to:
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
Matrix.translateM(mModelMatrix, 0, Xposition, Yposition, 0.0f);
This gives me the rotation that I want, always about the point I am looking at. The problem is that after a rotation, the translations are wrong. Left/right on the screen translates the object at a different angle, along the rotated axes.
I need some way to get both behaviors at the same time. It needs to rotate about the point I am looking at, and translate in the direction that the finger moves on the screen.
Is this even possible? Am I basically trying to reconcile quantum mechanics with Newtonian physics, and doomed to failure?
I don't want to list all of the tricks that I've tried, because I want to consider all possibilities with a fresh perspective.
EDIT:
I'm still completely stuck on this.
I have an object that starts at (0, 0, 0) in world coordinates. My view is looking down the z-axis at the object, and I want to limit translation to the x/y plane. I also want to rotate the object about the z-axis only. The center of the rotation must always be the center of the screen.
I am controlling the translation with the touch screen so I need the object to the same way the finger moves, regardless of how it it rotated.
As soon as I rotate, then all of my translations start happening on the rotated coordinate system, which means the object does not move with the pointer on the screen. I've tried to do a second translation as Hugh Fisher recommended, but I can't figure out how to calculate the second translation. Is there another way?
I had the same problem. However i was using C# with OpenGL (SharpGL) and using a rotation Matrix.
Translation after rotation was required to keep rotation point at center of screen.
As a CAD type application does.
Problem was mouse translations are not always parallel to screen after rotations.
I found a fix here.
(Xposition, Yposition) = (Xposition, Yposition) + mRotation.transposed() * (XIncr, YIncr)
or
NewTranslationPosition = oldTranslationPosition + rotationMatrix.Transposed * UserTranslationIncrement.
Many many thanks to reto.koradi (at OpenGL)!
So I roughly coded in 3D like:
double gXposition = 0;
double gYposition = 0;
double gZposition = 0;
double gXincr = 0;
double gYincr = 0;
double gZincr = 0;
float[] rotMatrix = new float[16]; //Rotational matrix
private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{
OpenGL gl = openGLControl.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.LoadIdentity();
gl.MultMatrix(rotMatrix); //This is my rotation, using a rotation matrix
gl.Translate(gXposition, gYposition, gZposition); //translate second to keep rotation at center of screen
DrawCube(ref gl);
}
private void buttonTransLeft_Click(object sender, EventArgs e)
{
double tX = -0.1;
double tY = 0;
double tZ = 0;
TransposeRotMatrixFindPoint(ref tX, ref tY, ref tZ);
gXposition = gXposition + tX;
gYposition = gYposition + tY;
gZposition = gZposition + tZ;
}
private void buttonTransRight_Click(object sender, EventArgs e)
{
double tX = 0.1;
double tY = 0;
double tZ = 0;
TransposeRotMatrixFindPoint(ref tX, ref tY, ref tZ);
gXposition = gXposition + tX;
gYposition = gYposition + tY;
gZposition = gZposition + tZ;
}
public void TransposeRotMatrixFindPoint(ref double x, ref double y, ref double z)
{
//Multiply [x,y,z] by Transpose Rotation matrix to generate new [x,y,z]
double Xt = 0; //Tempoary variable
double Yt = 0; //Tempoary variable
Xt = (x * rotMatrix[0, 0]) + (y * rotMatrix[0, 1]) + (z * rotMatrix[0, 2]);
Yt = (x * rotMatrix[1, 0]) + (y * rotMatrix[1, 1]) + (z * rotMatrix[1, 2]);
z = (x * rotMatrix[2, 0]) + (y * rotMatrix[2, 1]) + (z * rotMatrix[2, 2]);
//or try this
//Xt = (x * rotMatrix[0, 0]) + (y * rotMatrix[1, 0]) + (z * rotMatrix[2, 0]);
//Yt = (x * rotMatrix[0, 1]) + (y * rotMatrix[1, 1]) + (z * rotMatrix[2, 1]);
//z = (x * rotMatrix[0, 2]) + (y * rotMatrix[1, 2]) + (z * rotMatrix[2, 2]);
x = Xt;
y = Yt;
}
This is an old post, but I'm posting the solution that worked best for me for posterity.
The solution was to keep a separate model matrix that accumulates transformations as they occur, and multiply each transformation by this matrix in the onDrawFrame() method.
//Initialize the model matrix for the current transformation
Matrix.setIdentityM(mModelMatrixCurrent, 0);
//Apply the current transformations
Matrix.translateM(mModelMatrixCurrent, 0, cameraX, cameraY, cameraZ);
Matrix.rotateM(mModelMatrixCurrent, 0, mAngle, 0.0f, 0.0f, 1.0f);
//Multiply the accumulated transformations by the current transformations
Matrix.multiplyMM(mTempMatrix, 0, mModelMatrixCurrent, 0, mModelMatrixAccumulated, 0);
System.arraycopy(mTempMatrix, 0, mModelMatrixAccumulated, 0, 16);
Then the accumulated matrix is used to position the object.
Here's how I think about translations and rotations: you are not moving the object, you are moving the origin of the coordinate system. Thinking about it this way, you'll need an extra translation on your first behaviour.
The finger motion is a translation that should be aligned to the XY axes of the screen, so as you've worked out, should be done before rotation. Then your rotation takes place, which rotates the coordinate system of the object around that point. If you want the object to be drawn somewhere else relative to that point, you'll need to do another translation first to move the origin there.
So I think your final sequence should be something like
translate(dx, dy) ; rotate(A) ; translate(cx, cy) ; draw()
where cx and cy are the distance between the centre of the map and the point being looked at. (Might simplify to -dx, -dy)
Hope this helps.
You should use your first method, although mathematically the second one makes more sense. There is a difference between how OpenGL and Android store matrices. They are arrays after all, but are the first 4 values a row or a column?
That's why it is "backwards". Check this for more info, or read about row major vs column major matrix operations.
I noticed that the first method "backwards" works as intended.
Mathematically:
Suppose you want to rotate around a point (x1, y1, z1). The origin of your object is (Ox, Oy, Oz).
Set Origin:
Matrix.setIdentityM(mModelMatrix, 0);
Then move the point you want to rotate about to the origin:
Matrix.translateM(mModelMatrix, 0, -x1, -y1, -z1);
Then
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
Then move it back:
Matrix.translateM(mModelMatrix, 0, x1, y1, z1);
Then move it where you want:
Matrix.translateM(mModelMatrix, 0, x, y, z);
However, on the backward thinking, you do it in reverse order.
Try:
Set Origin:
Matrix.setIdentityM(mModelMatrix, 0);
Then do stuff in reverse order:
Matrix.translateM(mModelMatrix, 0, x, y, z);
Matrix.translateM(mModelMatrix, 0, x1, y1, z1);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
Matrix.translateM(mModelMatrix, 0, -x1, -y1, -z1);
I hope this helps.
Edit
I may have miss understood the question: Here is what worked for me is:
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, x1, y1, z1);
Matrix.rotateM(mModelMatrix, 0, rotationAngle, 0.0f, 0.0f, 1.0f);
Matrix.Multiply(mViewProjection, 0, mProjection, 0, mCameraView, 0); //something like this, but do you have the right order?
In my Shader, i have mViewProjection * mModelMatrix * a_Position;
Are you using the vertex shader to do the final multiplication?
Try to do static translate/ rotate (with constant values) instead of controlling the translation with the touch screen. If it works fine, probably you have a bug somewhere else
I m woring on an android opengl 1.1 2d game with a top view on a vehicule and a camera zoom relative to the vehicule speed. When the speed increases the camera zoom out to offer the player a best road visibility.
I have litte trouble finding the exact way to detect if a sprite is visible or not regarding his position and the current camera zoom.
Important precision, all of my game's objects are on the same z coord. I use 3d just for camera effect. (that's why I do not need frustrum complicated calculations)
here is a sample of my GLSurfaceView.Renderer class
public static float fov_degrees = 45f;
public static float fov_radians = fov_degrees / 180 * (float) Math.PI;
public static float aspect; //1.15572 on my device
public static float camZ; //927 on my device
#Override
public void onSurfaceChanged(GL10 gl, int x, int y) {
aspect = (float) x / (float) y;
camZ = y / 2 / (float) Math.tan(fov_radians / 2);
Camera.MINIDECAL = y / 4; // minimum cam zoom out (192 on my device)
if (x == 0) { // Prevent A Divide By Zero By
x = 1; // Making Height Equal One
}
gl.glViewport(0, 0, x, y); // Reset The Current Viewport
gl.glMatrixMode(GL10.GL_PROJECTION); // Select The Projection Matrix
gl.glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
GLU.gluPerspective(gl, fov_degrees, aspect , camZ / 10, camZ * 10);
GLU.gluLookAt(gl, 0, 0, camZ, 0, 0, 0, 0, 1, 0); // move camera back
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select The Modelview Matrix
gl.glLoadIdentity(); // Reset The Modelview Matrix
when I draw any camera relative object I use this translation method :
gl.glTranslatef(position.x - camera.centerPosition.x , position.y -camera.centerPosition.y , - camera.zDecal);
Eveything is displayed fine, the problem comes from my physic thread when he checks if an object is visible or not:
public static boolean isElementVisible(Element element) {
xDelta = (float) ((camera.zDecal + GameRenderer.camZ) * GameRenderer.aspect * Math.atan(GameRenderer.fov_radians));
yDelta = (float) ((camera.zDecal + GameRenderer.camZ)* Math.atan(GameRenderer.fov_radians));
//(xDelta and yDelta are in reallity updated only ones a frame or when camera zoom change)
Camera camera = ObjectRegistry.INSTANCE.camera;
float xMin = camera.centerPosition.x - xDelta/2;
float xMax = camera.centerPosition.x + xDelta/2;
float yMin = camera.centerPosition.y - yDelta/2;
float yMax = camera.centerPosition.y + yDelta/2;
//xMin and yMin are supposed to be the lower bounds x and y of the visible plan
// same for yMax and xMax
// then i just check that my sprite is visible on this rectangle.
Vector2 phD = element.getDimToTestIfVisibleOnScreen();
int sizeXd2 = (int) phD.x / 2;
int sizeYd2 = (int) phD.y / 2;
return (element.position.x + sizeXd2 > xMin)
&& (element.position.x - sizeXd2 < xMax)
&& (element.position.y - sizeYd2 < yMax)
&& (element.position.y + sizeYd2 > yMin);
}
Unfortunately the object were disapearing too soon and appearing to late so i manuelly added some zoom out on the camera for test purpose.
I did some manual test and found that by adding approx 260 to the camera z index while calculating xDelta and yDelta it, was good.
So the line is now :
xDelta = (float) ((camera.zDecal + GameRenderer.camZ + 260) * GameRenderer.aspect * Math.atan(GameRenderer.fov_radians));
yDelta = (float) ((camera.zDecal + GameRenderer.camZ + 260)* Math.atan(GameRenderer.fov_radians));
Because it's a hack and the magic number may not work on every device I would like to understand what i missed. I guess there is something in that "260" magic number that comes from the fov or ration width/height and that could be set as a formula parameter for pixel perfect detection.
Any guess ?
My guess is that you should be using Math.tan(GameRenderer.fov_radians) instead of Math.atan(GameRenderer.fov_radians).
Reasoning:
If you used a camera with 90 degree fov, then xDelta and yDelta should be infinitely large, right? Since the camera would have to view the entire infinite plane.
tan(pi/2) is infinite (and negative infinity). atan(pi/2) is merely 1.00388...
tan(pi/4) is 1, compared to atan(pi/4) of 0.66577...
I'm new to OpenGL-ES on Android, and I have a question regarding generating a mesh for a texture that represents a circle.
Desired mesh on the left, and my Texture on the right:
How do i generate the mesh on the left? and then render it in the following way:
triangle1{Centerpoint, WhitePoint, nextpointclockwise(say #1)},
triangle2{Centerpoint, point#1, nextpointclockwise(say #2)},
triangle3{Centerpoint, point#2, nextpointclockwise(say #3)}
This will create the vertices and texture coordinates of a 1 radius circle(but i didnt actually tried it so it may not work :) )
Then you can draw them as TRIANGLE_FAN
public void MakeCircle2d(int points)
{
float[] verts=new float[points*2+2];
float[] txtcord=new float[points*2+2];
verts[0]=0;
verts[1]=0;
txtcord[0]=0.5f;
txtcord[1]=0.5f;
int c=2;
for (int i = 0; i < points; i++)
{
float fi = 2*Math.PI*i/points;
float x = Math.cos(fi + Math.PI) ;
float y = Math.sin(fi + Math.PI) ;
verts[c]=x;
verts[c+1]=y;
txtcord[c]=x*0.5f+0.5f;//scale the circle to 0.5f radius and plus 0.5f because we want the center of the circle tex cordinates to be at 0.5f,0.5f
txtcord[c+1]=y*0.5f+0.5f;
c+=2;
}
}
Thanks to your code I made it work on a 2D OGLes1.1 project on iOS.
Works pretty good, A bit messy but might be good for learners.
Thanks.
-(void) MakeCircle2d:(int)points pos:(CGPoint)pos rad:(GLfloat)rad texName:(GLuint)texName
{
float verts[(points*2)+2];
float txtcord[(points*2)+2];
verts[0]=pos.x;
verts[1]=pos.y;
txtcord[0]=0.5f;
txtcord[1]=0.5f;
int c=2;
for (int i = 0; i < points; i++)
{
float fi = 2.0*M_PI*((float)i/(float)(points-2.0));
float x = sinf(fi + M_PI) ;
float y = cosf(fi + M_PI) ;
verts[c]=pos.x+(x*rad);
verts[c+1]=pos.y+(y*rad);
txtcord[c]=x*0.5f+0.5f;//scale the circle to 0.5f radius and plus 0.5f because we want the center of the circle tex cordinates to be at 0.5f,0.5f
txtcord[c+1]=y*0.5f+0.5f;
c+=2;
}
glColor4f(1.0,1.0,1.0,1.0);
//glColor4f(0.0,0.0,0.0, 0.0);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glPushMatrix();
glBindTexture(GL_TEXTURE_2D, texName);
glTexCoordPointer(2, GL_FLOAT, 0, txtcord);
glVertexPointer(2, GL_FLOAT, 0, verts);
glDrawArrays(GL_TRIANGLE_FAN, 0, points);
//glBindTexture(GL_TEXTURE_2D, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
}
here's my problem: I'm making a pool game in android and I want to make the camera rotate freely around the center of the table. The thing is that when I stop my movement it looks like the glulookat resets itself because I only see the same thing ver and over again. If somebody knows a way on how to solve this or another way to do what I watn to do i'd be REALLY appreciated
This is my renderer class:
public void onDrawFrame(GL10 gl) {
// Redraw background color
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// Set GL_MODELVIEW transformation mode
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity(); // reset the matrix to its default state
// Screen position to angle conversion
theta = (float) ((360.0/screenHeight)*mMoveY*3.0); //3.0 rotations possible
phi = (float) ((360.0/screenWidth)*mMoveX*3.0);
// Spherical to Cartesian conversion.
// Degrees to radians conversion factor 0.0174532
eyeX = (float) (r * Math.sin(theta/**0.0174532*/) * Math.sin(phi/**0.0174532*/));
eyeY = (float) (r * Math.cos(theta/**0.0174532*/));
eyeZ = (float) (r * Math.sin(theta/**0.0174532*/) * Math.cos(phi/**0.0174532*/));
// Reduce theta slightly to obtain another point on the same longitude line on the sphere.
eyeXtemp = (float) (r * Math.sin(theta/**0.0174532*/-dt) * Math.sin(phi/**0.0174532*/));
eyeYtemp = (float) (r * Math.cos(theta/**0.0174532*/-dt));
eyeZtemp = (float) (r * Math.sin(theta/**0.0174532*/-dt) * Math.cos(phi/**0.0174532*/));
// Connect these two points to obtain the camera's up vector.
upX=eyeXtemp-eyeX;
upY=eyeYtemp-eyeY;
upZ=eyeZtemp-eyeZ;
// Set the view point
GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, 0,0,0, upX, upY, upZ);
and here's my onTouch method in my activity class:
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
// reverse direction of rotation above the mid-line
/*if (y > getHeight() / 2) {
dx = dx * -1 ;
}
// reverse direction of rotation to left of the mid-line
if (x < getWidth() / 2) {
dy = dy * -1 ;
}*/
mRenderer.mMoveX = dx * TOUCH_SCALE_FACTOR/100;
mRenderer.mMoveY = dy * TOUCH_SCALE_FACTOR/100;
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
When you do the math, you're using mMoveX and mMoveY. It looks like you should be adding those to other, more persistent x/y variables instead.
Something like:
mCamX += mMoveX;
mCamY += mMoveY;
theta = (float) ((360.0/screenHeight)*mCamY*3.0); //3.0 rotations possible
phi = (float) ((360.0/screenWidth)*mCamX*3.0);