【Android】OpenGL ESでのテクスチャの再読み込みについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
奥兵

【Android】OpenGL ESでのテクスチャの再読み込みについて

#1

投稿記事 by 奥兵 » 4年前

AndroidでOpenGLESを使用してゲームを作っています。
テクスチャを再読込する部分で詰まったので質問させていただきます。

テクスチャの読み込みと描画はできています。
しかし、アクティビティがポーズ状態になった際にテクスチャは自動的に開放されるようで、再読み込みを行いたいのですがうまくいきません。

アクティビティの再開時に下のクラスのreloadTextureメソッドを呼び出してテクスチャを再読み込みしようとしたのですがうまくいきません。
テクスチャはHashMap型のtexMapで管理しています。keyを画像のリソースIDにし、valueをテクスチャナンバーにして描画時drawメソッドにリソースIDを渡して、それに応じたテクスチャをバインドするようにしています。リソースIDは一度だけですが実験の結果アクティビティのポーズ前後で変化がなかったので、texMapのKey(リソースID)をfor文の中でloadTextureの引数に与えて全て再度読み込みすればいいと考えたのですがうまくいきません。
アドバイスお願いします。

テクスチャの読み込みと描画用クラス(一部略)

コード:

package glView;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;

import javax.microedition.khronos.opengles.GL10;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
public class TexbyArray {
	
	//テクスチャNo
	public int textureNo;
    //表示位置
	float  pos_x;
	float  pos_y;
	float  pos_z;
	//テクスチャ(画像)の位置とサイズ
	int    texX;
	int    texY;
	int    texWidth;
	int    texHeight;
	 
	float  width;
	float  height;
	//表示倍率
	final int DEFAULT_SCALE = 1;
	int scale = 1;
	
	//テクスチャ用連想配列。  
	static HashMap<Integer,Integer> texMap = new HashMap<Integer,Integer>();//(ResorceID,glTextureID)
	
	float uv[] = {
			0.0f, 0.0f,// !< 左上
            0.0f, 1.0f,// !< 左下
            1.0f, 0.0f,// !< 右上
            1.0f, 1.0f,// !< 右下
	};

	public void reloadTexture(GL10 gl ,Resources res){//ハッシュマップに読みこまれたテクスチャ再読み込み		
		
		for (Map.Entry entry : texMap.entrySet()) {// entry.getKey() => キー // entry.getValue() => 値		
			loadTexture(gl,res,(Integer)entry.getKey());
		}
		
	}
	
