Merged with master

This commit is contained in:
Olivier Prat 2018-01-06 10:01:00 +01:00
commit b296b96e5e
112 changed files with 1548 additions and 2164 deletions

4
.gitignore vendored
View file

@ -20,7 +20,7 @@ android/.gradle
android/app/src/main/jniLibs
# 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
@ -66,7 +66,7 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.sw[po]
*.qmlc
*.jsc
# ignore QML compilation output
*.qmlc

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

@ -214,7 +214,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
uint64_t getTimestamp() const override {
return _lastEncodeTime;
}
const AvatarSharedPointer& getAvatar() const { return _avatar; }
AvatarSharedPointer getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
@ -326,7 +326,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) {
const auto& avatarData = sortedAvatars.top().getAvatar();
const auto avatarData = sortedAvatars.top().getAvatar();
sortedAvatars.pop();
remainingAvatars--;

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

@ -33,6 +33,7 @@ Rectangle {
property string itemName;
property string itemId;
property string itemHref;
property string itemAuthor;
property double balanceAfterPurchase;
property bool alreadyOwned: false;
property int itemPrice: -1;
@ -81,12 +82,12 @@ Rectangle {
if (result.status !== 'success') {
failureErrorText.text = result.message;
root.activeView = "checkoutFailure";
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message);
UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message);
} else {
root.itemHref = result.data.download_url;
root.isWearable = result.data.categories.indexOf("Wearables") > -1;
root.activeView = "checkoutSuccess";
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned);
UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned);
}
}
@ -410,7 +411,8 @@ Rectangle {
Rectangle {
id: buyTextContainer;
visible: buyText.text !== "";
anchors.top: parent.top;
anchors.top: cancelPurchaseButton.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.right: parent.right;
height: buyText.height + 30;
@ -465,8 +467,8 @@ Rectangle {
enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || !itemIsJson;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top;
anchors.topMargin: buyTextContainer.visible ? 12 : 16;
anchors.top: checkoutActionButtonsContainer.top;
anchors.topMargin: 16;
height: 40;
anchors.left: parent.left;
anchors.right: parent.right;
@ -879,6 +881,7 @@ Rectangle {
root.itemPrice = message.params.itemPrice;
itemHref = message.params.itemHref;
referrer = message.params.referrer;
itemAuthor = message.params.itemAuthor;
setBuyText();
break;
default:
@ -926,11 +929,11 @@ Rectangle {
buyText.text = "";
}
} else {
buyText.text = "This free item <b>will not</b> be added to your <b>Purchases</b>. Non-entities can't yet be purchased for HFC.";
buyTextContainer.color = "#FFD6AD";
buyTextContainer.border.color = "#FAC07D";
buyGlyph.text = hifi.glyphs.alert;
buyGlyph.size = 46;
buyText.text = '<i>This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.</i>';
buyTextContainer.color = hifi.colors.white;
buyTextContainer.border.color = hifi.colors.white;
buyGlyph.text = "";
buyGlyph.size = 0;
}
}

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

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

Binary file not shown.

View file

@ -191,6 +191,7 @@
#include <GPUIdent.h>
#include <gl/GLHelpers.h>
#include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>
#include <src/scripting/GooglePolyScriptingInterface.h>
#include <EntityScriptClient.h>
#include <ModelScriptingInterface.h>
@ -698,6 +699,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<EntityScriptClient>();
DependencyManager::set<EntityScriptServerLogClient>();
DependencyManager::set<LimitlessVoiceRecognitionScriptingInterface>();
DependencyManager::set<GooglePolyScriptingInterface>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
DependencyManager::set<AvatarBookmarks>();
DependencyManager::set<LocationBookmarks>();
@ -1189,8 +1191,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
@ -1443,7 +1443,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
@ -1467,8 +1466,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
@ -4279,7 +4277,7 @@ void Application::init() {
getEntities()->init();
getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) {
auto dims = item.getDimensions();
auto dims = item.getScaledDimensions();
auto maxSize = glm::compMax(dims);
if (maxSize <= 0.0f) {
@ -4557,7 +4555,7 @@ void Application::reloadResourceCaches() {
_lastQueriedTime = 0;
_octreeQuery.incrementConnectionID();
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
DependencyManager::get<AssetClient>()->clearCache();
@ -4633,7 +4631,7 @@ void Application::setKeyboardFocusEntity(const EntityItemID& entityItemID) {
_lastAcceptedKeyPress = usecTimestampNow();
setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
entity->getDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
}
}
}
@ -5050,7 +5048,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;
@ -5271,15 +5269,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());
@ -5294,147 +5289,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);
}
});
}
@ -5549,11 +5419,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();
});
@ -5755,8 +5620,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;
@ -5771,42 +5634,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;
@ -5827,7 +5654,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);
@ -5941,6 +5768,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>().data());
scriptEngine->registerGlobalObject("GooglePoly", DependencyManager::get<GooglePolyScriptingInterface>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine.data(), steamClient.get()));

View file

@ -228,8 +228,6 @@ public:
FileLogger* getLogger() const { return _logger; }
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
float getRenderResolutionScale() const;
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
@ -450,7 +448,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();
@ -571,7 +569,6 @@ private:
StDev _idleLoopStdev;
float _idleLoopMeasuredJitter;
NodeToJurisdictionMap _entityServerJurisdictions;
NodeToOctreeSceneStats _octreeServerSceneStats;
ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
QPointer<LogDialog> _logDialog;

View file

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

View file

@ -407,7 +407,7 @@ void shapeInfoCalculator(const ShapeEntityItem * const shapeEntity, ShapeInfo &s
ShapeInfo::PointList points;
pointCollection.push_back(points);
GeometryCache::computeSimpleHullPointListForShape((int)shapeEntity->getShape(), shapeEntity->getDimensions(), pointCollection.back());
GeometryCache::computeSimpleHullPointListForShape((int)shapeEntity->getShape(), shapeEntity->getScaledDimensions(), pointCollection.back());
shapeInfo.setPointCollection(pointCollection);
}

View file

@ -150,7 +150,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override { return std::static_pointer_cast<Avatar>(_avatar)->getBoundingRadius(); }
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_avatar)->getLastRenderUpdateTime(); }
const AvatarSharedPointer& getAvatar() const { return _avatar; }
AvatarSharedPointer getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
};
@ -185,7 +185,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
render::Transaction transaction;
while (!sortedAvatars.empty()) {
const SortableAvatar& sortData = sortedAvatars.top();
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
const auto avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
if (ignoring) {
@ -239,7 +239,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
sortedAvatars.pop();
while (inView && !sortedAvatars.empty()) {
const SortableAvatar& newSortData = sortedAvatars.top();
const auto& newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
if (inView && newAvatar->hasNewJointData()) {
numAVatarsNotUpdated++;

View file

@ -61,7 +61,7 @@ void Ledger::send(const QString& endpoint, const QString& success, const QString
void Ledger::signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure) {
auto wallet = DependencyManager::get<Wallet>();
QString signature = key.isEmpty() ? "" : wallet->signWithKey(text, key);
QString signature = wallet->signWithKey(text, key);
QJsonObject request;
request[propertyName] = QString(text);
if (!controlled_failure) {

View file

@ -548,13 +548,16 @@ QStringList Wallet::listPublicKeys() {
// the horror of code pages and so on (changing the bytes) by just returning a base64
// encoded string representing the signature (suitable for http, etc...)
QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
qCInfo(commerce) << "Signing text" << text << "with key" << key;
EC_KEY* ecPrivateKey = NULL;
auto keyFilePathString = keyFilePath().toStdString();
if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) {
unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)];
unsigned int signatureBytes = 0;
qCInfo(commerce) << "Hashing and signing plaintext" << text << "with key at address" << ecPrivateKey;
QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256);
@ -747,12 +750,10 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
}
EC_KEY_free(ec);
QByteArray ba = sig.toLocal8Bit();
const char *sigChar = ba.data();
QByteArray textByteArray;
if (status > -1) {
textByteArray = QByteArray(sigChar, (int) strlen(sigChar));
textByteArray = sig.toUtf8();
}
textByteArraySize = textByteArray.size();
int certIDSize = certID.size();

View file

@ -0,0 +1,181 @@
//
// GooglePolyScriptingInterface.cpp
// interface/src/scripting
//
// Created by Elisa Lupin-Jimenez on 12/3/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QEventLoop>
#include <QtGlobal>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QString>
#include <QTime>
#include <QUrl>
#include <random>
#include "GooglePolyScriptingInterface.h"
#include "ScriptEngineLogging.h"
const QString LIST_POLY_URL = "https://poly.googleapis.com/v1/assets?";
const QString GET_POLY_URL = "https://poly.googleapis.com/v1/assets/model?";
const QStringList VALID_FORMATS = QStringList() << "BLOCKS" << "FBX" << "GLTF" << "GLTF2" << "OBJ" << "TILT" << "";
const QStringList VALID_CATEGORIES = QStringList() << "animals" << "architecture" << "art" << "food" <<
"nature" << "objects" << "people" << "scenes" << "technology" << "transport" << "";
GooglePolyScriptingInterface::GooglePolyScriptingInterface() {
// nothing to be implemented
}
void GooglePolyScriptingInterface::setAPIKey(const QString& key) {
_authCode = key;
}
QString GooglePolyScriptingInterface::getAssetList(const QString& keyword, const QString& category, const QString& format) {
QUrl url = formatURLQuery(keyword, category, format);
if (!url.isEmpty()) {
QByteArray json = parseJSON(url, 0).toJsonDocument().toJson();
return (QString) json;
} else {
qCDebug(scriptengine) << "Invalid filters were specified.";
return "";
}
}
QString GooglePolyScriptingInterface::getFBX(const QString& keyword, const QString& category) {
QUrl url = formatURLQuery(keyword, category, "FBX");
return getModelURL(url);
}
QString GooglePolyScriptingInterface::getOBJ(const QString& keyword, const QString& category) {
QUrl url = formatURLQuery(keyword, category, "OBJ");
return getModelURL(url);
}
QString GooglePolyScriptingInterface::getBlocks(const QString& keyword, const QString& category) {
QUrl url = formatURLQuery(keyword, category, "BLOCKS");
return getModelURL(url);
}
QString GooglePolyScriptingInterface::getGLTF(const QString& keyword, const QString& category) {
QUrl url = formatURLQuery(keyword, category, "GLTF");
return getModelURL(url);
}
QString GooglePolyScriptingInterface::getGLTF2(const QString& keyword, const QString& category) {
QUrl url = formatURLQuery(keyword, category, "GLTF2");
return getModelURL(url);
}
// This method will not be useful until we support Tilt models
QString GooglePolyScriptingInterface::getTilt(const QString& keyword, const QString& category) {
QUrl url = formatURLQuery(keyword, category, "TILT");
return getModelURL(url);
}
// Can provide asset name or full URL to model
QString GooglePolyScriptingInterface::getModelInfo(const QString& input) {
QString name(input);
if (input.contains("poly.googleapis") || input.contains("poly.google.com")) {
QStringList list = input.split("/");
if (input.contains("poly.googleapis")) {
name = list[4];
} else {
name = list.last();
}
}
QString urlString(GET_POLY_URL);
urlString = urlString.replace("model", name) + "key=" + _authCode;
qCDebug(scriptengine) << "Google URL request: " << urlString;
QUrl url(urlString);
QString json = parseJSON(url, 2).toString();
return json;
}
int GooglePolyScriptingInterface::getRandIntInRange(int length) {
QTime time = QTime::currentTime();
qsrand((uint)time.msec());
return qrand() % length;
}
QUrl GooglePolyScriptingInterface::formatURLQuery(const QString& keyword, const QString& category, const QString& format) {
QString queries;
if (!VALID_FORMATS.contains(format, Qt::CaseInsensitive) || !VALID_CATEGORIES.contains(category, Qt::CaseInsensitive)) {
return QUrl("");
} else {
if (!keyword.isEmpty()) {
QString keywords(keyword);
keywords.replace(" ", "+");
queries.append("&keywords=" + keywords);
}
if (!category.isEmpty()) {
queries.append("&category=" + category);
}
if (!format.isEmpty()) {
queries.append("&format=" + format);
}
QString urlString(LIST_POLY_URL + "key=" + _authCode + queries);
return QUrl(urlString);
}
}
QString GooglePolyScriptingInterface::getModelURL(const QUrl& url) {
qCDebug(scriptengine) << "Google URL request: " << url;
if (!url.isEmpty()) {
return parseJSON(url, 1).toString();
} else {
qCDebug(scriptengine) << "Invalid filters were specified.";
return "";
}
}
// FIXME: synchronous
QByteArray GooglePolyScriptingInterface::getHTTPRequest(const QUrl& url) {
QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(url));
QEventLoop event;
connect(response, SIGNAL(finished()), &event, SLOT(quit()));
event.exec();
return response->readAll();
}
// 0 = asset list, 1 = model from asset list, 2 = specific model
QVariant GooglePolyScriptingInterface::parseJSON(const QUrl& url, int fileType) {
QByteArray jsonString = getHTTPRequest(url);
QJsonDocument doc = QJsonDocument::fromJson(jsonString);
QJsonObject obj = doc.object();
if (obj.isEmpty()) {
qCDebug(scriptengine) << "Assets with specified filters not found";
return "";
}
if (obj.keys().first() == "error") {
QString error = obj.value("error").toObject().value("message").toString();
qCDebug(scriptengine) << error;
return "";
}
if (fileType == 0 || fileType == 1) {
QJsonArray arr = obj.value("assets").toArray();
// return model url
if (fileType == 1) {
int random = getRandIntInRange(arr.size());
QJsonObject json = arr.at(random).toObject();
// nested JSONs
return json.value("formats").toArray().at(0).toObject().value("root").toObject().value("url");
}
// return whole asset list
return QJsonDocument(arr);
// return specific object
} else {
return jsonString;
}
}

View file

@ -0,0 +1,47 @@
//
// GooglePolyScriptingInterface.h
// interface/src/scripting
//
// Created by Elisa Lupin-Jimenez on 12/3/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_GooglePolyScriptingInterface_h
#define hifi_GooglePolyScriptingInterface_h
#include <QObject>
#include <DependencyManager.h>
class GooglePolyScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
GooglePolyScriptingInterface();
public slots:
void setAPIKey(const QString& key);
QString getAssetList(const QString& keyword, const QString& category, const QString& format);
QString getFBX(const QString& keyword, const QString& category);
QString getOBJ(const QString& keyword, const QString& category);
QString getBlocks(const QString& keyword, const QString& categoryy);
QString getGLTF(const QString& keyword, const QString& category);
QString getGLTF2(const QString& keyword, const QString& category);
QString getTilt(const QString& keyword, const QString& category);
QString getModelInfo(const QString& input);
private:
QString _authCode;
QUrl formatURLQuery(const QString& keyword, const QString& category, const QString& format);
QString getModelURL(const QUrl& url);
QByteArray getHTTPRequest(const QUrl& url);
QVariant parseJSON(const QUrl& url, int fileType);
int getRandIntInRange(int length);
};
#endif // hifi_GooglePolyScriptingInterface_h

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

@ -42,7 +42,7 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
setTransform(base3DOverlay->getTransform());
}
QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties) {
QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, bool scalesWithParent) {
// the position and rotation in _transform are relative to the parent (aka local). The versions coming from
// scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties
// so that "position" and "rotation" are relative-to-parent values.
@ -56,7 +56,7 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert
result["position"] = result["localPosition"];
} else if (result["position"].isValid()) {
glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]),
parentID, parentJointIndex, success);
parentID, parentJointIndex, scalesWithParent, success);
if (success) {
result["position"] = vec3toVariant(localPosition);
}
@ -66,7 +66,7 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert
result["orientation"] = result["localOrientation"];
} else if (result["orientation"].isValid()) {
glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]),
parentID, parentJointIndex, success);
parentID, parentJointIndex, scalesWithParent, success);
if (success) {
result["orientation"] = quatToVariant(localOrientation);
}
@ -118,7 +118,7 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
}
}
properties = convertOverlayLocationFromScriptSemantics(properties);
properties = convertOverlayLocationFromScriptSemantics(properties, getScalesWithParent());
Overlay::setProperties(properties);
bool needRenderItemUpdate = false;
@ -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") {
@ -335,4 +333,4 @@ SpatialParentTree* Base3DOverlay::getParentTree() const {
void Base3DOverlay::setVisible(bool visible) {
Parent::setVisible(visible);
notifyRenderVariableChange();
}
}

View file

