mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 08:36:26 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into tablet-ui-edit-js
This commit is contained in:
commit
1d446064be
24 changed files with 354 additions and 50 deletions
|
@ -17,10 +17,11 @@
|
||||||
#include <ScriptCache.h>
|
#include <ScriptCache.h>
|
||||||
#include <EntityEditFilters.h>
|
#include <EntityEditFilters.h>
|
||||||
|
|
||||||
|
#include "AssignmentParentFinder.h"
|
||||||
|
#include "EntityNodeData.h"
|
||||||
#include "EntityServer.h"
|
#include "EntityServer.h"
|
||||||
#include "EntityServerConsts.h"
|
#include "EntityServerConsts.h"
|
||||||
#include "EntityNodeData.h"
|
#include "EntityTreeSendThread.h"
|
||||||
#include "AssignmentParentFinder.h"
|
|
||||||
|
|
||||||
const char* MODEL_SERVER_NAME = "Entity";
|
const char* MODEL_SERVER_NAME = "Entity";
|
||||||
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
|
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
|
||||||
|
@ -77,6 +78,10 @@ OctreePointer EntityServer::createTree() {
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OctreeServer::UniqueSendThread EntityServer::newSendThread(const SharedNodePointer& node) {
|
||||||
|
return std::unique_ptr<EntityTreeSendThread>(new EntityTreeSendThread(this, node));
|
||||||
|
}
|
||||||
|
|
||||||
void EntityServer::beforeRun() {
|
void EntityServer::beforeRun() {
|
||||||
_pruneDeletedEntitiesTimer = new QTimer();
|
_pruneDeletedEntitiesTimer = new QTimer();
|
||||||
connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));
|
connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));
|
||||||
|
|
|
@ -67,6 +67,7 @@ public slots:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual OctreePointer createTree() override;
|
virtual OctreePointer createTree() override;
|
||||||
|
virtual UniqueSendThread newSendThread(const SharedNodePointer& node) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
|
132
assignment-client/src/entities/EntityTreeSendThread.cpp
Normal file
132
assignment-client/src/entities/EntityTreeSendThread.cpp
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
//
|
||||||
|
// EntityTreeSendThread.cpp
|
||||||
|
// assignment-client/src/entities
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2/15/17.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "EntityTreeSendThread.h"
|
||||||
|
|
||||||
|
#include <EntityNodeData.h>
|
||||||
|
#include <EntityTypes.h>
|
||||||
|
|
||||||
|
#include "EntityServer.h"
|
||||||
|
|
||||||
|
void EntityTreeSendThread::preDistributionProcessing() {
|
||||||
|
auto node = _node.toStrongRef();
|
||||||
|
auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||||
|
|
||||||
|
if (nodeData) {
|
||||||
|
auto jsonQuery = nodeData->getJSONParameters();
|
||||||
|
|
||||||
|
// check if we have a JSON query with flags
|
||||||
|
auto flags = jsonQuery[EntityJSONQueryProperties::FLAGS_PROPERTY].toObject();
|
||||||
|
if (!flags.isEmpty()) {
|
||||||
|
// check the flags object for specific flags that require special pre-processing
|
||||||
|
|
||||||
|
bool includeAncestors = flags[EntityJSONQueryProperties::INCLUDE_ANCESTORS_PROPERTY].toBool();
|
||||||
|
bool includeDescendants = flags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY].toBool();
|
||||||
|
|
||||||
|
if (includeAncestors || includeDescendants) {
|
||||||
|
// we need to either include the ancestors, descendants, or both for entities matching the filter
|
||||||
|
// included in the JSON query
|
||||||
|
|
||||||
|
// first reset our flagged extra entities so we start with an empty set
|
||||||
|
nodeData->resetFlaggedExtraEntities();
|
||||||
|
|
||||||
|
auto entityTree = std::static_pointer_cast<EntityTree>(_myServer->getOctree());
|
||||||
|
|
||||||
|
bool requiresFullScene = false;
|
||||||
|
|
||||||
|
// enumerate the set of entity IDs we know currently match the filter
|
||||||
|
foreach(const QUuid& entityID, nodeData->getSentFilteredEntities()) {
|
||||||
|
if (includeAncestors) {
|
||||||
|
// we need to include ancestors - recurse up to reach them all and add their IDs
|
||||||
|
// to the set of extra entities to include for this node
|
||||||
|
entityTree->withReadLock([&]{
|
||||||
|
auto filteredEntity = entityTree->findEntityByID(entityID);
|
||||||
|
if (filteredEntity) {
|
||||||
|
requiresFullScene |= addAncestorsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDescendants) {
|
||||||
|
// we need to include descendants - recurse down to reach them all and add their IDs
|
||||||
|
// to the set of extra entities to include for this node
|
||||||
|
entityTree->withReadLock([&]{
|
||||||
|
auto filteredEntity = entityTree->findEntityByID(entityID);
|
||||||
|
if (filteredEntity) {
|
||||||
|
requiresFullScene |= addDescendantsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requiresFullScene) {
|
||||||
|
// for one or more of the entities matching our filter we found new extra entities to include
|
||||||
|
|
||||||
|
// because it is possible that one of these entities hasn't changed since our last send
|
||||||
|
// and therefore would not be recursed to, we need to force a full traversal for this pass
|
||||||
|
// of the tree to allow it to grab all of the extra entities we're asking it to include
|
||||||
|
nodeData->setShouldForceFullScene(requiresFullScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID,
|
||||||
|
EntityItem& entityItem, EntityNodeData& nodeData) {
|
||||||
|
// check if this entity has a parent that is also an entity
|
||||||
|
bool success = false;
|
||||||
|
auto entityParent = entityItem.getParentPointer(success);
|
||||||
|
|
||||||
|
if (success && entityParent && entityParent->getNestableType() == NestableType::Entity) {
|
||||||
|
// we found a parent that is an entity item
|
||||||
|
|
||||||
|
// first add it to the extra list of things we need to send
|
||||||
|
bool parentWasNew = nodeData.insertFlaggedExtraEntity(filteredEntityID, entityParent->getID());
|
||||||
|
|
||||||
|
// now recursively call ourselves to get its ancestors added too
|
||||||
|
auto parentEntityItem = std::static_pointer_cast<EntityItem>(entityParent);
|
||||||
|
bool ancestorsWereNew = addAncestorsToExtraFlaggedEntities(filteredEntityID, *parentEntityItem, nodeData);
|
||||||
|
|
||||||
|
// return boolean if our parent or any of our ancestors were new additions (via insertFlaggedExtraEntity)
|
||||||
|
return parentWasNew || ancestorsWereNew;
|
||||||
|
}
|
||||||
|
|
||||||
|
// since we didn't have a parent niether of our parents or ancestors could be new additions
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID,
|
||||||
|
EntityItem& entityItem, EntityNodeData& nodeData) {
|
||||||
|
bool hasNewChild = false;
|
||||||
|
bool hasNewDescendants = false;
|
||||||
|
|
||||||
|
// enumerate the immediate children of this entity
|
||||||
|
foreach (SpatiallyNestablePointer child, entityItem.getChildren()) {
|
||||||
|
|
||||||
|
if (child && child->getNestableType() == NestableType::Entity) {
|
||||||
|
// this is a child that is an entity
|
||||||
|
|
||||||
|
// first add it to the extra list of things we need to send
|
||||||
|
hasNewChild |= nodeData.insertFlaggedExtraEntity(filteredEntityID, child->getID());
|
||||||
|
|
||||||
|
// now recursively call ourselves to get its descendants added too
|
||||||
|
auto childEntityItem = std::static_pointer_cast<EntityItem>(child);
|
||||||
|
hasNewDescendants |= addDescendantsToExtraFlaggedEntities(filteredEntityID, *childEntityItem, nodeData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return our boolean indicating if we added new children or descendants as extra entities to send
|
||||||
|
// (via insertFlaggedExtraEntity)
|
||||||
|
return hasNewChild || hasNewDescendants;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
35
assignment-client/src/entities/EntityTreeSendThread.h
Normal file
35
assignment-client/src/entities/EntityTreeSendThread.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// EntityTreeSendThread.h
|
||||||
|
// assignment-client/src/entities
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2/15/17.
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_EntityTreeSendThread_h
|
||||||
|
#define hifi_EntityTreeSendThread_h
|
||||||
|
|
||||||
|
#include "../octree/OctreeSendThread.h"
|
||||||
|
|
||||||
|
class EntityNodeData;
|
||||||
|
class EntityItem;
|
||||||
|
|
||||||
|
class EntityTreeSendThread : public OctreeSendThread {
|
||||||
|
|
||||||
|
public:
|
||||||
|
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) {};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void preDistributionProcessing() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// the following two methods return booleans to indicate if any extra flagged entities were new additions to set
|
||||||
|
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
|
||||||
|
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_EntityTreeSendThread_h
|
|
@ -309,6 +309,12 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nodeData->elementBag.isEmpty()) {
|
||||||
|
// if we're about to do a fresh pass,
|
||||||
|
// give our pre-distribution processing a chance to do what it needs
|
||||||
|
preDistributionProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
// calculate max number of packets that can be sent during this interval
|
// calculate max number of packets that can be sent during this interval
|
||||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||||
|
@ -316,9 +322,17 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
int truePacketsSent = 0;
|
int truePacketsSent = 0;
|
||||||
int trueBytesSent = 0;
|
int trueBytesSent = 0;
|
||||||
int packetsSentThisInterval = 0;
|
int packetsSentThisInterval = 0;
|
||||||
bool isFullScene = nodeData->haveJSONParametersChanged() ||
|
|
||||||
(nodeData->getUsesFrustum()
|
bool isFullScene = nodeData->shouldForceFullScene();
|
||||||
&& ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
|
if (isFullScene) {
|
||||||
|
// we're forcing a full scene, clear the force in OctreeQueryNode so we don't force it next time again
|
||||||
|
nodeData->setShouldForceFullScene(false);
|
||||||
|
} else {
|
||||||
|
// we aren't forcing a full scene, check if something else suggests we should
|
||||||
|
isFullScene = nodeData->haveJSONParametersChanged() ||
|
||||||
|
(nodeData->getUsesFrustum()
|
||||||
|
&& ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
|
||||||
|
}
|
||||||
|
|
||||||
bool somethingToSend = true; // assume we have something
|
bool somethingToSend = true; // assume we have something
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include <GenericThread.h>
|
#include <GenericThread.h>
|
||||||
|
#include <Node.h>
|
||||||
|
#include <OctreePacketData.h>
|
||||||
|
|
||||||
class OctreeQueryNode;
|
class OctreeQueryNode;
|
||||||
class OctreeServer;
|
class OctreeServer;
|
||||||
|
@ -49,13 +51,17 @@ protected:
|
||||||
/// Implements generic processing behavior for this thread.
|
/// Implements generic processing behavior for this thread.
|
||||||
virtual bool process() override;
|
virtual bool process() override;
|
||||||
|
|
||||||
|
/// Called before a packetDistributor pass to allow for pre-distribution processing
|
||||||
|
virtual void preDistributionProcessing() {};
|
||||||
|
|
||||||
|
OctreeServer* _myServer { nullptr };
|
||||||
|
QWeakPointer<Node> _node;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false);
|
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false);
|
||||||
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
|
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
|
||||||
|
|
||||||
|
|
||||||
OctreeServer* _myServer { nullptr };
|
|
||||||
QWeakPointer<Node> _node;
|
|
||||||
QUuid _nodeUuid;
|
QUuid _nodeUuid;
|
||||||
|
|
||||||
OctreePacketData _packetData;
|
OctreePacketData _packetData;
|
||||||
|
|
|
@ -872,8 +872,12 @@ void OctreeServer::parsePayload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePointer& node) {
|
||||||
|
return std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
|
||||||
|
}
|
||||||
|
|
||||||
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
|
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
|
||||||
auto sendThread = std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
|
auto sendThread = newSendThread(node);
|
||||||
|
|
||||||
// we want to be notified when the thread finishes
|
// we want to be notified when the thread finishes
|
||||||
connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread);
|
connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread);
|
||||||
|
|
|
@ -158,6 +158,7 @@ protected:
|
||||||
QString getStatusLink();
|
QString getStatusLink();
|
||||||
|
|
||||||
UniqueSendThread createSendThread(const SharedNodePointer& node);
|
UniqueSendThread createSendThread(const SharedNodePointer& node);
|
||||||
|
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
|
||||||
|
|
||||||
int _argc;
|
int _argc;
|
||||||
const char** _argv;
|
const char** _argv;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
#include <AudioInjectorManager.h>
|
#include <AudioInjectorManager.h>
|
||||||
#include <ClientServerUtils.h>
|
#include <ClientServerUtils.h>
|
||||||
|
#include <EntityNodeData.h>
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
#include <MessagesClient.h>
|
#include <MessagesClient.h>
|
||||||
|
@ -264,8 +265,14 @@ void EntityScriptServer::run() {
|
||||||
|
|
||||||
// setup the JSON filter that asks for entities with a non-default serverScripts property
|
// setup the JSON filter that asks for entities with a non-default serverScripts property
|
||||||
QJsonObject queryJSONParameters;
|
QJsonObject queryJSONParameters;
|
||||||
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
|
queryJSONParameters[EntityJSONQueryProperties::SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault;
|
||||||
queryJSONParameters[SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault;
|
|
||||||
|
QJsonObject queryFlags;
|
||||||
|
|
||||||
|
queryFlags[EntityJSONQueryProperties::INCLUDE_ANCESTORS_PROPERTY] = true;
|
||||||
|
queryFlags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY] = true;
|
||||||
|
|
||||||
|
queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags;
|
||||||
|
|
||||||
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
|
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
|
||||||
_entityViewer.getOctreeQuery().setUsesFrustum(false);
|
_entityViewer.getOctreeQuery().setUsesFrustum(false);
|
||||||
|
@ -412,6 +419,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
||||||
|
|
||||||
connect(newEngine.data(), &ScriptEngine::update, this, [this] {
|
connect(newEngine.data(), &ScriptEngine::update, this, [this] {
|
||||||
_entityViewer.queryOctree();
|
_entityViewer.queryOctree();
|
||||||
|
_entityViewer.getTree()->update();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,8 @@ Window {
|
||||||
function addButton(properties) {
|
function addButton(properties) {
|
||||||
properties = properties || {}
|
properties = properties || {}
|
||||||
|
|
||||||
|
unpinnedAlpha = 1;
|
||||||
|
|
||||||
// If a name is specified, then check if there's an existing button with that name
|
// If a name is specified, then check if there's an existing button with that name
|
||||||
// and return it if so. This will allow multiple clients to listen to a single button,
|
// and return it if so. This will allow multiple clients to listen to a single button,
|
||||||
// and allow scripts to be idempotent so they don't duplicate buttons if they're reloaded
|
// and allow scripts to be idempotent so they don't duplicate buttons if they're reloaded
|
||||||
|
@ -154,7 +156,7 @@ Window {
|
||||||
updatePinned();
|
updatePinned();
|
||||||
|
|
||||||
if (buttons.length === 0) {
|
if (buttons.length === 0) {
|
||||||
visible = false;
|
unpinnedAlpha = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6896,12 +6896,12 @@ void Application::toggleMuteAudio() {
|
||||||
menu->setIsOptionChecked(MenuOption::MuteAudio, !menu->isOptionChecked(MenuOption::MuteAudio));
|
menu->setIsOptionChecked(MenuOption::MuteAudio, !menu->isOptionChecked(MenuOption::MuteAudio));
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayID Application::getTabletScreenID() {
|
OverlayID Application::getTabletScreenID() const {
|
||||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||||
return HMD->getCurrentTabletScreenID();
|
return HMD->getCurrentTabletScreenID();
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayID Application::getTabletHomeButtonID() {
|
OverlayID Application::getTabletHomeButtonID() const {
|
||||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||||
return HMD->getCurrentHomeButtonUUID();
|
return HMD->getCurrentHomeButtonUUID();
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,8 +298,8 @@ public:
|
||||||
Q_INVOKABLE void sendHoverOverEntity(QUuid id, PointerEvent event);
|
Q_INVOKABLE void sendHoverOverEntity(QUuid id, PointerEvent event);
|
||||||
Q_INVOKABLE void sendHoverLeaveEntity(QUuid id, PointerEvent event);
|
Q_INVOKABLE void sendHoverLeaveEntity(QUuid id, PointerEvent event);
|
||||||
|
|
||||||
OverlayID getTabletScreenID();
|
OverlayID getTabletScreenID() const;
|
||||||
OverlayID getTabletHomeButtonID();
|
OverlayID getTabletHomeButtonID() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
|
|
|
@ -154,8 +154,7 @@ public:
|
||||||
AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
|
AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
|
||||||
AvatarSharedPointer avatar;
|
AvatarSharedPointer avatar;
|
||||||
float priority;
|
float priority;
|
||||||
// NOTE: we invert the less-than operator to sort high priorities to front
|
bool operator<(const AvatarPriority& other) const { return priority < other.priority; }
|
||||||
bool operator<(const AvatarPriority& other) const { return priority > other.priority; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
|
@ -205,15 +204,17 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
||||||
float radius = avatar->getBoundingRadius();
|
float radius = avatar->getBoundingRadius();
|
||||||
const glm::vec3& forward = cameraView.getDirection();
|
const glm::vec3& forward = cameraView.getDirection();
|
||||||
float apparentSize = radius / distance;
|
float apparentSize = 2.0f * radius / distance;
|
||||||
float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance;
|
float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance;
|
||||||
const float TIME_PENALTY = 0.080f; // seconds
|
float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND);
|
||||||
float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY;
|
|
||||||
// NOTE: we are adding values of different units to get a single measure of "priority".
|
// NOTE: we are adding values of different units to get a single measure of "priority".
|
||||||
// Thus we multiply each component by a conversion "weight" that scales its units
|
// Thus we multiply each component by a conversion "weight" that scales its units relative to the others.
|
||||||
// relative to the others. These weights are pure magic tuning and are hard coded in the
|
// These weights are pure magic tuning and should be hard coded in the relation below,
|
||||||
// relation below: (hint: unitary weights are not explicityly shown)
|
// but are currently exposed for anyone who would like to explore fine tuning:
|
||||||
float priority = apparentSize + 0.25f * cosineAngle + age;
|
float priority = _avatarSortCoefficientSize * apparentSize
|
||||||
|
+ _avatarSortCoefficientCenter * cosineAngle
|
||||||
|
+ _avatarSortCoefficientAge * age;
|
||||||
|
|
||||||
// decrement priority of avatars outside keyhole
|
// decrement priority of avatars outside keyhole
|
||||||
if (distance > cameraView.getCenterRadius()) {
|
if (distance > cameraView.getCenterRadius()) {
|
||||||
|
@ -593,3 +594,29 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay&
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK
|
||||||
|
float AvatarManager::getAvatarSortCoefficient(const QString& name) {
|
||||||
|
if (name == "size") {
|
||||||
|
return _avatarSortCoefficientSize;
|
||||||
|
} else if (name == "center") {
|
||||||
|
return _avatarSortCoefficientCenter;
|
||||||
|
} else if (name == "age") {
|
||||||
|
return _avatarSortCoefficientAge;
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK
|
||||||
|
void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptValue& value) {
|
||||||
|
if (value.isNumber()) {
|
||||||
|
float numericalValue = (float)value.toNumber();
|
||||||
|
if (name == "size") {
|
||||||
|
_avatarSortCoefficientSize = numericalValue;
|
||||||
|
} else if (name == "center") {
|
||||||
|
_avatarSortCoefficientCenter = numericalValue;
|
||||||
|
} else if (name == "age") {
|
||||||
|
_avatarSortCoefficientAge = numericalValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -81,6 +81,10 @@ public:
|
||||||
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||||
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
||||||
|
|
||||||
|
// TODO: remove this HACK once we settle on optimal default sort coefficients
|
||||||
|
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);
|
||||||
|
Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value);
|
||||||
|
|
||||||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -120,6 +124,11 @@ private:
|
||||||
int _partiallySimulatedAvatars { 0 };
|
int _partiallySimulatedAvatars { 0 };
|
||||||
float _avatarSimulationTime { 0.0f };
|
float _avatarSimulationTime { 0.0f };
|
||||||
|
|
||||||
|
// TODO: remove this HACK once we settle on optimal sort coefficients
|
||||||
|
// These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline.
|
||||||
|
float _avatarSortCoefficientSize { 0.5f };
|
||||||
|
float _avatarSortCoefficientCenter { 0.25 };
|
||||||
|
float _avatarSortCoefficientAge { 1.0f };
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||||
|
|
|
@ -462,7 +462,7 @@ RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
|
||||||
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
|
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
|
||||||
auto obj = engine->newObject();
|
auto obj = engine->newObject();
|
||||||
obj.setProperty("intersects", value.intersects);
|
obj.setProperty("intersects", value.intersects);
|
||||||
obj.setProperty("overlayID", OverlayIDtoScriptValue(engine, value.overlayID));
|
obj.setProperty("overlayID", OverlayIDtoScriptValue(engine, value.overlayID));
|
||||||
obj.setProperty("distance", value.distance);
|
obj.setProperty("distance", value.distance);
|
||||||
|
|
||||||
QString faceName = "";
|
QString faceName = "";
|
||||||
|
|
|
@ -1120,7 +1120,7 @@ void AudioClient::prepareLocalAudioInjectors() {
|
||||||
while (samplesNeeded > 0) {
|
while (samplesNeeded > 0) {
|
||||||
// lock for every write to avoid locking out the device callback
|
// lock for every write to avoid locking out the device callback
|
||||||
// this lock is intentional - the buffer is only lock-free in its use in the device callback
|
// this lock is intentional - the buffer is only lock-free in its use in the device callback
|
||||||
Lock lock(_localAudioMutex);
|
RecursiveLock lock(_localAudioMutex);
|
||||||
|
|
||||||
samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed);
|
samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed);
|
||||||
if (samplesNeeded <= 0) {
|
if (samplesNeeded <= 0) {
|
||||||
|
@ -1457,7 +1457,7 @@ void AudioClient::outputNotify() {
|
||||||
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
|
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
|
||||||
bool supportedFormat = false;
|
bool supportedFormat = false;
|
||||||
|
|
||||||
Lock lock(_localAudioMutex);
|
RecursiveLock lock(_localAudioMutex);
|
||||||
_localSamplesAvailable.exchange(0, std::memory_order_release);
|
_localSamplesAvailable.exchange(0, std::memory_order_release);
|
||||||
|
|
||||||
// cleanup any previously initialized device
|
// cleanup any previously initialized device
|
||||||
|
@ -1671,7 +1671,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
||||||
|
|
||||||
int injectorSamplesPopped = 0;
|
int injectorSamplesPopped = 0;
|
||||||
{
|
{
|
||||||
Lock lock(_audio->_localAudioMutex);
|
RecursiveLock lock(_audio->_localAudioMutex);
|
||||||
bool append = networkSamplesPopped > 0;
|
bool append = networkSamplesPopped > 0;
|
||||||
samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire));
|
samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire));
|
||||||
if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) {
|
if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) {
|
||||||
|
|
|
@ -94,6 +94,8 @@ public:
|
||||||
using AudioPositionGetter = std::function<glm::vec3()>;
|
using AudioPositionGetter = std::function<glm::vec3()>;
|
||||||
using AudioOrientationGetter = std::function<glm::quat()>;
|
using AudioOrientationGetter = std::function<glm::quat()>;
|
||||||
|
|
||||||
|
using RecursiveMutex = std::recursive_mutex;
|
||||||
|
using RecursiveLock = std::unique_lock<RecursiveMutex>;
|
||||||
using Mutex = std::mutex;
|
using Mutex = std::mutex;
|
||||||
using Lock = std::unique_lock<Mutex>;
|
using Lock = std::unique_lock<Mutex>;
|
||||||
|
|
||||||
|
@ -328,7 +330,7 @@ private:
|
||||||
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
|
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
|
||||||
float* _localOutputMixBuffer { NULL };
|
float* _localOutputMixBuffer { NULL };
|
||||||
AudioInjectorsThread _localAudioThread;
|
AudioInjectorsThread _localAudioThread;
|
||||||
Mutex _localAudioMutex;
|
RecursiveMutex _localAudioMutex;
|
||||||
|
|
||||||
// for output audio (used by this thread)
|
// for output audio (used by this thread)
|
||||||
int _outputPeriod { 0 };
|
int _outputPeriod { 0 };
|
||||||
|
|
30
libraries/entities/src/EntityNodeData.cpp
Normal file
30
libraries/entities/src/EntityNodeData.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// EntityNodeData.cpp
|
||||||
|
// libraries/entities/src
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2/15/17
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "EntityNodeData.h"
|
||||||
|
|
||||||
|
bool EntityNodeData::insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID) {
|
||||||
|
_flaggedExtraEntities[filteredEntityID].insert(extraEntityID);
|
||||||
|
return !_previousFlaggedExtraEntities[filteredEntityID].contains(extraEntityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityNodeData::isEntityFlaggedAsExtra(const QUuid& entityID) const {
|
||||||
|
|
||||||
|
// enumerate each of the sets for the entities that matched our filter
|
||||||
|
// and immediately return true if any of them contain this entity ID
|
||||||
|
foreach(QSet<QUuid> entitySet, _flaggedExtraEntities) {
|
||||||
|
if (entitySet.contains(entityID)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// EntityNodeData.h
|
// EntityNodeData.h
|
||||||
// assignment-client/src/entities
|
// libraries/entities/src
|
||||||
//
|
//
|
||||||
// Created by Brad Hefta-Gaub on 4/29/14
|
// Created by Brad Hefta-Gaub on 4/29/14
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
@ -16,6 +16,13 @@
|
||||||
|
|
||||||
#include <OctreeQueryNode.h>
|
#include <OctreeQueryNode.h>
|
||||||
|
|
||||||
|
namespace EntityJSONQueryProperties {
|
||||||
|
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
|
||||||
|
static const QString FLAGS_PROPERTY = "flags";
|
||||||
|
static const QString INCLUDE_ANCESTORS_PROPERTY = "includeAncestors";
|
||||||
|
static const QString INCLUDE_DESCENDANTS_PROPERTY = "includeDescendants";
|
||||||
|
}
|
||||||
|
|
||||||
class EntityNodeData : public OctreeQueryNode {
|
class EntityNodeData : public OctreeQueryNode {
|
||||||
public:
|
public:
|
||||||
virtual PacketType getMyPacketType() const override { return PacketType::EntityData; }
|
virtual PacketType getMyPacketType() const override { return PacketType::EntityData; }
|
||||||
|
@ -24,13 +31,24 @@ public:
|
||||||
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
||||||
|
|
||||||
// these can only be called from the OctreeSendThread for the given Node
|
// these can only be called from the OctreeSendThread for the given Node
|
||||||
void insertEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.insert(entityID); }
|
void insertSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.insert(entityID); }
|
||||||
void removeEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.remove(entityID); }
|
void removeSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.remove(entityID); }
|
||||||
bool sentEntityLastFrame(const QUuid& entityID) { return _entitiesSentLastFrame.contains(entityID); }
|
bool sentFilteredEntity(const QUuid& entityID) { return _sentFilteredEntities.contains(entityID); }
|
||||||
|
QSet<QUuid> getSentFilteredEntities() { return _sentFilteredEntities; }
|
||||||
|
|
||||||
|
// the following flagged extra entity methods can only be called from the OctreeSendThread for the given Node
|
||||||
|
|
||||||
|
// inserts the extra entity and returns a boolean indicating wether the extraEntityID was a new addition
|
||||||
|
bool insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID);
|
||||||
|
|
||||||
|
bool isEntityFlaggedAsExtra(const QUuid& entityID) const;
|
||||||
|
void resetFlaggedExtraEntities() { _previousFlaggedExtraEntities = _flaggedExtraEntities; _flaggedExtraEntities.clear(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
|
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
|
||||||
QSet<QUuid> _entitiesSentLastFrame;
|
QSet<QUuid> _sentFilteredEntities;
|
||||||
|
QHash<QUuid, QSet<QUuid>> _flaggedExtraEntities;
|
||||||
|
QHash<QUuid, QSet<QUuid>> _previousFlaggedExtraEntities;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityNodeData_h
|
#endif // hifi_EntityNodeData_h
|
||||||
|
|
|
@ -310,16 +310,17 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
||||||
|
|
||||||
if (entityMatchesFilters) {
|
if (entityMatchesFilters) {
|
||||||
// make sure this entity is in the set of entities sent last frame
|
// make sure this entity is in the set of entities sent last frame
|
||||||
entityNodeData->insertEntitySentLastFrame(entity->getID());
|
entityNodeData->insertSentFilteredEntity(entity->getID());
|
||||||
|
} else if (entityNodeData->sentFilteredEntity(entity->getID())) {
|
||||||
} else {
|
// this entity matched in the previous frame - we send it still so the client realizes it just
|
||||||
// we might include this entity if it matched in the previous frame
|
// fell outside of their filter
|
||||||
if (entityNodeData->sentEntityLastFrame(entity->getID())) {
|
entityNodeData->removeSentFilteredEntity(entity->getID());
|
||||||
|
} else if (!entityNodeData->isEntityFlaggedAsExtra(entity->getID())) {
|
||||||
entityNodeData->removeEntitySentLastFrame(entity->getID());
|
// we don't send this entity because
|
||||||
} else {
|
// (1) it didn't match our filter
|
||||||
includeThisEntity = false;
|
// (2) it didn't match our filter last frame
|
||||||
}
|
// (3) it isn't one the JSON query flags told us we should still include
|
||||||
|
includeThisEntity = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return VERSION_ENTITIES_ZONE_FILTERS;
|
return VERSION_ENTITIES_ZONE_FILTERS;
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::JsonFilter);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
case PacketType::AvatarData:
|
case PacketType::AvatarData:
|
||||||
case PacketType::BulkAvatarData:
|
case PacketType::BulkAvatarData:
|
||||||
|
|
|
@ -207,7 +207,8 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
|
||||||
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
|
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
|
||||||
|
|
||||||
enum class EntityQueryPacketVersion: PacketVersion {
|
enum class EntityQueryPacketVersion: PacketVersion {
|
||||||
JsonFilter = 18
|
JSONFilter = 18,
|
||||||
|
JSONFilterWithFamilyTree = 19
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AssetServerPacketVersion: PacketVersion {
|
enum class AssetServerPacketVersion: PacketVersion {
|
||||||
|
|
|
@ -103,6 +103,9 @@ public:
|
||||||
// call only from OctreeSendThread for the given node
|
// call only from OctreeSendThread for the given node
|
||||||
bool haveJSONParametersChanged();
|
bool haveJSONParametersChanged();
|
||||||
|
|
||||||
|
bool shouldForceFullScene() const { return _shouldForceFullScene; }
|
||||||
|
void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OctreeQueryNode(const OctreeQueryNode &);
|
OctreeQueryNode(const OctreeQueryNode &);
|
||||||
OctreeQueryNode& operator= (const OctreeQueryNode&);
|
OctreeQueryNode& operator= (const OctreeQueryNode&);
|
||||||
|
@ -148,6 +151,8 @@ private:
|
||||||
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
|
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
|
||||||
|
|
||||||
QJsonObject _lastCheckJSONParameters;
|
QJsonObject _lastCheckJSONParameters;
|
||||||
|
|
||||||
|
bool _shouldForceFullScene { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_OctreeQueryNode_h
|
#endif // hifi_OctreeQueryNode_h
|
||||||
|
|
|
@ -70,6 +70,9 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) {
|
||||||
_parentKnowsMe = false;
|
_parentKnowsMe = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
getParentPointer(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const {
|
Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const {
|
||||||
|
|
Loading…
Reference in a new issue