package org.crazyit.opengl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;

/**
 * Description:
 * <br/>site: <a href="http://www.crazyit.org">crazyit.org</a>
 * <br/>Copyright (C), 2001-2014, Yeeku.H.Lee
 * <br/>This program is protected by copyright laws.
 * <br/>Program Name:
 * <br/>Date:
 * @author  Yeeku.H.Lee kongyeeku@163.com
 * @version  1.0
 */
public class Texture3D extends Activity
	implements OnGestureListener
{
	// ¶¨ÒåÐý×ª½Ç¶È
	private float anglex = 0f;
	private float angley = 0f;
	static final float ROTATE_FACTOR = 60;
	// ¶¨ÒåÊÖÊÆ¼ì²âÆ÷ÊµÀý
	GestureDetector detector;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		// ´´½¨Ò»¸öGLSurfaceView£¬ÓÃÓÚÏÔÊ¾OpenGL»æÖÆµÄÍ¼ÐÎ
		GLSurfaceView glView = new GLSurfaceView(this);
		// ´´½¨GLSurfaceViewµÄÄÚÈÝ»æÖÆÆ÷
		MyRenderer myRender = new MyRenderer(this);
		// ÎªGLSurfaceViewÉèÖÃ»æÖÆÆ÷
		glView.setRenderer(myRender);
		setContentView(glView);
		// ´´½¨ÊÖÊÆ¼ì²âÆ÷
		detector = new GestureDetector(this , this);
	}

	@Override
	public boolean onTouchEvent(MotionEvent me)
	{
		// ½«¸ÃActivityÉÏµÄ´¥ÅöÊÂ¼þ½»¸øGestureDetector´¦Àí
		return detector.onTouchEvent(me);
	}

	@Override
	public boolean onFling(MotionEvent event1, MotionEvent event2,
		float velocityX, float velocityY)
	{
		velocityX = velocityX > 2000 ? 2000 : velocityX;
		velocityX = velocityX < -2000 ? -2000 : velocityX;
		velocityY = velocityY > 2000 ? 2000 : velocityY;
		velocityY = velocityY < -2000 ? -2000 : velocityY;
		// ¸ù¾ÝºáÏòÉÏµÄËÙ¶È¼ÆËãÑØYÖáÐý×ªµÄ½Ç¶È
		angley += velocityX * ROTATE_FACTOR / 4000;
		// ¸ù¾Ý×ÝÏòÉÏµÄËÙ¶È¼ÆËãÑØXÖáÐý×ªµÄ½Ç¶È
		anglex += velocityY * ROTATE_FACTOR / 4000;
		return true;
	}

	@Override
	public boolean onDown(MotionEvent arg0)
	{
		return false;
	}

	@Override
	public void onLongPress(MotionEvent event)
	{
	}

	@Override
	public boolean onScroll(MotionEvent event1, MotionEvent event2,
		float distanceX, float distanceY)
	{
		return false;
	}

	@Override
	public void onShowPress(MotionEvent event)
	{
	}

	@Override
	public boolean onSingleTapUp(MotionEvent event)
	{
		return false;
	}

	public class MyRenderer implements Renderer
	{
		// Á¢·½ÌåµÄ¶¥µã×ù±ê£¨Ò»¹²ÊÇ36¸ö¶¥µã£¬×é³É12¸öÈý½ÇÐÎ£©
		private float[] cubeVertices = { -0.6f, -0.6f, -0.6f, -0.6f, 0.6f,
			-0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f, -0.6f,
			-0.6f, -0.6f, -0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
			0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f,
			0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f,
			0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f, 0.6f,
			-0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f,
			0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f,
			-0.6f, 0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f, 0.6f,
			0.6f, 0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f,
			-0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
			-0.6f, 0.6f, -0.6f, };
		// ¶¨ÒåÁ¢·½ÌåËùÐèÒªµÄ6¸öÃæ£¨Ò»¹²ÊÇ12¸öÈý½ÇÐÎËùÐèµÄ¶¥µã£©
		private byte[] cubeFacets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
			13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
			30, 31, 32, 33, 34, 35, };
		// ¶¨ÒåÎÆÀíÌùÍ¼µÄ×ù±êÊý¾Ý
		private float[] cubeTextures = { 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
			1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
			0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
			0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
			0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f };

		private Context context;
		private FloatBuffer cubeVerticesBuffer;
		private ByteBuffer cubeFacetsBuffer;
		private FloatBuffer cubeTexturesBuffer;
		// ¶¨Òå±¾³ÌÐòËùÊ¹ÓÃµÄÎÆÀí
		private int texture;

		public MyRenderer(Context main)
		{
			this.context = main;
			// ½«Á¢·½ÌåµÄ¶¥µãÎ»ÖÃÊý¾ÝÊý×é°ü×°³ÉFloatBuffer;
			cubeVerticesBuffer = floatBufferUtil(cubeVertices);
			// ½«Á¢·½ÌåµÄ6¸öÃæ£¨12¸öÈý½ÇÐÎ£©µÄÊý×é°ü×°³ÉByteBuffer
			cubeFacetsBuffer = ByteBuffer.wrap(cubeFacets);
			// ½«Á¢·½ÌåµÄÎÆÀíÌùÍ¼µÄ×ù±êÊý¾Ý°ü×°³ÉFloatBuffer
			cubeTexturesBuffer = floatBufferUtil(cubeTextures);
		}

		@Override
		public void onSurfaceCreated(GL10 gl, EGLConfig config)
		{
			// ¹Ø±Õ¿¹¶¶¶¯
			gl.glDisable(GL10.GL_DITHER);
			// ÉèÖÃÏµÍ³¶ÔÍ¸ÊÓ½øÐÐÐÞÕý
			gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
			gl.glClearColor(0, 0, 0, 0);
			// ÉèÖÃÒõÓ°Æ½»¬Ä£Ê½
			gl.glShadeModel(GL10.GL_SMOOTH);
			// ÆôÓÃÉî¶È²âÊÔ
			gl.glEnable(GL10.GL_DEPTH_TEST);
			// ÉèÖÃÉî¶È²âÊÔµÄÀàÐÍ
			gl.glDepthFunc(GL10.GL_LEQUAL);
			// ÆôÓÃ2DÎÆÀíÌùÍ¼
			gl.glEnable(GL10.GL_TEXTURE_2D);
			// ×°ÔØÎÆÀí
			loadTexture(gl);
		}

		@Override
		public void onSurfaceChanged(GL10 gl, int width, int height)
		{
			// ÉèÖÃ3DÊÓ´°µÄ´óÐ¡¼°Î»ÖÃ
			gl.glViewport(0, 0, width, height);
			// ½«µ±Ç°¾ØÕóÄ£Ê½ÉèÎªÍ¶Ó°¾ØÕó
			gl.glMatrixMode(GL10.GL_PROJECTION);
			// ³õÊ¼»¯µ¥Î»¾ØÕó
			gl.glLoadIdentity();
			// ¼ÆËãÍ¸ÊÓÊÓ´°µÄ¿í¶È¡¢¸ß¶È±È
			float ratio = (float) width / height;
			// µ÷ÓÃ´Ë·½·¨ÉèÖÃÍ¸ÊÓÊÓ´°µÄ¿Õ¼ä´óÐ¡¡£
			gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
		}

		public void onDrawFrame(GL10 gl)
		{
			// Çå³ýÆÁÄ»»º´æºÍÉî¶È»º´æ
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
			// ÆôÓÃ¶¥µã×ù±êÊý¾Ý
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			// ÆôÓÃÌùÍ¼×ù±êÊý×éÊý¾Ý
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  //¢Ù
			// ÉèÖÃµ±Ç°¾ØÕóÄ£Ê½ÎªÄ£ÐÍÊÓÍ¼¡£
			gl.glMatrixMode(GL10.GL_MODELVIEW);
			gl.glLoadIdentity();
			// °Ñ»æÍ¼ÖÐÐÄÒÆÈëÆÁÄ»2¸öµ¥Î»
			gl.glTranslatef(0f, 0.0f, -2.0f);
			// Ðý×ªÍ¼ÐÎ
			gl.glRotatef(angley, 0, 1, 0);
			gl.glRotatef(anglex, 1, 0, 0);
			// ÉèÖÃ¶¥µãµÄÎ»ÖÃÊý¾Ý
			gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeVerticesBuffer);
			// ÉèÖÃÌùÍ¼µÄ×ù±êÊý¾Ý
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer);//¢Ú
			// Ö´ÐÐÎÆÀíÌùÍ¼
			gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);  //¢Û
			// °´cubeFacetsBufferÖ¸¶¨µÄÃæ»æÖÆÈý½ÇÐÎ
			gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacetsBuffer.remaining(),
				GL10.GL_UNSIGNED_BYTE, cubeFacetsBuffer);
			// »æÖÆ½áÊø
			gl.glFinish();
			// ½ûÓÃ¶¥µã¡¢ÎÆÀí×ù±êÊý×é
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			// µÝÔö½Ç¶ÈÖµÒÔ±ãÃ¿´ÎÒÔ²»Í¬½Ç¶È»æÖÆ
		}

		private void loadTexture(GL10 gl)
		{
			Bitmap bitmap = null;
			try
			{
				// ¼ÓÔØÎ»Í¼
				bitmap = BitmapFactory.decodeResource(context.getResources(),
					R.drawable.sand);
				int[] textures = new int[1];
				// Ö¸¶¨Éú³ÉN¸öÎÆÀí£¨µÚÒ»¸ö²ÎÊýÖ¸¶¨Éú³É1¸öÎÆÀí£©£¬
				// texturesÊý×é½«¸ºÔð´æ´¢ËùÓÐÎÆÀíµÄ´úºÅ¡£
				gl.glGenTextures(1, textures, 0);
				// »ñÈ¡texturesÎÆÀíÊý×éÖÐµÄµÚÒ»¸öÎÆÀí
				texture = textures[0];
				// Í¨ÖªOpenGL½«textureÎÆÀí°ó¶¨µ½GL10.GL_TEXTURE_2DÄ¿±êÖÐ
				gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
				// ÉèÖÃÎÆÀí±»ËõÐ¡£¨¾àÀëÊÓµãºÜÔ¶Ê±±»ËõÐ¡£©Ê±ºòµÄÂË²¨·½Ê½
				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);
				// ÉèÖÃÔÚºáÏò¡¢×ÝÏòÉÏ¶¼ÊÇÆ½ÆÌÎÆÀí
				gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
					GL10.GL_REPEAT);
				gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
					GL10.GL_REPEAT);
				// ¼ÓÔØÎ»Í¼Éú³ÉÎÆÀí
				GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
			}
			finally
			{
				// Éú³ÉÎÆÀíÖ®ºó£¬»ØÊÕÎ»Í¼
				if (bitmap != null)
					bitmap.recycle();
			}
		}
	}
	// ¶¨ÒåÒ»¸ö¹¤¾ß·½·¨£¬½«float[]Êý×é×ª»»ÎªOpenGL ESËùÐèµÄFloatBuffer
	private FloatBuffer floatBufferUtil(float[] arr)
	{
		FloatBuffer mBuffer;
		// ³õÊ¼»¯ByteBuffer£¬³¤¶ÈÎªarrÊý×éµÄ³¤¶È*4£¬ÒòÎªÒ»¸öintÕ¼4¸ö×Ö½Ú
		ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
		// Êý×éÅÅÁÐÓÃnativeOrder
		qbb.order(ByteOrder.nativeOrder());
		mBuffer = qbb.asFloatBuffer();
		mBuffer.put(arr);
		mBuffer.position(0);
		return mBuffer;
	}	
}