« AndroidのOpenGL ESでマウスイベント | トップページ | AndroidのCanvas.getPixels(int[]) »

AndroidのOpenGL ESでマウスピッキング 〜完結編〜

AndroidのOpenGL ESでのマウスピッキングについに成功しました。

前回の物では、マウス座標と3D空間の関連付けを自前の関数でやっていたのですが、 それにバグがあり、視点の位置によってはうまくいかないことがありました。

そこで利用したのがこの関数。

GLU.gluUnProject

これがもう便利べんり。あとはあたり判定は実装済みだったので、置き換えて完了。

使い方は大体以下の通り。

    	int[] bits = new int[16];
    float[] model = new float[16];
    float[] proj = new float[16];
    gl.glGetIntegerv(gl.GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES, bits, 0);
    for(int i = 0; i < bits.length; i++){
    model[i] = Float.intBitsToFloat(bits[i]);
    }
    ((GL11)gl).glGetIntegerv(gl.GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES, bits, 0);
    for(int i = 0; i < bits.length; i++){
    proj[i] = Float.intBitsToFloat(bits[i]);
    }
   
    float[] ret = new float[3];
    GLU.gluUnProject((float)curX, (float)curY, 0f, model, 0, proj, 0, new int[]{0, 0, getWidth(), getHeight()}, 0, ret, 0);
    int x = (int)(ret[0] * 0x10000);
    int y = (int)(ret[1] * 0x10000);
    int z = (int)(ret[2] * 0x10000);

これでx、y、zにクリックされた点の座標が格納されるので、それとカメラ位置を通る 直線の式を計算し、それがポリゴンと衝突するかを判定すればOK

以下あたり判定のソース

package net.chephes.opengl;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import javax.microedition.khronos.opengles.GL10;

import net.chephes.math.Matrix3D;

public class Plane{
    protected ByteBuffer  mIndexBuffer;
    protected IntBuffer   mVertexBuffer;
    protected int x, y, z;
    protected int rotateX, rotateY, rotateZ;
    protected int width, height;

    protected int one = 0x10000;
    protected int vertices[] = {
           -one, one, 0,
            one, one, 0,
            one,  -one, 0,
           -one,  -one, 0,
        };
       
    protected byte indices[] = {
            0, 1, 2,    2, 3, 0
    };
   
    public Plane(){
    this(1, 1);
    }
   
    public Plane(int width, int height){
    for(int i = 0; i < vertices.length; i+=3){
    vertices[i] *= width;
    vertices[i+1] *= height;
    }
    mVertexBuffer = GLUtil.createIntBuffer(vertices);
    mIndexBuffer = GLUtil.createByteBuffer(indices);
    }
   
    public void setX(int x){
    this.x = x;
    for(int i = 0; i < vertices.length; i+=3){
        mVertexBuffer.put(i, vertices[i] + x);
    }
    }
   
    public void setY(int y){
    this.y = y;
    for(int i = 1; i < vertices.length; i+=3){
        mVertexBuffer.put(i, vertices[i] + y);
    }
    }
   
    public void setZ(int z){
    this.z = z;
    for(int i = 2; i < vertices.length; i+=3){
        mVertexBuffer.put(i, vertices[i] + z);
    }
    }
   
    public void setRotateX(int rotateX){
    this.rotateX = rotateX;
    double theta = rotateX * Math.PI / 0x10000;
    double cos = Math.cos(theta);
    double sin = Math.sin(theta);
    for(int i = 1; i < vertices.length; i+=3){
    double y = (double)(vertices[i]) / 0x10000;
    double z = (double)(vertices[i+1]) / 0x10000;
    int flagY = y > 0 ? 1 : -1;
    double r = Math.sqrt(y * y + z * z);
        mVertexBuffer.put(i,
        flagY * (int)(r * cos * 0x10000) + this.y);
        mVertexBuffer.put(i+1,
        - flagY * (int)(r * sin * 0x10000) + this.z);
    }
    }
   
    public void setRotateY(int rotateY){
    this.rotateY = rotateY;
    double theta = rotateY * Math.PI / 0x10000;
    double cos = Math.cos(theta);
    double sin = Math.sin(theta);
    for(int i = 0; i < vertices.length; i+=3){
    double x = (double)(vertices[i]) / 0x10000;
    double z = (double)(vertices[i+2]) / 0x10000;
    int flagX = x > 0 ? 1 : -1;
    double r = Math.sqrt(x * x + z * z);
        mVertexBuffer.put(i,
        flagX * (int)(r * cos * 0x10000) + this.x);
        mVertexBuffer.put(i+2,
        - flagX * (int)(r *sin * 0x10000) + this.z);
    }
    }
   
