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が台無しになるというバグ付き。そのうち直します。
| 固定リンク | コメント (10) | トラックバック (0)






最近のコメント