[ヅ] はじめての OpenGL ES 1.0 for Android. 立方体を表示するサンプル (2013-08-16) を改造して作ってみた。

サンプル画像。

OpenGL ES 1.0 for Android で立方体を操作する

OpenGL ES 1.0 for Android で立方体を操作する

apkファイル: openglsample.apk

以下、延々とサンプルコード。

OpenGL0.java


package com.example.openglsample;
 
import net.shisashi.android.widget.LongClickRepeatAdapter;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
 
public class OpenGL0 extends Activity {
 
  private TextView eyeText;
  private Button eyeXPlusButton;
  private Button eyeXMinusButton;
  private Button eyeYPlusButton;
  private Button eyeYMinusButton;
  private Button eyeZPlusButton;
  private Button eyeZMinusButton;
  
  private MyGLView myGLView;

  @Override
  public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    myGLView = new MyGLView(this);

    eyeText = new TextView(this);
    updateEyeText();
    
    eyeXPlusButton = new Button(this);
    eyeXPlusButton.setText("eyeX[+]");
    eyeXPlusButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        myGLView.getMyRenderer().setEyeX(OpenGL0.this.myGLView.getMyRenderer().getEyeX() + 1.0f);
        update();
      }
    });
    eyeXMinusButton = new Button(this);
    eyeXMinusButton.setText("eyeX[-]");
    eyeXMinusButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        myGLView.getMyRenderer().setEyeX(OpenGL0.this.myGLView.getMyRenderer().getEyeX() - 1.0f);
        update();
      }
    });
    eyeYPlusButton = new Button(this);
    eyeYPlusButton.setText("eyeY[+]");
    eyeYPlusButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        myGLView.getMyRenderer().setEyeY(OpenGL0.this.myGLView.getMyRenderer().getEyeY() + 1.0f);
        update();
      }
    });
    eyeYMinusButton = new Button(this);
    eyeYMinusButton.setText("eyeY[-]");
    eyeYMinusButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        myGLView.getMyRenderer().setEyeY(OpenGL0.this.myGLView.getMyRenderer().getEyeY() - 1.0f);
        update();
      }
    });
    eyeZPlusButton = new Button(this);
    eyeZPlusButton.setText("eyeZ[+]");
    eyeZPlusButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        myGLView.getMyRenderer().setEyeZ(OpenGL0.this.myGLView.getMyRenderer().getEyeZ() + 1.0f);
        update();
      }
    });
    eyeZMinusButton = new Button(this);
    eyeZMinusButton.setText("eyeZ[-]");
    eyeZMinusButton.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        myGLView.getMyRenderer().setEyeZ(OpenGL0.this.myGLView.getMyRenderer().getEyeZ() - 1.0f);
        update();
      }
    });
    LinearLayout sideLayout = new LinearLayout(this);
    sideLayout.setOrientation(LinearLayout.VERTICAL);
    sideLayout.addView(eyeText);
    sideLayout.addView(eyeXPlusButton);
    sideLayout.addView(eyeXMinusButton);
    sideLayout.addView(eyeYPlusButton);
    sideLayout.addView(eyeYMinusButton);
    sideLayout.addView(eyeZPlusButton);
    sideLayout.addView(eyeZMinusButton);
    // ボタンのキーリピートを実装
    LongClickRepeatAdapter.bless(eyeXPlusButton);
    LongClickRepeatAdapter.bless(eyeXMinusButton);
    LongClickRepeatAdapter.bless(eyeYPlusButton);
    LongClickRepeatAdapter.bless(eyeYMinusButton);
    LongClickRepeatAdapter.bless(eyeZPlusButton);
    LongClickRepeatAdapter.bless(eyeZMinusButton);
 
    LinearLayout mainLayout = new LinearLayout(this);
    mainLayout.setOrientation(LinearLayout.HORIZONTAL);
    mainLayout.addView(sideLayout);
    mainLayout.addView(myGLView);
    
    setContentView(mainLayout);
  }
 
  @Override
  protected void onResume() {
    super.onResume();
    myGLView.onResume();
  }

  @Override
  protected void onPause() {
    super.onPause();
    myGLView.onPause();
  }
  
  private void update(){
    updateEyeText();
  }
  
  private void updateEyeText(){
    eyeText.setText(
      "(" +
      OpenGL0.this.myGLView.getMyRenderer().getEyeX() + 
      "," +
      OpenGL0.this.myGLView.getMyRenderer().getEyeY() + 
      "," +
      OpenGL0.this.myGLView.getMyRenderer().getEyeZ() + 
      ")"
    );
  }
}

