LISMO! CUBE☆
LISMOルービックキューブはじめました
しかしこのLISMOルービックキューブ、各面の絵にほとんど差が無くまた向きも分からず、正解見本を見ながらじゃないと完成させる事は非常に難しい…いつでもどこでも正解見本を確認出来るandroidアプリが必須な訳です(?
とりあえず思いつくのがOpenGL。というわけで OpenGL on android に初挑戦です!多分立方体を描いてLISMOキューブの各面の写真をそれぞれテクスチャとして貼り付ければそれっぽくなるんではなかろうか?簡単そう♪
立方体を描くまではググりながら適当にサンプルを参考にしながらすぐ出来ました。しかしテクスチャを貼り付けるところが訳わからん!なんじゃこりゃ
いろんなサイトを見て回った結果、Googleさん作(多分)の GLTutorialBase.java というのを使えば結構簡単にテクスチャが張れるらしいことが分かったのでこれを使わせて頂きました
サンプルコードを参考に、テクスチャを張ることは出来ました(テクスチャが表裏逆に張られたり、色がおかしかったりという問題はあったけどもとりあえず無視!)が複数面にそれぞれ別のテクスチャを貼り付けるにはどうすればいいのか分からない…なかなかはまりました。ググってもそれらしき情報がなかなか見つからない!
一通りサイトを見終えた後、どうも glBindTextureメソッドを呼び出すことでテクスチャの切り替えが出来るような気がする…という見解に至りました
ということで各面を描く前に毎回コレを呼び出して見ると…一応成功したっぽい。やったぜ!!
(もっとよい方法があれば教えてください)
あとはテクスチャの座標を試行錯誤しながら手直しして(実はこれが一番めんどかったりw)、その後GLTutorialBase.java中のloadTextureメソッドを改良しました
for文でものすごいループをしていてテクスチャ数を増やすと読み込みに莫大な時間がかかってしまっていたのでfor文を削除してGLUtils.texImage2Dメソッドに変えてみると色も直ったし読み込み速度も高速化しました
最後にキューブをタッチで回転出来るようにタッチイベントメソッドを適当に書いて完了!本当に適当なので完璧な動作はしません…回転って難しいですねorz
完成!
一応最初想定していたものは完成しました。クオリティは別にして…。あとはこれを見ながら本物を楽しむだけですなw
ココ にapkファイルを置いてますので自己責任で是非ダウンロードしてみてください!ただしちゃんとダウンロード出来るのかは不明です(EVOでは確認済。けど普通は出来ないという噂も…)
終わりに
はじめてOpenGL on androidに挑戦したわけですが思っていた以上に特殊で難しい!まだよく分からない…でもまだまだ奥が深いし複雑なことも出来るOpenGL、とても楽しそう^^ (多分もうやらないけど)
では最後に汚いソース晒しましょう!
メイン LismoCube.java
短!w
package lismocube.app; import android.app.Activity; import android.os.Bundle; import android.view.View; public class LismoCube extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View v=new Cube(this); setContentView(v); } }
図形 (これがメインっぽい) Cube.java
Googleさんのサンプルコードをほぼ丸パクリですが…一応。ちょこちょこ変えてたりもする
package lismocube.app; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.MotionEvent; public class Cube extends GLTutorialBase{ float lightAmbient[] = new float[] { 0.3f, 0.3f, 0.3f, 1.0f }; float lightDiffuse[] = new float[] { 0.6f, 0.6f, 0.6f, 1.0f }; float[] lightPos = new float[] {0,0,3,1}; //素材? float matAmbient[] = new float[] { 1f, 1f, 1f, 1.0f }; float matDiffuse[] = new float[] { 1f, 1f, 1f, 1.0f }; int tex[]; //テクスチャ Bitmap[] bmps=new Bitmap[6]; テクスチャ用画像格納用 float box[] = new float[] { // FRONT -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, // BACK -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, // LEFT -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, // RIGHT 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, // TOP -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, // BOTTOM -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, }; float texCoords[] = new float[] { // FRONT 0.0f, 0.0f, //左下 0.0f, 1.0f, //右下 1.0f, 0.0f, //左上 1.0f, 1.0f, //右上 // BACK 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // LEFT 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // RIGHT 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // TOP 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // BOTTOM 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; FloatBuffer cubeBuff; FloatBuffer texBuff; //回転角 float xrot = 0.0f; float yrot = 0.0f; float zrot = 0.0f; //一つ前の状態の座標(タッチイベントで使用) float pX=0.0f; float pY=0.0f; int flag=0; //現在の回転状態を表す private final float TOUCH_SCALE_FACTOR = 180.0f / 320; //scale変換? //コンストラクタ public Cube(Context c) { super(c, 20); cubeBuff = makeFloatBuffer(box); texBuff = makeFloatBuffer(texCoords); bmps[0] = BitmapFactory.decodeResource(c.getResources(), R.drawable.img04); bmps[1] = BitmapFactory.decodeResource(c.getResources(), R.drawable.img02); bmps[2] = BitmapFactory.decodeResource(c.getResources(), R.drawable.img03); bmps[3] = BitmapFactory.decodeResource(c.getResources(), R.drawable.img01); bmps[4] = BitmapFactory.decodeResource(c.getResources(), R.drawable.img05); bmps[5] = BitmapFactory.decodeResource(c.getResources(), R.drawable.img06); setFocusable(true); } protected void init(GL10 gl) { gl.glEnable(GL10.GL_LIGHTING); gl.glEnable(GL10.GL_LIGHT0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, matAmbient, 0); gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, matDiffuse, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl.glClearDepthf(1.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuff); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glEnable(GL10.GL_CULL_FACE); gl.glShadeModel(GL10.GL_SMOOTH); tex = loadTexture(gl, bmps); } //描画 繰り返し何度も呼ばれる protected void drawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0,0,-3.3f); //視点 //回転 gl.glRotatef(xrot, 0, 1, 0); gl.glRotatef(yrot, 1, 0, 1); gl.glRotatef(zrot, 0, 0, 1); //面を描く。あとテクスチャ切り替えも gl.glBindTexture(GL10.GL_TEXTURE_2D, tex[0]); gl.glNormal3f(0,0,1); //法線ベクトル gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); gl.glBindTexture(GL10.GL_TEXTURE_2D, tex[1]); gl.glNormal3f(0,0,-1); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4); gl.glBindTexture(GL10.GL_TEXTURE_2D, tex[2]); gl.glNormal3f(-1,0,0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4); gl.glBindTexture(GL10.GL_TEXTURE_2D, tex[3]); gl.glNormal3f(1,0,0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4); gl.glBindTexture(GL10.GL_TEXTURE_2D, tex[4]); gl.glNormal3f(0,1,0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4); gl.glBindTexture(GL10.GL_TEXTURE_2D, tex[5]); gl.glNormal3f(0,-1,0); gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4); } //角度の調整。名前適当w private void checkV(){ if(xrot>=360) xrot=xrot-360; if(yrot>=360) yrot=yrot-360; if(zrot>=360) zrot=zrot-360; if(xrot<0) xrot=xrot+360; if(yrot<0) yrot=yrot+360; if(zrot<0) zrot=zrot+360; //X軸の回転状態によってYが回転するのかZが回転するのかを決める(不十分だけど…) if((xrot>=45 && xrot<=135)){ flag=1; //Z roll }else if((xrot>=225 && xrot<=315)){ flag=2; //Z roll - }else if(xrot>135 && xrot<225){ flag=3; //Y roll }else{ flag=0; //Y roll - } } //タッチイベント public boolean onTouchEvent(MotionEvent e) { float x=e.getX(); float y=e.getY(); switch (e.getAction()){ case MotionEvent.ACTION_MOVE: float dx=x-pX; float dy=y-pY; xrot+=dx*TOUCH_SCALE_FACTOR; switch(flag){ case 1: zrot+=dy*TOUCH_SCALE_FACTOR; break; case 2: zrot-=dy*TOUCH_SCALE_FACTOR; break; case 3: yrot-=dy*TOUCH_SCALE_FACTOR; break; default: yrot+=dy*TOUCH_SCALE_FACTOR; break; } checkV(); //0〜360度を超えたら1回転させて戻す break; } pX=x; pY=y; return true; } }
例の GLTutorialBase.java の変えたところだけ
loadTextureメソッドの変更以外は全く同じ
protected static int[] loadTexture(GL10 gl, Bitmap[] bmps) { int[] texs = new int[bmps.length]; //テクスチャのIDが格納される gl.glGenTextures(bmps.length, texs, 0); for(int i=0;i<bmps.length;i++){ gl.glBindTexture(GL10.GL_TEXTURE_2D, texs[i]); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); GLUtils.texImage2D( GL10.GL_TEXTURE_2D, 0, bmps[i], 0 ); bmps[i].recycle(); } return texs; }
あとは…略!(サンプルに付いてたViewAnimater.java)