Home | History | Annotate | Download | only in globaltime
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.globaltime;
     18 
     19 import java.util.ArrayList;
     20 import java.util.Collections;
     21 import java.util.Iterator;
     22 import java.util.List;
     23 import java.util.Map;
     24 import java.util.HashMap;
     25 import javax.microedition.khronos.opengles.GL10;
     26 
     27 import android.graphics.Canvas;
     28 import android.graphics.Paint;
     29 import android.view.KeyEvent;
     30 
     31 class Message {
     32 
     33     private String mText;
     34     private long mExpirationTime;
     35 
     36     public Message(String text, long expirationTime) {
     37         this.mText = text;
     38         this.mExpirationTime = expirationTime;
     39     }
     40 
     41     public String getText() {
     42         return mText;
     43     }
     44 
     45     public long getExpirationTime() {
     46         return mExpirationTime;
     47     }
     48 }
     49 
     50 /**
     51  * A helper class to simplify writing an Activity that renders using
     52  * OpenGL ES.
     53  *
     54  * <p> A GLView object stores common elements of GL state and allows
     55  * them to be modified interactively.  This is particularly useful for
     56  * determining the proper settings of parameters such as the view
     57  * frustum and light intensities during application development.
     58  *
     59  * <p> A GLView is not an actual View; instead, it is meant to be
     60  * called from within a View to perform event processing on behalf of the
     61  * actual View.
     62  *
     63  * <p> By passing key events to the GLView object from the View,
     64  * the application can automatically allow certain parameters to
     65  * be user-controlled from the keyboard.  Key events may be passed as
     66  * shown below:
     67  *
     68  * <pre>
     69  * GLView mGlView = new GLView();
     70  *
     71  * public boolean onKeyDown(int keyCode, KeyEvent event) {
     72  *     // Hand the key to the GLView object first
     73  *     if (mGlView.processKey(keyCode)) {
     74  *         return;
     75  *     }
     76  *
     77  *     switch (keyCode) {
     78  *     case KeyEvent.KEY_CODE_X:
     79  *         // perform app processing
     80  *         break;
     81  *
     82  *     default:
     83  *         super.onKeyDown(keyCode, event);
     84  *         break;
     85  *     }
     86  * }
     87  * </pre>
     88  *
     89  * <p> During drawing of a frame, the GLView object should be given the
     90  * opportunity to manage GL parameters as shown below:
     91  *
     92  * OpenGLContext mGLContext; // initialization not shown
     93  * int mNumTrianglesDrawn = 0;
     94  *
     95  * protected void onDraw(Canvas canvas) {
     96  *     int w = getWidth();
     97  *     int h = getHeight();
     98  *
     99  *     float ratio = (float) w / h;
    100  *     mGLView.setAspectRatio(ratio);
    101  *
    102  *     GL10 gl = (GL10) mGLContext.getGL();
    103  *     mGLContext.waitNative(canvas, this);
    104  *
    105  *     // Enable a light for the GLView to manipulate
    106  *     gl.glEnable(GL10.GL_LIGHTING);
    107  *     gl.glEnable(GL10.GL_LIGHT0);
    108  *
    109  *     // Allow the GLView to set GL parameters
    110  *     mGLView.setTextureParameters(gl);
    111  *     mGLView.setProjection(gl);
    112  *     mGLView.setView(gl);
    113  *     mGLView.setLights(gl, GL10.GL_LIGHT0);
    114  *
    115  *     // Draw some stuff (not shown)
    116  *     mNumTrianglesDrawn += <num triangles just drawn>;
    117  *
    118  *     // Wait for GL drawing to complete
    119  *     mGLContext.waitGL();
    120  *
    121  *     // Inform the GLView of what was drawn, and ask it to display statistics
    122  *     mGLView.setNumTriangles(mNumTrianglesDrawn);
    123  *     mGLView.showMessages(canvas);
    124  *     mGLView.showStatistics(canvas, w);
    125  * }
    126  * </pre>
    127  *
    128  * <p> At the end of each frame, following the call to
    129  * GLContext.waitGL, the showStatistics and showMessages methods
    130  * will cause additional information to be displayed.
    131  *
    132  * <p> To enter the interactive command mode, the 'tab' key must be
    133  * pressed twice in succession.  A subsequent press of the 'tab' key
    134  * exits the interactive command mode.  Entering a multi-letter code
    135  * sets the parameter to be modified. The 'newline' key erases the
    136  * current code, and the 'del' key deletes the last letter of
    137  * the code. The parameter value may be modified by pressing the
    138  * keypad left or up to decrement the value and right or down to
    139  * increment the value.  The current value will be displayed as an
    140  * overlay above the GL rendered content.
    141  *
    142  * <p> The supported keyboard commands are as follows:
    143  *
    144  * <ul>
    145  * <li>     h - display a list of commands
    146  * <li>    fn - near frustum
    147  * <li>    ff - far frustum
    148  * <li>    tx - translate x
    149  * <li>    ty - translate y
    150  * <li>    tz - translate z
    151  * <li>     z - zoom (frustum size)
    152  * <li>    la - ambient light (all RGB channels)
    153  * <li>   lar - ambient light red channel
    154  * <li>   lag - ambient light green channel
    155  * <li>   lab - ambient light blue channel
    156  * <li>    ld - diffuse light (all RGB channels)
    157  * <li>   ldr - diffuse light red channel
    158  * <li>   ldg - diffuse light green channel
    159  * <li>   ldb - diffuse light blue channel
    160  * <li>    ls - specular light (all RGB channels)
    161  * <li>   lsr - specular light red channel
    162  * <li>   lsg - specular light green channel
    163  * <li>   lsb - specular light blue channel
    164  * <li>   lma - light model ambient (all RGB channels)
    165  * <li>  lmar - light model ambient light red channel
    166  * <li>  lmag - light model ambient green channel
    167  * <li>  lmab - light model ambient blue channel
    168  * <li>  tmin - texture min filter
    169  * <li>  tmag - texture mag filter
    170  * <li>  tper - texture perspective correction
    171  * </ul>
    172  *
    173  * {@hide}
    174  */
    175 public class GLView {
    176 
    177     private static final int DEFAULT_DURATION_MILLIS = 1000;
    178     private static final int STATE_KEY = KeyEvent.KEYCODE_TAB;
    179     private static final int HAVE_NONE = 0;
    180     private static final int HAVE_ONE = 1;
    181     private static final int HAVE_TWO = 2;
    182 
    183     private static final float MESSAGE_Y_SPACING = 12.0f;
    184 
    185     private int mState = HAVE_NONE;
    186 
    187     private static final int NEAR_FRUSTUM  = 0;
    188     private static final int FAR_FRUSTUM   = 1;
    189     private static final int TRANSLATE_X   = 2;
    190     private static final int TRANSLATE_Y   = 3;
    191     private static final int TRANSLATE_Z   = 4;
    192     private static final int ZOOM_EXPONENT = 5;
    193 
    194     private static final int AMBIENT_INTENSITY = 6;
    195     private static final int AMBIENT_RED = 7;
    196     private static final int AMBIENT_GREEN = 8;
    197     private static final int AMBIENT_BLUE = 9;
    198 
    199     private static final int DIFFUSE_INTENSITY = 10;
    200     private static final int DIFFUSE_RED = 11;
    201     private static final int DIFFUSE_GREEN = 12;
    202     private static final int DIFFUSE_BLUE = 13;
    203 
    204     private static final int SPECULAR_INTENSITY = 14;
    205     private static final int SPECULAR_RED = 15;
    206     private static final int SPECULAR_GREEN = 16;
    207     private static final int SPECULAR_BLUE = 17;
    208 
    209     private static final int LIGHT_MODEL_AMBIENT_INTENSITY = 18;
    210     private static final int LIGHT_MODEL_AMBIENT_RED = 19;
    211     private static final int LIGHT_MODEL_AMBIENT_GREEN = 20;
    212     private static final int LIGHT_MODEL_AMBIENT_BLUE = 21;
    213 
    214     private static final int TEXTURE_MIN_FILTER = 22;
    215     private static final int TEXTURE_MAG_FILTER = 23;
    216     private static final int TEXTURE_PERSPECTIVE_CORRECTION = 24;
    217 
    218     private static final String[] commands = {
    219         "fn",
    220         "ff",
    221         "tx",
    222         "ty",
    223         "tz",
    224         "z",
    225         "la", "lar", "lag", "lab",
    226         "ld", "ldr", "ldg", "ldb",
    227         "ls", "lsr", "lsg", "lsb",
    228         "lma", "lmar", "lmag", "lmab",
    229         "tmin", "tmag", "tper"
    230    };
    231 
    232     private static final String[] labels = {
    233         "Near Frustum",
    234         "Far Frustum",
    235         "Translate X",
    236         "Translate Y",
    237         "Translate Z",
    238         "Zoom",
    239         "Ambient Intensity",
    240         "Ambient Red",
    241         "Ambient Green",
    242         "Ambient Blue",
    243         "Diffuse Intensity",
    244         "Diffuse Red",
    245         "Diffuse Green",
    246         "Diffuse Blue",
    247         "Specular Intenstity",
    248         "Specular Red",
    249         "Specular Green",
    250         "Specular Blue",
    251         "Light Model Ambient Intensity",
    252         "Light Model Ambient Red",
    253         "Light Model Ambient Green",
    254         "Light Model Ambient Blue",
    255         "Texture Min Filter",
    256         "Texture Mag Filter",
    257         "Texture Perspective Correction",
    258     };
    259 
    260     private static final float[] defaults = {
    261         5.0f, 100.0f,
    262         0.0f, 0.0f, -50.0f,
    263         0,
    264         0.125f,	1.0f, 1.0f, 1.0f,
    265         0.125f,	1.0f, 1.0f, 1.0f,
    266         0.125f,	1.0f, 1.0f, 1.0f,
    267         0.125f,	1.0f, 1.0f, 1.0f,
    268         GL10.GL_NEAREST, GL10.GL_NEAREST,
    269         GL10.GL_FASTEST
    270     };
    271 
    272     private static final float[] increments = {
    273         0.01f, 0.5f,
    274         0.125f, 0.125f, 0.125f,
    275         1.0f,
    276         0.03125f, 0.1f, 0.1f, 0.1f,
    277         0.03125f, 0.1f, 0.1f, 0.1f,
    278         0.03125f, 0.1f, 0.1f, 0.1f,
    279         0.03125f, 0.1f, 0.1f, 0.1f,
    280         0, 0, 0
    281     };
    282 
    283     private float[] params = new float[commands.length];
    284 
    285     private static final float mZoomScale = 0.109f;
    286     private static final float mZoomBase  = 1.01f;
    287 
    288     private int             mParam = -1;
    289     private float           mIncr = 0;
    290 
    291     private Paint           mPaint = new Paint();
    292 
    293     private float           mAspectRatio = 1.0f;
    294 
    295     private float           mZoom;
    296 
    297     // private boolean         mPerspectiveCorrection = false;
    298     // private int             mTextureMinFilter = GL10.GL_NEAREST;
    299     // private int             mTextureMagFilter = GL10.GL_NEAREST;
    300 
    301     // Counters for FPS calculation
    302     private boolean         mDisplayFPS = false;
    303     private boolean         mDisplayCounts = false;
    304     private int             mFramesFPS = 10;
    305     private long[]          mTimes = new long[mFramesFPS];
    306     private int             mTimesIdx = 0;
    307 
    308     private Map<String,Message> mMessages = new HashMap<String,Message>();
    309 
    310     /**
    311      * Constructs a new GLView.
    312      */
    313     public GLView() {
    314         mPaint.setColor(0xffffffff);
    315         reset();
    316     }
    317 
    318     /**
    319      * Sets the aspect ratio (width/height) of the screen.
    320      *
    321      * @param aspectRatio the screen width divided by the screen height
    322      */
    323     public void setAspectRatio(float aspectRatio) {
    324         this.mAspectRatio = aspectRatio;
    325     }
    326 
    327     /**
    328      * Sets the overall ambient light intensity.  This intensity will
    329      * be used to modify the ambient light value for each of the red,
    330      * green, and blue channels passed to glLightfv(...GL_AMBIENT...).
    331      * The default value is 0.125f.
    332      *
    333      * @param intensity a floating-point value controlling the overall
    334      * ambient light intensity.
    335      */
    336     public void setAmbientIntensity(float intensity) {
    337         params[AMBIENT_INTENSITY] = intensity;
    338     }
    339 
    340     /**
    341      * Sets the light model ambient intensity.  This intensity will be
    342      * used to modify the ambient light value for each of the red,
    343      * green, and blue channels passed to
    344      * glLightModelfv(GL_LIGHT_MODEL_AMBIENT...).  The default value
    345      * is 0.125f.
    346      *
    347      * @param intensity a floating-point value controlling the overall
    348      * light model ambient intensity.
    349      */
    350     public void setLightModelAmbientIntensity(float intensity) {
    351         params[LIGHT_MODEL_AMBIENT_INTENSITY] = intensity;
    352     }
    353 
    354     /**
    355      * Sets the ambient color for the red, green, and blue channels
    356      * that will be multiplied by the value of setAmbientIntensity and
    357      * passed to glLightfv(...GL_AMBIENT...).  The default values are
    358      * {1, 1, 1}.
    359      *
    360      * @param ambient an arry of three floats containing ambient
    361      * red, green, and blue intensity values.
    362      */
    363     public void setAmbientColor(float[] ambient) {
    364         params[AMBIENT_RED]   = ambient[0];
    365         params[AMBIENT_GREEN] = ambient[1];
    366         params[AMBIENT_BLUE]  = ambient[2];
    367     }
    368 
    369     /**
    370      * Sets the overall diffuse light intensity.  This intensity will
    371      * be used to modify the diffuse light value for each of the red,
    372      * green, and blue channels passed to glLightfv(...GL_DIFFUSE...).
    373      * The default value is 0.125f.
    374      *
    375      * @param intensity a floating-point value controlling the overall
    376      * ambient light intensity.
    377      */
    378     public void setDiffuseIntensity(float intensity) {
    379         params[DIFFUSE_INTENSITY] = intensity;
    380     }
    381 
    382     /**
    383      * Sets the diffuse color for the red, green, and blue channels
    384      * that will be multiplied by the value of setDiffuseIntensity and
    385      * passed to glLightfv(...GL_DIFFUSE...).  The default values are
    386      * {1, 1, 1}.
    387      *
    388      * @param diffuse an array of three floats containing diffuse
    389      * red, green, and blue intensity values.
    390      */
    391     public void setDiffuseColor(float[] diffuse) {
    392         params[DIFFUSE_RED]   = diffuse[0];
    393         params[DIFFUSE_GREEN] = diffuse[1];
    394         params[DIFFUSE_BLUE]  = diffuse[2];
    395     }
    396 
    397     /**
    398      * Sets the overall specular light intensity.  This intensity will
    399      * be used to modify the diffuse light value for each of the red,
    400      * green, and blue channels passed to glLightfv(...GL_SPECULAR...).
    401      * The default value is 0.125f.
    402      *
    403      * @param intensity a floating-point value controlling the overall
    404      * ambient light intensity.
    405      */
    406     public void setSpecularIntensity(float intensity) {
    407         params[SPECULAR_INTENSITY] = intensity;
    408     }
    409 
    410     /**
    411      * Sets the specular color for the red, green, and blue channels
    412      * that will be multiplied by the value of setSpecularIntensity and
    413      * passed to glLightfv(...GL_SPECULAR...).  The default values are
    414      * {1, 1, 1}.
    415      *
    416      * @param specular an array of three floats containing specular
    417      * red, green, and blue intensity values.
    418      */
    419     public void setSpecularColor(float[] specular) {
    420         params[SPECULAR_RED]   = specular[0];
    421         params[SPECULAR_GREEN] = specular[1];
    422         params[SPECULAR_BLUE]  = specular[2];
    423     }
    424 
    425     /**
    426      * Returns the current X translation of the modelview
    427      * transformation as passed to glTranslatef.  The default value is
    428      * 0.0f.
    429      *
    430      * @return the X modelview translation as a float.
    431      */
    432     public float getTranslateX() {
    433         return params[TRANSLATE_X];
    434     }
    435 
    436     /**
    437      * Returns the current Y translation of the modelview
    438      * transformation as passed to glTranslatef.  The default value is
    439      * 0.0f.
    440      *
    441      * @return the Y modelview translation as a float.
    442      */
    443     public float getTranslateY() {
    444         return params[TRANSLATE_Y];
    445     }
    446 
    447     /**
    448      * Returns the current Z translation of the modelview
    449      * transformation as passed to glTranslatef.  The default value is
    450      * -50.0f.
    451      *
    452      * @return the Z modelview translation as a float.
    453      */
    454     public float getTranslateZ() {
    455         return params[TRANSLATE_Z];
    456     }
    457 
    458     /**
    459      * Sets the position of the near frustum clipping plane as passed
    460      * to glFrustumf.  The default value is 5.0f;
    461      *
    462      * @param nearFrustum the near frustum clipping plane distance as
    463      * a float.
    464      */
    465     public void setNearFrustum(float nearFrustum) {
    466         params[NEAR_FRUSTUM] = nearFrustum;
    467     }
    468 
    469     /**
    470      * Sets the position of the far frustum clipping plane as passed
    471      * to glFrustumf.  The default value is 100.0f;
    472      *
    473      * @param farFrustum the far frustum clipping plane distance as a
    474      * float.
    475      */
    476     public void setFarFrustum(float farFrustum) {
    477         params[FAR_FRUSTUM] = farFrustum;
    478     }
    479 
    480     private void computeZoom() {
    481         mZoom = mZoomScale*(float)Math.pow(mZoomBase, -params[ZOOM_EXPONENT]);
    482     }
    483 
    484     /**
    485      * Resets all parameters to their default values.
    486      */
    487     public void reset() {
    488         for (int i = 0; i < params.length; i++) {
    489             params[i] = defaults[i];
    490         }
    491         computeZoom();
    492     }
    493 
    494     private void removeExpiredMessages() {
    495         long now = System.currentTimeMillis();
    496 
    497         List<String> toBeRemoved = new ArrayList<String>();
    498 
    499         Iterator<String> keyIter = mMessages.keySet().iterator();
    500         while (keyIter.hasNext()) {
    501             String key = keyIter.next();
    502             Message msg = mMessages.get(key);
    503             if (msg.getExpirationTime() < now) {
    504                 toBeRemoved.add(key);
    505             }
    506         }
    507 
    508         Iterator<String> tbrIter = toBeRemoved.iterator();
    509         while (tbrIter.hasNext()) {
    510             String key = tbrIter.next();
    511             mMessages.remove(key);
    512         }
    513     }
    514 
    515     /**
    516      * Displays the message overlay on the given Canvas.  The
    517      * GLContext.waitGL method should be called prior to calling this
    518      * method.  The interactive command display is drawn by this
    519      * method.
    520      *
    521      * @param canvas the Canvas on which messages are to appear.
    522      */
    523     public void showMessages(Canvas canvas) {
    524         removeExpiredMessages();
    525 
    526         float y = 10.0f;
    527 
    528         List<String> l = new ArrayList<String>();
    529         l.addAll(mMessages.keySet());
    530         Collections.sort(l);
    531 
    532         Iterator<String> iter = l.iterator();
    533         while (iter.hasNext()) {
    534             String key = iter.next();
    535             String text = mMessages.get(key).getText();
    536             canvas.drawText(text, 10.0f, y, mPaint);
    537             y += MESSAGE_Y_SPACING;
    538         }
    539     }
    540 
    541     private int mTriangles;
    542 
    543     /**
    544      * Sets the number of triangles drawn in the previous frame for
    545      * display by the showStatistics method.  The number of triangles
    546      * is not computed by GLView but must be supplied by the
    547      * calling Activity.
    548      *
    549      * @param triangles an Activity-supplied estimate of the number of
    550      * triangles drawn in the previous frame.
    551      */
    552     public void setNumTriangles(int triangles) {
    553         this.mTriangles = triangles;
    554     }
    555 
    556     /**
    557      * Displays statistics on frames and triangles per second. The
    558      * GLContext.waitGL method should be called prior to calling this
    559      * method.
    560      *
    561      * @param canvas the Canvas on which statistics are to appear.
    562      * @param width the width of the Canvas.
    563      */
    564     public void showStatistics(Canvas canvas, int width) {
    565         long endTime = mTimes[mTimesIdx] = System.currentTimeMillis();
    566         mTimesIdx = (mTimesIdx + 1) % mFramesFPS;
    567 
    568         float th = mPaint.getTextSize();
    569 
    570         if (mDisplayFPS) {
    571             // Use end time from mFramesFPS frames ago
    572             long startTime = mTimes[mTimesIdx];
    573             String fps = "" + (1000.0f*mFramesFPS/(endTime - startTime));
    574 
    575             // Normalize fps to XX.XX format
    576             if (fps.indexOf(".") == 1) {
    577                 fps = " " + fps;
    578             }
    579             int len = fps.length();
    580             if (len == 2) {
    581                 fps += ".00";
    582             } else if (len == 4) {
    583                 fps += "0";
    584             } else if (len > 5) {
    585                 fps = fps.substring(0, 5);
    586             }
    587 
    588             canvas.drawText(fps + " fps", width - 60.0f, 10.0f, mPaint);
    589         }
    590 
    591         if (mDisplayCounts) {
    592             canvas.drawText(mTriangles + " triangles",
    593                             width - 100.0f, 10.0f + th + 5, mPaint);
    594         }
    595     }
    596 
    597     private void addMessage(String key, String text, int durationMillis) {
    598         long expirationTime = System.currentTimeMillis() + durationMillis;
    599 
    600         mMessages.put(key, new Message(text, expirationTime));
    601     }
    602 
    603     private void addMessage(String key, String text) {
    604         addMessage(key, text, DEFAULT_DURATION_MILLIS);
    605     }
    606 
    607     private void addMessage(String text) {
    608         addMessage(text, text, DEFAULT_DURATION_MILLIS);
    609     }
    610 
    611     private void clearMessages() {
    612         mMessages.clear();
    613     }
    614 
    615     String command = "";
    616 
    617     private void toggleFilter() {
    618         if (params[mParam] == GL10.GL_NEAREST) {
    619             params[mParam] = GL10.GL_LINEAR;
    620         } else {
    621             params[mParam] = GL10.GL_NEAREST;
    622         }
    623         addMessage(commands[mParam],
    624                    "Texture " +
    625                    (mParam == TEXTURE_MIN_FILTER ? "min" : "mag") +
    626                    " filter = " +
    627                    (params[mParam] == GL10.GL_NEAREST ?
    628                     "nearest" : "linear"));
    629     }
    630 
    631     private void togglePerspectiveCorrection() {
    632         if (params[mParam] == GL10.GL_NICEST) {
    633             params[mParam] = GL10.GL_FASTEST;
    634         } else {
    635             params[mParam] = GL10.GL_NICEST;
    636         }
    637         addMessage(commands[mParam],
    638                    "Texture perspective correction = " +
    639                    (params[mParam] == GL10.GL_FASTEST ?
    640                     "fastest" : "nicest"));
    641     }
    642 
    643     private String valueString() {
    644         if (mParam == TEXTURE_MIN_FILTER ||
    645             mParam == TEXTURE_MAG_FILTER) {
    646             if (params[mParam] == GL10.GL_NEAREST) {
    647                 return "nearest";
    648             }
    649             if (params[mParam] == GL10.GL_LINEAR) {
    650                 return "linear";
    651             }
    652         }
    653         if (mParam == TEXTURE_PERSPECTIVE_CORRECTION) {
    654             if (params[mParam] == GL10.GL_FASTEST) {
    655                 return "fastest";
    656             }
    657             if (params[mParam] == GL10.GL_NICEST) {
    658                 return "nicest";
    659             }
    660         }
    661         return "" + params[mParam];
    662     }
    663 
    664     /**
    665      *
    666      * @return true if the view
    667      */
    668     public boolean hasMessages() {
    669         return mState == HAVE_TWO || mDisplayFPS || mDisplayCounts;
    670     }
    671 
    672     /**
    673      * Process a key stroke.  The calling Activity should pass all
    674      * keys from its onKeyDown method to this method.  If the key is
    675      * part of a GLView command, true is returned and the calling
    676      * Activity should ignore the key event.  Otherwise, false is
    677      * returned and the calling Activity may process the key event
    678      * normally.
    679      *
    680      * @param keyCode the key code as passed to Activity.onKeyDown.
    681      *
    682      * @return true if the key is part of a GLView command sequence,
    683      * false otherwise.
    684      */
    685     public boolean processKey(int keyCode) {
    686         // Pressing the state key twice enters the UI
    687         // Pressing it again exits the UI
    688         if ((keyCode == STATE_KEY) ||
    689             (keyCode == KeyEvent.KEYCODE_SLASH) ||
    690             (keyCode == KeyEvent.KEYCODE_PERIOD))
    691         {
    692             mState = (mState + 1) % 3;
    693             if (mState == HAVE_NONE) {
    694                 clearMessages();
    695             }
    696             if (mState == HAVE_TWO) {
    697                 clearMessages();
    698                 addMessage("aaaa", "GL", Integer.MAX_VALUE);
    699                 addMessage("aaab", "", Integer.MAX_VALUE);
    700                 command = "";
    701             }
    702             return true;
    703         } else {
    704             if (mState == HAVE_ONE) {
    705                 mState = HAVE_NONE;
    706                 return false;
    707             }
    708         }
    709 
    710         // If we're not in the UI, exit without handling the key
    711         if (mState != HAVE_TWO) {
    712             return false;
    713         }
    714 
    715         if (keyCode == KeyEvent.KEYCODE_ENTER) {
    716             command = "";
    717         } else if (keyCode == KeyEvent.KEYCODE_DEL) {
    718             if (command.length() > 0) {
    719                 command = command.substring(0, command.length() - 1);
    720             }
    721 
    722         } else if (keyCode >= KeyEvent.KEYCODE_A &&
    723                    keyCode <= KeyEvent.KEYCODE_Z) {
    724             command += "" + (char)(keyCode - KeyEvent.KEYCODE_A + 'a');
    725         }
    726 
    727         addMessage("aaaa", "GL " + command, Integer.MAX_VALUE);
    728 
    729         if (command.equals("h")) {
    730             addMessage("aaaa", "GL", Integer.MAX_VALUE);
    731             addMessage("h - help");
    732             addMessage("fn/ff - frustum near/far clip Z");
    733             addMessage("la/lar/lag/lab - abmient intensity/r/g/b");
    734             addMessage("ld/ldr/ldg/ldb - diffuse intensity/r/g/b");
    735             addMessage("ls/lsr/lsg/lsb - specular intensity/r/g/b");
    736             addMessage("s - toggle statistics display");
    737             addMessage("tmin/tmag - texture min/mag filter");
    738             addMessage("tpersp - texture perspective correction");
    739             addMessage("tx/ty/tz - view translate x/y/z");
    740             addMessage("z - zoom");
    741             command = "";
    742             return true;
    743         } else if (command.equals("s")) {
    744             mDisplayCounts = !mDisplayCounts;
    745             mDisplayFPS = !mDisplayFPS;
    746             command = "";
    747             return true;
    748         }
    749 
    750         mParam = -1;
    751         for (int i = 0; i < commands.length; i++) {
    752             if (command.equals(commands[i])) {
    753                 mParam = i;
    754                 mIncr = increments[i];
    755             }
    756         }
    757         if (mParam == -1) {
    758             return true;
    759         }
    760 
    761         boolean addMessage = true;
    762 
    763         // Increment or decrement
    764         if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
    765             keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
    766             if (mParam == ZOOM_EXPONENT) {
    767                 params[mParam] += mIncr;
    768                 computeZoom();
    769             } else if ((mParam == TEXTURE_MIN_FILTER) ||
    770                        (mParam == TEXTURE_MAG_FILTER)) {
    771                 toggleFilter();
    772             } else if (mParam == TEXTURE_PERSPECTIVE_CORRECTION) {
    773                 togglePerspectiveCorrection();
    774             } else {
    775                 params[mParam] += mIncr;
    776             }
    777         } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP ||
    778                    keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
    779             if (mParam == ZOOM_EXPONENT) {
    780                 params[mParam] -= mIncr;
    781                 computeZoom();
    782             } else if ((mParam == TEXTURE_MIN_FILTER) ||
    783                        (mParam == TEXTURE_MAG_FILTER)) {
    784                 toggleFilter();
    785             } else if (mParam == TEXTURE_PERSPECTIVE_CORRECTION) {
    786                 togglePerspectiveCorrection();
    787             } else {
    788                 params[mParam] -= mIncr;
    789             }
    790         }
    791 
    792         if (addMessage) {
    793             addMessage(commands[mParam],
    794                        labels[mParam] + ": " + valueString());
    795         }
    796 
    797         return true;
    798     }
    799 
    800     /**
    801      * Zoom in by a given number of steps.  A negative value of steps
    802      * zooms out.  Each step zooms in by 1%.
    803      *
    804      * @param steps the number of steps to zoom by.
    805      */
    806     public void zoom(int steps) {
    807         params[ZOOM_EXPONENT] += steps;
    808         computeZoom();
    809     }
    810 
    811     /**
    812      * Set the projection matrix using glFrustumf.  The left and right
    813      * clipping planes are set at -+(aspectRatio*zoom), the bottom and
    814      * top clipping planes are set at -+zoom, and the near and far
    815      * clipping planes are set to the values set by setNearFrustum and
    816      * setFarFrustum or interactively.
    817      *
    818      * <p> GL side effects:
    819      * <ul>
    820      *    <li>overwrites the matrix mode</li>
    821      *    <li>overwrites the projection matrix</li>
    822      * </ul>
    823      *
    824      * @param gl a GL10 instance whose projection matrix is to be modified.
    825      */
    826     public void setProjection(GL10 gl) {
    827         gl.glMatrixMode(GL10.GL_PROJECTION);
    828         gl.glLoadIdentity();
    829 
    830         if (mAspectRatio >= 1.0f) {
    831             gl.glFrustumf(-mAspectRatio*mZoom, mAspectRatio*mZoom,
    832                           -mZoom, mZoom,
    833                           params[NEAR_FRUSTUM], params[FAR_FRUSTUM]);
    834         } else {
    835             gl.glFrustumf(-mZoom, mZoom,
    836                           -mZoom / mAspectRatio, mZoom / mAspectRatio,
    837                           params[NEAR_FRUSTUM], params[FAR_FRUSTUM]);
    838         }
    839     }
    840 
    841     /**
    842      * Set the modelview matrix using glLoadIdentity and glTranslatef.
    843      * The translation values are set interactively.
    844      *
    845      * <p> GL side effects:
    846      * <ul>
    847      * <li>overwrites the matrix mode</li>
    848      * <li>overwrites the modelview matrix</li>
    849      * </ul>
    850      *
    851      * @param gl a GL10 instance whose modelview matrix is to be modified.
    852      */
    853     public void setView(GL10 gl) {
    854         gl.glMatrixMode(GL10.GL_MODELVIEW);
    855         gl.glLoadIdentity();
    856 
    857         // Move the viewpoint backwards
    858         gl.glTranslatef(params[TRANSLATE_X],
    859                         params[TRANSLATE_Y],
    860                         params[TRANSLATE_Z]);
    861     }
    862 
    863     /**
    864      * Sets texture parameters.
    865      *
    866      * <p> GL side effects:
    867      * <ul>
    868      * <li>sets the GL_PERSPECTIVE_CORRECTION_HINT</li>
    869      * <li>sets the GL_TEXTURE_MIN_FILTER texture parameter</li>
    870      * <li>sets the GL_TEXTURE_MAX_FILTER texture parameter</li>
    871      * </ul>
    872      *
    873      * @param gl a GL10 instance whose texture parameters are to be modified.
    874      */
    875     public void setTextureParameters(GL10 gl) {
    876         gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
    877                   (int)params[TEXTURE_PERSPECTIVE_CORRECTION]);
    878         gl.glTexParameterf(GL10.GL_TEXTURE_2D,
    879                            GL10.GL_TEXTURE_MIN_FILTER,
    880                            params[TEXTURE_MIN_FILTER]);
    881         gl.glTexParameterf(GL10.GL_TEXTURE_2D,
    882                            GL10.GL_TEXTURE_MAG_FILTER,
    883                            params[TEXTURE_MAG_FILTER]);
    884     }
    885 
    886     /**
    887      * Sets the lighting parameters for the given light.
    888      *
    889      * <p> GL side effects:
    890      * <ul>
    891      * <li>sets the GL_LIGHT_MODEL_AMBIENT intensities
    892      * <li>sets the GL_AMBIENT intensities for the given light</li>
    893      * <li>sets the GL_DIFFUSE intensities for the given light</li>
    894      * <li>sets the GL_SPECULAR intensities for the given light</li>
    895      * </ul>
    896      *
    897      * @param gl a GL10 instance whose texture parameters are to be modified.
    898      */
    899     public void setLights(GL10 gl, int lightNum) {
    900         float[] light = new float[4];
    901         light[3] = 1.0f;
    902 
    903         float lmi = params[LIGHT_MODEL_AMBIENT_INTENSITY];
    904         light[0] = params[LIGHT_MODEL_AMBIENT_RED]*lmi;
    905         light[1] = params[LIGHT_MODEL_AMBIENT_GREEN]*lmi;
    906         light[2] = params[LIGHT_MODEL_AMBIENT_BLUE]*lmi;
    907         gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, light, 0);
    908 
    909         float ai = params[AMBIENT_INTENSITY];
    910         light[0] = params[AMBIENT_RED]*ai;
    911         light[1] = params[AMBIENT_GREEN]*ai;
    912         light[2] = params[AMBIENT_BLUE]*ai;
    913         gl.glLightfv(lightNum, GL10.GL_AMBIENT, light, 0);
    914 
    915         float di = params[DIFFUSE_INTENSITY];
    916         light[0] = params[DIFFUSE_RED]*di;
    917         light[1] = params[DIFFUSE_GREEN]*di;
    918         light[2] = params[DIFFUSE_BLUE]*di;
    919         gl.glLightfv(lightNum, GL10.GL_DIFFUSE, light, 0);
    920 
    921         float si = params[SPECULAR_INTENSITY];
    922         light[0] = params[SPECULAR_RED]*si;
    923         light[1] = params[SPECULAR_GREEN]*si;
    924         light[2] = params[SPECULAR_BLUE]*si;
    925         gl.glLightfv(lightNum, GL10.GL_SPECULAR, light, 0);
    926     }
    927 }
    928