An OpenGL ES renderer based on the GLSurfaceView rendering framework.

   
/*
 * 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.
 */

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Semaphore;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * An OpenGL ES renderer based on the GLSurfaceView rendering framework. This
 * class is responsible for drawing a list of renderables to the screen every
 * frame. It also manages loading of textures and (when VBOs are used) the
 * allocation of vertex buffer objects.
 */
public class SimpleGLRenderer implements GLSurfaceView.Renderer {
  // Specifies the format our textures should be converted to upon load.
  private static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
  // An array of things to draw every frame.
  private GLSprite[] mSprites;
  // Pre-allocated arrays to use at runtime so that allocation during the
  // test can be avoided.
  private int[] mTextureNameWorkspace;
  private int[] mCropWorkspace;
  // A reference to the application context.
  private Context mContext;

  // Determines the use of vertex arrays.

  // Determines the use of vertex buffer objects.

  public SimpleGLRenderer(Context context) {
    // Pre-allocate and store these objects so we can use them at runtime
    // without allocating memory mid-frame.
    mTextureNameWorkspace = new int[1];
    mCropWorkspace = new int[4];

    // Set our bitmaps to 16-bit, 565 format.
    sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;

    mContext = context;
  }

  public int[] getConfigSpec() {
    // We don't need a depth buffer, and don't care about our
    // color depth.
    int[] configSpec = EGL10.EGL_DEPTH_SIZE, 0, EGL10.EGL_NONE };
    return configSpec;
  }

  public void setSprites(GLSprite[] sprites) {
    mSprites = sprites;
  }

  /**
   * Changes the vertex mode used for drawing.
   
   @param useVerts
   *            Specifies whether to use a vertex array. If false, the
   *            DrawTexture extension is used.
   @param useHardwareBuffers
   *            Specifies whether to store vertex arrays in main memory or on
   *            the graphics card. Ignored if useVerts is false.
   */

  /** Draws the sprites. */
  public void drawFrame(GL10 gl) {
    if (mSprites != null) {

      gl.glMatrixMode(GL10.GL_MODELVIEW);

      for (int x = 0; x < mSprites.length; x++) {
        mSprites[x].draw(gl);
      }

    }
  }

  /* Called when the size of the window changes. */
  public void sizeChanged(GL10 gl, int width, int height) {
    gl.glViewport(00, width, height);

    /*
     * 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.
     */
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glOrthof(0.0f, width, 0.0f, height, 0.0f1.0f);

    gl.glShadeModel(GL10.GL_FLAT);
    gl.glEnable(GL10.GL_BLEND);
    gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    gl.glColor4x(0x100000x100000x100000x10000);
    gl.glEnable(GL10.GL_TEXTURE_2D);
  }

  /**
   * Called whenever the surface is created. This happens at startup, and may
   * be called again at runtime if the device context is lost (the screen goes
   * to sleep, etc). This function must fill the contents of vram with texture
   * data and (when using VBOs) hardware vertex arrays.
   */
  public void surfaceCreated(GL10 gl) {
    /*
     * Some one-time OpenGL initialization can be made here probably based
     * on features of this particular context
     */
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

    gl.glClearColor(0.5f0.5f0.5f1);
    gl.glShadeModel(GL10.GL_FLAT);
    gl.glDisable(GL10.GL_DEPTH_TEST);
    gl.glEnable(GL10.GL_TEXTURE_2D);
    /*
     * By default, OpenGL enables features that improve quality but reduce
     * performance. One might want to tweak that especially on software
     * renderer.
     */
    gl.glDisable(GL10.GL_DITHER);
    gl.glDisable(GL10.GL_LIGHTING);

    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    if (mSprites != null) {

      // If we are using hardware buffers and the screen lost context
      // then the buffer indexes that we recorded previously are now
      // invalid. Forget them here and recreate them below.

      // Load our texture and set its texture name on all sprites.

      // To keep this sample simple we will assume that sprites that share
      // the same texture are grouped together in our sprite list. A real
      // app would probably have another level of texture management,
      // like a texture hash.

      int lastLoadedResource = -1;
      int lastTextureId = -1;

      for (int x = 0; x < mSprites.length; x++) {
        int resource = mSprites[x].getResourceId();
        if (resource != lastLoadedResource) {
          lastTextureId = loadBitmap(mContext, gl, resource);
          lastLoadedResource = resource;
        }
        mSprites[x].setTextureName(lastTextureId);
        // mSprites[x].getGrid().generateHardwareBuffers(gl);

      }
    }
  }

  /**
   * Called when the rendering thread shuts down. This is a good place to
   * release OpenGL ES resources.
   
   @param gl
   */
  public void shutdown(GL10 gl) {
    if (mSprites != null) {

      int lastFreedResource = -1;
      int[] textureToDelete = new int[1];

      for (int x = 0; x < mSprites.length; x++) {
        int resource = mSprites[x].getResourceId();
        if (resource != lastFreedResource) {
          textureToDelete[0= mSprites[x].getTextureName();
          gl.glDeleteTextures(1, textureToDelete, 0);
          mSprites[x].setTextureName(0);
        }
      }
    }
  }

  /**
   * Loads a bitmap into OpenGL and sets up the common parameters for 2D
   * texture maps.
   */
  protected int loadBitmap(Context context, GL10 gl, int resourceId) {
    int textureName = -1;
    if (context != null && gl != null) {
      gl.glGenTextures(1, mTextureNameWorkspace, 0);

      textureName = mTextureNameWorkspace[0];
      gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);

      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_CLAMP_TO_EDGE);
      gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
          GL10.GL_CLAMP_TO_EDGE);

      gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
          GL10.GL_REPLACE);

      InputStream is = context.getResources().openRawResource(resourceId);
      Bitmap bitmap;
      try {
        bitmap = BitmapFactory.decodeStream(is, null, sBitmapOptions);
      finally {
        try {
          is.close();
        catch (IOException e) {
          // Ignore.
        }
      }

      GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

      mCropWorkspace[00;
      mCropWorkspace[1= bitmap.getHeight();
      mCropWorkspace[2= bitmap.getWidth();
      mCropWorkspace[3= -bitmap.getHeight();

      bitmap.recycle();

      ((GL11gl).glTexParameteriv(GL10.GL_TEXTURE_2D,
          GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0);

      int error = gl.glGetError();
      if (error != GL10.GL_NO_ERROR) {
        Log.e("SpriteMethodTest""Texture Load GLError: " + error);
      }

    }

    return textureName;
  }

}

/**
 * Base class defining the core set of information necessary to render (and move
 * an object on the screen. This is an abstract type and must be derived to add
 * methods to actually draw (see CanvasSprite and GLSprite).
 */
abstract class Renderable {
  // Position.
  public float x;
  public float y;
  public float z;

  // Velocity.
  public float velocityX;
  public float velocityY;
  public float velocityZ;

  // Size.
  public float width;
  public float height;
}

/**
 * This is the OpenGL ES version of a sprite. It is more complicated than the
 * CanvasSprite class because it can be used in more than one way. This class
 * can draw using a grid of verts, a grid of verts stored in VBO objects, or
 * using the DrawTexture extension.
 */
class GLSprite extends Renderable {
  // The OpenGL ES texture handle to draw.
  private int mTextureName;
  // The id of the original resource that mTextureName is based on.
  private int mResourceId;

  // If drawing with verts or VBO verts, the grid object defining those verts.

  public GLSprite(int resourceId) {
    super();
    mResourceId = resourceId;
  }

  public void setTextureName(int name) {
    mTextureName = name;
  }

  public int getTextureName() {
    return mTextureName;
  }

  public void setResourceId(int id) {
    mResourceId = id;
  }

  public int getResourceId() {
    return mResourceId;
  }

  public void draw(GL10 gl) {
    gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureName);

    // Draw using the DrawTexture extension.
    ((GL11Extgl).glDrawTexfOES(x, y, z, width, height);

  }
}

/**
 * An implementation of SurfaceView that uses the dedicated surface for
 * displaying an OpenGL animation. This allows the animation to run in a
 * separate thread, without requiring that it be driven by the update mechanism
 * of the view hierarchy.
 
 * The application-specific rendering code is delegated to a GLView.Renderer
 * instance.
 */
class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
  public GLSurfaceView(Context context) {
    super(context);
    init();
  }

  public GLSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  private void init() {
    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed
    mHolder = getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
  }

  public SurfaceHolder getSurfaceHolder() {
    return mHolder;
  }

  public void setGLWrapper(GLWrapper glWrapper) {
    mGLWrapper = glWrapper;
  }

  public void setRenderer(Renderer renderer) {
    mGLThread = new GLThread(renderer);
    mGLThread.start();
  }

  public void surfaceCreated(SurfaceHolder holder) {
    mGLThread.surfaceCreated();
  }

  public void surfaceDestroyed(SurfaceHolder holder) {
    // Surface will be destroyed when we return
    mGLThread.surfaceDestroyed();
  }

  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Surface size or format has changed. This should not happen in this
    // example.
    mGLThread.onWindowResize(w, h);
  }

  /**
   * Inform the view that the activity is paused.
   */
  public void onPause() {
    mGLThread.onPause();
  }

  /**
   * Inform the view that the activity is resumed.
   */
  public void onResume() {
    mGLThread.onResume();
  }

  /**
   * Inform the view that the window focus has changed.
   */
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    mGLThread.onWindowFocusChanged(hasFocus);
  }

  /**
   * Set an "event" to be run on the GL rendering thread.
   
   @param r
   *            the runnable to be run on the GL rendering thread.
   */
  public void setEvent(Runnable r) {
    mGLThread.setEvent(r);
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    mGLThread.requestExitAndWait();
  }

  // ----------------------------------------------------------------------

  public interface GLWrapper {
    GL wrap(GL gl);
  }

  // ----------------------------------------------------------------------

  /**
   * A generic renderer interface.
   */
  public interface Renderer {
    /**
     @return the EGL configuration specification desired by the renderer.
     */
    int[] getConfigSpec();

    /**
     * Surface created. Called when the surface is created. Called when the
     * application starts, and whenever the GPU is reinitialized. This will
     * typically happen when the device awakes after going to sleep. Set
     * your textures here.
     */
    void surfaceCreated(GL10 gl);

    /**
     * Called when the rendering thread is about to shut down. This is a
     * good place to release OpenGL ES resources (textures, buffers, etc).
     
     @param gl
     */
    void shutdown(GL10 gl);

    /**
     * Surface changed size. Called after the surface is created and
     * whenever the OpenGL ES surface size changes. Set your viewport here.
     
     @param gl
     @param width
     @param height
     */
    void sizeChanged(GL10 gl, int width, int height);

    /**
     * Draw the current frame.
     
     @param gl
     */
    void drawFrame(GL10 gl);
  }

  /**
   * An EGL helper class.
   */

  private class EglHelper {
    public EglHelper() {

    }

    /**
     * Initialize EGL for a given configuration spec.
     
     @param configSpec
     */
    public void start(int[] configSpec) {
      /*
       * Get an EGL instance
       */
      mEgl = (EGL10EGLContext.getEGL();

      /*
       * Get to the default display.
       */
      mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

      /*
       * We can now initialize EGL for that display
       */
      int[] version = new int[2];
      mEgl.eglInitialize(mEglDisplay, version);

      EGLConfig[] configs = new EGLConfig[1];
      int[] num_config = new int[1];
      mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1,
          num_config);
      mEglConfig = configs[0];

      /*
       * Create an OpenGL ES context. This must be done only once, an
       * OpenGL context is a somewhat heavy object.
       */
      mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig,
          EGL10.EGL_NO_CONTEXT, null);

      mEglSurface = null;
    }

    /*
     * Create and return an OpenGL surface
     */
    public GL createSurface(SurfaceHolder holder) {
      /*
       * The window size has changed, so we need to create a new surface.
       */
      if (mEglSurface != null) {

        /*
         * Unbind and destroy the old EGL surface, if there is one.
         */
        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
            EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
      }

      /*
       * Create an EGL surface we can render into.
       */
      mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig,
          holder, null);

      /*
       * Before we can issue GL commands, we need to make sure the context
       * is current and bound to a surface.
       */
      mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
          mEglContext);

      GL gl = mEglContext.getGL();
      if (mGLWrapper != null) {
        gl = mGLWrapper.wrap(gl);
      }
      return gl;
    }

    /**
     * Display the current render surface.
     
     @return false if the context has been lost.
     */
    public boolean swap() {
      mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);

      /*
       * Always check for EGL_CONTEXT_LOST, which means the context and
       * all associated data were lost (For instance because the device
       * went to sleep). We need to sleep until we get a new surface.
       */
      return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
    }

    public void finish() {
      if (mEglSurface != null) {
        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
            EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
        mEglSurface = null;
      }
      if (mEglContext != null) {
        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
        mEglContext = null;
      }
      if (mEglDisplay != null) {
        mEgl.eglTerminate(mEglDisplay);
        mEglDisplay = null;
      }
    }

    EGL10 mEgl;
    EGLDisplay mEglDisplay;
    EGLSurface mEglSurface;
    EGLConfig mEglConfig;
    EGLContext mEglContext;
  }

  /**
   * A generic GL Thread. Takes care of initializing EGL and GL. Delegates to
   * a Renderer instance to do the actual drawing.
   
   */

  class GLThread extends Thread {
    GLThread(Renderer renderer) {
      super();
      mDone = false;
      mWidth = 0;
      mHeight = 0;
      mRenderer = renderer;
      setName("GLThread");
    }

    @Override
    public void run() {
      /*
       * When the android framework launches a second instance of an
       * activity, the new instance's onCreate() method may be called
       * before the first instance returns from onDestroy().
       
       * This semaphore ensures that only one instance at a time accesses
       * EGL.
       */
      try {
        try {
          sEglSemaphore.acquire();
        catch (InterruptedException e) {
          return;
        }
        guardedRun();
      catch (InterruptedException e) {
        // fall thru and exit normally
      finally {
        sEglSemaphore.release();
      }
    }

    private void guardedRun() throws InterruptedException {
      mEglHelper = new EglHelper();
      /*
       * Specify a configuration for our opengl session and grab the first
       * configuration that matches is
       */
      int[] configSpec = mRenderer.getConfigSpec();
      mEglHelper.start(configSpec);

      GL10 gl = null;
      boolean tellRendererSurfaceCreated = true;
      boolean tellRendererSurfaceChanged = true;

      /*
       * This is our main activity thread's loop, we go until asked to
       * quit.
       */
      while (!mDone) {

        /*
         * Update the asynchronous state (window size)
         */
        int w, h;
        boolean changed;
        boolean needStart = false;
        synchronized (this) {
          if (mEvent != null) {

            mEvent.run();

          }
          if (mPaused) {
            mEglHelper.finish();
            needStart = true;
          }
          if (needToWait()) {
            while (needToWait()) {
              wait();
            }
          }
          if (mDone) {
            break;
          }
          changed = mSizeChanged;
          w = mWidth;
          h = mHeight;
          mSizeChanged = false;
        }
        if (needStart) {
          mEglHelper.start(configSpec);
          tellRendererSurfaceCreated = true;
          changed = true;
        }
        if (changed) {
          gl = (GL10mEglHelper.createSurface(mHolder);
          tellRendererSurfaceChanged = true;
        }
        if (tellRendererSurfaceCreated) {
          mRenderer.surfaceCreated(gl);
          tellRendererSurfaceCreated = false;
        }
        if (tellRendererSurfaceChanged) {
          mRenderer.sizeChanged(gl, w, h);
          tellRendererSurfaceChanged = false;
        }
        if ((w > 0&& (h > 0)) {

          /* draw a frame here */
          mRenderer.drawFrame(gl);

          /*
           * Once we're done with GL, we need to call swapBuffers() to
           * instruct the system to display the rendered frame
           */

          mEglHelper.swap();

        }

      }

      /*
       * clean-up everything...
       */
      if (gl != null) {
        mRenderer.shutdown(gl);
      }

      mEglHelper.finish();
    }

    private boolean needToWait() {
      return (mPaused || (!mHasFocus|| (!mHasSurface|| mContextLost)
          && (!mDone);
    }

    public void surfaceCreated() {
      synchronized (this) {
        mHasSurface = true;
        mContextLost = false;
        notify();
      }
    }

    public void surfaceDestroyed() {
      synchronized (this) {
        mHasSurface = false;
        notify();
      }
    }

    public void onPause() {
      synchronized (this) {
        mPaused = true;
      }
    }

    public void onResume() {
      synchronized (this) {
        mPaused = false;
        notify();
      }
    }

    public void onWindowFocusChanged(boolean hasFocus) {
      synchronized (this) {
        mHasFocus = hasFocus;
        if (mHasFocus == true) {
          notify();
        }
      }
    }

    public void onWindowResize(int w, int h) {
      synchronized (this) {
        mWidth = w;
        mHeight = h;
        mSizeChanged = true;
      }
    }

    public void requestExitAndWait() {
      // don't call this from GLThread thread or it is a guaranteed
      // deadlock!
      synchronized (this) {
        mDone = true;
        notify();
      }
      try {
        join();
      catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
      }
    }

    /**
     * Queue an "event" to be run on the GL rendering thread.
     
     @param r
     *            the runnable to be run on the GL rendering thread.
     */
    public void setEvent(Runnable r) {
      synchronized (this) {
        mEvent = r;
      }
    }

    public void clearEvent() {
      synchronized (this) {
        mEvent = null;
      }
    }

    private boolean mDone;
    private boolean mPaused;
    private boolean mHasFocus;
    private boolean mHasSurface;
    private boolean mContextLost;
    private int mWidth;
    private int mHeight;
    private Renderer mRenderer;
    private Runnable mEvent;
    private EglHelper mEglHelper;
  }

  private static final Semaphore sEglSemaphore = new Semaphore(1);
  private boolean mSizeChanged = true;

  private SurfaceHolder mHolder;
  private GLThread mGLThread;
  private GLWrapper mGLWrapper;
}

   
原文地址:https://www.cnblogs.com/weinyzhou/p/2750066.html