package org.wheelmap.android.tango;
import android.opengl.Matrix;
import android.util.Log;
import com.google.atap.tangoservice.TangoCameraIntrinsics;
import com.google.atap.tangoservice.TangoPointCloudData;
import com.google.atap.tangoservice.TangoPoseData;
import com.google.atap.tangoservice.TangoXyzIjData;
import com.projecttango.tangosupport.TangoPointCloudManager;
import com.projecttango.tangosupport.TangoSupport;
class TangoPointCloudUtils {
private static final String TAG = TangoPointCloudUtils.class.getSimpleName();
/**
* Use the TangoSupport library with point cloud data to calculate the plane
* of the world feature pointed at the location the camera is looking.
* It returns the transform of the fitted plane in a double array.
*/
static float[] doFitPlane(TangoPointCloudManager pointCloudManager, int displayRotation, float u, float v, double rgbTimestamp) {
TangoPointCloudData pointCloud = pointCloudManager.getLatestPointCloud();
if (pointCloud == null) {
return null;
}
// We need to calculate the transform between the color camera at the
// time the user clicked and the depth camera at the time the depth
// cloud was acquired.
TangoPoseData depthTcolorPose = TangoSupport.calculateRelativePose(
pointCloud.timestamp, TangoPoseData.COORDINATE_FRAME_CAMERA_DEPTH,
rgbTimestamp, TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR);
// Perform plane fitting with the latest available point cloud data.
double[] identityTranslation = {0.0, 0.0, 0.0};
double[] identityRotation = {0.0, 0.0, 0.0, 1.0};
TangoSupport.IntersectionPointPlaneModelPair intersectionPointPlaneModelPair =
TangoSupport.fitPlaneModelNearPoint(pointCloud,
identityTranslation, identityRotation, u, v, displayRotation,
depthTcolorPose.translation, depthTcolorPose.rotation);
// Get the transform from depth camera to OpenGL world at the timestamp of the cloud.
TangoSupport.TangoMatrixTransformData transform =
TangoSupport.getMatrixTransformAtTime(pointCloud.timestamp,
TangoPoseData.COORDINATE_FRAME_AREA_DESCRIPTION,
TangoPoseData.COORDINATE_FRAME_CAMERA_DEPTH,
TangoSupport.TANGO_SUPPORT_ENGINE_OPENGL,
TangoSupport.TANGO_SUPPORT_ENGINE_TANGO,
TangoSupport.ROTATION_IGNORED);
if (transform.statusCode == TangoPoseData.POSE_VALID) {
float[] openGlTPlane = calculatePlaneTransform(
intersectionPointPlaneModelPair.intersectionPoint,
intersectionPointPlaneModelPair.planeModel, transform.matrix);
return openGlTPlane;
} else {
Log.w(TAG, "Can't get depth camera transform at time " + pointCloud.timestamp);
return null;
}
}
/**
* Calculate the pose of the plane based on the position and normal orientation of the plane
* and align it with gravity.
*/
private static float[] calculatePlaneTransform(double[] point, double normal[],
float[] openGlTdepth) {
// Vector aligned to gravity.
float[] openGlUp = new float[]{0, 1, 0, 0};
float[] depthTOpenGl = new float[16];
Matrix.invertM(depthTOpenGl, 0, openGlTdepth, 0);
float[] depthUp = new float[4];
Matrix.multiplyMV(depthUp, 0, depthTOpenGl, 0, openGlUp, 0);
// Create the plane matrix transform in depth frame from a point, the plane normal and the
// up vector.
float[] depthTplane = matrixFromPointNormalUp(point, normal, depthUp);
float[] openGlTplane = new float[16];
Matrix.multiplyMM(openGlTplane, 0, openGlTdepth, 0, depthTplane, 0);
return openGlTplane;
}
/**
* Calculates a transformation matrix based on a point, a normal and the up gravity vector.
* The coordinate frame of the target transformation will be Z forward, X left, Y up.
*/
private static float[] matrixFromPointNormalUp(double[] point, double[] normal, float[] up) {
float[] zAxis = new float[]{(float) normal[0], (float) normal[1], (float) normal[2]};
normalize(zAxis);
float[] xAxis = crossProduct(up, zAxis);
normalize(xAxis);
float[] yAxis = crossProduct(zAxis, xAxis);
normalize(yAxis);
float[] m = new float[16];
Matrix.setIdentityM(m, 0);
m[0] = xAxis[0];
m[1] = xAxis[1];
m[2] = xAxis[2];
m[4] = yAxis[0];
m[5] = yAxis[1];
m[6] = yAxis[2];
m[8] = zAxis[0];
m[9] = zAxis[1];
m[10] = zAxis[2];
m[12] = (float) point[0];
m[13] = (float) point[1];
m[14] = (float) point[2];
return m;
}
/**
* Normalize a vector.
*/
private static void normalize(float[] v) {
double norm = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] /= norm;
v[1] /= norm;
v[2] /= norm;
}
/**
* Cross product between two vectors following the right hand rule.
*/
private static float[] crossProduct(float[] v1, float[] v2) {
float[] result = new float[3];
result[0] = v1[1] * v2[2] - v2[1] * v1[2];
result[1] = v1[2] * v2[0] - v2[2] * v1[0];
result[2] = v1[0] * v2[1] - v2[0] * v1[1];
return result;
}
}