mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 20:22:22 +02:00
Merge branch 'android' into android_render_pr
This commit is contained in:
commit
68cd768a65
125 changed files with 1795 additions and 2189 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -23,7 +23,7 @@ android/app/src/main/res/values/libs.xml
|
|||
android/app/src/main/assets/bundled
|
||||
|
||||
# VSCode
|
||||
# List taken from Github Global Ignores master@435c4d92
|
||||
# List taken from Github Global Ignores master@435c4d92
|
||||
# https://github.com/github/gitignore/commits/master/Global/VisualStudioCode.gitignore
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
|
@ -69,7 +69,7 @@ gvr-interface/libs/*
|
|||
# ignore files for various dev environments
|
||||
TAGS
|
||||
*.sw[po]
|
||||
*.qmlc
|
||||
*.jsc
|
||||
|
||||
# ignore QML compilation output
|
||||
*.qmlc
|
||||
|
|
|
@ -520,6 +520,8 @@ task copyInterfaceAssets() {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
|
||||
// See the comment on the qtBundle task above
|
||||
/*
|
||||
// FIXME derive the path from the gradle environment
|
||||
def toolchain = [
|
||||
|
|
|
@ -94,7 +94,6 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
packetReceiver.registerListenerForTypes(
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
|
||||
|
||||
|
@ -149,17 +148,6 @@ void Agent::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNo
|
|||
}
|
||||
}
|
||||
|
||||
void Agent::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
NodeType_t nodeType;
|
||||
message->peekPrimitive(&nodeType);
|
||||
|
||||
// PacketType_JURISDICTION, first byte is the node type...
|
||||
if (nodeType == NodeType::EntityServer) {
|
||||
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
|
||||
queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::handleAudioPacket(QSharedPointer<ReceivedMessage> message) {
|
||||
_receivedAudioStream.parseData(*message);
|
||||
_lastReceivedAudioLoudness = _receivedAudioStream.getNextOutputFrameLoudness();
|
||||
|
@ -483,10 +471,7 @@ void Agent::executeScript() {
|
|||
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||
_scriptEngine->registerGlobalObject("Recording", recordingInterface.data());
|
||||
|
||||
// we need to make sure that init has been called for our EntityScriptingInterface
|
||||
// so that it actually has a jurisdiction listener when we ask it for it next
|
||||
entityScriptingInterface->init();
|
||||
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
|
||||
|
||||
_entityViewer.init();
|
||||
|
||||
|
|
|
@ -73,7 +73,6 @@ private slots:
|
|||
|
||||
void handleAudioPacket(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void nodeActivated(SharedNodePointer activatedNode);
|
||||
|
|
|
@ -23,23 +23,6 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
char serverType = getMyNodeType();
|
||||
PacketType packetType = getMyQueryMessageType();
|
||||
|
||||
NodeToJurisdictionMap& jurisdictions = *_jurisdictionListener->getJurisdictions();
|
||||
|
||||
bool wantExtraDebugging = false;
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "OctreeHeadlessViewer::queryOctree() _jurisdictionListener=" << _jurisdictionListener;
|
||||
qCDebug(octree) << "---------------";
|
||||
qCDebug(octree) << "_jurisdictionListener=" << _jurisdictionListener;
|
||||
qCDebug(octree) << "Jurisdictions...";
|
||||
jurisdictions.withReadLock([&] {
|
||||
for (NodeToJurisdictionMapIterator i = jurisdictions.begin(); i != jurisdictions.end(); ++i) {
|
||||
qCDebug(octree) << i.key() << ": " << &i.value();
|
||||
}
|
||||
});
|
||||
qCDebug(octree) << "---------------";
|
||||
}
|
||||
|
||||
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
|
||||
_octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
|
||||
_octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
|
||||
|
@ -51,159 +34,22 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
_octreeQuery.setOctreeSizeScale(_voxelSizeScale);
|
||||
_octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
|
||||
|
||||
// Iterate all of the nodes, and get a count of how many voxel servers we have...
|
||||
int totalServers = 0;
|
||||
int inViewServers = 0;
|
||||
int unknownJurisdictionServers = 0;
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node){
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
totalServers++;
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
VoxelPositionSize rootDetails;
|
||||
bool foundRootDetails = false;
|
||||
jurisdictions.withReadLock([&] {
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownJurisdictionServers++;
|
||||
return;
|
||||
}
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
if (!rootCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
foundRootDetails = true;
|
||||
});
|
||||
|
||||
if (foundRootDetails) {
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
if ((bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds))) {
|
||||
inViewServers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree, "Servers: total %d, in view %d, unknown jurisdiction %d",
|
||||
totalServers, inViewServers, unknownJurisdictionServers);
|
||||
}
|
||||
|
||||
int perServerPPS = 0;
|
||||
const int SMALL_BUDGET = 10;
|
||||
int perUnknownServer = SMALL_BUDGET;
|
||||
int totalPPS = getMaxPacketsPerSecond();
|
||||
|
||||
// determine PPS based on number of servers
|
||||
if (inViewServers >= 1) {
|
||||
// set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
|
||||
// for each unknown jurisdiction server
|
||||
perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
|
||||
} else {
|
||||
if (unknownJurisdictionServers > 0) {
|
||||
perUnknownServer = (totalPPS / unknownJurisdictionServers);
|
||||
}
|
||||
}
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
auto node = nodeList->soloNodeOfType(serverType);
|
||||
if (node && node->getActiveSocket()) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(getMaxPacketsPerSecond());
|
||||
|
||||
bool inView = false;
|
||||
bool unknownView = false;
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
VoxelPositionSize rootDetails;
|
||||
bool foundRootDetails = false;
|
||||
jurisdictions.withReadLock([&] {
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownView = true; // assume it's in view
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "no known jurisdiction for node " << *node << ", assume it's visible.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
// encode the query data
|
||||
auto packetData = reinterpret_cast<unsigned char*>(queryPacket->getPayload());
|
||||
int packetSize = _octreeQuery.getBroadcastData(packetData);
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (!rootCode) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
|
||||
}
|
||||
return;
|
||||
}
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
foundRootDetails = true;
|
||||
});
|
||||
|
||||
if (foundRootDetails) {
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
inView = (bool)(_viewFrustum.calculateCubeKeyholeIntersection(serverBounds));
|
||||
}
|
||||
|
||||
if (inView) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "inView for node " << *node << ", give it budget of " << perServerPPS;
|
||||
}
|
||||
} else if (unknownView) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "no known jurisdiction for node " << *node << ", give it budget of "
|
||||
<< perUnknownServer << " to send us jurisdiction.";
|
||||
}
|
||||
|
||||
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
|
||||
// If there's only one server, then don't do this, and just let the normal voxel query pass through
|
||||
// as expected... this way, we will actually get a valid scene if there is one to be seen
|
||||
if (totalServers > 1) {
|
||||
_octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1));
|
||||
const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
|
||||
_octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
|
||||
_octreeQuery.setCameraNearClip(0.1f);
|
||||
_octreeQuery.setCameraFarClip(0.1f);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "Using 'minimal' camera position for node" << *node;
|
||||
}
|
||||
} else {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(octree) << "Using regular camera position for node" << *node;
|
||||
}
|
||||
}
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
|
||||
} else {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
|
||||
// setup the query packet
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
// read the data to our packet and set the payload size to fit the query
|
||||
int querySize = _octreeQuery.getBroadcastData(reinterpret_cast<unsigned char*>(queryPacket->getPayload()));
|
||||
queryPacket->setPayloadSize(querySize);
|
||||
|
||||
// ask the NodeList to send it
|
||||
nodeList->sendPacket(std::move(queryPacket), *node);
|
||||
}
|
||||
});
|
||||
// make sure we still have an active socket
|
||||
nodeList->sendUnreliablePacket(*queryPacket, *node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_OctreeHeadlessViewer_h
|
||||
|
||||
#include <OctreeProcessor.h>
|
||||
#include <JurisdictionListener.h>
|
||||
#include <OctreeQuery.h>
|
||||
|
||||
|
||||
|
@ -23,8 +22,6 @@ class OctreeHeadlessViewer : public OctreeProcessor {
|
|||
public:
|
||||
OctreeHeadlessViewer();
|
||||
virtual ~OctreeHeadlessViewer() {};
|
||||
|
||||
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
|
||||
|
||||
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
|
||||
|
||||
|
@ -57,7 +54,6 @@ public slots:
|
|||
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
|
||||
|
||||
private:
|
||||
JurisdictionListener* _jurisdictionListener = nullptr;
|
||||
OctreeQuery _octreeQuery;
|
||||
|
||||
ViewFrustum _viewFrustum;
|
||||
|
|
|
@ -391,8 +391,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
|
||||
nodeData->sceneStart(usecTimestampNow() - CHANGE_FUDGE);
|
||||
// start tracking our stats
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
|
||||
_myServer->getOctree()->getRoot(), _myServer->getJurisdiction());
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot());
|
||||
|
||||
preStartNewScene(nodeData, isFullScene);
|
||||
}
|
||||
|
@ -507,7 +506,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
|
||||
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
|
||||
isFullScene, _myServer->getJurisdiction(), nodeData);
|
||||
isFullScene, nodeData);
|
||||
// Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent
|
||||
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
|
||||
|
|
|
@ -237,8 +237,6 @@ OctreeServer::OctreeServer(ReceivedMessage& message) :
|
|||
_debugSending(false),
|
||||
_debugReceiving(false),
|
||||
_verboseDebug(false),
|
||||
_jurisdiction(NULL),
|
||||
_jurisdictionSender(NULL),
|
||||
_octreeInboundPacketProcessor(NULL),
|
||||
_persistThread(NULL),
|
||||
_started(time(0)),
|
||||
|
@ -257,12 +255,6 @@ OctreeServer::~OctreeServer() {
|
|||
delete[] _parsedArgV;
|
||||
}
|
||||
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->terminating();
|
||||
_jurisdictionSender->terminate();
|
||||
_jurisdictionSender->deleteLater();
|
||||
}
|
||||
|
||||
if (_octreeInboundPacketProcessor) {
|
||||
_octreeInboundPacketProcessor->terminating();
|
||||
_octreeInboundPacketProcessor->terminate();
|
||||
|
@ -275,9 +267,6 @@ OctreeServer::~OctreeServer() {
|
|||
_persistThread->deleteLater();
|
||||
}
|
||||
|
||||
delete _jurisdiction;
|
||||
_jurisdiction = NULL;
|
||||
|
||||
// cleanup our tree here...
|
||||
qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]";
|
||||
_tree.reset();
|
||||
|
@ -933,10 +922,6 @@ void OctreeServer::handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> me
|
|||
}
|
||||
}
|
||||
|
||||
void OctreeServer::handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
_jurisdictionSender->queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
|
||||
void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message) {
|
||||
if (!_isFinished && !_isShuttingDown) {
|
||||
// these messages are only allowed to come from the domain server, so make sure that is the case
|
||||
|
@ -1111,23 +1096,6 @@ void OctreeServer::readConfiguration() {
|
|||
qDebug() << "statusPort= DISABLED";
|
||||
}
|
||||
|
||||
QString jurisdictionFile;
|
||||
if (readOptionString(QString("jurisdictionFile"), settingsSectionObject, jurisdictionFile)) {
|
||||
qDebug("jurisdictionFile=%s", qPrintable(jurisdictionFile));
|
||||
qDebug("about to readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile));
|
||||
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionFile));
|
||||
qDebug("after readFromFile().... jurisdictionFile=%s", qPrintable(jurisdictionFile));
|
||||
} else {
|
||||
QString jurisdictionRoot;
|
||||
bool hasRoot = readOptionString(QString("jurisdictionRoot"), settingsSectionObject, jurisdictionRoot);
|
||||
QString jurisdictionEndNodes;
|
||||
bool hasEndNodes = readOptionString(QString("jurisdictionEndNodes"), settingsSectionObject, jurisdictionEndNodes);
|
||||
|
||||
if (hasRoot || hasEndNodes) {
|
||||
_jurisdiction = new JurisdictionMap(qPrintable(jurisdictionRoot), qPrintable(jurisdictionEndNodes));
|
||||
}
|
||||
}
|
||||
|
||||
readOptionBool(QString("verboseDebug"), settingsSectionObject, _verboseDebug);
|
||||
qDebug("verboseDebug=%s", debug::valueOf(_verboseDebug));
|
||||
|
||||
|
@ -1241,7 +1209,6 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
|
||||
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
|
||||
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
|
||||
|
||||
|
@ -1365,13 +1332,6 @@ void OctreeServer::domainSettingsRequestComplete() {
|
|||
_persistThread->initialize(true);
|
||||
}
|
||||
|
||||
// set up our jurisdiction broadcaster...
|
||||
if (_jurisdiction) {
|
||||
_jurisdiction->setNodeType(getMyNodeType());
|
||||
}
|
||||
_jurisdictionSender = new JurisdictionSender(_jurisdiction, getMyNodeType());
|
||||
_jurisdictionSender->initialize(true);
|
||||
|
||||
// set up our OctreeServerPacketProcessor
|
||||
_octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this);
|
||||
_octreeInboundPacketProcessor->initialize(true);
|
||||
|
@ -1441,10 +1401,6 @@ void OctreeServer::aboutToFinish() {
|
|||
_octreeInboundPacketProcessor->terminating();
|
||||
}
|
||||
|
||||
if (_jurisdictionSender) {
|
||||
_jurisdictionSender->terminating();
|
||||
}
|
||||
|
||||
// Shut down all the send threads
|
||||
for (auto& it : _sendThreads) {
|
||||
auto& sendThread = *it.second;
|
||||
|
|
|
@ -44,7 +44,6 @@ public:
|
|||
bool wantsVerboseDebug() const { return _verboseDebug; }
|
||||
|
||||
OctreePointer getOctree() { return _tree; }
|
||||
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
|
||||
|
||||
int getPacketsPerClientPerInterval() const { return std::min(_packetsPerClientPerInterval,
|
||||
std::max(1, getPacketsTotalPerInterval() / std::max(1, getCurrentClientCount()))); }
|
||||
|
@ -138,7 +137,6 @@ private slots:
|
|||
void domainSettingsRequestComplete();
|
||||
void handleOctreeQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message);
|
||||
void handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message);
|
||||
void removeSendThread();
|
||||
|
@ -190,8 +188,6 @@ protected:
|
|||
bool _debugReceiving;
|
||||
bool _debugTimestampNow;
|
||||
bool _verboseDebug;
|
||||
JurisdictionMap* _jurisdiction;
|
||||
JurisdictionSender* _jurisdictionSender;
|
||||
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
|
||||
OctreePersistThread* _persistThread;
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <SharedUtil.h>
|
||||
#include <NodeList.h> // for MAX_PACKET_SIZE
|
||||
#include <JurisdictionSender.h>
|
||||
|
||||
const int MAX_FILENAME_LENGTH = 1024;
|
||||
|
||||
|
|
|
@ -79,7 +79,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket");
|
||||
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
|
||||
|
||||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||
|
@ -283,11 +282,8 @@ void EntityScriptServer::run() {
|
|||
// Setup Script Engine
|
||||
resetEntitiesScriptEngine();
|
||||
|
||||
// we need to make sure that init has been called for our EntityScriptingInterface
|
||||
// so that it actually has a jurisdiction listener when we ask it for it next
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->init();
|
||||
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
|
||||
|
||||
_entityViewer.init();
|
||||
|
||||
|
@ -566,17 +562,6 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer<ReceivedMessage> mess
|
|||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
NodeType_t nodeType;
|
||||
message->peekPrimitive(&nodeType);
|
||||
|
||||
// PacketType_JURISDICTION, first byte is the node type...
|
||||
if (nodeType == NodeType::EntityServer) {
|
||||
DependencyManager::get<EntityScriptingInterface>()->getJurisdictionListener()->
|
||||
queueReceivedPacket(message, senderNode);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptServer::aboutToFinish() {
|
||||
shutdownScriptEngine();
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
|
||||
|
||||
void handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
|
|
@ -56,29 +56,28 @@
|
|||
|
||||
{
|
||||
"from": "Vive.LeftFoot", "to" : "Standard.LeftFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.RightFoot", "to" : "Standard.RightFoot",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Hips", "to" : "Standard.Hips",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{
|
||||
"from": "Vive.Spine2", "to" : "Standard.Spine2",
|
||||
"filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}]
|
||||
"filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}]
|
||||
},
|
||||
|
||||
{ "from": "Vive.Head", "to" : "Standard.Head"},
|
||||
|
||||
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
|
||||
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" },
|
||||
|
||||
|
||||
{ "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" },
|
||||
{ "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" },
|
||||
{ "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" },
|
||||
|
|
|
@ -110,7 +110,7 @@ Item {
|
|||
}
|
||||
|
||||
function pullFreshValues() {
|
||||
if (Audio.getRecording()) {
|
||||
if (AudioScriptingInterface.getRecording()) {
|
||||
updateRecordingLabel();
|
||||
}
|
||||
|
||||
|
@ -129,14 +129,14 @@ Item {
|
|||
_wavFilePath = _wavFilePath.replace(/[\-:]|\.\d*Z$/g, "").replace("T", "-") + ".wav";
|
||||
// Using controller recording default directory
|
||||
_wavFilePath = Recording.getDefaultRecordingSaveDirectory() + _wavFilePath;
|
||||
if (!Audio.startRecording(_wavFilePath)) {
|
||||
if (!AudioScriptingInterface.startRecording(_wavFilePath)) {
|
||||
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Error creating: "+_wavFilePath}));
|
||||
updateRecordingUI(false);
|
||||
}
|
||||
}
|
||||
|
||||
function stopRecording() {
|
||||
Audio.stopRecording();
|
||||
AudioScriptingInterface.stopRecording();
|
||||
setRecordingLabelOpacity(0.0);
|
||||
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Saved: "+_wavFilePath}));
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ Item {
|
|||
}
|
||||
|
||||
function toggleRecording() {
|
||||
if (Audio.getRecording()) {
|
||||
if (AudioScriptingInterface.getRecording()) {
|
||||
updateRecordingUI(false);
|
||||
stopRecording();
|
||||
} else {
|
||||
|
|
|
@ -597,18 +597,11 @@ Item {
|
|||
// Function body by Howard Stearns 2017-01-08
|
||||
function goToUserInDomain(avatarUuid) {
|
||||
var avatar = AvatarList.getAvatar(avatarUuid);
|
||||
if (!avatar) {
|
||||
if (!avatar || !avatar.position || !avatar.orientation) {
|
||||
console.log("This avatar is no longer present. goToUserInDomain() failed.");
|
||||
return;
|
||||
}
|
||||
// FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up,
|
||||
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
|
||||
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
|
||||
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
|
||||
// Position avatar 2 metres from the target in the direction that target avatar was facing.
|
||||
MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2}));
|
||||
|
||||
// Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch.
|
||||
MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1}));
|
||||
// This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username.
|
||||
MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ Rectangle {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
property var eventBridge;
|
||||
property string title: "Audio Settings - " + Audio.context;
|
||||
property string title: "Audio Settings - " + AudioScriptingInterface.context;
|
||||
signal sendToScript(var message);
|
||||
|
||||
color: hifi.colors.baseGray;
|
||||
|
@ -37,7 +37,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
|
||||
property bool isVR: Audio.context === "VR"
|
||||
property bool isVR: AudioScriptingInterface.context === "VR"
|
||||
property real rightMostInputLevelPos: 0
|
||||
//placeholder for control sizes and paddings
|
||||
//recalculates dynamically in case of UI size is changed
|
||||
|
@ -72,17 +72,17 @@ Rectangle {
|
|||
property bool showPeaks: true;
|
||||
|
||||
function enablePeakValues() {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
|
||||
if (!enabled && root.showPeaks) {
|
||||
Audio.devices.input.peakValuesEnabled = true;
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function disablePeakValues() {
|
||||
root.showPeaks = false;
|
||||
Audio.devices.input.peakValuesEnabled = false;
|
||||
AudioScriptingInterface.devices.input.peakValuesEnabled = false;
|
||||
}
|
||||
|
||||
Component.onCompleted: enablePeakValues();
|
||||
|
@ -117,10 +117,10 @@ Rectangle {
|
|||
text: qsTr("Mute microphone");
|
||||
spacing: margins.sizeCheckBox - boxSize
|
||||
isRedCheck: true;
|
||||
checked: Audio.muted;
|
||||
checked: AudioScriptingInterface.muted;
|
||||
onClicked: {
|
||||
Audio.muted = checked;
|
||||
checked = Qt.binding(function() { return Audio.muted; }); // restore binding
|
||||
AudioScriptingInterface.muted = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,10 +130,10 @@ Rectangle {
|
|||
AudioControls.CheckBox {
|
||||
spacing: muteMic.spacing
|
||||
text: qsTr("Enable noise reduction");
|
||||
checked: Audio.noiseReduction;
|
||||
checked: AudioScriptingInterface.noiseReduction;
|
||||
onClicked: {
|
||||
Audio.noiseReduction = checked;
|
||||
checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding
|
||||
AudioScriptingInterface.noiseReduction = checked;
|
||||
checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding
|
||||
}
|
||||
}
|
||||
AudioControls.CheckBox {
|
||||
|
@ -184,7 +184,7 @@ Rectangle {
|
|||
spacing: 4;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
model: Audio.devices.input;
|
||||
model: AudioScriptingInterface.devices.input;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
|
||||
|
@ -204,7 +204,7 @@ Rectangle {
|
|||
text: devicename
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
Audio.setInputDevice(info, bar.currentIndex === 1);
|
||||
AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ Rectangle {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: ((bar.currentIndex === 1 && isVR) ||
|
||||
(bar.currentIndex === 0 && !isVR)) &&
|
||||
Audio.devices.input.peakValuesAvailable;
|
||||
AudioScriptingInterface.devices.input.peakValuesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ Rectangle {
|
|||
spacing: 4;
|
||||
snapMode: ListView.SnapToItem;
|
||||
clip: true;
|
||||
model: Audio.devices.output;
|
||||
model: AudioScriptingInterface.devices.output;
|
||||
delegate: Item {
|
||||
width: rightMostInputLevelPos
|
||||
height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ?
|
||||
|
@ -273,7 +273,7 @@ Rectangle {
|
|||
text: devicename
|
||||
onPressed: {
|
||||
if (!checked) {
|
||||
Audio.setOutputDevice(info, bar.currentIndex === 1);
|
||||
AudioScriptingInterface.setOutputDevice(info, bar.currentIndex === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ Rectangle {
|
|||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
|
||||
visible: Audio.muted;
|
||||
visible: AudioScriptingInterface.muted;
|
||||
color: colors.muted;
|
||||
|
||||
text: "MUTED";
|
||||
|
|
|
@ -17,7 +17,7 @@ import QtGraphicalEffects 1.0
|
|||
import TabletScriptingInterface 1.0
|
||||
|
||||
Rectangle {
|
||||
readonly property var level: Audio.inputLevel;
|
||||
readonly property var level: AudioScriptingInterface.inputLevel;
|
||||
|
||||
property bool standalone: false;
|
||||
property var dragTarget: null;
|
||||
|
@ -60,7 +60,7 @@ Rectangle {
|
|||
hoverEnabled: true;
|
||||
scrollGestureEnabled: false;
|
||||
onClicked: {
|
||||
Audio.muted = !Audio.muted;
|
||||
AudioScriptingInterface.muted = !AudioScriptingInterface.muted;
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
drag.target: dragTarget;
|
||||
|
@ -82,7 +82,7 @@ Rectangle {
|
|||
readonly property string red: colors.muted;
|
||||
readonly property string fill: "#55000000";
|
||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||
readonly property string icon: Audio.muted ? muted : unmuted;
|
||||
readonly property string icon: AudioScriptingInterface.muted ? muted : unmuted;
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -103,7 +103,7 @@ Rectangle {
|
|||
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
|
||||
|
||||
id: image;
|
||||
source: Audio.muted ? mutedIcon : unmutedIcon;
|
||||
source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon;
|
||||
|
||||
width: 30;
|
||||
height: 30;
|
||||
|
@ -126,9 +126,9 @@ Rectangle {
|
|||
Item {
|
||||
id: status;
|
||||
|
||||
readonly property string color: Audio.muted ? colors.muted : colors.unmuted;
|
||||
readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted;
|
||||
|
||||
visible: Audio.muted;
|
||||
visible: AudioScriptingInterface.muted;
|
||||
|
||||
anchors {
|
||||
left: parent.left;
|
||||
|
@ -147,7 +147,7 @@ Rectangle {
|
|||
|
||||
color: parent.color;
|
||||
|
||||
text: Audio.muted ? "MUTED" : "MUTE";
|
||||
text: AudioScriptingInterface.muted ? "MUTED" : "MUTE";
|
||||
font.pointSize: 12;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ RowLayout {
|
|||
}
|
||||
function playSound() {
|
||||
// FIXME: MyAvatar is not properly exposed to QML; MyAvatar.qmlPosition is a stopgap
|
||||
// FIXME: Audio.playSystemSound should not require position
|
||||
// FIXME: AudioScriptingInterface.playSystemSound should not require position
|
||||
if (sample === null && !isPlaying) {
|
||||
sample = Audio.playSystemSound(sound, MyAvatar.qmlPosition);
|
||||
sample = AudioScriptingInterface.playSystemSound(sound, MyAvatar.qmlPosition);
|
||||
isPlaying = true;
|
||||
sample.finished.connect(reset);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ Item {
|
|||
source: "image://security/securityImage";
|
||||
cache: false;
|
||||
onVisibleChanged: {
|
||||
commerce.getSecurityImage();
|
||||
Commerce.getSecurityImage();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
|
@ -194,7 +194,7 @@ Item {
|
|||
securityImageSubmitButton.text = "Submitting...";
|
||||
securityImageSubmitButton.enabled = false;
|
||||
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
|
||||
commerce.chooseSecurityImage(securityImagePath);
|
||||
Commerce.chooseSecurityImage(securityImagePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,14 +197,36 @@ Item {
|
|||
anchors.topMargin: 26;
|
||||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 30;
|
||||
width: paintedWidth;
|
||||
height: 30;
|
||||
// Text size
|
||||
size: 22;
|
||||
// Style
|
||||
color: hifi.colors.baseGrayHighlight;
|
||||
}
|
||||
|
||||
RalewaySemiBold {
|
||||
id: myPurchasesLink;
|
||||
text: '<font color="#0093C5"><a href="#myPurchases">My Purchases</a></font>';
|
||||
// Anchors
|
||||
anchors.top: parent.top;
|
||||
anchors.topMargin: 26;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 20;
|
||||
width: paintedWidth;
|
||||
height: 30;
|
||||
y: 4;
|
||||
// Text size
|
||||
size: 18;
|
||||
// Style
|
||||
color: hifi.colors.baseGrayHighlight;
|
||||
horizontalAlignment: Text.AlignRight;
|
||||
|
||||
onLinkActivated: {
|
||||
sendSignalToWallet({method: 'goToPurchases_fromWalletHome'});
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: tempTransactionHistoryModel;
|
||||
}
|
||||
|
|
|
@ -72,10 +72,14 @@ StackView {
|
|||
Component { id: tabletWebView; TabletWebView {} }
|
||||
Component.onCompleted: {
|
||||
updateLocationText(false);
|
||||
addressLine.focus = !HMD.active;
|
||||
root.parentChanged.connect(center);
|
||||
center();
|
||||
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
Qt.callLater(function() {
|
||||
addressBarDialog.keyboardEnabled = HMD.active;
|
||||
addressLine.forceActiveFocus();
|
||||
})
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.parentChanged.disconnect(center);
|
||||
|
|
|
@ -23,11 +23,26 @@ Item {
|
|||
property double sortOrder: 100
|
||||
property int stableOrder: 0
|
||||
property var tabletRoot;
|
||||
property var flickable: null
|
||||
property var gridView: null
|
||||
|
||||
property int buttonIndex: -1
|
||||
|
||||
width: 129
|
||||
height: 129
|
||||
|
||||
signal clicked()
|
||||
|
||||
Connections {
|
||||
target: flickable
|
||||
onMovingChanged: {
|
||||
//when flick/move started, and hover is on, clean hove state
|
||||
if (flickable.moving && tabletButton.state.indexOf("hover") !== -1) {
|
||||
tabletButton.state = (tabletButton.isActive) ? "active state" : "base state";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeProperty(key, value) {
|
||||
tabletButton[key] = value;
|
||||
}
|
||||
|
@ -121,8 +136,10 @@ Item {
|
|||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
preventStealing: true
|
||||
preventStealing: false
|
||||
onClicked: {
|
||||
gridView.currentIndex = buttonIndex
|
||||
|
||||
if (tabletButton.inDebugMode) {
|
||||
if (tabletButton.isActive) {
|
||||
tabletButton.isActive = false;
|
||||
|
@ -130,12 +147,15 @@ Item {
|
|||
tabletButton.isActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
tabletButton.clicked();
|
||||
if (tabletRoot) {
|
||||
Tablet.playSound(TabletEnums.ButtonClick);
|
||||
}
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
gridView.currentIndex = buttonIndex
|
||||
tabletButton.isEntered = true;
|
||||
Tablet.playSound(TabletEnums.ButtonHover);
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import TabletScriptingInterface 1.0
|
||||
|
||||
import "."
|
||||
import "../../styles-uit"
|
||||
import "../audio" as HifiAudio
|
||||
|
@ -10,7 +13,11 @@ Item {
|
|||
id: tablet
|
||||
objectName: "tablet"
|
||||
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
|
||||
property var currentGridItems: null
|
||||
|
||||
focus: true
|
||||
|
||||
Rectangle {
|
||||
id: bgTopBar
|
||||
height: 90
|
||||
|
@ -85,7 +92,6 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
id: bgMain
|
||||
clip: true
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0
|
||||
|
@ -102,55 +108,186 @@ Item {
|
|||
anchors.left: parent.left
|
||||
anchors.top: bgTopBar.bottom
|
||||
|
||||
GridView {
|
||||
id: flickable
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 15
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: cellWidth * 3
|
||||
cellHeight: 145
|
||||
cellWidth: 145
|
||||
model: tabletProxy.buttons
|
||||
delegate: Item {
|
||||
width: flickable.cellWidth
|
||||
height: flickable.cellHeight
|
||||
property var proxy: modelData
|
||||
|
||||
TabletButton {
|
||||
id: tabletButton
|
||||
anchors.centerIn: parent
|
||||
onClicked: modelData.clicked()
|
||||
state: wrapper.GridView.isCurrentItem ? "hover state" : "base state"
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
clip: false
|
||||
currentIndex: -1
|
||||
property int previousIndex: -1
|
||||
Repeater {
|
||||
id: pageRepeater
|
||||
model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage)
|
||||
onItemAdded: {
|
||||
item.proxyModel.sourceModel = tabletProxy.buttons;
|
||||
item.proxyModel.pageIndex = index;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelData;
|
||||
onPropertiesChanged: {
|
||||
updateProperties();
|
||||
delegate: Item {
|
||||
id: page
|
||||
property TabletButtonsProxyModel proxyModel: TabletButtonsProxyModel {}
|
||||
|
||||
GridView {
|
||||
id: gridView
|
||||
keyNavigationEnabled: false
|
||||
highlightFollowsCurrentItem: false
|
||||
property int previousGridIndex: -1
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: 20
|
||||
leftMargin: 30
|
||||
rightMargin: 30
|
||||
bottomMargin: 0
|
||||
}
|
||||
|
||||
function setButtonState(buttonIndex, buttonstate) {
|
||||
if (buttonIndex < 0 || gridView.contentItem === undefined
|
||||
|| gridView.contentItem.children.length - 1 < buttonIndex) {
|
||||
return;
|
||||
}
|
||||
var itemat = gridView.contentItem.children[buttonIndex].children[0];
|
||||
if (itemat.isActive) {
|
||||
itemat.state = "active state";
|
||||
} else {
|
||||
itemat.state = buttonstate;
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
setButtonState(previousGridIndex, "base state");
|
||||
setButtonState(currentIndex, "hover state");
|
||||
previousGridIndex = currentIndex
|
||||
}
|
||||
|
||||
cellWidth: width/3
|
||||
cellHeight: cellWidth
|
||||
flow: GridView.LeftToRight
|
||||
model: page.proxyModel
|
||||
|
||||
delegate: Item {
|
||||
id: wrapper
|
||||
width: gridView.cellWidth
|
||||
height: gridView.cellHeight
|
||||
|
||||
property var proxy: modelData
|
||||
|
||||
TabletButton {
|
||||
id: tabletButton
|
||||
anchors.centerIn: parent
|
||||
gridView: wrapper.GridView.view
|
||||
buttonIndex: page.proxyModel.buttonIndex(uuid);
|
||||
flickable: swipeView.contentItem;
|
||||
onClicked: modelData.clicked()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: modelData;
|
||||
onPropertiesChanged: {
|
||||
updateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: updateProperties()
|
||||
|
||||
function updateProperties() {
|
||||
var keys = Object.keys(modelData.properties).forEach(function (key) {
|
||||
if (tabletButton[key] !== modelData.properties[key]) {
|
||||
tabletButton[key] = modelData.properties[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: updateProperties()
|
||||
|
||||
function updateProperties() {
|
||||
var keys = Object.keys(modelData.properties).forEach(function (key) {
|
||||
if (tabletButton[key] !== modelData.properties[key]) {
|
||||
tabletButton[key] = modelData.properties[key];
|
||||
}
|
||||
});
|
||||
onCurrentIndexChanged: {
|
||||
if (swipeView.currentIndex < 0
|
||||
|| swipeView.itemAt(swipeView.currentIndex) === null
|
||||
|| swipeView.itemAt(swipeView.currentIndex).children[0] === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentGridItems = swipeView.itemAt(swipeView.currentIndex).children[0];
|
||||
|
||||
currentGridItems.currentIndex = (previousIndex > swipeView.currentIndex ? currentGridItems.count - 1 : 0);
|
||||
previousIndex = currentIndex;
|
||||
}
|
||||
|
||||
hoverEnabled: true
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
bottom: pageIndicator.top
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicator {
|
||||
id: pageIndicator
|
||||
currentIndex: swipeView.currentIndex
|
||||
|
||||
delegate: Item {
|
||||
width: 15
|
||||
height: 15
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
opacity: index === pageIndicator.currentIndex ? 0.95 : 0.45
|
||||
implicitWidth: index === pageIndicator.currentIndex ? 15 : 10
|
||||
implicitHeight: implicitWidth
|
||||
radius: width/2
|
||||
color: "white"
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interactive: false
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
count: swipeView.count
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
focus = true;
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
Keys.onRightPressed: {
|
||||
if (!currentGridItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
var index = currentGridItems.currentIndex;
|
||||
currentGridItems.moveCurrentIndexRight();
|
||||
if (index === currentGridItems.count - 1 && index === currentGridItems.currentIndex) {
|
||||
if (swipeView.currentIndex < swipeView.count - 1) {
|
||||
swipeView.incrementCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onRightPressed: flickable.moveCurrentIndexRight();
|
||||
Keys.onLeftPressed: flickable.moveCurrentIndexLeft();
|
||||
Keys.onDownPressed: flickable.moveCurrentIndexDown();
|
||||
Keys.onUpPressed: flickable.moveCurrentIndexUp();
|
||||
Keys.onLeftPressed: {
|
||||
if (!currentGridItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
var index = currentGridItems.currentIndex;
|
||||
currentGridItems.moveCurrentIndexLeft();
|
||||
if (index === 0 && index === currentGridItems.currentIndex) {
|
||||
if (swipeView.currentIndex > 0) {
|
||||
swipeView.decrementCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onDownPressed: currentGridItems.moveCurrentIndexDown();
|
||||
Keys.onUpPressed: currentGridItems.moveCurrentIndexUp();
|
||||
Keys.onReturnPressed: {
|
||||
if (flickable.currentItem) {
|
||||
flickable.currentItem.proxy.clicked();
|
||||
if (currentGridItems.currentItem) {
|
||||
currentGridItems.currentItem.proxy.clicked();
|
||||
if (tabletRoot) {
|
||||
tabletRoot.playButtonClickSound();
|
||||
}
|
||||
|
|
|
@ -48,11 +48,15 @@ Item {
|
|||
}
|
||||
|
||||
function pushSource(path) {
|
||||
d.push(Qt.resolvedUrl(path));
|
||||
d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
|
||||
d.push(Qt.resolvedUrl("../../" + path));
|
||||
if (d.currentItem.sendToScript !== undefined) {
|
||||
d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
|
||||
}
|
||||
d.currentItem.focus = true;
|
||||
d.currentItem.forceActiveFocus();
|
||||
breadcrumbText.text = d.currentItem.title;
|
||||
if (d.currentItem.title !== undefined) {
|
||||
breadcrumbText.text = d.currentItem.title;
|
||||
}
|
||||
if (typeof bgNavBar !== "undefined") {
|
||||
d.currentItem.y = bgNavBar.height;
|
||||
d.currentItem.height -= bgNavBar.height;
|
||||
|
|
BIN
interface/resources/styles/filter.png
Normal file
BIN
interface/resources/styles/filter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 244 B |
|
@ -67,8 +67,22 @@ QPushButton#revealLogButton {
|
|||
font-size: 11px;
|
||||
}
|
||||
|
||||
QPushButton#showAllButton {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background-color: #333333;
|
||||
color: #BBBBBB;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QCheckBox {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
text-align: center;
|
||||
color: #3d3d3d;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QCheckBox::indicator:unchecked {
|
||||
|
@ -77,4 +91,25 @@ QCheckBox::indicator:unchecked {
|
|||
|
||||
QCheckBox::indicator:checked {
|
||||
image: url(styles/checked.svg);
|
||||
}
|
||||
|
||||
QComboBox {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
text-align: center;
|
||||
background-color: #CCCCCC;
|
||||
color: #3d3d3d;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
QComboBox::drop-down {
|
||||
border-width: 0px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
|
||||
QComboBox::down-arrow {
|
||||
image: url(styles/filter.png);
|
||||
border-width: 0px;
|
||||
}
|
|
@ -154,7 +154,6 @@
|
|||
#include "scripting/Audio.h"
|
||||
#include "networking/CloseEventSender.h"
|
||||
#include "scripting/TestScriptingInterface.h"
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/AssetMappingsScriptingInterface.h"
|
||||
#include "scripting/ClipboardScriptingInterface.h"
|
||||
#include "scripting/DesktopScriptingInterface.h"
|
||||
|
@ -793,6 +792,7 @@ const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 100.0f;
|
|||
const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
|
||||
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
|
||||
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
|
||||
const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false;
|
||||
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
|
||||
const QString DEFAULT_CURSOR_NAME = "DEFAULT";
|
||||
|
||||
|
@ -812,6 +812,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT),
|
||||
_desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR),
|
||||
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
|
||||
_preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER),
|
||||
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
|
||||
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
|
||||
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),
|
||||
|
@ -1245,8 +1246,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
userActivityLogger.logAction("launch", properties);
|
||||
}
|
||||
|
||||
// Tell our entity edit sender about our known jurisdictions
|
||||
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
|
||||
_entityEditSender.setMyAvatar(myAvatar.get());
|
||||
|
||||
// The entity octree will have to know about MyAvatar for the parentJointName import
|
||||
|
@ -1499,7 +1498,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(audioIO.data(), &AudioClient::noiseGateClosed, audioScriptingInterface.data(), &AudioScriptingInterface::noiseGateClosed);
|
||||
connect(audioIO.data(), &AudioClient::inputReceived, audioScriptingInterface.data(), &AudioScriptingInterface::inputReceived);
|
||||
|
||||
|
||||
this->installEventFilter(this);
|
||||
|
||||
#ifdef HAVE_DDE
|
||||
|
@ -1523,8 +1521,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
applicationUpdater->checkForUpdate();
|
||||
}
|
||||
|
||||
// Now that menu is initialized we can sync myAvatar with it's state.
|
||||
myAvatar->updateMotionBehaviorFromMenu();
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ActionMotorControl, true);
|
||||
|
||||
// FIXME spacemouse code still needs cleanup
|
||||
#if 0
|
||||
|
@ -2386,9 +2383,10 @@ void Application::initializeUi() {
|
|||
|
||||
setupPreferences();
|
||||
|
||||
// For some reason there is already an "Application" object in the QML context,
|
||||
// though I can't find it. Hence, "ApplicationInterface"
|
||||
surfaceContext->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
surfaceContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
|
||||
|
||||
|
@ -2439,7 +2437,7 @@ void Application::initializeUi() {
|
|||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
|
@ -2648,6 +2646,10 @@ void Application::setHmdTabletBecomesToolbarSetting(bool value) {
|
|||
updateSystemTabletMode();
|
||||
}
|
||||
|
||||
void Application::setPreferStylusOverLaser(bool value) {
|
||||
_preferStylusOverLaserSetting.set(value);
|
||||
}
|
||||
|
||||
void Application::setPreferAvatarFingerOverStylus(bool value) {
|
||||
_preferAvatarFingerOverStylusSetting.set(value);
|
||||
}
|
||||
|
@ -4629,7 +4631,7 @@ void Application::reloadResourceCaches() {
|
|||
_lastQueriedTime = 0;
|
||||
_octreeQuery.incrementConnectionID();
|
||||
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||
|
||||
DependencyManager::get<AssetClient>()->clearCache();
|
||||
|
||||
|
@ -5122,7 +5124,7 @@ void Application::update(float deltaTime) {
|
|||
if (queryIsDue || viewIsDifferentEnough) {
|
||||
_lastQueriedTime = now;
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
|
||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||
}
|
||||
sendAvatarViewFrustum();
|
||||
_lastQueriedViewFrustum = _viewFrustum;
|
||||
|
@ -5343,15 +5345,12 @@ int Application::sendNackPackets() {
|
|||
return packetsSent;
|
||||
}
|
||||
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions) {
|
||||
void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
|
||||
|
||||
if (!_settingsLoaded) {
|
||||
return; // bail early if settings are not loaded
|
||||
}
|
||||
|
||||
//qCDebug(interfaceapp) << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
|
||||
bool wantExtraDebugging = getLogger()->extraDebugging();
|
||||
|
||||
ViewFrustum viewFrustum;
|
||||
copyViewFrustum(viewFrustum);
|
||||
_octreeQuery.setCameraPosition(viewFrustum.getPosition());
|
||||
|
@ -5366,147 +5365,22 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
||||
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
|
||||
|
||||
// Iterate all of the nodes, and get a count of how many octree servers we have...
|
||||
int totalServers = 0;
|
||||
int inViewServers = 0;
|
||||
int unknownJurisdictionServers = 0;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
totalServers++;
|
||||
auto node = nodeList->soloNodeOfType(serverType);
|
||||
if (node && node->getActiveSocket()) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(getMaxOctreePacketsPerSecond());
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownJurisdictionServers++;
|
||||
} else {
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
// encode the query data
|
||||
auto packetData = reinterpret_cast<unsigned char*>(queryPacket->getPayload());
|
||||
int packetSize = _octreeQuery.getBroadcastData(packetData);
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (rootCode) {
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x * TREE_SCALE,
|
||||
rootDetails.y * TREE_SCALE,
|
||||
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
|
||||
rootDetails.s * TREE_SCALE);
|
||||
if (viewFrustum.cubeIntersectsKeyhole(serverBounds)) {
|
||||
inViewServers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp, "Servers: total %d, in view %d, unknown jurisdiction %d",
|
||||
totalServers, inViewServers, unknownJurisdictionServers);
|
||||
// make sure we still have an active socket
|
||||
nodeList->sendUnreliablePacket(*queryPacket, *node);
|
||||
}
|
||||
|
||||
int perServerPPS = 0;
|
||||
const int SMALL_BUDGET = 10;
|
||||
int perUnknownServer = SMALL_BUDGET;
|
||||
int totalPPS = getMaxOctreePacketsPerSecond();
|
||||
|
||||
// determine PPS based on number of servers
|
||||
if (inViewServers >= 1) {
|
||||
// set our preferred PPS to be exactly evenly divided among all of the voxel servers... and allocate 1 PPS
|
||||
// for each unknown jurisdiction server
|
||||
perServerPPS = (totalPPS / inViewServers) - (unknownJurisdictionServers * perUnknownServer);
|
||||
} else {
|
||||
if (unknownJurisdictionServers > 0) {
|
||||
perUnknownServer = (totalPPS / unknownJurisdictionServers);
|
||||
}
|
||||
}
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
|
||||
}
|
||||
|
||||
auto queryPacket = NLPacket::create(packetType);
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
// only send to the NodeTypes that are serverType
|
||||
if (node->getActiveSocket() && node->getType() == serverType) {
|
||||
|
||||
// get the server bounds for this server
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
bool inView = false;
|
||||
bool unknownView = false;
|
||||
|
||||
// if we haven't heard from this voxel server, go ahead and send it a query, so we
|
||||
// can get the jurisdiction...
|
||||
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
|
||||
unknownView = true; // assume it's in view
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", assume it's visible.";
|
||||
}
|
||||
} else {
|
||||
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (rootCode) {
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x * TREE_SCALE,
|
||||
rootDetails.y * TREE_SCALE,
|
||||
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
|
||||
rootDetails.s * TREE_SCALE);
|
||||
|
||||
|
||||
inView = viewFrustum.cubeIntersectsKeyhole(serverBounds);
|
||||
} else if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
|
||||
}
|
||||
}
|
||||
|
||||
if (inView) {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
|
||||
} else if (unknownView) {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of "
|
||||
<< perUnknownServer << " to send us jurisdiction.";
|
||||
}
|
||||
|
||||
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
|
||||
// If there's only one server, then don't do this, and just let the normal voxel query pass through
|
||||
// as expected... this way, we will actually get a valid scene if there is one to be seen
|
||||
if (totalServers > 1) {
|
||||
_octreeQuery.setCameraPosition(glm::vec3(-0.1,-0.1,-0.1));
|
||||
const glm::quat OFF_IN_NEGATIVE_SPACE = glm::quat(-0.5, 0, -0.5, 1.0);
|
||||
_octreeQuery.setCameraOrientation(OFF_IN_NEGATIVE_SPACE);
|
||||
_octreeQuery.setCameraNearClip(0.1f);
|
||||
_octreeQuery.setCameraFarClip(0.1f);
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "Using 'minimal' camera position for node" << *node;
|
||||
}
|
||||
} else {
|
||||
if (wantExtraDebugging) {
|
||||
qCDebug(interfaceapp) << "Using regular camera position for node" << *node;
|
||||
}
|
||||
}
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(perUnknownServer);
|
||||
} else {
|
||||
_octreeQuery.setMaxQueryPacketsPerSecond(0);
|
||||
}
|
||||
|
||||
// encode the query data
|
||||
int packetSize = _octreeQuery.getBroadcastData(reinterpret_cast<unsigned char*>(queryPacket->getPayload()));
|
||||
queryPacket->setPayloadSize(packetSize);
|
||||
|
||||
// make sure we still have an active socket
|
||||
nodeList->sendUnreliablePacket(*queryPacket, *node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -5621,11 +5495,6 @@ void Application::clearDomainOctreeDetails() {
|
|||
|
||||
resetPhysicsReadyInformation();
|
||||
|
||||
// reset our node to stats and node to jurisdiction maps... since these must be changing...
|
||||
_entityServerJurisdictions.withWriteLock([&] {
|
||||
_entityServerJurisdictions.clear();
|
||||
});
|
||||
|
||||
_octreeServerSceneStats.withWriteLock([&] {
|
||||
_octreeServerSceneStats.clear();
|
||||
});
|
||||
|
@ -5827,8 +5696,6 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
|
|||
}
|
||||
|
||||
int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) {
|
||||
// But, also identify the sender, and keep track of the contained jurisdiction root for this server
|
||||
|
||||
// parse the incoming stats datas stick it in a temporary object for now, while we
|
||||
// determine which server it belongs to
|
||||
int statsMessageLength = 0;
|
||||
|
@ -5843,42 +5710,6 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
if (octreeStats.isFullScene()) {
|
||||
_fullSceneReceivedCounter++;
|
||||
}
|
||||
|
||||
// see if this is the first we've heard of this node...
|
||||
NodeToJurisdictionMap* jurisdiction = nullptr;
|
||||
QString serverType;
|
||||
if (sendingNode->getType() == NodeType::EntityServer) {
|
||||
jurisdiction = &_entityServerJurisdictions;
|
||||
serverType = "Entity";
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
jurisdiction->withReadLock([&] {
|
||||
if (jurisdiction->find(nodeUUID) != jurisdiction->end()) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(octreeStats.getJurisdictionRoot().get(), rootDetails);
|
||||
|
||||
qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",
|
||||
qPrintable(serverType),
|
||||
(double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
// store jurisdiction details for later use
|
||||
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it
|
||||
// but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the
|
||||
// details from the OctreeSceneStats to construct the JurisdictionMap
|
||||
JurisdictionMap jurisdictionMap;
|
||||
jurisdictionMap.copyContents(octreeStats.getJurisdictionRoot(), octreeStats.getJurisdictionEndNodes());
|
||||
jurisdiction->withWriteLock([&] {
|
||||
(*jurisdiction)[nodeUUID] = jurisdictionMap;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return statsMessageLength;
|
||||
|
@ -5899,7 +5730,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
return !entityServerNode || isPhysicsEnabled();
|
||||
});
|
||||
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// setup the packet sender of the script engine's scripting interfaces so
|
||||
// we can use the same ones from the application.
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
entityScriptingInterface->setPacketSender(&_entityEditSender);
|
||||
|
@ -5985,7 +5816,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
|
||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Account", GlobalServicesScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
|
@ -6391,7 +6222,7 @@ void Application::showAssetServerWidget(QString filePath) {
|
|||
if (!hmd->getShouldShowTablet() && !isHMDMode()) {
|
||||
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
|
||||
} else {
|
||||
static const QUrl url("../../hifi/dialogs/TabletAssetServer.qml");
|
||||
static const QUrl url("hifi/dialogs/TabletAssetServer.qml");
|
||||
tablet->pushOntoStack(url);
|
||||
}
|
||||
}
|
||||
|
@ -6964,7 +6795,7 @@ void Application::loadLODToolsDialog() {
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->lodTools();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletLODTools.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/TabletLODTools.qml");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6976,7 +6807,7 @@ void Application::loadEntityStatisticsDialog() {
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->octreeStatsDetails();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletEntityStatistics.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/TabletEntityStatistics.qml");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6987,7 +6818,7 @@ void Application::loadDomainConnectionDialog() {
|
|||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->showDomainConnectionDialog();
|
||||
} else {
|
||||
tablet->pushOntoStack("../../hifi/dialogs/TabletDCDialog.qml");
|
||||
tablet->pushOntoStack("hifi/dialogs/TabletDCDialog.qml");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -207,7 +207,11 @@ public:
|
|||
void setDesktopTabletBecomesToolbarSetting(bool value);
|
||||
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
|
||||
void setHmdTabletBecomesToolbarSetting(bool value);
|
||||
bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
|
||||
bool getPreferStylusOverLaser() { return _preferStylusOverLaserSetting.get(); }
|
||||
void setPreferStylusOverLaser(bool value);
|
||||
// FIXME: Remove setting completely or make available through JavaScript API?
|
||||
//bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
|
||||
bool getPreferAvatarFingerOverStylus() { return false; }
|
||||
void setPreferAvatarFingerOverStylus(bool value);
|
||||
|
||||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
|
@ -228,8 +232,6 @@ public:
|
|||
|
||||
FileLogger* getLogger() const { return _logger; }
|
||||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
float getRenderResolutionScale() const;
|
||||
|
||||
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
||||
|
@ -453,7 +455,7 @@ private:
|
|||
void updateThreads(float deltaTime);
|
||||
void updateDialogs(float deltaTime) const;
|
||||
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions);
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType);
|
||||
|
||||
int sendNackPackets();
|
||||
void sendAvatarViewFrustum();
|
||||
|
@ -556,6 +558,7 @@ private:
|
|||
Setting::Handle<float> _desktopTabletScale;
|
||||
Setting::Handle<bool> _desktopTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _preferStylusOverLaserSetting;
|
||||
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
|
||||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
Setting::Handle<QString> _preferredCursor;
|
||||
|
@ -574,7 +577,6 @@ private:
|
|||
StDev _idleLoopStdev;
|
||||
float _idleLoopMeasuredJitter;
|
||||
|
||||
NodeToJurisdictionMap _entityServerJurisdictions;
|
||||
NodeToOctreeSceneStats _octreeServerSceneStats;
|
||||
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
|
||||
QPointer<LogDialog> _logDialog;
|
||||
|
|
|
@ -166,7 +166,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog");
|
||||
});
|
||||
|
||||
// Avatar > Size
|
||||
|
@ -309,13 +309,13 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, nullptr, nullptr, QAction::PreferencesRole);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/GeneralPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");
|
||||
QString("hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog");
|
||||
});
|
||||
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Audio...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
static const QUrl widgetUrl("hifi/dialogs/Audio.qml");
|
||||
static const QUrl tabletUrl("../../hifi/audio/Audio.qml");
|
||||
static const QUrl tabletUrl("hifi/audio/Audio.qml");
|
||||
static const QString name("AudioDialog");
|
||||
qApp->showDialog(widgetUrl, tabletUrl, name);
|
||||
});
|
||||
|
@ -324,14 +324,14 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
QString("hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
});
|
||||
|
||||
// Settings > LOD...
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "LOD...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/LodPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
|
||||
QString("hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
|
||||
});
|
||||
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings...");
|
||||
|
@ -364,7 +364,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(developerMenu, "Graphics...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/GraphicsPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog");
|
||||
});
|
||||
|
||||
// Developer > UI >>>
|
||||
|
@ -589,8 +589,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderDetailedCollision, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawDetailedCollision(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl, 0, true,
|
||||
avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ScriptedMotorControl, 0, true,
|
||||
|
@ -615,7 +615,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(networkMenu, MenuOption::Networking);
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/NetworkingPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletNetworkingPreferences.qml"), "NetworkingPreferencesDialog");
|
||||
QString("hifi/tablet/TabletNetworkingPreferences.qml"), "NetworkingPreferencesDialog");
|
||||
});
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ClearDiskCache, 0,
|
||||
|
@ -676,7 +676,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(audioDebugMenu, "Buffers...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AudioBuffers.qml"),
|
||||
QString("../../hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog");
|
||||
QString("hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog");
|
||||
});
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
|
|
@ -424,6 +424,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
emit positionGoneTo();
|
||||
// Run safety tests as soon as we can after goToLocation, or clear if we're not colliding.
|
||||
_physicsSafetyPending = getCollisionsEnabled();
|
||||
_characterController.recomputeFlying(); // In case we've gone to into the sky.
|
||||
}
|
||||
if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) {
|
||||
// When needed and ready, arrange to check and fix.
|
||||
|
@ -2315,6 +2316,19 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
|||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
|
||||
// Most cases of going to a place or user go through this now. Some possible improvements to think about in the future:
|
||||
// - It would be nice if this used the same teleport steps and smoothing as in the teleport.js script, as long as it
|
||||
// still worked if the target is in the air.
|
||||
// - Sometimes (such as the response from /api/v1/users/:username/location), the location can be stale, but there is a
|
||||
// node_id supplied by which we could update the information after going to the stale location first and "looking around".
|
||||
// This could be passed through AddressManager::goToAddressFromObject => AddressManager::handleViewpoint => here.
|
||||
// The trick is that you have to yield enough time to resolve the node_id.
|
||||
// - Instead of always doing the same thing for shouldFaceLocation -- which places users uncomfortabley on top of each other --
|
||||
// it would be nice to see how many users are already "at" a person or place, and place ourself in semicircle or other shape
|
||||
// around the target. Avatars and entities (specified by the node_id) could define an adjustable "face me" method that would
|
||||
// compute the position (e.g., so that if I'm on stage, going to me would compute an available seat in the audience rather than
|
||||
// being in my face on-stage). Note that this could work for going to an entity as well as to a person.
|
||||
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", "
|
||||
<< newPosition.y << ", " << newPosition.z;
|
||||
|
||||
|
|
|
@ -218,14 +218,40 @@ Pointer::PickedObject LaserPointer::getHoveredObject(const PickResultPointer& pi
|
|||
return PickedObject(rayPickResult->objectID, rayPickResult->type);
|
||||
}
|
||||
|
||||
Pointer::Buttons LaserPointer::getPressedButtons() {
|
||||
Pointer::Buttons LaserPointer::getPressedButtons(const PickResultPointer& pickResult) {
|
||||
std::unordered_set<std::string> toReturn;
|
||||
for (const PointerTrigger& trigger : _triggers) {
|
||||
// TODO: right now, LaserPointers don't support axes, only on/off buttons
|
||||
if (trigger.getEndpoint()->peek() >= 1.0f) {
|
||||
toReturn.insert(trigger.getButton());
|
||||
auto rayPickResult = std::static_pointer_cast<const RayPickResult>(pickResult);
|
||||
|
||||
if (rayPickResult) {
|
||||
for (const PointerTrigger& trigger : _triggers) {
|
||||
std::string button = trigger.getButton();
|
||||
TriggerState& state = _states[button];
|
||||
// TODO: right now, LaserPointers don't support axes, only on/off buttons
|
||||
if (trigger.getEndpoint()->peek() >= 1.0f) {
|
||||
toReturn.insert(button);
|
||||
|
||||
if (_previousButtons.find(button) == _previousButtons.end()) {
|
||||
// start triggering for buttons that were just pressed
|
||||
state.triggeredObject = PickedObject(rayPickResult->objectID, rayPickResult->type);
|
||||
state.intersection = rayPickResult->intersection;
|
||||
state.triggerPos2D = findPos2D(state.triggeredObject, rayPickResult->intersection);
|
||||
state.triggerStartTime = usecTimestampNow();
|
||||
state.surfaceNormal = rayPickResult->surfaceNormal;
|
||||
state.deadspotExpired = false;
|
||||
state.wasTriggering = true;
|
||||
state.triggering = true;
|
||||
_latestState = state;
|
||||
}
|
||||
} else {
|
||||
// stop triggering for buttons that aren't pressed
|
||||
state.wasTriggering = state.triggering;
|
||||
state.triggering = false;
|
||||
_latestState = state;
|
||||
}
|
||||
}
|
||||
_previousButtons = toReturn;
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
@ -303,7 +329,7 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) {
|
|||
return RenderState(startID, pathID, endID);
|
||||
}
|
||||
|
||||
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover) const {
|
||||
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button, bool hover) {
|
||||
QUuid pickedID;
|
||||
glm::vec3 intersection, surfaceNormal, direction, origin;
|
||||
auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);
|
||||
|
@ -316,20 +342,48 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P
|
|||
pickedID = rayPickResult->objectID;
|
||||
}
|
||||
|
||||
glm::vec2 pos2D;
|
||||
if (pickedID != target.objectID) {
|
||||
if (target.type == ENTITY) {
|
||||
intersection = RayPick::intersectRayWithEntityXYPlane(target.objectID, origin, direction);
|
||||
} else if (target.type == OVERLAY) {
|
||||
intersection = RayPick::intersectRayWithOverlayXYPlane(target.objectID, origin, direction);
|
||||
}
|
||||
intersection = findIntersection(target, origin, direction);
|
||||
}
|
||||
if (target.type == ENTITY) {
|
||||
pos2D = RayPick::projectOntoEntityXYPlane(target.objectID, intersection);
|
||||
} else if (target.type == OVERLAY) {
|
||||
pos2D = RayPick::projectOntoOverlayXYPlane(target.objectID, intersection);
|
||||
} else if (target.type == HUD) {
|
||||
pos2D = DependencyManager::get<PickManager>()->calculatePos2DFromHUD(intersection);
|
||||
glm::vec2 pos2D = findPos2D(target, intersection);
|
||||
|
||||
// If we just started triggering and we haven't moved too much, don't update intersection and pos2D
|
||||
TriggerState& state = hover ? _latestState : _states[button];
|
||||
float sensorToWorldScale = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
|
||||
float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale;
|
||||
bool withinDeadspot = usecTimestampNow() - state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, state.triggerPos2D) < deadspotSquared;
|
||||
if ((state.triggering || state.wasTriggering) && !state.deadspotExpired && withinDeadspot) {
|
||||
pos2D = state.triggerPos2D;
|
||||
intersection = state.intersection;
|
||||
surfaceNormal = state.surfaceNormal;
|
||||
}
|
||||
if (!withinDeadspot) {
|
||||
state.deadspotExpired = true;
|
||||
}
|
||||
|
||||
return PointerEvent(pos2D, intersection, surfaceNormal, direction);
|
||||
}
|
||||
|
||||
glm::vec3 LaserPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) {
|
||||
switch (pickedObject.type) {
|
||||
case ENTITY:
|
||||
return RayPick::intersectRayWithEntityXYPlane(pickedObject.objectID, origin, direction);
|
||||
case OVERLAY:
|
||||
return RayPick::intersectRayWithOverlayXYPlane(pickedObject.objectID, origin, direction);
|
||||
default:
|
||||
return glm::vec3(NAN);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec2 LaserPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) {
|
||||
switch (pickedObject.type) {
|
||||
case ENTITY:
|
||||
return RayPick::projectOntoEntityXYPlane(pickedObject.objectID, origin);
|
||||
case OVERLAY:
|
||||
return RayPick::projectOntoOverlayXYPlane(pickedObject.objectID, origin);
|
||||
case HUD:
|
||||
return DependencyManager::get<PickManager>()->calculatePos2DFromHUD(origin);
|
||||
default:
|
||||
return glm::vec2(NAN);
|
||||
}
|
||||
}
|
|
@ -80,10 +80,10 @@ public:
|
|||
static RenderState buildRenderState(const QVariantMap& propMap);
|
||||
|
||||
protected:
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover = true) const override;
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override;
|
||||
|
||||
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||
Pointer::Buttons getPressedButtons() override;
|
||||
Pointer::Buttons getPressedButtons(const PickResultPointer& pickResult) override;
|
||||
|
||||
bool shouldHover(const PickResultPointer& pickResult) override { return _currentRenderState != ""; }
|
||||
bool shouldTrigger(const PickResultPointer& pickResult) override { return _currentRenderState != ""; }
|
||||
|
@ -105,6 +105,23 @@ private:
|
|||
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState);
|
||||
void disableRenderState(const RenderState& renderState);
|
||||
|
||||
struct TriggerState {
|
||||
PickedObject triggeredObject;
|
||||
glm::vec3 intersection { NAN };
|
||||
glm::vec3 surfaceNormal { NAN };
|
||||
glm::vec2 triggerPos2D { NAN };
|
||||
quint64 triggerStartTime { 0 };
|
||||
bool deadspotExpired { true };
|
||||
bool triggering { false };
|
||||
bool wasTriggering { false };
|
||||
};
|
||||
|
||||
Pointer::Buttons _previousButtons;
|
||||
std::unordered_map<std::string, TriggerState> _states;
|
||||
TriggerState _latestState;
|
||||
static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction);
|
||||
static glm::vec2 findPos2D(const PickedObject& pickedObject, const glm::vec3& origin);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LaserPointer_h
|
||||
|
|
|
@ -92,4 +92,4 @@ glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::
|
|||
glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) {
|
||||
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
|
||||
return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized);
|
||||
}
|
||||
}
|
|
@ -28,10 +28,6 @@ static const float TABLET_MAX_TOUCH_DISTANCE = 0.005f;
|
|||
static const float HOVER_HYSTERESIS = 0.01f;
|
||||
static const float TOUCH_HYSTERESIS = 0.001f;
|
||||
|
||||
static const float STYLUS_MOVE_DELAY = 0.33f * USECS_PER_SECOND;
|
||||
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f;
|
||||
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED = TOUCH_PRESS_TO_MOVE_DEADSPOT * TOUCH_PRESS_TO_MOVE_DEADSPOT;
|
||||
|
||||
StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled) :
|
||||
Pointer(DependencyManager::get<PickScriptingInterface>()->createStylusPick(props), enabled, hover),
|
||||
_stylusOverlay(stylusOverlay)
|
||||
|
@ -112,37 +108,37 @@ bool StylusPointer::shouldHover(const PickResultPointer& pickResult) {
|
|||
|
||||
bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) {
|
||||
auto stylusPickResult = std::static_pointer_cast<const StylusPickResult>(pickResult);
|
||||
bool wasTriggering = false;
|
||||
if (_renderState == EVENTS_ON && stylusPickResult) {
|
||||
auto sensorScaleFactor = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
|
||||
float distance = stylusPickResult->distance;
|
||||
|
||||
// If we're triggering on an object, recalculate the distance instead of using the pickResult
|
||||
glm::vec3 origin = vec3FromVariant(stylusPickResult->pickVariant["position"]);
|
||||
glm::vec3 direction = -_state.surfaceNormal;
|
||||
if (!_state.triggeredObject.objectID.isNull() && stylusPickResult->objectID != _state.triggeredObject.objectID) {
|
||||
glm::vec3 direction = _state.triggering ? -_state.surfaceNormal : -stylusPickResult->surfaceNormal;
|
||||
if ((_state.triggering || _state.wasTriggering) && stylusPickResult->objectID != _state.triggeredObject.objectID) {
|
||||
distance = glm::dot(findIntersection(_state.triggeredObject, origin, direction) - origin, direction);
|
||||
}
|
||||
|
||||
float hysteresis = _state.triggering ? TOUCH_HYSTERESIS * sensorScaleFactor : 0.0f;
|
||||
if (isWithinBounds(distance, TABLET_MIN_TOUCH_DISTANCE * sensorScaleFactor,
|
||||
TABLET_MAX_TOUCH_DISTANCE * sensorScaleFactor, hysteresis)) {
|
||||
if (_state.triggeredObject.objectID.isNull()) {
|
||||
TABLET_MAX_TOUCH_DISTANCE * sensorScaleFactor, hysteresis)) {
|
||||
_state.wasTriggering = _state.triggering;
|
||||
if (!_state.triggering) {
|
||||
_state.triggeredObject = PickedObject(stylusPickResult->objectID, stylusPickResult->type);
|
||||
_state.intersection = findIntersection(_state.triggeredObject, origin, direction);
|
||||
_state.intersection = stylusPickResult->intersection;
|
||||
_state.triggerPos2D = findPos2D(_state.triggeredObject, origin);
|
||||
_state.triggerStartTime = usecTimestampNow();
|
||||
_state.surfaceNormal = stylusPickResult->surfaceNormal;
|
||||
_state.deadspotExpired = false;
|
||||
_state.triggering = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
wasTriggering = _state.triggering;
|
||||
}
|
||||
|
||||
_state.triggeredObject = PickedObject();
|
||||
_state.intersection = glm::vec3(NAN);
|
||||
_state.triggerPos2D = glm::vec2(NAN);
|
||||
_state.triggerStartTime = 0;
|
||||
_state.surfaceNormal = glm::vec3(NAN);
|
||||
_state.wasTriggering = wasTriggering;
|
||||
_state.triggering = false;
|
||||
return false;
|
||||
}
|
||||
|
@ -155,13 +151,13 @@ Pointer::PickedObject StylusPointer::getHoveredObject(const PickResultPointer& p
|
|||
return PickedObject(stylusPickResult->objectID, stylusPickResult->type);
|
||||
}
|
||||
|
||||
Pointer::Buttons StylusPointer::getPressedButtons() {
|
||||
Pointer::Buttons StylusPointer::getPressedButtons(const PickResultPointer& pickResult) {
|
||||
// TODO: custom buttons for styluses
|
||||
Pointer::Buttons toReturn({ "Primary", "Focus" });
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
PointerEvent StylusPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover) const {
|
||||
PointerEvent StylusPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button, bool hover) {
|
||||
QUuid pickedID;
|
||||
glm::vec2 pos2D;
|
||||
glm::vec3 intersection, surfaceNormal, direction, origin;
|
||||
|
@ -177,18 +173,22 @@ PointerEvent StylusPointer::buildPointerEvent(const PickedObject& target, const
|
|||
}
|
||||
|
||||
// If we just started triggering and we haven't moved too much, don't update intersection and pos2D
|
||||
if (!_state.triggeredObject.objectID.isNull() && usecTimestampNow() - _state.triggerStartTime < STYLUS_MOVE_DELAY &&
|
||||
glm::distance2(pos2D, _state.triggerPos2D) < TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED) {
|
||||
float sensorToWorldScale = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
|
||||
float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale;
|
||||
bool withinDeadspot = usecTimestampNow() - _state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, _state.triggerPos2D) < deadspotSquared;
|
||||
if ((_state.triggering || _state.wasTriggering) && !_state.deadspotExpired && withinDeadspot) {
|
||||
pos2D = _state.triggerPos2D;
|
||||
intersection = _state.intersection;
|
||||
} else if (pickedID != target.objectID) {
|
||||
intersection = findIntersection(target, origin, direction);
|
||||
}
|
||||
if (!withinDeadspot) {
|
||||
_state.deadspotExpired = true;
|
||||
}
|
||||
|
||||
return PointerEvent(pos2D, intersection, surfaceNormal, direction);
|
||||
}
|
||||
|
||||
|
||||
bool StylusPointer::isWithinBounds(float distance, float min, float max, float hysteresis) {
|
||||
return (distance == glm::clamp(distance, min - hysteresis, max + hysteresis));
|
||||
}
|
||||
|
|
|
@ -37,11 +37,11 @@ public:
|
|||
|
||||
protected:
|
||||
PickedObject getHoveredObject(const PickResultPointer& pickResult) override;
|
||||
Buttons getPressedButtons() override;
|
||||
Buttons getPressedButtons(const PickResultPointer& pickResult) override;
|
||||
bool shouldHover(const PickResultPointer& pickResult) override;
|
||||
bool shouldTrigger(const PickResultPointer& pickResult) override;
|
||||
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover = true) const override;
|
||||
PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override;
|
||||
|
||||
private:
|
||||
void show(const StylusTip& tip);
|
||||
|
@ -53,7 +53,9 @@ private:
|
|||
glm::vec2 triggerPos2D { NAN };
|
||||
glm::vec3 surfaceNormal { NAN };
|
||||
quint64 triggerStartTime { 0 };
|
||||
bool deadspotExpired { true };
|
||||
bool triggering { false };
|
||||
bool wasTriggering { false };
|
||||
|
||||
bool hovering { false };
|
||||
};
|
||||
|
|
|
@ -12,47 +12,25 @@
|
|||
#include "AccountManager.h"
|
||||
|
||||
#include "AccountScriptingInterface.h"
|
||||
#include "GlobalServicesScriptingInterface.h"
|
||||
|
||||
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
|
||||
static AccountScriptingInterface sharedInstance;
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
QObject::connect(accountManager.data(), &AccountManager::profileChanged,
|
||||
&sharedInstance, &AccountScriptingInterface::usernameChanged);
|
||||
QObject::connect(accountManager.data(), &AccountManager::usernameChanged,
|
||||
&sharedInstance, &AccountScriptingInterface::onUsernameChanged);
|
||||
return &sharedInstance;
|
||||
}
|
||||
|
||||
bool AccountScriptingInterface::isLoggedIn() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
bool AccountScriptingInterface::checkAndSignalForAccessToken() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->checkAndSignalForAccessToken();
|
||||
return GlobalServicesScriptingInterface::getInstance()->isLoggedIn();
|
||||
}
|
||||
|
||||
void AccountScriptingInterface::logOut() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->logout();
|
||||
GlobalServicesScriptingInterface::getInstance()->logOut();
|
||||
}
|
||||
|
||||
AccountScriptingInterface::AccountScriptingInterface(QObject *parent): QObject(parent) {
|
||||
m_loggedIn = isLoggedIn();
|
||||
emit loggedInChanged(m_loggedIn);
|
||||
}
|
||||
|
||||
void AccountScriptingInterface::onUsernameChanged(QString username) {
|
||||
m_loggedIn = (username != QString());
|
||||
emit loggedInChanged(m_loggedIn);
|
||||
bool AccountScriptingInterface::loggedIn() const {
|
||||
return GlobalServicesScriptingInterface::getInstance()->loggedIn();
|
||||
}
|
||||
|
||||
QString AccountScriptingInterface::getUsername() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
if (accountManager->isLoggedIn()) {
|
||||
return accountManager->getAccountInfo().getUsername();
|
||||
} else {
|
||||
return "Unknown user";
|
||||
}
|
||||
return GlobalServicesScriptingInterface::getInstance()->getUsername();
|
||||
}
|
||||
|
|
|
@ -17,18 +17,12 @@
|
|||
class AccountScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
|
||||
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Account
|
||||
* @property username {String} username if user is logged in, otherwise it returns "Unknown user"
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL)
|
||||
QUrl getMetaverseServerURL() { return DependencyManager::get<AccountManager>()->getMetaverseServerURL(); }
|
||||
Q_PROPERTY(QString username READ getUsername)
|
||||
Q_PROPERTY(bool loggedIn READ loggedIn)
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -56,21 +50,11 @@ public slots:
|
|||
* @return {bool} true when user is logged into the High Fidelity metaverse.
|
||||
*/
|
||||
bool isLoggedIn();
|
||||
bool checkAndSignalForAccessToken();
|
||||
void logOut();
|
||||
|
||||
public:
|
||||
AccountScriptingInterface(QObject* parent = nullptr);
|
||||
bool loggedIn() const {
|
||||
return m_loggedIn;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onUsernameChanged(QString username);
|
||||
|
||||
private:
|
||||
bool m_loggedIn { false };
|
||||
|
||||
AccountScriptingInterface(QObject* parent = nullptr) {}
|
||||
bool loggedIn() const;
|
||||
};
|
||||
|
||||
#endif // hifi_AccountScriptingInterface_h
|
||||
|
|
|
@ -31,6 +31,8 @@ AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() {
|
|||
_proxyModel.setSourceModel(&_assetMappingModel);
|
||||
_proxyModel.setSortRole(Qt::DisplayRole);
|
||||
_proxyModel.setDynamicSortFilter(true);
|
||||
_proxyModel.setSortLocaleAware(true);
|
||||
_proxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
_proxyModel.sort(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::myUsernameChanged);
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged);
|
||||
connect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
|
||||
connect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected);
|
||||
|
||||
|
@ -31,11 +31,14 @@ GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() {
|
|||
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
this, &GlobalServicesScriptingInterface::discoverabilityModeChanged);
|
||||
|
||||
_loggedIn = isLoggedIn();
|
||||
emit loggedInChanged(_loggedIn);
|
||||
}
|
||||
|
||||
GlobalServicesScriptingInterface::~GlobalServicesScriptingInterface() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::myUsernameChanged);
|
||||
disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged);
|
||||
disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
|
||||
disconnect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected);
|
||||
}
|
||||
|
@ -45,8 +48,28 @@ GlobalServicesScriptingInterface* GlobalServicesScriptingInterface::getInstance(
|
|||
return &sharedInstance;
|
||||
}
|
||||
|
||||
const QString& GlobalServicesScriptingInterface::getUsername() const {
|
||||
return DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();
|
||||
const QString GlobalServicesScriptingInterface::getUsername() const {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
if (accountManager->isLoggedIn()) {
|
||||
return accountManager->getAccountInfo().getUsername();
|
||||
} else {
|
||||
return "Unknown user";
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalServicesScriptingInterface::isLoggedIn() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->isLoggedIn();
|
||||
}
|
||||
|
||||
bool GlobalServicesScriptingInterface::checkAndSignalForAccessToken() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->checkAndSignalForAccessToken();
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::logOut() {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
return accountManager->logout();
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::loggedOut() {
|
||||
|
@ -77,6 +100,12 @@ void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverabilit
|
|||
emit findableByChanged(DiscoverabilityManager::findableByString(discoverabilityMode));
|
||||
}
|
||||
|
||||
void GlobalServicesScriptingInterface::onUsernameChanged(const QString& username) {
|
||||
_loggedIn = (username != QString());
|
||||
emit myUsernameChanged(username);
|
||||
emit loggedInChanged(_loggedIn);
|
||||
}
|
||||
|
||||
DownloadInfoResult::DownloadInfoResult() :
|
||||
downloading(QList<float>()),
|
||||
pending(0.0f)
|
||||
|
|
|
@ -35,17 +35,25 @@ void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoR
|
|||
class GlobalServicesScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString username READ getUsername)
|
||||
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
|
||||
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
|
||||
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged)
|
||||
Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL)
|
||||
|
||||
public:
|
||||
static GlobalServicesScriptingInterface* getInstance();
|
||||
|
||||
const QString& getUsername() const;
|
||||
const QString getUsername() const;
|
||||
bool loggedIn() const { return _loggedIn; }
|
||||
QUrl getMetaverseServerURL() { return DependencyManager::get<AccountManager>()->getMetaverseServerURL(); }
|
||||
|
||||
public slots:
|
||||
DownloadInfoResult getDownloadInfo();
|
||||
void updateDownloadInfo();
|
||||
|
||||
bool isLoggedIn();
|
||||
bool checkAndSignalForAccessToken();
|
||||
void logOut();
|
||||
|
||||
private slots:
|
||||
void loggedOut();
|
||||
|
@ -55,18 +63,22 @@ private slots:
|
|||
void setFindableBy(const QString& discoverabilityMode);
|
||||
void discoverabilityModeChanged(Discoverability::Mode discoverabilityMode);
|
||||
|
||||
void onUsernameChanged(const QString& username);
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected(const QString& reason);
|
||||
void myUsernameChanged(const QString& username);
|
||||
void downloadInfoChanged(DownloadInfoResult info);
|
||||
void findableByChanged(const QString& discoverabilityMode);
|
||||
void loggedInChanged(bool loggedIn);
|
||||
|
||||
private:
|
||||
GlobalServicesScriptingInterface();
|
||||
~GlobalServicesScriptingInterface();
|
||||
|
||||
bool _downloading;
|
||||
bool _loggedIn{ false };
|
||||
};
|
||||
|
||||
#endif // hifi_GlobalServicesScriptingInterface_h
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
#include <QPlainTextEdit>
|
||||
#include <QTextCursor>
|
||||
#include <QPushButton>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#include <PathUtils.h>
|
||||
|
||||
const int TOP_BAR_HEIGHT = 46;
|
||||
const int TOP_BAR_HEIGHT = 124;
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int INITIAL_HEIGHT = 480;
|
||||
const int MINIMAL_WIDTH = 700;
|
||||
|
@ -34,21 +33,6 @@ const QColor HIGHLIGHT_COLOR = QColor("#3366CC");
|
|||
const QColor BOLD_COLOR = QColor("#445c8c");
|
||||
const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]";
|
||||
|
||||
class Highlighter : public QSyntaxHighlighter {
|
||||
public:
|
||||
Highlighter(QTextDocument* parent = nullptr);
|
||||
void setBold(int indexToBold);
|
||||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString& text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat boldFormat;
|
||||
QTextCharFormat keywordFormat;
|
||||
|
||||
};
|
||||
|
||||
BaseLogDialog::BaseLogDialog(QWidget* parent) : QDialog(parent, Qt::Window) {
|
||||
setWindowTitle("Base Log");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
@ -77,7 +61,7 @@ void BaseLogDialog::initControls() {
|
|||
_searchButton->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_BUTTON_WIDTH, ELEMENT_HEIGHT);
|
||||
_leftPad += SEARCH_BUTTON_WIDTH;
|
||||
_searchButton->show();
|
||||
connect(_searchButton, SIGNAL(clicked()), SLOT(handleSearchButton()));
|
||||
connect(_searchButton, &QPushButton::clicked, this, &BaseLogDialog::handleSearchButton);
|
||||
|
||||
_searchTextBox = new QLineEdit(this);
|
||||
// disable blue outline in Mac
|
||||
|
@ -85,8 +69,8 @@ void BaseLogDialog::initControls() {
|
|||
_searchTextBox->setGeometry(_leftPad, ELEMENT_MARGIN, SEARCH_TEXT_WIDTH, ELEMENT_HEIGHT);
|
||||
_leftPad += SEARCH_TEXT_WIDTH + BUTTON_MARGIN;
|
||||
_searchTextBox->show();
|
||||
connect(_searchTextBox, SIGNAL(textChanged(QString)), SLOT(handleSearchTextChanged(QString)));
|
||||
connect(_searchTextBox, SIGNAL(returnPressed()), SLOT(toggleSearchNext()));
|
||||
connect(_searchTextBox, &QLineEdit::textChanged, this, &BaseLogDialog::handleSearchTextChanged);
|
||||
connect(_searchTextBox, &QLineEdit::returnPressed, this, &BaseLogDialog::toggleSearchNext);
|
||||
|
||||
_searchPrevButton = new QPushButton(this);
|
||||
_searchPrevButton->setObjectName("searchPrevButton");
|
||||
|
@ -94,7 +78,7 @@ void BaseLogDialog::initControls() {
|
|||
_searchPrevButton->setText("Prev");
|
||||
_leftPad += SEARCH_TOGGLE_BUTTON_WIDTH + BUTTON_MARGIN;
|
||||
_searchPrevButton->show();
|
||||
connect(_searchPrevButton, SIGNAL(clicked()), SLOT(toggleSearchPrev()));
|
||||
connect(_searchPrevButton, &QPushButton::clicked, this, &BaseLogDialog::toggleSearchPrev);
|
||||
|
||||
_searchNextButton = new QPushButton(this);
|
||||
_searchNextButton->setObjectName("searchNextButton");
|
||||
|
@ -102,16 +86,16 @@ void BaseLogDialog::initControls() {
|
|||
_searchNextButton->setText("Next");
|
||||
_leftPad += SEARCH_TOGGLE_BUTTON_WIDTH + CHECKBOX_MARGIN;
|
||||
_searchNextButton->show();
|
||||
connect(_searchNextButton, SIGNAL(clicked()), SLOT(toggleSearchNext()));
|
||||
connect(_searchNextButton, &QPushButton::clicked, this, &BaseLogDialog::toggleSearchNext);
|
||||
|
||||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
_highlighter = new Highlighter(_logTextBox->document());
|
||||
connect(_logTextBox, SIGNAL(selectionChanged()), SLOT(updateSelection()));
|
||||
connect(_logTextBox, &QPlainTextEdit::selectionChanged, this, &BaseLogDialog::updateSelection);
|
||||
}
|
||||
|
||||
void BaseLogDialog::showEvent(QShowEvent* event) {
|
||||
void BaseLogDialog::showEvent(QShowEvent* event) {
|
||||
showLogData();
|
||||
}
|
||||
|
||||
|
@ -132,15 +116,12 @@ void BaseLogDialog::handleSearchButton() {
|
|||
}
|
||||
|
||||
void BaseLogDialog::handleSearchTextChanged(QString searchText) {
|
||||
if (searchText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTextCursor cursor = _logTextBox->textCursor();
|
||||
|
||||
if (cursor.hasSelection()) {
|
||||
QString selectedTerm = cursor.selectedText();
|
||||
if (selectedTerm == searchText) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,6 +139,10 @@ void BaseLogDialog::handleSearchTextChanged(QString searchText) {
|
|||
_highlighter->rehighlight();
|
||||
}
|
||||
|
||||
void BaseLogDialog::clearSearch() {
|
||||
_searchTextBox->setText("");
|
||||
}
|
||||
|
||||
void BaseLogDialog::toggleSearchPrev() {
|
||||
QTextCursor searchCursor = _logTextBox->textCursor();
|
||||
if (searchCursor.hasSelection()) {
|
||||
|
|
|
@ -13,17 +13,33 @@
|
|||
#define hifi_BaseLogDialog_h
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
const int ELEMENT_MARGIN = 7;
|
||||
const int ELEMENT_HEIGHT = 32;
|
||||
const int CHECKBOX_MARGIN = 12;
|
||||
const int CHECKBOX_WIDTH = 140;
|
||||
const int CHECKBOX_WIDTH = 110;
|
||||
const int COMBOBOX_WIDTH = 160;
|
||||
const int BUTTON_MARGIN = 8;
|
||||
|
||||
class QPushButton;
|
||||
class QLineEdit;
|
||||
class QPlainTextEdit;
|
||||
class Highlighter;
|
||||
|
||||
class Highlighter : public QSyntaxHighlighter {
|
||||
public:
|
||||
Highlighter(QTextDocument* parent = nullptr);
|
||||
void setBold(int indexToBold);
|
||||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString& text) override;
|
||||
|
||||
private:
|
||||
QTextCharFormat boldFormat;
|
||||
QTextCharFormat keywordFormat;
|
||||
|
||||
};
|
||||
|
||||
class BaseLogDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
@ -33,7 +49,7 @@ public:
|
|||
~BaseLogDialog();
|
||||
|
||||
public slots:
|
||||
void appendLogLine(QString logLine);
|
||||
virtual void appendLogLine(QString logLine);
|
||||
|
||||
private slots:
|
||||
void updateSelection();
|
||||
|
@ -43,21 +59,22 @@ private slots:
|
|||
void toggleSearchNext();
|
||||
|
||||
protected:
|
||||
int _leftPad { 0 };
|
||||
int _leftPad{ 0 };
|
||||
QString _searchTerm;
|
||||
QPlainTextEdit* _logTextBox{ nullptr };
|
||||
Highlighter* _highlighter{ nullptr };
|
||||
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
virtual QString getCurrentLog() = 0;
|
||||
void clearSearch();
|
||||
|
||||
private:
|
||||
QPushButton* _searchButton { nullptr };
|
||||
QLineEdit* _searchTextBox { nullptr };
|
||||
QPlainTextEdit* _logTextBox { nullptr };
|
||||
QPushButton* _searchPrevButton { nullptr };
|
||||
QPushButton* _searchNextButton { nullptr };
|
||||
QString _searchTerm;
|
||||
Highlighter* _highlighter { nullptr };
|
||||
|
||||
QPushButton* _searchButton{ nullptr };
|
||||
QLineEdit* _searchTextBox{ nullptr };
|
||||
QPushButton* _searchPrevButton{ nullptr };
|
||||
QPushButton* _searchNextButton{ nullptr };
|
||||
|
||||
void initControls();
|
||||
void showLogData();
|
||||
};
|
||||
|
|
|
@ -13,48 +13,287 @@
|
|||
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
|
||||
const int REVEAL_BUTTON_WIDTH = 122;
|
||||
const int CLEAR_FILTER_BUTTON_WIDTH = 80;
|
||||
const int MARGIN_LEFT = 25;
|
||||
const int DEBUG_CHECKBOX_WIDTH = 70;
|
||||
const int INFO_CHECKBOX_WIDTH = 65;
|
||||
const int CRITICAL_CHECKBOX_WIDTH = 85;
|
||||
const int WARNING_CHECKBOX_WIDTH = 80;
|
||||
const int SUPPRESS_CHECKBOX_WIDTH = 87;
|
||||
const int FATAL_CHECKBOX_WIDTH = 70;
|
||||
const int UNKNOWN_CHECKBOX_WIDTH = 80;
|
||||
const int SECOND_ROW = ELEMENT_MARGIN + ELEMENT_MARGIN + ELEMENT_HEIGHT;
|
||||
const int THIRD_ROW = SECOND_ROW + ELEMENT_MARGIN + ELEMENT_HEIGHT;
|
||||
const QString DEBUG_TEXT = "[DEBUG]";
|
||||
const QString INFO_TEXT = "[INFO]";
|
||||
const QString CRITICAL_TEXT = "[CRITICAL]";
|
||||
const QString WARNING_TEXT = "[WARNING]";
|
||||
const QString FATAL_TEXT = "[FATAL]";
|
||||
const QString SUPPRESS_TEXT = "[SUPPRESS]";
|
||||
const QString UNKNOWN_TEXT = "[UNKNOWN]";
|
||||
|
||||
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLogDialog(parent) {
|
||||
_logger = logger;
|
||||
setWindowTitle("Log");
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
_extraDebuggingBox->setGeometry(_leftPad, ELEMENT_MARGIN, CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_extraDebuggingBox->show();
|
||||
connect(_extraDebuggingBox, SIGNAL(stateChanged(int)), SLOT(handleExtraDebuggingCheckbox(int)));
|
||||
|
||||
_revealLogButton = new QPushButton("Reveal log file", this);
|
||||
// set object name for css styling
|
||||
_revealLogButton->setObjectName("revealLogButton");
|
||||
_revealLogButton->show();
|
||||
connect(_revealLogButton, SIGNAL(clicked()), SLOT(handleRevealButton()));
|
||||
connect(_revealLogButton, &QPushButton::clicked, this, &LogDialog::handleRevealButton);
|
||||
|
||||
connect(_logger, SIGNAL(logReceived(QString)), this, SLOT(appendLogLine(QString)), Qt::QueuedConnection);
|
||||
connect(_logger, &AbstractLoggerInterface::logReceived, this, &LogDialog::appendLogLine);
|
||||
|
||||
_leftPad = MARGIN_LEFT;
|
||||
_debugPrintBox = new QCheckBox("DEBUG", this);
|
||||
_debugPrintBox->setGeometry(_leftPad, SECOND_ROW, DEBUG_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->debugPrint()) {
|
||||
_debugPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_debugPrintBox->show();
|
||||
connect(_debugPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleDebugPrintBox);
|
||||
|
||||
_leftPad += DEBUG_CHECKBOX_WIDTH + BUTTON_MARGIN;
|
||||
_infoPrintBox = new QCheckBox("INFO", this);
|
||||
_infoPrintBox->setGeometry(_leftPad, SECOND_ROW, INFO_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->infoPrint()) {
|
||||
_infoPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_infoPrintBox->show();
|
||||
connect(_infoPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleInfoPrintBox);
|
||||
|
||||
_leftPad += INFO_CHECKBOX_WIDTH + BUTTON_MARGIN;
|
||||
_criticalPrintBox = new QCheckBox("CRITICAL", this);
|
||||
_criticalPrintBox->setGeometry(_leftPad, SECOND_ROW, CRITICAL_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->criticalPrint()) {
|
||||
_criticalPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_criticalPrintBox->show();
|
||||
connect(_criticalPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleCriticalPrintBox);
|
||||
|
||||
_leftPad += CRITICAL_CHECKBOX_WIDTH + BUTTON_MARGIN;
|
||||
_warningPrintBox = new QCheckBox("WARNING", this);
|
||||
_warningPrintBox->setGeometry(_leftPad, SECOND_ROW, WARNING_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->warningPrint()) {
|
||||
_warningPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_warningPrintBox->show();
|
||||
connect(_warningPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleWarningPrintBox);
|
||||
|
||||
_leftPad += WARNING_CHECKBOX_WIDTH + BUTTON_MARGIN;
|
||||
_suppressPrintBox = new QCheckBox("SUPPRESS", this);
|
||||
_suppressPrintBox->setGeometry(_leftPad, SECOND_ROW, SUPPRESS_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->suppressPrint()) {
|
||||
_suppressPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_suppressPrintBox->show();
|
||||
connect(_suppressPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleSuppressPrintBox);
|
||||
|
||||
_leftPad += SUPPRESS_CHECKBOX_WIDTH + BUTTON_MARGIN;
|
||||
_fatalPrintBox = new QCheckBox("FATAL", this);
|
||||
_fatalPrintBox->setGeometry(_leftPad, SECOND_ROW, FATAL_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->fatalPrint()) {
|
||||
_fatalPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_fatalPrintBox->show();
|
||||
connect(_fatalPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleFatalPrintBox);
|
||||
|
||||
_leftPad += FATAL_CHECKBOX_WIDTH + BUTTON_MARGIN;
|
||||
_unknownPrintBox = new QCheckBox("UNKNOWN", this);
|
||||
_unknownPrintBox->setGeometry(_leftPad, SECOND_ROW, UNKNOWN_CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->unknownPrint()) {
|
||||
_unknownPrintBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_unknownPrintBox->show();
|
||||
connect(_unknownPrintBox, &QCheckBox::stateChanged, this, &LogDialog::handleUnknownPrintBox);
|
||||
|
||||
_leftPad = MARGIN_LEFT;
|
||||
_filterDropdown = new QComboBox(this);
|
||||
_filterDropdown->setGeometry(_leftPad, THIRD_ROW, COMBOBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
_filterDropdown->addItem("No secondary filter...");
|
||||
_filterDropdown->addItem("default");
|
||||
_filterDropdown->addItem("hifi.audio");
|
||||
_filterDropdown->addItem("hifi.audioclient");
|
||||
_filterDropdown->addItem("hifi.animation");
|
||||
_filterDropdown->addItem("hifi.avatars");
|
||||
_filterDropdown->addItem("hifi.commerce");
|
||||
_filterDropdown->addItem("hifi.controllers");
|
||||
_filterDropdown->addItem("hifi.entities");
|
||||
_filterDropdown->addItem("hifi.gl");
|
||||
_filterDropdown->addItem("hifi.gpu.gl");
|
||||
_filterDropdown->addItem("hifi.interface");
|
||||
_filterDropdown->addItem("hifi.interface.deadlock");
|
||||
_filterDropdown->addItem("hifi.modelformat");
|
||||
_filterDropdown->addItem("hifi.networking");
|
||||
_filterDropdown->addItem("hifi.networking.resource");
|
||||
_filterDropdown->addItem("hifi.plugins");
|
||||
_filterDropdown->addItem("hifi.render");
|
||||
_filterDropdown->addItem("hifi.scriptengine");
|
||||
_filterDropdown->addItem("hifi.scriptengine.module");
|
||||
_filterDropdown->addItem("hifi.scriptengine.script");
|
||||
_filterDropdown->addItem("hifi.shared");
|
||||
_filterDropdown->addItem("hifi.ui");
|
||||
_filterDropdown->addItem("qml");
|
||||
connect(_filterDropdown, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &LogDialog::handleFilterDropdownChanged);
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_extraDebuggingBox->show();
|
||||
connect(_extraDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleExtraDebuggingCheckbox);
|
||||
|
||||
_clearFilterButton = new QPushButton("Clear Filters", this);
|
||||
// set object name for css styling
|
||||
_clearFilterButton->setObjectName("showAllButton");
|
||||
_clearFilterButton->show();
|
||||
connect(_clearFilterButton, &QPushButton::clicked, this, &LogDialog::handleClearFilterButton);
|
||||
handleClearFilterButton();
|
||||
}
|
||||
|
||||
void LogDialog::resizeEvent(QResizeEvent* event) {
|
||||
BaseLogDialog::resizeEvent(event);
|
||||
_revealLogButton->setGeometry(width() - ELEMENT_MARGIN - REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_MARGIN,
|
||||
REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
ELEMENT_MARGIN,
|
||||
REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_clearFilterButton->setGeometry(width() - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH,
|
||||
THIRD_ROW,
|
||||
CLEAR_FILTER_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
}
|
||||
|
||||
void LogDialog::handleRevealButton() {
|
||||
_logger->locateLog();
|
||||
}
|
||||
|
||||
void LogDialog::handleClearFilterButton() {
|
||||
_logger->setExtraDebugging(false);
|
||||
_extraDebuggingBox->setCheckState(Qt::Unchecked);
|
||||
_logger->setDebugPrint(false);
|
||||
_debugPrintBox->setCheckState(Qt::Unchecked);
|
||||
_logger->setInfoPrint(false);
|
||||
_infoPrintBox->setCheckState(Qt::Unchecked);
|
||||
_logger->setCriticalPrint(true);
|
||||
_criticalPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setWarningPrint(true);
|
||||
_warningPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setSuppressPrint(true);
|
||||
_suppressPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setFatalPrint(true);
|
||||
_fatalPrintBox->setCheckState(Qt::Checked);
|
||||
_logger->setUnknownPrint(true);
|
||||
_unknownPrintBox->setCheckState(Qt::Checked);
|
||||
clearSearch();
|
||||
_filterDropdown->setCurrentIndex(0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleExtraDebuggingCheckbox(int state) {
|
||||
_logger->setExtraDebugging(state != 0);
|
||||
}
|
||||
|
||||
void LogDialog::handleDebugPrintBox(int state) {
|
||||
_logger->setDebugPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleInfoPrintBox(int state) {
|
||||
_logger->setInfoPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleCriticalPrintBox(int state) {
|
||||
_logger->setCriticalPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleWarningPrintBox(int state) {
|
||||
_logger->setWarningPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleSuppressPrintBox(int state) {
|
||||
_logger->setSuppressPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleFatalPrintBox(int state) {
|
||||
_logger->setFatalPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleUnknownPrintBox(int state) {
|
||||
_logger->setUnknownPrint(state != 0);
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleFilterDropdownChanged(int selection) {
|
||||
if (selection != 0) {
|
||||
_filterSelection = "[" + _filterDropdown->currentText() + "]";
|
||||
} else {
|
||||
_filterSelection = "";
|
||||
}
|
||||
printLogFile();
|
||||
}
|
||||
|
||||
QString LogDialog::getCurrentLog() {
|
||||
return _logger->getLogData();
|
||||
}
|
||||
|
||||
void LogDialog::appendLogLine(QString logLine) {
|
||||
if (logLine.contains(_searchTerm, Qt::CaseInsensitive) &&
|
||||
logLine.contains(_filterSelection, Qt::CaseSensitive)) {
|
||||
int indexToBold = _logTextBox->document()->characterCount();
|
||||
_highlighter->setBold(indexToBold);
|
||||
|
||||
if (logLine.contains(DEBUG_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->debugPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
} else if (logLine.contains(INFO_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->infoPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
} else if (logLine.contains(CRITICAL_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->criticalPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
} else if (logLine.contains(WARNING_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->warningPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
} else if (logLine.contains(SUPPRESS_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->suppressPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
} else if (logLine.contains(FATAL_TEXT, Qt::CaseSensitive)) {
|
||||
if (_logger->fatalPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
} else {
|
||||
if (_logger->unknownPrint()) {
|
||||
_logTextBox->appendPlainText(logLine.trimmed());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LogDialog::printLogFile() {
|
||||
_logTextBox->clear();
|
||||
QString log = getCurrentLog();
|
||||
QStringList logList = log.split('\n');
|
||||
for (const auto& message : logList) {
|
||||
appendLogLine(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QComboBox;
|
||||
class QResizeEvent;
|
||||
class AbstractLoggerInterface;
|
||||
|
||||
|
@ -25,17 +26,40 @@ class LogDialog : public BaseLogDialog {
|
|||
public:
|
||||
LogDialog(QWidget* parent, AbstractLoggerInterface* logger);
|
||||
|
||||
public slots:
|
||||
void appendLogLine(QString logLine) override;
|
||||
|
||||
private slots:
|
||||
void handleRevealButton();
|
||||
void handleExtraDebuggingCheckbox(int);
|
||||
void handleDebugPrintBox(int);
|
||||
void handleInfoPrintBox(int);
|
||||
void handleCriticalPrintBox(int);
|
||||
void handleWarningPrintBox(int);
|
||||
void handleSuppressPrintBox(int);
|
||||
void handleFatalPrintBox(int);
|
||||
void handleUnknownPrintBox(int);
|
||||
void handleFilterDropdownChanged(int);
|
||||
void handleClearFilterButton();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
QString getCurrentLog() override;
|
||||
|
||||
void printLogFile();
|
||||
|
||||
private:
|
||||
QCheckBox* _extraDebuggingBox;
|
||||
QPushButton* _revealLogButton;
|
||||
QPushButton* _clearFilterButton;
|
||||
QCheckBox* _debugPrintBox;
|
||||
QCheckBox* _infoPrintBox;
|
||||
QCheckBox* _criticalPrintBox;
|
||||
QCheckBox* _warningPrintBox;
|
||||
QCheckBox* _suppressPrintBox;
|
||||
QCheckBox* _fatalPrintBox;
|
||||
QCheckBox* _unknownPrintBox;
|
||||
QComboBox* _filterDropdown;
|
||||
QString _filterSelection;
|
||||
|
||||
AbstractLoggerInterface* _logger;
|
||||
};
|
||||
|
|
|
@ -26,27 +26,17 @@
|
|||
|
||||
OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* model) :
|
||||
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
|
||||
_model(model),
|
||||
_averageUpdatesPerSecond(SAMPLES_PER_SECOND)
|
||||
_model(model)
|
||||
{
|
||||
|
||||
_statCount = 0;
|
||||
_octreeServerLabelsCount = 0;
|
||||
|
||||
for (int i = 0; i < MAX_VOXEL_SERVERS; i++) {
|
||||
_octreeServerLables[i] = 0;
|
||||
_extraServerDetails[i] = LESS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_STATS; i++) {
|
||||
_labels[i] = NULL;
|
||||
_labels[i] = nullptr;
|
||||
}
|
||||
|
||||
this->setWindowTitle("Octree Server Statistics");
|
||||
setWindowTitle("Octree Server Statistics");
|
||||
|
||||
// Create layouter
|
||||
_form = new QFormLayout();
|
||||
this->QDialog::setLayout(_form);
|
||||
setLayout(_form);
|
||||
|
||||
// Setup stat items
|
||||
_serverElements = AddStatItem("Elements on Servers");
|
||||
|
@ -63,7 +53,14 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo
|
|||
|
||||
_entityUpdateTime = AddStatItem("Entity Update Time");
|
||||
_entityUpdates = AddStatItem("Entity Updates");
|
||||
|
||||
|
||||
|
||||
_octreeServerLabel = AddStatItem("Entity Server");
|
||||
_labels[_octreeServerLabel]->setTextFormat(Qt::RichText);
|
||||
_labels[_octreeServerLabel]->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
connect(_labels[_octreeServerLabel], SIGNAL(linkActivated(const QString&)),
|
||||
this, SLOT(moreless(const QString&)));
|
||||
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
|
||||
|
@ -74,23 +71,16 @@ void OctreeStatsDialog::RemoveStatItem(int item) {
|
|||
_form->removeWidget(automaticLabel);
|
||||
automaticLabel->deleteLater();
|
||||
myLabel->deleteLater();
|
||||
_labels[item] = NULL;
|
||||
_labels[item] = nullptr;
|
||||
}
|
||||
|
||||
void OctreeStatsDialog::moreless(const QString& link) {
|
||||
QStringList linkDetails = link.split("-");
|
||||
const int COMMAND_ITEM = 0;
|
||||
const int SERVER_NUMBER_ITEM = 1;
|
||||
QString serverNumberString = linkDetails[SERVER_NUMBER_ITEM];
|
||||
QString command = linkDetails[COMMAND_ITEM];
|
||||
int serverNumber = serverNumberString.toInt();
|
||||
|
||||
if (command == "more") {
|
||||
_extraServerDetails[serverNumber-1] = MORE;
|
||||
} else if (command == "most") {
|
||||
_extraServerDetails[serverNumber-1] = MOST;
|
||||
if (link == "more") {
|
||||
_extraServerDetails = MORE;
|
||||
} else if (link == "most") {
|
||||
_extraServerDetails = MOST;
|
||||
} else {
|
||||
_extraServerDetails[serverNumber-1] = LESS;
|
||||
_extraServerDetails = LESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,91 +366,34 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) {
|
|||
QDialog::paintEvent(event);
|
||||
}
|
||||
void OctreeStatsDialog::showAllOctreeServers() {
|
||||
int serverCount = 0;
|
||||
|
||||
showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity",
|
||||
qApp->getEntityServerJurisdictions());
|
||||
|
||||
if (_octreeServerLabelsCount > serverCount) {
|
||||
for (int i = serverCount; i < _octreeServerLabelsCount; i++) {
|
||||
int serverLabel = _octreeServerLables[i];
|
||||
RemoveStatItem(serverLabel);
|
||||
_octreeServerLables[i] = 0;
|
||||
}
|
||||
_octreeServerLabelsCount = serverCount;
|
||||
}
|
||||
showOctreeServersOfType(NodeType::EntityServer);
|
||||
}
|
||||
|
||||
void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName,
|
||||
NodeToJurisdictionMap& serverJurisdictions) {
|
||||
|
||||
void OctreeStatsDialog::showOctreeServersOfType(NodeType_t serverType) {
|
||||
QLocale locale(QLocale::English);
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||
|
||||
// only send to the NodeTypes that are NodeType_t_VOXEL_SERVER
|
||||
if (node->getType() == serverType) {
|
||||
serverCount++;
|
||||
|
||||
if (serverCount > _octreeServerLabelsCount) {
|
||||
QString label = QString("%1 Server %2").arg(serverTypeName).arg(serverCount);
|
||||
int thisServerRow = _octreeServerLables[serverCount-1] = AddStatItem(label.toUtf8().constData());
|
||||
_labels[thisServerRow]->setTextFormat(Qt::RichText);
|
||||
_labels[thisServerRow]->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
connect(_labels[thisServerRow], SIGNAL(linkActivated(const QString&)), this, SLOT(moreless(const QString&)));
|
||||
_octreeServerLabelsCount++;
|
||||
}
|
||||
|
||||
std::stringstream serverDetails("");
|
||||
std::stringstream extraDetails("");
|
||||
std::stringstream linkDetails("");
|
||||
|
||||
if (node->getActiveSocket()) {
|
||||
serverDetails << "active ";
|
||||
} else {
|
||||
serverDetails << "inactive ";
|
||||
}
|
||||
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
// lookup our nodeUUID in the jurisdiction map, if it's missing then we're
|
||||
// missing at least one jurisdiction
|
||||
serverJurisdictions.withReadLock([&] {
|
||||
if (serverJurisdictions.find(nodeUUID) == serverJurisdictions.end()) {
|
||||
serverDetails << " unknown jurisdiction ";
|
||||
return;
|
||||
}
|
||||
const JurisdictionMap& map = serverJurisdictions[nodeUUID];
|
||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(serverType);
|
||||
if (node) {
|
||||
std::stringstream serverDetails("");
|
||||
std::stringstream extraDetails("");
|
||||
std::stringstream linkDetails("");
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
if (node->getActiveSocket()) {
|
||||
serverDetails << "active ";
|
||||
} else {
|
||||
serverDetails << "inactive ";
|
||||
}
|
||||
|
||||
if (rootCode) {
|
||||
QString rootCodeHex = octalCodeToHexString(rootCode.get());
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
serverDetails << " jurisdiction: "
|
||||
<< qPrintable(rootCodeHex)
|
||||
<< " ["
|
||||
<< rootDetails.x << ", "
|
||||
<< rootDetails.y << ", "
|
||||
<< rootDetails.z << ": "
|
||||
<< rootDetails.s << "] ";
|
||||
} else {
|
||||
serverDetails << " jurisdiction has no rootCode";
|
||||
} // root code
|
||||
});
|
||||
|
||||
// now lookup stats details for this server...
|
||||
if (_extraServerDetails[serverCount-1] != LESS) {
|
||||
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
|
||||
sceneStats->withReadLock([&] {
|
||||
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
|
||||
OctreeSceneStats& stats = sceneStats->at(nodeUUID);
|
||||
// now lookup stats details for this server...
|
||||
if (_extraServerDetails != LESS) {
|
||||
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
|
||||
sceneStats->withReadLock([&] {
|
||||
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
|
||||
OctreeSceneStats& stats = sceneStats->at(nodeUUID);
|
||||
|
||||
switch (_extraServerDetails[serverCount - 1]) {
|
||||
switch (_extraServerDetails) {
|
||||
case MOST: {
|
||||
extraDetails << "<br/>";
|
||||
|
||||
|
@ -538,7 +471,7 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser
|
|||
" Average Ping Time: " << qPrintable(incomingPingTimeString) << " msecs";
|
||||
|
||||
serverDetails << "<br/>" <<
|
||||
" Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs" <<
|
||||
" Average Clock Skew: " << qPrintable(incomingClockSkewString) << " msecs" <<
|
||||
" [" << qPrintable(formattedClockSkewString) << "]";
|
||||
|
||||
|
||||
|
@ -547,38 +480,37 @@ void OctreeStatsDialog::showOctreeServersOfType(int& serverCount, NodeType_t ser
|
|||
" Wasted Bytes: " << qPrintable(incomingWastedBytesString);
|
||||
|
||||
serverDetails << extraDetails.str();
|
||||
if (_extraServerDetails[serverCount - 1] == MORE) {
|
||||
linkDetails << " " << " [<a href='most-" << serverCount << "'>most...</a>]";
|
||||
linkDetails << " " << " [<a href='less-" << serverCount << "'>less...</a>]";
|
||||
if (_extraServerDetails == MORE) {
|
||||
linkDetails << " [<a href='most'>most...</a>]";
|
||||
linkDetails << " [<a href='less'>less...</a>]";
|
||||
} else {
|
||||
linkDetails << " " << " [<a href='more-" << serverCount << "'>less...</a>]";
|
||||
linkDetails << " " << " [<a href='less-" << serverCount << "'>least...</a>]";
|
||||
linkDetails << " [<a href='more'>less...</a>]";
|
||||
linkDetails << " [<a href='less'>least...</a>]";
|
||||
}
|
||||
|
||||
} break;
|
||||
case LESS: {
|
||||
// nothing
|
||||
} break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
linkDetails << " " << " [<a href='more-" << serverCount << "'>more...</a>]";
|
||||
linkDetails << " " << " [<a href='most-" << serverCount << "'>most...</a>]";
|
||||
}
|
||||
serverDetails << linkDetails.str();
|
||||
_labels[_octreeServerLables[serverCount - 1]]->setText(serverDetails.str().c_str());
|
||||
} // is VOXEL_SERVER
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
linkDetails << " [<a href='more'>more...</a>]";
|
||||
linkDetails << " [<a href='most'>most...</a>]";
|
||||
}
|
||||
serverDetails << linkDetails.str();
|
||||
_labels[_octreeServerLabel]->setText(serverDetails.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeStatsDialog::reject() {
|
||||
// Just regularly close upon ESC
|
||||
this->QDialog::close();
|
||||
QDialog::close();
|
||||
}
|
||||
|
||||
void OctreeStatsDialog::closeEvent(QCloseEvent* event) {
|
||||
this->QDialog::closeEvent(event);
|
||||
QDialog::closeEvent(event);
|
||||
emit closed();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <OctreeSceneStats.h>
|
||||
|
||||
#define MAX_STATS 100
|
||||
#define MAX_VOXEL_SERVERS 50
|
||||
#define DEFAULT_COLOR 0
|
||||
|
||||
class OctreeStatsDialog : public QDialog {
|
||||
|
@ -47,18 +46,22 @@ protected:
|
|||
void RemoveStatItem(int item);
|
||||
void showAllOctreeServers();
|
||||
|
||||
void showOctreeServersOfType(int& serverNumber, NodeType_t serverType,
|
||||
const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions);
|
||||
void showOctreeServersOfType(NodeType_t serverType);
|
||||
|
||||
private:
|
||||
enum details {
|
||||
LESS,
|
||||
MORE,
|
||||
MOST
|
||||
};
|
||||
|
||||
typedef enum { LESS, MORE, MOST } details;
|
||||
|
||||
QFormLayout* _form;
|
||||
QFormLayout* _form { nullptr };
|
||||
QLabel* _labels[MAX_STATS];
|
||||
NodeToOctreeSceneStats* _model;
|
||||
int _statCount;
|
||||
|
||||
NodeToOctreeSceneStats* _model { nullptr };
|
||||
int _statCount { 0 };
|
||||
|
||||
int _octreeServerLabel;
|
||||
|
||||
int _sendingMode;
|
||||
int _serverElements;
|
||||
int _localElements;
|
||||
|
@ -72,16 +75,14 @@ private:
|
|||
int _processedPacketsTiming;
|
||||
int _outboundEditPackets;
|
||||
|
||||
const int SAMPLES_PER_SECOND = 10;
|
||||
SimpleMovingAverage _averageUpdatesPerSecond;
|
||||
quint64 _lastWindowAt = usecTimestampNow();
|
||||
quint64 _lastKnownTrackedEdits = 0;
|
||||
const int SAMPLES_PER_SECOND { 10 };
|
||||
SimpleMovingAverage _averageUpdatesPerSecond { SAMPLES_PER_SECOND };
|
||||
quint64 _lastWindowAt { usecTimestampNow() };
|
||||
quint64 _lastKnownTrackedEdits { 0 };
|
||||
|
||||
quint64 _lastRefresh = 0;
|
||||
quint64 _lastRefresh { 0 };
|
||||
|
||||
int _octreeServerLables[MAX_VOXEL_SERVERS];
|
||||
int _octreeServerLabelsCount;
|
||||
details _extraServerDetails[MAX_VOXEL_SERVERS];
|
||||
details _extraServerDetails { LESS };
|
||||
};
|
||||
|
||||
#endif // hifi_OctreeStatsDialog_h
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) :
|
||||
QObject(parent),
|
||||
_model(model)
|
||||
, _statCount(0)
|
||||
, _averageUpdatesPerSecond(SAMPLES_PER_SECOND)
|
||||
_model(model),
|
||||
_statCount(0),
|
||||
_averageUpdatesPerSecond(SAMPLES_PER_SECOND)
|
||||
{
|
||||
//schedule updates
|
||||
connect(&_updateTimer, &QTimer::timeout, this, &OctreeStatsProvider::updateOctreeStatsData);
|
||||
|
@ -237,140 +237,105 @@ void OctreeStatsProvider::updateOctreeStatsData() {
|
|||
}
|
||||
|
||||
void OctreeStatsProvider::updateOctreeServers() {
|
||||
showOctreeServersOfType(NodeType::EntityServer);
|
||||
}
|
||||
|
||||
void OctreeStatsProvider::showOctreeServersOfType(NodeType_t serverType) {
|
||||
m_servers.clear();
|
||||
int serverCount = 0;
|
||||
|
||||
showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity",
|
||||
qApp->getEntityServerJurisdictions());
|
||||
if (m_serversNum != serverCount) {
|
||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(serverType);
|
||||
if (node) {
|
||||
++serverCount;
|
||||
|
||||
QString lesserDetails;
|
||||
QString moreDetails;
|
||||
QString mostDetails;
|
||||
|
||||
if (node->getActiveSocket()) {
|
||||
lesserDetails += "active ";
|
||||
} else {
|
||||
lesserDetails += "inactive ";
|
||||
}
|
||||
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
// now lookup stats details for this server...
|
||||
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
|
||||
sceneStats->withReadLock([&] {
|
||||
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
|
||||
OctreeSceneStats& stats = sceneStats->at(nodeUUID);
|
||||
|
||||
float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC;
|
||||
float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC;
|
||||
float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND;
|
||||
float lastFullPackets = stats.getLastFullTotalPackets();
|
||||
float lastFullPPS = lastFullPackets;
|
||||
if (lastFullSendInSeconds > 0) {
|
||||
lastFullPPS = lastFullPackets / lastFullSendInSeconds;
|
||||
}
|
||||
|
||||
mostDetails += QString("<br/><br/>Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS")
|
||||
.arg(lastFullEncode)
|
||||
.arg(lastFullSend)
|
||||
.arg(lastFullPackets)
|
||||
.arg(stats.getLastFullTotalBytes())
|
||||
.arg(lastFullPPS);
|
||||
|
||||
for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) {
|
||||
OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i);
|
||||
OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item);
|
||||
mostDetails += QString("<br/> %1 %2")
|
||||
.arg(itemInfo.caption).arg(stats.getItemValue(item));
|
||||
}
|
||||
|
||||
moreDetails += "<br/>Node UUID: " +nodeUUID.toString() + " ";
|
||||
|
||||
moreDetails += QString("<br/>Elements: %1 total %2 internal %3 leaves ")
|
||||
.arg(stats.getTotalElements())
|
||||
.arg(stats.getTotalInternal())
|
||||
.arg(stats.getTotalLeaves());
|
||||
|
||||
const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats();
|
||||
qint64 clockSkewInUsecs = node->getClockSkewUsec();
|
||||
qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC;
|
||||
|
||||
moreDetails += QString("<br/>Incoming Packets: %1/ Lost: %2/ Recovered: %3")
|
||||
.arg(stats.getIncomingPackets())
|
||||
.arg(seqStats.getLost())
|
||||
.arg(seqStats.getRecovered());
|
||||
|
||||
moreDetails += QString("<br/> Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4")
|
||||
.arg(seqStats.getOutOfOrder())
|
||||
.arg(seqStats.getEarly())
|
||||
.arg(seqStats.getLate())
|
||||
.arg(seqStats.getUnreasonable());
|
||||
|
||||
moreDetails += QString("<br/> Average Flight Time: %1 msecs")
|
||||
.arg(stats.getIncomingFlightTimeAverage());
|
||||
|
||||
moreDetails += QString("<br/> Average Ping Time: %1 msecs")
|
||||
.arg(node->getPingMs());
|
||||
|
||||
moreDetails += QString("<br/> Average Clock Skew: %1 msecs [%2]")
|
||||
.arg(clockSkewInMS)
|
||||
.arg(formatUsecTime(clockSkewInUsecs));
|
||||
|
||||
|
||||
moreDetails += QString("<br/>Incoming Bytes: %1 Wasted Bytes: %2")
|
||||
.arg(stats.getIncomingBytes())
|
||||
.arg(stats.getIncomingWastedBytes());
|
||||
}
|
||||
});
|
||||
m_servers.append(lesserDetails);
|
||||
m_servers.append(moreDetails);
|
||||
m_servers.append(mostDetails);
|
||||
}
|
||||
|
||||
if (serverCount != m_serversNum) {
|
||||
m_serversNum = serverCount;
|
||||
emit serversNumChanged(m_serversNum);
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName,
|
||||
NodeToJurisdictionMap& serverJurisdictions) {
|
||||
|
||||
m_servers.clear();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
|
||||
// only send to the NodeTypes that are NodeType_t_VOXEL_SERVER
|
||||
if (node->getType() == serverType) {
|
||||
serverCount++;
|
||||
|
||||
QString lesserDetails;
|
||||
QString moreDetails;
|
||||
QString mostDetails;
|
||||
|
||||
if (node->getActiveSocket()) {
|
||||
lesserDetails += "active ";
|
||||
} else {
|
||||
lesserDetails += "inactive ";
|
||||
}
|
||||
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
// lookup our nodeUUID in the jurisdiction map, if it's missing then we're
|
||||
// missing at least one jurisdiction
|
||||
serverJurisdictions.withReadLock([&] {
|
||||
if (serverJurisdictions.find(nodeUUID) == serverJurisdictions.end()) {
|
||||
lesserDetails += " unknown jurisdiction ";
|
||||
return;
|
||||
}
|
||||
const JurisdictionMap& map = serverJurisdictions[nodeUUID];
|
||||
|
||||
auto rootCode = map.getRootOctalCode();
|
||||
|
||||
if (rootCode) {
|
||||
QString rootCodeHex = octalCodeToHexString(rootCode.get());
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(rootCode.get(), rootDetails);
|
||||
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
|
||||
lesserDetails += QString(" jurisdiction: %1 [%2, %3, %4: %5]")
|
||||
.arg(rootCodeHex)
|
||||
.arg(rootDetails.x)
|
||||
.arg(rootDetails.y)
|
||||
.arg(rootDetails.z)
|
||||
.arg(rootDetails.s);
|
||||
} else {
|
||||
lesserDetails += " jurisdiction has no rootCode";
|
||||
} // root code
|
||||
});
|
||||
|
||||
// now lookup stats details for this server...
|
||||
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
|
||||
sceneStats->withReadLock([&] {
|
||||
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
|
||||
OctreeSceneStats& stats = sceneStats->at(nodeUUID);
|
||||
|
||||
float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC;
|
||||
float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC;
|
||||
float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND;
|
||||
float lastFullPackets = stats.getLastFullTotalPackets();
|
||||
float lastFullPPS = lastFullPackets;
|
||||
if (lastFullSendInSeconds > 0) {
|
||||
lastFullPPS = lastFullPackets / lastFullSendInSeconds;
|
||||
}
|
||||
|
||||
mostDetails += QString("<br/><br/>Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS")
|
||||
.arg(lastFullEncode)
|
||||
.arg(lastFullSend)
|
||||
.arg(lastFullPackets)
|
||||
.arg(stats.getLastFullTotalBytes())
|
||||
.arg(lastFullPPS);
|
||||
|
||||
for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) {
|
||||
OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i);
|
||||
OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item);
|
||||
mostDetails += QString("<br/> %1 %2")
|
||||
.arg(itemInfo.caption).arg(stats.getItemValue(item));
|
||||
}
|
||||
|
||||
moreDetails += "<br/>Node UUID: " +nodeUUID.toString() + " ";
|
||||
|
||||
moreDetails += QString("<br/>Elements: %1 total %2 internal %3 leaves ")
|
||||
.arg(stats.getTotalElements())
|
||||
.arg(stats.getTotalInternal())
|
||||
.arg(stats.getTotalLeaves());
|
||||
|
||||
const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats();
|
||||
qint64 clockSkewInUsecs = node->getClockSkewUsec();
|
||||
qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC;
|
||||
|
||||
moreDetails += QString("<br/>Incoming Packets: %1/ Lost: %2/ Recovered: %3")
|
||||
.arg(stats.getIncomingPackets())
|
||||
.arg(seqStats.getLost())
|
||||
.arg(seqStats.getRecovered());
|
||||
|
||||
moreDetails += QString("<br/> Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4")
|
||||
.arg(seqStats.getOutOfOrder())
|
||||
.arg(seqStats.getEarly())
|
||||
.arg(seqStats.getLate())
|
||||
.arg(seqStats.getUnreasonable());
|
||||
|
||||
moreDetails += QString("<br/> Average Flight Time: %1 msecs")
|
||||
.arg(stats.getIncomingFlightTimeAverage());
|
||||
|
||||
moreDetails += QString("<br/> Average Ping Time: %1 msecs")
|
||||
.arg(node->getPingMs());
|
||||
|
||||
moreDetails += QString("<br/> Average Clock Skew: %1 msecs [%2]")
|
||||
.arg(clockSkewInMS)
|
||||
.arg(formatUsecTime(clockSkewInUsecs));
|
||||
|
||||
|
||||
moreDetails += QString("<br/>Incoming Bytes: %1 Wasted Bytes: %2")
|
||||
.arg(stats.getIncomingBytes())
|
||||
.arg(stats.getIncomingWastedBytes());
|
||||
}
|
||||
});
|
||||
m_servers.append(lesserDetails);
|
||||
m_servers.append(moreDetails);
|
||||
m_servers.append(mostDetails);
|
||||
} // is VOXEL_SERVER
|
||||
});
|
||||
emit serversChanged(m_servers);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
#define MAX_STATS 100
|
||||
#define MAX_VOXEL_SERVERS 50
|
||||
#define DEFAULT_COLOR 0
|
||||
|
||||
class OctreeStatsProvider : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
@ -121,8 +117,7 @@ private slots:
|
|||
void updateOctreeStatsData();
|
||||
protected:
|
||||
void updateOctreeServers();
|
||||
void showOctreeServersOfType(int& serverNumber, NodeType_t serverType,
|
||||
const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions);
|
||||
void showOctreeServersOfType(NodeType_t serverType);
|
||||
|
||||
private:
|
||||
NodeToOctreeSceneStats* _model;
|
||||
|
@ -136,7 +131,7 @@ private:
|
|||
quint64 _lastRefresh = 0;
|
||||
|
||||
QTimer _updateTimer;
|
||||
int m_serversNum {0};
|
||||
int m_serversNum { 0 };
|
||||
QString m_serverElements;
|
||||
QString m_localElements;
|
||||
QString m_localElementsMemory;
|
||||
|
|
|
@ -90,11 +90,19 @@ void setupPreferences() {
|
|||
preference->setMax(500);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferStylusOverLaser(); };
|
||||
auto setter = [](bool value) { qApp->setPreferStylusOverLaser(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Stylus Over Laser", getter, setter));
|
||||
}
|
||||
// FIXME: Remove setting completely or make available through JavaScript API?
|
||||
/*
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); };
|
||||
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter));
|
||||
}
|
||||
*/
|
||||
{
|
||||
static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) };
|
||||
auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; };
|
||||
|
|
|
@ -212,8 +212,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
|
||||
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
|
||||
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
|
||||
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
|
||||
* be removed.
|
||||
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
|
||||
* <code>dashed</code>.
|
||||
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,
|
||||
|
@ -241,7 +239,7 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "localRotation" || property == "localOrientation") {
|
||||
return quatToVariant(getLocalOrientation());
|
||||
}
|
||||
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled" || property == "filed") {
|
||||
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled") {
|
||||
return _isSolid;
|
||||
}
|
||||
if (property == "isWire" || property == "wire") {
|
||||
|
|
|
@ -151,7 +151,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
|
|||
glm::vec3 normal;
|
||||
boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal);
|
||||
float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE;
|
||||
if (DependencyManager::get<PointerManager>()->isLeftHand(event.getID())) {
|
||||
if (event.getID() == 1) { // "1" is left hand
|
||||
offsetAngle *= -1.0f;
|
||||
}
|
||||
contextOverlayPosition = cameraPosition +
|
||||
|
|
|
@ -97,6 +97,11 @@ void ModelOverlay::update(float deltatime) {
|
|||
_model->setLayeredInHUD(getDrawHUDLayer(), scene);
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
||||
if (!_texturesLoaded && _model->getGeometry() && _model->getGeometry()->areTexturesLoaded()) {
|
||||
_texturesLoaded = true;
|
||||
_model->updateRenderItems();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
|
@ -170,10 +175,12 @@ void ModelOverlay::setProperties(const QVariantMap& properties) {
|
|||
_url = urlValue.toString();
|
||||
_updateModel = true;
|
||||
_isLoaded = false;
|
||||
_texturesLoaded = false;
|
||||
}
|
||||
|
||||
auto texturesValue = properties["textures"];
|
||||
if (texturesValue.isValid() && texturesValue.canConvert(QVariant::Map)) {
|
||||
_texturesLoaded = false;
|
||||
QVariantMap textureMap = texturesValue.toMap();
|
||||
QMetaObject::invokeMethod(_model.get(), "setTextures", Qt::AutoConnection,
|
||||
Q_ARG(const QVariantMap&, textureMap));
|
||||
|
|
|
@ -79,6 +79,7 @@ private:
|
|||
|
||||
ModelPointer _model;
|
||||
QVariantMap _modelTextures;
|
||||
bool _texturesLoaded { false };
|
||||
|
||||
render::ItemIDs _subRenderItemIDs;
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include <UserActivityLoggerScriptingInterface.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include <AddressManager.h>
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include "scripting/AssetMappingsScriptingInterface.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
|
@ -193,8 +192,12 @@ void Web3DOverlay::setupQmlSurface() {
|
|||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance());
|
||||
|
||||
// in Qt 5.10.0 there is already an "Audio" object in the QML context
|
||||
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper());
|
||||
|
@ -514,7 +517,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
|
|||
*
|
||||
* @property {string} url - The URL of the Web page to display.
|
||||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page.
|
||||
* @property {Vec2} resolution - <strong>Deprecated:</strong> This property has been removed.
|
||||
* @property {number} dpi=30 - The dots per inch to display the Web page at, on the overlay.
|
||||
* @property {Vec2} dimensions=1,1 - The size of the overlay to display the Web page on, in meters. Synonyms:
|
||||
* <code>scale</code>, <code>size</code>.
|
||||
|
@ -605,4 +607,4 @@ Web3DOverlay* Web3DOverlay::createClone() const {
|
|||
|
||||
void Web3DOverlay::emitScriptEvent(const QVariant& message) {
|
||||
QMetaObject::invokeMethod(this, "scriptEventReceived", Q_ARG(QVariant, message));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "filters/PostTransformFilter.h"
|
||||
#include "filters/RotateFilter.h"
|
||||
#include "filters/LowVelocityFilter.h"
|
||||
#include "filters/ExponentialSmoothingFilter.h"
|
||||
|
||||
using namespace controller;
|
||||
|
||||
|
@ -49,6 +50,7 @@ REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform")
|
|||
REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(LowVelocityFilter, "lowVelocity")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(ExponentialSmoothingFilter, "exponentialSmoothing")
|
||||
|
||||
const QString JSON_FILTER_TYPE = QStringLiteral("type");
|
||||
const QString JSON_FILTER_PARAMS = QStringLiteral("params");
|
||||
|
@ -93,7 +95,7 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
|
|||
output = objectParameters[name].toDouble();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -117,7 +119,7 @@ bool Filter::parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output)
|
|||
objectParameters["z"].toDouble());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -126,7 +128,7 @@ bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output)
|
|||
auto objectParameters = parameters.toObject();
|
||||
|
||||
|
||||
if (objectParameters.contains("r0c0") &&
|
||||
if (objectParameters.contains("r0c0") &&
|
||||
objectParameters.contains("r1c0") &&
|
||||
objectParameters.contains("r2c0") &&
|
||||
objectParameters.contains("r3c0") &&
|
||||
|
@ -169,9 +171,9 @@ bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output)
|
|||
bool Filter::parseQuatParameter(const QJsonValue& parameters, glm::quat& output) {
|
||||
if (parameters.isObject()) {
|
||||
auto objectParameters = parameters.toObject();
|
||||
if (objectParameters.contains("w") &&
|
||||
objectParameters.contains("x") &&
|
||||
objectParameters.contains("y") &&
|
||||
if (objectParameters.contains("w") &&
|
||||
objectParameters.contains("x") &&
|
||||
objectParameters.contains("y") &&
|
||||
objectParameters.contains("z")) {
|
||||
|
||||
output = glm::quat(objectParameters["w"].toDouble(),
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "filters/PostTransformFilter.h"
|
||||
#include "filters/RotateFilter.h"
|
||||
#include "filters/LowVelocityFilter.h"
|
||||
#include "filters/ExponentialSmoothingFilter.h"
|
||||
#include "conditionals/AndConditional.h"
|
||||
|
||||
using namespace controller;
|
||||
|
@ -134,6 +135,11 @@ QObject* RouteBuilderProxy::lowVelocity(float rotationConstant, float translatio
|
|||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::exponentialSmoothing(float rotationConstant, float translationConstant) {
|
||||
addFilter(std::make_shared<ExponentialSmoothingFilter>(rotationConstant, translationConstant));
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::constrainToInteger() {
|
||||
addFilter(std::make_shared<ConstrainToIntegerFilter>());
|
||||
return this;
|
||||
|
|
|
@ -53,6 +53,7 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
|
||||
Q_INVOKABLE QObject* rotate(glm::quat rotation);
|
||||
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
|
||||
Q_INVOKABLE QObject* exponentialSmoothing(float rotationConstant, float translationConstant);
|
||||
Q_INVOKABLE QObject* logicalNot();
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// Created by Anthony Thibault 2017/12/07
|
||||
// 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 "ExponentialSmoothingFilter.h"
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include "../../UserInputMapper.h"
|
||||
#include "../../Input.h"
|
||||
#include <DependencyManager.h>
|
||||
|
||||
static const QString JSON_ROTATION = QStringLiteral("rotation");
|
||||
static const QString JSON_TRANSLATION = QStringLiteral("translation");
|
||||
namespace controller {
|
||||
|
||||
Pose ExponentialSmoothingFilter::apply(Pose value) const {
|
||||
|
||||
if (value.isValid()) {
|
||||
|
||||
// to perform filtering in sensor space, we need to compute the transformations.
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
const InputCalibrationData calibrationData = userInputMapper->getInputCalibrationData();
|
||||
glm::mat4 sensorToAvatarMat = glm::inverse(calibrationData.avatarMat) * calibrationData.sensorToWorldMat;
|
||||
glm::mat4 avatarToSensorMat = glm::inverse(calibrationData.sensorToWorldMat) * calibrationData.avatarMat;
|
||||
|
||||
// transform pose into sensor space.
|
||||
Pose sensorValue = value.transform(avatarToSensorMat);
|
||||
|
||||
if (_prevSensorValue.isValid()) {
|
||||
// exponential smoothing filter
|
||||
sensorValue.translation = _translationConstant * sensorValue.getTranslation() + (1.0f - _translationConstant) * _prevSensorValue.getTranslation();
|
||||
sensorValue.rotation = safeMix(sensorValue.getRotation(), _prevSensorValue.getRotation(), _rotationConstant);
|
||||
|
||||
// remember previous sensor space value.
|
||||
_prevSensorValue = sensorValue;
|
||||
|
||||
// transform back into avatar space
|
||||
return sensorValue.transform(sensorToAvatarMat);
|
||||
} else {
|
||||
// remember previous sensor space value.
|
||||
_prevSensorValue = sensorValue;
|
||||
|
||||
// no previous value to smooth with, so return value unchanged
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
// return invalid value unchanged
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
bool ExponentialSmoothingFilter::parseParameters(const QJsonValue& parameters) {
|
||||
|
||||
if (parameters.isObject()) {
|
||||
auto obj = parameters.toObject();
|
||||
if (obj.contains(JSON_ROTATION) && obj.contains(JSON_TRANSLATION)) {
|
||||
_rotationConstant = glm::clamp((float)obj[JSON_ROTATION].toDouble(), 0.0f, 1.0f);
|
||||
_translationConstant = glm::clamp((float)obj[JSON_TRANSLATION].toDouble(), 0.0f, 1.0f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Created by Anthony Thibault 2017/12/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_Controllers_Filters_Exponential_Smoothing_h
|
||||
#define hifi_Controllers_Filters_Exponential_Smoothing_h
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class ExponentialSmoothingFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(ExponentialSmoothingFilter);
|
||||
|
||||
public:
|
||||
ExponentialSmoothingFilter() {}
|
||||
ExponentialSmoothingFilter(float rotationConstant, float translationConstant) :
|
||||
_translationConstant(translationConstant), _rotationConstant(rotationConstant) {}
|
||||
|
||||
float apply(float value) const override { return value; }
|
||||
Pose apply(Pose value) const override;
|
||||
bool parseParameters(const QJsonValue& parameters) override;
|
||||
|
||||
private:
|
||||
|
||||
// Constant between 0 and 1.
|
||||
// 1 indicates no smoothing at all, poses are passed through unaltered.
|
||||
// Values near 1 are less smooth with lower latency.
|
||||
// Values near 0 are more smooth with higher latency.
|
||||
float _translationConstant { 0.375f };
|
||||
float _rotationConstant { 0.375f };
|
||||
|
||||
mutable Pose _prevSensorValue { Pose() }; // sensor space
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -16,4 +16,3 @@ include_hifi_library_headers(controllers)
|
|||
|
||||
target_bullet()
|
||||
target_polyvox()
|
||||
|
||||
|
|
|
@ -1080,6 +1080,10 @@ bool ModelEntityRenderer::needsRenderUpdate() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model->needsReload()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1216,6 +1220,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
// From here on, we are guaranteed a populated model
|
||||
withWriteLock([&] {
|
||||
if (_parsedModelURL != model->getURL()) {
|
||||
_texturesLoaded = false;
|
||||
model->setURL(_parsedModelURL);
|
||||
}
|
||||
});
|
||||
|
@ -1247,6 +1252,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
|
||||
if (_lastTextures != entity->getTextures()) {
|
||||
_texturesLoaded = false;
|
||||
_lastTextures = entity->getTextures();
|
||||
auto newTextures = parseTexturesToMap(_lastTextures, entity->_originalTextures);
|
||||
if (newTextures != _currentTextures) {
|
||||
|
@ -1301,12 +1307,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
}
|
||||
|
||||
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
|
||||
_texturesLoaded = true;
|
||||
model->updateRenderItems();
|
||||
}
|
||||
|
||||
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
|
||||
// we will watch for that and ask the model to update it's render items
|
||||
if (model->getRenderItemsNeedUpdate()) {
|
||||
model->updateRenderItems();
|
||||
}
|
||||
|
||||
|
||||
// The code to deal with the change of properties is now in ModelEntityItem.cpp
|
||||
// That is where _currentFrame and _lastAnimated were updated.
|
||||
if (_animating) {
|
||||
|
|
|
@ -158,10 +158,11 @@ private:
|
|||
virtual bool isTransparent() const override { return false; }
|
||||
|
||||
bool _hasModel { false };
|
||||
::ModelPointer _model;
|
||||
ModelPointer _model;
|
||||
GeometryResource::Pointer _compoundShapeResource;
|
||||
QString _lastTextures;
|
||||
QVariantMap _currentTextures;
|
||||
bool _texturesLoaded { false };
|
||||
AnimationPropertyGroup _renderAnimationProperties;
|
||||
int _lastKnownCurrentFrame { -1 };
|
||||
#ifdef MODEL_ENTITY_USE_FADE_EFFECT
|
||||
|
|
|
@ -686,18 +686,18 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
||||
}
|
||||
} else if (_simulationOwner.pendingTake(now - maxPingRoundTrip)) {
|
||||
// we sent a bid before this packet could have been sent from the server
|
||||
// so we ignore it and pretend we own the object's simulation
|
||||
// we sent a bid already but maybe before this packet was sent from the server
|
||||
weOwnSimulation = true;
|
||||
if (newSimOwner.getID().isNull()) {
|
||||
// entity-server is trying to clear someone else's ownership
|
||||
// but we want to own it, therefore we ignore this clear event
|
||||
// the entity-server is trying to clear someone else's ownership
|
||||
if (!_simulationOwner.isNull()) {
|
||||
// someone else really did own it
|
||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||
somethingChanged = true;
|
||||
_simulationOwner.clearCurrentOwner();
|
||||
}
|
||||
} else if (newSimOwner.getID() == myNodeID) {
|
||||
// the entity-server is awarding us ownership which is what we want
|
||||
_simulationOwner.set(newSimOwner);
|
||||
}
|
||||
} else if (newSimOwner.matchesValidID(myNodeID) && !_hasBidOnSimulation) {
|
||||
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,
|
||||
|
@ -2082,17 +2082,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
}
|
||||
|
||||
EntityDynamicPointer action = _objectActions[actionID];
|
||||
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
|
||||
if (simulation) {
|
||||
action->removeFromSimulation(simulation);
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
auto removedActionType = action->getType();
|
||||
if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) {
|
||||
_dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING;
|
||||
|
@ -2109,7 +2098,17 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
|||
// because they should have been set correctly when the action was added
|
||||
// and/or when children were linked
|
||||
}
|
||||
action->setOwnerEntity(nullptr);
|
||||
action->setIsMine(false);
|
||||
_objectActions.remove(actionID);
|
||||
|
||||
if (simulation) {
|
||||
action->removeFromSimulation(simulation);
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
setDynamicDataNeedsTransmit(true);
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ public:
|
|||
static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args);
|
||||
|
||||
virtual int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
|
|
|
@ -355,11 +355,11 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
|||
} else if (submittedID == senderID) {
|
||||
// the sender is trying to take or continue ownership
|
||||
if (entity->getSimulatorID().isNull()) {
|
||||
// the sender it taking ownership
|
||||
// the sender is taking ownership
|
||||
properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||
simulationBlocked = false;
|
||||
} else if (entity->getSimulatorID() == senderID) {
|
||||
// the sender is asserting ownership
|
||||
// the sender is asserting ownership, maybe changing priority
|
||||
simulationBlocked = false;
|
||||
} else {
|
||||
// the sender is trying to steal ownership from another simulator
|
||||
|
|
|
@ -457,13 +457,33 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
|
|||
// vertex-index/texture-index
|
||||
// vertex-index/texture-index/surface-normal-index
|
||||
QByteArray token = tokenizer.getDatum();
|
||||
if (!isdigit(token[0])) { // Tokenizer treats line endings as whitespace. Non-digit indicates done;
|
||||
auto firstChar = token[0];
|
||||
// Tokenizer treats line endings as whitespace. Non-digit and non-negative sign indicates done;
|
||||
if (!isdigit(firstChar) && firstChar != '-') {
|
||||
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
|
||||
break;
|
||||
}
|
||||
QList<QByteArray> parts = token.split('/');
|
||||
assert(parts.count() >= 1);
|
||||
assert(parts.count() <= 3);
|
||||
// If indices are negative relative indices then adjust them to absolute indices based on current vector sizes
|
||||
// Also add 1 to each index as 1 will be subtracted later on from each index in OBJFace::add
|
||||
for (int i = 0; i < parts.count(); ++i) {
|
||||
int part = parts[i].toInt();
|
||||
if (part < 0) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
parts[i].setNum(vertices.size() + part + 1);
|
||||
break;
|
||||
case 1:
|
||||
parts[i].setNum(textureUVs.size() + part + 1);
|
||||
break;
|
||||
case 2:
|
||||
parts[i].setNum(normals.size() + part + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const QByteArray noData {};
|
||||
face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData,
|
||||
vertices, vertexColors);
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
#include <QtCore/QtGlobal>
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#define HIFI_GLES
|
||||
#define HIFI_EGL
|
||||
#endif
|
||||
|
||||
#if defined(HIFI_GLES)
|
||||
// Minimum GL ES version required is 3.2
|
||||
#define GL_MIN_VERSION_MAJOR 0x03
|
||||
#define GL_MIN_VERSION_MINOR 0x02
|
||||
|
@ -30,9 +35,11 @@
|
|||
|
||||
#define MINIMUM_GL_VERSION ((GL_MIN_VERSION_MAJOR << 8) | GL_MIN_VERSION_MINOR)
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
|
||||
#if defined(HIFI_GLES)
|
||||
#include <EGL/egl.h>
|
||||
#endif
|
||||
|
||||
#if defined(HIFI_GLES)
|
||||
#include <GLES3/gl32.h>
|
||||
|
||||
#define GL_DEPTH_COMPONENT32_OES 0x81A7
|
||||
|
@ -61,7 +68,7 @@ extern "C" {
|
|||
extern PFNGLFRAMEBUFFERTEXTUREEXTPROC __glFramebufferTextureEXT;
|
||||
}
|
||||
|
||||
#else // !defined(Q_OS_ANDROID)
|
||||
#else // !defined(HIFI_GLES)
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES 1
|
||||
#include <GL/glew.h>
|
||||
|
|
|
@ -28,7 +28,7 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
|
|||
static QSurfaceFormat format;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
#if defined(Q_OS_ANDROID)
|
||||
#if defined(HIFI_GLES)
|
||||
format.setRenderableType(QSurfaceFormat::OpenGLES);
|
||||
format.setRedBufferSize(8);
|
||||
format.setGreenBufferSize(8);
|
||||
|
|
|
@ -52,16 +52,15 @@ bool OffscreenGLCanvas::create(QOpenGLContext* sharedContext) {
|
|||
|
||||
_offscreenSurface->setFormat(_context->format());
|
||||
_offscreenSurface->create();
|
||||
|
||||
// Due to a https://bugreports.qt.io/browse/QTBUG-65125 we can't rely on `isValid`
|
||||
// to determine if the offscreen surface was successfully created, so we use
|
||||
// makeCurrent as a proxy test. Bug is fixed in Qt 5.9.4
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (!_context->makeCurrent(_offscreenSurface)) {
|
||||
qFatal("Unable to make offscreen surface current");
|
||||
}
|
||||
#else
|
||||
// For some reason, the offscreen surface is considered invalid on android
|
||||
// possibly because of a bad format? Would need to add some logging to the
|
||||
// eglpbuffer implementation used from the android platform plugin.
|
||||
// Alternatively investigate the use of an invisible surface view to do
|
||||
// a 'native' offscreen surface
|
||||
if (!_offscreenSurface->isValid()) {
|
||||
qFatal("Offscreen surface is invalid");
|
||||
}
|
||||
|
|
|
@ -596,7 +596,7 @@ bool AddressManager::handleDomainID(const QString& host) {
|
|||
void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) {
|
||||
if (!handleViewpoint(path, false, trigger, wasPathOnly)) {
|
||||
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path <<
|
||||
"- wll attempt to ask domain-server to resolve.";
|
||||
"- will attempt to ask domain-server to resolve.";
|
||||
|
||||
if (!wasPathOnly) {
|
||||
// if we received a path with a host then we need to remember what it was here so we can not
|
||||
|
|
|
@ -42,7 +42,10 @@ static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.Loc
|
|||
const std::set<NodeType_t> SOLO_NODE_TYPES = {
|
||||
NodeType::AvatarMixer,
|
||||
NodeType::AudioMixer,
|
||||
NodeType::AssetServer
|
||||
NodeType::AssetServer,
|
||||
NodeType::EntityServer,
|
||||
NodeType::MessagesMixer,
|
||||
NodeType::EntityScriptServer
|
||||
};
|
||||
|
||||
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
||||
|
|
|
@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(EntityVersion::OwnershipChallengeFix);
|
||||
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConnectionIdentifier);
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);
|
||||
case PacketType::AvatarIdentity:
|
||||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
|
|
|
@ -56,8 +56,8 @@ public:
|
|||
ICEServerPeerInformation,
|
||||
ICEServerQuery,
|
||||
OctreeStats,
|
||||
Jurisdiction,
|
||||
JurisdictionRequest,
|
||||
UNUSED_PACKET_TYPE_1,
|
||||
UNUSED_PACKET_TYPE_2,
|
||||
AssignmentClientStatus,
|
||||
NoisyMute,
|
||||
AvatarIdentity,
|
||||
|
@ -212,7 +212,8 @@ enum class EntityScriptCallMethodVersion : PacketVersion {
|
|||
enum class EntityQueryPacketVersion: PacketVersion {
|
||||
JSONFilter = 18,
|
||||
JSONFilterWithFamilyTree = 19,
|
||||
ConnectionIdentifier = 20
|
||||
ConnectionIdentifier = 20,
|
||||
RemovedJurisdictions = 21
|
||||
};
|
||||
|
||||
enum class AssetServerPacketVersion: PacketVersion {
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
//
|
||||
// JurisdictionListener.cpp
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 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 <PerfStat.h>
|
||||
|
||||
#include <OctalCode.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include "JurisdictionListener.h"
|
||||
|
||||
JurisdictionListener::JurisdictionListener(NodeType_t type) :
|
||||
_nodeType(type),
|
||||
_packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND)
|
||||
{
|
||||
setObjectName("Jurisdiction Listener");
|
||||
|
||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled);
|
||||
|
||||
// tell our NodeList we want to hear about nodes with our node type
|
||||
DependencyManager::get<NodeList>()->addNodeTypeToInterestSet(type);
|
||||
}
|
||||
|
||||
void JurisdictionListener::nodeKilled(SharedNodePointer node) {
|
||||
if (_jurisdictions.find(node->getUUID()) != _jurisdictions.end()) {
|
||||
_jurisdictions.erase(_jurisdictions.find(node->getUUID()));
|
||||
}
|
||||
}
|
||||
|
||||
bool JurisdictionListener::queueJurisdictionRequest() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int nodeCount = 0;
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
if (node->getType() == getNodeType() && node->getActiveSocket()) {
|
||||
auto packet = NLPacket::create(PacketType::JurisdictionRequest, 0);
|
||||
_packetSender.queuePacketForSending(node, std::move(packet));
|
||||
nodeCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (nodeCount > 0){
|
||||
_packetSender.setPacketsPerSecond(nodeCount);
|
||||
} else {
|
||||
_packetSender.setPacketsPerSecond(NO_SERVER_CHECK_RATE);
|
||||
}
|
||||
|
||||
// keep going if still running
|
||||
return isStillRunning();
|
||||
}
|
||||
|
||||
void JurisdictionListener::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
if (message->getType() == PacketType::Jurisdiction) {
|
||||
JurisdictionMap map;
|
||||
map.unpackFromPacket(*message);
|
||||
_jurisdictions[message->getSourceID()] = map;
|
||||
}
|
||||
}
|
||||
|
||||
bool JurisdictionListener::process() {
|
||||
bool continueProcessing = isStillRunning();
|
||||
|
||||
// If we're still running, and we don't have any requests waiting to be sent, then queue our jurisdiction requests
|
||||
if (continueProcessing && !_packetSender.hasPacketsToSend()) {
|
||||
queueJurisdictionRequest();
|
||||
}
|
||||
|
||||
if (continueProcessing) {
|
||||
continueProcessing = _packetSender.process();
|
||||
}
|
||||
if (continueProcessing) {
|
||||
// NOTE: This will sleep if there are no pending packets to process
|
||||
continueProcessing = ReceivedPacketProcessor::process();
|
||||
}
|
||||
|
||||
return continueProcessing;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
//
|
||||
// JurisdictionListener.h
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Voxel Packet Sender
|
||||
//
|
||||
// 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_JurisdictionListener_h
|
||||
#define hifi_JurisdictionListener_h
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketSender.h>
|
||||
#include <ReceivedPacketProcessor.h>
|
||||
|
||||
#include "JurisdictionMap.h"
|
||||
|
||||
/// Sends out PacketType::_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
|
||||
/// the PacketType::_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
|
||||
/// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
|
||||
/// and adding them to the processing queue by calling queueReceivedPacket()
|
||||
class JurisdictionListener : public ReceivedPacketProcessor {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const int DEFAULT_PACKETS_PER_SECOND = 1;
|
||||
static const int NO_SERVER_CHECK_RATE = 60; // if no servers yet detected, keep checking at 60fps
|
||||
|
||||
JurisdictionListener(NodeType_t type = NodeType::EntityServer);
|
||||
|
||||
virtual bool process() override;
|
||||
|
||||
NodeToJurisdictionMap* getJurisdictions() { return &_jurisdictions; }
|
||||
|
||||
|
||||
NodeType_t getNodeType() const { return _nodeType; }
|
||||
void setNodeType(NodeType_t type) { _nodeType = type; }
|
||||
|
||||
public slots:
|
||||
/// Called by NodeList to inform us that a node has been killed.
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
protected:
|
||||
/// Callback for processing of received packets. Will process any queued PacketType::_JURISDICTION and update the
|
||||
/// jurisdiction map member variable
|
||||
virtual void processPacket(QSharedPointer<ReceivedMessage> messsage, SharedNodePointer sendingNode) override;
|
||||
|
||||
private:
|
||||
NodeToJurisdictionMap _jurisdictions;
|
||||
NodeType_t _nodeType;
|
||||
|
||||
bool queueJurisdictionRequest();
|
||||
|
||||
PacketSender _packetSender;
|
||||
};
|
||||
#endif // hifi_JurisdictionListener_h
|
|
@ -1,325 +0,0 @@
|
|||
//
|
||||
// JurisdictionMap.cpp
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/1/13.
|
||||
// Copyright 2013 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 <QSettings>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "OctreeLogging.h"
|
||||
#include "JurisdictionMap.h"
|
||||
|
||||
void myDebugOutputBits(unsigned char byte, bool withNewLine) {
|
||||
if (isalnum(byte)) {
|
||||
printf("[ %d (%c): ", byte, byte);
|
||||
} else {
|
||||
printf("[ %d (0x%x): ", byte, byte);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
printf("%d", byte >> (7 - i) & 1);
|
||||
}
|
||||
printf(" ] ");
|
||||
|
||||
if (withNewLine) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void myDebugPrintOctalCode(const unsigned char* octalCode, bool withNewLine) {
|
||||
if (!octalCode) {
|
||||
printf("nullptr");
|
||||
} else {
|
||||
for (size_t i = 0; i < bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); i++) {
|
||||
myDebugOutputBits(octalCode[i], false);
|
||||
}
|
||||
}
|
||||
if (withNewLine) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// standard assignment
|
||||
// copy assignment
|
||||
JurisdictionMap& JurisdictionMap::operator=(const JurisdictionMap& other) {
|
||||
copyContents(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
JurisdictionMap::JurisdictionMap(const JurisdictionMap& other) : _rootOctalCode(nullptr) {
|
||||
copyContents(other);
|
||||
}
|
||||
|
||||
void JurisdictionMap::copyContents(const OctalCodePtr& rootCodeIn, const OctalCodePtrList& endNodesIn) {
|
||||
OctalCodePtr rootCode = rootCodeIn;
|
||||
if (!rootCode) {
|
||||
rootCode = createOctalCodePtr(1);
|
||||
*rootCode = 0;
|
||||
}
|
||||
|
||||
OctalCodePtrList emptyEndNodes;
|
||||
init(rootCode, endNodesIn);
|
||||
}
|
||||
|
||||
void JurisdictionMap::copyContents(const JurisdictionMap& other) {
|
||||
_nodeType = other._nodeType;
|
||||
|
||||
OctalCodePtr rootOctalCode;
|
||||
OctalCodePtrList endNodes;
|
||||
|
||||
std::tie(rootOctalCode, endNodes) = other.getRootAndEndNodeOctalCodes();
|
||||
|
||||
init(rootOctalCode, endNodes);
|
||||
}
|
||||
|
||||
JurisdictionMap::~JurisdictionMap() {
|
||||
}
|
||||
|
||||
JurisdictionMap::JurisdictionMap(NodeType_t type) : _rootOctalCode(nullptr) {
|
||||
_nodeType = type;
|
||||
OctalCodePtr rootCode = createOctalCodePtr(1);
|
||||
*rootCode = 0;
|
||||
|
||||
OctalCodePtrList emptyEndNodes;
|
||||
init(rootCode, emptyEndNodes);
|
||||
}
|
||||
|
||||
JurisdictionMap::JurisdictionMap(const char* filename) : _rootOctalCode(nullptr) {
|
||||
readFromFile(filename);
|
||||
}
|
||||
|
||||
JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHexCodes) {
|
||||
|
||||
qCDebug(octree, "JurisdictionMap::JurisdictionMap(const char* rootHexCode=[%p] %s, const char* endNodesHexCodes=[%p] %s)",
|
||||
rootHexCode, rootHexCode, endNodesHexCodes, endNodesHexCodes);
|
||||
|
||||
_rootOctalCode = hexStringToOctalCode(QString(rootHexCode));
|
||||
|
||||
qCDebug(octree, "JurisdictionMap::JurisdictionMap() _rootOctalCode=%p octalCode=", _rootOctalCode.get());
|
||||
myDebugPrintOctalCode(_rootOctalCode.get(), true);
|
||||
|
||||
QString endNodesHexStrings(endNodesHexCodes);
|
||||
QString delimiterPattern(",");
|
||||
QStringList endNodeList = endNodesHexStrings.split(delimiterPattern);
|
||||
|
||||
for (int i = 0; i < endNodeList.size(); i++) {
|
||||
QString endNodeHexString = endNodeList.at(i);
|
||||
|
||||
auto endNodeOctcode = hexStringToOctalCode(endNodeHexString);
|
||||
|
||||
qCDebug(octree, "JurisdictionMap::JurisdictionMap() endNodeList(%d)=%s",
|
||||
i, endNodeHexString.toLocal8Bit().constData());
|
||||
|
||||
//printOctalCode(endNodeOctcode);
|
||||
_endNodes.push_back(endNodeOctcode);
|
||||
|
||||
qCDebug(octree, "JurisdictionMap::JurisdictionMap() endNodeOctcode=%p octalCode=", endNodeOctcode.get());
|
||||
myDebugPrintOctalCode(endNodeOctcode.get(), true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<OctalCodePtr, OctalCodePtrList> JurisdictionMap::getRootAndEndNodeOctalCodes() const {
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
return std::tuple<OctalCodePtr, OctalCodePtrList>(_rootOctalCode, _endNodes);
|
||||
}
|
||||
|
||||
OctalCodePtr JurisdictionMap::getRootOctalCode() const {
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
return _rootOctalCode;
|
||||
}
|
||||
|
||||
OctalCodePtrList JurisdictionMap::getEndNodeOctalCodes() const {
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
return _endNodes;
|
||||
}
|
||||
|
||||
void JurisdictionMap::init(OctalCodePtr rootOctalCode, const OctalCodePtrList& endNodes) {
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
_rootOctalCode = rootOctalCode;
|
||||
_endNodes = endNodes;
|
||||
}
|
||||
|
||||
JurisdictionMap::Area JurisdictionMap::isMyJurisdiction(const unsigned char* nodeOctalCode, int childIndex) const {
|
||||
// to be in our jurisdiction, we must be under the root...
|
||||
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
|
||||
// if the node is an ancestor of my root, then we return ABOVE
|
||||
if (isAncestorOf(nodeOctalCode, _rootOctalCode.get())) {
|
||||
return ABOVE;
|
||||
}
|
||||
|
||||
// otherwise...
|
||||
bool isInJurisdiction = isAncestorOf(_rootOctalCode.get(), nodeOctalCode, childIndex);
|
||||
// if we're under the root, then we can't be under any of the endpoints
|
||||
if (isInJurisdiction) {
|
||||
for (size_t i = 0; i < _endNodes.size(); i++) {
|
||||
bool isUnderEndNode = isAncestorOf(_endNodes[i].get(), nodeOctalCode);
|
||||
if (isUnderEndNode) {
|
||||
isInJurisdiction = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isInJurisdiction ? WITHIN : BELOW;
|
||||
}
|
||||
|
||||
|
||||
bool JurisdictionMap::readFromFile(const char* filename) {
|
||||
QString settingsFile(filename);
|
||||
QSettings settings(settingsFile, QSettings::IniFormat);
|
||||
QString rootCode = settings.value("root","00").toString();
|
||||
qCDebug(octree) << "rootCode=" << rootCode;
|
||||
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
_rootOctalCode = hexStringToOctalCode(rootCode);
|
||||
printOctalCode(_rootOctalCode.get());
|
||||
|
||||
settings.beginGroup("endNodes");
|
||||
const QStringList childKeys = settings.childKeys();
|
||||
QHash<QString, QString> values;
|
||||
foreach (const QString &childKey, childKeys) {
|
||||
QString childValue = settings.value(childKey).toString();
|
||||
values.insert(childKey, childValue);
|
||||
qCDebug(octree) << childKey << "=" << childValue;
|
||||
|
||||
auto octcode = hexStringToOctalCode(childValue);
|
||||
printOctalCode(octcode.get());
|
||||
|
||||
_endNodes.push_back(octcode);
|
||||
}
|
||||
settings.endGroup();
|
||||
return true;
|
||||
}
|
||||
|
||||
void JurisdictionMap::displayDebugDetails() const {
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
|
||||
QString rootNodeValue = octalCodeToHexString(_rootOctalCode.get());
|
||||
|
||||
qCDebug(octree) << "root:" << rootNodeValue;
|
||||
|
||||
for (size_t i = 0; i < _endNodes.size(); i++) {
|
||||
QString value = octalCodeToHexString(_endNodes[i].get());
|
||||
qCDebug(octree) << "End node[" << i << "]: " << rootNodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool JurisdictionMap::writeToFile(const char* filename) {
|
||||
QString settingsFile(filename);
|
||||
QSettings settings(settingsFile, QSettings::IniFormat);
|
||||
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
|
||||
QString rootNodeValue = octalCodeToHexString(_rootOctalCode.get());
|
||||
|
||||
settings.setValue("root", rootNodeValue);
|
||||
|
||||
settings.beginGroup("endNodes");
|
||||
for (size_t i = 0; i < _endNodes.size(); i++) {
|
||||
QString key = QString("endnode%1").arg(i);
|
||||
QString value = octalCodeToHexString(_endNodes[i].get());
|
||||
settings.setValue(key, value);
|
||||
}
|
||||
settings.endGroup();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<NLPacket> JurisdictionMap::packEmptyJurisdictionIntoMessage(NodeType_t type) {
|
||||
int bytes = 0;
|
||||
auto packet = NLPacket::create(PacketType::Jurisdiction, sizeof(type) + sizeof(bytes));
|
||||
|
||||
// Pack the Node Type in first byte
|
||||
packet->writePrimitive(type);
|
||||
// No root or end node details to pack!
|
||||
packet->writePrimitive(bytes);
|
||||
|
||||
return packet; // includes header!
|
||||
}
|
||||
|
||||
std::unique_ptr<NLPacket> JurisdictionMap::packIntoPacket() {
|
||||
auto packet = NLPacket::create(PacketType::Jurisdiction);
|
||||
|
||||
// Pack the Node Type in first byte
|
||||
NodeType_t type = getNodeType();
|
||||
packet->writePrimitive(type);
|
||||
|
||||
// add the root jurisdiction
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
if (_rootOctalCode) {
|
||||
size_t bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_rootOctalCode.get()));
|
||||
// No root or end node details to pack!
|
||||
packet->writePrimitive(bytes);
|
||||
packet->write(reinterpret_cast<char*>(_rootOctalCode.get()), bytes);
|
||||
|
||||
// if and only if there's a root jurisdiction, also include the end nodes
|
||||
int endNodeCount = (int)_endNodes.size();
|
||||
packet->writePrimitive(endNodeCount);
|
||||
|
||||
for (int i=0; i < endNodeCount; i++) {
|
||||
auto endNodeCode = _endNodes[i].get();
|
||||
size_t bytes = 0;
|
||||
if (endNodeCode) {
|
||||
bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode));
|
||||
}
|
||||
packet->writePrimitive(bytes);
|
||||
packet->write(reinterpret_cast<char*>(endNodeCode), bytes);
|
||||
}
|
||||
} else {
|
||||
int bytes = 0;
|
||||
packet->writePrimitive(bytes);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
int JurisdictionMap::unpackFromPacket(ReceivedMessage& message) {
|
||||
// read the root jurisdiction
|
||||
int bytes = 0;
|
||||
message.readPrimitive(&bytes);
|
||||
|
||||
std::lock_guard<std::mutex> lock(_octalCodeMutex);
|
||||
_rootOctalCode = nullptr;
|
||||
_endNodes.clear();
|
||||
|
||||
if (bytes > 0 && bytes <= message.getBytesLeftToRead()) {
|
||||
_rootOctalCode = createOctalCodePtr(bytes);
|
||||
message.read(reinterpret_cast<char*>(_rootOctalCode.get()), bytes);
|
||||
|
||||
// if and only if there's a root jurisdiction, also include the end nodes
|
||||
int endNodeCount = 0;
|
||||
message.readPrimitive(&endNodeCount);
|
||||
|
||||
for (int i = 0; i < endNodeCount; i++) {
|
||||
int bytes = 0;
|
||||
message.readPrimitive(&bytes);
|
||||
|
||||
if (bytes <= message.getBytesLeftToRead()) {
|
||||
auto endNodeCode = createOctalCodePtr(bytes);
|
||||
message.read(reinterpret_cast<char*>(endNodeCode.get()), bytes);
|
||||
|
||||
// if the endNodeCode was 0 length then don't add it
|
||||
if (bytes > 0) {
|
||||
_endNodes.push_back(endNodeCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return message.getPosition(); // excludes header
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
//
|
||||
// JurisdictionMap.h
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/1/13.
|
||||
// Copyright 2013 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_JurisdictionMap_h
|
||||
#define hifi_JurisdictionMap_h
|
||||
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include <NLPacket.h>
|
||||
#include <Node.h>
|
||||
#include <OctalCode.h>
|
||||
|
||||
class JurisdictionMap {
|
||||
public:
|
||||
enum Area {
|
||||
ABOVE,
|
||||
WITHIN,
|
||||
BELOW
|
||||
};
|
||||
|
||||
// standard constructors
|
||||
JurisdictionMap(NodeType_t type = NodeType::EntityServer); // default constructor
|
||||
JurisdictionMap(const JurisdictionMap& other); // copy constructor
|
||||
|
||||
// standard assignment
|
||||
JurisdictionMap& operator=(const JurisdictionMap& other); // copy assignment
|
||||
|
||||
// application constructors
|
||||
JurisdictionMap(const char* filename);
|
||||
JurisdictionMap(const char* rootHextString, const char* endNodesHextString);
|
||||
|
||||
~JurisdictionMap();
|
||||
|
||||
Area isMyJurisdiction(const unsigned char* nodeOctalCode, int childIndex) const;
|
||||
|
||||
bool writeToFile(const char* filename);
|
||||
bool readFromFile(const char* filename);
|
||||
|
||||
// Provide an atomic way to get both the rootOctalCode and endNodeOctalCodes.
|
||||
std::tuple<OctalCodePtr, OctalCodePtrList> getRootAndEndNodeOctalCodes() const;
|
||||
OctalCodePtr getRootOctalCode() const;
|
||||
OctalCodePtrList getEndNodeOctalCodes() const;
|
||||
|
||||
void copyContents(const OctalCodePtr& rootCodeIn, const OctalCodePtrList& endNodesIn);
|
||||
|
||||
int unpackFromPacket(ReceivedMessage& message);
|
||||
std::unique_ptr<NLPacket> packIntoPacket();
|
||||
|
||||
/// Available to pack an empty or unknown jurisdiction into a network packet, used when no JurisdictionMap is available
|
||||
static std::unique_ptr<NLPacket> packEmptyJurisdictionIntoMessage(NodeType_t type);
|
||||
|
||||
void displayDebugDetails() const;
|
||||
|
||||
NodeType_t getNodeType() const { return _nodeType; }
|
||||
void setNodeType(NodeType_t type) { _nodeType = type; }
|
||||
|
||||
private:
|
||||
void copyContents(const JurisdictionMap& other); // use assignment instead
|
||||
void init(OctalCodePtr rootOctalCode, const OctalCodePtrList& endNodes);
|
||||
|
||||
mutable std::mutex _octalCodeMutex;
|
||||
OctalCodePtr _rootOctalCode { nullptr };
|
||||
OctalCodePtrList _endNodes;
|
||||
NodeType_t _nodeType;
|
||||
};
|
||||
|
||||
/// Map between node IDs and their reported JurisdictionMap. Typically used by classes that need to know which nodes are
|
||||
/// managing which jurisdictions.
|
||||
class NodeToJurisdictionMap : public QMap<QUuid, JurisdictionMap>, public ReadWriteLockable {};
|
||||
typedef QMap<QUuid, JurisdictionMap>::iterator NodeToJurisdictionMapIterator;
|
||||
|
||||
|
||||
#endif // hifi_JurisdictionMap_h
|
|
@ -1,68 +0,0 @@
|
|||
//
|
||||
// JurisdictionSender.cpp
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 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 <PerfStat.h>
|
||||
|
||||
#include <OctalCode.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include "JurisdictionSender.h"
|
||||
|
||||
|
||||
JurisdictionSender::JurisdictionSender(JurisdictionMap* map, NodeType_t type) :
|
||||
ReceivedPacketProcessor(),
|
||||
_jurisdictionMap(map),
|
||||
_nodeType(type),
|
||||
_packetSender(JurisdictionSender::DEFAULT_PACKETS_PER_SECOND)
|
||||
{
|
||||
}
|
||||
|
||||
JurisdictionSender::~JurisdictionSender() {
|
||||
}
|
||||
|
||||
void JurisdictionSender::processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
if (message->getType() == PacketType::JurisdictionRequest) {
|
||||
lockRequestingNodes();
|
||||
_nodesRequestingJurisdictions.push(sendingNode->getUUID());
|
||||
unlockRequestingNodes();
|
||||
}
|
||||
}
|
||||
|
||||
bool JurisdictionSender::process() {
|
||||
bool continueProcessing = isStillRunning();
|
||||
|
||||
// call our ReceivedPacketProcessor base class process so we'll get any pending packets
|
||||
if (continueProcessing && (continueProcessing = ReceivedPacketProcessor::process())) {
|
||||
int nodeCount = 0;
|
||||
|
||||
lockRequestingNodes();
|
||||
while (!_nodesRequestingJurisdictions.empty()) {
|
||||
|
||||
QUuid nodeUUID = _nodesRequestingJurisdictions.front();
|
||||
_nodesRequestingJurisdictions.pop();
|
||||
SharedNodePointer node = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);
|
||||
|
||||
if (node && node->getActiveSocket()) {
|
||||
auto packet = (_jurisdictionMap) ? _jurisdictionMap->packIntoPacket()
|
||||
: JurisdictionMap::packEmptyJurisdictionIntoMessage(getNodeType());
|
||||
_packetSender.queuePacketForSending(node, std::move(packet));
|
||||
nodeCount++;
|
||||
}
|
||||
}
|
||||
unlockRequestingNodes();
|
||||
|
||||
// set our packets per second to be the number of nodes
|
||||
_packetSender.setPacketsPerSecond(nodeCount);
|
||||
|
||||
continueProcessing = _packetSender.process();
|
||||
}
|
||||
return continueProcessing;
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// JurisdictionSender.h
|
||||
// libraries/octree/src
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/12/13.
|
||||
// Copyright 2013 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_JurisdictionSender_h
|
||||
#define hifi_JurisdictionSender_h
|
||||
|
||||
#include <queue>
|
||||
#include <QMutex>
|
||||
|
||||
#include <PacketSender.h>
|
||||
#include <ReceivedPacketProcessor.h>
|
||||
#include "JurisdictionMap.h"
|
||||
|
||||
/// Will process PacketType::_JURISDICTION_REQUEST packets and send out PacketType::_JURISDICTION packets
|
||||
/// to requesting parties. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
|
||||
/// and adding them to the processing queue by calling queueReceivedPacket()
|
||||
class JurisdictionSender : public ReceivedPacketProcessor {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const int DEFAULT_PACKETS_PER_SECOND = 1;
|
||||
|
||||
JurisdictionSender(JurisdictionMap* map, NodeType_t type = NodeType::EntityServer);
|
||||
~JurisdictionSender();
|
||||
|
||||
void setJurisdiction(JurisdictionMap* map) { _jurisdictionMap = map; }
|
||||
|
||||
virtual bool process() override;
|
||||
|
||||
NodeType_t getNodeType() const { return _nodeType; }
|
||||
void setNodeType(NodeType_t type) { _nodeType = type; }
|
||||
|
||||
protected:
|
||||
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
|
||||
|
||||
/// Locks all the resources of the thread.
|
||||
void lockRequestingNodes() { _requestingNodeMutex.lock(); }
|
||||
|
||||
/// Unlocks all the resources of the thread.
|
||||
void unlockRequestingNodes() { _requestingNodeMutex.unlock(); }
|
||||
|
||||
|
||||
private:
|
||||
QMutex _requestingNodeMutex;
|
||||
JurisdictionMap* _jurisdictionMap;
|
||||
std::queue<QUuid> _nodesRequestingJurisdictions;
|
||||
NodeType_t _nodeType;
|
||||
|
||||
PacketSender _packetSender;
|
||||
};
|
||||
#endif // hifi_JurisdictionSender_h
|
|
@ -1020,16 +1020,6 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
// If we've been provided a jurisdiction map, then we need to honor it.
|
||||
if (params.jurisdictionMap) {
|
||||
// here's how it works... if we're currently above our root jurisdiction, then we proceed normally.
|
||||
// but once we're in our own jurisdiction, then we need to make sure we're not below it.
|
||||
if (JurisdictionMap::BELOW == params.jurisdictionMap->isMyJurisdiction(element->getOctalCode(), CHECK_NODE_ONLY)) {
|
||||
params.stopReason = EncodeBitstreamParams::OUT_OF_JURISDICTION;
|
||||
return bytesAtThisLevel;
|
||||
}
|
||||
}
|
||||
|
||||
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
|
||||
if (octreeQueryNode->getUsesFrustum() && !params.recurseEverything) {
|
||||
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
|
||||
|
@ -1152,18 +1142,9 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
|
|||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
OctreeElementPointer childElement = element->getChildAtIndex(i);
|
||||
|
||||
// if the caller wants to include childExistsBits, then include them even if not in view, if however,
|
||||
// we're in a portion of the tree that's not our responsibility, then we assume the child nodes exist
|
||||
// even if they don't in our local tree
|
||||
bool notMyJurisdiction = false;
|
||||
if (params.jurisdictionMap) {
|
||||
notMyJurisdiction = JurisdictionMap::WITHIN != params.jurisdictionMap->isMyJurisdiction(element->getOctalCode(), i);
|
||||
}
|
||||
if (params.includeExistsBits) {
|
||||
// If the child is known to exist, OR, it's not my jurisdiction, then we mark the bit as existing
|
||||
if (childElement || notMyJurisdiction) {
|
||||
childrenExistInTreeBits += (1 << (7 - i));
|
||||
}
|
||||
// if the caller wants to include childExistsBits, then include them
|
||||
if (params.includeExistsBits && childElement) {
|
||||
childrenExistInTreeBits += (1 << (7 - i));
|
||||
}
|
||||
|
||||
sortedChildren[i] = childElement;
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "JurisdictionMap.h"
|
||||
#include "OctreeElement.h"
|
||||
#include "OctreeElementBag.h"
|
||||
#include "OctreePacketData.h"
|
||||
|
@ -62,7 +61,6 @@ const int NO_BOUNDARY_ADJUST = 0;
|
|||
const int LOW_RES_MOVING_ADJUST = 1;
|
||||
|
||||
#define IGNORE_COVERAGE_MAP NULL
|
||||
#define IGNORE_JURISDICTION_MAP NULL
|
||||
|
||||
class EncodeBitstreamParams {
|
||||
public:
|
||||
|
@ -77,7 +75,6 @@ public:
|
|||
int boundaryLevelAdjust;
|
||||
float octreeElementSizeScale;
|
||||
bool forceSendScene;
|
||||
JurisdictionMap* jurisdictionMap;
|
||||
NodeData* nodeData;
|
||||
|
||||
// output hints from the encode process
|
||||
|
@ -87,7 +84,6 @@ public:
|
|||
NULL_NODE,
|
||||
NULL_NODE_DATA,
|
||||
TOO_DEEP,
|
||||
OUT_OF_JURISDICTION,
|
||||
LOD_SKIP,
|
||||
OUT_OF_VIEW,
|
||||
WAS_IN_VIEW,
|
||||
|
@ -105,7 +101,6 @@ public:
|
|||
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
|
||||
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
|
||||
bool forceSendScene = true,
|
||||
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP,
|
||||
NodeData* nodeData = nullptr) :
|
||||
maxEncodeLevel(maxEncodeLevel),
|
||||
maxLevelReached(0),
|
||||
|
@ -115,7 +110,6 @@ public:
|
|||
boundaryLevelAdjust(boundaryLevelAdjust),
|
||||
octreeElementSizeScale(octreeElementSizeScale),
|
||||
forceSendScene(forceSendScene),
|
||||
jurisdictionMap(jurisdictionMap),
|
||||
nodeData(nodeData),
|
||||
stopReason(UNKNOWN)
|
||||
{
|
||||
|
@ -131,7 +125,6 @@ public:
|
|||
case DIDNT_FIT: qDebug("DIDNT_FIT"); break;
|
||||
case NULL_NODE: qDebug("NULL_NODE"); break;
|
||||
case TOO_DEEP: qDebug("TOO_DEEP"); break;
|
||||
case OUT_OF_JURISDICTION: qDebug("OUT_OF_JURISDICTION"); break;
|
||||
case LOD_SKIP: qDebug("LOD_SKIP"); break;
|
||||
case OUT_OF_VIEW: qDebug("OUT_OF_VIEW"); break;
|
||||
case WAS_IN_VIEW: qDebug("WAS_IN_VIEW"); break;
|
||||
|
@ -148,7 +141,6 @@ public:
|
|||
case DIDNT_FIT: return QString("DIDNT_FIT"); break;
|
||||
case NULL_NODE: return QString("NULL_NODE"); break;
|
||||
case TOO_DEEP: return QString("TOO_DEEP"); break;
|
||||
case OUT_OF_JURISDICTION: return QString("OUT_OF_JURISDICTION"); break;
|
||||
case LOD_SKIP: return QString("LOD_SKIP"); break;
|
||||
case OUT_OF_VIEW: return QString("OUT_OF_VIEW"); break;
|
||||
case WAS_IN_VIEW: return QString("WAS_IN_VIEW"); break;
|
||||
|
|
|
@ -22,13 +22,10 @@ const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::D
|
|||
|
||||
|
||||
OctreeEditPacketSender::OctreeEditPacketSender() :
|
||||
PacketSender(),
|
||||
_shouldSend(true),
|
||||
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
|
||||
_releaseQueuedMessagesPending(false),
|
||||
_serverJurisdictions(NULL)
|
||||
_releaseQueuedMessagesPending(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OctreeEditPacketSender::~OctreeEditPacketSender() {
|
||||
|
@ -40,34 +37,8 @@ OctreeEditPacketSender::~OctreeEditPacketSender() {
|
|||
|
||||
|
||||
bool OctreeEditPacketSender::serversExist() const {
|
||||
bool hasServers = false;
|
||||
bool atLeastOneJurisdictionMissing = false; // assume the best
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNodeBreakable([&](const SharedNodePointer& node){
|
||||
if (node->getType() == getMyNodeType() && node->getActiveSocket()) {
|
||||
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
// If we've got Jurisdictions set, then check to see if we know the jurisdiction for this server
|
||||
if (_serverJurisdictions) {
|
||||
// lookup our nodeUUID in the jurisdiction map, if it's missing then we're
|
||||
// missing at least one jurisdiction
|
||||
_serverJurisdictions->withReadLock([&] {
|
||||
if ((*_serverJurisdictions).find(nodeUUID) == (*_serverJurisdictions).end()) {
|
||||
atLeastOneJurisdictionMissing = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
hasServers = true;
|
||||
}
|
||||
|
||||
if (atLeastOneJurisdictionMissing) {
|
||||
return false; // no point in looking further - return false from anonymous function
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return (hasServers && !atLeastOneJurisdictionMissing);
|
||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
|
||||
return node && node->getActiveSocket();
|
||||
}
|
||||
|
||||
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
|
||||
|
@ -132,7 +103,7 @@ void OctreeEditPacketSender::queuePacketListToNode(const QUuid& nodeUUID, std::u
|
|||
}
|
||||
|
||||
void OctreeEditPacketSender::processPreServerExistsPackets() {
|
||||
assert(serversExist()); // we should only be here if we have jurisdictions
|
||||
assert(serversExist()); // we should only be here if we have servers
|
||||
|
||||
// First send out all the single message packets...
|
||||
_pendingPacketsLock.lock();
|
||||
|
@ -150,7 +121,7 @@ void OctreeEditPacketSender::processPreServerExistsPackets() {
|
|||
|
||||
_pendingPacketsLock.unlock();
|
||||
|
||||
// if while waiting for the jurisdictions the caller called releaseQueuedMessages()
|
||||
// if while waiting for the servers the caller called releaseQueuedMessages()
|
||||
// then we want to honor that request now.
|
||||
if (_releaseQueuedMessagesPending) {
|
||||
releaseQueuedMessages();
|
||||
|
@ -178,34 +149,12 @@ void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet
|
|||
return; // bail early
|
||||
}
|
||||
|
||||
assert(serversExist()); // we must have jurisdictions to be here!!
|
||||
assert(serversExist()); // we must have servers to be here!!
|
||||
|
||||
const unsigned char* octCode = reinterpret_cast<unsigned char*>(packet->getPayload()) + sizeof(short) + sizeof(quint64);
|
||||
|
||||
// We want to filter out edit messages for servers based on the server's Jurisdiction
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// for a different server... So we need to actually manage multiple queued packets... one
|
||||
// for each server
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node){
|
||||
// only send to the NodeTypes that are getMyNodeType()
|
||||
if (node->getActiveSocket() && node->getType() == getMyNodeType()) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
bool isMyJurisdiction = true;
|
||||
// we need to get the jurisdiction for this
|
||||
// here we need to get the "pending packet" for this server
|
||||
_serverJurisdictions->withReadLock([&] {
|
||||
const JurisdictionMap& map = (*_serverJurisdictions)[nodeUUID];
|
||||
isMyJurisdiction = (map.isMyJurisdiction(octCode, CHECK_NODE_ONLY) == JurisdictionMap::WITHIN);
|
||||
});
|
||||
|
||||
if (isMyJurisdiction) {
|
||||
// make a copy of this packet for this node and queue
|
||||
auto packetCopy = NLPacket::createCopy(*packet);
|
||||
queuePacketToNode(nodeUUID, std::move(packetCopy));
|
||||
}
|
||||
}
|
||||
});
|
||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
|
||||
if (node && node->getActiveSocket()) {
|
||||
queuePacketToNode(node->getUUID(), std::move(packet));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -216,8 +165,8 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray&
|
|||
return; // bail early
|
||||
}
|
||||
|
||||
// If we don't have jurisdictions, then we will simply queue up all of these packets and wait till we have
|
||||
// jurisdictions for processing
|
||||
// If we don't have servers, then we will simply queue up all of these packets and wait till we have
|
||||
// servers for processing
|
||||
if (!serversExist()) {
|
||||
if (_maxPendingMessages > 0) {
|
||||
EditMessagePair messagePair { type, QByteArray(editMessage) };
|
||||
|
@ -235,107 +184,80 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray&
|
|||
return; // bail early
|
||||
}
|
||||
|
||||
// We want to filter out edit messages for servers based on the server's Jurisdiction
|
||||
// But we can't really do that with a packed message, since each edit message could be destined
|
||||
// for a different server... So we need to actually manage multiple queued packets... one
|
||||
// for each server
|
||||
_packetsQueueLock.lock();
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& node){
|
||||
// only send to the NodeTypes that are getMyNodeType()
|
||||
if (node->getActiveSocket() && node->getType() == getMyNodeType()) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
bool isMyJurisdiction = true;
|
||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
|
||||
if (node && node->getActiveSocket()) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
|
||||
if (type == PacketType::EntityErase) {
|
||||
isMyJurisdiction = true; // send erase messages to all servers
|
||||
} else if (_serverJurisdictions) {
|
||||
// we need to get the jurisdiction for this
|
||||
// here we need to get the "pending packet" for this server
|
||||
_serverJurisdictions->withReadLock([&] {
|
||||
if ((*_serverJurisdictions).find(nodeUUID) != (*_serverJurisdictions).end()) {
|
||||
const JurisdictionMap& map = (*_serverJurisdictions)[nodeUUID];
|
||||
isMyJurisdiction = (map.isMyJurisdiction(reinterpret_cast<const unsigned char*>(editMessage.data()),
|
||||
CHECK_NODE_ONLY) == JurisdictionMap::WITHIN);
|
||||
} else {
|
||||
isMyJurisdiction = false;
|
||||
}
|
||||
});
|
||||
// for edit messages, we will attempt to combine multiple edit commands where possible, we
|
||||
// don't do this for add because we send those reliably
|
||||
if (type == PacketType::EntityAdd) {
|
||||
auto newPacket = NLPacketList::create(type, QByteArray(), true, true);
|
||||
auto nodeClockSkew = node->getClockSkewUsec();
|
||||
|
||||
// pack sequence number
|
||||
quint16 sequence = _outgoingSequenceNumbers[nodeUUID]++;
|
||||
newPacket->writePrimitive(sequence);
|
||||
|
||||
// pack in timestamp
|
||||
quint64 now = usecTimestampNow() + nodeClockSkew;
|
||||
newPacket->writePrimitive(now);
|
||||
|
||||
|
||||
// We call this virtual function that allows our specific type of EditPacketSender to
|
||||
// fixup the buffer for any clock skew
|
||||
if (nodeClockSkew != 0) {
|
||||
adjustEditPacketForClockSkew(type, editMessage, nodeClockSkew);
|
||||
}
|
||||
if (isMyJurisdiction) {
|
||||
|
||||
// for edit messages, we will attempt to combine multiple edit commands where possible, we
|
||||
// don't do this for add because we send those reliably
|
||||
if (type == PacketType::EntityAdd) {
|
||||
newPacket->write(editMessage);
|
||||
|
||||
auto newPacket = NLPacketList::create(type, QByteArray(), true, true);
|
||||
auto nodeClockSkew = node->getClockSkewUsec();
|
||||
// release the new packet
|
||||
releaseQueuedPacketList(nodeUUID, std::move(newPacket));
|
||||
|
||||
// pack sequence number
|
||||
quint16 sequence = _outgoingSequenceNumbers[nodeUUID]++;
|
||||
newPacket->writePrimitive(sequence);
|
||||
// tell the sent packet history that we used a sequence number for an untracked packet
|
||||
auto& sentPacketHistory = _sentPacketHistories[nodeUUID];
|
||||
sentPacketHistory.untrackedPacketSent(sequence);
|
||||
} else {
|
||||
// only a NLPacket for now
|
||||
std::unique_ptr<NLPacket>& bufferedPacket = _pendingEditPackets[nodeUUID].first;
|
||||
|
||||
// pack in timestamp
|
||||
quint64 now = usecTimestampNow() + nodeClockSkew;
|
||||
newPacket->writePrimitive(now);
|
||||
if (!bufferedPacket) {
|
||||
bufferedPacket = initializePacket(type, node->getClockSkewUsec());
|
||||
} else {
|
||||
// If we're switching type, then we send the last one and start over
|
||||
if ((type != bufferedPacket->getType() && bufferedPacket->getPayloadSize() > 0) ||
|
||||
(editMessage.size() >= bufferedPacket->bytesAvailableForWrite())) {
|
||||
|
||||
// create the new packet and swap it with the packet in _pendingEditPackets
|
||||
auto packetToRelease = initializePacket(type, node->getClockSkewUsec());
|
||||
bufferedPacket.swap(packetToRelease);
|
||||
|
||||
// We call this virtual function that allows our specific type of EditPacketSender to
|
||||
// fixup the buffer for any clock skew
|
||||
if (nodeClockSkew != 0) {
|
||||
adjustEditPacketForClockSkew(type, editMessage, nodeClockSkew);
|
||||
}
|
||||
|
||||
newPacket->write(editMessage);
|
||||
|
||||
// release the new packet
|
||||
releaseQueuedPacketList(nodeUUID, std::move(newPacket));
|
||||
|
||||
// tell the sent packet history that we used a sequence number for an untracked packet
|
||||
auto& sentPacketHistory = _sentPacketHistories[nodeUUID];
|
||||
sentPacketHistory.untrackedPacketSent(sequence);
|
||||
} else {
|
||||
|
||||
std::unique_ptr<NLPacket>& bufferedPacket = _pendingEditPackets[nodeUUID].first; //only a NLPacket for now
|
||||
|
||||
if (!bufferedPacket) {
|
||||
bufferedPacket = initializePacket(type, node->getClockSkewUsec());
|
||||
} else {
|
||||
// If we're switching type, then we send the last one and start over
|
||||
if ((type != bufferedPacket->getType() && bufferedPacket->getPayloadSize() > 0) ||
|
||||
(editMessage.size() >= bufferedPacket->bytesAvailableForWrite())) {
|
||||
|
||||
// create the new packet and swap it with the packet in _pendingEditPackets
|
||||
auto packetToRelease = initializePacket(type, node->getClockSkewUsec());
|
||||
bufferedPacket.swap(packetToRelease);
|
||||
|
||||
// release the previously buffered packet
|
||||
releaseQueuedPacket(nodeUUID, std::move(packetToRelease));
|
||||
}
|
||||
}
|
||||
|
||||
// This is really the first time we know which server/node this particular edit message
|
||||
// is going to, so we couldn't adjust for clock skew till now. But here's our chance.
|
||||
// We call this virtual function that allows our specific type of EditPacketSender to
|
||||
// fixup the buffer for any clock skew
|
||||
if (node->getClockSkewUsec() != 0) {
|
||||
adjustEditPacketForClockSkew(type, editMessage, node->getClockSkewUsec());
|
||||
}
|
||||
|
||||
bufferedPacket->write(editMessage);
|
||||
|
||||
// release the previously buffered packet
|
||||
releaseQueuedPacket(nodeUUID, std::move(packetToRelease));
|
||||
}
|
||||
}
|
||||
|
||||
// This is really the first time we know which server/node this particular edit message
|
||||
// is going to, so we couldn't adjust for clock skew till now. But here's our chance.
|
||||
// We call this virtual function that allows our specific type of EditPacketSender to
|
||||
// fixup the buffer for any clock skew
|
||||
if (node->getClockSkewUsec() != 0) {
|
||||
adjustEditPacketForClockSkew(type, editMessage, node->getClockSkewUsec());
|
||||
}
|
||||
|
||||
bufferedPacket->write(editMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_packetsQueueLock.unlock();
|
||||
|
||||
}
|
||||
|
||||
void OctreeEditPacketSender::releaseQueuedMessages() {
|
||||
// if we don't yet have jurisdictions then we can't actually release messages yet because we don't
|
||||
// know where to send them to. Instead, just remember this request and when we eventually get jurisdictions
|
||||
// if we don't yet have servers then we can't actually release messages yet because we don't
|
||||
// know where to send them to. Instead, just remember this request and when we eventually get servers
|
||||
// call release again at that time.
|
||||
if (!serversExist()) {
|
||||
_releaseQueuedMessagesPending = true;
|
||||
|
@ -397,8 +319,8 @@ std::unique_ptr<NLPacket> OctreeEditPacketSender::initializePacket(PacketType ty
|
|||
}
|
||||
|
||||
bool OctreeEditPacketSender::process() {
|
||||
// if we have server jurisdiction details, and we have pending pre-jurisdiction packets, then process those
|
||||
// before doing our normal process step. This processPreJurisdictionPackets()
|
||||
// if we have servers, and we have pending pre-servers exist packets, then process those
|
||||
// before doing our normal process step. This processPreServerExistPackets()
|
||||
if (serversExist() && (!_preServerEdits.empty() || !_preServerSingleMessagePackets.empty() )) {
|
||||
processPreServerExistsPackets();
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <PacketSender.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
||||
#include "JurisdictionMap.h"
|
||||
#include "SentPacketHistory.h"
|
||||
|
||||
/// Utility for processing, packing, queueing and sending of outbound edit messages.
|
||||
|
@ -49,14 +48,6 @@ public:
|
|||
/// in an application like interface when all octree features are disabled.
|
||||
void setShouldSend(bool shouldSend) { _shouldSend = shouldSend; }
|
||||
|
||||
/// call this to inform the OctreeEditPacketSender of the server jurisdictions. This is required for normal operation.
|
||||
/// The internal contents of the jurisdiction map may change throughout the lifetime of the OctreeEditPacketSender. This map
|
||||
/// can be set prior to servers being present, so long as the contents of the map accurately reflect the current
|
||||
/// known jurisdictions.
|
||||
void setServerJurisdictions(NodeToJurisdictionMap* serverJurisdictions) {
|
||||
_serverJurisdictions = serverJurisdictions;
|
||||
}
|
||||
|
||||
/// if you're running in non-threaded mode, you must call this method regularly
|
||||
virtual bool process() override;
|
||||
|
||||
|
@ -108,8 +99,6 @@ protected:
|
|||
std::list<EditMessagePair> _preServerEdits; // these will get packed into other larger packets
|
||||
std::list<std::unique_ptr<NLPacket>> _preServerSingleMessagePackets; // these will go out as is
|
||||
|
||||
NodeToJurisdictionMap* _serverJurisdictions;
|
||||
|
||||
QMutex _releaseQueuedPacketMutex;
|
||||
|
||||
// TODO: add locks for this and _pendingEditPackets
|
||||
|
|
|
@ -37,15 +37,13 @@ OctreeSceneStats::OctreeSceneStats() :
|
|||
_incomingBytes(0),
|
||||
_incomingWastedBytes(0),
|
||||
_incomingOctreeSequenceNumberStats(),
|
||||
_incomingFlightTimeAverage(samples),
|
||||
_jurisdictionRoot(NULL)
|
||||
_incomingFlightTimeAverage(samples)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
// copy constructor
|
||||
OctreeSceneStats::OctreeSceneStats(const OctreeSceneStats& other) :
|
||||
_jurisdictionRoot(NULL) {
|
||||
OctreeSceneStats::OctreeSceneStats(const OctreeSceneStats& other) {
|
||||
copyFromOther(other);
|
||||
}
|
||||
|
||||
|
@ -109,26 +107,6 @@ void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) {
|
|||
_existsInPacketBitsWritten = other._existsInPacketBitsWritten;
|
||||
_treesRemoved = other._treesRemoved;
|
||||
|
||||
// before copying the jurisdictions, delete any current values...
|
||||
_jurisdictionRoot = nullptr;
|
||||
_jurisdictionEndNodes.clear();
|
||||
|
||||
// Now copy the values from the other
|
||||
if (other._jurisdictionRoot) {
|
||||
auto bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(other._jurisdictionRoot.get()));
|
||||
_jurisdictionRoot = createOctalCodePtr(bytes);
|
||||
memcpy(_jurisdictionRoot.get(), other._jurisdictionRoot.get(), bytes);
|
||||
}
|
||||
for (size_t i = 0; i < other._jurisdictionEndNodes.size(); i++) {
|
||||
auto& endNodeCode = other._jurisdictionEndNodes[i];
|
||||
if (endNodeCode) {
|
||||
auto bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode.get()));
|
||||
auto endNodeCodeCopy = createOctalCodePtr(bytes);
|
||||
memcpy(endNodeCodeCopy.get(), endNodeCode.get(), bytes);
|
||||
_jurisdictionEndNodes.push_back(endNodeCodeCopy);
|
||||
}
|
||||
}
|
||||
|
||||
_incomingPacket = other._incomingPacket;
|
||||
_incomingBytes = other._incomingBytes;
|
||||
_incomingWastedBytes = other._incomingWastedBytes;
|
||||
|
@ -141,8 +119,7 @@ OctreeSceneStats::~OctreeSceneStats() {
|
|||
reset();
|
||||
}
|
||||
|
||||
void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, const OctreeElementPointer& root,
|
||||
JurisdictionMap* jurisdictionMap) {
|
||||
void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, const OctreeElementPointer& root) {
|
||||
reset(); // resets packet and octree stats
|
||||
_isStarted = true;
|
||||
_start = usecTimestampNow();
|
||||
|
@ -153,14 +130,6 @@ void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, const Octre
|
|||
|
||||
_isFullScene = isFullScene;
|
||||
_isMoving = isMoving;
|
||||
|
||||
// setup jurisdictions
|
||||
if (jurisdictionMap) {
|
||||
std::tie(_jurisdictionRoot, _jurisdictionEndNodes) = jurisdictionMap->getRootAndEndNodeOctalCodes();
|
||||
} else {
|
||||
_jurisdictionRoot = nullptr;
|
||||
_jurisdictionEndNodes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void OctreeSceneStats::sceneCompleted() {
|
||||
|
@ -236,9 +205,6 @@ void OctreeSceneStats::reset() {
|
|||
_existsBitsWritten = 0;
|
||||
_existsInPacketBitsWritten = 0;
|
||||
_treesRemoved = 0;
|
||||
|
||||
_jurisdictionRoot = nullptr;
|
||||
_jurisdictionEndNodes.clear();
|
||||
}
|
||||
|
||||
void OctreeSceneStats::packetSent(int bytes) {
|
||||
|
@ -374,29 +340,6 @@ int OctreeSceneStats::packIntoPacket() {
|
|||
_statsPacket->writePrimitive(_existsInPacketBitsWritten);
|
||||
_statsPacket->writePrimitive(_treesRemoved);
|
||||
|
||||
// add the root jurisdiction
|
||||
if (_jurisdictionRoot) {
|
||||
// copy the
|
||||
int bytes = (int)bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_jurisdictionRoot.get()));
|
||||
_statsPacket->writePrimitive(bytes);
|
||||
_statsPacket->write(reinterpret_cast<char*>(_jurisdictionRoot.get()), bytes);
|
||||
|
||||
// if and only if there's a root jurisdiction, also include the end elements
|
||||
int endNodeCount = (int)_jurisdictionEndNodes.size();
|
||||
|
||||
_statsPacket->writePrimitive(endNodeCount);
|
||||
|
||||
for (int i=0; i < endNodeCount; i++) {
|
||||
auto& endNodeCode = _jurisdictionEndNodes[i];
|
||||
auto bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode.get()));
|
||||
_statsPacket->writePrimitive(bytes);
|
||||
_statsPacket->write(reinterpret_cast<char*>(endNodeCode.get()), bytes);
|
||||
}
|
||||
} else {
|
||||
int bytes = 0;
|
||||
_statsPacket->writePrimitive(bytes);
|
||||
}
|
||||
|
||||
return _statsPacket->getPayloadSize();
|
||||
}
|
||||
|
||||
|
@ -458,38 +401,6 @@ int OctreeSceneStats::unpackFromPacket(ReceivedMessage& packet) {
|
|||
packet.readPrimitive(&_existsBitsWritten);
|
||||
packet.readPrimitive(&_existsInPacketBitsWritten);
|
||||
packet.readPrimitive(&_treesRemoved);
|
||||
// before allocating new juridiction, clean up existing ones
|
||||
_jurisdictionRoot = nullptr;
|
||||
_jurisdictionEndNodes.clear();
|
||||
|
||||
// read the root jurisdiction
|
||||
int bytes = 0;
|
||||
packet.readPrimitive(&bytes);
|
||||
|
||||
if (bytes == 0) {
|
||||
_jurisdictionRoot = nullptr;
|
||||
_jurisdictionEndNodes.clear();
|
||||
} else {
|
||||
_jurisdictionRoot = createOctalCodePtr(bytes);
|
||||
packet.read(reinterpret_cast<char*>(_jurisdictionRoot.get()), bytes);
|
||||
|
||||
// if and only if there's a root jurisdiction, also include the end elements
|
||||
_jurisdictionEndNodes.clear();
|
||||
|
||||
int endNodeCount = 0;
|
||||
packet.readPrimitive(&endNodeCount);
|
||||
|
||||
for (int i=0; i < endNodeCount; i++) {
|
||||
int bytes = 0;
|
||||
|
||||
packet.readPrimitive(&bytes);
|
||||
|
||||
auto endNodeCode = createOctalCodePtr(bytes);
|
||||
packet.read(reinterpret_cast<char*>(endNodeCode.get()), bytes);
|
||||
|
||||
_jurisdictionEndNodes.push_back(endNodeCode);
|
||||
}
|
||||
}
|
||||
|
||||
// running averages
|
||||
_elapsedAverage.updateAverage((float)_elapsed);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <NodeList.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
#include "JurisdictionMap.h"
|
||||
#include "OctreePacketData.h"
|
||||
#include "SequenceNumberStats.h"
|
||||
#include "OctalCode.h"
|
||||
|
@ -39,7 +38,7 @@ public:
|
|||
OctreeSceneStats& operator= (const OctreeSceneStats& other); // copy assignment
|
||||
|
||||
/// Call when beginning the computation of a scene. Initializes internal structures
|
||||
void sceneStarted(bool fullScene, bool moving, const OctreeElementPointer& root, JurisdictionMap* jurisdictionMap);
|
||||
void sceneStarted(bool fullScene, bool moving, const OctreeElementPointer& root);
|
||||
bool getIsSceneStarted() const { return _isStarted; }
|
||||
|
||||
/// Call when the computation of a scene is completed. Finalizes internal structures
|
||||
|
@ -143,12 +142,6 @@ public:
|
|||
/// \param Item item The item from the stats you're interested in.
|
||||
const char* getItemValue(Item item);
|
||||
|
||||
/// Returns OctCode for root element of the jurisdiction of this particular octree server
|
||||
OctalCodePtr getJurisdictionRoot() const { return _jurisdictionRoot; }
|
||||
|
||||
/// Returns list of OctCodes for end elements of the jurisdiction of this particular octree server
|
||||
const OctalCodePtrList& getJurisdictionEndNodes() const { return _jurisdictionEndNodes; }
|
||||
|
||||
bool isMoving() const { return _isMoving; }
|
||||
bool isFullScene() const { return _isFullScene; }
|
||||
quint64 getTotalElements() const { return _totalElements; }
|
||||
|
@ -277,9 +270,6 @@ private:
|
|||
static ItemInfo _ITEMS[];
|
||||
static const int MAX_ITEM_VALUE_LENGTH = 128;
|
||||
char _itemValueBuffer[MAX_ITEM_VALUE_LENGTH];
|
||||
|
||||
OctalCodePtr _jurisdictionRoot;
|
||||
std::vector<OctalCodePtr> _jurisdictionEndNodes;
|
||||
};
|
||||
|
||||
/// Map between element IDs and their reported OctreeSceneStats. Typically used by classes that need to know which elements sent
|
||||
|
|
|
@ -13,12 +13,9 @@
|
|||
|
||||
#include "OctreeScriptingInterface.h"
|
||||
|
||||
OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender,
|
||||
JurisdictionListener* jurisdictionListener) :
|
||||
OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender) :
|
||||
_packetSender(packetSender),
|
||||
_jurisdictionListener(jurisdictionListener),
|
||||
_managedPacketSender(false),
|
||||
_managedJurisdictionListener(false),
|
||||
_managedPacketSender(false),
|
||||
_initialized(false)
|
||||
{
|
||||
}
|
||||
|
@ -28,12 +25,6 @@ OctreeScriptingInterface::~OctreeScriptingInterface() {
|
|||
}
|
||||
|
||||
void OctreeScriptingInterface::cleanupManagedObjects() {
|
||||
if (_managedJurisdictionListener) {
|
||||
_jurisdictionListener->terminate();
|
||||
_jurisdictionListener->deleteLater();
|
||||
_managedJurisdictionListener = false;
|
||||
_jurisdictionListener = NULL;
|
||||
}
|
||||
if (_managedPacketSender) {
|
||||
_packetSender->terminate();
|
||||
_packetSender->deleteLater();
|
||||
|
@ -46,29 +37,16 @@ void OctreeScriptingInterface::setPacketSender(OctreeEditPacketSender* packetSen
|
|||
_packetSender = packetSender;
|
||||
}
|
||||
|
||||
void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jurisdictionListener) {
|
||||
_jurisdictionListener = jurisdictionListener;
|
||||
}
|
||||
|
||||
void OctreeScriptingInterface::init() {
|
||||
if (_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_jurisdictionListener) {
|
||||
_managedJurisdictionListener = false;
|
||||
} else {
|
||||
_managedJurisdictionListener = true;
|
||||
_jurisdictionListener = new JurisdictionListener(getServerNodeType());
|
||||
_jurisdictionListener->initialize(true);
|
||||
}
|
||||
|
||||
if (_packetSender) {
|
||||
_managedPacketSender = false;
|
||||
} else {
|
||||
_managedPacketSender = true;
|
||||
_packetSender = createPacketSender();
|
||||
_packetSender->setServerJurisdictions(_jurisdictionListener->getJurisdictions());
|
||||
}
|
||||
|
||||
if (QCoreApplication::instance()) {
|
||||
|
|
|
@ -14,21 +14,18 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "JurisdictionListener.h"
|
||||
#include "OctreeEditPacketSender.h"
|
||||
|
||||
/// handles scripting of Particle commands from JS passed to assigned clients
|
||||
class OctreeScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OctreeScriptingInterface(OctreeEditPacketSender* packetSender = NULL, JurisdictionListener* jurisdictionListener = NULL);
|
||||
OctreeScriptingInterface(OctreeEditPacketSender* packetSender = nullptr);
|
||||
|
||||
~OctreeScriptingInterface();
|
||||
|
||||
OctreeEditPacketSender* getPacketSender() const { return _packetSender; }
|
||||
JurisdictionListener* getJurisdictionListener() const { return _jurisdictionListener; }
|
||||
void setPacketSender(OctreeEditPacketSender* packetSender);
|
||||
void setJurisdictionListener(JurisdictionListener* jurisdictionListener);
|
||||
void init();
|
||||
|
||||
virtual NodeType_t getServerNodeType() const = 0;
|
||||
|
@ -86,9 +83,7 @@ public slots:
|
|||
protected:
|
||||
/// attached OctreeEditPacketSender that handles queuing and sending of packets to VS
|
||||
OctreeEditPacketSender* _packetSender = nullptr;
|
||||
JurisdictionListener* _jurisdictionListener = nullptr;
|
||||
bool _managedPacketSender;
|
||||
bool _managedJurisdictionListener;
|
||||
bool _initialized;
|
||||
};
|
||||
|
||||
|
|
|
@ -391,6 +391,10 @@ void CharacterController::setState(State desiredState) {
|
|||
}
|
||||
}
|
||||
|
||||
void CharacterController::recomputeFlying() {
|
||||
_pendingFlags |= PENDING_FLAG_RECOMPUTE_FLYING;
|
||||
}
|
||||
|
||||
void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) {
|
||||
float x = scale.x;
|
||||
float z = scale.z;
|
||||
|
@ -657,6 +661,13 @@ void CharacterController::updateState() {
|
|||
if (!_dynamicsWorld) {
|
||||
return;
|
||||
}
|
||||
if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) {
|
||||
SET_STATE(CharacterController::State::Hover, "recomputeFlying");
|
||||
_hasSupport = false;
|
||||
_stepHeight = _minStepHeight; // clears memory of last step obstacle
|
||||
_pendingFlags &= ~PENDING_FLAG_RECOMPUTE_FLYING;
|
||||
}
|
||||
|
||||
const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius;
|
||||
const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight;
|
||||
const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND;
|
||||
|
|
|
@ -31,6 +31,7 @@ const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1;
|
|||
const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2;
|
||||
const uint32_t PENDING_FLAG_JUMP = 1U << 3;
|
||||
const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4;
|
||||
const uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5;
|
||||
const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f);
|
||||
|
||||
class btRigidBody;
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
|
||||
void setGravity(float gravity);
|
||||
float getGravity();
|
||||
void recomputeFlying();
|
||||
|
||||
virtual void updateShapeIfNecessary() = 0;
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
// (1) we own it but may need to change the priority OR...
|
||||
// (2) we don't own it but should bid (because a local script has been changing physics properties)
|
||||
uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority();
|
||||
_outgoingPriority = glm::max(_outgoingPriority, newPriority);
|
||||
upgradeOutgoingPriority(newPriority);
|
||||
|
||||
// reset bid expiry so that we bid ASAP
|
||||
_nextOwnershipBid = 0;
|
||||
|
@ -403,7 +403,8 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
_outgoingPriority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
uint8_t priority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
upgradeOutgoingPriority(priority);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -502,17 +503,21 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
// we don't own the simulation
|
||||
|
||||
// NOTE: we do not volunteer to own kinematic or static objects
|
||||
uint8_t insufficientPriority = _body->isStaticOrKinematicObject() ? VOLUNTEER_SIMULATION_PRIORITY : 0;
|
||||
uint8_t volunteerPriority = _body->isStaticOrKinematicObject() ? VOLUNTEER_SIMULATION_PRIORITY : 0;
|
||||
|
||||
bool shouldBid = _outgoingPriority > insufficientPriority && // but we would like to own it AND
|
||||
bool shouldBid = _outgoingPriority > volunteerPriority && // but we would like to own it AND
|
||||
usecTimestampNow() > _nextOwnershipBid; // it is time to bid again
|
||||
if (shouldBid && _outgoingPriority < _entity->getSimulationPriority()) {
|
||||
// we are insufficiently interested so clear our interest
|
||||
// we are insufficiently interested so clear _outgoingPriority
|
||||
// and reset the bid expiry
|
||||
_outgoingPriority = 0;
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
}
|
||||
return shouldBid;
|
||||
} else {
|
||||
// When we own the simulation: make sure _outgoingPriority is not less than current owned priority
|
||||
// because: an _outgoingPriority of zero indicates that we should drop ownership when we have it.
|
||||
upgradeOutgoingPriority(_entity->getSimulationPriority());
|
||||
}
|
||||
|
||||
return remoteSimulationOutOfSync(simulationStep);
|
||||
|
@ -618,8 +623,10 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
|||
_entity->setPendingOwnershipPriority(_outgoingPriority, now);
|
||||
// don't forget to remember that we have made a bid
|
||||
_entity->rememberHasSimulationOwnershipBid();
|
||||
// ...then reset _outgoingPriority in preparation for the next frame
|
||||
// ...then reset _outgoingPriority
|
||||
_outgoingPriority = 0;
|
||||
// _outgoingPrioriuty will be re-computed before next bid,
|
||||
// or will be set to agree with ownership priority should we win the bid
|
||||
} else if (_outgoingPriority != _entity->getSimulationPriority()) {
|
||||
// we own the simulation but our desired priority has changed
|
||||
if (_outgoingPriority == 0) {
|
||||
|
|
|
@ -317,6 +317,7 @@ void PhysicsEngine::stepSimulation() {
|
|||
|
||||
auto onSubStep = [this]() {
|
||||
this->updateContactMap();
|
||||
this->doOwnershipInfectionForConstraints();
|
||||
};
|
||||
|
||||
int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS,
|
||||
|
@ -451,7 +452,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
|||
// NOTE: we might own the simulation of a kinematic object (A)
|
||||
// but we don't claim ownership of kinematic objects (B) based on collisions here.
|
||||
if (!objectB->isStaticOrKinematicObject() && motionStateB->getSimulatorID() != Physics::getSessionUUID()) {
|
||||
quint8 priorityA = motionStateA ? motionStateA->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
uint8_t priorityA = motionStateA ? motionStateA->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
motionStateB->bump(priorityA);
|
||||
}
|
||||
} else if (motionStateA &&
|
||||
|
@ -460,7 +461,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
|||
// SIMILARLY: we might own the simulation of a kinematic object (B)
|
||||
// but we don't claim ownership of kinematic objects (A) based on collisions here.
|
||||
if (!objectA->isStaticOrKinematicObject() && motionStateA->getSimulatorID() != Physics::getSessionUUID()) {
|
||||
quint8 priorityB = motionStateB ? motionStateB->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
uint8_t priorityB = motionStateB ? motionStateB->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
motionStateA->bump(priorityB);
|
||||
}
|
||||
}
|
||||
|
@ -501,6 +502,54 @@ void PhysicsEngine::updateContactMap() {
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::doOwnershipInfectionForConstraints() {
|
||||
BT_PROFILE("ownershipInfectionForConstraints");
|
||||
const btCollisionObject* characterObject = _myAvatarController ? _myAvatarController->getCollisionObject() : nullptr;
|
||||
foreach(const auto& dynamic, _objectDynamics) {
|
||||
if (!dynamic) {
|
||||
continue;
|
||||
}
|
||||
QList<btRigidBody*> bodies = std::static_pointer_cast<ObjectDynamic>(dynamic)->getRigidBodies();
|
||||
if (bodies.size() > 1) {
|
||||
int32_t numOwned = 0;
|
||||
int32_t numStatic = 0;
|
||||
uint8_t priority = VOLUNTEER_SIMULATION_PRIORITY;
|
||||
foreach(btRigidBody* body, bodies) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(body->getUserPointer());
|
||||
if (body->isStaticObject()) {
|
||||
++numStatic;
|
||||
} else if (motionState->getType() == MOTIONSTATE_TYPE_AVATAR) {
|
||||
// we can never take ownership of this constraint
|
||||
numOwned = 0;
|
||||
break;
|
||||
} else {
|
||||
if (motionState && motionState->getSimulatorID() == Physics::getSessionUUID()) {
|
||||
priority = glm::max(priority, motionState->getSimulationPriority());
|
||||
} else if (body == characterObject) {
|
||||
priority = glm::max(priority, PERSONAL_SIMULATION_PRIORITY);
|
||||
}
|
||||
numOwned++;
|
||||
}
|
||||
}
|
||||
|
||||
if (numOwned > 0) {
|
||||
if (numOwned + numStatic != bodies.size()) {
|
||||
// we have partial ownership but it isn't complete so we walk each object
|
||||
// and bump the simulation priority to the highest priority we encountered earlier
|
||||
foreach(btRigidBody* body, bodies) {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(body->getUserPointer());
|
||||
if (motionState) {
|
||||
// NOTE: we submit priority+1 because the default behavior of bump() is to actually use priority - 1
|
||||
// and we want all priorities of the objects to be at the SAME level
|
||||
motionState->bump(priority + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CollisionEvents& PhysicsEngine::getCollisionEvents() {
|
||||
_collisionEvents.clear();
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
void harvestPerformanceStats();
|
||||
void printPerformanceStatsToFile(const QString& filename);
|
||||
void updateContactMap();
|
||||
void doOwnershipInfectionForConstraints();
|
||||
|
||||
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
#include "PickManager.h"
|
||||
#include "PointerManager.h"
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
|
||||
const float Pointer::POINTER_MOVE_DELAY = 0.33f * USECS_PER_SECOND;
|
||||
const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f;
|
||||
const float Pointer::TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED = TOUCH_PRESS_TO_MOVE_DEADSPOT * TOUCH_PRESS_TO_MOVE_DEADSPOT;
|
||||
|
||||
Pointer::~Pointer() {
|
||||
DependencyManager::get<PickManager>()->removePick(_pickUID);
|
||||
}
|
||||
|
@ -77,7 +83,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
Buttons newButtons;
|
||||
Buttons sameButtons;
|
||||
if (_enabled && shouldTrigger(pickResult)) {
|
||||
buttons = getPressedButtons();
|
||||
buttons = getPressedButtons(pickResult);
|
||||
for (const std::string& button : buttons) {
|
||||
if (_prevButtons.find(button) == _prevButtons.end()) {
|
||||
newButtons.insert(button);
|
||||
|
@ -175,17 +181,6 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
}
|
||||
}
|
||||
|
||||
// send hoverEnd events if we disable the pointer or disable hovering
|
||||
if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) {
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == OVERLAY) {
|
||||
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == HUD) {
|
||||
emit pointerManager->hoverEndHUD(hoveredEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger begin
|
||||
const std::string SHOULD_FOCUS_BUTTON = "Focus";
|
||||
for (const std::string& button : newButtons) {
|
||||
|
@ -204,7 +199,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
|
||||
// Trigger continue
|
||||
for (const std::string& button : sameButtons) {
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, false);
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, button, false);
|
||||
triggeredEvent.setID(pointerID);
|
||||
triggeredEvent.setType(PointerEvent::Move);
|
||||
triggeredEvent.setButton(chooseButton(button));
|
||||
|
@ -219,7 +214,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
|
||||
// Trigger end
|
||||
for (const std::string& button : _prevButtons) {
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, false);
|
||||
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, button, false);
|
||||
triggeredEvent.setID(pointerID);
|
||||
triggeredEvent.setType(PointerEvent::Release);
|
||||
triggeredEvent.setButton(chooseButton(button));
|
||||
|
@ -233,6 +228,17 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
|
|||
_triggeredObjects.erase(button);
|
||||
}
|
||||
|
||||
// if we disable the pointer or disable hovering, send hoverEnd events after triggerEnd
|
||||
if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) {
|
||||
if (_prevHoveredObject.type == ENTITY) {
|
||||
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == OVERLAY) {
|
||||
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent);
|
||||
} else if (_prevHoveredObject.type == HUD) {
|
||||
emit pointerManager->hoverEndHUD(hoveredEvent);
|
||||
}
|
||||
}
|
||||
|
||||
_prevHoveredObject = hoveredObject;
|
||||
_prevButtons = buttons;
|
||||
_prevEnabled = _enabled;
|
||||
|
|
|
@ -82,14 +82,17 @@ protected:
|
|||
bool _enabled;
|
||||
bool _hover;
|
||||
|
||||
virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover = true) const = 0;
|
||||
virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) = 0;
|
||||
|
||||
virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0;
|
||||
virtual Buttons getPressedButtons() = 0;
|
||||
virtual Buttons getPressedButtons(const PickResultPointer& pickResult) = 0;
|
||||
|
||||
virtual bool shouldHover(const PickResultPointer& pickResult) { return true; }
|
||||
virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; }
|
||||
|
||||
static const float POINTER_MOVE_DELAY;
|
||||
static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED;
|
||||
|
||||
private:
|
||||
PickedObject _prevHoveredObject;
|
||||
Buttons _prevButtons;
|
||||
|
|
|
@ -39,7 +39,6 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform
|
|||
}
|
||||
|
||||
void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
|
||||
// Still relying on the raw data from the model
|
||||
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization;
|
||||
if (useCauterizedMesh) {
|
||||
if (_cauterizedClusterBuffer) {
|
||||
|
|
|
@ -71,12 +71,12 @@ enum DeferredShader_BufferSlot {
|
|||
SCATTERING_PARAMETERS_BUFFER_SLOT,
|
||||
LIGHTING_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::LIGHTING_MODEL,
|
||||
LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT,
|
||||
LIGHT_AMBIENT_SLOT,
|
||||
LIGHT_AMBIENT_SLOT = render::ShapePipeline::Slot::LIGHT_AMBIENT_BUFFER,
|
||||
HAZE_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::HAZE_MODEL,
|
||||
LIGHT_INDEX_GPU_SLOT,
|
||||
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT,
|
||||
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT,
|
||||
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT,
|
||||
HAZE_MODEL_BUFFER_SLOT
|
||||
};
|
||||
|
||||
static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations);
|
||||
|
|
|
@ -501,7 +501,6 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
|
|||
}
|
||||
|
||||
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
|
||||
// Still relying on the raw data from the model
|
||||
if (_clusterBuffer) {
|
||||
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer);
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
_scaledToFit = false;
|
||||
}
|
||||
|
||||
const float SCALE_CHANGE_EPSILON = 0.001f;
|
||||
const float SCALE_CHANGE_EPSILON = 0.0000001f;
|
||||
|
||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||
if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {
|
||||
|
@ -594,47 +594,72 @@ void Model::calculateTriangleSets() {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setVisibleInScene(bool newValue, const render::ScenePointer& scene) {
|
||||
if (_isVisible != newValue) {
|
||||
_isVisible = newValue;
|
||||
void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene) {
|
||||
if (_isVisible != isVisible) {
|
||||
_isVisible = isVisible;
|
||||
|
||||
bool isLayeredInFront = _isLayeredInFront;
|
||||
bool isLayeredInHUD = _isLayeredInHUD;
|
||||
|
||||
render::Transaction transaction;
|
||||
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.resetItem(item, _modelMeshRenderItemsMap[item]);
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.resetItem(item, _collisionRenderItemsMap[item]);
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Model::setLayeredInFront(bool layered, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInFront != layered) {
|
||||
_isLayeredInFront = layered;
|
||||
void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInFront != isLayeredInFront) {
|
||||
_isLayeredInFront = isLayeredInFront;
|
||||
|
||||
bool isVisible = _isVisible;
|
||||
bool isLayeredInHUD = _isLayeredInHUD;
|
||||
|
||||
render::Transaction transaction;
|
||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.resetItem(item, _modelMeshRenderItemsMap[item]);
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.resetItem(item, _collisionRenderItemsMap[item]);
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setLayeredInHUD(bool layered, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInHUD != layered) {
|
||||
_isLayeredInHUD = layered;
|
||||
void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInHUD != isLayeredInHUD) {
|
||||
_isLayeredInHUD = isLayeredInHUD;
|
||||
|
||||
bool isVisible = _isVisible;
|
||||
bool isLayeredInFront = _isLayeredInFront;
|
||||
|
||||
render::Transaction transaction;
|
||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.resetItem(item, _modelMeshRenderItemsMap[item]);
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.resetItem(item, _collisionRenderItemsMap[item]);
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) {
|
||||
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
|
|
@ -82,9 +82,9 @@ public:
|
|||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
// new Scene/Engine rendering support
|
||||
void setVisibleInScene(bool newValue, const render::ScenePointer& scene);
|
||||
void setLayeredInFront(bool layered, const render::ScenePointer& scene);
|
||||
void setLayeredInHUD(bool layered, const render::ScenePointer& scene);
|
||||
void setVisibleInScene(bool isVisible, const render::ScenePointer& scene);
|
||||
void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene);
|
||||
void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene);
|
||||
bool needsFixupInScene() const;
|
||||
|
||||
bool needsReload() const { return _needsReload; }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue