Rotating An Object With Touch Events

Rotating an object in 3D is a neat way of letting your users interact with the scene, but the math can be tricky to get right. In this article, I’ll take a look at a simple way to rotate an object based on the touch events, and how to work around the main drawback of this method.

Simple rotations.

This is the easiest way to rotate an object based on touch movement. Here is example pseudocode:

Matrix.setIdentity(modelMatrix);

... do other translations here ...

Matrix.rotateX(totalYMovement);
Matrix.rotateY(totalXMovement);

This is done every frame.

To rotate an object up or down, we rotate it around the X-axis, and to rotate an object left or right, we rotate it around the Y axis. We could also rotate an object around the Z axis if we wanted it to spin around.

How to make the rotation appear relative to the user’s point of view.

The main problem with the simple way of rotating is that the object is being rotated in relation to itself, instead of in relation to the user’s point of view. If you rotate left and right from a point of zero rotation, the cube will rotate as you expect, but what if you then rotate it up or down 180 degrees? Trying to rotate the cube left or right will now rotate it in the opposite direction!

One easy way to work around this problem is to keep a second matrix around that will store all of the accumulated rotations.

Here’s what we need to do:

  1. Every frame, calculate the delta between the last position of the pointer, and the current position of the pointer. This delta will be used to rotate our accumulated rotation matrix.
  2. Use this matrix to rotate the cube.

What this means is that drags left, right, up, and down will always move the cube in the direction that we expect.

Android Code

The code examples here are written for Android, but can easily be adapted to any platform running OpenGL ES. The code is based on Android Lesson Six: An Introduction to Texture Filtering.

In LessonSixGLSurfaceView.java, we declare a few member variables:

private float mPreviousX;
private float mPreviousY;

private float mDensity;

We will store the previous pointer position each frame, so that we can calculate the relative movement left, right, up, or down. We also store the screen density so that drags across the screen can move the object a consistent amount across devices, regardless of the pixel density.

Here’s how to get the pixel density:

final DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
density = displayMetrics.density

Then we add our touch event handler to our custom GLSurfaceView:

public boolean onTouchEvent(MotionEvent event)
{
	if (event != null)
	{
		float x = event.getX();
		float y = event.getY();

		if (event.getAction() == MotionEvent.ACTION_MOVE)
		{
			if (mRenderer != null)
			{
				float deltaX = (x - mPreviousX) / mDensity / 2f;
				float deltaY = (y - mPreviousY) / mDensity / 2f;

				mRenderer.mDeltaX += deltaX;
				mRenderer.mDeltaY += deltaY;
			}
		}

		mPreviousX = x;
		mPreviousY = y;

		return true;
	}
	else
	{
		return super.onTouchEvent(event);
	}
}

Every frame, we compare the current pointer position with the previous, and use that to calculate the delta offset. We then divide that delta offset by the pixel density and a slowing factor of 2.0f to get our final delta values. We apply those directly to the renderer to a couple of public variables that we have also declared as volatile, so that they can be updated between threads.

Remember, on Android, the OpenGL renderer runs in a different thread than the UI event handler thread, and there is a slim chance that the other thread fires in-between the X and Y variable assignments (there are also additional points of contention with the += syntax). I have left the code like this to bring up this point; as an exercise for the reader I leave it to you to add synchronized statements around the public variable read and write pairs instead of using volatile variables.

First, let’s add a couple of matrices and initialize them:

/** Store the accumulated rotation. */
private final float[] mAccumulatedRotation = new float[16];

/** Store the current rotation. */
private final float[] mCurrentRotation = new float[16];
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
{

    ...

    // Initialize the accumulated rotation matrix
     Matrix.setIdentityM(mAccumulatedRotation, 0);
}

Here’s what our matrix code looks like in the onDrawFrame method:

// Draw a cube.
// Translate the cube into the screen.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, 0.8f, -3.5f);

// Set a matrix that contains the current rotation.
Matrix.setIdentityM(mCurrentRotation, 0);
Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);
mDeltaX = 0.0f;
mDeltaY = 0.0f;

// Multiply the current rotation by the accumulated rotation, and then set the accumulated
// rotation to the result.
Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);

// Rotate the cube taking the overall rotation into account.
Matrix.multiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);
  1. First we translate the cube.
  2. Then we build a matrix that will contain the current amount of rotation, between this frame and the preceding frame.
  3. We then multiply this matrix with the accumulated rotation, and assign the accumulated rotation to the result. The accumulated rotation contains the result of all of our rotations since the beginning.
  4. Now that we’ve updated the accumulated rotation matrix with the most recent rotation, we finally rotate the cube by multiplying the model matrix with our rotation matrix, and then we set the model matrix to the result.

The above code might look a bit confusion due to the placement of the variables, so remember the definitions:

public static void multiplyMM (float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset)

public static void arraycopy (Object src, int srcPos, Object dst, int dstPos, int length)

Note the position of source and destination for each method call.

Trouble spots and pitfalls
  • The accumulated matrix should be set to identity once when initialized, and should not be reset to identity each frame.
  • Previous pointer positions must also be set on pointer down events, not only on pointer move events.
  • Watch the order of parameters, and also watch out for corrupting your matrices. Android’s Matrix.multiplyMM states that “the result element values are undefined if the result elements overlap either the lhs or rhs elements.” Use temporary matrices to avoid this problem.
WebGL examples

The example on the left uses the simplest method of rotating, while the example on the right uses the accumulated rotations matrix.

Your browser does not support the canvas tag. Your browser does not support the canvas tag.
Further exercises

What are the drawbacks of using a matrix to hold accumulated rotations and updating it every frame based on the movement delta for that frame? What other ways of rotation are there? Try experimenting, and see what else you can come up with!

  

About the book

Android is booming like never before, with millions of devices shipping every day. In OpenGL ES 2 for Android: A Quick-Start Guide, you’ll learn all about shaders and the OpenGL pipeline, and discover the power of OpenGL ES 2.0, which is much more feature-rich than its predecessor.

It’s never been a better time to learn how to create your own 3D games and live wallpapers. If you can program in Java and you have a creative vision that you’d like to share with the world, then this is the book for you.

Share

Author: Admin

Kevin is the author of OpenGL ES 2 for Android: A Quick-Start Guide. He also has extensive experience in Android development.

56 thoughts on “Rotating An Object With Touch Events”

  1. Very nice tutorial. I’m currently trying to adapt the code for the “global” rotation but I’m using OpenGL ES 1.0 and it doesn’t seem to work. Do you have any idea how the matrix code would look in OpenGL ES 1.0? The reason I’m not using OpenGL ES 2.0 is that I have an object loader for OpenGL ES 1.0.

    1. Hi Frank,

      I haven’t played with OpenGL ES 1.0 matrix stuff in a while, so I’m curious!

      I imagine the code might look something like this:

      // Draw a cube.
      // Translate the cube into the screen.
      glLoadIdentity(GL_MODELVIEW_MATRIX)
      glTranslatef(0.0f, 0.8f, -3.5f);

      // Set a matrix that contains the current rotation.
      Matrix.setIdentityM(mCurrentRotation, 0);
      Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
      Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);
      mDeltaX = 0.0f;
      mDeltaY = 0.0f;

      // Multiply the current rotation by the accumulated rotation, and then set the accumulated
      // rotation to the result.
      Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
      System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);

      // Rotate the cube taking the overall rotation into account.
      glMultMatrix(mAccumulatedRotation, 0);

      Since the matrices used by android.opengl.Matrix are in column-major order, and the matrices used by OpenGL are also in column major order, this seems like it should work. Let me know!

  2. I wasn’t expecting such a quick reply. I only had time today to test it. It works:

    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();
    gl.glTranslatef(0, 0, -3.0f);

    Matrix.setIdentityM(mCurrentRotation, 0);
    Matrix.rotateM(mCurrentRotation, 0, mAngleX, 0.0f, 1.0f, 0.0f);
    Matrix.rotateM(mCurrentRotation, 0, mAngleY, 1.0f, 0.0f, 0.0f);
    mAngleX=0.0f;
    mAngleY=0.0f;

    Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
    System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);

    // Rotate the cube taking the overall rotation into account.
    gl.glMultMatrixf(mAccumulatedRotation, 0);

    My problem was that I didn’t initialize the accumulation rotation matrix in the OnSurfaceCreated method so it wasn’t diplaying anything. Thanks a lot for your input.

    1. Excellent point, Frank, I’ve updated the article to include the initialization step as well, as others might run into the same issue. Thanks for letting me know! 🙂

  3. First of all I must say that this is a very nice tutorial even for a beginner on OpenGL like me.

    What I would like to ask is how I could change the location of an object (e.g. a triangle on lesson one). I have implement the onTouchEvent method on the GLSurfaceView class and I have added a .translateM call to the onDrawFrame of the renderer, but I cannot figure out how to translate the values of getX() and getY() to the values needed by the .translateM method.

    Thank you in advance for your time.

    1. Hi Kostas,

      I haven’t tried that myself, but I think you will need to use this method:

      gluUnProject(float winX, float winY, float winZ, float[] model, int modelOffset, float[] project, int projectOffset, int[] view, int viewOffset, float[] obj, int objOffset)
      Map window coordinates to object coordinates.

      This will help you reconvert your touch coordinates back into your object coordinates, which should give you the right X and Y values to use for translation. Let me know if this helps.

      1. The method can be found in the GLU class; here’s the doc on the parameters:

        Map window coordinates to object coordinates. gluUnProject maps the specified window coordinates into object coordinates using model, proj, and view. The result is stored in obj.
        Note that you can use the OES_matrix_get extension, if present, to get the current modelView and projection matrices.
        Parameters

        winX window coordinates X
        winY window coordinates Y
        winZ window coordinates Z
        model the current modelview matrix
        modelOffset the offset into the model array where the modelview maxtrix data starts.
        project the current projection matrix
        projectOffset the offset into the project array where the project matrix data starts.
        view the current view, {x, y, width, height}
        viewOffset the offset into the view array where the view vector data starts.
        obj the output vector {objX, objY, objZ}, that returns the computed object coordinates.
        objOffset the offset into the obj array where the obj vector data starts.
        Returns

        A return value of GL10.GL_TRUE indicates success, a return value of GL10.GL_FALSE indicates failure.

  4. I’m working on the same issue for an iOS app with the vuforia SDK.
    Can anybody help me figure out how to implement this in their ImageTargets sample?

    The rendering part goes like this:

    modelViewMatrix = QCAR::Tool::convertPose2GLMatrix(trackable->getPose());

    ShaderUtils::scalePoseMatrix(kBowlScale, kBowlScale, kBowlScale,&modelViewMatrix.data[0]);
    //ShaderUtils::rotatePoseMatrix(floatAngleY, 0.0f, 0.0f, 1.0f, &modelViewMatrix.data[0]);
    ShaderUtils::rotatePoseMatrix(floatAngleY, 0.0f, 1.0f, 0.0f, &modelViewMatrix.data[0]);
    ShaderUtils::rotatePoseMatrix(floatAngleX, 1.0f, 0.0f, 0.0f, &modelViewMatrix.data[0]);
    ShaderUtils::multiplyMatrix(&qUtils.projectionMatrix.data[0], &modelViewMatrix.data[0], &modelViewProjection.data[0]);

    glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*) &objectVertices[0]);
    glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*) &objectNormals[0]);
    glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*) &objectTexCoords[0]);

    glBindTexture(GL_TEXTURE_2D, [[textures objectAtIndex:2] textureID]);
    glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (GLfloat*)&modelViewProjection.data[0] );
    //glDrawElements(GL_TRIANGLES, NUM_OBJECT_INDEX, GL_UNSIGNED_SHORT, (const GLvoid*) &objectIndices[0]);

  5. Thanks for the help!
    It worked flawlessly! Until I had 3 cubes stacked…they all seem to rotate through their center. Do you know how I can avoid this? Thanks!

    1. It’s probably a small issue. A cube is not rotating for fun. mModelMatrix perhaps rotate around the variable angleInDegrees, you have forgotten to delete while copying and pasting;).
      If you’re still using Ios, I’ll remind you that there are OS specific differences in touch recognization.

        1. Thanks for answering so fast!
          Don’t worry about me using iOS, I’ve changed everything I needed to change for it to work. Not only that, I’m working with Vuforia and OpenGL ES 2.0.
          I’ve managed to work this out by -as suggested- changing the order of the operations.
          Since translation and scaling were being applied to the modelview matrix and the rotation was being applied to another matrix, it was just a matter of translating and scaling AFTER the rotation matrix was multiplied with the modelview one.
          Now everything works as it should!
          Thanks every.body for the help, and I hope others reading this answer might find it helpful.

          Best
          Juan
          PS: i promise to come back every now and then in case something interesting shows up!

  6. I am using opengles 1.0 and have made a spinning cube but that’s an application ,how to make it a live wallpaper tell me the changes that i need to make in manifest file and renderer file(if any)
    thanks

  7. Hi,
    I want to rotate my cube on touch event.I don’t know how it possible.Is it done in renderer class.I don’t know.Sorry

    public void onDrawFrame(GL10 gl) {
    // Clear color and depth buffers
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    // —– Render the Cube —–
    gl.glLoadIdentity(); // Reset the model-view matrix
    gl.glTranslatef(-0.5f, -2.5f, 2.0f);
    gl.glScalef(0.1f, 0.1f, 0.1f); // Translate into the screen
    gl.glRotatef(angleCube, 0.15f, 1.0f, 0.3f); // Rotate
    cube.draw(gl);

    // Update the rotational angle after each refresh.
    angleCube += speedCube;
    }

    1. You mean like to keep it spinning? I know the NeHe guys do that in their tutorials, you could check out how they do it here: http://nehe.gamedev.net/tutorial/lessons_06__10/17010/

      They do work with keyboard though so it’s a bit different. Your pseudo-code seems like it would work — maybe you can take this sample and try capturing the delta between the previous touch and the current touch, and use that as a “speed” to increment the angle on each frame?

  8. Hai,
    I want to make game which is like rubic’s cube.I had created four cubes which consisting of different images.I used four render classes and four surface view to doing it.Instead of that within one surface view i want to create four cube and it want to spin on touch event.How can we detect the object in the surface view???Please help!!!

    1. Hi Salu,

      You can use basic collision detection — I have not written up about that here but this might help you out: http://stackoverflow.com/search?q=collision+detection+opengl

      Basically when the user touches the screen you check which cube was touched. Heck, if you only have four touchable cubes maybe you can do this with the coordinates alone — if the user touched the top left part of the screen, move the top-left cube when they drag their finger.

  9. hai,
    I want to know the object co-ordinates of a 3D cube on touch event.Here I am getting the screen coordinates.
    public boolean onTouchEvent(MotionEvent e)
    {
    float x = e.getX();
    float y = e.getY();
    Log.d(“Mytag”, “Coords: x=” + e.getX() + “,y=” + e.getY());
    return true;
    }
    Please help me…

  10. Hey Salu,

    That is correct. If you step back for a moment you will realize that the phone can only give you the coords of the user’s touch on the screen. After all, the screen is nothing mor than a 2D piece of glass (sortof..).
    You will need to translate real screen coords to model coords. After an “OnDraw” event, you know exactly where your object is. The info is in the Matrix.
    Also, check “LessonSevenActivity” (I’m messing with that), in the ontouch event you can see that the previous X and Y are saved and are being used to “remember” where the object was.

    Further, searching google with “opengl object picking” will get you a few usefull answers.

    Cheers

  11. I forgot, DO check the source of ModelViewer I mentioned above. In ModelViewerActivity there’s a Handler() and an OnTouch() in which you can see an alternative solution. There too, the previous coords are saved.
    This is inevitable, all movers/touchers/translaters/rotators I have seen do this…

  12. Hi Salu,

    I am a very experienced software developer, but know little about 3D development. What I do know, however, is that on one side you create this OpenGL 3D world and on the other side there is this dumb 2D PC/Phone real world with it’s user events. Neither world does know the other world exist:

    OpenGL provides the 3D “manipulation and viewing” routines, the PC/Phone the “user interaction” routines. And simply said, the “3D handlers” (move/translate/rotate etc) have to react on what the device’s “eventhandlers” (touch, fling, tap etc) tell what the user is currently doing.

    It may be a little abstract, but the only way YOU can unfluence both worlds is to REMEMBER what happened/is happening in both worlds and act on that info.

    In the case of a MOVE-action:

    The user touches and holds finger down -> remember x,y -> user moves finger -> check distance between old x,y and new x,y -> if finger moved, translate new screen coords to 3D distance save new x,y to old and render again. Keep doing this until users lifts finger.

    Hope this helps a bit more….

    Cheers, Rene

  13. Hai rene,
    Thankyou for the prompt response and well explanation.Actually I am a beginner in software development and this opengl stuffs.So that i am not able to do that.I created four 3D cube with this vertices.When I touch on the cube i want to know the vertices of the object and thereby spin that cube.Please help me!! sorry for the inconvinence.
    private float[] vertices = {
    -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
    };

  14. Hi Salu,

    For the time being I can’t help you further with that as I am currently also struggling to understand the same subject of object picking.

    I can give you a piece of code (in object pascal) which I am trying to understand. The point is that OpenGL, next to GL_PROJECTION and GL_RENDER, also has a GL_SELECT constant you can pas the glRenderMode() function. This effectively means that you also have to create a “selection” matrix/buffer.

    As soon as I have a simple working android app I will get back to you with the code. For now the Pascal version (close enough to Java)(only the relevant code):

    The program shows a matrix of 3×3 colored squares on the screen and tells you which square you picked with the mouse.

    procedure OnDraw();
    var i : GLint;
    begin
    glClear(GL_COLOR_BUFFER_BIT);// Clear the colour buffer

    DrawSquares(GL_RENDER);// Render the scene
    glFlush();// ( Force the buffer to draw or send a network packet of commands in a networked system)
    end;

    procedure DrawSquares(mode : GLenum);
    var i, j : GLuint;
    begin

    for i := 0 to 2 do // For each column
    begin
    if mode = GL_SELECT then glLoadName(i+1); // Load a name
    for j := 0 to 2 do // For each Row
    begin
    if mode = GL_SELECT then
    begin
    glPushName(j+1); // Add the name to the object
    end;

    glColor3f ( i/3.0, j/3.0,board[i][j]/3.0); // Set the colour for the square
    glRecti (i, j, i+1, j+1); // Draw the square
    if mode = GL_SELECT then glPopName; // “Close” the named object
    end;
    end;
    end;

    procedure OnMouse;
    begin
    case MouseButton of
    1: // Left Mouse Button
    begin
    PickSquares();// Pick any squares underneath the mouse pointer
    MouseButton := 0; // Cancel our mouse click (To use this procedure as a mouse down event remove this line)
    end;
    2: // Right Mouse Button
    begin
    MouseButton := 0; // Cancel our mouse click (To use this procedure as a mouse down event remove this line)
    end;
    3: // Middle Mouse Button
    begin
    MouseButton := 0; // Cancel our mouse click (To use this procedure as a mouse down event remove this line)
    end;
    end;
    end;

    procedure ProcessHits(hits : GLint; buffer : array of GLuint);
    var i, j, ii, jj : Cardinal;
    names : GLuint;
    ptr : PGLuint;
    begin
    SetLength(Output,1);
    Output[High(Output)] := ‘Hits = ‘ + IntToStr(hits);
    ptr := @PGLuint(buffer[0]); // Create a pointer to the buffer
    for i := 0 to hits – 1 do // For each hit
    begin
    names := ptr^;
    SetLength(Output,Length(Output)+1);
    Output[High(Output)] := ‘ Number of names for hit ‘ + IntToStr(i+1) + ‘ = ‘ + IntToStr(names); Inc(ptr);
    SetLength(Output,Length(Output)+1);
    Output[High(Output)] := ‘ z1 is ‘ + FloatToStr(RoundTo(ptr^/$7fffffff,-2)); Inc(ptr);
    SetLength(Output,Length(Output)+1);
    Output[High(Output)] := ‘ z2 is ‘ + FloatToStr(RoundTo(ptr^/$7fffffff,-2)); Inc(ptr);
    SetLength(Output,Length(Output)+1);
    Output[High(Output)] := ‘ the name is ‘;
    for j := 0 to names – 1 do // for each name
    begin
    Output[High(Output)] := Output[High(Output)] + IntToStr(ptr^) + ‘.’;
    if (j = 0) then // set row and column */
    ii := ptr^ -1
    else if (j = 1) then
    jj := ptr^ – 1;
    Inc(ptr);

    end;
    board[ii][jj] := (board[ii][jj] + 1) mod 3;
    end;

    end;

    procedure PickSquares();
    var selectBuf : array [0..BUFSIZE -1] of GLuint;
    hits : GLint;
    viewport : array [0..3] of GLint;
    x, y : GLint;
    begin

    x := Xcoord; // Cet the current mouse coordinates
    y := Ycoord;

    glGetIntegerv (GL_VIEWPORT, @viewport); // Get the current viewport

    glSelectBuffer (BUFSIZE, @selectBuf); // Assign the selection buffer
    glRenderMode(GL_SELECT); // Change the render mode to Selction Mode

    glInitNames();// Init Names for Selection
    glPushName(0);// Add the default name

    glMatrixMode (GL_PROJECTION);// Switch to Projection matrix/stack
    glPushMatrix();// Load a new projection matrix onto the stack

    glLoadIdentity ();
    // Create 5×5 pixel picking region near cursor location
    gluPickMatrix(x, (viewport[3] – y), 5.0, 5.0, @viewport);
    gluOrtho2D (0.0, 3.0, 0.0, 3.0);
    DrawSquares (GL_SELECT);// Draw squares in selection mode
    glPopMatrix();
    glFlush();// Force the render

    hits := glRenderMode(GL_RENDER);// Change back to Render mode and return any hits
    processHits(hits, selectBuf);// Process all hits in the selection buffer
    end;

  15. Hi Salu,

    Some more reading on Nehe Productions, check lesson 32 -> Picking, Alpha Blending, Alpha Testing, Sorting.
    It’s a simple picking game and a more elaborate example of my previous post. At the bottom of the lesson you will find a link to a Java port (LWJGL version).

    Cheers (again!)

  16. Hi Salu,

    Here is a nice piece of code to help you on your way, it’s part of the Android SDK:

    com.example.android.apis.graphics.kube

    This is a package in the ApiDemos examples showing a Rubik cube!

  17. Hai Rene,
    Thankyou very much for your kind consideration.I go through all those stuffs but cannot able to do my requirement and still working for it…

  18. Hai rene,
    I am also stuck in getting the coordinates.Actually I’m using textures so that it is difficult to checking the pixel color with glReadPixels.In object picking MatrixTakling and MatrixGrabber but its not supporting in openGl.so i don’t getting the how to solve the problem.So please help me…
    Here i am sending my classes.
    cube class
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.nio.FloatBuffer;
    import javax.microedition.khronos.opengles.GL10;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.opengl.GLU;
    import android.opengl.GLUtils;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;

    public class Cube1 {
    private FloatBuffer vertexBuffer; // Buffer for vertex-array
    private FloatBuffer texBuffer; // Buffer for texture-coords-array (NEW)

    private float[] vertices = {
    -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
    };

    float[] texCoords = { // Texture coords for the above face (NEW)
    0.0f, 1.0f, // A. left-bottom (NEW)
    1.0f, 1.0f, // B. right-bottom (NEW)
    0.0f, 0.0f, // C. left-top (NEW)
    1.0f, 0.0f // D. right-top (NEW)
    };
    private int numFaces = 6;
    private int[] imageFileIDs = { // Image file IDs
    R.drawable.teddy_1,
    R.drawable.teddy_2,
    R.drawable.teddy_3,
    R.drawable.teddy_4,
    R.drawable.teddy_5,
    R.drawable.teddy_6
    };
    private int[] textureIDs = new int[numFaces];
    private Bitmap[] bitmap = new Bitmap[numFaces];

    private float cubeHalfSize = 1.2f;
    private boolean touched;
    private int x,y;

    public Cube1(Context context) {
    // Allocate vertex buffer. An float has 4 bytes
    ByteBuffer vbb = ByteBuffer.allocateDirect(12 * 4 * numFaces);
    vbb.order(ByteOrder.nativeOrder());
    vertexBuffer = vbb.asFloatBuffer();

    // Read images. Find the aspect ratio and adjust the vertices accordingly.
    for (int face = 0; face imgHeight) {
    faceHeight = faceHeight * imgHeight / imgWidth;
    } else {
    faceWidth = faceWidth * imgWidth / imgHeight;
    }
    float faceLeft = -faceWidth / 2;
    float faceRight = -faceLeft;
    float faceTop = faceHeight / 2;
    float faceBottom = -faceTop;

    // Define the vertices for this face
    float[] vertices = {
    faceLeft, faceBottom, 0.0f, // 0. left-bottom-front
    faceRight, faceBottom, 0.0f, // 1. right-bottom-front
    faceLeft, faceTop, 0.0f, // 2. left-top-front
    faceRight, faceTop, 0.0f, // 3. right-top-front
    };
    vertexBuffer.put(vertices); // Populate
    }
    vertexBuffer.position(0); // Rewind

    // Allocate texture buffer. An float has 4 bytes. Repeat for 6 faces.
    float[] texCoords = {
    0.0f, 1.0f, // A. left-bottom
    1.0f, 1.0f, // B. right-bottom
    0.0f, 0.0f, // C. left-top
    1.0f, 0.0f // D. right-top
    };
    ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4 * numFaces);
    tbb.order(ByteOrder.nativeOrder());
    texBuffer = tbb.asFloatBuffer();
    for (int face = 0; face < numFaces; face++) {
    texBuffer.put(texCoords);
    }
    texBuffer.position(0); // Rewind
    }

    // Draw the shape
    public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW); // Front face in counter-clockwise orientation
    gl.glEnable(GL10.GL_CULL_FACE); // Enable cull face
    gl.glCullFace(GL10.GL_BACK); // Cull the back face (don't display)

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // Enable texture-coords-array (NEW)
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer); // Define texture-coords buffer (NEW)

    // front
    gl.glPushMatrix();
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // left
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[1]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // back
    gl.glPushMatrix();
    gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[2]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // right
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[3]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // top
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[4]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // bottom
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[5]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisable(GL10.GL_CULL_FACE);
    }

    public void loadTexture(GL10 gl) {
    gl.glGenTextures(6, textureIDs, 0); // Generate texture-ID array for 6 IDs

    // Generate OpenGL texture images
    for (int face = 0; face < numFaces; face++) {
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[face]);
    // Build Texture from loaded bitmap for the currently-bind texture ID
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[face], 0);
    bitmap[face].recycle();

    }
    }

    }
    Render class
    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.nio.FloatBuffer;
    import javax.microedition.khronos.opengles.GL10;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.opengl.GLU;
    import android.opengl.GLUtils;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;

    public class Cube1 {
    private FloatBuffer vertexBuffer; // Buffer for vertex-array
    private FloatBuffer texBuffer; // Buffer for texture-coords-array (NEW)

    private float[] vertices = {
    -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
    };

    float[] texCoords = { // Texture coords for the above face (NEW)
    0.0f, 1.0f, // A. left-bottom (NEW)
    1.0f, 1.0f, // B. right-bottom (NEW)
    0.0f, 0.0f, // C. left-top (NEW)
    1.0f, 0.0f // D. right-top (NEW)
    };
    private int numFaces = 6;
    private int[] imageFileIDs = { // Image file IDs
    R.drawable.teddy_1,
    R.drawable.teddy_2,
    R.drawable.teddy_3,
    R.drawable.teddy_4,
    R.drawable.teddy_5,
    R.drawable.teddy_6
    };
    private int[] textureIDs = new int[numFaces];
    private Bitmap[] bitmap = new Bitmap[numFaces];

    private float cubeHalfSize = 1.2f;
    private boolean touched;
    private int x,y;

    public Cube1(Context context) {
    // Allocate vertex buffer. An float has 4 bytes
    ByteBuffer vbb = ByteBuffer.allocateDirect(12 * 4 * numFaces);
    vbb.order(ByteOrder.nativeOrder());
    vertexBuffer = vbb.asFloatBuffer();

    // Read images. Find the aspect ratio and adjust the vertices accordingly.
    for (int face = 0; face imgHeight) {
    faceHeight = faceHeight * imgHeight / imgWidth;
    } else {
    faceWidth = faceWidth * imgWidth / imgHeight;
    }
    float faceLeft = -faceWidth / 2;
    float faceRight = -faceLeft;
    float faceTop = faceHeight / 2;
    float faceBottom = -faceTop;

    // Define the vertices for this face
    float[] vertices = {
    faceLeft, faceBottom, 0.0f, // 0. left-bottom-front
    faceRight, faceBottom, 0.0f, // 1. right-bottom-front
    faceLeft, faceTop, 0.0f, // 2. left-top-front
    faceRight, faceTop, 0.0f, // 3. right-top-front
    };
    vertexBuffer.put(vertices); // Populate
    }
    vertexBuffer.position(0); // Rewind

    // Allocate texture buffer. An float has 4 bytes. Repeat for 6 faces.
    float[] texCoords = {
    0.0f, 1.0f, // A. left-bottom
    1.0f, 1.0f, // B. right-bottom
    0.0f, 0.0f, // C. left-top
    1.0f, 0.0f // D. right-top
    };
    ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4 * numFaces);
    tbb.order(ByteOrder.nativeOrder());
    texBuffer = tbb.asFloatBuffer();
    for (int face = 0; face < numFaces; face++) {
    texBuffer.put(texCoords);
    }
    texBuffer.position(0); // Rewind
    }

    // Draw the shape
    public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW); // Front face in counter-clockwise orientation
    gl.glEnable(GL10.GL_CULL_FACE); // Enable cull face
    gl.glCullFace(GL10.GL_BACK); // Cull the back face (don't display)

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // Enable texture-coords-array (NEW)
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer); // Define texture-coords buffer (NEW)

    // front
    gl.glPushMatrix();
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // left
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[1]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // back
    gl.glPushMatrix();
    gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[2]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // right
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[3]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // top
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[4]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // bottom
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[5]);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisable(GL10.GL_CULL_FACE);
    }

    public void loadTexture(GL10 gl) {
    gl.glGenTextures(6, textureIDs, 0); // Generate texture-ID array for 6 IDs

    // Generate OpenGL texture images
    for (int face = 0; face < numFaces; face++) {
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[face]);
    // Build Texture from loaded bitmap for the currently-bind texture ID
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[face], 0);
    bitmap[face].recycle();

    }
    }

    }
    Surfaceview
    package com.tcs.ignite;

    import javax.microedition.khronos.opengles.GL10;

    import android.app.Activity;
    import android.content.Context;
    import android.opengl.GLSurfaceView;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.Display;
    import android.view.MotionEvent;

    public class Surfaceview1 extends GLSurfaceView{
    private static final float TOUCH_SCALE_FACTOR = 1.0f;
    public static Object matrixTrackingGL;
    GL10 gl;
    Renderer1 mrenderer = new Renderer1(getContext());
    private float mPreviousX;
    private float mPreviousY;
    private float mDensity;
    int windowwidth;
    private Cube1 mcube1;
    private Cube1 mcube2;
    private Cube1 mcube3;
    private Cube1 mcube4;

    // final DisplayMetrics displayMetrics = new DisplayMetrics();
    //Display display = getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    public Surfaceview1(Context context,GL10 gl)
    {
    super(context);
    setRenderer(mrenderer);
    this.gl=gl;
    //mDensity = metrics.density;
    setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    /* public boolean onTouchEvent(MotionEvent e)
    {
    float x = e.getX();
    float y = e.getY();
    Log.d("Mytag", "Coords: x=" + x + ",y=" + y);
    return true;
    }*/
    }

  19. Hi Salu,

    I can already see signs of “spaghettification” in your code. I’m not making fun of you or try to be belittling, but I think you should really step back for a while and try to understand what you need to know to develop a 3D program. Pulling random code together and try to make it work will work eventually, but you will probably be left with such a big heap of spaghetti it would even make an Italian choke.

    Why don’t we team up for a while and see where it leads? You want this cube rotation/selection mechanism, so do I. No need to reinvent things twice. In the mean time we both have fun learning and programming while others too can profit from our findings. What do you think?

    I have already found fairly good structured code in the SDK examples and written a first lesson (will follow shortly) on how to create a new project with it. The second lesson will discuss splitting the code in structured parts and how to make it recyclable. Of course I will also discuss making a cube “touchable”. Lesson three will be about thinking and envisioning 3D environments linked to selecting and picking 3D objects. I guess lesson four will be about texturing the darn thing.

    Probably not entirely what you were hoping for, but while I DO think in 3D and software development, I don’t do well with OpenGL and Java (just yet).

    Let me know what you think.
    Cheers!

  20. Hi rene,
    I am very happy to your kind reply.I am agree with your views.I am very new in this software development even in this programming language.This is my first assignment.So that I become stuck.But i will do that thing within a day.I am not making fun of you:(
    Thankyou very much for your thoughtful gift.And waiting for your tutorial:)

    Thanking You,
    Salu

  21. Hey everyone,

    It has been a long time since I last messed with OpenGL (must be a decade ago) and most certainly not with Android/Java. While I searched the net for

    examples and code I might be able to use, I came across learnopengles.com. Going over the various tutorials, I realized I had forgotten a lot about

    OpenGL and needed to start all over again. Trying to help Salu I decided that I could help others and myself as well. After all, this IS a learning site and I HAVE

    been writing software since, I dunno, 1987. Currently I am both a software engineer and a photographer with a long interest in 3D software (as a user).

    So, why not give back some experience and have fun with OpenGL and Java…

    This first tutorial is not too interesting, just a recipe for creating a working OpenGL project with an Android shell. It is mostly about creating a new Android

    project and reusing the “Rubik Cube” example from the Android SDK ApiDemos (API 17 samples). While I am extending and changing the code, I will write

    further tutorials on why I changed things and how I did it.

    For now, let’s get busy!!

    (1) Clean start
    In your IDE: close ALL open projects

    (2) Creating a new project: Kube

    Menu: [New][Other][Android…Android Application Project]

    “Create Application” – window:
    => Application Name: Kube
    => Project Name: Kube
    => Package Name: com.example.android.apis.graphics.kube (the same package name as the “Kube” activity in the ApiDemos example). Ignore the

    warning sign!

    => Select the API that works for you (I choose 7, 17 and 17 (Android 2.1, 4.2 and 4.2) this depends on your test phone and the

    SDKs you downloaded).

    – Button

    => Un/check “Create custom launcher icon” -> your choice
    => Check “Create activity”=>
    => Uncheck “Mark this project as library”
    => Check “Create project in workspace”
    => Uncheck “Working sets”

    – Button

    => Check “Create Activity”
    => Select “Fullscreen Activity”

    – Button

    => Activity name: Kube
    => Layout name: kube

    – Button

    (3) Just checking
    => Run the project you just created, so you know what it does… Nothing too interesting, but at least you’ve got something runnable.

    (4) Rename
    => In the IDE goto package: com.example.android.apis.graphics.kube of the new project and rename (refactor) Kube.java to

    KubeOrg.java. In the next step we will copy some java source into this package and we don’t want to overwrite this file.

    (5) Copy SDK sample code

    In the graphics/OpenGL section of the SDK example “ApiDemos” you can find an animated and rotating Rubik Cube demo. The code of this demo is rather

    well constructed and offers almost all of the code we need to construct our own version.

    => Go to the SDK examples folder and find the Kube demo, mine resides in:
    C:\Program Files (x86)\Android\android-sdk\samples\android-17\ApiDemos\src\com\example\android\apis\graphics\kube
    (So, don’t copy the folder, only the files!)

    => Select all files in this folder and paste them into the new project in de IDE workspace, in my case:
    F:\Development\workspace\Kube\src\com\example\android\apis\graphics\kube
    If your IDE auto-refreshes you will see the pasted files in your project, otherwise press to refresh. Ignore any errors, no need to correct them at this

    stage. Your project will probably even compile and run, but won’t see any OpenGL action, just the same output as in step (3).

    (6) Merging the code
    This can get messy if you’re not careful enough. What we are going to do is to copy code from KubeOrg.java into the pasted Kube.java and

    create a running version of the Kube demo on top of the generated Fullscreen layout. We could have chosen to remove all the generated code, but this code

    offers a nice shell for our demo which can be reused at a later stage.

    Files affected in this merge:
    src.com.example.android.apis.graphics.kube.Kube.java, res.layout.Kube.xml and AndroidManifest.xml. No more, no less. We are not

    even going to open any of the other files so we won’t get distracted with irrelevant (for now) code. During the merge you will get errors here and there,

    simply ignore them.

    Merging Kube.java and KubeOrg.java
    => The imports: Hit + in both sources (“expand all” so you can see what you’re doing) and copy all imports of KubeOrg into

    Kube and remove any duplicate imports from Kube
    => Now it’s time to pay extra attention as we will copy at lot of code blindly: First hit + in both sources (“collapse all”, I told

    you, we’re gonna drive blind…).
    => Go to Kube and find the statements:

    public void animate() {
    GLSurfaceView mView;

    and insert a few blank lines between them, just to make a bit of room.

    => Go to KubeOrg and select everything from statement:

    protected void onPostCreate(Bundle savedInstanceState) {

    up to AND including the last } of method:
    private void delayedHide(int delayMillis) {

    Copy the lines and paste them into the blank lines of the previous step. Time to save <Kube.java, while still ignoring errors….
    Believe it or not, most of the heavy work has now been done with three more steps to go.

    (7) Editting Kube.xml
    The easiest way to show our Rubik Kube in the existing Activity Layout is to create the possibility to add the glSurfaceView (mView in the code) as a

    child to the layout. Inserting a new FrameLayout will do that job nicely.

    => Open layout Kube.xml in text edit mode, find the tag and insert a blank line or 2.
    => Copy the below code and insert it into the blank lines. Make sure it is at the same level as the !!!

    No more editting to do in this file.

    (8) Editting AndroidManifest.xml
    Your manifest shoul look like the code below. Make sure that are no references to KubeOrg, otherwise change those to Kube.

    The manifest:

    (9) Final touches to Kube.java
    The last thing we need to do is properly connecting our Rubik Cube (glSurface)View to our Activity Layout.

    => Go to Kube.java again and first hit + so everthing gets collapsed again.
    => Then only expand
    => Find setContentView(mView); and overwrite this statement with the following 3 lines of code:


    setContentView(R.layout.kube);
    final FrameLayout mBucketFrame = (FrameLayout) findViewById(R.id.bucketFrame);
    mBucketFrame.addView(mView);

    That’s it! Now you’ve got (theoretically) an error free compilable piece of code. So, hit save.

    Final step: getting this show on the road
    => “Clean” your project: [Project][Clean…] and build it. This will clean out any existing errors, maybe close and reopen your project. In my version there are

    no “warning” or other errors, so yours should have none too.

    => [Run as][1 Android Application]

    So far the first part of my beginners’ series in which we created a fairly solid base for further OpenGL development. In the next part we are going to have a

    closer look at the code, disect it and figure out how we need to change the code to do what we want it to do. I will also explain why I specifically choose

    Kube and will discuss the need of structured application design.

    If you are going to fiddle with the code yourself, don’t forget to make a backup copy of the Kube project first…

  22. Sigh…
    You need to put in the TAG marker’s yourself


    // FrameLayout
    // android:id="@+id/bucketFrame"
    // android:layout_width="match_parent"
    // android:layout_height="wrap_content"
    // android:layout_gravity="top|center_horizontal"
    // tools:ignore="UselessParent"
    // /FrameLayout

  23. Hai Rene,
    I am very happy to see your reply .But unfortunately I can’t get that.My Bad luck.Actually this forum become full of our comments.I think it also no fare.So it’s ok rene.Thankyou very much for your valuable thoughts and great help.
    Thanking you,
    Salu

  24. Salu,
    Don’t give up just yet. I get my own little corner on this site shortly. We can discuss there,
    no worries.

    I will make a ZIP You can download…

    1. I would like to join you. Some time ago I developed an app to visualise a rotating Rubik’s cube and have spent a very long time to resolve the perspective rotating in a realistic way. The solution is amazingly simple 😉

      1. Hi Drasko,

        Just today I received a notice someone had reacted, sorry for the delay, but it is great you want to join. It is my aim to help out beginning developers and learn stuff myself at th same time. I hope we can end up with a structured and easy to use Android developer toolbox.

        At the moment I am preparing the first few subjects for discussion. Feel free to ask for subjects you find difficult as a developer, doesn’t have to be Android/Java related…

        Cheers, Rene

    1. This is an implementation of Android’s renderer interface (android.opengl.GLSurfaceView.Renderer) that handles surface created & surface draw events. Things will make more sense if you check out the source on GitHub. Someday, I’m gonna come back to all of these posts and make the source excerpts clearer.

    2. In simple terms: if mGlSurfaceView is a piece of paper/canvas, mRenderer is the painter who creates a nice painting. And, as OpenGL(ES)loops, mRenderer is called each loop. This way you can create a 3D “movie”, by simply changing camera positions, object rotation, texture changes, etc. each time it loops.

  25. Hi, I would like to ask why we are not using queueEvent in onTouchEvent here, meanwhile we used queueEvent in article “Listening to Android touchEvent and acting on them”?

    1. queueEvent is usually better, but if you’re just updating 1 or 2 floats, you could also get away with just declaring them as volatile variables.

    1. Probably, but it’s not something that I’ve looked into all that much. You might be able to find some resources & answers on stackoverflow.com

  26. Hey,
    I really love this tutorial and I successfully tried it. But now I’m using very complex models with thousands of faces. So I decided to take blender and export them as .obj… With the help of the library min3d I can parse these formats and it automatically make the model. I successfully added rotation but I cannot add the model to the Matrix… Could you help?
    Here is my Code:

    import android.opengl.Matrix;
    import android.os.Bundle;
    import android.util.DisplayMetrics;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;

    import min3d.core.Object3dContainer;
    import min3d.core.RendererActivity;
    import min3d.parser.IParser;
    import min3d.parser.Parser;
    import min3d.vos.Light;

    /**
    * Created by Jonas on 11/21/2014.
    */
    public class OpenGLResultActivityMin3D extends RendererActivity {
    private Object3dContainer objModel;

    public float mDeltaX = 0.0f;
    public float mDeltaY = 0.0f;
    public float mDeltax = 0.0f;
    public float mDeltay = 0.0f;
    private float mPreviousX;
    private float mPreviousY;
    private float mSize = 1.0f;
    private float mDensity;

    private ScaleGestureDetector SGD;

    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SGD = new ScaleGestureDetector(this,new ScaleListener());
    final DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    mDensity = displayMetrics.density;
    }

    @Override
    public void initScene() {

    scene.lights().add(new Light());

    IParser parser = Parser.createParser(Parser.Type.OBJ, getResources(), "com.apgtigers.bookscann:raw/cubemash_obj", true);
    parser.parse();

    objModel = parser.getParsedObject();
    objModel.scale().x = objModel.scale().y = objModel.scale().z = mSize;
    scene.addChild(objModel);
    Matrix.setIdentityM(mAccumulatedRotation, 0);
    }

    @Override
    public void updateScene() {
    objModel.rotation().y+=mDeltaX;
    objModel.rotation().x+=mDeltaY;
    mDeltaX = 0;
    mDeltaY = 0;
    }

    public boolean onTouchEvent(MotionEvent event){
    SGD.onTouchEvent(event);
    if (event != null)
    {
    float x = event.getX();
    float y = event.getY();

    if (event.getAction() == MotionEvent.ACTION_MOVE)
    {
    float deltaX = (x - mPreviousX) / mDensity / 2f;
    float deltaY = (y - mPreviousY) / mDensity / 2f;

    mDeltaX = deltaX;
    mDeltaY = deltaY;
    }

    mPreviousX = x;
    mPreviousY = y;

    return true;
    }
    else
    {
    return super.onTouchEvent(event);
    }
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
    float scale = detector.getScaleFactor();
    mSize *= scale;
    objModel.scale().x = objModel.scale().y = objModel.scale().z = mSize;
    return true;
    }
    }
    }

    You could see, in the TouchEvent Function I also added a posibility to Zoom in. But everything what is important is:
    the initScene(){...} and updateScene(){...} funktion
    same as:
    the onSurfaceCreated(){...} and onDrawFrame(){...} funktion

    Jonas

  27. Hi, this is a really great method for rotating. Thank you so much for this tutorial. It’s working in my program but I have some problems while using it.
    1. My screen doesn’t show anything until I delete “Matrix.translateM(mModelMatrix, 0, 0.0f, 0.8f, -3.5f);”. Why?
    2. I still confuse about getting the pixel density. How can I write your code into my program? It seems can’t be implemented.

    Thank you so much!

  28. Hi,
    So one behavior I’m seeing is that: with accumulated rotations, the cube becomes wonky and sort of tilts on it’s axis sideways and can only be straightened by turning it around, rotating it on another axis and turning it back around again. This does not happen with simple rotations (but that has it’s own glaring problem).
    How do I deal with this?

  29. With the accumulated rotations, how would i limit the rotation on one of the axes? Say i only want to be able to tilt the cube up and down a certain degree but allow it rotate completely left and right. Since I only have an mDeltaY, I’m not sure how I would do this

Leave a Reply to Frank Cancel reply

Your email address will not be published. Required fields are marked *