From 45964c65430f2e152df1c23c77bb82a75c015113 Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Sun, 22 Nov 2020 22:09:30 +0100 Subject: [PATCH 1/9] start work to transformable node --- .../ArCoreAugmentedImagesView.kt | 1 + .../arcore_flutter_plugin/ArCoreView.kt | 159 ++++++++---------- .../arcore_flutter_plugin/BaseNode.kt | 16 ++ .../arcore_flutter_plugin/Coordinator.kt | 83 +++++++++ .../arcore_flutter_plugin/NodeFactory.kt | 24 ++- .../SelectionNodeVisualizer.kt | 44 +++++ .../flutter_models/FlutterArCoreNode.kt | 13 ++ .../flutter_models/FlutterArCoreSceneSetup.kt | 10 ++ example/lib/home.dart | 8 + example/lib/screens/transformable_node.dart | 81 +++++++++ 10 files changed, 351 insertions(+), 88 deletions(-) create mode 100644 android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt create mode 100644 android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/Coordinator.kt create mode 100644 android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/SelectionNodeVisualizer.kt create mode 100644 android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreSceneSetup.kt create mode 100644 example/lib/screens/transformable_node.dart diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreAugmentedImagesView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreAugmentedImagesView.kt index 376f4e01..9cbf809e 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreAugmentedImagesView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreAugmentedImagesView.kt @@ -17,6 +17,7 @@ import com.google.ar.core.exceptions.CameraNotAvailableException import com.google.ar.core.exceptions.UnavailableException import com.google.ar.sceneform.AnchorNode import com.google.ar.sceneform.Scene +import com.google.ar.sceneform.ux.TransformationSystem import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt index 11ba9e5d..8b4773e4 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt @@ -14,6 +14,7 @@ import android.widget.Toast import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreHitTestResult import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreNode import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCorePose +import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreSceneSetup import com.difrancescogianmarco.arcore_flutter_plugin.models.RotatingNode import com.difrancescogianmarco.arcore_flutter_plugin.utils.ArCoreUtils import com.google.ar.core.* @@ -24,7 +25,6 @@ import com.google.ar.sceneform.* import com.google.ar.sceneform.rendering.ModelRenderable import com.google.ar.sceneform.rendering.Texture import com.google.ar.sceneform.ux.AugmentedFaceNode -import io.flutter.app.FlutterApplication import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @@ -32,11 +32,11 @@ import io.flutter.plugin.platform.PlatformView class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMessenger, id: Int, private val isAugmentedFaces: Boolean, private val debug: Boolean) : PlatformView, MethodChannel.MethodCallHandler { private val methodChannel: MethodChannel = MethodChannel(messenger, "arcore_flutter_plugin_$id") - // private val activity: Activity = (context.applicationContext as FlutterApplication).currentActivity + + private val TAG: String = ArCoreView::class.java.name lateinit var activityLifecycleCallbacks: Application.ActivityLifecycleCallbacks - private var installRequested: Boolean = false + private val coordinator by lazy { Coordinator(activity, ::onArTap, ::onNodeSelected, ::onNodeFocused, ::onNodeTapped) } private var mUserRequestedInstall = true - private val TAG: String = ArCoreView::class.java.name private var arSceneView: ArSceneView? = null private val gestureDetector: GestureDetector private val RC_PERMISSIONS = 0x123 @@ -160,7 +160,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "init" -> { - arScenViewInit(call, result, activity) + arScenViewInit(FlutterArCoreSceneSetup(call.arguments as Map), result) } "addArCoreNode" -> { debugLog(" addArCoreNode") @@ -169,7 +169,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess onAddNode(flutterNode, result) } "addArCoreNodeWithAnchor" -> { - debugLog(" addArCoreNode") + debugLog(" addArCoreNodeWithAnchor") val map = call.arguments as HashMap val flutterNode = FlutterArCoreNode(map) addNodeWithAnchor(flutterNode, result) @@ -299,42 +299,30 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } } - private fun arScenViewInit(call: MethodCall, result: MethodChannel.Result, context: Context) { + private fun arScenViewInit(flutterArCoreSceneSetup:FlutterArCoreSceneSetup, result: MethodChannel.Result) { debugLog("arScenViewInit") - val enableTapRecognizer: Boolean? = call.argument("enableTapRecognizer") - if (enableTapRecognizer != null && enableTapRecognizer) { - arSceneView - ?.scene - ?.setOnTouchListener { hitTestResult: HitTestResult, event: MotionEvent? -> - - if (hitTestResult.node != null) { - debugLog(" onNodeTap " + hitTestResult.node?.name) - debugLog(hitTestResult.node?.localPosition.toString()) - debugLog(hitTestResult.node?.worldPosition.toString()) - methodChannel.invokeMethod("onNodeTap", hitTestResult.node?.name) - return@setOnTouchListener true - } - return@setOnTouchListener gestureDetector.onTouchEvent(event) - } + + if(flutterArCoreSceneSetup.enableTapRecognizer == true){ + arSceneView?.scene?.addOnPeekTouchListener { hitTestResult, motionEvent -> + coordinator.onTouch(hitTestResult, motionEvent) + } + } - val enableUpdateListener: Boolean? = call.argument("enableUpdateListener") - if (enableUpdateListener != null && enableUpdateListener) { + + if(flutterArCoreSceneSetup.enableUpdateListener == true){ // Set an update listener on the Scene that will hide the loading message once a Plane is // detected. arSceneView?.scene?.addOnUpdateListener(sceneUpdateListener) } - val enablePlaneRenderer: Boolean? = call.argument("enablePlaneRenderer") - if (enablePlaneRenderer != null && !enablePlaneRenderer) { - debugLog(" The plane renderer (enablePlaneRenderer) is set to " + enablePlaneRenderer.toString()) - arSceneView!!.planeRenderer.isVisible = false - } - + if(flutterArCoreSceneSetup.enablePlaneRenderer == true){ debugLog(" The plane renderer (enablePlaneRenderer) is set to " + enablePlaneRenderer.toString()) + arSceneView!!.planeRenderer.isVisible = false} + result.success(null) } fun addNodeWithAnchor(flutterArCoreNode: FlutterArCoreNode, result: MethodChannel.Result) { - + debugLog("addNodeWithAnchor " + flutterArCoreNode.name) if (arSceneView == null) { return } @@ -344,18 +332,17 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess result.error("Make Renderable Error", t.localizedMessage, null) return@makeRenderable } - val myAnchor = arSceneView?.session?.createAnchor(Pose(flutterArCoreNode.getPosition(), flutterArCoreNode.getRotation())) - if (myAnchor != null) { - val anchorNode = AnchorNode(myAnchor) - anchorNode.name = flutterArCoreNode.name - anchorNode.renderable = renderable - - debugLog("addNodeWithAnchor inserted ${anchorNode.name}") - attachNodeToParent(anchorNode, flutterArCoreNode.parentNodeName) - for (node in flutterArCoreNode.children) { - node.parentNodeName = flutterArCoreNode.name - onAddNode(node, null) + val myAnchor = arSceneView?.session?.createAnchor(Pose(flutterArCoreNode.getPosition(), flutterArCoreNode.getRotation())) + myAnchor?.let{anchor-> + NodeFactory.makeTransformableNode(activity.applicationContext, flutterArCoreNode, debug, coordinator) { node, throwable -> + node?.let{baseNode-> + baseNode.attach(anchor,arSceneView!!.scene,true) + for (n in flutterArCoreNode.children) { + n.parentNodeName = flutterArCoreNode.name + onAddNode(n, null) + } + } } } result.success(null) @@ -368,16 +355,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess NodeFactory.makeNode(activity.applicationContext, flutterArCoreNode, debug) { node, throwable -> debugLog("onAddNode inserted ${node?.name}") - -/* if (flutterArCoreNode.parentNodeName != null) { - debugLog(flutterArCoreNode.parentNodeName); - val parentNode: Node? = arSceneView?.scene?.findByName(flutterArCoreNode.parentNodeName) - parentNode?.addChild(node) - } else { - debugLog("addNodeToSceneWithGeometry: NOT PARENT_NODE_NAME") - arSceneView?.scene?.addChild(node) - }*/ - if (node != null) { + node?.let { attachNodeToParent(node, flutterArCoreNode.parentNodeName) for (n in flutterArCoreNode.children) { n.parentNodeName = flutterArCoreNode.name @@ -509,7 +487,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } fun onDestroy() { - if (arSceneView != null) { + if (arSceneView != null) { debugLog("Goodbye ARCore! Destroying the Activity now 7.") try { @@ -520,43 +498,54 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess arSceneView?.destroy() arSceneView = null - }catch (e : Exception){ + } catch (e: Exception) { e.printStackTrace(); - } + } } } - /* private fun tryPlaceNode(tap: MotionEvent?, frame: Frame) { - if (tap != null && frame.camera.trackingState == TrackingState.TRACKING) { - for (hit in frame.hitTest(tap)) { - val trackable = hit.trackable - if (trackable is Plane && trackable.isPoseInPolygon(hit.hitPose)) { - // Create the Anchor. - val anchor = hit.createAnchor() - val anchorNode = AnchorNode(anchor) - anchorNode.setParent(arSceneView?.scene) - - ModelRenderable.builder() - .setSource(activity.applicationContext, Uri.parse("TocoToucan.sfb")) - .build() - .thenAccept { renderable -> - val node = Node() - node.renderable = renderable - anchorNode.addChild(node) - }.exceptionally { throwable -> - Log.e(TAG, "Unable to load Renderable.", throwable); - return@exceptionally null - } - } - } + private fun onArTap(motionEvent: MotionEvent) { + debugLog("onArTap") + val frame = arSceneView?.arFrame ?: return + if (frame.camera.trackingState != TrackingState.TRACKING) { + coordinator.selectNode(null) + return } - }*/ + frame.hitTest(motionEvent).firstOrNull { + val trackable = it.trackable + when { + trackable is Plane && trackable.isPoseInPolygon(it.hitPose) -> true + trackable is Point -> true + else -> false + } + }?.let { hit -> + val list = ArrayList>() + val distance: Float = hit.distance + val translation = hit.hitPose.translation + val rotation = hit.hitPose.rotationQuaternion + val flutterArCoreHitTestResult = FlutterArCoreHitTestResult(distance, translation, rotation) + val arguments = flutterArCoreHitTestResult.toHashMap() + list.add(arguments) + methodChannel.invokeMethod("onPlaneTap", list) + } ?: coordinator.selectNode(null) + } - /* fun updatePosition(call: MethodCall, result: MethodChannel.Result) { - val name = call.argument("name") - val node = arSceneView?.scene?.findByName(name) - node?.localPosition = parseVector3(call.arguments as HashMap) - result.success(null) - }*/ + private fun onNodeUpdate(node: BaseNode?) { + debugLog("onNodeUpdate: " + node?.name) + } + + private fun onNodeSelected(old: BaseNode? = coordinator.selectedNode, new: BaseNode?) { + debugLog("onNodeSelected old: " + old?.name) + debugLog("onNodeSelected new: " + new?.name) + } + + private fun onNodeFocused(node: BaseNode?) { + debugLog("onNodeFocused: " + node?.name) + } + + private fun onNodeTapped(node: Node?){ + debugLog("onNodeTapped: " + node?.name) + methodChannel.invokeMethod("onNodeTap", node?.name) + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt new file mode 100644 index 00000000..3274610a --- /dev/null +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt @@ -0,0 +1,16 @@ +package com.difrancescogianmarco.arcore_flutter_plugin + +import com.google.ar.core.Anchor +import com.google.ar.sceneform.AnchorNode +import com.google.ar.sceneform.Scene +import com.google.ar.sceneform.ux.TransformableNode + +class BaseNode(coordinator: Coordinator) : TransformableNode(coordinator) { + + fun attach(anchor: Anchor, scene: Scene, focus: Boolean = false) { + setParent(AnchorNode(anchor).apply { setParent(scene) }) + if (focus) { + (transformationSystem as Coordinator).focusNode(this) + } + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/Coordinator.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/Coordinator.kt new file mode 100644 index 00000000..f0bf437f --- /dev/null +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/Coordinator.kt @@ -0,0 +1,83 @@ +package com.difrancescogianmarco.arcore_flutter_plugin + +import android.content.Context +import android.view.GestureDetector +import android.view.MotionEvent +import com.google.ar.sceneform.HitTestResult +import com.google.ar.sceneform.Node +import com.google.ar.sceneform.ux.BaseTransformableNode +import com.google.ar.sceneform.ux.SelectionVisualizer +import com.google.ar.sceneform.ux.TransformationSystem + +class Coordinator( + context: Context, + private val onArTap: (MotionEvent) -> Unit, + private val onNodeSelected: (old: BaseNode?, new: BaseNode?) -> Unit, + private val onNodeFocused: (nodes: BaseNode?) -> Unit, + private val onNodeTapped: (nodes: Node?) -> Unit +) : TransformationSystem( + context.resources.displayMetrics, SelectionNodeVisualizer(context) +) { + + override fun getSelectedNode(): BaseNode? = super.getSelectedNode() as? BaseNode + + private val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapUp(motionEvent: MotionEvent): Boolean { + onArTap(motionEvent) + return true + } + }) + + override fun getSelectionVisualizer(): SelectionNodeVisualizer { + return super.getSelectionVisualizer() as SelectionNodeVisualizer + } + + override fun setSelectionVisualizer(selectionVisualizer: SelectionVisualizer?) { + // Prevent changing the selection visualizer + } + + override fun onTouch(hitTestResult: HitTestResult?, motionEvent: MotionEvent?) { + super.onTouch(hitTestResult, motionEvent) + hitTestResult?.let{ + if (it.node == null) { + gestureDetector.onTouchEvent(motionEvent) + }else{ + onNodeTapped(it.node) + } + } + + } + + override fun selectNode(node: BaseTransformableNode?): Boolean { + val old = selectedNode + when (node) { + selectedNode -> return true /*ignored*/ + is BaseNode -> { + return super.selectNode(node).also { selected -> + if (!selected) return@also + onNodeSelected(old, node) + /*transfer current focus*/ + if (old != null && old == focusedNode) focusNode(node) + } + } + null -> { + return super.selectNode(null).also { + focusNode(null) + onNodeSelected(old, null) + } + } + } + return false + } + + var focusedNode: BaseNode? = null + private set + + fun focusNode(node: BaseNode?) { + if (node == focusedNode) return /*ignored*/ + focusedNode = node + if (node != null && node != selectedNode) selectNode(node) + onNodeFocused(node) + } + +} diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/NodeFactory.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/NodeFactory.kt index c8dc062c..8f923378 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/NodeFactory.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/NodeFactory.kt @@ -4,15 +4,18 @@ import android.content.Context import android.util.Log import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreNode import com.google.ar.sceneform.Node +import com.google.ar.sceneform.ux.TransformableNode +import com.google.ar.sceneform.ux.TransformationSystem typealias NodeHandler = (Node?, Throwable?) -> Unit +typealias TransformableNodeHandler = (BaseNode?, Throwable?) -> Unit class NodeFactory { companion object { val TAG: String = NodeFactory::class.java.name - fun makeNode(context: Context, flutterNode: FlutterArCoreNode, debug: Boolean, handler: NodeHandler) { + fun makeNode(context: Context, flutterNode: FlutterArCoreNode, debug: Boolean, handler: NodeHandler) { if (debug) { Log.i(TAG, flutterNode.toString()) } @@ -21,8 +24,23 @@ class NodeFactory { if (renderable != null) { node.renderable = renderable handler(node, null) - }else{ - handler(null,t) + } else { + handler(null, t) + } + } + } + + fun makeTransformableNode(context: Context, flutterNode: FlutterArCoreNode, debug: Boolean, transformationSystem: Coordinator, handler: TransformableNodeHandler) { + if (debug) { + Log.i(TAG, flutterNode.toString()) + } + val node = flutterNode.buildTransformableNode(transformationSystem) + RenderableCustomFactory.makeRenderable(context, flutterNode) { renderable, t -> + if (renderable != null) { + node.renderable = renderable + handler(node, null) + } else { + handler(null, t) } } } diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/SelectionNodeVisualizer.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/SelectionNodeVisualizer.kt new file mode 100644 index 00000000..4181c21c --- /dev/null +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/SelectionNodeVisualizer.kt @@ -0,0 +1,44 @@ +package com.difrancescogianmarco.arcore_flutter_plugin + +import android.content.Context +import com.google.ar.sceneform.Node +import com.google.ar.sceneform.rendering.ModelRenderable +import com.google.ar.sceneform.ux.BaseTransformableNode +import com.google.ar.sceneform.ux.SelectionVisualizer + +class SelectionNodeVisualizer(context: Context) : SelectionVisualizer { + + interface Invisible + + private val node: Node = Node() + + var isEnabled: Boolean + get() = node.isEnabled + set(value) { + node.isEnabled = value + } + + init { + ModelRenderable.builder() + .setSource(context, R.raw.sceneform_footprint) + .build() + .thenAccept { + it.collisionShape = null + node.renderable = it.apply { collisionShape = null } + } + } + + override fun applySelectionVisual(node: BaseTransformableNode) { + when (node) { + is Invisible -> return + else -> this.node.setParent(node) + } + } + + override fun removeSelectionVisual(node: BaseTransformableNode) { + when (node) { + is Invisible -> return + else -> this.node.setParent(null) + } + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt index 3622e607..b30bee30 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt @@ -1,5 +1,7 @@ package com.difrancescogianmarco.arcore_flutter_plugin.flutter_models +import com.difrancescogianmarco.arcore_flutter_plugin.BaseNode +import com.difrancescogianmarco.arcore_flutter_plugin.Coordinator import com.difrancescogianmarco.arcore_flutter_plugin.models.RotatingNode import com.difrancescogianmarco.arcore_flutter_plugin.utils.DecodableUtils.Companion.parseQuaternion import com.difrancescogianmarco.arcore_flutter_plugin.utils.DecodableUtils.Companion.parseVector3 @@ -7,6 +9,7 @@ import com.google.ar.core.Pose import com.google.ar.sceneform.Node import com.google.ar.sceneform.math.Quaternion import com.google.ar.sceneform.math.Vector3 +import com.google.ar.sceneform.ux.TransformableNode class FlutterArCoreNode(map: HashMap) { @@ -46,6 +49,16 @@ class FlutterArCoreNode(map: HashMap) { return node } + fun buildTransformableNode(transformationSystem: Coordinator): BaseNode { + val node = BaseNode(transformationSystem) + node.name = name + node.localPosition = position + node.localScale = scale + node.localRotation = rotation + + return node + } + fun getPosition(): FloatArray { return floatArrayOf(position.x, position.y, position.z) } diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreSceneSetup.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreSceneSetup.kt new file mode 100644 index 00000000..ce2c8542 --- /dev/null +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreSceneSetup.kt @@ -0,0 +1,10 @@ +package com.difrancescogianmarco.arcore_flutter_plugin.flutter_models + +class FlutterArCoreSceneSetup(map: Map) { + + + val enableTapRecognizer: Boolean? = map["enableTapRecognizer"] as? Boolean + val enableUpdateListener: Boolean? = map["enableUpdateListener"] as? Boolean + val enablePlaneRenderer: Boolean? = map["enablePlaneRenderer"] as? Boolean + +} \ No newline at end of file diff --git a/example/lib/home.dart b/example/lib/home.dart index 34d354b6..a270e4a3 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -3,6 +3,7 @@ import 'package:arcore_flutter_plugin_example/screens/augmented_images.dart'; import 'package:arcore_flutter_plugin_example/screens/image_object.dart'; import 'package:arcore_flutter_plugin_example/screens/matri_3d.dart'; import 'package:arcore_flutter_plugin_example/screens/multiple_augmented_images.dart'; +import 'package:arcore_flutter_plugin_example/screens/transformable_node.dart'; import 'package:flutter/material.dart'; import 'screens/hello_world.dart'; import 'screens/custom_object.dart'; @@ -105,6 +106,13 @@ class HomeScreen extends StatelessWidget { }, title: Text("Augmented Faces"), ), + ListTile( + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => TransformableNodeScreen())); + }, + title: Text("Transformable Node"), + ), ], ), ); diff --git a/example/lib/screens/transformable_node.dart b/example/lib/screens/transformable_node.dart new file mode 100644 index 00000000..d1465ea8 --- /dev/null +++ b/example/lib/screens/transformable_node.dart @@ -0,0 +1,81 @@ +import 'dart:typed_data'; + +import 'package:arcore_flutter_plugin/arcore_flutter_plugin.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:vector_math/vector_math_64.dart' as vector; + +class TransformableNodeScreen extends StatefulWidget { + @override + _TransformableNodeState createState() => _TransformableNodeState(); +} + +class _TransformableNodeState extends State { + ArCoreController arCoreController; + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Custom Object on plane detected'), + ), + body: ArCoreView( + onArCoreViewCreated: _onArCoreViewCreated, + enableTapRecognizer: true, + enableUpdateListener: true, + debug: true, + ), + ), + ); + } + + void _onArCoreViewCreated(ArCoreController controller) { + arCoreController = controller; + arCoreController.onPlaneTap = _handleOnPlaneTap; + arCoreController.onNodeTap = (node) { + print('TransformableNodeScreen: onNodeTap $node'); + }; + } + + Future _addSphere(ArCoreHitTestResult hit) async { + final ByteData textureBytes = await rootBundle.load('assets/earth.jpg'); + + final earthMaterial = ArCoreMaterial( + color: Color.fromARGB(120, 66, 134, 244), + textureBytes: textureBytes.buffer.asUint8List()); + + final earthShape = ArCoreSphere( + materials: [earthMaterial], + radius: 0.1, + ); + + final earth = ArCoreNode( + shape: earthShape, + position: hit.pose.translation + vector.Vector3(0.0, 1.0, 0.0), + rotation: hit.pose.rotation, + ); + + arCoreController.addArCoreNodeWithAnchor(earth); + } + + void _handleOnPlaneTap(List hits) { + final hit = hits.first; + _addSphere(hit); + } + + void onTapHandler(String name) { + print("Flutter: onNodeTap"); + showDialog( + context: context, + builder: (BuildContext context) => + AlertDialog(content: Text('onNodeTap on $name')), + ); + } + + @override + void dispose() { + arCoreController.dispose(); + super.dispose(); + } +} From a70759154055265b6715275f087334243d78d74c Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Sun, 22 Nov 2020 22:14:57 +0100 Subject: [PATCH 2/9] Detach sceneOnPeekTouchListener on destroy --- .../arcore_flutter_plugin/ArCoreView.kt | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt index 8b4773e4..a3fab226 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt @@ -42,6 +42,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess private val RC_PERMISSIONS = 0x123 private var sceneUpdateListener: Scene.OnUpdateListener private var faceSceneUpdateListener: Scene.OnUpdateListener + private var sceneOnPeekTouchListener: Scene.OnPeekTouchListener //AUGMENTEDFACE private var faceRegionsRenderable: ModelRenderable? = null @@ -88,6 +89,10 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } } + sceneOnPeekTouchListener = Scene.OnPeekTouchListener {hitTestResult, motionEvent -> + coordinator.onTouch(hitTestResult, motionEvent) + } + faceSceneUpdateListener = Scene.OnUpdateListener { frameTime -> run { // if (faceRegionsRenderable == null || faceMeshTexture == null) { @@ -160,7 +165,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "init" -> { - arScenViewInit(FlutterArCoreSceneSetup(call.arguments as Map), result) + arScenViewInit(FlutterArCoreSceneSetup(call.arguments as Map), result) } "addArCoreNode" -> { debugLog(" addArCoreNode") @@ -299,24 +304,23 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } } - private fun arScenViewInit(flutterArCoreSceneSetup:FlutterArCoreSceneSetup, result: MethodChannel.Result) { + private fun arScenViewInit(flutterArCoreSceneSetup: FlutterArCoreSceneSetup, result: MethodChannel.Result) { debugLog("arScenViewInit") - if(flutterArCoreSceneSetup.enableTapRecognizer == true){ - arSceneView?.scene?.addOnPeekTouchListener { hitTestResult, motionEvent -> - coordinator.onTouch(hitTestResult, motionEvent) - } - + if (flutterArCoreSceneSetup.enableTapRecognizer == true) { + arSceneView?.scene?.addOnPeekTouchListener(sceneOnPeekTouchListener) } - if(flutterArCoreSceneSetup.enableUpdateListener == true){ + if (flutterArCoreSceneSetup.enableUpdateListener == true) { // Set an update listener on the Scene that will hide the loading message once a Plane is // detected. arSceneView?.scene?.addOnUpdateListener(sceneUpdateListener) } - if(flutterArCoreSceneSetup.enablePlaneRenderer == true){ debugLog(" The plane renderer (enablePlaneRenderer) is set to " + enablePlaneRenderer.toString()) - arSceneView!!.planeRenderer.isVisible = false} + if (flutterArCoreSceneSetup.enablePlaneRenderer == false) { + debugLog(" The plane renderer (enablePlaneRenderer) is set to false") + arSceneView!!.planeRenderer.isVisible = false + } result.success(null) } @@ -334,10 +338,10 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } val myAnchor = arSceneView?.session?.createAnchor(Pose(flutterArCoreNode.getPosition(), flutterArCoreNode.getRotation())) - myAnchor?.let{anchor-> + myAnchor?.let { anchor -> NodeFactory.makeTransformableNode(activity.applicationContext, flutterArCoreNode, debug, coordinator) { node, throwable -> - node?.let{baseNode-> - baseNode.attach(anchor,arSceneView!!.scene,true) + node?.let { baseNode -> + baseNode.attach(anchor, arSceneView!!.scene, true) for (n in flutterArCoreNode.children) { n.parentNodeName = flutterArCoreNode.name onAddNode(n, null) @@ -493,6 +497,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess try { arSceneView?.scene?.removeOnUpdateListener(sceneUpdateListener) arSceneView?.scene?.removeOnUpdateListener(faceSceneUpdateListener) + arSceneView?.scene?.removeOnPeekTouchListener(sceneOnPeekTouchListener) debugLog("Goodbye arSceneView.") arSceneView?.destroy() @@ -544,7 +549,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess debugLog("onNodeFocused: " + node?.name) } - private fun onNodeTapped(node: Node?){ + private fun onNodeTapped(node: Node?) { debugLog("onNodeTapped: " + node?.name) methodChannel.invokeMethod("onNodeTap", node?.name) } From 3270fc7e47223fc16427f56a7eb61b7dcda0320d Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Mon, 23 Nov 2020 00:56:17 +0100 Subject: [PATCH 3/9] Add transformable node configuration class (Android side) --- .../arcore_flutter_plugin/BaseNode.kt | 44 +++++++++++++++- .../flutter_models/FlutterArCoreNode.kt | 52 ++++++------------- .../FlutterArCoreNodeConfiguration.kt | 27 ++++++++++ 3 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt index 3274610a..7029fb72 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt @@ -1,16 +1,56 @@ package com.difrancescogianmarco.arcore_flutter_plugin +import android.view.MotionEvent +import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreNodeConfiguration import com.google.ar.core.Anchor import com.google.ar.sceneform.AnchorNode +import com.google.ar.sceneform.HitTestResult import com.google.ar.sceneform.Scene +import com.google.ar.sceneform.rendering.Renderable import com.google.ar.sceneform.ux.TransformableNode -class BaseNode(coordinator: Coordinator) : TransformableNode(coordinator) { +class BaseNode(coordinator: Coordinator, config: FlutterArCoreNodeConfiguration) : TransformableNode(coordinator) { + + init { + + name = config.name + + localScale = config.currentScale + scaleController.apply { + isEnabled = config.scaleEnabled + minScale = config.minScale + maxScale = config.maxScale + } + + localPosition = config.currentPosition + translationController.apply { + isEnabled = config.translationEnabled + + } + + localRotation = config.currentRotation + rotationController.apply { + isEnabled = config.rotationEnabled + } + } + + override fun getTransformationSystem(): Coordinator = super.getTransformationSystem() as Coordinator + fun attach(anchor: Anchor, scene: Scene, focus: Boolean = false) { setParent(AnchorNode(anchor).apply { setParent(scene) }) if (focus) { - (transformationSystem as Coordinator).focusNode(this) + transformationSystem.focusNode(this) } } + + override fun setRenderable(renderable: Renderable?) { + super.setRenderable(renderable?.apply {}) + } + + override fun onTap(hitTestResult: HitTestResult?, motionEvent: MotionEvent?) { + super.onTap(hitTestResult, motionEvent) + if (isTransforming) return + transformationSystem.focusNode(this) + } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt index b30bee30..5c732d0e 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt @@ -3,30 +3,19 @@ package com.difrancescogianmarco.arcore_flutter_plugin.flutter_models import com.difrancescogianmarco.arcore_flutter_plugin.BaseNode import com.difrancescogianmarco.arcore_flutter_plugin.Coordinator import com.difrancescogianmarco.arcore_flutter_plugin.models.RotatingNode -import com.difrancescogianmarco.arcore_flutter_plugin.utils.DecodableUtils.Companion.parseQuaternion -import com.difrancescogianmarco.arcore_flutter_plugin.utils.DecodableUtils.Companion.parseVector3 import com.google.ar.core.Pose import com.google.ar.sceneform.Node -import com.google.ar.sceneform.math.Quaternion -import com.google.ar.sceneform.math.Vector3 -import com.google.ar.sceneform.ux.TransformableNode class FlutterArCoreNode(map: HashMap) { val dartType: String = map["dartType"] as String - val name: String = map["name"] as String val image: FlutterArCoreImage? = createArCoreImage(map["image"] as? HashMap) val objectUrl: String? = map["objectUrl"] as? String val object3DFileName: String? = map["object3DFileName"] as? String val shape: FlutterArCoreShape? = getShape(map["shape"] as? HashMap) - val position: Vector3 = parseVector3(map["position"] as? HashMap) ?: Vector3() - val scale: Vector3 = parseVector3(map["scale"] as? HashMap) - ?: Vector3(1.0F, 1.0F, 1.0F) - val rotation: Quaternion = parseQuaternion(map["rotation"] as? HashMap) - ?: Quaternion() - val degreesPerSecond: Float? = getDegreesPerSecond((map["degreesPerSecond"] as? Double)) + private val degreesPerSecond: Float? = getDegreesPerSecond((map["degreesPerSecond"] as? Double)) var parentNodeName: String? = map["parentNodeName"] as? String - + private val configuration: FlutterArCoreNodeConfiguration = FlutterArCoreNodeConfiguration(map) val children: ArrayList = getChildrenFromMap(map["children"] as ArrayList>) private fun getChildrenFromMap(list: ArrayList>): ArrayList { @@ -34,37 +23,30 @@ class FlutterArCoreNode(map: HashMap) { } fun buildNode(): Node { - lateinit var node: Node - if (degreesPerSecond != null) { - node = RotatingNode(degreesPerSecond, true, 0.0f) + val node: Node = if (degreesPerSecond != null) { + RotatingNode(degreesPerSecond, true, 0.0f) } else { - node = Node() + Node() } - node.name = name - node.localPosition = position - node.localScale = scale - node.localRotation = rotation + node.name = configuration.name + node.localPosition = configuration.currentPosition + node.localScale = configuration.currentScale + node.localRotation = configuration.currentRotation return node } fun buildTransformableNode(transformationSystem: Coordinator): BaseNode { - val node = BaseNode(transformationSystem) - node.name = name - node.localPosition = position - node.localScale = scale - node.localRotation = rotation - - return node + return BaseNode(transformationSystem, configuration) } - + fun getPosition(): FloatArray { - return floatArrayOf(position.x, position.y, position.z) + return floatArrayOf(configuration.currentPosition.x, configuration.currentPosition.y, configuration.currentPosition.z) } fun getRotation(): FloatArray { - return floatArrayOf(rotation.x, rotation.y, rotation.z, rotation.w) + return floatArrayOf(configuration.currentRotation.x, configuration.currentRotation.y, configuration.currentRotation.z, configuration.currentRotation.w) } fun getPose(): Pose { @@ -94,13 +76,13 @@ class FlutterArCoreNode(map: HashMap) { override fun toString(): String { return "dartType: $dartType\n" + - "name: $name\n" + + "name: ${configuration.name}\n" + "shape: ${shape.toString()}\n" + "object3DFileName: $object3DFileName \n" + "objectUrl: $objectUrl \n" + - "position: $position\n" + - "scale: $scale\n" + - "rotation: $rotation\n" + + "position: ${configuration.currentPosition}\n" + + "scale: ${configuration.currentScale}\n" + + "rotation: ${configuration.currentRotation}\n" + "parentNodeName: $parentNodeName" } diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt new file mode 100644 index 00000000..71213a16 --- /dev/null +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt @@ -0,0 +1,27 @@ +package com.difrancescogianmarco.arcore_flutter_plugin.flutter_models + +import com.difrancescogianmarco.arcore_flutter_plugin.utils.DecodableUtils +import com.google.ar.sceneform.math.Quaternion +import com.google.ar.sceneform.math.Vector3 + +class FlutterArCoreNodeConfiguration(map: Map) { + + val name: String = map["name"] as String + + //ScaleController + val scaleEnabled = map["scaleEnabled"] as? Boolean ?: true + val minScale = map["minScale"] as? Float ?: 0.25F + val maxScale = map["maxScale"] as? Float ?: 5.0F + val currentScale: Vector3 = DecodableUtils.parseVector3(map["scale"] as? HashMap) + ?: Vector3(1.0F, 1.0F, 1.0F) + + //TranslationController + val translationEnabled = map["translationEnabled"] as? Boolean ?: true + val currentPosition: Vector3 = DecodableUtils.parseVector3(map["position"] as? HashMap) ?: Vector3() + + //RotationController + val rotationEnabled = map["rotationEnabled"] as? Boolean ?: true + val currentRotation: Quaternion = DecodableUtils.parseQuaternion(map["rotation"] as? HashMap) + ?: Quaternion() + +} \ No newline at end of file From 0122c5e2c912ae64832aff9158d9669820ba5157 Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Mon, 23 Nov 2020 23:08:24 +0100 Subject: [PATCH 4/9] Implemented scale, rotate and translate functions --- .../arcore_flutter_plugin/ArCoreView.kt | 45 +++++-- .../arcore_flutter_plugin/BaseArCoreView.kt | 2 +- .../arcore_flutter_plugin/BaseNode.kt | 120 ++++++++++++++++- .../flutter_models/FlutterArCoreNode.kt | 2 +- example/lib/screens/transformable_node.dart | 123 +++++++++++++++++- example/pubspec.lock | 2 +- example/pubspec.yaml | 2 +- lib/arcore_flutter_plugin.dart | 1 + lib/src/arcore_controller.dart | 40 ++++-- lib/src/arcore_node.dart | 6 +- lib/src/utils/vector_utils.dart | 10 ++ pubspec.yaml | 2 +- 12 files changed, 320 insertions(+), 35 deletions(-) diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt index a3fab226..d6e82fad 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt @@ -17,11 +17,13 @@ import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCo import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreSceneSetup import com.difrancescogianmarco.arcore_flutter_plugin.models.RotatingNode import com.difrancescogianmarco.arcore_flutter_plugin.utils.ArCoreUtils +import com.difrancescogianmarco.arcore_flutter_plugin.utils.DecodableUtils import com.google.ar.core.* import com.google.ar.core.exceptions.CameraNotAvailableException import com.google.ar.core.exceptions.UnavailableException import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationException import com.google.ar.sceneform.* +import com.google.ar.sceneform.math.Vector3 import com.google.ar.sceneform.rendering.ModelRenderable import com.google.ar.sceneform.rendering.Texture import com.google.ar.sceneform.ux.AugmentedFaceNode @@ -89,7 +91,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } } - sceneOnPeekTouchListener = Scene.OnPeekTouchListener {hitTestResult, motionEvent -> + sceneOnPeekTouchListener = Scene.OnPeekTouchListener { hitTestResult, motionEvent -> coordinator.onTouch(hitTestResult, motionEvent) } @@ -134,12 +136,19 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess setupLifeCycle(context) } - fun debugLog(message: String) { + fun debugLog(message: String?) { if (debug) { - Log.i(TAG, message) + Log.i(TAG, message ?: "null message") } } +/* private fun onArUpdate() { + val frame = arSceneView?.arFrame + val camera = frame?.camera + val state = camera?.trackingState + val reason = camera?.trackingFailureReason + }*/ + fun loadMesh(textureBytes: ByteArray?) { // Load the face regions renderable. @@ -186,12 +195,26 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } "positionChanged" -> { debugLog(" positionChanged") + coordinator.selectedNode?.apply { + localPosition = DecodableUtils.parseVector3(call.argument("position") as? HashMap) + } + result.success(null) + } + "scaleChanged" -> { + debugLog(" scaleChanged") + coordinator.selectedNode?.parent?.apply { + localScale = DecodableUtils.parseVector3(call.argument("scale") as? HashMap) + } + + result.success(null) } "rotationChanged" -> { debugLog(" rotationChanged") - updateRotation(call, result) - + coordinator.selectedNode?.apply { + localRotation = DecodableUtils.parseQuaternion(call.argument("rotation") as? HashMap) + } + result.success(null) } "updateMaterials" -> { debugLog(" updateMaterials") @@ -326,7 +349,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } fun addNodeWithAnchor(flutterArCoreNode: FlutterArCoreNode, result: MethodChannel.Result) { - debugLog("addNodeWithAnchor " + flutterArCoreNode.name) + debugLog("addNodeWithAnchor " + flutterArCoreNode.configuration.name) if (arSceneView == null) { return } @@ -343,7 +366,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess node?.let { baseNode -> baseNode.attach(anchor, arSceneView!!.scene, true) for (n in flutterArCoreNode.children) { - n.parentNodeName = flutterArCoreNode.name + n.parentNodeName = flutterArCoreNode.configuration.name onAddNode(n, null) } } @@ -362,7 +385,7 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess node?.let { attachNodeToParent(node, flutterArCoreNode.parentNodeName) for (n in flutterArCoreNode.children) { - n.parentNodeName = flutterArCoreNode.name + n.parentNodeName = flutterArCoreNode.configuration.name onAddNode(n, null) } } @@ -536,13 +559,17 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess } ?: coordinator.selectNode(null) } + //Called every frame private fun onNodeUpdate(node: BaseNode?) { - debugLog("onNodeUpdate: " + node?.name) + //debugLog("onNodeUpdate: " + node?.name) + } private fun onNodeSelected(old: BaseNode? = coordinator.selectedNode, new: BaseNode?) { debugLog("onNodeSelected old: " + old?.name) debugLog("onNodeSelected new: " + new?.name) + old?.onNodeUpdate = null + new?.onNodeUpdate = ::onNodeUpdate } private fun onNodeFocused(node: BaseNode?) { diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseArCoreView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseArCoreView.kt index 963f63db..52b6f54c 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseArCoreView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseArCoreView.kt @@ -166,7 +166,7 @@ open class BaseArCoreView(val activity: Activity, context: Context, messenger: B if (node != null) { attachNodeToParent(node, flutterArCoreNode.parentNodeName) for (n in flutterArCoreNode.children) { - n.parentNodeName = flutterArCoreNode.name + n.parentNodeName = flutterArCoreNode.configuration.name onAddNode(n, null) } result?.success(null) diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt index 7029fb72..6fa26ec1 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/BaseNode.kt @@ -3,16 +3,15 @@ package com.difrancescogianmarco.arcore_flutter_plugin import android.view.MotionEvent import com.difrancescogianmarco.arcore_flutter_plugin.flutter_models.FlutterArCoreNodeConfiguration import com.google.ar.core.Anchor -import com.google.ar.sceneform.AnchorNode -import com.google.ar.sceneform.HitTestResult -import com.google.ar.sceneform.Scene +import com.google.ar.sceneform.* +import com.google.ar.sceneform.math.MathHelper +import com.google.ar.sceneform.math.Vector3 import com.google.ar.sceneform.rendering.Renderable -import com.google.ar.sceneform.ux.TransformableNode +import com.google.ar.sceneform.ux.* class BaseNode(coordinator: Coordinator, config: FlutterArCoreNodeConfiguration) : TransformableNode(coordinator) { init { - name = config.name localScale = config.currentScale @@ -53,4 +52,113 @@ class BaseNode(coordinator: Coordinator, config: FlutterArCoreNodeConfiguration) if (isTransforming) return transformationSystem.focusNode(this) } -} \ No newline at end of file + + var onNodeUpdate: ((BaseNode) -> Any)? = null + + override fun onUpdate(frameTime: FrameTime) { + onNodeUpdate?.invoke(this) + } +} + +// +//class CustomScaleController( +// transformableNode: BaseTransformableNode, +// gestureRecognizer: PinchGestureRecognizer +//) : +// BaseTransformationController(transformableNode, gestureRecognizer) { +// +// var minScale = DEFAULT_MIN_SCALE +// var maxScale = DEFAULT_MAX_SCALE +// var sensitivity = DEFAULT_SENSITIVITY +// var elasticity = DEFAULT_ELASTICITY +// +// private var currentScaleRatio: Float = 0.toFloat() +// +// private val scaleDelta: Float +// get() { +// val scaleDelta = maxScale - minScale +// +// if (scaleDelta <= 0.0f) { +// throw IllegalStateException("maxScale must be greater than minScale.") +// } +// +// return scaleDelta +// } +// +// private val clampedScaleRatio: Float +// get() = Math.min(1.0f, Math.max(0.0f, currentScaleRatio)) +// +// private val finalScale: Float +// get() { +// val elasticScaleRatio = clampedScaleRatio + elasticDelta +// return minScale + elasticScaleRatio * scaleDelta +// } +// +// private val elasticDelta: Float +// get() { +// val overRatio: Float +// if (currentScaleRatio > 1.0f) { +// overRatio = currentScaleRatio - 1.0f +// } else if (currentScaleRatio < 0.0f) { +// overRatio = currentScaleRatio +// } else { +// return 0.0f +// } +// +// return (1.0f - 1.0f / (Math.abs(overRatio) * elasticity + 1.0f)) * Math.signum(overRatio) +// } +// +// override fun onActivated(node: Node?) { +// super.onActivated(node) +// val scale = transformableNode.localScale +// currentScaleRatio = (scale.x - minScale) / scaleDelta +// } +// +// override fun onUpdated(node: Node?, frameTime: FrameTime?) { +// if (isTransforming) { +// return +// } +// +// val t = MathHelper.clamp(frameTime!!.deltaSeconds * LERP_SPEED, 0f, 1f) +// currentScaleRatio = MathHelper.lerp(currentScaleRatio, clampedScaleRatio, t) +// val finalScaleValue = finalScale +// val finalScale = Vector3(finalScaleValue, finalScaleValue, finalScaleValue) +// transformableNode.localScale = finalScale +// } +// +// public override fun canStartTransformation(gesture: PinchGesture): Boolean { +// return transformableNode.isSelected +// } +// +// fun getScaleRatio(): Float { +// return currentScaleRatio +// } +// +// fun updateScaleRatio(ratio: Float) { +// currentScaleRatio = ratio +// } +// +// public override fun onContinueTransformation(gesture: PinchGesture) { +// currentScaleRatio += gesture.gapDeltaInches() * sensitivity +// +// val finalScaleValue = finalScale +// val finalScale = Vector3(finalScaleValue, finalScaleValue, finalScaleValue) +// transformableNode.localScale = finalScale +// +// if (currentScaleRatio < -ELASTIC_RATIO_LIMIT || currentScaleRatio > 1.0f + ELASTIC_RATIO_LIMIT) { +// gesture.wasCancelled() +// } +// } +// +// public override fun onEndTransformation(gesture: PinchGesture) {} +// +// companion object { +// const val DEFAULT_MIN_SCALE = 0.75f +// const val DEFAULT_MAX_SCALE = 1.75f +// const val DEFAULT_SENSITIVITY = 0.75f +// const val DEFAULT_ELASTICITY = 0.15f +// +// private const val ELASTIC_RATIO_LIMIT = 0.8f +// private const val LERP_SPEED = 8.0f +// } +//} \ No newline at end of file diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt index 5c732d0e..d6734d21 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNode.kt @@ -15,7 +15,7 @@ class FlutterArCoreNode(map: HashMap) { val shape: FlutterArCoreShape? = getShape(map["shape"] as? HashMap) private val degreesPerSecond: Float? = getDegreesPerSecond((map["degreesPerSecond"] as? Double)) var parentNodeName: String? = map["parentNodeName"] as? String - private val configuration: FlutterArCoreNodeConfiguration = FlutterArCoreNodeConfiguration(map) + val configuration: FlutterArCoreNodeConfiguration = FlutterArCoreNodeConfiguration(map) val children: ArrayList = getChildrenFromMap(map["children"] as ArrayList>) private fun getChildrenFromMap(list: ArrayList>): ArrayList { diff --git a/example/lib/screens/transformable_node.dart b/example/lib/screens/transformable_node.dart index d1465ea8..194168ed 100644 --- a/example/lib/screens/transformable_node.dart +++ b/example/lib/screens/transformable_node.dart @@ -12,7 +12,8 @@ class TransformableNodeScreen extends StatefulWidget { class _TransformableNodeState extends State { ArCoreController arCoreController; - + String selectedNode; + final nodesMap = {}; @override Widget build(BuildContext context) { return MaterialApp( @@ -20,11 +21,115 @@ class _TransformableNodeState extends State { appBar: AppBar( title: const Text('Custom Object on plane detected'), ), - body: ArCoreView( - onArCoreViewCreated: _onArCoreViewCreated, - enableTapRecognizer: true, - enableUpdateListener: true, - debug: true, + body: Stack( + fit: StackFit.expand, + children: [ + ArCoreView( + onArCoreViewCreated: _onArCoreViewCreated, + enableTapRecognizer: true, + enableUpdateListener: true, + debug: true, + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + color: Colors.white, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(selectedNode ?? 'Unselected node'), + Row( + children: [ + Text('Scale'), + IconButton( + icon: Icon(Icons.remove), + onPressed: selectedNode != null + ? () { + if (nodesMap.containsKey(selectedNode)) { + nodesMap[selectedNode].scale.value -= + vector.Vector3(1.0, 1.0, 1.0); + setState(() {}); + } + } + : null), + IconButton( + icon: Icon(Icons.add), + onPressed: selectedNode != null + ? () { + if (nodesMap.containsKey(selectedNode)) { + nodesMap[selectedNode].scale.value += + vector.Vector3(1.0, 1.0, 1.0); + setState(() {}); + } + } + : null), + if (selectedNode != null && + nodesMap.containsKey(selectedNode)) + Text(nodesMap[selectedNode].scale.value.text), + ], + ), + Row( + children: [ + Text('Position'), + IconButton( + icon: Icon(Icons.remove), + onPressed: selectedNode != null + ? () { + if (nodesMap.containsKey(selectedNode)) { + nodesMap[selectedNode].position.value -= + vector.Vector3(0.3, 0.0, 0.0); + setState(() {}); + } + } + : null), + IconButton( + icon: Icon(Icons.add), + onPressed: selectedNode != null + ? () { + if (nodesMap.containsKey(selectedNode)) { + nodesMap[selectedNode].position.value += + vector.Vector3(0.3, 0.0, 0.0); + } + } + : null), + if (selectedNode != null && + nodesMap.containsKey(selectedNode)) + Text(nodesMap[selectedNode].position.value.text), + ], + ), + Row( + children: [ + Text('Rotation'), + IconButton( + icon: Icon(Icons.remove), + onPressed: selectedNode != null + ? () { + if (nodesMap.containsKey(selectedNode)) { + nodesMap[selectedNode].rotation.value -= + vector.Vector4(0.1, 0.1, 0.1, 0.1); + } + } + : null), + IconButton( + icon: Icon(Icons.add), + onPressed: selectedNode != null + ? () { + if (nodesMap.containsKey(selectedNode)) { + nodesMap[selectedNode].rotation.value += + vector.Vector4(0.1, 0.1, 0.1, 0.1); + } + } + : null), + if (selectedNode != null && + nodesMap.containsKey(selectedNode)) + Text(nodesMap[selectedNode].rotation.value.text), + ], + ), + ], + ), + ), + ), + ], ), ), ); @@ -35,6 +140,9 @@ class _TransformableNodeState extends State { arCoreController.onPlaneTap = _handleOnPlaneTap; arCoreController.onNodeTap = (node) { print('TransformableNodeScreen: onNodeTap $node'); + setState(() { + selectedNode = node; + }); }; } @@ -52,10 +160,13 @@ class _TransformableNodeState extends State { final earth = ArCoreNode( shape: earthShape, + scale: vector.Vector3(1.0, 1.0, 1.0), position: hit.pose.translation + vector.Vector3(0.0, 1.0, 0.0), rotation: hit.pose.rotation, ); + nodesMap[earth.name] = earth; + arCoreController.addArCoreNodeWithAnchor(earth); } diff --git a/example/pubspec.lock b/example/pubspec.lock index f71733c9..28403167 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -206,5 +206,5 @@ packages: source: hosted version: "3.5.0" sdks: - dart: ">=2.4.0 <3.0.0" + dart: ">=2.7.0 <3.0.0" flutter: ">=1.10.0 <2.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 4420f974..5696a90e 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the arcore_flutter_plugin plugin. publish_to: 'none' environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: flutter: diff --git a/lib/arcore_flutter_plugin.dart b/lib/arcore_flutter_plugin.dart index 880de1b9..cd3b37f2 100644 --- a/lib/arcore_flutter_plugin.dart +++ b/lib/arcore_flutter_plugin.dart @@ -13,3 +13,4 @@ export 'package:arcore_flutter_plugin/src/arcore_plane.dart'; export 'package:arcore_flutter_plugin/src/arcore_image.dart'; export 'package:arcore_flutter_plugin/src/arcore_reference_node.dart'; export 'package:arcore_flutter_plugin/src/arcore_augmented_image.dart'; +export 'package:arcore_flutter_plugin/src/utils/vector_utils.dart'; diff --git a/lib/src/arcore_controller.dart b/lib/src/arcore_controller.dart index 2fd63952..86162c63 100644 --- a/lib/src/arcore_controller.dart +++ b/lib/src/arcore_controller.dart @@ -178,24 +178,48 @@ class ArCoreController { void _addListeners(ArCoreNode node) { node.position.addListener(() => _handlePositionChanged(node)); + node.scale.addListener(() => _handleScaleChanged(node)); + node.rotation.addListener(() => _handleRotationChanged(node)); node?.shape?.materials?.addListener(() => _updateMaterials(node)); - if (node is ArCoreRotatingNode) { - node.degreesPerSecond.addListener(() => _handleRotationChanged(node)); - } + // if (node is ArCoreRotatingNode) { + // node.degreesPerSecond.addListener(() => _handleRotationChanged(node)); + // } + } + + void _handleScaleChanged(ArCoreNode node) { + print('_handleScaleChanged: ${node.name}'); + _channel.invokeMethod( + 'scaleChanged', + _getHandlerParams( + node, + _getHandlerParams(node, { + 'scale': convertVector3ToMap(node.scale.value) + }))); } void _handlePositionChanged(ArCoreNode node) { - _channel.invokeMethod('positionChanged', - _getHandlerParams(node, convertVector3ToMap(node.position.value))); + print('_handlePositionChanged: ${node.name}'); + _channel.invokeMethod( + 'positionChanged', + _getHandlerParams( + node, + _getHandlerParams(node, { + 'position': convertVector3ToMap(node.position.value) + }))); } - void _handleRotationChanged(ArCoreRotatingNode node) { - _channel.invokeMethod('rotationChanged', - {'name': node.name, 'degreesPerSecond': node.degreesPerSecond.value}); + void _handleRotationChanged(ArCoreNode node) { + print('_handleRotationChanged: ${node.name}'); + _channel.invokeMethod( + 'rotationChanged', + _getHandlerParams(node, { + 'rotation': convertVector4ToMap(node.rotation.value) + })); } void _updateMaterials(ArCoreNode node) { + print('_updateMaterials: ${node.name}'); _channel.invokeMethod( 'updateMaterials', _getHandlerParams(node, node.shape.toMap())); } diff --git a/lib/src/arcore_node.dart b/lib/src/arcore_node.dart index 5688647e..f87c34f9 100644 --- a/lib/src/arcore_node.dart +++ b/lib/src/arcore_node.dart @@ -15,7 +15,7 @@ class ArCoreNode { Vector3 scale, Vector4 rotation, this.children = const [], - }) : name = name ?? random_string.randomString(), + }) : name = name ?? 'node_${random_string.randomString(length: 6)}', position = ValueNotifier(position), scale = ValueNotifier(scale), rotation = ValueNotifier(rotation), @@ -23,6 +23,10 @@ class ArCoreNode { final List children; + // final double minScale; + // + // final double maxScale; + final ArCoreShape shape; final ValueNotifier position; diff --git a/lib/src/utils/vector_utils.dart b/lib/src/utils/vector_utils.dart index 6784e6cb..44c415db 100755 --- a/lib/src/utils/vector_utils.dart +++ b/lib/src/utils/vector_utils.dart @@ -6,3 +6,13 @@ Map convertVector3ToMap(Vector3 vector) => Map convertVector4ToMap(Vector4 vector) => vector == null ? null : {'x': vector.x, 'y': vector.y, 'z': vector.z, 'w': vector.w}; + +extension Vector3Ext on Vector3 { + String get text => + 'x: ${this.x.toStringAsFixed(2)} y: ${this.y.toStringAsFixed(2)} z: ${this.z.toStringAsFixed(2)}'; +} + +extension Vector4Ext on Vector4 { + String get text => + 'x: ${this.x.toStringAsFixed(2)} y: ${this.y.toStringAsFixed(2)} z: ${this.z.toStringAsFixed(2)} t: ${this.t.toStringAsFixed(2)}'; +} diff --git a/pubspec.yaml b/pubspec.yaml index f685bb09..d002ea01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.9 homepage: https://github.com/giandifra/arcore_flutter_plugin environment: - sdk: '>=2.1.0 <3.0.0' + sdk: '>=2.7.0 <3.0.0' flutter: '>=1.10.0 <2.0.0' dependencies: From a6325d227b11b6086da0629fd2922337e4f4a9eb Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Tue, 24 Nov 2020 17:26:51 +0100 Subject: [PATCH 5/9] Remove cupertino_icons --- example/pubspec.lock | 7 ------- example/pubspec.yaml | 4 ---- 2 files changed, 11 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 28403167..57f8a9f8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -64,13 +64,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.3" equatable: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 5696a90e..a20f89e6 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,10 +9,6 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - dev_dependencies: flutter_test: sdk: flutter From 87ea3ea4a87eef00710f40382999244ff2fa3898 Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Tue, 24 Nov 2020 17:30:27 +0100 Subject: [PATCH 6/9] Gesture on/off on node is editable at runtime --- .../arcore_flutter_plugin/ArCoreView.kt | 31 +-- .../FlutterArCoreNodeConfiguration.kt | 6 +- example/lib/screens/transformable_node.dart | 144 +++++++++++--- lib/src/arcore_controller.dart | 31 ++- lib/src/arcore_node.dart | 184 ++++++++++++++++-- 5 files changed, 335 insertions(+), 61 deletions(-) diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt index d6e82fad..1af52bc3 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt @@ -193,33 +193,38 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess val map = call.arguments as HashMap removeNode(map["nodeName"] as String, result) } - "positionChanged" -> { - debugLog(" positionChanged") + "positionConfigChanged" -> { + debugLog("positionConfigChanged") + val position =DecodableUtils.parseVector3(call.argument("position") as? HashMap) coordinator.selectedNode?.apply { - localPosition = DecodableUtils.parseVector3(call.argument("position") as? HashMap) + localPosition = position + translationController.isEnabled = call.argument("enabled") as? Boolean ?: translationController.isEnabled } result.success(null) } - "scaleChanged" -> { - debugLog(" scaleChanged") - - coordinator.selectedNode?.parent?.apply { - localScale = DecodableUtils.parseVector3(call.argument("scale") as? HashMap) + "scaleConfigChanged" -> { + debugLog(" scaleConfigChanged") + val scale = DecodableUtils.parseVector3(call.argument("scale") as? HashMap) + coordinator.focusedNode?.apply{ + scaleController.isEnabled = call.argument("enabled") as? Boolean ?: coordinator.focusedNode?.scaleController!!.isEnabled + parent?.apply { + localScale = scale + } } - result.success(null) } - "rotationChanged" -> { - debugLog(" rotationChanged") + "rotationConfigChanged" -> { + debugLog("rotationConfigChanged") + val rotation = DecodableUtils.parseQuaternion(call.argument("rotation") as? HashMap) coordinator.selectedNode?.apply { - localRotation = DecodableUtils.parseQuaternion(call.argument("rotation") as? HashMap) + localRotation = rotation + rotationController.isEnabled = call.argument("enabled") as? Boolean ?: rotationController.isEnabled } result.success(null) } "updateMaterials" -> { debugLog(" updateMaterials") updateMaterials(call, result) - } "loadMesh" -> { val map = call.arguments as HashMap diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt index 71213a16..5ec36212 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt @@ -9,18 +9,18 @@ class FlutterArCoreNodeConfiguration(map: Map) { val name: String = map["name"] as String //ScaleController - val scaleEnabled = map["scaleEnabled"] as? Boolean ?: true + val scaleEnabled = map["scaleGestureEnabled"] as? Boolean ?: true val minScale = map["minScale"] as? Float ?: 0.25F val maxScale = map["maxScale"] as? Float ?: 5.0F val currentScale: Vector3 = DecodableUtils.parseVector3(map["scale"] as? HashMap) ?: Vector3(1.0F, 1.0F, 1.0F) //TranslationController - val translationEnabled = map["translationEnabled"] as? Boolean ?: true + val translationEnabled = map["translationGestureEnabled"] as? Boolean ?: true val currentPosition: Vector3 = DecodableUtils.parseVector3(map["position"] as? HashMap) ?: Vector3() //RotationController - val rotationEnabled = map["rotationEnabled"] as? Boolean ?: true + val rotationEnabled = map["rotationGestureEnabled"] as? Boolean ?: true val currentRotation: Quaternion = DecodableUtils.parseQuaternion(map["rotation"] as? HashMap) ?: Quaternion() diff --git a/example/lib/screens/transformable_node.dart b/example/lib/screens/transformable_node.dart index 194168ed..b49e2104 100644 --- a/example/lib/screens/transformable_node.dart +++ b/example/lib/screens/transformable_node.dart @@ -46,8 +46,13 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - nodesMap[selectedNode].scale.value -= - vector.Vector3(1.0, 1.0, 1.0); + final controller = nodesMap[selectedNode] + .scaleControllerNode; + controller.value = controller.value + .copyWith( + scale: controller.value.scale - + vector.Vector3( + 1.0, 1.0, 1.0)); setState(() {}); } } @@ -57,15 +62,39 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - nodesMap[selectedNode].scale.value += - vector.Vector3(1.0, 1.0, 1.0); + final controller = nodesMap[selectedNode] + .scaleControllerNode; + controller.value = controller.value + .copyWith( + scale: controller.value.scale + + vector.Vector3( + 1.0, 1.0, 1.0)); setState(() {}); } } : null), if (selectedNode != null && - nodesMap.containsKey(selectedNode)) - Text(nodesMap[selectedNode].scale.value.text), + nodesMap.containsKey(selectedNode)) ...[ + Checkbox( + value: nodesMap[selectedNode] + .scaleControllerNode + .value + .enabled, + onChanged: (value) { + nodesMap[selectedNode].scaleControllerNode.value = + nodesMap[selectedNode] + .scaleControllerNode + .value + .copyWith(enabled: value); + setState(() {}); + }, + ), + Text(nodesMap[selectedNode] + .scaleControllerNode + .value + .scale + .text), + ] ], ), Row( @@ -76,8 +105,14 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - nodesMap[selectedNode].position.value -= - vector.Vector3(0.3, 0.0, 0.0); + final controller = nodesMap[selectedNode] + .translationControllerNode; + controller.value = controller.value + .copyWith( + position: + controller.value.position - + vector.Vector3( + 0.3, 0.0, 0.0)); setState(() {}); } } @@ -87,14 +122,42 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - nodesMap[selectedNode].position.value += - vector.Vector3(0.3, 0.0, 0.0); + final controller = nodesMap[selectedNode] + .translationControllerNode; + controller.value = controller.value + .copyWith( + position: + controller.value.position + + vector.Vector3( + 0.3, 0.0, 0.0)); + setState(() {}); } } : null), if (selectedNode != null && - nodesMap.containsKey(selectedNode)) - Text(nodesMap[selectedNode].position.value.text), + nodesMap.containsKey(selectedNode)) ...[ + Checkbox( + value: nodesMap[selectedNode] + .translationControllerNode + .value + .enabled, + onChanged: (value) { + nodesMap[selectedNode] + .translationControllerNode + .value = + nodesMap[selectedNode] + .translationControllerNode + .value + .copyWith(enabled: value); + setState(() {}); + }, + ), + Text(nodesMap[selectedNode] + .translationControllerNode + .value + .position + .text), + ] ], ), Row( @@ -104,10 +167,14 @@ class _TransformableNodeState extends State { icon: Icon(Icons.remove), onPressed: selectedNode != null ? () { - if (nodesMap.containsKey(selectedNode)) { - nodesMap[selectedNode].rotation.value -= - vector.Vector4(0.1, 0.1, 0.1, 0.1); - } + final controller = nodesMap[selectedNode] + .rotationControllerNode; + controller.value = controller.value + .copyWith( + rotation: + controller.value.rotation - + vector.Vector4( + 0.1, 0.1, 0.1, 0.1)); } : null), IconButton( @@ -115,14 +182,39 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - nodesMap[selectedNode].rotation.value += - vector.Vector4(0.1, 0.1, 0.1, 0.1); + final controller = nodesMap[selectedNode] + .rotationControllerNode; + controller.value = RotationControllerNode( + rotation: controller.value.rotation + + vector.Vector4( + 0.1, 0.1, 0.1, 0.1)); } } : null), if (selectedNode != null && - nodesMap.containsKey(selectedNode)) - Text(nodesMap[selectedNode].rotation.value.text), + nodesMap.containsKey(selectedNode)) ...[ + Checkbox( + value: nodesMap[selectedNode] + .rotationControllerNode + .value + .enabled, + onChanged: (value) { + nodesMap[selectedNode] + .rotationControllerNode + .value = + nodesMap[selectedNode] + .rotationControllerNode + .value + .copyWith(enabled: value); + setState(() {}); + }, + ), + Text(nodesMap[selectedNode] + .rotationControllerNode + .value + .rotation + .text), + ] ], ), ], @@ -160,9 +252,15 @@ class _TransformableNodeState extends State { final earth = ArCoreNode( shape: earthShape, - scale: vector.Vector3(1.0, 1.0, 1.0), - position: hit.pose.translation + vector.Vector3(0.0, 1.0, 0.0), - rotation: hit.pose.rotation, + scaleControllerNode: ScaleControllerNode( + scale: vector.Vector3(1.0, 1.0, 1.0), + ), + translationControllerNode: TranslationControllerNode( + position: hit.pose.translation + vector.Vector3(0.0, 1.0, 0.0), + ), + rotationControllerNode: RotationControllerNode( + rotation: hit.pose.rotation, + ), ); nodesMap[earth.name] = earth; diff --git a/lib/src/arcore_controller.dart b/lib/src/arcore_controller.dart index 86162c63..d0cd1f0e 100644 --- a/lib/src/arcore_controller.dart +++ b/lib/src/arcore_controller.dart @@ -177,9 +177,11 @@ class ArCoreController { } void _addListeners(ArCoreNode node) { - node.position.addListener(() => _handlePositionChanged(node)); - node.scale.addListener(() => _handleScaleChanged(node)); - node.rotation.addListener(() => _handleRotationChanged(node)); + node.translationControllerNode + .addListener(() => _handlePositionConfigChanged(node)); + node.scaleControllerNode.addListener(() => _handleScaleConfigChanged(node)); + node.rotationControllerNode + .addListener(() => _handleRotationConfigChanged(node)); node?.shape?.materials?.addListener(() => _updateMaterials(node)); // if (node is ArCoreRotatingNode) { @@ -187,7 +189,7 @@ class ArCoreController { // } } - void _handleScaleChanged(ArCoreNode node) { +/* void _handleScaleChanged(ArCoreNode node) { print('_handleScaleChanged: ${node.name}'); _channel.invokeMethod( 'scaleChanged', @@ -216,6 +218,26 @@ class ArCoreController { _getHandlerParams(node, { 'rotation': convertVector4ToMap(node.rotation.value) })); + }*/ + + void _handleRotationConfigChanged(ArCoreNode node) { + print('_handleRotationGestureChanged: ${node.name}'); + _channel.invokeMethod('rotationConfigChanged', + _getHandlerParams(node, node.rotationControllerNode?.value?.toMap())); + } + + void _handleScaleConfigChanged(ArCoreNode node) { + print('_handleScaleConfigChanged: ${node.name}'); + _channel.invokeMethod('scaleConfigChanged', + _getHandlerParams(node, node.scaleControllerNode?.value?.toMap())); + } + + void _handlePositionConfigChanged(ArCoreNode node) { + print('_handlePositionConfigChanged: ${node.name}'); + _channel.invokeMethod( + 'positionConfigChanged', + _getHandlerParams( + node, node.translationControllerNode?.value?.toMap())); } void _updateMaterials(ArCoreNode node) { @@ -228,6 +250,7 @@ class ArCoreController { ArCoreNode node, Map params) { final Map values = {'name': node.name} ..addAll(params); + values.removeWhere((k, v) => v == null); return values; } diff --git a/lib/src/arcore_node.dart b/lib/src/arcore_node.dart index f87c34f9..f2840c71 100644 --- a/lib/src/arcore_node.dart +++ b/lib/src/arcore_node.dart @@ -1,5 +1,6 @@ import 'package:arcore_flutter_plugin/src/arcore_image.dart'; import 'package:arcore_flutter_plugin/src/utils/vector_utils.dart'; +import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; import 'package:vector_math/vector_math_64.dart'; import 'package:arcore_flutter_plugin/src/utils/random_string.dart' @@ -8,46 +9,193 @@ import 'package:arcore_flutter_plugin/src/shape/arcore_shape.dart'; class ArCoreNode { ArCoreNode({ - this.shape, - this.image, + ScaleControllerNode scaleControllerNode, + TranslationControllerNode translationControllerNode, + RotationControllerNode rotationControllerNode, String name, Vector3 position, Vector3 scale, Vector4 rotation, + this.shape, + this.image, this.children = const [], }) : name = name ?? 'node_${random_string.randomString(length: 6)}', - position = ValueNotifier(position), - scale = ValueNotifier(scale), - rotation = ValueNotifier(rotation), - assert(!(shape != null && image != null)); + scaleControllerNode = ValueNotifier( + scaleControllerNode ?? ScaleControllerNode(scale: scale)), + translationControllerNode = ValueNotifier(translationControllerNode ?? + TranslationControllerNode(position: position)), + rotationControllerNode = ValueNotifier(rotationControllerNode ?? + RotationControllerNode(rotation: rotation)), + assert(!(shape != null && image != null)), + assert(!(scaleControllerNode != null && scale != null)), + assert(!(translationControllerNode != null && position != null)), + assert(!(rotationControllerNode != null && rotation != null)); final List children; - // final double minScale; - // - // final double maxScale; - final ArCoreShape shape; - final ValueNotifier position; + final String name; - final ValueNotifier scale; + final ArCoreImage image; - final ValueNotifier rotation; + final ValueNotifier scaleControllerNode; - final String name; + final ValueNotifier translationControllerNode; - final ArCoreImage image; + final ValueNotifier rotationControllerNode; + + void disableScaleGesture() { + scaleControllerNode.value = + scaleControllerNode.value.copyWith(enabled: false); + } + + void enableScaleGesture() { + scaleControllerNode.value = + scaleControllerNode.value.copyWith(enabled: true); + } + + // factory ArCoreNode.fromMap(Map map) { + // return ArCoreNode( + // name: map['name'], + // position: map['position'], + // scale: map['scale'], + // rotation: map['rotation'], + // ); + // } Map toMap() => { 'dartType': runtimeType.toString(), 'shape': shape?.toMap(), - 'position': convertVector3ToMap(position.value), - 'scale': convertVector3ToMap(scale.value), - 'rotation': convertVector4ToMap(rotation.value), + // 'position': convertVector3ToMap(position.value), + // 'scale': convertVector3ToMap(scale.value), + // 'rotation': convertVector4ToMap(rotation.value), + // 'scaleGestureEnabled': scaleGestureEnabled.value, + // 'translationGestureEnabled': translationGestureEnabled.value, + // 'rotationGestureEnabled': rotationGestureEnabled.value, + 'scaleControllerNode': scaleControllerNode?.value?.toMap(), + 'translationControllerNode': translationControllerNode?.value?.toMap(), + 'rotationControllerNode': rotationControllerNode?.value?.toMap(), 'name': name, 'image': image?.toMap(), 'children': this.children.map((arCoreNode) => arCoreNode.toMap()).toList(), }..removeWhere((String k, dynamic v) => v == null); } + +class ScaleControllerNode extends Equatable { + final Vector3 scale; + final double minScale; + final bool enabled; + final double maxScale; + + ScaleControllerNode({ + this.scale, + this.minScale = 0.25, + this.maxScale = 5.0, + this.enabled = true, + }); + + Map toMap() { + return { + 'scale': convertVector3ToMap(this.scale), + 'minScale': this.minScale, + 'enabled': this.enabled, + 'maxScale': this.maxScale, + }; + } + + ScaleControllerNode copyWith({ + Vector3 scale, + double minScale, + bool enabled, + double maxScale, + }) { + if ((scale == null || identical(scale, this.scale)) && + (minScale == null || identical(minScale, this.minScale)) && + (enabled == null || identical(enabled, this.enabled)) && + (maxScale == null || identical(maxScale, this.maxScale))) { + return this; + } + + return new ScaleControllerNode( + scale: scale ?? this.scale, + minScale: minScale ?? this.minScale, + enabled: enabled ?? this.enabled, + maxScale: maxScale ?? this.maxScale, + ); + } + + @override + List get props => [scale, minScale, maxScale, enabled]; +} + +class TranslationControllerNode extends Equatable { + final Vector3 position; + final bool enabled; + + TranslationControllerNode({ + this.position, + this.enabled = true, + }); + + Map toMap() { + return { + 'position': convertVector3ToMap(this.position), + 'enabled': this.enabled, + }; + } + + TranslationControllerNode copyWith({ + Vector3 position, + bool enabled, + }) { + if ((position == null || identical(position, this.position)) && + (enabled == null || identical(enabled, this.enabled))) { + return this; + } + + return new TranslationControllerNode( + position: position ?? this.position, + enabled: enabled ?? this.enabled, + ); + } + + @override + List get props => [position, enabled]; +} + +class RotationControllerNode extends Equatable { + final Vector4 rotation; + final bool enabled; + + RotationControllerNode({ + this.rotation, + this.enabled = true, + }); + + Map toMap() { + return { + 'rotation': convertVector4ToMap(this.rotation), + 'enabled': this.enabled, + }; + } + + RotationControllerNode copyWith({ + Vector4 rotation, + bool enabled, + }) { + if ((rotation == null || identical(rotation, this.rotation)) && + (enabled == null || identical(enabled, this.enabled))) { + return this; + } + + return new RotationControllerNode( + rotation: rotation ?? this.rotation, + enabled: enabled ?? this.enabled, + ); + } + + @override + List get props => [rotation, enabled]; +} From 62656fb1ad9ee7a83f33efc4f260479d087e477e Mon Sep 17 00:00:00 2001 From: Gian Marco Di Francesco Date: Thu, 26 Nov 2020 17:51:08 +0100 Subject: [PATCH 7/9] Improve code usability --- example/lib/screens/transformable_node.dart | 154 +++++++++----------- lib/src/arcore_controller.dart | 2 +- lib/src/arcore_node.dart | 46 ++++-- 3 files changed, 104 insertions(+), 98 deletions(-) diff --git a/example/lib/screens/transformable_node.dart b/example/lib/screens/transformable_node.dart index b49e2104..601c859b 100644 --- a/example/lib/screens/transformable_node.dart +++ b/example/lib/screens/transformable_node.dart @@ -46,13 +46,17 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - final controller = nodesMap[selectedNode] - .scaleControllerNode; - controller.value = controller.value - .copyWith( - scale: controller.value.scale - - vector.Vector3( - 1.0, 1.0, 1.0)); + final node = nodesMap[selectedNode]; + node.changeScale(node.scale - + vector.Vector3(1.0, 1.0, 1.0)); + + // final controller = nodesMap[selectedNode] + // .scaleControllerNode; + // controller.value = controller.value + // .copyWith( + // scale: controller.value.scale - + // vector.Vector3( + // 1.0, 1.0, 1.0)); setState(() {}); } } @@ -62,13 +66,16 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - final controller = nodesMap[selectedNode] - .scaleControllerNode; - controller.value = controller.value - .copyWith( - scale: controller.value.scale + - vector.Vector3( - 1.0, 1.0, 1.0)); + final node = nodesMap[selectedNode]; + node.changeScale(node.scale + + vector.Vector3(1.0, 1.0, 1.0)); + // final controller = nodesMap[selectedNode] + // .scaleControllerNode; + // controller.value = controller.value + // .copyWith( + // scale: controller.value.scale + + // vector.Vector3( + // 1.0, 1.0, 1.0)); setState(() {}); } } @@ -76,24 +83,14 @@ class _TransformableNodeState extends State { if (selectedNode != null && nodesMap.containsKey(selectedNode)) ...[ Checkbox( - value: nodesMap[selectedNode] - .scaleControllerNode - .value - .enabled, + value: nodesMap[selectedNode].scaleGestureEnabled, onChanged: (value) { - nodesMap[selectedNode].scaleControllerNode.value = - nodesMap[selectedNode] - .scaleControllerNode - .value - .copyWith(enabled: value); + nodesMap[selectedNode].scaleGestureEnabled = + value; setState(() {}); }, ), - Text(nodesMap[selectedNode] - .scaleControllerNode - .value - .scale - .text), + Text(nodesMap[selectedNode].scale.text), ] ], ), @@ -105,14 +102,18 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - final controller = nodesMap[selectedNode] + final node = nodesMap[selectedNode]; + node.changePosition(node.position - + vector.Vector3(0.3, 0.0, 0.0)); + + /* final controller = nodesMap[selectedNode] .translationControllerNode; controller.value = controller.value .copyWith( position: controller.value.position - vector.Vector3( - 0.3, 0.0, 0.0)); + 0.3, 0.0, 0.0));*/ setState(() {}); } } @@ -122,14 +123,18 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - final controller = nodesMap[selectedNode] - .translationControllerNode; - controller.value = controller.value - .copyWith( - position: - controller.value.position + - vector.Vector3( - 0.3, 0.0, 0.0)); + final node = nodesMap[selectedNode]; + node.changePosition(node.position + + vector.Vector3(0.3, 0.0, 0.0)); + + // final controller = nodesMap[selectedNode] + // .translationControllerNode; + // controller.value = controller.value + // .copyWith( + // position: + // controller.value.position + + // vector.Vector3( + // 0.3, 0.0, 0.0)); setState(() {}); } } @@ -137,26 +142,15 @@ class _TransformableNodeState extends State { if (selectedNode != null && nodesMap.containsKey(selectedNode)) ...[ Checkbox( - value: nodesMap[selectedNode] - .translationControllerNode - .value - .enabled, + value: + nodesMap[selectedNode].positionGestureEnabled, onChanged: (value) { - nodesMap[selectedNode] - .translationControllerNode - .value = - nodesMap[selectedNode] - .translationControllerNode - .value - .copyWith(enabled: value); + nodesMap[selectedNode].positionGestureEnabled = + value; setState(() {}); }, ), - Text(nodesMap[selectedNode] - .translationControllerNode - .value - .position - .text), + Text(nodesMap[selectedNode].position.text), ] ], ), @@ -167,14 +161,19 @@ class _TransformableNodeState extends State { icon: Icon(Icons.remove), onPressed: selectedNode != null ? () { - final controller = nodesMap[selectedNode] - .rotationControllerNode; - controller.value = controller.value - .copyWith( - rotation: - controller.value.rotation - - vector.Vector4( - 0.1, 0.1, 0.1, 0.1)); + if (nodesMap.containsKey(selectedNode)) { + final node = nodesMap[selectedNode]; + node.changeRotation(node.rotation - + vector.Vector4(0.1, 0.1, 0.1, 0.1)); + setState(() {}); + // final controller = + // node.rotationControllerNode; + // controller.value = controller.value + // .copyWith( + // rotation: node.rotation - + // vector.Vector4( + // 0.1, 0.1, 0.1, 0.1)); + } } : null), IconButton( @@ -182,38 +181,25 @@ class _TransformableNodeState extends State { onPressed: selectedNode != null ? () { if (nodesMap.containsKey(selectedNode)) { - final controller = nodesMap[selectedNode] - .rotationControllerNode; - controller.value = RotationControllerNode( - rotation: controller.value.rotation + - vector.Vector4( - 0.1, 0.1, 0.1, 0.1)); + final node = nodesMap[selectedNode]; + node.changeRotation(node.rotation + + vector.Vector4(0.1, 0.1, 0.1, 0.1)); + setState(() {}); } } : null), if (selectedNode != null && nodesMap.containsKey(selectedNode)) ...[ Checkbox( - value: nodesMap[selectedNode] - .rotationControllerNode - .value - .enabled, + value: + nodesMap[selectedNode].rotationGestureEnabled, onChanged: (value) { - nodesMap[selectedNode] - .rotationControllerNode - .value = - nodesMap[selectedNode] - .rotationControllerNode - .value - .copyWith(enabled: value); + nodesMap[selectedNode].rotationGestureEnabled = + value; setState(() {}); }, ), - Text(nodesMap[selectedNode] - .rotationControllerNode - .value - .rotation - .text), + Text(nodesMap[selectedNode].rotation.text), ] ], ), diff --git a/lib/src/arcore_controller.dart b/lib/src/arcore_controller.dart index d0cd1f0e..8d83794a 100644 --- a/lib/src/arcore_controller.dart +++ b/lib/src/arcore_controller.dart @@ -141,7 +141,7 @@ class ArCoreController { return _channel.invokeMethod('getTrackingState'); } - addArCoreNodeToAugmentedImage(ArCoreNode node, int index, + Future addArCoreNodeToAugmentedImage(ArCoreNode node, int index, {String parentNodeName}) { assert(node != null); diff --git a/lib/src/arcore_node.dart b/lib/src/arcore_node.dart index f2840c71..17d202f4 100644 --- a/lib/src/arcore_node.dart +++ b/lib/src/arcore_node.dart @@ -45,14 +45,40 @@ class ArCoreNode { final ValueNotifier rotationControllerNode; - void disableScaleGesture() { + Vector3 get position => translationControllerNode.value.position; + bool get positionGestureEnabled => translationControllerNode.value.enabled; + set positionGestureEnabled(bool value) { + translationControllerNode.value = + translationControllerNode.value.copyWith(enabled: value); + } + + Vector4 get rotation => rotationControllerNode.value.rotation; + bool get rotationGestureEnabled => rotationControllerNode.value.enabled; + set rotationGestureEnabled(bool value) { + rotationControllerNode.value = + rotationControllerNode.value.copyWith(enabled: value); + } + + Vector3 get scale => scaleControllerNode.value.scale; + bool get scaleGestureEnabled => scaleControllerNode.value.enabled; + set scaleGestureEnabled(bool value) { scaleControllerNode.value = - scaleControllerNode.value.copyWith(enabled: false); + scaleControllerNode.value.copyWith(enabled: value); + } + + void changeRotation(Vector4 newRotation) { + rotationControllerNode.value = + rotationControllerNode.value.copyWith(rotation: newRotation); + } + + void changePosition(Vector3 newPosition) { + translationControllerNode.value = + translationControllerNode.value.copyWith(position: newPosition); } - void enableScaleGesture() { + void changeScale(Vector3 newScale) { scaleControllerNode.value = - scaleControllerNode.value.copyWith(enabled: true); + scaleControllerNode.value.copyWith(scale: newScale); } // factory ArCoreNode.fromMap(Map map) { @@ -67,12 +93,6 @@ class ArCoreNode { Map toMap() => { 'dartType': runtimeType.toString(), 'shape': shape?.toMap(), - // 'position': convertVector3ToMap(position.value), - // 'scale': convertVector3ToMap(scale.value), - // 'rotation': convertVector4ToMap(rotation.value), - // 'scaleGestureEnabled': scaleGestureEnabled.value, - // 'translationGestureEnabled': translationGestureEnabled.value, - // 'rotationGestureEnabled': rotationGestureEnabled.value, 'scaleControllerNode': scaleControllerNode?.value?.toMap(), 'translationControllerNode': translationControllerNode?.value?.toMap(), 'rotationControllerNode': rotationControllerNode?.value?.toMap(), @@ -90,7 +110,7 @@ class ScaleControllerNode extends Equatable { final double maxScale; ScaleControllerNode({ - this.scale, + @required this.scale, this.minScale = 0.25, this.maxScale = 5.0, this.enabled = true, @@ -135,7 +155,7 @@ class TranslationControllerNode extends Equatable { final bool enabled; TranslationControllerNode({ - this.position, + @required this.position, this.enabled = true, }); @@ -170,7 +190,7 @@ class RotationControllerNode extends Equatable { final bool enabled; RotationControllerNode({ - this.rotation, + @required this.rotation, this.enabled = true, }); From 7ef36763816ef2ea0915b6918c8e81d286dfd447 Mon Sep 17 00:00:00 2001 From: Jorge Verdugo Date: Thu, 15 Apr 2021 02:39:08 -0400 Subject: [PATCH 8/9] Fix initial controllers values --- .../flutter_models/FlutterArCoreNodeConfiguration.kt | 10 +++++++--- lib/src/arcore_reference_node.dart | 9 ++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt index 5ec36212..3a0f32ad 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/flutter_models/FlutterArCoreNodeConfiguration.kt @@ -12,16 +12,20 @@ class FlutterArCoreNodeConfiguration(map: Map) { val scaleEnabled = map["scaleGestureEnabled"] as? Boolean ?: true val minScale = map["minScale"] as? Float ?: 0.25F val maxScale = map["maxScale"] as? Float ?: 5.0F - val currentScale: Vector3 = DecodableUtils.parseVector3(map["scale"] as? HashMap) + + val scaleControllerNodeMap = map["scaleControllerNode"] as HashMap + val currentScale: Vector3 = DecodableUtils.parseVector3(scaleControllerNodeMap["scale"] as? HashMap) ?: Vector3(1.0F, 1.0F, 1.0F) //TranslationController + val translationControllerNodeMap = map["translationControllerNode"] as HashMap val translationEnabled = map["translationGestureEnabled"] as? Boolean ?: true - val currentPosition: Vector3 = DecodableUtils.parseVector3(map["position"] as? HashMap) ?: Vector3() + val currentPosition: Vector3 = DecodableUtils.parseVector3(translationControllerNodeMap["position"] as? HashMap) ?: Vector3() //RotationController + val rotationControllerNode = map["rotationControllerNode"] as HashMap val rotationEnabled = map["rotationGestureEnabled"] as? Boolean ?: true - val currentRotation: Quaternion = DecodableUtils.parseQuaternion(map["rotation"] as? HashMap) + val currentRotation: Quaternion = DecodableUtils.parseQuaternion(rotationControllerNode["rotation"] as? HashMap) ?: Quaternion() } \ No newline at end of file diff --git a/lib/src/arcore_reference_node.dart b/lib/src/arcore_reference_node.dart index d90fc35e..1c323f48 100644 --- a/lib/src/arcore_reference_node.dart +++ b/lib/src/arcore_reference_node.dart @@ -1,3 +1,4 @@ +import 'package:flutter/widgets.dart'; import 'package:vector_math/vector_math_64.dart'; import 'arcore_node.dart'; @@ -11,6 +12,9 @@ class ArCoreReferenceNode extends ArCoreNode { final String objectUrl; ArCoreReferenceNode({ + ScaleControllerNode scaleControllerNode, + TranslationControllerNode translationControllerNode, + RotationControllerNode rotationControllerNode, String name, this.object3DFileName, this.objectUrl, @@ -23,7 +27,10 @@ class ArCoreReferenceNode extends ArCoreNode { children: children, position: position, scale: scale, - rotation: rotation); + rotation: rotation, + scaleControllerNode: scaleControllerNode, + translationControllerNode: translationControllerNode, + rotationControllerNode: rotationControllerNode); @override Map toMap() => { From 2bcdb35f5cb715f61600ba547d4fd29de567749d Mon Sep 17 00:00:00 2001 From: Jorge Verdugo Date: Thu, 15 Apr 2021 10:04:22 -0400 Subject: [PATCH 9/9] Fix onAddNode Future --- .../difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt index 1af52bc3..9768f6bd 100644 --- a/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt +++ b/android/src/main/kotlin/com/difrancescogianmarco/arcore_flutter_plugin/ArCoreView.kt @@ -394,9 +394,9 @@ class ArCoreView(val activity: Activity, context: Context, messenger: BinaryMess onAddNode(n, null) } } - + result?.success(null) } - result?.success(null) + } fun attachNodeToParent(node: Node?, parentNodeName: String?) {