File: xr_view_geometry.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (153 lines) | stat: -rw-r--r-- 5,976 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define _USE_MATH_DEFINES  // For VC++ to get M_PI. This has to be first.

#include "third_party/blink/renderer/modules/xr/xr_view_geometry.h"

#include <cmath>

#include "device/vr/public/mojom/vr_service.mojom-blink.h"
#include "third_party/blink/renderer/modules/xr/xr_graphics_binding.h"
#include "ui/gfx/geometry/transform.h"

namespace {
const double kDegToRad = M_PI / 180.0;

constexpr float kDefaultNearDepth = 0.0001;
constexpr float kDefaultFarDepth = 10000;
}

namespace blink {

XRViewGeometry::XRViewGeometry(XRGraphicsBinding::Api graphics_api)
    : graphics_api_(graphics_api) {}

XRViewGeometry::XRViewGeometry(
    const device::mojom::blink::XRViewGeometryPtr& view_geometry,
    XRGraphicsBinding::Api graphics_api)
    : graphics_api_(graphics_api) {
  CHECK(view_geometry);
  UpdateViewGeometry(view_geometry, kDefaultNearDepth, kDefaultFarDepth);
}

void XRViewGeometry::UpdateViewGeometry(
    const device::mojom::blink::XRViewGeometryPtr& view_geometry,
    double depth_near,
    double depth_far) {
  CHECK(view_geometry);

  const auto& fov = view_geometry->field_of_view;
  UpdateProjectionMatrixFromFoV(
      fov->up_degrees * kDegToRad, fov->down_degrees * kDegToRad,
      fov->left_degrees * kDegToRad, fov->right_degrees * kDegToRad, depth_near,
      depth_far);

  mojo_from_view_ = view_geometry->mojo_from_view;
}

void XRViewGeometry::UpdateProjectionMatrixFromFoV(float up_rad,
                                                   float down_rad,
                                                   float left_rad,
                                                   float right_rad,
                                                   float near_depth,
                                                   float far_depth) {
  float up_tan = tanf(up_rad);
  float down_tan = tanf(down_rad);
  float left_tan = tanf(left_rad);
  float right_tan = tanf(right_rad);
  float x_scale = 2.0f / (left_tan + right_tan);
  float y_scale = 2.0f / (up_tan + down_tan);
  float inv_nf = 1.0f / (near_depth - far_depth);

  // Compute the appropriate matrix for the graphics API being used.
  // WebGPU uses a clip space with a depth range of [0, 1], which requires a
  // different projection matrix than WebGL, which uses a clip space with a
  // depth range of [-1, 1].
  if (graphics_api_ == XRGraphicsBinding::Api::kWebGPU) {
    projection_matrix_ = gfx::Transform::ColMajor(
        x_scale, 0.0f, 0.0f, 0.0f, 0.0f, y_scale, 0.0f, 0.0f,
        -((left_tan - right_tan) * x_scale * 0.5),
        ((up_tan - down_tan) * y_scale * 0.5), far_depth * inv_nf, -1.0f, 0.0f,
        0.0f, far_depth * near_depth * inv_nf, 0.0f);
  } else {
    projection_matrix_ = gfx::Transform::ColMajor(
        x_scale, 0.0f, 0.0f, 0.0f, 0.0f, y_scale, 0.0f, 0.0f,
        -((left_tan - right_tan) * x_scale * 0.5),
        ((up_tan - down_tan) * y_scale * 0.5),
        (near_depth + far_depth) * inv_nf, -1.0f, 0.0f, 0.0f,
        (2.0f * far_depth * near_depth) * inv_nf, 0.0f);
  }
}

void XRViewGeometry::UpdateProjectionMatrixFromAspect(float fovy,
                                                      float aspect,
                                                      float near_depth,
                                                      float far_depth) {
  float f = 1.0f / tanf(fovy / 2);
  float inv_nf = 1.0f / (near_depth - far_depth);

  if (graphics_api_ == XRGraphicsBinding::Api::kWebGPU) {
    projection_matrix_ = gfx::Transform::ColMajor(
        f / aspect, 0.0f, 0.0f, 0.0f, 0.0f, f, 0.0f, 0.0f, 0.0f, 0.0f,
        far_depth * inv_nf, -1.0f, 0.0f, 0.0f, far_depth * near_depth * inv_nf,
        0.0f);
  } else {
    projection_matrix_ = gfx::Transform::ColMajor(
        f / aspect, 0.0f, 0.0f, 0.0f, 0.0f, f, 0.0f, 0.0f, 0.0f, 0.0f,
        (far_depth + near_depth) * inv_nf, -1.0f, 0.0f, 0.0f,
        (2.0f * far_depth * near_depth) * inv_nf, 0.0f);
  }

  inv_projection_dirty_ = true;
}

gfx::Transform XRViewGeometry::UnprojectPointer(double x,
                                                double y,
                                                double canvas_width,
                                                double canvas_height) {
  // Recompute the inverse projection matrix if needed.
  if (inv_projection_dirty_) {
    inv_projection_ = projection_matrix_.InverseOrIdentity();
    inv_projection_dirty_ = false;
  }

  // Transform the x/y coordinate into WebGL normalized device coordinates.
  // Z coordinate of -1 means the point will be projected onto the projection
  // matrix near plane.
  gfx::Point3F point_in_projection_space(
      x / canvas_width * 2.0 - 1.0,
      (canvas_height - y) / canvas_height * 2.0 - 1.0, -1.0);

  gfx::Point3F point_in_view_space =
      inv_projection_.MapPoint(point_in_projection_space);

  const gfx::Vector3dF kUp(0.0, 1.0, 0.0);

  // Generate a "Look At" matrix
  gfx::Vector3dF z_axis = -point_in_view_space.OffsetFromOrigin();
  z_axis.GetNormalized(&z_axis);

  gfx::Vector3dF x_axis = gfx::CrossProduct(kUp, z_axis);
  x_axis.GetNormalized(&x_axis);

  gfx::Vector3dF y_axis = gfx::CrossProduct(z_axis, x_axis);
  y_axis.GetNormalized(&y_axis);

  // TODO(bajones): There's probably a more efficient way to do this?
  auto inv_pointer = gfx::Transform::ColMajor(
      x_axis.x(), y_axis.x(), z_axis.x(), 0.0, x_axis.y(), y_axis.y(),
      z_axis.y(), 0.0, x_axis.z(), y_axis.z(), z_axis.z(), 0.0, 0.0, 0.0, 0.0,
      1.0);
  inv_pointer.Translate3d(-point_in_view_space.x(), -point_in_view_space.y(),
                          -point_in_view_space.z());

  // LookAt matrices are view matrices (inverted), so invert before returning.
  return inv_pointer.InverseOrIdentity();
}

void XRViewGeometry::SetMojoFromView(const gfx::Transform& mojo_from_view) {
  mojo_from_view_ = mojo_from_view;
}
}  // namespace blink