3D пикинг с gles

Я пытаюсь сделать 3D-выбор, когда пользователь касается экрана телефона Android.

Иногда это работает, но когда я поворачиваю экран --> луч не попадает в сферу

Я вставлю исходный код с тем, что у меня есть до сих пор:

а) класс renderer.java:

... // variables used
    public static   picking gPicker     = null;   
    public static   vector3 campos      = new vector3(0,0, 500);
    public static   vector3 sphpos      = new vector3(60,-40,0);
...




... // this function is called when user taps Screen 
public void screenWasTapped(vector2 touchcoords) 
{
    // get current time
    now = System.currentTimeMillis();

    // can shoot/tap every 100 msecs., not earlyer.
    if (now > mLastFiredTime + mInterval) 
    {
        // do we have a valid GL context?
        if(gGL!=null)
        {   
            // screen was tapped , so call "pick" function wich will check if ray hits sphere wich is located at "sphpos".
            gPicker.pick(gGL, gScreenWidth, gScreenHeight, touchcoords.x, touchcoords.y);

            // update last time
            mLastFiredTime = now;
        }
    }
}
....

Это настройки GL OnSurfaceChanged:

public void onSurfaceChanged(GL10 gl, int width, int height) 
{
    // To prevent divide by zero
    if (height == 0) height = 1;   
    float aspect = (float) width/height;

    // Set the viewport (display area) to cover the entire window
    gl.glViewport(0, 0, width, height);

    // Setup perspective projection, with aspect ratio matches viewport
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();                
    // Use perspective projection
    GLU.gluPerspective(gl, 60.0f, aspect, 0.1f, 5000.f);

    gl.glMatrixMode(GL10.GL_MODELVIEW); 
    gl.glLoadIdentity();                

    // Fast Perspective Calculations
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); 
    gl.glEnable(GL10.GL_TEXTURE_2D);


    // Enable GL Blending.
    gl.glEnable(GL10.GL_BLEND);
    gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);

    // Drawing order & culling face settings.
    gl.glFrontFace(GL10.GL_CW);     // Front face in counter-clockwise orientation
    gl.glEnable(GL10.GL_CULL_FACE);     // Enable cull face
    gl.glCullFace(GL10.GL_BACK);        // Cull the back face (don't display)


    gScreenWidth  = width;
    gScreenHeight = height; 
}

...и вот как я РИСУЮ сцену:

public void onDrawFrame(GL10 gl) 
{
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    gTimer.startupdate();
    float dt = gTimer.getElapsed();

    gl.glLoadIdentity();
    gl.glTranslatef(-campos.x, -campos.y, -campos.z);
    gl.glRotatef(tableRotXAngle, 1, 0, 0); 
    gl.glRotatef(tableRotYAngle, 0, 1, 0f);


        gl.glColor4f(0,1,1,1);
        gl.glPushMatrix();
        gl.glTranslatef(sphpos.x,sphpos.y,sphpos.y); 
            newgame.gPyramid.render(gl);            // draw pyramid at 3d location specified by "sphpos"
        gl.glPopMatrix();
        gl.glColor4f(1,1,1,1);



        doGameAI(dt);
        doDisplayHUD(gl);

    gTimer.stopupdate();
    gl.glFlush();
}

ВАЖНОЕ ЗАМЕЧАНИЕ здесь: это вращение камеры по дуге, камера всегда вращается, глядя на центральные координаты (0,0,0) и перемещаясь на 500 единиц назад (см. инициализацию Campos).

