bool VideoFrameTransform::transformPos()

in Transform360/Library/VideoFrameTransform.cpp [935:1321]


bool VideoFrameTransform::transformPos(
    float x,
    float y,
    float* outX,
    float* outY,
    int transformMatPlaneIndex,
    float inputPixelWidth) {
  try {
    int isRight = 0;

    if (ctx_.input_stereo_format != STEREO_FORMAT_MONO) {
      switch (ctx_.output_stereo_format) {
        case STEREO_FORMAT_LR:
          {
            if (x > kXHalf) {
              x = (x - kXHalf) / kXHalf;
              isRight = 1;
            } else {
              x = x / kXHalf;
            }
            break;
          }
        case STEREO_FORMAT_TB:
          {
            if (y > kYHalf) {
              y = (y - kYHalf) / kYHalf;
              if (ctx_.vflip) {
                y = 1.0f - y;
              }
              isRight = 1;
              } else {
                y = y / kYHalf;
              }
              break;
          }
        case STEREO_FORMAT_MONO:
        case STEREO_FORMAT_GUESS:
        case STEREO_FORMAT_N:
          break;
      }
    }

    float qx, qy, qz, tx, ty, tz, d;
    float yaw, pitch;
    bool hasMapping = true;
    if (ctx_.output_layout != LAYOUT_FLAT_FIXED) {
      y = 1.0f - y;
    }
    array<float, 3> vx, vy, p;
    int face = 0, vFace, hFace;

    switch (ctx_.output_layout) {
      case LAYOUT_CUBEMAP_32:
        {
          vFace = (int) (y * 2);
          hFace = (int) (x * 3);
          x = x * 3.0f - hFace;
          y = y * 2.0f - vFace;
          face = hFace + (1 - vFace) * 3;
          break;
        }
      case LAYOUT_CUBEMAP_23_OFFCENTER:
        {
          vFace = (int) (y * 3);
          hFace = (int) (x * 2);
          x = x * 2.0f - hFace;
          y = y * 3.0f - vFace;
          face = hFace + (2 - vFace) * 2;
          break;
        }
#ifdef FACEBOOK_LAYOUT
      case LAYOUT_FB:
        break;
#endif
      case LAYOUT_FLAT_FIXED:
        break;
      case LAYOUT_EQUIRECT:
        {
          yaw = (2.0f * x - 1.0f) * M_PI;
          pitch = (y - 0.5f) * M_PI;
          break;
        }
      case LAYOUT_BARREL:
        {
          if (x <= 0.8f) {
            yaw = (2.5f * x - 1.0f) * ctx_.expand_coef * M_PI;
            pitch = (y * 0.5f  - 0.25f) * ctx_.expand_coef * M_PI;
            face = -1;
          } else {
            vFace = (int) (y * 2);
            face = (vFace == 1) ? TOP : BOTTOM;
            x = x * 5.0f - 4.0f;
            y = y * 2.0f - vFace;
          }
          break;
        }
      case LAYOUT_BARREL_SPLIT:
        //  For LAYOUT_BARREL_SPLIT, we separate the front view (-90~+90) and
        //  back view (-180~-90 & +90~+180) of the sphere into top & bottom rows
        //  of the projection. The top row holds the front half with its top and
        //  bottom part in the top row, and the back half with its top & bottom
        //  part in the bottom row.
        //
        //  Illustration of Equirectangular:
        //
        //   +---+---+---+---+---+---+---+---+
        //   |   1   |       3       |   1   |
        //   +---+---+---+---+---+---+---+---+
        //   |       |               |       |     Front half: 3, 4, 2
        //   +   5   +       4       +   5   +
        //   |       |               |       |     Back half: 1, 5, 0
        //   +---+---+---+---+---+---+---+---+
        //   |   0   |       2       |   0   |
        //   +---+---+---+---+---+---+---+---+
        //   <-Back-><---- Front ----><-Back->
        //
        //  Projection to LAYOUT_BARREL_SPLIT:
        //
        //   +---+---+---+---+---+---+
        //   |               |\  3  /|     3: Front Top
        //   +       4       +  - -  +
        //   |               |/  2  \|     2: Front Bottom
        //   +---+---+---+---+---+---+
        //   |               |\  1  /|     1: Back Top
        //   +       5       +  - -  +
        //   |               |/  0  \|     0: Back Bottom
        //   +---+---+---+---+---+---+
        //
        //  - Area 0, 1, 2, 3 are all half circles.
        //
        {
          if (3.0f * x <= 2.0f) {
            vFace = (int)(y * 2);
            yaw =
                ((3.0f / 2.0f * x - 0.5f) * ctx_.expand_coef - vFace + 1.0f) *
                M_PI;
            pitch = (y - 0.25f - 0.5f * vFace) * ctx_.expand_coef * M_PI;
            face = -1;
          } else {
            //  Index which half circle the area is rendering from
            //  - Area 0: Back Bottom.
            //    * face: BOTTOM
            //    * Needs to rotate 180 degrees to match shape.
            //    * Rendering Range: [0, 0.5] -> [0.5, 0]
            //  - Area 1: Back Top.
            //    * face: TOP
            //    * Needs to rotate 180 degrees to match shape.
            //    * Rendering Range: [0.5, 1] -> [1, 0.5]
            //  - Area 2: Front Bottom.
            //    * face: BOTTOM
            //    * Rendering Range: [0.5, 1]
            //  - Area 3: Front Top.
            //    * face: TOP
            //    * Rendering Range: [0, 0.5]
            //
            int halfVFace = (int)(y * 4);
            face = (halfVFace == 1 || halfVFace == 3) ? TOP : BOTTOM;
            x = x * 3.0f - 2.0f;
            switch (halfVFace) {
              case 0:
                y = y * 2.0f;
                // Rotate 180 degrees
                x = 1.0f - x;
                y = (0.5f - y) * ctx_.expand_coef;
                break;
              case 1:
                y = y * 2.0f;
                // Rotate 180 degrees
                x = 1.0f - x;
                y = 1.0f - ctx_.expand_coef * (y - 0.5f);
                break;
              case 2:
                y = y * 2.0f - 0.5f;
                y = 1.0f - ctx_.expand_coef * (1.0f - y);
                break;
              case 3:
                y = y * 2.0f - 1.5f;
                y = y * ctx_.expand_coef;
                break;
            }
          }
          break;
        }
      case LAYOUT_EAC_32:
        {
          vFace = (int) (y * 2);
          hFace = (int) (x * 3);
          x = x * 3.0f - hFace;
          y = y * 2.0f - vFace;
          x = tan((x - 0.5f) * M_PI * 0.5f) * 0.5f + 0.5f;
          y = tan((y - 0.5f) * M_PI * 0.5f) * 0.5f + 0.5f;
          face = hFace + (1 - vFace) * 3;
          break;
        }
      case LAYOUT_N:
        {
          printf(
            "Invalid layout type for plane %d.\n", transformMatPlaneIndex);
          return false;
        }
    }

    switch (ctx_.output_layout) {
      case LAYOUT_CUBEMAP_32:
      case LAYOUT_CUBEMAP_23_OFFCENTER:
      case LAYOUT_EQUIRECT:
      case LAYOUT_BARREL:
      case LAYOUT_BARREL_SPLIT:
      case LAYOUT_EAC_32:
      {
        if (ctx_.output_layout == LAYOUT_EQUIRECT ||
          (ctx_.output_layout == LAYOUT_BARREL && face < 0) ||
          (ctx_.output_layout == LAYOUT_BARREL_SPLIT && face < 0)) {
          float sin_yaw = sin(yaw);
          float sin_pitch = sin(pitch);
          float cos_yaw = cos(yaw);
          float cos_pitch = cos(pitch);
          qx = sin_yaw * cos_pitch;
          qy = sin_pitch;
          qz = cos_yaw * cos_pitch;
        } else {
          assert(x >= 0 && x <= 1);
          assert(y >= 0 && y <= 1);
          assert(face >= 0 && face < 6);
          if (ctx_.output_layout == LAYOUT_BARREL ||
            ctx_.output_layout == LAYOUT_BARREL_SPLIT) {
            float radius = (x - 0.5f) * (x - 0.5f) + (y - 0.5f) * (y - 0.5f);
            if (radius > 0.25f * ctx_.expand_coef * ctx_.expand_coef) {
              hasMapping = false;
              break;
            }
          }

          x = (x - 0.5f) * ctx_.expand_coef + 0.5f;
          y = (y - 0.5f) * ctx_.expand_coef + 0.5f;

          TransformFaceType enumFace = static_cast<TransformFaceType>(face);
          if (ctx_.output_layout == LAYOUT_CUBEMAP_23_OFFCENTER) {
            switch (enumFace) {
              case RIGHT:  p = P4; vx = PY; vy = NZ; break;
              case LEFT:   p = P3; vx = NX; vy = PZ; break;
              case TOP:    p = P5; vx = PY; vy = NX; break;
              case BOTTOM: p = P1; vx = NX; vy = PY; break;
              case FRONT:  p = P1; vx = PY; vy = PZ; break;
              case BACK:   p = P5; vx = NX; vy = NZ; break;
            }
          } else {
            switch (enumFace) {
              case RIGHT:   p = P5; vx = NZ; vy = PY; break;
              case LEFT:    p = P0; vx = PZ; vy = PY; break;
              case TOP:     p = P6; vx = PX; vy = NZ; break;
              case BOTTOM:  p = P0; vx = PX; vy = PZ; break;
              case FRONT:   p = P4; vx = PX; vy = PY; break;
              case BACK:    p = P1; vx = NX; vy = PY; break;
            }
          }

          qx = p [0] + vx [0] * x + vy [0] * y;
          qy = p [1] + vx [1] * x + vy [1] * y;
          qz = p [2] + vx [2] * x + vy [2] * y;
        }

        if (std::abs(ctx_.fixed_cube_offcenter_x) > kEpsilon ||
          std::abs(ctx_.fixed_cube_offcenter_y) > kEpsilon ||
          std::abs(ctx_.fixed_cube_offcenter_z) > kEpsilon) {
          float dist;
          d = sqrtf(qx * qx + qy * qy + qz * qz);
          qx = qx / d;
          qy = qy / d;
          qz = qz / d;
          if (ctx_.is_horizontal_offset) {
            d = sqrtf(qx * qx + qz * qz);
            qx = qx / d;
            qy = qy / d;
            qz = qz / d;
            dist = intersectSphereOffset(
              qx, 0, qz,
              ctx_.fixed_cube_offcenter_x, 0, ctx_.fixed_cube_offcenter_z);
            if (dist > 0.0f) {
              qx = qx * dist - ctx_.fixed_cube_offcenter_x;
              qz = qz * dist - ctx_.fixed_cube_offcenter_z;
            }
          } else {
            dist = intersectSphereOffset(
              qx, qy, qz, ctx_.fixed_cube_offcenter_x,
              ctx_.fixed_cube_offcenter_y, ctx_.fixed_cube_offcenter_z);
            if (dist > 0.0f) {
              qx = qx * dist - ctx_.fixed_cube_offcenter_x;
              qy = qy * dist - ctx_.fixed_cube_offcenter_y;
              qz = qz * dist - ctx_.fixed_cube_offcenter_z;
            }
          }
        }

        // rotation
        float s1 = sin(ctx_.fixed_yaw * M_PI / 180.0f);
        float s2 = sin(ctx_.fixed_pitch * M_PI / 180.0f);
        float s3 = sin(ctx_.fixed_roll * M_PI / 180.0f);
        float c1 = cos(ctx_.fixed_yaw * M_PI / 180.0f);
        float c2 = cos(ctx_.fixed_pitch * M_PI / 180.0f);
        float c3 = cos(ctx_.fixed_roll * M_PI / 180.0f);

        tx = qx * (c1 * c3 + s1 * s2 * s3)
          - qy * (c3 * s1 * s2 - c1 * s3)
          + qz * (c2 * s1);
        ty = qx * (c2 * s3) - qy * (c2 * c3)
          + qz * (-s2);
        tz = qx * (c1 * s2 * s3 - c3 * s1)
          - qy * (c1 * c3 * s2 + s1 * s3)
          + qz * (c1 * c2);

        ty = -ty;

        transformInputPos(tx, ty, tz, inputPixelWidth, outX, outY);
        break;
      }
 #ifdef FACEBOOK_LAYOUT
      case LAYOUT_FB:
      {
        calculateTranspos(
          ctx_.fixed_yaw,
          ctx_.fixed_pitch,
          ctx_.fixed_hfov,
          ctx_.fixed_vfov,
          x,
          y,
          outX,
          outY);
        break;
      }
#endif
      case LAYOUT_FLAT_FIXED:
      {
        *outX = ((x - 0.5f) * ctx_.fixed_hfov + ctx_.fixed_yaw) / 360.0f + 0.5f;
        *outY = ((y - 0.5f) * ctx_.fixed_vfov - ctx_.fixed_pitch) / 180.0f + 0.5f;
        normalize_equirectangular(*outX, *outY, outX, outY);
        break;
      }
      case LAYOUT_N:
        {
          printf(
            "Invalid layout type for plane %d.\n", transformMatPlaneIndex);
          return false;
        }
    }

    if (hasMapping) {
      switch (ctx_.input_stereo_format) {
        case STEREO_FORMAT_TB:
          {
            if (isRight) {
              *outY = *outY * kYHalf + kYHalf;
            } else {
              *outY = *outY * kYHalf;
          }
          break;
        }
        case STEREO_FORMAT_LR:
          {
            if (isRight) {
              *outX = *outX * kXHalf + kXHalf;
            } else {
              *outX = *outX * kXHalf;
            }
            break;
          }
        case STEREO_FORMAT_MONO:
        case STEREO_FORMAT_GUESS:
        case STEREO_FORMAT_N:
          break;
      }

      assert(*outX >= 0 && *outX <= 1);
      assert(*outY >= 0 && *outY <= 1);
    } else {
      *outX = -1;
      *outY = 0;
    }
    return true;
  } catch (const exception& ex) {
    printf(
      "Could not transform the plane %d. Error: %s\n",
       transformMatPlaneIndex,
       ex.what());
    return false;
  }
}