3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-27 15:35:30 +02:00

Merge branch 'master' into feature/secondary-pose-support

This commit is contained in:
Anthony J. Thibault 2017-07-27 09:09:32 -07:00
commit 78c6564174
61 changed files with 1013 additions and 165 deletions
BUILD_WIN.mdCMakeLists.txt
cmake
domain-server/src
interface
libraries
plugins
scripts/system
tools/atp-client/src
unpublishedScripts
DomainContent/Cupcake
gravity.js
marketplace/dart

View file

@ -1,42 +1,45 @@
This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.
## Building High Fidelity
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
### Step 1. Installing Visual Studio 2013
Note: The prerequisites will require about 10 GB of space on your drive.
If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer.
### Step 1. Visual Studio 2017
Note: Newer versions of Visual Studio are not yet compatible.
If you dont have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/).
When selecting components, check "Desktop development with C++." Also check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)" on the Summary toolbar on the right.
### Step 2. Installing CMake
Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
Download and install the latest version of CMake 3.9. Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). Make sure to check "Add CMake to system PATH for all users" when prompted during installation.
### Step 3. Installing Qt
Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe).
Download and install the [Qt Online Installer](https://www.qt.io/download-open-source/?hsCtaTracking=f977210e-de67-475f-a32b-65cec207fd03%7Cd62710cd-e1db-46aa-8d4d-2f1c1ffdacea). While installing, you only need to have the following components checked under Qt 5.9.1: "msvc2017 64-bit", "Qt WebEngine", and "Qt Script (Deprecated)".
Keep the default components checked when going through the installer.
Note: Installing the Sources is optional but recommended if you have room for them (~2GB).
### Step 4. Setting Qt Environment Variable
Go to `Control Panel > System > Advanced System Settings > Environment Variables > New...` (or search “Environment Variables” in Start Search).
* Set "Variable name": `QT_CMAKE_PREFIX_PATH`
* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake`
* Set "Variable value": `C:\Qt\5.9.1\msvc2017_64\lib\cmake`
### Step 5. Installing OpenSSL
Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe).
Download and install the Win64 OpenSSL v1.0.2 Installer[https://slproweb.com/products/Win32OpenSSL.html].
### Step 6. Running CMake to Generate Build Files
Run Command Prompt from Start and run the following commands:
````
```
cd "%HIFI_DIR%"
mkdir build
cd build
cmake .. -G "Visual Studio 12 Win64"
````
cmake .. -G "Visual Studio 15 Win64"
```
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
@ -72,14 +75,6 @@ For any problems after Step #6, first try this:
Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory.
#### nmake cannot be found
Make sure nmake.exe is located at the following path:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin
If not, add the directory where nmake is located to the PATH environment variable.
#### Qt is throwing an error
Make sure you have the correct version (5.6.2) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.
Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly.

View file

@ -1,4 +1,8 @@
cmake_minimum_required(VERSION 3.2)
if (WIN32)
cmake_minimum_required(VERSION 3.7)
else()
cmake_minimum_required(VERSION 3.2)
endif()
if (USE_ANDROID_TOOLCHAIN)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/android/android.toolchain.cmake")
@ -33,6 +37,10 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
find_package( Threads )
if (WIN32)
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
message( FATAL_ERROR "Only 64 bit builds supported." )
endif()
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
if (NOT WINDOW_SDK_PATH)
@ -41,16 +49,13 @@ if (WIN32)
# sets path for Microsoft SDKs
# if you get build error about missing 'glu32' this path is likely wrong
if (MSVC10)
set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 " CACHE PATH "Windows SDK PATH")
elseif (MSVC12)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(WINDOW_SDK_FOLDER "x64")
else()
set(WINDOW_SDK_FOLDER "x86")
endif()
if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017
set(WINDOW_SDK_PATH "C:/Program Files (x86)/Windows Kits/10/Lib/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64" CACHE PATH "Windows SDK PATH")
elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}" CACHE PATH "Windows SDK PATH")
endif ()
else()
message( FATAL_ERROR "Visual Studio 2013 or higher required." )
endif()
if (DEBUG_DISCOVERED_SDK_PATH)
message(STATUS "The discovered Windows SDK path is ${WINDOW_SDK_PATH}")
@ -103,7 +108,7 @@ else ()
endif ()
if (APPLE)
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++0x")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
endif ()

View file

@ -21,8 +21,8 @@ endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip
URL_MD5 2955176048a31262c09259ca8d309d19
URL https://hifi-public.s3.amazonaws.com/dependencies/quazip-0.7.3.zip
URL_MD5 ed03754d39b9da1775771819b8001d45
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
CMAKE_ARGS ${QUAZIP_CMAKE_ARGS}
LOG_DOWNLOAD 1

View file

@ -126,8 +126,14 @@ macro(SET_PACKAGING_PARAMETERS)
# check if we need to find signtool
if (PRODUCTION_BUILD OR PR_BUILD)
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64")
if (MSVC_VERSION GREATER_EQUAL 1910) # VS 2017
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/10" PATH_SUFFIXES "bin/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/x64")
elseif (MSVC_VERSION GREATER_EQUAL 1800) # VS 2013
find_program(SIGNTOOL_EXECUTABLE signtool PATHS "C:/Program Files (x86)/Windows Kits/8.1" PATH_SUFFIXES "bin/x64")
else()
message( FATAL_ERROR "Visual Studio 2013 or higher required." )
endif()
if (NOT SIGNTOOL_EXECUTABLE)
message(FATAL_ERROR "Code signing of executables was requested but signtool.exe could not be found.")
endif ()

View file

@ -385,7 +385,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
// user is attempting to prove their identity to us, but we don't have enough information
sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
// ask for their public key right now to make sure we have it
requestUserPublicKey(username);
requestUserPublicKey(username, true);
getGroupMemberships(username); // optimistically get started on group memberships
#ifdef WANT_DEBUG
qDebug() << "stalling login because we have no username-signature:" << username;
@ -521,7 +521,10 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
const HifiSockAddr& senderSockAddr) {
// it's possible this user can be allowed to connect, but we need to check their username signature
auto lowerUsername = username.toLower();
QByteArray publicKeyArray = _userPublicKeys.value(lowerUsername);
KeyFlagPair publicKeyPair = _userPublicKeys.value(lowerUsername);
QByteArray publicKeyArray = publicKeyPair.first;
bool isOptimisticKey = publicKeyPair.second;
const QUuid& connectionToken = _connectionTokenHash.value(lowerUsername);
@ -555,10 +558,16 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
return true;
} else {
if (!senderSockAddr.isNull()) {
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
// we only send back a LoginError if this wasn't an "optimistic" key
// (a key that we hoped would work but is probably stale)
if (!senderSockAddr.isNull() && !isOptimisticKey) {
qDebug() << "Error decrypting username signature for" << username << "- denying connection.";
sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr,
DomainHandler::ConnectionRefusedReason::LoginError);
} else if (!senderSockAddr.isNull()) {
qDebug() << "Error decrypting username signature for" << username << "with optimisitic key -"
<< "re-requesting public key and delaying connection";
}
// free up the public key, we don't need it anymore
@ -604,20 +613,7 @@ bool DomainGatekeeper::isWithinMaxCapacity() {
return true;
}
void DomainGatekeeper::preloadAllowedUserPublicKeys() {
QStringList allowedUsers = _server->_settingsManager.getAllNames();
if (allowedUsers.size() > 0) {
// in the future we may need to limit how many requests here - for now assume that lists of allowed users are not
// going to create > 100 requests
foreach(const QString& username, allowedUsers) {
requestUserPublicKey(username);
}
}
}
void DomainGatekeeper::requestUserPublicKey(const QString& username) {
void DomainGatekeeper::requestUserPublicKey(const QString& username, bool isOptimistic) {
// don't request public keys for the standard psuedo-account-names
if (NodePermissions::standardNames.contains(username, Qt::CaseInsensitive)) {
return;
@ -628,7 +624,7 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) {
// public-key request for this username is already flight, not rerequesting
return;
}
_inFlightPublicKeyRequests += lowerUsername;
_inFlightPublicKeyRequests.insert(lowerUsername, isOptimistic);
// even if we have a public key for them right now, request a new one in case it has just changed
JSONCallbackParameters callbackParams;
@ -640,7 +636,7 @@ void DomainGatekeeper::requestUserPublicKey(const QString& username) {
const QString USER_PUBLIC_KEY_PATH = "api/v1/users/%1/public_key";
qDebug() << "Requesting public key for user" << username;
qDebug().nospace() << "Requesting " << (isOptimistic ? "optimistic " : " ") << "public key for user " << username;
DependencyManager::get<AccountManager>()->sendRequest(USER_PUBLIC_KEY_PATH.arg(username),
AccountManagerAuth::None,
@ -662,16 +658,21 @@ void DomainGatekeeper::publicKeyJSONCallback(QNetworkReply& requestReply) {
QJsonObject jsonObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QString username = extractUsernameFromPublicKeyRequest(requestReply);
bool isOptimisticKey = _inFlightPublicKeyRequests.take(username);
if (jsonObject["status"].toString() == "success" && !username.isEmpty()) {
// pull the public key as a QByteArray from this response
const QString JSON_DATA_KEY = "data";
const QString JSON_PUBLIC_KEY_KEY = "public_key";
_userPublicKeys[username.toLower()] =
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8());
}
qDebug().nospace() << "Extracted " << (isOptimisticKey ? "optimistic " : " ") << "public key for " << username.toLower();
_inFlightPublicKeyRequests.remove(username);
_userPublicKeys[username.toLower()] =
{
QByteArray::fromBase64(jsonObject[JSON_DATA_KEY].toObject()[JSON_PUBLIC_KEY_KEY].toString().toUtf8()),
isOptimisticKey
};
}
}
void DomainGatekeeper::publicKeyJSONErrorCallback(QNetworkReply& requestReply) {
@ -927,9 +928,12 @@ void DomainGatekeeper::getDomainOwnerFriendsList() {
callbackParams.errorCallbackMethod = "getDomainOwnerFriendsListErrorCallback";
const QString GET_FRIENDS_LIST_PATH = "api/v1/user/friends";
DependencyManager::get<AccountManager>()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required,
QNetworkAccessManager::GetOperation, callbackParams, QByteArray(),
NULL, QVariantMap());
if (DependencyManager::get<AccountManager>()->hasValidAccessToken()) {
DependencyManager::get<AccountManager>()->sendRequest(GET_FRIENDS_LIST_PATH, AccountManagerAuth::Required,
QNetworkAccessManager::GetOperation, callbackParams, QByteArray(),
NULL, QVariantMap());
}
}
void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply& requestReply) {

View file

@ -39,8 +39,6 @@ public:
const QUuid& walletUUID, const QString& nodeVersion);
QUuid assignmentUUIDForPendingAssignment(const QUuid& tempUUID);
void preloadAllowedUserPublicKeys();
void removeICEPeer(const QUuid& peerUUID) { _icePeers.remove(peerUUID); }
static void sendProtocolMismatchConnectionDenial(const HifiSockAddr& senderSockAddr);
@ -93,7 +91,7 @@ private:
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
void requestUserPublicKey(const QString& username);
void requestUserPublicKey(const QString& username, bool isOptimistic = false);
DomainServer* _server;
@ -102,8 +100,17 @@ private:
QHash<QUuid, SharedNetworkPeer> _icePeers;
QHash<QString, QUuid> _connectionTokenHash;
QHash<QString, QByteArray> _userPublicKeys;
QSet<QString> _inFlightPublicKeyRequests; // keep track of which we've already asked for
// the word "optimistic" below is used for keys that we request during user connection before the user has
// had a chance to upload a new public key
// we don't send back user signature decryption errors for those keys so that there isn't a thrasing of key re-generation
// and connection refusal
using KeyFlagPair = QPair<QByteArray, bool>;
QHash<QString, KeyFlagPair> _userPublicKeys; // keep track of keys and flag them as optimistic or not
QHash<QString, bool> _inFlightPublicKeyRequests; // keep track of keys we've asked for (and if it was optimistic)
QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner
QSet<QString> _inFlightGroupMembershipsRequests; // keep track of which we've already asked for

View file

@ -160,9 +160,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
getTemporaryName();
}
_gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
//send signal to DomainMetadata when descriptors changed
// send signal to DomainMetadata when descriptors changed
_metadata = new DomainMetadata(this);
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
_metadata, &DomainMetadata::descriptorsChanged);

View file

@ -1,6 +1,15 @@
{
"name": "Vive to Standard",
"channels": [
{ "from": "Vive.LY", "to": "Standard.LeftIndexPoint",
"peek": true,
"filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ]
},
{ "from": "Vive.RY", "to": "Standard.RightIndexPoint",
"peek": true,
"filters": [ { "type": "hysteresis", "min": 0.7, "max": 0.75 } ]
},
{ "from": "Vive.LY", "when": "Vive.LSY", "filters": ["invert"], "to": "Standard.LY" },
{ "from": "Vive.LX", "when": "Vive.LSX", "to": "Standard.LX" },
{
@ -13,6 +22,10 @@
{ "from": "Vive.LeftGrip", "to": "Standard.LeftGrip" },
{ "from": "Vive.LS", "to": "Standard.LS" },
{ "from": "Vive.LSTouch", "to": "Standard.LeftThumbUp",
"peek": true,
"filters": [ { "type": "logicalNot" } ]
},
{ "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
{ "from": "Vive.RY", "when": "Vive.RSY", "filters": ["invert"], "to": "Standard.RY" },
@ -27,6 +40,10 @@
{ "from": "Vive.RightGrip", "to": "Standard.RightGrip" },
{ "from": "Vive.RS", "to": "Standard.RS" },
{ "from": "Vive.RSTouch", "to": "Standard.RightThumbUp",
"peek": true,
"filters": [ { "type": "logicalNot" } ]
},
{ "from": "Vive.RSTouch", "to": "Standard.RSTouch" },
{ "from": "Vive.LSCenter", "to": "Standard.LeftPrimaryThumb" },
@ -59,7 +76,7 @@
{ "from": "Vive.Head", "to" : "Standard.Head"},
{ "from": "Vive.RightArm", "to" : "Standard.RightArm"},
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm"}
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }
]
}

View file

@ -151,13 +151,17 @@ ScrollingWindow {
var SHAPE_TYPE_SIMPLE_HULL = 1;
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
var SHAPE_TYPE_STATIC_MESH = 3;
var SHAPE_TYPE_BOX = 4;
var SHAPE_TYPE_SPHERE = 5;
var SHAPE_TYPES = [];
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
var DYNAMIC_DEFAULT = false;
var prompt = desktop.customInputDialog({
@ -196,6 +200,12 @@ ScrollingWindow {
case SHAPE_TYPE_STATIC_MESH:
shapeType = "static-mesh";
break;
case SHAPE_TYPE_BOX:
shapeType = "box";
break;
case SHAPE_TYPE_SPHERE:
shapeType = "sphere";
break;
default:
shapeType = "none";
}

View file

@ -0,0 +1,71 @@
//
// RadioButton.qml
//
// Created by Cain Kilgore on 20th July 2017
// Copyright 2017 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
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
import "../controls-uit" as HifiControls
Original.RadioButton {
id: radioButton
HifiConstants { id: hifi }
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
readonly property int boxSize: 14
readonly property int boxRadius: 3
readonly property int checkSize: 10
readonly property int checkRadius: 2
style: RadioButtonStyle {
indicator: Rectangle {
id: box
width: boxSize
height: boxSize
radius: 7
gradient: Gradient {
GradientStop {
position: 0.2
color: pressed || hovered
? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkStart : hifi.colors.checkboxLightStart)
: (radioButton.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart)
}
GradientStop {
position: 1.0
color: pressed || hovered
? (radioButton.isLightColorScheme ? hifi.colors.checkboxDarkFinish : hifi.colors.checkboxLightFinish)
: (radioButton.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
}
}
Rectangle {
id: check
width: checkSize
height: checkSize
radius: 7
anchors.centerIn: parent
color: "#00B4EF"
border.width: 1
border.color: "#36CDFF"
visible: checked && !pressed || !checked && pressed
}
}
label: RalewaySemiBold {
text: control.text
size: hifi.fontSizes.inputLabel
color: isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText
x: radioButton.boxSize / 2
}
}
}

View file

@ -0,0 +1,17 @@
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import "../styles"
import "."
Original.RadioButton {
text: "Radio Button"
style: RadioButtonStyle {
indicator: FontAwesome {
text: control.checked ? "\uf046" : "\uf096"
}
label: Text {
text: control.text
renderType: Text.QtRendering
}
}
}

View file

@ -0,0 +1,101 @@
//
// PrimaryHandPreference.qml
//
// Created by Cain Kilgore on 20th July 2017
// Copyright 2017 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
//
import QtQuick 2.5
import "../../controls-uit"
Preference {
id: root
property alias box1: box1
property alias box2: box2
height: control.height + hifi.dimensions.controlInterlineHeight
Component.onCompleted: {
if (preference.value == "left") {
box1.checked = true;
} else {
box2.checked = true;
}
}
function save() {
if (box1.checked && !box2.checked) {
preference.value = "left";
}
if (!box1.checked && box2.checked) {
preference.value = "right";
}
preference.save();
}
Item {
id: control
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: Math.max(labelName.height, box1.height, box2.height)
Label {
id: labelName
text: root.label + ":"
colorScheme: hifi.colorSchemes.dark
anchors {
left: parent.left
right: box1.left
rightMargin: hifi.dimensions.labelPadding
verticalCenter: parent.verticalCenter
}
horizontalAlignment: Text.AlignRight
wrapMode: Text.Wrap
}
RadioButton {
id: box1
text: "Left"
width: 60
anchors {
right: box2.left
verticalCenter: parent.verticalCenter
}
onClicked: {
if (box2.checked) {
box2.checked = false;
}
if (!box1.checked && !box2.checked) {
box1.checked = true;
}
}
colorScheme: hifi.colorSchemes.dark
}
RadioButton {
id: box2
text: "Right"
width: 60
anchors {
right: parent.right
verticalCenter: parent.verticalCenter
}
onClicked: {
if (box1.checked) {
box1.checked = false;
}
if (!box1.checked && !box2.checked) {
box2.checked = true;
}
}
colorScheme: hifi.colorSchemes.dark
}
}
}

View file

@ -73,6 +73,7 @@ Preference {
property var buttonBuilder: Component { ButtonPreference { } }
property var comboBoxBuilder: Component { ComboBoxPreference { } }
property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } }
property var primaryHandBuilder: Component { PrimaryHandPreference { } }
property var preferences: []
property int checkBoxCount: 0
@ -134,6 +135,11 @@ Preference {
checkBoxCount = 0;
builder = spinnerSliderBuilder;
break;
case Preference.PrimaryHand:
checkBoxCount++;
builder = primaryHandBuilder;
break;
};
if (builder) {

View file

@ -17,7 +17,7 @@ PreferencesDialog {
id: root
objectName: "GeneralPreferencesDialog"
title: "General Settings"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
property var settings: Settings {
category: root.objectName
property alias x: root.x

View file

@ -152,13 +152,17 @@ Rectangle {
var SHAPE_TYPE_SIMPLE_HULL = 1;
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
var SHAPE_TYPE_STATIC_MESH = 3;
var SHAPE_TYPE_BOX = 4;
var SHAPE_TYPE_SPHERE = 5;
var SHAPE_TYPES = [];
SHAPE_TYPES[SHAPE_TYPE_NONE] = "No Collision";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
var DYNAMIC_DEFAULT = false;
var prompt = tabletRoot.customInputDialog({
@ -197,6 +201,12 @@ Rectangle {
case SHAPE_TYPE_STATIC_MESH:
shapeType = "static-mesh";
break;
case SHAPE_TYPE_BOX:
shapeType = "box";
break;
case SHAPE_TYPE_SPHERE:
shapeType = "sphere";
break;
default:
shapeType = "none";
}

View file

@ -145,7 +145,9 @@ Rectangle {
model: ["No Collision",
"Basic - Whole model",
"Good - Sub-meshes",
"Exact - All polygons"]
"Exact - All polygons",
"Box",
"Sphere"]
}
Row {

View file

@ -32,6 +32,6 @@ StackView {
TabletPreferencesDialog {
id: root
objectName: "TabletGeneralPreferences"
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration", "Leap Motion"]
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Game Controller", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
}
}

View file

@ -82,6 +82,7 @@ Preference {
property var buttonBuilder: Component { ButtonPreference { } }
property var comboBoxBuilder: Component { ComboBoxPreference { } }
property var spinnerSliderBuilder: Component { SpinnerSliderPreference { } }
property var primaryHandBuilder: Component { PrimaryHandPreference { } }
property var preferences: []
property int checkBoxCount: 0
@ -144,10 +145,16 @@ Preference {
//to be not overlapped when drop down is active
zpos = root.z + 1000 - itemNum
break;
case Preference.SpinnerSlider:
checkBoxCount = 0;
builder = spinnerSliderBuilder;
break;
case Preference.PrimaryHand:
checkBoxCount++;
builder = primaryHandBuilder;
break;
};
if (builder) {

View file

@ -255,6 +255,12 @@ MyAvatar::~MyAvatar() {
_lookAtTargetAvatar.reset();
}
void MyAvatar::setDominantHand(const QString& hand) {
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
_dominantHand = hand;
}
}
void MyAvatar::registerMetaTypes(QScriptEngine* engine) {
QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
engine->globalObject().setProperty("MyAvatar", value);
@ -926,6 +932,7 @@ void MyAvatar::saveData() {
Settings settings;
settings.beginGroup("Avatar");
settings.setValue("dominantHand", _dominantHand);
settings.setValue("headPitch", getHead()->getBasePitch());
settings.setValue("scale", _targetScale);
@ -1122,7 +1129,7 @@ void MyAvatar::loadData() {
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool());
setClearOverlayWhenMoving(settings.value("clearOverlayWhenMoving", _clearOverlayWhenMoving).toBool());
setDominantHand(settings.value("dominantHand", _dominantHand).toString().toLower());
settings.endGroup();
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));

View file

@ -43,6 +43,7 @@ enum AudioListenerMode {
FROM_CAMERA,
CUSTOM
};
Q_DECLARE_METATYPE(AudioListenerMode);
class MyAvatar : public Avatar {
@ -141,6 +142,9 @@ class MyAvatar : public Avatar {
Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone)
Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate)
const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right";
public:
enum DriveKeys {
TRANSLATE_X = 0,
@ -339,6 +343,9 @@ public:
Q_INVOKABLE bool getClearOverlayWhenMoving() const { return _clearOverlayWhenMoving; }
Q_INVOKABLE void setClearOverlayWhenMoving(bool on) { _clearOverlayWhenMoving = on; }
Q_INVOKABLE void setDominantHand(const QString& hand);
Q_INVOKABLE QString getDominantHand() const { return _dominantHand; }
Q_INVOKABLE void setHMDLeanRecenterEnabled(bool value) { _hmdLeanRecenterEnabled = value; }
Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; }
@ -424,7 +431,6 @@ public:
Q_INVOKABLE QString getFullAvatarModelName() const { return _fullAvatarModelName; }
void resetFullAvatarURL();
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
MyCharacterController* getCharacterController() { return &_characterController; }
@ -683,6 +689,7 @@ private:
QUrl _fstAnimGraphOverrideUrl;
bool _useSnapTurn { true };
bool _clearOverlayWhenMoving { true };
QString _dominantHand { DOMINANT_RIGHT_HAND };
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg
const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg

View file

@ -181,6 +181,11 @@ void setupPreferences() {
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString { return myAvatar->getDominantHand(); };
auto setter = [=](const QString& value) { myAvatar->setDominantHand(value); };
preferences->addPreference(new PrimaryHandPreference(AVATAR_TUNING, "Dominant Hand", getter, setter));
}
{
auto getter = [=]()->float { return myAvatar->getUniformScale(); };
auto setter = [=](float value) { myAvatar->setTargetScale(value); };
@ -300,17 +305,6 @@ void setupPreferences() {
preferences->addPreference(preference);
}
{
auto getter = []()->float { return controller::InputDevice::getReticleMoveSpeed(); };
auto setter = [](float value) { controller::InputDevice::setReticleMoveSpeed(value); };
auto preference = new SpinnerPreference("Sixense Controllers", "Reticle movement speed", getter, setter);
preference->setMin(0);
preference->setMax(100);
preference->setStep(1);
preferences->addPreference(preference);
}
{
static const QString RENDER("Graphics");
auto renderConfig = qApp->getRenderEngine()->getConfiguration();

View file

@ -15,20 +15,6 @@
namespace controller {
const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
//Constants for getCursorPixelRangeMultiplier()
const float MIN_PIXEL_RANGE_MULT = 0.4f;
const float MAX_PIXEL_RANGE_MULT = 2.0f;
const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
//Returns a multiplier to be applied to the cursor range for the controllers
float InputDevice::getCursorPixelRangeMult() {
//scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
}
float InputDevice::getButton(int channel) const {
if (!_buttonPressedMap.empty()) {
if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {

View file

@ -73,10 +73,6 @@ public:
int getDeviceID() { return _deviceID; }
void setDeviceID(int deviceID) { _deviceID = deviceID; }
static float getCursorPixelRangeMult();
static float getReticleMoveSpeed() { return _reticleMoveSpeed; }
static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; }
Input makeInput(StandardButtonChannel button) const;
Input makeInput(StandardAxisChannel axis) const;
Input makeInput(StandardPoseChannel pose) const;
@ -99,9 +95,6 @@ protected:
ButtonPressedMap _buttonPressedMap;
AxisStateMap _axisStateMap;
PoseStateMap _poseStateMap;
private:
static float _reticleMoveSpeed;
};
}

View file

@ -22,6 +22,7 @@
#include "filters/DeadZoneFilter.h"
#include "filters/HysteresisFilter.h"
#include "filters/InvertFilter.h"
#include "filters/NotFilter.h"
#include "filters/PulseFilter.h"
#include "filters/ScaleFilter.h"
#include "filters/TranslateFilter.h"
@ -40,6 +41,7 @@ REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPos
REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone")
REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
REGISTER_FILTER_CLASS_INSTANCE(NotFilter, "logicalNot")
REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate")

View file

@ -24,6 +24,7 @@
#include "filters/DeadZoneFilter.h"
#include "filters/HysteresisFilter.h"
#include "filters/InvertFilter.h"
#include "filters/NotFilter.h"
#include "filters/PulseFilter.h"
#include "filters/ScaleFilter.h"
#include "filters/TranslateFilter.h"
@ -148,6 +149,11 @@ QObject* RouteBuilderProxy::pulse(float interval) {
return this;
}
QObject* RouteBuilderProxy::logicalNot() {
addFilter(std::make_shared<NotFilter>());
return this;
}
void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
_route->filters.push_back(filter);
}

View file

@ -53,6 +53,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
Q_INVOKABLE QObject* rotate(glm::quat rotation);
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
Q_INVOKABLE QObject* logicalNot();
private:
void to(const Endpoint::Pointer& destination);

View file

@ -0,0 +1,10 @@
#include "NotFilter.h"
using namespace controller;
NotFilter::NotFilter() {
}
float NotFilter::apply(float value) const {
return (value == 0.0f) ? 1.0f : 0.0f;
}

View file

@ -0,0 +1,21 @@
#pragma once
#ifndef hifi_Controllers_Filters_Not_h
#define hifi_Controllers_Filters_Not_h
#include "../Filter.h"
namespace controller {
class NotFilter : public Filter {
REGISTER_FILTER_CLASS(NotFilter);
public:
NotFilter();
virtual float apply(float value) const override;
virtual Pose apply(Pose value) const override { return value; }
};
}
#endif

View file

@ -23,6 +23,7 @@
#include <PerfStat.h>
#include <render/Scene.h>
#include <DependencyManager.h>
#include <shared/QtHelpers.h>
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
@ -1282,3 +1283,11 @@ void RenderableModelEntityItem::mapJoints(const QStringList& modelJointNames) {
}
}
}
bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) {
if (!_model || !_model->isLoaded()) {
return false;
}
BLOCKING_INVOKE_METHOD(_model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result));
return !result.isEmpty();
}

View file

@ -116,6 +116,8 @@ public:
return _animation;
}
bool getMeshes(MeshProxyList& result) override;
private:
QVariantMap parseTexturesToMap(QString textures);
void remapTextures();

View file

@ -1663,6 +1663,7 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
// the mesh will be in voxel-space. transform it into object-space
meshProxy = new SimpleMeshProxy(
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[=](glm::vec3 color){ return color; },
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
[&](uint32_t index){ return index; }));
result << meshProxy;

View file

@ -182,7 +182,6 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);
EntityPropertyFlags requestedProperties = getEntityProperties(params);
EntityPropertyFlags propertiesDidntFit = requestedProperties;
// If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item,
// then our entityTreeElementExtraEncodeData should include data about which properties we need to append.
@ -190,6 +189,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
requestedProperties = entityTreeElementExtraEncodeData->entities.value(getEntityItemID());
}
EntityPropertyFlags propertiesDidntFit = requestedProperties;
LevelDetails entityLevel = packetData->startLevel();
quint64 lastEdited = getLastEdited();

View file

@ -1736,9 +1736,7 @@ glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) {
if (entity) {
glm::mat4 translation = glm::translate(entity->getPosition());
glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT -
entity->getRegistrationPoint());
result = translation * rotation * registration;
result = translation * rotation;
}
});
}
@ -1753,9 +1751,7 @@ glm::mat4 EntityScriptingInterface::getEntityLocalTransform(const QUuid& entityI
if (entity) {
glm::mat4 translation = glm::translate(entity->getLocalPosition());
glm::mat4 rotation = glm::mat4_cast(entity->getLocalOrientation());
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT -
entity->getRegistrationPoint());
result = translation * rotation * registration;
result = translation * rotation;
}
});
}

View file

@ -118,7 +118,7 @@ glm::vec3 OBJTokenizer::getVec3() {
auto z = getFloat();
auto v = glm::vec3(x, y, z);
while (isNextTokenFloat()) {
// the spec(s) get(s) vague here. might be w, might be a color... chop it off.
// ignore any following floats
nextToken();
}
return v;
@ -139,7 +139,7 @@ bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) {
// only a single value) that it's a vertex color.
r = getFloat();
if (isNextTokenFloat()) {
// Safe to assume the following values are the green/blue components.
// Safe to assume the following values are the green/blue components.
g = getFloat();
b = getFloat();
@ -351,6 +351,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
bool result = true;
int originalFaceCountForDebugging = 0;
QString currentGroup;
bool anyVertexColor { false };
int vertexCount { 0 };
setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.count()));
@ -412,14 +414,25 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
#endif
}
} else if (token == "v") {
glm::vec3 vertex, vertexColor;
glm::vec3 vertex;
glm::vec3 vertexColor { glm::vec3(1.0f) };
bool hasVertexColor = tokenizer.getVertex(vertex, vertexColor);
vertices.append(vertex);
if(hasVertexColor) {
// if any vertex has color, they all need to.
if (hasVertexColor && !anyVertexColor) {
// we've had a gap of zero or more vertices without color, followed
// by one that has color. catch up:
for (int i = 0; i < vertexCount; i++) {
vertexColors.append(glm::vec3(1.0f));
}
anyVertexColor = true;
}
if (anyVertexColor) {
vertexColors.append(vertexColor);
}
vertexCount++;
} else if (token == "vn") {
normals.append(tokenizer.getVec3());
} else if (token == "vt") {

View file

@ -40,8 +40,6 @@ static QString formatFloat(double n) {
}
bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
// each mesh's vertices are numbered from zero. We're combining all their vertices into one list here,
// so keep track of the start index for each mesh.
QList<int> meshVertexStartOffset;
@ -49,10 +47,15 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
int currentVertexStartOffset = 0;
int currentNormalStartOffset = 0;
// write out vertices
// write out vertices (and maybe colors)
foreach (const MeshPointer& mesh, meshes) {
meshVertexStartOffset.append(currentVertexStartOffset);
const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer();
const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(gpu::Stream::COLOR);
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
gpu::BufferView::Index colorIndex = 0;
int vertexCount = 0;
gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertexBuffer.cbegin<const glm::vec3>();
while (vertexItr != vertexBuffer.cend<const glm::vec3>()) {
@ -60,7 +63,15 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
out << "v ";
out << formatFloat(v[0]) << " ";
out << formatFloat(v[1]) << " ";
out << formatFloat(v[2]) << "\n";
out << formatFloat(v[2]);
if (colorIndex < numColors) {
glm::vec3 color = colorsBufferView.get<glm::vec3>(colorIndex);
out << " " << formatFloat(color[0]);
out << " " << formatFloat(color[1]);
out << " " << formatFloat(color[2]);
colorIndex++;
}
out << "\n";
vertexItr++;
vertexCount++;
}
@ -72,7 +83,7 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
bool haveNormals = true;
foreach (const MeshPointer& mesh, meshes) {
meshNormalStartOffset.append(currentNormalStartOffset);
const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal);
const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
glm::vec3 normal = normalsBufferView.get<glm::vec3>(i);

View file

@ -95,7 +95,7 @@ void GL41Backend::updateInput() {
// GLenum perLocationStride = strides[bufferNum];
GLenum perLocationStride = attrib._element.getLocationSize();
GLuint stride = (GLuint)strides[bufferNum];
GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]);
uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]);
GLboolean isNormalized = attrib._element.isNormalized();
for (size_t locNum = 0; locNum < locationCount; ++locNum) {

View file

@ -192,7 +192,7 @@ public:
BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT);
BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT);
Size getNumElements() const { return (_size - _offset) / _stride; }
Size getNumElements() const { return _size / _stride; }
//Template iterator with random access on the buffer sysmem
template<typename T>

View file

@ -11,8 +11,6 @@
#include "Geometry.h"
#include <QDebug>
using namespace model;
Mesh::Mesh() :
@ -136,11 +134,13 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const {
model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
std::function<glm::vec3(glm::vec3)> colorFunc,
std::function<glm::vec3(glm::vec3)> normalFunc,
std::function<uint32_t(uint32_t)> indexFunc) {
std::function<uint32_t(uint32_t)> indexFunc) const {
// vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3);
unsigned char* resultVertexData = new unsigned char[vertexSize];
unsigned char* vertexDataCursor = resultVertexData;
@ -151,6 +151,21 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
vertexDataCursor += sizeof(pos);
}
// color data
int attributeTypeColor = gpu::Stream::COLOR;
const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor);
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
gpu::Resource::Size colorSize = numColors * sizeof(glm::vec3);
unsigned char* resultColorData = new unsigned char[colorSize];
unsigned char* colorDataCursor = resultColorData;
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i));
memcpy(colorDataCursor, &color, sizeof(color));
colorDataCursor += sizeof(color);
}
// normal data
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
@ -187,6 +202,12 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement);
result->setVertexBuffer(resultVertexBufferView);
gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData);
gpu::BufferPointer resultColorsBufferPointer(resultColorsBuffer);
gpu::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement);
result->addAttribute(attributeTypeColor, resultColorsBufferView);
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData);
gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer);
@ -215,6 +236,7 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
std::function<void(glm::vec3)> colorFunc,
std::function<void(glm::vec3)> normalFunc,
std::function<void(uint32_t)> indexFunc) {
// vertex data
@ -224,6 +246,14 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
vertexFunc(vertexBufferView.get<glm::vec3>(i));
}
// color data
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor);
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
colorFunc(colorsBufferView.get<glm::vec3>(i));
}
// normal data
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);

View file

@ -123,10 +123,12 @@ public:
// create a copy of this mesh after passing its vertices, normals, and indexes though the provided functions
MeshPointer map(std::function<glm::vec3(glm::vec3)> vertexFunc,
std::function<glm::vec3(glm::vec3)> colorFunc,
std::function<glm::vec3(glm::vec3)> normalFunc,
std::function<uint32_t(uint32_t)> indexFunc);
std::function<uint32_t(uint32_t)> indexFunc) const;
void forEach(std::function<void(glm::vec3)> vertexFunc,
std::function<void(glm::vec3)> colorFunc,
std::function<void(glm::vec3)> normalFunc,
std::function<void(uint32_t)> indexFunc);

View file

@ -327,7 +327,7 @@ void NodeList::sendDomainServerCheckIn() {
<< "but no keypair is present. Waiting for keypair generation to complete.";
accountManager->generateNewUserKeypair();
// don't send the check in packet - wait for the keypair first
// don't send the check in packet - wait for the new public key to be available to the domain-server first
return;
}

View file

@ -24,6 +24,7 @@
#include <PerfStat.h>
#include <ViewFrustum.h>
#include <GLMHelpers.h>
#include <model-networking/SimpleMeshProxy.h>
#include "AbstractViewStateInterface.h"
#include "MeshPartPayload.h"
@ -462,6 +463,41 @@ bool Model::convexHullContains(glm::vec3 point) {
return false;
}
MeshProxyList Model::getMeshes() const {
MeshProxyList result;
const Geometry::Pointer& renderGeometry = getGeometry();
const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes();
if (!isLoaded()) {
return result;
}
Transform offset;
offset.setScale(_scale);
offset.postTranslate(_offset);
glm::mat4 offsetMat = offset.getMatrix();
for (std::shared_ptr<const model::Mesh> mesh : meshes) {
if (!mesh) {
continue;
}
MeshProxy* meshProxy = new SimpleMeshProxy(
mesh->map(
[=](glm::vec3 position) {
return glm::vec3(offsetMat * glm::vec4(position, 1.0f));
},
[=](glm::vec3 color) { return color; },
[=](glm::vec3 normal) {
return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f)));
},
[&](uint32_t index) { return index; }));
result << meshProxy;
}
return result;
}
void Model::calculateTriangleSets() {
PROFILE_RANGE(render, __FUNCTION__);

View file

@ -257,6 +257,8 @@ public:
int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); }
int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); }
Q_INVOKABLE MeshProxyList getMeshes() const;
public slots:
void loadURLFinished(bool success);

View file

@ -38,16 +38,22 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) {
QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
// figure out the size of the resulting mesh
size_t totalVertexCount { 0 };
size_t totalAttributeCount { 0 };
size_t totalColorCount { 0 };
size_t totalNormalCount { 0 };
size_t totalIndexCount { 0 };
foreach (const MeshProxy* meshProxy, in) {
MeshPointer mesh = meshProxy->getMeshPointer();
totalVertexCount += mesh->getNumVertices();
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(attributeTypeColor);
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
totalColorCount += numColors;
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
totalAttributeCount += numNormals;
totalNormalCount += numNormals;
totalIndexCount += mesh->getNumIndices();
}
@ -57,7 +63,11 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
unsigned char* combinedVertexData = new unsigned char[combinedVertexSize];
unsigned char* combinedVertexDataCursor = combinedVertexData;
gpu::Resource::Size combinedNormalSize = totalAttributeCount * sizeof(glm::vec3);
gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3);
unsigned char* combinedColorData = new unsigned char[combinedColorSize];
unsigned char* combinedColorDataCursor = combinedColorData;
gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3);
unsigned char* combinedNormalData = new unsigned char[combinedNormalSize];
unsigned char* combinedNormalDataCursor = combinedNormalData;
@ -74,6 +84,10 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
memcpy(combinedVertexDataCursor, &position, sizeof(position));
combinedVertexDataCursor += sizeof(position);
},
[&](glm::vec3 color){
memcpy(combinedColorDataCursor, &color, sizeof(color));
combinedColorDataCursor += sizeof(color);
},
[&](glm::vec3 normal){
memcpy(combinedNormalDataCursor, &normal, sizeof(normal));
combinedNormalDataCursor += sizeof(normal);
@ -96,6 +110,13 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement);
result->setVertexBuffer(combinedVertexBufferView);
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData);
gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer);
gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement);
result->addAttribute(attributeTypeColor, combinedColorsBufferView);
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData);
@ -132,12 +153,48 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro
}
model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[&](glm::vec3 color){ return color; },
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
[&](uint32_t index){ return index; });
MeshProxy* resultProxy = new SimpleMeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);
}
QScriptValue ModelScriptingInterface::getVertexCount(MeshProxy* meshProxy) {
if (!meshProxy) {
return QScriptValue(false);
}
MeshPointer mesh = meshProxy->getMeshPointer();
if (!mesh) {
return QScriptValue(false);
}
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices();
return numVertices;
}
QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertexIndex) {
if (!meshProxy) {
return QScriptValue(false);
}
MeshPointer mesh = meshProxy->getMeshPointer();
if (!mesh) {
return QScriptValue(false);
}
const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices();
if (vertexIndex < 0 || vertexIndex >= numVertices) {
return QScriptValue(false);
}
glm::vec3 pos = vertexBufferView.get<glm::vec3>(vertexIndex);
return vec3toScriptValue(_modelScriptEngine, pos);
}
QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices,
const QVector<glm::vec3>& normals,
const QVector<MeshFace>& faces) {

View file

@ -29,6 +29,8 @@ public:
Q_INVOKABLE QScriptValue newMesh(const QVector<glm::vec3>& vertices,
const QVector<glm::vec3>& normals,
const QVector<MeshFace>& faces);
Q_INVOKABLE QScriptValue getVertexCount(MeshProxy* meshProxy);
Q_INVOKABLE QScriptValue getVertex(MeshProxy* meshProxy, int vertexIndex);
private:
QScriptEngine* _modelScriptEngine { nullptr };

View file

@ -56,6 +56,7 @@ public:
Checkbox,
Button,
ComboBox,
PrimaryHand,
// Special casing for an unusual preference
Avatar
};
@ -339,6 +340,14 @@ public:
Type getType() override { return Checkbox; }
};
class PrimaryHandPreference : public StringPreference {
Q_OBJECT
public:
PrimaryHandPreference(const QString& category, const QString& name, Getter getter, Setter setter)
: StringPreference(category, name, getter, setter) { }
Type getType() override { return PrimaryHand; }
};
#endif

View file

@ -92,9 +92,8 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) {
FileStorage::~FileStorage() {
if (_mapped) {
if (!_file.unmap(_mapped)) {
throw std::runtime_error("Unable to unmap file");
}
_file.unmap(_mapped);
_mapped = nullptr;
}
if (_file.isOpen()) {
_file.close();

View file

@ -367,6 +367,12 @@ void NeuronPlugin::init() {
auto preferences = DependencyManager::get<Preferences>();
static const QString NEURON_PLUGIN { "Perception Neuron" };
{
auto getter = [this]()->bool { return _enabled; };
auto setter = [this](bool value) { _enabled = value; saveSettings(); };
auto preference = new CheckPreference(NEURON_PLUGIN, "Enabled", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = [this]()->QString { return _serverAddress; };
auto setter = [this](const QString& value) { _serverAddress = value; saveSettings(); };
@ -387,12 +393,6 @@ void NeuronPlugin::init() {
preference->setStep(1);
preferences->addPreference(preference);
}
{
auto getter = [this]()->bool { return _enabled; };
auto setter = [this](bool value) { _enabled = value; saveSettings(); };
auto preference = new CheckPreference(NEURON_PLUGIN, "Enabled", getter, setter);
preferences->addPreference(preference);
}
}
bool NeuronPlugin::isSupported() const {
@ -455,6 +455,10 @@ void NeuronPlugin::deactivate() {
}
void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
if (!_enabled) {
return;
}
std::vector<NeuronJoint> joints;
{
// lock and copy

View file

@ -11,8 +11,10 @@
#include <qapplication.h>
#include <PerfStat.h>
#include <controllers/UserInputMapper.h>
#include <PerfStat.h>
#include <Preferences.h>
#include <SettingHandle.h>
#include "SDL2Manager.h"
@ -38,10 +40,13 @@ static_assert(
(int)controller::RY == (int)SDL_CONTROLLER_AXIS_RIGHTY &&
(int)controller::LT == (int)SDL_CONTROLLER_AXIS_TRIGGERLEFT &&
(int)controller::RT == (int)SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
"SDL2 equvalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h");
"SDL2 equivalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h");
const char* SDL2Manager::NAME = "SDL2";
const char* SDL2Manager::SDL2_ID_STRING = "SDL2";
const bool DEFAULT_ENABLED = false;
SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
@ -49,6 +54,20 @@ SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) {
}
void SDL2Manager::init() {
loadSettings();
auto preferences = DependencyManager::get<Preferences>();
static const QString SDL2_PLUGIN { "Game Controller" };
{
auto getter = [this]()->bool { return _isEnabled; };
auto setter = [this](bool value) {
_isEnabled = value;
saveSettings();
};
auto preference = new CheckPreference(SDL2_PLUGIN, "Enabled", getter, setter);
preferences->addPreference(preference);
}
bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == 0);
if (initSuccess) {
@ -110,6 +129,27 @@ void SDL2Manager::deactivate() {
InputPlugin::deactivate();
}
const char* SETTINGS_ENABLED_KEY = "enabled";
void SDL2Manager::saveSettings() const {
Settings settings;
QString idString = getID();
settings.beginGroup(idString);
{
settings.setValue(QString(SETTINGS_ENABLED_KEY), _isEnabled);
}
settings.endGroup();
}
void SDL2Manager::loadSettings() {
Settings settings;
QString idString = getID();
settings.beginGroup(idString);
{
_isEnabled = settings.value(SETTINGS_ENABLED_KEY, QVariant(DEFAULT_ENABLED)).toBool();
}
settings.endGroup();
}
bool SDL2Manager::isSupported() const {
return true;
@ -122,6 +162,10 @@ void SDL2Manager::pluginFocusOutEvent() {
}
void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
if (!_isEnabled) {
return;
}
if (_isInitialized) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
for (auto joystick : _openJoysticks) {

View file

@ -25,6 +25,7 @@ public:
// Plugin functions
bool isSupported() const override;
const QString getName() const override { return NAME; }
const QString getID() const override { return SDL2_ID_STRING; }
QStringList getSubdeviceNames() override;
@ -39,6 +40,9 @@ public:
void pluginFocusOutEvent() override;
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
virtual void saveSettings() const override;
virtual void loadSettings() override;
signals:
void joystickAdded(Joystick* joystick);
void joystickRemoved(Joystick* joystick);
@ -77,8 +81,10 @@ private:
int buttonRelease() const { return SDL_RELEASED; }
QMap<SDL_JoystickID, Joystick::Pointer> _openJoysticks;
bool _isInitialized { false } ;
bool _isEnabled { false };
bool _isInitialized { false };
static const char* NAME;
static const char* SDL2_ID_STRING;
QStringList _subdeviceNames;
};

View file

@ -29,6 +29,7 @@
#include <PathUtils.h>
#include <PerfStat.h>
#include <ui-plugins/PluginContainer.h>
#include <Preferences.h>
#include <SettingHandle.h>
#include <QLoggingCategory>
@ -46,19 +47,26 @@ static const unsigned int BUTTON_TRIGGER = 1U << 8;
const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame
const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f };
bool SixenseManager::_isEnabled = false;
bool SixenseManager::_sixenseLoaded = false;
#define BAIL_IF_NOT_ENABLED \
if (!_isEnabled) { \
return; \
}
#define BAIL_IF_NOT_LOADED \
if (!_sixenseLoaded) { \
return; \
}
const char* SixenseManager::NAME { "Sixense" };
const char* SixenseManager::HYDRA_ID_STRING { "Razer Hydra" };
const char* SixenseManager::SIXENSE_ID_STRING { "Sixense" };
const char* MENU_PARENT { "Developer" };
const bool DEFAULT_ENABLED = false;
const char* MENU_PARENT{ "Developer" };
const char* MENU_NAME { "Sixense" };
const char* MENU_PATH { "Developer" ">" "Sixense" };
const char* TOGGLE_SMOOTH { "Smooth Sixense Movement" };
@ -73,6 +81,22 @@ bool SixenseManager::isSupported() const {
#endif
}
void SixenseManager::init() {
loadSettings();
auto preferences = DependencyManager::get<Preferences>();
static const QString SIXENSE_PLUGIN { "Sixense Controllers" };
{
auto getter = [this]()->bool { return _isEnabled; };
auto setter = [this](bool value) {
_isEnabled = value;
saveSettings();
};
auto preference = new CheckPreference(SIXENSE_PLUGIN, "Enabled", getter, setter);
preferences->addPreference(preference);
}
}
bool SixenseManager::activate() {
InputPlugin::activate();
@ -133,6 +157,7 @@ void SixenseManager::setSixenseFilter(bool filter) {
}
void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
BAIL_IF_NOT_ENABLED
BAIL_IF_NOT_LOADED
#ifdef HAVE_SIXENSE
@ -553,14 +578,19 @@ QString SixenseManager::InputDevice::getDefaultMappingConfig() const {
return MAPPING_JSON;
}
const char* SETTINGS_ENABLED_KEY = "enabled";
const char* SETTINGS_AVATAR_POSITION_KEY = "avatarPosition";
const char* SETTINGS_AVATAR_ROTATION_KEY = "avatarPosition";
// virtual
void SixenseManager::saveSettings() const {
Settings settings;
QString idString = getID();
settings.beginGroup(idString);
{
settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition);
settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation);
settings.setValue(QString(SETTINGS_ENABLED_KEY), _isEnabled);
settings.setVec3Value(QString(SETTINGS_AVATAR_POSITION_KEY), _inputDevice->_avatarPosition);
settings.setQuatValue(QString(SETTINGS_AVATAR_ROTATION_KEY), _inputDevice->_avatarRotation);
}
settings.endGroup();
}
@ -570,8 +600,9 @@ void SixenseManager::loadSettings() {
QString idString = getID();
settings.beginGroup(idString);
{
settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition);
settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation);
_isEnabled = settings.value(SETTINGS_ENABLED_KEY, QVariant(DEFAULT_ENABLED)).toBool();
settings.getVec3ValueIfValid(QString(SETTINGS_AVATAR_POSITION_KEY), _inputDevice->_avatarPosition);
settings.getQuatValueIfValid(QString(SETTINGS_AVATAR_ROTATION_KEY), _inputDevice->_avatarRotation);
}
settings.endGroup();
}

View file

@ -29,12 +29,14 @@ public:
// Plugin functions
virtual bool isSupported() const override;
virtual const QString getName() const override { return NAME; }
virtual const QString getID() const override { return HYDRA_ID_STRING; }
virtual const QString getID() const override { return SIXENSE_ID_STRING; }
// Sixense always seems to initialize even if the hydras are not present. Is there
// a way we can properly detect whether the hydras are present?
// bool isHandController() const override { return true; }
virtual void init() override;
virtual bool activate() override;
virtual void deactivate() override;
@ -93,8 +95,9 @@ private:
std::shared_ptr<InputDevice> _inputDevice { std::make_shared<InputDevice>() };
static const char* NAME;
static const char* HYDRA_ID_STRING;
static const char* SIXENSE_ID_STRING;
static bool _isEnabled;
static bool _sixenseLoaded;
};

View file

@ -282,7 +282,7 @@ public:
static const vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
static const vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
vr::Texture_t texture{ (void*)_colors[currentColorBuffer], vr::TextureType_OpenGL, vr::ColorSpace_Auto };
vr::Texture_t texture{ (void*)(uintptr_t)_colors[currentColorBuffer], vr::TextureType_OpenGL, vr::ColorSpace_Auto };
vr::VRCompositor()->Submit(vr::Eye_Left, &texture, &leftBounds);
vr::VRCompositor()->Submit(vr::Eye_Right, &texture, &rightBounds);
_plugin._presentRate.increment();
@ -643,7 +643,7 @@ void OpenVrDisplayPlugin::hmdPresent() {
_submitThread->waitForPresent();
} else {
GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
vr::Texture_t vrTexture { (void*)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto };
vr::Texture_t vrTexture { (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto };
vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT);
vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT);
vr::VRCompositor()->PostPresentHandoff();

View file

@ -129,7 +129,7 @@ void releaseOpenVrSystem() {
#endif
// HACK: workaround openvr crash, call submit with an invalid texture, right before VR_Shutdown.
const GLuint INVALID_GL_TEXTURE_HANDLE = -1;
const void* INVALID_GL_TEXTURE_HANDLE = (void*)(uintptr_t)-1;
vr::Texture_t vrTexture{ (void*)INVALID_GL_TEXTURE_HANDLE, vr::TextureType_OpenGL, vr::ColorSpace_Auto };
static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_LEFT{ 0, 0, 0.5f, 1 };
static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_RIGHT{ 0.5f, 0, 1, 1 };

View file

@ -337,6 +337,8 @@ var toolBar = (function () {
var SHAPE_TYPE_SIMPLE_HULL = 1;
var SHAPE_TYPE_SIMPLE_COMPOUND = 2;
var SHAPE_TYPE_STATIC_MESH = 3;
var SHAPE_TYPE_BOX = 4;
var SHAPE_TYPE_SPHERE = 5;
var DYNAMIC_DEFAULT = false;
function handleNewModelDialogResult(result) {
@ -353,6 +355,12 @@ var toolBar = (function () {
case SHAPE_TYPE_STATIC_MESH:
shapeType = "static-mesh";
break;
case SHAPE_TYPE_BOX:
shapeType = "box";
break;
case SHAPE_TYPE_SPHERE:
shapeType = "sphere";
break;
default:
shapeType = "none";
}
@ -450,6 +458,8 @@ var toolBar = (function () {
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_HULL] = "Basic - Whole model";
SHAPE_TYPES[SHAPE_TYPE_SIMPLE_COMPOUND] = "Good - Sub-meshes";
SHAPE_TYPES[SHAPE_TYPE_STATIC_MESH] = "Exact - All polygons";
SHAPE_TYPES[SHAPE_TYPE_BOX] = "Box";
SHAPE_TYPES[SHAPE_TYPE_SPHERE] = "Sphere";
var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH;
// tablet version of new-model dialog
@ -654,6 +664,7 @@ var toolBar = (function () {
selectionDisplay.triggerMapping.enable();
print("starting tablet in landscape mode");
tablet.landscape = true;
entityIconOverlayManager.setIconsSelectable(null,false);
// Not sure what the following was meant to accomplish, but it currently causes
// everybody else to think that Interface has lost focus overall. fogbugzid:558
// Window.setFocus();
@ -981,6 +992,7 @@ function mouseClickEvent(event) {
} else {
selectionManager.addEntity(foundEntity, true);
}
if (wantDebug) {
print("Model selected: " + foundEntity);
}

View file

@ -1,6 +1,7 @@
/* globals EntityIconOverlayManager:true */
EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
var visible = false;
// List of all created overlays
@ -69,6 +70,29 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
}
};
this.setIconsSelectable = function(arrayOfSelectedEntityIDs, isIconsSelectable) {
if (arrayOfSelectedEntityIDs === null) {
for (var id in entityOverlays) {
Overlays.editOverlay(entityOverlays[id], {
ignoreRayIntersection: isIconsSelectable
});
}
} else {
for (var id in entityOverlays) {
if (arrayOfSelectedEntityIDs.indexOf(id) !== -1) { // in the entityOverlays array and selectable
Overlays.editOverlay(entityOverlays[id], {
ignoreRayIntersection: isIconsSelectable
});
} else {
Overlays.editOverlay(entityOverlays[id], {
ignoreRayIntersection: !isIconsSelectable
});
}
}
}
};
// Allocate or get an unused overlay
function getOverlay() {
var overlay;
@ -114,6 +138,9 @@ EntityIconOverlayManager = function(entityTypes, getOverlayPropertiesFunc) {
overlayProperties[key] = customProperties[key];
}
}
if(properties.type === 'ParticleEffect' || properties.type === 'Light'){
overlayProperties.ignoreRayIntersection = true;
}
Overlays.editOverlay(overlay, overlayProperties);
}
}

View file

@ -4060,6 +4060,8 @@ SelectionDisplay = (function() {
return false;
}
entityIconOverlayManager.setIconsSelectable(selectionManager.selections,true);
// ignore ray intersection for our selection box and yaw/pitch/roll
result = Overlays.findRayIntersection(pickRay, true, null, [ yawHandle, pitchHandle, rollHandle, selectionBox ] );
if (result.intersects) {

View file

@ -23,6 +23,7 @@
#include <DependencyManager.h>
#include <SettingHandle.h>
#include <AssetUpload.h>
#include <StatTracker.h>
#include "ATPClientApp.h"
@ -137,6 +138,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) :
Setting::init();
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::set<StatTracker>();
DependencyManager::set<AccountManager>([&]{ return QString(HIGH_FIDELITY_ATP_CLIENT_USER_AGENT); });
DependencyManager::set<AddressManager>();
DependencyManager::set<NodeList>(NodeType::Agent, _listenPort);

View file

@ -0,0 +1,36 @@
//
// eatable.js
//
// Created by Alan-Michael Moody on 7/24/2017
// Copyright 2017 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
//
(function () {
var NOMNOM_SOUND = SoundCache.getSound('http://hifi-content.s3.amazonaws.com/DomainContent/production/audio/vegcrunch.wav');
var _entityID;
this.preload = function (entityID) {
_entityID = entityID;
};
this.collisionWithEntity = function(entityUuid, collisionEntityID, collisionInfo) {
var entity = Entities.getEntityProperties(collisionEntityID, ['userData', 'name']);
var isClone = (entity.name.substring(1).split('-')[0] === 'clone');
var isEatable = (JSON.parse(entity.userData).eatable);
if (isEatable && isClone) {
Audio.playSound(NOMNOM_SOUND, {
position: Entities.getEntityProperties(_entityID, ['position']).position,
volume: 0.2,
localOnly: false
});
Entities.deleteEntity(collisionEntityID);
}
};
});

View file

@ -0,0 +1,56 @@
//
// gravity.js
//
// Created by Alan-Michael Moody on 7/24/17
// Copyright 2017 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
//
(function () {
var _entityID;
this.preload = function (entityID) {
_entityID = entityID;
};
function update(deltatime) {
var planet = Entities.getEntityProperties(_entityID);
//normalization happens in rotationBetween.
var direction = Vec3.subtract(MyAvatar.position, planet.position);
var localUp = Quat.getUp(MyAvatar.orientation);
MyAvatar.orientation = Quat.normalize(Quat.multiply(Quat.rotationBetween(localUp, direction), MyAvatar.orientation));
}
function init() {
Script.update.connect(update);
}
function clean() {
Script.update.disconnect(update);
MyAvatar.orientation = Quat.fromVec3Degrees({
x: 0,
y: 0,
z: 0
});
}
var _switch = true;
this.clickDownOnEntity = function(uuid, mouseEvent) {
if (_switch) {
init();
} else {
clean();
}
_switch = !_switch;
};
Script.scriptEnding.connect(clean);
});

View file

@ -0,0 +1,91 @@
"use strict";
//
// createDart.js
//
// Created by MrRoboman on 17/05/04
// Copyright 2017 High Fidelity, Inc.
//
// Creates five throwing darts.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
var MODEL_URL = "https://hifi-content.s3.amazonaws.com/wadewatts/dart.fbx";
var SCRIPT_URL = Script.resolvePath("./dart.js?v=" + Date.now());
var START_POSITION = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), 0.75));
var START_ROTATION = MyAvatar.orientation
var SCALE_FACTOR = 1;
var dart = {
type: "Model",
shapeType: "box",
name: "Dart",
description: "Throw it at something!",
script: SCRIPT_URL,
modelURL: MODEL_URL,
position: START_POSITION,
rotation: START_ROTATION,
lifetime: 300,
gravity: {
x: 0,
y: -9.8,
z: 0
},
dimensions: {
x: 0.1,
y: 0.1,
z: 0.3
},
dynamic: true,
owningAvatarID: MyAvatar.sessionUUID,
userData: JSON.stringify({
grabbableKey: {
grabbable: true,
invertSolidWhileHeld: true,
ignoreIK: false
}
})
};
var avatarUp = Quat.getUp(MyAvatar.orientation);
var platformPosition = Vec3.sum(START_POSITION, Vec3.multiply(avatarUp, -0.05));
var platform = {
type: "Box",
name: "Dart Platform",
description: "Holds darts",
position: platformPosition,
rotation: START_ROTATION,
lifetime: 60,
dimensions: {
x: 0.15 * 5,
y: 0.01,
z: 0.3
},
color: {
red: 129,
green: 92,
blue: 11
},
owningAvatarID: MyAvatar.sessionUUID,
userData: JSON.stringify({
grabbableKey: {
grabbable: true,
invertSolidWhileHeld: true,
ignoreIK: false
}
})
};
Entities.addEntity(platform);
var dartCount = 5;
var avatarRight = Quat.getRight(MyAvatar.orientation);
for (var i = 0; i < dartCount; i++) {
var j = i - Math.floor(dartCount / 2);
var position = Vec3.sum(START_POSITION, Vec3.multiply(avatarRight, 0.15 * j));
dart.position = position;
Entities.addEntity(dart);
}
Script.stop();

View file

@ -0,0 +1,81 @@
"use strict";
//
// dart.js
//
// Created by MrRoboman on 17/05/13
// Copyright 2017 High Fidelity, Inc.
//
// Simple throwing dart. Sticks to static objects.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
(function() {
var THROW_FACTOR = 3;
var DART_SOUND_URL = Script.resolvePath('https://hifi-content.s3.amazonaws.com/wadewatts/dart.wav?v=' + Date.now());
var Dart = function() {};
Dart.prototype = {
preload: function(entityID) {
this.entityID = entityID;
this.actionID = null;
this.soundHasPlayed = true;
this.dartSound = SoundCache.getSound(DART_SOUND_URL);
},
playDartSound: function(sound) {
if (this.soundHasPlayed) {
return;
}
this.soundHasPlayed = true;
var position = Entities.getEntityProperties(this.entityID, 'position').position;
var audioProperties = {
volume: 0.15,
position: position
};
Audio.playSound(this.dartSound, audioProperties);
},
releaseGrab: function() {
this.soundHasPlayed = false;
var velocity = Entities.getEntityProperties(this.entityID, 'velocity').velocity;
var newVelocity = {};
Object.keys(velocity).forEach(function(key) {
newVelocity[key] = velocity[key] * THROW_FACTOR;
});
Entities.editEntity(this.entityID, {
velocity: newVelocity
});
if (this.actionID) {
Entities.deleteAction(this.entityID, this.actionID);
}
this.actionID = Entities.addAction("travel-oriented", this.entityID, {
forward: { x: 0, y: 0, z: 1 },
angularTimeScale: 0.1,
tag: "throwing dart",
ttl: 3600
});
},
collisionWithEntity: function(myID, otherID, collisionInfo) {
this.playDartSound();
Entities.editEntity(myID, {
velocity: {x: 0, y: 0, z: 0},
angularVelocity: {x: 0, y: 0, z: 0}
});
if (this.actionID) {
Entities.deleteAction(myID, this.actionID);
this.actionID = null;
}
}
};
return new Dart();
});