mirror of
https://github.com/lubosz/overte.git
synced 2025-04-16 09:46:29 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into rework-visual-physics-debug
This commit is contained in:
commit
89ed3751ae
16 changed files with 272 additions and 190 deletions
|
@ -96,29 +96,130 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) {
|
|||
return shouldSendDeletedEntities;
|
||||
}
|
||||
|
||||
// FIXME - most of the old code for this was encapsulated in EntityTree, I liked that design from a data
|
||||
// hiding and object oriented perspective. But that didn't really allow us to handle the case of lots
|
||||
// of entities being deleted at the same time. I'd like to look to move this back into EntityTree but
|
||||
// for now this works and addresses the bug.
|
||||
int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
|
||||
int totalBytes = 0;
|
||||
|
||||
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
|
||||
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
||||
quint64 considerEntitiesSince = EntityTree::getAdjustedConsiderSince(deletedEntitiesSentAt);
|
||||
|
||||
quint64 deletePacketSentAt = usecTimestampNow();
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
auto recentlyDeleted = tree->getRecentlyDeletedEntityIDs();
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
packetsSent = 0;
|
||||
|
||||
while (hasMoreToSend) {
|
||||
auto specialPacket = tree->encodeEntitiesDeletedSince(queryNode->getSequenceNumber(), deletedEntitiesSentAt,
|
||||
hasMoreToSend);
|
||||
// create a new special packet
|
||||
std::unique_ptr<NLPacket> deletesPacket = NLPacket::create(PacketType::EntityErase);
|
||||
|
||||
queryNode->packetSent(*specialPacket);
|
||||
// pack in flags
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
deletesPacket->writePrimitive(flags);
|
||||
|
||||
totalBytes += specialPacket->getDataSize();
|
||||
packetsSent++;
|
||||
// pack in sequence number
|
||||
auto sequenceNumber = queryNode->getSequenceNumber();
|
||||
deletesPacket->writePrimitive(sequenceNumber);
|
||||
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(specialPacket), *node);
|
||||
}
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
deletesPacket->writePrimitive(now);
|
||||
|
||||
// figure out where we are now and pack a temporary number of IDs
|
||||
uint16_t numberOfIDs = 0;
|
||||
qint64 numberOfIDsPos = deletesPacket->pos();
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been
|
||||
// deleted since we last sent to this node
|
||||
auto it = recentlyDeleted.constBegin();
|
||||
while (it != recentlyDeleted.constEnd()) {
|
||||
|
||||
// if the timestamp is more recent then out last sent time, include it
|
||||
if (it.key() > considerEntitiesSince) {
|
||||
|
||||
// get all the IDs for this timestamp
|
||||
const auto& entityIDsFromTime = recentlyDeleted.values(it.key());
|
||||
|
||||
for (const auto& entityID : entityIDsFromTime) {
|
||||
|
||||
// check to make sure we have room for one more ID, if we don't have more
|
||||
// room, then send out this packet and create another one
|
||||
if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) {
|
||||
|
||||
// replace the count for the number of included IDs
|
||||
deletesPacket->seek(numberOfIDsPos);
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// Send the current packet
|
||||
queryNode->packetSent(*deletesPacket);
|
||||
auto thisPacketSize = deletesPacket->getDataSize();
|
||||
totalBytes += thisPacketSize;
|
||||
packetsSent++;
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(deletesPacket), *node);
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityServer::sendSpecialPackets() sending packet packetsSent[" << packetsSent << "] size:" << thisPacketSize;
|
||||
#endif
|
||||
|
||||
|
||||
// create another packet
|
||||
deletesPacket = NLPacket::create(PacketType::EntityErase);
|
||||
|
||||
// pack in flags
|
||||
deletesPacket->writePrimitive(flags);
|
||||
|
||||
// pack in sequence number
|
||||
sequenceNumber = queryNode->getSequenceNumber();
|
||||
deletesPacket->writePrimitive(sequenceNumber);
|
||||
|
||||
// pack in timestamp
|
||||
deletesPacket->writePrimitive(now);
|
||||
|
||||
// figure out where we are now and pack a temporary number of IDs
|
||||
numberOfIDs = 0;
|
||||
numberOfIDsPos = deletesPacket->pos();
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
}
|
||||
|
||||
// FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server
|
||||
// to the client. These were causing "lost" entities like flashlights and laser pointers
|
||||
// now that we keep around some additional history of the erased entities and resend that
|
||||
// history for a longer time window, these entities are not "lost". But we haven't yet
|
||||
// found/fixed the underlying issue that caused bad UUIDs to be sent to some users.
|
||||
deletesPacket->write(entityID.toRfc4122());
|
||||
++numberOfIDs;
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID;
|
||||
#endif
|
||||
} // end for (ids)
|
||||
|
||||
} // end if (it.val > sinceLast)
|
||||
|
||||
|
||||
++it;
|
||||
} // end while
|
||||
|
||||
// replace the count for the number of included IDs
|
||||
deletesPacket->seek(numberOfIDsPos);
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// Send the current packet
|
||||
queryNode->packetSent(*deletesPacket);
|
||||
auto thisPacketSize = deletesPacket->getDataSize();
|
||||
totalBytes += thisPacketSize;
|
||||
packetsSent++;
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(deletesPacket), *node);
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityServer::sendSpecialPackets() sending packet packetsSent[" << packetsSent << "] size:" << thisPacketSize;
|
||||
#endif
|
||||
|
||||
nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt);
|
||||
}
|
||||
|
@ -134,6 +235,7 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN
|
|||
return totalBytes;
|
||||
}
|
||||
|
||||
|
||||
void EntityServer::pruneDeletedEntities() {
|
||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||
if (tree->hasAnyDeletedEntities()) {
|
||||
|
|
|
@ -240,6 +240,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
int packetsSent = 0;
|
||||
int totalBytesSent = 0;
|
||||
|
||||
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
|
||||
while (i != _singleSenderStats.end()) {
|
||||
|
@ -291,12 +292,15 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
packetsSent += nackPacketList->getNumPackets();
|
||||
|
||||
// send the list of nack packets
|
||||
nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||
totalBytesSent += nodeList->sendPacketList(std::move(nackPacketList), *destinationNode);
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
OctreeSendThread::_totalPackets += packetsSent;
|
||||
OctreeSendThread::_totalBytes += totalBytesSent;
|
||||
|
||||
return packetsSent;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,10 @@ AtomicUIntStat OctreeSendThread::_totalBytes { 0 };
|
|||
AtomicUIntStat OctreeSendThread::_totalWastedBytes { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_totalPackets { 0 };
|
||||
|
||||
AtomicUIntStat OctreeSendThread::_totalSpecialBytes { 0 };
|
||||
AtomicUIntStat OctreeSendThread::_totalSpecialPackets { 0 };
|
||||
|
||||
|
||||
int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
OctreeServer::didHandlePacketSend(this);
|
||||
|
||||
|
@ -579,11 +583,17 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// send the environment packet
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketsToSend(_node) && !nodeData->isShuttingDown()) {
|
||||
int specialPacketsSent;
|
||||
int specialPacketsSent = 0;
|
||||
trueBytesSent += _myServer->sendSpecialPackets(_node, nodeData, specialPacketsSent);
|
||||
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
|
||||
truePacketsSent += specialPacketsSent;
|
||||
packetsSentThisInterval += specialPacketsSent;
|
||||
|
||||
_totalPackets += specialPacketsSent;
|
||||
_totalBytes += trueBytesSent;
|
||||
|
||||
_totalSpecialPackets += specialPacketsSent;
|
||||
_totalSpecialBytes += trueBytesSent;
|
||||
}
|
||||
|
||||
// Re-send packets that were nacked by the client
|
||||
|
|
|
@ -38,6 +38,9 @@ public:
|
|||
static AtomicUIntStat _totalWastedBytes;
|
||||
static AtomicUIntStat _totalPackets;
|
||||
|
||||
static AtomicUIntStat _totalSpecialBytes;
|
||||
static AtomicUIntStat _totalSpecialPackets;
|
||||
|
||||
static AtomicUIntStat _usleepTime;
|
||||
static AtomicUIntStat _usleepCalls;
|
||||
|
||||
|
|
|
@ -415,6 +415,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
|
||||
quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
||||
|
||||
quint64 totalOutboundSpecialPackets = OctreeSendThread::_totalSpecialPackets;
|
||||
quint64 totalOutboundSpecialBytes = OctreeSendThread::_totalSpecialBytes;
|
||||
|
||||
statsString += QString(" Total Clients Connected: %1 clients\r\n")
|
||||
.arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
@ -606,6 +609,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
.arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Outbound Bytes: %1 bytes\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
statsString += QString(" Total Outbound Special Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundSpecialPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Outbound Special Bytes: %1 bytes\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundSpecialBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
statsString += QString(" Total Wasted Bytes: %1 bytes\r\n")
|
||||
.arg(locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString().sprintf(" Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 + ":" :
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -887,8 +887,13 @@ void EntityTree::update() {
|
|||
}
|
||||
}
|
||||
|
||||
quint64 EntityTree::getAdjustedConsiderSince(quint64 sinceTime) {
|
||||
return (sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER);
|
||||
}
|
||||
|
||||
|
||||
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
|
||||
quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER;
|
||||
quint64 considerEntitiesSince = getAdjustedConsiderSince(sinceTime);
|
||||
|
||||
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
||||
bool hasSomethingNewer = false;
|
||||
|
@ -915,88 +920,6 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
|
|||
return hasSomethingNewer;
|
||||
}
|
||||
|
||||
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
||||
std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
|
||||
bool& hasMore) {
|
||||
quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER;
|
||||
auto deletesPacket = NLPacket::create(PacketType::EntityErase);
|
||||
|
||||
// pack in flags
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
deletesPacket->writePrimitive(flags);
|
||||
|
||||
// pack in sequence number
|
||||
deletesPacket->writePrimitive(sequenceNumber);
|
||||
|
||||
// pack in timestamp
|
||||
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
|
||||
deletesPacket->writePrimitive(now);
|
||||
|
||||
// figure out where we are now and pack a temporary number of IDs
|
||||
uint16_t numberOfIDs = 0;
|
||||
qint64 numberOfIDsPos = deletesPacket->pos();
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
// we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been
|
||||
// deleted since we last sent to this node
|
||||
{
|
||||
QReadLocker locker(&_recentlyDeletedEntitiesLock);
|
||||
|
||||
bool hasFilledPacket = false;
|
||||
|
||||
auto it = _recentlyDeletedEntityItemIDs.constBegin();
|
||||
while (it != _recentlyDeletedEntityItemIDs.constEnd()) {
|
||||
QList<QUuid> values = _recentlyDeletedEntityItemIDs.values(it.key());
|
||||
for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
|
||||
|
||||
// if the timestamp is more recent then out last sent time, include it
|
||||
if (it.key() > considerEntitiesSince) {
|
||||
QUuid entityID = values.at(valueItem);
|
||||
|
||||
// FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server
|
||||
// to the client. These were causing "lost" entities like flashlights and laser pointers
|
||||
// now that we keep around some additional history of the erased entities and resend that
|
||||
// history for a longer time window, these entities are not "lost". But we haven't yet
|
||||
// found/fixed the underlying issue that caused bad UUIDs to be sent to some users.
|
||||
deletesPacket->write(entityID.toRfc4122());
|
||||
++numberOfIDs;
|
||||
|
||||
#ifdef EXTRA_ERASE_DEBUGGING
|
||||
qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID;
|
||||
#endif
|
||||
|
||||
// check to make sure we have room for one more ID
|
||||
if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) {
|
||||
hasFilledPacket = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check to see if we're about to return
|
||||
if (hasFilledPacket) {
|
||||
// let our caller know how far we got
|
||||
sinceTime = it.key();
|
||||
break;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
// if we got to the end, then we're done sending
|
||||
if (it == _recentlyDeletedEntityItemIDs.constEnd()) {
|
||||
hasMore = false;
|
||||
}
|
||||
}
|
||||
|
||||
// replace the count for the number of included IDs
|
||||
deletesPacket->seek(numberOfIDsPos);
|
||||
deletesPacket->writePrimitive(numberOfIDs);
|
||||
|
||||
return deletesPacket;
|
||||
}
|
||||
|
||||
|
||||
// called by the server when it knows all nodes have been sent deleted packets
|
||||
void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) {
|
||||
quint64 considerSinceTime = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER;
|
||||
|
|
|
@ -147,10 +147,19 @@ public:
|
|||
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||
|
||||
bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; }
|
||||
bool hasAnyDeletedEntities() const {
|
||||
QReadLocker locker(&_recentlyDeletedEntitiesLock);
|
||||
return _recentlyDeletedEntityItemIDs.size() > 0;
|
||||
}
|
||||
|
||||
bool hasEntitiesDeletedSince(quint64 sinceTime);
|
||||
std::unique_ptr<NLPacket> encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
|
||||
bool& hasMore);
|
||||
static quint64 getAdjustedConsiderSince(quint64 sinceTime);
|
||||
|
||||
QMultiMap<quint64, QUuid> getRecentlyDeletedEntityIDs() const {
|
||||
QReadLocker locker(&_recentlyDeletedEntitiesLock);
|
||||
return _recentlyDeletedEntityItemIDs;
|
||||
}
|
||||
|
||||
void forgetEntitiesDeletedBefore(quint64 sinceTime);
|
||||
|
||||
int processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode);
|
||||
|
@ -243,7 +252,7 @@ private:
|
|||
QReadWriteLock _newlyCreatedHooksLock;
|
||||
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;
|
||||
|
||||
QReadWriteLock _recentlyDeletedEntitiesLock;
|
||||
mutable QReadWriteLock _recentlyDeletedEntitiesLock;
|
||||
QMultiMap<quint64, QUuid> _recentlyDeletedEntityItemIDs;
|
||||
EntityItemFBXService* _fbxService;
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -89,11 +89,7 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
}
|
||||
|
||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||
float scaleLength = glm::length(_scale);
|
||||
float relativeDeltaScale = glm::length(_scale - scale) / scaleLength;
|
||||
|
||||
const float ONE_PERCENT = 0.01f;
|
||||
if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) {
|
||||
if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) {
|
||||
_scale = scale;
|
||||
initJointTransforms();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue