Tango refactoring (#534)

* Created general CameraMobile interface. Tango is now optional. Camera is disabled on visualizaion (battery saving).

* Working android app on non-tango android phones (tested on x86_64 android emulator).

* fixed typo

* android: updated tango not available msg

* android: fixed build with latest android sdk/ndk

* Added ARCore limited support for pose and rgb streams.

* Added AREngine support. Don't assert if depth size is not a modulo of rgb size.

* Added ARCore shared camera support

* android: fixed read/write runtime permissions for >=api23. arcore ndk: added feature point cloud. Fixed file sharing persmissions (>=api24 issue)

* ARCore NDK: mapping with feature point cloud

* android: Added post build strip command to reduce native library size

* android: fixed some compilation issues

* android: put back gtsam as default optimizer, manifest min api is dynamic based on cmake parameters

* AREngine: min api 24

* android: Fixed build without AREngine

* android: fixed not available libraries for API<24

* android: fixed build with old cmake versions

* android: set arcore min api to 23

* android: fixed arengine error on start when not built with native arengine support

* android: fixed tango camera permission for api>=23

* Added bionic android docker files

* Fixed wrong 3D words projection when depth size is not an exact multiple of rgb size. DbViewer: fixed images size not correctly shown in label of the calibration. ImageView: fixed depth scale when depth size is not a multiple of rgb size

* CameraMobile: added exact display rotation for local transform

* DbViewer: fixed gravity link shown in constraint view, show full local transform matrix in camera calibration label

* Android: fixed localization mode in visualization, fixed some tansitions between some UI states

* Android: Hide stop button when HUD is hidden. Updated About years.
This commit is contained in:
matlabbe
2020-04-22 15:05:07 -04:00
committed by GitHub
parent 931d525874
commit 012439aa0b
62 changed files with 6260 additions and 2194 deletions
@@ -0,0 +1,556 @@
package com.introlab.rtabmap;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.ar.core.Camera;
import com.google.ar.core.CameraIntrinsics;
import com.google.ar.core.Config;
import com.google.ar.core.Frame;
import com.google.ar.core.ImageMetadata;
import com.google.ar.core.Pose;
import com.google.ar.core.Session;
import com.google.ar.core.SharedCamera;
import com.google.ar.core.TrackingState;
import com.google.ar.core.exceptions.CameraNotAvailableException;
import com.google.ar.core.exceptions.NotYetAvailableException;
import com.google.ar.core.exceptions.UnavailableException;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.Surface;
public class ARCoreSharedCamera {
public static final String TAG = ARCoreSharedCamera.class.getSimpleName();
private static RTABMapActivity mActivity;
public ARCoreSharedCamera(RTABMapActivity c) {
mActivity = c;
}
// Depth TOF Image.
// Use 240 * 180 for now, hardcoded for Huawei P30 Pro
private static final int DEPTH_WIDTH = 240;
private static final int DEPTH_HEIGHT = 180;
// GL Surface used to draw camera preview image.
public GLSurfaceView surfaceView;
// ARCore session that supports camera sharing.
private Session sharedSession;
// Camera capture session. Used by both non-AR and AR modes.
private CameraCaptureSession captureSession;
// Reference to the camera system service.
private CameraManager cameraManager;
// Camera device. Used by both non-AR and AR modes.
private CameraDevice cameraDevice;
// Looper handler thread.
private HandlerThread backgroundThread;
// Looper handler.
private Handler backgroundHandler;
// ARCore shared camera instance, obtained from ARCore session that supports sharing.
private SharedCamera sharedCamera;
// Camera ID for the camera used by ARCore.
private String cameraId;
private AtomicBoolean mReady = new AtomicBoolean(false);
// Camera preview capture request builder
private CaptureRequest.Builder previewCaptureRequestBuilder;
private int cameraTextureId = -1;
// Image reader that continuously processes CPU images.
public TOF_ImageReader mTOFImageReader = new TOF_ImageReader();
private boolean mTOFAvailable = false;
// Camera device state callback.
private final CameraDevice.StateCallback cameraDeviceCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera device ID " + cameraDevice.getId() + " opened.");
ARCoreSharedCamera.this.cameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onClosed(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera device ID " + cameraDevice.getId() + " closed.");
ARCoreSharedCamera.this.cameraDevice = null;
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
Log.w(TAG, "Camera device ID " + cameraDevice.getId() + " disconnected.");
cameraDevice.close();
ARCoreSharedCamera.this.cameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
Log.e(TAG, "Camera device ID " + cameraDevice.getId() + " error " + error);
cameraDevice.close();
ARCoreSharedCamera.this.cameraDevice = null;
}
};
// Repeating camera capture session state callback.
CameraCaptureSession.StateCallback cameraCaptureCallback =
new CameraCaptureSession.StateCallback() {
// Called when the camera capture session is first configured after the app
// is initialized, and again each time the activity is resumed.
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
Log.d(TAG, "Camera capture session configured.");
captureSession = session;
setRepeatingCaptureRequest();
}
@Override
public void onSurfacePrepared(
@NonNull CameraCaptureSession session, @NonNull Surface surface) {
Log.d(TAG, "Camera capture surface prepared.");
}
@Override
public void onReady(@NonNull CameraCaptureSession session) {
Log.d(TAG, "Camera capture session ready.");
}
@Override
public void onActive(@NonNull CameraCaptureSession session) {
Log.d(TAG, "Camera capture session active.");
resumeARCore();
}
@Override
public void onClosed(@NonNull CameraCaptureSession session) {
Log.d(TAG, "Camera capture session closed.");
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.e(TAG, "Failed to configure camera capture session.");
}
};
// Repeating camera capture session capture callback.
private final CameraCaptureSession.CaptureCallback captureSessionCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
Log.i(TAG, "onCaptureCompleted");
}
//@Override // android 23
public void onCaptureBufferLost(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull Surface target,
long frameNumber) {
Log.e(TAG, "onCaptureBufferLost: " + frameNumber);
}
@Override
public void onCaptureFailed(
@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureFailure failure) {
Log.e(TAG, "onCaptureFailed: " + failure.getFrameNumber() + " " + failure.getReason());
}
@Override
public void onCaptureSequenceAborted(
@NonNull CameraCaptureSession session, int sequenceId) {
Log.e(TAG, "onCaptureSequenceAborted: " + sequenceId + " " + session);
}
};
private void resumeARCore() {
// Ensure that session is valid before triggering ARCore resume. Handles the case where the user
// manually uninstalls ARCore while the app is paused and then resumes.
if (sharedSession == null) {
return;
}
try {
Log.i(TAG, "Resume ARCore.");
// Resume ARCore.
sharedSession.resume();
// Set capture session callback while in AR mode.
sharedCamera.setCaptureCallback(captureSessionCallback, backgroundHandler);
} catch (CameraNotAvailableException e) {
Log.e(TAG, "Failed to resume ARCore session", e);
return;
}
}
// Called when starting non-AR mode or switching to non-AR mode.
// Also called when app starts in AR mode, or resumes in AR mode.
private void setRepeatingCaptureRequest() {
try {
captureSession.setRepeatingRequest(
previewCaptureRequestBuilder.build(), captureSessionCallback, backgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to set repeating request", e);
}
}
private void createCameraPreviewSession() {
Log.e(TAG, "createCameraPreviewSession: " + "starting camera preview session.");
try {
// Note that isGlAttached will be set to true in AR mode in onDrawFrame().
sharedSession.setCameraTextureName(cameraTextureId);
// Create an ARCore compatible capture request using `TEMPLATE_RECORD`.
previewCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
// Build surfaces list, starting with ARCore provided surfaces.
List<Surface> surfaceList = sharedCamera.getArCoreSurfaces();
Log.e(TAG, " createCameraPreviewSession: " + "surfaceList: sharedCamera.getArCoreSurfaces(): " + surfaceList.size());
// Add a CPU image reader surface. On devices that don't support CPU image access, the image
// may arrive significantly later, or not arrive at all.
if (mTOFAvailable) surfaceList.add(mTOFImageReader.imageReader.getSurface());
// Surface list should now contain three surfacemReadymReadys:
// 0. sharedCamera.getSurfaceTexture()
// 1. …
// 2. depthImageReader.getSurface()
// Add ARCore surfaces and CPU image surface targets.
for (Surface surface : surfaceList) {
previewCaptureRequestBuilder.addTarget(surface);
}
// Wrap our callback in a shared camera callback.
CameraCaptureSession.StateCallback wrappedCallback = sharedCamera.createARSessionStateCallback(cameraCaptureCallback, backgroundHandler);
// Create camera capture session for camera preview using ARCore wrapped callback.
cameraDevice.createCaptureSession(surfaceList, wrappedCallback, backgroundHandler);
mReady.set(true);
} catch (CameraAccessException e) {
Log.e(TAG, "CameraAccessException", e);
}
}
// Start background handler thread, used to run callbacks without blocking UI thread.
private void startBackgroundThread() {
backgroundThread = new HandlerThread("sharedCameraBackground");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
mTOFImageReader.startBackgroundThread();
}
// Stop background handler thread.
private void stopBackgroundThread() {
if (backgroundThread != null) {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted while trying to join background handler thread", e);
}
}
mTOFImageReader.stopBackgroundThread();
}
private long mPreviousTime = 0;
// Perform various checks, then open camera device and create CPU image reader.
public boolean openCamera() {
close();
startBackgroundThread();
mPreviousTime = System.currentTimeMillis();
if(cameraTextureId == -1)
{
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
cameraTextureId = textures[0];
}
Log.v(TAG + " opencamera: ", "Perform various checks, then open camera device and create CPU image reader.");
// Don't open camera if already opened.
if (cameraDevice != null) {
return false;
}
if (sharedSession == null) {
try {
// Create ARCore session that supports camera sharing.
sharedSession = new Session(mActivity, EnumSet.of(Session.Feature.SHARED_CAMERA));
} catch (UnavailableException e) {
Log.e(TAG, "Failed to create ARCore session that supports camera sharing", e);
return false;
}
// Enable auto focus mode while ARCore is running.
Config config = sharedSession.getConfig();
config.setFocusMode(Config.FocusMode.AUTO);
config.setUpdateMode(Config.UpdateMode.LATEST_CAMERA_IMAGE);
config.setPlaneFindingMode(Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL);
config.setLightEstimationMode(Config.LightEstimationMode.DISABLED);
config.setCloudAnchorMode(Config.CloudAnchorMode.ENABLED);
sharedSession.configure(config);
}
// Store the ARCore shared camera reference.
sharedCamera = sharedSession.getSharedCamera();
// Store the ID of the camera used by ARCore.
cameraId = sharedSession.getCameraConfig().getCameraId();
initCamera(mActivity, cameraId, 1);
ArrayList<String> resolutions;
mTOFAvailable = false;
resolutions = getResolutions(mActivity, cameraId, ImageFormat.DEPTH16);
if (resolutions != null) {
for( String temp : resolutions) {
Log.v(TAG + "DEPTH16 resolution: ", temp);
};
if (resolutions.size() > 0) mTOFAvailable = true;
}
// Color CPU Image.
// Use the currently configured CPU image size.
//Size desiredCPUImageSize = sharedSession.getCameraConfig().getImageSize();
if (mTOFAvailable) mTOFImageReader.createImageReader(DEPTH_WIDTH, DEPTH_HEIGHT);
// When ARCore is running, make sure it also updates our CPU image surface.
if (mTOFAvailable) {
sharedCamera.setAppSurfaces(this.cameraId, Arrays.asList(mTOFImageReader.imageReader.getSurface()));
}
try {
// Wrap our callback in a shared camera callback.
CameraDevice.StateCallback wrappedCallback = sharedCamera.createARDeviceStateCallback(cameraDeviceCallback, backgroundHandler);
// Store a reference to the camera system service.
cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
// Get the characteristics for the ARCore camera.
//CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(this.cameraId);
// Open the camera device using the ARCore wrapped callback.
cameraManager.openCamera(cameraId, wrappedCallback, backgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to open camera", e);
return false;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Failed to open camera", e);
return false;
} catch (SecurityException e) {
Log.e(TAG, "Failed to open camera", e);
return false;
}
Log.i(TAG, " opencamera: TOF_available: " + mTOFAvailable);
return true;
}
// Close the camera device.
public void close() {
if (sharedSession != null) {
sharedSession.pause();
}
if (captureSession != null) {
captureSession.close();
captureSession = null;
}
if (cameraDevice != null) {
cameraDevice.close();
}
if (mTOFImageReader.imageReader != null) {
mTOFImageReader.imageReader.close();
mTOFImageReader.imageReader = null;
}
if(cameraTextureId>=0)
{
GLES20.glDeleteTextures(1, new int[] {cameraTextureId}, 0);
}
stopBackgroundThread();
}
/*************************************************** ONDRAWFRAME ARCORE ************************************************************* */
// Draw frame when in AR mode. Called on the GL thread.
public void updateGL() throws CameraNotAvailableException {
if(!mReady.get())
{
return;
}
if (mTOFAvailable && mTOFImageReader.frameCount == 0) return;
// Perform ARCore per-frame update.
Frame frame = null;
try {
frame = sharedSession.update();
} catch (Exception e) {
e.printStackTrace();
return;
}
Camera camera = null;
if (frame != null) {
camera = frame.getCamera();
}else
{
return;
}
if (camera == null) return;
// If not tracking, don't draw 3D objects.
if (camera.getTrackingState() == TrackingState.PAUSED) return;
if (frame.getTimestamp() != 0) {
Pose pose = camera.getPose();
if(!mActivity.DISABLE_LOG) Log.d(TAG, String.format("pose=%f %f %f q=%f %f %f %f", pose.tx(), pose.ty(), pose.tz(), pose.qx(), pose.qy(), pose.qz(), pose.qw()));
RTABMapLib.postCameraPoseEvent(mActivity.nativeApplication, pose.tx(), pose.ty(), pose.tz(), pose.qx(), pose.qy(), pose.qz(), pose.qw());
int rateMs = 100; // send images at most 10 Hz
if(System. currentTimeMillis() - mPreviousTime < rateMs)
{
return;
}
mPreviousTime = System. currentTimeMillis();
CameraIntrinsics intrinsics = camera.getImageIntrinsics();
try{
Image image = frame.acquireCameraImage();
if (image.getFormat() != ImageFormat.YUV_420_888) {
throw new IllegalArgumentException(
"Expected image in YUV_420_888 format, got format " + image.getFormat());
}
float[] fl = intrinsics.getFocalLength();
float[] pp = intrinsics.getPrincipalPoint();
if(!mActivity.DISABLE_LOG) Log.d(TAG, String.format("fx=%f fy=%f cx=%f cy=%f", fl[0], fl[1], pp[0], pp[1]));
ByteBuffer rgb = image.getPlanes()[0].getBuffer().asReadOnlyBuffer();
double stamp = (double)image.getTimestamp()/10e8;
if(!mActivity.DISABLE_LOG) Log.d(TAG, String.format("RGB %dx%d len=%dbytes format=%d =%f",
image.getWidth(), image.getHeight(), rgb.limit(), image.getFormat(), stamp));
if(!mActivity.DISABLE_LOG) Log.d(TAG, String.format("Depth %dx%d len=%dbytes format=%d stamp=%f",
mTOFImageReader.WIDTH, mTOFImageReader.HEIGHT, mTOFImageReader.depth16_raw.limit(), ImageFormat.DEPTH16, (double)mTOFImageReader.timestamp/10e9));
RTABMapLib.postOdometryEvent(
mActivity.nativeApplication,
pose.tx(), pose.ty(), pose.tz(), pose.qx(), pose.qy(), pose.qz(), pose.qw(),
fl[0], fl[1], pp[0], pp[1], stamp,
rgb, rgb.limit(), image.getWidth(), image.getHeight(), image.getFormat(),
mTOFImageReader.depth16_raw, mTOFImageReader.depth16_raw.limit(), mTOFImageReader.WIDTH, mTOFImageReader.HEIGHT, ImageFormat.DEPTH16);
image.close();
} catch (NotYetAvailableException e) {
}
}
}
/********************************************************************************************************************* */
/*************************************************** End ************************************************************* */
/********************************************************************************************************************* */
public ArrayList<String> getResolutions (Context context, String cameraId,int imageFormat){
Log.v(TAG + "getResolutions:", " cameraId:" + cameraId + " imageFormat: " + imageFormat);
ArrayList<String> output = new ArrayList<String>();
try {
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
for (android.util.Size s : characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(imageFormat)) {
output.add(s.getWidth() + "x" + s.getHeight());
}
} catch (Exception e) {
e.printStackTrace();
}
return output;
}
public void initCamera (Context context, String cameraId,int index){
boolean ok = false;
try {
int current = 0;
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
for (android.util.Size s : characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.DEPTH16)) {
ok = true;
if (current == index)
break;
else ;
current++;
}
} catch (Exception e) {
e.printStackTrace();
}
if (!ok) {
Log.e(TAG + " initCamera", "Depth sensor not found!");
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.introlab.rtabmap;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
/** Helper to ask camera permission. */
public class PermissionHelper {
public static final int CAMERA_CODE = 0;
public static final int READ_EXTERNAL_STORAGE_CODE = 1;
public static final int WRITE_EXTERNAL_STORAGE_CODE = 2;
public static final int INTERNET_CODE = 3;
public static final int ACCESS_NETWORK_STATE_CODE = 4;
public static final int ACCESS_FINE_LOCATION_CODE = 5;
public static final int ACCESS_WIFI_STATE_CODE = 6;
/** Check to see we have the necessary permissions for this app. */
public static boolean hasPermission(Activity activity, String permission) {
return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED;
}
/** Check to see we have the necessary permissions for this app, and ask for them if we don't. */
public static void requestPermission(Activity activity, String permission) {
int requestCode = -1;
if (permission == Manifest.permission.CAMERA) {
requestCode = CAMERA_CODE;
} else if(permission == Manifest.permission.READ_EXTERNAL_STORAGE) {
requestCode = READ_EXTERNAL_STORAGE_CODE;
} else if(permission == Manifest.permission.WRITE_EXTERNAL_STORAGE) {
requestCode = WRITE_EXTERNAL_STORAGE_CODE;
} else if(permission == Manifest.permission.INTERNET) {
requestCode = INTERNET_CODE;
} else if(permission == Manifest.permission.ACCESS_NETWORK_STATE) {
requestCode = ACCESS_NETWORK_STATE_CODE;
} else if(permission == Manifest.permission.ACCESS_FINE_LOCATION) {
requestCode = ACCESS_FINE_LOCATION_CODE;
} else if(permission == Manifest.permission.ACCESS_WIFI_STATE) {
requestCode = ACCESS_WIFI_STATE_CODE;
}
if(requestCode >=0)
{
ActivityCompat.requestPermissions(
activity, new String[] {permission}, requestCode);
}
}
/** Check to see if we need to show the rationale for this permission. */
public static boolean shouldShowRequestPermissionRationale(Activity activity, String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
}
/** Launch Application Setting to grant permission. */
public static void launchPermissionSettings(Activity activity) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", activity.getPackageName(), null));
activity.startActivity(intent);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,9 @@
package com.introlab.rtabmap;
import java.nio.ByteBuffer;
import android.app.Activity;
import android.content.Context;
import android.os.IBinder;
import android.view.KeyEvent;
import android.util.Log;
@@ -15,7 +19,7 @@ public class RTABMapLib
// the correct library first.
if (TangoInitializationHelper.loadTangoSharedLibrary() ==
TangoInitializationHelper.ARCH_ERROR) {
Log.e(RTABMapActivity.class.getSimpleName(), "ERROR! Unable to load libtango_client_api.so!");
Log.w(RTABMapActivity.class.getSimpleName(), "WArning! Unable to load libtango_client_api.so! This can be safely ignored if RTAB-Map NDK is not build with tango support.");
}
System.loadLibrary("NativeRTABMap");
}
@@ -23,88 +27,89 @@ public class RTABMapLib
// Initialize the Tango Service, this function starts the communication
// between the application and Tango Service.
// The activity object is used for checking if the API version is outdated.
public static native void onCreate(RTABMapActivity activity);
public static native long createNativeApplication(RTABMapActivity activity);
public static native void setScreenRotation(int displayRotation, int cameraRotation);
public static native void destroyNativeApplication(long nativeApplication);
public static native int openDatabase(String databasePath, boolean databaseInMemory, boolean optimize);
public static native int openDatabase2(String databaseSource, String databasePath, boolean databaseInMemory, boolean optimize);
public static native void setScreenRotation(long nativeApplication, int displayRotation, int cameraRotation);
public static native int openDatabase(long nativeApplication, String databasePath, boolean databaseInMemory, boolean optimize);
public static native int openDatabase2(long nativeApplication, String databaseSource, String databasePath, boolean databaseInMemory, boolean optimize);
/*
* Called when the Tango service is connected.
* Called when the camera service is connected.
*
* @param binder The native binder object.
*/
public static native boolean onTangoServiceConnected(IBinder binder);
public static native boolean startCamera(long nativeApplication, IBinder binder, Context context, Activity activity, int driver);
// Release all non OpenGl resources that are allocated from the program.
public static native void onPause();
public static native void stopCamera(long nativeApplication);
// Allocate OpenGL resources for rendering.
public static native void initGlContent();
public static native void initGlContent(long nativeApplication);
// Setup the view port width and height.
public static native void setupGraphic(int width, int height);
public static native void setupGraphic(long nativeApplication, int width, int height);
// Main render loop.
public static native int render();
public static native int render(long nativeApplication);
// Set the render camera's viewing angle:
// first person, third person, or top down.
public static native void setCamera(int cameraIndex);
public static native void setCamera(long nativeApplication, int cameraIndex);
// Pass touch events to the native layer.
public static native void onTouchEvent(int touchCount, int event0,
public static native void onTouchEvent(long nativeApplication, int touchCount, int event0,
float x0, float y0, float x1, float y1);
public static native void setPausedMapping(boolean paused);
public static native void setOnlineBlending(boolean enabled);
public static native void setMapCloudShown(boolean shown);
public static native void setOdomCloudShown(boolean shown);
public static native void setMeshRendering(boolean enabled, boolean withTexture);
public static native void setLocalizationMode(boolean enabled);
public static native void setTrajectoryMode(boolean enabled);
public static native void setGraphOptimization(boolean enabled);
public static native void setNodesFiltering(boolean enabled);
public static native void setGraphVisible(boolean visible);
public static native void setGridVisible(boolean visible);
public static native void setRawScanSaved(boolean enabled);
public static native void setFullResolution(boolean enabled);
public static native void setSmoothing(boolean enabled);
public static native void setCameraColor(boolean enabled);
public static native void setAppendMode(boolean enabled);
public static native void setDataRecorderMode(boolean enabled);
public static native void setMaxCloudDepth(float value);
public static native void setMinCloudDepth(float value);
public static native void setPointSize(float value);
public static native void setFOV(float value);
public static native void setOrthoCropFactor(float value);
public static native void setGridRotation(float value);
public static native void setLighting(boolean enabled);
public static native void setBackfaceCulling(boolean enabled);
public static native void setWireframe(boolean enabled);
public static native void setCloudDensityLevel(int value);
public static native void setMeshAngleTolerance(float value);
public static native void setMeshTriangleSize(int value);
public static native void setClusterRatio(float value);
public static native void setMaxGainRadius(float value);
public static native void setRenderingTextureDecimation(int value);
public static native void setBackgroundColor(float gray);
public static native int setMappingParameter(String key, String value);
public static native void setPausedMapping(long nativeApplication, boolean paused);
public static native void setOnlineBlending(long nativeApplication, boolean enabled);
public static native void setMapCloudShown(long nativeApplication, boolean shown);
public static native void setOdomCloudShown(long nativeApplication, boolean shown);
public static native void setMeshRendering(long nativeApplication, boolean enabled, boolean withTexture);
public static native void setLocalizationMode(long nativeApplication, boolean enabled);
public static native void setTrajectoryMode(long nativeApplication, boolean enabled);
public static native void setGraphOptimization(long nativeApplication, boolean enabled);
public static native void setNodesFiltering(long nativeApplication, boolean enabled);
public static native void setGraphVisible(long nativeApplication, boolean visible);
public static native void setGridVisible(long nativeApplication, boolean visible);
public static native void setRawScanSaved(long nativeApplication, boolean enabled);
public static native void setFullResolution(long nativeApplication, boolean enabled);
public static native void setSmoothing(long nativeApplication, boolean enabled);
public static native void setCameraColor(long nativeApplication, boolean enabled);
public static native void setAppendMode(long nativeApplication, boolean enabled);
public static native void setDataRecorderMode(long nativeApplication, boolean enabled);
public static native void setMaxCloudDepth(long nativeApplication, float value);
public static native void setMinCloudDepth(long nativeApplication, float value);
public static native void setPointSize(long nativeApplication, float value);
public static native void setFOV(long nativeApplication, float value);
public static native void setOrthoCropFactor(long nativeApplication, float value);
public static native void setGridRotation(long nativeApplication, float value);
public static native void setLighting(long nativeApplication, boolean enabled);
public static native void setBackfaceCulling(long nativeApplication, boolean enabled);
public static native void setWireframe(long nativeApplication, boolean enabled);
public static native void setCloudDensityLevel(long nativeApplication, int value);
public static native void setMeshAngleTolerance(long nativeApplication, float value);
public static native void setMeshTriangleSize(long nativeApplication, int value);
public static native void setClusterRatio(long nativeApplication, float value);
public static native void setMaxGainRadius(long nativeApplication, float value);
public static native void setRenderingTextureDecimation(long nativeApplication, int value);
public static native void setBackgroundColor(long nativeApplication, float gray);
public static native int setMappingParameter(long nativeApplication, String key, String value);
public static native void setGPS(
long nativeApplication,
double stamp,
double longitude,
double latitude,
double altitude,
double accuracy,
double bearing);
public static native void addEnvSensor(int type, float value);
public static native void addEnvSensor(long nativeApplication, int type, float value);
public static native void resetMapping();
public static native void save(String outputDatabasePath);
public static native void cancelProcessing();
public static native void save(long nativeApplication, String outputDatabasePath);
public static native void cancelProcessing(long nativeApplication);
public static native boolean exportMesh(
long nativeApplication,
float cloudVoxelSize,
boolean regenerateCloud,
boolean meshing,
@@ -121,15 +126,23 @@ public class RTABMapLib
float optimizedMaxTextureDistance,
int optimizedMinTextureClusterSize,
boolean blockRendering);
public static native boolean writeExportedMesh(String directory, String name);
public static native boolean postExportation(boolean visualize);
public static native int postProcessing(int approach);
public static native boolean writeExportedMesh(long nativeApplication, String directory, String name);
public static native boolean postExportation(long nativeApplication, boolean visualize);
public static native int postProcessing(long nativeApplication, int approach);
public static native String getStatus();
public static native int getTotalNodes();
public static native int getTotalWords();
public static native int getTotalPoints();
public static native float getUpdateTime();
public static native int getLoopClosureId();
public static native String getStatus(long nativeApplication);
public static native int getTotalNodes(long nativeApplication);
public static native int getTotalWords(long nativeApplication);
public static native int getTotalPoints(long nativeApplication);
public static native float getUpdateTime(long nativeApplication);
public static native int getLoopClosureId(long nativeApplication);
public static native void postCameraPoseEvent(long nativeApplication, float x, float y, float z, float qx, float qy, float qz, float qw);
public static native void postOdometryEvent(long nativeApplication,
float x, float y, float z, float qx, float qy, float qz, float qw,
float fx, float fy, float cx, float cy,
double stamp,
ByteBuffer rgb, int rgbLen, int rgbWidth, int rgbHeight, int rgbFormat,
ByteBuffer depth, int depthLen, int depthWidth, int depthHeight, int depthFormat);
}
@@ -43,6 +43,7 @@ public class Renderer implements GLSurfaceView.Renderer {
private float mSurfaceHeight = 0.0f;
private float mTextColor = 1.0f;
private int mOffset = 0;
private ARCoreSharedCamera mCamera = null;
private Vector<TextObject> mTexts;
@@ -71,91 +72,109 @@ public class Renderer implements GLSurfaceView.Renderer {
{
mOffset = offset;
}
public void setCamera(ARCoreSharedCamera camera)
{
mCamera = camera;
}
// Render loop of the Gl context.
public void onDrawFrame(GL10 useGLES20instead) {
try
{
final int value = RTABMapLib.render();
if(mTextManager!=null)
synchronized (this) {
if(mActivity.nativeApplication != 0)
{
if(mTextChanged)
try
{
mTextChanged = false;
Vector<TextObject> txtcollection = new Vector<TextObject>();
mTextLock.lock();
try {
if(mTexts.size() > 0)
{
txtcollection.addAll(mTexts);
}
} finally {
mTextLock.unlock();
}
// Prepare the text for rendering
mTextManager.PrepareDraw(txtcollection);
}
float[] mvp = new float[16];
Matrix.translateM(mvp, 0, mtrxProjectionAndView, 0, 0, mOffset, 0);
mTextManager.Draw(mvp);
}
if(value != 0 && mProgressDialog != null && mProgressDialog.isShowing())
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(!RTABMapActivity.DISABLE_LOG) Log.i("RTABMapActivity", "Renderer: dismiss dialog, value received=" + String.valueOf(value));
mProgressDialog.dismiss();
mActivity.resetNoTouchTimer();
}
});
}
if(value==-1)
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(mToast!=null)
{
mToast.makeText(mActivity, String.format("Out of Memory!"), Toast.LENGTH_SHORT).show();
}
}
});
}
else if(value==-2)
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(mToast!=null)
{
mToast.makeText(mActivity, String.format("Rendering Error!"), Toast.LENGTH_SHORT).show();
}
}
});
}
}
catch(final Exception e)
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(mToast!=null)
if(mCamera!=null)
{
mToast.makeText(mActivity, String.format("Rendering error! %s", e.getMessage()), Toast.LENGTH_SHORT).show();
mCamera.updateGL();
}
final int value = RTABMapLib.render(mActivity.nativeApplication);
if(mTextManager!=null)
{
if(mTextChanged)
{
mTextChanged = false;
Vector<TextObject> txtcollection = new Vector<TextObject>();
mTextLock.lock();
try {
if(mTexts.size() > 0)
{
txtcollection.addAll(mTexts);
}
} finally {
mTextLock.unlock();
}
// Prepare the text for rendering
mTextManager.PrepareDraw(txtcollection);
}
float[] mvp = new float[16];
Matrix.translateM(mvp, 0, mtrxProjectionAndView, 0, 0, mOffset, 0);
mTextManager.Draw(mvp);
}
if(value != 0 && mProgressDialog != null && mProgressDialog.isShowing())
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(!RTABMapActivity.DISABLE_LOG) Log.i("RTABMapActivity", "Renderer: dismiss dialog, value received=" + String.valueOf(value));
mProgressDialog.dismiss();
mActivity.resetNoTouchTimer();
}
});
}
if(value==-1)
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(mToast!=null)
{
mToast.makeText(mActivity, String.format("Out of Memory!"), Toast.LENGTH_SHORT).show();
}
}
});
}
else if(value==-2)
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(mToast!=null)
{
mToast.makeText(mActivity, String.format("Rendering Error!"), Toast.LENGTH_SHORT).show();
}
}
});
}
}
});
catch(final Exception e)
{
mActivity.runOnUiThread(new Runnable() {
public void run() {
if(mToast!=null)
{
mToast.makeText(mActivity, String.format("Rendering error! %s", e.getMessage()), Toast.LENGTH_SHORT).show();
}
}
});
}
}
}
}
// Called when the surface size changes.
public void onSurfaceChanged(GL10 useGLES20instead, int width, int height) {
RTABMapLib.setupGraphic(width, height);
if(mActivity.nativeApplication!=0)
{
RTABMapLib.setupGraphic(mActivity.nativeApplication, width, height);
}
mSurfaceHeight = (float)height;
@@ -180,7 +199,10 @@ public class Renderer implements GLSurfaceView.Renderer {
// Called when the surface is created or recreated.
public void onSurfaceCreated(GL10 useGLES20instead, EGLConfig config) {
RTABMapLib.initGlContent();
if(mActivity.nativeApplication != 0)
{
RTABMapLib.initGlContent(mActivity.nativeApplication);
}
// Create our text manager
mTextManager = new TextManager(mActivity);
@@ -7,11 +7,14 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.Map.Entry;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
@@ -19,6 +22,7 @@ import android.text.InputType;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.Toast;
public class SettingsActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
@@ -34,7 +38,7 @@ public class SettingsActivity extends PreferenceActivity implements OnSharedPref
@Override
public boolean onPreferenceClick(Preference preference) {
getPreferenceScreen().getSharedPreferences().edit().clear().commit();
recreate();
return true;
@@ -184,7 +188,31 @@ public class SettingsActivity extends PreferenceActivity implements OnSharedPref
}
});
Preference buttonGPS = findPreference(getString(R.string.pref_key_gps_saved));
buttonGPS.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if(((CustomSwitchPreference)preference).isChecked())
{
if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) {
((CustomSwitchPreference)preference).setChecked(false);
PermissionHelper.requestPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION);
return false;
}
}
return true;
}
});
if(((CustomSwitchPreference)buttonGPS).isChecked())
{
if (!PermissionHelper.hasPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
((CustomSwitchPreference)buttonGPS).setChecked(false);
PermissionHelper.requestPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
}
}
((Preference)findPreference(getString(R.string.pref_key_camera_driver))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_camera_driver))).getEntry() + ") "+getString(R.string.pref_summary_camera_driver));
((Preference)findPreference(getString(R.string.pref_key_density))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_density))).getEntry() + ") "+getString(R.string.pref_summary_density));
((Preference)findPreference(getString(R.string.pref_key_depth))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_depth))).getEntry() + ") "+getString(R.string.pref_summary_depth));
((Preference)findPreference(getString(R.string.pref_key_min_depth))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_min_depth))).getEntry() + ") "+getString(R.string.pref_summary_min_depth));
@@ -228,6 +256,7 @@ public class SettingsActivity extends PreferenceActivity implements OnSharedPref
Preference pref = findPreference(key);
if (pref instanceof ListPreference) {
if(key.compareTo(getString(R.string.pref_key_camera_driver))==0) pref.setSummary("("+ ((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_camera_driver));
if(key.compareTo(getString(R.string.pref_key_density))==0) pref.setSummary("("+ ((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_density));
if(key.compareTo(getString(R.string.pref_key_depth))==0)
{
@@ -331,4 +360,28 @@ public class SettingsActivity extends PreferenceActivity implements OnSharedPref
}
ed.commit(); //save it.
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) {
switch (requestCode) {
case PermissionHelper.ACCESS_FINE_LOCATION_CODE: {
// If request is cancelled, the result arrays are empty.
if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
Preference buttonGPS = findPreference(getString(R.string.pref_key_gps_saved));
((CustomSwitchPreference)buttonGPS).setChecked(true);
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, "Location permission is needed to use GPS functionality", Toast.LENGTH_LONG).show();
if (!PermissionHelper.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
PermissionHelper.launchPermissionSettings(this); // Permission denied with checking "Do not ask again".
}
}
return;
}
}
}
}
@@ -245,7 +245,7 @@ public class SketchfabActivity extends Activity implements OnClickListener {
File exportDir = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_EXPORT_DIR);
exportDir.mkdirs();
if(RTABMapLib.writeExportedMesh(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR, RTABMapActivity.RTABMAP_TMP_FILENAME))
if(RTABMapLib.writeExportedMesh(RTABMapActivity.nativeApplication, mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR, RTABMapActivity.RTABMAP_TMP_FILENAME))
{
String[] files = new String[0];
// verify if we have all files
@@ -0,0 +1,87 @@
package com.introlab.rtabmap;
import android.graphics.ImageFormat;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import java.nio.ByteBuffer;
public class TOF_ImageReader implements ImageReader.OnImageAvailableListener {
public int WIDTH;
public int HEIGHT;
public ImageReader imageReader;
public int frameCount = 0;
public long timestamp;
// Looper handler thread.
private HandlerThread backgroundThread;
// Looper handler.
private Handler backgroundHandler;
public ByteBuffer depth16_raw;
TOF_ImageReader(){
}
public void createImageReader(int width, int height){
this.WIDTH = width;
this.HEIGHT = height;
this.imageReader =
ImageReader.newInstance(
width,
height,
ImageFormat.DEPTH16,
2);
this.imageReader.setOnImageAvailableListener(this, this.backgroundHandler);
}
// CPU image reader callback.
@Override
public void onImageAvailable(ImageReader imageReader) {
Image image = imageReader.acquireLatestImage();
if (image == null) {
Log.w("RTABMapActivity", "onImageAvailable: Skipping null image.");
return;
}
else{
if(image.getFormat() == ImageFormat.DEPTH16){
this.timestamp = image.getTimestamp();
depth16_raw = image.getPlanes()[0].getBuffer().asReadOnlyBuffer();
// copy raw undecoded DEPTH16 format depth data to NativeBuffer
frameCount++;
}
else{
Log.w("RTABMapActivity", "onImageAvailable: depth image not in DEPTH16 format, skipping image");
}
}
image.close();
}
// Start background handler thread, used to run callbacks without blocking UI thread.
public void startBackgroundThread() {
this.backgroundThread = new HandlerThread("DepthDecoderThread");
this.backgroundThread.start();
this.backgroundHandler = new Handler(backgroundThread.getLooper());
}
// Stop background handler thread.
public void stopBackgroundThread() {
if (this.backgroundThread != null) {
this.backgroundThread.quitSafely();
try {
this.backgroundThread.join();
this.backgroundThread = null;
this.backgroundHandler = null;
} catch (InterruptedException e) {
Log.e("RTABMapActivity", "Interrupted while trying to join depth background handler thread", e);
}
}
}
}
@@ -17,6 +17,7 @@ import android.graphics.Typeface;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.text.TextPaint;
import android.util.Log;
public class TextManager {
@@ -336,6 +337,8 @@ public class TextManager {
float y = val.y;
String text = val.text;
Log.i("RTABMapActivity", String.format("convertTextToTriangleInfo() set status=%s", text));
// Create
for(int j=0; j<text.length(); j++)
{