googlevr / gvr-ios-sdk

Google VR SDK for iOS
http://developers.google.com/vr/ios/
Other
645 stars 191 forks source link

Display issue on modal presenting #319

Open bill350 opened 6 years ago

bill350 commented 6 years ago

Hi Google VR team,

We have some issues with displaying the GVRPlayer correctly on a UIViewController which is displayed into a UINavigationController (which is displayed in modal).

Use case: the player is firstly embedded in our UIViewController (as it is in your sample code, the only difference is that it is embedded into a VRVideoViewController. See source below), and we want to display/switch the video in full screen next.

The final issue is that when we tap on fullscreen / cardboard, the player don't switch to fullscreen mode. We've tried to push the player in the current UINavigationController stack (displayed in modal mode), but the OpenGL video rendering is still broken, and lens not correctly fit the entire size of the screen.

Here our code integration extract for the player configuration :

protocol VRVideoViewControllerDelegate: class {
  func playerIsReady()
}

/// This controller contains a GVRRendererViewController from the GVRKit by Google
final class VRVideoViewController: ViewController {

  fileprivate(set) var vrPlayer: AVPlayer?
  fileprivate var vrVideoPlayerController: GVRRendererViewController?
  fileprivate var vrRendererView: GVRRendererView?

  weak var delegate: VRVideoViewControllerDelegate?

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }

  init(with url: String) {
    super.init(nibName: nil, bundle: nil)

    if OpenGLUtils.isOpenGL3Compliant() {

      if let url = URL(string: url) {
        self.vrPlayer = AVPlayer(url: url)
      }

      self.vrPlayer?.actionAtItemEnd = .none

      self.vrVideoPlayerController = GVRRendererViewController()
      // Be careful here, just after the delegate is set, GVR SDK Call the method "renderer()"
      // in the GVRRendererViewControllerDelegate implementation
      self.vrVideoPlayerController?.delegate = self
      if let vrVideoPlayerController = vrVideoPlayerController {
        self.ex.addChildViewController(vrVideoPlayerController, in: self.view)
      }

      let sceneRenderer = self.vrVideoPlayerController?.rendererView.renderer as? GVRSceneRenderer
      let videoRenderer = sceneRenderer?.renderList.object(at: 0) as? GVRVideoRenderer
      videoRenderer?.player = self.vrPlayer
      self.vrRendererView = self.vrVideoPlayerController?.view as? GVRRendererView
    } else {
      let placeholder = VRVideoPlaceholder(frame: self.view.frame)
      self.view.ex.addSubview(placeholder)
    }

  }

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.delegate?.playerIsReady()
  }
}

// MARK: - GVRRendererViewControllerDelegate
extension VRVideoViewController: GVRRendererViewControllerDelegate {

  func shouldHideTransitionView() -> Bool {
    return true
  }

  func renderer(for displayMode: GVRDisplayMode) -> GVRRenderer! {

    let videoRenderer = GVRVideoRenderer()
    videoRenderer.player = self.vrPlayer

    // Monoscopic = panorama style
    // TopBottom = Video splitted and duplicated in two parts, vertically
    // LeftRight = Video splitted and duplicated in two parts, horizontally
    videoRenderer.setSphericalMeshOfRadius(50,
                                           latitudes: 12,
                                           longitudes: 24,
                                           verticalFov: 180,
                                           horizontalFov: 360,
                                           meshType: GVRMeshType.monoscopic)

    let sceneRenderer = GVRSceneRenderer()
    sceneRenderer.renderList.add(videoRenderer)

    if displayMode == .embedded {
      sceneRenderer.hidesReticle = true
    }

    videoRenderer.player?.play()

    return sceneRenderer
  }
}

Could you please update the sample code with a modal example, similar to the root UIViewController which is used today? We have tested your sample with modal display and we have the same rendering issue, any idea?

Thanks

Performador commented 6 years ago

I was not able to repo this. Are you sure you are laying out the vrVideoViewController.view in the view[Will/Did]LayoutSubviews?

Try the following code where the only difference is that I had to implement self.ex.addChildViewController.

protocol VRVideoViewControllerDelegate: class {
  func playerIsReady()
}

/// This controller contains a GVRRendererViewController from the GVRKit by Google
final class VRVideoViewController: ViewController, GVRRendererViewControllerDelegate {

