diff --git a/README.md b/README.md index 502a6b53..e4f23aca 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,11 @@ flutter pub add ar_flutter_plugin ``` Or manually add this to your `pubspec.yaml` file (and run `flutter pub get`): +# ar_flutter_plugin package extension ```yaml dependencies: - ar_flutter_plugin: ^0.6.4 + ar_flutter_plugin: ^0.6.5 ``` ### Importing @@ -92,3 +93,4 @@ Contributions to this plugin are very welcome. To contribute code and discuss id This is a rough sketch of the architecture the plugin implements: ![ar_plugin_architecture](./AR_Plugin_Architecture_highlevel.svg) + diff --git a/android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt b/android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt index 1b39d2ea..2868c81e 100644 --- a/android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt +++ b/android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt @@ -21,6 +21,7 @@ import com.google.ar.sceneform.ux.* import io.carius.lars.ar_flutter_plugin.Serialization.deserializeMatrix4 import io.carius.lars.ar_flutter_plugin.Serialization.serializeAnchor import io.carius.lars.ar_flutter_plugin.Serialization.serializeHitResult +import io.carius.lars.ar_flutter_plugin.Serialization.serializePose import io.flutter.FlutterInjector import io.flutter.embedding.engine.loader.FlutterLoader import io.flutter.plugin.common.BinaryMessenger @@ -99,6 +100,22 @@ internal class AndroidARView( "init" -> { initializeARView(call, result) } + "getAnchorPose" -> { + val anchorNode = arSceneView.scene.findByName(call.argument("anchorId")) as AnchorNode? + if (anchorNode != null) { + result.success(serializePose(anchorNode.anchor!!.pose)) + } else { + result.error("Error", "could not get anchor pose", null) + } + } + "getCameraPose" -> { + val cameraPose = arSceneView.arFrame?.camera?.displayOrientedPose + if (cameraPose != null) { + result.success(serializePose(cameraPose!!)) + } else { + result.error("Error", "could not get camera pose", null) + } + } "snapshot" -> { var bitmap = Bitmap.createBitmap(arSceneView.width, arSceneView.height, Bitmap.Config.ARGB_8888); diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 39792193..11a7fafa 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -166,7 +166,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fb2dffc4..c87d15a3 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ This app needs access to location when open. NSLocationAlwaysUsageDescription This app needs access to location when in the background. + CADisableMinimumFrameDurationOnPhone + diff --git a/ios/Classes/IosARView.swift b/ios/Classes/IosARView.swift index 08763b38..05da6705 100644 --- a/ios/Classes/IosARView.swift +++ b/ios/Classes/IosARView.swift @@ -81,6 +81,20 @@ class IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureReco //result(nil) initializeARView(arguments: arguments!, result: result) break + case "getCameraPose": + if let cameraPose = sceneView.session.currentFrame?.camera.transform { + result(serializeMatrix(cameraPose)) + } else { + result(FlutterError()) + } + break + case "getAnchorPose": + if let cameraPose = anchorCollection[arguments?["anchorId"] as! String]?.transform { + result(serializeMatrix(cameraPose)) + } else { + result(FlutterError()) + } + break case "snapshot": // call the SCNView Snapshot method and return the Image let snapshotImage = sceneView.snapshot() diff --git a/ios/ar_flutter_plugin.podspec b/ios/ar_flutter_plugin.podspec index 6ad81669..4af5e5f8 100644 --- a/ios/ar_flutter_plugin.podspec +++ b/ios/ar_flutter_plugin.podspec @@ -20,7 +20,7 @@ A Flutter plugin for shared AR experiences supporting Android and iOS. s.static_framework = true #s.dependency 'ARCore/CloudAnchors', '~> 1.12.0' #s.dependency 'ARCore', '~> 1.2.0' - s.dependency 'ARCore/CloudAnchors', '~> 1.26.0' + s.dependency 'ARCore/CloudAnchors', '~> 1.32.0' s.platform = :ios, '13.0' diff --git a/lib/managers/ar_location_manager.dart b/lib/managers/ar_location_manager.dart index 7485aa15..c6384079 100644 --- a/lib/managers/ar_location_manager.dart +++ b/lib/managers/ar_location_manager.dart @@ -78,7 +78,7 @@ class ARLocationManager { // When we reach here, permissions are granted and we can // continue accessing the position of the device. locationStream = - Geolocator.getPositionStream(desiredAccuracy: LocationAccuracy.high) + Geolocator.getPositionStream(locationSettings: LocationSettings(accuracy: LocationAccuracy.high)) .listen((Position position) { //print(position.latitude.toString() + ', ' + position.longitude.toString()); currentLocation = position; diff --git a/lib/managers/ar_session_manager.dart b/lib/managers/ar_session_manager.dart index 6ac28144..102c46a8 100644 --- a/lib/managers/ar_session_manager.dart +++ b/lib/managers/ar_session_manager.dart @@ -1,8 +1,13 @@ +import 'dart:math' show sqrt; import 'dart:typed_data'; + +import 'package:ar_flutter_plugin/datatypes/config_planedetection.dart'; +import 'package:ar_flutter_plugin/models/ar_anchor.dart'; import 'package:ar_flutter_plugin/models/ar_hittest_result.dart'; +import 'package:ar_flutter_plugin/utils/json_converters.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:ar_flutter_plugin/datatypes/config_planedetection.dart'; +import 'package:vector_math/vector_math_64.dart'; // Type definitions to enforce a consistent use of the API typedef ARHitResultHandler = void Function(List hits); @@ -24,8 +29,7 @@ class ARSessionManager { /// Receives hit results from user taps with tracked planes or feature points late ARHitResultHandler onPlaneOrPointTap; - ARSessionManager(int id, this.buildContext, this.planeDetectionConfig, - {this.debug = false}) { + ARSessionManager(int id, this.buildContext, this.planeDetectionConfig, {this.debug = false}) { _channel = MethodChannel('arsession_$id'); _channel.setMethodCallHandler(_platformCallHandler); if (debug) { @@ -33,6 +37,68 @@ class ARSessionManager { } } + /// Returns the camera pose in Matrix4 format with respect to the world coordinate system of the [ARView] + Future getCameraPose() async { + try { + final serializedCameraPose = await _channel.invokeMethod>('getCameraPose', {}); + return MatrixConverter().fromJson(serializedCameraPose!); + } catch (e) { + print('Error caught: ' + e.toString()); + return null; + } + } + + /// Returns the given anchor pose in Matrix4 format with respect to the world coordinate system of the [ARView] + Future getPose(ARAnchor anchor) async { + try { + if (anchor.name.isEmpty) { + throw Exception("Anchor can not be resolved. Anchor name is empty."); + } + final serializedCameraPose = await _channel.invokeMethod>('getAnchorPose', { + "anchorId": anchor.name, + }); + return MatrixConverter().fromJson(serializedCameraPose!); + } catch (e) { + print('Error caught: ' + e.toString()); + return null; + } + } + + /// Returns the distance in meters between @anchor1 and @anchor2. + Future getDistanceBetweenAnchors(ARAnchor anchor1, ARAnchor anchor2) async { + var anchor1Pose = await getPose(anchor1); + var anchor2Pose = await getPose(anchor2); + var anchor1Translation = anchor1Pose?.getTranslation(); + var anchor2Translation = anchor2Pose?.getTranslation(); + if (anchor1Translation != null && anchor2Translation != null) { + return getDistanceBetweenVectors(anchor1Translation, anchor2Translation); + } else { + return null; + } + } + + /// Returns the distance in meters between @anchor and device's camera. + Future getDistanceFromAnchor(ARAnchor anchor) async { + Matrix4? cameraPose = await getCameraPose(); + Matrix4? anchorPose = await getPose(anchor); + Vector3? cameraTranslation = cameraPose?.getTranslation(); + Vector3? anchorTranslation = anchorPose?.getTranslation(); + if (anchorTranslation != null && cameraTranslation != null) { + return getDistanceBetweenVectors(anchorTranslation, cameraTranslation); + } else { + return null; + } + } + + /// Returns the distance in meters between @vector1 and @vector2. + double getDistanceBetweenVectors(Vector3 vector1, Vector3 vector2) { + num dx = vector1.x - vector2.x; + num dy = vector1.y - vector2.y; + num dz = vector1.z - vector2.z; + double distance = sqrt(dx * dx + dy * dy + dz * dz); + return distance; + } + Future _platformCallHandler(MethodCall call) { if (debug) { print('_platformCallHandler call ${call.method} ${call.arguments}'); @@ -48,10 +114,7 @@ class ARSessionManager { case 'onPlaneOrPointTap': if (onPlaneOrPointTap != null) { final rawHitTestResults = call.arguments as List; - final serializedHitTestResults = rawHitTestResults - .map( - (hitTestResult) => Map.from(hitTestResult)) - .toList(); + final serializedHitTestResults = rawHitTestResults.map((hitTestResult) => Map.from(hitTestResult)).toList(); final hitTestResults = serializedHitTestResults.map((e) { return ARHitTestResult.fromJson(e); }).toList(); @@ -101,11 +164,7 @@ class ARSessionManager { /// Displays the [errorMessage] in a snackbar of the parent widget onError(String errorMessage) { ScaffoldMessenger.of(buildContext).showSnackBar(SnackBar( - content: Text(errorMessage), - action: SnackBarAction( - label: 'HIDE', - onPressed: - ScaffoldMessenger.of(buildContext).hideCurrentSnackBar))); + content: Text(errorMessage), action: SnackBarAction(label: 'HIDE', onPressed: ScaffoldMessenger.of(buildContext).hideCurrentSnackBar))); } /// Dispose the AR view on the platforms to pause the scenes and disconnect the platform handlers. diff --git a/pubspec.yaml b/pubspec.yaml index 52d0dcf3..ba9e7d42 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: ar_flutter_plugin description: Flutter Plugin for creating (collaborative) Augmented Reality experiences - Supports ARKit for iOS and ARCore for Android devices. -version: 0.6.4 +version: 0.6.5 homepage: https://lars.carius.io repository: https://github.com/CariusLars/ar_flutter_plugin @@ -11,10 +11,10 @@ environment: dependencies: flutter: sdk: flutter - permission_handler: ^8.1.6 - vector_math: ^2.1.0 - json_annotation: ^4.0.1 - geolocator: ^7.6.2 + permission_handler: ^10.0.0 + vector_math: ^2.1.2 + json_annotation: ^4.5.0 + geolocator: ^9.0.0 dev_dependencies: flutter_test: