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