mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 14:15:28 +02:00
Merged with master
This commit is contained in:
commit
d9a83539b2
167 changed files with 4334 additions and 2131 deletions
assignment-client/src
cmake/macros
domain-server/src
interface
external/leapmotion
resources
avatar
controllers
qml
dialogs
hifi
src
libraries
animation/src
AnimDefaultPose.cppAnimDefaultPose.hAnimInverseKinematics.cppAnimNode.hAnimNodeLoader.cppAnimOverlay.cppAnimOverlay.hAnimationCache.cppAnimationCache.hRig.cpp
audio-client/src
avatars/src
controllers/src/controllers
entities-renderer/src
EntityTreeRenderer.cppEntityTreeRenderer.hRenderableEntityItem.hRenderableModelEntityItem.cppRenderableModelEntityItem.hRenderablePolyVoxEntityItem.cppRenderablePolyVoxEntityItem.h
entities
fbx/src
gpu-gl/src/gpu/gl
gpu/src/gpu
model-networking/src/model-networking
model/src/model
networking/src
|
@ -62,7 +62,7 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
_entityEditSender.setPacketsPerSecond(DEFAULT_ENTITY_PPS_PER_SCRIPT);
|
||||
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
||||
|
||||
ResourceManager::init();
|
||||
DependencyManager::set<ResourceManager>();
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
|
||||
|
@ -199,7 +199,7 @@ void Agent::requestScript() {
|
|||
return;
|
||||
}
|
||||
|
||||
auto request = ResourceManager::createResourceRequest(this, scriptURL);
|
||||
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL);
|
||||
|
||||
if (!request) {
|
||||
qWarning() << "Could not create ResourceRequest for Agent script at" << scriptURL.toString();
|
||||
|
@ -779,7 +779,7 @@ void Agent::aboutToFinish() {
|
|||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
ResourceManager::cleanup();
|
||||
DependencyManager::get<ResourceManager>()->cleanup();
|
||||
|
||||
// cleanup the AudioInjectorManager (and any still running injectors)
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QThread>
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -20,10 +24,6 @@
|
|||
#include "Assignment.h"
|
||||
#include "AssignmentClient.h"
|
||||
#include "AssignmentClientMonitor.h"
|
||||
#include "AssignmentClientApp.h"
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
||||
|
||||
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv)
|
||||
|
@ -87,6 +87,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory");
|
||||
parser.addOption(logDirectoryOption);
|
||||
|
||||
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
|
||||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
parser.showHelp();
|
||||
|
@ -203,6 +206,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
}
|
||||
}
|
||||
|
||||
if (parser.isSet(parentPIDOption)) {
|
||||
bool ok = false;
|
||||
int parentPID = parser.value(parentPIDOption).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
qDebug() << "Parent process PID is" << parentPID;
|
||||
watchParentProcess(parentPID);
|
||||
}
|
||||
}
|
||||
|
||||
QThread::currentThread()->setObjectName("main thread");
|
||||
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
|
|
|
@ -131,7 +131,6 @@ void AssignmentClientMonitor::aboutToQuit() {
|
|||
void AssignmentClientMonitor::spawnChildClient() {
|
||||
QProcess* assignmentClient = new QProcess(this);
|
||||
|
||||
|
||||
// unparse the parts of the command-line that the child cares about
|
||||
QStringList _childArguments;
|
||||
if (_assignmentPool != "") {
|
||||
|
@ -160,6 +159,9 @@ void AssignmentClientMonitor::spawnChildClient() {
|
|||
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
|
||||
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
|
||||
|
||||
_childArguments.append("--" + PARENT_PID_OPTION);
|
||||
_childArguments.append(QString::number(QCoreApplication::applicationPid()));
|
||||
|
||||
QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp;
|
||||
|
||||
|
||||
|
|
|
@ -497,13 +497,14 @@ float computeGain(const AvatarAudioStream& listeningNodeStream, const Positional
|
|||
// avatar: apply fixed off-axis attenuation to make them quieter as they turn away
|
||||
} else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) {
|
||||
glm::vec3 rotatedListenerPosition = glm::inverse(streamToAdd.getOrientation()) * relativePosition;
|
||||
float angleOfDelivery = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
|
||||
glm::normalize(rotatedListenerPosition));
|
||||
|
||||
// source directivity is based on angle of emission, in local coordinates
|
||||
glm::vec3 direction = glm::normalize(rotatedListenerPosition);
|
||||
float angleOfDelivery = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
|
||||
|
||||
const float MAX_OFF_AXIS_ATTENUATION = 0.2f;
|
||||
const float OFF_AXIS_ATTENUATION_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
|
||||
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
|
||||
(angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO));
|
||||
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION + (angleOfDelivery * (OFF_AXIS_ATTENUATION_STEP / PI_OVER_TWO));
|
||||
|
||||
gain *= offAxisCoefficient;
|
||||
}
|
||||
|
@ -545,7 +546,6 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio
|
|||
const glm::vec3& relativePosition) {
|
||||
glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation());
|
||||
|
||||
// Compute sample delay for the two ears to create phase panning
|
||||
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
|
||||
|
||||
// project the rotated source position vector onto the XZ plane
|
||||
|
@ -553,11 +553,16 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio
|
|||
|
||||
const float SOURCE_DISTANCE_THRESHOLD = 1e-30f;
|
||||
|
||||
if (glm::length2(rotatedSourcePosition) > SOURCE_DISTANCE_THRESHOLD) {
|
||||
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
|
||||
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
|
||||
|
||||
// produce an oriented angle about the y-axis
|
||||
return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
} else {
|
||||
// there is no distance between listener and source - return no azimuth
|
||||
return 0;
|
||||
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
|
||||
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
|
||||
return (direction.x < 0.0f) ? -angle : angle;
|
||||
|
||||
} else {
|
||||
// no azimuth if they are in same spot
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -285,6 +285,13 @@ void AvatarMixer::start() {
|
|||
// is guaranteed to not be accessed by other thread
|
||||
void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
||||
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
|
||||
// there is no need to manage identity data we haven't received yet
|
||||
// so bail early if we've never received an identity packet for this avatar
|
||||
if (!nodeData || !nodeData->getAvatar().hasProcessedFirstIdentity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool sendIdentity = false;
|
||||
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
|
|
|
@ -320,14 +320,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
++numOtherAvatars;
|
||||
|
||||
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
|
||||
|
||||
// If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO
|
||||
// the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A.
|
||||
if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
|
||||
if (otherAvatar->hasProcessedFirstIdentity()
|
||||
&& nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) {
|
||||
identityBytesSent += sendIdentityPacket(otherNodeData, node);
|
||||
|
||||
// remember the last time we sent identity details about this other node to the receiver
|
||||
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
|
||||
}
|
||||
|
||||
const AvatarData* otherAvatar = otherNodeData->getConstAvatarData();
|
||||
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
|
||||
|
||||
// determine if avatar is in view, to determine how much data to include...
|
||||
|
@ -400,9 +404,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// set the last sent sequence number for this sender on the receiver
|
||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||
otherNodeData->getLastReceivedSequenceNumber());
|
||||
|
||||
// remember the last time we sent details about this other node to the receiver
|
||||
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ EntityServer::EntityServer(ReceivedMessage& message) :
|
|||
OctreeServer(message),
|
||||
_entitySimulation(NULL)
|
||||
{
|
||||
ResourceManager::init();
|
||||
DependencyManager::set<ResourceManager>();
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<ScriptCache>();
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
|
||||
|
||||
ResourceManager::init();
|
||||
DependencyManager::set<ResourceManager>();
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
|
||||
|
@ -67,7 +67,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
DependencyManager::set<ScriptCache>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
|
||||
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
|
@ -493,7 +492,7 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) {
|
||||
QString scriptUrl = entity->getServerScripts();
|
||||
if (!scriptUrl.isEmpty()) {
|
||||
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
||||
scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
|
||||
qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID;
|
||||
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
|
||||
}
|
||||
|
@ -551,7 +550,7 @@ void EntityScriptServer::aboutToFinish() {
|
|||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
ResourceManager::cleanup();
|
||||
DependencyManager::get<ResourceManager>()->cleanup();
|
||||
|
||||
// cleanup the AudioInjectorManager (and any still running injectors)
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
|
12
cmake/macros/TargetLeapMotion.cmake
Normal file
12
cmake/macros/TargetLeapMotion.cmake
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Created by David Rowe on 16 Jun 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
|
||||
#
|
||||
|
||||
macro(TARGET_LEAPMOTION)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${LEAPMOTION_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES})
|
||||
endmacro()
|
|
@ -171,7 +171,7 @@ void DomainMetadata::maybeUpdateUsers() {
|
|||
if (linkedData) {
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(linkedData);
|
||||
|
||||
if (!nodeData->wasAssigned()) {
|
||||
if (!nodeData->wasAssigned() && node->getType() == NodeType::Agent) {
|
||||
++numConnected;
|
||||
|
||||
if (nodeData->getUsername().isEmpty()) {
|
||||
|
|
|
@ -221,6 +221,8 @@ void DomainServer::parseCommandLine() {
|
|||
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
|
||||
parser.addOption(masterConfigOption);
|
||||
|
||||
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
|
||||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qWarning() << parser.errorText() << endl;
|
||||
|
@ -249,6 +251,17 @@ void DomainServer::parseCommandLine() {
|
|||
_overrideDomainID = true;
|
||||
qDebug() << "domain-server ID is" << _overridingDomainID;
|
||||
}
|
||||
|
||||
|
||||
if (parser.isSet(parentPIDOption)) {
|
||||
bool ok = false;
|
||||
int parentPID = parser.value(parentPIDOption).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
qDebug() << "Parent process PID is" << parentPID;
|
||||
watchParentProcess(parentPID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DomainServer::~DomainServer() {
|
||||
|
|
8
interface/external/leapmotion/readme.txt
vendored
8
interface/external/leapmotion/readme.txt
vendored
|
@ -10,7 +10,7 @@ Interface has been tested with SDK versions:
|
|||
1. Copy the LeapSDK folders from the LeapDeveloperKit installation directory (Lib, Include) into the interface/externals/leapmotion folder.
|
||||
This readme.txt should be there as well.
|
||||
|
||||
The files neeeded in the folders are:
|
||||
The files needed in the folders are:
|
||||
|
||||
include/
|
||||
- Leap.h
|
||||
|
@ -21,8 +21,8 @@ Interface has been tested with SDK versions:
|
|||
x86/
|
||||
- Leap.dll
|
||||
- Leap.lib
|
||||
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
|
||||
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed)
|
||||
- mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistributable installed)
|
||||
- mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistributable installed)
|
||||
- lipLeap.dylib
|
||||
libc++/
|
||||
-libLeap.dylib
|
||||
|
@ -30,4 +30,4 @@ Interface has been tested with SDK versions:
|
|||
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
|
||||
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'leapmotion' that contains the 2 folders mentioned above (Include, Lib).
|
||||
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
|
|
|
@ -150,28 +150,19 @@
|
|||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "hipsManipulatorOverlay",
|
||||
"id": "defaultPoseOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"boneSet": "hipsOnly"
|
||||
"alphaVar": "defaultPoseOverlayAlpha",
|
||||
"boneSet": "fullBody",
|
||||
"boneSetVar": "defaultPoseOverlayBoneSet"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "hipsManipulator",
|
||||
"type": "manipulator",
|
||||
"id": "defaultPose",
|
||||
"type": "defaultPose",
|
||||
"data": {
|
||||
"alpha": 0.0,
|
||||
"alphaVar": "hipsManipulatorAlpha",
|
||||
"joints": [
|
||||
{
|
||||
"jointName": "Hips",
|
||||
"rotationType": "absolute",
|
||||
"translationType": "absolute",
|
||||
"rotationVar": "hipsManipulatorRotation",
|
||||
"translationVar": "hipsManipulatorPosition"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
|
|
48
interface/resources/controllers/leapmotion.json
Normal file
48
interface/resources/controllers/leapmotion.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "Leap Motion to Standard",
|
||||
"channels": [
|
||||
{ "from": "LeapMotion.LeftHand", "to": "Standard.LeftHand" },
|
||||
{ "from": "LeapMotion.LeftHandThumb1", "to": "Standard.LeftHandThumb1"},
|
||||
{ "from": "LeapMotion.LeftHandThumb2", "to": "Standard.LeftHandThumb2"},
|
||||
{ "from": "LeapMotion.LeftHandThumb3", "to": "Standard.LeftHandThumb3"},
|
||||
{ "from": "LeapMotion.LeftHandThumb4", "to": "Standard.LeftHandThumb4"},
|
||||
{ "from": "LeapMotion.LeftHandIndex1", "to": "Standard.LeftHandIndex1"},
|
||||
{ "from": "LeapMotion.LeftHandIndex2", "to": "Standard.LeftHandIndex2"},
|
||||
{ "from": "LeapMotion.LeftHandIndex3", "to": "Standard.LeftHandIndex3"},
|
||||
{ "from": "LeapMotion.LeftHandIndex4", "to": "Standard.LeftHandIndex4"},
|
||||
{ "from": "LeapMotion.LeftHandMiddle1", "to": "Standard.LeftHandMiddle1"},
|
||||
{ "from": "LeapMotion.LeftHandMiddle2", "to": "Standard.LeftHandMiddle2"},
|
||||
{ "from": "LeapMotion.LeftHandMiddle3", "to": "Standard.LeftHandMiddle3"},
|
||||
{ "from": "LeapMotion.LeftHandMiddle4", "to": "Standard.LeftHandMiddle4"},
|
||||
{ "from": "LeapMotion.LeftHandRing1", "to": "Standard.LeftHandRing1"},
|
||||
{ "from": "LeapMotion.LeftHandRing2", "to": "Standard.LeftHandRing2"},
|
||||
{ "from": "LeapMotion.LeftHandRing3", "to": "Standard.LeftHandRing3"},
|
||||
{ "from": "LeapMotion.LeftHandRing4", "to": "Standard.LeftHandRing4"},
|
||||
{ "from": "LeapMotion.LeftHandPinky1", "to": "Standard.LeftHandPinky1"},
|
||||
{ "from": "LeapMotion.LeftHandPinky2", "to": "Standard.LeftHandPinky2"},
|
||||
{ "from": "LeapMotion.LeftHandPinky3", "to": "Standard.LeftHandPinky3"},
|
||||
{ "from": "LeapMotion.LeftHandPinky4", "to": "Standard.LeftHandPinky4"},
|
||||
|
||||
{ "from": "LeapMotion.RightHand", "to": "Standard.RightHand" },
|
||||
{ "from": "LeapMotion.RightHandThumb1", "to": "Standard.RightHandThumb1"},
|
||||
{ "from": "LeapMotion.RightHandThumb2", "to": "Standard.RightHandThumb2"},
|
||||
{ "from": "LeapMotion.RightHandThumb3", "to": "Standard.RightHandThumb3"},
|
||||
{ "from": "LeapMotion.RightHandThumb4", "to": "Standard.RightHandThumb4"},
|
||||
{ "from": "LeapMotion.RightHandIndex1", "to": "Standard.RightHandIndex1"},
|
||||
{ "from": "LeapMotion.RightHandIndex2", "to": "Standard.RightHandIndex2"},
|
||||
{ "from": "LeapMotion.RightHandIndex3", "to": "Standard.RightHandIndex3"},
|
||||
{ "from": "LeapMotion.RightHandIndex4", "to": "Standard.RightHandIndex4"},
|
||||
{ "from": "LeapMotion.RightHandMiddle1", "to": "Standard.RightHandMiddle1"},
|
||||
{ "from": "LeapMotion.RightHandMiddle2", "to": "Standard.RightHandMiddle2"},
|
||||
{ "from": "LeapMotion.RightHandMiddle3", "to": "Standard.RightHandMiddle3"},
|
||||
{ "from": "LeapMotion.RightHandMiddle4", "to": "Standard.RightHandMiddle4"},
|
||||
{ "from": "LeapMotion.RightHandRing1", "to": "Standard.RightHandRing1"},
|
||||
{ "from": "LeapMotion.RightHandRing2", "to": "Standard.RightHandRing2"},
|
||||
{ "from": "LeapMotion.RightHandRing3", "to": "Standard.RightHandRing3"},
|
||||
{ "from": "LeapMotion.RightHandRing4", "to": "Standard.RightHandRing4"},
|
||||
{ "from": "LeapMotion.RightHandPinky1", "to": "Standard.RightHandPinky1"},
|
||||
{ "from": "LeapMotion.RightHandPinky2", "to": "Standard.RightHandPinky2"},
|
||||
{ "from": "LeapMotion.RightHandPinky3", "to": "Standard.RightHandPinky3"},
|
||||
{ "from": "LeapMotion.RightHandPinky4", "to": "Standard.RightHandPinky4"}
|
||||
]
|
||||
}
|
|
@ -58,7 +58,48 @@
|
|||
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
|
||||
|
||||
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
|
||||
{ "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"},
|
||||
{ "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"},
|
||||
{ "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"},
|
||||
{ "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"},
|
||||
{ "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"},
|
||||
{ "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"},
|
||||
{ "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"},
|
||||
{ "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"},
|
||||
{ "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"},
|
||||
{ "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"},
|
||||
{ "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"},
|
||||
{ "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"},
|
||||
{ "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"},
|
||||
{ "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"},
|
||||
{ "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"},
|
||||
{ "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"},
|
||||
{ "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"},
|
||||
{ "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"},
|
||||
{ "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"},
|
||||
{ "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"},
|
||||
|
||||
{ "from": "Standard.RightHand", "to": "Actions.RightHand" },
|
||||
{ "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"},
|
||||
{ "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"},
|
||||
{ "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"},
|
||||
{ "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"},
|
||||
{ "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"},
|
||||
{ "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"},
|
||||
{ "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"},
|
||||
{ "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"},
|
||||
{ "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"},
|
||||
{ "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"},
|
||||
{ "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"},
|
||||
{ "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"},
|
||||
{ "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"},
|
||||
{ "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"},
|
||||
{ "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"},
|
||||
{ "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"},
|
||||
{ "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"},
|
||||
{ "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"},
|
||||
{ "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"},
|
||||
{ "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"},
|
||||
|
||||
{ "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" },
|
||||
{ "from": "Standard.RightFoot", "to": "Actions.RightFoot" },
|
||||
|
|
|
@ -34,6 +34,8 @@ ModalWindow {
|
|||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
property var filesModel: ListModel { }
|
||||
|
||||
Settings {
|
||||
category: "FileDialog"
|
||||
property alias width: root.width
|
||||
|
@ -253,7 +255,9 @@ ModalWindow {
|
|||
}
|
||||
|
||||
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
|
||||
currentSelectionIsFolder = fileTableView.model.isFolder(row);
|
||||
currentSelectionIsFolder = fileTableView.model !== filesModel ?
|
||||
fileTableView.model.isFolder(row) :
|
||||
fileTableModel.isFolder(row);
|
||||
if (root.selectDirectory || !currentSelectionIsFolder) {
|
||||
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
|
||||
} else {
|
||||
|
@ -331,7 +335,12 @@ ModalWindow {
|
|||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
Component {
|
||||
id: filesModelBuilder
|
||||
ListModel { }
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: fileTableModel
|
||||
|
||||
// FolderListModel has a couple of problems:
|
||||
|
@ -383,7 +392,11 @@ ModalWindow {
|
|||
if (row === -1) {
|
||||
return false;
|
||||
}
|
||||
return get(row).fileIsDir;
|
||||
return filesModel.get(row).fileIsDir;
|
||||
}
|
||||
|
||||
function get(row) {
|
||||
return filesModel.get(row)
|
||||
}
|
||||
|
||||
function update() {
|
||||
|
@ -401,7 +414,7 @@ ModalWindow {
|
|||
rows = 0,
|
||||
i;
|
||||
|
||||
clear();
|
||||
var newFilesModel = filesModelBuilder.createObject(root);
|
||||
|
||||
comparisonFunction = sortOrder === Qt.AscendingOrder
|
||||
? function(a, b) { return a < b; }
|
||||
|
@ -423,7 +436,7 @@ ModalWindow {
|
|||
while (lower < upper) {
|
||||
middle = Math.floor((lower + upper) / 2);
|
||||
var lessThan;
|
||||
if (comparisonFunction(sortValue, get(middle)[sortField])) {
|
||||
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
|
||||
lessThan = true;
|
||||
upper = middle;
|
||||
} else {
|
||||
|
@ -432,7 +445,7 @@ ModalWindow {
|
|||
}
|
||||
}
|
||||
|
||||
insert(lower, {
|
||||
newFilesModel.insert(lower, {
|
||||
fileName: fileName,
|
||||
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
|
||||
fileSize: model.getItem(i, "fileSize"),
|
||||
|
@ -443,6 +456,7 @@ ModalWindow {
|
|||
|
||||
rows++;
|
||||
}
|
||||
filesModel = newFilesModel;
|
||||
|
||||
d.clearSelection();
|
||||
}
|
||||
|
@ -469,7 +483,7 @@ ModalWindow {
|
|||
sortIndicatorOrder: Qt.AscendingOrder
|
||||
sortIndicatorVisible: true
|
||||
|
||||
model: fileTableModel
|
||||
model: filesModel
|
||||
|
||||
function updateSort() {
|
||||
model.sortOrder = sortIndicatorOrder;
|
||||
|
@ -561,11 +575,12 @@ ModalWindow {
|
|||
}
|
||||
|
||||
function navigateToCurrentRow() {
|
||||
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
|
||||
var row = fileTableView.currentRow
|
||||
var isFolder = model.isFolder(row);
|
||||
var file = model.get(row).filePath;
|
||||
var isFolder = currentModel.isFolder(row);
|
||||
var file = currentModel.get(row).filePath;
|
||||
if (isFolder) {
|
||||
fileTableView.model.folder = helper.pathToUrl(file);
|
||||
currentModel.folder = helper.pathToUrl(file);
|
||||
} else {
|
||||
okAction.trigger();
|
||||
}
|
||||
|
@ -580,7 +595,8 @@ ModalWindow {
|
|||
var newPrefix = prefix + event.text.toLowerCase();
|
||||
var matchedIndex = -1;
|
||||
for (var i = 0; i < model.count; ++i) {
|
||||
var name = model.get(i).fileName.toLowerCase();
|
||||
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
|
||||
filesModel.get(i).fileName.toLowerCase();
|
||||
if (0 === name.indexOf(newPrefix)) {
|
||||
matchedIndex = i;
|
||||
break;
|
||||
|
|
|
@ -25,11 +25,14 @@ import "fileDialog"
|
|||
//FIXME implement shortcuts for favorite location
|
||||
TabletModalWindow {
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
property var filesModel: ListModel { }
|
||||
|
||||
Settings {
|
||||
category: "FileDialog"
|
||||
property alias width: root.width
|
||||
|
@ -250,7 +253,9 @@ TabletModalWindow {
|
|||
}
|
||||
|
||||
currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);
|
||||
currentSelectionIsFolder = fileTableView.model.isFolder(row);
|
||||
currentSelectionIsFolder = fileTableView.model !== filesModel ?
|
||||
fileTableView.model.isFolder(row) :
|
||||
fileTableModel.isFolder(row);
|
||||
if (root.selectDirectory || !currentSelectionIsFolder) {
|
||||
currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));
|
||||
} else {
|
||||
|
@ -288,7 +293,7 @@ TabletModalWindow {
|
|||
}
|
||||
|
||||
onFolderChanged: {
|
||||
fileTableModel.update(); // Update once the data from the folder change is available.
|
||||
fileTableModel.update()
|
||||
}
|
||||
|
||||
function getItem(index, field) {
|
||||
|
@ -328,7 +333,12 @@ TabletModalWindow {
|
|||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
Component {
|
||||
id: filesModelBuilder
|
||||
ListModel { }
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: fileTableModel
|
||||
|
||||
// FolderListModel has a couple of problems:
|
||||
|
@ -359,17 +369,16 @@ TabletModalWindow {
|
|||
}
|
||||
|
||||
onFolderChanged: {
|
||||
|
||||
if (folder === rootFolder) {
|
||||
model = driveListModel;
|
||||
helper.monitorDirectory("");
|
||||
update();
|
||||
} else {
|
||||
var needsUpdate = model === driveListModel && folder === folderListModel.folder;
|
||||
|
||||
model = folderListModel;
|
||||
folderListModel.folder = folder;
|
||||
helper.monitorDirectory(helper.urlToPath(folder));
|
||||
|
||||
if (needsUpdate) {
|
||||
update();
|
||||
}
|
||||
|
@ -380,7 +389,11 @@ TabletModalWindow {
|
|||
if (row === -1) {
|
||||
return false;
|
||||
}
|
||||
return get(row).fileIsDir;
|
||||
return filesModel.get(row).fileIsDir;
|
||||
}
|
||||
|
||||
function get(row) {
|
||||
return filesModel.get(row)
|
||||
}
|
||||
|
||||
function update() {
|
||||
|
@ -398,7 +411,7 @@ TabletModalWindow {
|
|||
rows = 0,
|
||||
i;
|
||||
|
||||
clear();
|
||||
var newFilesModel = filesModelBuilder.createObject(root);
|
||||
|
||||
comparisonFunction = sortOrder === Qt.AscendingOrder
|
||||
? function(a, b) { return a < b; }
|
||||
|
@ -420,7 +433,7 @@ TabletModalWindow {
|
|||
while (lower < upper) {
|
||||
middle = Math.floor((lower + upper) / 2);
|
||||
var lessThan;
|
||||
if (comparisonFunction(sortValue, get(middle)[sortField])) {
|
||||
if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) {
|
||||
lessThan = true;
|
||||
upper = middle;
|
||||
} else {
|
||||
|
@ -429,7 +442,7 @@ TabletModalWindow {
|
|||
}
|
||||
}
|
||||
|
||||
insert(lower, {
|
||||
newFilesModel.insert(lower, {
|
||||
fileName: fileName,
|
||||
fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),
|
||||
fileSize: model.getItem(i, "fileSize"),
|
||||
|
@ -440,6 +453,7 @@ TabletModalWindow {
|
|||
|
||||
rows++;
|
||||
}
|
||||
filesModel = newFilesModel;
|
||||
|
||||
d.clearSelection();
|
||||
}
|
||||
|
@ -467,7 +481,7 @@ TabletModalWindow {
|
|||
sortIndicatorOrder: Qt.AscendingOrder
|
||||
sortIndicatorVisible: true
|
||||
|
||||
model: fileTableModel
|
||||
model: filesModel
|
||||
|
||||
function updateSort() {
|
||||
model.sortOrder = sortIndicatorOrder;
|
||||
|
@ -559,11 +573,12 @@ TabletModalWindow {
|
|||
}
|
||||
|
||||
function navigateToCurrentRow() {
|
||||
var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel
|
||||
var row = fileTableView.currentRow
|
||||
var isFolder = model.isFolder(row);
|
||||
var file = model.get(row).filePath;
|
||||
var isFolder = currentModel.isFolder(row);
|
||||
var file = currentModel.get(row).filePath;
|
||||
if (isFolder) {
|
||||
fileTableView.model.folder = helper.pathToUrl(file);
|
||||
currentModel.folder = helper.pathToUrl(file);
|
||||
} else {
|
||||
okAction.trigger();
|
||||
}
|
||||
|
@ -578,7 +593,8 @@ TabletModalWindow {
|
|||
var newPrefix = prefix + event.text.toLowerCase();
|
||||
var matchedIndex = -1;
|
||||
for (var i = 0; i < model.count; ++i) {
|
||||
var name = model.get(i).fileName.toLowerCase();
|
||||
var name = model !== filesModel ? model.get(i).fileName.toLowerCase() :
|
||||
filesModel.get(i).fileName.toLowerCase();
|
||||
if (0 === name.indexOf(newPrefix)) {
|
||||
matchedIndex = i;
|
||||
break;
|
||||
|
|
|
@ -17,7 +17,7 @@ PreferencesDialog {
|
|||
id: root
|
||||
objectName: "GeneralPreferencesDialog"
|
||||
title: "General Settings"
|
||||
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"]
|
||||
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"]
|
||||
property var settings: Settings {
|
||||
category: root.objectName
|
||||
property alias x: root.x
|
||||
|
|
|
@ -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"]
|
||||
showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration", "Leap Motion"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,7 +145,6 @@
|
|||
#include "avatar/MyHead.h"
|
||||
#include "CrashHandler.h"
|
||||
#include "devices/DdeFaceTracker.h"
|
||||
#include "devices/Leapmotion.h"
|
||||
#include "DiscoverabilityManager.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "InterfaceDynamicFactory.h"
|
||||
|
@ -582,6 +581,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<LocationBookmarks>();
|
||||
DependencyManager::set<Snapshot>();
|
||||
DependencyManager::set<CloseEventSender>();
|
||||
DependencyManager::set<ResourceManager>();
|
||||
|
||||
return previousSessionCrashed;
|
||||
}
|
||||
|
@ -776,7 +776,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(this, &Application::activeDisplayPluginChanged,
|
||||
reinterpret_cast<scripting::Audio*>(audioScriptingInterface.data()), &scripting::Audio::onContextChanged);
|
||||
|
||||
ResourceManager::init();
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
|
@ -915,11 +914,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_saveAvatarOverrideUrl = true;
|
||||
}
|
||||
|
||||
QString defaultScriptsLocation = getCmdOption(argc, constArgv, "--scripts");
|
||||
if (!defaultScriptsLocation.isEmpty()) {
|
||||
PathUtils::defaultScriptsLocation(defaultScriptsLocation);
|
||||
}
|
||||
|
||||
_glWidget = new GLCanvas();
|
||||
getApplicationCompositor().setRenderingWidget(_glWidget);
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
@ -1188,7 +1182,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// do this as late as possible so that all required subsystems are initialized
|
||||
// If we've overridden the default scripts location, just load default scripts
|
||||
// otherwise, load 'em all
|
||||
if (!defaultScriptsLocation.isEmpty()) {
|
||||
|
||||
// we just want to see if --scripts was set, we've already parsed it and done
|
||||
// the change in PathUtils. Rather than pass that in the constructor, lets just
|
||||
// look (this could be debated)
|
||||
QString scriptsSwitch = QString("--").append(SCRIPTS_SWITCH);
|
||||
QDir defaultScriptsLocation(getCmdOption(argc, constArgv, scriptsSwitch.toStdString().c_str()));
|
||||
if (!defaultScriptsLocation.exists()) {
|
||||
scriptEngines->loadDefaultScripts();
|
||||
scriptEngines->defaultScriptsLocationOverridden(true);
|
||||
} else {
|
||||
|
@ -1882,13 +1882,11 @@ Application::~Application() {
|
|||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<OctreeStatsProvider>();
|
||||
|
||||
ResourceManager::cleanup();
|
||||
DependencyManager::get<ResourceManager>()->cleanup();
|
||||
|
||||
// remove the NodeList from the DependencyManager
|
||||
DependencyManager::destroy<NodeList>();
|
||||
|
||||
Leapmotion::destroy();
|
||||
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
steamClient->shutdown();
|
||||
}
|
||||
|
@ -4056,8 +4054,6 @@ void Application::init() {
|
|||
|
||||
qCDebug(interfaceapp) << "Loaded settings";
|
||||
|
||||
Leapmotion::init();
|
||||
|
||||
// fire off an immediate domain-server check in now that settings are loaded
|
||||
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
|
||||
|
||||
|
@ -4096,7 +4092,10 @@ void Application::init() {
|
|||
EntityTreePointer tree = getEntities()->getTree();
|
||||
if (auto entity = tree->findEntityByEntityItemID(id)) {
|
||||
auto sound = DependencyManager::get<SoundCache>()->getSound(newURL);
|
||||
entity->setCollisionSound(sound);
|
||||
auto renderable = entity->getRenderableInterface();
|
||||
if (renderable) {
|
||||
renderable->setCollisionSound(sound);
|
||||
}
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
connect(getMyAvatar().get(), &MyAvatar::newCollisionSoundURL, this, [this](QUrl newURL) {
|
||||
|
@ -4518,7 +4517,6 @@ void Application::update(float deltaTime) {
|
|||
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
DeviceTracker::updateAll();
|
||||
|
||||
FaceTracker* tracker = getSelectedFaceTracker();
|
||||
if (tracker && Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking) != tracker->isMuted()) {
|
||||
|
@ -4587,8 +4585,6 @@ void Application::update(float deltaTime) {
|
|||
keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->updateInputControllers();
|
||||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
// FIXME can we drop drive keys and just have the avatar read the action states directly?
|
||||
myAvatar->clearDriveKeys();
|
||||
|
@ -4613,6 +4609,31 @@ void Application::update(float deltaTime) {
|
|||
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
|
||||
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
|
||||
|
||||
// If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if
|
||||
// fingers are not being controlled, finger joints are not updated in MySkeletonModel.
|
||||
// Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint.
|
||||
MyAvatar::FingerPosesMap leftHandFingerPoses;
|
||||
if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0
|
||||
|| userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) {
|
||||
for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) {
|
||||
leftHandFingerPoses[i] = {
|
||||
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
|
||||
userInputMapper->getActionName((controller::Action)i)
|
||||
};
|
||||
}
|
||||
}
|
||||
MyAvatar::FingerPosesMap rightHandFingerPoses;
|
||||
if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0
|
||||
|| userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) {
|
||||
for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) {
|
||||
rightHandFingerPoses[i] = {
|
||||
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
|
||||
userInputMapper->getActionName((controller::Action)i)
|
||||
};
|
||||
}
|
||||
}
|
||||
myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses);
|
||||
|
||||
controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT);
|
||||
controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT);
|
||||
myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix));
|
||||
|
@ -5939,7 +5960,7 @@ void Application::addAssetToWorldFromURL(QString url) {
|
|||
|
||||
addAssetToWorldInfo(filename, "Downloading model file " + filename + ".");
|
||||
|
||||
auto request = ResourceManager::createResourceRequest(nullptr, QUrl(url));
|
||||
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, QUrl(url));
|
||||
connect(request, &ResourceRequest::finished, this, &Application::addAssetToWorldFromURLRequestFinished);
|
||||
request->send();
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ static const UINT UWM_SHOW_APPLICATION =
|
|||
#endif
|
||||
|
||||
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
|
||||
static const QString SCRIPTS_SWITCH = "scripts";
|
||||
|
||||
class Application;
|
||||
#if defined(qApp)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QQmlContext>
|
||||
#include <QList>
|
||||
|
||||
#include <Application.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
@ -24,6 +25,8 @@
|
|||
#include "AvatarBookmarks.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
AvatarBookmarks::AvatarBookmarks() {
|
||||
|
@ -58,16 +61,48 @@ void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
|||
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark);
|
||||
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
|
||||
|
||||
Bookmarks::setupMenus(menubar, menu);
|
||||
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
|
||||
addBookmarkToMenu(menubar, it.key(), it.value());
|
||||
}
|
||||
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::changeToBookmarkedAvatar() {
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
const QString& address = action->data().toString();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
myAvatar->useFullAvatarURL(address);
|
||||
|
||||
|
||||
|
||||
if (action->data().type() == QVariant::String) {
|
||||
// TODO: Phase this out eventually.
|
||||
// Legacy avatar bookmark.
|
||||
|
||||
myAvatar->useFullAvatarURL(action->data().toString());
|
||||
qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark ";
|
||||
} else {
|
||||
|
||||
const QMap<QString, QVariant> bookmark = action->data().toMap();
|
||||
// Not magic value. This is the current made version, and if it changes this interpreter should be updated to
|
||||
// handle the new one separately.
|
||||
// This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous
|
||||
if (bookmark.value(ENTRY_VERSION) == 3) {
|
||||
const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString();
|
||||
myAvatar->useFullAvatarURL(avatarUrl);
|
||||
qCDebug(interfaceapp) << "Avatar On " << avatarUrl;
|
||||
const QList<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).toList();
|
||||
|
||||
qCDebug(interfaceapp) << "Attach " << attachments;
|
||||
myAvatar->setAttachmentsVariant(attachments);
|
||||
|
||||
const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat();
|
||||
myAvatar->setAvatarScale(qScale);
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmark() {
|
||||
|
@ -83,13 +118,23 @@ void AvatarBookmarks::addBookmark() {
|
|||
}
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
const QString& bookmarkAddress = myAvatar->getSkeletonModelURL().toString();
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
|
||||
|
||||
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
|
||||
const QVariant& avatarScale = myAvatar->getAvatarScale();
|
||||
|
||||
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
|
||||
QVariantMap *bookmark = new QVariantMap;
|
||||
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
|
||||
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
|
||||
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
|
||||
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
|
||||
|
||||
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
|
||||
}
|
||||
|
||||
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
|
||||
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) {
|
||||
QAction* changeAction = _bookmarksMenu->newAction();
|
||||
changeAction->setData(address);
|
||||
changeAction->setData(bookmark);
|
||||
connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar()));
|
||||
if (!_isMenuSorted) {
|
||||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
|
||||
|
|
|
@ -21,18 +21,23 @@ class AvatarBookmarks: public Bookmarks, public Dependency {
|
|||
|
||||
public:
|
||||
AvatarBookmarks();
|
||||
|
||||
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
|
||||
|
||||
public slots:
|
||||
void addBookmark();
|
||||
|
||||
protected:
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
|
||||
void readFromFile();
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override;
|
||||
void readFromFile() override;
|
||||
|
||||
private:
|
||||
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
|
||||
const QString ENTRY_AVATAR_URL = "avatarUrl";
|
||||
const QString ENTRY_AVATAR_ATTACHMENTS = "attachments";
|
||||
const QString ENTRY_AVATAR_SCALE = "avatarScale";
|
||||
const QString ENTRY_VERSION = "version";
|
||||
|
||||
const int AVATAR_BOOKMARK_VERSION = 3;
|
||||
|
||||
private slots:
|
||||
void changeToBookmarkedAvatar();
|
||||
|
|
|
@ -28,19 +28,6 @@ Bookmarks::Bookmarks() :
|
|||
_isMenuSorted(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
||||
// Enable/Disable menus as needed
|
||||
enableMenuItems(_bookmarks.count() > 0);
|
||||
|
||||
// Load Bookmarks
|
||||
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
|
||||
QString bookmarkName = it.key();
|
||||
QString bookmarkAddress = it.value().toString();
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
|
||||
}
|
||||
}
|
||||
|
||||
void Bookmarks::deleteBookmark() {
|
||||
QStringList bookmarkList;
|
||||
QList<QAction*> menuItems = _bookmarksMenu->actions();
|
||||
|
@ -67,7 +54,7 @@ void Bookmarks::deleteBookmark() {
|
|||
}
|
||||
}
|
||||
|
||||
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress) {
|
||||
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark) {
|
||||
Menu* menubar = Menu::getInstance();
|
||||
if (contains(bookmarkName)) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
@ -75,7 +62,6 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo
|
|||
"The bookmark name you entered already exists in your list.",
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
|
||||
|
||||
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
|
||||
if (result != QMessageBox::Yes) {
|
||||
return;
|
||||
|
@ -83,19 +69,20 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo
|
|||
removeBookmarkFromMenu(menubar, bookmarkName);
|
||||
}
|
||||
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
|
||||
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmark);
|
||||
insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName.
|
||||
enableMenuItems(true);
|
||||
}
|
||||
|
||||
void Bookmarks::insert(const QString& name, const QString& address) {
|
||||
_bookmarks.insert(name, address);
|
||||
void Bookmarks::insert(const QString& name, const QVariant& bookmark) {
|
||||
_bookmarks.insert(name, bookmark);
|
||||
|
||||
if (contains(name)) {
|
||||
qCDebug(interfaceapp) << "Added bookmark:" << name << "," << address;
|
||||
qCDebug(interfaceapp) << "Added bookmark:" << name;
|
||||
persistToFile();
|
||||
} else {
|
||||
qWarning() << "Couldn't add bookmark: " << name << "," << address;
|
||||
}
|
||||
else {
|
||||
qWarning() << "Couldn't add bookmark: " << name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,18 +27,20 @@ class Bookmarks: public QObject {
|
|||
public:
|
||||
Bookmarks();
|
||||
|
||||
virtual void setupMenus(Menu* menubar, MenuWrapper* menu);
|
||||
virtual void setupMenus(Menu* menubar, MenuWrapper* menu) = 0;
|
||||
QString addressForBookmark(const QString& name) const;
|
||||
|
||||
protected:
|
||||
virtual void addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress);
|
||||
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) = 0;
|
||||
void addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark);
|
||||
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0;
|
||||
void enableMenuItems(bool enabled);
|
||||
void readFromFile();
|
||||
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
|
||||
virtual void readFromFile();
|
||||
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
|
||||
void sortActions(Menu* menubar, MenuWrapper* menu);
|
||||
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
|
||||
|
||||
|
||||
bool contains(const QString& name) const;
|
||||
|
||||
QVariantMap _bookmarks; // { name: url, ... }
|
||||
QPointer<MenuWrapper> _bookmarksMenu;
|
||||
QPointer<QAction> _deleteBookmarksAction;
|
||||
|
@ -50,7 +52,6 @@ protected slots:
|
|||
|
||||
private:
|
||||
void remove(const QString& name);
|
||||
bool contains(const QString& name) const;
|
||||
static bool sortOrder(QAction* a, QAction* b);
|
||||
|
||||
void persistToFile();
|
||||
|
|
|
@ -41,13 +41,25 @@ void LocationBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
|
|||
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark);
|
||||
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
|
||||
|
||||
Bookmarks::setupMenus(menubar, menu);
|
||||
// Legacy Location to Bookmark.
|
||||
|
||||
// Enable/Disable menus as needed
|
||||
enableMenuItems(_bookmarks.count() > 0);
|
||||
|
||||
// Load Bookmarks
|
||||
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
|
||||
QString bookmarkName = it.key();
|
||||
QString bookmarkAddress = it.value().toString();
|
||||
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
|
||||
}
|
||||
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
|
||||
void LocationBookmarks::setHomeLocation() {
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
QString bookmarkAddress = addressManager->currentAddress().toString();
|
||||
|
||||
Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress);
|
||||
}
|
||||
|
||||
|
@ -74,7 +86,7 @@ void LocationBookmarks::addBookmark() {
|
|||
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
|
||||
}
|
||||
|
||||
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
|
||||
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) {
|
||||
QAction* teleportAction = _bookmarksMenu->newAction();
|
||||
teleportAction->setData(address);
|
||||
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
|
||||
|
@ -85,4 +97,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co
|
|||
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
|
||||
Bookmarks::sortActions(menubar, _bookmarksMenu);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ public slots:
|
|||
void addBookmark();
|
||||
|
||||
protected:
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
|
||||
void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) override;
|
||||
|
||||
private:
|
||||
const QString LOCATIONBOOKMARKS_FILENAME = "bookmarks.json";
|
||||
|
|
|
@ -566,9 +566,6 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawHandControllers(bool)));
|
||||
|
||||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
||||
// Developer > Entities >>>
|
||||
MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities");
|
||||
|
||||
|
|
|
@ -114,7 +114,6 @@ namespace MenuOption {
|
|||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IndependentMode = "Independent Mode";
|
||||
const QString ActionMotorControl = "Enable Default Motor Control";
|
||||
const QString LeapMotionOnHMD = "Leap Motion on HMD";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
const QString LoadScriptURL = "Open and Run Script from URL...";
|
||||
const QString LodTools = "LOD Tools";
|
||||
|
|
|
@ -79,6 +79,7 @@ public:
|
|||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.disableContextStereo();
|
||||
batch.disableContextViewCorrection();
|
||||
});
|
||||
|
||||
auto srcViewFrustum = args->getViewFrustum();
|
||||
|
@ -112,6 +113,7 @@ public:
|
|||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.restoreContextStereo();
|
||||
batch.restoreContextViewCorrection();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -106,7 +106,8 @@ void ATPAssetMigrator::loadEntityServerFile() {
|
|||
|
||||
jsonValue = entityObject;
|
||||
} else if (wantsToMigrateResource(migrationURL)) {
|
||||
auto request = ResourceManager::createResourceRequest(this, migrationURL);
|
||||
auto request =
|
||||
DependencyManager::get<ResourceManager>()->createResourceRequest(this, migrationURL);
|
||||
|
||||
if (request) {
|
||||
qCDebug(asset_migrator) << "Requesting" << migrationURL << "for ATP asset migration";
|
||||
|
|
|
@ -1455,6 +1455,19 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
|
|||
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
|
||||
}
|
||||
|
||||
void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) {
|
||||
_leftHandFingerPosesInSensorFramceCache.set(left);
|
||||
_rightHandFingerPosesInSensorFramceCache.set(right);
|
||||
}
|
||||
|
||||
MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const {
|
||||
return _leftHandFingerPosesInSensorFramceCache.get();
|
||||
}
|
||||
|
||||
MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const {
|
||||
return _rightHandFingerPosesInSensorFramceCache.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
|
||||
_leftFootControllerPoseInSensorFrameCache.set(left);
|
||||
_rightFootControllerPoseInSensorFrameCache.set(right);
|
||||
|
@ -2524,6 +2537,21 @@ bool MyAvatar::getFlyingEnabled() {
|
|||
return _enableFlying;
|
||||
}
|
||||
|
||||
// Public interface for targetscale
|
||||
float MyAvatar::getAvatarScale() {
|
||||
return getTargetScale();
|
||||
}
|
||||
|
||||
void MyAvatar::setAvatarScale(float val) {
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAvatarScale", Q_ARG(float, val));
|
||||
return;
|
||||
}
|
||||
|
||||
setTargetScale(val);
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionsEnabled(bool enabled) {
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
|
|
@ -366,7 +366,7 @@ public:
|
|||
float getDriveKey(DriveKeys key) const;
|
||||
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
|
||||
void relayDriveKeysToCharacterController();
|
||||
|
||||
|
||||
Q_INVOKABLE void disableDriveKey(DriveKeys key);
|
||||
Q_INVOKABLE void enableDriveKey(DriveKeys key);
|
||||
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
|
||||
|
@ -474,6 +474,11 @@ public:
|
|||
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
|
||||
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
|
||||
|
||||
typedef std::map<int, std::pair<controller::Pose, QString>> FingerPosesMap;
|
||||
void setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right);
|
||||
FingerPosesMap getLeftHandFingerControllerPosesInSensorFrame() const;
|
||||
FingerPosesMap getRightHandFingerControllerPosesInSensorFrame() const;
|
||||
|
||||
void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
|
||||
controller::Pose getLeftFootControllerPoseInSensorFrame() const;
|
||||
controller::Pose getRightFootControllerPoseInSensorFrame() const;
|
||||
|
@ -512,6 +517,9 @@ public:
|
|||
Q_INVOKABLE void setFlyingEnabled(bool enabled);
|
||||
Q_INVOKABLE bool getFlyingEnabled();
|
||||
|
||||
Q_INVOKABLE float getAvatarScale();
|
||||
Q_INVOKABLE void setAvatarScale(float scale);
|
||||
|
||||
Q_INVOKABLE void setCollisionsEnabled(bool enabled);
|
||||
Q_INVOKABLE bool getCollisionsEnabled();
|
||||
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated
|
||||
|
@ -788,13 +796,15 @@ private:
|
|||
// These are stored in SENSOR frame
|
||||
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<FingerPosesMap> _leftHandFingerPosesInSensorFramceCache { };
|
||||
ThreadSafeValueCache<FingerPosesMap> _rightHandFingerPosesInSensorFramceCache { };
|
||||
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
|
||||
bool _hmdLeanRecenterEnabled = true;
|
||||
AnimPose _prePhysicsRoomPose;
|
||||
|
|
|
@ -96,7 +96,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity;
|
||||
params.controllerActiveFlags[Rig::ControllerType_RightArm] = false;
|
||||
}
|
||||
|
||||
|
||||
auto avatarLeftArmPose = myAvatar->getLeftArmControllerPoseInAvatarFrame();
|
||||
if (avatarLeftArmPose.isValid()) {
|
||||
AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation());
|
||||
|
@ -174,5 +174,50 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex;
|
||||
|
||||
_rig.updateFromEyeParameters(eyeParams);
|
||||
|
||||
updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame());
|
||||
updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame());
|
||||
}
|
||||
|
||||
|
||||
void MySkeletonModel::updateFingers(const MyAvatar::FingerPosesMap& fingerPoses) {
|
||||
// Assumes that finger poses are kept in order in the poses map.
|
||||
|
||||
if (fingerPoses.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto posesMapItr = fingerPoses.begin();
|
||||
|
||||
bool isLeftHand = posesMapItr->first < (int)controller::Action::RIGHT_HAND_THUMB1;
|
||||
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
auto handPose = isLeftHand
|
||||
? myAvatar->getLeftHandControllerPoseInSensorFrame()
|
||||
: myAvatar->getRightHandControllerPoseInSensorFrame();
|
||||
auto handJointRotation = handPose.getRotation();
|
||||
|
||||
bool isHandValid = handPose.isValid();
|
||||
bool isFingerValid = false;
|
||||
glm::quat previousJointRotation;
|
||||
|
||||
while (posesMapItr != fingerPoses.end()) {
|
||||
auto jointName = posesMapItr->second.second;
|
||||
if (isHandValid && jointName.right(1) == "1") {
|
||||
isFingerValid = posesMapItr->second.first.isValid();
|
||||
previousJointRotation = handJointRotation;
|
||||
}
|
||||
|
||||
if (isHandValid && isFingerValid) {
|
||||
auto thisJointRotation = posesMapItr->second.first.getRotation();
|
||||
const float CONTROLLER_PRIORITY = 2.0f;
|
||||
_rig.setJointRotation(_rig.indexOfJoint(jointName), true, glm::inverse(previousJointRotation) * thisJointRotation,
|
||||
CONTROLLER_PRIORITY);
|
||||
previousJointRotation = thisJointRotation;
|
||||
} else {
|
||||
_rig.clearJointAnimationPriority(_rig.indexOfJoint(jointName));
|
||||
}
|
||||
|
||||
posesMapItr++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define hifi_MySkeletonModel_h
|
||||
|
||||
#include <avatars-renderer/SkeletonModel.h>
|
||||
#include "MyAvatar.h"
|
||||
|
||||
/// A skeleton loaded from a model.
|
||||
class MySkeletonModel : public SkeletonModel {
|
||||
|
@ -21,6 +22,9 @@ private:
|
|||
public:
|
||||
MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr);
|
||||
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
||||
|
||||
private:
|
||||
void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses);
|
||||
};
|
||||
|
||||
#endif // hifi_MySkeletonModel_h
|
||||
|
|
|
@ -1,246 +0,0 @@
|
|||
//
|
||||
// Created by Sam Cake on 6/2/2014
|
||||
// Copyright 2014 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 "Leapmotion.h"
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "Menu.h"
|
||||
|
||||
const int PALMROOT_NUM_JOINTS = 3;
|
||||
const int FINGER_NUM_JOINTS = 4;
|
||||
const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS;
|
||||
|
||||
const DeviceTracker::Name Leapmotion::NAME = "Leapmotion";
|
||||
|
||||
// find the index of a joint from
|
||||
// the side: true = right
|
||||
// the finger & the bone:
|
||||
// finger in [0..4] : bone in [0..3] a finger phalange
|
||||
// [-1] up the hand branch : bone in [0..2] <=> [ hand, forearm, arm]
|
||||
MotionTracker::Index evalJointIndex(bool isRightSide, int finger, int bone) {
|
||||
|
||||
MotionTracker::Index offset = 1 // start after root
|
||||
+ (int(isRightSide) * HAND_NUM_JOINTS) // then offset for side
|
||||
+ PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain
|
||||
if (finger >= 0) {
|
||||
// from there go down in the correct finger and bone
|
||||
return offset + (finger * FINGER_NUM_JOINTS) + bone;
|
||||
} else {
|
||||
// or go back up for the correct root bone
|
||||
return offset - 1 - bone;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void Leapmotion::init() {
|
||||
DeviceTracker* device = DeviceTracker::getDevice(NAME);
|
||||
|
||||
if (!device) {
|
||||
// create a new Leapmotion and register it
|
||||
Leapmotion* leap = new Leapmotion();
|
||||
DeviceTracker::registerDevice(NAME, leap);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void Leapmotion::destroy() {
|
||||
DeviceTracker::destroyDevice(NAME);
|
||||
}
|
||||
|
||||
// static
|
||||
Leapmotion* Leapmotion::getInstance() {
|
||||
DeviceTracker* device = DeviceTracker::getDevice(NAME);
|
||||
if (!device) {
|
||||
// create a new Leapmotion and register it
|
||||
device = new Leapmotion();
|
||||
DeviceTracker::registerDevice(NAME, device);
|
||||
}
|
||||
return dynamic_cast< Leapmotion* > (device);
|
||||
}
|
||||
|
||||
Leapmotion::Leapmotion() :
|
||||
MotionTracker(),
|
||||
_active(false)
|
||||
{
|
||||
// Create the Leapmotion joint hierarchy
|
||||
std::vector< Semantic > sides;
|
||||
sides.push_back("joint_L_");
|
||||
sides.push_back("joint_R_");
|
||||
|
||||
std::vector< Semantic > rootBones;
|
||||
rootBones.push_back("elbow");
|
||||
rootBones.push_back("wrist");
|
||||
rootBones.push_back("hand");
|
||||
|
||||
std::vector< Semantic > fingers;
|
||||
fingers.push_back("thumb");
|
||||
fingers.push_back("index");
|
||||
fingers.push_back("middle");
|
||||
fingers.push_back("ring");
|
||||
fingers.push_back("pinky");
|
||||
|
||||
std::vector< Semantic > fingerBones;
|
||||
fingerBones.push_back("1");
|
||||
fingerBones.push_back("2");
|
||||
fingerBones.push_back("3");
|
||||
fingerBones.push_back("4");
|
||||
|
||||
std::vector< Index > palms;
|
||||
for (unsigned int s = 0; s < sides.size(); s++) {
|
||||
Index rootJoint = 0;
|
||||
for (unsigned int rb = 0; rb < rootBones.size(); rb++) {
|
||||
rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint);
|
||||
}
|
||||
|
||||
// capture the hand index for debug
|
||||
palms.push_back(rootJoint);
|
||||
|
||||
for (unsigned int f = 0; f < fingers.size(); f++) {
|
||||
for (unsigned int b = 0; b < fingerBones.size(); b++) {
|
||||
rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LEAPMOTION
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::LeapMotionOnHMD)) {
|
||||
_controller.setPolicyFlags(Leap::Controller::POLICY_OPTIMIZE_HMD);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Leapmotion::~Leapmotion() {
|
||||
}
|
||||
|
||||
#ifdef HAVE_LEAPMOTION
|
||||
glm::quat quatFromLeapBase(float sideSign, const Leap::Matrix& basis) {
|
||||
|
||||
// fix the handness to right and always...
|
||||
glm::vec3 xAxis = glm::normalize(sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z));
|
||||
glm::vec3 yAxis = glm::normalize(glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z));
|
||||
glm::vec3 zAxis = glm::normalize(glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z));
|
||||
|
||||
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
|
||||
|
||||
glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
|
||||
return orientation;
|
||||
}
|
||||
|
||||
glm::vec3 vec3FromLeapVector(const Leap::Vector& vec) {
|
||||
return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Leapmotion::update() {
|
||||
#ifdef HAVE_LEAPMOTION
|
||||
bool wasActive = _active;
|
||||
_active = _controller.isConnected();
|
||||
|
||||
if (_active || wasActive) {
|
||||
// Go through all the joints and increment their counter since last update.
|
||||
// Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive.
|
||||
// TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
|
||||
for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
|
||||
(*jointIt).tickNewFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the most recent frame and report some basic information
|
||||
const Leap::Frame frame = _controller.frame();
|
||||
static int64_t lastFrameID = -1;
|
||||
int64_t newFrameID = frame.id();
|
||||
|
||||
// If too soon then exit
|
||||
if (lastFrameID >= newFrameID)
|
||||
return;
|
||||
|
||||
glm::vec3 delta(0.0f);
|
||||
glm::quat handOri;
|
||||
if (!frame.hands().isEmpty()) {
|
||||
for (int handNum = 0; handNum < frame.hands().count(); handNum++) {
|
||||
|
||||
const Leap::Hand hand = frame.hands()[handNum];
|
||||
int side = (hand.isRight() ? 1 : -1);
|
||||
|
||||
JointTracker* parentJointTracker = _jointsArray.data();
|
||||
|
||||
|
||||
int rootBranchIndex = -1;
|
||||
|
||||
Leap::Arm arm = hand.arm();
|
||||
if (arm.isValid()) {
|
||||
glm::quat ori = quatFromLeapBase(float(side), arm.basis());
|
||||
glm::vec3 pos = vec3FromLeapVector(arm.elbowPosition());
|
||||
JointTracker* elbow = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 2)); // 2 is the index of the elbow joint
|
||||
elbow->editAbsFrame().setTranslation(pos);
|
||||
elbow->editAbsFrame().setRotation(ori);
|
||||
elbow->updateLocFromAbsTransform(parentJointTracker);
|
||||
elbow->activeFrame();
|
||||
parentJointTracker = elbow;
|
||||
|
||||
pos = vec3FromLeapVector(arm.wristPosition());
|
||||
JointTracker* wrist = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 1)); // 1 is the index of the wrist joint
|
||||
wrist->editAbsFrame().setTranslation(pos);
|
||||
wrist->editAbsFrame().setRotation(ori);
|
||||
wrist->updateLocFromAbsTransform(parentJointTracker);
|
||||
wrist->activeFrame();
|
||||
parentJointTracker = wrist;
|
||||
}
|
||||
|
||||
JointTracker* palmJoint = NULL;
|
||||
{
|
||||
glm::vec3 pos = vec3FromLeapVector(hand.palmPosition());
|
||||
glm::quat ori = quatFromLeapBase(float(side), hand.basis());
|
||||
|
||||
palmJoint = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 0)); // 0 is the index of the palm joint
|
||||
palmJoint->editAbsFrame().setTranslation(pos);
|
||||
palmJoint->editAbsFrame().setRotation(ori);
|
||||
palmJoint->updateLocFromAbsTransform(parentJointTracker);
|
||||
palmJoint->activeFrame();
|
||||
}
|
||||
|
||||
// Check if the hand has any fingers
|
||||
const Leap::FingerList fingers = hand.fingers();
|
||||
if (!fingers.isEmpty()) {
|
||||
// For every fingers in the list
|
||||
for (int i = 0; i < fingers.count(); ++i) {
|
||||
// Reset the parent joint to the palmJoint for every finger traversal
|
||||
parentJointTracker = palmJoint;
|
||||
|
||||
// surprisingly, Leap::Finger::Type start at 0 for thumb a until 4 for the pinky
|
||||
Index fingerIndex = evalJointIndex((side > 0), int(fingers[i].type()), 0);
|
||||
|
||||
// let's update the finger's joints
|
||||
for (int b = 0; b < FINGER_NUM_JOINTS; b++) {
|
||||
Leap::Bone::Type type = Leap::Bone::Type(b + Leap::Bone::TYPE_METACARPAL);
|
||||
Leap::Bone bone = fingers[i].bone(type);
|
||||
JointTracker* ljointTracker = editJointTracker(fingerIndex + b);
|
||||
if (bone.isValid()) {
|
||||
Leap::Vector bp = bone.nextJoint();
|
||||
|
||||
ljointTracker->editAbsFrame().setTranslation(vec3FromLeapVector(bp));
|
||||
ljointTracker->editAbsFrame().setRotation(quatFromLeapBase(float(side), bone.basis()));
|
||||
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
|
||||
ljointTracker->activeFrame();
|
||||
}
|
||||
parentJointTracker = ljointTracker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastFrameID = newFrameID;
|
||||
#endif
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// Created by Sam Cake on 6/2/2014
|
||||
// Copyright 2014 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_Leapmotion_h
|
||||
#define hifi_Leapmotion_h
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include <trackers/MotionTracker.h>
|
||||
|
||||
#ifdef HAVE_LEAPMOTION
|
||||
#include <Leap.h>
|
||||
#endif
|
||||
|
||||
/// Handles interaction with the Leapmotion skeleton tracking suit.
|
||||
class Leapmotion : public MotionTracker {
|
||||
public:
|
||||
static const Name NAME;
|
||||
|
||||
static void init();
|
||||
static void destroy();
|
||||
|
||||
/// Leapmotion MotionTracker factory
|
||||
static Leapmotion* getInstance();
|
||||
|
||||
bool isActive() const { return _active; }
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
protected:
|
||||
Leapmotion();
|
||||
virtual ~Leapmotion();
|
||||
|
||||
private:
|
||||
#ifdef HAVE_LEAPMOTION
|
||||
Leap::Listener _listener;
|
||||
Leap::Controller _controller;
|
||||
#endif
|
||||
|
||||
bool _active;
|
||||
};
|
||||
|
||||
#endif // hifi_Leapmotion_h
|
|
@ -73,12 +73,14 @@ int main(int argc, const char* argv[]) {
|
|||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
|
||||
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
|
||||
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
|
||||
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
|
||||
parser.addOption(urlOption);
|
||||
parser.addOption(noUpdaterOption);
|
||||
parser.addOption(checkMinSpecOption);
|
||||
parser.addOption(runServerOption);
|
||||
parser.addOption(serverContentPathOption);
|
||||
parser.addOption(overrideAppLocalDataPathOption);
|
||||
parser.addOption(overrideScriptsPathOption);
|
||||
parser.addOption(allowMultipleInstancesOption);
|
||||
parser.parse(arguments);
|
||||
|
||||
|
@ -99,6 +101,16 @@ int main(int argc, const char* argv[]) {
|
|||
if (allowMultipleInstances) {
|
||||
instanceMightBeRunning = false;
|
||||
}
|
||||
// this needs to be done here in main, as the mechanism for setting the
|
||||
// scripts directory appears not to work. See the bug report
|
||||
// https://highfidelity.fogbugz.com/f/cases/5759/Issues-changing-scripts-directory-in-ScriptsEngine
|
||||
if (parser.isSet(overrideScriptsPathOption)) {
|
||||
QDir scriptsPath(parser.value(overrideScriptsPathOption));
|
||||
if (scriptsPath.exists()) {
|
||||
PathUtils::defaultScriptsLocation(scriptsPath.path());
|
||||
}
|
||||
}
|
||||
|
||||
if (parser.isSet(overrideAppLocalDataPathOption)) {
|
||||
// get dir to use for cache
|
||||
QString cacheDir = parser.value(overrideAppLocalDataPathOption);
|
||||
|
@ -106,7 +118,7 @@ int main(int argc, const char* argv[]) {
|
|||
// tell everyone to use the right cache location
|
||||
//
|
||||
// this handles data8 and prepared
|
||||
ResourceManager::setCacheDir(cacheDir);
|
||||
DependencyManager::get<ResourceManager>()->setCacheDir(cacheDir);
|
||||
|
||||
// this does the ktx_cache
|
||||
PathUtils::getAppLocalDataPath(cacheDir);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <plugins/PluginManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include <trackers/MotionTracker.h>
|
||||
|
||||
void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
|
||||
if (event->type() == HFActionEvent::startType()) {
|
||||
|
@ -97,86 +96,6 @@ QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const {
|
|||
return qRectToVariant(rect);
|
||||
}
|
||||
|
||||
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
|
||||
// This is where we retrieve the Device Tracker category and then the sub tracker within it
|
||||
auto icIt = _inputControllers.find(0);
|
||||
if (icIt != _inputControllers.end()) {
|
||||
return (*icIt).second.get();
|
||||
}
|
||||
|
||||
// Look for device
|
||||
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
|
||||
if (deviceID < 0) {
|
||||
deviceID = 0;
|
||||
}
|
||||
// TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
|
||||
// in the near future we need to change that to a real mapping between the devices and the deviceName
|
||||
// ALso we need to expand the spec so we can fall back on the "default" controller per categories
|
||||
|
||||
if (deviceID >= 0) {
|
||||
// TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
|
||||
MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
|
||||
if (motionTracker) {
|
||||
MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
|
||||
if (trackerID >= 0) {
|
||||
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
|
||||
controller::InputController::Key key = inputController->getKey();
|
||||
_inputControllers.insert(InputControllerMap::value_type(key, inputController));
|
||||
return inputController.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::releaseInputController(controller::InputController* input) {
|
||||
_inputControllers.erase(input->getKey());
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::updateInputControllers() {
|
||||
for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
|
||||
(*it).second->update();
|
||||
}
|
||||
}
|
||||
|
||||
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
|
||||
_deviceTrackerId(deviceTrackerId),
|
||||
_subTrackerId(subTrackerId),
|
||||
_isActive(false)
|
||||
{
|
||||
}
|
||||
|
||||
void InputController::update() {
|
||||
_isActive = false;
|
||||
|
||||
// TODO for now the InputController is only supporting a JointTracker from a MotionTracker
|
||||
MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId));
|
||||
if (motionTracker) {
|
||||
if ((int)_subTrackerId < motionTracker->numJointTrackers()) {
|
||||
const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId);
|
||||
|
||||
if (joint->isActive()) {
|
||||
joint->getAbsFrame().getTranslation(_eventCache.absTranslation);
|
||||
joint->getAbsFrame().getRotation(_eventCache.absRotation);
|
||||
joint->getLocFrame().getTranslation(_eventCache.locTranslation);
|
||||
joint->getLocFrame().getRotation(_eventCache.locRotation);
|
||||
|
||||
_isActive = true;
|
||||
//emit spatialEvent(_eventCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned int INPUTCONTROLLER_KEY_DEVICE_OFFSET = 16;
|
||||
const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16;
|
||||
|
||||
InputController::Key InputController::getKey() const {
|
||||
return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
|
||||
}
|
||||
|
||||
|
||||
void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
|
||||
void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
|
||||
|
||||
|
|
|
@ -25,38 +25,6 @@
|
|||
#include <WheelEvent.h>
|
||||
class ScriptEngine;
|
||||
|
||||
class PalmData;
|
||||
|
||||
class InputController : public controller::InputController {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL);
|
||||
|
||||
virtual void update() override;
|
||||
virtual Key getKey() const override;
|
||||
|
||||
public slots:
|
||||
|
||||
virtual bool isActive() const override { return _isActive; }
|
||||
virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; }
|
||||
virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; }
|
||||
virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; }
|
||||
virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; }
|
||||
|
||||
private:
|
||||
|
||||
int _deviceTrackerId;
|
||||
uint _subTrackerId;
|
||||
|
||||
// cache for the spatial
|
||||
SpatialEvent _eventCache;
|
||||
bool _isActive;
|
||||
|
||||
signals:
|
||||
};
|
||||
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
class ControllerScriptingInterface : public controller::ScriptingInterface {
|
||||
Q_OBJECT
|
||||
|
@ -86,8 +54,6 @@ public:
|
|||
bool isJoystickCaptured(int joystickIndex) const;
|
||||
bool areEntityClicksCaptured() const;
|
||||
|
||||
void updateInputControllers();
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
|
@ -102,10 +68,6 @@ public slots:
|
|||
virtual glm::vec2 getViewportDimensions() const;
|
||||
virtual QVariant getRecommendedOverlayRect() const;
|
||||
|
||||
/// Factory to create an InputController
|
||||
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
|
||||
virtual void releaseInputController(controller::InputController* input);
|
||||
|
||||
signals:
|
||||
void keyPressEvent(const KeyEvent& event);
|
||||
void keyReleaseEvent(const KeyEvent& event);
|
||||
|
@ -135,8 +97,6 @@ private:
|
|||
bool _captureEntityClicks;
|
||||
|
||||
using InputKey = controller::InputController::Key;
|
||||
using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>;
|
||||
InputControllerMap _inputControllers;
|
||||
};
|
||||
|
||||
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
|
||||
|
|
|
@ -62,7 +62,7 @@ bool TestScriptingInterface::loadTestScene(QString scene) {
|
|||
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
|
||||
static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/";
|
||||
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([scene]()->QVariant {
|
||||
ResourceManager::setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
|
||||
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
|
||||
auto tree = qApp->getEntities()->getTree();
|
||||
auto treeIsClient = tree->getIsClient();
|
||||
// Force the tree to accept the load regardless of permissions
|
||||
|
|
34
libraries/animation/src/AnimDefaultPose.cpp
Normal file
34
libraries/animation/src/AnimDefaultPose.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AnimDefaultPose.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on 6/26/17.
|
||||
// Copyright (c) 2017 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimDefaultPose.h"
|
||||
|
||||
AnimDefaultPose::AnimDefaultPose(const QString& id) :
|
||||
AnimNode(AnimNode::Type::DefaultPose, id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AnimDefaultPose::~AnimDefaultPose() {
|
||||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
if (_skeleton) {
|
||||
_poses = _skeleton->getRelativeDefaultPoses();
|
||||
} else {
|
||||
_poses.clear();
|
||||
}
|
||||
return _poses;
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimDefaultPose::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
36
libraries/animation/src/AnimDefaultPose.h
Normal file
36
libraries/animation/src/AnimDefaultPose.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// AnimDefaultPose.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 6/26/17.
|
||||
// Copyright (c) 2017 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// 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_AnimDefaultPose_h
|
||||
#define hifi_AnimDefaultPose_h
|
||||
|
||||
#include <string>
|
||||
#include "AnimNode.h"
|
||||
|
||||
// Always returns the default pose of the current skeleton.
|
||||
|
||||
class AnimDefaultPose : public AnimNode {
|
||||
public:
|
||||
AnimDefaultPose(const QString& id);
|
||||
virtual ~AnimDefaultPose() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
protected:
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
// no copies
|
||||
AnimDefaultPose(const AnimDefaultPose&) = delete;
|
||||
AnimDefaultPose& operator=(const AnimDefaultPose&) = delete;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimDefaultPose_h
|
|
@ -870,9 +870,9 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||
glm::vec3 hipsOffset = scaleFactor * _hipsOffset;
|
||||
if (_hipsParentIndex == -1) {
|
||||
_relativePoses[_hipsIndex].trans() = underPoses[_hipsIndex].trans() + hipsOffset;
|
||||
_relativePoses[_hipsIndex].trans() = _relativePoses[_hipsIndex].trans() + hipsOffset;
|
||||
} else {
|
||||
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, underPoses);
|
||||
auto absHipsPose = _skeleton->getAbsolutePose(_hipsIndex, _relativePoses);
|
||||
absHipsPose.trans() += hipsOffset;
|
||||
_relativePoses[_hipsIndex] = _skeleton->getAbsolutePose(_hipsParentIndex, _relativePoses).inverse() * absHipsPose;
|
||||
}
|
||||
|
@ -1732,6 +1732,10 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
|
|||
break;
|
||||
case SolutionSource::RelaxToLimitCenterPoses:
|
||||
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||
// special case for hips: copy over hips pose whether or not IK is enabled.
|
||||
if (_hipsIndex >= 0 && _hipsIndex < (int)_relativePoses.size()) {
|
||||
_relativePoses[_hipsIndex] = _limitCenterPoses[_hipsIndex];
|
||||
}
|
||||
break;
|
||||
case SolutionSource::PreviousSolution:
|
||||
// do nothing... _relativePoses is already the previous solution
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
StateMachine,
|
||||
Manipulator,
|
||||
InverseKinematics,
|
||||
DefaultPose,
|
||||
NumTypes
|
||||
};
|
||||
using Pointer = std::shared_ptr<AnimNode>;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "AnimStateMachine.h"
|
||||
#include "AnimManipulator.h"
|
||||
#include "AnimInverseKinematics.h"
|
||||
#include "AnimDefaultPose.h"
|
||||
|
||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
@ -35,6 +36,7 @@ static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QStri
|
|||
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
||||
// called after children have been loaded
|
||||
// returns node on success, nullptr on failure.
|
||||
|
@ -50,6 +52,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
|||
case AnimNode::Type::StateMachine: return "stateMachine";
|
||||
case AnimNode::Type::Manipulator: return "manipulator";
|
||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -109,6 +112,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
||||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -123,6 +127,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
||||
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -347,7 +352,8 @@ static const char* boneSetStrings[AnimOverlay::NumBoneSets] = {
|
|||
"empty",
|
||||
"leftHand",
|
||||
"rightHand",
|
||||
"hipsOnly"
|
||||
"hipsOnly",
|
||||
"bothFeet"
|
||||
};
|
||||
|
||||
static AnimOverlay::BoneSet stringToBoneSetEnum(const QString& str) {
|
||||
|
@ -517,6 +523,11 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS
|
|||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
auto node = std::make_shared<AnimDefaultPose>(id);
|
||||
return node;
|
||||
}
|
||||
|
||||
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
||||
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
||||
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
||||
|
|
|
@ -35,6 +35,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
|
|||
case LeftHandBoneSet: buildLeftHandBoneSet(); break;
|
||||
case RightHandBoneSet: buildRightHandBoneSet(); break;
|
||||
case HipsOnlyBoneSet: buildHipsOnlyBoneSet(); break;
|
||||
case BothFeetBoneSet: buildBothFeetBoneSet(); break;
|
||||
default:
|
||||
case EmptyBoneSet: buildEmptyBoneSet(); break;
|
||||
}
|
||||
|
@ -196,6 +197,20 @@ void AnimOverlay::buildHipsOnlyBoneSet() {
|
|||
_boneSetVec[hipsJoint] = 1.0f;
|
||||
}
|
||||
|
||||
void AnimOverlay::buildBothFeetBoneSet() {
|
||||
assert(_skeleton);
|
||||
buildEmptyBoneSet();
|
||||
int rightFoot = _skeleton->nameToJointIndex("RightFoot");
|
||||
for_each_child_joint(_skeleton, rightFoot, [&](int i) {
|
||||
_boneSetVec[i] = 1.0f;
|
||||
});
|
||||
int leftFoot = _skeleton->nameToJointIndex("LeftFoot");
|
||||
for_each_child_joint(_skeleton, leftFoot, [&](int i) {
|
||||
_boneSetVec[i] = 1.0f;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const AnimPoseVec& AnimOverlay::getPosesInternal() const {
|
||||
return _poses;
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
LeftHandBoneSet,
|
||||
RightHandBoneSet,
|
||||
HipsOnlyBoneSet,
|
||||
BothFeetBoneSet,
|
||||
NumBoneSets
|
||||
};
|
||||
|
||||
|
@ -77,6 +78,7 @@ public:
|
|||
void buildLeftHandBoneSet();
|
||||
void buildRightHandBoneSet();
|
||||
void buildHipsOnlyBoneSet();
|
||||
void buildBothFeetBoneSet();
|
||||
|
||||
// no copies
|
||||
AnimOverlay(const AnimOverlay&) = delete;
|
||||
|
|
|
@ -144,38 +144,3 @@ void Animation::animationParseError(int error, QString str) {
|
|||
finishedLoading(false);
|
||||
}
|
||||
|
||||
AnimationDetails::AnimationDetails() :
|
||||
role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false),
|
||||
startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), currentFrame(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
|
||||
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame) :
|
||||
role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold),
|
||||
startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame),
|
||||
running(running), currentFrame(currentFrame)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& details) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("role", details.role);
|
||||
obj.setProperty("url", details.url.toString());
|
||||
obj.setProperty("fps", details.fps);
|
||||
obj.setProperty("priority", details.priority);
|
||||
obj.setProperty("loop", details.loop);
|
||||
obj.setProperty("hold", details.hold);
|
||||
obj.setProperty("startAutomatically", details.startAutomatically);
|
||||
obj.setProperty("firstFrame", details.firstFrame);
|
||||
obj.setProperty("lastFrame", details.lastFrame);
|
||||
obj.setProperty("running", details.running);
|
||||
obj.setProperty("currentFrame", details.currentFrame);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& details) {
|
||||
// nothing for now...
|
||||
}
|
||||
|
||||
|
|
|
@ -107,26 +107,5 @@ private:
|
|||
QByteArray _data;
|
||||
};
|
||||
|
||||
class AnimationDetails {
|
||||
public:
|
||||
AnimationDetails();
|
||||
AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
|
||||
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame);
|
||||
|
||||
QString role;
|
||||
QUrl url;
|
||||
float fps;
|
||||
float priority;
|
||||
bool loop;
|
||||
bool hold;
|
||||
bool startAutomatically;
|
||||
float firstFrame;
|
||||
float lastFrame;
|
||||
bool running;
|
||||
float currentFrame;
|
||||
};
|
||||
Q_DECLARE_METATYPE(AnimationDetails);
|
||||
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event);
|
||||
void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& event);
|
||||
|
||||
#endif // hifi_AnimationCache_h
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "AnimClip.h"
|
||||
#include "AnimInverseKinematics.h"
|
||||
#include "AnimOverlay.h"
|
||||
#include "AnimSkeleton.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "IKTarget.h"
|
||||
|
@ -1459,13 +1460,28 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||
params.controllerPoses[ControllerType_LeftFoot], params.controllerPoses[ControllerType_RightFoot]);
|
||||
|
||||
if (hipsEnabled) {
|
||||
// if the hips or the feet are being controlled.
|
||||
if (hipsEnabled || rightFootEnabled || leftFootEnabled) {
|
||||
// for more predictable IK solve from the center of the joint limits, not from the underpose
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToLimitCenterPoses);
|
||||
|
||||
// replace the feet animation with the default pose, this is to prevent unexpected toe wiggling.
|
||||
_animVars.set("defaultPoseOverlayAlpha", 1.0f);
|
||||
_animVars.set("defaultPoseOverlayBoneSet", (int)AnimOverlay::BothFeetBoneSet);
|
||||
} else {
|
||||
// augment the IK with the underPose.
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
||||
|
||||
// feet should follow source animation
|
||||
_animVars.unset("defaultPoseOverlayAlpha");
|
||||
_animVars.unset("defaultPoseOverlayBoneSet");
|
||||
}
|
||||
|
||||
if (hipsEnabled) {
|
||||
_animVars.set("hipsType", (int)IKTarget::Type::RotationAndPosition);
|
||||
_animVars.set("hipsPosition", params.controllerPoses[ControllerType_Hips].trans());
|
||||
_animVars.set("hipsRotation", params.controllerPoses[ControllerType_Hips].rot());
|
||||
} else {
|
||||
_animVars.set("solutionSource", (int)AnimInverseKinematics::SolutionSource::RelaxToUnderPoses);
|
||||
_animVars.set("hipsType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
#include "AudioClientLogging.h"
|
||||
#include "AudioLogging.h"
|
||||
#include "AudioHelpers.h"
|
||||
|
||||
#include "AudioClient.h"
|
||||
|
||||
|
@ -1688,23 +1689,24 @@ int AudioClient::calculateNumberOfFrameSamples(int numBytes) const {
|
|||
}
|
||||
|
||||
float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
|
||||
// copied from AudioMixer, more or less
|
||||
glm::quat inverseOrientation = glm::inverse(_orientationGetter());
|
||||
|
||||
// compute sample delay for the 2 ears to create phase panning
|
||||
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
|
||||
|
||||
// project the rotated source position vector onto x-y plane
|
||||
// project the rotated source position vector onto the XZ plane
|
||||
rotatedSourcePosition.y = 0.0f;
|
||||
|
||||
static const float SOURCE_DISTANCE_THRESHOLD = 1e-30f;
|
||||
|
||||
if (glm::length2(rotatedSourcePosition) > SOURCE_DISTANCE_THRESHOLD) {
|
||||
float rotatedSourcePositionLength2 = glm::length2(rotatedSourcePosition);
|
||||
if (rotatedSourcePositionLength2 > SOURCE_DISTANCE_THRESHOLD) {
|
||||
|
||||
// produce an oriented angle about the y-axis
|
||||
return glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), glm::normalize(rotatedSourcePosition), glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
} else {
|
||||
|
||||
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
|
||||
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
|
||||
return (direction.x < 0.0f) ? -angle : angle;
|
||||
|
||||
} else {
|
||||
// no azimuth if they are in same spot
|
||||
return 0.0f;
|
||||
}
|
||||
|
|
|
@ -1978,7 +1978,7 @@ JointData jointDataFromJsonValue(const QJsonValue& json) {
|
|||
result.rotation = quatFromJsonValue(array[0]);
|
||||
result.rotationSet = true;
|
||||
result.translation = vec3FromJsonValue(array[1]);
|
||||
result.translationSet = false;
|
||||
result.translationSet = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -2146,12 +2146,9 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) {
|
|||
QVector<JointData> jointArray;
|
||||
QJsonArray jointArrayJson = json[JSON_AVATAR_JOINT_ARRAY].toArray();
|
||||
jointArray.reserve(jointArrayJson.size());
|
||||
int i = 0;
|
||||
for (const auto& jointJson : jointArrayJson) {
|
||||
auto joint = jointDataFromJsonValue(jointJson);
|
||||
jointArray.push_back(joint);
|
||||
setJointData(i, joint.rotation, joint.translation);
|
||||
i++;
|
||||
}
|
||||
setRawJointData(jointArray);
|
||||
}
|
||||
|
|
|
@ -627,6 +627,7 @@ public:
|
|||
void markIdentityDataChanged() { _identityDataChanged = true; }
|
||||
|
||||
void pushIdentitySequenceNumber() { ++_identitySequenceNumber; };
|
||||
bool hasProcessedFirstIdentity() const { return _hasProcessedFirstIdentity; }
|
||||
|
||||
float getDensity() const { return _density; }
|
||||
|
||||
|
|
|
@ -59,6 +59,48 @@ namespace controller {
|
|||
makePosePair(Action::SPINE2, "Spine2"),
|
||||
makePosePair(Action::HEAD, "Head"),
|
||||
|
||||
makePosePair(Action::LEFT_HAND_THUMB1, "LeftHandThumb1"),
|
||||
makePosePair(Action::LEFT_HAND_THUMB2, "LeftHandThumb2"),
|
||||
makePosePair(Action::LEFT_HAND_THUMB3, "LeftHandThumb3"),
|
||||
makePosePair(Action::LEFT_HAND_THUMB4, "LeftHandThumb4"),
|
||||
makePosePair(Action::LEFT_HAND_INDEX1, "LeftHandIndex1"),
|
||||
makePosePair(Action::LEFT_HAND_INDEX2, "LeftHandIndex2"),
|
||||
makePosePair(Action::LEFT_HAND_INDEX3, "LeftHandIndex3"),
|
||||
makePosePair(Action::LEFT_HAND_INDEX4, "LeftHandIndex4"),
|
||||
makePosePair(Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1"),
|
||||
makePosePair(Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2"),
|
||||
makePosePair(Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3"),
|
||||
makePosePair(Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4"),
|
||||
makePosePair(Action::LEFT_HAND_RING1, "LeftHandRing1"),
|
||||
makePosePair(Action::LEFT_HAND_RING2, "LeftHandRing2"),
|
||||
makePosePair(Action::LEFT_HAND_RING3, "LeftHandRing3"),
|
||||
makePosePair(Action::LEFT_HAND_RING4, "LeftHandRing4"),
|
||||
makePosePair(Action::LEFT_HAND_PINKY1, "LeftHandPinky1"),
|
||||
makePosePair(Action::LEFT_HAND_PINKY2, "LeftHandPinky2"),
|
||||
makePosePair(Action::LEFT_HAND_PINKY3, "LeftHandPinky3"),
|
||||
makePosePair(Action::LEFT_HAND_PINKY4, "LeftHandPinky4"),
|
||||
|
||||
makePosePair(Action::RIGHT_HAND_THUMB1, "RightHandThumb1"),
|
||||
makePosePair(Action::RIGHT_HAND_THUMB2, "RightHandThumb2"),
|
||||
makePosePair(Action::RIGHT_HAND_THUMB3, "RightHandThumb3"),
|
||||
makePosePair(Action::RIGHT_HAND_THUMB4, "RightHandThumb4"),
|
||||
makePosePair(Action::RIGHT_HAND_INDEX1, "RightHandIndex1"),
|
||||
makePosePair(Action::RIGHT_HAND_INDEX2, "RightHandIndex2"),
|
||||
makePosePair(Action::RIGHT_HAND_INDEX3, "RightHandIndex3"),
|
||||
makePosePair(Action::RIGHT_HAND_INDEX4, "RightHandIndex4"),
|
||||
makePosePair(Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1"),
|
||||
makePosePair(Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2"),
|
||||
makePosePair(Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3"),
|
||||
makePosePair(Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4"),
|
||||
makePosePair(Action::RIGHT_HAND_RING1, "RightHandRing1"),
|
||||
makePosePair(Action::RIGHT_HAND_RING2, "RightHandRing2"),
|
||||
makePosePair(Action::RIGHT_HAND_RING3, "RightHandRing3"),
|
||||
makePosePair(Action::RIGHT_HAND_RING4, "RightHandRing4"),
|
||||
makePosePair(Action::RIGHT_HAND_PINKY1, "RightHandPinky1"),
|
||||
makePosePair(Action::RIGHT_HAND_PINKY2, "RightHandPinky2"),
|
||||
makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"),
|
||||
makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"),
|
||||
|
||||
makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
|
||||
makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),
|
||||
|
||||
|
|
|
@ -104,6 +104,47 @@ enum class Action {
|
|||
LEFT_ARM,
|
||||
RIGHT_ARM,
|
||||
|
||||
LEFT_HAND_THUMB1,
|
||||
LEFT_HAND_THUMB2,
|
||||
LEFT_HAND_THUMB3,
|
||||
LEFT_HAND_THUMB4,
|
||||
LEFT_HAND_INDEX1,
|
||||
LEFT_HAND_INDEX2,
|
||||
LEFT_HAND_INDEX3,
|
||||
LEFT_HAND_INDEX4,
|
||||
LEFT_HAND_MIDDLE1,
|
||||
LEFT_HAND_MIDDLE2,
|
||||
LEFT_HAND_MIDDLE3,
|
||||
LEFT_HAND_MIDDLE4,
|
||||
LEFT_HAND_RING1,
|
||||
LEFT_HAND_RING2,
|
||||
LEFT_HAND_RING3,
|
||||
LEFT_HAND_RING4,
|
||||
LEFT_HAND_PINKY1,
|
||||
LEFT_HAND_PINKY2,
|
||||
LEFT_HAND_PINKY3,
|
||||
LEFT_HAND_PINKY4,
|
||||
|
||||
RIGHT_HAND_THUMB1,
|
||||
RIGHT_HAND_THUMB2,
|
||||
RIGHT_HAND_THUMB3,
|
||||
RIGHT_HAND_THUMB4,
|
||||
RIGHT_HAND_INDEX1,
|
||||
RIGHT_HAND_INDEX2,
|
||||
RIGHT_HAND_INDEX3,
|
||||
RIGHT_HAND_INDEX4,
|
||||
RIGHT_HAND_MIDDLE1,
|
||||
RIGHT_HAND_MIDDLE2,
|
||||
RIGHT_HAND_MIDDLE3,
|
||||
RIGHT_HAND_MIDDLE4,
|
||||
RIGHT_HAND_RING1,
|
||||
RIGHT_HAND_RING2,
|
||||
RIGHT_HAND_RING3,
|
||||
RIGHT_HAND_RING4,
|
||||
RIGHT_HAND_PINKY1,
|
||||
RIGHT_HAND_PINKY2,
|
||||
RIGHT_HAND_PINKY3,
|
||||
RIGHT_HAND_PINKY4,
|
||||
|
||||
NUM_ACTIONS,
|
||||
};
|
||||
|
|
|
@ -101,7 +101,47 @@ Input::NamedVector StandardController::getAvailableInputs() const {
|
|||
|
||||
// Poses
|
||||
makePair(LEFT_HAND, "LeftHand"),
|
||||
makePair(LEFT_HAND_THUMB1, "LeftHandThumb1"),
|
||||
makePair(LEFT_HAND_THUMB2, "LeftHandThumb2"),
|
||||
makePair(LEFT_HAND_THUMB3, "LeftHandThumb3"),
|
||||
makePair(LEFT_HAND_THUMB4, "LeftHandThumb4"),
|
||||
makePair(LEFT_HAND_INDEX1, "LeftHandIndex1"),
|
||||
makePair(LEFT_HAND_INDEX2, "LeftHandIndex2"),
|
||||
makePair(LEFT_HAND_INDEX3, "LeftHandIndex3"),
|
||||
makePair(LEFT_HAND_INDEX4, "LeftHandIndex4"),
|
||||
makePair(LEFT_HAND_MIDDLE1, "LeftHandMiddle1"),
|
||||
makePair(LEFT_HAND_MIDDLE2, "LeftHandMiddle2"),
|
||||
makePair(LEFT_HAND_MIDDLE3, "LeftHandMiddle3"),
|
||||
makePair(LEFT_HAND_MIDDLE4, "LeftHandMiddle4"),
|
||||
makePair(LEFT_HAND_RING1, "LeftHandRing1"),
|
||||
makePair(LEFT_HAND_RING2, "LeftHandRing2"),
|
||||
makePair(LEFT_HAND_RING3, "LeftHandRing3"),
|
||||
makePair(LEFT_HAND_RING4, "LeftHandRing4"),
|
||||
makePair(LEFT_HAND_PINKY1, "LeftHandPinky1"),
|
||||
makePair(LEFT_HAND_PINKY2, "LeftHandPinky2"),
|
||||
makePair(LEFT_HAND_PINKY3, "LeftHandPinky3"),
|
||||
makePair(LEFT_HAND_PINKY4, "LeftHandPinky4"),
|
||||
makePair(RIGHT_HAND, "RightHand"),
|
||||
makePair(RIGHT_HAND_THUMB1, "RightHandThumb1"),
|
||||
makePair(RIGHT_HAND_THUMB2, "RightHandThumb2"),
|
||||
makePair(RIGHT_HAND_THUMB3, "RightHandThumb3"),
|
||||
makePair(RIGHT_HAND_THUMB4, "RightHandThumb4"),
|
||||
makePair(RIGHT_HAND_INDEX1, "RightHandIndex1"),
|
||||
makePair(RIGHT_HAND_INDEX2, "RightHandIndex2"),
|
||||
makePair(RIGHT_HAND_INDEX3, "RightHandIndex3"),
|
||||
makePair(RIGHT_HAND_INDEX4, "RightHandIndex4"),
|
||||
makePair(RIGHT_HAND_MIDDLE1, "RightHandMiddle1"),
|
||||
makePair(RIGHT_HAND_MIDDLE2, "RightHandMiddle2"),
|
||||
makePair(RIGHT_HAND_MIDDLE3, "RightHandMiddle3"),
|
||||
makePair(RIGHT_HAND_MIDDLE4, "RightHandMiddle4"),
|
||||
makePair(RIGHT_HAND_RING1, "RightHandRing1"),
|
||||
makePair(RIGHT_HAND_RING2, "RightHandRing2"),
|
||||
makePair(RIGHT_HAND_RING3, "RightHandRing3"),
|
||||
makePair(RIGHT_HAND_RING4, "RightHandRing4"),
|
||||
makePair(RIGHT_HAND_PINKY1, "RightHandPinky1"),
|
||||
makePair(RIGHT_HAND_PINKY2, "RightHandPinky2"),
|
||||
makePair(RIGHT_HAND_PINKY3, "RightHandPinky3"),
|
||||
makePair(RIGHT_HAND_PINKY4, "RightHandPinky4"),
|
||||
makePair(LEFT_FOOT, "LeftFoot"),
|
||||
makePair(RIGHT_FOOT, "RightFoot"),
|
||||
makePair(RIGHT_ARM, "RightArm"),
|
||||
|
|
|
@ -163,7 +163,6 @@ void EntityTreeRenderer::reloadEntityScripts() {
|
|||
void EntityTreeRenderer::init() {
|
||||
OctreeProcessor::init();
|
||||
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
entityTree->setFBXService(this);
|
||||
|
||||
if (_wantScripts) {
|
||||
resetEntitiesScriptEngine();
|
||||
|
@ -188,7 +187,6 @@ void EntityTreeRenderer::shutdown() {
|
|||
|
||||
void EntityTreeRenderer::setTree(OctreePointer newTree) {
|
||||
OctreeProcessor::setTree(newTree);
|
||||
std::static_pointer_cast<EntityTree>(_tree)->setFBXService(this);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::update() {
|
||||
|
@ -373,31 +371,6 @@ bool EntityTreeRenderer::applyLayeredZones() {
|
|||
return true;
|
||||
}
|
||||
|
||||
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
|
||||
const FBXGeometry* result = NULL;
|
||||
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||
assert(modelEntityItem); // we need this!!!
|
||||
ModelPointer model = modelEntityItem->getModel(getSharedFromThis());
|
||||
if (model && model->isLoaded()) {
|
||||
result = &model->getFBXGeometry();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ModelPointer EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) {
|
||||
ModelPointer result = nullptr;
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||
result = modelEntityItem->getModel(getSharedFromThis());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
|
||||
}
|
||||
|
@ -880,7 +853,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
entity->scriptHasUnloaded();
|
||||
}
|
||||
if (shouldLoad) {
|
||||
scriptUrl = ResourceManager::normalizeURL(scriptUrl);
|
||||
scriptUrl = DependencyManager::get<ResourceManager>()->normalizeURL(scriptUrl);
|
||||
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload);
|
||||
entity->scriptHasPreloaded();
|
||||
}
|
||||
|
@ -889,7 +862,12 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
|
||||
void EntityTreeRenderer::playEntityCollisionSound(EntityItemPointer entity, const Collision& collision) {
|
||||
assert((bool)entity);
|
||||
SharedSoundPointer collisionSound = entity->getCollisionSound();
|
||||
auto renderable = entity->getRenderableInterface();
|
||||
if (!renderable) {
|
||||
return;
|
||||
}
|
||||
|
||||
SharedSoundPointer collisionSound = renderable->getCollisionSound();
|
||||
if (!collisionSound) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ using ModelWeakPointer = std::weak_ptr<Model>;
|
|||
using CalculateEntityLoadingPriority = std::function<float(const EntityItem& item)>;
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class EntityTreeRenderer : public OctreeProcessor, public EntityItemFBXService, public Dependency {
|
||||
class EntityTreeRenderer : public OctreeProcessor, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
|
@ -68,9 +68,6 @@ public:
|
|||
|
||||
virtual void init() override;
|
||||
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override;
|
||||
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override;
|
||||
|
||||
/// clears the tree
|
||||
virtual void clear() override;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <render/Scene.h>
|
||||
#include <EntityItem.h>
|
||||
#include <Sound.h>
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
||||
|
@ -40,7 +41,11 @@ public:
|
|||
virtual void render(RenderArgs* args) {};
|
||||
virtual bool addToScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
|
||||
virtual void removeFromScene(const EntityItemPointer& self, const render::ScenePointer& scene, render::Transaction& transaction) = 0;
|
||||
const SharedSoundPointer& getCollisionSound() { return _collisionSound; }
|
||||
void setCollisionSound(const SharedSoundPointer& sound) { _collisionSound = sound; }
|
||||
virtual RenderableEntityInterface* getRenderableInterface() { return nullptr; }
|
||||
private:
|
||||
SharedSoundPointer _collisionSound;
|
||||
};
|
||||
|
||||
class RenderableEntityItemProxy {
|
||||
|
|
|
@ -69,11 +69,9 @@ void RenderableModelEntityItem::setModelURL(const QString& url) {
|
|||
|
||||
void RenderableModelEntityItem::loader() {
|
||||
_needsModelReload = true;
|
||||
auto renderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
assert(renderer);
|
||||
{
|
||||
PerformanceTimer perfTimer("getModel");
|
||||
getModel(renderer);
|
||||
getModel();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,8 +388,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
if (!_model || _needsModelReload) {
|
||||
// TODO: this getModel() appears to be about 3% of model render time. We should optimize
|
||||
PerformanceTimer perfTimer("getModel");
|
||||
auto renderer = qSharedPointerCast<EntityTreeRenderer>(args->_renderData);
|
||||
getModel(renderer);
|
||||
getModel();
|
||||
|
||||
// Remap textures immediately after loading to avoid flicker
|
||||
remapTextures();
|
||||
|
@ -483,7 +480,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
auto& currentURL = getParsedModelURL();
|
||||
if (currentURL != _model->getURL()) {
|
||||
// Defer setting the url to the render thread
|
||||
getModel(_myRenderer);
|
||||
getModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -492,16 +489,11 @@ ModelPointer RenderableModelEntityItem::getModelNotSafe() {
|
|||
return _model;
|
||||
}
|
||||
|
||||
ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRenderer> renderer) {
|
||||
if (!renderer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModelPointer RenderableModelEntityItem::getModel() {
|
||||
// make sure our renderer is setup
|
||||
if (!_myRenderer) {
|
||||
_myRenderer = renderer;
|
||||
_myRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
}
|
||||
assert(_myRenderer == renderer); // you should only ever render on one renderer
|
||||
|
||||
if (!_myRenderer || QThread::currentThread() != _myRenderer->thread()) {
|
||||
return _model;
|
||||
|
@ -513,7 +505,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRender
|
|||
if (!getModelURL().isEmpty()) {
|
||||
// If we don't have a model, allocate one *immediately*
|
||||
if (!_model) {
|
||||
_model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this), this);
|
||||
_model = _myRenderer->allocateModel(getModelURL(), _myRenderer->getEntityLoadingPriority(*this), this);
|
||||
_needsInitialSimulation = true;
|
||||
// If we need to change URLs, update it *after rendering* (to avoid access violations)
|
||||
} else if (QUrl(getModelURL()) != _model->getURL()) {
|
||||
|
@ -587,6 +579,17 @@ EntityItemProperties RenderableModelEntityItem::getProperties(EntityPropertyFlag
|
|||
properties.setRenderInfoHasTransparent(_model->getRenderInfoHasTransparent());
|
||||
}
|
||||
|
||||
|
||||
if (_model && _model->isLoaded()) {
|
||||
// TODO: improve naturalDimensions in the future,
|
||||
// for now we've added this hack for setting natural dimensions of models
|
||||
Extents meshExtents = _model->getFBXGeometry().getUnscaledMeshExtents();
|
||||
properties.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
|
||||
properties.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -1255,3 +1258,27 @@ QStringList RenderableModelEntityItem::getJointNames() const {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::mapJoints(const QStringList& modelJointNames) {
|
||||
// if we don't have animation, or we're already joint mapped then bail early
|
||||
if (!hasAnimation() || jointsMapped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_animation || _animation->getURL().toString() != getAnimationURL()) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(getAnimationURL());
|
||||
}
|
||||
|
||||
if (_animation && _animation->isLoaded()) {
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
|
||||
_jointMapping.resize(modelJointNames.size());
|
||||
for (int i = 0; i < modelJointNames.size(); i++) {
|
||||
_jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]);
|
||||
}
|
||||
_jointMappingCompleted = true;
|
||||
_jointMappingURL = _animationProperties.getURL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QStringList>
|
||||
|
||||
#include <ModelEntityItem.h>
|
||||
#include <AnimationCache.h>
|
||||
|
||||
class Model;
|
||||
class EntityTreeRenderer;
|
||||
|
@ -53,7 +54,7 @@ public:
|
|||
bool& keepSearching, OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
void** intersectedObject, bool precisionPicking) const override;
|
||||
ModelPointer getModel(QSharedPointer<EntityTreeRenderer> renderer);
|
||||
ModelPointer getModel();
|
||||
ModelPointer getModelNotSafe();
|
||||
|
||||
virtual bool needsToCallUpdate() const override;
|
||||
|
@ -106,6 +107,15 @@ public:
|
|||
// Transparency is handled in ModelMeshPartPayload
|
||||
bool isTransparent() override { return false; }
|
||||
|
||||
void mapJoints(const QStringList& modelJointNames);
|
||||
bool jointsMapped() const {
|
||||
return _jointMappingURL == getAnimationURL() && _jointMappingCompleted;
|
||||
}
|
||||
|
||||
AnimationPointer getAnimation() const {
|
||||
return _animation;
|
||||
}
|
||||
|
||||
private:
|
||||
QVariantMap parseTexturesToMap(QString textures);
|
||||
void remapTextures();
|
||||
|
@ -131,6 +141,12 @@ private:
|
|||
bool _needsJointSimulation { false };
|
||||
bool _showCollisionGeometry { false };
|
||||
const void* _collisionMeshKey { nullptr };
|
||||
|
||||
// used on client side
|
||||
bool _jointMappingCompleted { false };
|
||||
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
||||
QString _jointMappingURL;
|
||||
AnimationPointer _animation;
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableModelEntityItem_h
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <model-networking/SimpleMeshProxy.h>
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
|
@ -54,6 +58,7 @@
|
|||
#include "polyvox_fade_vert.h"
|
||||
#include "polyvox_frag.h"
|
||||
#include "polyvox_fade_frag.h"
|
||||
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
#include "EntityEditPacketSender.h"
|
||||
#include "PhysicalEntitySimulation.h"
|
||||
|
@ -1648,6 +1653,7 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
|
|||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
|
||||
bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
|
||||
if (!updateDependents()) {
|
||||
return false;
|
||||
|
@ -1667,7 +1673,7 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
|
|||
} else {
|
||||
success = true;
|
||||
// the mesh will be in voxel-space. transform it into object-space
|
||||
meshProxy = new MeshProxy(
|
||||
meshProxy = new SimpleMeshProxy(
|
||||
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
|
||||
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
|
||||
[&](uint32_t index){ return index; }));
|
||||
|
|
|
@ -12,17 +12,19 @@
|
|||
#ifndef hifi_RenderablePolyVoxEntityItem_h
|
||||
#define hifi_RenderablePolyVoxEntityItem_h
|
||||
|
||||
#include <QSemaphore>
|
||||
#include <atomic>
|
||||
|
||||
#include <QSemaphore>
|
||||
|
||||
#include <PolyVoxCore/SimpleVolume.h>
|
||||
#include <PolyVoxCore/Raycast.h>
|
||||
|
||||
#include <gpu/Context.h>
|
||||
#include <model/Forward.h>
|
||||
#include <TextureCache.h>
|
||||
#include <PolyVoxEntityItem.h>
|
||||
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "gpu/Context.h"
|
||||
|
||||
class PolyVoxPayload {
|
||||
public:
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
set(TARGET_NAME entities)
|
||||
setup_hifi_library(Network Script)
|
||||
link_hifi_libraries(avatars shared audio octree model model-networking fbx networking animation)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gpu)
|
||||
|
||||
target_bullet()
|
||||
|
||||
include_hifi_library_headers(render)
|
||||
link_hifi_libraries(shared networking octree avatars)
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimationPropertyGroup.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
#include <AnimationLoop.h>
|
||||
|
||||
#include "AnimationPropertyGroup.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include "AnimationLoop.h"
|
||||
#include <shared/types/AnimationLoop.h> // for Animation, AnimationCache, and AnimationPointer classes
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
#include "PropertyGroup.h"
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ void EntityEditFilters::addFilter(EntityItemID entityID, QString filterURL) {
|
|||
_filterDataMap.insert(entityID, filterData);
|
||||
_lock.unlock();
|
||||
|
||||
auto scriptRequest = ResourceManager::createResourceRequest(this, scriptURL);
|
||||
auto scriptRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, scriptURL);
|
||||
if (!scriptRequest) {
|
||||
qWarning() << "Could not create ResourceRequest for Entity Edit filter script at" << scriptURL.toString();
|
||||
scriptRequestFinished(entityID);
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
#include <PhysicsHelpers.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SharedUtil.h> // usecTimestampNow()
|
||||
#include <SoundCache.h>
|
||||
#include <LogHandler.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
#include "EntitiesLogging.h"
|
||||
|
@ -988,21 +988,6 @@ void EntityItem::setCollisionSoundURL(const QString& value) {
|
|||
}
|
||||
}
|
||||
|
||||
SharedSoundPointer EntityItem::getCollisionSound() {
|
||||
SharedSoundPointer result;
|
||||
withReadLock([&] {
|
||||
result = _collisionSound;
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
result = DependencyManager::get<SoundCache>()->getSound(_collisionSoundURL);
|
||||
withWriteLock([&] {
|
||||
_collisionSound = result;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::simulate(const quint64& now) {
|
||||
if (getLastSimulated() == 0) {
|
||||
setLastSimulated(now);
|
||||
|
@ -2650,12 +2635,6 @@ QString EntityItem::getCollisionSoundURL() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setCollisionSound(SharedSoundPointer sound) {
|
||||
withWriteLock([&] {
|
||||
_collisionSound = sound;
|
||||
});
|
||||
}
|
||||
|
||||
glm::vec3 EntityItem::getRegistrationPoint() const {
|
||||
glm::vec3 result;
|
||||
withReadLock([&] {
|
||||
|
|
|
@ -19,14 +19,13 @@
|
|||
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
|
||||
#include <shared/types/AnimationLoop.h> // for Animation, AnimationCache, and AnimationPointer classes
|
||||
#include <Octree.h> // for EncodeBitstreamParams class
|
||||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||
#include <OctreePacketData.h>
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
#include <ShapeInfo.h>
|
||||
#include <Transform.h>
|
||||
#include <Sound.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
#include <Interpolate.h>
|
||||
|
||||
|
@ -260,9 +259,6 @@ public:
|
|||
QString getCollisionSoundURL() const;
|
||||
void setCollisionSoundURL(const QString& value);
|
||||
|
||||
SharedSoundPointer getCollisionSound();
|
||||
void setCollisionSound(SharedSoundPointer sound);
|
||||
|
||||
glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity
|
||||
|
||||
/// registration point as ratio of entity
|
||||
|
@ -527,7 +523,6 @@ protected:
|
|||
quint64 _loadedScriptTimestamp { ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
|
||||
|
||||
QString _collisionSoundURL;
|
||||
SharedSoundPointer _collisionSound;
|
||||
glm::vec3 _registrationPoint;
|
||||
float _angularDamping;
|
||||
bool _visible;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntityItemID.h"
|
||||
#include <QtCore/QObject>
|
||||
#include <QDebug>
|
||||
|
||||
|
@ -17,7 +18,6 @@
|
|||
#include <UUID.h>
|
||||
|
||||
#include "RegisteredMetaTypes.h"
|
||||
#include "EntityItemID.h"
|
||||
|
||||
int entityItemIDTypeID = qRegisterMetaType<EntityItemID>();
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <ByteCountCoding.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityItem.h"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtCore/QObject>
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <VariantMapToScriptValue.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <SpatialParentFinder.h>
|
||||
#include <model-networking/MeshProxy.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityDynamicFactoryInterface.h"
|
||||
|
@ -298,18 +297,6 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
}
|
||||
|
||||
results = entity->getProperties(desiredProperties);
|
||||
|
||||
// TODO: improve naturalDimensions in the future,
|
||||
// for now we've added this hack for setting natural dimensions of models
|
||||
if (entity->getType() == EntityTypes::Model) {
|
||||
const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
|
||||
if (geometry) {
|
||||
Extents meshExtents = geometry->getUnscaledMeshExtents();
|
||||
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
|
||||
results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,11 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <QDateTime>
|
||||
#include "EntityTree.h"
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QQueue>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include <PerfStat.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "EntitySimulation.h"
|
||||
#include "VariantMapToScriptValue.h"
|
||||
|
||||
|
@ -55,9 +59,7 @@ public:
|
|||
|
||||
|
||||
EntityTree::EntityTree(bool shouldReaverage) :
|
||||
Octree(shouldReaverage),
|
||||
_fbxService(NULL),
|
||||
_simulation(NULL)
|
||||
Octree(shouldReaverage)
|
||||
{
|
||||
resetClientEditStats();
|
||||
}
|
||||
|
|
|
@ -41,13 +41,6 @@ public:
|
|||
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) = 0;
|
||||
};
|
||||
|
||||
class EntityItemFBXService {
|
||||
public:
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0;
|
||||
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) = 0;
|
||||
};
|
||||
|
||||
|
||||
class SendEntitiesOperationArgs {
|
||||
public:
|
||||
glm::vec3 root;
|
||||
|
@ -189,15 +182,6 @@ public:
|
|||
int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
|
||||
int processEraseMessageDetails(const QByteArray& buffer, const SharedNodePointer& sourceNode);
|
||||
|
||||
EntityItemFBXService* getFBXService() const { return _fbxService; }
|
||||
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
|
||||
const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) {
|
||||
return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL;
|
||||
}
|
||||
ModelPointer getModelForEntityItem(EntityItemPointer entityItem) {
|
||||
return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL;
|
||||
}
|
||||
|
||||
EntityTreeElementPointer getContainingElement(const EntityItemID& entityItemID) /*const*/;
|
||||
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElementPointer element);
|
||||
void debugDumpMap();
|
||||
|
@ -325,8 +309,6 @@ protected:
|
|||
_deletedEntityItemIDs << id;
|
||||
}
|
||||
|
||||
EntityItemFBXService* _fbxService;
|
||||
|
||||
mutable QReadWriteLock _entityToElementLock;
|
||||
QHash<EntityItemID, EntityTreeElementPointer> _entityToElementMap;
|
||||
|
||||
|
|
|
@ -9,17 +9,18 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntityTreeElement.h"
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <FBXReader.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <OctreeUtils.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityNodeData.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
#include "EntityTypes.h"
|
||||
|
||||
EntityTreeElement::EntityTreeElement(unsigned char* octalCode) : OctreeElement() {
|
||||
|
|
|
@ -9,12 +9,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "KeyLightPropertyGroup.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
#include <AnimationLoop.h>
|
||||
|
||||
#include "KeyLightPropertyGroup.h"
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(
|
|||
_animationLoop.setResetOnRunning(false);
|
||||
|
||||
_type = EntityTypes::Model;
|
||||
_jointMappingCompleted = false;
|
||||
_lastKnownCurrentFrame = -1;
|
||||
_color[0] = _color[1] = _color[2] = 0;
|
||||
}
|
||||
|
@ -204,30 +203,6 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
}
|
||||
|
||||
|
||||
void ModelEntityItem::mapJoints(const QStringList& modelJointNames) {
|
||||
// if we don't have animation, or we're already joint mapped then bail early
|
||||
if (!hasAnimation() || jointsMapped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_animation || _animation->getURL().toString() != getAnimationURL()) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(getAnimationURL());
|
||||
}
|
||||
|
||||
if (_animation && _animation->isLoaded()) {
|
||||
QStringList animationJointNames = _animation->getJointNames();
|
||||
|
||||
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
|
||||
_jointMapping.resize(modelJointNames.size());
|
||||
for (int i = 0; i < modelJointNames.size(); i++) {
|
||||
_jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]);
|
||||
}
|
||||
_jointMappingCompleted = true;
|
||||
_jointMappingURL = _animationProperties.getURL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelEntityItem::isAnimatingSomething() const {
|
||||
return getAnimationIsPlaying() &&
|
||||
getAnimationFPS() != 0.0f &&
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#ifndef hifi_ModelEntityItem_h
|
||||
#define hifi_ModelEntityItem_h
|
||||
|
||||
#include <AnimationLoop.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "AnimationPropertyGroup.h"
|
||||
|
||||
|
@ -103,10 +101,7 @@ public:
|
|||
void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
|
||||
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
void mapJoints(const QStringList& modelJointNames);
|
||||
bool jointsMapped() const { return _jointMappingURL == getAnimationURL() && _jointMappingCompleted; }
|
||||
|
||||
AnimationPointer getAnimation() const { return _animation; }
|
||||
bool getAnimationIsPlaying() const { return _animationLoop.getRunning(); }
|
||||
float getAnimationCurrentFrame() const { return _animationLoop.getCurrentFrame(); }
|
||||
float getAnimationFPS() const { return _animationLoop.getFPS(); }
|
||||
|
@ -158,7 +153,6 @@ protected:
|
|||
QUrl _parsedModelURL;
|
||||
QString _compoundShapeURL;
|
||||
|
||||
AnimationPointer _animation;
|
||||
AnimationPropertyGroup _animationProperties;
|
||||
AnimationLoop _animationLoop;
|
||||
|
||||
|
@ -166,11 +160,6 @@ protected:
|
|||
QString _textures;
|
||||
|
||||
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
||||
|
||||
// used on client side
|
||||
bool _jointMappingCompleted;
|
||||
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
||||
QString _jointMappingURL;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelEntityItem_h
|
||||
|
|
|
@ -202,7 +202,7 @@ bool OBJReader::isValidTexture(const QByteArray &filename) {
|
|||
}
|
||||
QUrl candidateUrl = _url.resolved(QUrl(filename));
|
||||
|
||||
return ResourceManager::resourceExists(candidateUrl);
|
||||
return DependencyManager::get<ResourceManager>()->resourceExists(candidateUrl);
|
||||
}
|
||||
|
||||
void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
||||
|
@ -267,7 +267,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
}
|
||||
|
||||
std::tuple<bool, QByteArray> requestData(QUrl& url) {
|
||||
auto request = ResourceManager::createResourceRequest(nullptr, url);
|
||||
auto request = DependencyManager::get<ResourceManager>()->createResourceRequest(nullptr, url);
|
||||
|
||||
if (!request) {
|
||||
return std::make_tuple(false, QByteArray());
|
||||
|
|
|
@ -114,7 +114,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::gl::GLBackend::do_getQuery),
|
||||
|
||||
(&::gpu::gl::GLBackend::do_resetStages),
|
||||
|
||||
|
||||
(&::gpu::gl::GLBackend::do_disableContextViewCorrection),
|
||||
(&::gpu::gl::GLBackend::do_restoreContextViewCorrection),
|
||||
(&::gpu::gl::GLBackend::do_disableContextStereo),
|
||||
(&::gpu::gl::GLBackend::do_restoreContextStereo),
|
||||
|
||||
|
@ -374,6 +376,13 @@ void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) {
|
|||
resetStages();
|
||||
}
|
||||
|
||||
void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) {
|
||||
_transform._viewCorrectionEnabled = false;
|
||||
}
|
||||
|
||||
void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) {
|
||||
_transform._viewCorrectionEnabled = true;
|
||||
}
|
||||
|
||||
void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) {
|
||||
|
||||
|
|
|
@ -143,6 +143,10 @@ public:
|
|||
// Reset stages
|
||||
virtual void do_resetStages(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
|
||||
virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final;
|
||||
virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final;
|
||||
|
||||
|
@ -333,6 +337,8 @@ protected:
|
|||
bool _skybox { false };
|
||||
Transform _view;
|
||||
CameraCorrection _correction;
|
||||
bool _viewCorrectionEnabled{ true };
|
||||
|
||||
|
||||
Mat4 _projection;
|
||||
Vec4i _viewport { 0, 0, 1, 1 };
|
||||
|
@ -400,6 +406,7 @@ protected:
|
|||
bool _invalidProgram { false };
|
||||
|
||||
BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
|
||||
BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
|
||||
|
||||
State::Data _stateCache{ State::DEFAULT };
|
||||
State::Signature _stateSignatureCache { 0 };
|
||||
|
@ -409,6 +416,8 @@ protected:
|
|||
|
||||
PipelineStageState() {
|
||||
_cameraCorrectionBuffer.edit<CameraCorrection>() = CameraCorrection();
|
||||
_cameraCorrectionBufferIdentity.edit<CameraCorrection>() = CameraCorrection();
|
||||
_cameraCorrectionBufferIdentity._buffer->flush();
|
||||
}
|
||||
} _pipeline;
|
||||
|
||||
|
|
|
@ -77,8 +77,14 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) {
|
|||
if (_pipeline._invalidProgram) {
|
||||
glUseProgram(_pipeline._program);
|
||||
if (_pipeline._cameraCorrectionLocation != -1) {
|
||||
auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer);
|
||||
gl::GLBuffer* cameraCorrectionBuffer = nullptr;
|
||||
if (_transform._viewCorrectionEnabled) {
|
||||
cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer);
|
||||
} else {
|
||||
cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer);
|
||||
}
|
||||
glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection));
|
||||
|
||||
}
|
||||
(void) CHECK_GL_ERROR();
|
||||
_pipeline._invalidProgram = false;
|
||||
|
|
|
@ -102,7 +102,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
|
|||
|
||||
if (_invalidView) {
|
||||
// Apply the correction
|
||||
if (_viewIsCamera && _correction.correction != glm::mat4()) {
|
||||
if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) {
|
||||
// FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out?
|
||||
Transform result;
|
||||
_view.mult(result, _view, _correction.correction);
|
||||
|
|
|
@ -390,6 +390,13 @@ void Batch::resetStages() {
|
|||
ADD_COMMAND(resetStages);
|
||||
}
|
||||
|
||||
void Batch::disableContextViewCorrection() {
|
||||
ADD_COMMAND(disableContextViewCorrection);
|
||||
}
|
||||
|
||||
void Batch::restoreContextViewCorrection() {
|
||||
ADD_COMMAND(restoreContextViewCorrection);
|
||||
}
|
||||
|
||||
void Batch::disableContextStereo() {
|
||||
ADD_COMMAND(disableContextStereo);
|
||||
|
|
|
@ -217,6 +217,9 @@ public:
|
|||
// Reset the stage caches and states
|
||||
void resetStages();
|
||||
|
||||
void disableContextViewCorrection();
|
||||
void restoreContextViewCorrection();
|
||||
|
||||
void disableContextStereo();
|
||||
void restoreContextStereo();
|
||||
|
||||
|
@ -304,6 +307,9 @@ public:
|
|||
|
||||
COMMAND_resetStages,
|
||||
|
||||
COMMAND_disableContextViewCorrection,
|
||||
COMMAND_restoreContextViewCorrection,
|
||||
|
||||
COMMAND_disableContextStereo,
|
||||
COMMAND_restoreContextStereo,
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// MeshFace.cpp
|
||||
// libraries/model/src/model/
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-23
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "MeshFace.h"
|
||||
|
||||
|
||||
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult) {
|
||||
qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices);
|
||||
}
|
||||
|
||||
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector) {
|
||||
QScriptValue array = engine->newArray();
|
||||
for (int i = 0; i < vector.size(); i++) {
|
||||
array.setProperty(i, meshFaceToScriptValue(engine, vector.at(i)));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result) {
|
||||
int length = array.property("length").toInteger();
|
||||
result.clear();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
MeshFace meshFace = MeshFace();
|
||||
meshFaceFromScriptValue(array.property(i), meshFace);
|
||||
result << meshFace;
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// MeshFace.h
|
||||
// libraries/model/src/model/
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-23
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_MeshFace_h
|
||||
#define hifi_MeshFace_h
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <model/Geometry.h>
|
||||
|
||||
using MeshPointer = std::shared_ptr<model::Mesh>;
|
||||
|
||||
class MeshFace {
|
||||
|
||||
public:
|
||||
MeshFace() {}
|
||||
~MeshFace() {}
|
||||
|
||||
QVector<uint32_t> vertexIndices;
|
||||
// TODO -- material...
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MeshFace)
|
||||
Q_DECLARE_METATYPE(QVector<MeshFace>)
|
||||
|
||||
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace);
|
||||
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult);
|
||||
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector);
|
||||
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result);
|
||||
|
||||
|
||||
|
||||
#endif // hifi_MeshFace_h
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// MeshProxy.cpp
|
||||
// libraries/model/src/model/
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-22.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "MeshProxy.h"
|
||||
|
||||
|
||||
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) {
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership,
|
||||
QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) {
|
||||
out = qobject_cast<MeshProxy*>(value.toQObject());
|
||||
}
|
||||
|
||||
QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) {
|
||||
// QScriptValueList result;
|
||||
QScriptValue result = engine->newArray();
|
||||
int i = 0;
|
||||
foreach (MeshProxy* const meshProxy, in) {
|
||||
result.setProperty(i++, meshToScriptValue(engine, meshProxy));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) {
|
||||
QScriptValueIterator itr(value);
|
||||
|
||||
qDebug() << "in meshesFromScriptValue, value.length =" << value.property("length").toInt32();
|
||||
|
||||
while(itr.hasNext()) {
|
||||
itr.next();
|
||||
MeshProxy* meshProxy = qscriptvalue_cast<MeshProxyList::value_type>(itr.value());
|
||||
if (meshProxy) {
|
||||
out.append(meshProxy);
|
||||
} else {
|
||||
qDebug() << "null meshProxy";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
//
|
||||
// MeshProxy.h
|
||||
// libraries/model/src/model/
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_MeshProxy_h
|
||||
#define hifi_MeshProxy_h
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <model/Geometry.h>
|
||||
|
||||
using MeshPointer = std::shared_ptr<model::Mesh>;
|
||||
|
||||
class MeshProxy : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MeshProxy(MeshPointer mesh) : _mesh(mesh) {}
|
||||
~MeshProxy() {}
|
||||
|
||||
MeshPointer getMeshPointer() const { return _mesh; }
|
||||
|
||||
Q_INVOKABLE int getNumVertices() const { return (int)_mesh->getNumVertices(); }
|
||||
Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); }
|
||||
|
||||
|
||||
protected:
|
||||
MeshPointer _mesh;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MeshProxy*);
|
||||
|
||||
class MeshProxyList : public QList<MeshProxy*> {}; // typedef and using fight with the Qt macros/templates, do this instead
|
||||
Q_DECLARE_METATYPE(MeshProxyList);
|
||||
|
||||
|
||||
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in);
|
||||
void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out);
|
||||
|
||||
QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in);
|
||||
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out);
|
||||
|
||||
#endif // hifi_MeshProxy_h
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// SimpleMeshProxy.cpp
|
||||
// libraries/model-networking/src/model-networking/
|
||||
//
|
||||
// Created by Seth Alves on 2017-3-22.
|
||||
// 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
|
||||
//
|
||||
|
||||
#include "SimpleMeshProxy.h"
|
||||
|
||||
#include <model/Geometry.h>
|
||||
|
||||
MeshPointer SimpleMeshProxy::getMeshPointer() const {
|
||||
return _mesh;
|
||||
}
|
||||
|
||||
int SimpleMeshProxy::getNumVertices() const {
|
||||
return (int)_mesh->getNumVertices();
|
||||
}
|
||||
|
||||
glm::vec3 SimpleMeshProxy::getPos3(int index) const {
|
||||
return _mesh->getPos3(index);
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// SimpleMeshProxy.h
|
||||
// libraries/model-networking/src/model-networking/
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_SimpleMeshProxy_h
|
||||
#define hifi_SimpleMeshProxy_h
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
class SimpleMeshProxy : public MeshProxy {
|
||||
public:
|
||||
SimpleMeshProxy(const MeshPointer& mesh) : _mesh(mesh) { }
|
||||
|
||||
MeshPointer getMeshPointer() const override;
|
||||
|
||||
int getNumVertices() const override;
|
||||
|
||||
glm::vec3 getPos3(int index) const override;
|
||||
|
||||
|
||||
protected:
|
||||
const MeshPointer _mesh;
|
||||
};
|
||||
|
||||
#endif // hifi_SimpleMeshProxy_h
|
|
@ -384,7 +384,7 @@ void NetworkTexture::makeRequest() {
|
|||
// Add a fragment to the base url so we can identify the section of the ktx being requested when debugging
|
||||
// The actual requested url is _activeUrl and will not contain the fragment
|
||||
_url.setFragment("head");
|
||||
_ktxHeaderRequest = ResourceManager::createResourceRequest(this, _activeUrl);
|
||||
_ktxHeaderRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl);
|
||||
|
||||
if (!_ktxHeaderRequest) {
|
||||
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
|
||||
|
@ -454,7 +454,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
|
|||
|
||||
bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL;
|
||||
|
||||
_ktxMipRequest = ResourceManager::createResourceRequest(this, _activeUrl);
|
||||
_ktxMipRequest = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl);
|
||||
|
||||
if (!_ktxMipRequest) {
|
||||
qCWarning(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
|
||||
|
|
19
libraries/model/src/model/Forward.h
Normal file
19
libraries/model/src/model/Forward.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Forward.h
|
||||
// libraries/model/src/model
|
||||
//
|
||||
// Created by Bradley Austin Davis on 2017/06/21
|
||||
// Copyright 2013-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
|
||||
//
|
||||
#ifndef hifi_model_Forward_h
|
||||
#define hifi_model_Forward_h
|
||||
|
||||
namespace model {
|
||||
class Mesh;
|
||||
using MeshPointer = std::shared_ptr<Mesh>;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -13,7 +13,7 @@
|
|||
#include "AtpReply.h"
|
||||
|
||||
AtpReply::AtpReply(const QUrl& url, QObject* parent) :
|
||||
_resourceRequest(ResourceManager::createResourceRequest(parent, url)) {
|
||||
_resourceRequest(DependencyManager::get<ResourceManager>()->createResourceRequest(parent, url)) {
|
||||
setOperation(QNetworkAccessManager::GetOperation);
|
||||
|
||||
connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress);
|
||||
|
|
|
@ -672,7 +672,7 @@ void Resource::makeRequest() {
|
|||
|
||||
PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } });
|
||||
|
||||
_request = ResourceManager::createResourceRequest(this, _activeUrl);
|
||||
_request = DependencyManager::get<ResourceManager>()->createResourceRequest(this, _activeUrl);
|
||||
|
||||
if (!_request) {
|
||||
qCDebug(networking).noquote() << "Failed to get request for" << _url.toDisplayString();
|
||||
|
|
|
@ -24,10 +24,16 @@
|
|||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
QThread ResourceManager::_thread;
|
||||
ResourceManager::PrefixMap ResourceManager::_prefixMap;
|
||||
QMutex ResourceManager::_prefixMapLock;
|
||||
QString ResourceManager::_cacheDir;
|
||||
|
||||
ResourceManager::ResourceManager() {
|
||||
_thread.setObjectName("Resource Manager Thread");
|
||||
|
||||
auto assetClient = DependencyManager::set<AssetClient>(_cacheDir);
|
||||
assetClient->moveToThread(&_thread);
|
||||
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
void ResourceManager::setUrlPrefixOverride(const QString& prefix, const QString& replacement) {
|
||||
QMutexLocker locker(&_prefixMapLock);
|
||||
|
@ -75,16 +81,6 @@ QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) {
|
|||
return url;
|
||||
}
|
||||
|
||||
void ResourceManager::init() {
|
||||
_thread.setObjectName("Resource Manager Thread");
|
||||
|
||||
auto assetClient = DependencyManager::set<AssetClient>(_cacheDir);
|
||||
assetClient->moveToThread(&_thread);
|
||||
QObject::connect(&_thread, &QThread::started, assetClient.data(), &AssetClient::init);
|
||||
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
void ResourceManager::cleanup() {
|
||||
// cleanup the AssetClient thread
|
||||
DependencyManager::destroy<AssetClient>();
|
||||
|
|
|
@ -14,7 +14,11 @@
|
|||
|
||||
#include <functional>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QThread>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "ResourceRequest.h"
|
||||
|
||||
|
@ -24,34 +28,38 @@ const QString URL_SCHEME_HTTPS = "https";
|
|||
const QString URL_SCHEME_FTP = "ftp";
|
||||
const QString URL_SCHEME_ATP = "atp";
|
||||
|
||||
class ResourceManager {
|
||||
class ResourceManager: public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
ResourceManager();
|
||||
|
||||
static void setUrlPrefixOverride(const QString& prefix, const QString& replacement);
|
||||
static QString normalizeURL(const QString& urlString);
|
||||
static QUrl normalizeURL(const QUrl& url);
|
||||
void setUrlPrefixOverride(const QString& prefix, const QString& replacement);
|
||||
QString normalizeURL(const QString& urlString);
|
||||
QUrl normalizeURL(const QUrl& url);
|
||||
|
||||
static ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url);
|
||||
ResourceRequest* createResourceRequest(QObject* parent, const QUrl& url);
|
||||
|
||||
static void init();
|
||||
static void cleanup();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
// Blocking call to check if a resource exists. This function uses a QEventLoop internally
|
||||
// to return to the calling thread so that events can still be processed.
|
||||
static bool resourceExists(const QUrl& url);
|
||||
bool resourceExists(const QUrl& url);
|
||||
|
||||
// adjust where we persist the cache
|
||||
static void setCacheDir(const QString& cacheDir);
|
||||
void setCacheDir(const QString& cacheDir);
|
||||
|
||||
private:
|
||||
static QThread _thread;
|
||||
QThread _thread;
|
||||
|
||||
using PrefixMap = std::map<QString, QString>;
|
||||
|
||||
static PrefixMap _prefixMap;
|
||||
static QMutex _prefixMapLock;
|
||||
PrefixMap _prefixMap;
|
||||
QMutex _prefixMapLock;
|
||||
|
||||
static QString _cacheDir;
|
||||
QString _cacheDir;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,5 +11,5 @@
|
|||
#include "ResourceManager.h"
|
||||
|
||||
void ResourceScriptingInterface::overrideUrlPrefix(const QString& prefix, const QString& replacement) {
|
||||
ResourceManager::setUrlPrefixOverride(prefix, replacement);
|
||||
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride(prefix, replacement);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue