找回密码
 注册帐号

扫一扫,访问微社区

AR酱 Magic Leap开发指南(5)-- Hand Tracking

3
回复
660
查看
[ 复制链接 ]
排名
2299
昨日变化

28

主题

249

帖子

1454

积分

Rank: 9Rank: 9Rank: 9

UID
156756
好友
11
蛮牛币
1330
威望
0
注册时间
2016-7-13
在线时间
485 小时
最后登录
2019-8-20

专栏作家

2019-7-22 14:43:52 显示全部楼层 阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 geekli 于 2019-7-22 14:43 编辑

这篇文章主要介绍关于Hand Tracking API的一些东西,帮助大家了解:

  • 使用“OK”手势旋转3D模型
  • 识别手关键点的坐标(用户指尖)


实现效果如下:
1.gif

本部分用的到资源:https://pan.baidu.com/s/1bJej22-U9-UDuhTk8vzPQA  密码:j0ek

Step 1:在Lumin Runtime Editor设置项目

1. 从Package Manager启动 Lumin Runtime Editor。

2. 在Lumin Runtime Editor中创建一个新项目。

3. 命名为HandTracking,然后单击Create Project。如下图:

4. 在Asset Library下,右键单击model,然后导入earth.fbx文件。

5. 重复步骤4,将cube模型文件也导入到项目中。

6. 在Scene层次结构下,右键单击根节点。

7. 点击Insert > Core > Model。如下图所示:


8. 将模型的Id更改为earth。

9. 在材质和模型部分,点击右边的三个点。

10. 选择earth.fbx,并将其余属性应用于场景的地球模型(重点放在External checkbox, Shader and Scale)。如下图所示:


11. 重复6-10步骤对cube操作。

12. 点击File > Save Scene。如下图:


你的场景应该是这样的:


ps:对 Lumin Runtime Editor和Package Manager 不熟悉的可以查看以前的文章(Magic Leap开发指南(1)--开发前准备 Magic Leap开发指南(2)-- Hello,Cube

Step 2:在Visual Studio (Windows)设置项目

1. 启动Microsoft Visual Studio。

2. 点击File > New > Import Magic Leap Mabu Projects。

3. 在导入Magic Leap项目中单击Browse,并选择Lumin Runtime Editor的项目文件夹。


4. 点击Import。

HandTracking项目应该如下图所示:


Step 3:在Visual Studio Code (Windows / macOS)设置项目

1. 打开Visual Studio Code。

2. 点击左侧的图标 3.jpg

3. 在Lumin SDK窗口标题中,单击图标 4.jpg ,设置Lumin SDK的路径(如果还没有设置)。通常是:/Users/user/MagicLeap/mlsdk/v0.x.x。

4. 在签名证书窗口标题中,单击图标,然后设置.cert包签名证书文件的路径(如果还没有设置)。

5. 回到Lumin Runtime Editor,在项目菜单上,单击Code Generation > Open code in External Editor。

6. 出现下面窗口时单击OK即可。




项目文件夹如下图:


Step 4:脚本处理

Source Files文件夹(或Visual Studio code中的code/src文件夹)下,打开HandTracking.cpp脚本。Lumin Runtime已经为我们生成了大量的代码。下面我们将重点讨论需要修改的部分。

Directives, Namespaces and Globals
在脚本的顶部添加以下指令,在最后一个#include指令之后:
[AppleScript] 纯文本查看 复制代码
#include <lumin/event/GestureInputEventData.h>
GestureInputEventData.h用于接收和跟踪用户手部的数据。
然后,添加以下指令和声明:
[AppleScript] 纯文本查看 复制代码
using namespace lumin;
using namespace lumin::input;

namespace {
  //consts for the rotation axis (y), the rotation angle, and the rotation interval
  const glm::vec3 y(0.0f, 1.0f, 0.0f);
  const float rotate = 0.3f;
  const float duration = 0.0f;
  //vector that will contain the position of the hand's finger tip
  glm::vec3 handPos;
  //bool variable that will turn true if the earth rotates, false otherwise
  bool OK = false;
  //bool variable that will turn true if the Finger gesture is recognized, false otherwise
  bool Finger = false;

  //Nodes to link to the 3D models
  Node* modelEarth;
  Node* modelCube;
  TransformNode* transformNodeEarth;
  TransformNode* transformNodeCube;
}
这些行声明如下:

  • 名称空间luminlumin::input用于简化代码。
  • glm: vec3 y是一个矢量,它被用来沿着正确的轴旋转地球。
  • float rotate是指每帧旋转的角度。
  • float duration指的是移动所需的时间(以秒为单位)。
  • vec3 handPos将包含手的指尖位置。
  • 当Magic Leap One识别出相关的关键姿势时,bool变量OKFinger将变为true。
  • 节点modelEarthmodelCube用于标识地球和立方体对象。
  • transformnodetransformNodeEarthtransformNodeCube被用来转换这两个模型。


glm::vec3 HandTracking::getInitialPrismSize()更改为:
[AppleScript] 纯文本查看 复制代码
const glm::vec3 HandTracking::getInitialPrismSize() const {
  return glm::vec3(5.0f, 5.0f, 5.0f);
}


int HandTracking::init()方法负责Prism的初始化(默认情况下称为prism_):
[AppleScript] 纯文本查看 复制代码
int HandTracking::init() {

  ML_LOG(Debug, "HandTracking Initializing.");

  createInitialPrism();
  lumin::ui::Cursor::SetEnabled(prism_, false); 
  spawnInitialScenes();
  
  uint32_t handGestureFlags =
    uint32_t(HandGestureFlags::kHandOk) |
    uint32_t(HandGestureFlags::kHandFinger);
  prism_->startTrackHandGesture(handGestureFlags);

  prism_->setHandGestureFilterConfidenceLevel(0.9f);

  //Find the model nodes in the scene and cast them to a TransformNode
  modelCube = prism_->findNode("cube", prism_->getRootNode());
  transformNodeCube = static_cast<lumin::TransformNode*>(modelCube);
  modelEarth = prism_->findNode("earth", prism_->getRootNode());
  transformNodeEarth = static_cast<lumin::TransformNode*>(modelEarth);

  return 0;
}
  • createInitialPrism()创建我们的Prism
  • lumin::ui::Cursor::SetEnabled(prism_, false)禁用Prism (prism_)的光标。
  • spawnInitialScenes()实例化唯一的场景。
  • uint32_t handGestureFlags存储了我们的应用程序能够识别的两个手势(kHandOkkHandFinger)。
  • prism_->startTrackHandGesture(handGestureFlags)开始跟踪手的姿势。
  • prism_-> sethandgesturefilterconfidence encelevel (0.9f)设置输入级别(包含0到1的值)。
  • modelEarth = prism_->findNode("earth", prism_->getRootNode())在场景中找到地球节点。
  • transformNodeEarth=static_cast<lumin::TransformNode*>(modelEarth) 表示并转换一个地球节点的转换。
  • modelCubetransformNodeCube的作用类似于上述。

[AppleScript] 纯文本查看 复制代码
 bool HandTracking::updateLoop(float fDelta) {
  if (OK) {
      //rotate the earth
      transformNodeEarth->rotateBy(y, rotate * fDelta, duration);
  }
  if (Finger) {    
      glm::mat4 prism_inverse_matrix = glm::inverse(prism_->getTransform());
      handPos = prism_inverse_matrix * glm::vec4(handPos, 1.0f);
      transformNodeCube->setLocalPosition(handPos);
  }
  return true;
}

  • 检查bool OK是否为真。
  • 如果是,旋转transformNodeEarth(地球)。
  • 检查bool手指是否为真。

如果识别出手指和手的姿势,我们会做以下操作:
  • prism_inverse_matrix中,我们存储了Prism变换矩阵的逆矩阵。
  • 由于Lumin运行时的坐标系,手的跟踪数据在世界空间坐标系,而我们Prism的变换将在Prism空间坐标系。我们需要做一个投影。
  • 我们计算prism_inverse_matrixeventListener捕获的用户指尖位置相关的新handPos值(下一节中会介绍到)。
  • 我们设置了这个小立方体的最终位置。

[AppleScript] 纯文本查看 复制代码
bool HandTracking::eventListener(ServerEvent* anEvent) {

  if (anEvent->isInputEventType()) {
    //ServerEventType serverEventType = anEvent->getServerEventType();
    ServerEventTypeValue serverEventType = anEvent->getServerEventTypeValue();
    if (serverEventType == GestureInputEventData::GetServerEventTypeValue()) {
      const GestureInputEventData* gestureEventData = static_cast<GestureInputEventData*>(anEvent);
      const GestureType gestureType = gestureEventData->getGesture();
      if (gestureType == GestureType::HAND_OK) {
        OK = true;
        Finger = false;
      }
      else if (gestureType == GestureType::HAND_FINGER) {
        Finger = true;
        OK = false;
        gestureEventData->getHandGestureKeypoint(HandGestureKeypointName::INDEX_FINGER_TIP, handPos.x, handPos.y, handPos.z);
      }
    }
  }
  return false;
}

此方法将每个事件捕获为ServerEvent* anEvent。如果该事件是InputEvent(即它引用输入),则:

  • 它获取serverEventTypeValue
  • 它检查事件类型是否引用手势事件。
  • 如果是,它读取手势的数据。
  • 如果手势类型是HAND_OK one,那么它将布尔变量OK变为true, Finger变为false
  • 如果手势类型是HAND_FINGER one,那么它将布尔变量OK变为false,将Finger变为true
  • gestureEventData->getHandGestureKeypoint(HandGestureKeypointName::INDEX_FINGER_TIP, handPos.x, handPos.y, handPos.z)接收用户指尖的关键点,并将其存储到handPos向量中。

Step 5:Build

构建签名.mpk文件并将其安装到设备上的过程取决于使用的IDE。
Visual Studio (Windows)

  • 使用USB-C电缆将Magic Leap One连接到计算机。
  • 选择调试配置和ML目标。
  • 切换到Magic Leap调试器。
  • 运行该应用程序。
  • 如果需要调试,单击Continue继续执行

Visual Studio Code (Windows / macOS)

  • 把你的设备插入电脑。
  • 单击左边的图标 5.jpg
  • 将调试目标设置为Lumin OS Debug
  • 单击三角形图标开始调试。


Step 6:总结
  • 如果这是你第一次在设备上部署应用程序,则会打开一个安装证书的通知。在这种情况下,接受证书安装应用程序即可。
  • 一旦证书被接受,地球模型就会出现。用OK手势让它旋转,或者用手指手势(看到你指尖上的一个小立方体)。长时间按住触发器移动整个Prism的位置。
  • 如果应用程序无法识别手的姿势,关闭所有应用程序(长时间按住Home按钮),重新启动我们的应用程序。
  • 如果你仍然什么也没看到,看看你周围的一切,因为设备启动时的方向决定了它在世界上的起始方向。

完整代码参考:
[AppleScript] 纯文本查看 复制代码
// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
//
// Copyright (c) 2018 Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Creator Agreement, located
// here: https://id.magicleap.com/creator-terms
//
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%

// %SRC_VERSION%: 1

#include <HandTracking.h>
#include <lumin/node/RootNode.h>
#include <lumin/ui/Cursor.h>
#include <ml_logging.h>
#include <scenes.h>
#include <PrismSceneManager.h>
#include <lumin/event/GestureInputEventData.h>


using namespace lumin;
using namespace lumin::input;

namespace {
  //consts for the rotation axis (y), the rotation angle, and the rotation interval
  const glm::vec3 y(0.0f, 1.0f, 0.0f);
  const float rotate = 0.3f;
  const float duration = 0.0f;
  //vector that will contain the position of the hand's finger tip
  glm::vec3 handPos;
  //bool variable that will turn true if the earth rotates, false otherwise
  bool OK = false;
  //bool variable that will turn true if the Finger gesture is recognized, false otherwise
  bool Finger = false;

  //Nodes to link to the 3D models
  Node* modelEarth;
  Node* modelCube;
  TransformNode* transformNodeEarth;
  TransformNode* transformNodeCube;
}



HandTracking::HandTracking() {
  ML_LOG(Debug, "HandTracking Constructor.");

  // Place your constructor implementation here.
}

HandTracking::~HandTracking() {
  ML_LOG(Debug, "HandTracking Destructor.");

  // Place your destructor implementation here.
}

const glm::vec3 HandTracking::getInitialPrismSize() const {
  return glm::vec3(5.0f, 5.0f, 5.0f);
}

void HandTracking::createInitialPrism() {
  prism_ = requestNewPrism(getInitialPrismSize());
  if (!prism_) {
    ML_LOG(Error, "HandTracking Error creating default prism.");
    abort();
  }
  prismSceneManager_ = new PrismSceneManager(prism_);
}

int HandTracking::init() {

  ML_LOG(Debug, "HandTracking Initializing.");

  createInitialPrism();
  lumin::ui::Cursor::SetEnabled(prism_, false);
  spawnInitialScenes();

  uint32_t handGestureFlags =
    uint32_t(HandGestureFlags::kHandOk) |
    uint32_t(HandGestureFlags::kHandFinger);
  prism_->startTrackHandGesture(handGestureFlags);

  prism_->setHandGestureFilterConfidenceLevel(0.9f);

  //Find the model nodes in the scene and cast them to a TransformNode
  modelCube = prism_->findNode("cube", prism_->getRootNode());
  transformNodeCube = static_cast<lumin::TransformNode*>(modelCube);
  modelEarth = prism_->findNode("earth", prism_->getRootNode());
  transformNodeEarth = static_cast<lumin::TransformNode*>(modelEarth);


  return 0;
}

int HandTracking::deInit() {
  ML_LOG(Debug, "HandTracking Deinitializing.");

  // Place your deinitialization here.

  return 0;
}

void HandTracking::spawnInitialScenes() {

  // Iterate over all the exported scenes
  for (auto& exportedSceneEntry : scenes::externalScenes) {

    // If this scene was marked to be instanced at app initialization, do it
    const SceneDescriptor &sd = exportedSceneEntry.second;
    if (sd.getInitiallySpawned()) {
      lumin::Node* const spawnedRoot = prismSceneManager_->spawn(sd);
      if (spawnedRoot) {
        if (!prism_->getRootNode()->addChild(spawnedRoot)) {
          ML_LOG(Error, "HandTracking Failed to add spawnedRoot to the prism root node");
          abort();
        }
      }
    }
  }
}

bool HandTracking::updateLoop(float fDelta) {
  if (OK) {
    //rotate the earth
    transformNodeEarth->rotateBy(y, rotate * fDelta, duration);
  }
  if (Finger) {
    glm::mat4 prism_inverse_matrix = glm::inverse(prism_->getTransform());
    handPos = prism_inverse_matrix * glm::vec4(handPos, 1.0f);
    transformNodeCube->setLocalPosition(handPos);
  }
  return true;
}

bool HandTracking::eventListener(ServerEvent* anEvent) {

  if (anEvent->isInputEventType()) {
    ServerEventTypeValue serverEventType = anEvent->getServerEventTypeValue();
    if (serverEventType == GestureInputEventData::GetServerEventTypeValue()) {
      const GestureInputEventData* gestureEventData = static_cast<GestureInputEventData*>(anEvent);
      const GestureType gestureType = gestureEventData->getGesture();
      if (gestureType == GestureType::HAND_OK) {
        OK = true;
        Finger = false;
      }
      else if (gestureType == GestureType::HAND_FINGER) {
        Finger = true;
        OK = false;
        gestureEventData->getHandGestureKeypoint(HandGestureKeypointName::INDEX_FINGER_TIP, handPos.x, handPos.y, handPos.z);
      }
    }
  }
  return false;
}




------AR Portal(AR开发者社区)整理
关注微信公众号:AR开发者社区  (国内领先的AR开发者交流学习社区和AR内容平台)
回复

使用道具 举报

7日久生情
2132/5000
排名
4092
昨日变化

0

主题

1406

帖子

2132

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
254705
好友
1
蛮牛币
1933
威望
0
注册时间
2017-11-16
在线时间
364 小时
最后登录
2019-8-24
2019-7-23 10:19:29 显示全部楼层
666666666666666666666666
回复 支持 反对

使用道具 举报

5熟悉之中
783/1000
排名
10706
昨日变化

0

主题

523

帖子

783

积分

Rank: 5Rank: 5

UID
301976
好友
1
蛮牛币
1171
威望
0
注册时间
2018-10-31
在线时间
162 小时
最后登录
2019-8-24
2019-7-27 13:27:26 显示全部楼层
Nice...
回复

使用道具 举报

6蛮牛粉丝
1316/1500
排名
2342
昨日变化

0

主题

320

帖子

1316

积分

Rank: 6Rank: 6Rank: 6

UID
119648
好友
3
蛮牛币
1788
威望
0
注册时间
2015-8-25
在线时间
390 小时
最后登录
2019-8-22
QQ
4 天前 显示全部楼层
Nice... 6666666666
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则