@ -56,12 +56,12 @@ glm::vec3 Line3DOverlay::getEnd() const {
if (_endParentID != QUuid()) {
glm::vec3 localOffset = _direction * _length;
bool success;
worldEnd = localToWorld(localOffset, _endParentID, _endParentJointIndex, success);
worldEnd = localToWorld(localOffset, _endParentID, _endParentJointIndex, getScalesWithParent(), success);
return worldEnd;
}
localEnd = getLocalEnd();
worldEnd = localToWorld(localEnd, getParentID(), getParentJointIndex(), success);
worldEnd = localToWorld(localEnd, getParentID(), getParentJointIndex(), getScalesWithParent(), success);
if (!success) {
qDebug() << "Line3DOverlay::getEnd failed";
}
@ -79,10 +79,10 @@ void Line3DOverlay::setEnd(const glm::vec3& end) {
glm::vec3 offset;
if (_endParentID != QUuid()) {
offset = worldToLocal(end, _endParentID, _endParentJointIndex, success);
offset = worldToLocal(end, _endParentID, _endParentJointIndex, getScalesWithParent(), success);
} else {
localStart = getLocalStart();
localEnd = worldToLocal(end, getParentID(), getParentJointIndex(), success);
localEnd = worldToLocal(end, getParentID(), getParentJointIndex(), getScalesWithParent(), success);
offset = localEnd - localStart;
}
if (!success) {

View file

@ -24,7 +24,6 @@ ModelOverlay::ModelOverlay()
: _model(std::make_shared<Model>(nullptr, this)),
_modelTextures(QVariantMap())
{
_model->init();
_model->setLoadingPriority(_loadPriority);
_isLoaded = false;
}
@ -38,7 +37,6 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
_scaleToFit(modelOverlay->_scaleToFit),
_loadPriority(modelOverlay->_loadPriority)
{
_model->init();
_model->setLoadingPriority(_loadPriority);
if (_url.isValid()) {
_updateModel = true;

View file

@ -138,7 +138,6 @@ Avatar::~Avatar() {
void Avatar::init() {
getHead()->init();
_skeletonModel->init();
_initialized = true;
}

View file

@ -268,6 +268,7 @@ public:
virtual float getModelScale() const { return _modelScale; }
virtual void setModelScale(float scale) { _modelScale = scale; }
virtual glm::vec3 scaleForChildren() const override { return glm::vec3(getModelScale()); }
virtual void setAvatarEntityDataChanged(bool value) override;

View file

@ -349,7 +349,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
float getRadius() const override { return 0.5f * _renderer->getEntity()->getQueryAACube().getScale(); }
uint64_t getTimestamp() const override { return _renderer->getUpdateTime(); }
const EntityRendererPointer& getRenderer() const { return _renderer; }
EntityRendererPointer getRenderer() const { return _renderer; }
private:
EntityRendererPointer _renderer;
};
@ -382,7 +382,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr;
size_t numSorted = sortedRenderables.size();
while (!sortedRenderables.empty() && usecTimestampNow() < expiry) {
const EntityRendererPointer& renderable = sortedRenderables.top().getRenderer();
const auto renderable = sortedRenderables.top().getRenderer();
renderable->updateInScene(scene, transaction);
_renderablesToUpdate.erase(renderable->getEntity()->getID());
sortedRenderables.pop();
@ -612,7 +612,7 @@ static glm::vec2 projectOntoEntityXYPlane(EntityItemPointer entity, const PickRa
glm::vec3 entityPosition = entity->getWorldPosition();
glm::quat entityRotation = entity->getWorldOrientation();
glm::vec3 entityDimensions = entity->getDimensions();
glm::vec3 entityDimensions = entity->getScaledDimensions();
glm::vec3 entityRegistrationPoint = entity->getRegistrationPoint();
// project the intersection point onto the local xy plane of the object.

View file

@ -37,7 +37,7 @@ void LightEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
lightPayload.editBound() = render::Item::Bound();
}
glm::vec3 dimensions = entity->getDimensions();
glm::vec3 dimensions = entity->getScaledDimensions();
float largestDiameter = glm::compMax(dimensions);
light->setMaximumRadius(largestDiameter / 2.0f);

View file

@ -78,11 +78,11 @@ RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityI
RenderableModelEntityItem::~RenderableModelEntityItem() { }
void RenderableModelEntityItem::setDimensions(const glm::vec3& value) {
void RenderableModelEntityItem::setUnscaledDimensions(const glm::vec3& value) {
glm::vec3 newDimensions = glm::max(value, glm::vec3(0.0f)); // can never have negative dimensions
if (getDimensions() != newDimensions) {
if (getUnscaledDimensions() != newDimensions) {
_dimensionsInitialized = true;
ModelEntityItem::setDimensions(value);
ModelEntityItem::setUnscaledDimensions(value);
}
}
@ -124,11 +124,11 @@ void RenderableModelEntityItem::doInitialModelSimulation() {
// translation/rotation/scale/registration. The first two are straightforward, but the latter two have guards to
// make sure they don't happen after they've already been set. Here we reset those guards. This doesn't cause the
// entity values to change -- it just allows the model to match once it comes in.
model->setScaleToFit(false, getDimensions());
model->setScaleToFit(false, getScaledDimensions());
model->setSnapModelToRegistrationPoint(false, getRegistrationPoint());
// now recalculate the bounds and registration
model->setScaleToFit(true, getDimensions());
model->setScaleToFit(true, getScaledDimensions());
model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
model->setRotation(getWorldOrientation());
model->setTranslation(getWorldPosition());
@ -169,7 +169,7 @@ bool RenderableModelEntityItem::needsUpdateModelBounds() const {
return true;
}
if (model->getScaleToFitDimensions() != getDimensions()) {
if (model->getScaleToFitDimensions() != getScaledDimensions()) {
return true;
}
@ -209,17 +209,17 @@ void RenderableModelEntityItem::updateModelBounds() {
updateRenderItems = true;
}
if (model->getScaleToFitDimensions() != getDimensions() ||
if (model->getScaleToFitDimensions() != getScaledDimensions() ||
model->getRegistrationPoint() != getRegistrationPoint()) {
// The machinery for updateModelBounds will give existing models the opportunity to fix their
// translation/rotation/scale/registration. The first two are straightforward, but the latter two
// have guards to make sure they don't happen after they've already been set. Here we reset those guards.
// This doesn't cause the entity values to change -- it just allows the model to match once it comes in.
model->setScaleToFit(false, getDimensions());
model->setScaleToFit(false, getScaledDimensions());
model->setSnapModelToRegistrationPoint(false, getRegistrationPoint());
// now recalculate the bounds and registration
model->setScaleToFit(true, getDimensions());
model->setScaleToFit(true, getScaledDimensions());
model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
updateRenderItems = true;
model->scaleToFit();
@ -372,7 +372,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
const uint32_t QUAD_STRIDE = 4;
ShapeType type = getShapeType();
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
auto model = getModel();
if (type == SHAPE_TYPE_COMPOUND) {
updateModelBounds();
@ -1153,7 +1153,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
return true;
}
if (model->getScaleToFitDimensions() != entity->getDimensions() ||
if (model->getScaleToFitDimensions() != entity->getScaledDimensions() ||
model->getRegistrationPoint() != entity->getRegistrationPoint()) {
return true;
}
@ -1209,7 +1209,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate);
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
model->init();
entity->setModel(model);
withWriteLock([&] { _model = model; });
}

View file

@ -60,7 +60,7 @@ public:
RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized);
virtual ~RenderableModelEntityItem();
virtual void setDimensions(const glm::vec3& value) override;
virtual void setUnscaledDimensions(const glm::vec3& value) override;
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
void doInitialModelSimulation();

View file

@ -303,6 +303,7 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) {
batch.setInputBuffer(0, _verticesBuffer, 0, sizeof(Vertex));
#ifndef POLYLINE_ENTITY_USE_FADE_EFFECT
// glColor4f must be called after setInputFormat if it must be taken into account
if (_isFading) {
batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime));
} else {

View file

@ -213,7 +213,7 @@ void RenderablePolyVoxEntityItem::setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxel
glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const {
glm::vec3 result;
withReadLock([&] {
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
glm::vec3 scale = getScaledDimensions() / _voxelVolumeSize; // meters / voxel-units
if (isEdged(_voxelSurfaceStyle)) {
result = scale / -2.0f;
}
@ -228,7 +228,7 @@ glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const {
voxelVolumeSize = _voxelVolumeSize;
});
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 scale = dimensions / voxelVolumeSize; // meters / voxel-units
bool success; // TODO -- Does this actually have to happen in world space?
glm::vec3 center = getCenterPosition(success); // this handles registrationPoint changes
@ -393,7 +393,7 @@ bool RenderablePolyVoxEntityItem::setSphere(const vec3& centerWorldCoords, float
glm::mat4 vtwMatrix = voxelToWorldMatrix();
glm::mat4 wtvMatrix = glm::inverse(vtwMatrix);
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 voxelSize = dimensions / _voxelVolumeSize;
float smallestDimensionSize = voxelSize.x;
smallestDimensionSize = glm::min(smallestDimensionSize, voxelSize.y);
@ -454,7 +454,7 @@ bool RenderablePolyVoxEntityItem::setCapsule(const vec3& startWorldCoords, const
glm::mat4 vtwMatrix = voxelToWorldMatrix();
glm::mat4 wtvMatrix = glm::inverse(vtwMatrix);
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 voxelSize = dimensions / _voxelVolumeSize;
float smallestDimensionSize = voxelSize.x;
smallestDimensionSize = glm::min(smallestDimensionSize, voxelSize.y);
@ -580,7 +580,7 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
// the PolyVox ray intersection code requires a near and far point.
// set ray cast length to long enough to cover all of the voxel space
float distanceToEntity = glm::distance(origin, getWorldPosition());
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
float largestDimension = glm::compMax(dimensions) * 2.0f;
glm::vec3 farPoint = origin + normDirection * (distanceToEntity + largestDimension);
@ -668,7 +668,7 @@ bool RenderablePolyVoxEntityItem::isReadyToComputeShape() const {
void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) {
ShapeType shapeType = getShapeType();
if (shapeType == SHAPE_TYPE_NONE) {
info.setParams(getShapeType(), 0.5f * getDimensions());
info.setParams(getShapeType(), 0.5f * getScaledDimensions());
return;
}

View file

@ -63,7 +63,7 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
return true;
}
if (_dimensions != entity->getDimensions()) {
if (_dimensions != entity->getScaledDimensions()) {
return true;
}
@ -82,7 +82,7 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
_shape = entity->getShape();
_position = entity->getWorldPosition();
_dimensions = entity->getDimensions();
_dimensions = entity->getScaledDimensions();
_orientation = entity->getWorldOrientation();
_renderTransform = getModelTransform();
@ -137,11 +137,10 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
});
if (proceduralRender) {
batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
geometryCache->renderWireShape(batch, geometryShape);
geometryCache->renderWireShape(batch, geometryShape, outColor);
} else {
geometryCache->renderShape(batch, geometryShape);
geometryCache->renderShape(batch, geometryShape, outColor);
}
} else {
// FIXME, support instanced multi-shape rendering using multidraw indirect

View file

@ -54,7 +54,7 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return true;
}
if (_dimensions != entity->getDimensions()) {
if (_dimensions != entity->getScaledDimensions()) {
return true;
}
@ -67,7 +67,7 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
_textColor = toGlm(entity->getTextColorX());
_backgroundColor = toGlm(entity->getBackgroundColorX());
_dimensions = entity->getDimensions();
_dimensions = entity->getScaledDimensions();
_faceCamera = entity->getFaceCamera();
_lineHeight = entity->getLineHeight();
_text = entity->getText();

View file

@ -126,7 +126,10 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
withWriteLock([&] {
// This work must be done on the main thread
if (!hasWebSurface()) {
buildWebSurface(entity);
// If we couldn't create a new web surface, exit
if (!buildWebSurface(entity)) {
return;
}
}
if (_contextPosition != entity->getWorldPosition()) {
@ -146,7 +149,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
glm::vec2 windowSize = getWindowSize(entity);
_webSurface->resize(QSize(windowSize.x, windowSize.y));
_renderTransform = getModelTransform();
_renderTransform.postScale(entity->getDimensions());
_renderTransform.postScale(entity->getScaledDimensions());
});
}
@ -190,7 +193,6 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
});
batch.setResourceTexture(0, _texture);
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
DependencyManager::get<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
@ -277,7 +279,7 @@ void WebEntityRenderer::destroyWebSurface() {
}
glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) const {
glm::vec2 dims = glm::vec2(entity->getDimensions());
glm::vec2 dims = glm::vec2(entity->getScaledDimensions());
dims *= METERS_TO_INCHES * _lastDPI;
// ensure no side is never larger then MAX_WINDOW_SIZE

View file

@ -208,7 +208,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
entity->resetRenderingPropertiesChanged();
_lastPosition = entity->getWorldPosition();
_lastRotation = entity->getWorldOrientation();
_lastDimensions = entity->getDimensions();
_lastDimensions = entity->getScaledDimensions();
_keyLightProperties = entity->getKeyLightProperties();
_skyboxProperties = entity->getSkyboxProperties();
@ -280,7 +280,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
if (entity->getWorldPosition() != _lastPosition) {
return true;
}
if (entity->getDimensions() != _lastDimensions) {
if (entity->getScaledDimensions() != _lastDimensions) {
return true;
}
if (entity->getWorldOrientation() != _lastRotation) {

View file

@ -50,6 +50,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
{
setLocalVelocity(ENTITY_ITEM_DEFAULT_VELOCITY);
setLocalAngularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY);
setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS);
// explicitly set transform parts to set dirty flags used by batch rendering
locationChanged();
dimensionsChanged();
@ -243,7 +244,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getLocalAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ACCELERATION, getAcceleration());
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getDimensions());
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, getUnscaledDimensions());
APPEND_ENTITY_PROPERTY(PROP_DENSITY, getDensity());
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, getGravity());
APPEND_ENTITY_PROPERTY(PROP_DAMPING, getDamping());
@ -685,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,
@ -779,7 +780,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, customSetAcceleration);
}
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, setDimensions);
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, setUnscaledDimensions);
READ_ENTITY_PROPERTY(PROP_DENSITY, float, setDensity);
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, setGravity);
@ -898,7 +899,7 @@ void EntityItem::debugDump() const {
qCDebug(entities) << "EntityItem id:" << getEntityItemID();
qCDebug(entities, " edited ago:%f", (double)getEditedAgo());
qCDebug(entities, " position:%f,%f,%f", (double)position.x, (double)position.y, (double)position.z);
qCDebug(entities) << " dimensions:" << getDimensions();
qCDebug(entities) << " dimensions:" << getScaledDimensions();
}
// adjust any internal timestamps to fix clock skew for this server
@ -923,7 +924,7 @@ void EntityItem::adjustEditPacketForClockSkew(QByteArray& buffer, qint64 clockSk
}
float EntityItem::computeMass() const {
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
return getDensity() * _volumeMultiplier * dimensions.x * dimensions.y * dimensions.z;
}
@ -942,7 +943,7 @@ void EntityItem::setMass(float mass) {
// we must protect the density range to help maintain stability of physics simulation
// therefore this method might not accept the mass that is supplied.
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
float volume = _volumeMultiplier * dimensions.x * dimensions.y * dimensions.z;
// compute new density
@ -1178,7 +1179,7 @@ bool EntityItem::wantTerseEditLogging() const {
glm::mat4 EntityItem::getEntityToWorldMatrix() const {
glm::mat4 translation = glm::translate(getWorldPosition());
glm::mat4 rotation = glm::mat4_cast(getWorldOrientation());
glm::mat4 scale = glm::scale(getDimensions());
glm::mat4 scale = glm::scale(getScaledDimensions());
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
return translation * rotation * scale * registration;
}
@ -1218,7 +1219,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(simulationOwner, getSimulationOwner);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getLocalPosition);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensions); // NOTE: radius is obsolete
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getUnscaledDimensions);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getLocalOrientation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(density, getDensity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getLocalVelocity);
@ -1326,7 +1327,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(acceleration, setAcceleration);
// these (along with "position" above) affect tree structure
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensions);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setUnscaledDimensions);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
// these (along with all properties above) affect the simulation
@ -1429,7 +1430,7 @@ void EntityItem::recordCreationTime() {
const Transform EntityItem::getTransformToCenter(bool& success) const {
Transform result = getTransform(success);
if (getRegistrationPoint() != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center
result.postTranslate((ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()) * getDimensions()); // Position to center
result.postTranslate((ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()) * getScaledDimensions()); // Position to center
}
return result;
}
@ -1445,7 +1446,7 @@ AACube EntityItem::getMaximumAACube(bool& success) const {
// we want to compute the furthestExtent that an entity can extend out from its "position"
// to do this we compute the max of these two vec3s: registration and 1-registration
// and then scale by dimensions
glm::vec3 maxExtents = getDimensions() * glm::max(_registrationPoint, glm::vec3(1.0f) - _registrationPoint);
glm::vec3 maxExtents = getScaledDimensions() * glm::max(_registrationPoint, glm::vec3(1.0f) - _registrationPoint);
// there exists a sphere that contains maxExtents for all rotations
float radius = glm::length(maxExtents);
@ -1470,7 +1471,7 @@ AACube EntityItem::getMinimumAACube(bool& success) const {
glm::vec3 position = getWorldPosition(success);
if (success) {
_recalcMinAACube = false;
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 unrotatedMinRelativeToEntity = - (dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint);
Extents extents = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
@ -1500,7 +1501,7 @@ AABox EntityItem::getAABox(bool& success) const {
glm::vec3 position = getWorldPosition(success);
if (success) {
_recalcAABox = false;
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 unrotatedMinRelativeToEntity = - (dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint);
Extents extents = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
@ -1540,12 +1541,12 @@ bool EntityItem::shouldPuffQueryAACube() const {
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
// ... radius = sqrt(3 x maxDimension ^ 2) / 2.0f;
float EntityItem::getRadius() const {
return 0.5f * glm::length(getDimensions());
return 0.5f * glm::length(getScaledDimensions());
}
void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const {
if (_registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) {
glm::mat4 scale = glm::scale(getDimensions());
glm::mat4 scale = glm::scale(getScaledDimensions());
glm::mat4 registration = scale * glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
glm::vec3 regTransVec = glm::vec3(registration[3]); // extract position component from matrix
info.setOffset(regTransVec);
@ -1566,12 +1567,12 @@ bool EntityItem::contains(const glm::vec3& point) const {
}
void EntityItem::computeShapeInfo(ShapeInfo& info) {
info.setParams(getShapeType(), 0.5f * getDimensions());
info.setParams(getShapeType(), 0.5f * getScaledDimensions());
adjustShapeInfoByRegistration(info);
}
float EntityItem::getVolumeEstimate() const {
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
return dimensions.x * dimensions.y * dimensions.z;
}
@ -1671,11 +1672,21 @@ void EntityItem::setParentID(const QUuid& value) {
}
}
void EntityItem::setDimensions(const glm::vec3& value) {
glm::vec3 EntityItem::getScaledDimensions() const {
glm::vec3 scale = getSNScale();
return _unscaledDimensions * scale;
}
void EntityItem::setScaledDimensions(const glm::vec3& value) {
glm::vec3 parentScale = getSNScale();
setUnscaledDimensions(value * parentScale);
}
void EntityItem::setUnscaledDimensions(const glm::vec3& value) {
glm::vec3 newDimensions = glm::max(value, glm::vec3(0.0f)); // can never have negative dimensions
if (getDimensions() != newDimensions) {
if (getUnscaledDimensions() != newDimensions) {
withWriteLock([&] {
_dimensions = newDimensions;
_unscaledDimensions = newDimensions;
});
locationChanged();
dimensionsChanged();
@ -2071,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;
@ -2098,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;
}
@ -2366,6 +2376,16 @@ void EntityItem::dimensionsChanged() {
somethingChangedNotification();
}
bool EntityItem::getScalesWithParent() const {
// keep this logic the same as in EntityItemProperties::getScalesWithParent
if (getClientOnly()) {
QUuid ancestorID = findAncestorOfType(NestableType::Avatar);
return !ancestorID.isNull();
} else {
return false;
}
}
void EntityItem::globalizeProperties(EntityItemProperties& properties, const QString& messageTemplate, const glm::vec3& offset) const {
// TODO -- combine this with convertLocationToScriptSemantics
bool success;
@ -2373,7 +2393,7 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt
if (success) {
properties.setPosition(globalPosition + offset);
properties.setRotation(getWorldOrientation());
properties.setDimensions(getDimensions());
properties.setDimensions(getScaledDimensions());
// Should we do velocities and accelerations, too? This could end up being quite involved, which is why the method exists.
} else {
properties.setPosition(getQueryAACube().calcCenter() + offset); // best we can do

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,
@ -180,8 +180,11 @@ public:
void setDescription(const QString& value);
/// Dimensions in meters (0.0 - TREE_SCALE)
inline const glm::vec3 getDimensions() const { return _dimensions; }
virtual void setDimensions(const glm::vec3& value);
glm::vec3 getScaledDimensions() const;
virtual void setScaledDimensions(const glm::vec3& value);
inline const glm::vec3 getUnscaledDimensions() const { return _unscaledDimensions; }
virtual void setUnscaledDimensions(const glm::vec3& value);
float getLocalRenderAlpha() const;
void setLocalRenderAlpha(float localRenderAlpha);
@ -456,6 +459,8 @@ public:
virtual void locationChanged(bool tellPhysics = true) override;
virtual bool getScalesWithParent() const override;
using ChangeHandlerCallback = std::function<void(const EntityItemID&)>;
using ChangeHandlerId = QUuid;
ChangeHandlerId registerChangeHandler(const ChangeHandlerCallback& handler);
@ -477,7 +482,7 @@ protected:
virtual void dimensionsChanged() override;
glm::vec3 _dimensions { ENTITY_ITEM_DEFAULT_DIMENSIONS };
glm::vec3 _unscaledDimensions { ENTITY_ITEM_DEFAULT_DIMENSIONS };
EntityTypes::EntityType _type { EntityTypes::Unknown };
quint64 _lastSimulated { 0 }; // last time this entity called simulate(), this includes velocity, angular velocity,
// and physics changes

View file

@ -365,6 +365,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_LOCAL_ROTATION, localRotation);
CHECK_PROPERTY_CHANGE(PROP_LOCAL_VELOCITY, localVelocity);
CHECK_PROPERTY_CHANGE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity);
CHECK_PROPERTY_CHANGE(PROP_LOCAL_DIMENSIONS, localDimensions);
CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed);
CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
@ -628,6 +629,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_VELOCITY, localVelocity);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID);
@ -805,6 +807,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(localRotation, glmQuat, setLocalRotation);
COPY_PROPERTY_FROM_QSCRIPTVALUE(localVelocity, glmVec3, setLocalVelocity);
COPY_PROPERTY_FROM_QSCRIPTVALUE(localAngularVelocity, glmVec3, setLocalAngularVelocity);
COPY_PROPERTY_FROM_QSCRIPTVALUE(localDimensions, glmVec3, setLocalDimensions);
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotationsSet, qVectorBool, setJointRotationsSet);
COPY_PROPERTY_FROM_QSCRIPTVALUE(jointRotations, qVectorQuat, setJointRotations);
@ -953,6 +956,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(localRotation);
COPY_PROPERTY_IF_CHANGED(localVelocity);
COPY_PROPERTY_IF_CHANGED(localAngularVelocity);
COPY_PROPERTY_IF_CHANGED(localDimensions);
COPY_PROPERTY_IF_CHANGED(jointRotationsSet);
COPY_PROPERTY_IF_CHANGED(jointRotations);
@ -1132,6 +1136,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glm::quat);
ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector<bool>);
ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector<glm::quat>);
@ -2470,9 +2475,25 @@ bool EntityItemProperties::transformChanged() const {
localPositionChanged() || localRotationChanged();
}
bool EntityItemProperties::getScalesWithParent() const {
// keep this logic the same as in EntityItem::getScalesWithParent
bool scalesWithParent { false };
if (parentIDChanged()) {
bool success;
SpatiallyNestablePointer parent = SpatiallyNestable::findByID(getParentID(), success);
if (success && parent) {
bool avatarAncestor = (parent->getNestableType() == NestableType::Avatar ||
parent->hasAncestorOfType(NestableType::Avatar));
scalesWithParent = getClientOnly() && avatarAncestor;
}
}
return scalesWithParent;
}
bool EntityItemProperties::parentRelatedPropertyChanged() const {
return positionChanged() || rotationChanged() ||
localPositionChanged() || localRotationChanged() ||
localDimensionsChanged() ||
parentIDChanged() || parentJointIndexChanged();
}
@ -2530,7 +2551,8 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte
return false;
}
const unsigned char* key = reinterpret_cast<const unsigned char*>(publicKey.toUtf8().constData());
auto keyByteArray = publicKey.toUtf8();
auto key = keyByteArray.constData();
int keyLength = publicKey.length();
BIO *bio = BIO_new_mem_buf((void*)key, keyLength);
@ -2548,19 +2570,23 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte
// ECSDA verification prototype: note that type is currently ignored
// int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen,
// const unsigned char *sig, int siglen, EC_KEY *eckey);
bool answer = ECDSA_verify(0,
int answer = ECDSA_verify(0,
digest,
digestLength,
signature,
signatureLength,
ec);
long error = ERR_get_error();
if (error != 0) {
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str
if (error != 0 || answer == -1) {
qCWarning(entities) << "ERROR while verifying signature!"
<< "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength
<< "\nDigest:" << digest << "\nDigest Length:" << digestLength
<< "\nSignature:" << signature << "\nSignature Length:" << signatureLength;
while (error != 0) {
const char* error_str = ERR_error_string(error, NULL);
qCWarning(entities) << "EC error:" << error_str;
error = ERR_get_error();
}
}
EC_KEY_free(ec);
if (bio) {
@ -2569,7 +2595,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte
if (evp_key) {
EVP_PKEY_free(evp_key);
}
return answer;
return (answer == 1);
} else {
if (bio) {
BIO_free(bio);

View file

@ -88,6 +88,7 @@ public:
EntityPropertyFlags getChangedProperties() const;
bool transformChanged() const;
bool getScalesWithParent() const;
bool parentRelatedPropertyChanged() const;
bool queryAACubeRelatedPropertyChanged() const;
@ -227,6 +228,7 @@ public:
DEFINE_PROPERTY_REF(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glmQuat, ENTITY_ITEM_DEFAULT_ROTATION);
DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glmVec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glmVec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glmVec3, ENTITY_ITEM_ZERO_VEC3);
DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector<bool>, QVector<bool>());
DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector<glm::quat>, QVector<glm::quat>());

View file

@ -220,6 +220,8 @@ enum EntityPropertyList {
PROP_HAZE_KEYLIGHT_RANGE,
PROP_HAZE_KEYLIGHT_ALTITUDE,
PROP_LOCAL_DIMENSIONS, // only used to convert values to and from scripts
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line
PROP_AFTER_LAST_ITEM,

View file

@ -117,7 +117,8 @@ void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) {
}
}
EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties& entitySideProperties) {
EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperties& entitySideProperties,
bool scalesWithParent) {
// In EntityTree code, properties.position and properties.rotation are relative to the parent. In javascript,
// they are in world-space. The local versions are put into localPosition and localRotation and position and
// rotation are converted from local to world space.
@ -126,35 +127,48 @@ EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties
scriptSideProperties.setLocalRotation(entitySideProperties.getRotation());
scriptSideProperties.setLocalVelocity(entitySideProperties.getLocalVelocity());
scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getLocalAngularVelocity());
scriptSideProperties.setLocalDimensions(entitySideProperties.getDimensions());
bool success;
glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
scalesWithParent,
success);
glm::quat worldRotation = SpatiallyNestable::localToWorld(entitySideProperties.getRotation(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
scalesWithParent,
success);
glm::vec3 worldVelocity = SpatiallyNestable::localToWorldVelocity(entitySideProperties.getVelocity(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
scalesWithParent,
success);
glm::vec3 worldAngularVelocity = SpatiallyNestable::localToWorldAngularVelocity(entitySideProperties.getAngularVelocity(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
scalesWithParent,
success);
glm::vec3 worldDimensions = SpatiallyNestable::localToWorldDimensions(entitySideProperties.getDimensions(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
scalesWithParent,
success);
scriptSideProperties.setPosition(worldPosition);
scriptSideProperties.setRotation(worldRotation);
scriptSideProperties.setVelocity(worldVelocity);
scriptSideProperties.setAngularVelocity(worldAngularVelocity);
scriptSideProperties.setDimensions(worldDimensions);
return scriptSideProperties;
}
EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperties& scriptSideProperties) {
EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties,
bool scalesWithParent) {
// convert position and rotation properties from world-space to local, unless localPosition and localRotation
// are set. If they are set, they overwrite position and rotation.
EntityItemProperties entitySideProperties = scriptSideProperties;
@ -168,7 +182,7 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
glm::vec3 localPosition = SpatiallyNestable::worldToLocal(entitySideProperties.getPosition(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
success);
scalesWithParent, success);
entitySideProperties.setPosition(localPosition);
}
@ -178,7 +192,7 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
glm::quat localRotation = SpatiallyNestable::worldToLocal(entitySideProperties.getRotation(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
success);
scalesWithParent, success);
entitySideProperties.setRotation(localRotation);
}
@ -188,7 +202,7 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
glm::vec3 localVelocity = SpatiallyNestable::worldToLocalVelocity(entitySideProperties.getVelocity(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
success);
scalesWithParent, success);
entitySideProperties.setVelocity(localVelocity);
}
@ -199,10 +213,20 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
SpatiallyNestable::worldToLocalAngularVelocity(entitySideProperties.getAngularVelocity(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
success);
scalesWithParent, success);
entitySideProperties.setAngularVelocity(localAngularVelocity);
}
if (scriptSideProperties.localDimensionsChanged()) {
entitySideProperties.setDimensions(scriptSideProperties.getLocalDimensions());
} else if (scriptSideProperties.dimensionsChanged()) {
glm::vec3 localDimensions = SpatiallyNestable::worldToLocalDimensions(entitySideProperties.getDimensions(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex(),
scalesWithParent, success);
entitySideProperties.setDimensions(localDimensions);
}
return entitySideProperties;
}
@ -212,9 +236,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
_activityTracking.addedEntityCount++;
EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties);
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
EntityItemProperties propertiesWithSimID = properties;
if (clientOnly) {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
@ -222,6 +244,11 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
propertiesWithSimID.setOwningAvatarID(myNodeID);
}
bool scalesWithParent = propertiesWithSimID.getScalesWithParent();
propertiesWithSimID = convertPropertiesFromScriptSemantics(propertiesWithSimID, scalesWithParent);
propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged());
auto dimensions = propertiesWithSimID.getDimensions();
float volume = dimensions.x * dimensions.y * dimensions.z;
auto density = propertiesWithSimID.getDensity();
@ -295,15 +322,20 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
PROFILE_RANGE(script_entities, __FUNCTION__);
bool scalesWithParent { false };
EntityItemProperties results;
if (_entityTree) {
_entityTree->withReadLock([&] {
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
if (entity) {
scalesWithParent = entity->getScalesWithParent();
if (desiredProperties.getHasProperty(PROP_POSITION) ||
desiredProperties.getHasProperty(PROP_ROTATION) ||
desiredProperties.getHasProperty(PROP_LOCAL_POSITION) ||
desiredProperties.getHasProperty(PROP_LOCAL_ROTATION)) {
desiredProperties.getHasProperty(PROP_LOCAL_ROTATION) ||
desiredProperties.getHasProperty(PROP_LOCAL_VELOCITY) ||
desiredProperties.getHasProperty(PROP_LOCAL_ANGULAR_VELOCITY) ||
desiredProperties.getHasProperty(PROP_LOCAL_DIMENSIONS)) {
// if we are explicitly getting position or rotation, we need parent information to make sense of them.
desiredProperties.setHasProperty(PROP_PARENT_ID);
desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX);
@ -316,6 +348,9 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
desiredProperties = entity->getEntityProperties(params);
desiredProperties.setHasProperty(PROP_LOCAL_POSITION);
desiredProperties.setHasProperty(PROP_LOCAL_ROTATION);
desiredProperties.setHasProperty(PROP_LOCAL_VELOCITY);
desiredProperties.setHasProperty(PROP_LOCAL_ANGULAR_VELOCITY);
desiredProperties.setHasProperty(PROP_LOCAL_DIMENSIONS);
}
results = entity->getProperties(desiredProperties);
@ -323,7 +358,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
});
}
return convertLocationToScriptSemantics(results);
return convertPropertiesToScriptSemantics(results, scalesWithParent);
}
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
@ -390,10 +425,13 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) {
properties.setRotation(entity->getWorldOrientation());
}
if (!scriptSideProperties.localDimensionsChanged() && !scriptSideProperties.dimensionsChanged()) {
properties.setDimensions(entity->getScaledDimensions());
}
}
properties = convertLocationFromScriptSemantics(properties);
properties.setClientOnly(entity->getClientOnly());
properties.setOwningAvatarID(entity->getOwningAvatarID());
properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent());
float cost = calculateCost(density * volume, oldVelocity, newVelocity);
cost *= costMultiplier;
@ -529,7 +567,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
return;
}
auto dimensions = entity->getDimensions();
auto dimensions = entity->getScaledDimensions();
float volume = dimensions.x * dimensions.y * dimensions.z;
auto density = entity->getDensity();
auto velocity = entity->getWorldVelocity().length();

View file

@ -1189,13 +1189,15 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
key = sent.second;
}
QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key.insert(64, "\n") + "\n-----END PUBLIC KEY-----";
bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8());
QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key.insert(64, "\n") + "\n-----END PUBLIC KEY-----\n";
QByteArray hashedActualNonce = QCryptographicHash::hash(QByteArray(actualNonce.toUtf8()), QCryptographicHash::Sha256);
bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8()));
if (verificationSuccess) {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded.";
} else {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for nonce" << actualNonce << "key" << key << "signature" << nonce;
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce <<
"\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key;
}
return verificationSuccess;

View file

@ -667,7 +667,7 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = entity->getDimensions();
glm::vec3 dimensions = entity->getScaledDimensions();
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
@ -763,7 +763,7 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
glm::vec3 penetration;
if (!success || entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
glm::vec3 dimensions = entity->getDimensions();
glm::vec3 dimensions = entity->getScaledDimensions();
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably dull actuall hull testing if they wanted to

View file

@ -41,16 +41,16 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID) : EntityItem(
_color[RED_INDEX] = _color[GREEN_INDEX] = _color[BLUE_INDEX] = 0;
}
void LightEntityItem::setDimensions(const glm::vec3& value) {
void LightEntityItem::setUnscaledDimensions(const glm::vec3& value) {
if (_isSpotlight) {
// If we are a spotlight, treat the z value as our radius or length, and
// recalculate the x/y dimensions to properly encapsulate the spotlight.
const float length = value.z;
const float width = length * glm::sin(glm::radians(_cutoff));
EntityItem::setDimensions(glm::vec3(width, width, length));
EntityItem::setUnscaledDimensions(glm::vec3(width, width, length));
} else {
float maxDimension = glm::compMax(value);
EntityItem::setDimensions(glm::vec3(maxDimension, maxDimension, maxDimension));
EntityItem::setUnscaledDimensions(glm::vec3(maxDimension, maxDimension, maxDimension));
}
}
@ -98,7 +98,7 @@ void LightEntityItem::setIsSpotlight(bool value) {
return;
}
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 newDimensions;
if (value) {
const float length = dimensions.z;
@ -112,7 +112,7 @@ void LightEntityItem::setIsSpotlight(bool value) {
_isSpotlight = value;
_lightPropertiesChanged = true;
});
setDimensions(newDimensions);
setScaledDimensions(newDimensions);
}
void LightEntityItem::setCutoff(float value) {
@ -128,9 +128,9 @@ void LightEntityItem::setCutoff(float value) {
if (getIsSpotlight()) {
// If we are a spotlight, adjusting the cutoff will affect the area we encapsulate,
// so update the dimensions to reflect this.
const float length = getDimensions().z;
const float length = getScaledDimensions().z;
const float width = length * glm::sin(glm::radians(_cutoff));
setDimensions(glm::vec3(width, width, length));
setScaledDimensions(glm::vec3(width, width, length));
}
withWriteLock([&] {

View file

@ -29,7 +29,7 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value) override;
virtual void setUnscaledDimensions(const glm::vec3& value) override;
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual bool setSubClassProperties(const EntityItemProperties& properties) override;

View file

@ -80,7 +80,7 @@ bool LineEntityItem::appendPoint(const glm::vec3& point) {
qCDebug(entities) << "MAX POINTS REACHED!";
return false;
}
glm::vec3 halfBox = getDimensions() * 0.5f;
glm::vec3 halfBox = getScaledDimensions() * 0.5f;
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
qCDebug(entities) << "Point is outside entity's bounding box";
return false;
@ -96,7 +96,7 @@ bool LineEntityItem::setLinePoints(const QVector<glm::vec3>& points) {
if (points.size() > MAX_POINTS_PER_LINE) {
return false;
}
glm::vec3 halfBox = getDimensions() * 0.5f;
glm::vec3 halfBox = getScaledDimensions() * 0.5f;
for (int i = 0; i < points.size(); i++) {
glm::vec3 point = points.at(i);
if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
@ -157,7 +157,7 @@ void LineEntityItem::debugDump() const {
qCDebug(entities) << " LINE EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

View file

@ -289,7 +289,7 @@ void ModelEntityItem::debugDump() const {
qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID();
qCDebug(entities) << " edited ago:" << getEditedAgo();
qCDebug(entities) << " position:" << getWorldPosition();
qCDebug(entities) << " dimensions:" << getDimensions();
qCDebug(entities) << " dimensions:" << getScaledDimensions();
qCDebug(entities) << " model URL:" << getModelURL();
qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL();
}

View file

@ -353,7 +353,7 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() {
float maxDistanceValue = glm::compMax(maxDistance);
//times 2 because dimensions are diameters not radii
glm::vec3 dims(2.0f * maxDistanceValue);
EntityItem::setDimensions(dims);
EntityItem::setScaledDimensions(dims);
}
@ -593,7 +593,7 @@ void ParticleEffectEntityItem::debugDump() const {
_particleProperties.color.gradient.target.green << "," <<
_particleProperties.color.gradient.target.blue;
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

View file

@ -191,7 +191,7 @@ void PolyLineEntityItem::calculateScaleAndRegistrationPoint() {
}
// if Polyline has only one or fewer points, use default dimension settings
setDimensions(newScale);
setScaledDimensions(newScale);
EntityItem::setRegistrationPoint(newRegistrationPoint);
}
@ -257,7 +257,7 @@ void PolyLineEntityItem::debugDump() const {
qCDebug(entities) << " QUAD EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}

View file

@ -229,7 +229,7 @@ void PolyVoxEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " POLYVOX EntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
}
@ -377,7 +377,7 @@ EntityItemID PolyVoxEntityItem::getZPNeighborID() const {
glm::vec3 PolyVoxEntityItem::getSurfacePositionAdjustment() const {
glm::vec3 result;
withReadLock([&] {
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
glm::vec3 scale = getScaledDimensions() / _voxelVolumeSize; // meters / voxel-units
if (isEdged()) {
result = scale / -2.0f;
}
@ -392,7 +392,7 @@ glm::mat4 PolyVoxEntityItem::voxelToLocalMatrix() const {
voxelVolumeSize = _voxelVolumeSize;
});
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 scale = dimensions / voxelVolumeSize; // meters / voxel-units
bool success; // TODO -- Does this actually have to happen in world space?
glm::vec3 center = getCenterPosition(success); // this handles registrationPoint changes

View file

@ -106,11 +106,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) {
break;
case entity::Shape::Circle:
// Circle is implicitly flat so we enforce flat dimensions
setDimensions(getDimensions());
setUnscaledDimensions(getUnscaledDimensions());
break;
case entity::Shape::Quad:
// Quad is implicitly flat so we enforce flat dimensions
setDimensions(getDimensions());
setUnscaledDimensions(getUnscaledDimensions());
break;
default:
_type = EntityTypes::Shape;
@ -204,15 +204,15 @@ void ShapeEntityItem::setColor(const QColor& value) {
setAlpha(value.alpha());
}
void ShapeEntityItem::setDimensions(const glm::vec3& value) {
void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) {
const float MAX_FLAT_DIMENSION = 0.0001f;
if ((_shape == entity::Shape::Circle || _shape == entity::Shape::Quad) && value.y > MAX_FLAT_DIMENSION) {
if ((_shape == entity::Shape::Circle || _shape == entity::Shape::Quad) && value.y > MAX_FLAT_DIMENSION) {
// enforce flatness in Y
glm::vec3 newDimensions = value;
newDimensions.y = MAX_FLAT_DIMENSION;
EntityItem::setDimensions(newDimensions);
} else {
EntityItem::setDimensions(value);
EntityItem::setUnscaledDimensions(newDimensions);
} else {
EntityItem::setUnscaledDimensions(value);
}
}
@ -256,7 +256,7 @@ void ShapeEntityItem::debugDump() const {
qCDebug(entities) << " collisionShapeType:" << ShapeInfo::getNameForShapeType(getShapeType());
qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2];
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
qCDebug(entities) << "SHAPE EntityItem Ptr:" << this;
}
@ -266,7 +266,7 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
// This will be called whenever DIRTY_SHAPE flag (set by dimension change, etc)
// is set.
const glm::vec3 entityDimensions = getDimensions();
const glm::vec3 entityDimensions = getScaledDimensions();
switch (_shape){
case entity::Shape::Quad:

View file

@ -80,7 +80,7 @@ public:
const rgbColor& getColor() const { return _color; }
void setColor(const rgbColor& value);
void setDimensions(const glm::vec3& value) override;
void setUnscaledDimensions(const glm::vec3& value) override;
xColor getXColor() const;
void setColor(const xColor& value);

View file

@ -41,9 +41,9 @@ TextEntityItem::TextEntityItem(const EntityItemID& entityItemID) : EntityItem(en
const float TEXT_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
void TextEntityItem::setDimensions(const glm::vec3& value) {
void TextEntityItem::setUnscaledDimensions(const glm::vec3& value) {
// NOTE: Text Entities always have a "depth" of 1cm.
EntityItem::setDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH));
EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH));
}
EntityItemProperties TextEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
@ -132,7 +132,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking) const {
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
glm::quat rotation = getWorldOrientation();
glm::vec3 position = getWorldPosition() + rotation *

View file

@ -23,7 +23,7 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value) override;
virtual void setUnscaledDimensions(const glm::vec3& value) override;
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity

View file

@ -36,9 +36,9 @@ WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(enti
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
void WebEntityItem::setDimensions(const glm::vec3& value) {
void WebEntityItem::setUnscaledDimensions(const glm::vec3& value) {
// NOTE: Web Entities always have a "depth" of 1cm.
EntityItem::setDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH));
EntityItem::setUnscaledDimensions(glm::vec3(value.x, value.y, WEB_ENTITY_ITEM_FIXED_DEPTH));
}
EntityItemProperties WebEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
@ -109,7 +109,7 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking) const {
glm::vec3 dimensions = getDimensions();
glm::vec3 dimensions = getScaledDimensions();
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
glm::quat rotation = getWorldOrientation();
glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));

View file

@ -22,7 +22,7 @@ public:
ALLOW_INSTANTIATION // This class can be instantiated
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value) override;
virtual void setUnscaledDimensions(const glm::vec3& value) override;
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity

View file

@ -242,7 +242,7 @@ void ZoneEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
qCDebug(entities) << " _backgroundMode:" << EntityItemProperties::getBackgroundModeString(_backgroundMode);
qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getHazeModeString(_hazeMode);

View file

@ -605,6 +605,10 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) {
if (_input._colorAttribute != newColor) {
_input._colorAttribute = newColor;
glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r);
// Color has been changed and is not white. To prevent colors from bleeding
// between different objects, we need to set the _hadColorAttribute flag
// as if a previous render call had potential colors
_input._hadColorAttribute = (newColor != glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
}
(void)CHECK_GL_ERROR();
}

View file

@ -253,6 +253,7 @@ protected:
struct InputStageState {
bool _invalidFormat { true };
bool _hadColorAttribute{ true };
Stream::FormatPointer _format;
std::string _formatKey;

View file

@ -62,6 +62,8 @@ void GL41Backend::updateInput() {
// now we need to bind the buffers and assign the attrib pointers
if (_input._format) {
bool hasColorAttribute{ false };
const Buffers& buffers = _input._buffers;
const Offsets& offsets = _input._bufferOffsets;
const Offsets& strides = _input._bufferStrides;
@ -98,6 +100,8 @@ void GL41Backend::updateInput() {
uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]);
GLboolean isNormalized = attrib._element.isNormalized();
hasColorAttribute = hasColorAttribute || (slot == Stream::COLOR);
for (size_t locNum = 0; locNum < locationCount; ++locNum) {
if (attrib._element.isInteger()) {
glVertexAttribIPointer(slot + (GLuint)locNum, count, type, stride,
@ -117,6 +121,15 @@ void GL41Backend::updateInput() {
}
}
}
if (_input._hadColorAttribute && !hasColorAttribute) {
// The previous input stage had a color attribute but this one doesn't so reset
// color to pure white.
const auto white = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
glVertexAttrib4fv(Stream::COLOR, &white.r);
_input._colorAttribute = white;
}
_input._hadColorAttribute = hasColorAttribute;
}
// everything format related should be in sync now
_input._invalidFormat = false;

View file

@ -32,6 +32,8 @@ void GL45Backend::updateInput() {
// Assign the vertex format required
if (_input._format) {
bool hasColorAttribute{ false };
_input._attribBindingBuffers.reset();
const Stream::Format::AttributeMap& attributes = _input._format->getAttributes();
@ -54,6 +56,9 @@ void GL45Backend::updateInput() {
GLboolean isNormalized = attrib._element.isNormalized();
GLenum perLocationSize = attrib._element.getLocationSize();
hasColorAttribute = hasColorAttribute || (slot == Stream::COLOR);
for (GLuint locNum = 0; locNum < locationCount; ++locNum) {
GLuint attriNum = (GLuint)(slot + locNum);
newActivation.set(attriNum);
@ -84,6 +89,15 @@ void GL45Backend::updateInput() {
glVertexBindingDivisor(bufferChannelNum, frequency);
#endif
}
if (_input._hadColorAttribute && !hasColorAttribute) {
// The previous input stage had a color attribute but this one doesn't so reset
// color to pure white.
const auto white = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
glVertexAttrib4fv(Stream::COLOR, &white.r);
_input._colorAttribute = white;
}
_input._hadColorAttribute = hasColorAttribute;
}
// Manage Activation what was and what is expected now

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

@ -24,8 +24,7 @@ SentPacketHistory::SentPacketHistory(int size)
}
void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& packet) {
void SentPacketHistory::untrackedPacketSent(uint16_t sequenceNumber) {
// check if given seq number has the expected value. if not, something's wrong with
// the code calling this function
uint16_t expectedSequenceNumber = _newestSequenceNumber + (uint16_t)1;
@ -34,6 +33,10 @@ void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& pack
<< "Expected:" << expectedSequenceNumber << "Actual:" << sequenceNumber;
}
_newestSequenceNumber = sequenceNumber;
}
void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& packet) {
untrackedPacketSent(sequenceNumber);
QWriteLocker locker(&_packetsLock);
_sentPackets.insert(NLPacket::createCopy(packet));

View file

@ -27,6 +27,8 @@ class SentPacketHistory {
public:
SentPacketHistory(int size = MAX_REASONABLE_SEQUENCE_GAP);
void untrackedPacketSent(uint16_t sequenceNumber);
void packetSent(uint16_t sequenceNumber, const NLPacket& packet);
const NLPacket* getPacket(uint16_t sequenceNumber) const;

View file

@ -89,17 +89,19 @@ void UserActivityLoggerScriptingInterface::doLogAction(QString action, QJsonObje
Q_ARG(QJsonObject, details));
}
void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem) {
void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["contentCreator"] = contentCreator;
payload["cost"] = cost;
payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem;
doLogAction("commercePurchaseSuccess", payload);
}
void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails) {
void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails) {
QJsonObject payload;
payload["marketplaceID"] = marketplaceID;
payload["contentCreator"] = contentCreator;
payload["cost"] = cost;
payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem;
payload["errorDetails"] = errorDetails;

View file

@ -33,8 +33,8 @@ public:
Q_INVOKABLE void bubbleToggled(bool newValue);
Q_INVOKABLE void bubbleActivated();
Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{});
Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem);
Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails);
Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem);
Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails);
Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type);
Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain);
Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName);

