mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 19:04:32 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into baseball
This commit is contained in:
commit
5154fbc8c5
76 changed files with 1332 additions and 610 deletions
|
@ -63,7 +63,9 @@ AvatarMixer::~AvatarMixer() {
|
|||
_broadcastThread.wait();
|
||||
}
|
||||
|
||||
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
|
||||
// An 80% chance of sending a identity packet within a 5 second interval.
|
||||
// assuming 60 htz update rate.
|
||||
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f;
|
||||
|
||||
// NOTE: some additional optimizations to consider.
|
||||
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
|
||||
|
@ -243,6 +245,46 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
return;
|
||||
}
|
||||
|
||||
// make sure we send out identity and billboard packets to and from new arrivals.
|
||||
bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID());
|
||||
|
||||
// we will also force a send of billboard or identity packet
|
||||
// if either has changed in the last frame
|
||||
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
|
||||
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
|
||||
|
||||
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
|
||||
billboardPacket->write(rfcUUID);
|
||||
billboardPacket->write(billboard);
|
||||
|
||||
nodeList->sendPacket(std::move(billboardPacket), *node);
|
||||
|
||||
++_sumBillboardPackets;
|
||||
}
|
||||
|
||||
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||
|| distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||
|
||||
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
|
||||
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
||||
|
||||
identityPacket->write(individualData);
|
||||
|
||||
nodeList->sendPacket(std::move(identityPacket), *node);
|
||||
|
||||
++_sumIdentityPackets;
|
||||
}
|
||||
|
||||
AvatarData& otherAvatar = otherNodeData->getAvatar();
|
||||
// Decide whether to send this avatar's data based on it's distance from us
|
||||
|
||||
|
@ -254,10 +296,10 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
// potentially update the max full rate distance for this frame
|
||||
maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar);
|
||||
|
||||
if (distanceToAvatar != 0.0f
|
||||
if (distanceToAvatar != 0.0f
|
||||
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID());
|
||||
AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber();
|
||||
|
@ -291,53 +333,11 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
|
||||
numAvatarDataBytes +=
|
||||
avatarPacketList->write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO));
|
||||
avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO));
|
||||
|
||||
avatarPacketList->endSegment();
|
||||
|
||||
// if the receiving avatar has just connected make sure we send out the mesh and billboard
|
||||
// for this avatar (assuming they exist)
|
||||
bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();
|
||||
|
||||
// we will also force a send of billboard or identity packet
|
||||
// if either has changed in the last frame
|
||||
|
||||
if (otherNodeData->getBillboardChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|
||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray rfcUUID = otherNode->getUUID().toRfc4122();
|
||||
QByteArray billboard = otherNodeData->getAvatar().getBillboard();
|
||||
|
||||
auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size());
|
||||
billboardPacket->write(rfcUUID);
|
||||
billboardPacket->write(billboard);
|
||||
|
||||
nodeList->sendPacket(std::move(billboardPacket), *node);
|
||||
|
||||
++_sumBillboardPackets;
|
||||
}
|
||||
|
||||
if (otherNodeData->getIdentityChangeTimestamp() > 0
|
||||
&& (forceSend
|
||||
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|
||||
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
|
||||
|
||||
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
|
||||
|
||||
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
|
||||
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
|
||||
|
||||
identityPacket->write(individualData);
|
||||
|
||||
nodeList->sendPacket(std::move(identityPacket), *node);
|
||||
|
||||
++_sumIdentityPackets;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// close the current packet so that we're always sending something
|
||||
avatarPacketList->closeCurrentPacket(true);
|
||||
|
||||
|
@ -484,7 +484,7 @@ void AvatarMixer::sendStatsPacket() {
|
|||
|
||||
// add the key to ask the domain-server for a username replacement, if it has it
|
||||
avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID());
|
||||
|
||||
|
||||
avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth();
|
||||
avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth();
|
||||
|
||||
|
@ -537,7 +537,7 @@ void AvatarMixer::run() {
|
|||
qDebug() << "Waiting for domain settings from domain-server.";
|
||||
|
||||
// block until we get the settingsRequestComplete signal
|
||||
|
||||
|
||||
QEventLoop loop;
|
||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
||||
|
|
|
@ -16,15 +16,17 @@
|
|||
int AvatarMixerClientData::parseData(NLPacket& packet) {
|
||||
// pull the sequence number from the data first
|
||||
packet.readPrimitive(&_lastReceivedSequenceNumber);
|
||||
|
||||
|
||||
// compute the offset to the data payload
|
||||
return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead()));
|
||||
}
|
||||
|
||||
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
|
||||
bool oldValue = _hasReceivedFirstPackets;
|
||||
_hasReceivedFirstPackets = true;
|
||||
return oldValue;
|
||||
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) {
|
||||
if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) {
|
||||
_hasReceivedFirstPacketsFrom.insert(uuid);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
||||
|
@ -45,9 +47,9 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
|||
jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond();
|
||||
jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond();
|
||||
jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends;
|
||||
|
||||
|
||||
jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps();
|
||||
jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||
|
||||
|
||||
jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
@ -34,25 +35,25 @@ class AvatarMixerClientData : public NodeData {
|
|||
public:
|
||||
int parseData(NLPacket& packet);
|
||||
AvatarData& getAvatar() { return _avatar; }
|
||||
|
||||
bool checkAndSetHasReceivedFirstPackets();
|
||||
|
||||
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
||||
|
||||
uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const;
|
||||
void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber)
|
||||
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
||||
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
||||
|
||||
|
||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||
|
||||
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
|
||||
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
|
||||
|
||||
|
||||
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
|
||||
|
||||
|
||||
void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; }
|
||||
float getFullRateDistance() const { return _fullRateDistance; }
|
||||
|
||||
|
||||
void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; }
|
||||
float getMaxAvatarDistance() const { return _maxAvatarDistance; }
|
||||
|
||||
|
@ -73,31 +74,31 @@ public:
|
|||
void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; }
|
||||
|
||||
void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); }
|
||||
|
||||
|
||||
float getOutboundAvatarDataKbps() const
|
||||
{ return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; }
|
||||
|
||||
|
||||
void loadJSONStats(QJsonObject& jsonObject) const;
|
||||
private:
|
||||
AvatarData _avatar;
|
||||
|
||||
|
||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||
|
||||
bool _hasReceivedFirstPackets = false;
|
||||
quint64 _billboardChangeTimestamp = 0;
|
||||
quint64 _identityChangeTimestamp = 0;
|
||||
|
||||
|
||||
float _fullRateDistance = FLT_MAX;
|
||||
float _maxAvatarDistance = FLT_MAX;
|
||||
|
||||
|
||||
int _numAvatarsSentLastFrame = 0;
|
||||
int _numFramesSinceAdjustment = 0;
|
||||
|
||||
SimpleMovingAverage _otherAvatarStarves;
|
||||
SimpleMovingAverage _otherAvatarSkips;
|
||||
int _numOutOfOrderSends = 0;
|
||||
|
||||
|
||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "OctreeSendThread.h"
|
||||
#include "OctreeServer.h"
|
||||
#include "OctreeServerConsts.h"
|
||||
#include "OctreeLogging.h"
|
||||
|
||||
quint64 startSceneSleepTime = 0;
|
||||
quint64 endSceneSleepTime = 0;
|
||||
|
@ -572,14 +573,12 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
OctreeServer::trackInsideTime((float)elapsedInsideUsecs);
|
||||
}
|
||||
|
||||
|
||||
if (somethingToSend && _myServer->wantsVerboseDebug()) {
|
||||
qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
|
||||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
|
||||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
}
|
||||
|
||||
|
||||
// Here's where we can/should allow the server to send other data...
|
||||
// send the environment packet
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
|
|
|
@ -272,6 +272,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket");
|
||||
packetReceiver.registerMessageListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket");
|
||||
|
||||
// NodeList won't be available to the settings manager when it is created, so call registerListener here
|
||||
packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket");
|
||||
|
@ -1826,3 +1827,24 @@ void DomainServer::processPathQueryPacket(QSharedPointer<NLPacket> packet) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet) {
|
||||
// This packet has been matched to a source node and they're asking not to be in the domain anymore
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
const QUuid& nodeUUID = packet->getSourceID();
|
||||
|
||||
qDebug() << "Received a disconnect request from node with UUID" << nodeUUID;
|
||||
|
||||
if (limitedNodeList->killNodeWithUUID(nodeUUID)) {
|
||||
static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID);
|
||||
|
||||
removedNodePacket->reset();
|
||||
removedNodePacket->write(nodeUUID.toRfc4122());
|
||||
|
||||
// broadcast out the DomainServerRemovedNode message
|
||||
limitedNodeList->eachNode([&limitedNodeList](const SharedNodePointer& otherNode){
|
||||
limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@ public slots:
|
|||
void processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||
void processNodeJSONStatsPacket(QSharedPointer<NLPacketList> packetList, SharedNodePointer sendingNode);
|
||||
void processPathQueryPacket(QSharedPointer<NLPacket> packet);
|
||||
|
||||
void processNodeDisconnectRequestPacket(QSharedPointer<NLPacket> packet);
|
||||
|
||||
private slots:
|
||||
void aboutToQuit();
|
||||
|
||||
|
|
|
@ -33,15 +33,6 @@ var SHOW = 4;
|
|||
var HIDE = 5;
|
||||
var LOAD = 6;
|
||||
|
||||
var COLORS = [];
|
||||
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
||||
COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 };
|
||||
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
||||
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
||||
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
||||
COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 };
|
||||
|
||||
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
|
@ -138,6 +129,7 @@ function setupToolBars() {
|
|||
}
|
||||
|
||||
function sendCommand(id, action) {
|
||||
|
||||
if (action === SHOW) {
|
||||
toolBars[id].selectTool(onOffIcon[id], false);
|
||||
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
|
||||
|
@ -154,24 +146,29 @@ function sendCommand(id, action) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (id === (toolBars.length - 1)) {
|
||||
for (i = 0; i < NUM_AC; i++) {
|
||||
sendCommand(i, action);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (id === (toolBars.length - 1))
|
||||
id = -1;
|
||||
|
||||
var position = { x: controlEntityPosition.x + id * controlEntitySize,
|
||||
y: controlEntityPosition.y, z: controlEntityPosition.z };
|
||||
Entities.addEntity({
|
||||
name: "Actor Controller",
|
||||
userData: clip_url,
|
||||
var controlEntity = Entities.addEntity({
|
||||
name: 'New Actor Controller',
|
||||
type: "Box",
|
||||
position: position,
|
||||
dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize },
|
||||
color: COLORS[action],
|
||||
lifetime: 5
|
||||
});
|
||||
color: { red: 0, green: 0, blue: 0 },
|
||||
position: controlEntityPosition,
|
||||
dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize },
|
||||
visible: false,
|
||||
lifetime: 10,
|
||||
userData: JSON.stringify({
|
||||
idKey: {
|
||||
uD_id: id
|
||||
},
|
||||
actionKey: {
|
||||
uD_action: action
|
||||
},
|
||||
clipKey: {
|
||||
uD_url: clip_url
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
|
@ -191,8 +188,12 @@ function mousePressEvent(event) {
|
|||
sendCommand(i, PLAY_LOOP);
|
||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, STOP);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, LOAD);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||
if (!(input_text === "" || input_text === null)) {
|
||||
clip_url = input_text;
|
||||
sendCommand(i, LOAD);
|
||||
}
|
||||
} else {
|
||||
// Check individual controls
|
||||
for (i = 0; i < NUM_AC; i++) {
|
||||
|
@ -210,7 +211,7 @@ function mousePressEvent(event) {
|
|||
sendCommand(i, STOP);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||
if(!(input_text === "" || input_text === null)){
|
||||
if (!(input_text === "" || input_text === null)) {
|
||||
clip_url = input_text;
|
||||
sendCommand(i, LOAD);
|
||||
}
|
||||
|
|
|
@ -38,18 +38,6 @@ var SHOW = 4;
|
|||
var HIDE = 5;
|
||||
var LOAD = 6;
|
||||
|
||||
var COLORS = [];
|
||||
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
||||
COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 };
|
||||
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
||||
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
||||
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
||||
COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 };
|
||||
|
||||
controlEntityPosition.x += id * controlEntitySize;
|
||||
|
||||
Avatar.loadRecording(clip_url);
|
||||
|
||||
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Avatar.setPlayerUseDisplayName(useDisplayName);
|
||||
Avatar.setPlayerUseAttachments(useAttachments);
|
||||
|
@ -67,27 +55,27 @@ function setupEntityViewer() {
|
|||
EntityViewer.queryOctree();
|
||||
}
|
||||
|
||||
function getAction(controlEntity) {
|
||||
clip_url = controlEntity.userData;
|
||||
function getAction(controlEntity) {
|
||||
if (controlEntity === null) {
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
var userData = JSON.parse(Entities.getEntityProperties(controlEntity, ["userData"]).userData);
|
||||
|
||||
if (controlEntity === null ||
|
||||
controlEntity.position.x !== controlEntityPosition.x ||
|
||||
controlEntity.position.y !== controlEntityPosition.y ||
|
||||
controlEntity.position.z !== controlEntityPosition.z ||
|
||||
controlEntity.dimensions.x !== controlEntitySize) {
|
||||
var uD_id = userData.idKey.uD_id;
|
||||
var uD_action = userData.actionKey.uD_action;
|
||||
var uD_url = userData.clipKey.uD_url;
|
||||
|
||||
Entities.deleteEntity((Entities.getEntityProperties(controlEntity)).id);
|
||||
|
||||
if (uD_id === id || uD_id === -1) {
|
||||
if (uD_action === 6)
|
||||
clip_url = uD_url;
|
||||
|
||||
return uD_action;
|
||||
} else {
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
for (i in COLORS) {
|
||||
if (controlEntity.color.red === COLORS[i].red &&
|
||||
controlEntity.color.green === COLORS[i].green &&
|
||||
controlEntity.color.blue === COLORS[i].blue) {
|
||||
Entities.deleteEntity(controlEntity.id);
|
||||
return parseInt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return DO_NOTHING;
|
||||
}
|
||||
}
|
||||
|
||||
count = 100; // This is necessary to wait for the audio mixer to connect
|
||||
|
@ -100,7 +88,7 @@ function update(event) {
|
|||
|
||||
|
||||
var controlEntity = Entities.findClosestEntity(controlEntityPosition, controlEntitySize);
|
||||
var action = getAction(Entities.getEntityProperties(controlEntity));
|
||||
var action = getAction(controlEntity);
|
||||
|
||||
switch(action) {
|
||||
case PLAY:
|
||||
|
|
|
@ -506,6 +506,7 @@ Grabber.prototype.activateEntity = function(entityID, grabbedProperties) {
|
|||
if (data["refCount"] == 1) {
|
||||
data["gravity"] = grabbedProperties.gravity;
|
||||
data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions;
|
||||
data["collisionsWillMove"] = grabbedProperties.collisionsWillMove;
|
||||
var whileHeldProperties = {gravity: {x:0, y:0, z:0}};
|
||||
if (invertSolidWhileHeld) {
|
||||
whileHeldProperties["ignoreForCollisions"] = ! grabbedProperties.ignoreForCollisions;
|
||||
|
@ -522,7 +523,8 @@ Grabber.prototype.deactivateEntity = function(entityID) {
|
|||
if (data["refCount"] < 1) {
|
||||
Entities.editEntity(entityID, {
|
||||
gravity: data["gravity"],
|
||||
ignoreForCollisions: data["ignoreForCollisions"]
|
||||
ignoreForCollisions: data["ignoreForCollisions"],
|
||||
collisionsWillMove: data["collisionsWillMove"]
|
||||
});
|
||||
data = null;
|
||||
}
|
||||
|
|
137
examples/grabInspector.js
Normal file
137
examples/grabInspector.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// grabInspector.js
|
||||
// examples
|
||||
//
|
||||
// Created by Seth Alves on 2015-9-30.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("libraries/utils.js");
|
||||
|
||||
var INSPECT_RADIUS = 10;
|
||||
var overlays = {};
|
||||
|
||||
var toType = function(obj) {
|
||||
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
|
||||
}
|
||||
|
||||
function grabDataToString(grabData) {
|
||||
var result = "";
|
||||
|
||||
for (var argumentName in grabData) {
|
||||
if (grabData.hasOwnProperty(argumentName)) {
|
||||
if (argumentName == "type") {
|
||||
continue;
|
||||
}
|
||||
var arg = grabData[argumentName];
|
||||
var argType = toType(arg);
|
||||
var argString = arg;
|
||||
if (argType == "object") {
|
||||
if (Object.keys(arg).length == 3) {
|
||||
argString = vec3toStr(arg, 1);
|
||||
}
|
||||
} else if (argType == "number") {
|
||||
argString = arg.toFixed(2);
|
||||
}
|
||||
result += argumentName + ": "
|
||||
// + toType(arg) + " -- "
|
||||
+ argString + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateOverlay(entityID, grabText) {
|
||||
var properties = Entities.getEntityProperties(entityID, ["position", "dimensions"]);
|
||||
var position = Vec3.sum(properties.position, {x:0, y:properties.dimensions.y, z:0});
|
||||
if (entityID in overlays) {
|
||||
var overlay = overlays[entityID];
|
||||
Overlays.editOverlay(overlay, {
|
||||
text: grabText,
|
||||
position: position
|
||||
});
|
||||
} else {
|
||||
var lines = grabText.split(/\r\n|\r|\n/);
|
||||
|
||||
var maxLineLength = lines[0].length;
|
||||
for (var i = 1; i < lines.length; i++) {
|
||||
if (lines[i].length > maxLineLength) {
|
||||
maxLineLength = lines[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
var textWidth = maxLineLength * 0.034; // XXX how to know this?
|
||||
var textHeight = .5;
|
||||
var numberOfLines = lines.length;
|
||||
var textMargin = 0.05;
|
||||
var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines;
|
||||
|
||||
overlays[entityID] = Overlays.addOverlay("text3d", {
|
||||
position: position,
|
||||
dimensions: { x: textWidth, y: textHeight },
|
||||
backgroundColor: { red: 0, green: 0, blue: 0},
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
topMargin: textMargin,
|
||||
leftMargin: textMargin,
|
||||
bottomMargin: textMargin,
|
||||
rightMargin: textMargin,
|
||||
text: grabText,
|
||||
lineHeight: lineHeight,
|
||||
alpha: 0.9,
|
||||
backgroundAlpha: 0.9,
|
||||
ignoreRayIntersection: true,
|
||||
visible: true,
|
||||
isFacingAvatar: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cleanup() {
|
||||
for (var entityID in overlays) {
|
||||
Overlays.deleteOverlay(overlays[entityID]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Script.setInterval(function() {
|
||||
var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS);
|
||||
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
|
||||
var entityID = nearbyEntities[entityIndex];
|
||||
var userData = getEntityUserData(entityID);
|
||||
var grabData = userData["grabKey"]
|
||||
|
||||
// {"grabbableKey":{"invertSolidWhileHeld":true},
|
||||
// "grabKey":{"activated":true,"avatarId":"{6ea8b092-10e0-4058-888b-6facc40d0fe9}","refCount":1,"gravity":{"x":0,"y":0,"z":0},"ignoreForCollisions":0,"collisionsWillMove":1}
|
||||
// }
|
||||
|
||||
if (typeof grabData != 'undefined') {
|
||||
var grabText = grabDataToString(grabData);
|
||||
updateOverlay(entityID, grabText);
|
||||
} else {
|
||||
if (entityID in overlays) {
|
||||
Overlays.deleteOverlay(overlays[entityID]);
|
||||
delete overlays[entityID];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if an entity is too far away, remove its overlay
|
||||
for (var entityID in overlays) {
|
||||
var position = Entities.getEntityProperties(entityID, ["position"]).position;
|
||||
if (Vec3.distance(position, MyAvatar.position) > INSPECT_RADIUS) {
|
||||
Overlays.deleteOverlay(overlays[entityID]);
|
||||
delete overlays[entityID];
|
||||
}
|
||||
}
|
||||
|
||||
}, 100);
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -176,14 +176,13 @@ function formatTime(time) {
|
|||
var SEC_PER_MIN = 60;
|
||||
var MSEC_PER_SEC = 1000;
|
||||
|
||||
var hours = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR));
|
||||
time -= hours * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR);
|
||||
var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR));
|
||||
time -= hours * (SEC_PER_MIN * MIN_PER_HOUR);
|
||||
|
||||
var minutes = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN));
|
||||
time -= minutes * (MSEC_PER_SEC * SEC_PER_MIN);
|
||||
var minutes = Math.floor(time / (SEC_PER_MIN));
|
||||
time -= minutes * (SEC_PER_MIN);
|
||||
|
||||
var seconds = Math.floor(time / MSEC_PER_SEC);
|
||||
seconds = time / MSEC_PER_SEC;
|
||||
var seconds = time;
|
||||
|
||||
var text = "";
|
||||
text += (hours > 0) ? hours + ":" :
|
||||
|
|
|
@ -62,10 +62,24 @@ var overlaysCounter = new CounterWidget(panel, "Overlays",
|
|||
);
|
||||
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value); },
|
||||
function() { return Scene.doEngineDisplayItemStatus(); },
|
||||
function(value) { return (value); }
|
||||
// see libraries/render/src/render/Engine.h
|
||||
var showDisplayStatusFlag = 1;
|
||||
var showNetworkStatusFlag = 2;
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value ?
|
||||
Scene.doEngineDisplayItemStatus() | showDisplayStatusFlag :
|
||||
Scene.doEngineDisplayItemStatus() & ~showDisplayStatusFlag); },
|
||||
function() { return (Scene.doEngineDisplayItemStatus() & showDisplayStatusFlag) > 0; },
|
||||
function(value) { return (value & showDisplayStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Network/Physics status",
|
||||
function(value) { Scene.setEngineDisplayItemStatus(value ?
|
||||
Scene.doEngineDisplayItemStatus() | showNetworkStatusFlag :
|
||||
Scene.doEngineDisplayItemStatus() & ~showNetworkStatusFlag); },
|
||||
function() { return (Scene.doEngineDisplayItemStatus() & showNetworkStatusFlag) > 0; },
|
||||
function(value) { return (value & showNetworkStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
|
|
@ -55,8 +55,7 @@ bool IceServer::packetVersionMatch(const udt::Packet& packet) {
|
|||
if (headerVersion == versionForPacketType(headerType)) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Packet version mismatch for packet" << headerType
|
||||
<< "(" << nameForPacketType(headerType) << ") from" << packet.getSenderSockAddr();
|
||||
qDebug() << "Packet version mismatch for packet" << headerType << " from" << packet.getSenderSockAddr();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
31
interface/resources/icons/statusIconAtlas.svg
Normal file
31
interface/resources/icons/statusIconAtlas.svg
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" width="1600px" height="100px" viewBox="0 0 1600 100" xml:space="preserve">
|
||||
<path fill="#FFFFFF" d="M92.8,69.3c-0.8-1.5-1.9-2.7-3.1-3.8c4.4-11.4,3.7-24.6-2.6-35.8c-9.2-16.3-28.3-24.4-46.3-20l-3.1,0.7
|
||||
l4.9,8.7l1.7-0.3c13.7-2.7,27.6,3.6,34.5,15.7c4.9,8.7,5.5,18.9,1.9,27.9c-2.1,0.1-4.3,0.6-6.2,1.8c-6.5,3.7-8.8,12-5.1,18.5
|
||||
c3.7,6.5,12,8.8,18.5,5.1C94.2,84.1,96.5,75.8,92.8,69.3z"/>
|
||||
<path fill="#FFFFFF" d="M54.2,82.6l-1.5,0.1c-12.3,0.8-24.2-5.6-30.2-16.3c-3.8-6.6-4.9-14.2-3.7-21.3c2.8,0.4,5.9-0.1,8.6-1.6
|
||||
c6.5-3.7,8.8-12,5.1-18.5s-12-8.8-18.5-5.1C7.7,23.7,5.4,32,9,38.5c0.3,0.6,0.7,1.2,1.2,1.7c-2.6,10.3-1.4,21.5,4.1,31.1
|
||||
c7.5,13.2,21.5,21.2,36.5,21.2c1.8,0,3.5-0.1,5.2-0.3l3.6-0.4L54.2,82.6z"/>
|
||||
<path fill="#FFFFFF" d="M67.2,63.4H33.8c-1,0-2.1-0.5-2.6-1.5c-0.5-0.9-0.5-2.1,0-3L47.8,30c0.5-0.9,1.6-1.5,2.6-1.5
|
||||
s2.1,0.5,2.6,1.5l16.7,28.9c0.5,0.9,0.5,2.1,0,3C69.3,62.9,68.3,63.4,67.2,63.4z M39,57.4h23L50.4,37.5L39,57.4z"/>
|
||||
<polygon fill="#FFFFFF" points="175.4,30.6 149.9,8 123.9,30.7 139,30.7 139.2,59.6 161,59.3 160.8,30.7 "/>
|
||||
<polygon fill="#FFFFFF" points="225.6,39.8 251.1,62.5 277.1,39.8 261.9,39.8 261.7,8.9 240,9.2 240.2,39.8 "/>
|
||||
<path fill="#FFFFFF" d="M174.3,42.8c1.8,3.7,2.8,7.8,2.8,12.1c0,15.2-12.3,27.5-27.5,27.5c-15.2,0-27.5-12.3-27.5-27.5
|
||||
c0-4.4,1-8.5,2.9-12.1h-7.9c-1.4,3.8-2.2,7.8-2.2,12.1c0,19.2,15.6,34.7,34.7,34.7c19.2,0,34.7-15.6,34.7-34.7
|
||||
c0-4.3-0.8-8.3-2.2-12.1H174.3z"/>
|
||||
<path fill="#FFFFFF" d="M278.8,53c0.1,0.7,0.1,1.5,0.1,2.2c0,15.2-12.4,27.6-27.6,27.6c-15.2,0-27.6-12.4-27.6-27.6
|
||||
c0-1.1,0.1-2.1,0.2-3.1c-2.1-2.1-4.1-4.1-6.2-6.2c-0.8,3-1.3,6.1-1.3,9.3c0,19.2,15.6,34.9,34.9,34.9s34.9-15.6,34.9-34.9
|
||||
c0-2.9-0.4-5.8-1.1-8.5L278.8,53z"/>
|
||||
<circle fill="none" stroke="#000000" stroke-width="7" stroke-miterlimit="10" stroke-dasharray="7.7202,7.7202" cx="-174" cy="-5.8" r="14.7"/>
|
||||
<path d="M-174-10.6c2.6,0,4.7,2.1,4.7,4.7s-2.1,4.7-4.7,4.7s-4.7-2.1-4.7-4.7S-176.6-10.6-174-10.6 M-174-17.6
|
||||
c-6.5,0-11.7,5.3-11.7,11.7s5.3,11.7,11.7,11.7s11.7-5.3,11.7-11.7S-167.5-17.6-174-17.6L-174-17.6z"/>
|
||||
<path fill="#FFFFFF" d="M353.3,91.2c-0.3,0-0.7,0-1,0c-1.8-0.2-3.5-0.4-5.3-0.7c-21.3-3.6-35.2-22.8-32-44.2
|
||||
c2.7-18.2,17.7-31.4,36.8-32.5c17.2-0.9,33.8,11.4,38.2,28.5c0.8,3.1,1.1,6.3,1.6,9.5c0,0.3,0,0.7,0,1c-0.2,0.9-0.4,1.8-0.5,2.7
|
||||
c-1.3,16.3-12.9,30.1-28.8,34C359.3,90.4,356.3,90.7,353.3,91.2z M353.7,83.9c8.3,0,16.1-3.4,22.6-9.9c2.2-2.2,2-3.1-0.7-4.5
|
||||
c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1
|
||||
c-5.9,0.1-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2
|
||||
c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"/>
|
||||
<polygon fill="#FFFFFF" points="445.3,14.1 484.6,14.1 461.5,38.2 485.6,41.4 422.2,86.9 441.2,53.4 425.6,49.3 "/>
|
||||
</svg>
|
After Width: | Height: | Size: 3 KiB |
|
@ -804,8 +804,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
void Application::aboutToQuit() {
|
||||
emit beforeAboutToQuit();
|
||||
|
||||
getActiveDisplayPlugin()->deactivate();
|
||||
|
||||
_aboutToQuit = true;
|
||||
|
||||
cleanupBeforeQuit();
|
||||
}
|
||||
|
||||
|
@ -831,8 +834,14 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
_entities.clear(); // this will allow entity scripts to properly shutdown
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// send the domain a disconnect packet, force stoppage of domain-server check-ins
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
nodeList->setIsShuttingDown(true);
|
||||
|
||||
// tell the packet receiver we're shutting down, so it can drop packets
|
||||
DependencyManager::get<NodeList>()->getPacketReceiver().setShouldDropPackets(true);
|
||||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||
|
||||
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
|
||||
|
@ -852,9 +861,6 @@ void Application::cleanupBeforeQuit() {
|
|||
saveSettings();
|
||||
_window->saveGeometry();
|
||||
|
||||
// let the avatar mixer know we're out
|
||||
MyAvatar::sendKillAvatar();
|
||||
|
||||
// stop the AudioClient
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
|
||||
"stop", Qt::BlockingQueuedConnection);
|
||||
|
@ -3499,10 +3505,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
|
||||
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
|
||||
renderDebugFlags =
|
||||
(RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
|
||||
}
|
||||
renderArgs->_debugFlags = renderDebugFlags;
|
||||
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);
|
||||
}
|
||||
|
@ -3562,6 +3564,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems();
|
||||
|
||||
renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus();
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
|
||||
renderContext._drawItemStatus |= render::showNetworkStatusFlag;
|
||||
}
|
||||
renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect();
|
||||
|
||||
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||
|
|
|
@ -271,6 +271,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
|
|
@ -608,7 +608,7 @@ float MyAvatar::recorderElapsed() {
|
|||
if (!_recorder) {
|
||||
return 0;
|
||||
}
|
||||
return (float)_recorder->position() / MSECS_PER_SECOND;
|
||||
return (float)_recorder->position() / (float) MSECS_PER_SECOND;
|
||||
}
|
||||
|
||||
QMetaObject::Connection _audioClientRecorderConnection;
|
||||
|
@ -1024,11 +1024,6 @@ int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
return buffer.size();
|
||||
}
|
||||
|
||||
void MyAvatar::sendKillAvatar() {
|
||||
auto killPacket = NLPacket::create(PacketType::KillAvatar, 0);
|
||||
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer);
|
||||
}
|
||||
|
||||
void MyAvatar::updateLookAtTargetAvatar() {
|
||||
//
|
||||
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
|
||||
|
|
|
@ -162,8 +162,6 @@ public:
|
|||
|
||||
eyeContactTarget getEyeContactTarget();
|
||||
|
||||
static void sendKillAvatar();
|
||||
|
||||
Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; }
|
||||
Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); }
|
||||
Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); }
|
||||
|
|
|
@ -804,12 +804,12 @@ float AvatarData::playerElapsed() {
|
|||
return 0;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qint64 result;
|
||||
float result;
|
||||
QMetaObject::invokeMethod(this, "playerElapsed", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(qint64, result));
|
||||
Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
return (float)_player->position() / MSECS_PER_SECOND;
|
||||
return (float)_player->position() / (float) MSECS_PER_SECOND;
|
||||
}
|
||||
|
||||
float AvatarData::playerLength() {
|
||||
|
@ -817,12 +817,12 @@ float AvatarData::playerLength() {
|
|||
return 0;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qint64 result;
|
||||
float result;
|
||||
QMetaObject::invokeMethod(this, "playerLength", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(qint64, result));
|
||||
Q_RETURN_ARG(float, result));
|
||||
return result;
|
||||
}
|
||||
return _player->length() / MSECS_PER_SECOND;
|
||||
return (float)_player->length() / (float) MSECS_PER_SECOND;
|
||||
}
|
||||
|
||||
void AvatarData::loadRecording(const QString& filename) {
|
||||
|
@ -1513,7 +1513,8 @@ void AvatarData::setRecordingBasis(std::shared_ptr<Transform> recordingBasis) {
|
|||
recordingBasis = std::make_shared<Transform>();
|
||||
recordingBasis->setRotation(getOrientation());
|
||||
recordingBasis->setTranslation(getPosition());
|
||||
recordingBasis->setScale(getTargetScale());
|
||||
// TODO: find a different way to record/playback the Scale of the avatar
|
||||
//recordingBasis->setScale(getTargetScale());
|
||||
}
|
||||
_recordingBasis = recordingBasis;
|
||||
}
|
||||
|
@ -1532,7 +1533,7 @@ Transform AvatarData::getTransform() const {
|
|||
|
||||
static const QString JSON_AVATAR_BASIS = QStringLiteral("basisTransform");
|
||||
static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform");
|
||||
static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations");
|
||||
static const QString JSON_AVATAR_JOINT_ARRAY = QStringLiteral("jointArray");
|
||||
static const QString JSON_AVATAR_HEAD = QStringLiteral("head");
|
||||
static const QString JSON_AVATAR_HEAD_ROTATION = QStringLiteral("rotation");
|
||||
static const QString JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS = QStringLiteral("blendShapes");
|
||||
|
@ -1544,6 +1545,24 @@ static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel");
|
|||
static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName");
|
||||
static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments");
|
||||
|
||||
QJsonValue toJsonValue(const JointData& joint) {
|
||||
QJsonArray result;
|
||||
result.push_back(toJsonValue(joint.rotation));
|
||||
result.push_back(toJsonValue(joint.translation));
|
||||
return result;
|
||||
}
|
||||
|
||||
JointData jointDataFromJsonValue(const QJsonValue& json) {
|
||||
JointData result;
|
||||
if (json.isArray()) {
|
||||
QJsonArray array = json.toArray();
|
||||
result.rotation = quatFromJsonValue(array[0]);
|
||||
result.rotationSet = true;
|
||||
result.translation = vec3FromJsonValue(array[1]);
|
||||
result.translationSet = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Every frame will store both a basis for the recording and a relative transform
|
||||
// This allows the application to decide whether playback should be relative to an avatar's
|
||||
|
@ -1575,13 +1594,16 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) {
|
|||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform);
|
||||
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
|
||||
}
|
||||
} else {
|
||||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(_avatar->getTransform());
|
||||
}
|
||||
|
||||
QJsonArray jointRotations;
|
||||
for (const auto& jointRotation : _avatar->getJointRotations()) {
|
||||
jointRotations.push_back(toJsonValue(jointRotation));
|
||||
// Skeleton pose
|
||||
QJsonArray jointArray;
|
||||
for (const auto& joint : _avatar->getRawJointData()) {
|
||||
jointArray.push_back(toJsonValue(joint));
|
||||
}
|
||||
root[JSON_AVATAR_JOINT_ROTATIONS] = jointRotations;
|
||||
root[JSON_AVATAR_JOINT_ARRAY] = jointArray;
|
||||
|
||||
const HeadData* head = _avatar->getHeadData();
|
||||
if (head) {
|
||||
|
@ -1643,24 +1665,34 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) {
|
|||
auto worldTransform = currentBasis->worldTransform(relativeTransform);
|
||||
_avatar->setPosition(worldTransform.getTranslation());
|
||||
_avatar->setOrientation(worldTransform.getRotation());
|
||||
_avatar->setTargetScale(worldTransform.getScale().x);
|
||||
|
||||
// TODO: find a way to record/playback the Scale of the avatar
|
||||
//_avatar->setTargetScale(worldTransform.getScale().x);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
if (root.contains(JSON_AVATAR_ATTACHEMENTS)) {
|
||||
// FIXME de-serialize attachment data
|
||||
}
|
||||
|
||||
// Joint rotations are relative to the avatar, so they require no basis correction
|
||||
if (root.contains(JSON_AVATAR_JOINT_ROTATIONS)) {
|
||||
QVector<quat> jointRotations;
|
||||
QJsonArray jointRotationsJson = root[JSON_AVATAR_JOINT_ROTATIONS].toArray();
|
||||
jointRotations.reserve(jointRotationsJson.size());
|
||||
for (const auto& jointRotationJson : jointRotationsJson) {
|
||||
jointRotations.push_back(quatFromJsonValue(jointRotationJson));
|
||||
if (root.contains(JSON_AVATAR_JOINT_ARRAY)) {
|
||||
QVector<JointData> jointArray;
|
||||
QJsonArray jointArrayJson = root[JSON_AVATAR_JOINT_ARRAY].toArray();
|
||||
jointArray.reserve(jointArrayJson.size());
|
||||
for (const auto& jointJson : jointArrayJson) {
|
||||
jointArray.push_back(jointDataFromJsonValue(jointJson));
|
||||
}
|
||||
|
||||
QVector<glm::quat> jointRotations;
|
||||
jointRotations.reserve(jointArray.size());
|
||||
for (const auto& joint : jointArray) {
|
||||
jointRotations.push_back(joint.rotation);
|
||||
}
|
||||
_avatar->setJointRotations(jointRotations);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Most head data is relative to the avatar, and needs no basis correction,
|
||||
// but the lookat vector does need correction
|
||||
HeadData* head = _avatar->_headData;
|
||||
|
|
|
@ -457,6 +457,9 @@ public:
|
|||
bool translationSet = false;
|
||||
};
|
||||
|
||||
QJsonValue toJsonValue(const JointData& joint);
|
||||
JointData jointDataFromJsonValue(const QJsonValue& q);
|
||||
|
||||
class AttachmentData {
|
||||
public:
|
||||
QUrl modelURL;
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <ObjectMotionState.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
#include "../render-utils/simple_vert.h"
|
||||
#include "../render-utils/simple_frag.h"
|
||||
|
||||
|
@ -63,6 +62,4 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
|
|||
} else {
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
|
||||
}
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
//
|
||||
// RenderableDebugableEntityItem.cpp
|
||||
// libraries/entities-renderer/src/
|
||||
//
|
||||
// Created by Seth Alves on 5/1/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <gpu/Batch.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <ObjectMotionState.h>
|
||||
|
||||
|
||||
void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args,
|
||||
float puffedOut, glm::vec4& color) {
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
auto shapeTransform = entity->getTransformToCenter();
|
||||
if (puffedOut != 0.0f) {
|
||||
shapeTransform.postScale(1.0f + puffedOut);
|
||||
}
|
||||
batch.setModelTransform(Transform()); // we want to include the scale as well
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderWireCubeInstance(batch, shapeTransform, color);
|
||||
}
|
||||
|
||||
void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) {
|
||||
if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) {
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
|
||||
batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
||||
bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID);
|
||||
if (highlightSimulationOwnership) {
|
||||
glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f);
|
||||
renderBoundingBox(entity, args, 0.08f, greenColor);
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) {
|
||||
glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
renderBoundingBox(entity, args, 0.16f, redColor);
|
||||
}
|
||||
|
||||
if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) {
|
||||
glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f);
|
||||
renderBoundingBox(entity, args, 0.24f, yellowColor);
|
||||
}
|
||||
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState && motionState->isActive()) {
|
||||
glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
renderBoundingBox(entity, args, 0.32f, blueColor);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
//
|
||||
// RenderableDebugableEntityItem.h
|
||||
// libraries/entities-renderer/src/
|
||||
//
|
||||
// Created by Seth Alves on 5/1/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RenderableDebugableEntityItem_h
|
||||
#define hifi_RenderableDebugableEntityItem_h
|
||||
|
||||
#include <EntityItem.h>
|
||||
|
||||
class RenderableDebugableEntityItem {
|
||||
public:
|
||||
static void renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color);
|
||||
static void render(EntityItem* entity, RenderArgs* args);
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableDebugableEntityItem_h
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
|
||||
#include <ObjectMotionState.h>
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
namespace render {
|
||||
|
@ -40,5 +41,60 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) {
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote();
|
||||
const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND);
|
||||
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
|
||||
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
|
||||
// Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green)
|
||||
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ?
|
||||
render::Item::Status::Value::GREEN :
|
||||
render::Item::Status::Value::RED),
|
||||
(unsigned char) RenderItemStatusIcon::PACKET_RECEIVED);
|
||||
});
|
||||
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
quint64 delta = usecTimestampNow() - entity->getLastBroadcast();
|
||||
const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND);
|
||||
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
|
||||
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
|
||||
// Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green)
|
||||
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ?
|
||||
render::Item::Status::Value::MAGENTA :
|
||||
render::Item::Status::Value::CYAN),
|
||||
(unsigned char)RenderItemStatusIcon::PACKET_SENT);
|
||||
});
|
||||
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(entity->getPhysicsInfo());
|
||||
if (motionState && motionState->isActive()) {
|
||||
return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE,
|
||||
(unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET);
|
||||
}
|
||||
return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE,
|
||||
(unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET);
|
||||
});
|
||||
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
||||
bool weOwnSimulation = entity->getSimulationOwner().matchesValidID(myNodeID);
|
||||
|
||||
if (weOwnSimulation) {
|
||||
return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE,
|
||||
(unsigned char)RenderItemStatusIcon::SIMULATION_OWNER);
|
||||
}
|
||||
return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE,
|
||||
(unsigned char)RenderItemStatusIcon::SIMULATION_OWNER);
|
||||
});
|
||||
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
if (entity->hasActions()) {
|
||||
return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN,
|
||||
(unsigned char)RenderItemStatusIcon::HAS_ACTIONS);
|
||||
}
|
||||
return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN,
|
||||
(unsigned char)RenderItemStatusIcon::HAS_ACTIONS);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,13 +15,26 @@
|
|||
#include <render/Scene.h>
|
||||
#include <EntityItem.h>
|
||||
|
||||
// These or the icon "name" used by the render item status value, they correspond to the atlas texture used by the DrawItemStatus
|
||||
// job in the current rendering pipeline defined as of now (11/2015) in render-utils/RenderDeferredTask.cpp.
|
||||
enum class RenderItemStatusIcon {
|
||||
ACTIVE_IN_BULLET = 0,
|
||||
PACKET_SENT = 1,
|
||||
PACKET_RECEIVED = 2,
|
||||
SIMULATION_OWNER = 3,
|
||||
HAS_ACTIONS = 4,
|
||||
NONE = 255
|
||||
};
|
||||
|
||||
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters);
|
||||
|
||||
|
||||
class RenderableEntityItemProxy {
|
||||
public:
|
||||
RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { }
|
||||
typedef render::Payload<RenderableEntityItemProxy> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
|
||||
EntityItemPointer entity;
|
||||
};
|
||||
|
||||
|
@ -36,19 +49,23 @@ class SimpleRenderableEntityItem {
|
|||
public:
|
||||
bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||
_myItem = scene->allocateID();
|
||||
|
||||
|
||||
auto renderData = std::make_shared<RenderableEntityItemProxy>(self);
|
||||
auto renderPayload = std::make_shared<RenderableEntityItemProxy::Payload>(renderData);
|
||||
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(self, statusGetters);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
|
||||
pendingChanges.resetItem(_myItem, renderPayload);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||
pendingChanges.removeItem(_myItem);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
render::ItemID _myItem;
|
||||
};
|
||||
|
@ -62,5 +79,4 @@ private: \
|
|||
SimpleRenderableEntityItem _renderHelper;
|
||||
|
||||
|
||||
|
||||
#endif // hifi_RenderableEntityItem_h
|
||||
|
|
|
@ -54,6 +54,4 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
|
|||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
|
||||
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID);
|
||||
}
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_RenderableLineEntityItem_h
|
||||
|
||||
#include <LineEntityItem.h>
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
#include <GeometryCache.h>
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return std::make_shared<RenderableModelEntityItem>(entityID, properties);
|
||||
|
@ -179,25 +181,6 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item::Status::Getters& statusGetters) {
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote();
|
||||
const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND);
|
||||
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
|
||||
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
|
||||
// Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green)
|
||||
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : render::Item::Status::Value::RED));
|
||||
});
|
||||
statusGetters.push_back([entity] () -> render::Item::Status::Value {
|
||||
quint64 delta = usecTimestampNow() - entity->getLastBroadcast();
|
||||
const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND);
|
||||
float normalizedDelta = delta * WAIT_THRESHOLD_INV;
|
||||
// Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD
|
||||
// Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green)
|
||||
return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : render::Item::Status::Value::CYAN));
|
||||
});
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
_myMetaItem = scene->allocateID();
|
||||
|
@ -209,11 +192,11 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
|
|||
|
||||
if (_model) {
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(this, statusGetters);
|
||||
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
||||
|
||||
// note: we don't care if the model fails to add items, we always added our meta item and therefore we return
|
||||
// true so that the system knows our meta item is in the scene!
|
||||
_model->addToScene(scene, pendingChanges, statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -245,14 +228,16 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
|
||||
// check to see if when we added our models to the scene they were ready, if they were not ready, then
|
||||
// fix them up in the scene
|
||||
if (_model->needsFixupInScene()) {
|
||||
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0;
|
||||
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
|
||||
_showCollisionHull = shouldShowCollisionHull;
|
||||
render::PendingChanges pendingChanges;
|
||||
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(this, statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters);
|
||||
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
@ -274,7 +259,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
|
||||
getModel(renderer);
|
||||
}
|
||||
|
||||
|
||||
if (_model) {
|
||||
// handle animations..
|
||||
if (hasAnimation()) {
|
||||
|
@ -320,11 +305,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
|
||||
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
auto shapeTransform = getTransformToCenter();
|
||||
batch.setModelTransform(Transform()); // we want to include the scale as well
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderWireCubeInstance(batch, shapeTransform, greenColor);
|
||||
}
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
}
|
||||
|
||||
Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <QStringList>
|
||||
|
||||
#include <ModelEntityItem.h>
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
|
||||
class Model;
|
||||
class EntityTreeRenderer;
|
||||
|
@ -85,6 +84,8 @@ private:
|
|||
bool _dimensionsInitialized = true;
|
||||
|
||||
render::ItemID _myMetaItem;
|
||||
|
||||
bool _showCollisionHull = false;
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableModelEntityItem_h
|
||||
|
|
|
@ -139,6 +139,9 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
|
|||
_renderItemId = scene->allocateID();
|
||||
auto renderData = ParticlePayload::Pointer(particlePayload);
|
||||
auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData));
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
pendingChanges.resetItem(_renderItemId, renderPayload);
|
||||
_scene = scene;
|
||||
return true;
|
||||
|
|
|
@ -153,6 +153,4 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
|
|||
batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);
|
||||
|
||||
batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0);
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <gpu/Batch.h>
|
||||
#include <PolyLineEntityItem.h>
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
#include <GeometryCache.h>
|
||||
#include <QReadWriteLock>
|
||||
|
|
|
@ -538,8 +538,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z);
|
||||
|
||||
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
}
|
||||
|
||||
bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
|
||||
|
@ -551,6 +549,10 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
|
|||
auto renderData = PolyVoxPayload::Pointer(renderItem);
|
||||
auto renderPayload = std::make_shared<PolyVoxPayload::Payload>(renderData);
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
|
||||
pendingChanges.resetItem(_myItem, renderPayload);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <TextureCache.h>
|
||||
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
#include "gpu/Context.h"
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
#include "../render-utils/simple_vert.h"
|
||||
#include "../render-utils/simple_frag.h"
|
||||
|
||||
|
@ -70,7 +69,4 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
|
|||
batch.setModelTransform(Transform());
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch, modelTransform, sphereColor);
|
||||
}
|
||||
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
|
||||
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
||||
static const float SPHERE_ENTITY_SCALE = 0.5f;
|
||||
|
@ -112,7 +114,9 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
|
|||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
_model->addToScene(scene, pendingChanges);
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, false);
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
|
@ -203,7 +207,11 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt
|
|||
|
||||
auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
|
||||
auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);
|
||||
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(shared_from_this(), statusGetters);
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
|
||||
pendingChanges.resetItem(_myMetaItem, renderPayload);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -304,3 +304,24 @@ QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType)
|
|||
entityActionType = (EntityActionType)actionTypeAsInt;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QString serializedActionsToDebugString(QByteArray data) {
|
||||
if (data.size() == 0) {
|
||||
return QString();
|
||||
}
|
||||
QVector<QByteArray> serializedActions;
|
||||
QDataStream serializedActionsStream(data);
|
||||
serializedActionsStream >> serializedActions;
|
||||
|
||||
QString result;
|
||||
foreach(QByteArray serializedAction, serializedActions) {
|
||||
QDataStream serializedActionStream(serializedAction);
|
||||
EntityActionType actionType;
|
||||
QUuid actionID;
|
||||
serializedActionStream >> actionType;
|
||||
serializedActionStream >> actionID;
|
||||
result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " ";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -89,4 +89,6 @@ typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
|||
QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType);
|
||||
QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType);
|
||||
|
||||
QString serializedActionsToDebugString(QByteArray data);
|
||||
|
||||
#endif // hifi_EntityActionInterface_h
|
||||
|
|
|
@ -630,6 +630,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
dataAt += bytes;
|
||||
bytesRead += bytes;
|
||||
|
||||
if (wantTerseEditLogging() && _simulationOwner != newSimOwner) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner;
|
||||
}
|
||||
if (_simulationOwner.set(newSimOwner)) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
|
@ -704,17 +707,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref);
|
||||
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
|
||||
|
||||
{ // When we own the simulation we don't accept updates to the entity's actions
|
||||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
||||
// NOTE: this prevents userB from adding an action to an object1 when UserA
|
||||
// has simulation ownership of it.
|
||||
// TODO: figure out how to allow multiple users to update actions simultaneously
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
overwriteLocalData = oldOverwrite;
|
||||
}
|
||||
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, somethingChanged);
|
||||
|
@ -737,7 +730,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// this "new" data is actually slightly out of date. We calculate the time we need to skip forward and
|
||||
// use our simulation helper routine to get a best estimate of where the entity should be.
|
||||
float skipTimeForward = (float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND);
|
||||
|
||||
|
||||
// we want to extrapolate the motion forward to compensate for packet travel time, but
|
||||
// we don't want the side effect of flag setting.
|
||||
simulateKinematicMotion(skipTimeForward, false);
|
||||
|
@ -745,7 +738,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
|
||||
if (overwriteLocalData) {
|
||||
if (!_simulationOwner.matchesValidID(myNodeID)) {
|
||||
|
||||
_lastSimulated = now;
|
||||
}
|
||||
}
|
||||
|
@ -996,6 +988,11 @@ EntityTreePointer EntityItem::getTree() const {
|
|||
return tree;
|
||||
}
|
||||
|
||||
bool EntityItem::wantTerseEditLogging() {
|
||||
EntityTreePointer tree = getTree();
|
||||
return tree ? tree->wantTerseEditLogging() : false;
|
||||
}
|
||||
|
||||
glm::mat4 EntityItem::getEntityToWorldMatrix() const {
|
||||
glm::mat4 translation = glm::translate(getPosition());
|
||||
glm::mat4 rotation = glm::mat4_cast(getRotation());
|
||||
|
@ -1492,20 +1489,35 @@ void EntityItem::updateCreated(uint64_t value) {
|
|||
}
|
||||
|
||||
void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) {
|
||||
if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority;
|
||||
}
|
||||
_simulationOwner.set(id, priority);
|
||||
}
|
||||
|
||||
void EntityItem::setSimulationOwner(const SimulationOwner& owner) {
|
||||
if (wantTerseEditLogging() && _simulationOwner != owner) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner;
|
||||
}
|
||||
|
||||
_simulationOwner.set(owner);
|
||||
}
|
||||
|
||||
void EntityItem::updateSimulatorID(const QUuid& value) {
|
||||
if (wantTerseEditLogging() && _simulationOwner.getID() != value) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value;
|
||||
}
|
||||
|
||||
if (_simulationOwner.setID(value)) {
|
||||
_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::clearSimulationOwnership() {
|
||||
if (wantTerseEditLogging() && !_simulationOwner.isNull()) {
|
||||
qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null";
|
||||
}
|
||||
|
||||
_simulationOwner.clear();
|
||||
// don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership()
|
||||
// is only ever called entity-server-side and the flags are only used client-side
|
||||
|
@ -1607,6 +1619,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
|
|||
bool success = true;
|
||||
serializeActions(success, _allActionsDataCache);
|
||||
_dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
setActionDataNeedsTransmit(true);
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
|
@ -1649,7 +1662,7 @@ void EntityItem::deserializeActionsInternal() {
|
|||
return;
|
||||
}
|
||||
|
||||
EntityTreePointer entityTree = _element ? _element->getTree() : nullptr;
|
||||
EntityTreePointer entityTree = getTree();
|
||||
assert(entityTree);
|
||||
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
assert(simulation);
|
||||
|
@ -1770,6 +1783,7 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const {
|
|||
serializedActionsStream << serializedActions;
|
||||
|
||||
if (result.size() >= _maxActionsDataSize) {
|
||||
qDebug() << "EntityItem::serializeActions size is too large -- " << result.size() << ">=" << _maxActionsDataSize;
|
||||
success = false;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -307,6 +307,7 @@ public:
|
|||
|
||||
QString getName() const { return _name; }
|
||||
void setName(const QString& value) { _name = value; }
|
||||
QString getDebugName() { return _name != "" ? _name : getID().toString(); }
|
||||
|
||||
bool getVisible() const { return _visible; }
|
||||
void setVisible(bool value) { _visible = value; }
|
||||
|
@ -381,6 +382,7 @@ public:
|
|||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||
EntityTreeElementPointer getElement() const { return _element; }
|
||||
EntityTreePointer getTree() const;
|
||||
bool wantTerseEditLogging();
|
||||
|
||||
static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; }
|
||||
static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; }
|
||||
|
@ -406,7 +408,13 @@ public:
|
|||
QList<QUuid> getActionIDs() { return _objectActions.keys(); }
|
||||
QVariantMap getActionArguments(const QUuid& actionID) const;
|
||||
void deserializeActions();
|
||||
|
||||
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
|
||||
bool actionDataDirty() const { return _actionDataDirty; }
|
||||
|
||||
void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; }
|
||||
bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; }
|
||||
|
||||
bool shouldSuppressLocationEdits() const;
|
||||
|
||||
void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; }
|
||||
|
@ -439,7 +447,7 @@ protected:
|
|||
mutable bool _recalcAABox = true;
|
||||
mutable bool _recalcMinAACube = true;
|
||||
mutable bool _recalcMaxAACube = true;
|
||||
|
||||
|
||||
float _glowLevel;
|
||||
float _localRenderAlpha;
|
||||
float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3
|
||||
|
@ -510,6 +518,7 @@ protected:
|
|||
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
|
||||
mutable QSet<QUuid> _actionsToRemove;
|
||||
mutable bool _actionDataDirty = false;
|
||||
mutable bool _actionDataNeedsTransmit = false;
|
||||
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
|
||||
static quint64 _rememberDeletedActionTime;
|
||||
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
|
||||
|
|
|
@ -611,7 +611,8 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
const QVariantMap& arguments) {
|
||||
QUuid actionID = QUuid::createUuid();
|
||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||
bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
bool success = false;
|
||||
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
// create this action even if the entity doesn't have physics info. it will often be the
|
||||
// case that a script adds an action immediately after an object is created, and the physicsInfo
|
||||
// is computed asynchronously.
|
||||
|
@ -623,16 +624,16 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
return false;
|
||||
}
|
||||
EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
|
||||
if (action) {
|
||||
entity->addAction(simulation, action);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
if (entity->getSimulatorID() != myNodeID) {
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
return true;
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
success = entity->addAction(simulation, action);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
if (entity->getSimulatorID() != myNodeID) {
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
if (success) {
|
||||
return actionID;
|
||||
|
@ -656,9 +657,12 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
|||
}
|
||||
|
||||
bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) {
|
||||
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
return entity->removeAction(simulation, actionID);
|
||||
bool success = false;
|
||||
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
success = entity->removeAction(simulation, actionID);
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
return success;
|
||||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {
|
||||
|
|
|
@ -198,6 +198,10 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
|
|||
properties.setVelocityChanged(false);
|
||||
properties.setAngularVelocityChanged(false);
|
||||
properties.setAccelerationChanged(false);
|
||||
|
||||
if (wantTerseEditLogging()) {
|
||||
qCDebug(entities) << senderNode->getUUID() << "physical edits suppressed";
|
||||
}
|
||||
}
|
||||
}
|
||||
// else client accepts what the server says
|
||||
|
@ -612,6 +616,14 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit
|
|||
}
|
||||
|
||||
void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties) {
|
||||
static quint64 lastTerseLog = 0;
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
if (now - lastTerseLog > USECS_PER_SECOND) {
|
||||
qCDebug(entities) << "-------------------------";
|
||||
}
|
||||
lastTerseLog = now;
|
||||
|
||||
if (properties.simulationOwnerChanged()) {
|
||||
int simIndex = changedProperties.indexOf("simulationOwner");
|
||||
if (simIndex >= 0) {
|
||||
|
@ -619,6 +631,79 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
|
|||
changedProperties[simIndex] = QString("simulationOwner:") + QString::number((int)simOwner.getPriority());
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.velocityChanged()) {
|
||||
int index = changedProperties.indexOf("velocity");
|
||||
if (index >= 0) {
|
||||
glm::vec3 value = properties.getVelocity();
|
||||
QString changeHint = "0";
|
||||
if (value.x + value.y + value.z > 0) {
|
||||
changeHint = "+";
|
||||
} else if (value.x + value.y + value.z < 0) {
|
||||
changeHint = "-";
|
||||
}
|
||||
changedProperties[index] = QString("velocity:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.gravityChanged()) {
|
||||
int index = changedProperties.indexOf("gravity");
|
||||
if (index >= 0) {
|
||||
glm::vec3 value = properties.getGravity();
|
||||
QString changeHint = "0";
|
||||
if (value.x + value.y + value.z > 0) {
|
||||
changeHint = "+";
|
||||
} else if (value.x + value.y + value.z < 0) {
|
||||
changeHint = "-";
|
||||
}
|
||||
changedProperties[index] = QString("gravity:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.actionDataChanged()) {
|
||||
int index = changedProperties.indexOf("actionData");
|
||||
if (index >= 0) {
|
||||
QByteArray value = properties.getActionData();
|
||||
QString changeHint = serializedActionsToDebugString(value);
|
||||
changedProperties[index] = QString("actionData:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.ignoreForCollisionsChanged()) {
|
||||
int index = changedProperties.indexOf("ignoreForCollisions");
|
||||
if (index >= 0) {
|
||||
bool value = properties.getIgnoreForCollisions();
|
||||
QString changeHint = "0";
|
||||
if (value) {
|
||||
changeHint = "1";
|
||||
}
|
||||
changedProperties[index] = QString("ignoreForCollisions:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.collisionsWillMoveChanged()) {
|
||||
int index = changedProperties.indexOf("collisionsWillMove");
|
||||
if (index >= 0) {
|
||||
bool value = properties.getCollisionsWillMove();
|
||||
QString changeHint = "0";
|
||||
if (value) {
|
||||
changeHint = "1";
|
||||
}
|
||||
changedProperties[index] = QString("collisionsWillMove:") + changeHint;
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.lockedChanged()) {
|
||||
int index = changedProperties.indexOf("locked");
|
||||
if (index >= 0) {
|
||||
bool value = properties.getLocked();
|
||||
QString changeHint = "0";
|
||||
if (value) {
|
||||
changeHint = "1";
|
||||
}
|
||||
changedProperties[index] = QString("locked:") + changeHint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
|
||||
|
@ -673,7 +758,8 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
|
|||
if (wantTerseEditLogging()) {
|
||||
QList<QString> changedProperties = properties.listChangedProperties();
|
||||
fixupTerseEditLogging(properties, changedProperties);
|
||||
qCDebug(entities) << "edit" << entityItemID.toString() << changedProperties;
|
||||
qCDebug(entities) << senderNode->getUUID() << "edit" <<
|
||||
existingEntity->getDebugName() << changedProperties;
|
||||
}
|
||||
endLogging = usecTimestampNow();
|
||||
|
||||
|
@ -703,7 +789,7 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi
|
|||
if (wantTerseEditLogging()) {
|
||||
QList<QString> changedProperties = properties.listChangedProperties();
|
||||
fixupTerseEditLogging(properties, changedProperties);
|
||||
qCDebug(entities) << "add" << entityItemID.toString() << changedProperties;
|
||||
qCDebug(entities) << senderNode->getUUID() << "add" << entityItemID << changedProperties;
|
||||
}
|
||||
endLogging = usecTimestampNow();
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ void SimulationOwner::test() {
|
|||
}
|
||||
|
||||
bool SimulationOwner::operator!=(const SimulationOwner& other) {
|
||||
return (_id != other._id && _priority != other._priority);
|
||||
return (_id != other._id || _priority != other._priority);
|
||||
}
|
||||
|
||||
SimulationOwner& SimulationOwner::operator=(const SimulationOwner& other) {
|
||||
|
|
|
@ -331,7 +331,7 @@ public:
|
|||
|
||||
template <typename T> Iterator<T> begin() { return Iterator<T>(&edit<T>(0), _stride); }
|
||||
template <typename T> Iterator<T> end() { return Iterator<T>(&edit<T>(getNum<T>()), _stride); }
|
||||
template <typename T> Iterator<const T> cbegin() const { return Iterator<const T>(&get<T>(0), _stride); }
|
||||
template <typename T> Iterator<const T> cbegin() const { return Iterator<const T>(&get<T>(), _stride); }
|
||||
template <typename T> Iterator<const T> cend() const { return Iterator<const T>(&get<T>(getNum<T>()), _stride); }
|
||||
|
||||
// the number of elements of the specified type fitting in the view size
|
||||
|
|
|
@ -46,7 +46,13 @@ DomainHandler::DomainHandler(QObject* parent) :
|
|||
connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer);
|
||||
}
|
||||
|
||||
void DomainHandler::clearConnectionInfo() {
|
||||
void DomainHandler::disconnect() {
|
||||
// if we're currently connected to a domain, send a disconnect packet on our way out
|
||||
if (_isConnected) {
|
||||
sendDisconnectPacket();
|
||||
}
|
||||
|
||||
// clear member variables that hold the connection state to a domain
|
||||
_uuid = QUuid();
|
||||
_connectionToken = QUuid();
|
||||
|
||||
|
@ -60,6 +66,18 @@ void DomainHandler::clearConnectionInfo() {
|
|||
setIsConnected(false);
|
||||
}
|
||||
|
||||
void DomainHandler::sendDisconnectPacket() {
|
||||
// The DomainDisconnect packet is not verified - we're relying on the eventual addition of DTLS to the
|
||||
// domain-server connection to stop greifing here
|
||||
|
||||
// construct the disconnect packet once (an empty packet but sourced with our current session UUID)
|
||||
static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnectRequest, 0);
|
||||
|
||||
// send the disconnect packet to the current domain server
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->sendUnreliablePacket(*disconnectPacket, _sockAddr);
|
||||
}
|
||||
|
||||
void DomainHandler::clearSettings() {
|
||||
_settingsObject = QJsonObject();
|
||||
_failedSettingsRequests = 0;
|
||||
|
@ -67,7 +85,7 @@ void DomainHandler::clearSettings() {
|
|||
|
||||
void DomainHandler::softReset() {
|
||||
qCDebug(networking) << "Resetting current domain connection information.";
|
||||
clearConnectionInfo();
|
||||
disconnect();
|
||||
clearSettings();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class DomainHandler : public QObject {
|
|||
public:
|
||||
DomainHandler(QObject* parent = 0);
|
||||
|
||||
void clearConnectionInfo();
|
||||
void disconnect();
|
||||
void clearSettings();
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
|
@ -113,6 +113,7 @@ signals:
|
|||
void settingsReceiveFail();
|
||||
|
||||
private:
|
||||
void sendDisconnectPacket();
|
||||
void hardReset();
|
||||
|
||||
QUuid _uuid;
|
||||
|
|
|
@ -441,7 +441,7 @@ void LimitedNodeList::reset() {
|
|||
_nodeSocket.clearConnections();
|
||||
}
|
||||
|
||||
void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||
bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||
QReadLocker readLocker(&_nodeMutex);
|
||||
|
||||
NodeHash::iterator it = _nodeHash.find(nodeUUID);
|
||||
|
@ -456,7 +456,10 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
|||
}
|
||||
|
||||
handleNodeKill(matchingNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LimitedNodeList::processKillNode(NLPacket& packet) {
|
||||
|
|
|
@ -230,7 +230,7 @@ public slots:
|
|||
virtual void sendSTUNRequest();
|
||||
void sendPingPackets();
|
||||
|
||||
void killNodeWithUUID(const QUuid& nodeUUID);
|
||||
bool killNodeWithUUID(const QUuid& nodeUUID);
|
||||
|
||||
signals:
|
||||
void dataSent(quint8 channelType, int bytes);
|
||||
|
|
|
@ -103,6 +103,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
|||
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket");
|
||||
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket");
|
||||
packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse");
|
||||
packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode");
|
||||
}
|
||||
|
||||
qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) {
|
||||
|
@ -218,6 +219,10 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes)
|
|||
}
|
||||
|
||||
void NodeList::sendDomainServerCheckIn() {
|
||||
if (_isShuttingDown) {
|
||||
qCDebug(networking) << "Refusing to send a domain-server check in while shutting down.";
|
||||
}
|
||||
|
||||
if (_publicSockAddr.isNull()) {
|
||||
// we don't know our public socket and we need to send it to the domain server
|
||||
qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
|
||||
|
@ -513,6 +518,13 @@ void NodeList::processDomainServerAddedNode(QSharedPointer<NLPacket> packet) {
|
|||
parseNodeFromPacketStream(packetStream);
|
||||
}
|
||||
|
||||
void NodeList::processDomainServerRemovedNode(QSharedPointer<NLPacket> packet) {
|
||||
// read the UUID from the packet, remove it if it exists
|
||||
QUuid nodeUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
qDebug() << "Received packet from domain-server to remove node with UUID" << uuidStringWithoutCurlyBraces(nodeUUID);
|
||||
killNodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||
// setup variables to read into from QDataStream
|
||||
qint8 nodeType;
|
||||
|
|
|
@ -66,6 +66,8 @@ public:
|
|||
|
||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||
void sendAssignment(Assignment& assignment);
|
||||
|
||||
void setIsShuttingDown(bool isShuttingDown) { _isShuttingDown = isShuttingDown; }
|
||||
|
||||
public slots:
|
||||
void reset();
|
||||
|
@ -74,6 +76,7 @@ public slots:
|
|||
|
||||
void processDomainServerList(QSharedPointer<NLPacket> packet);
|
||||
void processDomainServerAddedNode(QSharedPointer<NLPacket> packet);
|
||||
void processDomainServerRemovedNode(QSharedPointer<NLPacket> packet);
|
||||
void processDomainServerPathResponse(QSharedPointer<NLPacket> packet);
|
||||
|
||||
void processDomainServerConnectionTokenPacket(QSharedPointer<NLPacket> packet);
|
||||
|
@ -114,6 +117,7 @@ private:
|
|||
DomainHandler _domainHandler;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
bool _isShuttingDown { false };
|
||||
};
|
||||
|
||||
#endif // hifi_NodeList_h
|
||||
|
|
|
@ -33,14 +33,19 @@ void ThreadedAssignment::setFinished(bool isFinished) {
|
|||
if (_isFinished) {
|
||||
|
||||
qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up.";
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
||||
// we should de-register immediately for any of our packets
|
||||
packetReceiver.unregisterListener(this);
|
||||
|
||||
// we should also tell the packet receiver to drop packets while we're cleaning up
|
||||
packetReceiver.setShouldDropPackets(true);
|
||||
|
||||
// send a disconnect packet to the domain
|
||||
nodeList->getDomainHandler().disconnect();
|
||||
|
||||
if (_domainServerTimer) {
|
||||
// stop the domain-server check in timer by calling deleteLater so it gets cleaned up on NL thread
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
#include <math.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QMetaEnum>
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketType::NodeJsonStats << PacketType::EntityQuery
|
||||
<< PacketType::OctreeDataNack << PacketType::EntityEditNack
|
||||
<< PacketType::DomainListRequest << PacketType::StopNode;
|
||||
<< PacketType::DomainListRequest << PacketType::StopNode
|
||||
<< PacketType::DomainDisconnectRequest;
|
||||
|
||||
const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
|
||||
<< PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment
|
||||
|
@ -29,7 +31,8 @@ const QSet<PacketType> NON_SOURCED_PACKETS = QSet<PacketType>()
|
|||
<< PacketType::DomainSettingsRequest << PacketType::DomainSettings
|
||||
<< PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat
|
||||
<< PacketType::ICEPing << PacketType::ICEPingReply
|
||||
<< PacketType::AssignmentClientStatus << PacketType::StopNode;
|
||||
<< PacketType::AssignmentClientStatus << PacketType::StopNode
|
||||
<< PacketType::DomainServerRemovedNode;
|
||||
|
||||
const QSet<PacketType> RELIABLE_PACKETS = QSet<PacketType>();
|
||||
|
||||
|
@ -46,66 +49,17 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
}
|
||||
}
|
||||
|
||||
#define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x);
|
||||
|
||||
QString nameForPacketType(PacketType packetType) {
|
||||
switch (packetType) {
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::Unknown);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::StunResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainList);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::Ping);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::PingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::KillAvatar);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::InjectAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::MixedAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::MicrophoneAudioNoEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::MicrophoneAudioWithEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::BulkAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::SilentAudioFrame);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainListRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::RequestAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::CreateAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainConnectionDenied);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::MuteEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::AudioStreamStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::OctreeStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::Jurisdiction);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::JurisdictionRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarIdentity);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarBillboard);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainConnectRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerRequireDTLS);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::NodeJsonStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityErase);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::OctreeDataNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::StopNode);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::AudioEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerHeartbeat);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerAddedNode);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerPeerInformation);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEPing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::ICEPingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityAdd);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEdit);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerConnectionToken);
|
||||
default:
|
||||
return QString("Type: ") + QString::number((int)packetType);
|
||||
}
|
||||
return QString("unexpected");
|
||||
}
|
||||
|
||||
uint qHash(const PacketType& key, uint seed) {
|
||||
// seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch to
|
||||
// strongly typed enum for PacketType
|
||||
// seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch
|
||||
// to strongly typed enum for PacketType
|
||||
return qHash((quint8) key, seed);
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const PacketType& type) {
|
||||
debug.nospace() << (uint8_t) type << " (" << qPrintable(nameForPacketType(type)) << ")";
|
||||
QMetaObject metaObject = PacketTypeEnum::staticMetaObject;
|
||||
QMetaEnum metaEnum = metaObject.enumerator(metaObject.enumeratorOffset());
|
||||
QString typeName = metaEnum.valueToKey((int) type);
|
||||
|
||||
debug.nospace().noquote() << (uint8_t) type << " (" << typeName << ")";
|
||||
return debug.space();
|
||||
}
|
||||
|
|
|
@ -18,70 +18,81 @@
|
|||
#include <map>
|
||||
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
// If adding a new packet packetType, you can replace one marked usable or add at the end.
|
||||
// If you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well
|
||||
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
|
||||
enum class PacketType : uint8_t {
|
||||
Unknown,
|
||||
StunResponse,
|
||||
DomainList,
|
||||
Ping,
|
||||
PingReply,
|
||||
KillAvatar,
|
||||
AvatarData,
|
||||
InjectAudio,
|
||||
MixedAudio,
|
||||
MicrophoneAudioNoEcho,
|
||||
MicrophoneAudioWithEcho,
|
||||
BulkAvatarData,
|
||||
SilentAudioFrame,
|
||||
DomainListRequest,
|
||||
RequestAssignment,
|
||||
CreateAssignment,
|
||||
DomainConnectionDenied,
|
||||
MuteEnvironment,
|
||||
AudioStreamStats,
|
||||
DomainServerPathQuery,
|
||||
DomainServerPathResponse,
|
||||
DomainServerAddedNode,
|
||||
ICEServerPeerInformation,
|
||||
ICEServerQuery,
|
||||
OctreeStats,
|
||||
Jurisdiction,
|
||||
JurisdictionRequest,
|
||||
AssignmentClientStatus,
|
||||
NoisyMute,
|
||||
AvatarIdentity,
|
||||
AvatarBillboard,
|
||||
DomainConnectRequest,
|
||||
DomainServerRequireDTLS,
|
||||
NodeJsonStats,
|
||||
OctreeDataNack,
|
||||
StopNode,
|
||||
AudioEnvironment,
|
||||
EntityEditNack,
|
||||
ICEServerHeartbeat,
|
||||
ICEPing,
|
||||
ICEPingReply,
|
||||
EntityData,
|
||||
EntityQuery,
|
||||
EntityAdd,
|
||||
EntityErase,
|
||||
EntityEdit,
|
||||
DomainServerConnectionToken,
|
||||
DomainSettingsRequest,
|
||||
DomainSettings,
|
||||
AssetGet,
|
||||
AssetGetReply,
|
||||
AssetUpload,
|
||||
AssetUploadReply,
|
||||
AssetGetInfo,
|
||||
AssetGetInfoReply
|
||||
// The enums are inside this PacketTypeEnum for run-time conversion of enum value to string via
|
||||
// Q_ENUMS, without requiring a macro that is called for each enum value.
|
||||
class PacketTypeEnum {
|
||||
Q_GADGET
|
||||
Q_ENUMS(Value)
|
||||
public:
|
||||
// If adding a new packet packetType, you can replace one marked usable or add at the end.
|
||||
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
|
||||
enum class Value : uint8_t {
|
||||
Unknown,
|
||||
StunResponse,
|
||||
DomainList,
|
||||
Ping,
|
||||
PingReply,
|
||||
KillAvatar,
|
||||
AvatarData,
|
||||
InjectAudio,
|
||||
MixedAudio,
|
||||
MicrophoneAudioNoEcho,
|
||||
MicrophoneAudioWithEcho,
|
||||
BulkAvatarData,
|
||||
SilentAudioFrame,
|
||||
DomainListRequest,
|
||||
RequestAssignment,
|
||||
CreateAssignment,
|
||||
DomainConnectionDenied,
|
||||
MuteEnvironment,
|
||||
AudioStreamStats,
|
||||
DomainServerPathQuery,
|
||||
DomainServerPathResponse,
|
||||
DomainServerAddedNode,
|
||||
ICEServerPeerInformation,
|
||||
ICEServerQuery,
|
||||
OctreeStats,
|
||||
Jurisdiction,
|
||||
JurisdictionRequest,
|
||||
AssignmentClientStatus,
|
||||
NoisyMute,
|
||||
AvatarIdentity,
|
||||
AvatarBillboard,
|
||||
DomainConnectRequest,
|
||||
DomainServerRequireDTLS,
|
||||
NodeJsonStats,
|
||||
OctreeDataNack,
|
||||
StopNode,
|
||||
AudioEnvironment,
|
||||
EntityEditNack,
|
||||
ICEServerHeartbeat,
|
||||
ICEPing,
|
||||
ICEPingReply,
|
||||
EntityData,
|
||||
EntityQuery,
|
||||
EntityAdd,
|
||||
EntityErase,
|
||||
EntityEdit,
|
||||
DomainServerConnectionToken,
|
||||
DomainSettingsRequest,
|
||||
DomainSettings,
|
||||
AssetGet,
|
||||
AssetGetReply,
|
||||
AssetUpload,
|
||||
AssetUploadReply,
|
||||
AssetGetInfo,
|
||||
AssetGetInfoReply,
|
||||
DomainDisconnectRequest,
|
||||
DomainServerRemovedNode
|
||||
};
|
||||
};
|
||||
|
||||
using PacketType = PacketTypeEnum::Value;
|
||||
|
||||
const int NUM_BYTES_MD5_HASH = 16;
|
||||
|
||||
typedef char PacketVersion;
|
||||
|
@ -90,7 +101,6 @@ extern const QSet<PacketType> NON_VERIFIED_PACKETS;
|
|||
extern const QSet<PacketType> NON_SOURCED_PACKETS;
|
||||
extern const QSet<PacketType> RELIABLE_PACKETS;
|
||||
|
||||
QString nameForPacketType(PacketType packetType);
|
||||
PacketVersion versionForPacketType(PacketType packetType);
|
||||
|
||||
uint qHash(const PacketType& key, uint seed);
|
||||
|
|
|
@ -1890,8 +1890,8 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr
|
|||
versionForPacketType(expectedDataPacketType()), gotVersion);
|
||||
}
|
||||
} else {
|
||||
qCDebug(octree) << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
|
||||
<< " Got: " << nameForPacketType(gotType);
|
||||
qCDebug(octree) << "SVO file type mismatch. Expected: " << expectedType
|
||||
<< " Got: " << gotType;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
@ -112,8 +112,8 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine)
|
|||
_outgoingPriority = NO_PRORITY;
|
||||
} else {
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() > _outgoingPriority) {
|
||||
// we own the simulation or our priority looses to remote
|
||||
if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
|
||||
// we own the simulation or our priority looses to (or ties with) remote
|
||||
_outgoingPriority = NO_PRORITY;
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
|
|||
return false;
|
||||
}
|
||||
assert(entityTreeIsLocked());
|
||||
return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID();
|
||||
return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit();
|
||||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
|
@ -292,7 +292,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
_serverPosition += dt * _serverVelocity;
|
||||
}
|
||||
|
||||
if (_serverActionData != _entity->getActionData()) {
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
return true;
|
||||
}
|
||||
|
@ -370,11 +370,15 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
|
|||
assert(_body);
|
||||
assert(entityTreeIsLocked());
|
||||
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_entity->getSimulatorID() != sessionID) {
|
||||
// we don't own the simulation, but maybe we should...
|
||||
if (_outgoingPriority != NO_PRORITY) {
|
||||
if (_outgoingPriority < _entity->getSimulationPriority()) {
|
||||
// our priority looses to remote, so we don't bother to bid
|
||||
// our priority loses to remote, so we don't bother to bid
|
||||
_outgoingPriority = NO_PRORITY;
|
||||
return false;
|
||||
}
|
||||
|
@ -456,7 +460,10 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
|||
properties.setVelocity(_serverVelocity);
|
||||
properties.setAcceleration(_serverAcceleration);
|
||||
properties.setAngularVelocity(_serverAngularVelocity);
|
||||
properties.setActionData(_serverActionData);
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
_entity->setActionDataNeedsTransmit(false);
|
||||
properties.setActionData(_serverActionData);
|
||||
}
|
||||
|
||||
// set the LastEdited of the properties but NOT the entity itself
|
||||
quint64 now = usecTimestampNow();
|
||||
|
@ -526,7 +533,7 @@ void EntityMotionState::clearIncomingDirtyFlags() {
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
// virtual
|
||||
quint8 EntityMotionState::getSimulationPriority() const {
|
||||
if (_entity) {
|
||||
return _entity->getSimulationPriority();
|
||||
|
|
|
@ -130,6 +130,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) {
|
|||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
|
|
@ -163,6 +163,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
|
|||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setActionDataDirty(true);
|
||||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
|
|
|
@ -25,6 +25,8 @@ void Deck::queueClip(ClipPointer clip, Time timeOffset) {
|
|||
|
||||
// FIXME if the time offset is not zero, wrap the clip in a OffsetClip wrapper
|
||||
_clips.push_back(clip);
|
||||
|
||||
_length = std::max(_length, clip->duration());
|
||||
}
|
||||
|
||||
void Deck::play() {
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
static const FrameType TYPE_INVALID = 0xFFFF;
|
||||
static const FrameType TYPE_HEADER = 0x0;
|
||||
FrameType type { TYPE_INVALID };
|
||||
Time timeOffset { 0 };
|
||||
Time timeOffset { 0 }; // milliseconds
|
||||
QByteArray data;
|
||||
|
||||
Frame() {}
|
||||
|
|
|
@ -501,8 +501,10 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
|
|||
}
|
||||
|
||||
|
||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||
if (!_meshGroupsKnown && isLoaded()) {
|
||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, bool showCollisionHull) {
|
||||
|
||||
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
|
||||
_showCollisionHull = showCollisionHull;
|
||||
segregateMeshGroups();
|
||||
}
|
||||
|
||||
|
@ -525,8 +527,12 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChan
|
|||
return somethingAdded;
|
||||
}
|
||||
|
||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) {
|
||||
if (!_meshGroupsKnown && isLoaded()) {
|
||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges,
|
||||
render::Item::Status::Getters& statusGetters,
|
||||
bool showCollisionHull) {
|
||||
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
|
||||
_showCollisionHull = showCollisionHull;
|
||||
segregateMeshGroups();
|
||||
}
|
||||
|
||||
|
@ -1139,8 +1145,14 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
|
|||
}
|
||||
|
||||
void Model::segregateMeshGroups() {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = _geometry->getMeshes();
|
||||
QSharedPointer<NetworkGeometry> networkGeometry;
|
||||
if (_showCollisionHull && _collisionGeometry && _collisionGeometry->isLoaded()) {
|
||||
networkGeometry = _collisionGeometry;
|
||||
} else {
|
||||
networkGeometry = _geometry;
|
||||
}
|
||||
const FBXGeometry& geometry = networkGeometry->getFBXGeometry();
|
||||
const std::vector<std::unique_ptr<NetworkMesh>>& networkMeshes = networkGeometry->getMeshes();
|
||||
|
||||
_rig->makeAnimSkeleton(geometry);
|
||||
|
||||
|
|
|
@ -76,10 +76,13 @@ public:
|
|||
return !_needsReload && isRenderable() && isActive() && isLoaded();
|
||||
}
|
||||
bool initWhenReady(render::ScenePointer scene);
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges,
|
||||
render::Item::Status::Getters& statusGetters);
|
||||
bool showCollisionHull = false);
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges,
|
||||
render::Item::Status::Getters& statusGetters,
|
||||
bool showCollisionHull = false);
|
||||
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
void renderSetup(RenderArgs* args);
|
||||
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().empty()); }
|
||||
|
@ -368,6 +371,7 @@ private:
|
|||
bool _readyWhenAdded = false;
|
||||
bool _needsReload = true;
|
||||
bool _needsUpdateClusterMatrices = true;
|
||||
bool _showCollisionHull = false;
|
||||
|
||||
friend class MeshPartPayload;
|
||||
protected:
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "RenderDeferredTask.h"
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <PathUtils.h>
|
||||
#include <RenderArgs.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <gpu/Context.h>
|
||||
|
@ -111,8 +112,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
|
|||
_jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false))));
|
||||
_jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput())));
|
||||
|
||||
_jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques)));
|
||||
// Grab a texture map representing the different status icons and assign that to the drawStatsuJob
|
||||
auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg";
|
||||
|
||||
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath);
|
||||
_jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap))));
|
||||
|
||||
_jobs.back().setEnabled(false);
|
||||
_drawStatusJobIndex = _jobs.size() - 1;
|
||||
|
@ -387,4 +391,4 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
|
|||
|
||||
});
|
||||
args->_batch = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,12 @@ public:
|
|||
int _drawStatusJobIndex = -1;
|
||||
int _drawHitEffectJobIndex = -1;
|
||||
|
||||
void setDrawItemStatus(bool draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw); } }
|
||||
void setDrawItemStatus(int draw) {
|
||||
if (_drawStatusJobIndex >= 0) {
|
||||
_jobs[_drawStatusJobIndex].setEnabled(draw > 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } }
|
||||
|
||||
void setDrawHitEffect(bool draw) { if (_drawHitEffectJobIndex >= 0) { _jobs[_drawHitEffectJobIndex].setEnabled(draw); } }
|
||||
|
|
|
@ -29,7 +29,7 @@ using namespace render;
|
|||
|
||||
|
||||
|
||||
const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() {
|
||||
const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() {
|
||||
if (!_drawItemBoundsPipeline) {
|
||||
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert)));
|
||||
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag)));
|
||||
|
@ -56,18 +56,20 @@ const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() {
|
|||
return _drawItemBoundsPipeline;
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() {
|
||||
const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() {
|
||||
if (!_drawItemStatusPipeline) {
|
||||
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert)));
|
||||
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag)));
|
||||
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("iconStatusMap"), 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
_drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos");
|
||||
_drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim");
|
||||
_drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus");
|
||||
_drawItemStatusValue0Loc = program->getUniforms().findLocation("inStatus0");
|
||||
_drawItemStatusValue1Loc = program->getUniforms().findLocation("inStatus1");
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
|
@ -84,11 +86,23 @@ const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() {
|
|||
return _drawItemStatusPipeline;
|
||||
}
|
||||
|
||||
void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) {
|
||||
void DrawStatus::setStatusIconMap(const gpu::TexturePointer& map) {
|
||||
_statusIconMap = map;
|
||||
}
|
||||
|
||||
const gpu::TexturePointer DrawStatus::getStatusIconMap() const {
|
||||
return _statusIconMap;
|
||||
}
|
||||
|
||||
void DrawStatus::run(const SceneContextPointer& sceneContext,
|
||||
const RenderContextPointer& renderContext,
|
||||
const ItemIDsBounds& inItems) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
RenderArgs* args = renderContext->args;
|
||||
auto& scene = sceneContext->_scene;
|
||||
const int NUM_STATUS_VEC4_PER_ITEM = 2;
|
||||
const int VEC4_LENGTH = 4;
|
||||
|
||||
// FIrst thing, we collect the bound and the status for all the items we want to render
|
||||
int nbItems = 0;
|
||||
|
@ -101,7 +115,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
}
|
||||
|
||||
_itemBounds->resize((inItems.size() * sizeof(AABox)));
|
||||
_itemStatus->resize((inItems.size() * sizeof(glm::vec4)));
|
||||
_itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4)));
|
||||
AABox* itemAABox = reinterpret_cast<AABox*> (_itemBounds->editData());
|
||||
glm::ivec4* itemStatus = reinterpret_cast<glm::ivec4*> (_itemStatus->editData());
|
||||
for (auto& item : inItems) {
|
||||
|
@ -112,11 +126,31 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
(*itemAABox).setBox(item.bounds.getCorner(), 0.1f);
|
||||
}
|
||||
auto& itemScene = scene->getItem(item.id);
|
||||
(*itemStatus) = itemScene.getStatusPackedValues();
|
||||
|
||||
auto itemStatusPointer = itemScene.getStatus();
|
||||
if (itemStatusPointer) {
|
||||
// Query the current status values, this is where the statusGetter lambda get called
|
||||
auto&& currentStatusValues = itemStatusPointer->getCurrentValues();
|
||||
int valueNum = 0;
|
||||
for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) {
|
||||
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
for (int component = 0; component < VEC4_LENGTH; component++) {
|
||||
valueNum = vec4Num * VEC4_LENGTH + component;
|
||||
if (valueNum < (int)currentStatusValues.size()) {
|
||||
(*itemStatus)[component] = currentStatusValues[valueNum].getPackedData();
|
||||
}
|
||||
}
|
||||
itemStatus++;
|
||||
}
|
||||
} else {
|
||||
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
itemStatus++;
|
||||
(*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData());
|
||||
itemStatus++;
|
||||
}
|
||||
|
||||
nbItems++;
|
||||
itemAABox++;
|
||||
itemStatus++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +165,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
Transform viewMat;
|
||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||
args->_viewFrustum->evalViewTransform(viewMat);
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
|
||||
batch.setProjectionTransform(projMat);
|
||||
batch.setViewTransform(viewMat);
|
||||
|
@ -144,20 +179,28 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
|
|||
|
||||
const unsigned int VEC3_ADRESS_OFFSET = 3;
|
||||
|
||||
for (int i = 0; i < nbItems; i++) {
|
||||
batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i));
|
||||
batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
|
||||
if ((renderContext->_drawItemStatus & showDisplayStatusFlag) > 0) {
|
||||
for (int i = 0; i < nbItems; i++) {
|
||||
batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i));
|
||||
batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
|
||||
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
batch.draw(gpu::LINES, 24, 0);
|
||||
}
|
||||
}
|
||||
|
||||
batch.setResourceTexture(0, gpu::TextureView(getStatusIconMap(), 0));
|
||||
|
||||
batch.setPipeline(getDrawItemStatusPipeline());
|
||||
for (int i = 0; i < nbItems; i++) {
|
||||
batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i));
|
||||
batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
|
||||
batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i));
|
||||
|
||||
batch.draw(gpu::TRIANGLES, 24, 0);
|
||||
if ((renderContext->_drawItemStatus & showNetworkStatusFlag) > 0) {
|
||||
for (int i = 0; i < nbItems; i++) {
|
||||
batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i));
|
||||
batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET);
|
||||
batch._glUniform4iv(_drawItemStatusValue0Loc, 1, (const int*)(itemStatus + NUM_STATUS_VEC4_PER_ITEM * i));
|
||||
batch._glUniform4iv(_drawItemStatusValue1Loc, 1, (const int*)(itemStatus + NUM_STATUS_VEC4_PER_ITEM * i + 1));
|
||||
batch.draw(gpu::TRIANGLES, 24 * NUM_STATUS_VEC4_PER_ITEM, 0);
|
||||
}
|
||||
}
|
||||
batch.setResourceTexture(0, 0);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,22 +21,30 @@ namespace render {
|
|||
int _drawItemBoundDimLoc = -1;
|
||||
int _drawItemStatusPosLoc = -1;
|
||||
int _drawItemStatusDimLoc = -1;
|
||||
int _drawItemStatusValueLoc = -1;
|
||||
int _drawItemStatusValue0Loc = -1;
|
||||
int _drawItemStatusValue1Loc = -1;
|
||||
|
||||
gpu::Stream::FormatPointer _drawItemFormat;
|
||||
gpu::PipelinePointer _drawItemBoundsPipeline;
|
||||
gpu::PipelinePointer _drawItemStatusPipeline;
|
||||
gpu::BufferPointer _itemBounds;
|
||||
gpu::BufferPointer _itemStatus;
|
||||
gpu::TexturePointer _statusIconMap;
|
||||
|
||||
public:
|
||||
|
||||
DrawStatus() {}
|
||||
DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); }
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems);
|
||||
|
||||
typedef Job::ModelI<DrawStatus, ItemIDsBounds> JobModel;
|
||||
|
||||
const gpu::PipelinePointer& getDrawItemBoundsPipeline();
|
||||
const gpu::PipelinePointer& getDrawItemStatusPipeline();
|
||||
const gpu::PipelinePointer getDrawItemBoundsPipeline();
|
||||
const gpu::PipelinePointer getDrawItemStatusPipeline();
|
||||
|
||||
void setStatusIconMap(const gpu::TexturePointer& map);
|
||||
const gpu::TexturePointer getStatusIconMap() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
|
||||
const Varying getInput() const { return _input; }
|
||||
|
||||
ModelI(const std::string& name, const Varying& input): Concept(name), _input(input) {}
|
||||
ModelI(const std::string& name, const Varying& input, Data data = Data()) : Concept(name), _data(data), _input(input) {}
|
||||
ModelI(const std::string& name, Data data): Concept(name), _data(data) {}
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
|
|
|
@ -25,6 +25,10 @@ public:
|
|||
};
|
||||
typedef std::shared_ptr<SceneContext> SceneContextPointer;
|
||||
|
||||
// see examples/utilities/tools/renderEngineDebug.js
|
||||
const int showDisplayStatusFlag = 1;
|
||||
const int showNetworkStatusFlag = 2;
|
||||
|
||||
|
||||
class RenderContext {
|
||||
public:
|
||||
|
@ -49,7 +53,7 @@ public:
|
|||
int _numDrawnOverlay3DItems = 0;
|
||||
int _maxDrawnOverlay3DItems = -1;
|
||||
|
||||
bool _drawItemStatus = false;
|
||||
int _drawItemStatus = 0;
|
||||
bool _drawHitEffect = false;
|
||||
|
||||
bool _occlusionStatus = false;
|
||||
|
|
|
@ -72,17 +72,20 @@ void Item::Status::Value::setScale(float scale) {
|
|||
void Item::Status::Value::setColor(float hue) {
|
||||
// Convert the HUe from range [0, 360] to signed normalized value
|
||||
const float HUE_MAX = 360.0f;
|
||||
_color = (std::numeric_limits<unsigned short>::max() - 1) * 0.5f * (1.0f + std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
|
||||
_color = (std::numeric_limits<unsigned char>::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX);
|
||||
}
|
||||
void Item::Status::Value::setIcon(unsigned char icon) {
|
||||
_icon = icon;
|
||||
}
|
||||
|
||||
void Item::Status::getPackedValues(glm::ivec4& values) const {
|
||||
for (unsigned int i = 0; i < (unsigned int)values.length(); i++) {
|
||||
if (i < _values.size()) {
|
||||
values[i] = _values[i]().getPackedData();
|
||||
} else {
|
||||
values[i] = Value::INVALID.getPackedData();
|
||||
}
|
||||
Item::Status::Values Item::Status::getCurrentValues() const {
|
||||
Values currentValues(_values.size());
|
||||
auto currentValue = currentValues.begin();
|
||||
for (auto& getter : _values) {
|
||||
(*currentValue) = getter();
|
||||
currentValue++;
|
||||
}
|
||||
return currentValues;
|
||||
}
|
||||
|
||||
void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) {
|
||||
|
@ -110,15 +113,6 @@ void Item::resetPayload(const PayloadPointer& payload) {
|
|||
}
|
||||
}
|
||||
|
||||
glm::ivec4 Item::getStatusPackedValues() const {
|
||||
glm::ivec4 values(Status::Value::INVALID.getPackedData());
|
||||
auto& status = getStatus();
|
||||
if (status) {
|
||||
status->getPackedValues(values);
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) {
|
||||
_resetItems.push_back(id);
|
||||
_resetPayloads.push_back(payload);
|
||||
|
|
|
@ -206,17 +206,21 @@ public:
|
|||
// It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue
|
||||
class Value {
|
||||
unsigned short _scale = 0xFFFF;
|
||||
unsigned short _color = 0xFFFF;
|
||||
unsigned char _color = 0xFF;
|
||||
unsigned char _icon = 0xFF;
|
||||
public:
|
||||
const static Value INVALID; // Invalid value meanss the status won't show
|
||||
|
||||
Value() {}
|
||||
Value(float scale, float hue) { setScale(scale); setColor(hue); }
|
||||
Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); }
|
||||
|
||||
// It can be scaled in the range [0, 1]
|
||||
void setScale(float scale);
|
||||
// the color hue in the range [0, 360] representing the color wheel hue
|
||||
void setColor(float hue);
|
||||
// the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would
|
||||
// hopefully have an icon available to display (see DrawStatusJob)
|
||||
void setIcon(unsigned char icon);
|
||||
|
||||
// Standard color Hue
|
||||
static const float RED; // 0.0f;
|
||||
|
@ -237,7 +241,10 @@ public:
|
|||
|
||||
void addGetter(const Getter& getter) { _values.push_back(getter); }
|
||||
|
||||
void getPackedValues(glm::ivec4& values) const;
|
||||
size_t getNumValues() const { return _values.size(); }
|
||||
|
||||
using Values = std::vector <Value>;
|
||||
Values getCurrentValues() const;
|
||||
};
|
||||
typedef std::shared_ptr<Status> StatusPointer;
|
||||
|
||||
|
@ -301,7 +308,6 @@ public:
|
|||
|
||||
// Access the status
|
||||
const StatusPointer& getStatus() const { return _payload->getStatus(); }
|
||||
glm::ivec4 getStatusPackedValues() const;
|
||||
|
||||
protected:
|
||||
PayloadPointer _payload;
|
||||
|
|
|
@ -12,9 +12,20 @@
|
|||
//
|
||||
|
||||
in vec4 varColor;
|
||||
in vec3 varTexcoord;
|
||||
out vec4 outFragColor;
|
||||
|
||||
uniform sampler2D _icons;
|
||||
vec2 getIconTexcoord(float icon, vec2 uv) {
|
||||
const vec2 ICON_COORD_SIZE = vec2(0.0625, 1.0);
|
||||
return vec2((uv.x + icon) * ICON_COORD_SIZE.x, uv.y * ICON_COORD_SIZE.y);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
outFragColor = varColor;
|
||||
if (varTexcoord.z < 254.5) {
|
||||
outFragColor = texture(_icons, getIconTexcoord(varTexcoord.z, varTexcoord.xy)) * varColor;
|
||||
} else {
|
||||
vec2 centerDir = varTexcoord.xy * 2.0f - 1.0f;
|
||||
outFragColor = vec4(varColor.xyz, 1.0 - step(1.0f, dot(centerDir.xy, centerDir.xy)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
<$declareStandardTransform()$>
|
||||
|
||||
out vec4 varColor;
|
||||
out vec3 varTexcoord;
|
||||
|
||||
uniform vec3 inBoundPos;
|
||||
uniform vec3 inBoundDim;
|
||||
uniform ivec4 inStatus;
|
||||
uniform ivec4 inStatus0;
|
||||
uniform ivec4 inStatus1;
|
||||
|
||||
vec3 paintRainbow(float normalizedHue) {
|
||||
float v = normalizedHue * 6.f;
|
||||
|
@ -43,16 +45,28 @@ vec3 paintRainbow(float normalizedHue) {
|
|||
}
|
||||
}
|
||||
|
||||
vec2 unpackStatus(int v) {
|
||||
return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0),
|
||||
clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0));
|
||||
const int INVALID_STATUS = int(0xFFFFFFFF);
|
||||
const int MAX_NUM_ICONS = 8;
|
||||
const int ICONS_PER_ROW = 4;
|
||||
|
||||
int getIconStatus(int icon) {
|
||||
if (icon < ICONS_PER_ROW) {
|
||||
return inStatus0[icon];
|
||||
} else if (icon < MAX_NUM_ICONS) {
|
||||
return inStatus1[icon - ICONS_PER_ROW];
|
||||
}
|
||||
return INVALID_STATUS;
|
||||
}
|
||||
|
||||
vec3 unpackStatus(int v) {
|
||||
return vec3(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0),
|
||||
clamp(float(uint((v >> 16) & 0xFF)) / 255.0, 0.0, 1.0),
|
||||
clamp(float(int((v >> 24) & 0xFF)), 0.0, 255.0));
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
const vec2 ICON_PIXEL_SIZE = vec2(10, 10);
|
||||
const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2);
|
||||
const int NUM_VERTICES = 6;
|
||||
const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES](
|
||||
const int NUM_VERTICES_PER_ICON = 6;
|
||||
const vec4 UNIT_QUAD[NUM_VERTICES_PER_ICON] = vec4[NUM_VERTICES_PER_ICON](
|
||||
vec4(-1.0, -1.0, 0.0, 1.0),
|
||||
vec4(1.0, -1.0, 0.0, 1.0),
|
||||
vec4(-1.0, 1.0, 0.0, 1.0),
|
||||
|
@ -61,6 +75,17 @@ void main(void) {
|
|||
vec4(1.0, 1.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
const vec2 ICON_PIXEL_SIZE = vec2(20, 20);
|
||||
const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2);
|
||||
const vec2 ICON_GRID_SLOTS[MAX_NUM_ICONS] = vec2[MAX_NUM_ICONS](vec2(-1.5, 0.5),
|
||||
vec2(-0.5, 0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(1.5, 0.5),
|
||||
vec2(-1.5,-0.5),
|
||||
vec2(-0.5,-0.5),
|
||||
vec2(0.5, -0.5),
|
||||
vec2(1.5, -0.5));
|
||||
|
||||
// anchor point in clip space
|
||||
vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0);
|
||||
TransformCamera cam = getTransformCamera();
|
||||
|
@ -68,36 +93,43 @@ void main(void) {
|
|||
<$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$>
|
||||
|
||||
// Which icon are we dealing with ?
|
||||
int iconNum = gl_VertexID / NUM_VERTICES;
|
||||
int iconNum = gl_VertexID / NUM_VERTICES_PER_ICON;
|
||||
int packedIconStatus = getIconStatus(iconNum);
|
||||
|
||||
// if invalid, just kill
|
||||
if (inStatus[iconNum] == 0xFFFFFFFF) {
|
||||
if (packedIconStatus == INVALID_STATUS) {
|
||||
gl_Position = anchorPoint;
|
||||
varColor = vec4(1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Which quad vertex pos?
|
||||
int twoTriID = gl_VertexID - iconNum * NUM_VERTICES_PER_ICON;
|
||||
vec4 quadPos = UNIT_QUAD[twoTriID];
|
||||
|
||||
// unpack to get x and y satus
|
||||
vec2 iconStatus = unpackStatus(inStatus[iconNum]);
|
||||
vec3 iconStatus = unpackStatus(packedIconStatus);
|
||||
|
||||
// Use the status for showing a color
|
||||
varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0);
|
||||
|
||||
// Pass the texcoord and the z texcoord is representing the texture icon
|
||||
varTexcoord = vec3((quadPos.xy + 1.0) * 0.5, iconStatus.z);
|
||||
|
||||
// Also changes the size of the notification
|
||||
vec2 iconScale = ICON_PIXEL_SIZE;
|
||||
iconScale = max(vec2(1, 1), (iconScale * iconStatus.x));
|
||||
iconScale = max(vec2(0, 0), (iconScale * iconStatus.x));
|
||||
|
||||
//Offset icon to the right based on the iconNum
|
||||
vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0);
|
||||
vec2 gridOffset = ICON_GRID_SLOTS[iconNum];
|
||||
vec2 offset = gridOffset * (ICON_PIXEL_SIZE + MARGIN_PIXEL_SIZE);
|
||||
|
||||
// Final position in pixel space
|
||||
int twoTriID = gl_VertexID - iconNum * NUM_VERTICES;
|
||||
vec4 pos = UNIT_QUAD[twoTriID];
|
||||
vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale;
|
||||
vec2 quadPixelPos = offset.xy + quadPos.xy * 0.5 * iconScale;
|
||||
|
||||
vec4 viewport;
|
||||
<$transformCameraViewport(cam, viewport)$>;
|
||||
vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w);
|
||||
gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,8 @@ public:
|
|||
Q_INVOKABLE void setEngineMaxDrawnOverlay3DItems(int count) { _maxDrawnOverlay3DItems = count; }
|
||||
Q_INVOKABLE int getEngineMaxDrawnOverlay3DItems() { return _maxDrawnOverlay3DItems; }
|
||||
|
||||
Q_INVOKABLE void setEngineDisplayItemStatus(bool display) { _drawItemStatus = display; }
|
||||
Q_INVOKABLE bool doEngineDisplayItemStatus() { return _drawItemStatus; }
|
||||
Q_INVOKABLE void setEngineDisplayItemStatus(int display) { _drawItemStatus = display; }
|
||||
Q_INVOKABLE int doEngineDisplayItemStatus() { return _drawItemStatus; }
|
||||
|
||||
Q_INVOKABLE void setEngineDisplayHitEffect(bool display) { _drawHitEffect = display; }
|
||||
Q_INVOKABLE bool doEngineDisplayHitEffect() { return _drawHitEffect; }
|
||||
|
@ -143,7 +143,7 @@ protected:
|
|||
int _maxDrawnTransparentItems = -1;
|
||||
int _maxDrawnOverlay3DItems = -1;
|
||||
|
||||
bool _drawItemStatus = false;
|
||||
int _drawItemStatus = 0;
|
||||
|
||||
bool _drawHitEffect = false;
|
||||
|
||||
|
|
|
@ -68,17 +68,16 @@ public:
|
|||
class RenderArgs {
|
||||
public:
|
||||
typedef std::function<bool(const RenderArgs* args, const AABox& bounds)> ShoudRenderFunctor;
|
||||
|
||||
|
||||
enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE };
|
||||
|
||||
enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT };
|
||||
|
||||
enum DebugFlags {
|
||||
RENDER_DEBUG_NONE = 0,
|
||||
RENDER_DEBUG_HULLS = 1,
|
||||
RENDER_DEBUG_SIMULATION_OWNERSHIP = 2,
|
||||
RENDER_DEBUG_HULLS = 1
|
||||
};
|
||||
|
||||
|
||||
RenderArgs(std::shared_ptr<gpu::Context> context = nullptr,
|
||||
OctreeRenderer* renderer = nullptr,
|
||||
ViewFrustum* viewFrustum = nullptr,
|
||||
|
|
92
libraries/shared/src/shared/QTryReadLocker.h
Normal file
92
libraries/shared/src/shared/QTryReadLocker.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// QTryReadLocker.h
|
||||
// shared/src/shared/QTryReadLocker.h
|
||||
//
|
||||
// Created by Clément Brisset on 10/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_QTryReadLocker_h
|
||||
#define hifi_QTryReadLocker_h
|
||||
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
class QTryReadLocker {
|
||||
public:
|
||||
QTryReadLocker(QReadWriteLock* readWriteLock);
|
||||
QTryReadLocker(QReadWriteLock* readWriteLock, int timeout);
|
||||
~QTryReadLocker();
|
||||
|
||||
bool isLocked() const;
|
||||
|
||||
void unlock();
|
||||
bool tryRelock();
|
||||
bool tryRelock(int timeout);
|
||||
|
||||
QReadWriteLock* readWriteLock() const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QTryReadLocker)
|
||||
quintptr _val;
|
||||
};
|
||||
|
||||
// Implementation
|
||||
inline QTryReadLocker::QTryReadLocker(QReadWriteLock* areadWriteLock) :
|
||||
_val(reinterpret_cast<quintptr>(areadWriteLock))
|
||||
{
|
||||
Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0),
|
||||
"QTryReadLocker", "QTryReadLocker pointer is misaligned");
|
||||
tryRelock();
|
||||
}
|
||||
|
||||
inline QTryReadLocker::QTryReadLocker(QReadWriteLock* areadWriteLock, int timeout) :
|
||||
_val(reinterpret_cast<quintptr>(areadWriteLock))
|
||||
{
|
||||
Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0),
|
||||
"QTryReadLocker", "QTryReadLocker pointer is misaligned");
|
||||
tryRelock(timeout);
|
||||
}
|
||||
|
||||
inline QTryReadLocker::~QTryReadLocker() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
inline bool QTryReadLocker::isLocked() const {
|
||||
return (_val & quintptr(1u)) == quintptr(1u);
|
||||
}
|
||||
|
||||
inline void QTryReadLocker::unlock() {
|
||||
if (_val && isLocked()) {
|
||||
_val &= ~quintptr(1u);
|
||||
readWriteLock()->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool QTryReadLocker::tryRelock() {
|
||||
if (_val && !isLocked()) {
|
||||
if (readWriteLock()->tryLockForRead()) {
|
||||
_val |= quintptr(1u);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool QTryReadLocker::tryRelock(int timeout) {
|
||||
if (_val && !isLocked()) {
|
||||
if (readWriteLock()->tryLockForRead(timeout)) {
|
||||
_val |= quintptr(1u);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline QReadWriteLock* QTryReadLocker::readWriteLock() const {
|
||||
return reinterpret_cast<QReadWriteLock*>(_val & ~quintptr(1u));
|
||||
}
|
||||
|
||||
#endif // hifi_QTryReadLocker_h
|
92
libraries/shared/src/shared/QTryWriteLocker.h
Normal file
92
libraries/shared/src/shared/QTryWriteLocker.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// QTryWriteLocker.h
|
||||
// shared/src/shared/QTryWriteLocker.h
|
||||
//
|
||||
// Created by Clément Brisset on 10/29/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_QTryWriteLocker_h
|
||||
#define hifi_QTryWriteLocker_h
|
||||
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
class QTryWriteLocker {
|
||||
public:
|
||||
QTryWriteLocker(QReadWriteLock* readWriteLock);
|
||||
QTryWriteLocker(QReadWriteLock* readWriteLock, int timeout);
|
||||
~QTryWriteLocker();
|
||||
|
||||
bool isLocked() const;
|
||||
|
||||
void unlock();
|
||||
bool tryRelock();
|
||||
bool tryRelock(int timeout);
|
||||
|
||||
QReadWriteLock* readWriteLock() const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QTryWriteLocker)
|
||||
quintptr _val;
|
||||
};
|
||||
|
||||
// Implementation
|
||||
inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock* readWriteLock) :
|
||||
_val(reinterpret_cast<quintptr>(readWriteLock))
|
||||
{
|
||||
Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0),
|
||||
"QTryWriteLocker", "QTryWriteLocker pointer is misaligned");
|
||||
tryRelock();
|
||||
}
|
||||
|
||||
inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock* readWriteLock, int timeout) :
|
||||
_val(reinterpret_cast<quintptr>(readWriteLock))
|
||||
{
|
||||
Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0),
|
||||
"QTryWriteLocker", "QTryWriteLocker pointer is misaligned");
|
||||
tryRelock(timeout);
|
||||
}
|
||||
|
||||
inline QTryWriteLocker::~QTryWriteLocker() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
inline bool QTryWriteLocker::isLocked() const {
|
||||
return (_val & quintptr(1u)) == quintptr(1u);
|
||||
}
|
||||
|
||||
inline void QTryWriteLocker::unlock() {
|
||||
if (_val && isLocked()) {
|
||||
_val &= ~quintptr(1u);
|
||||
readWriteLock()->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool QTryWriteLocker::tryRelock() {
|
||||
if (_val && !isLocked()) {
|
||||
if (readWriteLock()->tryLockForWrite()) {
|
||||
_val |= quintptr(1u);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool QTryWriteLocker::tryRelock(int timeout) {
|
||||
if (_val && !isLocked()) {
|
||||
if (readWriteLock()->tryLockForWrite(timeout)) {
|
||||
_val |= quintptr(1u);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline QReadWriteLock* QTryWriteLocker::readWriteLock() const {
|
||||
return reinterpret_cast<QReadWriteLock*>(_val & ~quintptr(1u));
|
||||
}
|
||||
|
||||
#endif // hifi_QTryWriteLocker_h
|
|
@ -9,54 +9,118 @@
|
|||
#pragma once
|
||||
#ifndef hifi_ReadWriteLockable_h
|
||||
#define hifi_ReadWriteLockable_h
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
#include "QTryReadLocker.h"
|
||||
#include "QTryWriteLocker.h"
|
||||
|
||||
class ReadWriteLockable {
|
||||
public:
|
||||
// Write locks
|
||||
template <typename F>
|
||||
bool withWriteLock(F f, bool require = true) const {
|
||||
if (!require) {
|
||||
bool result = _lock.tryLockForWrite();
|
||||
if (result) {
|
||||
f();
|
||||
_lock.unlock();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QWriteLocker locker(&_lock);
|
||||
f();
|
||||
return true;
|
||||
}
|
||||
void withWriteLock(F&& f) const;
|
||||
|
||||
template <typename F>
|
||||
bool withWriteLock(F&& f, bool require) const;
|
||||
|
||||
template <typename F>
|
||||
bool withTryWriteLock(F f) const {
|
||||
return withWriteLock(f, false);
|
||||
}
|
||||
|
||||
bool withTryWriteLock(F&& f) const;
|
||||
|
||||
template <typename F>
|
||||
bool withReadLock(F f, bool require = true) const {
|
||||
if (!require) {
|
||||
bool result = _lock.tryLockForRead();
|
||||
if (result) {
|
||||
f();
|
||||
_lock.unlock();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QReadLocker locker(&_lock);
|
||||
f();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool withTryWriteLock(F&& f, int timeout) const;
|
||||
|
||||
// Read locks
|
||||
template <typename F>
|
||||
bool withTryReadLock(F f) const {
|
||||
return withReadLock(f, false);
|
||||
}
|
||||
void withReadLock(F&& f) const;
|
||||
|
||||
template <typename F>
|
||||
bool withReadLock(F&& f, bool require) const;
|
||||
|
||||
template <typename F>
|
||||
bool withTryReadLock(F&& f) const;
|
||||
|
||||
template <typename F>
|
||||
bool withTryReadLock(F&& f, int timeout) const;
|
||||
|
||||
private:
|
||||
mutable QReadWriteLock _lock{ QReadWriteLock::Recursive };
|
||||
mutable QReadWriteLock _lock { QReadWriteLock::Recursive };
|
||||
};
|
||||
|
||||
// ReadWriteLockable
|
||||
template <typename F>
|
||||
inline void ReadWriteLockable::withWriteLock(F&& f) const {
|
||||
QWriteLocker locker(&_lock);
|
||||
f();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline bool ReadWriteLockable::withWriteLock(F&& f, bool require) const {
|
||||
if (require) {
|
||||
withWriteLock(std::forward<F>(f));
|
||||
return true;
|
||||
} else {
|
||||
return withTryReadLock(std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline bool ReadWriteLockable::withTryWriteLock(F&& f) const {
|
||||
QTryWriteLocker locker(&_lock);
|
||||
if (locker.isLocked()) {
|
||||
f();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline bool ReadWriteLockable::withTryWriteLock(F&& f, int timeout) const {
|
||||
QTryWriteLocker locker(&_lock, timeout);
|
||||
if (locker.isLocked()) {
|
||||
f();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline void ReadWriteLockable::withReadLock(F&& f) const {
|
||||
QReadLocker locker(&_lock);
|
||||
f();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline bool ReadWriteLockable::withReadLock(F&& f, bool require) const {
|
||||
if (require) {
|
||||
withReadLock(std::forward<F>(f));
|
||||
return true;
|
||||
} else {
|
||||
return withTryReadLock(std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline bool ReadWriteLockable::withTryReadLock(F&& f) const {
|
||||
QTryReadLocker locker(&_lock);
|
||||
if (locker.isLocked()) {
|
||||
f();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
inline bool ReadWriteLockable::withTryReadLock(F&& f, int timeout) const {
|
||||
QTryReadLocker locker(&_lock, timeout);
|
||||
if (locker.isLocked()) {
|
||||
f();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue