问题
I have a problem on dunno how to add touch or long click to manipulate the OpenGL translate and scale parameter?
Currently my coding is displaying a texture in the middle of the mobile screen, and I want to drag and scale the texture with finger, So how do I manipulate the OpenGL translate and scale parameter?
This is my main class:
import android.app.Activity;
import android.os.Bundle;
import android.view.WindowManager;
public class MainActivity extends Activity {
private Stage stage;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//screen setting
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
setContentView(R.layout.main_layout);
stage = (Stage)findViewById(R.id.my_stage);
}
@Override
protected void onPause() {
super.onPause();
stage.onPause();
}
@Override
protected void onResume() {
super.onResume();
stage.onResume();
}
}
this is my 2nd sub class:
import android.content.Context;
import android.opengl.GLES10;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class Stage extends GLSurfaceView{
/* Stage width and height */
private float w, h;
/* Screen width and height */
private int screenWidth, screenHeight;
/* Our native vertex buffer */
private FloatBuffer vertexBuffer;
private Texture tex;
public Stage(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLConfigChooser(8, 8, 8, 8, 0, 0);
setRenderer(new MyRenderer());
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
float vertices[] = {
-0.5f, -0.5f, 0.0f, // 0. left-bottom
0.5f, -0.5f, 0.0f, // 1. right-bottom
-0.5f, 0.5f, 0.0f, // 2. left-top
0.5f, 0.5f, 0.0f // 3. right-top
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
tex = new Texture(R.drawable.kdk);
}
private final class MyRenderer implements GLSurfaceView.Renderer {
public final void onDrawFrame(GL10 gl) {
gl.glClear(GLES10.GL_COLOR_BUFFER_BIT);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
tex.prepare(gl, GL10.GL_CLAMP_TO_EDGE);
tex.draw(gl, (w / 2), (h / 2), tex.getWidth(), tex.getHeight(), 0);
}
public final void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glClearColor(0, 0, 0, 1.0f);
if(width > height) {
h = 600;
w = width * h / height;
} else {
w = 600;
h = height * w / width;
}
screenWidth = width;
screenHeight = height;
gl.glViewport(0, 0, screenWidth, screenHeight);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, w, h, 0, -1, 1);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
public final void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Set up alpha blending
gl.glEnable(GL10.GL_ALPHA_TEST);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
// We are in 2D. Why needs depth?
gl.glDisable(GL10.GL_DEPTH_TEST);
// Enable vertex arrays (we'll use them to draw primitives).
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// Enable texture coordination arrays.
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
tex.load(getContext());
}
}
}
this is my third sub class:
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES10;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Texture {
/**
* The OpenGL ES texture name associated with this texture.
*/
protected int textureId;
/**
* The horizontal and vertical dimensions of the image.
*/
protected int width, height;
/**
* The resource identifier for the image we want to load.
*/
int resourceId;
/**
* Whether or not we should generate mip maps.
*/
boolean mipmaps;
/**
* The buffer containing texture mappings.
*/
private FloatBuffer tempTextureBuffer = null;
Texture(int resourceId, boolean mipmaps) {
this.resourceId = resourceId;
this.textureId = -1;
this.mipmaps = mipmaps;
}
Texture(int resourceId) {
this(resourceId, false);
}
/**
* Generates a new OpenGL ES texture name (identifier).
* @return The newly generated texture name.
*/
private static final int newTextureID() {
int[] temp = new int[1];
GLES10.glGenTextures(1, temp, 0);
return temp[0];
}
public final int getWidth() {
return width;
}
public final int getHeight() {
return height;
}
public final void load(Context context) {
// Load the bitmap from resources.
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inScaled = false;
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), resourceId, opts);
// Update this texture instance's width and height.
width = bmp.getWidth();
height = bmp.getHeight();
// Create and bind a new texture name.
textureId = newTextureID();
GLES10.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
// Load the texture into our texture name.
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
// Set magnification filter to bilinear interpolation.
GLES10.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
if(mipmaps) {
// If mipmaps are requested, generate mipmaps and set minification filter to trilinear filtering.
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
GLES10.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_LINEAR);
}
else GLES10.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
// Recycle the bitmap.
bmp.recycle();
// If texture mapping buffer has not been initialized yet, do it now.
if(tempTextureBuffer == null)
buildTextureMapping();
}
/**
* Builds the texture mapping buffer.
*/
private void buildTextureMapping() {
// The array of texture mapping coordinates.
final float texture[] = {
0, 0, // The first vertex
1, 0, // The second vertex
0, 1, // The third vertex
1, 1, // The fourth vertex
};
// Create a native buffer out of the above array.
final ByteBuffer ibb = ByteBuffer.allocateDirect(texture.length * 4);
ibb.order(ByteOrder.nativeOrder());
tempTextureBuffer = ibb.asFloatBuffer();
tempTextureBuffer.put(texture);
tempTextureBuffer.position(0);
}
/**
* Deletes the texture name and marks this instance as unloaded.
*/
public final void destroy() {
GLES10.glDeleteTextures(1, new int[] {textureId}, 0);
// Setting this value to -1 indicates that it is unloaded.
textureId = -1;
}
public final boolean isLoaded() {
return textureId >= 0;
}
public final void prepare(GL10 gl, int wrap) {
// Enable 2D texture
gl.glEnable(GL10.GL_TEXTURE_2D);
// Bind our texture name
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
// Set texture wrap methods
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, wrap);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, wrap);
// Enable texture coordinate arrays and load (activate) ours
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tempTextureBuffer);
}
public final void draw(GL10 gl, float x, float y, float w, float h, float rot) {
gl.glPushMatrix();
gl.glTranslatef(x, y, 0);
gl.glRotatef(rot, 0, 0, 1);
gl.glScalef(w, h, 0); // Scaling will be performed first.
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
gl.glPopMatrix();
}
}
回答1:
Your GLSurfaceView (Stage class) should be the class that gets touch events. So you should add this method to it:
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction() & MotionEvent.ACTION_MASK;
float x, y;
int pointerIndex;
if (action == MotionEvent.ACTION_DOWN) {
pointerId = event.getPointerId(0);
x = event.getX();
y = event.getY();
pointerIndex = 0;
} else {
pointerIndex = event.getActionIndex();
pointerId = event.getPointerId(pointerIndex);
x = event.getX(pointerIndex);
y = event.getY(pointerIndex);
}
// Now you have your x and y, your action (up, down, etc), and pointer index.
}
And remember that you have scaled the virtual dimensions of your GLSurfaceView, so you should probably do the same with x and y (add this just before you start using x and y):
x = x * w / screenWidth;
y = y * h / screenHeight;
For more information regarding the MotionEvent (how to handle touch down/up etc) please refer to the Android documentation for this class.
Update:
For translation, you need to have four member variables:
- Saved x and y for the touch.
- Saved X and Y of the sprite.
Initially set all of them to an invalid value such as -1. When you get the ACTION_DOWN event, save the event x and y and sprite X and Y in the variables.
On ACTION_MOVE, you check one of them if it is -1. If it is not, it means you are dragging. So you get the difference of the new x and y with the saved one, and add it to saved X and Y. This will be the new position of your sprite.
On ACTION_UP, simply set saved values back to -1 (or at least set the one you are checking on ACTION_MOVE).
Scaling is a little bit more complicated. You need to look for ACTION_POINTER_DOWN with a pointer index > 0. Once you have that, you save the values of the second pointer, too, just like the translation. You also need to save the original scaling factor of the sprite. Then whenever you get ACTION_MOVE or ACTION_POINTER_MOVE, you look to see if both pointers are present (the trick with -1), and if so, get their difference. But this time the difference will constitute to your scaling factor, not the position.
来源:https://stackoverflow.com/questions/32556914/how-to-apply-drag-drop-and-scale-in-android-opengl-es