How to detect touch events in a specific Renderer area in android?

In android, I adopted an example of a rotating sphere given here . He creates a simple application showing a rotating sphere (earth).

Now, I want to do something if the spinning sphere is pressed on the phone display. I do not want to respond to events outside the spinning sphere. Here is what I have tried so far using a derived version of GLSurfaceView :

 public class MyGLSurfaceView extends GLSurfaceView { MyGLSurfaceView(Context context) { super(context); } public boolean onTouchEvent(MotionEvent event) { int x = (int)event.getX(); int y = (int)event.getY(); Log.d("onTouchEvent",String.format("keyCode: %d coords: %d %d", event.getActionMasked(), x, y)); Renderer renderer = this.Renderer.getRenderer(); // ERROR return super.onTouchEvent(event); } } 

Without the ERROR line, this code works, and when I click on the display, I get the x / y coordinate.

However, to evaluate if the event was within the scope, my approach was to get a renderer that has a Sphere object to determine if the x / y coordinates are within the scope.

But the ERROR line is not working, I get an error

 Error:(26, 56) error: cannot find symbol method getRenderer() 

How can I fix this error; or is there a smarter way to automatically find out if there was an event on a Sphere graphic object?

It might be nice to change the constructor and pass the instance of the visualization to MyGLSurfaceView , eh? ADDENDUM: this idea does not seem to work. I get a constructor error that I don't understand ...

+6
source share
3 answers

Your question is much more complicated than just detecting touch events. The renderer not only has a spherical object, but also contains all the content of OpenGL ES. Thus, even after detecting touch events, you will need to convert your 2D screen coordinates to 3D rays and check if it intersects with the sphere. This is called rays . Here is a good explanation of ray casting algorithms. There is also a good video tutorial for implementing a Java cast beam.

You also need to pass the renderer in the GLSurfaceView constructor, like you and @ satm12.

+4
source

I think, as you said, it is a good idea to pass the renderer in the constructor. Then pass the event that will be handled by the renderer in the game loop. GLSurfaceView has a method of adding runnable to a rendering stream called queueEvent .

Then in the renderer, where you have all the rendered objects, you can use ray casting to check if the object was clicked.

 public SurfaceView(Context context, GLRenderer renderer) { super(context); setEGLContextClientVersion(2); setRenderer(renderer); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if(event == null) return false; // set touch coordinates to be between -1 and 1 float normalizedX = ((event.getX()/v.getWidth())*2 - 1; float normalizedY = -((event.getY()/v.getHeight())*2 - 1); if (event.getAction() == MotionEvent.ACTION_UP){ queueEvent(new Runnable() { @Override public void run() { renderer.handleTouchUP(normalizedX, normalizedY); } }); return true; } return false; } }); } 
+1
source

What you want to do here is to check if your touch is in the sphere. The most direct answer is to make a 3D ray passing through the touch position and see if the ray collides with the sphere, i.e. Ray-Sphere collisions. You can use gluUnProject () for this . Remember that you need to use this function twice with the nearest and far points to get 3D near the far vector. This vector is your 3D ray for conflict checking.

But in your case, you have a ball that just rotates. The center is fixed and the radius is known. Therefore, an easier way to do this is to find a circle on the screen that matches the sphere and check if the contact is inside that circle.

First, take the center of the sphere, from your sample code related to your question. Since you are doing the translation (0, 0, -10), which is a 3D center. center3d(0.0f, 0.0f, -10.0f) . Project it on the screen and get the screen center. You can use gluProject () . Let's say this is the result of center2d . Now take another point on the surface of your sphere, since the radius of the sphere is 2 (from your code link) the point may be point3d(2.0f, 0.0f, -10.0f) . Now project this on the screen and say that you get point2d . Now the spherical radius of the 2D circle on the screen will be length(point2D - center2D) .

Now check if the input value is within the circle. Just get the distance from the touch position from the center of the circle and check its radius 2D. This is an easy way for your case, but if you have more complex forms, beam casting is the way to go.

+1
source

Source: https://habr.com/ru/post/1013549/


All Articles