View file

@ -191,6 +191,8 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr<Packet> packet) {
pendingMessage.enqueuePacket(std::move(packet));
bool processedLastOrOnly = false;
while (pendingMessage.hasAvailablePackets()) {
auto packet = pendingMessage.removeNextPacket();
@ -201,9 +203,13 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr<Packet> packet) {
// if this was the last or only packet, then we can remove the pending message from our hash
if (packetPosition == Packet::PacketPosition::LAST ||
packetPosition == Packet::PacketPosition::ONLY) {
_pendingReceivedMessages.erase(messageNumber);
processedLastOrOnly = true;
}
}
if (processedLastOrOnly) {
_pendingReceivedMessages.erase(messageNumber);
}
}
void Connection::sync() {

View file

@ -30,10 +30,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::StaticCertJsonVersionOne);
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,
@ -197,10 +197,11 @@ uint qHash(const PacketType& key, uint seed);
QDebug operator<<(QDebug debug, const PacketType& type);
enum class EntityVersion : PacketVersion {
StrokeColorProperty = 77,
StrokeColorProperty = 0,
HasDynamicOwnershipTests,
HazeEffect,
StaticCertJsonVersionOne
StaticCertJsonVersionOne,
OwnershipChallengeFix,
};
enum class EntityScriptCallMethodVersion : PacketVersion {
@ -211,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,104 +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));
} 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;
@ -394,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

@ -40,17 +40,7 @@ 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);
if (useCauterizedMesh) {
ModelPointer model = _model.lock();
if (model) {
CauterizedModel* skeleton = static_cast<CauterizedModel*>(model.get());
useCauterizedMesh = useCauterizedMesh && skeleton->getEnableCauterization();
} else {
useCauterizedMesh = false;
}
}
bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization;
if (useCauterizedMesh) {
if (_cauterizedClusterBuffer) {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer);

View file

@ -21,9 +21,12 @@ public:
void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override;
void setEnableCauterization(bool enableCauterization) { _enableCauterization = enableCauterization; }
private:
gpu::BufferPointer _cauterizedClusterBuffer;
Transform _cauterizedTransform;
bool _enableCauterization { false };
};
#endif // hifi_CauterizedMeshPartPayload_h

View file

@ -178,6 +178,12 @@ void CauterizedModel::updateRenderItems() {
modelTransform.setTranslation(self->getTranslation());
modelTransform.setRotation(self->getRotation());
bool isWireframe = self->isWireframe();
bool isVisible = self->isVisible();
bool isLayeredInFront = self->isLayeredInFront();
bool isLayeredInHUD = self->isLayeredInHUD();
bool enableCauterization = self->getEnableCauterization();
render::Transaction transaction;
for (int i = 0; i < (int)self->_modelMeshRenderItemIDs.size(); i++) {
@ -186,7 +192,10 @@ void CauterizedModel::updateRenderItems() {
auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices);
auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices);
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized](CauterizedMeshPartPayload& data) {
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized, invalidatePayloadShapeKey,
isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) {
data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized);
Transform renderTransform = modelTransform;
@ -200,6 +209,11 @@ void CauterizedModel::updateRenderItems() {
renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0]));
}
data.updateTransformForCauterizedMesh(renderTransform);
data.setEnableCauterization(enableCauterization);
data.setKey(isVisible, isLayeredInFront || isLayeredInHUD);
data.setLayer(isLayeredInFront, isLayeredInHUD);
data.setShapeKey(invalidatePayloadShapeKey, isWireframe);
});
}

View file

@ -760,6 +760,20 @@ void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) {
_shapes[shape].drawWire(batch);
}
void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) {
batch.setInputFormat(getSolidStreamFormat());
// Color must be set after input format
batch._glColor4f(color.r, color.g, color.b, color.a);
_shapes[shape].draw(batch);
}
void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) {
batch.setInputFormat(getSolidStreamFormat());
// Color must be set after input format
batch._glColor4f(color.r, color.g, color.b, color.a);
_shapes[shape].drawWire(batch);
}
void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) {
gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT);
batch.setInputBuffer(gpu::Stream::COLOR, colorView);
@ -811,6 +825,14 @@ void GeometryCache::renderWireCube(gpu::Batch& batch) {
renderWireShape(batch, Cube);
}
void GeometryCache::renderCube(gpu::Batch& batch, const glm::vec4& color) {
renderShape(batch, Cube, color);
}
void GeometryCache::renderWireCube(gpu::Batch& batch, const glm::vec4& color) {
renderWireShape(batch, Cube, color);
}
void GeometryCache::renderSphere(gpu::Batch& batch) {
renderShape(batch, Sphere);
}
@ -819,6 +841,14 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch) {
renderWireShape(batch, Sphere);
}
void GeometryCache::renderSphere(gpu::Batch& batch, const glm::vec4& color) {
renderShape(batch, Sphere, color);
}
void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color) {
renderWireShape(batch, Sphere, color);
}
void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
int majorRows, int majorCols, float majorEdge,
int minorRows, int minorCols, float minorEdge,

