Merge branch 'master' into 21089

This commit is contained in:
David Rowe 2016-11-16 08:51:48 +13:00
commit 9b270834dd
133 changed files with 1710 additions and 417 deletions

View file

@ -94,7 +94,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
packetReceiver.registerListener(PacketType::MuteEnvironment, this, "handleMuteEnvironmentPacket");
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
}
@ -385,8 +386,11 @@ bool AudioMixer::prepareMixForListeningNode(Node* node) {
// loop through all other nodes that have sufficient audio to mix
DependencyManager::get<NodeList>()->eachNode([&](const SharedNodePointer& otherNode){
// make sure that we have audio data for this other node and that it isn't being ignored by our listening node
if (otherNode->getLinkedData() && !node->isIgnoringNodeWithID(otherNode->getUUID())) {
// make sure that we have audio data for this other node
// and that it isn't being ignored by our listening node
// and that it isn't ignoring our listening node
if (otherNode->getLinkedData()
&& !node->isIgnoringNodeWithID(otherNode->getUUID()) && !otherNode->isIgnoringNodeWithID(node->getUUID())) {
AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData();
// enumerate the ARBs attached to the otherNode and add all that should be added to mix
@ -489,7 +493,7 @@ void AudioMixer::handleNodeAudioPacket(QSharedPointer<ReceivedMessage> message,
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
auto nodeList = DependencyManager::get<NodeList>();
if (sendingNode->isAllowedEditor()) {
if (sendingNode->getCanKick()) {
glm::vec3 position;
float radius;
@ -599,6 +603,23 @@ void AudioMixer::handleNodeKilled(SharedNodePointer killedNode) {
});
}
void AudioMixer::handleNodeMuteRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto nodeList = DependencyManager::get<NodeList>();
QUuid nodeUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
if (sendingNode->getCanKick()) {
auto node = nodeList->nodeWithUUID(nodeUUID);
if (node) {
// we need to set a flag so we send them the appropriate packet to mute them
AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData();
nodeData->setShouldMuteClient(true);
} else {
qWarning() << "Node mute packet received for unknown node " << uuidStringWithoutCurlyBraces(nodeUUID);
}
} else {
qWarning() << "Node mute packet received from node that cannot mute, ignoring";
}
}
void AudioMixer::handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto clientData = dynamic_cast<AudioMixerClientData*>(sendingNode->getLinkedData());
if (clientData) {
@ -814,9 +835,13 @@ void AudioMixer::broadcastMixes() {
// if the stream should be muted, send mute packet
if (nodeData->getAvatarAudioStream()
&& shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) {
&& (shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())
|| nodeData->shouldMuteClient())) {
auto mutePacket = NLPacket::create(PacketType::NoisyMute, 0);
nodeList->sendPacket(std::move(mutePacket), *node);
// probably now we just reset the flag, once should do it (?)
nodeData->setShouldMuteClient(false);
}
if (node->getType() == NodeType::Agent && node->getActiveSocket()

View file

@ -49,6 +49,7 @@ private slots:
void handleNodeKilled(SharedNodePointer killedNode);
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void handleNodeMuteRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void removeHRTFsForFinishedInjector(const QUuid& streamID);

View file

@ -86,6 +86,9 @@ public:
bool shouldFlushEncoder() { return _shouldFlushEncoder; }
QString getCodecName() { return _selectedCodecName; }
bool shouldMuteClient() { return _shouldMuteClient; }
void setShouldMuteClient(bool shouldMuteClient) { _shouldMuteClient = shouldMuteClient; }
signals:
void injectorStreamFinished(const QUuid& streamIdentifier);
@ -114,6 +117,8 @@ private:
Decoder* _decoder{ nullptr }; // for mic stream
bool _shouldFlushEncoder { false };
bool _shouldMuteClient { false };
};
#endif // hifi_AudioMixerClientData_h

View file

@ -230,9 +230,11 @@ void AvatarMixer::broadcastAvatarData() {
[&](const SharedNodePointer& otherNode)->bool {
// make sure we have data for this avatar, that it isn't the same node,
// and isn't an avatar that the viewing node has ignored
// or that has ignored the viewing node
if (!otherNode->getLinkedData()
|| otherNode->getUUID() == node->getUUID()
|| node->isIgnoringNodeWithID(otherNode->getUUID())) {
|| node->isIgnoringNodeWithID(otherNode->getUUID())
|| otherNode->isIgnoringNodeWithID(node->getUUID())) {
return false;
} else {
return true;
@ -512,12 +514,19 @@ void AvatarMixer::domainSettingsRequestComplete() {
auto nodeList = DependencyManager::get<NodeList>();
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [] (Node* node) {
node->setLinkedData(std::unique_ptr<AvatarMixerClientData> { new AvatarMixerClientData });
};
// parse the settings to pull out the values we need
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
float domainMinimumScale = _domainMinimumScale;
float domainMaximumScale = _domainMaximumScale;
nodeList->linkedDataCreateCallback = [domainMinimumScale, domainMaximumScale] (Node* node) {
auto clientData = std::unique_ptr<AvatarMixerClientData> { new AvatarMixerClientData };
clientData->getAvatar().setDomainMinimumScale(domainMinimumScale);
clientData->getAvatar().setDomainMaximumScale(domainMaximumScale);
node->setLinkedData(std::move(clientData));
};
// start the broadcastThread
_broadcastThread.start();
@ -549,4 +558,22 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
_maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA;
qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps.";
const QString AVATARS_SETTINGS_KEY = "avatars";
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
float settingMinScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
_domainMinimumScale = glm::clamp(settingMinScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
_domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
// make sure that the domain owner didn't flip min and max
if (_domainMinimumScale > _domainMaximumScale) {
std::swap(_domainMinimumScale, _domainMaximumScale);
}
qDebug() << "This domain requires a minimum avatar scale of" << _domainMinimumScale
<< "and a maximum avatar scale of" << _domainMaximumScale;
}

View file

@ -59,6 +59,9 @@ private:
float _maxKbpsPerNode = 0.0f;
float _domainMinimumScale { MIN_AVATAR_SCALE };
float _domainMaximumScale { MAX_AVATAR_SCALE };
QTimer* _broadcastTimer = nullptr;
};

View file

@ -17,7 +17,7 @@
#include <AvatarData.h>
#include <ScriptEngine.h>
class ScriptableAvatar : public AvatarData, public Dependency{
class ScriptableAvatar : public AvatarData, public Dependency {
Q_OBJECT
public:
@ -39,4 +39,4 @@ private:
std::shared_ptr<AnimSkeleton> _animSkeleton;
};
#endif // hifi_ScriptableAvatar_h
#endif // hifi_ScriptableAvatar_h

View file

@ -278,6 +278,13 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
tree->setWantEditLogging(wantEditLogging);
tree->setWantTerseEditLogging(wantTerseEditLogging);
QString entityScriptSourceWhitelist;
if (readOptionString("entityScriptSourceWhitelist", settingsSectionObject, entityScriptSourceWhitelist)) {
tree->setEntityScriptSourceWhitelist(entityScriptSourceWhitelist);
} else {
tree->setEntityScriptSourceWhitelist("");
}
}
void EntityServer::nodeAdded(SharedNodePointer node) {

View file

@ -16,6 +16,7 @@ if (HIFI_MEMORY_DEBUGGING)
if (UNIX)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -U_FORTIFY_SOURCE -fno-stack-protector -fno-omit-frame-pointer")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=address")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libasan -static-libstdc++ -fsanitize=address")
endif (UNIX)
endif ()
endmacro(SETUP_MEMORY_DEBUGGER)

View file

@ -388,6 +388,23 @@
"default": "",
"advanced": false
},
{
"name": "ac_subnet_whitelist",
"label": "Assignment Client IP address Whitelist",
"type": "table",
"can_add_new_rows": true,
"help": "The IP addresses or subnets of ACs that can connect to this server. You can specify an IP address or a subnet in CIDR notation ('A.B.C.D/E', Example: '10.0.0.0/24'). Local ACs (localhost) are always permitted and do not need to be added here.",
"numbered": false,
"advanced": true,
"columns": [
{
"name": "ip",
"label": "IP Address",
"type": "ip",
"can_set": true
}
]
},
{
"name": "standard_permissions",
"type": "table",
@ -866,6 +883,29 @@
}
]
},
{
"name": "avatars",
"label": "Avatars",
"assignment-types": [1, 2],
"settings": [
{
"name": "min_avatar_scale",
"type": "double",
"label": "Minimum Avatar Scale",
"help": "Limits the scale of avatars in your domain. Must be at least 0.005.",
"placeholder": 0.25,
"default": 0.25
},
{
"name": "max_avatar_scale",
"type": "double",
"label": "Maximum Avatar Scale",
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.",
"placeholder": 3.0,
"default": 3.0
}
]
},
{
"name": "audio_env",
"label": "Audio Environment",
@ -1075,6 +1115,14 @@
"default": "3600",
"advanced": true
},
{
"name": "entityScriptSourceWhitelist",
"label": "Entity Scripts Allowed from:",
"help": "The domains that entity scripts are allowed from. A comma separated list of domains that entity scripts are allowed from, if someone attempts to create and entity or edit an entity to have a different domain, it will be rejected. If left blank, any domain is allowed.",
"placeholder": "",
"default": "",
"advanced": true
},
{
"name": "persistFilePath",
"label": "Entities File Path",

View file

@ -158,6 +158,42 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qDebug() << "domain-server is running";
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
static const Subnet LOCALHOST { QHostAddress("127.0.0.1"), 32 };
_acSubnetWhitelist = { LOCALHOST };
auto whitelist = _settingsManager.valueOrDefaultValueForKeyPath(AC_SUBNET_WHITELIST_SETTING_PATH).toStringList();
for (auto& subnet : whitelist) {
auto netmaskParts = subnet.trimmed().split("/");
if (netmaskParts.size() > 2) {
qDebug() << "Ignoring subnet in whitelist, malformed: " << subnet;
continue;
}
// The default netmask is 32 if one has not been specified, which will
// match only the ip provided.
int netmask = 32;
if (netmaskParts.size() == 2) {
bool ok;
netmask = netmaskParts[1].toInt(&ok);
if (!ok) {
qDebug() << "Ignoring subnet in whitelist, bad netmask: " << subnet;
continue;
}
}
auto ip = QHostAddress(netmaskParts[0]);
if (!ip.isNull()) {
qDebug() << "Adding AC whitelist subnet: " << subnet << " -> " << (ip.toString() + "/" + QString::number(netmask));
_acSubnetWhitelist.push_back({ ip , netmask });
} else {
qDebug() << "Ignoring subnet in whitelist, invalid ip portion: " << subnet;
}
}
}
void DomainServer::parseCommandLine() {
@ -1001,6 +1037,21 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer<ReceivedMessage
// construct the requested assignment from the packet data
Assignment requestAssignment(*message);
auto senderAddr = message->getSenderSockAddr().getAddress();
auto isHostAddressInSubnet = [&senderAddr](const Subnet& mask) -> bool {
return senderAddr.isInSubnet(mask);
};
auto it = find_if(_acSubnetWhitelist.begin(), _acSubnetWhitelist.end(), isHostAddressInSubnet);
if (it == _acSubnetWhitelist.end()) {
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(
"Received an assignment connect request from a disallowed ip address: [^ ]+");
qDebug() << "Received an assignment connect request from a disallowed ip address:"
<< senderAddr.toString();
return;
}
// Suppress these for Assignment::AgentType to once per 5 seconds
static QElapsedTimer noisyMessageTimer;
static bool wasNoisyTimerStarted = false;

View file

@ -36,6 +36,9 @@
typedef QSharedPointer<Assignment> SharedAssignmentPointer;
typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
using Subnet = QPair<QHostAddress, int>;
using SubnetList = std::vector<Subnet>;
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
Q_OBJECT
public:
@ -156,6 +159,8 @@ private:
void setupGroupCacheRefresh();
SubnetList _acSubnetWhitelist;
DomainGatekeeper _gatekeeper;
HTTPManager _httpManager;

View file

@ -634,7 +634,6 @@ bool DomainServerSettingsManager::ensurePermissionsForGroupRanks() {
return changed;
}
void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
// before we do any processing on this packet make sure it comes from a node that is allowed to kick
if (sendingNode->getCanKick()) {
@ -1077,6 +1076,9 @@ QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJson
}
bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject) {
static const QString SECURITY_ROOT_KEY = "security";
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
auto& settingsVariant = _configMap.getConfig();
bool needRestart = false;
@ -1127,7 +1129,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
if (!matchingDescriptionObject.isEmpty()) {
updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject);
if (rootKey != "security") {
if (rootKey != SECURITY_ROOT_KEY) {
needRestart = true;
}
} else {
@ -1143,7 +1145,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
if (!matchingDescriptionObject.isEmpty()) {
QJsonValue settingValue = rootValue.toObject()[settingKey];
updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject);
if (rootKey != "security") {
if (rootKey != SECURITY_ROOT_KEY || settingKey == AC_SUBNET_WHITELIST_KEY) {
needRestart = true;
}
} else {

View file

@ -4,12 +4,12 @@
{ "from": "GamePad.LY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateZ" },
{ "from": "GamePad.LX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateX" },
{ "from": "GamePad.LT", "to": "Standard.LTClick",
{ "from": "GamePad.LT", "to": "Standard.LTClick",
"peek": true,
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
},
{ "from": "GamePad.LT", "to": "Standard.LT" },
{ "from": "GamePad.LB", "to": "Standard.LB" },
{ "from": "GamePad.LT", "to": "Standard.LT" },
{ "from": "GamePad.LB", "to": "Standard.LB" },
{ "from": "GamePad.LS", "to": "Standard.LS" },
@ -27,34 +27,34 @@
{ "from": "GamePad.RX", "to": "Actions.Yaw" },
{ "from": "GamePad.RY",
"to": "Actions.VERTICAL_UP",
"filters":
{ "from": "GamePad.RY",
"to": "Actions.VERTICAL_UP",
"filters":
[
{ "type": "deadZone", "min": 0.95 },
"invert"
]
},
},
{ "from": "GamePad.RT", "to": "Standard.RTClick",
{ "from": "GamePad.RT", "to": "Standard.RTClick",
"peek": true,
"filters": [ { "type": "hysteresis", "min": 0.85, "max": 0.9 } ]
},
{ "from": "GamePad.RT", "to": "Standard.RT" },
{ "from": "GamePad.RB", "to": "Standard.RB" },
{ "from": "GamePad.RT", "to": "Standard.RT" },
{ "from": "GamePad.RB", "to": "Standard.RB" },
{ "from": "GamePad.RS", "to": "Standard.RS" },
{ "from": "GamePad.Start", "to": "Actions.CycleCamera" },
{ "from": "GamePad.Back", "to": "Actions.ContextMenu" },
{ "from": "GamePad.Back", "to": "Standard.Start" },
{ "from": "GamePad.DU", "to": "Standard.DU" },
{ "from": "GamePad.DD", "to": "Standard.DD" },
{ "from": "GamePad.DD", "to": "Standard.DD" },
{ "from": "GamePad.DL", "to": "Standard.DL" },
{ "from": "GamePad.DR", "to": "Standard.DR" },
{ "from": "GamePad.DR", "to": "Standard.DR" },
{ "from": [ "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true },
{ "from": "GamePad.A", "to": "Standard.A" },
{ "from": "GamePad.B", "to": "Standard.B" },
{ "from": "GamePad.A", "to": "Standard.A" },
{ "from": "GamePad.B", "to": "Standard.B" },
{ "from": "GamePad.X", "to": "Standard.X" },
{ "from": "GamePad.Y", "to": "Standard.Y" }
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View file

@ -69,6 +69,10 @@ Item {
StatText {
text: "Present Drop Rate: " + root.presentdroprate.toFixed(2);
}
StatText {
text: "Stutter Rate: " + root.stutterrate.toFixed(3);
visible: root.stutterrate != -1;
}
StatText {
text: "Simrate: " + root.simrate
}
@ -241,7 +245,7 @@ Item {
text: "GPU Buffers: "
}
StatText {
text: " Count: " + root.gpuTextures;
text: " Count: " + root.gpuBuffers;
}
StatText {
text: " Memory: " + root.gpuBufferMemory;

View file

@ -1199,6 +1199,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
properties["present_rate"] = displayPlugin->presentRate();
properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate();
properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate();
properties["stutter_rate"] = displayPlugin->stutterRate();
properties["sim_rate"] = getAverageSimsPerSecond();
properties["avatar_sim_rate"] = getAvatarSimrate();
properties["has_async_reprojection"] = displayPlugin->hasAsyncReprojection();

View file

@ -396,15 +396,6 @@ Menu::Menu() {
});
}
// Developer > Render > Enable Incremental Texture Transfer
{
auto action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::IncrementalTextureTransfer, 0, gpu::Texture::getEnableIncrementalTextureTransfers());
connect(action, &QAction::triggered, [&](bool checked) {
qDebug() << "[TEXTURE TRANSFER SUPPORT] --- Enable Incremental Texture Transfer menu option:" << checked;
gpu::Texture::setEnableIncrementalTextureTransfers(checked);
});
}
#else
qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer and Dynamic Texture Management not supported on this platform.";
#endif

View file

@ -112,7 +112,6 @@ namespace MenuOption {
const QString FrameTimer = "Show Timer";
const QString FullscreenMirror = "Mirror";
const QString Help = "Help...";
const QString IncrementalTextureTransfer = "Enable Incremental Texture Transfer";
const QString IncreaseAvatarSize = "Increase Avatar Size";
const QString IndependentMode = "Independent Mode";
const QString ActionMotorControl = "Enable Default Motor Control";

View file

@ -165,16 +165,17 @@ AABox Avatar::getBounds() const {
void Avatar::animateScaleChanges(float deltaTime) {
float currentScale = getUniformScale();
if (currentScale != _targetScale) {
// use exponential decay toward _targetScale
auto desiredScale = getDomainLimitedScale();
if (currentScale != desiredScale) {
// use exponential decay toward the domain limit clamped scale
const float SCALE_ANIMATION_TIMESCALE = 0.5f;
float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f);
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * _targetScale;
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale;
// snap to the end when we get close enough
const float MIN_RELATIVE_SCALE_ERROR = 0.03f;
if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) {
animatedScale = _targetScale;
if (fabsf(desiredScale - currentScale) / desiredScale < MIN_RELATIVE_SCALE_ERROR) {
animatedScale = desiredScale;
}
setScale(glm::vec3(animatedScale)); // avatar scale is uniform

View file

@ -152,7 +152,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
Transform avatarTransform;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
avatarTransform = myAvatar->getTransform();
palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getTargetScale());
palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getDomainLimitedScale());
palmRotation = avatarTransform.getRotation() * pose.getRotation();
} else {
glm::vec3 avatarRigidBodyPosition;

View file

@ -130,6 +130,15 @@ MyAvatar::MyAvatar(RigPointer rig) :
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, static_cast<SlotType>(&MyAvatar::goToLocation));
// handle scale constraints imposed on us by the domain-server
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
// when we connect to a domain and retrieve its settings, we restrict our max/min scale based on those settings
connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings);
// when we leave a domain we lift whatever restrictions that domain may have placed on our scale
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction);
_characterController.setEnabled(true);
_bodySensorMatrix = deriveBodyFromHMDSensor();
@ -1823,25 +1832,104 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
return false;
}
void MyAvatar::increaseSize() {
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) {
_targetScale *= (1.0f + SCALING_RATIO);
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
// There can be a separation between the _targetScale and the actual scale of the rendered avatar in a domain.
// When the avatar enters a domain where their target scale is not allowed according to the min/max
// we do not change their saved target scale. Instead, we use getDomainLimitedScale() to render the avatar
// at a domain appropriate size. When the avatar leaves the limiting domain, we'll return them to their previous target scale.
// While connected to a domain that limits avatar scale if the user manually changes their avatar scale, we change
// target scale to match the new scale they have chosen. When they leave the domain they will not return to the scale they were
// before they entered the limiting domain.
void MyAvatar::clampTargetScaleToDomainLimits() {
// when we're about to change the target scale because the user has asked to increase or decrease their scale,
// we first make sure that we're starting from a target scale that is allowed by the current domain
auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
if (clampedTargetScale != _targetScale) {
qCDebug(interfaceapp, "Clamped scale to %f since original target scale %f was not allowed by domain",
(double)clampedTargetScale, (double)_targetScale);
setTargetScale(clampedTargetScale);
}
}
void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) {
auto clampedTargetScale = glm::clamp(desiredScale, _domainMinimumScale, _domainMaximumScale);
if (clampedTargetScale != desiredScale) {
qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain",
clampedTargetScale, desiredScale);
}
setTargetScale(clampedTargetScale);
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
}
void MyAvatar::increaseSize() {
// make sure we're starting from an allowable scale
clampTargetScaleToDomainLimits();
// calculate what our new scale should be
float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO);
// attempt to change to desired scale (clamped to the domain limits)
clampScaleChangeToDomainLimits(updatedTargetScale);
}
void MyAvatar::decreaseSize() {
if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) {
_targetScale *= (1.0f - SCALING_RATIO);
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
}
// make sure we're starting from an allowable scale
clampTargetScaleToDomainLimits();
// calculate what our new scale should be
float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO);
// attempt to change to desired scale (clamped to the domain limits)
clampScaleChangeToDomainLimits(updatedTargetScale);
}
void MyAvatar::resetSize() {
_targetScale = 1.0f;
qCDebug(interfaceapp, "Reset scale to %f", (double)_targetScale);
// attempt to reset avatar size to the default (clamped to domain limits)
const float DEFAULT_AVATAR_SCALE = 1.0f;
clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE);
}
void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) {
// pull out the minimum and maximum scale and set them to restrict our scale
static const QString AVATAR_SETTINGS_KEY = "avatars";
auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject();
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
float settingMinScale = avatarsObject[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
setDomainMinimumScale(settingMinScale);
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
setDomainMaximumScale(settingMaxScale);
// make sure that the domain owner didn't flip min and max
if (_domainMinimumScale > _domainMaximumScale) {
std::swap(_domainMinimumScale, _domainMaximumScale);
}
qCDebug(interfaceapp, "This domain requires a minimum avatar scale of %f and a maximum avatar scale of %f",
(double)_domainMinimumScale, (double)_domainMaximumScale);
// debug to log if this avatar's scale in this domain will be clamped
auto clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
if (_targetScale != clampedScale) {
qCDebug(interfaceapp, "Avatar scale will be clamped to %f because %f is not allowed by current domain",
(double)clampedScale, (double)_targetScale);
}
}
void MyAvatar::clearScaleRestriction() {
_domainMinimumScale = MIN_AVATAR_SCALE;
_domainMaximumScale = MAX_AVATAR_SCALE;
}
void MyAvatar::goToLocation(const QVariant& propertiesVar) {
qCDebug(interfaceapp, "MyAvatar QML goToLocation");

View file

@ -292,6 +292,9 @@ public slots:
bool shouldFaceLocation = false);
void goToLocation(const QVariant& properties);
void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject);
void clearScaleRestriction();
// Set/Get update the thrust that will move the avatar around
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
glm::vec3 getThrust() { return _thrust; };
@ -369,6 +372,8 @@ private:
virtual void updatePalms() override {}
void lateUpdatePalms();
void clampTargetScaleToDomainLimits();
void clampScaleChangeToDomainLimits(float desiredScale);
float _driveKeys[MAX_DRIVE_KEYS];
bool _wasPushing;

View file

@ -19,12 +19,35 @@ class AccountScriptingInterface : public QObject {
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
/**jsdoc
* @namespace Account
* @property username {String} username if user is logged in, otherwise it returns "Unknown user"
*/
signals:
/**jsdoc
* Triggered when username has changed.
* @function Account.usernameChanged
* @return {Signal}
*/
void usernameChanged();
public slots:
static AccountScriptingInterface* getInstance();
/**jsdoc
* Returns the username for the currently logged in High Fidelity metaverse account.
* @function Account.getUsername
* @return {string} username if user is logged in, otherwise it returns "Unknown user"
*/
QString getUsername();
/**jsdoc
* Determine if the user is logged into the High Fidleity metaverse.
* @function Account.isLoggedIn
* @return {bool} true when user is logged into the High Fidelity metaverse.
*/
bool isLoggedIn();
bool checkAndSignalForAccessToken();
};

View file

@ -126,6 +126,23 @@ void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool
Q_ARG(bool, isChecked));
}
bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isMenuEnabled", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption));
return result;
}
void MenuScriptingInterface::setMenuEnabled(const QString& menuOption, bool isChecked) {
QMetaObject::invokeMethod(Menu::getInstance(), "setMenuEnabled",
Q_ARG(const QString&, menuOption),
Q_ARG(bool, isChecked));
}
void MenuScriptingInterface::triggerOption(const QString& menuOption) {
QMetaObject::invokeMethod(Menu::getInstance(), "triggerOption", Q_ARG(const QString&, menuOption));
}

View file

@ -50,6 +50,9 @@ public slots:
void setIsOptionChecked(const QString& menuOption, bool isChecked);
void triggerOption(const QString& menuOption);
bool isMenuEnabled(const QString& menuName);
void setMenuEnabled(const QString& menuName, bool isEnabled);
signals:
void menuItemEvent(const QString& menuItem);

View file

@ -128,7 +128,8 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(renderrate, displayPlugin->renderRate());
STAT_UPDATE(presentrate, displayPlugin->presentRate());
STAT_UPDATE(presentnewrate, displayPlugin->newFramePresentRate());
STAT_UPDATE(presentdroprate, qApp->getActiveDisplayPlugin()->droppedFrameRate());
STAT_UPDATE(presentdroprate, displayPlugin->droppedFrameRate());
STAT_UPDATE(stutterrate, displayPlugin->stutterRate());
} else {
STAT_UPDATE(presentrate, -1);
STAT_UPDATE(presentnewrate, -1);

View file

@ -36,7 +36,9 @@ class Stats : public QQuickItem {
STATS_PROPERTY(float, renderrate, 0)
// How often the display plugin is presenting to the device
STATS_PROPERTY(float, presentrate, 0)
// How often the display device reprojecting old frames
STATS_PROPERTY(float, stutterrate, 0)
STATS_PROPERTY(float, presentnewrate, 0)
STATS_PROPERTY(float, presentdroprate, 0)
STATS_PROPERTY(int, simrate, 0)
@ -140,6 +142,7 @@ signals:
void presentrateChanged();
void presentnewrateChanged();
void presentdroprateChanged();
void stutterrateChanged();
void simrateChanged();
void avatarSimrateChanged();
void avatarCountChanged();

View file

@ -29,7 +29,19 @@ class AnimationCache : public ResourceCache, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
/**jsdoc
* @namespace AnimationCache
* @augments ResourceCache
*/
public:
/**jsdoc
* Returns animation resource for particular animation
* @function AnimationCache.getAnimation
* @param url {string} url to load
* @return {Resource} animation
*/
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);

View file

@ -308,6 +308,13 @@ void Rig::clearIKJointLimitHistory() {
}
}
int Rig::getJointParentIndex(int childIndex) const {
if (_animSkeleton && isIndexValid(childIndex)) {
return _animSkeleton->getParentIndex(childIndex);
}
return -1;
}
void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {
if (isIndexValid(index)) {
if (valid) {
@ -414,6 +421,16 @@ bool Rig::getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& trans
}
}
bool Rig::getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) const {
QReadLocker readLock(&_externalPoseSetLock);
if (jointIndex >= 0 && jointIndex < (int)_externalPoseSet._absolutePoses.size()) {
returnPose = _externalPoseSet._absolutePoses[jointIndex];
return true;
} else {
return false;
}
}
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
// AJT: TODO: used by attachments
ASSERT(false);

