diff --git a/app/android/jni/RTABMapApp.cpp b/app/android/jni/RTABMapApp.cpp index a65d7a65..8a27447a 100644 --- a/app/android/jni/RTABMapApp.cpp +++ b/app/android/jni/RTABMapApp.cpp @@ -1598,6 +1598,14 @@ void RTABMapApp::setPointSize(float value) { main_scene_.setPointSize(value); } +void RTABMapApp::setFOV(float angle) +{ + main_scene_.setFOV(angle); +} +void RTABMapApp::setOrthoCropFactor(float value) +{ + main_scene_.setOrthoCropFactor(value); +} void RTABMapApp::setLighting(bool enabled) { main_scene_.setLighting(enabled); diff --git a/app/android/jni/RTABMapApp.h b/app/android/jni/RTABMapApp.h index 865815ca..50cfe500 100644 --- a/app/android/jni/RTABMapApp.h +++ b/app/android/jni/RTABMapApp.h @@ -119,6 +119,8 @@ class RTABMapApp : public UEventsHandler { void setOdomCloudShown(bool shown); void setMeshRendering(bool enabled, bool withTexture); void setPointSize(float value); + void setFOV(float angle); + void setOrthoCropFactor(float value); void setLighting(bool enabled); void setBackfaceCulling(bool enabled); void setLocalizationMode(bool enabled); diff --git a/app/android/jni/jni_interface.cpp b/app/android/jni/jni_interface.cpp index 4f2ae04c..9a56980e 100644 --- a/app/android/jni/jni_interface.cpp +++ b/app/android/jni/jni_interface.cpp @@ -157,6 +157,18 @@ Java_com_introlab_rtabmap_RTABMapLib_setPointSize( return app.setPointSize(value); } JNIEXPORT void JNICALL +Java_com_introlab_rtabmap_RTABMapLib_setFOV( + JNIEnv*, jobject, float fov) +{ + return app.setFOV(fov); +} +JNIEXPORT void JNICALL +Java_com_introlab_rtabmap_RTABMapLib_setOrthoCropFactor( + JNIEnv*, jobject, float value) +{ + return app.setOrthoCropFactor(value); +} +JNIEXPORT void JNICALL Java_com_introlab_rtabmap_RTABMapLib_setLighting( JNIEnv*, jobject, bool enabled) { diff --git a/app/android/jni/scene.cpp b/app/android/jni/scene.cpp index 9a25f20d..945b201d 100644 --- a/app/android/jni/scene.cpp +++ b/app/android/jni/scene.cpp @@ -201,7 +201,7 @@ void Scene::SetupViewPort(int w, int h) { LOGE("Setup graphic height not valid"); } UASSERT(gesture_camera_ != 0); - gesture_camera_->SetAspectRatio(static_cast(w) / static_cast(h)); + gesture_camera_->SetWindowSize(static_cast(w), static_cast(h)); glViewport(0, 0, w, h); if(screenWidth_ != w || fboId_ == 0) { @@ -426,7 +426,7 @@ int Scene::Render() { UTimer timer; - bool onlineBlending = blending_ && mapRendering_ && meshRendering_ && cloudsToDraw.size()>1; + bool onlineBlending = blending_ && gesture_camera_->GetCameraType()!=tango_gl::GestureCamera::kTopOrtho && mapRendering_ && meshRendering_ && cloudsToDraw.size()>1; if(onlineBlending && fboId_) { // set the rendering destination to FBO @@ -556,6 +556,15 @@ void Scene::SetCameraPose(const rtabmap::Transform & pose) *currentPose_ = pose; } +void Scene::setFOV(float angle) +{ + gesture_camera_->SetFieldOfView(angle); +} +void Scene::setOrthoCropFactor(float value) +{ + gesture_camera_->SetOrthoCropFactor(value); +} + rtabmap::Transform Scene::GetOpenGLCameraPose(float * fov) const { if(fov) @@ -563,7 +572,6 @@ rtabmap::Transform Scene::GetOpenGLCameraPose(float * fov) const *fov = gesture_camera_->getFOV(); } return glmToTransform(gesture_camera_->GetTransformationMatrix()); - } void Scene::OnTouchEvent(int touch_count, diff --git a/app/android/jni/scene.h b/app/android/jni/scene.h index 9c50ed4d..ed7fba5e 100644 --- a/app/android/jni/scene.h +++ b/app/android/jni/scene.h @@ -124,6 +124,8 @@ class Scene { void setMapRendering(bool enabled) {mapRendering_ = enabled;} void setMeshRendering(bool enabled, bool withTexture) {meshRendering_ = enabled; meshRenderingTexture_ = withTexture;} void setPointSize(float size) {pointSize_ = size;} + void setFOV(float angle); + void setOrthoCropFactor(float value); void setLighting(bool enabled) {lighting_ = enabled;} void setBackfaceCulling(bool enabled) {backfaceCulling_ = enabled;} void setBackgroundColor(float r, float g, float b) {r_=r; g_=g; b_=b;} // 0.0f <> 1.0f diff --git a/app/android/jni/tango-gl/camera.cpp b/app/android/jni/tango-gl/camera.cpp index f5634cb0..1f1cba95 100644 --- a/app/android/jni/tango-gl/camera.cpp +++ b/app/android/jni/tango-gl/camera.cpp @@ -22,8 +22,13 @@ namespace tango_gl { Camera::Camera() { field_of_view_ = 45.0f * DEGREE_2_RADIANS; aspect_ratio_ = 4.0f / 3.0f; + width_ = 800.0f; + height_ = 600.0f; near_clip_plane_ = 0.2f; far_clip_plane_ = 1000.0f; + ortho_ = false; + orthoScale_ = 2.0f; + orthoCropFactor_ = -1.0f; } glm::mat4 Camera::GetViewMatrix() { @@ -31,12 +36,17 @@ glm::mat4 Camera::GetViewMatrix() { } glm::mat4 Camera::GetProjectionMatrix() { - return glm::perspective(field_of_view_, aspect_ratio_, - near_clip_plane_, far_clip_plane_); + if(ortho_) + { + return glm::ortho(-orthoScale_*aspect_ratio_, orthoScale_*aspect_ratio_, -orthoScale_, orthoScale_, orthoScale_ + orthoCropFactor_, far_clip_plane_); + } + return glm::perspective(field_of_view_, aspect_ratio_, near_clip_plane_, far_clip_plane_); } -void Camera::SetAspectRatio(float aspect_ratio) { - aspect_ratio_ = aspect_ratio; +void Camera::SetWindowSize(float width, float height) { + width_ = width; + height_ = height; + aspect_ratio_ = width/height; } void Camera::SetFieldOfView(float fov) { diff --git a/app/android/jni/tango-gl/gesture_camera.cpp b/app/android/jni/tango-gl/gesture_camera.cpp index f8a5538e..d32f0038 100644 --- a/app/android/jni/tango-gl/gesture_camera.cpp +++ b/app/android/jni/tango-gl/gesture_camera.cpp @@ -62,68 +62,79 @@ GestureCamera::GestureCamera() : GestureCamera::~GestureCamera() { delete cam_parent_transform_; } void GestureCamera::OnTouchEvent(int touch_count, TouchEvent event, float x0, - float y0, float x1, float y1) { - if (camera_type_ == kFirstPerson) { - return; - } + float y0, float x1, float y1) { + if (camera_type_ == kFirstPerson) { + return; + } - if (touch_count == 1) { - switch (event) { - case kTouch0Down: { - cam_start_angle_ = cam_cur_angle_; + if (touch_count == 1) { + switch (event) { + case kTouch0Down: { + cam_start_angle_ = cam_cur_angle_; - touch0_start_position_.x = x0; - touch0_start_position_.y = y0; - break; - } - case kTouchMove: { - float rotation_x = (touch0_start_position_.y - y0) * kRotationSpeed; - float rotation_y = (touch0_start_position_.x - x0) * kRotationSpeed; + touch0_start_position_.x = x0; + touch0_start_position_.y = y0; + break; + } + case kTouchMove: { + glm::vec2 offset; - cam_cur_angle_.x = cam_start_angle_.x + rotation_x; - cam_cur_angle_.y = cam_start_angle_.y + rotation_y; - StartCameraToCurrentTransform(); - break; - } - default: { break; } - } - } - if (touch_count == 2) { - switch (event) { - case kTouch1Down: { - float abs_x = x0 - x1; - float abs_y = y0 - y1; - start_touch_dist_ = std::sqrt(abs_x * abs_x + abs_y * abs_y); - cam_start_dist_ = GetPosition().z; + float rotation_x = (touch0_start_position_.y - y0) * kRotationSpeed; + float rotation_y = (touch0_start_position_.x - x0) * kRotationSpeed; - // center touch - touch0_start_position_.x = (x0+x1)/2.0f; - touch0_start_position_.y = (y0+y1)/2.0f; - break; - } - case kTouchMove: { - float abs_x = x0 - x1; - float abs_y = y0 - y1; - float dist = start_touch_dist_ - std::sqrt(abs_x * abs_x + abs_y * abs_y); + if(camera_type_!=kTopOrtho) + cam_cur_angle_.x = cam_start_angle_.x + rotation_x; + cam_cur_angle_.y = cam_start_angle_.y + rotation_y; - cam_cur_dist_ = tango_gl::util::Clamp(cam_start_dist_ + dist * kZoomSpeed, - kCamViewMinDist, kCamViewMaxDist); + StartCameraToCurrentTransform(); - glm::vec2 touch_center_position((x0+x1)/2.0f, (y0+y1)/2.0f); - glm::vec2 offset; - offset.x = (touch_center_position.x - touch0_start_position_.x) * kMoveSpeed; - offset.y = (touch_center_position.y - touch0_start_position_.y) * kMoveSpeed; - touch0_start_position_ = touch_center_position; + break; + } + default: { break; } + } + } + if (touch_count == 2) { + switch (event) { + case kTouch1Down: { + float abs_x = x0 - x1; + float abs_y = y0 - y1; + start_touch_dist_ = std::sqrt(abs_x * abs_x + abs_y * abs_y); + cam_start_dist_ = GetPosition().z; - StartCameraToCurrentTransform(); + // center touch + touch0_start_position_.x = (x0+x1)/2.0f; + touch0_start_position_.y = (y0+y1)/2.0f; + break; + } + case kTouchMove: { + float abs_x = x0 - x1; + float abs_y = y0 - y1; + float dist = start_touch_dist_ - std::sqrt(abs_x * abs_x + abs_y * abs_y); - anchor_offset_ += glm::rotate(cam_parent_transform_->GetRotation(), glm::vec3(-offset.x, offset.y, 0)); + cam_cur_dist_ = tango_gl::util::Clamp(cam_start_dist_ + dist * kZoomSpeed, + kCamViewMinDist, kCamViewMaxDist); - break; - } - default: { break; } - } - } + this->SetOrthoMode(camera_type_ == kTopOrtho); + if(camera_type_ == kTopOrtho) + { + this->SetOrthoScale(cam_cur_dist_); + } + + glm::vec2 touch_center_position((x0+x1)/2.0f, (y0+y1)/2.0f); + glm::vec2 offset; + offset.x = (touch_center_position.x - touch0_start_position_.x) * kMoveSpeed; + offset.y = (touch_center_position.y - touch0_start_position_.y) * kMoveSpeed; + touch0_start_position_ = touch_center_position; + + StartCameraToCurrentTransform(); + + anchor_offset_ += glm::rotate(cam_parent_transform_->GetRotation(), glm::vec3(-offset.x, offset.y, 0)); + + break; + } + default: { break; } + } + } } Segment GestureCamera::GetSegmentFromTouch(float normalized_x, @@ -164,6 +175,7 @@ void GestureCamera::SetCameraType(CameraType camera_index) { camera_type_ = camera_index; switch (camera_index) { case kFirstPerson: + SetOrthoMode(false); SetFieldOfView(kLowFov); SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); @@ -177,7 +189,8 @@ void GestureCamera::SetCameraType(CameraType camera_index) { break; case kThirdPerson: case kThirdPersonFollow: - SetFieldOfView(kHighFov); + SetOrthoMode(false); + SetFieldOfView(kHighFov); SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); cam_cur_dist_ = kThirdPersonFollow?kThirdPersonFollowCameraDist:kThirdPersonCameraDist; @@ -188,9 +201,10 @@ void GestureCamera::SetCameraType(CameraType camera_index) { StartCameraToCurrentTransform(); break; case kTopDown: - SetFieldOfView(kHighFov); SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); + SetOrthoMode(false); + SetFieldOfView(kHighFov); cam_cur_dist_ = kTopDownCameraDist; anchor_offset_ = glm::vec3(0.0f,0.0f,0.0f); cam_cur_angle_.x = -M_PI / 2.0f; @@ -198,6 +212,19 @@ void GestureCamera::SetCameraType(CameraType camera_index) { cam_cur_target_rot_ = glm::quat(1,0,0,0); StartCameraToCurrentTransform(); break; + case kTopOrtho: + SetPosition(glm::vec3(0.0f, 0.0f, 0.0f)); + SetRotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)); + SetOrthoMode(true); + SetOrthoScale(kTopDownCameraDist); + SetOrthoCropFactor(-1.0f); + cam_cur_dist_ = kTopDownCameraDist; + anchor_offset_ = glm::vec3(0.0f,0.0f,0.0f); + cam_cur_angle_.x = -M_PI / 2.0f; + cam_cur_angle_.y = 0.0f; + cam_cur_target_rot_ = glm::quat(1,0,0,0); + StartCameraToCurrentTransform(); + break; default: break; } diff --git a/app/android/jni/tango-gl/include/tango-gl/camera.h b/app/android/jni/tango-gl/include/tango-gl/camera.h index 5c0caa3e..70b88bb6 100644 --- a/app/android/jni/tango-gl/include/tango-gl/camera.h +++ b/app/android/jni/tango-gl/include/tango-gl/camera.h @@ -27,8 +27,11 @@ class Camera : public Transform { Camera& operator=(const Camera&) = delete; ~Camera(); - void SetAspectRatio(const float aspect_ratio); + void SetWindowSize(const float width, const float height); void SetFieldOfView(const float fov); + void SetOrthoMode(bool enabled) {ortho_ = enabled;} + void SetOrthoScale(float scale) {orthoScale_ = scale;} + void SetOrthoCropFactor(float value) {orthoCropFactor_ = value;} void SetNearFarClipPlanes(const float near, const float far); glm::mat4 GetViewMatrix(); @@ -55,7 +58,12 @@ class Camera : public Transform { protected: float field_of_view_; float aspect_ratio_; + float width_; + float height_; float near_clip_plane_, far_clip_plane_; + bool ortho_; + float orthoScale_; + float orthoCropFactor_; }; } // namespace tango_gl #endif // TANGO_GL_CAMERA_H_ diff --git a/app/android/jni/tango-gl/include/tango-gl/gesture_camera.h b/app/android/jni/tango-gl/include/tango-gl/gesture_camera.h index de64e383..bcd487d8 100644 --- a/app/android/jni/tango-gl/include/tango-gl/gesture_camera.h +++ b/app/android/jni/tango-gl/include/tango-gl/gesture_camera.h @@ -29,7 +29,8 @@ class GestureCamera : public Camera { kFirstPerson = 0, kThirdPersonFollow = 1, kTopDown = 2, - kThirdPerson = 3 + kTopOrtho = 3, + kThirdPerson = 4 }; enum TouchEvent { diff --git a/app/android/res/layout/activity_rtabmap.xml b/app/android/res/layout/activity_rtabmap.xml index 5cc3ad5a..a8b400ee 100644 --- a/app/android/res/layout/activity_rtabmap.xml +++ b/app/android/res/layout/activity_rtabmap.xml @@ -122,5 +122,16 @@ android:layout_centerHorizontal="true" android:paddingLeft="5dp" android:text="@string/close_visualization" /> + + + \ No newline at end of file diff --git a/app/android/src/com/introlab/rtabmap/RTABMapActivity.java b/app/android/src/com/introlab/rtabmap/RTABMapActivity.java index 87a34bf4..557e6f52 100644 --- a/app/android/src/com/introlab/rtabmap/RTABMapActivity.java +++ b/app/android/src/com/introlab/rtabmap/RTABMapActivity.java @@ -85,6 +85,8 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.NumberPicker; import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import android.widget.Toast; import android.widget.ToggleButton; @@ -124,6 +126,7 @@ public class RTABMapActivity extends Activity implements OnClickListener { public static final long NOTOUCH_TIMEOUT = 5000; // 5 sec private boolean mHudVisible = true; + private boolean mTipOrthoShown_ = false; // UI states private static enum State { @@ -173,6 +176,7 @@ public class RTABMapActivity extends Activity implements OnClickListener { private Button mButtonCloseVisualization; private Button mButtonSaveOnDevice; private Button mButtonShareOnSketchfab; + private SeekBar mSeekBarFov; private String mOpenedDatabasePath = ""; private String mWorkingDirectory = ""; @@ -272,6 +276,32 @@ public class RTABMapActivity extends Activity implements OnClickListener { { mButtonBackfaceShown.setVisibility(mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked()?View.VISIBLE:View.INVISIBLE); } + + mSeekBarFov = (SeekBar)findViewById(R.id.seekBar_fov); + mSeekBarFov.setMax(45); + mSeekBarFov.setProgress(20); + mSeekBarFov.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progressValue, boolean fromUser) { + if(mButtonFirst.isChecked()) + { + RTABMapLib.setFOV((float)progressValue+45.0f); + } + else if(mButtonTop.isChecked()) + { + RTABMapLib.setOrthoCropFactor((float)progressValue/20.0f - 3.0f); + } + resetNoTouchTimer(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); mToast = Toast.makeText(getApplicationContext(), "", Toast.LENGTH_SHORT); @@ -590,16 +620,38 @@ public class RTABMapActivity extends Activity implements OnClickListener { } private void setCamera(int type) - { + { + if(!DISABLE_LOG) Log.i(TAG, String.format("called setCamera(type=%d);", type)); + // for convenience, for a refresh of the memory used mStatusTexts[1] = getString(R.string.memory)+String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024)); mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(getFreeMemory()); updateStatusTexts(); - + RTABMapLib.setCamera(type); mButtonFirst.setChecked(type==0); mButtonThird.setChecked(type==1); - mButtonTop.setChecked(type==2); + mButtonTop.setChecked(type==2 || type==3); + mButtonTop.setText(type==3?"Ortho":"Top"); + mButtonTop.setTextOn(type==3?"Ortho":"Top"); + mButtonTop.setTextOff(type==3?"Ortho":"Top"); + mSeekBarFov.setVisibility(type!=0 && type!=3?View.INVISIBLE:View.VISIBLE); + if(type==0) + { + mSeekBarFov.setMax(45); + mSeekBarFov.setProgress(20); + } + if(type==3) + { + mSeekBarFov.setMax(120); + mSeekBarFov.setProgress(40); + } + + if(type==2 && !mTipOrthoShown_) + { + mToast.makeText(this, "Tip: Click again on Top to set Ortho mode.", mToast.LENGTH_LONG).show(); + mTipOrthoShown_ = true; + } } @Override @@ -614,7 +666,7 @@ public class RTABMapActivity extends Activity implements OnClickListener { setCamera(1); break; case R.id.top_down_button: - setCamera(2); + setCamera(mButtonFirst.isChecked() || mButtonThird.isChecked() || mButtonTop.getTextOn().equals("Ortho")?2:3); break; case R.id.pause_button: pauseMapping(); @@ -1256,6 +1308,7 @@ public class RTABMapActivity extends Activity implements OnClickListener { mButtonThird.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE); mButtonTop.setVisibility(mHudVisible?View.VISIBLE:View.INVISIBLE); mButtonBackfaceShown.setVisibility(mHudVisible && (mItemRenderingMesh.isChecked() || mItemRenderingTextureMesh.isChecked())?View.VISIBLE:View.INVISIBLE); + mSeekBarFov.setVisibility(mHudVisible && (mButtonFirst.isChecked() || (mButtonTop.isChecked() && mButtonTop.getTextOn().equals("Ortho")))?View.VISIBLE:View.INVISIBLE); } private void pauseMapping() { @@ -2091,7 +2144,7 @@ public class RTABMapActivity extends Activity implements OnClickListener { } else { - mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! No files found in tmp directory!?", pathHuman), mToast.LENGTH_LONG).show(); + mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" failed! No files found in tmp directory!? Last export may have failed or have been canceled.", pathHuman), mToast.LENGTH_LONG).show(); success = false; } } diff --git a/app/android/src/com/introlab/rtabmap/RTABMapLib.java b/app/android/src/com/introlab/rtabmap/RTABMapLib.java index 44e3c3d8..e774dccb 100644 --- a/app/android/src/com/introlab/rtabmap/RTABMapLib.java +++ b/app/android/src/com/introlab/rtabmap/RTABMapLib.java @@ -77,6 +77,8 @@ public class RTABMapLib 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 setLighting(boolean enabled); public static native void setBackfaceCulling(boolean enabled); public static native void setCloudDensityLevel(int value);