MyGLView.java


package com.example.openglsample;
 
import android.content.Context;
import android.opengl.GLSurfaceView;
 
public class MyGLView extends GLSurfaceView {
 
  private MyRenderer myRenderer;
 
  public MyGLView(Context context) {
    super(context);
    myRenderer = new MyRenderer();
    setRenderer(myRenderer);
  }
  
  public MyRenderer getMyRenderer(){
    return myRenderer;
  }
}

MyRenderer.java


package com.example.openglsample;
 
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
 
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;
 
/**
 * GLSurfaceView 内部から適切なタイミングで呼び出されるクラス。
 */
public class MyRenderer implements Renderer {
 
  private MyCube cube = new MyCube();
  private int viewportWidth;
  private int viewportHeight;
  
  // カメラの位置
  private float eyeX = 0;
  private float eyeY = 0;
  private float eyeZ = 10.0f;
  
  /**
   * GLSurfaceView 用のメモリ確保が終了したタイミングで呼ばれるメソッド。
   */
  @Override
  public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    gl.glEnable(GL10.GL_DEPTH_TEST);
    
    // アルファブレンドを有効にする
    gl.glEnable(GL10.GL_BLEND);
    gl.glEnable(GL10.GL_ALPHA);
    gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    
    // ライト
    gl.glEnable(GL10.GL_LIGHTING);
    gl.glEnable(GL10.GL_LIGHT0);
    float[] lightColor = {1.0f, 0.0f, 0.0f, 0.5f}; // 赤いライト
    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightColor, 0);
    gl.glDepthFunc(GL10.GL_LEQUAL);
  }
  
  /**
   * GLSurfaceView 用の画面サイズが変更されたタイミングで呼ばれるメソッド。
   * @param width GLSurfaceView の縦ピクセル数
   * @param height GLSurfaceView の横ピクセル数
   */
  @Override
  public void onSurfaceChanged(GL10 gl, int width, int height) {
    final int margin = 30;
    viewportWidth = width - (margin * 2);
    viewportHeight = height - (margin * 2);
    gl.glViewport(0 + margin, 0 + margin, viewportWidth, viewportHeight);
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    GLU.gluPerspective(gl, 45f, (float) width / height, 1f, 50f);
  }
 
  /**
   * GLSurfaceView の再描画が必要なタイミングで呼ばれるメソッド。
   */
  @Override
  public void onDrawFrame(GL10 gl) {
    
    gl.glClearColor(0.5f, 1.0f, 1.0f, 1.0f); // 背景を水色に
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    
    // カメラ
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity(); // 行列をリセット
    float fovy = 45.0f; // Y方向の画角
    float aspect = (float)viewportWidth / (float)viewportHeight; // 画面の縦横比
    float zNear = 0.01f; // ニアクリップ。これよりカメラに近いオブジェクトはカメラに映らない
    float zFar = 100.0f; // ファークリップ。これより遠いオブジェクトはカメラに映らない
    GLU.gluPerspective(gl, fovy, aspect, zNear, zFar);
    // 注視位置(被写体)
    float centerX = 0;
    float centerY = 0;
    float centerZ = 0;
    // カメラの天井方向を示すベクトル
    float upX = 0;
    float upY = 1;
    float upZ = 0;
    GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
    
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity(); // 行列をリセット
    gl.glTranslatef(0, 0, -3f);
    gl.glRotatef(30f, 0.8f, 1, 0);
    
    // 3Dオブジェクトの描画
    cube.draw(gl);
  }
  
  public float getEyeX() {
    return eyeX;
  }
 
  public void setEyeX(float eyeX) {
    this.eyeX = eyeX;
  }
  
  public float getEyeY() {
    return eyeY;
  }
 
  public void setEyeY(float eyeY) {
    this.eyeY = eyeY;
  }
 
  public float getEyeZ() {
    return eyeZ;
  }
 
  public void setEyeZ(float eyeZ) {
    this.eyeZ = eyeZ;
  }
}

MyCube.java


package com.example.openglsample;
 
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
 
import javax.microedition.khronos.opengles.GL10;
 
public class MyCube {
 
  private final FloatBuffer mVertexBuffer;
 
  public MyCube() {
 
    float vertices[] = {
      // 前
      -0.5f, -0.5f,  0.5f,
       0.5f, -0.5f,  0.5f,
      -0.5f,  0.5f,  0.5f,
       0.5f,  0.5f,  0.5f,
      
      // 後
      -0.5f, -0.5f, -0.5f,
       0.5f, -0.5f, -0.5f,
      -0.5f,  0.5f, -0.5f,
       0.5f,  0.5f, -0.5f,
      
      // 左
      -0.5f, -0.5f,  0.5f,
      -0.5f, -0.5f, -0.5f,
      -0.5f,  0.5f,  0.5f,
      -0.5f,  0.5f, -0.5f,
      
      // 右
       0.5f, -0.5f,  0.5f,
       0.5f, -0.5f, -0.5f,
       0.5f,  0.5f,  0.5f,
       0.5f,  0.5f, -0.5f,
      
      // 上
      -0.5f,  0.5f,  0.5f,
       0.5f,  0.5f,  0.5f,
      -0.5f,  0.5f, -0.5f,
       0.5f,  0.5f, -0.5f,
      
      // 底
      -0.5f, -0.5f,  0.5f,
       0.5f, -0.5f,  0.5f,
      -0.5f, -0.5f, -0.5f,
       0.5f, -0.5f, -0.5f
    };
  
    // OpenGL は CPU ごとのネイティブエンディアンでデータを指定する必要がある
    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
    vbb.order(ByteOrder.nativeOrder());
    mVertexBuffer = vbb.asFloatBuffer();
    mVertexBuffer.put(vertices);
    mVertexBuffer.position(0);
  }
 
  public void draw(GL10 gl) {
 
    //gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); // 赤で描画
 
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
    
    // Front
    gl.glNormal3f(0, 0, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
 
    // Back
    gl.glNormal3f(0, 0, -1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
 
    // Left
    gl.glNormal3f(-1.0f, 0, 0);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
 
    // Right
    gl.glNormal3f(1.0f, 0, 0);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
 
    // Top
    gl.glNormal3f(0, 1.0f, 0);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
 
    // Right
    gl.glNormal3f(0, -1.0f, 0);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
  }
}

LongClickRepeatAdapter.java

AndroidのViewに、長押ししたらクリック処理をリピートする処理を付加するアダプタ をそのまま利用。


package net.shisashi.android.widget;
 
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
 
public class LongClickRepeatAdapter {
 
  /**
   * 連続してボタンを押す間隔のデフォルト値 (ms)
   */
  private static final int REPEAT_INTERVAL = 100;
 
  /**
   * Viewに長押し時のリピート処理を付加する。 リピート間隔は100ms。
   * 
   * @param view
   *      付加対象のView
   */
  public static void bless(View view) {
    bless(REPEAT_INTERVAL, view);
  }
 
  /**
   * リピート間隔を指定して、Viewに長押しリピート処理を付加する
   * 
   * @param repeatInterval
   *      連続してボタンを押す間隔(ms)
   * @param view
   *      付加対象のView
   */
  public static void bless(final int repeatInterval, final View view) {
    final Handler handler = new Handler();
    final BooleanWrapper isContinue = new BooleanWrapper(false);
 
    final Runnable repeatRunnable = new Runnable() {
      @Override
      public void run() {
        // 連打フラグをみて処理を続けるか判断する
        if (!isContinue.value) {
          return;
        }
 
        // クリック処理を実行する
        view.performClick();
 
        // 連打間隔を過ぎた後に、再び自分を呼び出す
        handler.postDelayed(this, repeatInterval);
      }
    };
 
    view.setOnLongClickListener(new OnLongClickListener() {
      @Override
      public boolean onLongClick(View v) {
        isContinue.value = true;
 
        // 長押しをきっかけに連打を開始する
        handler.post(repeatRunnable);
 
        return true;
      }
    });
 
    // タッチイベントを乗っ取る
    view.setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        // キーから指が離されたら連打をオフにする
        if (event.getAction() == MotionEvent.ACTION_UP) {
          isContinue.value = false;
        }
        return false;
      }
    });
  }
 
  private static class BooleanWrapper {
    public boolean value;
 
    public BooleanWrapper(boolean value) {
      this.value = value;
    }
  }
}

Ref. [ヅ] はじめての OpenGL ES 1.0 for Android. 立方体を表示するサンプル (2013-08-16)

tags: android opengl

Posted by NI-Lab. (@nilab)