onAnimationFrame method

void onAnimationFrame(
  1. double time,
  2. XRFrame frame
)

Implementation

void onAnimationFrame(double time, XRFrame frame ) {
  pose = frame.getViewerPose( customReferenceSpace ?? referenceSpace );
  xrFrame = frame;

  if ( pose != null ) {
    final views = pose?.views.toDart;

    if ( glBaseLayer != null) {
      renderer.setRenderTargetFramebuffer( newRenderTarget!, Framebuffer(glBaseLayer!.framebuffer));
      renderer.setRenderTarget( newRenderTarget );
    }

    bool cameraXRNeedsUpdate = false;

    // check if it's necessary to rebuild cameraXR's camera list
    if ( views?.length != cameraXR.cameras.length ) {
      cameraXR.cameras.length = 0;
      cameraXRNeedsUpdate = true;
    }

    for (int i = 0; i < (views?.length ?? 0); i ++ ) {
      final view = views![ i ];
      XRViewport? viewport;

      if ( glBaseLayer != null ) {
        viewport = glBaseLayer!.getViewport( view );
      }
      else {
        final glSubImage = glBinding?.getViewSubImage( glProjLayer!, view );
        viewport = glSubImage?.viewport;

        // For side-by-side projection, we only produce a single texture for both eyes.
        if ( i == 0 ) {
          renderer.setRenderTargetTextures(
            newRenderTarget!,
            glSubImage!.colorTexture,
            glSubImage.depthStencilTexture
          );

          renderer.setRenderTarget( newRenderTarget );
        }
      }

      Camera? camera = cameras[ i ];

      if ( camera == null ) {
        camera = PerspectiveCamera();
        camera.layers.enable( i );
        camera.viewport = Vector4();
        cameras[ i ] = camera;
      }

      camera.matrix.copyFromUnknown( view.transform.matrix );
      camera.matrix.decompose( camera.position, camera.quaternion, camera.scale );
      camera.projectionMatrix.copyFromUnknown( view.projectionMatrix );
      camera.projectionMatrixInverse.setFrom( camera.projectionMatrix ).invert();
      camera.viewport?.setValues( viewport!.x, viewport.y, viewport.width, viewport.height );

      if ( i == 0 ) {
        cameraXR.matrix.setFrom( camera.matrix );
        cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale );
      }

      if ( cameraXRNeedsUpdate == true ) {
        cameraXR.cameras.add( camera );
      }
    }

    final enabledFeatures = session?.enabledFeatures?.toDart;
    final gpuDepthSensingEnabled = enabledFeatures != null &&
      enabledFeatures.isNotEmpty &&
      enabledFeatures.contains( 'depth-sensing' ) &&
      session?.depthUsage == 'gpu-optimized';

    if ( gpuDepthSensingEnabled && glBinding != null) {
      final depthData = glBinding?.getDepthInformation( views![ 0 ] );

      if ( depthData != null && depthData.isValid) {
        depthSensing.init( renderer, depthData, session!.renderState );
      }
    }
  }

  for (int i = 0; i < controllers.length; i ++ ) {
    final inputSource = controllerInputSources.isNotEmpty?controllerInputSources[ i ]:null;
    final controller = controllers[ i ];
    if ( inputSource != null && controller != null ) {
      controller.update( inputSource, frame, customReferenceSpace ?? referenceSpace );
    }
  }

  if ( onAnimationFrameCallback != null) onAnimationFrameCallback( time, frame );
  if ( frame.detectedPlanes != null) {
    dispatchEvent(Event( type: 'planesdetected', data: frame ));
  }

  xrFrame = null;
}