I am trying to rotate the camera around the scene using touch (scroll):
public class SurfaceView extends GLSurfaceView
implements GestureDetector.OnGestureListener {
SceneRenderer renderer;
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
renderer.setMotion(distanceX, distanceY); // pass movement to render
return true;
}
}
The camera moves in the render as follows:
public class SceneRenderer implements GLSurfaceView.Renderer {
private float[] viewMatrix = new float[16];
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f,
0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
...
// movement of camera
public synchronized void setMotion(float xDistance, float yDistance) {
Matrix.rotateM(viewMatrix, 0, -xDistance * 0.1f, 0, 1, 0);
Matrix.rotateM(viewMatrix, 0, -yDistance * 0.1f, 1, 0, 0);
}
}
This works well at first. But then the camera starts to rotate not as expected. How can I make the camera move correctly relative to the center of the scene (object) using scrolling? Maybe someone solved a similar problem?
Thank you for any answer/comment!
Workaround solution:
private float kx = 0f;
private float ky = 0f;
private float radius = 3.0f;
private float x, y, z = 0f;
...
public synchronized void setMotion(float xDistance, float yDistance) {
kx = kx + xDistance * 0.001f;
x = (float) (radius * Math.sin(kx));
z = (float) (radius * Math.cos(kx));
ky = ky + yDistance * 0.001f;
y = (float) (radius * Math.sin(ky));
Matrix.setLookAtM(viewMatrix, 0, x, -y, z, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
But with this approach, the camera moves away from the scene when rotating along the Y-axis.
In matrix algebra, order does matter. Since incremental rotations interleave matrices and break order, it won't work as you expect. Thus you need to rebuild the view-matrix based on cumulative delta x/y on every setMotion() call so that rotations would always be applied in right order as follows.
public class SceneRenderer implements GLSurfaceView.Renderer {
private float[] viewMatrix = new float[16];
private float cumulativeX = 0.0f;
private float cumulativeY = 0.0f;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f,
0f, 0f, 0f, 0f, 1.0f, 0.0f);
cumulativeX = 0.0f;
cumulativeY = 0.0f;
}
...
// movement of camera
public synchronized void setMotion(float xDistance, float yDistance) {
cumulativeX += xDistance;
cumulativeY += yDistance;
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f,
0f, 0f, 0f, 0f, 1.0f, 0.0f);
//Matrix.rotateM(viewMatrix, 0, -cumulativeX * 0.1f, 0, 1, 0);
//Matrix.rotateM(viewMatrix, 0, -cumulativeY * 0.1f, 1, 0, 0);
Matrix.rotateM(viewMatrix, 0, -cumulativeY * 0.1f, 1, 0, 0);
Matrix.rotateM(viewMatrix, 0, -cumulativeX * 0.1f, 0, 1, 0);
}
}
It's surely possible to achieve the same only with setLookAtM. To keep view-point in constant distance, move the view-point in spherical coordinates. And adjust up-direction too.
public synchronized void setMotion(float xDistance, float yDistance) {
kx = kx + xDistance * 0.001f;
ky = ky + yDistance * 0.001f;
x = (float) (radius * Math.cos(ky) * Math.sin(kx));
z = (float) (radius * Math.cos(ky) * Math.cos(kx));
y = (float) (radius * Math.sin(ky));
float[] up =
{
(float) (Math.sin(ky) * Math.sin(kx)),
(float) (Math.cos(ky)),
(float) (Math.sin(ky) * Math.cos(kx)),
};
Matrix.setLookAtM(viewMatrix, 0,
x, -y, z,
0f, 0f, 0f,
up[0], up[1], up[2]
);
}