Move camera around using touch event in CardBoard

2020-06-28 02:48发布

Currently, I am working on VR application for android that using google CardBoard and Rajawali to play the 360 video. The sensor is working well, but I cannot use touch to drag the scene or camera around correctly. Is there any way to enable touch mode in this app?

Any help is greatly appreciated! Thank you.

1条回答
叛逆
2楼-- · 2020-06-28 03:29

I've worked on the same thing, and this is what I used:

First, take a look at Rajawali's ArcballCamera class. You can see there how touch events are handled to rotate the camera with touch events.

The problem is I didn't liked the default behaviour of the rotation when the user moved across the screen, so I did another implementation on my own, based on the previous one and rotating directly the sphere I was looking to instead of the camera, so here it goes (all of this is inside my Renderer class, btw):

First, the declarations:

private GestureDetector detector;           //gesture detector
private ScaleGestureDetector scaleDetector; //scale detector (for zooming)
private GestureListener gListener;          //gesture listener
private ScaleListener sListener;            //scale listener
private View.OnTouchListener touchListener; //touch events listener
private boolean isRotating;                 //true if the sphere is rotating
private boolean isScaling;                  //true if the sphere is scaling
private float xInicial,yInicial;            //inicial touch point
//sphere's yaw and pitch, used for rotation
private double yaw,pitch, yawAcumulado=0, pitchAcumulado=0, yawAcumuladoR=0, pitchAcumuladoR=0;
//physical to logical (in 3D world) conversion: screen scroll to sphere rotation
private final double gradosPorBarridoX=120, gradosPorBarridoY=90;
private final double gradosPorPixelYaw, gradosPorPixelPitch;

In the renderer's constructor I start the inizalizations (timer and control are used for the video control view, so don't pay atention to these):

    DisplayMetrics outMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
    gradosPorPixelPitch = gradosPorBarridoY / outMetrics.heightPixels;
    gradosPorPixelYaw = gradosPorBarridoX / outMetrics.widthPixels;
    addListeners();
    ...
    //from Rajawali ArcballCamera class
private void addListeners(){
    ((Activity)context).runOnUiThread(new Runnable() {
        @Override
        public void run() {
            gListener = new GestureListener();
            sListener = new ScaleListener();
            detector = new GestureDetector(context, gListener);
            scaleDetector = new ScaleGestureDetector(context, sListener);
            touchListener = new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    scaleDetector.onTouchEvent(event); //see if it is a scale event
                    //if not, check whether it is a scroll
                    if (!isScaling) {
                        detector.onTouchEvent(event);
                        //or an up motion
                        if (event.getAction() == MotionEvent.ACTION_UP) {
                            if (!isRotating) {
                                //change video control view's visibility
                                TouchActivity.timer.cancel();
                                if (TouchActivity.control.getVisibility() == View.INVISIBLE) {
                                    TouchActivity.control.setVisibility(View.VISIBLE);
                                    TouchActivity.timer.start(); //timer is restarted
                                } else {
                                    TouchActivity.control.setVisibility(View.INVISIBLE);
                                }
                            } else {
                                isRotating = false;   //cancel rotation
                            }
                        }
                    }
                    return true;
                }
            };
            TouchActivity.principal.setOnTouchListener(touchListener);
        }
    });
}

And last but not least, the event listening (both for scaling and rotating):

/**
 * called when the rotation starts
 * @param x
 * @param y
 */
private void startRotation(float x, float y){
    xInicial = x;
    yInicial = y;
}

/**
 * called during the consecutive events of a rotation movement
 * @param x
 * @param y
 */
private void updateRotation(float x, float y){
    float difX = xInicial - x;
    float difY = yInicial - y;
    yaw= difX * gradosPorPixelYaw;
    pitch = difY * gradosPorPixelPitch;
    yawAcumulado+=yaw;
    pitchAcumulado+=pitch;
}

/**
 * event listener. if the user scrolls his finger through the screen, it sends the
 * touch event to calculate the sphere's rotation
 */
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) {
        //starts or updates the rotation with the upcoming event x and y screen values
        if(!isRotating) {
            startRotation(event2.getX(), event2.getY());
            isRotating=true;
            return false;
        }else{
            isRotating = true;
            updateRotation(event2.getX(), event2.getY());
            return false;
        }
    }
}

/**
 * event listener. Zooms in or out depending on the user's action
 */
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
    //zooms in or out according to the scale detector value
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if(detector.getScaleFactor()>1){
            if(earthSphere.getScaleX()*1.1<120){
                earthSphere.setScaleX(earthSphere.getScaleX()*1.1);
                earthSphere.setScaleY(earthSphere.getScaleY() * 1.1);
                earthSphere.setScaleZ(earthSphere.getScaleZ() * 1.1);
            }
        }else{
            if(earthSphere.getScaleX()*0.9>0.95) {
                earthSphere.setScaleX(earthSphere.getScaleX() * 0.9);
                earthSphere.setScaleY(earthSphere.getScaleY() * 0.9);
                earthSphere.setScaleZ(earthSphere.getScaleZ() * 0.9);
            }
        }
        return true;
    }

    //the zoom begins
    @Override
    public boolean onScaleBegin (ScaleGestureDetector detector) {
        isScaling = true;
        isRotating = false;
        return super.onScaleBegin(detector);
    }

    //the zoom ends
    @Override
    public void onScaleEnd (ScaleGestureDetector detector) {
        isRotating = false;
        isScaling = false;
    }
}

With all of this settled, you only have to set the orientation on each render, like this:

    yawAcumuladoR = (yawAcumulado) * 0.04;
    pitchAcumuladoR = (pitchAcumulado) * 0.04;
    Quaternion q = new Quaternion();
    q.fromEuler(yawAcumuladoR, pitchAcumuladoR, 0);
    earthSphere.setOrientation(q);

As I've said, this works for me, but I'm only rotating the sphere. It shouldn't be difficult to addapt it to your needs, besides you have the Arcball class that is a camera and might be better for what you want. Anyway, I hope this is useful for you.

查看更多
登录 后发表回答