0

Im trying to show a square polygon with the 100% of the width of the screen, then, i supose that i must zoom it (with Z axis) until the polygon borders are tounching the screen borders.

I'm trying to achieve this using gluProject to project a coordinate in 3D into a 2D screen coordinate. If the screen coordinate is either 0 or matches the width or height, then it is touching a screen border.

The problem is that something is going wrong, the outputCoords array returned with gluProject is giving me fake values, then i'm doing something wrong. For example, if i put a Z value of -1,15, i can see how my polygon haves exactly the same width of the screen... but the outputCootrds array is telling me these rare values: 175,16,0.9 !!! 175,16!!! it is wrong, because the polygon haves exactly the same width of the screen and it is starting on the 0 pixel of the screen :S

If someone can help me explaining the correct way to achieve this with code examples, please i really need help with this. I readed thousands of tutorials and stackoverflow questions about transforming vertices into pixel coordinates, but all those tutorials, guides and questions doesn't help me to achieve this with my code.

This is myGlSurfaceView class, the code to calcule the screen pixel coordinates is on the method onDrawFrame.

public class MySurfaceView extends GLSurfaceView implements Renderer {  
private Context context;
private Square square;
private float xrot;                 //X Rotation
private float yrot;                 //Y Rotation
private float zrot;                 //Z Rotation
private float xspeed;               //X Rotation Speed
private float yspeed;               //Y Rotation Speed
private float z = -1.15f;           //Profundidad en el eje Z
private float oldX; //valor anterior de X, para rotación
private float oldY; //valor anterior de Y, para rotación
private final float TOUCH_SCALE = 0.2f;     //necesario para la rotación

//create the matrix grabber object in your initialization code  
private MatrixGrabber mg = new MatrixGrabber();           

private boolean firstTimeDone=false; //true si la aplicación ya ha sido inicializada.

public MySurfaceView(Context context, Bitmap image) {
    super(context);
    this.context = context;
    setEGLConfigChooser(8, 8, 8, 8, 16, 0); //fondo transparente
    getHolder().setFormat(PixelFormat.TRANSLUCENT); //fondo transparente
    //Transformamos esta clase en renderizadora
    this.setRenderer(this);
    //Request focus, para que los botones reaccionen
    this.requestFocus();
    this.setFocusableInTouchMode(true);
    square = new Square(image);                                 
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {       
    gl.glDisable(GL10.GL_DITHER);               //dithering OFF
    gl.glEnable(GL10.GL_TEXTURE_2D);            //Texture Mapping ON
    gl.glShadeModel(GL10.GL_SMOOTH);            //Smooth Shading 
    gl.glClearDepthf(1.0f);                     //Depth Buffer Setup
    gl.glEnable(GL10.GL_DEPTH_TEST);            //Depth Testing ON
    gl.glDepthFunc(GL10.GL_LEQUAL);
    gl.glClearColor(0,0,0,0); //fondo transparente
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);         
    //Cargamos la textura del cubo.
    square.loadGLTexture(gl, this.context);
}

public void onDrawFrame(GL10 gl) {
    //Limpiamos pantalla y Depth Buffer
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    gl.glLoadIdentity();
    //Dibujado
    gl.glTranslatef(0.0f, 0.0f, z);         //Move z units into the screen
    gl.glScalef(0.8f, 0.8f, 0.8f);          //Escalamos para que quepa en la pantalla
    //Rotamos sobre los ejes.
    gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);   //X
    gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);   //Y
    gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);   //Z
    //Dibujamos el cuadrado
    square.draw(gl);    
    //Factores de rotación.
    xrot += xspeed;
    yrot += yspeed;         


    if (!firstTimeDone)
    {       
        /////////////// NEW CODE FOR SCALING THE AR IMAGE TO THE DESIRED WIDTH /////////////////            
        mg.getCurrentProjection(gl); 
        mg.getCurrentModelView(gl);                     
        float [] modelMatrix = new float[16];
        float [] projMatrix = new float[16];
        modelMatrix=mg.mModelView;
        projMatrix=mg.mProjection;          
        int [] mView = new int[4];
        mView[0] = 0;
        mView[1] = 0;
        mView[2] = 800; //width
        mView[3] = 480; //height
        float [] outputCoords = new float[3];
        GLU.gluProject(-1.0f, -1.0f, z, modelMatrix, 0, projMatrix, 0, mView, 0, outputCoords, 0);

        int i=0;
        System.out.print(i);
       // firstTimeDone=true;
    }
}

