Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 1 | #include "include/private/dvr/eds_mesh.h" |
| 2 | |
| 3 | #include <math.h> |
| 4 | |
| 5 | #include <base/logging.h> |
| 6 | #include <private/dvr/types.h> |
| 7 | |
| 8 | namespace { |
| 9 | |
| 10 | using android::dvr::EdsVertex; |
| 11 | using android::dvr::EyeType; |
| 12 | using android::dvr::DistortionFunction; |
| 13 | using android::dvr::vec2; |
| 14 | |
| 15 | // Computes the vertices for a distortion mesh with resolution |resolution| and |
| 16 | // distortion provided by |hmd| and stores them in |vertices|. |
| 17 | static void ComputeDistortionMeshVertices( |
| 18 | EdsVertex* vertices, int resolution, |
| 19 | const DistortionFunction& distortion_function, EyeType eye) { |
| 20 | for (int row = 0; row < resolution; row++) { |
| 21 | for (int col = 0; col < resolution; col++) { |
| 22 | const float x_norm = |
| 23 | static_cast<float>(col) / (static_cast<float>(resolution - 1U)); |
| 24 | const float y_norm = |
| 25 | static_cast<float>(row) / (static_cast<float>(resolution - 1U)); |
| 26 | |
| 27 | const vec2 xy_norm(x_norm, y_norm); |
| 28 | const size_t index = col * resolution + row; |
| 29 | |
| 30 | // Evaluate distortion function to get the new coordinates for each color |
| 31 | // channel. The distortion function returns the new coordinates relative |
| 32 | // to a full viewport with 0 <= x <= 1 for each eye. |
| 33 | vec2 coords[3]; |
| 34 | distortion_function(eye, xy_norm, &vertices[index].position, coords); |
| 35 | |
| 36 | // Store distortion mapping in texture coordinates. |
| 37 | vertices[index].red_viewport_coords = coords[0]; |
| 38 | vertices[index].green_viewport_coords = coords[1]; |
| 39 | vertices[index].blue_viewport_coords = coords[2]; |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // Computes the triangle strip indices for a distortion mesh with resolution |
| 45 | // |resolution| and stores them in |indices|. |
| 46 | static void ComputeDistortionMeshIndices(uint16_t* indices, int resolution) { |
| 47 | // The following strip method has been used in the Cardboard SDK |
| 48 | // (java/com/google/vrtoolkit/cardboard/DistortionRenderer.java) and has |
| 49 | // originally been described at: |
| 50 | // |
| 51 | // http://dan.lecocq.us/wordpress/2009/12/25/triangle-strip-for-grids-a-construction/ |
| 52 | // |
| 53 | // For a grid with 4 rows and 4 columns of vertices, the strip would |
| 54 | // look like: |
| 55 | // ↻ |
| 56 | // 0 - 4 - 8 - 12 |
| 57 | // ↓ ↗ ↓ ↗ ↓ ↗ ↓ |
| 58 | // 1 - 5 - 9 - 13 |
| 59 | // ↓ ↖ ↓ ↖ ↓ ↖ ↓ |
| 60 | // 2 - 6 - 10 - 14 |
| 61 | // ↓ ↗ ↓ ↗ ↓ ↗ ↓ |
| 62 | // 3 - 7 - 11 - 15 |
| 63 | // ↺ |
| 64 | // |
| 65 | // Note the little circular arrows next to 7 and 8 that indicate |
| 66 | // repeating that vertex once so as to produce degenerate triangles. |
| 67 | // |
| 68 | // To facilitate scanline racing, the vertex order is left to right. |
| 69 | |
| 70 | int16_t index_offset = 0; |
| 71 | int16_t vertex_offset = 0; |
| 72 | for (int row = 0; row < resolution - 1; ++row) { |
| 73 | if (row > 0) { |
| 74 | indices[index_offset] = indices[index_offset - 1]; |
| 75 | ++index_offset; |
| 76 | } |
| 77 | for (int col = 0; col < resolution; ++col) { |
| 78 | if (col > 0) { |
| 79 | if (row % 2 == 0) { |
| 80 | // Move right on even rows. |
| 81 | ++vertex_offset; |
| 82 | } else { |
| 83 | --vertex_offset; |
| 84 | } |
| 85 | } |
| 86 | // A cast to uint16_t is safe here as |vertex_offset| will not drop below |
| 87 | // zero in this loop. As col is initially equal to zero |vertex_offset| is |
| 88 | // always incremented before being decremented, is initialized to zero and |
| 89 | // is only incremented outside of the loop. |
| 90 | indices[index_offset++] = static_cast<uint16_t>(vertex_offset); |
| 91 | indices[index_offset++] = static_cast<uint16_t>( |
| 92 | vertex_offset + static_cast<int16_t>(resolution)); |
| 93 | } |
| 94 | vertex_offset = |
| 95 | static_cast<int16_t>(static_cast<int>(resolution) + vertex_offset); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | } // anonymous namespace |
| 100 | |
| 101 | namespace android { |
| 102 | namespace dvr { |
| 103 | |
| 104 | // Builds a distortion mesh of resolution |resolution| using the distortion |
| 105 | // provided by |hmd| for |eye|. |
| 106 | EdsMesh BuildDistortionMesh(EyeType eye, int resolution, |
| 107 | const DistortionFunction& distortion_function) { |
| 108 | CHECK_GT(resolution, 2); |
| 109 | |
| 110 | // Number of indices produced by the strip method |
| 111 | // (see comment in ComputeDistortionMeshIndices): |
| 112 | // |
| 113 | // 1 vertex per triangle |
| 114 | // 2 triangles per quad, (rows - 1) * (cols - 1) quads |
| 115 | // 2 vertices at the start of each row for the first triangle |
| 116 | // 1 extra vertex per row (except first and last) for a |
| 117 | // degenerate triangle |
| 118 | // |
| 119 | const uint16_t index_count = |
| 120 | static_cast<uint16_t>(resolution * (2 * resolution - 1U) - 2U); |
| 121 | const uint16_t vertex_count = static_cast<uint16_t>(resolution * resolution); |
| 122 | |
| 123 | EdsMesh mesh; |
| 124 | mesh.vertices.resize(vertex_count); |
| 125 | mesh.indices.resize(index_count); |
| 126 | |
| 127 | // Populate vertex and index buffer. |
| 128 | ComputeDistortionMeshVertices(&mesh.vertices[0], resolution, |
| 129 | distortion_function, eye); |
| 130 | ComputeDistortionMeshIndices(&mesh.indices[0], resolution); |
| 131 | |
| 132 | return mesh; |
| 133 | } |
| 134 | |
| 135 | } // namespace dvr |
| 136 | } // namespace android |