懒得多说,上代码:
const lockTrackerEl = scene.createElement(xrFrameSystem.XRNode);
lockTrackerEl.addComponent(xrFrameSystem.ARTracker, {
mode: 'Marker',
src: i.src,
});
shadowNode.addChild(lockTrackerEl);
如上,其中shadowNode是自定义节点的引用 wxml:
<xr-shadow id="shadow-node" position="0 1 0" />
i.src自然就是追踪器识别的图片地址。
接下来是监听追踪器识别状态:
lockTrackerEl.event.add('ar-tracker-state', tracker => {
// 获取当前状态和错误信息
const {
state,
errorMessage
} = tracker;
console.log('>>> errorMessage', errorMessage)
if (state === 2 && !waiting) {
console.log('match')
waiting = true;
}
// 动态创建添加GLTF资源
i.trackerId?.forEach(id => {
const item = gltfs.find(item => item.id === id)
if (item) {
const gltfNode = scene.createElement(xrFrameSystem.XRAssetLoad, {
type: item.type,
"asset-id": 'gltf-' + item.id,
src: item.src
});
assetsNode?.addChild(gltfNode)
}
})
})
如上,gltfs是所有模型的列表,i.trackerId是追踪器对应的模型的id,assetsNode是资源加载器节点。
模型加载需要监控,等加载成功再设置模型的世界坐标为追踪器识别点的世界坐标。
this.loadedARGLTF = () => {
this.handleArTrackerState(i, lockTrackerEl)
}
如上,loadedARGLTF
方法是资源加载器加载完成的回调。
/**
* 追踪器识别成功
* @param {AssetItem} i 追踪器资源参数
* @param lockTrackerEl 追踪器节点
*/
handleArTrackerState(i: AssetItem, lockTrackerEl: XRNode) {
const shadowNode = this.shadowNode
const scene = this.scene
const list = this.list
const xrFrameSystem = wx.getXrFrameSystem();
const gltfs = list.filter(i => i.type === AssetType.GLTF)
// 追踪器和模型关系 一对多
const lockItemEles: Element[] = []
// 创建 TRS对应节点
const lockParentEl = scene.createElement(xrFrameSystem.XRNode);
i.trackerId?.forEach(id => {
// 找到对应的模型配置参数
const gltfParams = gltfs.find(p => p.id === id)
if (gltfParams) {
// 创建 SubTRS对应节点
// 初始化 渲染到屏幕外 x:10000
const lockItemEle = scene.createElement(xrFrameSystem.XRNode, {
position: '10000 0 0',
});
lockParentEl.addChild(lockItemEle);
// 记录 SubTRS对应节点
lockItemEles.push(lockItemEle)
// 创建 GLTF节点
const gltfNode = scene.createElement(xrFrameSystem.XRGLTF, {
model: 'gltf-' + id,
position: gltfParams.position || '',
scale: gltfParams.scale || '',
rotation: gltfParams.rotation || '',
'anim-autoplay': '',
});
// 监听 GLTF加载到屏幕的状态
gltfNode.event.add('gltf-loaded', (event: { target: XRGLTF }) => {
// console.log('>>> gltf-loaded', event)
if (gltfParams.hasEvent) {
this.addEvent(event.target, gltfParams.id)
}
});
// GLTF节点添加到 SubTRS对应节点
lockItemEle.addChild(gltfNode);
}
})
// 模型添加到场景中
shadowNode.addChild(lockParentEl);
// 延迟30毫秒 ,保证节点已经挂载
setTimeout(() => {
// 将 lockTrackerEl 的世界矩阵信息同步到 lockItemEle
const lockTrackerTrs = lockTrackerEl.getComponent(xrFrameSystem.Transform)
lockItemEles.forEach(lockItemEle => {
const lockItemTrs = lockItemEle.getComponent(xrFrameSystem.Transform)
lockItemTrs.setLocalMatrix(lockTrackerTrs.worldMatrix);
// 记录SubTRS
this.gltfItemSubTRS.push(lockItemTrs)
})
// 记录TRS
this.gltfItemTRS.push(lockParentEl.getComponent(xrFrameSystem.Transform))
// 去除tracker监听
shadowNode.removeChild(lockTrackerEl);
// 开启旋转缩放逻辑
scene.event.addOnce('touchstart', this.handleTouchStart.bind(this))
}, 30);
}
如上,逻辑略复杂。
总的来说就是在模型加载完成之后,直接添加GLTF元素节点。在GLTF节点渲染到场景之后,进行事件绑定。延迟30毫秒,设置GLTF节点的世界坐标(这一步按理说应该放在 GLTF节点渲染到场景之后 执行,不然可能有bug)。
最后移除追踪器,开启鼠标操作。
后续还要增加一个逻辑:如果页面有多个追踪器,应该全部移除。当前页面提供一个关闭操作,删除识别出来的模型,然后再把所有追踪器添加到场景。
这样可以反复使用。