//si el surface cambia, resetea la vista, imagino que esto pasa cuando cambias de modo portrait/landscape o sacas el teclado físico en móviles tipo Droid.
public void onSurfaceChanged(GL10 gl, int width, int height) {
    if(height == 0) {                       
        height = 1;                         
    }
    gl.glViewport(0, 0, width, height);     //Reset Viewport
    gl.glMatrixMode(GL10.GL_PROJECTION);    //Select Projection Matrix
    gl.glLoadIdentity();                    //Reset Projection Matrix
    //Aspect Ratio de la ventana
    GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);
    gl.glMatrixMode(GL10.GL_MODELVIEW);     //Select Modelview Matrix
    gl.glLoadIdentity();                    //Reset Modelview Matrix        

}

public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();
    switch (event.getAction()) 
    {
        case MotionEvent.ACTION_MOVE:
            //Calculamos el cambio
            float dx = x - oldX;
            float dy = y - oldY;
            xrot += dy * TOUCH_SCALE;
            yrot += dx * TOUCH_SCALE;
            //Log.w("XXXXXX", "ACTION_MOVE_NO_ZOOM");
            break;
    }
    oldX = x;
    oldY = y;
    return true; //El evento ha sido manejado
}

public void zoomIn(){ 
    z=z+0.2f;   
    if (z>-1.0f)
        z=-1.0f;
}
public void zoomOut(){ 
    z=z-0.2f; 
    if (z<-20.0f)
       z=-20.0f;
}
public void rotateL(){ 
    zrot=zrot+3.0f; 
}
public void rotateR(){ 
    zrot=zrot-3.0f; 
}   
public void reset()
{
    xrot=0;
    yrot=0;
    zrot=0;
    xspeed=0;
    yspeed=0;
    z = -5.0f;
}
}

This is my square class:

