Перемещайте камеру с помощью сенсорного события в CardBoard и Rajawali VR Android

В настоящее время я работаю над приложением VR для Android, которое использует Google CardBoard и Rajawali для воспроизведения видео 360. Датчик работает хорошо, но я не могу использовать сенсорный, чтобы правильно перемещать сцену или камеру. Есть ли способ включить сенсорный режим в этом приложении?

Любая помощь очень ценится! Спасибо.

1 ответ

Решение

Я работал над тем же, и вот что я использовал:

Во-первых, взгляните на класс Раджавали ArcballCamera. Вы можете увидеть, как обрабатываются события касания, чтобы вращать камеру с событиями касания.

Проблема в том, что мне не понравилось поведение поворота по умолчанию, когда пользователь перемещался по экрану, поэтому я сделал другую реализацию самостоятельно, основанную на предыдущей, и вращал непосредственно сферу, на которую я смотрел, вместо камеры, так что здесь (все это внутри моего класса Renderer, кстати):

Во-первых, декларации:

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;

В конструкторе рендерера я запускаю инициализации (таймер и элемент управления используются для представления управления видео, поэтому не обращайте на них внимания):

    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);
        }
    });
}

И последнее, но не менее важное, прослушивание событий (как для масштабирования, так и для поворота):

/**
 * 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;
    }
}

После того, как все это решено, вам нужно только установить ориентацию для каждого рендера, например так:

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

Как я уже сказал, это работает для меня, но я только вращаю сферу. Не должно быть сложным приспособить его к вашим потребностям, кроме того, у вас есть класс Arcball, который является камерой и может быть лучше, чем вы хотите. Во всяком случае, я надеюсь, что это полезно для вас.

Другие вопросы по тегам