/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.test;
import static android.opengl.GLES10.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
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.ETC1Util;
import android.opengl.GLES10;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
/**
* A GLSurfaceView.Renderer that uses the Android-specific
* android.opengl.GLESXXX static OpenGL ES APIs. The static APIs expose more of
* the OpenGL ES features than the javax.microedition.khronos.opengles APIs, and
* also provide a programming model that is closer to the C OpenGL ES APIs,
* which may make it easier to reuse code and documentation written for the C
* OpenGL ES APIs.
*
*/
class StaticTriangleRenderer implements GLSurfaceView.Renderer {
public interface TextureLoader {
/**
* Load a texture into the currently bound OpenGL texture.
*/
void load(GL10 gl);
}
public StaticTriangleRenderer(Context context) {
init(context, new RobotTextureLoader());
}
public StaticTriangleRenderer(Context context, TextureLoader loader) {
init(context, loader);
}
private void init(Context context, TextureLoader loader) {
mContext = context;
mTriangle = new Triangle();
mTextureLoader = loader;
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
/*
* By default, OpenGL enables features that improve quality but reduce
* performance. One might want to tweak that especially on software
* renderer.
*/
glDisable(GL_DITHER);
/*
* Some one-time OpenGL initialization can be made here probably based
* on features of this particular context
*/
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glClearColor(.5f, .5f, .5f, 1);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
/*
* Create our texture. This has to be done each time the surface is
* created.
*/
int[] textures = new int[1];
glGenTextures(1, textures, 0);
mTextureID = textures[0];
glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
mTextureLoader.load(gl);
}
public void onDrawFrame(GL10 gl) {
/*
* By default, OpenGL enables features that improve quality but reduce
* performance. One might want to tweak that especially on software
* renderer.
*/
glDisable(GL_DITHER);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
/*
* Usually, the first thing one might want to do is to clear the screen.
* The most efficient way of doing this is to use glClear().
*/
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/*
* Now we're ready to draw some 3D objects
*/
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
glRotatef(angle, 0, 0, 1.0f);
mTriangle.draw(gl);
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
glViewport(0, 0, w, h);
/*
* Set our projection matrix. This doesn't have to be done each time we
* draw, but usually a new projection needs to be set when the viewport
* is resized.
*/
float ratio = (float) w / h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustumf(-ratio, ratio, -1, 1, 3, 7);
}
private Context mContext;
private Triangle mTriangle;
private int mTextureID;
private TextureLoader mTextureLoader;
private class RobotTextureLoader implements TextureLoader {
public void load(GL10 gl) {
InputStream is = mContext.getResources().openRawResource(
R.raw.robot);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch (IOException e) {
// Ignore.
}
}
GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
}
static class Triangle {
public Triangle() {
// Buffers to be passed to gl*Pointer() functions
// must be direct, i.e., they must be placed on the
// native heap where the garbage collector cannot
// move them.
//
// Buffers with multi-byte datatypes (e.g., short, int, float)
// must have their byte order set to native order
ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
vbb.order(ByteOrder.nativeOrder());
mFVertexBuffer = vbb.asFloatBuffer();
ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
tbb.order(ByteOrder.nativeOrder());
mTexBuffer = tbb.asFloatBuffer();
ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
ibb.order(ByteOrder.nativeOrder());
mIndexBuffer = ibb.asShortBuffer();
// A unit-sided equilateral triangle centered on the origin.
float[] coords = {
// X, Y, Z
-0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 };
for (int i = 0; i < VERTS; i++) {
for (int j = 0; j < 3; j++) {
mFVertexBuffer.put(coords[i * 3 + j] * 2.0f);
}
}
for (int i = 0; i < VERTS; i++) {
for (int j = 0; j < 2; j++) {
mTexBuffer.put(coords[i * 3 + j] * 2.0f + 0.5f);
}
}
for (int i = 0; i < VERTS; i++) {
mIndexBuffer.put((short) i);
}
mFVertexBuffer.position(0);
mTexBuffer.position(0);
mIndexBuffer.position(0);
}
public void draw(GL10 gl) {
glFrontFace(GL_CCW);
glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer);
glEnable(GL_TEXTURE_2D);
glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer);
glDrawElements(GL_TRIANGLE_STRIP, VERTS, GL_UNSIGNED_SHORT,
mIndexBuffer);
}
private final static int VERTS = 3;
private FloatBuffer mFVertexBuffer;
private FloatBuffer mTexBuffer;
private ShortBuffer mIndexBuffer;
}
}
/**
* Demonstrate how to use ETC1 format compressed textures. This sample can be
* recompiled to use either resource-based textures (compressed offline using
* the etc1tool), or textures created on the fly by compressing images.
*
*/
public class Test extends Activity {
private final static String TAG = "CompressedTextureActivity";
/**
* Choose between creating a compressed texture on the fly or loading a
* compressed texture from a resource.
*/
private final static boolean TEST_CREATE_TEXTURE = false;
/**
* When creating a compressed texture on the fly, choose whether or not to
* use the i/o stream APIs.
*/
private final static boolean USE_STREAM_IO = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(this);
mGLView.setEGLConfigChooser(false);
StaticTriangleRenderer.TextureLoader loader;
if (TEST_CREATE_TEXTURE) {
loader = new SyntheticCompressedTextureLoader();
} else {
loader = new CompressedTextureLoader();
}
mGLView.setRenderer(new StaticTriangleRenderer(this, loader));
setContentView(mGLView);
}
@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}
@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}
/**
* Demonstrate how to load a compressed texture from an APK resource.
*
*/
private class CompressedTextureLoader implements
StaticTriangleRenderer.TextureLoader {
public void load(GL10 gl) {
Log.w(TAG, "ETC1 texture support: " + ETC1Util.isETC1Supported());
InputStream input = getResources().openRawResource(R.raw.androids);
try {
ETC1Util.loadTexture(GLES10.GL_TEXTURE_2D, 0, 0, GLES10.GL_RGB,
GLES10.GL_UNSIGNED_SHORT_5_6_5, input);
} catch (IOException e) {
Log.w(TAG, "Could not load texture: " + e);
} finally {
try {
input.close();
} catch (IOException e) {
// ignore exception thrown from close.
}
}
}
}
/**
* Demonstrate how to create a compressed texture on the fly.
*/
private class SyntheticCompressedTextureLoader implements
StaticTriangleRenderer.TextureLoader {
public void load(GL10 gl) {
int width = 128;
int height = 128;
Buffer image = createImage(width, height);
ETC1Util.ETC1Texture etc1Texture = ETC1Util.compressTexture(image,
width, height, 3, 3 * width);
if (USE_STREAM_IO) {
// Test the ETC1Util APIs for reading and writing compressed
// textures to I/O streams.
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ETC1Util.writeTexture(etc1Texture, bos);
ByteArrayInputStream bis = new ByteArrayInputStream(
bos.toByteArray());
ETC1Util.loadTexture(GLES10.GL_TEXTURE_2D, 0, 0,
GLES10.GL_RGB, GLES10.GL_UNSIGNED_SHORT_5_6_5, bis);
} catch (IOException e) {
Log.w(TAG, "Could not load texture: " + e);
}
} else {
ETC1Util.loadTexture(GLES10.GL_TEXTURE_2D, 0, 0, GLES10.GL_RGB,
GLES10.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
}
}
private Buffer createImage(int width, int height) {
int stride = 3 * width;
ByteBuffer image = ByteBuffer.allocateDirect(height * stride)
.order(ByteOrder.nativeOrder());
// Fill with a pretty "munching squares" pattern:
for (int t = 0; t < height; t++) {
byte red = (byte) (255 - 2 * t);
byte green = (byte) (2 * t);
byte blue = 0;
for (int x = 0; x < width; x++) {
int y = x ^ t;
image.position(stride * y + x * 3);
image.put(red);
image.put(green);
image.put(blue);
}
}
image.position(0);
return image;
}
}
private GLSurfaceView mGLView;
}