View file

@ -105,6 +105,8 @@ public:
void clearIKJointLimitHistory();
int getJointParentIndex(int childIndex) const;
// geometry space
void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority);
@ -133,6 +135,7 @@ public:
// rig space (thread-safe)
bool getAbsoluteJointRotationInRigFrame(int jointIndex, glm::quat& rotation) const;
bool getAbsoluteJointTranslationInRigFrame(int jointIndex, glm::vec3& translation) const;
bool getAbsoluteJointPoseInRigFrame(int jointIndex, AnimPose& returnPose) const;
// legacy
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;

View file

@ -210,7 +210,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y);
packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x);
packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z);
packFloatRatioToTwoByte((uint8_t*)(&header->scale), _targetScale);
packFloatRatioToTwoByte((uint8_t*)(&header->scale), getDomainLimitedScale());
header->lookAtPosition[0] = _headData->_lookAtPosition.x;
header->lookAtPosition[1] = _headData->_lookAtPosition.y;
header->lookAtPosition[2] = _headData->_lookAtPosition.z;
@ -516,7 +516,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
}
return buffer.size();
}
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, scale));
setTargetScale(scale);
glm::vec3 lookAt = glm::vec3(header->lookAtPosition[0], header->lookAtPosition[1], header->lookAtPosition[2]);
if (isNaN(lookAt)) {
@ -1439,7 +1439,7 @@ QJsonObject AvatarData::toJson() const {
if (!success) {
qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform";
}
avatarTransform.setScale(getTargetScale());
avatarTransform.setScale(getDomainLimitedScale());
if (recordingBasis) {
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
// Find the relative transform
@ -1451,7 +1451,7 @@ QJsonObject AvatarData::toJson() const {
root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatarTransform);
}
auto scale = getTargetScale();
auto scale = getDomainLimitedScale();
if (scale != 1.0f) {
root[JSON_AVATAR_SCALE] = scale;
}

View file

@ -243,6 +243,12 @@ public:
void setTargetScale(float targetScale);
void setTargetScaleVerbose(float targetScale);
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
void setDomainMinimumScale(float domainMinimumScale)
{ _domainMinimumScale = glm::clamp(domainMinimumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); }
void setDomainMaximumScale(float domainMaximumScale)
{ _domainMaximumScale = glm::clamp(domainMaximumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); }
// Hand State
Q_INVOKABLE void setHandState(char s) { _handState = s; }
Q_INVOKABLE char getHandState() const { return _handState; }
@ -377,6 +383,8 @@ protected:
// Body scale
float _targetScale;
float _domainMinimumScale { MIN_AVATAR_SCALE };
float _domainMaximumScale { MAX_AVATAR_SCALE };
// Hand state (are we grabbing something or not)
char _handState;

View file

@ -18,7 +18,7 @@ class Basic2DWindowOpenGLDisplayPlugin : public OpenGLDisplayPlugin {
Q_OBJECT
using Parent = OpenGLDisplayPlugin;
public:
virtual const QString& getName() const override { return NAME; }
virtual const QString getName() const override { return NAME; }
virtual float getTargetFrameRate() const override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; }

View file

@ -12,7 +12,7 @@
class NullDisplayPlugin : public DisplayPlugin {
public:
~NullDisplayPlugin() final {}
const QString& getName() const override { return NAME; }
const QString getName() const override { return NAME; }
grouping getGrouping() const override { return DEVELOPER; }
glm::uvec2 getRecommendedRenderSize() const override;

View file

@ -758,7 +758,6 @@ void OpenGLDisplayPlugin::render(std::function<void(gpu::Batch& batch)> f) {
OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
qDebug() << "Destroying OpenGLDisplayPlugin";
}
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {

View file

@ -13,7 +13,7 @@ class DebugHmdDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
const QString& getName() const override { return NAME; }
const QString getName() const override { return NAME; }
grouping getGrouping() const override { return DEVELOPER; }
bool isSupported() const override;

View file

@ -710,3 +710,7 @@ void HmdDisplayPlugin::compositeExtra() {
HmdDisplayPlugin::~HmdDisplayPlugin() {
qDebug() << "Destroying HmdDisplayPlugin";
}
float HmdDisplayPlugin::stutterRate() const {
return _stutterRate.rate();
}

View file

@ -44,6 +44,8 @@ public:
return false;
}
float stutterRate() const override;
protected:
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
@ -108,8 +110,9 @@ protected:
QMap<uint32_t, FrameInfo> _frameInfos;
FrameInfo _currentPresentFrameInfo;
FrameInfo _currentRenderFrameInfo;
RateCounter<> _stutterRate;
bool _disablePreview{ true };
bool _disablePreview { true };
private:
ivec4 getViewportForSourceSize(const uvec2& size) const;
float getLeftCenterPixel() const;

View file

@ -13,7 +13,7 @@ class InterleavedStereoDisplayPlugin : public StereoDisplayPlugin {
Q_OBJECT
using Parent = StereoDisplayPlugin;
public:
const QString& getName() const override { return NAME; }
const QString getName() const override { return NAME; }
grouping getGrouping() const override { return ADVANCED; }
glm::uvec2 getRecommendedRenderSize() const override;

View file

@ -15,7 +15,7 @@ class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin {
Q_OBJECT
using Parent = StereoDisplayPlugin;
public:
virtual const QString& getName() const override { return NAME; }
virtual const QString getName() const override { return NAME; }
virtual grouping getGrouping() const override { return ADVANCED; }
virtual glm::uvec2 getRecommendedRenderSize() const override;

View file

@ -310,14 +310,14 @@ bool RenderableModelEntityItem::getAnimationFrame() {
}
glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform *
rotationMat * fbxJoints[index].postTransform);
_absoluteJointTranslationsInObjectFrame[j] = extractTranslation(finalMat);
_absoluteJointTranslationsInObjectFrameSet[j] = true;
_absoluteJointTranslationsInObjectFrameDirty[j] = true;
_localJointTranslations[j] = extractTranslation(finalMat);
_localJointTranslationsSet[j] = true;
_localJointTranslationsDirty[j] = true;
_absoluteJointRotationsInObjectFrame[j] = glmExtractRotation(finalMat);
_localJointRotations[j] = glmExtractRotation(finalMat);
_absoluteJointRotationsInObjectFrameSet[j] = true;
_absoluteJointRotationsInObjectFrameDirty[j] = true;
_localJointRotationsSet[j] = true;
_localJointRotationsDirty[j] = true;
}
}
}
@ -390,18 +390,18 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
getAnimationFrame();
// relay any inbound joint changes from scripts/animation/network to the model/rig
for (int index = 0; index < _absoluteJointRotationsInObjectFrame.size(); index++) {
if (_absoluteJointRotationsInObjectFrameDirty[index]) {
glm::quat rotation = _absoluteJointRotationsInObjectFrame[index];
for (int index = 0; index < _localJointRotations.size(); index++) {
if (_localJointRotationsDirty[index]) {
glm::quat rotation = _localJointRotations[index];
_model->setJointRotation(index, true, rotation, 1.0f);
_absoluteJointRotationsInObjectFrameDirty[index] = false;
_localJointRotationsDirty[index] = false;
}
}
for (int index = 0; index < _absoluteJointTranslationsInObjectFrame.size(); index++) {
if (_absoluteJointTranslationsInObjectFrameDirty[index]) {
glm::vec3 translation = _absoluteJointTranslationsInObjectFrame[index];
for (int index = 0; index < _localJointTranslations.size(); index++) {
if (_localJointTranslationsDirty[index]) {
glm::vec3 translation = _localJointTranslations[index];
_model->setJointTranslation(index, true, translation, 1.0f);
_absoluteJointTranslationsInObjectFrameDirty[index] = false;
_localJointTranslationsDirty[index] = false;
}
}
});
@ -1028,15 +1028,101 @@ glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(in
}
bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) {
if (!_model) {
return false;
}
RigPointer rig = _model->getRig();
if (!rig) {
return false;
}
int jointParentIndex = rig->getJointParentIndex(index);
if (jointParentIndex == -1) {
return setLocalJointRotation(index, rotation);
}
bool success;
AnimPose jointParentPose;
success = rig->getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose);
if (!success) {
return false;
}
AnimPose jointParentInversePose = jointParentPose.inverse();
AnimPose jointAbsolutePose; // in rig frame
success = rig->getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose);
if (!success) {
return false;
}
jointAbsolutePose.rot = rotation;
AnimPose jointRelativePose = jointParentInversePose * jointAbsolutePose;
return setLocalJointRotation(index, jointRelativePose.rot);
}
bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) {
if (!_model) {
return false;
}
RigPointer rig = _model->getRig();
if (!rig) {
return false;
}
int jointParentIndex = rig->getJointParentIndex(index);
if (jointParentIndex == -1) {
return setLocalJointTranslation(index, translation);
}
bool success;
AnimPose jointParentPose;
success = rig->getAbsoluteJointPoseInRigFrame(jointParentIndex, jointParentPose);
if (!success) {
return false;
}
AnimPose jointParentInversePose = jointParentPose.inverse();
AnimPose jointAbsolutePose; // in rig frame
success = rig->getAbsoluteJointPoseInRigFrame(index, jointAbsolutePose);
if (!success) {
return false;
}
jointAbsolutePose.trans = translation;
AnimPose jointRelativePose = jointParentInversePose * jointAbsolutePose;
return setLocalJointTranslation(index, jointRelativePose.trans);
}
glm::quat RenderableModelEntityItem::getLocalJointRotation(int index) const {
if (_model) {
glm::quat result;
if (_model->getJointRotation(index, result)) {
return result;
}
}
return glm::quat();
}
glm::vec3 RenderableModelEntityItem::getLocalJointTranslation(int index) const {
if (_model) {
glm::vec3 result;
if (_model->getJointTranslation(index, result)) {
return result;
}
}
return glm::vec3();
}
bool RenderableModelEntityItem::setLocalJointRotation(int index, const glm::quat& rotation) {
bool result = false;
_jointDataLock.withWriteLock([&] {
_jointRotationsExplicitlySet = true;
resizeJointArrays();
if (index >= 0 && index < _absoluteJointRotationsInObjectFrame.size() &&
_absoluteJointRotationsInObjectFrame[index] != rotation) {
_absoluteJointRotationsInObjectFrame[index] = rotation;
_absoluteJointRotationsInObjectFrameSet[index] = true;
_absoluteJointRotationsInObjectFrameDirty[index] = true;
if (index >= 0 && index < _localJointRotations.size() &&
_localJointRotations[index] != rotation) {
_localJointRotations[index] = rotation;
_localJointRotationsSet[index] = true;
_localJointRotationsDirty[index] = true;
result = true;
_needsJointSimulation = true;
}
@ -1044,16 +1130,16 @@ bool RenderableModelEntityItem::setAbsoluteJointRotationInObjectFrame(int index,
return result;
}
bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) {
bool RenderableModelEntityItem::setLocalJointTranslation(int index, const glm::vec3& translation) {
bool result = false;
_jointDataLock.withWriteLock([&] {
_jointTranslationsExplicitlySet = true;
resizeJointArrays();
if (index >= 0 && index < _absoluteJointTranslationsInObjectFrame.size() &&
_absoluteJointTranslationsInObjectFrame[index] != translation) {
_absoluteJointTranslationsInObjectFrame[index] = translation;
_absoluteJointTranslationsInObjectFrameSet[index] = true;
_absoluteJointTranslationsInObjectFrameDirty[index] = true;
if (index >= 0 && index < _localJointTranslations.size() &&
_localJointTranslations[index] != translation) {
_localJointTranslations[index] = translation;
_localJointTranslationsSet[index] = true;
_localJointTranslationsDirty[index] = true;
result = true;
_needsJointSimulation = true;
}

View file

@ -74,6 +74,12 @@ public:
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override;
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override;
virtual glm::quat getLocalJointRotation(int index) const override;
virtual glm::vec3 getLocalJointTranslation(int index) const override;
virtual bool setLocalJointRotation(int index, const glm::quat& rotation) override;
virtual bool setLocalJointTranslation(int index, const glm::vec3& translation) override;
virtual void setJointRotations(const QVector<glm::quat>& rotations) override;
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet) override;
virtual void setJointTranslations(const QVector<glm::vec3>& translations) override;

View file

@ -43,6 +43,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
_lastSimulated(0),
_lastUpdated(0),
_lastEdited(0),
_lastEditedBy(ENTITY_ITEM_DEFAULT_LAST_EDITED_BY),
_lastEditedFromRemote(0),
_lastEditedFromRemoteInRemoteTime(0),
_created(UNKNOWN_CREATED_TIME),
@ -141,6 +142,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_CLIENT_ONLY;
requestedProperties += PROP_OWNING_AVATAR_ID;
requestedProperties += PROP_LAST_EDITED_BY;
return requestedProperties;
}
@ -279,6 +282,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, getParentID());
APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, getParentJointIndex());
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties,
@ -803,6 +807,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
}
READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube);
READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy);
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
@ -1205,6 +1210,8 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(clientOnly, getClientOnly);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy);
properties._defaultSettings = false;
return properties;
@ -1307,6 +1314,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(clientOnly, setClientOnly);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(owningAvatarID, setOwningAvatarID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy);
AACube saveQueryAACube = _queryAACube;
checkAndAdjustQueryAACube();
if (saveQueryAACube != _queryAACube) {

View file

@ -411,8 +411,9 @@ public:
// these are in the frame of this object
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); }
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
virtual bool setLocalJointRotation(int index, const glm::quat& rotation) override { return false; }
virtual bool setLocalJointTranslation(int index, const glm::vec3& translation) override { return false; }
virtual int getJointIndex(const QString& name) const { return -1; }
virtual QStringList getJointNames() const { return QStringList(); }
@ -448,6 +449,9 @@ public:
virtual void emitScriptEvent(const QVariant& message) {}
QUuid getLastEditedBy() const { return _lastEditedBy; }
void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
protected:
void setSimulated(bool simulated) { _simulated = simulated; }
@ -463,6 +467,7 @@ protected:
// and physics changes
quint64 _lastUpdated; // last time this entity called update(), this includes animations and non-physics changes
quint64 _lastEdited; // last official local or remote edit time
QUuid _lastEditedBy; // id of last editor
quint64 _lastBroadcast; // the last time we sent an edit packet about this entity
quint64 _lastEditedFromRemote; // last time we received and edit from the server

View file

@ -227,6 +227,7 @@ void EntityItemProperties::setBackgroundModeFromString(const QString& background
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
EntityPropertyFlags changedProperties;
CHECK_PROPERTY_CHANGE(PROP_LAST_EDITED_BY, lastEditedBy);
CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation);
@ -368,6 +369,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable
}
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LAST_EDITED_BY, lastEditedBy);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_POSITION, position);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DIMENSIONS, dimensions);
if (!skipDefaults) {
@ -611,6 +613,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
setType(typeScriptValue.toVariant().toString());
}
COPY_PROPERTY_FROM_QSCRIPTVALUE(lastEditedBy, QUuid, setLastEditedBy);
COPY_PROPERTY_FROM_QSCRIPTVALUE(position, glmVec3, setPosition);
COPY_PROPERTY_FROM_QSCRIPTVALUE(dimensions, glmVec3, setDimensions);
COPY_PROPERTY_FROM_QSCRIPTVALUE(rotation, glmQuat, setRotation);
@ -750,6 +753,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
}
void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(lastEditedBy);
COPY_PROPERTY_IF_CHANGED(position);
COPY_PROPERTY_IF_CHANGED(dimensions);
COPY_PROPERTY_IF_CHANGED(rotation);
@ -1667,6 +1671,7 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
}
void EntityItemProperties::markAllChanged() {
_lastEditedByChanged = true;
_simulationOwnerChanged = true;
_positionChanged = true;
_dimensionsChanged = true;

View file

@ -83,6 +83,7 @@ public:
quint64 getLastEdited() const { return _lastEdited; }
float getEditedAgo() const /// Elapsed seconds since this entity was last edited
{ return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; }
EntityPropertyFlags getChangedProperties() const;
bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret?
@ -218,6 +219,8 @@ public:
DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI);
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
static QString getBackgroundModeString(BackgroundMode mode);
@ -455,6 +458,8 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ClientOnly, clientOnly, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LastEditedBy, lastEditedBy, "");
properties.getAnimation().debugDump();
properties.getSkybox().debugDump();
properties.getStage().debugDump();

View file

@ -75,4 +75,6 @@ const QString ENTITY_ITEM_DEFAULT_NAME = QString("");
const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30;
const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid();
#endif // hifi_EntityItemPropertiesDefaults_h

View file

@ -181,6 +181,8 @@ enum EntityPropertyList {
PROP_LOCAL_VELOCITY, // only used to convert values to and from scripts
PROP_LOCAL_ANGULAR_VELOCITY, // only used to convert values to and from scripts
PROP_LAST_EDITED_BY,
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line
PROP_AFTER_LAST_ITEM,

View file

@ -1154,17 +1154,76 @@ bool EntityScriptingInterface::setAbsoluteJointRotationInObjectFrame(const QUuid
return false;
}
glm::vec3 EntityScriptingInterface::getLocalJointTranslation(const QUuid& entityID, int jointIndex) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
return modelEntity->getLocalJointTranslation(jointIndex);
} else {
return glm::vec3(0.0f);
}
}
glm::quat EntityScriptingInterface::getLocalJointRotation(const QUuid& entityID, int jointIndex) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
return modelEntity->getLocalJointRotation(jointIndex);
} else {
return glm::quat();
}
}
bool EntityScriptingInterface::setLocalJointTranslation(const QUuid& entityID, int jointIndex, glm::vec3 translation) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto now = usecTimestampNow();
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
bool result = modelEntity->setLocalJointTranslation(jointIndex, translation);
if (result) {
EntityItemProperties properties;
_entityTree->withWriteLock([&] {
properties = entity->getProperties();
entity->setLastBroadcast(now);
});
properties.setJointTranslationsDirty();
properties.setLastEdited(now);
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
return true;
}
}
return false;
}
bool EntityScriptingInterface::setLocalJointRotation(const QUuid& entityID, int jointIndex, glm::quat rotation) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto now = usecTimestampNow();
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
bool result = modelEntity->setLocalJointRotation(jointIndex, rotation);
if (result) {
EntityItemProperties properties;
_entityTree->withWriteLock([&] {
properties = entity->getProperties();
entity->setLastBroadcast(now);
});
properties.setJointRotationsDirty();
properties.setLastEdited(now);
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
return true;
}
}
return false;
}
bool EntityScriptingInterface::setAbsoluteJointRotationsInObjectFrame(const QUuid& entityID,
const QVector<glm::quat>& rotations) {
bool EntityScriptingInterface::setLocalJointRotations(const QUuid& entityID, const QVector<glm::quat>& rotations) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto now = usecTimestampNow();
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
bool result = false;
for (int index = 0; index < rotations.size(); index++) {
result |= modelEntity->setAbsoluteJointRotationInObjectFrame(index, rotations[index]);
result |= modelEntity->setLocalJointRotation(index, rotations[index]);
}
if (result) {
EntityItemProperties properties;
@ -1184,15 +1243,14 @@ bool EntityScriptingInterface::setAbsoluteJointRotationsInObjectFrame(const QUui
}
bool EntityScriptingInterface::setAbsoluteJointTranslationsInObjectFrame(const QUuid& entityID,
const QVector<glm::vec3>& translations) {
bool EntityScriptingInterface::setLocalJointTranslations(const QUuid& entityID, const QVector<glm::vec3>& translations) {
if (auto entity = checkForTreeEntityAndTypeMatch(entityID, EntityTypes::Model)) {
auto now = usecTimestampNow();
auto modelEntity = std::dynamic_pointer_cast<ModelEntityItem>(entity);
bool result = false;
for (int index = 0; index < translations.size(); index++) {
result |= modelEntity->setAbsoluteJointTranslationInObjectFrame(index, translations[index]);
result |= modelEntity->setLocalJointTranslation(index, translations[index]);
}
if (result) {
EntityItemProperties properties;
@ -1211,12 +1269,12 @@ bool EntityScriptingInterface::setAbsoluteJointTranslationsInObjectFrame(const Q
return false;
}
bool EntityScriptingInterface::setAbsoluteJointsDataInObjectFrame(const QUuid& entityID,
const QVector<glm::quat>& rotations,
const QVector<glm::vec3>& translations) {
bool EntityScriptingInterface::setLocalJointsData(const QUuid& entityID,
const QVector<glm::quat>& rotations,
const QVector<glm::vec3>& translations) {
// for a model with 80 joints, sending both these in one edit packet causes the packet to be too large.
return setAbsoluteJointRotationsInObjectFrame(entityID, rotations) ||
setAbsoluteJointTranslationsInObjectFrame(entityID, translations);
return setLocalJointRotations(entityID, rotations) ||
setLocalJointTranslations(entityID, translations);
}
int EntityScriptingInterface::getJointIndex(const QUuid& entityID, const QString& name) {

View file

@ -186,13 +186,17 @@ public slots:
Q_INVOKABLE glm::quat getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex);
Q_INVOKABLE bool setAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex, glm::vec3 translation);
Q_INVOKABLE bool setAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex, glm::quat rotation);
Q_INVOKABLE bool setAbsoluteJointRotationsInObjectFrame(const QUuid& entityID,
const QVector<glm::quat>& rotations);
Q_INVOKABLE bool setAbsoluteJointTranslationsInObjectFrame(const QUuid& entityID,
const QVector<glm::vec3>& translations);
Q_INVOKABLE bool setAbsoluteJointsDataInObjectFrame(const QUuid& entityID,
const QVector<glm::quat>& rotations,
const QVector<glm::vec3>& translations);
Q_INVOKABLE glm::vec3 getLocalJointTranslation(const QUuid& entityID, int jointIndex);
Q_INVOKABLE glm::quat getLocalJointRotation(const QUuid& entityID, int jointIndex);
Q_INVOKABLE bool setLocalJointTranslation(const QUuid& entityID, int jointIndex, glm::vec3 translation);
Q_INVOKABLE bool setLocalJointRotation(const QUuid& entityID, int jointIndex, glm::quat rotation);
Q_INVOKABLE bool setLocalJointRotations(const QUuid& entityID, const QVector<glm::quat>& rotations);
Q_INVOKABLE bool setLocalJointTranslations(const QUuid& entityID, const QVector<glm::vec3>& translations);
Q_INVOKABLE bool setLocalJointsData(const QUuid& entityID,
const QVector<glm::quat>& rotations,
const QVector<glm::vec3>& translations);
Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name);
Q_INVOKABLE QStringList getJointNames(const QUuid& entityID);

View file

@ -63,6 +63,11 @@ EntityTree::~EntityTree() {
eraseAllOctreeElements(false);
}
void EntityTree::setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist) {
_entityScriptSourceWhitelist = entityScriptSourceWhitelist.split(',');
}
void EntityTree::createRootElement() {
_rootElement = createNewElement();
}
@ -925,6 +930,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
quint64 startCreate = 0, endCreate = 0;
quint64 startLogging = 0, endLogging = 0;
const quint64 LAST_EDITED_SERVERSIDE_BUMP = 1; // usec
bool suppressDisallowedScript = false;
_totalEditMessages++;
EntityItemID entityItemID;
@ -935,7 +943,31 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
entityItemID, properties);
endDecode = usecTimestampNow();
const quint64 LAST_EDITED_SERVERSIDE_BUMP = 1; // usec
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) {
bool passedWhiteList = false;
auto entityScript = properties.getScript();
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
if (entityScript.startsWith(whiteListedPrefix, Qt::CaseInsensitive)) {
passedWhiteList = true;
break;
}
}
if (!passedWhiteList) {
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID() << "] attempting to set entity script not on whitelist, edit rejected";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (message.getType() == PacketType::EntityAdd) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = passedWhiteList;
} else {
suppressDisallowedScript = true;
}
}
}
if ((message.getType() == PacketType::EntityAdd ||
(message.getType() == PacketType::EntityEdit && properties.lifetimeChanged())) &&
!senderNode->getCanRez() && senderNode->getCanRezTmp()) {
@ -960,6 +992,12 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID);
endLookup = usecTimestampNow();
if (existingEntity && message.getType() == PacketType::EntityEdit) {
if (suppressDisallowedScript) {
properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP);
properties.setScript(existingEntity->getScript());
}
// if the EntityItem exists, then update it
startLogging = usecTimestampNow();
if (wantEditLogging()) {
@ -975,6 +1013,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
endLogging = usecTimestampNow();
startUpdate = usecTimestampNow();
properties.setLastEditedBy(senderNode->getUUID());
updateEntity(entityItemID, properties, senderNode);
existingEntity->markAsChangedOnServer();
endUpdate = usecTimestampNow();
@ -983,6 +1022,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
// this is a new entity... assign a new entityID
properties.setCreated(properties.getLastEdited());
properties.setLastEditedBy(senderNode->getUUID());
startCreate = usecTimestampNow();
EntityItemPointer newEntity = addEntity(entityItemID, properties);
endCreate = usecTimestampNow();

View file

@ -64,6 +64,7 @@ public:
void setEntityMaxTmpLifetime(float maxTmpEntityLifetime) { _maxTmpEntityLifetime = maxTmpEntityLifetime; }
void setEntityScriptSourceWhitelist(const QString& entityScriptSourceWhitelist);
/// Implements our type specific root element factory
virtual OctreeElementPointer createNewElement(unsigned char* octalCode = NULL) override;
@ -342,6 +343,8 @@ protected:
QHash<QUuid, QSet<EntityItemID>> _childrenOfAvatars; // which entities are children of which avatars
float _maxTmpEntityLifetime { DEFAULT_MAX_TMP_ENTITY_LIFETIME };
QStringList _entityScriptSourceWhitelist;
};
#endif // hifi_EntityTree_h

View file

