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;
}
}