б) класс "picker.java":

    public class picking 
    {

   picking(GL10 gl)
   {
    MatrixGrabber mg = new MatrixGrabber();
    mg.getCurrentState(gl);
   }

(UPDATED)
public void pick(GL10 gl, int width, int height, float xTouch, float yTouch) 
{
    MatrixGrabber mg = new MatrixGrabber();
    mg.getCurrentState(gl);

    int[] viewport = {0, 0, width, height};

    float[] nearCoOrds  = new float[3];
    float[] farCoOrds   = new float[3];
    float[] temp        = new float[4];
 // float[] temp2       = new float[4];

    float winx = xTouch, winy =(float)viewport[3] - yTouch;

    displayMatrix(mg.mModelView);
    displayMatrix(mg.mProjection);



    int result = GLU.gluUnProject(winx, winy, 0.0f, mg.mModelView, 0, mg.mProjection, 0, viewport, 0, temp, 0);
    // Matrix.multiplyMV(temp2, 0, mg.mModelView, 0, temp, 0);
    if(result == GL10.GL_TRUE){
        nearCoOrds[0] = temp[0] / temp[3];
        nearCoOrds[1] = temp[1] / temp[3];
        nearCoOrds[2] = temp[2] / temp[3];

    }



    Log.d("redwing", " ---> near pos "  + Float.toString(nearCoOrds[0]) + "," 
                                        + Float.toString(nearCoOrds[1]) + "," 
                                        + Float.toString(nearCoOrds[2]));

    result = GLU.gluUnProject(winx, winy, 1.0f , mg.mModelView, 0, mg.mProjection, 0, viewport, 0, temp, 0);
    // Matrix.multiplyMV(temp2,0, mg.mModelView, 0, temp, 0);
    if(result == GL10.GL_TRUE){
        farCoOrds[0] = temp[0] / temp[3];
        farCoOrds[1] = temp[1] / temp[3];
        farCoOrds[2] = temp[2] / temp[3];
    }

    Log.d("redwing", " ---> far pos "   + Float.toString(farCoOrds[0]) + "," 
                                        + Float.toString(farCoOrds[1]) + "," 
                                        + Float.toString(farCoOrds[2]));



    vector3 rayDirection = new vector3(farCoOrds[0]-nearCoOrds[0], farCoOrds[1]-nearCoOrds[1], farCoOrds[2]-nearCoOrds[2]);

    Log.d("redwing", " ---> raylen ="+ Float.toString(rayDirection.getLength()));

    rayDirection.setUnit();

    Log.d("redwing", " ---> dir "   + Float.toString(rayDirection.x) + "," 
                                    + Float.toString(rayDirection.y) + "," 
                                    + Float.toString(rayDirection.z));



    if(rayHitSphere(renderer.campos, rayDirection, renderer.sphpos, 10))
    {
        Log.e("redwing", "****************");
        Log.e("redwing", "ray hits OBJECT!");
        Log.e("redwing", "****************");
    }


}      


boolean rayHitSphere(vector3 rayPos, vector3 rayDir, vector3 sphereCntr, float radius)
{
      vector3 w = rayPos.sub(sphereCntr);   
      float A = rayDir.dot(rayDir);         
      float B = 2*w.dot(rayDir);            
      float C = w.dot(w) - radius*radius;   

      float D = B*B-4.0f*A*C;

      if(D>=0.0f)
      return true;
      else 
      return false;
    }


void displayMatrix(float[] m, String tag)
{
    for(byte i=0;i<4;i++)
    {
        Log.v("redbase "+tag, "mtx" + Byte.toString(i) + " - "  + new DecimalFormat("#.####").format(m[i*4+0])+","
                                                                + new DecimalFormat("#.####").format(m[i*4+1])+","
                                                                + new DecimalFormat("#.####").format(m[i*4+2])+","
                                                                + new DecimalFormat("#.####").format(m[i*4+3]));
    }
}

}

