Merge branch 'android' into android_render_pr

This commit is contained in:
Bradley Austin Davis 2018-01-08 12:49:52 -05:00 committed by GitHub
commit 68cd768a65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
125 changed files with 1795 additions and 2189 deletions

4
.gitignore vendored
View file

@ -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

View file

@ -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 = [

View file

@ -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();

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -14,7 +14,6 @@
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
const int MAX_FILENAME_LENGTH = 1024;

View file

@ -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();

View file

@ -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);

View file

@ -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" },

View file

@ -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 {

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -40,7 +40,7 @@ Rectangle {
verticalCenter: parent.verticalCenter;
}
visible: Audio.muted;
visible: AudioScriptingInterface.muted;
color: colors.muted;
text: "MUTED";

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View file

@ -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();
}

View file

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

View file

@ -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;
}

View file

@ -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");
}
}

View file

@ -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;

View file

@ -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>();

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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));
}

View file

@ -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 };
};

View file

@ -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();
}

View file

@ -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

View file

@ -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);
}

View file

@ -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)

View file

@ -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

View file

@ -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()) {

View file

@ -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();
};

View file

@ -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);
}
}

View file

@ -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;
};

View file

@ -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();
}

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -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; };

View file

@ -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") {

View file

@ -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 +

View file

@ -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));

View file

@ -79,6 +79,7 @@ private:
ModelPointer _model;
QVariantMap _modelTextures;
bool _texturesLoaded { false };
render::ItemIDs _subRenderItemIDs;

View file

@ -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));
}
}

View file

@ -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(),

View file

@ -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;

View file

@ -53,6 +53,7 @@ class RouteBuilderProxy : public QObject {
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
Q_INVOKABLE QObject* rotate(glm::quat rotation);
Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant);
Q_INVOKABLE QObject* exponentialSmoothing(float rotationConstant, float translationConstant);
Q_INVOKABLE QObject* logicalNot();
private:

View file

@ -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;
}
}

View file

@ -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

View file

@ -16,4 +16,3 @@ include_hifi_library_headers(controllers)
target_bullet()
target_polyvox()

View file

@ -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) {

View file

@ -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

View file

@ -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;
}

View file

@ -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,

View file

@ -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

View file

@ -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);

View file

@ -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>

View file

@ -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);

View file

@ -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");
}

View file

@ -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

View file

@ -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) :

View file

@ -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:

View file

@ -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 {

View file

@ -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;
}

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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();
}

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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()) {

View file

@ -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;
};

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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();

View file

@ -64,6 +64,7 @@ public:
void harvestPerformanceStats();
void printPerformanceStatsToFile(const QString& filename);
void updateContactMap();
void doOwnershipInfectionForConstraints();
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }

View file

@ -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;

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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