  fileprivate(set) var vrPlayer: AVPlayer?
  fileprivate var vrVideoPlayerController: GVRRendererViewController?
  fileprivate var vrRendererView: GVRRendererView?

  weak var delegate: VRVideoViewControllerDelegate?

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }

  init() {
    super.init(nibName: nil, bundle: nil)
  }

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated);

    if let url = URL(string: "[SOME_VIDEO_URL]") {
      self.vrPlayer = AVPlayer(url: url)
    }

    self.vrPlayer?.actionAtItemEnd = .none

    self.vrVideoPlayerController = GVRRendererViewController()
    // Be careful here, just after the delegate is set, GVR SDK Call the method "renderer()"
    // in the GVRRendererViewControllerDelegate implementation
    self.vrVideoPlayerController?.delegate = self
    if let vrVideoPlayerController = vrVideoPlayerController {

      //self.ex.addChildViewController(vrVideoPlayerController, in: self.view)
      self.addChildViewController(vrVideoPlayerController)
      self.view.addSubview(vrVideoPlayerController.view)
      vrVideoPlayerController.didMove(toParentViewController: self);
    }

    let sceneRenderer = self.vrVideoPlayerController?.rendererView.renderer as? GVRSceneRenderer
    let videoRenderer = sceneRenderer?.renderList.object(at: 0) as? GVRVideoRenderer
    videoRenderer?.player = self.vrPlayer
    self.vrRendererView = self.vrVideoPlayerController?.view as? GVRRendererView
  }

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.delegate?.playerIsReady()

    // bill350: Layout the child view controllers view here.
    vrVideoPlayerController?.view.frame = self.view.bounds
  }

  func shouldHideTransitionView() -> Bool {
    return true
  }

  func renderer(for displayMode: GVRDisplayMode) -> GVRRenderer! {
    let videoRenderer = GVRVideoRenderer()
    videoRenderer.player = self.vrPlayer

    // Monoscopic = panorama style
    // TopBottom = Video splitted and duplicated in two parts, vertically
    // LeftRight = Video splitted and duplicated in two parts, horizontally
    videoRenderer.setSphericalMeshOfRadius(50,
                                           latitudes: 12,
                                           longitudes: 24,
                                           verticalFov: 180,
                                           horizontalFov: 360,
                                           meshType: GVRMeshType.monoscopic)

    let sceneRenderer = GVRSceneRenderer()
    sceneRenderer.renderList.add(videoRenderer)

    if displayMode == .embedded {
      sceneRenderer.hidesReticle = true
    }

    videoRenderer.player?.play()

    return sceneRenderer
  }
}
bill350 commented 6 years ago

Thanks for reply @Performador. It still doesn't work.

I share with you a screen capture with the structure of the modal :

img_b1c0b40f5a7c-1

Is it more clear?

Thanks.

Performador commented 6 years ago

Ah, I see.

I think the problem is that you cannot have two modal VCs at the same time and GVRRendererViewController will call presentViewController if we are in the embedded mode (like the picture you shared).

bill350 commented 6 years ago

But it’s embedded into a navigation controller too, so it’s technically possible. The SDK bust be fixed with this “classic” use case.

Performador commented 6 years ago

GVRRendererViewController will call it's parent VCs presentViewController method.

You can override this method in your VRVideoViewController and call [self.navigationController presentViewController ...] to route the modal presentation to the navigation controller.

bill350 commented 6 years ago

"You can override this method in your VRVideoViewController and call [self.navigationController presentViewController ...] to route the modal presentation to the navigation controller."

I'm not sure about this.

If I'm right, the isModal method is called first : https://github.com/googlevr/gvr-ios-sdk/blob/master/Samples/GVRKit/GVRRendererViewController.m


- (void)didTapCardboardButton {
  // Toggle VR mode if we are being presented.
  if ([self isModal]) {
    self.rendererView.VRModeEnabled = !self.rendererView.VRModeEnabled;
  } else {
...
}

That's why the view switches from this state: simple

to this state: sample_2

Performador commented 6 years ago

Ah, yes, thanks for the explanation.

You are right. The SDK assumes that if we are modal, we are full screen but in your case, this is not true.