SSブログ

Androidアプリ開発 OpenGL 視点をスクロール(スライド)で操作する [Android OpenGL]

みなさん、こんにちは、本日もAndroidアプリを作っていこうと思います。

視点位置をgluLookAtで変更することができた。端末をタップすることでこれを動的に変えていこうと思う。OpenGLというかandroidの方のネタになる。

OpenGLは、iPhoneやPCでも使用できる。プラットフォームを選ばない、まさしくオープンなものである。なので、androidでの決まりは、OpenGLには持ち込むことはできない。

「モデルを作ったからクリックとかタップは、そっちでなんとかして」

というのは「まかりとおらない」話なのである。

そういった、感じなので、タップなどのイベントは、androidの世界で処理してやらなければならない。アクティビティでonTouchをオーバーライドすればタップした位置は取れるが、ドラッグ(スライド)はゼスチャーっていうのを使ってイベントを別途処理してやる必要がある。

androidは日々進化している。めざましい進化なのではあるが、すぐに「レガシー(時代遅れ)」になってしまう。まぁ、嘆いてもしょうがないことではある。そのうち程良く「枯れる」ことになるであろう。

「早くコードがみたいなぁ」

そうでした。まずは、アクティビティでゼスチャー検出機を作る。

ゼスチャー検出機といっても、ハードウェアではなくSDK中のクラスで提供されているもの。イベントを拾ったら、これをゼスチャーに処理してもらう。ゼスチャーが、イベントを処理して、これはダブルタップだな、とか時間が開いているからロングタップにしよう、といった処理が行われて、リスナーのonLongPressが呼び出される、といった具合。

久しぶりにアクティビティのコードに登場してもらおう。

public class TestOpenGLActivity extends Activity {
	private GestureDetector gesDetector = null;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//setContentView(R.layout.main);
		// OpenGL用のビューを作る
		SampleGLSurfaceView view = new SampleGLSurfaceView(this);
		// アクティビティにビューを設定
		setContentView(view);
		// ゼスチャー
		gesDetector = new GestureDetector(this, view);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event){
		return gesDetector.onTouchEvent(event);
	}
}


GestureDetector gesDetectorがゼスチャー検出機。コンストラクタで作成している。GestureDetectorのコンストラクタには引数がふたつ必要。最初の引数は、Context。アクティビティを入れればOK。ふたつ目の引数は、イベントを受けるためのリスナー。OpenGLのビューで受けたいのでviewを指定する。

onTouchEventをオーバーライドしてイベントを一度アクティビティで受けるようにする。イベントがどんどん上がってくるので、そのままゼスチャー検出機にかけてしまう。戻り値もそのまま返してしまう。

アクティビティはこれでOK。SampleGLSurfaceViewの方にイベント処理部分を作っていく。GestureDetector(this, view)のところがviewがリスナーになっていないので、エラーになっているはず。クイックフィックスを使って、SampleGLSurfaceViewクラスにインターフェース「OnGestureListener」の実装を指示する。

public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener {


今度は、SampleGLSurfaceViewにメソッドが足りないエラーになるので、必要なメソッドをクイックフィックスで作成する。
とりあえず、ドラッグ(スライド)したときに視点を変えたいので、onScrollのみ内容を書く。後はほっておく。

	@Override
	public boolean onScroll(MotionEvent event1, MotionEvent event2, float distx, float disty) {
		eyepos[0] += distx * 0.01;
		eyepos[1] += disty * 0.01;
		return true;
	}


引数の名前も変えている。onScrollには4つの引数でイベントが渡ってくる。詳細はSDKのマニュアルを参照して欲しい。移動量がdistxとdistyでわかるので、視点位置も移動させている。

public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener {
	private Model model = new Model();
	private float angle = 0.0f;
	private float eyepos[] = new float[3];


eyeposは、クラスのフィールドとして定義した。floatの配列で、x,y,z座標の値を記憶する。

		@Override
		public void onSurfaceCreated(GL10 gl, EGLConfig config) {
			gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
			gl.glClearDepthf(1.0f);
			eyepos[0] = 0;
			eyepos[1] = 0;
			eyepos[2] = 3;
		}


初期値は、レンダラーのonSurfaceCreatedで設定するようにした。
おっと、カメラ位置もeyeposにしなくては。

		@Override
		public void onDrawFrame(GL10 gl) {
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
			// カメラ位置を設定
			GLU.gluLookAt(gl, eyepos[0], eyepos[1], eyepos[2], 0, 0, 0, 0, 1, 0);


これで、スライドさせれば、視点が動くはず...

しかし、期待外れに終わってしまった。
いくらスライドさせても一向に変化しない。何か間違えたか...

イベントは来ている。eyeposの値も変化している。いろいろ調べった結果、gluLookAtの場所がまずいことが判明。glMatrixMode(GL_MODELVIEW)の後でやるとうまく動いた。
PROJECTIONを選択した後で、やらないといけないことはわかっていたのだが... どっちが正解なんだろうか。

とりあえず動いたので、このままでいくことにする。以下の動画は画面上でスライドさせて三角形を動かしている。三角形のローテーションは止めてある。screen castによる動画キャプチャなので、指が映っていないので、なんか実感がないが... スマホのカメラで動画撮影もやってはみたものの、やっぱりきれいじゃない。これで、かんべんして。



念のため、SampleGLSurfaceViewの全体も貼っておく。

public class SampleGLSurfaceView extends GLSurfaceView implements OnGestureListener {
	private Model model = new Model();
	private float angle = 0.0f;
	private float eyepos[] = new float[3];
	// レンダラークラス
	class OpenGLRenderer implements Renderer {
		float lightpos[] = {0.0f, 0.0f, 4.0f, 0.0f};
		float red[] = {1.0f, 0.0f, 0.0f, 1.0f};
		float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
		float blue[] = {0.0f, 0.0f, 1.0f, 1.0f};
		float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
		float gray[] = {0.5f, 0.5f, 0.5f, 1.0f};
		float yellow[] = {1.0f, 1.0f, 0.0f, 1.0f};

		@Override
		public void onDrawFrame(GL10 gl) {
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
			// ライティングをON
			gl.glEnable(GL10.GL_LIGHTING);
			// 光源を有効にして位置を設定
			gl.glEnable(GL10.GL_LIGHT0);
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightpos, 0);
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, red, 0);
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, blue, 0);
			gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, yellow, 0);
			gl.glMatrixMode(GL10.GL_MODELVIEW);
			gl.glLoadIdentity();
			// カメラ位置を設定
			GLU.gluLookAt(gl, eyepos[0], eyepos[1], eyepos[2], 0, 0, 0, 0, 1, 0);
			gl.glPushMatrix();		// マトリックス記憶
			gl.glTranslatef(1, 0, 0);
			gl.glRotatef(angle, 0, 1, 0);
			gl.glEnable(GL10.GL_CULL_FACE);
			gl.glCullFace(GL10.GL_BACK);
			// デプステスト
			gl.glEnable(GL10.GL_DEPTH_TEST);
			gl.glDepthFunc(GL10.GL_LEQUAL);
			gl.glDepthMask(true);

			gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, gray, 0);
			gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, gray, 0);
			gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, gray, 0);
			gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f);
			gl.glShadeModel(GL10.GL_FLAT);
			model.draw(gl);
			gl.glPopMatrix();		// マトリックスを戻す

			// ふたつめの描画
			gl.glPushMatrix();		// マトリックス記憶
			gl.glTranslatef(-1, 0, 0);
			gl.glRotatef(angle, 0, 1, 0);
			gl.glCullFace(GL10.GL_BACK);
			gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, gray, 0);
			gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, gray, 0);
			gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, gray, 0);
			gl.glMaterialf(GL10.GL_FRONT_AND_BACK, GL10.GL_SHININESS, 80f);
			// スムースシェーディング
			gl.glShadeModel(GL10.GL_SMOOTH);
			model.draw(gl);
			gl.glPopMatrix();		// マトリックスを戻す
			//angle += 0.5;			// 回転角度は最後に計算
		}

		@Override
		public void onSurfaceChanged(GL10 gl, int width, int height) {
			gl.glViewport(0, 0, width, height);
			gl.glMatrixMode(GL10.GL_PROJECTION);
			gl.glLoadIdentity();
			GLU.gluPerspective(gl, 50f, (float)width / height, 0.01f, 100f);
		}

		@Override
		public void onSurfaceCreated(GL10 gl, EGLConfig config) {
			gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
			gl.glClearDepthf(1.0f);
			eyepos[0] = 0;
			eyepos[1] = 0;
			eyepos[2] = 3;
		}
	}
	private OpenGLRenderer renderer;

	// サーフェースビューのコンストラクタ
	public SampleGLSurfaceView(Context context) {
		super(context);
		setEGLConfigChooser(8, 8, 8, 8, 16, 0);
		renderer = new OpenGLRenderer();
		setRenderer(renderer);
	}

	@Override
	public boolean onDown(MotionEvent arg0) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}

	@Override
	public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
			float arg3) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}

	@Override
	public void onLongPress(MotionEvent arg0) {
		// TODO 自動生成されたメソッド・スタブ
		
	}

	@Override
	public boolean onScroll(MotionEvent event1, MotionEvent event2, float distx, float disty) {
		eyepos[0] += distx * 0.01;
		eyepos[1] += disty * 0.01;
		return true;
	}

	@Override
	public void onShowPress(MotionEvent arg0) {
		// TODO 自動生成されたメソッド・スタブ
		
	}

	@Override
	public boolean onSingleTapUp(MotionEvent arg0) {
		// TODO 自動生成されたメソッド・スタブ
		return false;
	}

}






サイト内を検索

タグ:OpenGL Android
nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。



Copyright Atsushi Asai Google+朝井淳
[改訂第4版]SQLポケットリファレンス

[改訂第4版]SQLポケットリファレンス

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2017/02/18
  • メディア: 単行本(ソフトカバー)

イラストで理解 SQL はじめて入門

イラストで理解 SQL はじめて入門

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/05/16
  • メディア: 単行本(ソフトカバー)

[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)

[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2015/03/03
  • メディア: 単行本(ソフトカバー)

Access クエリ 徹底活用ガイド ~仕事の現場で即使える

Access クエリ 徹底活用ガイド ~仕事の現場で即使える

  • 作者: 朝井 淳
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/05/25
  • メディア: 大型本

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。