不完全ながらついにAndroid端末上でmqoを表示させるときが来た!テクスチャや色は無視して真っ黒にしていたり、ポリゴンは三角形にしか対応してないので、メタセコイアで四角形面を三角形化しておかないと表示できないが、とにかく先に表示してみよう。ということで早速OpenGLを使いたいところだが、まずは今までのプログラムを修正する。MQOObjectとMQOContainerを修正して、全ての頂点数と面の数を取得できるようにしておく
package net.d_kami.mqo; import java.util.List; import java.util.ArrayList; public class MQOObject{ private List<MQOVertex> vertices; private List<MQOFace> faces; public MQOObject(){ vertices = new ArrayList<MQOVertex>(); faces = new ArrayList<MQOFace>(); } public void addVertex(float x, float y, float z){ vertices.add(new MQOVertex(x, y, z)); } public void addFace(String[] elements){ if(elements.length == 3){ addFace(Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[2])); }else if(elements.length == 4){ addFace(Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[2]), Integer.parseInt(elements[3])); } } public void addFace(int index0, int index1, int index2){ faces.add(new MQOFace(index0, index1, index2)); } public void addFace(int index0, int index1, int index2, int index3){ faces.add(new MQOFace(index0, index1, index2, index3)); } public MQOVertex getVertex(int index){ return vertices.get(index); } public MQOFace getFace(int index){ return faces.get(index); } public List<MQOVertex> getVertices(){ return vertices; } public List<MQOFace> getFaces(){ return faces; } public int getVertexCount(){ return vertices.size(); } public int getFaceCount(){ return faces.size(); } }
package net.d_kami.mqo; import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class MQOContainer implements Iterable<MQOObject>{ private List<MQOObject> objects; public MQOContainer(){ objects = new ArrayList<MQOObject>(); } public void addObject(MQOObject object){ objects.add(object); } public int getVertexCount(){ int count = 0; for(MQOObject object : objects){ count += object.getVertexCount(); } return count; } public int getFaceCount(){ int count = 0; for(MQOObject object : objects){ count += object.getFaceCount(); } return count; } public int getObjectCount(){ return objects.size(); } public MQOObject getObject(int index){ return objects.get(index); } public Iterator<MQOObject> iterator(){ return objects.iterator(); } }
次にエントリポイントとなるActivityを作る。mqoファイルを読み込んでMQOContainerを後で作るGLRendererに渡す役割を持っている。
package net.d_kami.mqo.test; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import java.util.List; import java.util.ArrayList; import net.d_kami.mqo.MQOLines; import net.d_kami.mqo.MQOContainer; import net.d_kami.mqo.parser.MQOParser; public class MQOTestActivity extends Activity { private GLSurfaceView glView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try{ BufferedReader br = new BufferedReader(new InputStreamReader(getAssets().open("gun3tri.mqo"))); String line; List<String> list = new ArrayList<String>(); while((line = br.readLine()) != null){ list.add(line); } br.close(); MQOLines lines = new MQOLines(list.toArray(new String[0])); MQOContainer container = MQOParser.parse(lines); glView = new GLSurfaceView(this); glView.setRenderer(new GLRenderer(container)); setContentView(glView); }catch(IOException e){ } } }
最後にGLRenderer。MQOContainerをコンストラクタで受けとって、座標の配列と面の配列を作る。あとはOpenGLの手順通りに進めて表示!
package net.d_kami.mqo.test; import java.util.List; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLSurfaceView; import android.opengl.GLU; import net.d_kami.mqo.MQOContainer; import net.d_kami.mqo.MQOObject; import net.d_kami.mqo.MQOVertex; import net.d_kami.mqo.MQOFace; public class GLRenderer implements GLSurfaceView.Renderer{ private FloatBuffer vertexBuffer; private ShortBuffer indexBuffer; private float aspect; private float[] vertices; private short[] indices; private int angle; public GLRenderer(MQOContainer container){ vertices = new float[container.getVertexCount() * 3]; indices = new short[container.getFaceCount() * 3]; int vi = 0; int fi = 0; int fcount = 0; for(MQOObject object : container){ List<MQOVertex> vertexList = object.getVertices(); List<MQOFace> indexList = object.getFaces(); for(int i = 0; i < vertexList.size(); i++){ MQOVertex vertex = vertexList.get(i); vertices[vi++] = vertex.x; vertices[vi++] = vertex.y; vertices[vi++] = vertex.z; } for(int i = 0; i < indexList.size(); i++){ MQOFace face = indexList.get(i); indices[fi++] = (short)(face.getIndex(0) + fcount); indices[fi++] = (short)(face.getIndex(1) + fcount); indices[fi++] = (short)(face.getIndex(2) + fcount); } fcount = vi / 3; } angle = 0; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config){ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnable(GL10.GL_LIGHTING); gl.glEnable(GL10.GL_LIGHT0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, new float[]{0.7f, 0.7f, 0.7f, 1.0f}, 0); vertexBuffer = makeFloatBuffer(vertices); indexBuffer = makeShortBuffer(indices); } @Override public void onSurfaceChanged(GL10 gl, int w, int h){ gl.glViewport(0, 0, w, h); aspect = (float)w / (float)h; } @Override public void onDrawFrame(GL10 gl){ gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); GLU.gluPerspective(gl, 45.0f, aspect, 0.01f, 2000.0f); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, new float[]{0f, 500.0f, 500.0f, 1.0f}, 0); GLU.gluLookAt(gl, 0.0f, 0.0f, 1000.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); gl.glRotatef(angle++, 0, 1, 0); gl.glColor4f(0.0f, 0.0f, 0.0f, 1.0f); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, indexBuffer); } private FloatBuffer makeFloatBuffer(float[] array){ FloatBuffer buffer = ByteBuffer.allocateDirect(array.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); buffer.put(array).position(0); return buffer; } private ShortBuffer makeShortBuffer(short[] array){ ShortBuffer buffer = ByteBuffer.allocateDirect(array.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer(); buffer.put(array).position(0); return buffer; } }
ついでにスクリーンショット。色を全部黒くしてるので、真っ黒になり、というところはいいのだが、細かいパーツが見れない。法線がないからだろうか?外積を求めて使えばOKなのかな?