Zion插件分析——跟踪器

发表于2016-10-31
评论0 1.3k浏览
一、ZionTrackerObject.cs
  是一个MonoBehaviour类,绑定在跟踪器对象上,在Zion的[CameraRig]预置体里,缺省配置是三个跟踪器,头显,左手柄,右手柄。他们都已经绑定了ZionTrackerObject.cs脚本。简单的说,这个脚本的作用,就是,从底层获取现实中设备的位姿,把位置信息赋给Unity场景中这些设备对应的游戏对象
(如左手柄对象)
 

UML类图


字段
  index:设备索引,通过这个值区分跟踪器的具体类别,头显,左右手柄对应的索引值分别为0,1,2。而初始值为4294967295,表示无效设备。
  origin相对原点物体
构造方法
  ZionTrackedObject ()
实例方法
  SetDeviceIndex(uint index)
  设置设备索引,如果传入了手柄的索引(0或1),要先调用ZionVR.instance.supportTrackedController判断当前连接设备是否支持跟踪手柄,如果不支持,就禁用自身(手柄对象绑定的ZionTrackedObject.cs)。
  在项目中没有直接调用,而是通过广播方式通知。
  OnNewPoses(params object[] args)
  监听设备位置更新的事件,当位置发生改变时,会回调此方法,并传入新的设备位置,这里要把跟踪器对象的位置更新为传入的位置。
  如果指定了origin,那么要将传入位置的值进行一些处理再赋值。如果没有指定origin,就把传入的位置和方向直接赋给当前对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (origin != null)
{
    // 如果指定了origin,那么要将origin下的局部坐标转换为世界坐标
    pose = new ZionTransform(origin) * pose;
    pose.position.x *= origin.localScale.x;
    pose.position.y *= origin.localScale.y;
    pose.position.z *= origin.localScale.z;
    transform.position = pose.position;
    transform.rotation = pose.rotation;
}
else
{
    // 如果没有指定origin,那只需要局部坐标,其他交给父对象处理
    transform.localPosition = pose.position;
    transform.localRotation = pose.rotation;
}

MonoBehaviour类方法
OnEnable()
  一些初始化操作。当未连接VR设备或者ZionRender未实例化时,禁用自身。
  如果一切就绪,将OnNewPoses加入Zion的事件委托链,监听位置的变化。
1
2
ZionEvent.Listen(ZionEvent.NEW_POSES, OnNewPoses);
OnDisable()

  脚本禁用时要移除事件委托,停止监听
1
ZionEvent.Remove(ZionEvent.NEW_POSES, OnNewPoses);

二、ZionTrackedDeviceManager.cs
  是一个MonoBehaviour类,全局跟踪设备管理器,用于管理跟踪器(手柄、头显)的连接。绑定在[CameraRig]对象上。
UML类图


字段
  三个public字段,在Unity编辑器中可见,必须指定头显、左手柄、右手柄对象。


构造方法
  ZionTrackedDeviceManager ()
实例方法
  OnDeviceConnected(params object[] args)
  监听设备的连接,通过传入的参数来为场景中的ZionTrackedObject脚本设置索引值。
  虽然会传入设备索引,但是要判断连接状态,如果未连接却传入了有效索引,那么要设置为无效索引。
uint setIndex = connected ? index : Constants.invalidDeviceIndex;
  根据有效索引,判断设备类型,引用其对象obj(head/left/right)。然后调用该对象上的SetDeviceIndex方法,设置跟踪器的索引为setIndex。
  OnDeviceRoleChanged(params object[] args)
  跟踪设备发生变化,重新发送广播设置索引,目前看来只有SteamVR的跟踪脚本中有发送事件,但是也没传入参数,不知道作用是什么。
MonoBehaviour类方法
OnEnable()
1、2D模式下(未连接设备),禁用自身。
2、如果head不为空,则发送广播,把[Camera(head)]的跟踪器脚本的索引设置为Constants.indexHmd,即index=0;
3、如果当前连接的设备支持跟踪手柄,则发送广播,把[Camera(left)]和[Camera(right)]的索引分别设置为Constants.indexLeft和Constants.indexRight,即1和2。
4、将OnDeviceConnected和OnDeviceRoleChanged添加到Zion事件系统的委托链中,监听设备的连接和改变。
OnDisable()
  脚本禁用时,将OnDeviceConnected和OnDeviceRoleChanged从Zion事件系统的委托链中移除,取消监听设备的连接和改变。

三、从底层获取设备状态
  定义了基类ZionTrackerBase,获取跟踪设备的位置信息,各平台会从自己的底层来取数据。
1
2
3
4
5
6
7
8
// 派生类需要实现此方法,设置poses数组中位姿的位置和方向
public virtual bool GetPoses(ref ZionTransform []poses) {
    poses [Constants.indexHmd].Set (Matrix4x4.identity);
    poses [Constants.indexLeft].Set (Matrix4x4.identity);
    poses [Constants.indexRight].Set (Matrix4x4.identity);
 
    return true;
}
继承关系图


1、ZionTrackerOculusVR.cs
Oculus平台实现的跟踪器
GetPoses(ref Zion.ZionTransform[] posesOut)
不愧是Unity VR的亲儿子,方式最为简洁,头显的位置直接可以通过Unity内置的VR来获取。从VR底层获取位置和方向的方法分别为
获取位置:GetLocalPosition(VRNode node)
获取方向:GetLocalRotation(VRNode node)
1
2
3
4
5
6
// 设置头显位置和方向
    posesOut[Zion.Constants.indexHmd].Set
    (
    VR.InputTracking.GetLocalPosition(VR.VRNode.CenterEye),
    VR.InputTracking.GetLocalRotation(VR.VRNode.CenterEye)
    );

而获取跟踪手柄(刚上式的Touch手柄),就要通过OVRPlugin从底层来取了。
获取位姿:GetNodePose(Node nodeId).ToOVRPose()
1
2
3
// 获取位姿OVRPose
    OVRPose leftHandPosef = OVRPlugin.GetNodePose(OVRPlugin.Node.HandLeft).ToOVRPose();
    OVRPose rightHandPosef = OVRPlugin.GetNodePose(OVRPlugin.Node.HandRight).ToOVRPose();

从位姿OVRPose获取位置:.position
从位姿OVRPose获取方向:.orientation
1
2
3
// 设置Touch手柄的位置和方向
    posesOut[Zion.Constants.indexLeft].Set(leftHandPosef.position, leftHandPosef.orientation);
    posesOut[Zion.Constants.indexRight].Set(rightHandPosef.position, rightHandPosef.orientation);

  设置成功后返回true。
2、ZionTrackerSteamVR.cs
SteamVR平台实现的跟踪器,获取位姿,以及发送事件。
字段
  leftIndex:左手柄索引
  rightIndex:右手柄索引
  poses:存储所有跟踪设备的pose,大小为16
  gamePoses预测状态数组
构造方法
ZionTrackerSteamVR ()
  使用OpenVR尝试获取左右手柄的索引;将OnDeviceConnected和OnTrackedDeviceRoleChanged加入Zion事件系统的委托链中,分别监听设备连接和设备变动。
Monobehaviour方法
Disable()
  将OnDeviceConnected和OnTrackedDeviceRoleChanged从Zion事件系统的委托链中移除,取消监听设备连接和设备变动。
实例方法
OnDeviceConnected(params object[] args)
  发送事件,如手柄连接,改变,断开,头显连接状态改变等
OnTrackedDeviceRoleChanged(params object[] args)
发送手柄角色改变事件(Zion.ZionEvent.DEVICE_ROLE_CHANGED),理论上参数里面应该带了哪个索引变成了左右手,因为没有示例,所以不清楚具体的参数,只能每次都重新获取了。
GetPoses(ref Zion.ZionTransform []posesOut)
  将设备的位姿(位置,方向)填充到posesOut里,目前看来是填充到ZionRender里的poses里。实现的是原生SteamVR插件里SteamVR_Render的渲染协程RenderLoop的功能(渲染有关的功能在ZionRender里,获取位姿放在这里)。
 
SteamVR平台获取设备位姿是利用合成器来完成。
1
compositor.GetLastPoses(poses, gamePoses);// 填充位姿到poses中去,注意是全局数组poses,不是posesOut

获取完以后要发送通知,将新的poses通知给使用它的方法。
最后将poses中的位姿进行一些转换后,设置给posesOut以及更新左右眼与头部的偏移。
Convert(HmdMatrix34_t pose)
将HmdMatrix34_t转换为Matrix4x4的方法。在上面的GetPoses,我们从合成器获取位姿,最后设置给ZionRender的位姿数组。但是两者的格式是不一样的。
poses[i].mDeviceToAbsoluteTracking得到指定设备的位姿,格式为HmdMatrix34_t。
posesOut[i].Set,需要传入的是Matrix4x4类型参数。
因此要作转换。
3、ZionTrackerMojing.cs
  暴风魔镜平台跟踪器的实现,因为不支持跟踪手柄,只获取头显位姿就可以了。但是要考虑左右手坐标的转换。
字段
lipZ:转换矩阵
  一般情况下左手和右手坐标系的数值做转换的话只需把 Z 值乘以 -1 即可。比如左手坐标系的 (1, 1, 1) 转成为右手坐标系是 (1, 1, -1)。
  这里转换矩阵为Matrix4x4.Scale(new Vector3(1, 1, -1))
headView:头显位姿,矩阵
构造方法
  ZionTrackerMojing ()
  Disable()
  GetPoses(ref Zion.ZionTransform []posesOut)
1
2
3
4
5
6
7
// 提取头显的位置
    MojingSDK.Unity_getLastHeadView(ref headView);
    // 获取到的位置是左手法则,转成右手法则的
    posesOut [Zion.Constants.indexHmd].Set (flipZ * headView * flipZ);
    // 不支持手柄跟踪
    posesOut [Zion.Constants.indexLeft].Set (Matrix4x4.identity);
posesOut [Zion.Constants.indexRight].Set (Matrix4x4.identity);

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引