@ -389,13 +389,13 @@ bool ModelEntityItem::shouldBePhysical() const {
}
void ModelEntityItem::resizeJointArrays(int newSize) {
if (newSize >= 0 && newSize > _absoluteJointRotationsInObjectFrame.size()) {
_absoluteJointRotationsInObjectFrame.resize(newSize);
_absoluteJointRotationsInObjectFrameSet.resize(newSize);
_absoluteJointRotationsInObjectFrameDirty.resize(newSize);
_absoluteJointTranslationsInObjectFrame.resize(newSize);
_absoluteJointTranslationsInObjectFrameSet.resize(newSize);
_absoluteJointTranslationsInObjectFrameDirty.resize(newSize);
if (newSize >= 0 && newSize > _localJointRotations.size()) {
_localJointRotations.resize(newSize);
_localJointRotationsSet.resize(newSize);
_localJointRotationsDirty.resize(newSize);
_localJointTranslations.resize(newSize);
_localJointTranslationsSet.resize(newSize);
_localJointTranslationsDirty.resize(newSize);
}
}
@ -404,9 +404,9 @@ void ModelEntityItem::setJointRotations(const QVector<glm::quat>& rotations) {
_jointRotationsExplicitlySet = rotations.size() > 0;
resizeJointArrays(rotations.size());
for (int index = 0; index < rotations.size(); index++) {
if (_absoluteJointRotationsInObjectFrameSet[index]) {
_absoluteJointRotationsInObjectFrame[index] = rotations[index];
_absoluteJointRotationsInObjectFrameDirty[index] = true;
if (_localJointRotationsSet[index]) {
_localJointRotations[index] = rotations[index];
_localJointRotationsDirty[index] = true;
}
}
});
@ -417,7 +417,7 @@ void ModelEntityItem::setJointRotationsSet(const QVector<bool>& rotationsSet) {
_jointRotationsExplicitlySet = rotationsSet.size() > 0;
resizeJointArrays(rotationsSet.size());
for (int index = 0; index < rotationsSet.size(); index++) {
_absoluteJointRotationsInObjectFrameSet[index] = rotationsSet[index];
_localJointRotationsSet[index] = rotationsSet[index];
}
});
}
@ -427,9 +427,9 @@ void ModelEntityItem::setJointTranslations(const QVector<glm::vec3>& translation
_jointTranslationsExplicitlySet = translations.size() > 0;
resizeJointArrays(translations.size());
for (int index = 0; index < translations.size(); index++) {
if (_absoluteJointTranslationsInObjectFrameSet[index]) {
_absoluteJointTranslationsInObjectFrame[index] = translations[index];
_absoluteJointTranslationsInObjectFrameSet[index] = true;
if (_localJointTranslationsSet[index]) {
_localJointTranslations[index] = translations[index];
_localJointTranslationsSet[index] = true;
}
}
});
@ -440,7 +440,7 @@ void ModelEntityItem::setJointTranslationsSet(const QVector<bool>& translationsS
_jointTranslationsExplicitlySet = translationsSet.size() > 0;
resizeJointArrays(translationsSet.size());
for (int index = 0; index < translationsSet.size(); index++) {
_absoluteJointTranslationsInObjectFrameSet[index] = translationsSet[index];
_localJointTranslationsSet[index] = translationsSet[index];
}
});
}
@ -449,7 +449,7 @@ QVector<glm::quat> ModelEntityItem::getJointRotations() const {
QVector<glm::quat> result;
_jointDataLock.withReadLock([&] {
if (_jointRotationsExplicitlySet) {
result = _absoluteJointRotationsInObjectFrame;
result = _localJointRotations;
}
});
return result;
@ -459,7 +459,7 @@ QVector<bool> ModelEntityItem::getJointRotationsSet() const {
QVector<bool> result;
_jointDataLock.withReadLock([&] {
if (_jointRotationsExplicitlySet) {
result = _absoluteJointRotationsInObjectFrameSet;
result = _localJointRotationsSet;
}
});
@ -470,7 +470,7 @@ QVector<glm::vec3> ModelEntityItem::getJointTranslations() const {
QVector<glm::vec3> result;
_jointDataLock.withReadLock([&] {
if (_jointTranslationsExplicitlySet) {
result = _absoluteJointTranslationsInObjectFrame;
result = _localJointTranslations;
}
});
return result;
@ -480,7 +480,7 @@ QVector<bool> ModelEntityItem::getJointTranslationsSet() const {
QVector<bool> result;
_jointDataLock.withReadLock([&] {
if (_jointTranslationsExplicitlySet) {
result = _absoluteJointTranslationsInObjectFrameSet;
result = _localJointTranslationsSet;
}
});
return result;

View file

@ -117,9 +117,6 @@ public:
virtual bool shouldBePhysical() const override;
virtual glm::vec3 getJointPosition(int jointIndex) const { return glm::vec3(); }
virtual glm::quat getJointRotation(int jointIndex) const { return glm::quat(); }
virtual void setJointRotations(const QVector<glm::quat>& rotations);
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet);
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
@ -143,14 +140,14 @@ protected:
ReadWriteLockable _jointDataLock;
bool _jointRotationsExplicitlySet { false }; // were the joints set as a property or just side effect of animations
QVector<glm::quat> _absoluteJointRotationsInObjectFrame;
QVector<bool> _absoluteJointRotationsInObjectFrameSet; // ever set?
QVector<bool> _absoluteJointRotationsInObjectFrameDirty; // needs a relay to model/rig?
QVector<glm::quat> _localJointRotations;
QVector<bool> _localJointRotationsSet; // ever set?
QVector<bool> _localJointRotationsDirty; // needs a relay to model/rig?
bool _jointTranslationsExplicitlySet { false }; // were the joints set as a property or just side effect of animations
QVector<glm::vec3> _absoluteJointTranslationsInObjectFrame;
QVector<bool> _absoluteJointTranslationsInObjectFrameSet; // ever set?
QVector<bool> _absoluteJointTranslationsInObjectFrameDirty; // needs a relay to model/rig?
QVector<glm::vec3> _localJointTranslations;
QVector<bool> _localJointTranslationsSet; // ever set?
QVector<bool> _localJointTranslationsDirty; // needs a relay to model/rig?
int _lastKnownCurrentFrame;
virtual void resizeJointArrays(int newSize = -1);

View file

@ -21,6 +21,7 @@
#include "GLHelpers.h"
// Minimum gl version required is 4.1
#define MINIMUM_GL_VERSION 0x0401
OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) :
@ -49,7 +50,8 @@ QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) {
valid = true;
override = false;
QGLWidget* glWidget = new QGLWidget();
QGLWidget* glWidget = new QGLWidget(getDefaultGLFormat());
valid = glWidget->isValid();
// Inform user if no OpenGL support
if (!valid) {
@ -75,15 +77,26 @@ QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) {
// - major_number.minor_number
// - major_number.minor_number.release_number
// Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml
const QString version { "version" };
QString glVersion = glData[version].toString();
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
int majorNumber = versionParts[0].toInt();
int minorNumber = versionParts[1].toInt();
int minimumMajorNumber = (MINIMUM_GL_VERSION >> 16);
int minimumMajorNumber = (MINIMUM_GL_VERSION >> 8) & 0xFF;
int minimumMinorNumber = (MINIMUM_GL_VERSION & 0xFF);
valid = (majorNumber > minimumMajorNumber
|| (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber));
int majorNumber = 0;
int minorNumber = 0;
const QString version { "version" };
if (glData.contains(version)) {
QString glVersion = glData[version].toString();
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
if (versionParts.size() >= 2) {
majorNumber = versionParts[0].toInt();
minorNumber = versionParts[1].toInt();
valid = (majorNumber > minimumMajorNumber
|| (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber));
} else {
valid = false;
}
} else {
valid = false;
}
// Prompt user if below minimum
if (!valid) {

View file

@ -307,11 +307,20 @@ void GLBackend::render(const Batch& batch) {
renderPassTransfer(batch);
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
if (_stereo._enable) {
glEnable(GL_CLIP_DISTANCE0);
}
#endif
{
PROFILE_RANGE(_stereo._enable ? "Render Stereo" : "Render");
renderPassDraw(batch);
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
if (_stereo._enable) {
glDisable(GL_CLIP_DISTANCE0);
}
#endif
// Restore the saved stereo state for the next batch
_stereo._enable = savedStereo;
}
@ -326,13 +335,24 @@ void GLBackend::syncCache() {
glEnable(GL_LINE_SMOOTH);
}
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
void GLBackend::setupStereoSide(int side) {
ivec4 vp = _transform._viewport;
vp.z /= 2;
glViewport(vp.x + side * vp.z, vp.y, vp.z, vp.w);
#ifdef GPU_STEREO_CAMERA_BUFFER
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
glVertexAttribI1i(14, side);
#endif
#else
_transform.bindCurrentCamera(side);
#endif
}
#else
#endif
void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) {
resetStages();
@ -385,8 +405,11 @@ void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) {
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
// As long as we don;t use several versions of shaders we can avoid this more complex code path
// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo());
#ifdef GPU_STEREO_CAMERA_BUFFER
#define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, (GLShader::Version) isStereo())
#else
#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc
#endif
void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) {
if (_pipeline._program == 0) {

View file

@ -29,6 +29,29 @@
#include "GLShared.h"
// Different versions for the stereo drawcall
// Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only
//#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE
//#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER
#define GPU_STEREO_TECHNIQUE_INSTANCED
// Let these be configured by the one define picked above
#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE
#define GPU_STEREO_DRAWCALL_DOUBLED
#endif
#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER
#define GPU_STEREO_DRAWCALL_DOUBLED
#define GPU_STEREO_CAMERA_BUFFER
#endif
#ifdef GPU_STEREO_TECHNIQUE_INSTANCED
#define GPU_STEREO_DRAWCALL_INSTANCED
#define GPU_STEREO_CAMERA_BUFFER
#endif
namespace gpu { namespace gl {
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
@ -204,7 +227,10 @@ protected:
void renderPassTransfer(const Batch& batch);
void renderPassDraw(const Batch& batch);
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
void setupStereoSide(int side);
#endif
virtual void initInput() final;
virtual void killInput() final;
@ -262,7 +288,19 @@ protected:
};
struct TransformStageState {
#ifdef GPU_STEREO_CAMERA_BUFFER
struct Cameras {
TransformCamera _cams[2];
Cameras() {};
Cameras(const TransformCamera& cam) { memcpy(_cams, &cam, sizeof(TransformCamera)); };
Cameras(const TransformCamera& camL, const TransformCamera& camR) { memcpy(_cams, &camL, sizeof(TransformCamera)); memcpy(_cams + 1, &camR, sizeof(TransformCamera)); };
};
using CameraBufferElement = Cameras;
#else
using CameraBufferElement = TransformCamera;
#endif
using TransformCameras = std::vector<CameraBufferElement>;
TransformCamera _camera;

View file

@ -187,7 +187,11 @@ void GLBackend::updateInput() {
glVertexAttribFormat(slot + locNum, count, type, isNormalized, offset + locNum * perLocationSize);
glVertexAttribBinding(slot + locNum, attrib._channel);
}
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexBindingDivisor(attrib._channel, attrib._frequency * (isStereo() ? 2 : 1));
#else
glVertexBindingDivisor(attrib._channel, attrib._frequency);
#endif
}
(void)CHECK_GL_ERROR();
}
@ -306,7 +310,11 @@ void GLBackend::updateInput() {
for (size_t locNum = 0; locNum < locationCount; ++locNum) {
glVertexAttribPointer(slot + (GLuint)locNum, count, type, isNormalized, stride,
reinterpret_cast<GLvoid*>(pointer + perLocationStride * (GLuint)locNum));
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1));
#else
glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency);
#endif
}
// TODO: Support properly the IAttrib version

View file

@ -48,7 +48,13 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) {
// check the program cache
// pick the program version
// check the program cache
// pick the program version
#ifdef GPU_STEREO_CAMERA_BUFFER
GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version) isStereo());
#else
GLuint glprogram = pipelineObject->_program->getProgram();
#endif
if (_pipeline._program != glprogram) {
_pipeline._program = glprogram;

View file

@ -31,10 +31,25 @@ void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset
void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) {
memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i));
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
{
ivec4& vp = _transform._viewport;
glViewport(vp.x, vp.y, vp.z, vp.w);
// Where we assign the GL viewport
if (_stereo._enable) {
vp.z /= 2;
if (_stereo._pass) {
vp.x += vp.z;
}
}
}
#else
if (!_inRenderTransferPass && !isStereo()) {
ivec4& vp = _transform._viewport;
glViewport(vp.x, vp.y, vp.z, vp.w);
}
#endif
// The Viewport is tagged invalid because the CameraTransformUBO is not up to date and will need update on next drawcall
_transform._invalidViewport = true;
@ -100,13 +115,21 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
if (_invalidView || _invalidProj || _invalidViewport) {
size_t offset = _cameraUboSize * _cameras.size();
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
if (stereo._enable) {
_cameras.push_back((_camera.getEyeCamera(0, stereo, _view)));
_cameras.push_back((_camera.getEyeCamera(1, stereo, _view)));
} else {
_cameras.push_back((_camera.recomputeDerived(_view)));
}
if (stereo._enable) {
#ifdef GPU_STEREO_CAMERA_BUFFER
_cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view)));
#else
_cameras.push_back((_camera.getEyeCamera(0, stereo, _view)));
_cameras.push_back((_camera.getEyeCamera(1, stereo, _view)));
#endif
} else {
#ifdef GPU_STEREO_CAMERA_BUFFER
_cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view)));
#else
_cameras.push_back((_camera.recomputeDerived(_view)));
#endif
}
}
// Flags are clean
@ -122,9 +145,13 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta
}
if (offset != INVALID_OFFSET) {
#ifdef GPU_STEREO_CAMERA_BUFFER
bindCurrentCamera(0);
#else
if (!stereo._enable) {
bindCurrentCamera(0);
}
#endif
}
(void)CHECK_GL_ERROR();
}
@ -148,7 +175,11 @@ void GLBackend::updateTransform(const Batch& batch) {
glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer);
glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0,
_transform._drawCallInfoOffsets[batch._currentNamedCall]);
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, (isStereo() ? 2 : 1));
#else
glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1);
#endif
}
(void)CHECK_GL_ERROR();

View file

@ -54,9 +54,24 @@ static const std::array<std::string, NUM_SHADER_DOMAINS> DOMAIN_DEFINES { {
"#define GPU_GEOMETRY_SHADER",
} };
// Stereo specific defines
static const std::string stereoVersion {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN"
#endif
#ifdef GPU_STEREO_DRAWCALL_DOUBLED
#ifdef GPU_STEREO_CAMERA_BUFFER
"#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED"
#else
"#define GPU_TRANSFORM_IS_STEREO"
#endif
#endif
};
// Versions specific of the shader
static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
""
"",
stereoVersion
} };
GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) {
@ -181,7 +196,8 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin
// Define the public slots only from the default version
if (version == 0) {
shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs);
} else {
} // else
{
GLShader::UniformMapping mapping;
for (auto srcUniform : shader.getUniforms()) {
mapping[srcUniform._location] = uniforms.findLocation(srcUniform._name);

View file

@ -19,6 +19,8 @@ public:
enum Version {
Mono = 0,
Stereo,
NumVersions
};
@ -40,8 +42,8 @@ public:
GLint getUniformLocation(GLint srcLoc, Version version = Mono) {
// THIS will be used in the future PR as we grow the number of versions
// return _uniformMappings[version][srcLoc];
return srcLoc;
return _uniformMappings[version][srcLoc];
// return srcLoc;
}
const std::weak_ptr<GLBackend> _backend;

View file

@ -25,11 +25,14 @@ void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) {
uint32 startVertex = batch._params[paramOffset + 0]._uint;
if (isStereo()) {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawArraysInstanced(mode, startVertex, numVertices, 2);
#else
setupStereoSide(0);
glDrawArrays(mode, startVertex, numVertices);
setupStereoSide(1);
glDrawArrays(mode, startVertex, numVertices);
#endif
_stats._DSNumTriangles += 2 * numVertices / 3;
_stats._DSNumDrawcalls += 2;
@ -55,11 +58,14 @@ void GL41Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) {
GLvoid* indexBufferByteOffset = reinterpret_cast<GLvoid*>(startIndex * typeByteSize + _input._indexBufferOffset);
if (isStereo()) {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, 2);
#else
setupStereoSide(0);
glDrawElements(mode, numIndices, glType, indexBufferByteOffset);
setupStereoSide(1);
glDrawElements(mode, numIndices, glType, indexBufferByteOffset);
#endif
_stats._DSNumTriangles += 2 * numIndices / 3;
_stats._DSNumDrawcalls += 2;
} else {
@ -83,11 +89,14 @@ void GL41Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) {
if (isStereo()) {
GLint trueNumInstances = 2 * numInstances;
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawArraysInstanced(mode, startVertex, numVertices, trueNumInstances);
#else
setupStereoSide(0);
glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances);
glDrawArraysInstanced(mode, startVertex, numVertices, numInstances);
setupStereoSide(1);
glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances);
glDrawArraysInstanced(mode, startVertex, numVertices, numInstances);
#endif
_stats._DSNumTriangles += (trueNumInstances * numVertices) / 3;
_stats._DSNumDrawcalls += trueNumInstances;
} else {
@ -124,10 +133,14 @@ void GL41Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset
if (isStereo()) {
GLint trueNumInstances = 2 * numInstances;
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, trueNumInstances, 0, startInstance);
#else
setupStereoSide(0);
glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance);
setupStereoSide(1);
glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance);
#endif
_stats._DSNumTriangles += (trueNumInstances * numIndices) / 3;
_stats._DSNumDrawcalls += trueNumInstances;

View file

@ -25,10 +25,14 @@ void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) {
uint32 startVertex = batch._params[paramOffset + 0]._uint;
if (isStereo()) {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawArraysInstanced(mode, startVertex, numVertices, 2);
#else
setupStereoSide(0);
glDrawArrays(mode, startVertex, numVertices);
setupStereoSide(1);
glDrawArrays(mode, startVertex, numVertices);
#endif
_stats._DSNumTriangles += 2 * numVertices / 3;
_stats._DSNumDrawcalls += 2;
@ -55,11 +59,14 @@ void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) {
GLvoid* indexBufferByteOffset = reinterpret_cast<GLvoid*>(startIndex * typeByteSize + _input._indexBufferOffset);
if (isStereo()) {
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, 2);
#else
setupStereoSide(0);
glDrawElements(mode, numIndices, glType, indexBufferByteOffset);
setupStereoSide(1);
glDrawElements(mode, numIndices, glType, indexBufferByteOffset);
#endif
_stats._DSNumTriangles += 2 * numIndices / 3;
_stats._DSNumDrawcalls += 2;
} else {
@ -83,10 +90,14 @@ void GL45Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) {
if (isStereo()) {
GLint trueNumInstances = 2 * numInstances;
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawArraysInstanced(mode, startVertex, numVertices, trueNumInstances);
#else
setupStereoSide(0);
glDrawArraysInstanced(mode, startVertex, numVertices, numInstances);
setupStereoSide(1);
glDrawArraysInstanced(mode, startVertex, numVertices, numInstances);
#endif
_stats._DSNumTriangles += (trueNumInstances * numVertices) / 3;
_stats._DSNumDrawcalls += trueNumInstances;
@ -112,10 +123,15 @@ void GL45Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset
if (isStereo()) {
GLint trueNumInstances = 2 * numInstances;
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, trueNumInstances, 0, startInstance);
#else
setupStereoSide(0);
glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance);
setupStereoSide(1);
glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance);
#endif
_stats._DSNumTriangles += (trueNumInstances * numIndices) / 3;
_stats._DSNumDrawcalls += trueNumInstances;
} else {

View file

@ -14,6 +14,8 @@
#include "../gl/GLBackend.h"
#include "../gl/GLTexture.h"
#define INCREMENTAL_TRANSFER 0
namespace gpu { namespace gl45 {
using namespace gpu::gl;
@ -56,6 +58,7 @@ public:
GLint pageDimensionsIndex { 0 };
};
#if INCREMENTAL_TRANSFER
struct TransferState {
TransferState(GL45Texture& texture);
uvec3 currentPageSize() const;
@ -74,6 +77,10 @@ public:
uvec3 mipOffset;
const uint8_t* srcPointer { nullptr };
};
protected:
TransferState _transferState;
#endif
protected:
void updateMips() override;
void stripToMip(uint16_t newMinMip);
@ -91,7 +98,6 @@ public:
void derez();
SparseInfo _sparseInfo;
TransferState _transferState;
uint32_t _allocatedPages { 0 };
uint32_t _lastMipAllocatedPages { 0 };
uint16_t _mipOffset { 0 };

View file

@ -162,6 +162,8 @@ void GL45Backend::initTextureManagementStage() {
}
}
#if INCREMENTAL_TRANSFER
using TransferState = GL45Backend::GL45Texture::TransferState;
TransferState::TransferState(GL45Texture& texture) : texture(texture) {
@ -246,6 +248,7 @@ void TransferState::populatePage(std::vector<uint8_t>& buffer) {
uvec3 TransferState::currentPageSize() const {
return glm::clamp(mipDimensions - mipOffset, uvec3(1), texture._sparseInfo.pageDimensions);
}
#endif
GLuint GL45Texture::allocate(const Texture& texture) {
GLuint result;
@ -258,11 +261,19 @@ GLuint GL45Backend::getTextureID(const TexturePointer& texture, bool transfer) {
}
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, GLuint externalId)
: GLTexture(backend, texture, externalId), _sparseInfo(*this), _transferState(*this) {
: GLTexture(backend, texture, externalId), _sparseInfo(*this)
#if INCREMENTAL_TRANSFER
, _transferState(*this)
#endif
{
}
GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture& texture, bool transferrable)
: GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this), _transferState(*this) {
: GLTexture(backend, texture, allocate(texture), transferrable), _sparseInfo(*this)
#if INCREMENTAL_TRANSFER
, _transferState(*this)
#endif
{
auto theBackend = _backend.lock();
if (_transferrable && theBackend && theBackend->isTextureManagementSparseEnabled()) {
@ -274,10 +285,10 @@ GL45Texture::GL45Texture(const std::weak_ptr<GLBackend>& backend, const Texture&
}
GL45Texture::~GL45Texture() {
// External textures cycle very quickly, so don't spam the log with messages about them.
if (!_gpuObject.getUsage().isExternal()) {
qCDebug(gpugl45logging) << "Destroying texture " << _id << " from source " << _source.c_str();
}
// // External textures cycle very quickly, so don't spam the log with messages about them.
// if (!_gpuObject.getUsage().isExternal()) {
// qCDebug(gpugl45logging) << "Destroying texture " << _id << " from source " << _source.c_str();
// }
// Remove this texture from the candidate list of derezzable textures
if (_transferrable) {
@ -375,39 +386,40 @@ void GL45Texture::updateSize() const {
void GL45Texture::startTransfer() {
Parent::startTransfer();
_sparseInfo.update();
#if INCREMENTAL_TRANSFER
_transferState.updateMip();
#endif
}
bool GL45Texture::continueTransfer() {
if (!Texture::getEnableIncrementalTextureTransfers()) {
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
for (uint8_t face = 0; face < maxFace; ++face) {
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
auto size = _gpuObject.evalMipDimensions(mipLevel);
if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) {
glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE);
_allocatedPages += _sparseInfo.getPageCount(size);
}
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
if (GL_TEXTURE_2D == _target) {
glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
} else if (GL_TEXTURE_CUBE_MAP == _target) {
// DSA ARB does not work on AMD, so use EXT
// glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData());
auto target = CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
} else {
Q_ASSERT(false);
}
(void)CHECK_GL_ERROR();
#if !INCREMENTAL_TRANSFER
size_t maxFace = GL_TEXTURE_CUBE_MAP == _target ? CUBE_NUM_FACES : 1;
for (uint8_t face = 0; face < maxFace; ++face) {
for (uint16_t mipLevel = _minMip; mipLevel <= _maxMip; ++mipLevel) {
auto size = _gpuObject.evalMipDimensions(mipLevel);
if (_sparseInfo.sparse && mipLevel <= _sparseInfo.maxSparseLevel) {
glTexturePageCommitmentEXT(_id, mipLevel, 0, 0, face, size.x, size.y, 1, GL_TRUE);
_allocatedPages += _sparseInfo.getPageCount(size);
}
if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) {
auto mip = _gpuObject.accessStoredMipFace(mipLevel, face);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), mip->getFormat());
if (GL_TEXTURE_2D == _target) {
glTextureSubImage2D(_id, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
} else if (GL_TEXTURE_CUBE_MAP == _target) {
// DSA ARB does not work on AMD, so use EXT
// glTextureSubImage3D(_id, mipLevel, 0, 0, face, size.x, size.y, 1, texelFormat.format, texelFormat.type, mip->readData());
auto target = CUBE_FACE_LAYOUT[face];
glTextureSubImage2DEXT(_id, target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData());
} else {
Q_ASSERT(false);
}
(void)CHECK_GL_ERROR();
}
}
return false;
}
return false;
#else
static std::vector<uint8_t> buffer;
if (buffer.empty()) {
buffer.resize(DEFAULT_PAGE_BUFFER_SIZE);
@ -458,6 +470,7 @@ bool GL45Texture::continueTransfer() {
_lastMipAllocatedPages = _allocatedPages;
}
return result;
#endif
}
void GL45Texture::finishTransfer() {

View file

@ -35,18 +35,15 @@ std::atomic<Texture::Size> Texture::_allowedCPUMemoryUsage { 0 };
#define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5
bool recommendedIncrementalTransfers = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES);
bool recommendedSparseTextures = recommendedIncrementalTransfers;
bool recommendedSparseTextures = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES);
std::atomic<bool> Texture::_enableSparseTextures { recommendedIncrementalTransfers };
std::atomic<bool> Texture::_enableIncrementalTextureTransfers { recommendedSparseTextures };
std::atomic<bool> Texture::_enableSparseTextures { recommendedSparseTextures };
struct ReportTextureState {
ReportTextureState() {
qDebug() << "[TEXTURE TRANSFER SUPPORT]"
<< "\n\tidealThreadCount:" << QThread::idealThreadCount()
<< "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures
<< "\n\tRECOMMENDED enableIncrementalTextures:" << recommendedIncrementalTransfers;
<< "\n\tRECOMMENDED enableSparseTextures:" << recommendedSparseTextures;
}
} report;
@ -59,16 +56,6 @@ void Texture::setEnableSparseTextures(bool enabled) {
#endif
}
void Texture::setEnableIncrementalTextureTransfers(bool enabled) {
#ifdef Q_OS_WIN
qDebug() << "[TEXTURE TRANSFER SUPPORT] SETTING - Enable Incremental Texture Transfer:" << enabled;
_enableIncrementalTextureTransfers = enabled;
#else
qDebug() << "[TEXTURE TRANSFER SUPPORT] Incremental Texture Transfer not supported on this platform.";
#endif
}
void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) {
return;
@ -84,10 +71,6 @@ bool Texture::getEnableSparseTextures() {
return _enableSparseTextures.load();
}
bool Texture::getEnableIncrementalTextureTransfers() {
return _enableIncrementalTextureTransfers.load();
}
uint32_t Texture::getTextureCPUCount() {
return _textureCPUCount.load();
}

View file

@ -143,10 +143,8 @@ class Texture : public Resource {
static std::atomic<uint32_t> _textureCPUCount;
static std::atomic<Size> _textureCPUMemoryUsage;
static std::atomic<Size> _allowedCPUMemoryUsage;
static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
static std::atomic<bool> _enableSparseTextures;
static std::atomic<bool> _enableIncrementalTextureTransfers;
static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
public:
static uint32_t getTextureCPUCount();
@ -162,10 +160,7 @@ public:
static void setAllowedGPUMemoryUsage(Size size);
static bool getEnableSparseTextures();
static bool getEnableIncrementalTextureTransfers();
static void setEnableSparseTextures(bool enabled);
static void setEnableIncrementalTextureTransfers(bool enabled);
using ExternalRecycler = std::function<void(uint32, void*)>;
using ExternalIdAndFence = std::pair<uint32, void*>;

View file

@ -22,29 +22,84 @@ struct TransformCamera {
};
layout(std140) uniform transformCameraBuffer {
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
TransformCamera _camera[2];
#else
TransformCamera _camera;
#endif
#else
TransformCamera _camera;
#endif
};
#ifdef GPU_VERTEX_SHADER
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
#ifdef GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED
layout(location=14) in int _inStereoSide;
#endif
flat out int _stereoSide;
#endif
#endif
#endif
#ifdef GPU_PIXEL_SHADER
#ifdef GPU_TRANSFORM_STEREO_CAMERA
flat in int _stereoSide;
#endif
#endif
TransformCamera getTransformCamera() {
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
#ifdef GPU_VERTEX_SHADER
#ifdef GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED
_stereoSide = _inStereoSide;
#endif
#ifdef GPU_TRANSFORM_STEREO_CAMERA_INSTANCED
_stereoSide = gl_InstanceID % 2;
#endif
#endif
return _camera[_stereoSide];
#else
return _camera;
#endif
#else
return _camera;
#endif
}
vec3 getEyeWorldPos() {
return _camera._viewInverse[3].xyz;
return getTransformCamera()._viewInverse[3].xyz;
}
bool cam_isStereo() {
#ifdef GPU_TRANSFORM_IS_STEREO
return getTransformCamera()._stereoInfo.x > 0.0;
#else
return _camera._stereoInfo.x > 0.0;
#endif
}
float cam_getStereoSide() {
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_CAMERA
return getTransformCamera()._stereoInfo.y;
#else
return _camera._stereoInfo.y;
#endif
#else
return _camera._stereoInfo.y;
#endif
}
<@endfunc@>
<@func declareStandardObjectTransform()@>
struct TransformObject {
mat4 _model;
@ -92,6 +147,25 @@ TransformObject getTransformObject() {
<$viewport$> = <$cameraTransform$>._viewport;
<@endfunc@>
<@func transformStereoClipsSpace(cameraTransform, clipPos)@>
{
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1));
vec2 eyeOffsetScale = vec2(-0.5, +0.5);
uint eyeIndex = _stereoSide;
gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]);
float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w;
<$clipPos$>.x = newClipPosX;
#endif
#else
#endif
}
<@endfunc@>
<@func transformModelToEyeWorldAlignedPos(cameraTransform, objectTransform, modelPos, eyeWorldAlignedPos)@>
<!// Bring the model pos in the world aligned space centered on the eye axis !>
{ // _transformModelToEyeWorldAlignedPos
@ -108,6 +182,8 @@ TransformObject getTransformObject() {
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
}
<@endfunc@>
@ -117,6 +193,8 @@ TransformObject getTransformObject() {
<$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$>
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos;
<$eyePos$> = vec4((<$cameraTransform$>._view * vec4(eyeWAPos.xyz, 0.0)).xyz, 1.0);
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
}
<@endfunc@>
@ -135,11 +213,10 @@ TransformObject getTransformObject() {
<@endfunc@>
<@func transformModelToWorldDir(cameraTransform, objectTransform, modelDir, worldDir)@>
{ // transformModelToEyeDir
{ // transformModelToEyeDir
vec3 mr0 = <$objectTransform$>._modelInverse[0].xyz;
vec3 mr1 = <$objectTransform$>._modelInverse[1].xyz;
vec3 mr2 = <$objectTransform$>._modelInverse[2].xyz;
<$worldDir$> = vec3(dot(mr0, <$modelDir$>), dot(mr1, <$modelDir$>), dot(mr2, <$modelDir$>));
}
<@endfunc@>
@ -173,6 +250,8 @@ TransformObject getTransformObject() {
<@func transformEyeToClipPos(cameraTransform, eyePos, clipPos)@>
{ // transformEyeToClipPos
<$clipPos$> = <$cameraTransform$>._projection * vec4(<$eyePos$>.xyz, 1.0);
<$transformStereoClipsSpace($cameraTransform$, $clipPos$)$>
}
<@endfunc@>

View file

@ -18,7 +18,7 @@
#include <PathUtils.h>
#include <NumericalConstants.h>
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
const char* KeyboardMouseDevice::NAME = "Keyboard/Mouse";
bool KeyboardMouseDevice::_enableTouch = true;
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {

View file

@ -52,21 +52,21 @@ public:
MOUSE_AXIS_WHEEL_X_POS,
MOUSE_AXIS_WHEEL_X_NEG,
};
enum TouchAxisChannel {
TOUCH_AXIS_X_POS = MOUSE_AXIS_WHEEL_X_NEG + 1,
TOUCH_AXIS_X_NEG,
TOUCH_AXIS_Y_POS,
TOUCH_AXIS_Y_NEG,
};
enum TouchButtonChannel {
TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1,
};
// Plugin functions
bool isSupported() const override { return true; }
const QString& getName() const override { return NAME; }
const QString getName() const override { return NAME; }
bool isHandController() const override { return false; }
@ -88,8 +88,8 @@ public:
void wheelEvent(QWheelEvent* event);
static void enableTouch(bool enableTouch) { _enableTouch = enableTouch; }
static const QString NAME;
static const char* NAME;
protected:

View file

@ -21,7 +21,7 @@
#include <PathUtils.h>
#include <NumericalConstants.h>
const QString TouchscreenDevice::NAME = "Touchscreen";
const char* TouchscreenDevice::NAME = "Touchscreen";
bool TouchscreenDevice::isSupported() const {
for (auto touchDevice : QTouchDevice::devices()) {

View file

@ -22,7 +22,7 @@ class QGestureEvent;
class TouchscreenDevice : public InputPlugin {
Q_OBJECT
public:
enum TouchAxisChannel {
TOUCH_AXIS_X_POS = 0,
TOUCH_AXIS_X_NEG,
@ -37,7 +37,7 @@ public:
// Plugin functions
virtual bool isSupported() const override;
virtual const QString& getName() const override { return NAME; }
virtual const QString getName() const override { return NAME; }
bool isHandController() const override { return false; }
@ -48,8 +48,8 @@ public:
void touchEndEvent(const QTouchEvent* event);
void touchUpdateEvent(const QTouchEvent* event);
void touchGestureEvent(const QGestureEvent* event);
static const QString NAME;
static const char* NAME;
protected:

View file

@ -36,4 +36,6 @@ void main(void) {
// Position is supposed to come in clip space
gl_Position = vec4(inPosition.xy, 0.0, 1.0);
<$transformStereoClipsSpace(cam, gl_Position)$>
}

View file

@ -272,27 +272,17 @@ void DomainHandler::setIsConnected(bool isConnected) {
}
void DomainHandler::requestDomainSettings() {
// TODO: the nodes basically lock if they don't get a response - add a timeout to this so that they at least restart
// if they can't get settings
NodeType_t owningNodeType = DependencyManager::get<NodeList>()->getOwnerType();
if (owningNodeType == NodeType::Agent) {
// for now the agent nodes don't need any domain settings
_settingsObject = QJsonObject();
emit settingsReceived(_settingsObject);
} else {
qCDebug(networking) << "Requesting settings from domain server";
Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get<NodeList>()->getOwnerType());
auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false);
packet->writePrimitive(assignmentType);
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->sendPacket(std::move(packet), _sockAddr);
_settingsTimer.start();
}
qCDebug(networking) << "Requesting settings from domain server";
Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get<NodeList>()->getOwnerType());
auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false);
packet->writePrimitive(assignmentType);
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->sendPacket(std::move(packet), _sockAddr);
_settingsTimer.start();
}
void DomainHandler::processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList) {

View file

@ -815,3 +815,31 @@ void NodeList::kickNodeBySessionID(const QUuid& nodeID) {
}
}
void NodeList::muteNodeBySessionID(const QUuid& nodeID) {
// cannot mute yourself, or nobody
if (!nodeID.isNull() && _sessionUUID != nodeID ) {
if (getThisNodeCanKick()) {
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// setup the packet
auto mutePacket = NLPacket::create(PacketType::NodeMuteRequest, NUM_BYTES_RFC4122_UUID, true);
// write the node ID to the packet
mutePacket->write(nodeID.toRfc4122());
qDebug() << "Sending packet to mute node" << uuidStringWithoutCurlyBraces(nodeID);
sendPacket(std::move(mutePacket), *audioMixer);
} else {
qWarning() << "Couldn't find audio mixer to send node mute request";
}
} else {
qWarning() << "You do not have permissions to mute in this domain."
<< "Request to mute node" << uuidStringWithoutCurlyBraces(nodeID) << "will not be sent";
}
} else {
qWarning() << "NodeList::muteNodeBySessionID called with an invalid ID or an ID which matches the current session ID.";
}
}

View file

@ -74,6 +74,7 @@ public:
bool isIgnoringNode(const QUuid& nodeID) const;
void kickNodeBySessionID(const QUuid& nodeID);
void muteNodeBySessionID(const QUuid& nodeID);
public slots:
void reset();

View file

@ -88,7 +88,24 @@ class ScriptableResource : public QObject {
Q_PROPERTY(QUrl url READ getUrl)
Q_PROPERTY(int state READ getState NOTIFY stateChanged)
/**jsdoc
* @constructor Resource
* @property url {string} url of this resource
* @property state {Resource.State} current loading state
*/
public:
/**jsdoc
* @name Resource.State
* @static
* @property QUEUED {int} The resource is queued up, waiting to be loaded.
* @property LOADING {int} The resource is downloading
* @property LOADED {int} The resource has finished downloaded by is not complete
* @property FINISHED {int} The resource has completly finished loading and is ready.
* @property FAILED {int} Downloading the resource has failed.
*/
enum State {
QUEUED,
LOADING,
@ -101,6 +118,10 @@ public:
ScriptableResource(const QUrl& url);
virtual ~ScriptableResource() = default;
/**jsdoc
* Release this resource
* @function Resource#release
*/
Q_INVOKABLE void release();
const QUrl& getUrl() const { return _url; }
@ -111,7 +132,22 @@ public:
void setInScript(bool isInScript);
signals:
/**jsdoc
* Signaled when download progress for this resource has changed
* @function Resource#progressChanged
* @param bytesReceived {int} bytes downloaded so far
* @param bytesTotal {int} total number of bytes in the resource
* @returns {Signal}
*/
void progressChanged(uint64_t bytesReceived, uint64_t bytesTotal);
/**jsdoc
* Signaled when resource loading state has changed
* @function Resource#stateChanged
* @param bytesReceived {Resource.State} new state
* @returns {Signal}
*/
void stateChanged(int state);
protected:
@ -148,14 +184,49 @@ class ResourceCache : public QObject {
Q_PROPERTY(size_t numCached READ getNumCachedResources NOTIFY dirty)
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
/**jsdoc
* @namespace ResourceCache
* @property numTotal {number} total number of total resources
* @property numCached {number} total number of cached resource
* @property sizeTotal {number} size in bytes of all resources
* @property sizeCached {number} size in bytes of all cached resources
*/
public:
/**jsdoc
* Returns the total number of resources
* @function ResourceCache.getNumTotalResources
* @return {number}
*/
size_t getNumTotalResources() const { return _numTotalResources; }
/**jsdoc
* Returns the total size in bytes of all resources
* @function ResourceCache.getSizeTotalResources
* @return {number}
*/
size_t getSizeTotalResources() const { return _totalResourcesSize; }
/**jsdoc
* Returns the total number of cached resources
* @function ResourceCache.getNumCachedResources
* @return {number}
*/
size_t getNumCachedResources() const { return _numUnusedResources; }
/**jsdoc
* Returns the total size in bytes of cached resources
* @function ResourceCache.getSizeCachedResources
* @return {number}
*/
size_t getSizeCachedResources() const { return _unusedResourcesSize; }
/**jsdoc
* Returns list of all resource urls
* @function ResourceCache.getResourceList
* @return {string[]}
*/
Q_INVOKABLE QVariantList getResourceList();
static void setRequestLimit(int limit);
@ -192,6 +263,13 @@ protected slots:
/// returns an empty smart pointer and loads its asynchronously.
/// \param fallback a fallback URL to load if the desired one is unavailable
/// \param extra extra data to pass to the creator, if appropriate
/**jsdoc
* Asynchronously loads a resource from the spedified URL and returns it.
* @param url {string} url of resource to load
* @param fallback {string} fallback URL if load of the desired url fails
* @function ResourceCache.getResource
* @return {Resource}
*/
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
void* extra = NULL);
@ -203,6 +281,12 @@ protected:
// Pointers created through this method should be owned by the caller,
// which should be a QScriptEngine with ScriptableResource registered, so that
// the QScriptEngine will delete the pointer when it is garbage collected.
/**jsdoc
* Prefetches a resource.
* @param url {string} url of resource to load
* @function ResourceCache.prefetch
* @return {Resource}
*/
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); }
/// Creates a new resource.

View file

@ -26,7 +26,8 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< PacketType::NodeJsonStats << PacketType::EntityQuery
<< PacketType::OctreeDataNack << PacketType::EntityEditNack
<< PacketType::DomainListRequest << PacketType::StopNode
<< PacketType::DomainDisconnectRequest << PacketType::NodeKickRequest;
<< PacketType::DomainDisconnectRequest << PacketType::NodeKickRequest
<< PacketType::NodeMuteRequest;
const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
<< PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment
@ -47,7 +48,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityAdd:
case PacketType::EntityEdit:
case PacketType::EntityData:
return VERSION_ENTITIES_ARROW_ACTION;
return VERSION_ENTITIES_LAST_EDITED_BY;
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
case PacketType::BulkAvatarData:

View file

@ -99,7 +99,8 @@ public:
SelectedAudioFormat,
MoreEntityShapes,
NodeKickRequest,
LAST_PACKET_TYPE = NodeKickRequest
NodeMuteRequest,
LAST_PACKET_TYPE = NodeMuteRequest
};
};
@ -188,6 +189,7 @@ const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62;
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64;
const PacketVersion VERSION_ENTITIES_LAST_EDITED_BY = 65;
enum class AssetServerPacketVersion: PacketVersion {
VegasCongestionControl = 19

View file

@ -165,6 +165,12 @@ qint64 Socket::writePacketList(std::unique_ptr<PacketList> packetList, const Hif
// hand this packetList off to writeReliablePacketList
// because Qt can't invoke with the unique_ptr we have to release it here and re-construct in writeReliablePacketList
if (packetList->getNumPackets() == 0) {
qCWarning(networking) << "Trying to send packet list with 0 packets, bailing.";
return 0;
}
if (QThread::currentThread() != thread()) {
auto ptr = packetList.release();
QMetaObject::invokeMethod(this, "writeReliablePacketList", Qt::AutoConnection,

View file

@ -188,6 +188,8 @@ public:
virtual float renderRate() const { return -1.0f; }
// Rate at which we present to the display device
virtual float presentRate() const { return -1.0f; }
// Rate at which old frames are presented to the device display
virtual float stutterRate() const { return -1.0f; }
// Rate at which new frames are being presented to the display device
virtual float newFramePresentRate() const { return -1.0f; }
// Rate at which rendered frames are being skipped

View file

@ -7,7 +7,7 @@
//
#include "Plugin.h"
QString Plugin::UNKNOWN_PLUGIN_ID("unknown");
const char* Plugin::UNKNOWN_PLUGIN_ID { "unknown" };
void Plugin::setContainer(PluginContainer* container) {
_container = container;

View file

@ -18,7 +18,7 @@ class Plugin : public QObject {
Q_OBJECT
public:
/// \return human-readable name
virtual const QString& getName() const = 0;
virtual const QString getName() const = 0;
typedef enum { STANDARD, ADVANCED, DEVELOPER } grouping;
@ -26,10 +26,10 @@ public:
virtual grouping getGrouping() const { return STANDARD; }
/// \return string ID (not necessarily human-readable)
virtual const QString& getID() const { assert(false); return UNKNOWN_PLUGIN_ID; }
virtual const QString getID() const { assert(false); return UNKNOWN_PLUGIN_ID; }
virtual bool isSupported() const;
void setContainer(PluginContainer* container);
/// Called when plugin is initially loaded, typically at application start
@ -74,6 +74,6 @@ signals:
protected:
bool _active { false };
PluginContainer* _container { nullptr };
static QString UNKNOWN_PLUGIN_ID;
static const char* UNKNOWN_PLUGIN_ID;
};

View file

@ -121,7 +121,7 @@ AnimDebugDraw::AnimDebugDraw() :
// HACK: add red, green and blue axis at (1,1,1)
_animDebugDrawData->_vertexBuffer->resize(sizeof(AnimDebugDrawData::Vertex) * 6);
static std::vector<AnimDebugDrawData::Vertex> vertices({
AnimDebugDrawData::Vertex { glm::vec3(1.0, 1.0f, 1.0f), toRGBA(255, 0, 0, 255) },
AnimDebugDrawData::Vertex { glm::vec3(2.0, 1.0f, 1.0f), toRGBA(255, 0, 0, 255) },
@ -162,9 +162,10 @@ static const uint32_t blue = toRGBA(0, 0, 255, 255);
const int NUM_CIRCLE_SLICES = 24;
static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius, AnimDebugDrawData::Vertex*& v) {
static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius, glm::vec4& vecColor, AnimDebugDrawData::Vertex*& v) {
const float XYZ_AXIS_LENGTH = radius * 4.0f;
const uint32_t color = toRGBA(vecColor);
AnimPose finalPose = rootPose * pose;
glm::vec3 base = rootPose * pose.trans;
@ -192,10 +193,10 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius
// x-ring
for (int i = 0; i < NUM_CIRCLE_SLICES; i++) {
v->pos = xRing[i];
v->rgba = red;
v->rgba = color;
v++;
v->pos = xRing[i + 1];
v->rgba = red;
v->rgba = color;
v++;
}
@ -210,10 +211,10 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius
// y-ring
for (int i = 0; i < NUM_CIRCLE_SLICES; i++) {
v->pos = yRing[i];
v->rgba = green;
v->rgba = color;
v++;
v->pos = yRing[i + 1];
v->rgba = green;
v->rgba = color;
v++;
}
@ -228,10 +229,10 @@ static void addBone(const AnimPose& rootPose, const AnimPose& pose, float radius
// z-ring
for (int i = 0; i < NUM_CIRCLE_SLICES; i++) {
v->pos = zRing[i];
v->rgba = blue;
v->rgba = color;
v++;
v->pos = zRing[i + 1];
v->rgba = blue;
v->rgba = color;
v++;
}
}
@ -367,7 +368,7 @@ void AnimDebugDraw::update() {
const float radius = BONE_RADIUS / (absPoses[i].scale.x * rootPose.scale.x);
// draw bone
addBone(rootPose, absPoses[i], radius, v);
addBone(rootPose, absPoses[i], radius, color, v);
// draw link to parent
auto parentIndex = skeleton->getParentIndex(i);
@ -382,20 +383,18 @@ void AnimDebugDraw::update() {
for (auto& iter : markerMap) {
glm::quat rot = std::get<0>(iter.second);
glm::vec3 pos = std::get<1>(iter.second);
glm::vec4 color = std::get<2>(iter.second); // TODO: currently ignored.
Q_UNUSED(color);
glm::vec4 color = std::get<2>(iter.second);
const float radius = POSE_RADIUS;
addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, v);
addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, color, v);
}
AnimPose myAvatarPose(glm::vec3(1), DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos());
for (auto& iter : myAvatarMarkerMap) {
glm::quat rot = std::get<0>(iter.second);
glm::vec3 pos = std::get<1>(iter.second);
glm::vec4 color = std::get<2>(iter.second); // TODO: currently ignored.
Q_UNUSED(color);
glm::vec4 color = std::get<2>(iter.second);
const float radius = POSE_RADIUS;
addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, v);
addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, color, v);
}
// draw rays from shared DebugDraw singleton

View file

@ -24,7 +24,7 @@ out vec4 _texCoord0;
void main(void) {
if (sphereParam.w != 0.0) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
@ -33,9 +33,14 @@ void main(void) {
vec4 projected = gl_Position / gl_Position.w;
projected.xy = (projected.xy + 1.0) * 0.5;
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
#else
if (cam_isStereo()) {
projected.x = 0.5 * (projected.x + cam_getStereoSide());
}
#endif
#endif
_texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w;
} else {
const float depth = -1.0; //Draw at near plane
@ -47,11 +52,27 @@ void main(void) {
);
vec4 pos = UNIT_QUAD[gl_VertexID];
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
TransformCamera cam = getTransformCamera();
<$transformStereoClipsSpace(cam, pos)$>
#endif
#endif
_texCoord0 = vec4((pos.xy + 1.0) * 0.5, 0.0, 1.0);
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
#else
if (cam_isStereo()) {
_texCoord0.x = 0.5 * (_texCoord0.x + cam_getStereoSide());
}
#endif
#endif
gl_Position = pos;
}
}

View file

@ -46,9 +46,14 @@ void main(void) {
vec4 projected = gl_Position / gl_Position.w;
projected.xy = (projected.xy + 1.0) * 0.5;
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
#else
if (cam_isStereo()) {
projected.x = 0.5 * (projected.x + cam_getStereoSide());
}
#endif
#endif
_texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w;
} else {
const float depth = -1.0; //Draw at near plane
@ -60,10 +65,24 @@ void main(void) {
);
vec4 pos = UNIT_QUAD[gl_VertexID];
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
TransformCamera cam = getTransformCamera();
<$transformStereoClipsSpace(cam, pos)$>
#endif
#endif
_texCoord0 = vec4((pos.xy + 1.0) * 0.5, 0.0, 1.0);
#ifdef GPU_TRANSFORM_IS_STEREO
#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN
#else
if (cam_isStereo()) {
_texCoord0.x = 0.5 * (_texCoord0.x + cam_getStereoSide());
}
#endif
#endif
gl_Position = pos;
}
}

View file

@ -19,13 +19,60 @@
#include <AssetClient.h>
/**jsdoc
* @namespace Assets
*/
class AssetScriptingInterface : public QObject {
Q_OBJECT
public:
AssetScriptingInterface(QScriptEngine* engine);
/**jsdoc
* Upload content to the connected domain's asset server.
* @function Assets.uploadData
* @static
* @param data {string} content to upload
* @param callback {Assets~uploadDataCallback} called when upload is complete
*/
/**jsdoc
* Called when uploadData is complete
* @callback Assets~uploadDataCallback
* @param {string} url
* @param {string} hash
*/
Q_INVOKABLE void uploadData(QString data, QScriptValue callback);
/**jsdoc
* Download data from the connected domain's asset server.
* @function Assets.downloadData
* @static
* @param url {string} url of asset to download, must be atp scheme url.
* @param callback {Assets~downloadDataCallback}
*/
/**jsdoc
* Called when downloadData is complete
* @callback Assets~downloadDataCallback
* @param data {string} content that was downloaded
*/
Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete);
/**jsdoc
* Sets up a path to hash mapping within the connected domain's asset server
* @function Assets.setMapping
* @static
* @param path {string}
* @param hash {string}
* @param callback {Assets~setMappingCallback}
*/
/**jsdoc
* Called when setMapping is complete
* @callback Assets~setMappingCallback
*/
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
#if (PR_BUILD || DEV_BUILD)

View file

@ -1172,7 +1172,8 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
// Guard against meaningless query and fragment parts.
// Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())
const auto strippingFlags = QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment;
for (QString file : includeFiles) {
for (QString includeFile : includeFiles) {
QString file = ResourceManager::normalizeURL(includeFile);
QUrl thisURL;
bool isStandardLibrary = false;
if (file.startsWith("/~/")) {
@ -1187,20 +1188,15 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
thisURL = resolvePath(file);
}
if (!_includedURLs.contains(thisURL)) {
if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") &&
(currentSandboxURL.scheme() != "file" ||
!thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) {
qCWarning(scriptengine) << "Script.include() ignoring file path"
<< thisURL << "outside of original entity script" << currentSandboxURL;
} else {
// We could also check here for CORS, but we don't yet.
// It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here.
urls.append(thisURL);
_includedURLs << thisURL;
}
if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") &&
(currentSandboxURL.scheme() != "file" ||
!thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) {
qCWarning(scriptengine) << "Script.include() ignoring file path"
<< thisURL << "outside of original entity script" << currentSandboxURL;
} else {
qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL;
// We could also check here for CORS, but we don't yet.
// It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here.
urls.append(thisURL);
}
}
@ -1220,13 +1216,20 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
if (contents.isNull()) {
qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__;
} else {
// Set the parent url so that path resolution will be relative
// to this script's url during its initial evaluation
_parentURL = url.toString();
auto operation = [&]() {
evaluate(contents, url.toString());
};
doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
std::lock_guard<std::recursive_mutex> lock(_lock);
if (!_includedURLs.contains(url)) {
_includedURLs << url;
// Set the parent url so that path resolution will be relative
// to this script's url during its initial evaluation
_parentURL = url.toString();
auto operation = [&]() {
evaluate(contents, url.toString());
};
doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation);
} else {
qCDebug(scriptengine) << "Script.include() skipping evaluation of previously included url:" << url;
}
}
}
_parentURL = parentURL;

View file

@ -245,6 +245,7 @@ protected:
std::function<bool()> _emitScriptUpdates{ [](){ return true; } };
std::recursive_mutex _lock;
};
#endif // hifi_ScriptEngine_h

View file

@ -29,6 +29,11 @@ void UsersScriptingInterface::kick(const QUuid& nodeID) {
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);
}
void UsersScriptingInterface::mute(const QUuid& nodeID) {
// ask the NodeList to mute the user with the given session ID
DependencyManager::get<NodeList>()->muteNodeBySessionID(nodeID);
}
bool UsersScriptingInterface::getCanKick() {
// ask the NodeList to return our ability to kick
return DependencyManager::get<NodeList>()->getThisNodeCanKick();

View file

@ -28,6 +28,7 @@ public:
public slots:
void ignore(const QUuid& nodeID);
void kick(const QUuid& nodeID);
void mute(const QUuid& nodeID);
bool getCanKick();

View file

@ -226,5 +226,5 @@ QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool
shouldCreateIfMissing);
}
return NULL;
return nullptr;
}

View file

@ -144,6 +144,11 @@ public:
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) { return false; }
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) {return false; }
virtual glm::quat getLocalJointRotation(int index) const {return glm::quat(); }
virtual glm::vec3 getLocalJointTranslation(int index) const {return glm::vec3(); }
virtual bool setLocalJointRotation(int index, const glm::quat& rotation) { return false; }
virtual bool setLocalJointTranslation(int index, const glm::vec3& translation) { return false; }
SpatiallyNestablePointer getThisPointer() const;
void markAncestorMissing(bool value) { _missingAncestor = value; }

View file

@ -23,12 +23,12 @@ namespace hifi {
using Builder = std::function<Pointer()>;
using BuilderMap = std::map<Key, Builder>;
void registerBuilder(const Key& name, Builder builder) {
void registerBuilder(const Key name, Builder builder) {
// FIXME don't allow name collisions
_builders[name] = builder;
}
Pointer create(const Key& name) const {
Pointer create(const Key name) const {
const auto& entryIt = _builders.find(name);
if (entryIt != _builders.end()) {
return (*entryIt).second();
@ -39,7 +39,7 @@ namespace hifi {
template <typename Impl>
class Registrar {
public:
Registrar(const Key& name, SimpleFactory& factory) {
Registrar(const Key name, SimpleFactory& factory) {
factory.registerBuilder(name, [] { return std::make_shared<Impl>(); });
}
};

View file

@ -428,6 +428,25 @@ bool Menu::menuExists(const QString& menuName) {
return false;
}
bool Menu::isMenuEnabled(const QString& menuName) {
QAction* action = getMenuAction(menuName);
// only proceed if the menu actually exists
if (action) {
return action->isEnabled();
}
return false;
}
void Menu::setMenuEnabled(const QString& menuName, bool isEnabled) {
QAction* action = getMenuAction(menuName);
// only proceed if the menu actually exists
if (action) {
action->setEnabled(isEnabled);
}
}
void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) {
MenuWrapper* menuObj = getMenu(menuName);
if (menuObj) {

View file

@ -106,6 +106,9 @@ public slots:
bool isOptionChecked(const QString& menuOption) const;
void setIsOptionChecked(const QString& menuOption, bool isChecked);
bool isMenuEnabled(const QString& menuName);
void setMenuEnabled(const QString& menuName, bool isEnabled);
bool getGroupingIsVisible(const QString& grouping);
void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible

View file

@ -18,7 +18,7 @@
#include "HiFiCodec.h"
const QString HiFiCodec::NAME = "hifiAC";
const char* HiFiCodec::NAME { "hifiAC" };
void HiFiCodec::init() {
}

View file

@ -16,11 +16,11 @@
class HiFiCodec : public CodecPlugin {
Q_OBJECT
public:
// Plugin functions
bool isSupported() const override;
const QString& getName() const override { return NAME; }
const QString getName() const override { return NAME; }
void init() override;
void deinit() override;
@ -36,7 +36,7 @@ public:
virtual void releaseDecoder(Decoder* decoder) override;
private:
static const QString NAME;
static const char* NAME;
};
#endif // hifi_HiFiCodec_h

View file

@ -27,8 +27,8 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
#include <NeuronDataReader.h>
const QString NeuronPlugin::NAME = "Neuron";
const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron";
const char* NeuronPlugin::NAME = "Neuron";
const char* NeuronPlugin::NEURON_ID_STRING = "Perception Neuron";
// indices of joints of the Neuron standard skeleton.
// This is 'almost' the same as the High Fidelity standard skeleton.

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