Merge branch 'master' of github.com:highfidelity/hifi into qt-launcher
|
@ -82,6 +82,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
}
|
||||
|
||||
_assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true);
|
||||
if (_assignmentServerSocket.isNull()) {
|
||||
qCCritical(assignment_client) << "PAGE: Couldn't resolve domain server address" << _assignmentServerHostname;
|
||||
}
|
||||
_assignmentServerSocket.setObjectName("AssignmentServer");
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
|
||||
|
@ -183,16 +186,21 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
// we want to check again for the local domain-server port in case the DS has restarted
|
||||
quint16 localAssignmentServerPort;
|
||||
if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) {
|
||||
if (localAssignmentServerPort != _assignmentServerSocket.getPort()) {
|
||||
qCDebug(assignment_client) << "Port for local assignment server read from shared memory is"
|
||||
<< localAssignmentServerPort;
|
||||
if (localAssignmentServerPort == 0) {
|
||||
qCWarning(assignment_client) << "ALERT: Server port from shared memory is 0";
|
||||
} else {
|
||||
if (localAssignmentServerPort != _assignmentServerSocket.getPort()) {
|
||||
qCDebug(assignment_client) << "Port for local assignment server read from shared memory is"
|
||||
<< localAssignmentServerPort;
|
||||
|
||||
_assignmentServerSocket.setPort(localAssignmentServerPort);
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
_assignmentServerSocket.setPort(localAssignmentServerPort);
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCWarning(assignment_client) << "Failed to read local assignment server port from shared memory"
|
||||
<< "- will send assignment request to previous assignment server socket.";
|
||||
qCWarning(assignment_client) << "ALERT: Failed to read local assignment server port from shared memory ("
|
||||
<< DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY
|
||||
<< ")- will send assignment request to previous assignment server socket.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +258,7 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<ReceivedMessa
|
|||
// Starts an event loop, and emits workerThread->started()
|
||||
workerThread->start();
|
||||
} else {
|
||||
qCWarning(assignment_client) << "Received an assignment that could not be unpacked. Re-requesting.";
|
||||
qCWarning(assignment_client) << "ALERT: Received an assignment that could not be unpacked. Re-requesting.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor
|
|||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE PROCESS_GROUP = createProcessGroup();
|
||||
void* PROCESS_GROUP = createProcessGroup();
|
||||
#endif
|
||||
|
||||
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
ThreadedAssignment* AssignmentFactory::unpackAssignment(ReceivedMessage& message) {
|
||||
|
||||
quint8 packedType;
|
||||
message.peekPrimitive(&packedType);
|
||||
if (message.peekPrimitive(&packedType) != sizeof(packedType)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Assignment::Type unpackedType = (Assignment::Type) packedType;
|
||||
|
||||
|
|
|
@ -265,7 +265,7 @@ static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
|||
void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||
quint64 start = usecTimestampNow();
|
||||
|
||||
if (node->getType() == NodeType::Agent && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) {
|
||||
if ((node->getType() == NodeType::Agent || node->getType() == NodeType::EntityScriptServer) && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) {
|
||||
broadcastAvatarDataToAgent(node);
|
||||
} else if (node->getType() == NodeType::DownstreamAvatarMixer) {
|
||||
broadcastAvatarDataToDownstreamMixer(node);
|
||||
|
@ -448,13 +448,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// or that somehow we haven't sent
|
||||
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
|
||||
++numAvatarsHeldBack;
|
||||
|
||||
// BUGZ-781 verbose debugging:
|
||||
auto usecLastTimeSent = destinationNodeData->getLastOtherAvatarEncodeTime(sourceAvatarNodeData->getNodeLocalID());
|
||||
if (usecLastTimeSent != 0 && startIgnoreCalculation - usecLastTimeSent > 10 * USECS_PER_SECOND) {
|
||||
qCDebug(avatars) << "Not sent avatar" << *sourceAvatarNode << "to Node" << *destinationNode << "in > 10 s";
|
||||
}
|
||||
|
||||
sendAvatar = false;
|
||||
} else if (lastSeqFromSender == 0) {
|
||||
// We have have not yet received any data about this avatar. Ignore it for now
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
class EntitySimulation;
|
||||
|
||||
/**jsdoc
|
||||
* The <code>EntityViewer</code> API provides a headless viewer for assignment client scripts, so that they can "see" entities
|
||||
* in order for them to be available in the {@link Entities} API.
|
||||
*
|
||||
* @namespace EntityViewer
|
||||
*
|
||||
* @hifi-assignment-client
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Updates the entities currently in view.
|
||||
* @function EntityViewer.queryOctree
|
||||
*/
|
||||
void queryOctree();
|
||||
|
@ -36,26 +37,30 @@ public slots:
|
|||
// setters for camera attributes
|
||||
|
||||
/**jsdoc
|
||||
* Sets the position of the view frustum.
|
||||
* @function EntityViewer.setPosition
|
||||
* @param {Vec3} position
|
||||
* @param {Vec3} position - The position of the view frustum.
|
||||
*/
|
||||
void setPosition(const glm::vec3& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the orientation of the view frustum.
|
||||
* @function EntityViewer.setOrientation
|
||||
* @param {Quat} orientation
|
||||
* @param {Quat} orientation - The orientation of the view frustum.
|
||||
*/
|
||||
void setOrientation(const glm::quat& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the radius of the center "keyhole" in the view frustum.
|
||||
* @function EntityViewer.setCenterRadius
|
||||
* @param {number} radius
|
||||
* @param {number} radius - The radius of the center "keyhole" in the view frustum.
|
||||
*/
|
||||
void setCenterRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the radius of the center "keyhole" in the view frustum.
|
||||
* @function EntityViewer.setKeyholeRadius
|
||||
* @param {number} radius
|
||||
* @param {number} radius - The radius of the center "keyhole" in the view frustum.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link EntityViewer.setCenterRadius|setCenterRadius}
|
||||
* instead.
|
||||
*/
|
||||
|
@ -66,33 +71,38 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* @function EntityViewer.setVoxelSizeScale
|
||||
* @param {number} sizeScale
|
||||
* @param {number} sizeScale - The voxel size scale.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void setVoxelSizeScale(float sizeScale) { _octreeQuery.setOctreeSizeScale(sizeScale) ; }
|
||||
|
||||
/**jsdoc
|
||||
* @function EntityViewer.setBoundaryLevelAdjust
|
||||
* @param {number} boundaryLevelAdjust
|
||||
* @param {number} boundaryLevelAdjust - The boundary level adjust factor.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _octreeQuery.setBoundaryLevelAdjust(boundaryLevelAdjust); }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the maximum number of entity packets to receive from the domain server per second.
|
||||
* @function EntityViewer.setMaxPacketsPerSecond
|
||||
* @param {number} maxPacketsPerSecond
|
||||
* @param {number} maxPacketsPerSecond - The maximum number of entity packets to receive per second.
|
||||
*/
|
||||
void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _octreeQuery.setMaxQueryPacketsPerSecond(maxPacketsPerSecond); }
|
||||
|
||||
// getters for camera attributes
|
||||
|
||||
/**jsdoc
|
||||
* Gets the position of the view frustum.
|
||||
* @function EntityViewer.getPosition
|
||||
* @returns {Vec3}
|
||||
* @returns {Vec3} The position of the view frustum.
|
||||
*/
|
||||
const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); }
|
||||
|
||||
/**jsdoc
|
||||
* Gets the orientation of the view frustum.
|
||||
* @function EntityViewer.getOrientation
|
||||
* @returns {Quat}
|
||||
* @returns {Quat} The orientation of the view frustum.
|
||||
*/
|
||||
const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); }
|
||||
|
||||
|
@ -101,26 +111,30 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* @function EntityViewer.getVoxelSizeScale
|
||||
* @returns {number}
|
||||
* @returns {number} The voxel size scale.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
float getVoxelSizeScale() const { return _octreeQuery.getOctreeSizeScale(); }
|
||||
|
||||
/**jsdoc
|
||||
* @function EntityViewer.getBoundaryLevelAdjust
|
||||
* @returns {number}
|
||||
* @returns {number} The boundary level adjust factor.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
int getBoundaryLevelAdjust() const { return _octreeQuery.getBoundaryLevelAdjust(); }
|
||||
|
||||
/**jsdoc
|
||||
* Gets the maximum number of entity packets to receive from the domain server per second.
|
||||
* @function EntityViewer.getMaxPacketsPerSecond
|
||||
* @returns {number}
|
||||
* @returns {number} The maximum number of entity packets to receive per second.
|
||||
*/
|
||||
int getMaxPacketsPerSecond() const { return _octreeQuery.getMaxQueryPacketsPerSecond(); }
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Gets the number of nodes in the octree.
|
||||
* @function EntityViewer.getOctreeElementsCount
|
||||
* @returns {number}
|
||||
* @returns {number} The number of nodes in the octree.
|
||||
*/
|
||||
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
|
||||
|
||||
|
|
|
@ -86,8 +86,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
|
||||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||
|
||||
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket");
|
||||
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
|
||||
|
@ -255,6 +253,7 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer<Recei
|
|||
void EntityScriptServer::run() {
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
|
||||
DependencyManager::set<EntityScriptServerServices>();
|
||||
DependencyManager::set<AvatarHashMap>();
|
||||
|
||||
// make sure we request our script once the agent connects to the domain
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -448,6 +447,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
|||
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
newEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarHashMap>().data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
|
@ -556,7 +556,51 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool
|
|||
}
|
||||
|
||||
void EntityScriptServer::sendStatsPacket() {
|
||||
QJsonObject statsObject;
|
||||
|
||||
QJsonObject octreeStats;
|
||||
octreeStats["elementCount"] = (double)OctreeElement::getNodeCount();
|
||||
octreeStats["internalElementCount"] = (double)OctreeElement::getInternalNodeCount();
|
||||
octreeStats["leafElementCount"] = (double)OctreeElement::getLeafNodeCount();
|
||||
statsObject["octree_stats"] = octreeStats;
|
||||
|
||||
QJsonObject scriptEngineStats;
|
||||
int numberRunningScripts = 0;
|
||||
const auto scriptEngine = _entitiesScriptEngine;
|
||||
if (scriptEngine) {
|
||||
numberRunningScripts = scriptEngine->getNumRunningEntityScripts();
|
||||
}
|
||||
scriptEngineStats["number_running_scripts"] = numberRunningScripts;
|
||||
statsObject["script_engine_stats"] = scriptEngineStats;
|
||||
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
QJsonObject nodesObject;
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
QJsonObject clientStats;
|
||||
const QString uuidString(uuidStringWithoutCurlyBraces(node->getUUID()));
|
||||
clientStats["node_type"] = NodeType::getNodeTypeName(node->getType());
|
||||
auto& nodeStats = node->getConnectionStats();
|
||||
|
||||
static const QString NODE_OUTBOUND_KBPS_STAT_KEY("outbound_kbit/s");
|
||||
static const QString NODE_INBOUND_KBPS_STAT_KEY("inbound_kbit/s");
|
||||
|
||||
// add the key to ask the domain-server for a username replacement, if it has it
|
||||
clientStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidString;
|
||||
|
||||
clientStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundKbps();
|
||||
clientStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundKbps();
|
||||
|
||||
using namespace std::chrono;
|
||||
const float statsPeriod = duration<float, seconds::period>(nodeStats.endTime - nodeStats.startTime).count();
|
||||
clientStats["unreliable_packet/s"] = (nodeStats.sentUnreliablePackets + nodeStats.receivedUnreliablePackets) / statsPeriod;
|
||||
clientStats["reliable_packet/s"] = (nodeStats.sentPackets + nodeStats.receivedPackets) / statsPeriod;
|
||||
|
||||
nodesObject[uuidString] = clientStats;
|
||||
});
|
||||
|
||||
statsObject["nodes"] = nodesObject;
|
||||
addPacketStatsAndSendStatsPacket(statsObject);
|
||||
}
|
||||
|
||||
void EntityScriptServer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
|
4
cmake/externals/wasapi/CMakeLists.txt
vendored
|
@ -6,8 +6,8 @@ if (WIN32)
|
|||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi11.zip
|
||||
URL_MD5 d0eb8489455e7f79d59155535a2c8861
|
||||
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi12.zip
|
||||
URL_MD5 9e2eef41165f85344808f754b48bf08d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
36
cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch
Normal file
|
@ -0,0 +1,36 @@
|
|||
From 7638b7c5a659dceb4e580ae87d4d60b00847ef94 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Emil=20Nord=C3=A9n?= <emilnorden@yahoo.se>
|
||||
Date: Sat, 4 May 2019 08:38:53 +0200
|
||||
Subject: [PATCH] fixed build on latest version of clang
|
||||
|
||||
---
|
||||
src/Bullet3Common/b3Vector3.h | 2 +-
|
||||
src/LinearMath/btVector3.h | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/Bullet3Common/b3Vector3.h b/src/Bullet3Common/b3Vector3.h
|
||||
index 56e6c13311..a70d68d6e1 100644
|
||||
--- a/src/Bullet3Common/b3Vector3.h
|
||||
+++ b/src/Bullet3Common/b3Vector3.h
|
||||
@@ -36,7 +36,7 @@ subject to the following restrictions:
|
||||
#pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255'
|
||||
#endif
|
||||
|
||||
-#define B3_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x))
|
||||
+#define B3_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff)
|
||||
//#define b3_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) )
|
||||
#define b3_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask))
|
||||
#define b3_splat3_ps(_a, _i) b3_pshufd_ps((_a), B3_SHUFFLE(_i, _i, _i, 3))
|
||||
diff --git a/src/LinearMath/btVector3.h b/src/LinearMath/btVector3.h
|
||||
index 61fd8d1e46..d65ed9808d 100644
|
||||
--- a/src/LinearMath/btVector3.h
|
||||
+++ b/src/LinearMath/btVector3.h
|
||||
@@ -36,7 +36,7 @@ subject to the following restrictions:
|
||||
#pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255'
|
||||
#endif
|
||||
|
||||
-#define BT_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x))
|
||||
+#define BT_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff)
|
||||
//#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) )
|
||||
#define bt_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask))
|
||||
#define bt_splat3_ps(_a, _i) bt_pshufd_ps((_a), BT_SHUFFLE(_i, _i, _i, 3))
|
|
@ -27,6 +27,7 @@ vcpkg_from_github(
|
|||
REF ab8f16961e19a86ee20c6a1d61f662392524cc77
|
||||
SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55
|
||||
HEAD_REF master
|
||||
PATCHES "bullet-git-fix-build-clang-8.patch"
|
||||
)
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
|
|
|
@ -14,9 +14,9 @@ elseif (WIN32)
|
|||
elseif (APPLE)
|
||||
vcpkg_download_distfile(
|
||||
WEBRTC_SOURCE_ARCHIVE
|
||||
URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-20190626-osx.tar.gz
|
||||
SHA512 fc70cec1b5ee87395137b7090f424e2fc2300fc17d744d5ffa1cf7aa0e0f1a069a9d72ba1ad2fb4a640ebeb6c218bda24351ba0083e1ff96c4a4b5032648a9d2
|
||||
FILENAME webrtc-20190626-osx.tar.gz
|
||||
URLS https://hifi-public.s3.amazonaws.com/seth/webrtc-m78-osx.tar.gz
|
||||
SHA512 8b547da921cc96f5c22b4253a1c9e707971bb627898fbdb6b238ef1318c7d2512e878344885c936d4bd6a66005cc5b63dfc3fa5ddd14f17f378dcaa17b5e25df
|
||||
FILENAME webrtc-m78-osx.tar.gz
|
||||
)
|
||||
else ()
|
||||
# else Linux desktop
|
||||
|
|
|
@ -136,6 +136,23 @@ function getCurrentDomainIDType() {
|
|||
return DOMAIN_ID_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
function isCloudDomain() {
|
||||
|
||||
if (!domainIDIsSet()) {
|
||||
return false;
|
||||
}
|
||||
if (typeof DomainInfo === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (DomainInfo === null) {
|
||||
return false;
|
||||
}
|
||||
if (typeof DomainInfo.cloud_domain !== "boolean") {
|
||||
return false;
|
||||
}
|
||||
return DomainInfo.cloud_domain;
|
||||
}
|
||||
|
||||
function showLoadingDialog(msg) {
|
||||
var message = '<div class="text-center">';
|
||||
message += '<span class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></span> ' + msg;
|
||||
|
|
|
@ -606,6 +606,9 @@ $(document).ready(function(){
|
|||
var address = DomainInfo.network_address === null ? '' : DomainInfo.network_address;
|
||||
var port = DomainInfo.network_port === null ? '' : DomainInfo.network_port;
|
||||
var modal_body = "<div class='form-group'>";
|
||||
if (isCloudDomain()) {
|
||||
modal_body += '<div style="color:red;font-weight: bold">Changing the network settings may actually break your domain.</div>';
|
||||
}
|
||||
if (includeAddress) {
|
||||
modal_body += "<label class='control-label'>Address</label>";
|
||||
modal_body += "<input type='text' id='network-address-input' class='form-control' value='" + address + "'>";
|
||||
|
@ -867,6 +870,10 @@ $(document).ready(function(){
|
|||
}
|
||||
}
|
||||
|
||||
if (getCurrentDomainIDType() === DOMAIN_ID_TYPE_TEMP) {
|
||||
$(Settings.DOMAIN_ID_SELECTOR).siblings('span').append(" <b>This is a temporary domain and will not be visible in your domain list.</b>");
|
||||
}
|
||||
|
||||
if (accessTokenIsSet()) {
|
||||
appendAddButtonToPlacesTable();
|
||||
}
|
||||
|
|
|
@ -385,7 +385,7 @@ void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
if (_iceServerAddr.isEmpty()) {
|
||||
qCWarning(domain_server_ice) << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
qCWarning(domain_server_ice) << "ALERT: Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
::exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -876,7 +876,7 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
nodeList->startSTUNPublicSocketUpdate();
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Cannot enable domain-server automatic networking without a domain ID."
|
||||
qCCritical(domain_server) << "PAGE: Cannot enable domain-server automatic networking without a domain ID."
|
||||
<< "Please add an ID to your config file or via the web interface.";
|
||||
return;
|
||||
}
|
||||
|
@ -1635,8 +1635,9 @@ void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply* requestRepl
|
|||
} else {
|
||||
const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000;
|
||||
|
||||
qCWarning(domain_server_ice) << "Failed to update ice-server address (" << _iceServerSocket << ") with High Fidelity Metaverse - error was"
|
||||
<< requestReply->errorString();
|
||||
qCWarning(domain_server_ice) << "PAGE: Failed to update ice-server address (" << _iceServerSocket <<
|
||||
") with Metaverse (" << requestReply->url() << ") (critical error for auto-networking) error:" <<
|
||||
requestReply->errorString();
|
||||
qCWarning(domain_server_ice) << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds";
|
||||
|
||||
QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI()));
|
||||
|
@ -3450,8 +3451,9 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
|
|||
// we ended up with an empty list since everything we've tried has failed
|
||||
// so clear the set of failed addresses and start going through them again
|
||||
|
||||
qCWarning(domain_server_ice) << "All current ice-server addresses have failed - re-attempting all current addresses for"
|
||||
<< _iceServerAddr;
|
||||
qCWarning(domain_server_ice) <<
|
||||
"PAGE: All current ice-server addresses have failed - re-attempting all current addresses for"
|
||||
<< _iceServerAddr;
|
||||
|
||||
_failedIceServerAddresses.clear();
|
||||
candidateICEAddresses = _iceServerAddresses;
|
||||
|
|
|
@ -134,7 +134,7 @@ endif()
|
|||
downloadVcpkg = True
|
||||
|
||||
if not downloadVcpkg and not os.path.isfile(self.exe):
|
||||
print("Missing executable, boostrapping")
|
||||
print("Missing executable, boot-strapping")
|
||||
downloadVcpkg = True
|
||||
|
||||
# Make sure we have a vcpkg executable
|
||||
|
|
BIN
interface/resources/avatar/animations/sitting_idle04.fbx
Normal file
BIN
interface/resources/avatar/animations/sitting_idle05.fbx
Normal file
|
@ -78,6 +78,15 @@
|
|||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
|
@ -113,7 +122,16 @@
|
|||
"when": ["Application.CameraFirstPerson", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
["Keyboard.D"]
|
||||
|
@ -149,6 +167,15 @@
|
|||
"when": "Application.CameraFirstPerson",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraFirstPersonLookat",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
|
@ -222,10 +249,12 @@
|
|||
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
|
|
|
@ -33,6 +33,12 @@ Item {
|
|||
property var item: null
|
||||
|
||||
function load(url, scriptUrl) {
|
||||
// Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375
|
||||
if (root.item != null) {
|
||||
root.item.url = "about:blank"
|
||||
root.item.destroy()
|
||||
root.item = null
|
||||
}
|
||||
QmlSurface.load("./controls/WebView.qml", root, function(newItem) {
|
||||
root.item = newItem
|
||||
root.item.url = url
|
||||
|
|
|
@ -580,8 +580,9 @@ Rectangle {
|
|||
sendToScript(msg);
|
||||
} else if (msg.method === "showInvalidatedLightbox") {
|
||||
lightboxPopup.titleText = "Item Invalidated";
|
||||
lightboxPopup.bodyText = 'Your item is marked "invalidated" because this item has been suspended ' +
|
||||
"from the Marketplace due to a claim against its author.";
|
||||
lightboxPopup.bodyText = 'This item has been invalidated and is no longer available.<br>' +
|
||||
'If you have questions, please contact marketplace@highfidelity.com.<br>' +
|
||||
'Thank you!';
|
||||
lightboxPopup.button1text = "CLOSE";
|
||||
lightboxPopup.button1method = function() {
|
||||
lightboxPopup.visible = false;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
//
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import "../simplifiedConstants" as SimplifiedConstants
|
||||
import "../simplifiedControls" as SimplifiedControls
|
||||
import "./components" as AvatarAppComponents
|
||||
|
@ -79,7 +80,11 @@ Rectangle {
|
|||
errorText.text = "There was a problem while retrieving your inventory. " +
|
||||
"Please try closing and re-opening the Avatar app.\n\nInventory status: " + result.status + "\nMessage: " + result.message;
|
||||
} else if (result.data && result.data.assets && result.data.assets.length === 0 && avatarAppInventoryModel.count === 0) {
|
||||
errorText.text = "You have not created any avatars yet! Create an avatar with the Avatar Creator, then close and re-open the Avatar App."
|
||||
emptyInventoryContainer.visible = true;
|
||||
}
|
||||
|
||||
if (Settings.getValue("simplifiedUI/debugFTUE", 0) === 4) {
|
||||
emptyInventoryContainer.visible = true;
|
||||
}
|
||||
|
||||
avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result);
|
||||
|
@ -140,8 +145,95 @@ Rectangle {
|
|||
anchors.rightMargin: 24
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: emptyInventoryContainer
|
||||
visible: false
|
||||
anchors.top: displayNameHeader.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Flickable {
|
||||
id: emptyInventoryFlickable
|
||||
anchors.fill: parent
|
||||
contentWidth: parent.width
|
||||
contentHeight: emptyInventoryLayout.height
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: emptyInventoryLayout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 26
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 26
|
||||
spacing: 0
|
||||
|
||||
HifiStylesUit.GraphikSemiBold {
|
||||
text: "Stand out from the crowd!"
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: paintedHeight
|
||||
Layout.topMargin: 16
|
||||
size: 28
|
||||
color: simplifiedUI.colors.text.white
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
HifiStylesUit.GraphikRegular {
|
||||
text: "Create your custom avatar."
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: paintedHeight
|
||||
Layout.topMargin: 2
|
||||
size: 18
|
||||
wrapMode: Text.Wrap
|
||||
color: simplifiedUI.colors.text.white
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
id: avatarImage;
|
||||
source: "images/avatarProfilePic.png"
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: 450
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
mipmap: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Image {
|
||||
source: "images/qrCode.jpg"
|
||||
Layout.preferredWidth: 190
|
||||
Layout.preferredHeight: 190
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: -160
|
||||
mipmap: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
HifiStylesUit.GraphikSemiBold {
|
||||
text: "Scan for Mobile App"
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: paintedHeight
|
||||
Layout.topMargin: 12
|
||||
size: 28
|
||||
color: simplifiedUI.colors.text.white
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimplifiedControls.VerticalScrollBar {
|
||||
parent: emptyInventoryFlickable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
id: avatarInfoTextContainer
|
||||
visible: !emptyInventoryContainer.visible
|
||||
width: parent.implicitWidth
|
||||
height: childrenRect.height
|
||||
anchors.top: displayNameHeader.bottom
|
||||
|
@ -164,7 +256,7 @@ Rectangle {
|
|||
id: yourAvatarsSubtitle
|
||||
text: "These are the avatars that you've created and uploaded via the Avatar Creator."
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
wrapMode: Text.Wrap
|
||||
anchors.top: yourAvatarsTitle.bottom
|
||||
anchors.topMargin: 6
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
@ -208,9 +300,10 @@ Rectangle {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
visible: !emptyInventoryContainer.visible
|
||||
|
||||
AnimatedImage {
|
||||
visible: !inventoryContentsList.visible && !errorText.visible
|
||||
visible: !(inventoryContentsList.visible || errorText.visible)
|
||||
anchors.centerIn: parent
|
||||
width: 72
|
||||
height: width
|
||||
|
@ -271,6 +364,8 @@ Rectangle {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
root.avatarPreviewUrl = "../../images/defaultAvatar.svg";
|
||||
}
|
||||
|
||||
function fromScript(message) {
|
||||
|
|
After Width: | Height: | Size: 319 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 145 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 148 KiB |
|
@ -225,9 +225,9 @@ Flickable {
|
|||
SimplifiedControls.RadioButton {
|
||||
id: firstPerson
|
||||
text: "First Person View"
|
||||
checked: Camera.mode === "first person"
|
||||
checked: Camera.mode === "first person look at"
|
||||
onClicked: {
|
||||
Camera.mode = "first person"
|
||||
Camera.mode = "first person look at"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ Flickable {
|
|||
target: Camera
|
||||
|
||||
onModeUpdated: {
|
||||
if (Camera.mode === "first person") {
|
||||
if (Camera.mode === "first person look at") {
|
||||
firstPerson.checked = true
|
||||
} else if (Camera.mode === "look at") {
|
||||
thirdPerson.checked = true
|
||||
|
|
|
@ -54,8 +54,8 @@ Rectangle {
|
|||
|
||||
if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) &&
|
||||
topBarInventoryModel.count > 0) {
|
||||
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
|
||||
MyAvatar.useFullAvatarURL = topBarInventoryModel.get(0).download_url;
|
||||
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true);
|
||||
MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ Rectangle {
|
|||
if (isLoggedIn) {
|
||||
Commerce.getWalletStatus();
|
||||
} else {
|
||||
// Show some error to the user
|
||||
// Show some error to the user in the UI?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,12 +113,68 @@ Rectangle {
|
|||
topBarInventoryModel.getNextPage();
|
||||
} else {
|
||||
inventoryFullyReceived = true;
|
||||
var scriptExecutionCount = Settings.getValue("simplifiedUI/SUIScriptExecutionCount");
|
||||
var currentAvatarURL = MyAvatar.skeletonModelURL;
|
||||
var currentAvatarURLContainsDefaultAvatar = currentAvatarURL.indexOf("defaultAvatar") > -1;
|
||||
var currentAvatarURLContainsFST = currentAvatarURL.indexOf("fst") > -1;
|
||||
var currentAvatarURLContainsSimplifiedAvatar = currentAvatarURL.indexOf("simplifiedAvatar") > -1;
|
||||
var alreadyAutoSelectedAvatarFromInventory = Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false);
|
||||
var userHasValidAvatarInInventory = topBarInventoryModel.count > 0 &&
|
||||
topBarInventoryModel.get(0).download_url.indexOf(".fst") > -1;
|
||||
var simplifiedAvatarPrefix = "https://content.highfidelity.com/Experiences/Releases/simplifiedUI/simplifiedFTUE/avatars/simplifiedAvatar_";
|
||||
var simplifiedAvatarColors = ["Blue", "Cyan", "Green", "Magenta", "Red"];
|
||||
var simplifiedAvatarSuffix = "/avatar.fst";
|
||||
|
||||
// If we have an avatar in our inventory AND we haven't already auto-selected an avatar...
|
||||
if ((!Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatar", false) ||
|
||||
MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) {
|
||||
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
|
||||
MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url;
|
||||
// Use `Settings.setValue("simplifiedUI/debugFTUE", 0);` to turn off FTUE Debug Mode.
|
||||
// Use `Settings.setValue("simplifiedUI/debugFTUE", 1);` to debug FTUE Screen 1.
|
||||
// Use `Settings.setValue("simplifiedUI/debugFTUE", 2);` to debug FTUE Screen 2.
|
||||
// Use `Settings.setValue("simplifiedUI/debugFTUE", 3);` to debug FTUE Screen 3.
|
||||
// Use `Settings.setValue("simplifiedUI/debugFTUE", 4);` to force the UI to show what would happen if the user had an empty Inventory.
|
||||
|
||||
var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0);
|
||||
if (debugFTUE === 1 || debugFTUE === 2) {
|
||||
scriptExecutionCount = 1;
|
||||
currentAvatarURLContainsDefaultAvatar = true;
|
||||
if (debugFTUE === 1) {
|
||||
userHasValidAvatarInInventory = false;
|
||||
currentAvatarURLContainsSimplifiedAvatar = false;
|
||||
}
|
||||
} else if (debugFTUE === 3) {
|
||||
scriptExecutionCount = 2;
|
||||
currentAvatarURLContainsDefaultAvatar = false;
|
||||
currentAvatarURLContainsSimplifiedAvatar = true;
|
||||
}
|
||||
|
||||
// If we have never auto-selected and the user is still using a default avatar or if the current avatar is not valid (fst), or if
|
||||
// the current avatar is the old default (Woody), use top avatar from inventory or one of the new defaults.
|
||||
|
||||
// If the current avatar URL is invalid, OR the user is using the "default avatar" (Woody)...
|
||||
if (!currentAvatarURLContainsFST || currentAvatarURLContainsDefaultAvatar) {
|
||||
// If the user has a valid avatar in their inventory...
|
||||
if (userHasValidAvatarInInventory) {
|
||||
// ...use the first avatar in the user's inventory.
|
||||
MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url);
|
||||
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true);
|
||||
// Else if the user isn't wearing a "Simplified Avatar"
|
||||
} else if (!currentAvatarURLContainsSimplifiedAvatar) {
|
||||
// ...assign to the user a new "Simplified Avatar" (i.e. a simple avatar of random color)
|
||||
var avatarColor = simplifiedAvatarColors[Math.floor(Math.random() * simplifiedAvatarColors.length)];
|
||||
var simplifiedAvatarModelURL = simplifiedAvatarPrefix + avatarColor + simplifiedAvatarSuffix;
|
||||
MyAvatar.useFullAvatarURL(simplifiedAvatarModelURL);
|
||||
currentAvatarURLContainsSimplifiedAvatar = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptExecutionCount === 1) {
|
||||
sendToScript({
|
||||
"source": "SimplifiedTopBar.qml",
|
||||
"method": "displayInitialLaunchWindow"
|
||||
});
|
||||
} else if (scriptExecutionCount === 2 && currentAvatarURLContainsSimplifiedAvatar) {
|
||||
sendToScript({
|
||||
"source": "SimplifiedTopBar.qml",
|
||||
"method": "displaySecondLaunchWindow"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -384,6 +440,7 @@ Rectangle {
|
|||
placeholderTextColor: "#8E8E8E"
|
||||
font.pixelSize: 14
|
||||
placeholderText: width - leftPadding - rightPadding < goToTextFieldMetrics.width ? shortPlaceholderText : longPlaceholderText
|
||||
blankPlaceholderTextOnFocus: false
|
||||
clip: true
|
||||
selectByMouse: true
|
||||
autoScroll: true
|
||||
|
@ -555,7 +612,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
|
||||
function updatePreviewUrl() {
|
||||
function updatePreviewUrl() {
|
||||
var previewUrl = "";
|
||||
var downloadUrl = "";
|
||||
for (var i = 0; i < topBarInventoryModel.count; ++i) {
|
||||
|
@ -569,6 +626,8 @@ Rectangle {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
avatarButtonImage.source = "../images/defaultAvatar.svg";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -252,6 +252,7 @@
|
|||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <VersionHelpers.h>
|
||||
#include <Windows.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
@ -532,6 +533,11 @@ bool isDomainURL(QUrl url) {
|
|||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
static const UINT UWM_IDENTIFY_INSTANCES =
|
||||
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
|
||||
static const UINT UWM_SHOW_APPLICATION =
|
||||
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME"));
|
||||
|
||||
class MyNativeEventFilter : public QAbstractNativeEventFilter {
|
||||
public:
|
||||
static MyNativeEventFilter& getInstance() {
|
||||
|
@ -712,6 +718,7 @@ private:
|
|||
static const QString STATE_IN_HMD = "InHMD";
|
||||
static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM";
|
||||
static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson";
|
||||
static const QString STATE_CAMERA_FIRST_PERSON_LOOK_AT = "CameraFirstPersonLookat";
|
||||
static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson";
|
||||
static const QString STATE_CAMERA_ENTITY = "CameraEntity";
|
||||
static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent";
|
||||
|
@ -933,7 +940,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_FIRST_PERSON_LOOK_AT, STATE_CAMERA_THIRD_PERSON,
|
||||
STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE,
|
||||
STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED,
|
||||
STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } });
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
|
@ -968,6 +976,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
|
||||
QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "noteAwakening", Qt::QueuedConnection);
|
||||
});
|
||||
|
||||
|
||||
|
@ -1880,6 +1889,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON_LOOK_AT, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
|
||||
});
|
||||
|
@ -1989,7 +2001,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
settingsTimer->start();
|
||||
}, QThread::LowestPriority);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPersonLookAt)) {
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person.
|
||||
}
|
||||
|
||||
|
@ -2952,13 +2964,29 @@ Application::~Application() {
|
|||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not
|
||||
// fully understood whether it might cause other unknown crashes (B).
|
||||
//
|
||||
// (A) Although we try to shutdown the ScriptEngine threads in onAboutToQuit, there is
|
||||
// currently no guarantee that they have stopped. Waiting on them to stop has so far appeared to
|
||||
// never return on Mac, causing the application to hang on shutdown. Because ScriptEngines
|
||||
// may still be running, they may end up receiving events that are triggered from this processEvents call,
|
||||
// and then try to access resources that are no longer available at this point in time.
|
||||
// If the ScriptEngine threads were fully destroyed before getting here, this would
|
||||
// not be an issue.
|
||||
//
|
||||
// (B) It seems likely that a bunch of potential event handlers are dependent on Application
|
||||
// and other common dependencies to be available and not destroyed or in the middle of being
|
||||
// destroyed.
|
||||
|
||||
|
||||
// Clear the event queue before application is totally destructed.
|
||||
// This will drain the messasge queue of pending "deleteLaters" queued up
|
||||
// during shutdown of the script engines.
|
||||
// We do this here because there is a possiblty that [NSApplication terminate:]
|
||||
// will be called during processEvents which will invoke all static destructors.
|
||||
// We want to postpone this utill the last possible moment.
|
||||
QCoreApplication::processEvents();
|
||||
//QCoreApplication::processEvents();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -3604,16 +3632,18 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
// Using the latter will cause the camera to wobble with idle animations,
|
||||
// or with changes from the face tracker
|
||||
CameraMode mode = _myCamera.getMode();
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
if (isHMDMode()) {
|
||||
mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||
_myCamera.setPosition(extractTranslation(camMat));
|
||||
_myCamera.setOrientation(glmExtractRotation(camMat));
|
||||
}
|
||||
else {
|
||||
} else if (mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition());
|
||||
_myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation());
|
||||
} else {
|
||||
_myCamera.setPosition(myAvatar->getCameraEyesPosition(deltaTime));
|
||||
_myCamera.setOrientation(myAvatar->getLookAtRotation());
|
||||
}
|
||||
} else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
if (isHMDMode()) {
|
||||
|
@ -3647,9 +3677,9 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
if (mode == CAMERA_MODE_SELFIE) {
|
||||
lookAtRotation = lookAtRotation * glm::angleAxis(PI, myAvatar->getWorldOrientation() * Vectors::UP);
|
||||
}
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
_myCamera.setPosition(myAvatar->getLookAtPivotPoint()
|
||||
+ lookAtRotation * boomOffset);
|
||||
_myCamera.lookAt(myAvatar->getDefaultEyePosition());
|
||||
_myCamera.lookAt(myAvatar->getLookAtPivotPoint());
|
||||
}
|
||||
}
|
||||
} else if (mode == CAMERA_MODE_MIRROR) {
|
||||
|
@ -3677,8 +3707,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
+ glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0)
|
||||
+ mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ mirrorBodyOrientation * hmdOffset);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
const float YAW_SPEED = TWO_PI / 5.0f;
|
||||
float deltaYaw = userInputMapper->getActionState(controller::Action::YAW) * YAW_SPEED * deltaTime;
|
||||
|
@ -3699,8 +3728,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
_myCamera.setOrientation(cameraEntity->getWorldOrientation() * hmdRotation);
|
||||
glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setPosition(cameraEntity->getWorldPosition() + (hmdRotation * hmdOffset));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_myCamera.setOrientation(cameraEntity->getWorldOrientation());
|
||||
_myCamera.setPosition(cameraEntity->getWorldPosition());
|
||||
}
|
||||
|
@ -4403,7 +4431,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_1: {
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->triggerOption(MenuOption::FirstPerson);
|
||||
menu->triggerOption(MenuOption::FirstPersonLookAt);
|
||||
break;
|
||||
}
|
||||
case Qt::Key_2: {
|
||||
|
@ -4960,7 +4988,7 @@ extern "C" {
|
|||
CCHAR NumberOfProcessors;
|
||||
};
|
||||
|
||||
NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformation(
|
||||
NTSYSCALLAPI LONG NTAPI NtQuerySystemInformation(
|
||||
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
|
||||
_In_ ULONG SystemInformationLength,
|
||||
|
@ -4969,12 +4997,12 @@ extern "C" {
|
|||
|
||||
}
|
||||
template <typename T>
|
||||
NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, T& t) {
|
||||
LONG NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, T& t) {
|
||||
return NtQuerySystemInformation(SystemInformationClass, &t, (ULONG)sizeof(T), nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NTSTATUS NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, std::vector<T>& t) {
|
||||
LONG NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, std::vector<T>& t) {
|
||||
return NtQuerySystemInformation(SystemInformationClass, t.data(), (ULONG)(sizeof(T) * t.size()), nullptr);
|
||||
}
|
||||
|
||||
|
@ -5488,7 +5516,7 @@ void Application::loadSettings() {
|
|||
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD);
|
||||
} else {
|
||||
// if HMD is not active, only use first person if the menu option is checked
|
||||
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson);
|
||||
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonLookAt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5503,9 +5531,9 @@ void Application::loadSettings() {
|
|||
|
||||
// finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings
|
||||
// dictated that we should be in first person
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, isFirstPerson);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson);
|
||||
_myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_LOOK_AT);
|
||||
_myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON_LOOK_AT : CAMERA_MODE_LOOK_AT);
|
||||
cameraMenuChanged();
|
||||
|
||||
auto inputs = pluginManager->getInputPlugins();
|
||||
|
@ -5669,7 +5697,7 @@ void Application::pauseUntilLoginDetermined() {
|
|||
menu->getMenu("Developer")->setVisible(false);
|
||||
}
|
||||
_previousCameraMode = _myCamera.getMode();
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON_LOOK_AT);
|
||||
cameraModeChanged();
|
||||
|
||||
// disconnect domain handler.
|
||||
|
@ -5858,11 +5886,11 @@ void Application::cycleCamera() {
|
|||
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
|
||||
menu->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true);
|
||||
|
||||
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
} else if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) {
|
||||
|
||||
menu->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, false);
|
||||
menu->setIsOptionChecked(MenuOption::LookAtCamera, true);
|
||||
|
||||
} else if (menu->isOptionChecked(MenuOption::LookAtCamera)) {
|
||||
|
@ -5881,8 +5909,8 @@ void Application::cycleCamera() {
|
|||
|
||||
void Application::cameraModeChanged() {
|
||||
switch (_myCamera.getMode()) {
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
case CAMERA_MODE_FIRST_PERSON_LOOK_AT:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, true);
|
||||
break;
|
||||
case CAMERA_MODE_LOOK_AT:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true);
|
||||
|
@ -5902,12 +5930,12 @@ void Application::changeViewAsNeeded(float boomLength) {
|
|||
// This is called when the boom length has changed
|
||||
bool boomLengthGreaterThanMinimum = (boomLength > MyAvatar::ZOOM_MIN);
|
||||
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT && boomLengthGreaterThanMinimum) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true);
|
||||
cameraMenuChanged();
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_LOOK_AT && !boomLengthGreaterThanMinimum) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, false);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
@ -5915,9 +5943,9 @@ void Application::changeViewAsNeeded(float boomLength) {
|
|||
|
||||
void Application::cameraMenuChanged() {
|
||||
auto menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON_LOOK_AT) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON_LOOK_AT);
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN);
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::LookAtCamera)) {
|
||||
|
@ -9018,7 +9046,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
|||
}
|
||||
|
||||
if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) {
|
||||
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -91,12 +91,6 @@ namespace controller {
|
|||
class StateController;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
static const UINT UWM_IDENTIFY_INSTANCES =
|
||||
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
|
||||
static const UINT UWM_SHOW_APPLICATION =
|
||||
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME"));
|
||||
#endif
|
||||
|
||||
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
|
||||
static const QString SCRIPTS_SWITCH = "scripts";
|
||||
|
|
|
@ -20,7 +20,7 @@ class FancyCamera : public Camera {
|
|||
|
||||
/**jsdoc
|
||||
* The <code>Camera</code> API provides access to the "camera" that defines your view in desktop and HMD display modes.
|
||||
* The High Fidelity camera has axes <code>x</code> = right, <code>y</code> = up, </code>-z</code> = forward.
|
||||
* The High Fidelity camera has axes <code>x</code> = right, <code>y</code> = up, <code>-z</code> = forward.
|
||||
*
|
||||
* @namespace Camera
|
||||
*
|
||||
|
|
|
@ -172,7 +172,7 @@ Menu::Menu() {
|
|||
|
||||
// View > First Person
|
||||
auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::FirstPerson, 0,
|
||||
viewMenu, MenuOption::FirstPersonLookAt, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
@ -640,11 +640,6 @@ Menu::Menu() {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Developer >> Tests >>>
|
||||
MenuWrapper* testMenu = developerMenu->addMenu("Tests");
|
||||
addActionToQMenuAndActionHash(testMenu, MenuOption::RunClientScriptTests, 0, dialogsManager.data(), SLOT(showTestingResults()));
|
||||
|
||||
// Developer > Timing >>>
|
||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
|
@ -829,7 +824,7 @@ Menu::Menu() {
|
|||
// Help > Release Notes
|
||||
action = addActionToQMenuAndActionHash(helpMenu, "Release Notes");
|
||||
connect(action, &QAction::triggered, qApp, [] {
|
||||
QDesktopServices::openUrl(QUrl("http://steamcommunity.com/games/390540/announcements/"));
|
||||
QDesktopServices::openUrl(QUrl("https://docs.highfidelity.com/release-notes.html"));
|
||||
});
|
||||
|
||||
// Help > Report a Bug!
|
||||
|
|
|
@ -111,7 +111,8 @@ namespace MenuOption {
|
|||
const QString ExpandSimulationTiming = "Expand /simulation";
|
||||
const QString ExpandPhysicsTiming = "Expand /physics";
|
||||
const QString ExpandUpdateTiming = "Expand /update";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString FirstPerson = "First Person Legacy";
|
||||
const QString FirstPersonLookAt = "First Person";
|
||||
const QString FirstPersonHMD = "Enter First Person Mode in HMD";
|
||||
const QString FivePointCalibration = "5 Point Calibration";
|
||||
const QString FixGaze = "Fix Gaze (no saccade)";
|
||||
|
@ -178,7 +179,6 @@ namespace MenuOption {
|
|||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts...";
|
||||
const QString RunClientScriptTests = "Run Client Script Tests";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
const QString ShowTrackedObjects = "Show Tracked Objects";
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
#include <DependencyManager.h>
|
||||
|
||||
/**jsdoc
|
||||
* The <code>SpeechRecognizer</code> API provides facilities to recognize voice commands.
|
||||
* <p>Speech recognition is enabled or disabled via the Developer > Scripting > Enable Speech Control API menu item or
|
||||
* the {@link SpeechRecognizer.setEnabled} method.</p>
|
||||
*
|
||||
* @namespace SpeechRecognizer
|
||||
*
|
||||
* @hifi-interface
|
||||
|
@ -40,36 +44,86 @@ public:
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables speech recognition.
|
||||
* @function SpeechRecognizer.setEnabled
|
||||
* @param {boolean} enabled
|
||||
* @param {boolean} enabled - <code>true</code> to enable speech recognition, <code>false</code> to disable.
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Adds a voice command to the speech recognizer.
|
||||
* @function SpeechRecognizer.addCommand
|
||||
* @param {string} command
|
||||
* @param {string} command - The voice command to recognize.
|
||||
*/
|
||||
void addCommand(const QString& command);
|
||||
|
||||
/**jsdoc
|
||||
* Removes a voice command from the speech recognizer.
|
||||
* @function SpeechRecognizer.removeCommand
|
||||
* @param {string} command
|
||||
* @param {string} command - The voice command to stop recognizing.
|
||||
*/
|
||||
void removeCommand(const QString& command);
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when a voice command has been recognized.
|
||||
* @function SpeechRecognizer.commandRecognized
|
||||
* @param {string} command
|
||||
* @param {string} command - The voice command recognized.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Turn your avatar upon voice command.</caption>
|
||||
* var TURN_LEFT = "turn left";
|
||||
* var TURN_RIGHT = "turn right";
|
||||
* var TURN_RATE = 0.5;
|
||||
* var TURN_DURATION = 1000; // ms
|
||||
* var turnRate = 0;
|
||||
*
|
||||
* function getTurnRate() {
|
||||
* return turnRate;
|
||||
* }
|
||||
*
|
||||
* var MAPPING_NAME = "com.highfidelity.controllers.example.speechRecognizer";
|
||||
* var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
*
|
||||
* mapping.from(getTurnRate).to(Controller.Actions.Yaw);
|
||||
* Controller.enableMapping(MAPPING_NAME);
|
||||
*
|
||||
* function onCommandRecognized(command) {
|
||||
* print("Speech command: " + command);
|
||||
* switch (command) {
|
||||
* case TURN_LEFT:
|
||||
* turnRate = -TURN_RATE;
|
||||
* break;
|
||||
* case TURN_RIGHT:
|
||||
* turnRate = TURN_RATE;
|
||||
* break;
|
||||
* }
|
||||
* Script.setTimeout(function () {
|
||||
* turnRate = 0;
|
||||
* }, TURN_DURATION);
|
||||
* }
|
||||
*
|
||||
* SpeechRecognizer.addCommand(TURN_LEFT);
|
||||
* SpeechRecognizer.addCommand(TURN_RIGHT);
|
||||
* SpeechRecognizer.commandRecognized.connect(onCommandRecognized);
|
||||
*
|
||||
* Script.scriptEnding.connect(function () {
|
||||
* Controller.disableMapping(MAPPING_NAME);
|
||||
* SpeechRecognizer.removeCommand(TURN_LEFT);
|
||||
* SpeechRecognizer.removeCommand(TURN_RIGHT);
|
||||
* });
|
||||
*/
|
||||
void commandRecognized(const QString& command);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when speech recognition is enabled or disabled.
|
||||
* @function SpeechRecognizer.enabledUpdated
|
||||
* @param {boolean} enabled
|
||||
* @param {boolean} enabled - <code>true</code> if speech recognition is enabled, <code>false</code> if it is disabled.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when speech recognition is enabled or disabled.</caption>
|
||||
* SpeechRecognizer.enabledUpdated.connect(function (enabled) {
|
||||
* print("Speech recognition: " + (enabled ? "enabled" : "disabled"));
|
||||
* });
|
||||
*/
|
||||
void enabledUpdated(bool enabled);
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ static int triggerReactionNameToIndex(const QString& reactionName) {
|
|||
}
|
||||
|
||||
static int beginEndReactionNameToIndex(const QString& reactionName) {
|
||||
assert(NUM_AVATAR_BEGIN_END_REACTIONS == TRIGGER_REACTION_NAMES.size());
|
||||
assert(NUM_AVATAR_BEGIN_END_REACTIONS == BEGIN_END_REACTION_NAMES.size());
|
||||
return BEGIN_END_REACTION_NAMES.indexOf(reactionName);
|
||||
}
|
||||
|
||||
|
@ -958,7 +958,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
head->setScale(getModelScale());
|
||||
head->simulate(deltaTime);
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON ||
|
||||
mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
if (!_pointAtActive || !_isPointTargetValid) {
|
||||
updateHeadLookAt(deltaTime);
|
||||
} else {
|
||||
|
@ -2175,10 +2176,10 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP
|
|||
}
|
||||
|
||||
void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) {
|
||||
glm::vec3 myForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD;
|
||||
glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD;
|
||||
glm::vec3 myPosition = getHead()->getEyePosition();
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
myPosition = qApp->getCamera().getPosition();
|
||||
}
|
||||
|
||||
|
@ -2553,7 +2554,7 @@ void MyAvatar::clearWornAvatarEntities() {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* Information about an avatar entity.
|
||||
* <p>Information about an avatar entity.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Property</th><th>Type</th><th>Description</th></tr>
|
||||
|
@ -2718,7 +2719,8 @@ void MyAvatar::updateMotors() {
|
|||
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
|
||||
if (_characterController.getState() == CharacterController::State::Hover ||
|
||||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
|
||||
if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_SELFIE) {
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (!qApp->isHMDMode() && (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE)) {
|
||||
motorRotation = getLookAtRotation();
|
||||
} else {
|
||||
motorRotation = getMyHead()->getHeadOrientation();
|
||||
|
@ -3398,7 +3400,8 @@ bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
|
|||
|
||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
|
||||
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
|
||||
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT ||
|
||||
qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
|
||||
bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false;
|
||||
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
|
||||
return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead);
|
||||
|
@ -3442,8 +3445,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
// Smoothly rotate body with arrow keys
|
||||
float targetSpeed = getDriveKey(YAW) * _yawSpeed;
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
bool computeLookAt = (mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) && isReadyForPhysics() && !qApp->isHMDMode();
|
||||
if (computeLookAt) {
|
||||
bool computeLookAt = isReadyForPhysics() && !qApp->isHMDMode() &&
|
||||
(mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE);
|
||||
bool smoothCameraYaw = computeLookAt && mode != CAMERA_MODE_FIRST_PERSON_LOOK_AT;
|
||||
if (smoothCameraYaw) {
|
||||
// For "Look At" and "Selfie" camera modes we also smooth the yaw rotation from right-click mouse movement.
|
||||
float speedFromDeltaYaw = deltaTime > FLT_EPSILON ? getDriveKey(DELTA_YAW) / deltaTime : 0.0f;
|
||||
speedFromDeltaYaw *= _yawSpeed / YAW_SPEED_DEFAULT;
|
||||
|
@ -3472,7 +3477,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
}
|
||||
}
|
||||
float totalBodyYaw = _bodyYawDelta * deltaTime;
|
||||
if (!computeLookAt) {
|
||||
if (!smoothCameraYaw) {
|
||||
// Rotate directly proportional to delta yaw and delta pitch from right-click mouse movement.
|
||||
totalBodyYaw += getDriveKey(DELTA_YAW) * _yawSpeed / YAW_SPEED_DEFAULT;
|
||||
}
|
||||
|
@ -3557,18 +3562,22 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
if (faceForward || _shouldTurnToFaceCamera) {
|
||||
const float REORIENT_FORWARD_BLEND = 0.25f;
|
||||
const float REORIENT_TURN_BLEND = 0.03f;
|
||||
const float DIAGONAL_TURN_BLEND = 0.02f;
|
||||
const float DIAGONAL_TURN_BLEND = 0.1f;
|
||||
float blend = (_shouldTurnToFaceCamera ? REORIENT_TURN_BLEND : REORIENT_FORWARD_BLEND) * timeScale;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
}
|
||||
glm::quat faceRotation = _lookAtYaw;
|
||||
if (isMovingFwdBwd && isMovingSideways) {
|
||||
// Reorient avatar to face camera diagonal
|
||||
blend = DIAGONAL_TURN_BLEND;
|
||||
float turnSign = getDriveKey(TRANSLATE_Z) < 0.0f ? -1.0f : 1.0f;
|
||||
turnSign = getDriveKey(TRANSLATE_X) > 0.0f ? -turnSign : turnSign;
|
||||
faceRotation = _lookAtYaw * glm::angleAxis(turnSign * 0.25f * PI, Vectors::UP);
|
||||
if (isMovingFwdBwd) {
|
||||
if (isMovingSideways) {
|
||||
// Reorient avatar to face camera diagonal
|
||||
blend = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? 1.0f : DIAGONAL_TURN_BLEND;
|
||||
float turnSign = getDriveKey(TRANSLATE_Z) < 0.0f ? -1.0f : 1.0f;
|
||||
turnSign = getDriveKey(TRANSLATE_X) > 0.0f ? -turnSign : turnSign;
|
||||
faceRotation = _lookAtYaw * glm::angleAxis(turnSign * 0.25f * PI, Vectors::UP);
|
||||
} else if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) {
|
||||
blend = 1.0f;
|
||||
}
|
||||
}
|
||||
setWorldOrientation(glm::slerp(getWorldOrientation(), faceRotation, blend));
|
||||
} else if (isRotatingWhileSeated) {
|
||||
|
@ -3630,20 +3639,32 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
glm::vec3 avatarVectorRight = getWorldOrientation() * Vectors::RIGHT;
|
||||
float leftRightDot = glm::dot(cameraYawVector, avatarVectorRight);
|
||||
|
||||
const float REORIENT_ANGLE = 65.0f;
|
||||
const float DEFAULT_REORIENT_ANGLE = 65.0f;
|
||||
const float FIRST_PERSON_REORIENT_ANGLE = 95.0f;
|
||||
const float TRIGGER_REORIENT_ANGLE = 45.0f;
|
||||
const float FIRST_PERSON_TRIGGER_REORIENT_ANGLE = 65.0f;
|
||||
glm::vec3 ajustedYawVector = cameraYawVector;
|
||||
if (frontBackDot < 0.0f) {
|
||||
ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight);
|
||||
cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT;
|
||||
float limitAngle = 0.0f;
|
||||
float triggerAngle = -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE));
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) {
|
||||
limitAngle = glm::sin(glm::radians(90.0f - FIRST_PERSON_TRIGGER_REORIENT_ANGLE));
|
||||
triggerAngle = limitAngle;
|
||||
}
|
||||
float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? FIRST_PERSON_REORIENT_ANGLE : DEFAULT_REORIENT_ANGLE;
|
||||
if (frontBackDot < limitAngle) {
|
||||
if (frontBackDot < 0.0f) {
|
||||
ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight);
|
||||
cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT;
|
||||
}
|
||||
if (!isRotatingWhileSeated) {
|
||||
if (frontBackDot < -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE))) {
|
||||
if (frontBackDot < triggerAngle) {
|
||||
_shouldTurnToFaceCamera = true;
|
||||
_firstPersonSteadyHeadTimer = 0.0f;
|
||||
}
|
||||
} else {
|
||||
setWorldOrientation(previousOrientation);
|
||||
}
|
||||
} else if (frontBackDot > glm::sin(glm::radians(REORIENT_ANGLE))) {
|
||||
} else if (frontBackDot > glm::sin(glm::radians(reorientAngle))) {
|
||||
_shouldTurnToFaceCamera = false;
|
||||
}
|
||||
|
||||
|
@ -3664,6 +3685,22 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
_lookAtCameraTarget = targetPoint;
|
||||
}
|
||||
_headLookAtActive = true;
|
||||
const float FIRST_PERSON_RECENTER_SECONDS = 15.0f;
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) {
|
||||
if (getDriveKey(YAW) + getDriveKey(STEP_YAW) + getDriveKey(DELTA_YAW) == 0.0f) {
|
||||
if (_firstPersonSteadyHeadTimer < FIRST_PERSON_RECENTER_SECONDS) {
|
||||
if (_firstPersonSteadyHeadTimer > 0.0f) {
|
||||
_firstPersonSteadyHeadTimer += deltaTime;
|
||||
}
|
||||
} else {
|
||||
_shouldTurnToFaceCamera = true;
|
||||
_firstPersonSteadyHeadTimer = 0.0f;
|
||||
}
|
||||
} else {
|
||||
_firstPersonSteadyHeadTimer = deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
|
@ -3736,7 +3773,9 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig
|
|||
} else {
|
||||
// Desktop mode.
|
||||
direction = (zSpeed * forward) + (xSpeed * right);
|
||||
if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT && zSpeed != 0.0f && xSpeed != 0.0f){
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if ((mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_SELFIE) &&
|
||||
zSpeed != 0.0f && xSpeed != 0.0f && !isFlying()){
|
||||
direction = (zSpeed * forward);
|
||||
}
|
||||
|
||||
|
@ -4351,7 +4390,8 @@ bool MyAvatar::isFlying() {
|
|||
|
||||
bool MyAvatar::isInAir() {
|
||||
// If Avatar is Hover, Falling, or Taking off, they are in Air.
|
||||
return _characterController.getState() != CharacterController::State::Ground;
|
||||
return _characterController.getState() != CharacterController::State::Ground &&
|
||||
_characterController.getState() != CharacterController::State::Seated;
|
||||
}
|
||||
|
||||
bool MyAvatar::getFlyingEnabled() {
|
||||
|
@ -5399,7 +5439,7 @@ glm::quat MyAvatar::getOrientationForAudio() {
|
|||
case AudioListenerMode::FROM_HEAD: {
|
||||
// Using the camera's orientation instead, when the current mode is controlling the avatar's head.
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
bool headFollowsCamera = mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE;
|
||||
bool headFollowsCamera = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE;
|
||||
result = headFollowsCamera ? qApp->getCamera().getOrientation() : getHead()->getFinalOrientationInWorldFrame();
|
||||
break;
|
||||
}
|
||||
|
@ -6768,6 +6808,72 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) {
|
|||
_lookAtScriptTarget = lookAtTarget;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getLookAtPivotPoint() {
|
||||
glm::vec3 avatarUp = getWorldOrientation() * Vectors::UP;
|
||||
glm::vec3 yAxisEyePosition = getWorldPosition() + avatarUp * glm::dot(avatarUp, _skeletonModel->getDefaultEyeModelPosition());
|
||||
return yAxisEyePosition;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getCameraEyesPosition(float deltaTime) {
|
||||
glm::vec3 defaultEyesPosition = getLookAtPivotPoint();
|
||||
if (isFlying()) {
|
||||
return defaultEyesPosition;
|
||||
}
|
||||
glm::vec3 avatarFrontVector = getWorldOrientation() * Vectors::FRONT;
|
||||
glm::vec3 avatarUpVector = getWorldOrientation() * Vectors::UP;
|
||||
// Compute the offset between the default and real eye positions.
|
||||
glm::vec3 defaultEyesToEyesVector = getHead()->getEyePosition() - defaultEyesPosition;
|
||||
float FRONT_OFFSET_IDLE_MULTIPLIER = 2.5f;
|
||||
float FRONT_OFFSET_JUMP_MULTIPLIER = 1.5f;
|
||||
float frontOffset = FRONT_OFFSET_IDLE_MULTIPLIER * glm::length(defaultEyesPosition - getDefaultEyePosition());
|
||||
|
||||
// Looking down will aproximate move the camera forward to meet the real eye position
|
||||
float mixAlpha = glm::dot(_lookAtPitch * Vectors::FRONT, -avatarUpVector);
|
||||
bool isLanding = false;
|
||||
// When jumping the camera should follow the real eye on the Y coordenate
|
||||
float upOffset = 0.0f;
|
||||
if (isJumping() || _characterController.getState() == CharacterController::State::Takeoff) {
|
||||
upOffset = glm::dot(defaultEyesToEyesVector, avatarUpVector);
|
||||
frontOffset = glm::dot(defaultEyesToEyesVector, avatarFrontVector) * FRONT_OFFSET_JUMP_MULTIPLIER;
|
||||
mixAlpha = 1.0f;
|
||||
_landingAfterJumpTime = 0.0f;
|
||||
} else {
|
||||
// Limit the range effect from 45 to 0 degrees
|
||||
// between the front camera and the down vectors
|
||||
const float HEAD_OFFSET_RANGE_IN_DEGREES = 45.0f;
|
||||
const float HEAD_OFFSET_RANGE_OUT_DEGREES = 0.0f;
|
||||
float rangeIn = glm::cos(glm::radians(HEAD_OFFSET_RANGE_IN_DEGREES));
|
||||
float rangeOut = glm::cos(glm::radians(HEAD_OFFSET_RANGE_OUT_DEGREES));
|
||||
mixAlpha = mixAlpha < rangeIn ? 0.0f : (mixAlpha - rangeIn) / (rangeOut - rangeIn);
|
||||
const float WAIT_TO_LAND_TIME = 1.0f;
|
||||
if (_landingAfterJumpTime < WAIT_TO_LAND_TIME) {
|
||||
_landingAfterJumpTime += deltaTime;
|
||||
isLanding = true;
|
||||
}
|
||||
}
|
||||
const float FPS = 60.0f;
|
||||
float timeScale = deltaTime * FPS;
|
||||
frontOffset = frontOffset < 0.0f ? 0.0f : mixAlpha * frontOffset;
|
||||
glm::vec3 cameraOffset = upOffset * Vectors::UP + frontOffset * Vectors::FRONT;
|
||||
const float JUMPING_TAU = 0.1f;
|
||||
const float NO_JUMP_TAU = 0.3f;
|
||||
const float LANDING_TAU = 0.05f;
|
||||
float tau = NO_JUMP_TAU;
|
||||
if (isJumping()) {
|
||||
tau = JUMPING_TAU;
|
||||
} else if (isLanding) {
|
||||
tau = LANDING_TAU;
|
||||
}
|
||||
_cameraEyesOffset = _cameraEyesOffset + (cameraOffset - _cameraEyesOffset) * min(1.0f, tau * timeScale);
|
||||
glm::vec3 estimatedCameraPosition = defaultEyesPosition + getWorldOrientation() * _cameraEyesOffset;
|
||||
return estimatedCameraPosition;
|
||||
}
|
||||
|
||||
bool MyAvatar::isJumping() {
|
||||
return (_characterController.getState() == CharacterController::State::InAir ||
|
||||
_characterController.getState() == CharacterController::State::Takeoff) && !isFlying();
|
||||
}
|
||||
|
||||
bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
|
@ -6794,3 +6900,4 @@ void MyAvatar::resetPointAt() {
|
|||
POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -170,10 +170,12 @@ class MyAvatar : public Avatar {
|
|||
* collision. It can be a mono or stereo 16-bit WAV file running at either 24kHz or 48kHz. The latter is down-sampled
|
||||
* by the audio mixer, so all audio effectively plays back at a 24khz. 48kHz RAW files are also supported.
|
||||
* @property {number} audioListenerMode=0 - Specifies the listening position when hearing spatialized audio. Must be one
|
||||
* of the following property values:<br />
|
||||
* <code>Myavatar.audioListenerModeHead</code><br />
|
||||
* <code>Myavatar.audioListenerModeCamera</code><br />
|
||||
* <code>Myavatar.audioListenerModeCustom</code>
|
||||
* of the following property values:
|
||||
* <ul>
|
||||
* <li><code>MyAvatar.audioListenerModeHead</code></li>
|
||||
* <li><code>MyAvatar.audioListenerModeCamera</code></li>
|
||||
* <li><code>MyAvatar.audioListenerModeCustom</code></li>
|
||||
* </ul>
|
||||
* @property {number} audioListenerModeHead=0 - The audio listening position is at the avatar's head. <em>Read-only.</em>
|
||||
* @property {number} audioListenerModeCamera=1 - The audio listening position is at the camera. <em>Read-only.</em>
|
||||
* @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the
|
||||
|
@ -182,8 +184,8 @@ class MyAvatar : public Avatar {
|
|||
* property value is <code>audioListenerModeCustom</code>.
|
||||
* @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the
|
||||
* <code>audioListenerMode</code> property value is <code>audioListenerModeCustom</code>.
|
||||
* @property {boolean} hasScriptedBlendshapes=false - <code>true</code> to transmit blendshapes over the network.<br />
|
||||
* <strong>Note:</strong> Currently doesn't work. Use {@link MyAvatar.setForceFaceTrackerConnected} instead.
|
||||
* @property {boolean} hasScriptedBlendshapes=false - <code>true</code> to transmit blendshapes over the network.
|
||||
* <p><strong>Note:</strong> Currently doesn't work. Use {@link MyAvatar.setForceFaceTrackerConnected} instead.</p>
|
||||
* @property {boolean} hasProceduralBlinkFaceMovement=true - <code>true</code> if procedural blinking is turned on.
|
||||
* @property {boolean} hasProceduralEyeFaceMovement=true - <code>true</code> if procedural eye movement is turned on.
|
||||
* @property {boolean} hasAudioEnabledFaceMovement=true - <code>true</code> to move the mouth blendshapes with voice audio
|
||||
|
@ -241,8 +243,8 @@ class MyAvatar : public Avatar {
|
|||
* @property {boolean} useAdvancedMovementControls - Returns and sets the value of the Interface setting, Settings >
|
||||
* Controls > Walking. Note: Setting the value has no effect unless Interface is restarted.
|
||||
* @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Controls > Show room
|
||||
* boundaries while teleporting.<br />
|
||||
* <strong>Note:</strong> Setting the value has no effect unless Interface is restarted.
|
||||
* boundaries while teleporting.
|
||||
* <p><strong>Note:</strong> Setting the value has no effect unless Interface is restarted.</p>
|
||||
*
|
||||
* @property {number} yawSpeed=75 - The mouse X sensitivity value in Settings > General. <em>Read-only.</em>
|
||||
* @property {number} pitchSpeed=50 - The mouse Y sensitivity value in Settings > General. <em>Read-only.</em>
|
||||
|
@ -272,11 +274,12 @@ class MyAvatar : public Avatar {
|
|||
* the value.</p>
|
||||
* @property {number} analogPlusSprintSpeed - The sprint (run) speed of your avatar for the "AnalogPlus" control scheme.
|
||||
* @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior.
|
||||
* @property {number} isInSittingState - <code>true</code> if your avatar is sitting (avatar leaning is disabled,
|
||||
* recenntering is enabled), <code>false</code> if it is standing (avatar leaning is enabled, and avatar recenters if it
|
||||
* leans too far). If <code>userRecenterModel == 2</code> (i.e., auto) the property value automatically updates as the
|
||||
* user sits or stands, unless <code>isSitStandStateLocked == true</code>. Setting the property value overrides the
|
||||
* current siting / standing state, which is updated when the user next sits or stands unless
|
||||
* @property {number} isInSittingState - <code>true</code> if the user wearing the HMD is determined to be sitting
|
||||
* (avatar leaning is disabled, recenntering is enabled), <code>false</code> if the user wearing the HMD is
|
||||
* determined to be standing (avatar leaning is enabled, and avatar recenters if it leans too far).
|
||||
* If <code>userRecenterModel == 2</code> (i.e., auto) the property value automatically updates as the user sits
|
||||
* or stands, unless <code>isSitStandStateLocked == true</code>. Setting the property value overrides the current
|
||||
* siting / standing state, which is updated when the user next sits or stands unless
|
||||
* <code>isSitStandStateLocked == true</code>.
|
||||
* @property {boolean} isSitStandStateLocked - <code>true</code> to lock the avatar sitting/standing state, i.e., use this
|
||||
* to disable automatically changing state.
|
||||
|
@ -490,9 +493,10 @@ public:
|
|||
* <tr><td><code>2</code></td><td>Auto</td><td>Interface detects when the user is standing or seated in the real world.
|
||||
* Avatar leaning is disabled when the user is sitting (i.e., avatar always recenters), and avatar leaning is enabled
|
||||
* when the user is standing (i.e., avatar leans, then if leans too far it recenters).</td></tr>
|
||||
* <tr><td><code>3</code></td><td>DisableHMDLean</td><td>Both avatar leaning and recentering are disabled regardless of
|
||||
* <tr><td><code>3</code></td><td>DisableHMDLean</td><td><p>Both avatar leaning and recentering are disabled regardless of
|
||||
* what the user is doing in the real world and no matter what their avatar is doing in the virtual world. Enables
|
||||
* the avatar to sit on the floor when the user sits on the floor.<br /><strong>Note:</strong> Experimental.</td></tr>
|
||||
* the avatar to sit on the floor when the user sits on the floor.</p>
|
||||
* <p><strong>Note:</strong> Experimental.</p></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} MyAvatar.SitStandModelType
|
||||
|
@ -781,7 +785,7 @@ public:
|
|||
* additional properties specified when adding the different handlers.</p>
|
||||
* <p>A handler may change a value from <code>animStateDictionaryIn</code> or add different values in the
|
||||
* <code>animStateDictionaryOut</code> returned. Any property values set in <code>animStateDictionaryOut</code> will
|
||||
* override those of the internal animation machinery.</p.
|
||||
* override those of the internal animation machinery.</p>
|
||||
* @function MyAvatar.addAnimationStateHandler
|
||||
* @param {function} handler - The animation state handler function to add.
|
||||
* @param {Array<string>|null} propertiesList - The list of {@link MyAvatar.AnimStateDictionary|AnimStateDictionary}
|
||||
|
@ -1890,6 +1894,14 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void endSit(const glm::vec3& position, const glm::quat& rotation);
|
||||
|
||||
/**jsdoc
|
||||
* Gets whether the avatar is in a seated pose. The seated pose is set by calling the
|
||||
* MyAvatar::beginSit method.
|
||||
* @function MyAvatar.isSeated
|
||||
* @returns {boolean} <code>true</code> if the avatar is in a seated pose.
|
||||
*/
|
||||
Q_INVOKABLE bool isSeated() { return _characterController.getSeated(); }
|
||||
|
||||
int getOverrideJointCount() const;
|
||||
bool getFlowActive() const;
|
||||
bool getNetworkGraphActive() const;
|
||||
|
@ -1906,6 +1918,9 @@ public:
|
|||
void debugDrawPose(controller::Action action, const char* channelName, float size);
|
||||
|
||||
bool getIsJointOverridden(int jointIndex) const;
|
||||
glm::vec3 getLookAtPivotPoint();
|
||||
glm::vec3 getCameraEyesPosition(float deltaTime);
|
||||
bool isJumping();
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -2663,6 +2678,7 @@ private:
|
|||
bool _shouldTurnToFaceCamera { false };
|
||||
bool _scriptControlsHeadLookAt { false };
|
||||
float _scriptHeadControlTimer { 0.0f };
|
||||
float _firstPersonSteadyHeadTimer { 0.0f };
|
||||
bool _pointAtActive { false };
|
||||
bool _isPointTargetValid { true };
|
||||
|
||||
|
@ -2959,6 +2975,9 @@ private:
|
|||
|
||||
// used to prevent character from jumping after endSit is called.
|
||||
bool _endSitKeyPressComplete { false };
|
||||
|
||||
glm::vec3 _cameraEyesOffset;
|
||||
float _landingAfterJumpTime { 0.0f };
|
||||
};
|
||||
|
||||
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
|
||||
|
|
|
@ -315,6 +315,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
const float TALKING_TIME_THRESHOLD = 0.75f;
|
||||
params.isTalking = head->getTimeWithoutTalking() <= TALKING_TIME_THRESHOLD;
|
||||
|
||||
//pass X and Z input key floats (-1 to 1) to rig
|
||||
params.inputX = myAvatar->getDriveKey(MyAvatar::TRANSLATE_X);
|
||||
params.inputZ = myAvatar->getDriveKey(MyAvatar::TRANSLATE_Z);
|
||||
|
||||
myAvatar->updateRigControllerParameters(params);
|
||||
|
||||
_rig.updateFromControllerParameters(params, deltaTime);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "Profile.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
extern "C" {
|
||||
typedef int(__stdcall * CHECKMINSPECPROC) ();
|
||||
}
|
||||
|
|
|
@ -78,17 +78,17 @@ PickFilter getPickFilter(unsigned int filter) {
|
|||
* @property {boolean} [enabled=false] - <code>true</code> if this pick should start enabled, <code>false</code> if it should
|
||||
* start disabled. Disabled picks do not update their pick results.
|
||||
* @property {FilterFlags} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of <code>0.0</code>
|
||||
* means no maximum.
|
||||
* @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick.
|
||||
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint.
|
||||
* A value of <code>0</code> means no joint.<br />
|
||||
* <em>Used only if <code>parentID</code> is specified.</em>
|
||||
* A value of <code>0</code> means no joint.
|
||||
* <p><em>Used only if <code>parentID</code> is specified.</em></p>
|
||||
* @property {string} [joint] - <code>"Mouse"</code> parents the pick to the mouse; <code>"Avatar"</code> parents the pick to
|
||||
* the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not
|
||||
* parented to anything.<br />
|
||||
* <em>Used only if <code>parentID</code> is not specified.</em>
|
||||
* parented to anything.
|
||||
* <p><em>Used only if <code>parentID</code> is not specified.</em></p>
|
||||
* @property {Vec3} [position=Vec3.ZERO] - The offset of the ray origin from its parent if parented, otherwise the ray origin
|
||||
* in world coordinates.
|
||||
* @property {Vec3} [posOffset] - Synonym for <code>position</code>.
|
||||
|
@ -164,7 +164,7 @@ std::shared_ptr<PickQuery> PickScriptingInterface::buildRayPick(const QVariantMa
|
|||
* @property {boolean} [enabled=false] - <code>true</code> if this pick should start enabled, <code>false</code> if it should
|
||||
* start disabled. Disabled picks do not update their pick results.
|
||||
* @property {number} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* <p><strong>Note:</strong> Stylus picks do not intersect avatars or the HUD.</p>
|
||||
* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of <code>0.0</code>
|
||||
* means no maximum.
|
||||
|
@ -212,17 +212,17 @@ std::shared_ptr<PickQuery> PickScriptingInterface::buildStylusPick(const QVarian
|
|||
* @property {boolean} [enabled=false] - <code>true</code> if this pick should start enabled, <code>false</code> if it should
|
||||
* start disabled. Disabled picks do not update their pick results.
|
||||
* @property {number} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of <code>0.0</code>
|
||||
* means no maximum.
|
||||
* @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick.
|
||||
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint.
|
||||
* A value of <code>0</code> means no joint.<br />
|
||||
* <em>Used only if <code>parentID</code> is specified.</em>
|
||||
* A value of <code>0</code> means no joint.
|
||||
* <p><em>Used only if <code>parentID</code> is specified.</em></p>
|
||||
* @property {string} [joint] - <code>"Mouse"</code> parents the pick to the mouse; <code>"Avatar"</code> parents the pick to
|
||||
* the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not
|
||||
* parented to anything.
|
||||
* <em>Used only if <code>parentID</code> is not specified.</em>
|
||||
* <p><em>Used only if <code>parentID</code> is not specified.</em></p>
|
||||
* @property {Vec3} [position=Vec3.ZERO] - The offset of the parabola origin from its parent if parented, otherwise the
|
||||
* parabola origin in world coordinates.
|
||||
* @property {Vec3} [posOffset] - Synonym for <code>position</code>.
|
||||
|
@ -321,18 +321,18 @@ std::shared_ptr<PickQuery> PickScriptingInterface::buildParabolaPick(const QVari
|
|||
* @property {boolean} [enabled=false] - <code>true</code> if this pick should start enabled, <code>false</code> if it should
|
||||
* start disabled. Disabled picks do not update their pick results.
|
||||
* @property {FilterFlags} [filter=0] - The filter for this pick to use. Construct using {@link Picks} FilterFlags property
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* values (e.g., <code>Picks.PICK_DOMAIN_ENTITIES</code>) combined with <code>|</code> (bitwise OR) operators.
|
||||
* <p><strong>Note:</strong> Collision picks do not intersect the HUD.</p>
|
||||
* @property {number} [maxDistance=0.0] - The maximum distance at which this pick will intersect. A value of <code>0.0</code>
|
||||
* means no maximum.
|
||||
* @property {Uuid} [parentID] - The ID of the parent: an avatar, an entity, or another pick.
|
||||
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, an avatar joint.
|
||||
* A value of <code>0</code> means no joint.<br />
|
||||
* <em>Used only if <code>parentID</code> is specified.</em>
|
||||
* A value of <code>0</code> means no joint.
|
||||
* <p><em>Used only if <code>parentID</code> is specified.</em></p>
|
||||
* @property {string} [joint] - <code>"Mouse"</code> parents the pick to the mouse; <code>"Avatar"</code> parents the pick to
|
||||
* the user's avatar head; a joint name parents to the joint in the user's avatar; otherwise, the pick is "static", not
|
||||
* parented to anything.<br />
|
||||
* <em>Used only if <code>parentID</code> is not specified.</em>
|
||||
* parented to anything.
|
||||
* <p><em>Used only if <code>parentID</code> is not specified.</em></p>
|
||||
* @property {boolean} [scaleWithParent=true] - <code>true</code> to scale the pick's dimensions and threshold according to the
|
||||
* scale of the parent.
|
||||
*
|
||||
|
@ -344,9 +344,11 @@ std::shared_ptr<PickQuery> PickScriptingInterface::buildParabolaPick(const QVari
|
|||
* the collision region. The depth is in world coordinates but scales with the parent if defined.
|
||||
* @property {CollisionMask} [collisionGroup=8] - The type of objects the collision region collides as. Objects whose collision
|
||||
* masks overlap with the region's collision group are considered to be colliding with the region.
|
||||
* @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or {@link Picks.getPickScriptParameters}. A collision pick's type is {@link PickType.Collision}.
|
||||
* @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale (usually an avatar or an entity).
|
||||
* Its value is the original scale of the parent at the moment the pick was created, and is used to rescale the pick, and/or the pointer which owns this pick, if any.
|
||||
* @property {PickType} pickType - The type of pick when getting these properties from {@link Picks.getPickProperties} or
|
||||
* {@link Picks.getPickScriptParameters}. A collision pick's type is {@link PickType.Collision}.
|
||||
* @property {Vec3} baseScale - Returned from {@link Picks.getPickProperties} when the pick has a parent with varying scale
|
||||
* (usually an avatar or an entity). Its value is the original scale of the parent at the moment the pick was created, and
|
||||
* is used to rescale the pick, and/or the pointer which owns this pick, if any.
|
||||
*/
|
||||
std::shared_ptr<PickQuery> PickScriptingInterface::buildCollisionPick(const QVariantMap& propMap) {
|
||||
bool enabled = false;
|
||||
|
|
|
@ -287,7 +287,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* @function Picks.PICK_ENTITIES
|
||||
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_DOMAIN_ENTITIES |
|
||||
* Picks.PICK_AVATAR_ENTITIES</cpode> properties expression instead.
|
||||
* Picks.PICK_AVATAR_ENTITIES</code> properties expression instead.
|
||||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); }
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
* @property {Controller.Standard|Controller.Actions|function} action - The controller output or function that triggers the
|
||||
* events on the entity or overlay. If a function, it must return a number <code>>= 1.0</code> to start the action
|
||||
* and <code>< 1.0</code> to terminate the action.
|
||||
* @property {string} button - Which button to trigger.
|
||||
* @property {string} button - Which button to trigger:
|
||||
* <ul>
|
||||
* <li><code>"Primary"</code>, <code>"Secondary"</code>, and <code>"Tertiary"</code> cause {@link Entities} and
|
||||
* {@link Overlays} mouse pointer events. Other button names also cause mouse events but the <code>button</code>
|
||||
|
|
|
@ -50,12 +50,14 @@ class AccountServicesScriptingInterface : public QObject {
|
|||
* <code>"Unknown user"</code>. <em>Read-only.</em>
|
||||
* @property {boolean} loggedIn - <code>true</code> if the user is logged in, otherwise <code>false</code>.
|
||||
* <em>Read-only.</em>
|
||||
* @property {string} findableBy - The user's visibility to other users:<br />
|
||||
* <code>"none"</code> - user appears offline.<br />
|
||||
* <code>"friends"</code> - user is visible only to friends.<br />
|
||||
* <code>"connections"</code> - user is visible to friends and connections.<br />
|
||||
* <code>"all"</code> - user is visible to everyone.
|
||||
* @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in
|
||||
* @property {string} findableBy - The user's visibility to other users:
|
||||
* <ul>
|
||||
* <li><code>"none"</code> — user appears offline.</li>
|
||||
* <li><code>"friends"</code> — user is visible only to friends.</li>
|
||||
* <li><code>"connections"</code> — user is visible to friends and connections.</li>
|
||||
* <li><code>"all"</code> — user is visible to everyone.</li>
|
||||
* </ul>
|
||||
* @property {string} metaverseServerURL - The metaverse server that the user is authenticated against when logged in
|
||||
* — typically <code>"https://metaverse.highfidelity.com"</code>. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
|
@ -160,11 +162,13 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when the user's visibility to others changes.
|
||||
* @function AccountServices.findableByChanged
|
||||
* @param {string} findableBy - The user's visibility to other people:<br />
|
||||
* <code>"none"</code> - user appears offline.<br />
|
||||
* <code>"friends"</code> - user is visible only to friends.<br />
|
||||
* <code>"connections"</code> - user is visible to friends and connections.<br />
|
||||
* <code>"all"</code> - user is visible to everyone.
|
||||
* @param {string} findableBy - The user's visibility to other people:
|
||||
* <ul>
|
||||
* <li><code>"none"</code> — user appears offline.</li>
|
||||
* <li><code>"friends"</code> — user is visible only to friends.</li>
|
||||
* <li><code>"connections"</code> — user is visible to friends and connections.</li>
|
||||
* <li><code>"all"</code> — user is visible to everyone.</li>
|
||||
* </ul>
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when your visiblity changes.</caption>
|
||||
* AccountServices.findableByChanged.connect(function (findableBy) {
|
||||
|
|
|
@ -168,7 +168,6 @@ class ScriptEngine;
|
|||
* <td><code>startFarTrigger</code><br /><code>continueFarTrigger</code><br /><code>stopFarTrigger</code></td>
|
||||
* <td>These methods are called when a user is more than 0.3m away from the entity, the entity is triggerable, and the
|
||||
* user starts, continues, or stops squeezing the trigger.</td>
|
||||
* </td>
|
||||
* <td>A light switch that can be toggled on and off from a distance.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
@ -217,25 +216,25 @@ class ScriptEngine;
|
|||
*
|
||||
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
|
||||
* points in a {@link RouteObject} mapping. A synonym for <code>Controller.Hardware.Actions</code>.
|
||||
* <em>Read-only.</em><br /><br />
|
||||
* Default mappings are provided from the <code>Controller.Hardware.Keyboard</code> and <code>Controller.Standard</code> to
|
||||
* actions in
|
||||
* <em>Read-only.</em>
|
||||
* <p>Default mappings are provided from the <code>Controller.Hardware.Keyboard</code> and <code>Controller.Standard</code>
|
||||
* to actions in
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/keyboardMouse.json">
|
||||
* keyboardMouse.json</a> and
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/standard.json">
|
||||
* standard.json</a>, respectively.
|
||||
* standard.json</a>, respectively.</p>
|
||||
*
|
||||
* @property {Controller.Hardware} Hardware - Standard and hardware-specific controller and computer outputs, plus predefined
|
||||
* actions on Interface and the user's avatar. The outputs can be mapped to <code>Actions</code> or functions in a
|
||||
* {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped to <code>Standard</code>
|
||||
* controller outputs. <em>Read-only.</em>
|
||||
* {@link RouteObject} mapping. Additionally, hardware-specific controller outputs can be mapped to
|
||||
* <code>Controller.Standard</code> controller outputs. <em>Read-only.</em>
|
||||
*
|
||||
* @property {Controller.Standard} Standard - Standard controller outputs that can be mapped to <code>Actions</code> or
|
||||
* functions in a {@link RouteObject} mapping. <em>Read-only.</em><br /><br />
|
||||
* Each hardware device has a mapping from its outputs to <code>Controller.Standard</code> items, specified in a JSON file.
|
||||
* functions in a {@link RouteObject} mapping. <em>Read-only.</em>
|
||||
* <p>Each hardware device has a mapping from its outputs to <code>Controller.Standard</code> items, specified in a JSON file.
|
||||
* For example, <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/leapmotion.json">
|
||||
* leapmotion.json</a> and
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/vive.json">vive.json</a>.
|
||||
* <a href="https://github.com/highfidelity/hifi/blob/master/interface/resources/controllers/vive.json">vive.json</a>.</p>
|
||||
*/
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
|
|
|
@ -31,10 +31,10 @@
|
|||
* @property {InteractiveWindow.DockArea} RIGHT - Dock to the right edge of the Interface window.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A docking location of an <code>InteractiveWindow</code>.
|
||||
* <p>A docking location of an <code>InteractiveWindow</code>.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</p><th>Description</th>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>TOP</td><td>Dock to the top edge of the Interface window.</td></tr>
|
||||
|
@ -52,6 +52,23 @@ static const QVariantMap DOCK_AREA {
|
|||
{ "RIGHT", DockArea::RIGHT }
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* The possible "relative position anchors" of an <code>InteractiveWindow</code>. Used when defining the `relativePosition` property of an `InteractiveWindow`.
|
||||
* @typedef {object} InteractiveWindow.RelativePositionAnchors
|
||||
* @property {InteractiveWindow.RelativePositionAnchor} NO_ANCHOR - Specifies that the position of the `InteractiveWindow` will not be relative to any part of the Interface window.
|
||||
* @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window.
|
||||
* @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window.
|
||||
* @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window.
|
||||
* @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window.
|
||||
*/
|
||||
static const QVariantMap RELATIVE_POSITION_ANCHOR {
|
||||
{ "NO_ANCHOR", RelativePositionAnchor::NO_ANCHOR },
|
||||
{ "TOP_LEFT", RelativePositionAnchor::TOP_LEFT },
|
||||
{ "TOP_RIGHT", RelativePositionAnchor::TOP_RIGHT },
|
||||
{ "BOTTOM_RIGHT", RelativePositionAnchor::BOTTOM_RIGHT },
|
||||
{ "BOTTOM_LEFT", RelativePositionAnchor::BOTTOM_LEFT }
|
||||
};
|
||||
|
||||
DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted)
|
||||
: QObject(parent), _restricted(restricted) { }
|
||||
|
||||
|
@ -73,10 +90,10 @@ int DesktopScriptingInterface::getHeight() {
|
|||
* own separate window.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A display mode for an <code>InteractiveWindow</code>.
|
||||
* <p>A display mode for an <code>InteractiveWindow</code>.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</p><th>Description</th>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>VIRTUAL</td><td>The window is displayed inside Interface: in the desktop window in
|
||||
|
@ -99,6 +116,10 @@ QVariantMap DesktopScriptingInterface::getDockArea() {
|
|||
return DOCK_AREA;
|
||||
}
|
||||
|
||||
QVariantMap DesktopScriptingInterface::getRelativePositionAnchor() {
|
||||
return RELATIVE_POSITION_ANCHOR;
|
||||
}
|
||||
|
||||
void DesktopScriptingInterface::setHUDAlpha(float alpha) {
|
||||
qApp->getApplicationCompositor().setAlpha(alpha);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
* @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top,
|
||||
* bottom, left, or right of the Interface window.
|
||||
* <em>Read-only.</em>
|
||||
* @property {InteractiveWindow.RelativePositionAnchors} RelativePositionAnchor - The possible "relative position anchors" for an {@link InteractiveWindow}: top left,
|
||||
* top right, bottom right, or bottom left of the Interface window.
|
||||
* <em>Read-only.</em>
|
||||
*/
|
||||
class DesktopScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -50,6 +53,7 @@ class DesktopScriptingInterface : public QObject, public Dependency {
|
|||
|
||||
Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL)
|
||||
Q_PROPERTY(QVariantMap DockArea READ getDockArea CONSTANT FINAL)
|
||||
Q_PROPERTY(QVariantMap RelativePositionAnchor READ getRelativePositionAnchor CONSTANT FINAL)
|
||||
Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL)
|
||||
Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL)
|
||||
|
||||
|
@ -106,7 +110,7 @@ private:
|
|||
Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread);
|
||||
|
||||
static QVariantMap getDockArea();
|
||||
|
||||
static QVariantMap getRelativePositionAnchor();
|
||||
Q_INVOKABLE static QVariantMap getPresentationMode();
|
||||
const bool _restricted;
|
||||
};
|
||||
|
|
|
@ -24,7 +24,8 @@ class MenuItemProperties;
|
|||
* <h3>Groupings</h3>
|
||||
*
|
||||
* <p>A "grouping" provides a way to group a set of menus or menu items together so that they can all be set visible or invisible
|
||||
* as a group.</p> There is currently only one available group: <code>"Developer"</code>. This grouping can be toggled in the
|
||||
* as a group.</p>
|
||||
* <p>There is currently only one available group: <code>"Developer"</code>. This grouping can be toggled in the
|
||||
* "Settings" menu.</p>
|
||||
* <p>If a menu item doesn't belong to a group, it is always displayed.</p>
|
||||
*
|
||||
|
|
|
@ -82,11 +82,18 @@ void RenderScriptingInterface::forceShadowsEnabled(bool enabled) {
|
|||
_shadowsEnabled = (enabled);
|
||||
_shadowsEnabledSetting.set(enabled);
|
||||
|
||||
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
|
||||
auto renderConfig = qApp->getRenderEngine()->getConfiguration();
|
||||
assert(renderConfig);
|
||||
auto lightingModelConfig = renderConfig->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
|
||||
if (lightingModelConfig) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled);
|
||||
lightingModelConfig->setShadow(enabled);
|
||||
}
|
||||
auto secondaryLightingModelConfig = renderConfig->getConfig<MakeLightingModel>("RenderSecondView.LightingModel");
|
||||
if (secondaryLightingModelConfig) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled);
|
||||
secondaryLightingModelConfig->setShadow(enabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ SelectionScriptingInterface::SelectionScriptingInterface() {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* The type of a specific item in a selection list.
|
||||
* <p>The type of a specific item in a selection list.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Description</th></tr>
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
* @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. <em>Read-only.</em>
|
||||
* @property {boolean} limitedCommerce - <code>true</code> if Interface is running in limited commerce mode. In limited commerce
|
||||
* mode, certain Interface functionalities are disabled, e.g., users can't buy items that are not free from the Marketplace.
|
||||
* The Oculus Store version of Interface runs in limited commerce mode. <em>Read-only.</em>
|
||||
* The Oculus Store and Steam versions of Interface run in limited commerce mode. <em>Read-only.</em>
|
||||
*/
|
||||
class WalletScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -73,9 +73,9 @@ public:
|
|||
/**jsdoc
|
||||
* Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via
|
||||
* the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and
|
||||
* {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.<br />
|
||||
* <strong>Warning:</strong> Neither of these signals are triggered if the entity is not an avatar entity or is not
|
||||
* certified.
|
||||
* {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.
|
||||
* <p><strong>Warning:</strong> Neither of these signals are triggered if the entity is not an avatar entity or is not
|
||||
* certified.</p>
|
||||
* @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification
|
||||
* @param {Uuid} entityID - The avatar entity's ID.
|
||||
* @example <caption>Check the ownership of all nearby certified avatar entities.</caption>
|
||||
|
|
|
@ -326,10 +326,10 @@ public slots:
|
|||
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
|
||||
* dimensions is adjusted in order to match the aspect ratio.
|
||||
* @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user
|
||||
* name>-on-YYYY-MM-DD_HH-MM-SS".<br />
|
||||
* Still images are saved in JPEG or PNG format according to the extension provided — <code>".jpg"</code>,
|
||||
* name>-on-YYYY-MM-DD_HH-MM-SS".
|
||||
* <p>Still images are saved in JPEG or PNG format according to the extension provided — <code>".jpg"</code>,
|
||||
* <code>".jpeg"</code>, or <code>".png"</code> — or if not provided then in JPEG format with an extension of
|
||||
* <code>".jpg"</code>. Animated images are saved in GIF format.
|
||||
* <code>".jpg"</code>. Animated images are saved in GIF format.</p>
|
||||
*
|
||||
* @example <caption>Using the snapshot function and signals.</caption>
|
||||
* function onStillSnapshotTaken(path, notify) {
|
||||
|
@ -365,10 +365,10 @@ public slots:
|
|||
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
|
||||
* signal.
|
||||
* @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user
|
||||
* name>-on-YYYY-MM-DD_HH-MM-SS".<br />
|
||||
* Images are saved in JPEG or PNG format according to the extension provided — <code>".jpg"</code>,
|
||||
* name>-on-YYYY-MM-DD_HH-MM-SS".
|
||||
* <p>Images are saved in JPEG or PNG format according to the extension provided — <code>".jpg"</code>,
|
||||
* <code>".jpeg"</code>, or <code>".png"</code> — or if not provided then in JPEG format with an extension of
|
||||
* <code>".jpg"</code>.
|
||||
* <code>".jpg"</code>.</p>
|
||||
*/
|
||||
void takeSecondaryCameraSnapshot(const bool& notify = true, const QString& filename = QString());
|
||||
|
||||
|
@ -384,10 +384,10 @@ public slots:
|
|||
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
|
||||
* signal.
|
||||
* @param {string} [filename=""] - If a filename is not provided, the image is saved as "hifi-snap-by-<user
|
||||
* name>-on-YYYY-MM-DD_HH-MM-SS".<br />
|
||||
* Images are saved in JPEG or PNG format according to the extension provided — <code>".jpg"</code>,
|
||||
* name>-on-YYYY-MM-DD_HH-MM-SS".
|
||||
* <p>Images are saved in JPEG or PNG format according to the extension provided — <code>".jpg"</code>,
|
||||
* <code>".jpeg"</code>, or <code>".png"</code> — or if not provided then in JPEG format with an extension of
|
||||
* <code>".jpg"</code>.
|
||||
* <code>".jpg"</code>.</p>
|
||||
*/
|
||||
void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString());
|
||||
|
||||
|
@ -515,13 +515,13 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Opens a URL in the Interface window or other application, depending on the URL's scheme. The following schemes are
|
||||
* supported:<br />
|
||||
* supported:
|
||||
* <ul>
|
||||
* <li><code>hifi</code>: Navigate to the URL in Interface.</li>
|
||||
* <li><code>hifiapp</code>: Open a system app in Interface.</li>
|
||||
* </ul>
|
||||
* Other schemes will either be handled by the OS (e.g. <code>http</code>, <code>https</code>, or <code>mailto</code>) or
|
||||
* will display a dialog asking the user to confirm that they want to try to open the URL.
|
||||
* <p>Other schemes will either be handled by the OS (e.g. <code>http</code>, <code>https</code>, or <code>mailto</code>) or
|
||||
* will display a dialog asking the user to confirm that they want to try to open the URL.</p>
|
||||
* @function Window.openUrl
|
||||
* @param {string} url - The URL to open.
|
||||
*/
|
||||
|
@ -623,8 +623,8 @@ private slots:
|
|||
signals:
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when you change the domain you're visiting. <strong>Warning:</strong> Is not emitted if you go to a domain
|
||||
* that isn't running.
|
||||
* Triggered when you change the domain you're visiting.
|
||||
* <p><strong>Warning:</strong> Is not emitted if you go to a domain that isn't running.</p>
|
||||
* @function Window.domainChanged
|
||||
* @param {string} domainURL - The domain's URL.
|
||||
* @returns {Signal}
|
||||
|
|
|
@ -186,15 +186,6 @@ void DialogsManager::setAddressBarVisible(bool addressBarVisible) {
|
|||
emit addressBarShown(_addressBarVisible);
|
||||
}
|
||||
|
||||
void DialogsManager::showTestingResults() {
|
||||
if (!_testingDialog) {
|
||||
_testingDialog = new TestingDialog(qApp->getWindow());
|
||||
connect(_testingDialog, SIGNAL(closed()), _testingDialog, SLOT(deleteLater()));
|
||||
}
|
||||
_testingDialog->show();
|
||||
_testingDialog->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showDomainConnectionDialog() {
|
||||
// if the dialog already exists we delete it so the connection data is refreshed
|
||||
if (_domainConnectionDialog) {
|
||||
|
|
|
@ -53,7 +53,6 @@ public slots:
|
|||
void lodTools();
|
||||
void hmdTools(bool showTools);
|
||||
void showDomainConnectionDialog();
|
||||
void showTestingResults();
|
||||
void toggleAddressBar();
|
||||
|
||||
// Application Update
|
||||
|
|
|
@ -39,6 +39,9 @@ static const char* const ADDITIONAL_FLAGS_PROPERTY = "additionalFlags";
|
|||
static const char* const OVERRIDE_FLAGS_PROPERTY = "overrideFlags";
|
||||
static const char* const SOURCE_PROPERTY = "source";
|
||||
static const char* const TITLE_PROPERTY = "title";
|
||||
static const char* const RELATIVE_POSITION_ANCHOR_PROPERTY = "relativePositionAnchor";
|
||||
static const char* const RELATIVE_POSITION_PROPERTY = "relativePosition";
|
||||
static const char* const IS_FULL_SCREEN_WINDOW = "isFullScreenWindow";
|
||||
static const char* const POSITION_PROPERTY = "position";
|
||||
static const char* const INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition";
|
||||
static const char* const SIZE_PROPERTY = "size";
|
||||
|
@ -112,6 +115,15 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) {
|
|||
QCoreApplication::postEvent(QCoreApplication::instance(), event);
|
||||
}
|
||||
|
||||
void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) {
|
||||
// This handler is only connected `if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NONE)`.
|
||||
if (_isFullScreenWindow) {
|
||||
repositionAndResizeFullScreenWindow();
|
||||
} else if (_relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) {
|
||||
setPositionUsingRelativePositionAndAnchor(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveWindow::emitMainWindowResizeEvent() {
|
||||
emit qApp->getWindow()->windowGeometryChanged(qApp->getWindow()->geometry());
|
||||
}
|
||||
|
@ -184,22 +196,32 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
*/
|
||||
if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) {
|
||||
DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt();
|
||||
int tempWidth = 0;
|
||||
int tempHeight = 0;
|
||||
switch (dockedArea) {
|
||||
case DockArea::TOP:
|
||||
dockArea = Qt::TopDockWidgetArea;
|
||||
_dockWidget->setFixedHeight(windowSize.height());
|
||||
tempHeight = windowSize.height();
|
||||
_dockWidget->setFixedHeight(tempHeight);
|
||||
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, -tempHeight));
|
||||
break;
|
||||
case DockArea::BOTTOM:
|
||||
dockArea = Qt::BottomDockWidgetArea;
|
||||
_dockWidget->setFixedHeight(windowSize.height());
|
||||
tempHeight = windowSize.height();
|
||||
_dockWidget->setFixedHeight(tempHeight);
|
||||
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, tempHeight));
|
||||
break;
|
||||
case DockArea::LEFT:
|
||||
dockArea = Qt::LeftDockWidgetArea;
|
||||
_dockWidget->setFixedWidth(windowSize.width());
|
||||
tempWidth = windowSize.width();
|
||||
_dockWidget->setFixedWidth(tempWidth);
|
||||
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(-tempWidth, 0));
|
||||
break;
|
||||
case DockArea::RIGHT:
|
||||
dockArea = Qt::RightDockWidgetArea;
|
||||
_dockWidget->setFixedWidth(windowSize.width());
|
||||
tempWidth = windowSize.width();
|
||||
_dockWidget->setFixedWidth(tempWidth);
|
||||
qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(tempWidth, 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -255,6 +277,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
if (properties.contains(TITLE_PROPERTY)) {
|
||||
object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString());
|
||||
}
|
||||
if (properties.contains(VISIBLE_PROPERTY)) {
|
||||
object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool());
|
||||
}
|
||||
if (properties.contains(SIZE_PROPERTY)) {
|
||||
const auto size = vec2FromVariant(properties[SIZE_PROPERTY]);
|
||||
object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y));
|
||||
|
@ -263,8 +288,21 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
const auto position = vec2FromVariant(properties[POSITION_PROPERTY]);
|
||||
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y));
|
||||
}
|
||||
if (properties.contains(VISIBLE_PROPERTY)) {
|
||||
object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool());
|
||||
if (properties.contains(RELATIVE_POSITION_ANCHOR_PROPERTY)) {
|
||||
_relativePositionAnchor = static_cast<RelativePositionAnchor>(properties[RELATIVE_POSITION_ANCHOR_PROPERTY].toInt());
|
||||
}
|
||||
if (properties.contains(RELATIVE_POSITION_PROPERTY)) {
|
||||
_relativePosition = vec2FromVariant(properties[RELATIVE_POSITION_PROPERTY]);
|
||||
setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry());
|
||||
}
|
||||
if (properties.contains(IS_FULL_SCREEN_WINDOW)) {
|
||||
_isFullScreenWindow = properties[IS_FULL_SCREEN_WINDOW].toBool();
|
||||
}
|
||||
|
||||
if (_isFullScreenWindow) {
|
||||
QRect geo = qApp->getWindow()->geometry();
|
||||
object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(geo.x(), geo.y()));
|
||||
object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(geo.width(), geo.height()));
|
||||
}
|
||||
|
||||
// The qmlToScript method handles the thread-safety of this call. Because the QVariant argument
|
||||
|
@ -288,6 +326,10 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
|
|||
connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
|
||||
connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
|
||||
#endif
|
||||
|
||||
if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) {
|
||||
connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QUrl sourceURL{ sourceUrl };
|
||||
// If the passed URL doesn't correspond to a known scheme, assume it's a local file path
|
||||
|
@ -414,6 +456,71 @@ void InteractiveWindow::setPosition(const glm::vec2& position) {
|
|||
}
|
||||
}
|
||||
|
||||
RelativePositionAnchor InteractiveWindow::getRelativePositionAnchor() const {
|
||||
return _relativePositionAnchor;
|
||||
}
|
||||
|
||||
void InteractiveWindow::setRelativePositionAnchor(const RelativePositionAnchor& relativePositionAnchor) {
|
||||
_relativePositionAnchor = relativePositionAnchor;
|
||||
setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry());
|
||||
}
|
||||
|
||||
glm::vec2 InteractiveWindow::getRelativePosition() const {
|
||||
return _relativePosition;
|
||||
}
|
||||
|
||||
void InteractiveWindow::setRelativePosition(const glm::vec2& relativePosition) {
|
||||
_relativePosition = relativePosition;
|
||||
setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry());
|
||||
}
|
||||
|
||||
void InteractiveWindow::setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry) {
|
||||
RelativePositionAnchor relativePositionAnchor = getRelativePositionAnchor();
|
||||
glm::vec2 relativePosition = getRelativePosition();
|
||||
|
||||
glm::vec2 newPosition;
|
||||
|
||||
switch (relativePositionAnchor) {
|
||||
case RelativePositionAnchor::TOP_LEFT:
|
||||
newPosition.x = mainWindowGeometry.x() + relativePosition.x;
|
||||
newPosition.y = mainWindowGeometry.y() + relativePosition.y;
|
||||
break;
|
||||
case RelativePositionAnchor::TOP_RIGHT:
|
||||
newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x;
|
||||
newPosition.y = mainWindowGeometry.y() + relativePosition.y;
|
||||
break;
|
||||
case RelativePositionAnchor::BOTTOM_RIGHT:
|
||||
newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x;
|
||||
newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y;
|
||||
break;
|
||||
case RelativePositionAnchor::BOTTOM_LEFT:
|
||||
newPosition.x = mainWindowGeometry.x() + relativePosition.x;
|
||||
newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y;
|
||||
break;
|
||||
case RelativePositionAnchor::NO_ANCHOR:
|
||||
// No-op.
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure we include the dimensions of the docked widget!
|
||||
QSize dockedWidgetRelativePositionOffset = qApp->getWindow()->getDockedWidgetRelativePositionOffset();
|
||||
newPosition.x = newPosition.x + dockedWidgetRelativePositionOffset.width();
|
||||
newPosition.y = newPosition.y + dockedWidgetRelativePositionOffset.height();
|
||||
|
||||
if (_qmlWindowProxy) {
|
||||
QMetaObject::invokeMethod(_qmlWindowProxy.get(), "writeProperty", Q_ARG(QString, INTERACTIVE_WINDOW_POSITION_PROPERTY),
|
||||
Q_ARG(QVariant, QPointF(newPosition.x, newPosition.y)));
|
||||
}
|
||||
setPosition(newPosition);
|
||||
}
|
||||
|
||||
void InteractiveWindow::repositionAndResizeFullScreenWindow() {
|
||||
QRect windowGeometry = qApp->getWindow()->geometry();
|
||||
|
||||
setPosition(glm::vec2(windowGeometry.x(), windowGeometry.y()));
|
||||
setSize(glm::vec2(windowGeometry.width(), windowGeometry.height()));
|
||||
}
|
||||
|
||||
glm::vec2 InteractiveWindow::getSize() const {
|
||||
if (!_qmlWindowProxy) {
|
||||
return {};
|
||||
|
|
|
@ -56,8 +56,8 @@ namespace InteractiveWindowEnums {
|
|||
Q_NAMESPACE
|
||||
|
||||
/**jsdoc
|
||||
* A set of flags controlling <code>InteractiveWindow</code> behavior. The value is constructed by using the
|
||||
* <code>|</code> (bitwise OR) operator on the individual flag values.<br />
|
||||
* <p>A set of flags controlling <code>InteractiveWindow</code> behavior. The value is constructed by using the
|
||||
* <code>|</code> (bitwise OR) operator on the individual flag values.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Flag Name</th><th>Value</th><th>Description</th></tr>
|
||||
|
@ -89,6 +89,15 @@ namespace InteractiveWindowEnums {
|
|||
RIGHT
|
||||
};
|
||||
Q_ENUM_NS(DockArea);
|
||||
|
||||
enum RelativePositionAnchor {
|
||||
NO_ANCHOR,
|
||||
TOP_LEFT,
|
||||
TOP_RIGHT,
|
||||
BOTTOM_RIGHT,
|
||||
BOTTOM_LEFT
|
||||
};
|
||||
Q_ENUM_NS(RelativePositionAnchor);
|
||||
}
|
||||
|
||||
using namespace InteractiveWindowEnums;
|
||||
|
@ -121,6 +130,8 @@ class InteractiveWindow : public QObject {
|
|||
|
||||
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
|
||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(RelativePositionAnchor relativePositionAnchor READ getRelativePositionAnchor WRITE setRelativePositionAnchor)
|
||||
Q_PROPERTY(glm::vec2 relativePosition READ getRelativePosition WRITE setRelativePosition)
|
||||
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
||||
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
|
||||
Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode)
|
||||
|
@ -136,6 +147,21 @@ private:
|
|||
|
||||
Q_INVOKABLE glm::vec2 getPosition() const;
|
||||
Q_INVOKABLE void setPosition(const glm::vec2& position);
|
||||
|
||||
RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::NO_ANCHOR };
|
||||
Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const;
|
||||
Q_INVOKABLE void setRelativePositionAnchor(const RelativePositionAnchor& position);
|
||||
|
||||
// This "relative position" is relative to the "relative position anchor" and excludes the window frame.
|
||||
// This position will ALWAYS include the geometry of a docked widget, if one is present.
|
||||
glm::vec2 _relativePosition{ 0.0f, 0.0f };
|
||||
Q_INVOKABLE glm::vec2 getRelativePosition() const;
|
||||
Q_INVOKABLE void setRelativePosition(const glm::vec2& position);
|
||||
|
||||
Q_INVOKABLE void setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry);
|
||||
|
||||
bool _isFullScreenWindow{ false };
|
||||
Q_INVOKABLE void repositionAndResizeFullScreenWindow();
|
||||
|
||||
Q_INVOKABLE glm::vec2 getSize() const;
|
||||
Q_INVOKABLE void setSize(const glm::vec2& size);
|
||||
|
@ -320,6 +346,7 @@ protected slots:
|
|||
void forwardKeyPressEvent(int key, int modifiers);
|
||||
void forwardKeyReleaseEvent(int key, int modifiers);
|
||||
void emitMainWindowResizeEvent();
|
||||
void onMainWindowGeometryChanged(QRect geometry);
|
||||
|
||||
private:
|
||||
std::shared_ptr<QmlWindowProxy> _qmlWindowProxy;
|
||||
|
|
|
@ -1346,49 +1346,48 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <td>Some text.</td>
|
||||
* <td>{@link Overlays.OverlayProperties-Text|OverlayProperties-Text}</td></tr>
|
||||
* <tr><td><code>"cube"</code></td><td>3D</td>
|
||||
* <td>A cube. A <code>"shape"</code> overlay can also be used to create a cube.<br/>
|
||||
* <span class="important">Deprecated.</span>
|
||||
* </td>
|
||||
* <td><p>A cube. A <code>"shape"</code> overlay can also be used to create a cube.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube}</td></tr>
|
||||
* <tr><td><code>"sphere"</code></td><td>3D</td>
|
||||
* <td>A sphere. A <code>"shape"</code> overlay can also be used to create a sphere.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A sphere. A <code>"shape"</code> overlay can also be used to create a sphere.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere}</td></tr>
|
||||
* <tr><td><code>"shape"</code></td><td>3D</td>
|
||||
* <td>A geometric shape, such as a cube, sphere, or cylinder.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A geometric shape, such as a cube, sphere, or cylinder.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape}</td></tr>
|
||||
* <tr><td><code>"model"</code></td><td>3D</td>
|
||||
* <td>A model.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A model.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Model|OverlayProperties-Model}</td></tr>
|
||||
* <tr><td><code>"image3d"</code></td><td>3D</td>
|
||||
* <td>An image. Synonym: <code>"billboard"</code>.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>An image. Synonym: <code>"billboard"</code>.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D}</td></tr>
|
||||
* <tr><td><code>"rectangle3d"</code></td><td>3D</td>
|
||||
* <td>A rectangle.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A rectangle.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D}</td></tr>
|
||||
* <tr><td><code>"text3d"</code></td><td>3D</td>
|
||||
* <td>Some text.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>Some text.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D}</td></tr>
|
||||
* <tr><td><code>"web3d"</code></td><td>3D</td>
|
||||
* <td>Web content.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>Web content.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Web3D|OverlayProperties-Web3D}</td></tr>
|
||||
* <tr><td><code>"line3d"</code></td><td>3D</td>
|
||||
* <td>A line.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A line.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Line3D|OverlayProperties-Line3D}</td></tr>
|
||||
* <tr><td><code>"grid"</code></td><td>3D</td>
|
||||
* <td>A grid of lines in a plane.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A grid of lines in a plane.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid}</td></tr>
|
||||
* <tr><td><code>"circle3d"</code></td><td>3D</td>
|
||||
* <td>A circle.<br/>
|
||||
* <span class="important">Deprecated.</span></td>
|
||||
* <td><p>A circle.</p>
|
||||
* <p class="important">Deprecated.</p></td>
|
||||
* <td>{@link Overlays.OverlayProperties-Circle3D|OverlayProperties-Circle3D}</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
|
@ -1410,17 +1409,17 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @see {@link Overlays.OverlayProperties-Rectangle|OverlayProperties-Rectangle}
|
||||
* @see {@link Overlays.OverlayProperties-Image|OverlayProperties-Image}
|
||||
* @see {@link Overlays.OverlayProperties-Text|OverlayProperties-Text}
|
||||
* @see {@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Model|OverlayProperties-Model} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Web3D|OverlayProperties-Web3D} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Line3D|OverlayProperties-Line3D} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Circle3D|OverlayProperties-Circle3D} <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Cube|OverlayProperties-Cube} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Sphere|OverlayProperties-Sphere} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Shape|OverlayProperties-Shape} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Model|OverlayProperties-Model} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Rectangle3D|OverlayProperties-Rectangle3D} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Image3D|OverlayProperties-Image3D} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Text3D|OverlayProperties-Text3D} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Web3D|OverlayProperties-Web3D} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Line3D|OverlayProperties-Line3D} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Grid|OverlayProperties-Grid} — <span class="important">Deprecated.</span>
|
||||
* @see {@link Overlays.OverlayProperties-Circle3D|OverlayProperties-Circle3D} — <span class="important">Deprecated.</span>
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1502,11 +1501,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1520,7 +1519,7 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
|
||||
* @property {boolean} isSolid=false - <code>true</code> if the overlay is rendered as a solid, <code>false</code> if it is
|
||||
* rendered as a wire frame.
|
||||
* Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Synonyms: <code>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} ignorePickIntersection=false - <code>true</code> if {@link Picks} ignore the overlay, <code>false</code>
|
||||
* if they don't.
|
||||
|
@ -1550,11 +1549,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1568,7 +1567,7 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
|
||||
* @property {boolean} isSolid=false - <code>true</code> if the overlay is rendered as a solid, <code>false</code> if it is
|
||||
* rendered as a wire frame.
|
||||
* Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Synonyms: <code>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} ignorePickIntersection=false - <code>true</code> if {@link Picks} ignore the overlay, <code>false</code>
|
||||
* if they don't.
|
||||
|
@ -1599,11 +1598,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1617,7 +1616,7 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
|
||||
* @property {boolean} isSolid=false - <code>true</code> if the overlay is rendered as a solid, <code>false</code> if it is
|
||||
* rendered as a wire frame.
|
||||
* Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Synonyms: <code>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} ignorePickIntersection=false - <code>true</code> if {@link Picks} ignore the overlay, <code>false</code>
|
||||
* if they don't.
|
||||
|
@ -1641,7 +1640,7 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <tr><th>Value</th><th>Dimensions</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>"Circle"</code></td><td>2D</td><td>A circle oriented in 3D.</td></td></tr>
|
||||
* <tr><td><code>"Circle"</code></td><td>2D</td><td>A circle oriented in 3D.</td></tr>
|
||||
* <tr><td><code>"Cone"</code></td><td>3D</td><td></td></tr>
|
||||
* <tr><td><code>"Cube"</code></td><td>3D</td><td></td></tr>
|
||||
* <tr><td><code>"Cylinder"</code></td><td>3D</td><td></td></tr>
|
||||
|
@ -1674,11 +1673,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1692,7 +1691,7 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
|
||||
* @property {boolean} isSolid=false - <code>true</code> if the overlay is rendered as a solid, <code>false</code> if it is
|
||||
* rendered as a wire frame.
|
||||
* Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Synonyms: <code>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} ignorePickIntersection=false - <code>true</code> if {@link Picks} ignore the overlay, <code>false</code>
|
||||
* if they don't.
|
||||
|
@ -1761,17 +1760,17 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {Color} color=255,255,255 - The color of the overlay text. Synonym: <code>textColor</code>.
|
||||
* @property {number} alpha=0.7 - The opacity of the overlay text, <code>0.0</code> – <code>1.0</code>.
|
||||
* <p><em>Currently not used; use <code>textAlpha</code> instead.</em></p>
|
||||
* <CURRENTLY BROKEN>
|
||||
* @comment CURRENTLY BROKEN
|
||||
* @property {number} pulseMax=0 - The maximum value of the pulse multiplier.
|
||||
* @property {number} pulseMin=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1811,11 +1810,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1857,11 +1856,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -1916,41 +1915,45 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* HUD surface.
|
||||
* @property {boolean} grabbable=false - <code>true</code> if the overlay can be grabbed, <code>false</code> if it can't be.
|
||||
* @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to.
|
||||
* <p><em>Currently doesn't work.</em></p>
|
||||
* @comment CURRENTLY BROKEN
|
||||
* @property {number} parentJointIndex=65535 - Integer value specifying the joint of the entity or avatar that the entity is
|
||||
* parented to if <code>parentID</code> is set. Use 65535 or -1 to parent to the parent's position and orientation rather
|
||||
* than a joint.
|
||||
*
|
||||
* @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to.
|
||||
* <p><em>Currently doesn't work.</em></p>
|
||||
* <CURRENTLY BROKEN>
|
||||
* @comment CURRENTLY BROKEN
|
||||
* @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is
|
||||
* attached to if <code>parentID</code> is an avatar skeleton. A value of <code>65535</code> means "no joint".
|
||||
* <p><em>Currently doesn't work.</em></p>
|
||||
* <CURRENTLY BROKEN>
|
||||
* @comment CURRENTLY BROKEN
|
||||
|
||||
* @property {Vec3} start - The start point of the line. Synonyms: <code>startPoint</code> and <code>p1</code>.
|
||||
* <p><em>If <code>parentID<code> is set, use <code>localStart</code> to set the local position of the start point.</em></p>
|
||||
* <CURRENTLY BROKEN>
|
||||
* <p><strong>Note:</strong> If <code>parentID</code> is set, use <code>localStart</code> to set the local position of the
|
||||
* start point.</p>
|
||||
* @property {Vec3} end - The end point of the line. Synonyms: <code>endPoint</code> and <code>p2</code>.
|
||||
* <p><em>If <code>parentID<code> is set, use <code>localEnd</code> to set the local position of the end point.</em></p>
|
||||
* <CURRENTLY BROKEN>
|
||||
* <p><strong>Note:</strong> If <code>parentID</code> is set, use <code>localEnd</code> to set the local position of the
|
||||
* end point.</p>
|
||||
|
||||
* @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>parentID</code> set, otherwise the same value as <code>start</code>.
|
||||
* <CURRENTLY BROKEN>
|
||||
* <p><em>Currently doesn't work.</em></p>
|
||||
* @comment CURRENTLY BROKEN
|
||||
* @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a
|
||||
* <code>endParentID</code> set, otherwise the same value as <code>end</code>.
|
||||
* <CURRENTLY BROKEN>
|
||||
* <p><em>Currently doesn't work.</em></p>
|
||||
* @comment CURRENTLY BROKEN
|
||||
|
||||
* @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end
|
||||
* points.
|
||||
* <p><em>Currently doesn't work.</em></p>
|
||||
* <CURRENTLY BROKEN>
|
||||
* @comment CURRENTLY BROKEN
|
||||
|
||||
* @property {number} glow=0 - If <code>glow > 0</code>, the line is rendered with a glow.
|
||||
* @property {number} lineWidth=0.02 - Width of the line, in meters.
|
||||
* <p><em>You can set this property's value but currently cannot retrieve its value. Use the <code>strokeWidths</code>
|
||||
* property to retrieve its value instead.</p>
|
||||
* property to retrieve its value instead.</em></p>
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1967,11 +1970,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -2010,11 +2013,11 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>pulseMin</code> to <code>pulseMax</code>, then <code>pulseMax</code> to <code>pulseMin</code> in one period.
|
||||
* @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
* @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0
|
||||
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
|
||||
* used.)
|
||||
*
|
||||
|
@ -2029,7 +2032,7 @@ QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
|
|||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>. Synonym: <code>localOrientation</code>.
|
||||
* @property {boolean} isSolid=false - <code>true</code> if the overlay is rendered as a solid, <code>false</code> if it is
|
||||
* rendered as a wire frame.
|
||||
* Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Synonyms: <code>solid</code>, <code>isFilled</code>, and <code>filled</code>.
|
||||
* Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* @property {boolean} ignorePickIntersection=false - <code>true</code> if {@link Picks} ignore the overlay, <code>false</code>
|
||||
* if they don't.
|
||||
|
|
|
@ -6,5 +6,7 @@
|
|||
<array>
|
||||
<string>high-fidelity.hifi</string>
|
||||
</array>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -55,6 +55,8 @@ public:
|
|||
float getFrame() const { return _frame; }
|
||||
void loadURL(const QString& url);
|
||||
|
||||
AnimBlendType getBlendType() const { return _blendType; };
|
||||
|
||||
protected:
|
||||
|
||||
virtual void setCurrentFrameInternal(float frame) override;
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
* <p>Specifies the initial conditions of the IK solver.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</p><th>Description</th>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>RelaxToUnderPoses</td><td>This is a blend: it is 15/16 <code>PreviousSolution</code>
|
||||
|
@ -73,12 +73,14 @@ public:
|
|||
* <tr><td><code>1</code></td><td>RelaxToLimitCenterPoses</td><td>This is a blend: it is 15/16
|
||||
* <code>PreviousSolution</code> and 1/16 <code>LimitCenterPoses</code>. This should converge quickly because it is
|
||||
* close to the previous solution, but still provides the benefits of avoiding limb locking.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>PreviousSolution</td><td>The IK system will begin to solve from the same position and
|
||||
* orientations for each joint that was the result from the previous frame.<br />
|
||||
* Pros: As the end effectors typically do not move much from frame to frame, this is likely to converge quickly
|
||||
* to a valid solution.<br />
|
||||
* Cons: If the previous solution resulted in an awkward or uncomfortable posture, the next frame will also be
|
||||
* awkward and uncomfortable. It can also result in locked elbows and knees.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>PreviousSolution</td><td>
|
||||
* <p>The IK system will begin to solve from the same position and orientations for each joint that was the result
|
||||
* from the previous frame.</p>
|
||||
* <p>Pros: As the end effectors typically do not move much from frame to frame, this is likely to converge quickly
|
||||
* to a valid solution.</p>
|
||||
* <p>Cons: If the previous solution resulted in an awkward or uncomfortable posture, the next frame will also be
|
||||
* awkward and uncomfortable. It can also result in locked elbows and knees.</p>
|
||||
* </td></tr>
|
||||
* <tr><td><code>3</code></td><td>UnderPoses</td><td>The IK occurs at one of the top-most layers. It has access to the
|
||||
* full posture that was computed via canned animations and blends. We call this animated set of poses the "under
|
||||
* pose". The under poses are what would be visible if IK was completely disabled. Using the under poses as the
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
* <p>Specifies sets of joints.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</p><th>Description</th>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>FullBodyBoneSet</td><td>All joints.</td></tr>
|
||||
|
|
|
@ -20,14 +20,14 @@ public:
|
|||
* <p>An IK target type.</p>
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Value</th><th>Name</p><th>Description</th>
|
||||
* <tr><th>Value</th><th>Name</th><th>Description</th>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>0</code></td><td>RotationAndPosition</td><td>Attempt to reach the rotation and position end
|
||||
* effector.</td></tr>
|
||||
* <tr><td><code>1</code></td><td>RotationOnly</td><td>Attempt to reach the end effector rotation only.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>HmdHead</td><td>A special mode of IK that would attempt to prevent unnecessary
|
||||
* bending of the spine.<br />
|
||||
* <tr><td><code>2</code></td><td>HmdHead</td><td>
|
||||
* <p>A special mode of IK that would attempt to prevent unnecessary bending of the spine.</p>
|
||||
* <p class="important">Deprecated: This target type is deprecated and will be removed.</p></td></tr>
|
||||
* <tr><td><code>3</code></td><td>HipsRelativeRotationAndPosition</td><td>Attempt to reach a rotation and position end
|
||||
* effector that is not in absolute rig coordinates but is offset by the avatar hips translation.</td></tr>
|
||||
|
|
|
@ -95,7 +95,7 @@ static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRig
|
|||
* <p><strong>Warning:</strong> These properties are subject to change.
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr><th>Name</th><th>Type</p><th>Description</th>
|
||||
* <tr><th>Name</th><th>Type</th><th>Description</th>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td><code>userAnimNone</code></td><td>boolean</td><td><code>true</code> when no user overrideAnimation is
|
||||
|
@ -545,7 +545,8 @@ QStringList Rig::getAnimationRoles() const {
|
|||
auto clipNode = std::dynamic_pointer_cast<AnimClip>(node);
|
||||
if (clipNode) {
|
||||
// filter out the userAnims, they are for internal use only.
|
||||
if (!clipNode->getID().startsWith("userAnim")) {
|
||||
// also don't return additive blend node clips as valid roles.
|
||||
if (!clipNode->getID().startsWith("userAnim") && clipNode->getBlendType() == AnimBlendType_Normal) {
|
||||
list.append(node->getID());
|
||||
}
|
||||
}
|
||||
|
@ -1432,6 +1433,69 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
}
|
||||
_lastEnableInverseKinematics = _enableInverseKinematics;
|
||||
|
||||
|
||||
//stategraph vars based on input
|
||||
const float INPUT_DEADZONE_THRESHOLD = 0.05f;
|
||||
const float SLOW_SPEED_THRESHOLD = 1.5f;
|
||||
|
||||
if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD &&
|
||||
fabsf(_previousControllerParameters.inputZ) <= INPUT_DEADZONE_THRESHOLD) {
|
||||
// no WASD input
|
||||
if (fabsf(forwardSpeed) <= SLOW_SPEED_THRESHOLD && fabsf(lateralSpeed) <= SLOW_SPEED_THRESHOLD) {
|
||||
_animVars.set("isInputForward", false);
|
||||
_animVars.set("isInputBackward", false);
|
||||
_animVars.set("isInputRight", false);
|
||||
_animVars.set("isInputLeft", false);
|
||||
_animVars.set("isNotInput", true);
|
||||
_animVars.set("isNotInputSlow", true);
|
||||
|
||||
} else {
|
||||
_animVars.set("isInputForward", false);
|
||||
_animVars.set("isInputBackward", false);
|
||||
_animVars.set("isInputRight", false);
|
||||
_animVars.set("isInputLeft", false);
|
||||
_animVars.set("isNotInput", true);
|
||||
_animVars.set("isNotInputSlow", false);
|
||||
}
|
||||
} else if (fabsf(_previousControllerParameters.inputZ) >= fabsf(_previousControllerParameters.inputX)) {
|
||||
if (_previousControllerParameters.inputZ > 0.0f) {
|
||||
// forward
|
||||
_animVars.set("isInputForward", true);
|
||||
_animVars.set("isInputBackward", false);
|
||||
_animVars.set("isInputRight", false);
|
||||
_animVars.set("isInputLeft", false);
|
||||
_animVars.set("isNotInput", false);
|
||||
_animVars.set("isNotInputSlow", false);
|
||||
} else {
|
||||
// backward
|
||||
_animVars.set("isInputForward", false);
|
||||
_animVars.set("isInputBackward", true);
|
||||
_animVars.set("isInputRight", false);
|
||||
_animVars.set("isInputLeft", false);
|
||||
_animVars.set("isNotInput", false);
|
||||
_animVars.set("isNotInputSlow", false);
|
||||
}
|
||||
} else {
|
||||
if (_previousControllerParameters.inputX > 0.0f) {
|
||||
// right
|
||||
_animVars.set("isInputForward", false);
|
||||
_animVars.set("isInputBackward", false);
|
||||
_animVars.set("isInputRight", true);
|
||||
_animVars.set("isInputLeft", false);
|
||||
_animVars.set("isNotInput", false);
|
||||
_animVars.set("isNotInputSlow", false);
|
||||
} else {
|
||||
// left
|
||||
_animVars.set("isInputForward", false);
|
||||
_animVars.set("isInputBackward", false);
|
||||
_animVars.set("isInputRight", false);
|
||||
_animVars.set("isInputLeft", true);
|
||||
_animVars.set("isNotInput", false);
|
||||
_animVars.set("isNotInputSlow", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
_lastForward = forward;
|
||||
_lastPosition = worldPosition;
|
||||
|
@ -2160,6 +2224,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled;
|
||||
|
|
|
@ -88,6 +88,8 @@ public:
|
|||
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
|
||||
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
|
||||
bool isTalking;
|
||||
float inputX = 0.0f;
|
||||
float inputZ = 0.0f;
|
||||
bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS];
|
||||
bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS];
|
||||
HFMJointShapeInfo hipsShapeInfo;
|
||||
|
|
|
@ -1998,6 +1998,12 @@ void AudioClient::outputNotify() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioClient::noteAwakening() {
|
||||
qCDebug(audioclient) << "Restarting the audio devices.";
|
||||
switchInputToAudioDevice(_inputDeviceInfo);
|
||||
switchOutputToAudioDevice(_outputDeviceInfo);
|
||||
}
|
||||
|
||||
bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
|
||||
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
|
||||
|
||||
|
|
|
@ -255,6 +255,7 @@ public slots:
|
|||
void setOutputGain(float gain) { _outputGain = gain; };
|
||||
|
||||
void outputNotify();
|
||||
void noteAwakening();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
|
|
@ -545,7 +545,7 @@ void Avatar::relayJointDataToChildren() {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* An avatar has different types of data simulated at different rates, in Hz.
|
||||
* <p>An avatar has different types of data simulated at different rates, in Hz.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
|
@ -854,7 +854,8 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
float distanceToTarget = glm::length(toTarget);
|
||||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
updateDisplayNameAlpha(distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) {
|
||||
if (!isMyAvatar() || !(renderArgs->_cameraMode == (int8_t)CAMERA_MODE_FIRST_PERSON_LOOK_AT
|
||||
|| renderArgs->_cameraMode == (int8_t)CAMERA_MODE_FIRST_PERSON)) {
|
||||
auto& frustum = renderArgs->getViewFrustum();
|
||||
auto textPosition = getDisplayNamePosition();
|
||||
if (frustum.pointIntersectsFrustum(textPosition)) {
|
||||
|
|
|
@ -470,7 +470,7 @@ public:
|
|||
/**jsdoc
|
||||
* Sets the joint of the entity or avatar that the avatar is parented to.
|
||||
* @function MyAvatar.setParentJointIndex
|
||||
* @param {number} parentJointIndex - he joint of the entity or avatar that the avatar should be parented to. Use
|
||||
* @param {number} parentJointIndex - The joint of the entity or avatar that the avatar should be parented to. Use
|
||||
* <code>65535</code> or <code>-1</code> to parent to the entity or avatar's position and orientation rather than a
|
||||
* joint.
|
||||
*/
|
||||
|
|
|
@ -92,22 +92,23 @@ void Head::simulate(float deltaTime) {
|
|||
} else if (_timeWithoutTalking - deltaTime < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) {
|
||||
forceBlink = true;
|
||||
}
|
||||
|
||||
if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) {
|
||||
// no blinking when brows are raised; blink less with increasing loudness
|
||||
const float BASE_BLINK_RATE = 15.0f / 60.0f;
|
||||
const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f;
|
||||
if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) *
|
||||
ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) {
|
||||
_leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY;
|
||||
_rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY;
|
||||
float randSpeedVariability = randFloat();
|
||||
float eyeBlinkVelocity = BLINK_SPEED + randSpeedVariability * BLINK_SPEED_VARIABILITY;
|
||||
_leftEyeBlinkVelocity = eyeBlinkVelocity;
|
||||
_rightEyeBlinkVelocity = eyeBlinkVelocity;
|
||||
if (randFloat() < 0.5f) {
|
||||
_leftEyeBlink = BLINK_START_VARIABILITY;
|
||||
} else {
|
||||
_rightEyeBlink = BLINK_START_VARIABILITY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
_leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
|
||||
_rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
|
||||
|
||||
|
|
|
@ -1445,7 +1445,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* The avatar mixer data comprises different types of data, with the data rates of each being tracked in kbps.
|
||||
* <p>The avatar mixer data comprises different types of data, with the data rates of each being tracked in kbps.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
|
@ -1550,7 +1550,7 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* The avatar mixer data comprises different types of data updated at different rates, in Hz.
|
||||
* <p>The avatar mixer data comprises different types of data updated at different rates, in Hz.</p>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
|
|
|
@ -1277,9 +1277,9 @@ public:
|
|||
* @param {number} [scale=1.0] - The scale to apply to the model.
|
||||
* @param {boolean} [isSoft=false] - If the model has a skeleton, set this to <code>true</code> so that the bones of the
|
||||
* attached model's skeleton are rotated to fit the avatar's current pose. <code>isSoft</code> is used, for example,
|
||||
* to have clothing that moves with the avatar.<br />
|
||||
* If <code>true</code>, the <code>translation</code>, <code>rotation</code>, and <code>scale</code> parameters are
|
||||
* ignored.
|
||||
* to have clothing that moves with the avatar.
|
||||
* <p>If <code>true</code>, the <code>translation</code>, <code>rotation</code>, and <code>scale</code> parameters are
|
||||
* ignored.</p>
|
||||
* @param {boolean} [allowDuplicates=false] - If <code>true</code> then more than one copy of any particular model may be
|
||||
* attached to the same joint; if <code>false</code> then the same model cannot be attached to the same joint.
|
||||
* @param {boolean} [useSaved=true] - <em>Not used.</em>
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace controller {
|
|||
* <tr><th>Property</th><th>Type</th><th>Data</th><th>Description</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td colSpan=4><strong>Avatar Movement</strong></td>
|
||||
* <tr><td colSpan=4><strong>Avatar Movement</strong></td></tr>
|
||||
* <tr><td><code>TranslateX</code></td><td>number</td><td>number</td><td>Move the user's avatar in the direction of its
|
||||
* x-axis, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>TranslateY</code></td><td>number</td><td>number</td><td>Move the user's avatar in the direction of its
|
||||
|
@ -71,7 +71,7 @@ namespace controller {
|
|||
* step increment, if the camera isn't in independent or mirror modes.</td></tr>
|
||||
* <tr><td><code>StepRoll</code></td><td>number</td><td>number</td><td>No action.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Avatar Skeleton</strong></td>
|
||||
* <tr><td colSpan=4><strong>Avatar Skeleton</strong></td></tr>
|
||||
* <tr><td><code>Hips</code></td><td>number</td><td>{@link Pose}</td><td>Set the hips pose of the user's avatar.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Spine2</code></td><td>number</td><td>{@link Pose}</td><td>Set the spine2 pose of the user's avatar.
|
||||
|
@ -171,7 +171,7 @@ namespace controller {
|
|||
* <tr><td><code>RightFoot</code></td><td>number</td><td>{@link Pose}</td><td>Set the right foot pose of the user's
|
||||
* avatar.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong><strong>Application</strong></td>
|
||||
* <tr><td colSpan=4><strong>Application</strong></td></tr>
|
||||
* <tr><td><code>BoomIn</code></td><td>number</td><td>number</td><td>Zoom camera in from third person toward first
|
||||
* person view.</td></tr>
|
||||
* <tr><td><code>BoomOut</code></td><td>number</td><td>number</td><td>Zoom camera out from first person to third
|
||||
|
@ -212,7 +212,7 @@ namespace controller {
|
|||
* <tr><td><code>SecondaryAction</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Aliases</strong></td>
|
||||
* <tr><td colSpan=4><strong>Aliases</strong></td></tr>
|
||||
* <tr><td><code>Backward</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateZ</code> in the
|
||||
* positive direction.</td></tr>
|
||||
* <tr><td><code>Forward</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateZ</code> in the negative
|
||||
|
@ -234,7 +234,7 @@ namespace controller {
|
|||
* <tr><td><code>YawRight</code></td><td>number</td><td>number</td><td>Alias for <code>Yaw</code> in the negative
|
||||
* direction.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Deprecated Aliases</strong></td>
|
||||
* <tr><td colSpan=4><strong>Deprecated Aliases</strong></td></tr>
|
||||
* <tr><td><code>LEFT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use <code>LeftHand</code> instead.</span></td></tr>
|
||||
* <tr><td><code>RIGHT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated: This
|
||||
|
@ -282,7 +282,7 @@ namespace controller {
|
|||
* <tr><td><code>ACTION2</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use <code>SecondaryAction</code> instead.</span></td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Deprecated Trackers</strong></td>
|
||||
* <tr><td colSpan=4><strong>Deprecated Trackers</strong></td><tr>
|
||||
* <tr><td><code>TrackedObject00</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject01</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
|
@ -335,8 +335,8 @@ namespace controller {
|
|||
makeAxisPair(Action::STEP_PITCH, "StepPitch"),
|
||||
makeAxisPair(Action::STEP_ROLL, "StepRoll"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_Y, "StepTranslateY"),
|
||||
makeAxisPair(Action::STEP_TRANSLATE_Z, "StepTranslateZ"),
|
||||
|
||||
makePosePair(Action::LEFT_HAND, "LeftHand"),
|
||||
makePosePair(Action::RIGHT_HAND, "RightHand"),
|
||||
|
|
|
@ -33,7 +33,7 @@ void StandardController::focusOutEvent() {
|
|||
* identifying each output. <em>Read-only.</em></p>
|
||||
* <p>These outputs can be mapped to actions or functions in a {@link RouteObject} mapping. The data value provided by each
|
||||
* control is either a number or a {@link Pose}. Numbers are typically normalized to <code>0.0</code> or <code>1.0</code> for
|
||||
* button states, the range <code>0.0</code> – </code>1.0</code> for unidirectional scales, and the range
|
||||
* button states, the range <code>0.0</code> – <code>1.0</code> for unidirectional scales, and the range
|
||||
* <code>-1.0</code> – <code>1.0</code> for bidirectional scales.</p>
|
||||
* <p>Each hardware device has a mapping from its outputs to a subset of <code>Controller.Standard</code> items, specified in a
|
||||
* JSON file. For example,
|
||||
|
@ -118,8 +118,7 @@ void StandardController::focusOutEvent() {
|
|||
* button.</td></tr>
|
||||
* <tr><td><code>RightThumbUp</code></td><td>number</td><td>number</td><td>Right thumb not touching primary or secondary
|
||||
* thumb buttons.</td></tr>
|
||||
* <tr><td><code>LeftPrimaryIndex</code></td><td>number</td><td>number</td><td>Left primary index control
|
||||
* pressed.</em></td></tr>
|
||||
* <tr><td><code>LeftPrimaryIndex</code></td><td>number</td><td>number</td><td>Left primary index control pressed.</td></tr>
|
||||
* <tr><td><code>LeftSecondaryIndex</code></td><td>number</td><td>number</td><td>Left secondary index control pressed.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightPrimaryIndex</code></td><td>number</td><td>number</td><td>Right primary index control pressed.
|
||||
|
|
|
@ -86,7 +86,7 @@ class UserInputMapper;
|
|||
* @typedef {object} Controller.MappingJSONRoute
|
||||
* @property {string|Controller.MappingJSONAxis} from - The name of a {@link Controller.Hardware} property or an axis made from
|
||||
* them. If a property name, the leading <code>"Controller.Hardware."</code> can be omitted.
|
||||
* @property {boolean} [peek=false] - If <codd>true</code>, then peeking is enabled per {@link RouteObject#peek}.
|
||||
* @property {boolean} [peek=false] - If <code>true</code>, then peeking is enabled per {@link RouteObject#peek}.
|
||||
* @property {boolean} [debug=false] - If <code>true</code>, then debug is enabled per {@link RouteObject#debug}.
|
||||
* @property {string|string[]} [when=[]] - One or more numeric {@link Controller.Hardware} property names which are evaluated
|
||||
* as booleans and ANDed together. Prepend a property name with a <code>!</code> to do a logical NOT. The leading
|
||||
|
@ -135,8 +135,8 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* Creates a new {@link RouteObject} from a controller output, ready to be mapped to a standard control, action, or
|
||||
* function.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#from|from}: use this version in QML files.
|
||||
* function.
|
||||
* <p>This is a QML-specific version of {@link MappingObject#from|from}: use this version in QML files.</p>
|
||||
* @function MappingObject#fromQml
|
||||
* @param {Controller.Standard|Controller.Hardware|function} source - The controller output or function that is the source
|
||||
* of the route data. If a function, it must return a number or a {@link Pose} value as the route data.
|
||||
|
@ -146,8 +146,8 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* Creates a new {@link RouteObject} from two numeric {@link Controller.Hardware} outputs, one applied in the negative
|
||||
* direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#makeAxis|makeAxis}: use this version in QML files.
|
||||
* direction and the other in the positive direction, ready to be mapped to a standard control, action, or function.
|
||||
* <p>This is a QML-specific version of {@link MappingObject#makeAxis|makeAxis}: use this version in QML files.</p>
|
||||
* @function MappingObject#makeAxisQml
|
||||
* @param {Controller.Hardware} source1 - The first, negative-direction controller output.
|
||||
* @param {Controller.Hardware} source2 - The second, positive-direction controller output.
|
||||
|
@ -189,8 +189,8 @@ public:
|
|||
Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2);
|
||||
|
||||
/**jsdoc
|
||||
* Enables or disables the mapping. When enabled, the routes in the mapping take effect.<br />
|
||||
* Synonymous with {@link Controller.enableMapping}.
|
||||
* Enables or disables the mapping. When enabled, the routes in the mapping take effect.
|
||||
* <p>Synonymous with {@link Controller.enableMapping}.</p>
|
||||
* @function MappingObject#enable
|
||||
* @param {boolean} enable=true - If <code>true</code> then the mapping is enabled, otherwise it is disabled.
|
||||
* @returns {MappingObject} The mapping object, so that further routes can be added.
|
||||
|
@ -198,8 +198,8 @@ public:
|
|||
Q_INVOKABLE QObject* enable(bool enable = true);
|
||||
|
||||
/**jsdoc
|
||||
* Disables the mapping. When disabled, the routes in the mapping have no effect.<br />
|
||||
* Synonymous with {@link Controller.disableMapping}.
|
||||
* Disables the mapping. When disabled, the routes in the mapping have no effect.
|
||||
* <p>Synonymous with {@link Controller.disableMapping}.</p>
|
||||
* @function MappingObject#disable
|
||||
* @returns {MappingObject} The mapping object, so that further routes can be added.
|
||||
*/
|
||||
|
|
|
@ -52,8 +52,8 @@ class RouteBuilderProxy : public QObject {
|
|||
|
||||
/**jsdoc
|
||||
* Terminates the route with a standard control, an action, or a script function. The output value from the route is
|
||||
* sent to the specified destination.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.
|
||||
* sent to the specified destination.
|
||||
* <p>This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.</p>
|
||||
* @function RouteObject#toQml
|
||||
* @param {Controller.Standard|Controller.Actions|function} destination - The standard control, action, or JavaScript
|
||||
* function that the route output is mapped to. For a function, the parameter can be either the name of the function or
|
||||
|
@ -64,8 +64,8 @@ class RouteBuilderProxy : public QObject {
|
|||
/**jsdoc
|
||||
* Processes the route only if a condition is satisfied. The condition is evaluated before the route input is read, and
|
||||
* the input is read only if the condition is <code>true</code>. Thus, if the condition is not met then subsequent
|
||||
* routes using the same input are processed.<br />
|
||||
* This is a QML-specific version of {@link MappingObject#to|to}: use this version in QML files.
|
||||
* routes using the same input are processed.
|
||||
* <p>This is a QML-specific version of {@link MappingObject#when|when}: use this version in QML files.</p>
|
||||
* @function RouteObject#whenQml
|
||||
* @param {condition|condition[]} expression - <p>A <code>condition</code> may be a:</p>
|
||||
* <ul>
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QQuickWindow>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <DebugDraw.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
|
@ -177,9 +177,35 @@ QPointF CompositorHelper::getMouseEventPosition(QMouseEvent* event) {
|
|||
return event->localPos();
|
||||
}
|
||||
|
||||
static bool isWindowActive() {
|
||||
for (const auto& window : QGuiApplication::topLevelWindows()) {
|
||||
if (window->isActive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompositorHelper::shouldCaptureMouse() const {
|
||||
if (!_allowMouseCapture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isHMD()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!isWindowActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ui::Menu::isSomeSubmenuShown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu
|
||||
return _allowMouseCapture && isHMD() && QApplication::activeWindow() && !ui::Menu::isSomeSubmenuShown();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompositorHelper::setAllowMouseCapture(bool capture) {
|
||||
|
@ -206,9 +232,9 @@ void CompositorHelper::handleLeaveEvent() {
|
|||
mainWidgetFrame.moveTopLeft(topLeftScreen);
|
||||
}
|
||||
QRect uncoveredRect = mainWidgetFrame;
|
||||
foreach(QWidget* widget, QApplication::topLevelWidgets()) {
|
||||
if (widget->isWindow() && widget->isVisible() && widget != mainWidget) {
|
||||
QRect widgetFrame = widget->frameGeometry();
|
||||
for(QWindow* window : QGuiApplication::topLevelWindows()) {
|
||||
if (window->isVisible() && window != mainWidget->windowHandle()) {
|
||||
QRect widgetFrame = window->frameGeometry();
|
||||
if (widgetFrame.intersects(uncoveredRect)) {
|
||||
QRect intersection = uncoveredRect & widgetFrame;
|
||||
if (intersection.top() > uncoveredRect.top()) {
|
||||
|
@ -292,7 +318,7 @@ glm::vec2 CompositorHelper::getReticleMaximumPosition() const {
|
|||
if (isHMD()) {
|
||||
result = VIRTUAL_SCREEN_SIZE;
|
||||
} else {
|
||||
QRect rec = QApplication::desktop()->screenGeometry();
|
||||
QRect rec = QGuiApplication::primaryScreen()->geometry();
|
||||
result = glm::vec2(rec.right(), rec.bottom());
|
||||
}
|
||||
return result;
|
||||
|
@ -308,8 +334,8 @@ void CompositorHelper::sendFakeMouseEvent() {
|
|||
// in HMD mode we need to fake our mouse moves...
|
||||
QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y);
|
||||
auto button = Qt::NoButton;
|
||||
auto buttons = QApplication::mouseButtons();
|
||||
auto modifiers = QApplication::keyboardModifiers();
|
||||
auto buttons = QGuiApplication::mouseButtons();
|
||||
auto modifiers = QGuiApplication::keyboardModifiers();
|
||||
QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers);
|
||||
_fakeMouseEvent = true;
|
||||
qApp->sendEvent(_renderingWidget, &event);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <gl/Config.h>
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include <shaders/Shaders.h>
|
||||
#include <gpu/gl/GLShared.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <gpu/gl/GLTexelFormat.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
#include <CursorManager.h>
|
||||
|
@ -371,7 +373,7 @@ void OpenGLDisplayPlugin::customizeContext() {
|
|||
auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha();
|
||||
cursorData.texture->setUsage(usage.build());
|
||||
cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
cursorData.texture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
|
||||
cursorData.texture->setAutoGenerateMips(true);
|
||||
}
|
||||
}
|
||||
|
@ -475,30 +477,48 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
|
|||
});
|
||||
}
|
||||
|
||||
ktx::StoragePointer textureToKtx(const gpu::Texture& texture) {
|
||||
ktx::Header header;
|
||||
{
|
||||
auto gpuDims = texture.getDimensions();
|
||||
header.pixelWidth = gpuDims.x;
|
||||
header.pixelHeight = gpuDims.y;
|
||||
header.pixelDepth = 0;
|
||||
}
|
||||
|
||||
{
|
||||
auto gltexelformat = gpu::gl::GLTexelFormat::evalGLTexelFormat(texture.getStoredMipFormat());
|
||||
header.glInternalFormat = gltexelformat.internalFormat;
|
||||
header.glFormat = gltexelformat.format;
|
||||
header.glBaseInternalFormat = gltexelformat.format;
|
||||
header.glType = gltexelformat.type;
|
||||
header.glTypeSize = 1;
|
||||
header.numberOfMipmapLevels = 1 + texture.getMaxMip();
|
||||
}
|
||||
|
||||
auto memKtx = ktx::KTX::createBare(header);
|
||||
auto storage = memKtx->_storage;
|
||||
uint32_t faceCount = std::max(header.numberOfFaces, 1u);
|
||||
uint32_t mipCount = std::max(header.numberOfMipmapLevels, 1u);
|
||||
for (uint32_t mip = 0; mip < mipCount; ++mip) {
|
||||
for (uint32_t face = 0; face < faceCount; ++face) {
|
||||
const auto& image = memKtx->_images[mip];
|
||||
auto& faceBytes = const_cast<gpu::Byte*&>(image._faceBytes[face]);
|
||||
if (texture.isStoredMipFaceAvailable(mip, face)) {
|
||||
auto storedImage = texture.accessStoredMipFace(mip, face);
|
||||
auto storedSize = storedImage->size();
|
||||
memcpy(faceBytes, storedImage->data(), storedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
|
||||
withOtherThreadContext([&] {
|
||||
using namespace gpu;
|
||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||
FramebufferPointer framebuffer{ Framebuffer::create("captureFramebuffer") };
|
||||
TextureCapturer captureLambda = [&](const std::string& filename, const gpu::TexturePointer& texture, uint16 layer) {
|
||||
QImage image;
|
||||
if (texture->getUsageType() == TextureUsageType::STRICT_RESOURCE) {
|
||||
image = QImage{ 1, 1, QImage::Format_ARGB32 };
|
||||
auto storedImage = texture->accessStoredMipFace(0, 0);
|
||||
memcpy(image.bits(), storedImage->data(), image.sizeInBytes());
|
||||
//if (texture == textureCache->getWhiteTexture()) {
|
||||
//} else if (texture == textureCache->getBlackTexture()) {
|
||||
//} else if (texture == textureCache->getBlueTexture()) {
|
||||
//} else if (texture == textureCache->getGrayTexture()) {
|
||||
} else {
|
||||
ivec4 rect = { 0, 0, texture->getWidth(), texture->getHeight() };
|
||||
framebuffer->setRenderBuffer(0, texture, layer);
|
||||
glBackend->syncGPUObject(*framebuffer);
|
||||
|
||||
image = QImage{ rect.z, rect.w, QImage::Format_ARGB32 };
|
||||
glBackend->downloadFramebuffer(framebuffer, rect, image);
|
||||
}
|
||||
QImageWriter(filename.c_str()).write(image);
|
||||
TextureCapturer captureLambda = [&](const gpu::TexturePointer& texture)->storage::StoragePointer {
|
||||
return textureToKtx(*texture);
|
||||
};
|
||||
|
||||
if (_currentFrame) {
|
||||
|
|
|
@ -290,7 +290,7 @@ void HmdDisplayPlugin::internalPresent() {
|
|||
_previewTexture->setSource("HMD Preview Texture");
|
||||
_previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build());
|
||||
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
|
||||
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
_previewTexture->assignStoredMip(0, image.sizeInBytes(), image.constBits());
|
||||
_previewTexture->setAutoGenerateMips(true);
|
||||
|
||||
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));
|
||||
|
|
|
@ -96,6 +96,21 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
|
||||
// Handle mouse-clicking or laser-clicking on entities with the `href` property set
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
|
||||
if (!EntityTree::areEntityClicksCaptured() && (event.getButtons() & PointerEvent::PrimaryButton)) {
|
||||
auto entity = getEntity(entityID);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
auto properties = entity->getProperties();
|
||||
QString urlString = properties.getHref();
|
||||
QUrl url = QUrl(urlString, QUrl::StrictMode);
|
||||
if (url.isValid() && !url.isEmpty()) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) {
|
||||
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
|
||||
auto entity = getEntity(entityID);
|
||||
|
@ -153,47 +168,52 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() {
|
|||
entityScriptingInterface->setEntitiesScriptEngine(entitiesScriptEngineProvider);
|
||||
|
||||
// Connect mouse events to entity script callbacks
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event);
|
||||
// FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event);
|
||||
});
|
||||
if (!_mouseAndPreloadSignalHandlersConnected) {
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mousePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseDoublePressOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseDoublePressOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveOnEntity", event);
|
||||
// FIXME: this is a duplicate of mouseMoveOnEntity, but it seems like some scripts might use this naming
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseMoveEvent", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "mouseReleaseOnEntity", event);
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "clickDownOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::holdingClickOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "holdingClickOnEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickReleaseOnEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "clickReleaseOnEntity", event);
|
||||
});
|
||||
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverEnterEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverOverEntity", event);
|
||||
});
|
||||
connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, _entitiesScriptEngine.data(), [&](const EntityItemID& entityID, const PointerEvent& event) {
|
||||
_entitiesScriptEngine->callEntityScriptMethod(entityID, "hoverLeaveEntity", event);
|
||||
});
|
||||
|
||||
connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) {
|
||||
EntityItemPointer entity = getTree()->findEntityByID(entityID);
|
||||
if (entity) {
|
||||
entity->setScriptHasFinishedPreload(true);
|
||||
}
|
||||
});
|
||||
connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptPreloadFinished, [&](const EntityItemID& entityID) {
|
||||
EntityItemPointer entity = getTree()->findEntityByID(entityID);
|
||||
if (entity) {
|
||||
entity->setScriptHasFinishedPreload(true);
|
||||
}
|
||||
});
|
||||
|
||||
_mouseAndPreloadSignalHandlersConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
|
||||
|
@ -800,15 +820,6 @@ QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
|
|||
RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID);
|
||||
EntityItemPointer entity;
|
||||
if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) {
|
||||
if (!EntityTree::areEntityClicksCaptured() && event->button() == Qt::MouseButton::LeftButton) {
|
||||
auto properties = entity->getProperties();
|
||||
QString urlString = properties.getHref();
|
||||
QUrl url = QUrl(urlString, QUrl::StrictMode);
|
||||
if (url.isValid() && !url.isEmpty()) {
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult);
|
||||
PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID,
|
||||
pos2D, rayPickResult.intersection,
|
||||
|
|
|
@ -210,6 +210,8 @@ private:
|
|||
std::function<RayToEntityIntersectionResult(unsigned int)> _getPrevRayPickResultOperator;
|
||||
std::function<void(unsigned int, bool)> _setPrecisionPickingOperator;
|
||||
|
||||
bool _mouseAndPreloadSignalHandlersConnected { false };
|
||||
|
||||
class LayeredZone {
|
||||
public:
|
||||
LayeredZone(std::shared_ptr<ZoneEntityItem> zone) : zone(zone), id(zone->getID()), volume(zone->getVolumeEstimate()) {}
|
||||
|
|
|
@ -130,8 +130,8 @@ variables. These argument variables are used by the code which is run when bull
|
|||
* <tr><td><code>"ball-socket"</code></td><td>Object constraint</td>
|
||||
* <td>Connects two entities with a ball and socket joint.</td>
|
||||
* <td>{@link Entities.ActionArguments-BallSocket}</td></tr>
|
||||
* <tr><td><code>"spring"</code></td><td colspan="3">Synonym for <code>"tractor"</code>.
|
||||
* <span class="important">Deprecated.</span></td></tr>
|
||||
* <tr><td><code>"spring"</code></td><td> </td><td>Synonym for <code>"tractor"</code>.
|
||||
* <p class="important">Deprecated.</p></td><td> </td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {string} Entities.ActionType
|
||||
|
|
|
@ -706,15 +706,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
*
|
||||
* @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s<sup>2</sup> that the entity should move with, in
|
||||
* world coordinates. Use a value of <code>{ x: 0, y: -9.8, z: 0 }</code> to simulate Earth's gravity. Gravity is applied
|
||||
* to an entity's motion only if its <code>dynamic</code> property is <code>true</code>. The <code>gravity</code> value is
|
||||
* applied in addition to the <code>acceleration</code> value.
|
||||
* to an entity's motion only if its <code>dynamic</code> property is <code>true</code>.
|
||||
* <p>If changing an entity's <code>gravity</code> from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small
|
||||
* <code>velocity</code> in order to kick off physics simulation.</p>
|
||||
* @property {Vec3} acceleration=0,0,0 - A general acceleration in m/s<sup>2</sup> that the entity should move with, in world
|
||||
* coordinates. The acceleration is applied to an entity's motion only if its <code>dynamic</code> property is
|
||||
* <code>true</code>. The <code>acceleration</code> value is applied in addition to the <code>gravity</code> value.
|
||||
* <p>If changing an entity's <code>acceleration</code> from {@link Vec3(0)|Vec3.ZERO}, you need to give it a small
|
||||
* <code>velocity</code> in order to kick off physics simulation.<p>
|
||||
* @property {Vec3} acceleration - The current, measured acceleration of the entity, in m/s<sup>2</sup>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.</p>
|
||||
* @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, range <code>0.0</code> –
|
||||
* <code>0.99</code>. The higher the value, the more bouncy.
|
||||
* @property {number} friction=0.5 - How much an entity slows down when it's moving against another, range <code>0.0</code>
|
||||
|
@ -944,7 +940,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees.
|
||||
* @property {boolean} materialRepeat=true - <code>true</code> if the material repeats, <code>false</code> if it doesn't. If
|
||||
* <code>false</code>, fragments outside of texCoord 0 – 1 will be discarded. Works in both <code>"uv"</code> and
|
||||
* </code>"projected"</code> modes.
|
||||
* <code>"projected"</code> modes.
|
||||
* @example <caption>Color a sphere using a Material entity.</caption>
|
||||
* var entityID = Entities.addEntity({
|
||||
* type: "Sphere",
|
||||
|
@ -1217,10 +1213,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* <code>dimensions / voxelVolumesize</code>.
|
||||
* @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of
|
||||
* the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox
|
||||
* entity update it.<br />
|
||||
* The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how
|
||||
* entity update it.
|
||||
* <p>The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how
|
||||
* the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet,
|
||||
* there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.
|
||||
* there is a limit to the size and complexity of a PolyVox entity; edits which would result in an overflow are rejected.</p>
|
||||
* @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how
|
||||
* neighboring PolyVox entities are joined.
|
||||
* @property {string} xTextureURL="" - The URL of the texture to map to surfaces perpendicular to the entity's local x-axis.
|
||||
|
@ -1309,7 +1305,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
|
||||
* @property {boolean} unlit=false - <code>true</code> if the entity is unaffected by lighting, <code>false</code> if it is lit
|
||||
* by the key light and local lights.
|
||||
* @property {string} font="" - The font to render the text with. It can be one of the following: <code>"Courier"</code,
|
||||
* @property {string} font="" - The font to render the text with. It can be one of the following: <code>"Courier"</code>,
|
||||
* <code>"Inconsolata"</code>, <code>"Roboto"</code>, <code>"Timeless"</code>, or a path to a .sdff file.
|
||||
* @property {Entities.TextEffect} textEffect="none" - The effect that is applied to the text.
|
||||
* @property {Color} textEffectColor=255,255,255 - The color of the effect.
|
||||
|
|
|
@ -666,7 +666,7 @@ QScriptValue EntityScriptingInterface::getMultipleEntityProperties(QScriptContex
|
|||
const int ARGUMENT_EXTENDED_DESIRED_PROPERTIES = 1;
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
const auto entityIDs = qScriptValueToValue<QVector<QUuid>>(context->argument(ARGUMENT_ENTITY_IDS));
|
||||
const auto entityIDs = qscriptvalue_cast<QVector<QUuid>>(context->argument(ARGUMENT_ENTITY_IDS));
|
||||
return entityScriptingInterface->getMultipleEntityPropertiesInternal(engine, entityIDs, context->argument(ARGUMENT_EXTENDED_DESIRED_PROPERTIES));
|
||||
}
|
||||
|
||||
|
@ -716,7 +716,7 @@ QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScri
|
|||
psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FlagsActive);
|
||||
}
|
||||
|
||||
EntityPropertyFlags desiredProperties = qScriptValueToValue<EntityPropertyFlags>(extendedDesiredProperties);
|
||||
EntityPropertyFlags desiredProperties = qscriptvalue_cast<EntityPropertyFlags>(extendedDesiredProperties);
|
||||
bool needsScriptSemantics = desiredProperties.getHasProperty(PROP_POSITION) ||
|
||||
desiredProperties.getHasProperty(PROP_ROTATION) ||
|
||||
desiredProperties.getHasProperty(PROP_LOCAL_POSITION) ||
|
||||
|
|
|
@ -106,8 +106,9 @@ public:
|
|||
* "domain" entities, travel to different domains with a user as "avatar" entities, or be visible only to an individual user as
|
||||
* "local" entities (a.k.a. "overlays").
|
||||
*
|
||||
* <p>Note: For Interface scripts, the entities available to scripts are those that Interface has displayed and so knows
|
||||
* about.</p>
|
||||
* <p>Note: For Interface, avatar, and client entity scripts, the entities available to scripts are those that Interface has
|
||||
* displayed and so knows about. For assignment client scripts, the entities available are those that are "seen" by the
|
||||
* {@link EntityViewer}. For entity server scripts, all entities are available.</p>
|
||||
*
|
||||
* <h3>Entity Methods</h3>
|
||||
*
|
||||
|
@ -728,7 +729,8 @@ public slots:
|
|||
* Finds all domain and avatar entities whose axis-aligned boxes intersect a search frustum.
|
||||
* @function Entities.findEntitiesInFrustum
|
||||
* @param {ViewFrustum} frustum - The frustum to search in. The <code>position</code>, <code>orientation</code>,
|
||||
* <code>projection</code>, and <code>centerRadius</code> properties must be specified.
|
||||
* <code>projection</code>, and <code>centerRadius</code> properties must be specified. The <code>fieldOfView</code>
|
||||
* and <code>aspectRatio</code> properties are not used; these values are specified by the <code>projection</code>.
|
||||
* @returns {Uuid[]} An array of entity IDs whose axis-aligned boxes intersect the search frustum. The array is empty if no
|
||||
* entities could be found.
|
||||
* @example <caption>Report the number of entities in view.</caption>
|
||||
|
@ -1870,7 +1872,7 @@ public slots:
|
|||
/**jsdoc
|
||||
* Called when a {@link Entities.getMeshes} call is complete.
|
||||
* @callback Entities~getMeshesCallback
|
||||
* @param {MeshProxy[]} meshes - If <code>success<</code> is <code>true</code>, a {@link MeshProxy} per mesh in the
|
||||
* @param {MeshProxy[]} meshes - If <code>success</code> is <code>true</code>, a {@link MeshProxy} per mesh in the
|
||||
* <code>Model</code> or <code>PolyVox</code> entity; otherwise <code>undefined</code>.
|
||||
* @param {boolean} success - <code>true</code> if the {@link Entities.getMeshes} call was successful, <code>false</code>
|
||||
* otherwise. The call may be unsuccessful if the requested entity could not be found.
|
||||
|
|
|
@ -46,18 +46,18 @@ public:
|
|||
* See also, the <code>"Box"</code> and <code>"Sphere"</code> entity types.</td>
|
||||
* <td>{@link Entities.EntityProperties-Shape|EntityProperties-Shape}</td></tr>
|
||||
* <tr><td><code>"Box"</code></td><td>A rectangular prism. This is a synonym of <code>"Shape"</code> for the case
|
||||
* where the entity's <code>shape</code> property value is <code>"Cube"</code>.<br />
|
||||
* If an entity is created with its <code>type</code>
|
||||
* where the entity's <code>shape</code> property value is <code>"Cube"</code>.
|
||||
* <p>If an entity is created with its <code>type</code>
|
||||
* set to <code>"Box"</code> it will always be created with a <code>shape</code> property value of
|
||||
* <code>"Cube"</code>. If an entity of type <code>Shape</code> or <code>Sphere</code> has its <code>shape</code> set
|
||||
* to <code>"Cube"</code> then its <code>type</code> will be reported as <code>"Box"</code>.
|
||||
* to <code>"Cube"</code> then its <code>type</code> will be reported as <code>"Box"</code>.</p></td>
|
||||
* <td>{@link Entities.EntityProperties-Box|EntityProperties-Box}</td></tr>
|
||||
* <tr><td><code>"Sphere"</code></td><td>A sphere. This is a synonym of <code>"Shape"</code> for the case
|
||||
* where the entity's <code>shape</code> property value is <code>"Sphere"</code>.<br />
|
||||
* If an entity is created with its <code>type</code>
|
||||
* where the entity's <code>shape</code> property value is <code>"Sphere"</code>.
|
||||
* <p>If an entity is created with its <code>type</code>
|
||||
* set to <code>"Sphere"</code> it will always be created with a <code>shape</code> property value of
|
||||
* <code>"Sphere"</code>. If an entity of type <code>Box</code> or <code>Shape</code> has its <code>shape</code> set
|
||||
* to <code>"Sphere"</code> then its <code>type</code> will be reported as <code>"Sphere"</code>.
|
||||
* to <code>"Sphere"</code> then its <code>type</code> will be reported as <code>"Sphere"</code>.</td>
|
||||
* <td>{@link Entities.EntityProperties-Sphere|EntityProperties-Sphere}</td></tr>
|
||||
* <tr><td><code>"Model"</code></td><td>A mesh model from a glTF, FBX, or OBJ file.</td>
|
||||
* <td>{@link Entities.EntityProperties-Model|EntityProperties-Model}</td></tr>
|
||||
|
|
|
@ -1859,7 +1859,7 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c
|
|||
break;
|
||||
default:
|
||||
qWarning(modelformat) << "Unknown accessorType: " << accessorType;
|
||||
blobstream.unsetDevice();
|
||||
blobstream.setDevice(nullptr);
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
|
@ -1869,13 +1869,13 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c
|
|||
blobstream >> value;
|
||||
outarray.push_back(value);
|
||||
} else {
|
||||
blobstream.unsetDevice();
|
||||
blobstream.setDevice(nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blobstream.unsetDevice();
|
||||
blobstream.setDevice(nullptr);
|
||||
return true;
|
||||
}
|
||||
template<typename T>
|
||||
|
|
136
libraries/gpu/src/gpu/FrameIO.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/10/03
|
||||
// Copyright 2013-2019 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 "FrameIO.h"
|
||||
#include <shared/Storage.h>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace gpu::hfb;
|
||||
|
||||
static bool skip(const uint8_t*& ptr, size_t& remaining, uint32_t size) {
|
||||
if (remaining < size) {
|
||||
return false;
|
||||
}
|
||||
ptr += size;
|
||||
remaining -= size;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool read(const uint8_t*& ptr, size_t& remaining, T& output) {
|
||||
uint32_t readSize = (uint32_t)sizeof(T);
|
||||
if (remaining < readSize) {
|
||||
return false;
|
||||
}
|
||||
memcpy(&output, ptr, readSize);
|
||||
return skip(ptr, remaining, readSize);
|
||||
}
|
||||
|
||||
Descriptor::Descriptor(const StoragePointer& storage) : storage(storage) {
|
||||
const auto* const start = storage->data();
|
||||
const auto* ptr = storage->data();
|
||||
auto remaining = storage->size();
|
||||
|
||||
try {
|
||||
// Can't parse files more than 4GB
|
||||
if (remaining > UINT32_MAX) {
|
||||
throw std::runtime_error("File too large");
|
||||
}
|
||||
|
||||
if (!read(ptr, remaining, header)) {
|
||||
throw std::runtime_error("Couldn't read binary header");
|
||||
}
|
||||
|
||||
if (header.length != storage->size()) {
|
||||
throw std::runtime_error("Header/Actual size mismatch");
|
||||
}
|
||||
|
||||
while (remaining != 0) {
|
||||
chunks.emplace_back();
|
||||
auto& chunk = chunks.back();
|
||||
ChunkHeader& chunkHeader = chunk;
|
||||
if (!read(ptr, remaining, chunkHeader)) {
|
||||
throw std::runtime_error("Coulnd't read chunk header");
|
||||
}
|
||||
chunk.offset = (uint32_t)(ptr - start);
|
||||
if (chunk.end() > storage->size()) {
|
||||
throw std::runtime_error("Chunk too large for file");
|
||||
}
|
||||
if (!skip(ptr, remaining, chunk.length)) {
|
||||
throw std::runtime_error("Skip chunk data failed");
|
||||
}
|
||||
}
|
||||
} catch (const std::runtime_error&) {
|
||||
// LOG somnething
|
||||
header.magic = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Chunk::end() const {
|
||||
size_t result = offset;
|
||||
result += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
StoragePointer Descriptor::getChunk(uint32_t chunkIndex) const {
|
||||
if (chunkIndex >= chunks.size()) {
|
||||
return {};
|
||||
}
|
||||
const auto& chunk = chunks[chunkIndex];
|
||||
if (chunk.end() > storage->size()) {
|
||||
return {};
|
||||
}
|
||||
return storage->createView(chunk.length, chunk.offset);
|
||||
}
|
||||
|
||||
static void writeUint(uint8_t*& dest, uint32_t value) {
|
||||
memcpy(dest, &value, sizeof(uint32_t));
|
||||
dest += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void writeChunk(uint8_t*& dest, uint32_t chunkType, const T& chunkData) {
|
||||
uint32_t chunkSize = static_cast<uint32_t>(chunkData.size());
|
||||
writeUint(dest, chunkSize);
|
||||
writeUint(dest, chunkType);
|
||||
memcpy(dest, chunkData.data(), chunkSize);
|
||||
dest += chunkSize;
|
||||
}
|
||||
|
||||
void gpu::hfb::writeFrame(const std::string& filename,
|
||||
const std::string& json,
|
||||
const Buffer& binaryBuffer,
|
||||
const StorageBuilders& ktxBuilders) {
|
||||
uint32_t strLen = (uint32_t)json.size();
|
||||
uint32_t size = gpu::hfb::HEADER_SIZE + gpu::hfb::CHUNK_HEADER_SIZE + strLen;
|
||||
size += gpu::hfb::CHUNK_HEADER_SIZE + (uint32_t)binaryBuffer.size();
|
||||
for (const auto& builder : ktxBuilders) {
|
||||
auto storage = builder();
|
||||
if (storage) {
|
||||
size += gpu::hfb::CHUNK_HEADER_SIZE + (uint32_t)storage->size();
|
||||
}
|
||||
}
|
||||
|
||||
auto outputConst = storage::FileStorage::create(filename.c_str(), size, nullptr);
|
||||
auto output = std::const_pointer_cast<storage::Storage>(outputConst);
|
||||
auto ptr = output->mutableData();
|
||||
writeUint(ptr, gpu::hfb::MAGIC);
|
||||
writeUint(ptr, gpu::hfb::VERSION);
|
||||
writeUint(ptr, size);
|
||||
writeChunk(ptr, gpu::hfb::CHUNK_TYPE_JSON, json);
|
||||
writeChunk(ptr, gpu::hfb::CHUNK_TYPE_BIN, binaryBuffer);
|
||||
for (const auto& builder : ktxBuilders) {
|
||||
static StoragePointer EMPTY_STORAGE{ std::make_shared<storage::MemoryStorage>(0, nullptr) };
|
||||
auto storage = builder();
|
||||
if (!storage) {
|
||||
storage = EMPTY_STORAGE;
|
||||
}
|
||||
writeChunk(ptr, gpu::hfb::CHUNK_TYPE_KTX, *storage);
|
||||
}
|
||||
assert((ptr - output->data()) == size);
|
||||
}
|
|
@ -12,17 +12,72 @@
|
|||
#include "Forward.h"
|
||||
#include "Format.h"
|
||||
|
||||
#include <shared/Storage.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace gpu {
|
||||
|
||||
using TextureCapturer = std::function<void(const std::string&, const TexturePointer&, uint16 layer)>;
|
||||
using TextureLoader = std::function<void(const std::string&, const TexturePointer&, uint16 layer)>;
|
||||
using TextureCapturer = std::function<storage::StoragePointer(const TexturePointer&)>;
|
||||
//using TextureLoader = std::function<void(const storage::StoragePointer& storage, const TexturePointer&)>;
|
||||
void writeFrame(const std::string& filename, const FramePointer& frame, const TextureCapturer& capturer = nullptr);
|
||||
FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader = nullptr);
|
||||
FramePointer readFrame(const std::string& filename, uint32_t externalTexture);
|
||||
|
||||
using IndexOptimizer = std::function<void(Primitive, uint32_t faceCount, uint32_t indexCount, uint32_t* indices )>;
|
||||
void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer);
|
||||
namespace hfb {
|
||||
|
||||
using Storage = storage::Storage;
|
||||
using StoragePointer = storage::Pointer;
|
||||
using StorageBuilders = storage::Builders;
|
||||
|
||||
constexpr const char* const EXTENSION{ ".hfb" };
|
||||
constexpr uint32_t HEADER_SIZE{ sizeof(uint32_t) * 3 };
|
||||
constexpr uint32_t CHUNK_HEADER_SIZE = sizeof(uint32_t) * 2;
|
||||
constexpr uint32_t MAGIC{ 0x49464948 };
|
||||
constexpr uint32_t VERSION{ 0x01 };
|
||||
constexpr uint32_t CHUNK_TYPE_JSON{ 0x4E4F534A };
|
||||
constexpr uint32_t CHUNK_TYPE_KTX{ 0x0058544b };
|
||||
constexpr uint32_t CHUNK_TYPE_BIN{ 0x004E4942 };
|
||||
constexpr uint32_t CHUNK_TYPE_PNG{ 0x00474E50 };
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
|
||||
struct Header {
|
||||
uint32_t magic{ 0 };
|
||||
uint32_t version{ 0 };
|
||||
uint32_t length{ 0 };
|
||||
};
|
||||
|
||||
struct ChunkHeader {
|
||||
uint32_t length{ 0 };
|
||||
uint32_t type{ 0 };
|
||||
};
|
||||
|
||||
struct Chunk : public ChunkHeader {
|
||||
uint32_t offset{ 0 };
|
||||
|
||||
size_t end() const;
|
||||
};
|
||||
|
||||
using Chunks = std::vector<Chunk>;
|
||||
|
||||
struct Descriptor {
|
||||
using Pointer = std::shared_ptr<Descriptor>;
|
||||
|
||||
Header header;
|
||||
Chunks chunks;
|
||||
StoragePointer storage;
|
||||
|
||||
Descriptor(const StoragePointer& storage);
|
||||
operator bool() const { return header.magic == MAGIC; }
|
||||
StoragePointer getChunk(uint32_t chunk) const;
|
||||
};
|
||||
|
||||
void writeFrame(const std::string& filename,
|
||||
const std::string& json,
|
||||
const Buffer& binaryBuffer,
|
||||
const StorageBuilders& pngBuffers);
|
||||
|
||||
} // namespace hfb
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
|
|