找回密碼
 注冊帳號

掃一掃,訪問微社區

AR醬 Magic Leap開發指南(5)-- Hand Tracking

2
回復
544
查看
打印 上一主題 下一主題
[ 復制鏈接 ]
排名
2299
昨日變化

27

主題

248

帖子

1451

積分

Rank: 9Rank: 9Rank: 9

UID
156756
好友
11
蠻牛幣
1325
威望
0
注冊時間
2016-7-13
在線時間
484 小時
最后登錄
2019-8-10

專欄作家

馬上注冊,結交更多好友,享用更多功能,讓你輕松玩轉社區。

您需要 登錄 才可以下載或查看,沒有帳號?注冊帳號

x
本帖最后由 geekli 于 2019-7-22 14:43 編輯

這篇文章主要介紹關于Hand Tracking API的一些東西,幫助大家了解:

  • 使用“OK”手勢旋轉3D模型
  • 識別手關鍵點的坐標(用戶指尖)


實現效果如下:


本部分用的到資源: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. 在Lumin SDK窗口標題中,單擊圖標 ,設置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)

  • 把你的設備插入電腦。
  • 單擊左邊的圖標
  • 將調試目標設置為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日久生情
2075/5000
排名
4092
昨日變化

0

主題

1356

帖子

2075

積分

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

UID
254705
好友
1
蠻牛幣
1889
威望
0
注冊時間
2017-11-16
在線時間
357 小時
最后登錄
2019-8-13
沙發
2019-7-23 10:19:29 只看該作者
666666666666666666666666
回復 支持 反對

使用道具 舉報

5熟悉之中
721/1000
排名
10706
昨日變化

0

主題

470

帖子

721

積分

Rank: 5Rank: 5

UID
301976
好友
1
蠻牛幣
1077
威望
0
注冊時間
2018-10-31
在線時間
153 小時
最后登錄
2019-8-13
板凳
2019-7-27 13:27:26 只看該作者
Nice...
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 注冊帳號

本版積分規則

捕鱼王怎么进不去 吉林时时票查询 pk10彩票平台网站 pc蛋蛋网页计划 11选5前2直选技巧 极速北京pk赛车开结果 江苏快三计划软件 广东时时11选五开奖结果查 时时彩奖金9.98的平台 吉林时时走势 时时彩后一必中规律 极速赛车全天计划 一分快三的秘籍 北京pk拾直播开奖结果 极速6合网址 永利爆大奖平台 冠亚大