	Bitmap loadTexture(GL10 gl, Resources res, int id){

		InputStream is = res.openRawResource(id);
		Bitmap bitmap;
    	try{
    		bitmap = BitmapFactory.decodeStream(is);
    	}
    	finally{
    		try{
    			is.close();
    		}
    		catch(IOException e){	}
    	}
    	
    	gl.glEnable(GL10.GL_ALPHA_TEST);
    	gl.glEnable(GL10.GL_BLEND);
    	gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    	gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
    	
    	//テクスチャIDを割り当てる
    	int[] textureID = new int[1];
    	gl.glGenTextures(1, textureID, 0);
    	textureNo = textureID[0];//↓ 連想配列化して管理
    	texMap.put(id, textureNo);

    	//テクスチャIDのバインド
    	gl.glBindTexture(GL10.GL_TEXTURE_2D, textureNo);//textureNo
     	//OpenGL ES用のメモリ領域に画像データを渡す。上でバインドされたテクスチャIDと結び付けられる。
    	GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    	//テクスチャ座標が1.0fを超えたときの、テクスチャを繰り返す設定
    	gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT );
    	gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT );
    	//テクスチャを元のサイズから拡大、縮小して使用したときの色の使い方を設定
    	gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST );
    	gl.glTexParameterx(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST );
    	
    	return bitmap;
	}
	
	public void setTexture( GL10 gl, Resources res, int id ){
		
		setTexPosition(loadTexture(gl, res, id));

	}
	
	void setTexPosition(Bitmap bitmap){
		
		texX      = 0;
    	texY      = bitmap.getHeight();
     	pos_x     = 0;
     	pos_y     = 0;
     	pos_z     = 0;
     	texWidth = bitmap.getWidth();
     	texHeight = bitmap.getHeight();
     	width     = bitmap.getWidth();
     	height    = bitmap.getHeight();
     	
	}
	 
	public void draw( GL10 gl, int id ){

		gl.glBindTexture(GL10.GL_TEXTURE_2D, texMap.get(id));
	    //-------------------------------------------------------------
	    //テクスチャ座標の位置とデータを指定
	    // 位置情報 uvは左上が(0,0)
	    // ※本当は左下が(0,0)だが画像が上下ひっくり返る為、左上が(0,0)と考える

	    ByteBuffer bbuv = ByteBuffer.allocateDirect(uv.length * 4);
	    bbuv.order(ByteOrder.nativeOrder());
	    FloatBuffer fbuv = bbuv.asFloatBuffer();
	    fbuv.put(uv);
	    fbuv.position(0);
	    
	    // テクスチャバッファの成分を有効にする。
	    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	    // 1.要素数uv 2つ
	    // 2.Bufferが格納されている型の指定
	    // 3.第一引数を読み込んだ後、読み込みをスキップする数
	    // 4.値が格納されているバッファの指定
	    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, fbuv);
	    
	    //-------------------------------------------------------------
	    
	    // ! 位置情報
	    float positions[] = {
	    		pos_x				,pos_y					,0,
	    		pos_x				,pos_y+(height*scale)	,0,
	    		pos_x+(width*scale)	,pos_y					,0,
	    		pos_x+(width*scale)	,pos_y+(height*scale)	,0	    		
	    };
	    
	    // ! OpenGLはビッグエンディアンではなく、CPUごとの
	    //ネイティブエンディアンで数値を伝える必要がある。
	    // ! そのためJavaヒープを直接的には扱えず、
	    //java.nio配下のクラスへ一度値を格納する必要がある。
	    ByteBuffer bb = ByteBuffer.allocateDirect(positions.length * 4);
	    bb.order(ByteOrder.nativeOrder());
	    FloatBuffer fb = bb.asFloatBuffer();
	    fb.put(positions);
	    fb.position(0);
	
	    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
	    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fb);
	    // 1.モード(GL_TRIANGLE_STRIP:連続した三角形)
	    // 2.何番目の頂点から描画を行なうかを指定
	    // 3.いくつの頂点を利用するかを指定
	    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
	    
	    //-------------------------------------------------------------
	    //1776 1080
	}
	
	public void draw( GL10 gl, int id ,float _x, float _y ){
		
		setPosition(_x, _y);
		draw(gl, id);
		
	}
			
}

コード:


ISLe()

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#2

投稿記事 by ISLe() » 4年前

奥兵 さんが書きました:アクティビティの再開時に下のクラスのreloadTextureメソッドを呼び出してテクスチャを再読み込みしようとしたのですがうまくいきません。
単純に呼び出すタイミングの問題だけのような気もしますが、提示されたコードからはそれが分かりません。

奥兵

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#3

投稿記事 by 奥兵 » 4年前

ゲームのメイン部分のActivityのonResumeでDialogを表示させています。そのDialogでゲームの再開に当たるボタンを押した際に再読み込み用のメソッドを読んでいます。
それと実験のためにメニューボタンで再読み込みを呼ぶようにしてみたのですが、ゲーム中に再読み込みしてもテクスチャが表示されなくなりました。

コード:

private void DialogBoxMaker(){
		
		 AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

		 // ダイアログの設定
		 //alertDialog.setIcon(R.drawable.f03);   //アイコン設定
		 alertDialog.setTitle("PAUSE");      //タイトル設定
		 alertDialog.setMessage("Exit?");  //内容(メッセージ)設定

		 // OK(肯定的な)ボタンの設定
		 alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
			 public void onClick(DialogInterface dialog, int which){
				 GameMgr.threadStopper();
				 gameTask = null;
				 finish();
				 
				 return;
			 }
		 });

		 // NG(否定的な)ボタンの設定
		 alertDialog.setNegativeButton("NO", new DialogInterface.OnClickListener(){
			 public void onClick(DialogInterface dialog, int which) {
				 
				 myGLView.onResume();
				 
				 if(gameTask != null){// //ゲーム再開時の処理
					 GameMgr.setTasks(gameTask);
					 new TexbyArray() .reloadTexture(GameContext.gl, getResources());//ここです
					 gameTask = null;
				 }
				 
				 GameMgr.threadResumer();
		
				 return;
			 }
		 });

		 // ダイアログの作成と描画
		 alertDialog.create();//
		 alertDialog.show();

	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if(keyCode==KeyEvent.KEYCODE_BACK){
			GameMgr.threadPoser();
			DialogBoxMaker();
			return true;
		}else
		if(keyCode==KeyEvent.KEYCODE_MENU){
			new TexbyArray() .reloadTexture(GameContext.gl, getResources());	
			return true;
		}
		return false;
	}

ISLe()

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#4

投稿記事 by ISLe() » 4年前

ここで使われているGLコンテキストは、GLSurfaceViewのサーフェースが再構築されたあとのGLコンテキストでしょうか。
ActivityのonResumeが呼び出されるのは、GLSurfaceViewのサーフェースの再構築が行われた後でしたっけ。

PCでもOpenGLの命令は異なるスレッドコンテキストでは動作しなかったりするので、GLSurfaceViewのイベント(から呼び出される)メソッドで処理しないとダメだったりするかもしれませんね。

奥兵

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#5

投稿記事 by 奥兵 » 4年前

Logを使って実験してみたところ、再開ボタンを押した瞬間(再読み込みが呼ばれる瞬間)に、onSurfaceCreatedが呼ばれたので、
再構築される前のGLコンテキスを使用していました。
ただ、ActivityをPauseさせずにテクスチャ再読み込みメソッドを呼ぶと(Menuキーに登録)、画面が真っ白になってしまうので、どうも再読み込みようメソッドもしくは、クラスそのものの設計がおかしい気がします。
GLコンテキストの部分も不自然なので直さねばなりませんが・・

ISLe()

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#6

投稿記事 by ISLe() » 4年前

Menuからメソッドを呼び出して不具合が出るのは、先の投稿に書いたようにスレッドのコンテキストの問題ではないでしょうか。

むかしウィンドウズアプリでOpenGLを使ったとき、同様にテクスチャにピクセルを書き込めない現象にあって以来、イベントスレッドから直接テクスチャにアクセスするようなコードは避けています。
イベントメソッドではフラグを立てるだけにして、常に同じスレッドでOpenGLの命令を使うというふうに。

Androidで不具合を出した経験が不足しているので具体的なアドバイスにならなくてすみません。

奥兵

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#7

投稿記事 by 奥兵 » 4年前

とても返事が遅くなりましたが、解決できたので報告させていただきます。
結論からいいますと、ISLeさんのご指摘のとおりでした。
RendererクラスのonSurfaceCreated内でreloadTextureメソッドを呼び出すと、無事読み込むことが出来ました。ありがとうございました。

遅くなった言い訳をさせていただくと、reloadTextureを原因と疑っていたので、ご指摘いただいた部分を試す前にglTexSubImage2D等々同メソッドに対してそれっぽいメソッドを探しては手当たり次第実験してみました。
そして、全てうまく行かず精魂尽き果てモチベーションも使い果たし、暫く逃避しておりました。

最期に蛇足が付きましたが、おかげさまで解決することが出来ました。適切な指摘ありがとうございました。

ISLe()

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#8

投稿記事 by ISLe() » 4年前

奥兵 さんが書きました:そして、全てうまく行かず精魂尽き果てモチベーションも使い果たし、暫く逃避しておりました。
それ分かる気がします。
まったく異常を示すものはないのに、まるで命令が無視されたかのようになるのですよね。

先の投稿に書いた、むかし作ったウィンドウズアプリで経験しました。
Linux向けに書かれたサンプルを参考にして、Linuxでは表示されるのにウィンドウズでは表示されない。
プログラム自体は動作するし表示されないだけでエラーも出ないし原因特定は試行錯誤しかありませんでしたね。

奥兵

Re: 【Android】OpenGL ESでのテクスチャの再読み込みについて

#9

投稿記事 by 奥兵 » 4年前

時間と労力を消費しつつ、ただ焦りを募らせる一方でした。
しかも、まったく想像しなかった原因で、自力では到底解決不能な問題でした。
本当にありがとうございました。とても助かりました。

閉鎖

“C言語何でも質問掲示板” へ戻る