Android Custom view 3D Cube Effect Example
- 2021-12-13 17:12:30
- OfStack
Preface
I wrote an article about the effect of 3D before. With the help of sensor display, a small partner asked if it could be changed to gesture sliding operation (event distribution), so I wrote an article
Sensor related article link: Android 3D effect realization
1. Tips
In contrast to the common custom view, the inherited GLSurfaceView has only two constructors. It can be understood that there is no method to get custom attributes.
public TouchSurfaceView(Context context) {
super(context);
}
public TouchSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
2. Change the sensor to an event distribution mechanism
@Override
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;
mRenderer.mAngleX += dx * TOUCH_SCALE_FACTOR;
mRenderer.mAngleY += dy * TOUCH_SCALE_FACTOR;
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
Note that there is also a rolling ball event
@Override
public boolean onTrackballEvent(MotionEvent e) {
mRenderer.mAngleX += e.getX() * TRACKBALL_SCALE_FACTOR;
mRenderer.mAngleY += e.getY() * TRACKBALL_SCALE_FACTOR;
requestRender();
return true;
}
STEP 3 Use
mGLSurfaceView = new TouchSurfaceView(this);
setContentView(mGLSurfaceView);
mGLSurfaceView.requestFocus();
mGLSurfaceView.setFocusableInTouchMode(true);
Note that it should be handled in the corresponding life cycle
@Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
4. Source code
TouchSurfaceView.java
Except for the previous modifications, most of them are the same as the linked articles, except that the sensor is changed to event distribution. (The difficulties in the code are commented)
public class TouchSurfaceView extends GLSurfaceView {
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private final float TRACKBALL_SCALE_FACTOR = 36.0f;
private CubeRenderer mRenderer;
private float mPreviousX;
private float mPreviousY;
public TouchSurfaceView(Context context) {
super(context);
mRenderer = new CubeRenderer();
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
public TouchSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTrackballEvent(MotionEvent e) {
mRenderer.mAngleX += e.getX() * TRACKBALL_SCALE_FACTOR;
mRenderer.mAngleY += e.getY() * TRACKBALL_SCALE_FACTOR;
requestRender();
return true;
}
@Override
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;
mRenderer.mAngleX += dx * TOUCH_SCALE_FACTOR;
mRenderer.mAngleY += dy * TOUCH_SCALE_FACTOR;
requestRender();
}
mPreviousX = x;
mPreviousY = y;
return true;
}
private class CubeRenderer implements GLSurfaceView.Renderer {
private Cube mCube;
public float mAngleX;
public float mAngleY;
public CubeRenderer() {
mCube =new Cube();
}
public void onDrawFrame(GL10 gl) {
// | GL10.GL_DEPTH_BUFFER_BIT
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -3.0f);
gl.glRotatef(mAngleX, 0, 1, 0);
gl.glRotatef(mAngleY, 1, 0, 0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
mCube.draw(gl);
}
@Override
public void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig config) {
gl.glDisable(GL10.GL_DITHER);
gl.glClearColor(1,1,1,1);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
// Set the projection matrix. But you don't need to do it every time you draw. Usually, when the view is resized, you need to set 1 A new projection.
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
}
}
public class Cube {
//opengl In the coordinate system, the 3 Dimensional coordinates :
private FloatBuffer mVertexBuffer;
private FloatBuffer mColorBuffer;
private ByteBuffer mIndexBuffer;
public Cube() {
final float vertices[] = {
-1, -1, -1, 1, -1, -1,
1, 1, -1, -1, 1, -1,
-1, -1, 1, 1, -1, 1,
1, 1, 1, -1, 1, 1,
};
final float colors[] = {
0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 1,
};
final byte indices[] = {
0, 4, 5, 0, 5, 1,
1, 5, 6, 1, 6, 2,
2, 6, 7, 2, 7, 3,
3, 7, 4, 3, 4, 0,
4, 7, 6, 4, 6, 5,
3, 0, 1, 3, 1, 2
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
}
public void draw(GL10 gl) {
// Enable the server side GL Function.
gl.glEnable(GL10.GL_CULL_FACE);
// Defines the front and back sides of a polygon.
// Parameter :
//mode The direction of the front of the polygon. GL_CW And GL_CCW Allowed, with an initial value of GL_CCW .
gl.glFrontFace(GL10.GL_CW);
// Select constant or smooth shading mode.
//GL Primitives can be in constant or smooth shading mode, and the default value is smooth shading mode. When the primitive is rasterized, the color calculation of the insert vertex will be caused, and different colors will be evenly distributed to each pixel segment.
// Parameters:
//mode --Indicate 1 Symbolic constants to represent the shading technique to be used. The allowed values are GL_FLAT And GL_SMOOTH With an initial value of GL_SMOOTH .
gl.glShadeModel(GL10.GL_SMOOTH);
// Definition 1 Vertex coordinate matrix.
// Parameters:
//
//size --The coordinate dimension of each vertex must be 2, 3 Or 4 The initial value is 4 .
//
//type --Indicates the data type of each vertex coordinate. The allowed sign constants are GL_BYTE, GL_SHORT, GL_FIXED And GL_FLOAT With an initial value of GL_FLOAT .
//
//stride --Indicates the bit offset between consecutive vertices, if 0 The vertices are considered to be compact indentation matrices with initial values of 0 .
//
//pointer --Indicates the buffer for vertex coordinates, if null No buffer is set.
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
// Definition 1 A color matrix.
//size Indicates the number of elements per color, which must be 4 . type Indicates the data type of each color element, stride Indicates that the 1 Three colors to the bottom 1 The byte amplification of the allowed vertices, and the attribute values are squeezed into the simple matrix or stored in a separate matrix (simple matrix storage may be in 1 More efficient in some versions).
gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
// Rendering primitives from matrix data
// Independent vertex, normal, color and texture coordinate matrices can be specified in advance and can be called glDrawElements Method to use them to create sequence primitives.
gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new TouchSurfaceView(this);
setContentView(mGLSurfaceView);
mGLSurfaceView.requestFocus();
mGLSurfaceView.setFocusableInTouchMode(true);
}
@Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
}