View file

@ -251,14 +251,20 @@ public:
// Dynamic geometry
void renderShape(gpu::Batch& batch, Shape shape);
void renderWireShape(gpu::Batch& batch, Shape shape);
void renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color);
void renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color);
size_t getShapeTriangleCount(Shape shape);
void renderCube(gpu::Batch& batch);
void renderWireCube(gpu::Batch& batch);
void renderCube(gpu::Batch& batch, const glm::vec4& color);
void renderWireCube(gpu::Batch& batch, const glm::vec4& color);
size_t getCubeTriangleCount();
void renderSphere(gpu::Batch& batch);
void renderWireSphere(gpu::Batch& batch);
void renderSphere(gpu::Batch& batch, const glm::vec4& color);
void renderWireSphere(gpu::Batch& batch, const glm::vec4& color);
size_t getSphereTriangleCount();
void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,

View file

@ -122,11 +122,6 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) {
batch.setInputFormat((_drawMesh->getVertexFormat()));
batch.setInputStream(0, _drawMesh->getVertexStream());
// TODO: Get rid of that extra call
if (!_hasColorAttrib) {
batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
}
void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool enableTextures) const {
@ -325,7 +320,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
_shapeID(shapeIndex) {
assert(model && model->isLoaded());
_model = model;
_blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex];
auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex);
const Model::MeshState& state = model->getMeshState(_meshIndex);
@ -339,13 +334,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in
}
updateTransformForSkinnedMesh(renderTransform, transform);
initCache();
initCache(model);
}
void ModelMeshPartPayload::initCache() {
ModelPointer model = _model.lock();
assert(model && model->isLoaded());
void ModelMeshPartPayload::initCache(const ModelPointer& model) {
if (_drawMesh) {
auto vertexFormat = _drawMesh->getVertexFormat();
_hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR);
@ -355,6 +347,7 @@ void ModelMeshPartPayload::initCache() {
const FBXMesh& mesh = geometry.meshes.at(_meshIndex);
_isBlendShaped = !mesh.blendshapes.isEmpty();
_hasTangents = !mesh.tangents.isEmpty();
}
auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID);
@ -388,94 +381,70 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render
_worldBound.transform(boundTransform);
}
ItemKey ModelMeshPartPayload::getKey() const {
void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered) {
ItemKey::Builder builder;
builder.withTypeShape();
ModelPointer model = _model.lock();
if (model) {
if (!model->isVisible()) {
builder.withInvisible();
}
if (!isVisible) {
builder.withInvisible();
}
if (model->isLayeredInFront() || model->isLayeredInHUD()) {
builder.withLayered();
}
if (isLayered) {
builder.withLayered();
}
if (_isBlendShaped || _isSkinned) {
builder.withDeformed();
}
if (_isBlendShaped || _isSkinned) {
builder.withDeformed();
}
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
if (matKey.isTranslucent()) {
builder.withTransparent();
}
if (_drawMaterial) {
auto matKey = _drawMaterial->getKey();
if (matKey.isTranslucent()) {
builder.withTransparent();
}
}
return builder.build();
_itemKey = builder.build();
}
ItemKey ModelMeshPartPayload::getKey() const {
return _itemKey;
}
void ModelMeshPartPayload::setLayer(bool isLayeredInFront, bool isLayeredInHUD) {
if (isLayeredInFront) {
_layer = Item::LAYER_3D_FRONT;
} else if (isLayeredInHUD) {
_layer = Item::LAYER_3D_HUD;
} else {
_layer = Item::LAYER_3D;
}
}
int ModelMeshPartPayload::getLayer() const {
ModelPointer model = _model.lock();
if (model) {
if (model->isLayeredInFront()) {
return Item::LAYER_3D_FRONT;
} else if (model->isLayeredInHUD()) {
return Item::LAYER_3D_HUD;
}
}
return Item::LAYER_3D;
return _layer;
}
ShapeKey ModelMeshPartPayload::getShapeKey() const {
// guard against partially loaded meshes
ModelPointer model = _model.lock();
if (!model || !model->isLoaded() || !model->getGeometry()) {
return ShapeKey::Builder::invalid();
void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe) {
if (invalidateShapeKey) {
_shapeKey = ShapeKey::Builder::invalid();
return;
}
const FBXGeometry& geometry = model->getFBXGeometry();
const auto& networkMeshes = model->getGeometry()->getMeshes();
// guard against partially loaded meshes
if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)model->_meshStates.size()) {
return ShapeKey::Builder::invalid();
}
const FBXMesh& mesh = geometry.meshes.at(_meshIndex);
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
// to false to rebuild out mesh groups.
if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) {
model->_needsFixupInScene = true; // trigger remove/add cycle
model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
return ShapeKey::Builder::invalid();
}
int vertexCount = mesh.vertices.size();
if (vertexCount == 0) {
// sanity check
return ShapeKey::Builder::invalid(); // FIXME
}
model::MaterialKey drawMaterialKey;
if (_drawMaterial) {
drawMaterialKey = _drawMaterial->getKey();
}
bool isTranslucent = drawMaterialKey.isTranslucent();
bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty();
bool hasTangents = drawMaterialKey.isNormalMap() && _hasTangents;
bool hasSpecular = drawMaterialKey.isMetallicMap();
bool hasLightmap = drawMaterialKey.isLightmapMap();
bool isUnlit = drawMaterialKey.isUnlit();
bool isSkinned = _isSkinned;
bool wireframe = model->isWireframe();
if (wireframe) {
if (isWireframe) {
isTranslucent = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
}
@ -500,10 +469,14 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
if (isSkinned) {
builder.withSkinned();
}
if (wireframe) {
if (isWireframe) {
builder.withWireframe();
}
return builder.build();
_shapeKey = builder.build();
}
ShapeKey ModelMeshPartPayload::getShapeKey() const {
return _shapeKey;
}
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
@ -515,11 +488,10 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat()));
ModelPointer model = _model.lock();
if (model) {
batch.setInputBuffer(0, model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3));
if (_blendedVertexBuffer) {
batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3));
// Stride is 2*sizeof(glm::vec3) because normal and tangents are interleaved
batch.setInputBuffer(1, model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), 2*sizeof(NormalType));
batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), 2 * sizeof(NormalType));
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
} else {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
@ -527,11 +499,6 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
batch.setInputStream(0, _drawMesh->getVertexStream());
}
}
// TODO: Get rid of that extra call
if (!_hasColorAttrib) {
batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
}
void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const {
@ -545,31 +512,9 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
void ModelMeshPartPayload::render(RenderArgs* args) {
PerformanceTimer perfTimer("ModelMeshPartPayload::render");
ModelPointer model = _model.lock();
if (!model || !model->isAddedToScene() || !model->isVisible()) {
return; // bail asap
}
if (_state == WAITING_TO_START) {
if (model->isLoaded()) {
_state = STARTED;
model->setRenderItemsNeedUpdate();
} else {
return;
}
}
if (_materialNeedsUpdate && model->getGeometry()->areTexturesLoaded()) {
model->setRenderItemsNeedUpdate();
_materialNeedsUpdate = false;
}
if (!args) {
return;
}
if (!getShapeKey().isValid()) {
return;
}
gpu::Batch& batch = *(args->_batch);
auto locations = args->_shapePipeline->locations;

Some files were not shown because too many files have changed in this diff Show more