Просто не знаю, где я ошибаюсь .. :( Пожалуйста, помогите !!

     Here are some values from Eclipse console when User tapped screen:
     (UPDATED - i get a Hit!)
    <<< NOT ROTATED SCENE >>
    06-02 12:15:22.611: D/redwing(5524): screen resolution 480x764
    06-02 12:15:22.611: D/redwing(5524): tapped coordinate ( 317.07507,426.2164
    06-02 12:15:22.621: V/redbase(5524): mtx0 - 1,0,0,0
    06-02 12:15:22.631: V/redbase(5524): mtx1 - 0,1,0,0
    06-02 12:15:22.631: V/redbase(5524): mtx2 - 0,0,1,0
    06-02 12:15:22.631: V/redbase(5524): mtx3 - 0,0,-500,1
    06-02 12:15:22.641: V/redbase(5524): mtx0 - 2.7568,0,0,0
    06-02 12:15:22.641: V/redbase(5524): mtx1 - 0,1.7321,0,0
    06-02 12:15:22.641: V/redbase(5524): mtx2 - 0,0,-1,-1
    06-02 12:15:22.651: V/redbase(5524): mtx3 - 0,0,-0.2,0
    06-02 12:15:22.651: D/redwing(5524):  ---> near pos 0.011649653,-0.0066829454,499.9
    06-02 12:15:22.661: D/redwing(5524):  ---> far pos 581.6618,-333.6764,-4493.4097
    06-02 12:15:22.661: D/redwing(5524):  ---> raylen =5038.134
    06-02 12:15:22.661: D/redwing(5524):  ---> dir 0.115449525,-0.06622883,-0.991103
    06-02 12:15:22.661: E/redwing(5524): ****************
    06-02 12:15:22.661: E/redwing(5524): ray hits OBJECT!
    06-02 12:15:22.661: E/redwing(5524): ****************


    <<< ROTATED SCENE!!  UPDATED - Still NO HIT!  >>
    06-02 12:15:30.671: D/redwing(5524): screen resolution 480x764
    06-02 12:15:30.671: D/redwing(5524): tapped coordinate ( 337.04843,440.11392
    06-02 12:15:30.671: V/redbase(5524): mtx0 - 0.9684,0.1802,0.1725,0
    06-02 12:15:30.671: V/redbase(5524): mtx1 - 0,0.6913,-0.7226,0
    06-02 12:15:30.681: V/redbase(5524): mtx2 - -0.2495,0.6997,0.6695,0
    06-02 12:15:30.681: V/redbase(5524): mtx3 - 0,0,-500,1
    06-02 12:15:30.681: V/redbase(5524): mtx0 - 2.7568,0,0,0
    06-02 12:15:30.681: V/redbase(5524): mtx1 - 0,1.7321,0,0
    06-02 12:15:30.681: V/redbase(5524): mtx2 - 0,0,-1,-1
    06-02 12:15:30.681: V/redbase(5524): mtx3 - 0,0,-0.2,0
    06-02 12:15:30.681: D/redwing(5524):  ---> near pos 86.22043,-361.2092,334.655
    06-02 12:15:30.691: D/redwing(5524):  ---> far pos -144.60953,2943.3904,-3498.0571
    06-02 12:15:30.691: D/redwing(5524):  ---> raylen =5065.9
    06-02 12:15:30.691: D/redwing(5524):  ---> dir -0.04556544,0.65232235,-0.7565708
  • если я не поверну сцену, то у меня есть хит!
  • но .. если я поверну сцену, то НЕТ попадания :(

person redbase    schedule 02.06.2012    source источник


Ответы (2)


Не на 100% уверен, что я понимаю весь код, но мне кажется, что вы не должны умножать результат gluUnproject на матрицу modelview?

Если unproject дает местоположение клика в мировых координатах, и вы проверяете положение сферы в мировых координатах, я не понимаю, почему вы преобразуете unproject с помощью представления модели.

Если вы действительно хотите преобразовать клики в пространство камеры, то ваше местоположение сферы также должно быть преобразовано, чего я не вижу.

person Tim    schedule 02.06.2012
comment
Хорошо, тогда нужно сделать два шага: 1) преобразовать поз. сферы с помощью mtx modelview и 2) удалить Matrix.multiplyMV(temp2,0, mg.mModelView, 0, temp, 0); линии (их 2), верно? плохо вернуться сейчас с результатом. - person redbase; 02.06.2012
comment
@redbase: вам, вероятно, следует сделать одно или другое, выполнение обоих будет столь же неправильным, как и то, с чего вы начали. Попробуйте просто удалить представление модели, умноженное на результат unproject, и оставьте сферу как есть. - person Tim; 02.06.2012
comment
Спасибо, Тим, за твой ответ, я обновил мою функцию выбора. пожалуйста помоги. Теперь, кажется, не работает с обоими! Сейчас я попробую сделать то или иное, как вы сказали... посмотрим. - person redbase; 02.06.2012
comment
простите за это! я обновил функцию и результаты Pick, но все равно не повезло :(. - person redbase; 02.06.2012

Из начального просмотра вашего кода похоже, что это проблема изменения разрешения экрана. Ваш код, похоже, не транспонирует ширину и высоту при повороте экрана. Это могло быть выключено, но это выделялось как возможная проблема.

person Edward Carmack    schedule 04.06.2012