Android 使用Arcore 实现多点测距
已更新第二版,详情见github链接github源码 点这里 <==
主要使用了Anchor(锚点),Pose (姿势/姿态),Node(节点),Vector3(三维向量)
github源码 点这里 <==
1.准备
- 一台支持Arcore的手机
- 依赖arcore和sceneform
implementation 'com.google.ar:core:1.5.0'
implementation 'com.google.ar.sceneform:core:1.5.0'
implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.5.0'
-布局文件使用sceneform提供的fragment
<fragment
android:id="@+id/UI_ArSceneView"
android:name="com.gj.arcoredraw.MyArFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
import com.blankj.utilcode.util.ToastUtils;
import com.google.ar.core.exceptions.UnavailableApkTooOldException;
import com.google.ar.core.exceptions.UnavailableArcoreNotInstalledException;
import com.google.ar.core.exceptions.UnavailableDeviceNotCompatibleException;
import com.google.ar.core.exceptions.UnavailableException;
import com.google.ar.core.exceptions.UnavailableSdkTooOldException;
import com.google.ar.sceneform.ux.ArFragment;
//可以直接使用ArFragment 我这里为了中文提示
public class MyArFragment extends ArFragment {
@Override
protected void handleSessionException(UnavailableException sessionException) {
String message;
if (sessionException instanceof UnavailableArcoreNotInstalledException) {
message = "请安装ARCore";
} else if (sessionException instanceof UnavailableApkTooOldException) {
message = "请升级ARCore";
} else if (sessionException instanceof UnavailableSdkTooOldException) {
message = "请升级app";
} else if (sessionException instanceof UnavailableDeviceNotCompatibleException) {
message = "当前设备部不支持AR";
} else {
message = "未能创建AR会话,请查看机型适配,arcore版本与系统版本";
String var3 = String.valueOf(sessionException);
}
ToastUtils.showLong(message);
}
}
2.监听点击 生成锚点
- **设置ArFragment的Tap监听 **
(UI_ArSceneView as MyArFragment).setOnTapArPlaneListener { hitResult, plane, motionEvent ->
val currentAnchor=hitResult.createAnchor()
}
3.计算两个锚点之间的距离
val startPose = endAnchor.pose
val endPose = startAnchor.pose
val dx = startPose.tx() - endPose.tx()
val dy = startPose.ty() - endPose.ty()
val dz = startPose.tz() - endPose.tz()
val length = Math.sqrt((dx * dx + dy * dy + dz * dz).toDouble())
anchorInfoBean.dataText = "距离为${decimalFormat.format(length)}m"
4.UI 划线 (两个锚点在ui上连接划线)
private fun drawLine(firstAnchor: Anchor, secondAnchor: Anchor) {
val firstAnchorNode = AnchorNode(firstAnchor)
val secondAnchorNode = AnchorNode(secondAnchor)
firstAnchorNode.setParent((UI_ArSceneView as MyArFragment).arSceneView.scene)
val firstWorldPosition = firstAnchorNode.worldPosition
val secondWorldPosition = secondAnchorNode.worldPosition
val difference = Vector3.subtract(firstWorldPosition, secondWorldPosition)
val directionFromTopToBottom = difference.normalized()
val rotationFromAToB = Quaternion.lookRotation(directionFromTopToBottom, Vector3.up())
MaterialFactory.makeOpaqueWithColor(this@MainActivity, com.google.ar.sceneform.rendering.Color(0f, 191f, 255f))
.thenAccept { material ->
val lineMode = ShapeFactory.makeCube(Vector3(0.01f, 0.01f, difference.length()), Vector3.zero(), material)
val lineNode = Node()
lineNode.setParent(firstAnchorNode)
lineNode.renderable = lineMode
lineNode.worldPosition = Vector3.add(firstWorldPosition, secondWorldPosition).scaled(0.5f)
lineNode.worldRotation = rotationFromAToB
}
}
5.自定义Node 始终面向相机
override fun onUpdate(p0: FrameTime?) {
scene?.let { scene ->
val cameraPosition = scene.camera.worldPosition
val nodePosition = this@FaceToCameraNode.worldPosition
val direction = Vector3.subtract(cameraPosition, nodePosition)
this@FaceToCameraNode.worldRotation = Quaternion.lookRotation(direction, Vector3.up())
}
}
UI这里有点复杂,主要难点在Vector3(空间向量)这里.用的是数学知识了.
1.先用两个Anchor 获得 世界坐标(worldPosition)
2.使用向量计算两点空间差
3.使用起始Anchor ,生成一个Node.附加到arSceneView上
4.使用MaterialFactory 创建一个Material.再创建一个 Node使用刚才的材质,附加到之前的Node上.
github源码 点这里 <==