Tutorial how to draw 3D photo cube in Android Studio 1.4
Learn from this tutorial how to draw 3D cube with Open GL, rotate it, put six images, one on each side of it and play a sound on click in Android Studio version 1.4.
Create New Project with Application name: PhotoCube; Minimum SDK: API9 – Android 2.3 (Gingerbread).
Add 6 pictures from computer, in project folder res -> drawable: pic1.jpg, pic2.jpg, pic3.jpg, pic4.jpg, pic5.jpg, pic6.jpg.
Create New Java Class “MyGLActivity” and complete the code.
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class MyGLActivity extends Activity {
private GLSurfaceView glView; // use GLSurfaceView
// Call back when the activity is started, to initialize the view
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glView = new GLSurfaceView(this); // Allocate a GLSurfaceView
glView.setRenderer(new MyGLRenderer(this)); // Use a custom renderer
this.setContentView(glView); // This activity sets to GLSurfaceView
}
// Call back when the activity is going into the background
@Override
protected void onPause() {
super.onPause();
glView.onPause();
}
// Call back after onPause()
@Override
protected void onResume() {
super.onResume();
glView.onResume();
}
}
Create New Java Class “MyGLRenderer” and complete the code.
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
public class MyGLRenderer implements GLSurfaceView.Renderer {
private PhotoCube cube; // (NEW)
private static float angleCube = 0; // rotational angle in degree for cube
private static float speedCube = 0.5f; // rotational speed for cube
// Constructor
public MyGLRenderer(Context context) {
cube = new PhotoCube(context); // (NEW)
}
// Call back when the surface is first created or re-created.
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set color's clear-value to black
gl.glClearDepthf(1.0f); // Set depth's clear-value to farthest
gl.glEnable(GL10.GL_DEPTH_TEST); // Enables depth-buffer for hidden surface removal
gl.glDepthFunc(GL10.GL_LEQUAL); // The type of depth testing to do
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); // nice perspective view
gl.glShadeModel(GL10.GL_SMOOTH); // Enable smooth shading of color
gl.glDisable(GL10.GL_DITHER); // Disable dithering for better performance
// Setup Texture, each time the surface is created (NEW)
cube.loadTexture(gl); // Load images into textures (NEW)
gl.glEnable(GL10.GL_TEXTURE_2D); // Enable texture (NEW)
}
// Call back after onSurfaceCreated() or whenever the window's size changes
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if (height == 0) height = 1; // To prevent divide by zero
float aspect = (float)width / height;
// Set the viewport (display area) to cover the entire window
gl.glViewport(0, 0, width, height);
// Setup perspective projection, with aspect ratio matches viewport
gl.glMatrixMode(GL10.GL_PROJECTION); // Select projection matrix
gl.glLoadIdentity(); // Reset projection matrix
// Use perspective projection
GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.f);
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select model-view matrix
gl.glLoadIdentity(); // Reset
// You OpenGL|ES display re-sizing code here
// ......
}
// Call back to draw the current frame.
@Override
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.0f, 0.0f, -6.0f); // 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;
}
}
Create New Java Class “PhotoCube” and complete the code.
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.opengl.GLUtils;
/*
* A photo cube with 6 pictures (textures) on its 6 faces.
*/
public class PhotoCube {
private FloatBuffer vertexBuffer; // Vertex Buffer
private FloatBuffer texBuffer; // Texture Coords Buffer
private int numFaces = 6;
private int[] imageFileIDs = { // Image file IDs
R.drawable.pic1,
R.drawable.pic2,
R.drawable.pic3,
R.drawable.pic4,
R.drawable.pic5,
R.drawable.pic6
};
private int[] textureIDs = new int[numFaces];
private Bitmap[] bitmap = new Bitmap[numFaces];
private float cubeHalfSize = 1.2f;
// Constructor - Set up the vertex buffer
public PhotoCube(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 < numFaces; face++) {
bitmap[face] = BitmapFactory.decodeStream(
context.getResources().openRawResource(imageFileIDs[face]));
int imgWidth = bitmap[face].getWidth();
int imgHeight = bitmap[face].getHeight();
float faceWidth = 2.0f;
float faceHeight = 2.0f;
// Adjust for aspect ratio
if (imgWidth > 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
}
// Render the shape
public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CCW);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer);
// front
gl.glPushMatrix();
gl.glTranslatef(0f, 0f, cubeHalfSize);
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, 0f, 1f, 0f);
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[1]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
gl.glPopMatrix();
// back
gl.glPushMatrix();
gl.glRotatef(180.0f, 0f, 1f, 0f);
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[2]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
gl.glPopMatrix();
// right
gl.glPushMatrix();
gl.glRotatef(90.0f, 0f, 1f, 0f);
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[3]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
gl.glPopMatrix();
// top
gl.glPushMatrix();
gl.glRotatef(270.0f, 1f, 0f, 0f);
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[4]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
gl.glPopMatrix();
// bottom
gl.glPushMatrix();
gl.glRotatef(90.0f, 1f, 0f, 0f);
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[5]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
gl.glPopMatrix();
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
// Load images into 6 GL textures
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]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Build Texture from loaded bitmap for the currently-bind texture ID
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[face], 0);
bitmap[face].recycle();
}
}
}
Run the app in Emulator.
In next steps learn how to add sound on click and how to stop sound when exit app.
In “res” folder, Create New Directory: raw.
Add an mp3 with name “sound.mp3” in folder “raw“.
Complete code for play a sound on click in MyGLActivity.java file:
Runapp and see 3D cube that rotates and has on each side of it, one of the photos you’ve included in the project and on click it heard the sound also included in project.
Watchthe video tutorial to draw 3D photo cube in Android Studio version 1.4:Downloadfree appphoto-cube.apk from here and install it on your device to see 3D cube with 6 images on each side, rotating and playing a sound on touch.
For furtherquestionsleave a message.
Nice tutorial!
Haven’t tested it but maybe you could draw the sides like this in the PhotoCube:
float[] rotationArray = new float[] {270.0f, 180.0f, 90.0f, 270.0f, 90f};
for(int i = 0; i 0) {
gl.glRotatef(rotationArray[i – 1], (float) (i / 4), 1f – (float) (i / 4), 0f);
}
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[i]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, i * 4, 4);
gl.glPopMatrix();
}
If it works you could save a lot of lines of code.
Hello
Thanks for the tuto, it was really helpful.
I wanna know to add a sound for each side of the cube.
Cordially
nice tutorail…but the point is…how to know which face is touched?
Hey have you find out solution for which face is touched?
Hi, I know this is old but how could I do this so that it renders the shape with a colour rather than with an image?
Thanks
how to know which face is touched?
Nice tutorial!
Haven’t tested it but maybe you could draw the sides like this in the PhotoCube:
float[] rotationArray = new float[] {270.0f, 180.0f, 90.0f, 270.0f, 90f};
for(int i = 0; i 0) {
gl.glRotatef(rotationArray[i – 1], (float) (i / 4), 1f – (float) (i / 4), 0f);
}
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[i]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, i * 4, 4);
gl.glPopMatrix();
}
If it works you could save a lot of lines of code.
How to make png files transparent?