    public void setRotateZ(int rotateZ){
    this.rotateZ = rotateZ;
    double theta = rotateZ * Math.PI / 0x10000;
    double cos = Math.cos(theta);
    double sin = Math.sin(theta);
    for(int i = 0; i < vertices.length; i+=3){
    double x = (double)(vertices[i]) / 0x10000;
    double y = (double)(vertices[i+1]) / 0x10000;
    int flagX = x > 0 ? 1 : -1;
    double r = Math.sqrt(x * x + y * y);
        mVertexBuffer.put(i,
        flagX * (int)(r * cos * 0x10000) + this.x);
        mVertexBuffer.put(i+1,
        - flagX * (int)(r * sin * 0x10000) + this.y);
    }
    }
   
    public int getX(){return x;}
    public int getY(){return y;}
    public int getZ(){return z;}
    public int getRotateX(){return rotateX;}
    public int getRotateY(){return rotateY;}
    public int getRotateZ(){return rotateZ;}
   
    public void render(GL10 gl){
    synchronized(gl){
            gl.glColor4f(1f, 1f, 1f, 1f);
            gl.glVertexPointer(3, gl.GL_FIXED, 0, mVertexBuffer);
            gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_BYTE, mIndexBuffer);
    }

    }
   
    private boolean isInside(int[] x, int[] y, int[] p, int[] q, int[] r){
    float v[][] = new float[3][3];
    float inv[][];
    for(int i = 0; i < 3; i++){
        v[i][0] = (float)(x[i] - y[i]) / 0x10000;
        v[i][1] = (float)(q[i] - p[i]) / 0x10000;
        v[i][2] = (float)(r[i] - p[i]) / 0x10000;
    }
    try{
        inv = Matrix3D.invert(v);
    }catch(Exception e){
    return false;
    }

    float t = 0, s = 0, u = 0;
    for(int i = 0; i < 3; i++){
    t += inv[0][i] * (float)(x[i] - p[i]) / 0x10000;
    s += inv[1][i] * (float)(x[i] - p[i]) / 0x10000;
    u += inv[2][i] * (float)(x[i] - p[i]) / 0x10000;
    }
    return s >= 0 && u >= 0 && s + u <= 1;
    }
   
    public boolean isInside(int[] x, int[] y){
    int[] p = new int[3];
    int[] q = new int[3];
    int[] r = new int[3];
    for(int i = 0; i < 3; i++){
    p[i] = mVertexBuffer.get(i);
    q[i] = mVertexBuffer.get(3 + i);
    r[i] = mVertexBuffer.get(6 + i);
    }
    boolean ret = isInside(x, y, p, q, r);
    for(int i = 0; i < 3; i++){
    p[i] = mVertexBuffer.get(6 + i);
    q[i] = mVertexBuffer.get(9 + i);
    r[i] = mVertexBuffer.get(0 + i);
    }
   
    return ret || isInside(x, y, p, q, r);
    }
   
}

isInside関数のint[] x、int[] yはそれぞれカメラの座標とクリックされた点の座標。 pqrはそれぞれ三角形の頂点を表す。

なおこのplaneクラスは高レベルオブジェクトとして紹介した物と同一で、 setRotationXしてから移動するとrotationが台無しになるというバグ付き。そのうち直します。

|

« AndroidのOpenGL ESでマウスイベント | トップページ | AndroidのCanvas.getPixels(int[]) »

Android」カテゴリの記事

コメント

質問があります。

一番上の抜粋したコードのところで、
GLU.gluUnProject((float)curX, (float)curY, 0f, model, 0, proj, 0, new int[]{0, 0, getWidth(), getHeight()}, 0, ret, 0);
のところでtryすると、例外が起こります。

Android 2.2のGalaxy Tabを使っています。

どうすれば正常に動作するでしょうか?

投稿: たけし | 2011年4月16日 (土) 14時05分

先ほどに補足しますが、例外の内容は、

java.lang.IllegalArgumentException:
length - offset < n

です。

投稿: たけし | 2011年4月16日 (土) 14時24分

http://stackoverflow.com/questions/7248019/illegalargumentexception-from-gluunproject

投稿: 通りすがり | 2011年10月16日 (日) 00時15分

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/486213/9129336

この記事へのトラックバック一覧です: AndroidのOpenGL ESでマウスピッキング 〜完結編〜:

« AndroidのOpenGL ESでマウスイベント | トップページ | AndroidのCanvas.getPixels(int[]) »