public class Square {
//Buffer de vertices
private FloatBuffer vertexBuffer;
//Buffer de coordenadas de texturas
private FloatBuffer textureBuffer;
//Puntero de texturas
private int[] textures = new int[3];
//El item a representar
private Bitmap image;
//Definición de vertices

private float vertices[] = 
{ 
    -1.0f, -1.0f, 0.0f,     //Bottom Left
    1.0f, -1.0f, 0.0f,      //Bottom Right
    -1.0f, 1.0f, 0.0f,      //Top Left
    1.0f, 1.0f, 0.0f        //Top Right
};
/*  
private float vertices[] = 
{ 
-0.8f, -0.8f, 0.0f,     //Bottom Left
0.8f, -0.8f, 0.0f,      //Bottom Right
-0.8f, 0.8f, 0.0f,      //Top Left
0.8f, 0.8f, 0.0f 
};
*/
//Coordenadas (u, v) de las texturas    
/*
private float texture[] = 
{           
    //Mapping coordinates for the vertices
    0.0f, 0.0f,
    0.0f, 1.0f,
    1.0f, 0.0f,
    1.0f, 1.0f
};
*/
private float texture[] =
{
    //Mapping coordinates for the vertices
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f
};
//Inicializamos los buffers
public Square(Bitmap image) {
    ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
    byteBuf.order(ByteOrder.nativeOrder());
    vertexBuffer = byteBuf.asFloatBuffer();
    vertexBuffer.put(vertices);
    vertexBuffer.position(0);

    byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
    byteBuf.order(ByteOrder.nativeOrder());
    textureBuffer = byteBuf.asFloatBuffer();
    textureBuffer.put(texture);
    textureBuffer.position(0);

    this.image=image;
} 
//Funcion de dibujado
public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW);
    //gl.glEnable(GL10.GL_BLEND);
    //Bind our only previously generated texture in this case
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    //Point to our vertex buffer
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    //Enable vertex buffer
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    //Draw the vertices as triangle strip
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
    //Disable the client state before leaving
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    //gl.glDisable(GL10.GL_BLEND);      
}
//Carga de texturas
public void loadGLTexture(GL10 gl, Context context) {
    //Generamos un puntero de texturas
    gl.glGenTextures(1, textures, 0);       
    //y se lo asignamos a nuestro array
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    //Creamos filtros de texturas
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
    //Diferentes parametros de textura posibles GL10.GL_CLAMP_TO_EDGE
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);     
    /*
    String imagePath = "radiocd5.png";
    AssetManager mngr = context.getAssets();
    InputStream is=null;
    try {
        is = mngr.open(imagePath);
    } catch (IOException e1) {  e1.printStackTrace();   }
    */
    //Get the texture from the Android resource directory
    InputStream is=null;
    /*
    if (item.equals("rim"))
        is = context.getResources().openRawResource(R.drawable.rueda);
    else if (item.equals("selector"))
        is = context.getResources().openRawResource(R.drawable.selector);
    */      
    /*
    is = context.getResources().openRawResource(resourceId);
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(is);
    } finally {
        try {
            is.close();
            is = null;
        } catch (IOException e) {
        }
    }
    */
    Bitmap bitmap =image;       
    //con el siguiente código redimensionamos las imágenes que sean mas grandes de 256x256.
    int newW=bitmap.getWidth();
    int newH=bitmap.getHeight();
    float fact;
    if (newH>256 || newW>256)
    {
        if (newH>256)
        {
            fact=(float)255/(float)newH; //porcentaje por el que multiplicar para ser tamaño 256
            newH=(int)(newH*fact); //altura reducida al porcentaje necesario
            newW=(int)(newW*fact); //anchura reducida al porcentaje necesario   
        }
        if (newW>256)
        {
            fact=(float)255/(float)newW; //porcentaje por el que multiplicar para ser tamaño 256
            newH=(int)(newH*fact); //altura reducida al porcentaje necesario
            newW=(int)(newW*fact); //anchura reducida al porcentaje necesario
        }
        bitmap=Bitmap.createScaledBitmap(bitmap, newW, newH, true);
    }       
    //con el siguiente código transformamos imágenes no potencia de 2 en imágenes potencia de 2 (pot)
    //meto el bitmap NOPOT en un bitmap POT para que no aparezcan texturas blancas.
    int nextPot=256;
    int h = bitmap.getHeight();
    int w = bitmap.getWidth();
    int offx=(nextPot-w)/2; //distancia respecto a la izquierda, para que la imagen quede centrada en la nueva imagen POT
    int offy=(nextPot-h)/2; //distancia respecto a arriba, para que la imagen quede centrada en la nueva imagen POT
    Bitmap bitmap2 = Bitmap.createBitmap(nextPot, nextPot, Bitmap.Config.ARGB_8888); //crea un bitmap transparente gracias al ARGB_8888
    Canvas comboImage = new Canvas(bitmap2);
    comboImage.drawBitmap(bitmap, offx, offy, null);
    comboImage.save();

    //Usamos Android GLUtils para espcificar una textura de 2 dimensiones para nuestro bitmap
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0);

    //Checkeamos si el GL context es versión 1.1 y generamos los Mipmaps por Flag. Si no, llamamos a nuestra propia implementación
    if(gl instanceof GL11) {
        gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0);
    } else {
        buildMipmap(gl, bitmap2);
    }   
    //Limpiamos los bitmaps
    bitmap.recycle();
    bitmap2.recycle();
}
//Nuestra implementación de MipMap. Escalamos el bitmap original hacia abajo por factor de 2 y lo asignamos como nuevo nivel de mipmap
private void buildMipmap(GL10 gl, Bitmap bitmap) {
    int level = 0;
    int height = bitmap.getHeight();
    int width = bitmap.getWidth();
    while(height >= 1 || width >= 1) {
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
        if(height == 1 || width == 1) {
            break;
        }
        level++;
        height /= 2;
        width /= 2;
        Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
        bitmap.recycle();
        bitmap = bitmap2;
    }
}
}
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
NullPointerException
  • 36,107
  • 79
  • 222
  • 382
  • possible duplicate of [How does the gluProject function work? I can't understand it](http://stackoverflow.com/questions/7980430/how-does-the-gluproject-function-work-i-cant-understand-it) – Christian Rau Nov 02 '11 at 17:40
  • Does your `MatrixGrabber` class work correctly and give you the correct matrices? And is your viewport actually 800x480, which you hardcoded into the function? – Christian Rau Nov 02 '11 at 17:42
  • srry for open another question, but the other question is only about gluProject. MatrixGrabber should be working, it is a official java class from Google Android. The viewport is 800x480 because it is the resolution of the emulator 1.6 on landscape. – NullPointerException Nov 03 '11 at 08:46

1 Answers1

1

If I see correctly, the z-value you put into gluProject is the same value you put into glTranslate. But you still draw the polygon using the vertex (-1, -1, 0), the z translation comes from the glTranslate call (which in turn modifies the modelview matrix). But this matrix is also used in gluProject, so what actually happens is that you translate by z two times (not exactly, as the first translation is further distorted by the rotation). So put in the same vertex you also draw the polygon with, which would be (-1, -1, 0) and not (-1, -1, z).

Keep in mind that gluProject does the same thing as OpenGL's transformation pipeline (like explained in my answer to your other nearly exact same question), so you have to feed it with the same values you feed the OpenGL pipeline with (your polygon's vertices) if you want the same results (the polygon covering the screen).

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • if i pass -1 -1 0 to the gluProject function, then the values returned on the outPutCOrds array are 0 0 0 :S – NullPointerException Nov 03 '11 at 08:25
  • and also, z shouldnt be 0, because Z should be -1.15, with z=-1.15, the polygon is filling the 100% of the width of the screen, wich is my first objective, when i learn how to do it dinamically, i will do it with dinamic values for the width of the polygon. Please tell me what is wrong, i can't solve this :/ – NullPointerException Nov 03 '11 at 09:08
  • @AndroidUser99 No, z should be 0. The polygon (with z=0) fills the screen when you translate it with z=-1.15 (using `glTranslate`). But `gluProject` uses this exact same translation. So when you pass z=-1.15 into `gluProject`, this z is further translated with the modelview matrix (which contains the z you passed into `glTranslate`). Read the last paragraph of my answer if there are still unclarities about the workings of `gluProject` and OpenGL's transformation pipeline. – Christian Rau Nov 03 '11 at 13:15
  • @AndroidUser99 And in fact the result you get when passing (-1, -1, 0) is (0, 0, 0), which is the lower left corner of the screen and exactly the result you should get. But I wonder if the resulting depth is really exactly 0 or just some small value. – Christian Rau Nov 03 '11 at 13:20
  • But christian, if i pass -1 -1 0 to the gluProject function, then the values returned on the outPutCOrds array are 0 0 0, something is going wrong, 0,0,0 are not real values, the square is draw on the middle of the screen with Z=-5.0f, it haves an aproximated width of 100 px then the screen coordinates for the vertex -1,-1,0 should be something like 350,180 more or less, and not 0,0. There is something that it is wrong – NullPointerException Nov 03 '11 at 13:24
  • @AndroidUser99 With a translation of z=-1.15 the polygon covers the whole screen (your words). So putting in its lower left corner (-1, -1, 0) using the same translation should give you the lower left corner of the screen, shouldn't it? – Christian Rau Nov 03 '11 at 13:26
  • it is giving me 0,0,0 with z=-1.15, with z=-5.0, and with all the values for z, because this, something is wrong – NullPointerException Nov 03 '11 at 13:27
  • @AndroidUser99 So do you get (0, 0, 0.xxx) when you use a translation of z=-1.15 or z=-5.0 (putting in (-1, -1, 0), of course)? – Christian Rau Nov 03 '11 at 13:29
  • @AndroidUser99 Ok, now this is indeed interesting. – Christian Rau Nov 03 '11 at 13:30
  • @AndroidUser99 Sorry for asking again, but I have to make sure we're standing on equal ground. If you pass (-1, -1, 0) into `gluProject` it always returns (0, 0, 0), no matter what value of z you pass into `glTranslatef`? If that's the case something is really wrong, but wouldn't go that far as blaming `gluProject`. And as you say the `MatrixGrabber` is an official Android class it should work, too. – Christian Rau Nov 03 '11 at 13:44
  • yes, this is the class MatrixGrabber: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/spritetext/MatrixGrabber.html – NullPointerException Nov 03 '11 at 14:38
  • @AndroidUser99 Could you print out the values of `modelMatrix` and `projMatrix` after grabbing them? – Christian Rau Nov 03 '11 at 15:05
  • it works mate, sorry, i was doing a mistake with one of the parameters, your solution works!!! Thanks a lot !! just amazing!. But now i have another problem, ¿how can i force to move the square until it is on the pixel x=0 of the screen if the square haves the 50% of the screen dimensions? It is for do a alignment to the left of the square – NullPointerException Nov 04 '11 at 11:31
  • @AndroidUser99 Such things are not achievable that easy with a perspective projection. Maybe you perhaps really want an orthographic projection (`glOrtho` instead or `gluPerspective`)? In such a system the 3d cordinates are much easier to transform into screen coordinates, because they don't depend on the depth. Often you don't even need `gluProject` for this and can even configure `glOrtho` so that the "3d" coordinates are actually in pixels. But if you really need a perspective projection, you might look into `gluUnproject`, which does the reverse. But you need the object's depth for this. – Christian Rau Nov 04 '11 at 12:47
  • how can help me gluUnproject to achieve my needs with perspective projection? im checking gluUnProject uses on google but i can't find the way to use it to achieve this. – NullPointerException Nov 04 '11 at 12:56
  • @AndroidUser99 Maybe I haven't completely understood the question. If you want to compute the z-translation needed to align a given polygon (with given coordinates) to the screen-boundary, this won't be that easy (but it's possible, should be the solution to some non-inear, due to perspective, equation). Maybe this suffices its own question. – Christian Rau Nov 04 '11 at 13:08
  • dont worry, thanks a lot for all your help mate, please, can i send you links to future opengl questions? how can i do it? your skills are absolutly amazing and you are a wonderfull helper – NullPointerException Nov 04 '11 at 14:45
  • @AndroidUser99 Rather not. But I'm monitoring SO quite often. If I come to see a question that is answerable, I will try it. And I'm not the only one that's able to answer your questions. And thanks for accepting. – Christian Rau Nov 04 '11 at 15:21