mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 05:17:02 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into pal-proto
This commit is contained in:
commit
03d3e0b550
17 changed files with 821 additions and 8 deletions
17
cmake/macros/TargetKinect.cmake
Normal file
17
cmake/macros/TargetKinect.cmake
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#
|
||||||
|
# Created by Brad Hefta-Gaub on 2016/12/7
|
||||||
|
# Copyright 2016 High Fidelity, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the Apache License, Version 2.0.
|
||||||
|
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
#
|
||||||
|
macro(TARGET_KINECT)
|
||||||
|
# Kinect SDK data reader is only available on these platforms
|
||||||
|
if (WIN32)
|
||||||
|
#add_dependency_external_projects(kinect)
|
||||||
|
find_package(Kinect REQUIRED)
|
||||||
|
target_include_directories(${TARGET_NAME} PRIVATE ${KINECT_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${TARGET_NAME} ${KINECT_LIBRARIES})
|
||||||
|
add_definitions(-DHAVE_KINECT)
|
||||||
|
endif(WIN32)
|
||||||
|
endmacro()
|
59
cmake/modules/FindKinect.cmake
Normal file
59
cmake/modules/FindKinect.cmake
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#
|
||||||
|
# FindKinect.cmake
|
||||||
|
#
|
||||||
|
# Try to find the Perception Kinect SDK
|
||||||
|
#
|
||||||
|
# You must provide a KINECT_ROOT_DIR which contains lib and include directories
|
||||||
|
#
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# KINECT_FOUND - system found Kinect SDK
|
||||||
|
# KINECT_INCLUDE_DIRS - the Kinect SDK include directory
|
||||||
|
# KINECT_LIBRARIES - Link this to use Kinect
|
||||||
|
#
|
||||||
|
# Created by Brad Hefta-Gaub on 2016/12/7
|
||||||
|
# Copyright 2016 High Fidelity, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the Apache License, Version 2.0.
|
||||||
|
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
#
|
||||||
|
|
||||||
|
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||||
|
hifi_library_search_hints("kinect")
|
||||||
|
|
||||||
|
find_path(KINECT_INCLUDE_DIRS Kinect.h PATH_SUFFIXES inc HINTS $ENV{KINECT_ROOT_DIR})
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
|
||||||
|
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||||
|
set(ARCH_DIR "x64")
|
||||||
|
else()
|
||||||
|
set(ARCH_DIR "x86")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_library(
|
||||||
|
KINECT_LIBRARY_RELEASE Kinect20
|
||||||
|
PATH_SUFFIXES "Lib/${ARCH_DIR}" "lib"
|
||||||
|
HINTS ${KINECT_SEARCH_DIRS}
|
||||||
|
PATH $ENV{KINECT_ROOT_DIR})
|
||||||
|
|
||||||
|
set(KINECT_LIBRARIES ${KINECT_LIBRARY})
|
||||||
|
|
||||||
|
# DLL not needed yet??
|
||||||
|
#find_path(KINECT_DLL_PATH Kinect20.Face.dll PATH_SUFFIXES "bin" HINTS ${KINECT_SEARCH_DIRS})
|
||||||
|
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include(SelectLibraryConfigurations)
|
||||||
|
select_library_configurations(KINECT)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(KINECT DEFAULT_MSG KINECT_INCLUDE_DIRS KINECT_LIBRARY)
|
||||||
|
|
||||||
|
# DLLs not needed yet
|
||||||
|
#if (WIN32)
|
||||||
|
# add_paths_to_fixup_libs(${KINECT_DLL_PATH})
|
||||||
|
#endif ()
|
||||||
|
|
||||||
|
mark_as_advanced(KINECT_INCLUDE_DIRS KINECT_LIBRARIES KINECT_SEARCH_DIRS)
|
7
interface/resources/controllers/kinect.json
Normal file
7
interface/resources/controllers/kinect.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "Kinect to Standard",
|
||||||
|
"channels": [
|
||||||
|
{ "from": "Kinect.LeftHand", "to": "Standard.LeftHand" },
|
||||||
|
{ "from": "Kinect.RightHand", "to": "Standard.RightHand" }
|
||||||
|
]
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ PreferencesDialog {
|
||||||
id: root
|
id: root
|
||||||
objectName: "GeneralPreferencesDialog"
|
objectName: "GeneralPreferencesDialog"
|
||||||
title: "General Settings"
|
title: "General Settings"
|
||||||
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron"]
|
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"]
|
||||||
property var settings: Settings {
|
property var settings: Settings {
|
||||||
category: root.objectName
|
category: root.objectName
|
||||||
property alias x: root.x
|
property alias x: root.x
|
||||||
|
|
|
@ -278,6 +278,12 @@ int64_t AudioInjector::injectNextFrame() {
|
||||||
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation),
|
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation),
|
||||||
sizeof(_options.orientation));
|
sizeof(_options.orientation));
|
||||||
|
|
||||||
|
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.position),
|
||||||
|
sizeof(_options.position));
|
||||||
|
glm::vec3 boxCorner = glm::vec3(0);
|
||||||
|
audioPacketStream.writeRawData(reinterpret_cast<const char*>(&boxCorner),
|
||||||
|
sizeof(glm::vec3));
|
||||||
|
|
||||||
// pack zero for radius
|
// pack zero for radius
|
||||||
float radius = 0;
|
float radius = 0;
|
||||||
audioPacketStream << radius;
|
audioPacketStream << radius;
|
||||||
|
|
|
@ -44,7 +44,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void apply(const Pose& value, const Pointer& source) override {
|
virtual void apply(const Pose& value, const Pointer& source) override {
|
||||||
if (value != Pose()) {
|
if (value != Pose() && value.isValid()) {
|
||||||
_written = true;
|
_written = true;
|
||||||
}
|
}
|
||||||
VirtualEndpoint::apply(value, source);
|
VirtualEndpoint::apply(value, source);
|
||||||
|
|
|
@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::MicrophoneAudioNoEcho:
|
case PacketType::MicrophoneAudioNoEcho:
|
||||||
case PacketType::MicrophoneAudioWithEcho:
|
case PacketType::MicrophoneAudioWithEcho:
|
||||||
case PacketType::AudioStreamStats:
|
case PacketType::AudioStreamStats:
|
||||||
return static_cast<PacketVersion>(AudioVersion::TerminatingStreamStats);
|
return static_cast<PacketVersion>(AudioVersion::SpaceBubbleChanges);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 17;
|
return 17;
|
||||||
|
|
|
@ -234,6 +234,7 @@ enum class AudioVersion : PacketVersion {
|
||||||
CodecNameInAudioPackets,
|
CodecNameInAudioPackets,
|
||||||
Exactly10msAudioPackets,
|
Exactly10msAudioPackets,
|
||||||
TerminatingStreamStats,
|
TerminatingStreamStats,
|
||||||
|
SpaceBubbleChanges,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PacketHeaders_h
|
#endif // hifi_PacketHeaders_h
|
||||||
|
|
|
@ -26,6 +26,9 @@ if (NOT SERVER_ONLY AND NOT ANDROID)
|
||||||
add_subdirectory(${DIR})
|
add_subdirectory(${DIR})
|
||||||
set(DIR "hifiNeuron")
|
set(DIR "hifiNeuron")
|
||||||
add_subdirectory(${DIR})
|
add_subdirectory(${DIR})
|
||||||
|
|
||||||
|
set(DIR "hifiKinect")
|
||||||
|
add_subdirectory(${DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# server-side plugins
|
# server-side plugins
|
||||||
|
|
19
plugins/hifiKinect/CMakeLists.txt
Normal file
19
plugins/hifiKinect/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#
|
||||||
|
# Created by Brad Hefta-Gaub on 2016/12/7
|
||||||
|
# Copyright 2016 High Fidelity, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the Apache License, Version 2.0.
|
||||||
|
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
#
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
find_package(KINECT)
|
||||||
|
if (KINECT_FOUND)
|
||||||
|
set(TARGET_NAME hifiKinect)
|
||||||
|
setup_hifi_plugin(Script Qml Widgets)
|
||||||
|
link_hifi_libraries(shared controllers ui plugins input-plugins)
|
||||||
|
|
||||||
|
# need to setup appropriate externals...
|
||||||
|
target_kinect()
|
||||||
|
endif()
|
||||||
|
endif()
|
532
plugins/hifiKinect/src/KinectPlugin.cpp
Normal file
532
plugins/hifiKinect/src/KinectPlugin.cpp
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
//
|
||||||
|
// KinectPlugin.cpp
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2016/12/7
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "KinectPlugin.h"
|
||||||
|
|
||||||
|
#include <controllers/UserInputMapper.h>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include <DebugDraw.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include <SettingHandle.h>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
|
||||||
|
Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
|
||||||
|
|
||||||
|
|
||||||
|
const char* KinectPlugin::NAME = "Kinect";
|
||||||
|
const char* KinectPlugin::KINECT_ID_STRING = "Kinect";
|
||||||
|
|
||||||
|
QStringList kinectJointNames = {
|
||||||
|
"SpineBase",
|
||||||
|
"SpineMid",
|
||||||
|
"Neck",
|
||||||
|
"Head",
|
||||||
|
"ShoulderLeft",
|
||||||
|
"ElbowLeft",
|
||||||
|
"WristLeft",
|
||||||
|
"HandLeft",
|
||||||
|
"ShoulderRight",
|
||||||
|
"ElbowRight",
|
||||||
|
"WristRight",
|
||||||
|
"HandRight",
|
||||||
|
"HipLeft",
|
||||||
|
"KneeLeft",
|
||||||
|
"AnkleLeft",
|
||||||
|
"FootLeft",
|
||||||
|
"HipRight",
|
||||||
|
"KneeRight",
|
||||||
|
"AnkleRight",
|
||||||
|
"FootRight",
|
||||||
|
"SpineShoulder",
|
||||||
|
"HandTipLeft",
|
||||||
|
"ThumbLeft",
|
||||||
|
"HandTipRight",
|
||||||
|
"ThumbRight"
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool DEFAULT_ENABLED = false;
|
||||||
|
|
||||||
|
enum KinectJointIndex {
|
||||||
|
SpineBase = 0,
|
||||||
|
SpineMid,
|
||||||
|
Neck,
|
||||||
|
Head,
|
||||||
|
|
||||||
|
ShoulderLeft,
|
||||||
|
ElbowLeft,
|
||||||
|
WristLeft,
|
||||||
|
HandLeft,
|
||||||
|
|
||||||
|
ShoulderRight,
|
||||||
|
ElbowRight,
|
||||||
|
WristRight,
|
||||||
|
HandRight,
|
||||||
|
|
||||||
|
HipLeft,
|
||||||
|
KneeLeft,
|
||||||
|
AnkleLeft,
|
||||||
|
FootLeft,
|
||||||
|
|
||||||
|
HipRight,
|
||||||
|
KneeRight,
|
||||||
|
AnkleRight,
|
||||||
|
FootRight,
|
||||||
|
|
||||||
|
SpineShoulder,
|
||||||
|
|
||||||
|
HandTipLeft,
|
||||||
|
ThumbLeft,
|
||||||
|
|
||||||
|
HandTipRight,
|
||||||
|
ThumbRight,
|
||||||
|
|
||||||
|
Size
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UNKNOWN_JOINT (controller::StandardPoseChannel)0
|
||||||
|
|
||||||
|
static controller::StandardPoseChannel KinectJointIndexToPoseIndexMap[KinectJointIndex::Size] = {
|
||||||
|
controller::HIPS,
|
||||||
|
controller::SPINE,
|
||||||
|
controller::NECK,
|
||||||
|
controller::HEAD,
|
||||||
|
|
||||||
|
controller::LEFT_SHOULDER,
|
||||||
|
controller::LEFT_ARM,
|
||||||
|
controller::LEFT_FORE_ARM,
|
||||||
|
controller::LEFT_HAND,
|
||||||
|
|
||||||
|
controller::RIGHT_SHOULDER,
|
||||||
|
controller::RIGHT_ARM,
|
||||||
|
controller::RIGHT_FORE_ARM,
|
||||||
|
controller::RIGHT_HAND,
|
||||||
|
|
||||||
|
controller::RIGHT_UP_LEG, // hip socket
|
||||||
|
controller::RIGHT_LEG, // knee?
|
||||||
|
controller::RIGHT_FOOT, // ankle?
|
||||||
|
UNKNOWN_JOINT, // ????
|
||||||
|
|
||||||
|
controller::LEFT_UP_LEG, // hip socket
|
||||||
|
controller::LEFT_LEG, // knee?
|
||||||
|
controller::LEFT_FOOT, // ankle?
|
||||||
|
UNKNOWN_JOINT, // ????
|
||||||
|
|
||||||
|
UNKNOWN_JOINT, /* SpineShoulder */
|
||||||
|
|
||||||
|
controller::LEFT_HAND_INDEX4,
|
||||||
|
controller::LEFT_HAND_THUMB4,
|
||||||
|
|
||||||
|
controller::RIGHT_HAND_INDEX4,
|
||||||
|
controller::RIGHT_HAND_THUMB4,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// in rig frame
|
||||||
|
static glm::vec3 rightHandThumb1DefaultAbsTranslation(-2.155500650405884, -0.7610001564025879, 2.685631036758423);
|
||||||
|
static glm::vec3 leftHandThumb1DefaultAbsTranslation(2.1555817127227783, -0.7603635787963867, 2.6856393814086914);
|
||||||
|
|
||||||
|
static controller::StandardPoseChannel KinectJointIndexToPoseIndex(KinectJointIndex i) {
|
||||||
|
assert(i >= 0 && i < KinectJointIndex::Size);
|
||||||
|
if (i >= 0 && i < KinectJointIndex::Size) {
|
||||||
|
return KinectJointIndexToPoseIndexMap[i];
|
||||||
|
} else {
|
||||||
|
return UNKNOWN_JOINT; // not sure what to do here, but don't crash!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList controllerJointNames = {
|
||||||
|
"Hips",
|
||||||
|
"RightUpLeg",
|
||||||
|
"RightLeg",
|
||||||
|
"RightFoot",
|
||||||
|
"LeftUpLeg",
|
||||||
|
"LeftLeg",
|
||||||
|
"LeftFoot",
|
||||||
|
"Spine",
|
||||||
|
"Spine1",
|
||||||
|
"Spine2",
|
||||||
|
"Spine3",
|
||||||
|
"Neck",
|
||||||
|
"Head",
|
||||||
|
"RightShoulder",
|
||||||
|
"RightArm",
|
||||||
|
"RightForeArm",
|
||||||
|
"RightHand",
|
||||||
|
"RightHandThumb1",
|
||||||
|
"RightHandThumb2",
|
||||||
|
"RightHandThumb3",
|
||||||
|
"RightHandThumb4",
|
||||||
|
"RightHandIndex1",
|
||||||
|
"RightHandIndex2",
|
||||||
|
"RightHandIndex3",
|
||||||
|
"RightHandIndex4",
|
||||||
|
"RightHandMiddle1",
|
||||||
|
"RightHandMiddle2",
|
||||||
|
"RightHandMiddle3",
|
||||||
|
"RightHandMiddle4",
|
||||||
|
"RightHandRing1",
|
||||||
|
"RightHandRing2",
|
||||||
|
"RightHandRing3",
|
||||||
|
"RightHandRing4",
|
||||||
|
"RightHandPinky1",
|
||||||
|
"RightHandPinky2",
|
||||||
|
"RightHandPinky3",
|
||||||
|
"RightHandPinky4",
|
||||||
|
"LeftShoulder",
|
||||||
|
"LeftArm",
|
||||||
|
"LeftForeArm",
|
||||||
|
"LeftHand",
|
||||||
|
"LeftHandThumb1",
|
||||||
|
"LeftHandThumb2",
|
||||||
|
"LeftHandThumb3",
|
||||||
|
"LeftHandThumb4",
|
||||||
|
"LeftHandIndex1",
|
||||||
|
"LeftHandIndex2",
|
||||||
|
"LeftHandIndex3",
|
||||||
|
"LeftHandIndex4",
|
||||||
|
"LeftHandMiddle1",
|
||||||
|
"LeftHandMiddle2",
|
||||||
|
"LeftHandMiddle3",
|
||||||
|
"LeftHandMiddle4",
|
||||||
|
"LeftHandRing1",
|
||||||
|
"LeftHandRing2",
|
||||||
|
"LeftHandRing3",
|
||||||
|
"LeftHandRing4",
|
||||||
|
"LeftHandPinky1",
|
||||||
|
"LeftHandPinky2",
|
||||||
|
"LeftHandPinky3",
|
||||||
|
"LeftHandPinky4"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* getControllerJointName(controller::StandardPoseChannel i) {
|
||||||
|
if (i >= 0 && i < controller::NUM_STANDARD_POSES) {
|
||||||
|
return qPrintable(controllerJointNames[i]);
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// KinectPlugin
|
||||||
|
//
|
||||||
|
void KinectPlugin::init() {
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
auto preferences = DependencyManager::get<Preferences>();
|
||||||
|
static const QString KINECT_PLUGIN { "Kinect" };
|
||||||
|
{
|
||||||
|
auto getter = [this]()->bool { return _enabled; };
|
||||||
|
auto setter = [this](bool value) { _enabled = value; saveSettings(); };
|
||||||
|
auto preference = new CheckPreference(KINECT_PLUGIN, "Enabled", getter, setter);
|
||||||
|
preferences->addPreference(preference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KinectPlugin::isSupported() const {
|
||||||
|
// FIXME -- check to see if there's a camera or not...
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KinectPlugin::activate() {
|
||||||
|
InputPlugin::activate();
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
if (_enabled) {
|
||||||
|
|
||||||
|
// register with userInputMapper
|
||||||
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
|
userInputMapper->registerDevice(_inputDevice);
|
||||||
|
|
||||||
|
return initializeDefaultSensor();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KinectPlugin::initializeDefaultSensor() {
|
||||||
|
#ifdef HAVE_KINECT
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
hr = GetDefaultKinectSensor(&_kinectSensor);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_kinectSensor) {
|
||||||
|
// Initialize the Kinect and get coordinate mapper and the body reader
|
||||||
|
IBodyFrameSource* bodyFrameSource = NULL;
|
||||||
|
|
||||||
|
hr = _kinectSensor->Open();
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
hr = _kinectSensor->get_CoordinateMapper(&_coordinateMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
hr = _kinectSensor->get_BodyFrameSource(&bodyFrameSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
hr = bodyFrameSource->OpenReader(&_bodyFrameReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeRelease(bodyFrameSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_kinectSensor || FAILED(hr)) {
|
||||||
|
qDebug() << "No ready Kinect found!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Kinect found WOOT!";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void KinectPlugin::updateBody() {
|
||||||
|
#ifndef HAVE_KINECT
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
if (!_bodyFrameReader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IBodyFrame* pBodyFrame = NULL;
|
||||||
|
|
||||||
|
HRESULT hr = _bodyFrameReader->AcquireLatestFrame(&pBodyFrame);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
INT64 nTime = 0;
|
||||||
|
hr = pBodyFrame->get_RelativeTime(&nTime);
|
||||||
|
IBody* bodies[BODY_COUNT] = {0};
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
hr = pBodyFrame->GetAndRefreshBodyData(_countof(bodies), bodies);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
ProcessBody(nTime, BODY_COUNT, bodies);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _countof(bodies); ++i) {
|
||||||
|
SafeRelease(bodies[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeRelease(pBodyFrame);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_KINECT
|
||||||
|
void KinectPlugin::ProcessBody(INT64 time, int bodyCount, IBody** bodies) {
|
||||||
|
bool foundOneBody = false;
|
||||||
|
if (_coordinateMapper) {
|
||||||
|
for (int i = 0; i < bodyCount; ++i) {
|
||||||
|
if (foundOneBody) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
IBody* body = bodies[i];
|
||||||
|
if (body) {
|
||||||
|
BOOLEAN tracked = false;
|
||||||
|
HRESULT hr = body->get_IsTracked(&tracked);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && tracked) {
|
||||||
|
foundOneBody = true;
|
||||||
|
|
||||||
|
if (_joints.size() != JointType_Count) {
|
||||||
|
_joints.resize(JointType_Count, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0.0f } });
|
||||||
|
}
|
||||||
|
|
||||||
|
Joint joints[JointType_Count];
|
||||||
|
JointOrientation jointOrientations[JointType_Count];
|
||||||
|
HandState leftHandState = HandState_Unknown;
|
||||||
|
HandState rightHandState = HandState_Unknown;
|
||||||
|
|
||||||
|
body->get_HandLeftState(&leftHandState);
|
||||||
|
body->get_HandRightState(&rightHandState);
|
||||||
|
|
||||||
|
hr = body->GetJoints(_countof(joints), joints);
|
||||||
|
hr = body->GetJointOrientations(_countof(jointOrientations), jointOrientations);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
auto jointCount = _countof(joints);
|
||||||
|
//qDebug() << __FUNCTION__ << "nBodyCount:" << nBodyCount << "body:" << i << "jointCount:" << jointCount;
|
||||||
|
for (int j = 0; j < jointCount; ++j) {
|
||||||
|
//QString jointName = kinectJointNames[joints[j].JointType];
|
||||||
|
|
||||||
|
glm::vec3 jointPosition { joints[j].Position.X,
|
||||||
|
joints[j].Position.Y,
|
||||||
|
joints[j].Position.Z };
|
||||||
|
|
||||||
|
// Kinect Documentation is unclear on what these orientations are, are they absolute?
|
||||||
|
// or are the relative to the parent bones. It appears as if it has changed between the
|
||||||
|
// older 1.x SDK and the 2.0 sdk
|
||||||
|
//
|
||||||
|
// https://social.msdn.microsoft.com/Forums/en-US/31c9aff6-7dab-433d-9af9-59942dfd3d69/kinect-v20-preview-sdk-jointorientation-vs-boneorientation?forum=kinectv2sdk
|
||||||
|
// seems to suggest these are absolute...
|
||||||
|
// "These quaternions are absolute, so you can take a mesh in local space, transform it by the quaternion,
|
||||||
|
// and it will match the exact orientation of the bone. If you want relative orientation quaternion, you
|
||||||
|
// can multiply the absolute quaternion by the inverse of the parent joint's quaternion."
|
||||||
|
//
|
||||||
|
// - Bone direction(Y green) - always matches the skeleton.
|
||||||
|
// - Normal(Z blue) - joint roll, perpendicular to the bone
|
||||||
|
// - Binormal(X orange) - perpendicular to the bone and normal
|
||||||
|
|
||||||
|
glm::quat jointOrientation { jointOrientations[j].Orientation.x,
|
||||||
|
jointOrientations[j].Orientation.y,
|
||||||
|
jointOrientations[j].Orientation.z,
|
||||||
|
jointOrientations[j].Orientation.w };
|
||||||
|
|
||||||
|
// filling in the _joints data...
|
||||||
|
if (joints[j].TrackingState != TrackingState_NotTracked) {
|
||||||
|
_joints[j].position = jointPosition;
|
||||||
|
//_joints[j].orientation = jointOrientation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void KinectPlugin::deactivate() {
|
||||||
|
// unregister from userInputMapper
|
||||||
|
if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) {
|
||||||
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
|
userInputMapper->removeDevice(_inputDevice->_deviceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputPlugin::deactivate();
|
||||||
|
saveSettings();
|
||||||
|
|
||||||
|
#ifdef HAVE_KINECT
|
||||||
|
// done with body frame reader
|
||||||
|
SafeRelease(_bodyFrameReader);
|
||||||
|
|
||||||
|
// done with coordinate mapper
|
||||||
|
SafeRelease(_coordinateMapper);
|
||||||
|
|
||||||
|
// close the Kinect Sensor
|
||||||
|
if (_kinectSensor) {
|
||||||
|
_kinectSensor->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeRelease(_kinectSensor);
|
||||||
|
#endif // HAVE_KINECT
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void KinectPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
|
||||||
|
|
||||||
|
if (!_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBody(); // updates _joints
|
||||||
|
|
||||||
|
std::vector<KinectJoint> joints = _joints;
|
||||||
|
|
||||||
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
|
userInputMapper->withLock([&, this]() {
|
||||||
|
_inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints);
|
||||||
|
});
|
||||||
|
|
||||||
|
_prevJoints = joints;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KinectPlugin::saveSettings() const {
|
||||||
|
Settings settings;
|
||||||
|
QString idString = getID();
|
||||||
|
settings.beginGroup(idString);
|
||||||
|
{
|
||||||
|
settings.setValue(QString("enabled"), _enabled);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KinectPlugin::loadSettings() {
|
||||||
|
Settings settings;
|
||||||
|
QString idString = getID();
|
||||||
|
settings.beginGroup(idString);
|
||||||
|
{
|
||||||
|
// enabled
|
||||||
|
_enabled = settings.value("enabled", QVariant(DEFAULT_ENABLED)).toBool();
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// InputDevice
|
||||||
|
//
|
||||||
|
|
||||||
|
// FIXME - we probably should only return the inputs we ACTUALLY have
|
||||||
|
controller::Input::NamedVector KinectPlugin::InputDevice::getAvailableInputs() const {
|
||||||
|
static controller::Input::NamedVector availableInputs;
|
||||||
|
if (availableInputs.size() == 0) {
|
||||||
|
for (int i = 0; i < KinectJointIndex::Size; i++) {
|
||||||
|
auto channel = KinectJointIndexToPoseIndex(static_cast<KinectJointIndex>(i));
|
||||||
|
availableInputs.push_back(makePair(channel, getControllerJointName(channel)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return availableInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString KinectPlugin::InputDevice::getDefaultMappingConfig() const {
|
||||||
|
static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/kinect.json";
|
||||||
|
return MAPPING_JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KinectPlugin::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
|
||||||
|
const std::vector<KinectPlugin::KinectJoint>& joints, const std::vector<KinectPlugin::KinectJoint>& prevJoints) {
|
||||||
|
|
||||||
|
glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
||||||
|
glm::quat controllerToAvatarRotation = glmExtractRotation(controllerToAvatar);
|
||||||
|
|
||||||
|
vec3 kinectHipPos;
|
||||||
|
if (joints.size() > JointType_SpineBase) {
|
||||||
|
kinectHipPos = joints[JointType_SpineBase].position;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < joints.size(); i++) {
|
||||||
|
int poseIndex = KinectJointIndexToPoseIndex((KinectJointIndex)i);
|
||||||
|
glm::vec3 linearVel, angularVel;
|
||||||
|
|
||||||
|
// Adjust the position to be hip (avatar) relative, and rotated to match the avatar rotation
|
||||||
|
const glm::vec3& pos = controllerToAvatarRotation * (joints[i].position - kinectHipPos);
|
||||||
|
|
||||||
|
if (Vectors::ZERO == pos) {
|
||||||
|
_poseStateMap[poseIndex] = controller::Pose();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME - determine the correct orientation transform
|
||||||
|
glm::quat rot = joints[i].orientation;
|
||||||
|
|
||||||
|
if (i < prevJoints.size()) {
|
||||||
|
linearVel = (pos - (prevJoints[i].position * METERS_PER_CENTIMETER)) / deltaTime; // m/s
|
||||||
|
// quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation.
|
||||||
|
glm::quat d = glm::log(rot * glm::inverse(prevJoints[i].orientation));
|
||||||
|
angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s
|
||||||
|
}
|
||||||
|
|
||||||
|
_poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVel, angularVel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
117
plugins/hifiKinect/src/KinectPlugin.h
Normal file
117
plugins/hifiKinect/src/KinectPlugin.h
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// KinectPlugin.h
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2016/12/7
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_KinectPlugin_h
|
||||||
|
#define hifi_KinectPlugin_h
|
||||||
|
|
||||||
|
#ifdef HAVE_KINECT
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Windows Header Files
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <Shlobj.h>
|
||||||
|
|
||||||
|
// Kinect Header files
|
||||||
|
#include <Kinect.h>
|
||||||
|
|
||||||
|
// Safe release for interfaces
|
||||||
|
template<class Interface> inline void SafeRelease(Interface *& pInterfaceToRelease) {
|
||||||
|
if (pInterfaceToRelease != NULL) {
|
||||||
|
pInterfaceToRelease->Release();
|
||||||
|
pInterfaceToRelease = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <controllers/InputDevice.h>
|
||||||
|
#include <controllers/StandardControls.h>
|
||||||
|
#include <plugins/InputPlugin.h>
|
||||||
|
|
||||||
|
// Handles interaction with the Kinect SDK
|
||||||
|
class KinectPlugin : public InputPlugin {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
bool isHandController() const override { return true; }
|
||||||
|
|
||||||
|
// Plugin functions
|
||||||
|
virtual void init() override;
|
||||||
|
virtual bool isSupported() const override;
|
||||||
|
virtual const QString getName() const override { return NAME; }
|
||||||
|
const QString getID() const override { return KINECT_ID_STRING; }
|
||||||
|
|
||||||
|
virtual bool activate() override;
|
||||||
|
virtual void deactivate() override;
|
||||||
|
|
||||||
|
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||||
|
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
|
|
||||||
|
virtual void saveSettings() const override;
|
||||||
|
virtual void loadSettings() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
struct KinectJoint {
|
||||||
|
glm::vec3 position;
|
||||||
|
glm::quat orientation;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputDevice : public controller::InputDevice {
|
||||||
|
public:
|
||||||
|
friend class KinectPlugin;
|
||||||
|
|
||||||
|
InputDevice() : controller::InputDevice("Kinect") {}
|
||||||
|
|
||||||
|
// Device functions
|
||||||
|
virtual controller::Input::NamedVector getAvailableInputs() const override;
|
||||||
|
virtual QString getDefaultMappingConfig() const override;
|
||||||
|
virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override {};
|
||||||
|
virtual void focusOutEvent() override {};
|
||||||
|
|
||||||
|
void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData,
|
||||||
|
const std::vector<KinectPlugin::KinectJoint>& joints, const std::vector<KinectPlugin::KinectJoint>& prevJoints);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };
|
||||||
|
|
||||||
|
static const char* NAME;
|
||||||
|
static const char* KINECT_ID_STRING;
|
||||||
|
|
||||||
|
bool _enabled;
|
||||||
|
|
||||||
|
// copy of data directly from the KinectDataReader SDK
|
||||||
|
std::vector<KinectJoint> _joints;
|
||||||
|
|
||||||
|
// one frame old copy of _joints, used to caluclate angular and linear velocity.
|
||||||
|
std::vector<KinectJoint> _prevJoints;
|
||||||
|
|
||||||
|
|
||||||
|
// Kinect SDK related items...
|
||||||
|
|
||||||
|
bool KinectPlugin::initializeDefaultSensor();
|
||||||
|
void updateBody();
|
||||||
|
|
||||||
|
#ifdef HAVE_KINECT
|
||||||
|
void ProcessBody(INT64 time, int bodyCount, IBody** bodies);
|
||||||
|
|
||||||
|
// Current Kinect
|
||||||
|
IKinectSensor* _kinectSensor { nullptr };
|
||||||
|
ICoordinateMapper* _coordinateMapper { nullptr };
|
||||||
|
|
||||||
|
// Body reader
|
||||||
|
IBodyFrameReader* _bodyFrameReader { nullptr };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_KinectPlugin_h
|
||||||
|
|
49
plugins/hifiKinect/src/KinectProvider.cpp
Normal file
49
plugins/hifiKinect/src/KinectProvider.cpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2016/12/7
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QtPlugin>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
|
#include <plugins/RuntimePlugin.h>
|
||||||
|
#include <plugins/InputPlugin.h>
|
||||||
|
|
||||||
|
#include "KinectPlugin.h"
|
||||||
|
|
||||||
|
class KinectProvider : public QObject, public InputProvider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json")
|
||||||
|
Q_INTERFACES(InputProvider)
|
||||||
|
|
||||||
|
public:
|
||||||
|
KinectProvider(QObject* parent = nullptr) : QObject(parent) {}
|
||||||
|
virtual ~KinectProvider() {}
|
||||||
|
|
||||||
|
virtual InputPluginList getInputPlugins() override {
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [&] {
|
||||||
|
InputPluginPointer plugin(new KinectPlugin());
|
||||||
|
if (plugin->isSupported()) {
|
||||||
|
_inputPlugins.push_back(plugin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return _inputPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void destroyInputPlugins() override {
|
||||||
|
_inputPlugins.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
InputPluginList _inputPlugins;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "KinectProvider.moc"
|
1
plugins/hifiKinect/src/plugin.json
Normal file
1
plugins/hifiKinect/src/plugin.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"name":"Kinect"}
|
BIN
scripts/system/assets/models/Bubble-v14.fbx
Normal file
BIN
scripts/system/assets/models/Bubble-v14.fbx
Normal file
Binary file not shown.
Binary file not shown.
|
@ -23,11 +23,13 @@
|
||||||
var bubbleButtonFlashState = false;
|
var bubbleButtonFlashState = false;
|
||||||
// Used for flashing the HUD button upon activation
|
// Used for flashing the HUD button upon activation
|
||||||
var bubbleButtonTimestamp;
|
var bubbleButtonTimestamp;
|
||||||
|
// Affects bubble height
|
||||||
|
const BUBBLE_HEIGHT_SCALE = 0.15;
|
||||||
// The bubble model itself
|
// The bubble model itself
|
||||||
var bubbleOverlay = Overlays.addOverlay("model", {
|
var bubbleOverlay = Overlays.addOverlay("model", {
|
||||||
url: Script.resolvePath("assets/models/bubble-v12.fbx"), // If you'd like to change the model, modify this line (and the dimensions below)
|
url: Script.resolvePath("assets/models/Bubble-v14.fbx"), // If you'd like to change the model, modify this line (and the dimensions below)
|
||||||
dimensions: { x: 1.0, y: 0.75, z: 1.0 },
|
dimensions: { x: 1.0, y: 0.75, z: 1.0 },
|
||||||
position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * 0.28, z: MyAvatar.position.z },
|
position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z },
|
||||||
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
||||||
scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 },
|
scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 },
|
||||||
visible: false,
|
visible: false,
|
||||||
|
@ -71,7 +73,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlays.editOverlay(bubbleOverlay, {
|
Overlays.editOverlay(bubbleOverlay, {
|
||||||
position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * 0.28, z: MyAvatar.position.z },
|
position: { x: MyAvatar.position.x, y: -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, z: MyAvatar.position.z },
|
||||||
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
||||||
scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 },
|
scale: { x: 2, y: MyAvatar.scale * 0.5 + 0.5, z: 2 },
|
||||||
visible: true
|
visible: true
|
||||||
|
@ -112,7 +114,7 @@
|
||||||
// Quickly raise the bubble from the ground up
|
// Quickly raise the bubble from the ground up
|
||||||
position: {
|
position: {
|
||||||
x: MyAvatar.position.x,
|
x: MyAvatar.position.x,
|
||||||
y: (-((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * 0.28,
|
y: (-((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE,
|
||||||
z: MyAvatar.position.z
|
z: MyAvatar.position.z
|
||||||
},
|
},
|
||||||
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
||||||
|
@ -127,7 +129,7 @@
|
||||||
Overlays.editOverlay(bubbleOverlay, {
|
Overlays.editOverlay(bubbleOverlay, {
|
||||||
position: {
|
position: {
|
||||||
x: MyAvatar.position.x,
|
x: MyAvatar.position.x,
|
||||||
y: MyAvatar.position.y + MyAvatar.scale * 0.28,
|
y: MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE,
|
||||||
z: MyAvatar.position.z
|
z: MyAvatar.position.z
|
||||||
},
|
},
|
||||||
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
rotation: Quat.fromPitchYawRollDegrees(MyAvatar.bodyPitch, 0, MyAvatar.bodyRoll),
|
||||||
|
|
Loading…
Reference in a new issue