Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
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.*
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.*

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.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)
}

var onNodeUpdate: ((BaseNode) -> Any)? = null

override fun onUpdate(frameTime: FrameTime) {
onNodeUpdate?.invoke(this)
}
}

//
//class CustomScaleController(
// transformableNode: BaseTransformableNode,
// gestureRecognizer: PinchGestureRecognizer
//) :
// BaseTransformationController<PinchGesture>(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
// }
//}
Original file line number Diff line number Diff line change
@@ -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)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand All @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Loading