5

Actually I have an application for Android 1.5 with a GLSurfaceView class that shows a simple square polygon on the screen.

I want to learn how to add a new functionality, the functionality of moving the square touching it with the finger. I mean that when the user touches the square and moves the finger, the square should stick to the finger, until the finger releases the screen.

Any tutorials/code examples/help will be apreciated.

My code:

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;
    }
}
}
Jav_Rock
  • 22,059
  • 20
  • 123
  • 164
NullPointerException
  • 36,107
  • 79
  • 222
  • 382

3 Answers3

5

Have you looked at the Android tutorial code? They have something very similar to this with examples in OpenGL ES 1 and 2.

In the OpenGL ES 1 tutorial, there's a section just for handling touch events. http://developer.android.com/resources/tutorials/opengl/opengl-es10.html#touch

So you'd want to modify the AddMotion section from glrotatef command into gltranslatef;

edit

Looks like you're more interested in coordinate conversion than object selection. So, wherever you touch on the screen, that's where the image goes (as opposed to touching and dragging an image, which would imply selection). And your question about winZ makes me think you're trying gluunproject. If that's the case, you already know your winZ because you translate the camera back from the object by your "z" variable. Since your z is negative, why not try this?

Assuming you've set your GLWrapper for your GLSurfaceView in your activity:

    mGLView.setGLWrapper(new GLWrapper() {
        public GL wrap(GL gl) {
            return new MatrixTrackingGL(gl);
        }

    });

Then, in your GLSurfaceView/Renderer subclass...

public float[] unproject(GL10 gl, float x, float y) {
    mMatrixGrabber.getCurrentState(gl);
    int[] view = {0,0,this.getWidth(), this.getHeight()};
    float[] pos = new float[4];
    float[] result = null;
    int retval = GLU.gluUnProject(x, y, -z, 
            mMatrixGrabber.mModelView, 0,
            mMatrixGrabber.mProjection, 0,
            view, 0, 
            pos, 0);
    if (retval != GL10.GL_TRUE) {
        Log.e("unproject", GLU.gluErrorString(retval));
    } else {
        result = new float[3];
        result[0] = pos[0] / pos[3];
        result[1] = pos[1] / pos[3];
        result[2] = pos[2] / pos[3];
        result = pos;
    }
    return result;
}

Then you can modify your TouchEvent handler to contain

    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");
            touching = true;
            break;
        case MotionEvent.ACTION_UP:
            xrot = 0;
            yrot = 0;
            zrot = 0;
            touching = false;
            break;
    }

And put this next section in your draw method before the other translate/scale/rotation calls:

    if (touching) {
        float[] point = unproject(gl, oldX, (this.getHeight() - oldY));
        if (point == null) {
            Log.e("Draw", "No Point");
        } else {
            gl.glTranslatef(point[0], point[1], 0);
        }
    }

Hopefully this gives you the result you want.

Marc
  • 2,593
  • 2
  • 18
  • 21
  • yes, i checked it some hours ago, and it can't help me into my needs – NullPointerException Nov 04 '11 at 15:58
  • What, specifically, can it not help you with? It shows how to handle inputs, and modify opengl object location based on the inputs. Did you mean that you wanted to drag the object directly? So your problem has more to do with coordinate conversion than input handling? IF that's the case, you might want to look into using a 2d orthographic projection for universal coordinate conversion. Otherwise you have to do some math to calculate position based off the camera frustum with depth. – Marc Nov 04 '11 at 16:45
  • If you're having problems with the conversion, here's a SO link for the same problem: http://stackoverflow.com/questions/7437482/gluunproject-android-opengl-es-1-1-usage – Marc Nov 04 '11 at 17:16
  • thanks but that link can't help me, because i'm using openGL 1.0 and android 1.5, then i dont know how to obtain the Z value of the win coordinates :S – NullPointerException Nov 07 '11 at 09:11
2

check out min3d library http://code.google.com/p/min3d/

very simple implementation of opengl, and a lot of examples

Optimus
  • 2,716
  • 4
  • 29
  • 49
1

I would implement an ontouch listener waiting for an action down. Once that is triggered, get the position of the finger through getrawx or y, and then redraw the square in the canvas opengl accordingly. Here is a link with a good tutorial. http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-5-implementing-the-drag-gesture/1789?tag=content;siu-container

a sandwhich
  • 4,352
  • 12
  • 41
  • 62
  • is not that easy, opengl coordinates are not the same as x,y screen cordinates. And a little more help than two lines will be apreciated – NullPointerException Nov 04 '11 at 14:46
  • Can you not translate the coordinate pair into something opengl can use? I know I have done something similar with a different graphics library in android. It is actually pretty easy to get the size of the screen, and map those coordinates relationally to the screen. – a sandwhich Nov 04 '11 at 14:48
  • i can't apply that tutorial into my code, he is using matrix, me not, i'm using a Square class with another kind of implementation, i will edit my question to add my code in a few secs – NullPointerException Nov 04 '11 at 14:49
  • Matrix was not the reason I posted the tutorial. That varies depending on what you use, which apparently you are using something different. I posted that to show you how to implement the ontough event and register the dragging of the finger. Can you not directly operate/shift those vertices? I am not an expert with opengl, I was just hoping to give you something that you would be able to translate into opengl. – a sandwhich Nov 04 '11 at 14:51
  • the vertices of the square are in openGL coordinates, these cordinates are not the same as pixel coordinates, i will think on it, but atm i dont know how to implement it – NullPointerException Nov 04 '11 at 14:54
  • Get where the event is relationally on screen and then convert those into opengl coordinates? – a sandwhich Nov 04 '11 at 14:58
  • sorry but i can't implement your solution with my square and my GLsurfaceview, i can't find the way – NullPointerException Nov 04 '11 at 15:41