mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-14 11:46:34 +02:00
merge upstream/master into andrew/bispinor
This commit is contained in:
commit
461d08c0a9
104 changed files with 2040 additions and 2773 deletions
|
@ -73,17 +73,6 @@ void Agent::readPendingDatagrams() {
|
|||
}
|
||||
}
|
||||
|
||||
} else if (datagramPacketType == PacketTypeEntityAddResponse) {
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
EntityItemID::handleAddEntityResponse(receivedPacket);
|
||||
|
||||
// also give our local entity tree a chance to remap any internal locally created entities
|
||||
_entityViewer.getTree()->handleAddEntityResponse(receivedPacket);
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket);
|
||||
sourceNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
} else if (datagramPacketType == PacketTypeOctreeStats
|
||||
|| datagramPacketType == PacketTypeEntityData
|
||||
|| datagramPacketType == PacketTypeEntityErase
|
||||
|
|
|
@ -43,8 +43,7 @@ int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
|
|||
AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QString assignmentPool,
|
||||
QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort,
|
||||
quint16 assignmentMonitorPort) :
|
||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME),
|
||||
_localASPortSharedMem(NULL)
|
||||
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME)
|
||||
{
|
||||
LogUtils::init();
|
||||
|
||||
|
@ -181,8 +180,7 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
if (_assignmentServerHostname == "localhost") {
|
||||
// we want to check again for the local domain-server port in case the DS has restarted
|
||||
quint16 localAssignmentServerPort;
|
||||
if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, _localASPortSharedMem,
|
||||
localAssignmentServerPort)) {
|
||||
if (nodeList->getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, localAssignmentServerPort)) {
|
||||
if (localAssignmentServerPort != _assignmentServerSocket.getPort()) {
|
||||
qDebug() << "Port for local assignment server read from shared memory is"
|
||||
<< localAssignmentServerPort;
|
||||
|
@ -190,7 +188,11 @@ void AssignmentClient::sendAssignmentRequest() {
|
|||
_assignmentServerSocket.setPort(localAssignmentServerPort);
|
||||
nodeList->setAssignmentServerSocket(_assignmentServerSocket);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Failed to read local assignment server port from shared memory"
|
||||
<< "- will send assignment request to previous assignment server socket.";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
nodeList->sendAssignment(_requestAssignment);
|
||||
|
|
|
@ -44,7 +44,6 @@ private:
|
|||
QPointer<ThreadedAssignment> _currentAssignment;
|
||||
QString _assignmentServerHostname;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
QSharedMemory* _localASPortSharedMem; // memory shared with domain server
|
||||
QTimer _requestTimer; // timer for requesting and assignment
|
||||
QTimer _statsTimerACM; // timer for sending stats to assignment client monitor
|
||||
|
||||
|
|
|
@ -60,30 +60,6 @@ void EntityServer::beforeRun() {
|
|||
}
|
||||
|
||||
void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) {
|
||||
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int numBytesPacketHeader = nodeList->populatePacketHeader(reinterpret_cast<char*>(outputBuffer), PacketTypeEntityAddResponse);
|
||||
int packetLength = numBytesPacketHeader;
|
||||
copyAt += numBytesPacketHeader;
|
||||
|
||||
// encode the creatorTokenID
|
||||
uint32_t creatorTokenID = newEntity.getCreatorTokenID();
|
||||
memcpy(copyAt, &creatorTokenID, sizeof(creatorTokenID));
|
||||
copyAt += sizeof(creatorTokenID);
|
||||
packetLength += sizeof(creatorTokenID);
|
||||
|
||||
// encode the entity ID
|
||||
QUuid entityID = newEntity.getID();
|
||||
QByteArray encodedID = entityID.toRfc4122();
|
||||
memcpy(copyAt, encodedID.constData(), encodedID.size());
|
||||
copyAt += sizeof(entityID);
|
||||
packetLength += sizeof(entityID);
|
||||
|
||||
nodeList->writeDatagram((char*) outputBuffer, packetLength, senderNode);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -186,15 +186,15 @@ void OctreeQueryNode::resetOctreePacket() {
|
|||
_currentPacketIsCompressed = getWantCompression();
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
if (_currentPacketIsColor) {
|
||||
setAtBit(flags,PACKET_IS_COLOR_BIT);
|
||||
setAtBit(flags, PACKET_IS_COLOR_BIT);
|
||||
}
|
||||
if (_currentPacketIsCompressed) {
|
||||
setAtBit(flags,PACKET_IS_COMPRESSED_BIT);
|
||||
setAtBit(flags, PACKET_IS_COMPRESSED_BIT);
|
||||
}
|
||||
|
||||
_octreePacketAvailableBytes = MAX_PACKET_SIZE;
|
||||
int numBytesPacketHeader = DependencyManager::get<NodeList>()->populatePacketHeader(reinterpret_cast<char*>(_octreePacket),
|
||||
_myPacketType);
|
||||
_myPacketType);
|
||||
|
||||
_octreePacketAt = _octreePacket + numBytesPacketHeader;
|
||||
_octreePacketAvailableBytes -= numBytesPacketHeader;
|
||||
|
@ -204,7 +204,7 @@ void OctreeQueryNode::resetOctreePacket() {
|
|||
*flagsAt = flags;
|
||||
_octreePacketAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_FLAGS);
|
||||
|
||||
|
||||
// pack in sequence number
|
||||
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)_octreePacketAt;
|
||||
*sequenceAt = _sequenceNumber;
|
||||
|
@ -258,11 +258,16 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
|
|||
float originalFOV = getCameraFov();
|
||||
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
|
||||
|
||||
newestViewFrustum.setFieldOfView(wideFOV); // hack
|
||||
newestViewFrustum.setAspectRatio(getCameraAspectRatio());
|
||||
newestViewFrustum.setNearClip(getCameraNearClip());
|
||||
newestViewFrustum.setFarClip(getCameraFarClip());
|
||||
newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition());
|
||||
if (0.0f != getCameraAspectRatio() &&
|
||||
0.0f != getCameraNearClip() &&
|
||||
0.0f != getCameraFarClip()) {
|
||||
newestViewFrustum.setProjection(glm::perspective(
|
||||
glm::radians(wideFOV), // hack
|
||||
getCameraAspectRatio(),
|
||||
getCameraNearClip(),
|
||||
getCameraFarClip()));
|
||||
}
|
||||
|
||||
|
||||
// if there has been a change, then recalculate
|
||||
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
22
domain-server/resources/web/css/bootstrap-switch.min.css
vendored
Executable file
22
domain-server/resources/web/css/bootstrap-switch.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
|
@ -65,6 +65,10 @@ td.buttons {
|
|||
width: 30px;
|
||||
}
|
||||
|
||||
td.buttons.reorder-buttons {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
td .glyphicon {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
|
@ -105,6 +109,13 @@ table {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
/* styling for bootstrap-switch toggles */
|
||||
.checkbox-help {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* CSS only spinner for AJAX requests */
|
||||
|
||||
.spinner {
|
||||
margin: 30px auto 0;
|
||||
width: 70px;
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/style.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/stats/css/json.human.css" rel="stylesheet" media="screen">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
<script src='/js/underscore-min.js'></script>
|
||||
<script src='/js/underscore-keypath.min.js'></script>
|
||||
<script src='/js/bootbox.min.js'></script>
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
<script src='/js/settings.js'></script>
|
||||
<script src='/js/form2js.min.js'></script>
|
||||
<script src='js/bootstrap-switch.min.js'></script>
|
||||
<script src='js/sweetalert.min.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<script src='js/form2js.min.js'></script>
|
||||
<!--#include virtual="page-end.html"-->
|
||||
|
|
22
domain-server/resources/web/settings/js/bootstrap-switch.min.js
vendored
Executable file
22
domain-server/resources/web/settings/js/bootstrap-switch.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
|
@ -60,10 +60,15 @@ var viewHelpers = {
|
|||
if (setting.label) {
|
||||
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||
}
|
||||
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
|
||||
form_group += "<label for='" + keypath + "'>"
|
||||
form_group += "<input type='checkbox'" + common_attrs() + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
|
||||
form_group += " " + setting.help + "</label>";
|
||||
|
||||
form_group += "<div class='toggle-checkbox-container" + (isLocked ? " disabled" : "") + "'>"
|
||||
form_group += "<input type='checkbox'" + common_attrs('toggle-checkbox') + (setting_value ? "checked" : "")
|
||||
form_group += (isLocked ? " disabled" : "") + "/>"
|
||||
|
||||
if (setting.help) {
|
||||
form_group += "<span class='help-block checkbox-help'>" + setting.help + "</span>";
|
||||
}
|
||||
|
||||
form_group += "</div>"
|
||||
} else {
|
||||
input_type = _.has(setting, 'type') ? setting.type : "text"
|
||||
|
@ -168,12 +173,20 @@ $(document).ready(function(){
|
|||
|
||||
if (sibling.hasClass(Settings.DATA_COL_CLASS)) {
|
||||
// set focus to next input
|
||||
sibling.find('input').focus()
|
||||
} else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
||||
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click()
|
||||
sibling.find('input').focus();
|
||||
} else {
|
||||
|
||||
// set focus to the first input in the new row
|
||||
$target.closest('table').find('tr.inputs input:first').focus()
|
||||
// jump over the re-order row, if that's what we're on
|
||||
if (sibling.hasClass(Settings.REORDER_BUTTONS_CLASS)) {
|
||||
sibling = sibling.next();
|
||||
}
|
||||
|
||||
if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
||||
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click()
|
||||
|
||||
// set focus to the first input in the new row
|
||||
$target.closest('table').find('tr.inputs input:first').focus()
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($target.is('input')) {
|
||||
|
@ -193,10 +206,17 @@ $(document).ready(function(){
|
|||
|
||||
$('#' + Settings.FORM_ID).on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){
|
||||
// this input was changed, add the changed data attribute to it
|
||||
$(this).attr('data-changed', true)
|
||||
$(this).attr('data-changed', true);
|
||||
|
||||
badgeSidebarForDifferences($(this))
|
||||
})
|
||||
badgeSidebarForDifferences($(this));
|
||||
});
|
||||
|
||||
$('#' + Settings.FORM_ID).on('switchChange.bootstrapSwitch', 'input.toggle-checkbox', function(){
|
||||
// this checkbox was changed, add the changed data attribute to it
|
||||
$(this).attr('data-changed', true);
|
||||
|
||||
badgeSidebarForDifferences($(this));
|
||||
});
|
||||
|
||||
$('.advanced-toggle').click(function(){
|
||||
Settings.showAdvanced = !Settings.showAdvanced
|
||||
|
@ -234,12 +254,15 @@ $(document).ready(function(){
|
|||
});
|
||||
|
||||
$('#' + Settings.FORM_ID).on('click', '#' + Settings.DISCONNECT_ACCOUNT_BTN_ID, function(e){
|
||||
$(this).blur();
|
||||
disonnectHighFidelityAccount();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CONNECT_ACCOUNT_BTN_ID, function(e){
|
||||
$(this).blur();
|
||||
prepareAccessTokenPrompt();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
var panelsSource = $('#panels-template').html()
|
||||
|
@ -325,7 +348,7 @@ function disonnectHighFidelityAccount() {
|
|||
+ "</br></br>This could cause your domain to appear offline and no longer be reachable via any place names.",
|
||||
type: "warning",
|
||||
html: true,
|
||||
showCancelButton: true,
|
||||
showCancelButton: true
|
||||
}, function(){
|
||||
// we need to post to settings to clear the access-token
|
||||
$(Settings.ACCESS_TOKEN_SELECTOR).val('').change();
|
||||
|
@ -727,6 +750,9 @@ function reloadSettings() {
|
|||
// call our method to setup the place names table
|
||||
setupPlacesTable();
|
||||
|
||||
// setup any bootstrap switches
|
||||
$('.toggle-checkbox').bootstrapSwitch();
|
||||
|
||||
// add tooltip to locked settings
|
||||
$('label.locked').tooltip({
|
||||
placement: 'right',
|
||||
|
@ -767,7 +793,8 @@ $('body').on('click', '.save-button', function(e){
|
|||
});
|
||||
|
||||
function makeTable(setting, keypath, setting_value, isLocked) {
|
||||
var isArray = !_.has(setting, 'key')
|
||||
var isArray = !_.has(setting, 'key');
|
||||
var isHash = !isArray;
|
||||
|
||||
if (!isArray && setting.can_order) {
|
||||
setting.can_order = false;
|
||||
|
@ -780,7 +807,8 @@ function makeTable(setting, keypath, setting_value, isLocked) {
|
|||
}
|
||||
|
||||
html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name
|
||||
+ "' name='" + keypath + "' id='" + setting.html_id + "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>";
|
||||
+ "' name='" + keypath + "' id='" + (typeof setting.html_id !== 'undefined' ? setting.html_id : keypath)
|
||||
+ "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>";
|
||||
|
||||
// Column names
|
||||
html += "<tr class='headers'>"
|
||||
|
@ -799,8 +827,8 @@ function makeTable(setting, keypath, setting_value, isLocked) {
|
|||
|
||||
if (!isLocked && !setting.read_only) {
|
||||
if (setting.can_order) {
|
||||
html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES +
|
||||
"><a href='javascript:void(0);' class='glyphicon glyphicon-sort'></a></td>";
|
||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES +
|
||||
"'><a href='javascript:void(0);' class='glyphicon glyphicon-sort'></a></td>";
|
||||
}
|
||||
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES + "'></td></tr>"
|
||||
}
|
||||
|
@ -809,33 +837,38 @@ function makeTable(setting, keypath, setting_value, isLocked) {
|
|||
var row_num = 1;
|
||||
|
||||
if (keypath.length > 0 && _.size(setting_value) > 0) {
|
||||
_.each(setting_value, function(row, indexOrName) {
|
||||
html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + keypath + "." + indexOrName + "'") + ">"
|
||||
_.each(setting_value, function(row, rowIndexOrName) {
|
||||
html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + keypath + "." + rowIndexOrName + "'") + ">"
|
||||
|
||||
if (setting.numbered === true) {
|
||||
html += "<td class='numbered'>" + row_num + "</td>"
|
||||
}
|
||||
|
||||
if (setting.key) {
|
||||
html += "<td class='key'>" + indexOrName + "</td>"
|
||||
html += "<td class='key'>" + rowIndexOrName + "</td>"
|
||||
}
|
||||
|
||||
_.each(setting.columns, function(col) {
|
||||
html += "<td class='" + Settings.DATA_COL_CLASS + "'>"
|
||||
|
||||
if (isArray) {
|
||||
rowIsObject = setting.columns.length > 1
|
||||
colValue = rowIsObject ? row[col.name] : row
|
||||
html += colValue
|
||||
|
||||
// for arrays we add a hidden input to this td so that values can be posted appropriately
|
||||
html += "<input type='hidden' name='" + keypath + "[" + indexOrName + "]"
|
||||
+ (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>"
|
||||
} else if (row.hasOwnProperty(col.name)) {
|
||||
html += row[col.name]
|
||||
rowIsObject = setting.columns.length > 1;
|
||||
colValue = rowIsObject ? row[col.name] : row;
|
||||
colName = keypath + "[" + rowIndexOrName + "]" + (rowIsObject ? "." + col.name : "");
|
||||
} else {
|
||||
colValue = row[col.name];
|
||||
colName = keypath + "." + rowIndexOrName + "." + col.name;
|
||||
}
|
||||
|
||||
html += "</td>"
|
||||
// setup the td for this column
|
||||
html += "<td class='" + Settings.DATA_COL_CLASS + "' name='" + colName + "'>";
|
||||
|
||||
// add the actual value to the td so it is displayed
|
||||
html += colValue;
|
||||
|
||||
// for values to be posted properly we add a hidden input to this td
|
||||
html += "<input type='hidden' name='" + colName + "' value='" + colValue + "'/>";
|
||||
|
||||
html += "</td>";
|
||||
})
|
||||
|
||||
if (!isLocked && !setting.read_only) {
|
|
@ -44,13 +44,10 @@ int const DomainServer::EXIT_CODE_REBOOT = 234923;
|
|||
|
||||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.io";
|
||||
|
||||
|
||||
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
||||
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
||||
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
|
||||
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
|
||||
|
||||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
|
@ -776,9 +773,8 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
const HifiSockAddr& senderSockAddr,
|
||||
QString& reasonReturn) {
|
||||
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
bool isRestrictingAccess =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
||||
|
||||
// we always let in a user who is sending a packet from our local socket or from the localhost address
|
||||
if (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress()
|
||||
|
@ -786,45 +782,50 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (allowedUsers.count() > 0) {
|
||||
if (isRestrictingAccess) {
|
||||
|
||||
QStringList allowedUsers =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
|
||||
|
||||
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
|
||||
if (verifyUsersKey(username, usernameSignature, reasonReturn)) {
|
||||
return true;
|
||||
if (!verifyUsersKey(username, usernameSignature, reasonReturn)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Connect request denied for user" << username << "not in allowed users list.";
|
||||
reasonReturn = "User not on whitelist.";
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// we have no allowed user list.
|
||||
|
||||
// if this user is in the editors list, exempt them from the max-capacity check
|
||||
const QVariant* allowedEditorsVariant =
|
||||
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
||||
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
||||
if (allowedEditors.contains(username)) {
|
||||
if (verifyUsersKey(username, usernameSignature, reasonReturn)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we haven't reached max-capacity, let them in.
|
||||
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
||||
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
||||
if (maximumUserCapacity > 0) {
|
||||
unsigned int connectedUsers = countConnectedUsers();
|
||||
if (connectedUsers >= maximumUserCapacity) {
|
||||
// too many users, deny the new connection.
|
||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
|
||||
reasonReturn = "Too many connected users.";
|
||||
return false;
|
||||
}
|
||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, perhaps allowing new connection.";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// either we aren't restricting users, or this user is in the allowed list
|
||||
|
||||
// if this user is in the editors list, exempt them from the max-capacity check
|
||||
const QVariant* allowedEditorsVariant =
|
||||
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
|
||||
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
|
||||
if (allowedEditors.contains(username)) {
|
||||
if (verifyUsersKey(username, usernameSignature, reasonReturn)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we haven't reached max-capacity, let them in.
|
||||
const QVariant* maximumUserCapacityVariant = valueForKeyPath(_settingsManager.getSettingsMap(), MAXIMUM_USER_CAPACITY);
|
||||
unsigned int maximumUserCapacity = maximumUserCapacityVariant ? maximumUserCapacityVariant->toUInt() : 0;
|
||||
if (maximumUserCapacity > 0) {
|
||||
unsigned int connectedUsers = countConnectedUsers();
|
||||
if (connectedUsers >= maximumUserCapacity) {
|
||||
// too many users, deny the new connection.
|
||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, denying new connection.";
|
||||
reasonReturn = "Too many connected users.";
|
||||
return false;
|
||||
}
|
||||
qDebug() << connectedUsers << "/" << maximumUserCapacity << "users connected, perhaps allowing new connection.";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DomainServer::preloadAllowedUserPublicKeys() {
|
||||
|
@ -1255,10 +1256,8 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
|
|||
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
|
||||
const QString RESTRICTED_ACCESS_FLAG = "restricted";
|
||||
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
domainObject[RESTRICTED_ACCESS_FLAG] = (allowedUsers.size() > 0);
|
||||
domainObject[RESTRICTED_ACCESS_FLAG] =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
|
||||
|
||||
// add the number of currently connected agent users
|
||||
int numConnectedAuthedUsers = 0;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QtCore/QFile>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QUrlQuery>
|
||||
|
@ -42,33 +43,76 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
|||
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
|
||||
descriptionFile.open(QIODevice::ReadOnly);
|
||||
|
||||
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
|
||||
QJsonDocument descriptionDocument = QJsonDocument::fromJson(descriptionFile.readAll());
|
||||
|
||||
if (descriptionDocument.isObject()) {
|
||||
QJsonObject descriptionObject = descriptionDocument.object();
|
||||
|
||||
const QString DESCRIPTION_VERSION_KEY = "version";
|
||||
|
||||
if (descriptionObject.contains(DESCRIPTION_VERSION_KEY)) {
|
||||
// read the version from the settings description
|
||||
_descriptionVersion = descriptionObject[DESCRIPTION_VERSION_KEY].toDouble();
|
||||
|
||||
if (descriptionObject.contains(DESCRIPTION_SETTINGS_KEY)) {
|
||||
_descriptionArray = descriptionDocument.object()[DESCRIPTION_SETTINGS_KEY].toArray();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qCritical() << "Did not find settings decription in JSON at" << SETTINGS_DESCRIPTION_RELATIVE_PATH
|
||||
<< "- Unable to continue. domain-server will quit.";
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
_configMap.loadMasterAndUserConfig(argumentList);
|
||||
|
||||
// for now we perform a temporary transition from http-username and http-password to http_username and http_password
|
||||
const QVariant* oldUsername = valueForKeyPath(_configMap.getUserConfig(), "security.http-username");
|
||||
const QVariant* oldPassword = valueForKeyPath(_configMap.getUserConfig(), "security.http-password");
|
||||
// What settings version were we before and what are we using now?
|
||||
// Do we need to do any re-mapping?
|
||||
QSettings appSettings;
|
||||
const QString JSON_SETTINGS_VERSION_KEY = "json-settings/version";
|
||||
double oldVersion = appSettings.value(JSON_SETTINGS_VERSION_KEY, 0.0).toDouble();
|
||||
|
||||
if (oldUsername || oldPassword) {
|
||||
QVariantMap& settingsMap = *reinterpret_cast<QVariantMap*>(_configMap.getUserConfig()["security"].data());
|
||||
if (oldVersion != _descriptionVersion) {
|
||||
qDebug() << "Previous domain-server settings version was"
|
||||
<< QString::number(oldVersion, 'g', 8) << "and the new version is"
|
||||
<< QString::number(_descriptionVersion, 'g', 8) << "- checking if any re-mapping is required";
|
||||
|
||||
// remove old keys, move to new format
|
||||
if (oldUsername) {
|
||||
settingsMap["http_username"] = oldUsername->toString();
|
||||
settingsMap.remove("http-username");
|
||||
// we have a version mismatch - for now handle custom behaviour here since there are not many remappings
|
||||
if (oldVersion < 1.0) {
|
||||
// This was prior to the introduction of security.restricted_access
|
||||
// If the user has a list of allowed users then set their value for security.restricted_access to true
|
||||
|
||||
QVariant* allowedUsers = valueForKeyPath(_configMap.getMergedConfig(), ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
|
||||
if (allowedUsers
|
||||
&& allowedUsers->canConvert(QMetaType::QVariantList)
|
||||
&& reinterpret_cast<QVariantList*>(allowedUsers)->size() > 0) {
|
||||
|
||||
qDebug() << "Forcing security.restricted_access to TRUE since there was an"
|
||||
<< "existing list of allowed users.";
|
||||
|
||||
// In the pre-toggle system the user had a list of allowed users, so
|
||||
// we need to set security.restricted_access to true
|
||||
QVariant* restrictedAccess = valueForKeyPath(_configMap.getUserConfig(),
|
||||
RESTRICTED_ACCESS_SETTINGS_KEYPATH,
|
||||
true);
|
||||
|
||||
*restrictedAccess = QVariant(true);
|
||||
|
||||
// write the new settings to the json file
|
||||
persistToFile();
|
||||
|
||||
// reload the master and user config so that the merged config is right
|
||||
_configMap.loadMasterAndUserConfig(argumentList);
|
||||
}
|
||||
}
|
||||
|
||||
if (oldPassword) {
|
||||
settingsMap["http_password"] = oldPassword->toString();
|
||||
settingsMap.remove("http-password");
|
||||
}
|
||||
|
||||
// save the updated settings
|
||||
persistToFile();
|
||||
}
|
||||
|
||||
// write the current description version to our settings
|
||||
appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion);
|
||||
}
|
||||
|
||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) {
|
||||
|
|
|
@ -23,6 +23,9 @@ const QString SETTINGS_PATHS_KEY = "paths";
|
|||
const QString SETTINGS_PATH = "/settings";
|
||||
const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
|
||||
|
||||
const QString ALLOWED_USERS_SETTINGS_KEYPATH = "security.allowed_users";
|
||||
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
|
||||
|
||||
class DomainServerSettingsManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -44,6 +47,7 @@ private:
|
|||
QJsonObject settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName);
|
||||
void persistToFile();
|
||||
|
||||
double _descriptionVersion;
|
||||
QJsonArray _descriptionArray;
|
||||
HifiConfigVariantMap _configMap;
|
||||
};
|
||||
|
|
|
@ -198,7 +198,7 @@ function checkControllerSide(hand) {
|
|||
var closestEntity = Entities.findClosestEntity(hand.palmPosition(), CATCH_RADIUS);
|
||||
var modelUrl = Entities.getEntityProperties(closestEntity).modelURL;
|
||||
print("lol2"+closestEntity.isKnownID);
|
||||
if (closestEntity.isKnownID && validFrisbeeURL(Entities.getEntityProperties(closestEntity).modelURL)) {
|
||||
if (closestEntity && validFrisbeeURL(Entities.getEntityProperties(closestEntity).modelURL)) {
|
||||
print("lol");
|
||||
Entities.editEntity(closestEntity, {modelScale: 1, inHand: true, position: hand.holdPosition(), shouldDie: true});
|
||||
Entities.deleteEntity(closestEntity);
|
||||
|
@ -448,4 +448,4 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.update.connect(checkController);
|
||||
Script.update.connect(controlFrisbees);
|
||||
Script.update.connect(controlFrisbees);
|
||||
|
|
|
@ -386,13 +386,6 @@ MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitc
|
|||
Script.setTimeout(playLoadSound, 2000);
|
||||
|
||||
function update(deltaTime) {
|
||||
if (bulletID && !bulletID.isKnownID) {
|
||||
bulletID = Entities.identifyEntity(bulletID);
|
||||
}
|
||||
if (targetID && !targetID.isKnownID) {
|
||||
targetID = Entities.identifyEntity(targetID);
|
||||
}
|
||||
|
||||
if (activeControllers == 0) {
|
||||
if (Controller.getNumberOfSpatialControls() > 0) {
|
||||
activeControllers = Controller.getNumberOfSpatialControls();
|
||||
|
|
|
@ -132,12 +132,6 @@ function update(deltaTime) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!paddle.isKnownID) {
|
||||
paddle = Entities.identifyEntity(paddle);
|
||||
}
|
||||
if (!ball.isKnownID) {
|
||||
ball = Entities.identifyEntity(ball);
|
||||
} else {
|
||||
var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0));
|
||||
var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation);
|
||||
var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(),
|
||||
|
@ -157,7 +151,7 @@ function update(deltaTime) {
|
|||
Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)),
|
||||
velocity: Controller.getSpatialControlVelocity(controllerID),
|
||||
rotation: paddleWorldOrientation });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
|
|
|
@ -109,17 +109,10 @@ function checkControllerSide(whichSide) {
|
|||
var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5));
|
||||
|
||||
// If I don't currently have a ball in my hand, then try to catch closest one
|
||||
if (leftHandEntity && !leftHandEntity.isKnownID) {
|
||||
leftHandEntity = Entities.identifyEntity(leftHandEntity);
|
||||
}
|
||||
if (rightHandEntity && !rightHandEntity.isKnownID) {
|
||||
rightHandEntity = Entities.identifyEntity(rightHandEntity);
|
||||
}
|
||||
|
||||
if (!ballAlreadyInHand && grabButtonPressed) {
|
||||
var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);
|
||||
|
||||
if (closestEntity.isKnownID) {
|
||||
if (closestEntity) {
|
||||
var foundProperties = Entities.getEntityProperties(closestEntity);
|
||||
if (Vec3.length(foundProperties.velocity) > 0.0) {
|
||||
|
||||
|
|
|
@ -15,6 +15,6 @@ Script.load("controllers/hydra/hydraMove.js");
|
|||
Script.load("inspect.js");
|
||||
Script.load("lobby.js");
|
||||
Script.load("notifications.js");
|
||||
Script.load("look.js");
|
||||
Script.load("users.js");
|
||||
Script.load("grab.js");
|
||||
Script.load("pointer.js");
|
||||
|
|
|
@ -579,16 +579,6 @@ function findClickedEntity(event) {
|
|||
}
|
||||
|
||||
var foundEntity = result.entityID;
|
||||
|
||||
if (!foundEntity.isKnownID) {
|
||||
var identify = Entities.identifyEntity(foundEntity);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")");
|
||||
return null;
|
||||
}
|
||||
foundEntity = identify;
|
||||
}
|
||||
|
||||
return { pickRay: pickRay, entityID: foundEntity };
|
||||
}
|
||||
|
||||
|
@ -610,7 +600,7 @@ function mousePressEvent(event) {
|
|||
}
|
||||
}
|
||||
|
||||
var highlightedEntityID = { isKnownID: false };
|
||||
var highlightedEntityID = null;
|
||||
var mouseCapturedByTool = false;
|
||||
var lastMousePosition = null;
|
||||
var idleMouseTimerId = null;
|
||||
|
@ -625,9 +615,6 @@ function mouseMove(event) {
|
|||
mouseHasMovedSincePress = true;
|
||||
|
||||
if (placingEntityID) {
|
||||
if (!placingEntityID.isKnownID) {
|
||||
placingEntityID = Entities.identifyEntity(placingEntityID);
|
||||
}
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE;
|
||||
var offset = Vec3.multiply(distance, pickRay.direction);
|
||||
|
@ -664,9 +651,9 @@ function highlightEntityUnderCursor(position, accurateRay) {
|
|||
var pickRay = Camera.computePickRay(position.x, position.y);
|
||||
var entityIntersection = Entities.findRayIntersection(pickRay, accurateRay);
|
||||
if (entityIntersection.accurate) {
|
||||
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
|
||||
if(highlightedEntityID && highlightedEntityID != entityIntersection.entityID) {
|
||||
selectionDisplay.unhighlightSelectable(highlightedEntityID);
|
||||
highlightedEntityID = { id: -1, isKnownID: false };
|
||||
highlightedEntityID = { id: -1 };
|
||||
}
|
||||
|
||||
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
|
||||
|
@ -677,7 +664,7 @@ function highlightEntityUnderCursor(position, accurateRay) {
|
|||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (entityIntersection.entityID.isKnownID && sizeOK) {
|
||||
if (entityIntersection.entityID && sizeOK) {
|
||||
if (wantEntityGlow) {
|
||||
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
|
||||
}
|
||||
|
@ -736,7 +723,7 @@ function mouseClickEvent(event) {
|
|||
} else {
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
|
||||
print("Checking properties: " + properties.id + " " + " - Half Diagonal:" + halfDiagonal);
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
|
@ -775,7 +762,7 @@ function mouseClickEvent(event) {
|
|||
selectionManager.addEntity(foundEntity, true);
|
||||
}
|
||||
|
||||
print("Model selected: " + foundEntity.id);
|
||||
print("Model selected: " + foundEntity);
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
|
||||
if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) {
|
||||
|
@ -967,8 +954,8 @@ function deleteSelectedEntities() {
|
|||
var savedProperties = [];
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var entityID = SelectionManager.selections[i];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID.id];
|
||||
SelectionManager.savedProperties[entityID.id];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||
SelectionManager.savedProperties[entityID];
|
||||
savedProperties.push({
|
||||
entityID: entityID,
|
||||
properties: initialProperties
|
||||
|
@ -1127,8 +1114,8 @@ function applyEntityProperties(data) {
|
|||
var selectedEntityIDs = [];
|
||||
for (var i = 0; i < properties.length; i++) {
|
||||
var entityID = properties[i].entityID;
|
||||
if (DELETED_ENTITY_MAP[entityID.id] !== undefined) {
|
||||
entityID = DELETED_ENTITY_MAP[entityID.id];
|
||||
if (DELETED_ENTITY_MAP[entityID] !== undefined) {
|
||||
entityID = DELETED_ENTITY_MAP[entityID];
|
||||
}
|
||||
Entities.editEntity(entityID, properties[i].properties);
|
||||
selectedEntityIDs.push(entityID);
|
||||
|
@ -1137,15 +1124,15 @@ function applyEntityProperties(data) {
|
|||
var entityID = data.createEntities[i].entityID;
|
||||
var properties = data.createEntities[i].properties;
|
||||
var newEntityID = Entities.addEntity(properties);
|
||||
DELETED_ENTITY_MAP[entityID.id] = newEntityID;
|
||||
DELETED_ENTITY_MAP[entityID] = newEntityID;
|
||||
if (data.selectCreated) {
|
||||
selectedEntityIDs.push(newEntityID);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < data.deleteEntities.length; i++) {
|
||||
var entityID = data.deleteEntities[i].entityID;
|
||||
if (DELETED_ENTITY_MAP[entityID.id] !== undefined) {
|
||||
entityID = DELETED_ENTITY_MAP[entityID.id];
|
||||
if (DELETED_ENTITY_MAP[entityID] !== undefined) {
|
||||
entityID = DELETED_ENTITY_MAP[entityID];
|
||||
}
|
||||
Entities.deleteEntity(entityID);
|
||||
}
|
||||
|
@ -1170,7 +1157,7 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) {
|
|||
};
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var entityID = SelectionManager.selections[i];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID.id];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||
var currentProperties = Entities.getEntityProperties(entityID);
|
||||
undoData.setProperties.push({
|
||||
entityID: entityID,
|
||||
|
@ -1214,7 +1201,7 @@ PropertiesTool = function(opts) {
|
|||
var selections = [];
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var entity = {};
|
||||
entity.id = selectionManager.selections[i].id;
|
||||
entity.id = selectionManager.selections[i];
|
||||
entity.properties = Entities.getEntityProperties(selectionManager.selections[i]);
|
||||
entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation);
|
||||
selections.push(entity);
|
||||
|
@ -1264,7 +1251,7 @@ PropertiesTool = function(opts) {
|
|||
var dY = grid.getOrigin().y - (selectionManager.worldPosition.y - selectionManager.worldDimensions.y / 2),
|
||||
var diff = { x: 0, y: dY, z: 0 };
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i].id];
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i]];
|
||||
var newPosition = Vec3.sum(properties.position, diff);
|
||||
Entities.editEntity(selectionManager.selections[i], {
|
||||
position: newPosition,
|
||||
|
@ -1277,7 +1264,7 @@ PropertiesTool = function(opts) {
|
|||
if (selectionManager.hasSelection()) {
|
||||
selectionManager.saveProperties();
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i].id];
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i]];
|
||||
var bottomY = properties.boundingBox.center.y - properties.boundingBox.dimensions.y / 2;
|
||||
var dY = grid.getOrigin().y - bottomY;
|
||||
var diff = { x: 0, y: dY, z: 0 };
|
||||
|
@ -1293,7 +1280,7 @@ PropertiesTool = function(opts) {
|
|||
if (selectionManager.hasSelection()) {
|
||||
selectionManager.saveProperties();
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i].id];
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i]];
|
||||
var naturalDimensions = properties.naturalDimensions;
|
||||
|
||||
// If any of the natural dimensions are not 0, resize
|
||||
|
@ -1315,7 +1302,7 @@ PropertiesTool = function(opts) {
|
|||
if (selectionManager.hasSelection()) {
|
||||
selectionManager.saveProperties();
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i].id];
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i]];
|
||||
Entities.editEntity(selectionManager.selections[i], {
|
||||
dimensions: Vec3.multiply(multiplier, properties.dimensions),
|
||||
});
|
||||
|
@ -1327,7 +1314,7 @@ PropertiesTool = function(opts) {
|
|||
if (selectionManager.hasSelection()) {
|
||||
selectionManager.saveProperties();
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i].id];
|
||||
var properties = selectionManager.savedProperties[selectionManager.selections[i]];
|
||||
if (properties.type == "Zone") {
|
||||
var centerOfZone = properties.boundingBox.center;
|
||||
var atmosphereCenter = { x: centerOfZone.x,
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
// All callbacks start by updating the properties
|
||||
this.updateProperties = function(entityID) {
|
||||
// Piece ID
|
||||
if (this.entityID === null || !this.entityID.isKnownID) {
|
||||
this.entityID = Entities.identifyEntity(entityID);
|
||||
if (this.entityID === null) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
// Piece Properties
|
||||
this.properties = Entities.getEntityProperties(this.entityID);
|
||||
|
@ -27,12 +27,7 @@
|
|||
if (this.boardID === null) {
|
||||
// Read user data string and update boardID
|
||||
var userData = JSON.parse(this.properties.userData);
|
||||
var boardID = Entities.identifyEntity(userData.boardID);
|
||||
if (boardID.isKnownID) {
|
||||
this.boardID = boardID;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
this.boardID = userData.boardID;
|
||||
}
|
||||
|
||||
// Board User Data
|
||||
|
@ -52,13 +47,13 @@
|
|||
// Updates user data related objects
|
||||
this.updateUserData = function() {
|
||||
// Get board's user data
|
||||
if (this.boardID !== null && this.boardID.isKnownID) {
|
||||
if (this.boardID !== null) {
|
||||
this.boardUserData = this.getEntityUserData(this.boardID);
|
||||
|
||||
if (!(this.boardUserData &&
|
||||
this.boardUserData.firstTile &&
|
||||
this.boardUserData.tileSize)) {
|
||||
print("Incomplete boardUserData " + this.boardID.id);
|
||||
print("Incomplete boardUserData " + this.boardID);
|
||||
} else {
|
||||
this.FIRST_TILE = this.boardUserData.firstTile;
|
||||
this.TILE_SIZE = this.boardUserData.tileSize;
|
||||
|
@ -137,7 +132,7 @@
|
|||
for (var i = 0; i < others.length; i++) {
|
||||
var piece = others[i];
|
||||
|
||||
if (piece.id != this.entityID.id) {
|
||||
if (piece.id != this.entityID) {
|
||||
var properties = Entities.getEntityProperties(piece);
|
||||
|
||||
var isWhite = properties.modelURL.search("White") !== -1;
|
||||
|
@ -198,4 +193,4 @@
|
|||
this.updateProperties(entityID); // All callbacks start by updating the properties
|
||||
this.release(mouseEvent);
|
||||
};
|
||||
})
|
||||
})
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
}
|
||||
// All callbacks start by updating the properties
|
||||
this.updateProperties = function(entityID) {
|
||||
if (this.entityID === null || !this.entityID.isKnownID) {
|
||||
this.entityID = Entities.identifyEntity(entityID);
|
||||
if (this.entityID === null) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
this.properties = Entities.getEntityProperties(this.entityID);
|
||||
};
|
||||
|
|
|
@ -32,14 +32,14 @@
|
|||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
function didEntityExist(entityID) {
|
||||
return entityID && entityID.isKnownID;
|
||||
return entityID;
|
||||
}
|
||||
function doesEntityExistNow(entityID) {
|
||||
return entityID && getTrueID(entityID).isKnownID;
|
||||
return entityID;
|
||||
}
|
||||
function getTrueID(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
return { id: properties.id, creatorTokenID: properties.creatorTokenID, isKnownID: properties.isKnownID };
|
||||
return { id: properties.id };
|
||||
}
|
||||
function getUserData(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
|
@ -225,4 +225,4 @@
|
|||
this.updateRelativeLightPosition();
|
||||
}
|
||||
};
|
||||
})
|
||||
})
|
||||
|
|
|
@ -251,8 +251,8 @@
|
|||
};
|
||||
// All callbacks start by updating the properties
|
||||
this.updateProperties = function(entityID) {
|
||||
if (this.entityID === null || !this.entityID.isKnownID) {
|
||||
this.entityID = Entities.identifyEntity(entityID);
|
||||
if (this.entityID === null) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
this.properties = Entities.getEntityProperties(this.entityID);
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
var bird;
|
||||
|
||||
this.preload = function(entityID) {
|
||||
print("preload("+entityID.id+")");
|
||||
print("preload("+entityID+")");
|
||||
bird = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
|
||||
};
|
||||
|
||||
|
|
|
@ -22,17 +22,17 @@
|
|||
}
|
||||
|
||||
this.preload = function(entityID) {
|
||||
print("preload("+entityID.id+")");
|
||||
print("preload("+entityID+")");
|
||||
bird = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
|
||||
};
|
||||
|
||||
this.enterEntity = function(entityID) {
|
||||
print("enterEntity("+entityID.id+")");
|
||||
print("enterEntity("+entityID+")");
|
||||
playSound();
|
||||
};
|
||||
|
||||
this.leaveEntity = function(entityID) {
|
||||
print("leaveEntity("+entityID.id+")");
|
||||
print("leaveEntity("+entityID+")");
|
||||
playSound();
|
||||
};
|
||||
})
|
||||
|
|
|
@ -312,7 +312,7 @@
|
|||
this.indicator[i].position,
|
||||
this.indicator[i].scale / 2)) {
|
||||
clickedOnSeat = true;
|
||||
seat.model = this.entityID; // ??
|
||||
seat.model = this.entityID;
|
||||
seat.position = this.indicator[i].position;
|
||||
seat.rotation = this.indicator[i].orientation;
|
||||
}
|
||||
|
@ -333,8 +333,8 @@
|
|||
|
||||
// All callbacks start by updating the properties
|
||||
this.updateProperties = function(entityID) {
|
||||
if (this.entityID === null || !this.entityID.isKnownID) {
|
||||
this.entityID = Entities.identifyEntity(entityID);
|
||||
if (this.entityID === null) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
this.properties = Entities.getEntityProperties(this.entityID);
|
||||
};
|
||||
|
@ -369,4 +369,4 @@
|
|||
this.updateProperties(entityID); // All callbacks start by updating the properties
|
||||
};
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
@ -42,7 +42,6 @@ var originalProperties = {
|
|||
};
|
||||
|
||||
var modelID = Entities.addEntity(originalProperties);
|
||||
print("Entities.addEntity()... modelID.creatorTokenID = " + modelID.creatorTokenID);
|
||||
|
||||
var isPlaying = true;
|
||||
var playPauseEveryWhile = 360;
|
||||
|
@ -98,8 +97,6 @@ function moveModel(deltaTime) {
|
|||
|
||||
count++;
|
||||
|
||||
//print("modelID.creatorTokenID = " + modelID.creatorTokenID);
|
||||
|
||||
if (somethingChanged) {
|
||||
var newProperties = {
|
||||
animationIsPlaying: isPlaying,
|
||||
|
@ -123,7 +120,7 @@ Script.update.connect(moveModel);
|
|||
|
||||
Script.scriptEnding.connect(function () {
|
||||
print("cleaning up...");
|
||||
print("modelID="+ modelID.creatorTokenID + ", id:" + modelID.id);
|
||||
print("modelID=" + modelID);
|
||||
Models.deleteModel(modelID);
|
||||
});
|
||||
|
||||
|
|
|
@ -106,9 +106,6 @@ function updateButterflies(deltaTime) {
|
|||
var CHANCE_OF_IMPULSE = 0.04;
|
||||
for (var i = 0; i < numButterflies; i++) {
|
||||
if (Math.random() < CHANCE_OF_IMPULSE) {
|
||||
if (!butterflies[i].isKnownID) {
|
||||
butterflies[i] = Entities.identifyEntity(butterflies[i]);
|
||||
}
|
||||
var properties = Entities.getEntityProperties(butterflies[i]);
|
||||
if (Vec3.length(Vec3.subtract(properties.position, flockPosition)) > range) {
|
||||
Entities.editEntity(butterflies[i], { position: flockPosition } );
|
||||
|
|
|
@ -71,8 +71,6 @@ function moveEntity(deltaTime) {
|
|||
print("count =" + count);
|
||||
count++;
|
||||
|
||||
print("entityID.creatorTokenID = " + entityID.creatorTokenID);
|
||||
|
||||
var newProperties = {
|
||||
position: {
|
||||
x: originalProperties.position.x + (count * positionDelta.x),
|
||||
|
|
|
@ -70,8 +70,6 @@ function moveEntity(deltaTime) {
|
|||
print("count =" + count);
|
||||
count++;
|
||||
|
||||
print("entityID.creatorTokenID = " + entityID.creatorTokenID);
|
||||
|
||||
var newProperties = {
|
||||
position: {
|
||||
x: originalProperties.position.x + (count * positionDelta.x),
|
||||
|
|
|
@ -38,10 +38,6 @@ var moveSearch = { x: 0.1, y: 0, z: 0.1};
|
|||
var searchRadius = 1;
|
||||
var searchRadiusChange = 0;
|
||||
|
||||
print("entityA.creatorTokenID = " + entityA.creatorTokenID);
|
||||
print("entityB.creatorTokenID = " + entityB.creatorTokenID);
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
print("calling Entities.deleteEntity()");
|
||||
Entities.deleteEntity(entityA);
|
||||
|
@ -77,22 +73,6 @@ function findEntities(deltaTime) {
|
|||
print("--------------------------");
|
||||
print("iteration =" + iteration);
|
||||
iteration++;
|
||||
|
||||
// Check to see if we've been notified of the actual identity of the entities we created
|
||||
if (!entityA.isKnownID) {
|
||||
var identifyA = Entities.identifyEntity(entityA);
|
||||
if (identifyA.isKnownID) {
|
||||
entityA = identifyA;
|
||||
print(">>>> identified entityA.id = " + entityA.id);
|
||||
}
|
||||
}
|
||||
if (!entityB.isKnownID) {
|
||||
var identifyB = Entities.identifyEntity(entityB);
|
||||
if (identifyB.isKnownID) {
|
||||
entityB = identifyB;
|
||||
print(">>>> identified entityB.id = " + entityB.id);
|
||||
}
|
||||
}
|
||||
|
||||
// also check to see if we can "find" entities...
|
||||
print("searching for entities at:" + searchAt.x + ", " + searchAt.y + ", " + searchAt.z + " radius:" + searchRadius);
|
||||
|
|
|
@ -177,18 +177,11 @@ function updateBirds(deltaTime) {
|
|||
var averagePosition = { x: 0, y: 0, z: 0};
|
||||
var knownBirds = 0;
|
||||
for(var i =0; i < birdsInFlock; i++) {
|
||||
// identifyParticle() will check to see that the particle handle we have is in sync with the domain/server
|
||||
// context. If the handle is for a created particle that now has a known ID it will be updated to be a
|
||||
// handle with a known ID.
|
||||
birds[i].particle = Entities.identifyEntity(birds[i].particle);
|
||||
|
||||
if (birds[i].particle.isKnownID) {
|
||||
birds[i].properties = Entities.getEntityProperties(birds[i].particle);
|
||||
if (birds[i].properties.isKnownID) {
|
||||
knownBirds++;
|
||||
averageVelocity = Vec3.sum(averageVelocity, birds[i].properties.velocity);
|
||||
averagePosition = Vec3.sum(averagePosition, birds[i].properties.position);
|
||||
}
|
||||
birds[i].properties = Entities.getEntityProperties(birds[i].particle);
|
||||
if (birds[i].properties) {
|
||||
knownBirds++;
|
||||
averageVelocity = Vec3.sum(averageVelocity, birds[i].properties.velocity);
|
||||
averagePosition = Vec3.sum(averagePosition, birds[i].properties.position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +213,7 @@ function updateBirds(deltaTime) {
|
|||
|
||||
birds[i].thrust = { x: 0, y: 0, z: 0 }; // assume no thrust...
|
||||
|
||||
if (birds[i].particle.isKnownID) {
|
||||
if (birds[i].particle) {
|
||||
|
||||
if (enableFlyTowardPoints) {
|
||||
// if we're flying toward clusters, and the cluster changed, and this bird is flyingToward
|
||||
|
@ -400,7 +393,7 @@ function updateBirds(deltaTime) {
|
|||
for(var j =0; j < birdsInFlock; j++) {
|
||||
|
||||
// if this is not me, and a known bird, then check our position
|
||||
if (birds[i].properties.isKnownID && j != i) {
|
||||
if (birds[i].properties && j != i) {
|
||||
var positionMe = birds[i].properties.position;
|
||||
var positionYou = birds[j].properties.position;
|
||||
var awayFromYou = Vec3.subtract(positionMe, positionYou); // vector pointing away from "you"
|
||||
|
@ -428,7 +421,7 @@ function updateBirds(deltaTime) {
|
|||
|
||||
// iterate all birds again, apply their thrust
|
||||
for(var i =0; i < birdsInFlock; i++) {
|
||||
if (birds[i].particle.isKnownID) {
|
||||
if (birds[i].particle) {
|
||||
|
||||
var color;
|
||||
if (birds[i].gliding) {
|
||||
|
|
|
@ -29,10 +29,6 @@ function rideWithEntity(deltaTime) {
|
|||
if (iteration <= lengthOfRide) {
|
||||
|
||||
// Check to see if we've been notified of the actual identity of the entities we created
|
||||
if (!entityA.isKnownID) {
|
||||
entityA = Entities.identifyEntity(entityA);
|
||||
}
|
||||
|
||||
var propertiesA = Entities.getEntityProperties(entityA);
|
||||
var newPosition = propertiesA.position;
|
||||
MyAvatar.position = { x: propertiesA.position.x - 1,
|
||||
|
|
|
@ -392,6 +392,13 @@ function scriptEnding() {
|
|||
Overlays.deleteOverlay(spawnButton);
|
||||
Overlays.deleteOverlay(deleteButton);
|
||||
|
||||
|
||||
//We only want to delete everything if we've spawned them first.
|
||||
//Otherwise we'll throw an error- if we have edge1 we've spawned them all.
|
||||
if(!edge1){
|
||||
return;
|
||||
}
|
||||
|
||||
Entities.editEntity(edge1, {
|
||||
locked: false
|
||||
});
|
||||
|
@ -418,7 +425,6 @@ function scriptEnding() {
|
|||
});
|
||||
|
||||
|
||||
|
||||
Entities.deleteEntity(edge1);
|
||||
Entities.deleteEntity(edge2);
|
||||
Entities.deleteEntity(edge3a);
|
||||
|
|
|
@ -206,15 +206,9 @@ function keyPressEvent(event) {
|
|||
|
||||
function cleanup() {
|
||||
for (var i = 0; i < tableParts.length; i++) {
|
||||
if (!tableParts[i].isKnownID) {
|
||||
tableParts[i] = Entities.identifyEntity(tableParts[i]);
|
||||
}
|
||||
Entities.deleteEntity(tableParts[i]);
|
||||
}
|
||||
for (var i = 0; i < balls.length; i++) {
|
||||
if (!balls[i].isKnownID) {
|
||||
balls[i] = Entities.identifyEntity(balls[i]);
|
||||
}
|
||||
Entities.deleteEntity(balls[i]);
|
||||
}
|
||||
Overlays.deleteOverlay(reticle);
|
||||
|
@ -222,18 +216,13 @@ function cleanup() {
|
|||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
if (!cueBall.isKnownID) {
|
||||
cueBall = Entities.identifyEntity(cueBall);
|
||||
} else {
|
||||
// Check if cue ball has fallen off table, re-drop if so
|
||||
var cueProperties = Entities.getEntityProperties(cueBall);
|
||||
if (cueProperties.position.y < tableCenter.y) {
|
||||
// Replace the cueball
|
||||
Entities.editEntity(cueBall, { position: cuePosition } );
|
||||
// Check if cue ball has fallen off table, re-drop if so
|
||||
var cueProperties = Entities.getEntityProperties(cueBall);
|
||||
if (cueProperties.position.y < tableCenter.y) {
|
||||
// Replace the cueball
|
||||
Entities.editEntity(cueBall, { position: cuePosition } );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
|
|
|
@ -23,6 +23,8 @@ const GRAVITY = {x: 0, y: -2.8, z: 0};
|
|||
const DENSITY = 2000;
|
||||
const DAMPING_FACTOR = 0.98;
|
||||
const ANGULAR_DAMPING_FACTOR = 0.8;
|
||||
const FRICTION = 0.99;
|
||||
const RESTITUTION = 0.0;
|
||||
const SPAWN_DISTANCE = 3;
|
||||
const BLOCK_YAW_OFFSET = 45;
|
||||
const BUTTON_DIMENSIONS = {width: 49, height: 49};
|
||||
|
@ -110,6 +112,8 @@ function resetBlocks() {
|
|||
rotation: Quat.multiply(layerRotation, offsetRot),
|
||||
collisionsWillMove: true,
|
||||
damping: DAMPING_FACTOR,
|
||||
restitution: RESTITUTION,
|
||||
friction: FRICTION,
|
||||
angularDamping: ANGULAR_DAMPING_FACTOR,
|
||||
gravity: GRAVITY,
|
||||
density: DENSITY
|
||||
|
|
|
@ -157,7 +157,7 @@ ChessGame.Piece = (function(position, dimensions, url, rotation) {
|
|||
});
|
||||
// Build the user data string
|
||||
ChessGame.Piece.prototype.buildUserDataString = function() {
|
||||
if (!(ChessGame.board !== null || ChessGame.board.entity.isKnownID)) {
|
||||
if (!ChessGame.board) {
|
||||
print("ChessGame.Piece.prototype.buildUserDataString(): Called before proper initialization, bailing.");
|
||||
return null;
|
||||
}
|
||||
|
@ -200,9 +200,7 @@ ChessGame.scriptStarting = function() {
|
|||
}
|
||||
|
||||
ChessGame.update = function() {
|
||||
ChessGame.board.entity = Entities.identifyEntity(ChessGame.board.entity);
|
||||
|
||||
if (ChessGame.board.entity.isKnownID && ChessGame.pieces.length == 0) {
|
||||
if (ChessGame.board.entity && ChessGame.pieces.length == 0) {
|
||||
// Setup white pieces
|
||||
var isWhite = true;
|
||||
var row = 1;
|
||||
|
@ -260,4 +258,4 @@ ChessGame.scriptEnding = function() {
|
|||
|
||||
Script.scriptEnding.connect(ChessGame.scriptEnding);
|
||||
Script.update.connect(ChessGame.update);
|
||||
ChessGame.scriptStarting();
|
||||
ChessGame.scriptStarting();
|
||||
|
|
|
@ -183,8 +183,6 @@ function initializeInvaders() {
|
|||
modelURL: invaderModels[row].modelURL,
|
||||
lifetime: itemLifetimes
|
||||
});
|
||||
|
||||
print("invaders[row][column].creatorTokenID=" + invaders[row][column].creatorTokenID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +191,7 @@ function moveInvaders() {
|
|||
for (var row = 0; row < numberOfRows; row++) {
|
||||
for (var column = 0; column < invadersPerRow; column++) {
|
||||
props = Entities.getEntityProperties(invaders[row][column]);
|
||||
if (props.isKnownID) {
|
||||
if (props.id) {
|
||||
invaderPosition = getInvaderPosition(row, column);
|
||||
Entities.editEntity(invaders[row][column],
|
||||
{
|
||||
|
@ -292,7 +290,6 @@ function endGame() {
|
|||
}
|
||||
|
||||
function moveShipTo(position) {
|
||||
myShip = Entities.identifyEntity(myShip);
|
||||
Entities.editEntity(myShip, { position: position });
|
||||
}
|
||||
|
||||
|
@ -303,9 +300,8 @@ function fireMissile() {
|
|||
// If we've fired a missile, then check to see if it's still alive
|
||||
if (missileFired) {
|
||||
var missileProperties = Entities.getEntityProperties(myMissile);
|
||||
print("missileProperties.isKnownID=" + missileProperties.isKnownID);
|
||||
|
||||
if (!missileProperties.isKnownID) {
|
||||
if (!missileProperties) {
|
||||
print("canFire = true");
|
||||
canFire = true;
|
||||
}
|
||||
|
@ -374,24 +370,21 @@ Controller.captureKeyEvents({text: " "});
|
|||
function deleteIfInvader(possibleInvaderEntity) {
|
||||
for (var row = 0; row < numberOfRows; row++) {
|
||||
for (var column = 0; column < invadersPerRow; column++) {
|
||||
invaders[row][column] = Entities.identifyEntity(invaders[row][column]);
|
||||
if (invaders[row][column].isKnownID) {
|
||||
if (invaders[row][column].id == possibleInvaderEntity.id) {
|
||||
Entities.deleteEntity(possibleInvaderEntity);
|
||||
Entities.deleteEntity(myMissile);
|
||||
if (invaders[row][column].id && invaders[row][column].id == possibleInvaderEntity.id) {
|
||||
Entities.deleteEntity(possibleInvaderEntity);
|
||||
Entities.deleteEntity(myMissile);
|
||||
|
||||
// play the hit sound
|
||||
var options = {};
|
||||
if (soundInMyHead) {
|
||||
options.position = { x: MyAvatar.position.x + 0.0,
|
||||
y: MyAvatar.position.y + 0.1,
|
||||
z: MyAvatar.position.z + 0.0 };
|
||||
} else {
|
||||
options.position = getInvaderPosition(row, column);
|
||||
}
|
||||
|
||||
Audio.playSound(hitSound, options);
|
||||
// play the hit sound
|
||||
var options = {};
|
||||
if (soundInMyHead) {
|
||||
options.position = { x: MyAvatar.position.x + 0.0,
|
||||
y: MyAvatar.position.y + 0.1,
|
||||
z: MyAvatar.position.z + 0.0 };
|
||||
} else {
|
||||
options.position = getInvaderPosition(row, column);
|
||||
}
|
||||
|
||||
Audio.playSound(hitSound, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -402,7 +395,6 @@ function entityCollisionWithEntity(entityA, entityB, collision) {
|
|||
Vec3.print('entityCollisionWithEntity() penetration=', collision.penetration);
|
||||
Vec3.print('entityCollisionWithEntity() contactPoint=', collision.contactPoint);
|
||||
if (missileFired) {
|
||||
myMissile = Entities.identifyEntity(myMissile);
|
||||
if (myMissile.id == entityA.id) {
|
||||
deleteIfInvader(entityB);
|
||||
} else if (myMissile.id == entityB.id) {
|
||||
|
|
|
@ -42,9 +42,6 @@ function randomColor() {
|
|||
|
||||
function update(deltaTime) {
|
||||
time += deltaTime;
|
||||
if (!ball.isKnownID) {
|
||||
ball = Entities.identifyEntity(ball);
|
||||
}
|
||||
rotation = Quat.angleAxis(time * omega /Math.PI * 180.0, { x: 0, y: 1, z: 0 });
|
||||
Entities.editEntity(ball,
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
function onRowClicked(e) {
|
||||
var id = this.dataset.entityId;
|
||||
|
||||
var selection = [this.dataset.entityId];
|
||||
if (e.shiftKey) {
|
||||
selection = selection.concat(selectedEntities);
|
||||
|
|
|
@ -284,7 +284,7 @@
|
|||
var elModelOriginalTextures = document.getElementById("property-model-original-textures");
|
||||
|
||||
var elWebSections = document.querySelectorAll(".web-section");
|
||||
allSections.push(elModelSections);
|
||||
allSections.push(elWebSections);
|
||||
var elWebSourceURL = document.getElementById("property-web-source-url");
|
||||
|
||||
var elTextSections = document.querySelectorAll(".text-section");
|
||||
|
@ -481,7 +481,7 @@
|
|||
elModelTextures.value = properties.textures;
|
||||
elModelOriginalTextures.value = properties.originalTextures;
|
||||
} else if (properties.type == "Web") {
|
||||
for (var i = 0; i < elTextSections.length; i++) {
|
||||
for (var i = 0; i < elWebSections.length; i++) {
|
||||
elWebSections[i].style.display = 'block';
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,18 @@ var ALTITUDE_RATE = 200.0;
|
|||
var RADIUS_RATE = 1.0 / 100.0;
|
||||
var PAN_RATE = 50.0;
|
||||
|
||||
var Y_AXIS = { x: 0, y: 1, z: 0 };
|
||||
var X_AXIS = { x: 1, y: 0, z: 0 };
|
||||
var Y_AXIS = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var X_AXIS = {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var LOOK_AT_TIME = 500;
|
||||
|
||||
var alt = false;
|
||||
var shift = false;
|
||||
|
@ -34,6 +44,7 @@ var control = false;
|
|||
|
||||
var isActive = false;
|
||||
|
||||
var oldMode = Camera.mode;
|
||||
var noMode = 0;
|
||||
var orbitMode = 1;
|
||||
var radialMode = 2;
|
||||
|
@ -46,9 +57,21 @@ var mouseLastX = 0;
|
|||
var mouseLastY = 0;
|
||||
|
||||
|
||||
var center = { x: 0, y: 0, z: 0 };
|
||||
var position = { x: 0, y: 0, z: 0 };
|
||||
var vector = { x: 0, y: 0, z: 0 };
|
||||
var center = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
var position = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
var vector = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
var radius = 0.0;
|
||||
var azimuth = 0.0;
|
||||
var altitude = 0.0;
|
||||
|
@ -56,244 +79,262 @@ var altitude = 0.0;
|
|||
var avatarPosition;
|
||||
var avatarOrientation;
|
||||
|
||||
var rotatingTowardsTarget = false;
|
||||
var targetCamOrientation;
|
||||
var oldPosition, oldOrientation;
|
||||
|
||||
|
||||
function orientationOf(vector) {
|
||||
var direction,
|
||||
yaw,
|
||||
pitch;
|
||||
var direction,
|
||||
yaw,
|
||||
pitch;
|
||||
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
|
||||
function handleRadialMode(dx, dy) {
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
radius += radius * dy * RADIUS_RATE;
|
||||
if (radius < 1) {
|
||||
radius = 1;
|
||||
}
|
||||
|
||||
vector = { x: (Math.cos(altitude) * Math.cos(azimuth)) * radius,
|
||||
y: Math.sin(altitude) * radius,
|
||||
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius };
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
radius += radius * dy * RADIUS_RATE;
|
||||
if (radius < 1) {
|
||||
radius = 1;
|
||||
}
|
||||
|
||||
vector = {
|
||||
x: (Math.cos(altitude) * Math.cos(azimuth)) * radius,
|
||||
y: Math.sin(altitude) * radius,
|
||||
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius
|
||||
};
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
}
|
||||
|
||||
function handleOrbitMode(dx, dy) {
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
altitude += dy / ALTITUDE_RATE;
|
||||
if (altitude > PI / 2.0) {
|
||||
altitude = PI / 2.0;
|
||||
}
|
||||
if (altitude < -PI / 2.0) {
|
||||
altitude = -PI / 2.0;
|
||||
}
|
||||
|
||||
vector = { x:(Math.cos(altitude) * Math.cos(azimuth)) * radius,
|
||||
y:Math.sin(altitude) * radius,
|
||||
z:(Math.cos(altitude) * Math.sin(azimuth)) * radius };
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
altitude += dy / ALTITUDE_RATE;
|
||||
if (altitude > PI / 2.0) {
|
||||
altitude = PI / 2.0;
|
||||
}
|
||||
if (altitude < -PI / 2.0) {
|
||||
altitude = -PI / 2.0;
|
||||
}
|
||||
|
||||
vector = {
|
||||
x: (Math.cos(altitude) * Math.cos(azimuth)) * radius,
|
||||
y: Math.sin(altitude) * radius,
|
||||
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius
|
||||
};
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
}
|
||||
|
||||
|
||||
function handlePanMode(dx, dy) {
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
var distance = Vec3.length(vector);
|
||||
|
||||
var dv = Vec3.sum(Vec3.multiply(up, - distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE));
|
||||
|
||||
center = Vec3.sum(center, dv);
|
||||
position = Vec3.sum(position, dv);
|
||||
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
var up = Quat.getUp(Camera.getOrientation());
|
||||
var right = Quat.getRight(Camera.getOrientation());
|
||||
var distance = Vec3.length(vector);
|
||||
|
||||
var dv = Vec3.sum(Vec3.multiply(up, -distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE));
|
||||
|
||||
center = Vec3.sum(center, dv);
|
||||
position = Vec3.sum(position, dv);
|
||||
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
}
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.mode;
|
||||
var oldPosition = Camera.getPosition();
|
||||
Camera.mode = "independent";
|
||||
Camera.setPosition(oldPosition);
|
||||
oldMode = Camera.mode;
|
||||
oldPosition = Camera.getPosition();
|
||||
oldOrientation = Camera.getOrientation();
|
||||
|
||||
Camera.mode = "independent";
|
||||
Camera.setPosition(oldPosition);
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.mode = oldMode;
|
||||
Camera.mode = oldMode;
|
||||
Camera.setPosition(oldPosition);
|
||||
Camera.setOrientation(oldOrientation);
|
||||
}
|
||||
|
||||
function handleModes() {
|
||||
var newMode = (mode == noMode) ? noMode : detachedMode;
|
||||
if (alt) {
|
||||
if (control) {
|
||||
if (shift) {
|
||||
newMode = panningMode;
|
||||
} else {
|
||||
newMode = orbitMode;
|
||||
}
|
||||
} else {
|
||||
newMode = radialMode;
|
||||
}
|
||||
}
|
||||
|
||||
// if entering detachMode
|
||||
if (newMode == detachedMode && mode != detachedMode) {
|
||||
avatarPosition = MyAvatar.position;
|
||||
avatarOrientation = MyAvatar.orientation;
|
||||
}
|
||||
// if leaving detachMode
|
||||
if (mode == detachedMode && newMode == detachedMode &&
|
||||
(avatarPosition.x != MyAvatar.position.x ||
|
||||
avatarPosition.y != MyAvatar.position.y ||
|
||||
avatarPosition.z != MyAvatar.position.z ||
|
||||
avatarOrientation.x != MyAvatar.orientation.x ||
|
||||
avatarOrientation.y != MyAvatar.orientation.y ||
|
||||
avatarOrientation.z != MyAvatar.orientation.z ||
|
||||
avatarOrientation.w != MyAvatar.orientation.w)) {
|
||||
newMode = noMode;
|
||||
var newMode = (mode == noMode) ? noMode : detachedMode;
|
||||
if (alt) {
|
||||
if (control) {
|
||||
if (shift) {
|
||||
newMode = panningMode;
|
||||
} else {
|
||||
newMode = orbitMode;
|
||||
}
|
||||
} else {
|
||||
newMode = radialMode;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == noMode && newMode != noMode && Camera.mode == "independent") {
|
||||
newMode = noMode;
|
||||
}
|
||||
// if entering detachMode
|
||||
if (newMode == detachedMode && mode != detachedMode) {
|
||||
avatarPosition = MyAvatar.position;
|
||||
avatarOrientation = MyAvatar.orientation;
|
||||
}
|
||||
// if leaving detachMode
|
||||
if (mode == detachedMode && newMode == detachedMode &&
|
||||
(avatarPosition.x != MyAvatar.position.x ||
|
||||
avatarPosition.y != MyAvatar.position.y ||
|
||||
avatarPosition.z != MyAvatar.position.z ||
|
||||
avatarOrientation.x != MyAvatar.orientation.x ||
|
||||
avatarOrientation.y != MyAvatar.orientation.y ||
|
||||
avatarOrientation.z != MyAvatar.orientation.z ||
|
||||
avatarOrientation.w != MyAvatar.orientation.w)) {
|
||||
newMode = noMode;
|
||||
}
|
||||
|
||||
// if leaving noMode
|
||||
if (mode == noMode && newMode != noMode) {
|
||||
saveCameraState();
|
||||
}
|
||||
// if entering noMode
|
||||
if (newMode == noMode && mode != noMode) {
|
||||
restoreCameraState();
|
||||
}
|
||||
|
||||
mode = newMode;
|
||||
if (mode == noMode && newMode != noMode && Camera.mode == "independent") {
|
||||
newMode = noMode;
|
||||
}
|
||||
|
||||
// if leaving noMode
|
||||
if (mode == noMode && newMode != noMode) {
|
||||
saveCameraState();
|
||||
}
|
||||
// if entering noMode
|
||||
if (newMode == noMode && mode != noMode) {
|
||||
restoreCameraState();
|
||||
}
|
||||
|
||||
mode = newMode;
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
var changed = false;
|
||||
|
||||
if (event.text == "ALT") {
|
||||
alt = true;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
control = true;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
shift = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
handleModes();
|
||||
}
|
||||
var changed = false;
|
||||
|
||||
if (event.text == "ALT") {
|
||||
alt = true;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
control = true;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
shift = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
handleModes();
|
||||
}
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
var changed = false;
|
||||
|
||||
if (event.text == "ALT") {
|
||||
alt = false;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
control = false;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
shift = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
handleModes();
|
||||
}
|
||||
var changed = false;
|
||||
|
||||
if (event.text == "ALT") {
|
||||
alt = false;
|
||||
changed = true;
|
||||
mode = noMode;
|
||||
restoreCameraState();
|
||||
}
|
||||
if (event.text == "CONTROL") {
|
||||
control = false;
|
||||
changed = true;
|
||||
}
|
||||
if (event.text == "SHIFT") {
|
||||
shift = false;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
handleModes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (alt && !isActive) {
|
||||
mouseLastX = event.x;
|
||||
mouseLastY = event.y;
|
||||
|
||||
// Compute trajectories related values
|
||||
var pickRay = Camera.computePickRay(mouseLastX, mouseLastY);
|
||||
var modelIntersection = Entities.findRayIntersection(pickRay);
|
||||
|
||||
position = Camera.getPosition();
|
||||
|
||||
var avatarTarget = MyAvatar.getTargetAvatarPosition();
|
||||
|
||||
|
||||
var distance = -1;
|
||||
var string;
|
||||
|
||||
if (modelIntersection.intersects && modelIntersection.accurate) {
|
||||
distance = modelIntersection.distance;
|
||||
center = modelIntersection.properties.position;
|
||||
string = "Inspecting model";
|
||||
}
|
||||
|
||||
if ((distance == -1 || Vec3.length(Vec3.subtract(avatarTarget, position)) < distance) &&
|
||||
(avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0)) {
|
||||
distance = Vec3.length(Vec3.subtract(avatarTarget, position));
|
||||
center = avatarTarget;
|
||||
string = "Inspecting avatar";
|
||||
}
|
||||
|
||||
if (distance == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector = Vec3.subtract(position, center);
|
||||
radius = Vec3.length(vector);
|
||||
azimuth = Math.atan2(vector.z, vector.x);
|
||||
altitude = Math.asin(vector.y / Vec3.length(vector));
|
||||
|
||||
print(string);
|
||||
isActive = true;
|
||||
if (alt && !isActive) {
|
||||
mouseLastX = event.x;
|
||||
mouseLastY = event.y;
|
||||
|
||||
// Compute trajectories related values
|
||||
var pickRay = Camera.computePickRay(mouseLastX, mouseLastY);
|
||||
var modelIntersection = Entities.findRayIntersection(pickRay, true);
|
||||
|
||||
position = Camera.getPosition();
|
||||
|
||||
var avatarTarget = MyAvatar.getTargetAvatarPosition();
|
||||
|
||||
|
||||
var distance = -1;
|
||||
var string;
|
||||
|
||||
if (modelIntersection.intersects && modelIntersection.accurate) {
|
||||
distance = modelIntersection.distance;
|
||||
center = modelIntersection.intersection;
|
||||
string = "Inspecting model";
|
||||
//We've selected our target, now orbit towards it automatically
|
||||
rotatingTowardsTarget = true;
|
||||
//calculate our target cam rotation
|
||||
Script.setTimeout(function() {
|
||||
rotatingTowardsTarget = false;
|
||||
}, LOOK_AT_TIME);
|
||||
|
||||
vector = Vec3.subtract(position, center);
|
||||
targetCamOrientation = orientationOf(vector);
|
||||
radius = Vec3.length(vector);
|
||||
azimuth = Math.atan2(vector.z, vector.x);
|
||||
altitude = Math.asin(vector.y / Vec3.length(vector));
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (isActive) {
|
||||
isActive = false;
|
||||
}
|
||||
if (isActive) {
|
||||
isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isActive && mode != noMode) {
|
||||
if (mode == radialMode) {
|
||||
handleRadialMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
if (mode == orbitMode) {
|
||||
handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
if (mode == panningMode) {
|
||||
handlePanMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
|
||||
mouseLastX = event.x;
|
||||
mouseLastY = event.y;
|
||||
if (isActive && mode != noMode && !rotatingTowardsTarget) {
|
||||
if (mode == radialMode) {
|
||||
handleRadialMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
if (mode == orbitMode) {
|
||||
handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
if (mode == panningMode) {
|
||||
handlePanMode(event.x - mouseLastX, event.y - mouseLastY);
|
||||
}
|
||||
|
||||
}
|
||||
mouseLastX = event.x;
|
||||
mouseLastY = event.y;
|
||||
}
|
||||
|
||||
function update() {
|
||||
handleModes();
|
||||
handleModes();
|
||||
if (rotatingTowardsTarget) {
|
||||
rotateTowardsTarget();
|
||||
}
|
||||
}
|
||||
|
||||
function rotateTowardsTarget() {
|
||||
var newOrientation = Quat.mix(Camera.getOrientation(), targetCamOrientation, .1);
|
||||
Camera.setOrientation(newOrientation);
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
if (mode != noMode) {
|
||||
restoreCameraState();
|
||||
}
|
||||
if (mode != noMode) {
|
||||
restoreCameraState();
|
||||
}
|
||||
}
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
|
|
|
@ -21,7 +21,7 @@ EntityListTool = function(opts) {
|
|||
var selectedIDs = [];
|
||||
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
selectedIDs.push(selectionManager.selections[i].id);
|
||||
selectedIDs.push(selectionManager.selections[i]);
|
||||
}
|
||||
|
||||
data = {
|
||||
|
@ -38,7 +38,7 @@ EntityListTool = function(opts) {
|
|||
var id = ids[i];
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
entities.push({
|
||||
id: id.id,
|
||||
id: id,
|
||||
name: properties.name,
|
||||
type: properties.type,
|
||||
url: properties.type == "Model" ? properties.modelURL : "",
|
||||
|
@ -47,7 +47,7 @@ EntityListTool = function(opts) {
|
|||
|
||||
var selectedIDs = [];
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
selectedIDs.push(selectionManager.selections[i].id);
|
||||
selectedIDs.push(selectionManager.selections[i].id); // ?
|
||||
}
|
||||
|
||||
var data = {
|
||||
|
@ -64,11 +64,7 @@ EntityListTool = function(opts) {
|
|||
var ids = data.entityIds;
|
||||
var entityIDs = [];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
entityIDs.push({
|
||||
id: ids[i],
|
||||
isKnownID: true,
|
||||
creatorTokenID: 0,
|
||||
});
|
||||
entityIDs.push(ids[i]);
|
||||
}
|
||||
selectionManager.setSelections(entityIDs);
|
||||
if (data.focus) {
|
||||
|
|
|
@ -19,15 +19,8 @@ SPACE_WORLD = "world";
|
|||
SelectionManager = (function() {
|
||||
var that = {};
|
||||
|
||||
var PENDING_SELECTION_CHECK_INTERVAL = 50;
|
||||
|
||||
that.savedProperties = {};
|
||||
|
||||
that.selections = [];
|
||||
// These are selections that don't have a known ID yet
|
||||
that.pendingSelections = [];
|
||||
var pendingSelectionTimer = null;
|
||||
|
||||
var listeners = [];
|
||||
|
||||
that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
|
@ -45,7 +38,7 @@ SelectionManager = (function() {
|
|||
that.savedProperties = {};
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
var entityID = that.selections[i];
|
||||
that.savedProperties[entityID.id] = Entities.getEntityProperties(entityID);
|
||||
that.savedProperties[entityID] = Entities.getEntityProperties(entityID);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -61,41 +54,17 @@ SelectionManager = (function() {
|
|||
that.selections = [];
|
||||
for (var i = 0; i < entityIDs.length; i++) {
|
||||
var entityID = entityIDs[i];
|
||||
if (entityID.isKnownID) {
|
||||
that.selections.push(entityID);
|
||||
} else {
|
||||
that.pendingSelections.push(entityID);
|
||||
if (pendingSelectionTimer == null) {
|
||||
pendingSelectionTimer = Script.setInterval(that._checkPendingSelections, PENDING_SELECTION_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
that.selections.push(entityID);
|
||||
}
|
||||
|
||||
that._update();
|
||||
};
|
||||
|
||||
that._checkPendingSelections = function() {
|
||||
for (var i = 0; i < that.pendingSelections.length; i++) {
|
||||
var entityID = that.pendingSelections[i];
|
||||
var newEntityID = Entities.identifyEntity(entityID);
|
||||
if (newEntityID.isKnownID) {
|
||||
that.pendingSelections.splice(i, 1);
|
||||
that.addEntity(newEntityID);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (that.pendingSelections.length == 0) {
|
||||
Script.clearInterval(pendingSelectionTimer);
|
||||
pendingSelectionTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
that.addEntity = function(entityID, toggleSelection) {
|
||||
if (entityID.isKnownID) {
|
||||
if (entityID) {
|
||||
var idx = -1;
|
||||
for (var i = 0; i < that.selections.length; i++) {
|
||||
if (entityID.id == that.selections[i].id) {
|
||||
if (entityID == that.selections[i]) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
|
@ -105,14 +74,6 @@ SelectionManager = (function() {
|
|||
} else if (toggleSelection) {
|
||||
that.selections.splice(idx, 1);
|
||||
}
|
||||
} else {
|
||||
var idx = that.pendingSelections.indexOf(entityID);
|
||||
if (idx == -1) {
|
||||
that.pendingSelections.push(entityID);
|
||||
if (pendingSelectionTimer == null) {
|
||||
pendingSelectionTimer = Script.setInterval(that._checkPendingSelections, PENDING_SELECTION_CHECK_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that._update();
|
||||
|
@ -123,24 +84,11 @@ SelectionManager = (function() {
|
|||
if (idx >= 0) {
|
||||
that.selections.splice(idx, 1);
|
||||
}
|
||||
|
||||
var idx = that.pendingSelections.indexOf(entityID);
|
||||
if (idx >= 0) {
|
||||
that.pendingSelections.splice(idx, 1);
|
||||
}
|
||||
|
||||
that._update();
|
||||
};
|
||||
|
||||
that.clearSelections = function() {
|
||||
that.selections = [];
|
||||
that.pendingSelections = [];
|
||||
|
||||
if (pendingSelectionTimer !== null) {
|
||||
Script.clearInterval(pendingSelectionTimer);
|
||||
pendingSelectionTimer = null;
|
||||
}
|
||||
|
||||
that._update();
|
||||
};
|
||||
|
||||
|
@ -1213,7 +1161,7 @@ SelectionDisplay = (function () {
|
|||
rotation: rotation,
|
||||
visible: true,
|
||||
});
|
||||
var distance = (properties.dimensions.z / 2) * Math.tan(properties.cutoff * (Math.PI / 180));
|
||||
var distance = (properties.dimensions.z / 2) * Math.sin(properties.cutoff * (Math.PI / 180));
|
||||
|
||||
Overlays.editOverlay(grabberSpotLightL, {
|
||||
position: EdgeNL,
|
||||
|
@ -1563,7 +1511,7 @@ SelectionDisplay = (function () {
|
|||
var wantDebug = false;
|
||||
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var properties = SelectionManager.savedProperties[SelectionManager.selections[i].id];
|
||||
var properties = SelectionManager.savedProperties[SelectionManager.selections[i]];
|
||||
var newPosition = Vec3.sum(properties.position, { x: vector.x, y: 0, z: vector.z });
|
||||
Entities.editEntity(SelectionManager.selections[i], {
|
||||
position: newPosition,
|
||||
|
@ -1641,7 +1589,7 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var id = SelectionManager.selections[i];
|
||||
var properties = selectionManager.savedProperties[id.id];
|
||||
var properties = selectionManager.savedProperties[id];
|
||||
|
||||
var original = properties.position;
|
||||
var newPosition = Vec3.sum(properties.position, vector);
|
||||
|
@ -1900,7 +1848,7 @@ SelectionDisplay = (function () {
|
|||
vector = change;
|
||||
Vec3.print("Radius stretch: ", vector);
|
||||
var length = vector.x + vector.y + vector.z;
|
||||
var props = selectionManager.savedProperties[selectionManager.selections[0].id];
|
||||
var props = selectionManager.savedProperties[selectionManager.selections[0]];
|
||||
|
||||
var radius = props.dimensions.z / 2;
|
||||
var originalCutoff = props.cutoff;
|
||||
|
@ -1917,7 +1865,7 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
function radiusStretchFunc(vector, change) {
|
||||
var props = selectionManager.savedProperties[selectionManager.selections[0].id];
|
||||
var props = selectionManager.savedProperties[selectionManager.selections[0]];
|
||||
|
||||
// Find the axis being adjusted
|
||||
var size;
|
||||
|
@ -2084,7 +2032,7 @@ SelectionDisplay = (function () {
|
|||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var entityID = SelectionManager.selections[i];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
var initialProperties = SelectionManager.savedProperties[entityID.id];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||
|
||||
var newProperties = {
|
||||
rotation: Quat.multiply(yawChange, initialProperties.rotation),
|
||||
|
@ -2211,7 +2159,7 @@ SelectionDisplay = (function () {
|
|||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var entityID = SelectionManager.selections[i];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
var initialProperties = SelectionManager.savedProperties[entityID.id];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||
var dPos = Vec3.subtract(initialProperties.position, initialPosition);
|
||||
dPos = Vec3.multiplyQbyV(pitchChange, dPos);
|
||||
|
||||
|
@ -2331,7 +2279,7 @@ SelectionDisplay = (function () {
|
|||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
var entityID = SelectionManager.selections[i];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
var initialProperties = SelectionManager.savedProperties[entityID.id];
|
||||
var initialProperties = SelectionManager.savedProperties[entityID];
|
||||
var dPos = Vec3.subtract(initialProperties.position, initialPosition);
|
||||
dPos = Vec3.multiplyQbyV(rollChange, dPos);
|
||||
|
||||
|
|
|
@ -12,17 +12,17 @@ LightOverlayManager = function() {
|
|||
// List of overlays not currently being used
|
||||
var unusedOverlays = [];
|
||||
|
||||
// Map from EntityItemID.id to overlay id
|
||||
// Map from EntityItemID to overlay id
|
||||
var entityOverlays = {};
|
||||
|
||||
// Map from EntityItemID.id to EntityItemID object
|
||||
// Map from EntityItemID to EntityItemID object
|
||||
var entityIDs = {};
|
||||
|
||||
this.updatePositions = function(ids) {
|
||||
for (var id in entityIDs) {
|
||||
var entityID = entityIDs[id];
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
Overlays.editOverlay(entityOverlays[entityID.id], {
|
||||
Overlays.editOverlay(entityOverlays[entityID], {
|
||||
position: properties.position
|
||||
});
|
||||
}
|
||||
|
@ -77,10 +77,10 @@ LightOverlayManager = function() {
|
|||
|
||||
function addEntity(entityID) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
if (properties.type == "Light" && !(entityID.id in entityOverlays)) {
|
||||
if (properties.type == "Light" && !(entityID in entityOverlays)) {
|
||||
var overlay = getOverlay();
|
||||
entityOverlays[entityID.id] = overlay;
|
||||
entityIDs[entityID.id] = entityID;
|
||||
entityOverlays[entityID] = overlay;
|
||||
entityIDs[entityID] = entityID;
|
||||
Overlays.editOverlay(overlay, {
|
||||
position: properties.position,
|
||||
url: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL,
|
||||
|
@ -94,20 +94,12 @@ LightOverlayManager = function() {
|
|||
}
|
||||
|
||||
function deleteEntity(entityID) {
|
||||
if (entityID.id in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[entityID.id]);
|
||||
delete entityOverlays[entityID.id];
|
||||
if (entityID in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[entityID]);
|
||||
delete entityOverlays[entityID];
|
||||
}
|
||||
}
|
||||
|
||||
function changeEntityID(oldEntityID, newEntityID) {
|
||||
entityOverlays[newEntityID.id] = entityOverlays[oldEntityID.id];
|
||||
entityIDs[newEntityID.id] = newEntityID;
|
||||
|
||||
delete entityOverlays[oldEntityID.id];
|
||||
delete entityIDs[oldEntityID.id];
|
||||
}
|
||||
|
||||
function clearEntities() {
|
||||
for (var id in entityOverlays) {
|
||||
releaseOverlay(entityOverlays[id]);
|
||||
|
@ -117,7 +109,6 @@ LightOverlayManager = function() {
|
|||
}
|
||||
|
||||
Entities.addingEntity.connect(addEntity);
|
||||
Entities.changingEntityID.connect(changeEntityID);
|
||||
Entities.deletingEntity.connect(deleteEntity);
|
||||
Entities.clearingEntities.connect(clearEntities);
|
||||
|
||||
|
|
183
examples/look.js
183
examples/look.js
|
@ -1,183 +0,0 @@
|
|||
//
|
||||
// look.js
|
||||
// examples
|
||||
//
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller class
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var wantDebugging = false;
|
||||
|
||||
|
||||
// Configuration
|
||||
var TOUCH_YAW_SCALE = -0.25;
|
||||
var TOUCH_PITCH_SCALE = -12.5;
|
||||
var FIXED_TOUCH_TIMESTEP = 0.016;
|
||||
|
||||
var MOUSE_YAW_SCALE = -0.25;
|
||||
var MOUSE_PITCH_SCALE = -12.5;
|
||||
var FIXED_MOUSE_TIMESTEP = 0.016;
|
||||
|
||||
// Mouse Data
|
||||
var alwaysLook = false; // if you want the mouse look to happen only when you click, change this to false
|
||||
var isMouseDown = false;
|
||||
var lastTouchX = 0;
|
||||
var lastTouchY = 0;
|
||||
var yawFromTouch = 0;
|
||||
var pitchFromTouch = 0;
|
||||
|
||||
// Touch Data
|
||||
var TIME_BEFORE_GENERATED_END_TOUCH_EVENT = 50; // ms
|
||||
var startedTouching = false;
|
||||
var lastTouchEvent = 0;
|
||||
var lastMouseX = 0;
|
||||
var lastMouseY = 0;
|
||||
var yawFromMouse = 0;
|
||||
var pitchFromMouse = 0;
|
||||
|
||||
|
||||
// Mouse Events
|
||||
function mousePressEvent(event) {
|
||||
if (wantDebugging) {
|
||||
print("mousePressEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
if (event.isRightButton) {
|
||||
isMouseDown = true;
|
||||
lastMouseX = event.x;
|
||||
lastMouseY = event.y;
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (wantDebugging) {
|
||||
print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
isMouseDown = false;
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (wantDebugging) {
|
||||
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
|
||||
if (alwaysLook || isMouseDown) {
|
||||
yawFromMouse += ((event.x - lastMouseX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP);
|
||||
pitchFromMouse += ((event.y - lastMouseY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP);
|
||||
lastMouseX = event.x;
|
||||
lastMouseY = event.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Touch Events
|
||||
function touchBeginEvent(event) {
|
||||
if (wantDebugging) {
|
||||
print("touchBeginEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
lastTouchX = event.x;
|
||||
lastTouchY = event.y;
|
||||
yawFromTouch = 0;
|
||||
pitchFromTouch = 0;
|
||||
startedTouching = true;
|
||||
var d = new Date();
|
||||
lastTouchEvent = d.getTime();
|
||||
}
|
||||
|
||||
function touchEndEvent(event) {
|
||||
if (wantDebugging) {
|
||||
if (event) {
|
||||
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
|
||||
} else {
|
||||
print("touchEndEvent generated");
|
||||
}
|
||||
}
|
||||
startedTouching = false;
|
||||
}
|
||||
|
||||
function touchUpdateEvent(event) {
|
||||
// print("TOUCH UPDATE");
|
||||
if (wantDebugging) {
|
||||
print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y);
|
||||
}
|
||||
|
||||
if (!startedTouching) {
|
||||
// handle Qt 5.4.x bug where we get touch update without a touch begin event
|
||||
touchBeginEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
yawFromTouch += ((event.x - lastTouchX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP);
|
||||
pitchFromTouch += ((event.y - lastTouchY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP);
|
||||
lastTouchX = event.x;
|
||||
lastTouchY = event.y;
|
||||
var d = new Date();
|
||||
lastTouchEvent = d.getTime();
|
||||
}
|
||||
|
||||
|
||||
function update(deltaTime) {
|
||||
if (wantDebugging) {
|
||||
print("update()...");
|
||||
}
|
||||
|
||||
if (startedTouching) {
|
||||
var d = new Date();
|
||||
var sinceLastTouch = d.getTime() - lastTouchEvent;
|
||||
if (sinceLastTouch > TIME_BEFORE_GENERATED_END_TOUCH_EVENT) {
|
||||
touchEndEvent();
|
||||
}
|
||||
}
|
||||
|
||||
if (yawFromTouch != 0 || yawFromMouse != 0) {
|
||||
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromTouch + yawFromMouse, 0));
|
||||
|
||||
if (wantDebugging) {
|
||||
print("changing orientation"
|
||||
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","
|
||||
+ MyAvatar.orientation.z + "," + MyAvatar.orientation.w
|
||||
+ " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w);
|
||||
}
|
||||
|
||||
MyAvatar.orientation = newOrientation;
|
||||
yawFromTouch = 0;
|
||||
yawFromMouse = 0;
|
||||
}
|
||||
|
||||
if (pitchFromTouch != 0 || pitchFromMouse != 0) {
|
||||
var newPitch = MyAvatar.headPitch + pitchFromTouch + pitchFromMouse;
|
||||
|
||||
if (wantDebugging) {
|
||||
print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch);
|
||||
}
|
||||
|
||||
MyAvatar.headPitch = newPitch;
|
||||
pitchFromTouch = 0;
|
||||
pitchFromMouse = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map the mouse events to our functions
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
// Map the mouse events to our functions
|
||||
Controller.touchBeginEvent.connect(touchBeginEvent);
|
||||
Controller.touchUpdateEvent.connect(touchUpdateEvent);
|
||||
Controller.touchEndEvent.connect(touchEndEvent);
|
||||
|
||||
// disable the standard application for mouse events
|
||||
Controller.captureTouchEvents();
|
||||
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for mouse events
|
||||
Controller.releaseTouchEvents();
|
||||
}
|
||||
|
||||
// would be nice to change to update
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -1,77 +1,99 @@
|
|||
|
||||
var lineEntityID = null;
|
||||
var lineIsRezzed = false;
|
||||
|
||||
var altHeld = false;
|
||||
var lineCreated = false;
|
||||
|
||||
function nearLinePoint(targetPosition) {
|
||||
var handPosition = MyAvatar.getRightPalmPosition();
|
||||
var along = Vec3.subtract(targetPosition, handPosition);
|
||||
along = Vec3.normalize(along);
|
||||
along = Vec3.multiply(along, 0.4);
|
||||
return Vec3.sum(handPosition, along);
|
||||
var handPosition = MyAvatar.getRightPalmPosition();
|
||||
var along = Vec3.subtract(targetPosition, handPosition);
|
||||
along = Vec3.normalize(along);
|
||||
along = Vec3.multiply(along, 0.4);
|
||||
return Vec3.sum(handPosition, along);
|
||||
}
|
||||
|
||||
|
||||
function removeLine() {
|
||||
if (lineIsRezzed) {
|
||||
Entities.deleteEntity(lineEntityID);
|
||||
lineEntityID = null;
|
||||
lineIsRezzed = false;
|
||||
}
|
||||
if (lineIsRezzed) {
|
||||
Entities.deleteEntity(lineEntityID);
|
||||
lineEntityID = null;
|
||||
lineIsRezzed = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createOrUpdateLine(event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
var props = Entities.getEntityProperties(intersection.entityID);
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
var props = Entities.getEntityProperties(intersection.entityID);
|
||||
|
||||
if (intersection.intersects) {
|
||||
var dim = Vec3.subtract(intersection.intersection, nearLinePoint(intersection.intersection));
|
||||
if (lineIsRezzed) {
|
||||
Entities.editEntity(lineEntityID, {
|
||||
position: nearLinePoint(intersection.intersection),
|
||||
dimensions: dim,
|
||||
lifetime: 15 + props.lifespan // renew lifetime
|
||||
});
|
||||
} else {
|
||||
lineIsRezzed = true;
|
||||
lineEntityID = Entities.addEntity({
|
||||
type: "Line",
|
||||
position: nearLinePoint(intersection.intersection),
|
||||
dimensions: dim,
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
|
||||
});
|
||||
}
|
||||
if (intersection.intersects) {
|
||||
var dim = Vec3.subtract(intersection.intersection, nearLinePoint(intersection.intersection));
|
||||
if (lineIsRezzed) {
|
||||
Entities.editEntity(lineEntityID, {
|
||||
position: nearLinePoint(intersection.intersection),
|
||||
dimensions: dim,
|
||||
lifetime: 15 + props.lifespan // renew lifetime
|
||||
});
|
||||
} else {
|
||||
removeLine();
|
||||
lineIsRezzed = true;
|
||||
lineEntityID = Entities.addEntity({
|
||||
type: "Line",
|
||||
position: nearLinePoint(intersection.intersection),
|
||||
dimensions: dim,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
|
||||
});
|
||||
}
|
||||
} else {
|
||||
removeLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
createOrUpdateLine(event);
|
||||
}
|
||||
if (!event.isLeftButton || altHeld) {
|
||||
return;
|
||||
}
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
createOrUpdateLine(event);
|
||||
lineCreated = true;
|
||||
}
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
createOrUpdateLine(event);
|
||||
createOrUpdateLine(event);
|
||||
}
|
||||
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
|
||||
removeLine();
|
||||
if (!lineCreated) {
|
||||
return;
|
||||
}
|
||||
Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
|
||||
removeLine();
|
||||
lineCreated = false;
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.text == "ALT") {
|
||||
altHeld = true;
|
||||
}
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text == "ALT") {
|
||||
altHeld = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
|
@ -316,7 +316,7 @@ function addIndicators(modelID) {
|
|||
modelID.properties.sittingPoints[i].indicator = new SeatIndicator(modelID.properties, i);
|
||||
}
|
||||
|
||||
models[modelID.id] = modelID;
|
||||
models[modelID] = modelID;
|
||||
} else {
|
||||
Entities.editEntity(modelID, { glowLevel: 0.0 });
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ function removeIndicators(modelID) {
|
|||
for (var i = 0; i < modelID.properties.sittingPoints.length; ++i) {
|
||||
modelID.properties.sittingPoints[i].indicator.cleanup();
|
||||
}
|
||||
delete models[modelID.id];
|
||||
delete models[modelID];
|
||||
}
|
||||
|
||||
function showIndicators(doShow) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <glm/gtx/component_wise.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
// include this before QGLWidget, which includes an earlier version of OpenGL
|
||||
#include "InterfaceConfig.h"
|
||||
|
@ -339,7 +340,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_aboutToQuit(false),
|
||||
_notifiedPacketVersionMismatchThisDomain(false),
|
||||
_domainConnectionRefusals(QList<QString>()),
|
||||
_maxOctreePPS(maxOctreePacketsPerSecond.get())
|
||||
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
|
||||
_lastFaceTrackerUpdate(0)
|
||||
{
|
||||
setInstance(this);
|
||||
#ifdef Q_OS_WIN
|
||||
|
@ -978,12 +980,11 @@ void Application::showEditEntitiesHelp() {
|
|||
|
||||
void Application::resetCamerasOnResizeGL(Camera& camera, const glm::uvec2& size) {
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::configureCamera(camera, size.x, size.y);
|
||||
OculusManager::configureCamera(camera);
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
TV3DManager::configureCamera(camera, size.x, size.y);
|
||||
} else {
|
||||
camera.setAspectRatio((float)size.x / size.y);
|
||||
camera.setFieldOfView(_fieldOfView.get());
|
||||
camera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), (float)size.x / size.y, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -997,7 +998,7 @@ void Application::resizeGL() {
|
|||
renderSize = _glWidget->getDeviceSize() * getRenderResolutionScale();
|
||||
}
|
||||
if (_renderResolution == toGlm(renderSize)) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
_renderResolution = toGlm(renderSize);
|
||||
|
@ -1024,25 +1025,15 @@ void Application::updateProjectionMatrix() {
|
|||
}
|
||||
|
||||
void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum) {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
_projectionMatrix = camera.getProjection();
|
||||
|
||||
float left, right, bottom, top, nearVal, farVal;
|
||||
glm::vec4 nearClipPlane, farClipPlane;
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(glm::value_ptr(_projectionMatrix));
|
||||
|
||||
// Tell our viewFrustum about this change, using the application camera
|
||||
if (updateViewFrustum) {
|
||||
loadViewFrustum(camera, _viewFrustum);
|
||||
_viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
} else {
|
||||
ViewFrustum tempViewFrustum;
|
||||
loadViewFrustum(camera, tempViewFrustum);
|
||||
tempViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
}
|
||||
glFrustum(left, right, bottom, top, nearVal, farVal);
|
||||
|
||||
// save matrix
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&_projectionMatrix);
|
||||
}
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
@ -1263,6 +1254,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case Qt::Key_I:
|
||||
if (isShifted) {
|
||||
_myCamera.setEyeOffsetOrientation(glm::normalize(
|
||||
|
@ -1327,6 +1319,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
updateProjectionMatrix();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case Qt::Key_H:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
|
@ -2417,7 +2411,27 @@ void Application::update(float deltaTime) {
|
|||
FaceTracker* tracker = getActiveFaceTracker();
|
||||
if (tracker && !tracker->isMuted()) {
|
||||
tracker->update(deltaTime);
|
||||
|
||||
// Auto-mute microphone after losing face tracking?
|
||||
if (tracker->isTracking()) {
|
||||
_lastFaceTrackerUpdate = usecTimestampNow();
|
||||
} else {
|
||||
const quint64 MUTE_MICROPHONE_AFTER_USECS = 5000000; //5 secs
|
||||
Menu* menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !menu->isOptionChecked(MenuOption::MuteAudio)) {
|
||||
if (_lastFaceTrackerUpdate > 0
|
||||
&& ((usecTimestampNow() - _lastFaceTrackerUpdate) > MUTE_MICROPHONE_AFTER_USECS)) {
|
||||
menu->triggerOption(MenuOption::MuteAudio);
|
||||
_lastFaceTrackerUpdate = 0;
|
||||
}
|
||||
} else {
|
||||
_lastFaceTrackerUpdate = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_lastFaceTrackerUpdate = 0;
|
||||
}
|
||||
|
||||
SixenseManager::getInstance().update(deltaTime);
|
||||
JoystickScriptingInterface::getInstance().update();
|
||||
}
|
||||
|
@ -2470,7 +2484,6 @@ void Application::update(float deltaTime) {
|
|||
if (_physicsEngine.hasOutgoingChanges()) {
|
||||
_entitySimulation.lock();
|
||||
_entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID());
|
||||
_entitySimulation.handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
||||
_entitySimulation.unlock();
|
||||
|
||||
avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges());
|
||||
|
@ -2482,9 +2495,11 @@ void Application::update(float deltaTime) {
|
|||
|
||||
if (!_aboutToQuit) {
|
||||
PerformanceTimer perfTimer("entities");
|
||||
// NOTE: the _entities.update() call below will wait for lock
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk deadlock.)
|
||||
_entitySimulation.handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
||||
// NOTE: the _entities.update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
_entities.update(); // update the models...
|
||||
_entities.update(); // update the models...
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -2654,7 +2669,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
_octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
|
||||
_octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
|
||||
_octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
|
||||
_octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition());
|
||||
_octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
||||
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
|
||||
|
@ -2858,25 +2873,11 @@ QRect Application::getDesirableApplicationGeometry() {
|
|||
//
|
||||
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
||||
// We will use these below, from either the camera or head vectors calculated above
|
||||
glm::vec3 position(camera.getPosition());
|
||||
float fov = camera.getFieldOfView(); // degrees
|
||||
float nearClip = camera.getNearClip();
|
||||
float farClip = camera.getFarClip();
|
||||
float aspectRatio = camera.getAspectRatio();
|
||||
|
||||
glm::quat rotation = camera.getRotation();
|
||||
viewFrustum.setProjection(camera.getProjection());
|
||||
|
||||
// Set the viewFrustum up with the correct position and orientation of the camera
|
||||
viewFrustum.setPosition(position);
|
||||
viewFrustum.setOrientation(rotation);
|
||||
|
||||
// Also make sure it's got the correct lens details from the camera
|
||||
viewFrustum.setAspectRatio(aspectRatio);
|
||||
viewFrustum.setFieldOfView(fov); // degrees
|
||||
viewFrustum.setNearClip(nearClip);
|
||||
viewFrustum.setFarClip(farClip);
|
||||
viewFrustum.setEyeOffsetPosition(camera.getEyeOffsetPosition());
|
||||
viewFrustum.setEyeOffsetOrientation(camera.getEyeOffsetOrientation());
|
||||
viewFrustum.setPosition(camera.getPosition());
|
||||
viewFrustum.setOrientation(camera.getRotation());
|
||||
|
||||
// Ask the ViewFrustum class to calculate our corners
|
||||
viewFrustum.calculate();
|
||||
|
@ -2977,13 +2978,7 @@ void Application::updateShadowMap() {
|
|||
glm::vec3 shadowFrustumCenter = rotation * ((minima + maxima) * 0.5f);
|
||||
_shadowViewFrustum.setPosition(shadowFrustumCenter);
|
||||
_shadowViewFrustum.setOrientation(rotation);
|
||||
_shadowViewFrustum.setOrthographic(true);
|
||||
_shadowViewFrustum.setWidth(maxima.x - minima.x);
|
||||
_shadowViewFrustum.setHeight(maxima.y - minima.y);
|
||||
_shadowViewFrustum.setNearClip(minima.z);
|
||||
_shadowViewFrustum.setFarClip(maxima.z);
|
||||
_shadowViewFrustum.setEyeOffsetPosition(glm::vec3());
|
||||
_shadowViewFrustum.setEyeOffsetOrientation(glm::quat());
|
||||
_shadowViewFrustum.setProjection(glm::ortho(minima.x, maxima.x, minima.y, maxima.y, minima.z, maxima.z));
|
||||
_shadowViewFrustum.calculate();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
|
@ -3181,12 +3176,6 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
glm::vec3 eyeOffsetPos = theCamera.getEyeOffsetPosition();
|
||||
glm::quat eyeOffsetOrient = theCamera.getEyeOffsetOrientation();
|
||||
glm::vec3 eyeOffsetAxis = glm::axis(eyeOffsetOrient);
|
||||
glRotatef(-glm::degrees(glm::angle(eyeOffsetOrient)), eyeOffsetAxis.x, eyeOffsetAxis.y, eyeOffsetAxis.z);
|
||||
glTranslatef(-eyeOffsetPos.x, -eyeOffsetPos.y, -eyeOffsetPos.z);
|
||||
|
||||
// transform view according to theCamera
|
||||
// could be myCamera (if in normal mode)
|
||||
// or could be viewFrustumOffsetCamera if in offset mode
|
||||
|
@ -3204,8 +3193,6 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
Transform viewTransform;
|
||||
viewTransform.setTranslation(theCamera.getPosition());
|
||||
viewTransform.setRotation(rotation);
|
||||
viewTransform.postTranslate(eyeOffsetPos);
|
||||
viewTransform.postRotate(eyeOffsetOrient);
|
||||
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
viewTransform.setScale(Transform::Vec3(-1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
@ -3299,7 +3286,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
|
||||
// finally render the starfield
|
||||
if (hasStars) {
|
||||
_stars.render(theCamera.getFieldOfView(), theCamera.getAspectRatio(), theCamera.getNearClip(), alpha);
|
||||
_stars.render(_displayViewFrustum.getFieldOfView(), _displayViewFrustum.getAspectRatio(), _displayViewFrustum.getNearClip(), alpha);
|
||||
}
|
||||
|
||||
// draw the sky dome
|
||||
|
@ -3533,15 +3520,16 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
// Grab current viewport to reset it at the end
|
||||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
float aspect = (float)region.width() / region.height();
|
||||
float fov = MIRROR_FIELD_OF_VIEW;
|
||||
|
||||
// bool eyeRelativeCamera = false;
|
||||
if (billboard) {
|
||||
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
|
||||
fov = BILLBOARD_FIELD_OF_VIEW; // degees
|
||||
_mirrorCamera.setPosition(_myAvatar->getPosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * BILLBOARD_DISTANCE * _myAvatar->getScale());
|
||||
|
||||
} else if (RearMirrorTools::rearViewZoomLevel.get() == BODY) {
|
||||
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
|
||||
_mirrorCamera.setPosition(_myAvatar->getChestPosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
|
||||
|
||||
|
@ -3562,12 +3550,10 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
// This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further
|
||||
// investigated in order to adapt the technique while fixing the head rendering issue,
|
||||
// but the complexity of the hack suggests that a better approach
|
||||
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
|
||||
_mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
|
||||
}
|
||||
_mirrorCamera.setAspectRatio((float)region.width() / region.height());
|
||||
|
||||
_mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
||||
_mirrorCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
|
||||
|
||||
// set the bounds of rear mirror view
|
||||
|
|
|
@ -668,6 +668,8 @@ private:
|
|||
glm::uvec2 _renderResolution;
|
||||
|
||||
int _maxOctreePPS = DEFAULT_MAX_OCTREE_PPS;
|
||||
|
||||
quint64 _lastFaceTrackerUpdate;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -48,10 +48,7 @@ QString modeToString(CameraMode mode) {
|
|||
Camera::Camera() :
|
||||
_mode(CAMERA_MODE_THIRD_PERSON),
|
||||
_position(0.0f, 0.0f, 0.0f),
|
||||
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_aspectRatio(16.0f/9.0f),
|
||||
_nearClip(DEFAULT_NEAR_CLIP), // default
|
||||
_farClip(DEFAULT_FAR_CLIP), // default
|
||||
_projection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), 16.0f/9.0f, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)),
|
||||
_hmdPosition(),
|
||||
_hmdRotation(),
|
||||
_isKeepLookingAt(false),
|
||||
|
@ -91,32 +88,13 @@ void Camera::setHmdRotation(const glm::quat& hmdRotation) {
|
|||
}
|
||||
}
|
||||
|
||||
float Camera::getFarClip() const {
|
||||
return (_farClip < std::numeric_limits<int16_t>::max())
|
||||
? _farClip
|
||||
: std::numeric_limits<int16_t>::max() - 1;
|
||||
}
|
||||
|
||||
void Camera::setMode(CameraMode mode) {
|
||||
_mode = mode;
|
||||
emit modeUpdated(modeToString(mode));
|
||||
}
|
||||
|
||||
|
||||
void Camera::setFieldOfView(float f) {
|
||||
_fieldOfView = f;
|
||||
}
|
||||
|
||||
void Camera::setAspectRatio(float a) {
|
||||
_aspectRatio = a;
|
||||
}
|
||||
|
||||
void Camera::setNearClip(float n) {
|
||||
_nearClip = n;
|
||||
}
|
||||
|
||||
void Camera::setFarClip(float f) {
|
||||
_farClip = f;
|
||||
void Camera::setProjection(const glm::mat4& projection) {
|
||||
_projection = projection;
|
||||
}
|
||||
|
||||
PickRay Camera::computePickRay(float x, float y) {
|
||||
|
|
|
@ -44,35 +44,24 @@ public:
|
|||
void update( float deltaTime );
|
||||
|
||||
void setRotation(const glm::quat& rotation);
|
||||
void setProjection(const glm::mat4 & projection);
|
||||
void setHmdPosition(const glm::vec3& hmdPosition);
|
||||
void setHmdRotation(const glm::quat& hmdRotation);
|
||||
|
||||
void setMode(CameraMode m);
|
||||
void setFieldOfView(float f);
|
||||
void setAspectRatio(float a);
|
||||
void setNearClip(float n);
|
||||
void setFarClip(float f);
|
||||
void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; }
|
||||
void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; }
|
||||
|
||||
glm::quat getRotation() const { return _rotation * _hmdRotation; }
|
||||
const glm::mat4& getProjection() const { return _projection; }
|
||||
const glm::vec3& getHmdPosition() const { return _hmdPosition; }
|
||||
const glm::quat& getHmdRotation() const { return _hmdRotation; }
|
||||
|
||||
CameraMode getMode() const { return _mode; }
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
float getAspectRatio() const { return _aspectRatio; }
|
||||
float getNearClip() const { return _nearClip; }
|
||||
float getFarClip() const;
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
|
||||
public slots:
|
||||
QString getModeString() const;
|
||||
void setModeString(const QString& mode);
|
||||
|
||||
glm::vec3 getPosition() const { return _position + _hmdPosition; }
|
||||
void setPosition(const glm::vec3& position);
|
||||
|
||||
|
||||
void setOrientation(const glm::quat& orientation) { setRotation(orientation); }
|
||||
glm::quat getOrientation() const { return getRotation(); }
|
||||
|
||||
|
@ -95,13 +84,8 @@ signals:
|
|||
private:
|
||||
CameraMode _mode;
|
||||
glm::vec3 _position;
|
||||
float _fieldOfView; // degrees
|
||||
float _aspectRatio;
|
||||
float _nearClip;
|
||||
float _farClip;
|
||||
glm::vec3 _eyeOffsetPosition;
|
||||
glm::quat _eyeOffsetOrientation;
|
||||
glm::quat _rotation;
|
||||
glm::mat4 _projection;
|
||||
glm::vec3 _hmdPosition;
|
||||
glm::quat _hmdRotation;
|
||||
bool _isKeepLookingAt;
|
||||
|
|
|
@ -82,11 +82,6 @@ void DatagramProcessor::processDatagrams() {
|
|||
|
||||
break;
|
||||
}
|
||||
case PacketTypeEntityAddResponse:
|
||||
// this will keep creatorTokenIDs to IDs mapped correctly
|
||||
EntityItemID::handleAddEntityResponse(incomingPacket);
|
||||
application->getEntities()->getTree()->handleAddEntityResponse(incomingPacket);
|
||||
break;
|
||||
case PacketTypeEntityData:
|
||||
case PacketTypeEntityErase:
|
||||
case PacketTypeOctreeStats:
|
||||
|
|
|
@ -407,6 +407,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default
|
||||
qApp, SLOT(toggleFaceTrackerMute()));
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true);
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
|
|
@ -147,6 +147,7 @@ namespace MenuOption {
|
|||
const QString AudioScopeTwentyFrames = "Twenty";
|
||||
const QString AudioStats = "Audio Stats";
|
||||
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
||||
const QString AutoMuteAudio = "Auto Mute Microphone";
|
||||
const QString AvatarReceiveStats = "Show Receive Stats";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BinaryEyelidControl = "Binary Eyelid Control";
|
||||
|
|
|
@ -862,7 +862,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FRONT;
|
||||
glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition();
|
||||
|
||||
float smallestAngleTo = glm::radians(Application::getInstance()->getCamera()->getFieldOfView()) / 2.0f;
|
||||
float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f;
|
||||
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
|
||||
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ static const QString DDE_PROGRAM_PATH = "/dde.app/Contents/MacOS/dde";
|
|||
static const QStringList DDE_ARGUMENTS = QStringList()
|
||||
<< "--udp=" + DDE_SERVER_ADDR.toString() + ":" + QString::number(DDE_SERVER_PORT)
|
||||
<< "--receiver=" + QString::number(DDE_CONTROL_PORT)
|
||||
<< "--facedet_interval=500" // ms
|
||||
<< "--headless";
|
||||
|
||||
static const int NUM_EXPRESSIONS = 46;
|
||||
|
@ -293,6 +294,10 @@ void DdeFaceTracker::reset() {
|
|||
}
|
||||
|
||||
bool DdeFaceTracker::isActive() const {
|
||||
return (_ddeProcess != NULL);
|
||||
}
|
||||
|
||||
bool DdeFaceTracker::isTracking() const {
|
||||
static const quint64 ACTIVE_TIMEOUT_USECS = 3000000; //3 secs
|
||||
return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
virtual void reset();
|
||||
|
||||
virtual bool isActive() const;
|
||||
virtual bool isTracking() const { return isActive(); }
|
||||
virtual bool isTracking() const;
|
||||
|
||||
float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
|
||||
float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
|
||||
|
|
|
@ -184,7 +184,7 @@ void OculusManager::connect() {
|
|||
|
||||
if (!_camera) {
|
||||
_camera = new Camera;
|
||||
configureCamera(*_camera, 0, 0); // no need to use screen dimensions; they're ignored
|
||||
configureCamera(*_camera); // no need to use screen dimensions; they're ignored
|
||||
}
|
||||
#ifdef OVR_CLIENT_DISTORTION
|
||||
if (!_programInitialized) {
|
||||
|
@ -449,9 +449,19 @@ void OculusManager::endFrameTiming() {
|
|||
}
|
||||
|
||||
//Sets the camera FoV and aspect ratio
|
||||
void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {
|
||||
camera.setAspectRatio(_renderTargetSize.w * 0.5f / _renderTargetSize.h);
|
||||
camera.setFieldOfView(atan(_eyeFov[0].UpTan) * DEGREES_PER_RADIAN * 2.0f);
|
||||
void OculusManager::configureCamera(Camera& camera) {
|
||||
ovrFovPort fov;
|
||||
if (_activeEye == ovrEye_Count) {
|
||||
// When not rendering, provide a FOV encompasing both eyes
|
||||
fov = _eyeFov[0];
|
||||
fov.RightTan = _eyeFov[1].RightTan;
|
||||
} else {
|
||||
// When rendering, provide the exact FOV
|
||||
fov = _eyeFov[_activeEye];
|
||||
}
|
||||
// Convert the FOV to the correct projection matrix
|
||||
glm::mat4 projection = toGlm(ovrMatrix4f_Projection(fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded));
|
||||
camera.setProjection(projection);
|
||||
}
|
||||
|
||||
//Displays everything for the oculus, frame timing must be active
|
||||
|
@ -538,7 +548,8 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
|
|||
|
||||
glm::quat orientation;
|
||||
glm::vec3 trackerPosition;
|
||||
|
||||
auto deviceSize = qApp->getDeviceSize();
|
||||
|
||||
ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());
|
||||
ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position;
|
||||
|
||||
|
@ -576,9 +587,11 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
|
|||
whichCamera.setHmdPosition(trackerPosition);
|
||||
whichCamera.setHmdRotation(orientation);
|
||||
|
||||
|
||||
// Update our camera to what the application camera is doing
|
||||
_camera->setRotation(whichCamera.getRotation());
|
||||
_camera->setPosition(whichCamera.getPosition());
|
||||
configureCamera(*_camera);
|
||||
|
||||
// Store the latest left and right eye render locations for things that need to know
|
||||
glm::vec3 thisEyePosition = position + trackerPosition +
|
||||
|
@ -589,25 +602,17 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
|
|||
_camera->update(1.0f / Application::getInstance()->getFps());
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
const ovrFovPort& port = _eyeFov[_activeEye];
|
||||
float nearClip = whichCamera.getNearClip(), farClip = whichCamera.getFarClip();
|
||||
glFrustum(-nearClip * port.LeftTan, nearClip * port.RightTan, -nearClip * port.DownTan,
|
||||
nearClip * port.UpTan, nearClip, farClip);
|
||||
|
||||
ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
|
||||
vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
|
||||
vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
|
||||
|
||||
glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
|
||||
glLoadMatrixf(glm::value_ptr(_camera->getProjection()));
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
// HACK: instead of passing the stereo eye offset directly in the matrix, pass it in the camera offset
|
||||
//glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z);
|
||||
ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport;
|
||||
vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale;
|
||||
vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale;
|
||||
|
||||
glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h);
|
||||
|
||||
_camera->setEyeOffsetPosition(glm::vec3(-_eyeRenderDesc[eye].HmdToEyeViewOffset.x, -_eyeRenderDesc[eye].HmdToEyeViewOffset.y, -_eyeRenderDesc[eye].HmdToEyeViewOffset.z));
|
||||
qApp->displaySide(*_camera, false, RenderArgs::MONO);
|
||||
qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera);
|
||||
});
|
||||
|
@ -630,7 +635,6 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
|
|||
glPopMatrix();
|
||||
|
||||
// restore our normal viewport
|
||||
auto deviceSize = qApp->getDeviceSize();
|
||||
glViewport(0, 0, deviceSize.width(), deviceSize.height());
|
||||
|
||||
#if 0
|
||||
|
@ -644,16 +648,22 @@ void OculusManager::display(QGLWidget * glCanvas, const glm::quat &bodyOrientati
|
|||
//Wait till time-warp to reduce latency
|
||||
ovr_WaitTillTime(_hmdFrameTiming.TimewarpPointSeconds);
|
||||
|
||||
#ifdef DEBUG_RENDER_WITHOUT_DISTORTION
|
||||
auto fboSize = finalFbo->getSize();
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo));
|
||||
glBlitFramebuffer(
|
||||
0, 0, fboSize.x, fboSize.y,
|
||||
0, 0, deviceSize.width(), deviceSize.height(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
#else
|
||||
//Clear the color buffer to ensure that there isnt any residual color
|
||||
//Left over from when OR was not connected.
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(finalFbo->getRenderBuffer(0)));
|
||||
|
||||
//Renders the distorted mesh onto the screen
|
||||
renderDistortionMesh(eyeRenderPose);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
#endif
|
||||
glCanvas->swapBuffers();
|
||||
|
||||
#else
|
||||
|
|
|
@ -60,7 +60,7 @@ public:
|
|||
static void beginFrameTiming();
|
||||
static void endFrameTiming();
|
||||
static bool allowSwap();
|
||||
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
|
||||
static void configureCamera(Camera& camera);
|
||||
static void display(QGLWidget * glCanvas, const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera);
|
||||
static void reset();
|
||||
|
||||
|
|
|
@ -43,9 +43,9 @@ void TV3DManager::connect() {
|
|||
void TV3DManager::setFrustum(Camera& whichCamera) {
|
||||
const double DTR = 0.0174532925; // degree to radians
|
||||
const double IOD = 0.05; //intraocular distance
|
||||
double fovy = whichCamera.getFieldOfView(); // field of view in y-axis
|
||||
double nearZ = whichCamera.getNearClip(); // near clipping plane
|
||||
double screenZ = Application::getInstance()->getViewFrustum()->getFocalLength(); // screen projection plane
|
||||
double fovy = DEFAULT_FIELD_OF_VIEW_DEGREES; // field of view in y-axis
|
||||
double nearZ = DEFAULT_NEAR_CLIP; // near clipping plane
|
||||
double screenZ = 0.25f; // screen projection plane
|
||||
|
||||
double top = nearZ * tan(DTR * fovy / 2.0); //sets top of frustum based on fovy and near clipping plane
|
||||
double right = _aspect * top; // sets right of frustum based on aspect ratio
|
||||
|
@ -81,8 +81,8 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre
|
|||
}
|
||||
|
||||
void TV3DManager::display(Camera& whichCamera) {
|
||||
double nearZ = whichCamera.getNearClip(); // near clipping plane
|
||||
double farZ = whichCamera.getFarClip(); // far clipping plane
|
||||
double nearZ = DEFAULT_NEAR_CLIP; // near clipping plane
|
||||
double farZ = DEFAULT_FAR_CLIP; // far clipping plane
|
||||
|
||||
// left eye portal
|
||||
int portalX = 0;
|
||||
|
@ -95,7 +95,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
|
||||
DependencyManager::get<GlowEffect>()->prepare();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
Camera eyeCamera;
|
||||
eyeCamera.setRotation(whichCamera.getRotation());
|
||||
eyeCamera.setPosition(whichCamera.getPosition());
|
||||
|
@ -118,7 +118,6 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0));
|
||||
qApp->displaySide(eyeCamera, false, RenderArgs::MONO);
|
||||
qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov);
|
||||
_activeEye = NULL;
|
||||
|
|
|
@ -979,6 +979,14 @@ void ApplicationOverlay::renderStatsAndLogs() {
|
|||
glLineWidth(1.0f);
|
||||
glPointSize(1.0f);
|
||||
|
||||
// Determine whether to compute timing details
|
||||
bool shouldDisplayTimingDetail = Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
|
||||
Stats::getInstance()->isExpanded();
|
||||
if (shouldDisplayTimingDetail != PerformanceTimer::isActive()) {
|
||||
PerformanceTimer::setActive(shouldDisplayTimingDetail);
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
// let's set horizontal offset to give stats some margin to mirror
|
||||
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
|
||||
|
|
|
@ -59,6 +59,7 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
|
|||
connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)),
|
||||
this, SLOT(handleEvalutationFinished(QScriptValue, bool)));
|
||||
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
|
||||
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
|
||||
|
||||
resizeTextInput();
|
||||
}
|
||||
|
@ -96,6 +97,10 @@ void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException)
|
|||
appendMessage(gutter, resultStr);
|
||||
}
|
||||
|
||||
void JSConsole::handleError(const QString& message) {
|
||||
appendMessage(GUTTER_ERROR, "<span style='" + RESULT_ERROR_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
|
||||
}
|
||||
|
||||
void JSConsole::handlePrint(const QString& message) {
|
||||
appendMessage("", message);
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ protected slots:
|
|||
void resizeTextInput();
|
||||
void handleEvalutationFinished(QScriptValue result, bool isException);
|
||||
void handlePrint(const QString& message);
|
||||
void handleError(const QString& message);
|
||||
|
||||
private:
|
||||
void appendMessage(const QString& gutter, const QString& message);
|
||||
|
|
|
@ -224,9 +224,10 @@ void Stats::display(
|
|||
lines = 5;
|
||||
int columnOneWidth = _generalStatsWidth;
|
||||
|
||||
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
|
||||
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
|
||||
bool performanceTimerIsActive = PerformanceTimer::isActive();
|
||||
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
||||
if (displayPerf && performanceTimerIsActive) {
|
||||
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
|
||||
|
||||
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide...
|
||||
// we will also include room for 1 line per timing record and a header of 4 lines
|
||||
|
@ -276,7 +277,7 @@ void Stats::display(
|
|||
|
||||
|
||||
// TODO: the display of these timing details should all be moved to JavaScript
|
||||
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) {
|
||||
if (displayPerf && performanceTimerIsActive) {
|
||||
bool onlyDisplayTopTen = Menu::getInstance()->isOptionChecked(MenuOption::OnlyDisplayTopTen);
|
||||
// Timing details...
|
||||
verticalOffset += STATS_PELS_PER_LINE * 4; // skip 3 lines to be under the other columns
|
||||
|
|
|
@ -27,6 +27,8 @@ public:
|
|||
static void drawBackground(unsigned int rgba, int x, int y, int width, int height);
|
||||
|
||||
void toggleExpanded();
|
||||
bool isExpanded() { return _expanded; }
|
||||
|
||||
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
|
||||
void resetWidth(int width, int horizontalOffset);
|
||||
void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond,
|
||||
|
|
|
@ -66,8 +66,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory)
|
||||
|
||||
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
}
|
||||
|
||||
EntityTreeRenderer::~EntityTreeRenderer() {
|
||||
|
@ -961,7 +961,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
|||
}
|
||||
|
||||
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID();
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
||||
_lastMouseEventValid = true;
|
||||
}
|
||||
|
@ -1041,7 +1041,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
|||
currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs);
|
||||
}
|
||||
|
||||
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1174,8 +1174,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
if (!_tree || _shuttingDown) {
|
||||
return;
|
||||
}
|
||||
// Don't respond to small continuous contacts. It causes deadlocks when locking the entityTree.
|
||||
// Note that any entity script is likely to Entities.getEntityProperties(), which locks the tree.
|
||||
// Don't respond to small continuous contacts.
|
||||
const float COLLISION_MINUMUM_PENETRATION = 0.005;
|
||||
if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) {
|
||||
return;
|
||||
|
@ -1183,16 +1182,9 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
|
||||
// See if we should play sounds
|
||||
EntityTree* entityTree = static_cast<EntityTree*>(_tree);
|
||||
if (!entityTree->tryLockForRead()) {
|
||||
// I don't know why this can happen, but if it does,
|
||||
// the consequences are a deadlock, so bail.
|
||||
qCDebug(entitiesrenderer) << "NOTICE: skipping collision type " << collision.type << " penetration " << glm::length(collision.penetration);
|
||||
return;
|
||||
}
|
||||
const QUuid& myNodeID = DependencyManager::get<NodeList>()->getSessionUUID();
|
||||
playEntityCollisionSound(myNodeID, entityTree, idA, collision);
|
||||
playEntityCollisionSound(myNodeID, entityTree, idB, collision);
|
||||
entityTree->unlock();
|
||||
|
||||
// And now the entity scripts
|
||||
QScriptValue entityScriptA = loadEntityScript(idA);
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "EntityTreeRenderer.h"
|
||||
|
||||
const int FIXED_FONT_POINT_SIZE = 40;
|
||||
const float DPI = 30.47;
|
||||
const float METERS_TO_INCHES = 39.3701;
|
||||
|
||||
|
@ -99,7 +98,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (intersection.entityID.id == getID()) {
|
||||
if (intersection.entityID == getID()) {
|
||||
if (event->button() == Qt::MouseButton::RightButton) {
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||
|
@ -161,7 +160,6 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
float alpha = 1.0f;
|
||||
static const glm::vec2 texMin(0);
|
||||
static const glm::vec2 texMax(1);
|
||||
glm::vec2 topLeft(-halfDimensions.x, -halfDimensions.y);
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type,
|
||||
unsigned char* editBuffer, size_t length, int clockSkew) {
|
||||
|
||||
if (type == PacketTypeEntityAddOrEdit) {
|
||||
if (type == PacketTypeEntityAdd || type == PacketTypeEntityEdit) {
|
||||
EntityItem::adjustEditPacketForClockSkew(editBuffer, length, clockSkew);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,7 @@ bool EntityItem::_sendPhysicsUpdates = true;
|
|||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||
_type(EntityTypes::Unknown),
|
||||
_id(entityItemID.id),
|
||||
_creatorTokenID(entityItemID.creatorTokenID),
|
||||
_newlyCreated(false),
|
||||
_id(entityItemID),
|
||||
_lastSimulated(0),
|
||||
_lastUpdated(0),
|
||||
_lastEdited(0),
|
||||
|
@ -351,8 +349,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// id
|
||||
QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size
|
||||
_id = QUuid::fromRfc4122(encodedID);
|
||||
_creatorTokenID = UNKNOWN_ENTITY_TOKEN; // if we know the id, then we don't care about the creator token
|
||||
_newlyCreated = false;
|
||||
dataAt += encodedID.size();
|
||||
bytesRead += encodedID.size();
|
||||
|
||||
|
|
|
@ -96,11 +96,7 @@ public:
|
|||
// ID and EntityItemID related methods
|
||||
const QUuid& getID() const { return _id; }
|
||||
void setID(const QUuid& id) { _id = id; }
|
||||
uint32_t getCreatorTokenID() const { return _creatorTokenID; }
|
||||
void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; }
|
||||
bool isNewlyCreated() const { return _newlyCreated; }
|
||||
bool isKnownID() const { return getID() != UNKNOWN_ENTITY_ID; }
|
||||
EntityItemID getEntityItemID() const { return EntityItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_ENTITY_ID); }
|
||||
EntityItemID getEntityItemID() const { return EntityItemID(_id); }
|
||||
|
||||
// methods for getting/setting all properties of an entity
|
||||
virtual EntityItemProperties getProperties() const;
|
||||
|
@ -356,8 +352,6 @@ protected:
|
|||
static bool _sendPhysicsUpdates;
|
||||
EntityTypes::EntityType _type;
|
||||
QUuid _id;
|
||||
uint32_t _creatorTokenID;
|
||||
bool _newlyCreated;
|
||||
quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity, and physics changes
|
||||
quint64 _lastUpdated; // last time this entity called update(), this includes animations and non-physics changes
|
||||
quint64 _lastEdited; // last official local or remote edit time
|
||||
|
|
|
@ -14,130 +14,42 @@
|
|||
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "RegisteredMetaTypes.h"
|
||||
#include "EntityItemID.h"
|
||||
|
||||
// for locally created models
|
||||
QHash<uint32_t, QUuid> EntityItemID::_tokenIDsToIDs;
|
||||
uint32_t EntityItemID::_nextCreatorTokenID = 0;
|
||||
|
||||
EntityItemID::EntityItemID() :
|
||||
id(NEW_ENTITY),
|
||||
creatorTokenID(UNKNOWN_ENTITY_TOKEN),
|
||||
isKnownID(false)
|
||||
{
|
||||
}
|
||||
|
||||
EntityItemID::EntityItemID(const EntityItemID& other) :
|
||||
id(other.id),
|
||||
creatorTokenID(other.creatorTokenID),
|
||||
isKnownID(other.isKnownID)
|
||||
EntityItemID::EntityItemID() : QUuid()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
EntityItemID::EntityItemID(const QUuid& id, uint32_t creatorTokenID, bool isKnownID) :
|
||||
id(id),
|
||||
creatorTokenID(creatorTokenID),
|
||||
isKnownID(isKnownID)
|
||||
{
|
||||
};
|
||||
|
||||
EntityItemID::EntityItemID(const QUuid& id) :
|
||||
id(id),
|
||||
creatorTokenID(UNKNOWN_ENTITY_TOKEN),
|
||||
isKnownID(true)
|
||||
{
|
||||
};
|
||||
|
||||
EntityItemID EntityItemID::getIDfromCreatorTokenID(uint32_t creatorTokenID) {
|
||||
if (_tokenIDsToIDs.find(creatorTokenID) != _tokenIDsToIDs.end()) {
|
||||
return EntityItemID(_tokenIDsToIDs[creatorTokenID], creatorTokenID, true);
|
||||
}
|
||||
return EntityItemID(UNKNOWN_ENTITY_ID);
|
||||
EntityItemID::EntityItemID(const QUuid& id) : QUuid(id)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t EntityItemID::getNextCreatorTokenID() {
|
||||
uint32_t creatorTokenID = _nextCreatorTokenID;
|
||||
_nextCreatorTokenID++;
|
||||
return creatorTokenID;
|
||||
}
|
||||
|
||||
EntityItemID EntityItemID::assignActualIDForToken() const {
|
||||
EntityItemID newlyAssignedEntityID;
|
||||
|
||||
newlyAssignedEntityID.creatorTokenID = creatorTokenID;
|
||||
newlyAssignedEntityID.isKnownID = true;
|
||||
newlyAssignedEntityID.id = QUuid::createUuid();
|
||||
|
||||
return newlyAssignedEntityID;
|
||||
}
|
||||
|
||||
EntityItemID EntityItemID::convertToKnownIDVersion() const {
|
||||
EntityItemID knownIDVersionEntityID;
|
||||
|
||||
knownIDVersionEntityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN;
|
||||
knownIDVersionEntityID.isKnownID = true;
|
||||
knownIDVersionEntityID.id = id;
|
||||
|
||||
return knownIDVersionEntityID;
|
||||
}
|
||||
|
||||
EntityItemID EntityItemID::convertToCreatorTokenVersion() const {
|
||||
EntityItemID knownIDVersionEntityID;
|
||||
|
||||
knownIDVersionEntityID.creatorTokenID = creatorTokenID;
|
||||
knownIDVersionEntityID.isKnownID = false;
|
||||
knownIDVersionEntityID.id = UNKNOWN_ENTITY_ID;
|
||||
|
||||
return knownIDVersionEntityID;
|
||||
}
|
||||
// EntityItemID::EntityItemID(const EntityItemID& other) : QUuid(other)
|
||||
// {
|
||||
// }
|
||||
|
||||
EntityItemID EntityItemID::readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead) {
|
||||
EntityItemID result;
|
||||
|
||||
|
||||
if (bytesLeftToRead >= NUM_BYTES_RFC4122_UUID) {
|
||||
// id
|
||||
QByteArray encodedID((const char*)data, NUM_BYTES_RFC4122_UUID);
|
||||
result.id = QUuid::fromRfc4122(encodedID);
|
||||
result.isKnownID = true;
|
||||
result.creatorTokenID = UNKNOWN_ENTITY_TOKEN;
|
||||
result = QUuid::fromRfc4122(encodedID);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void EntityItemID::handleAddEntityResponse(const QByteArray& packet) {
|
||||
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data());
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||
int bytesRead = numBytesPacketHeader;
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
uint32_t creatorTokenID;
|
||||
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
||||
dataAt += sizeof(creatorTokenID);
|
||||
bytesRead += sizeof(creatorTokenID);
|
||||
|
||||
QUuid entityID = QUuid::fromRfc4122(packet.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
||||
dataAt += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
// add our token to id mapping
|
||||
_tokenIDsToIDs[creatorTokenID] = entityID;
|
||||
}
|
||||
|
||||
QScriptValue EntityItemID::toScriptValue(QScriptEngine* engine) const {
|
||||
return EntityItemIDtoScriptValue(engine, *this);
|
||||
}
|
||||
|
||||
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("id", id.id.toString());
|
||||
obj.setProperty("creatorTokenID", id.creatorTokenID);
|
||||
obj.setProperty("isKnownID", id.isKnownID);
|
||||
return obj;
|
||||
return quuidToScriptValue(engine, id);
|
||||
}
|
||||
|
||||
void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& id) {
|
||||
id.id = QUuid(object.property("id").toVariant().toString());
|
||||
id.creatorTokenID = object.property("creatorTokenID").toVariant().toUInt();
|
||||
id.isKnownID = object.property("isKnownID").toVariant().toBool();
|
||||
quuidFromScriptValue(object, id);
|
||||
}
|
||||
|
|
|
@ -20,87 +20,40 @@
|
|||
#include <QScriptEngine>
|
||||
#include <QUuid>
|
||||
|
||||
const uint32_t UNKNOWN_ENTITY_TOKEN = 0xFFFFFFFF;
|
||||
const QUuid UNKNOWN_ENTITY_ID; // null uuid
|
||||
|
||||
//const uint32_t NEW_ENTITY = 0xFFFFFFFF;
|
||||
//const uint32_t UNKNOWN_ENTITY_ID = 0xFFFFFFFF;
|
||||
|
||||
const QUuid NEW_ENTITY;
|
||||
const QUuid UNKNOWN_ENTITY_ID;
|
||||
|
||||
|
||||
/// Abstract ID for editing model items. Used in EntityItem JS API - When models are created in the JS api, they are given a
|
||||
/// local creatorTokenID, the actual id for the model is not known until the server responds to the creator with the
|
||||
/// correct mapping. This class works with the scripting API an allows the developer to edit models they created.
|
||||
class EntityItemID {
|
||||
/// Abstract ID for editing model items. Used in EntityItem JS API.
|
||||
class EntityItemID : public QUuid {
|
||||
public:
|
||||
EntityItemID();
|
||||
EntityItemID(const EntityItemID& other);
|
||||
EntityItemID(const QUuid& id, uint32_t creatorTokenID, bool isKnownID);
|
||||
EntityItemID(const QUuid& id);
|
||||
|
||||
//uint32_t id;
|
||||
QUuid id;
|
||||
|
||||
uint32_t creatorTokenID;
|
||||
bool isKnownID;
|
||||
|
||||
// these methods will reduce the ID down to half the IDs data to allow for comparisons and searches of known values
|
||||
EntityItemID convertToKnownIDVersion() const;
|
||||
EntityItemID convertToCreatorTokenVersion() const;
|
||||
|
||||
bool isInvalidID() const { return id == UNKNOWN_ENTITY_ID && creatorTokenID == UNKNOWN_ENTITY_TOKEN && isKnownID == false; }
|
||||
|
||||
// these methods allow you to create models, and later edit them.
|
||||
static EntityItemID createInvalidEntityID() { return EntityItemID(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false); }
|
||||
static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID);
|
||||
static uint32_t getNextCreatorTokenID();
|
||||
static void handleAddEntityResponse(const QByteArray& packet);
|
||||
// EntityItemID(const EntityItemID& other);
|
||||
static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead);
|
||||
|
||||
QScriptValue toScriptValue(QScriptEngine* engine) const;
|
||||
|
||||
private:
|
||||
friend class EntityTree;
|
||||
EntityItemID assignActualIDForToken() const; // only called by EntityTree
|
||||
|
||||
static uint32_t _nextCreatorTokenID; /// used by the static interfaces for creator token ids
|
||||
static QHash<uint32_t, QUuid> _tokenIDsToIDs;
|
||||
bool isInvalidID() const { return *this == UNKNOWN_ENTITY_ID; }
|
||||
|
||||
// QUuid id;
|
||||
};
|
||||
|
||||
inline bool operator<(const EntityItemID& a, const EntityItemID& b) {
|
||||
return (a.id == b.id) ? (a.creatorTokenID < b.creatorTokenID) : (a.id < b.id);
|
||||
}
|
||||
// inline bool operator<(const EntityItemID& a, const EntityItemID& b) {
|
||||
// return a.id < b.id;
|
||||
// }
|
||||
|
||||
inline bool operator==(const EntityItemID& a, const EntityItemID& b) {
|
||||
if (a.isInvalidID() && b.isInvalidID()) {
|
||||
return true;
|
||||
}
|
||||
if (a.isInvalidID() != b.isInvalidID()) {
|
||||
return false;
|
||||
}
|
||||
if (a.id == UNKNOWN_ENTITY_ID || b.id == UNKNOWN_ENTITY_ID) {
|
||||
return a.creatorTokenID == b.creatorTokenID;
|
||||
}
|
||||
return a.id == b.id;
|
||||
}
|
||||
// inline bool operator==(const EntityItemID& a, const EntityItemID& b) {
|
||||
// return a.id == b.id;
|
||||
// }
|
||||
|
||||
inline bool operator!=(const EntityItemID& a, const EntityItemID& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
// inline bool operator!=(const EntityItemID& a, const EntityItemID& b) {
|
||||
// return !(a == b);
|
||||
// }
|
||||
|
||||
inline uint qHash(const EntityItemID& a, uint seed) {
|
||||
QUuid temp;
|
||||
if (a.id == UNKNOWN_ENTITY_ID) {
|
||||
temp = QUuid(a.creatorTokenID,0,0,0,0,0,0,0,0,0,0);
|
||||
} else {
|
||||
temp = a.id;
|
||||
}
|
||||
return qHash(temp, seed);
|
||||
}
|
||||
// inline uint qHash(const EntityItemID& a, uint seed) {
|
||||
// return qHash(a.id, seed);
|
||||
// }
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const EntityItemID& id) {
|
||||
debug << "[ id:" << id.id << ", creatorTokenID:" << id.creatorTokenID << ", isKnownID:" << id.isKnownID << "]";
|
||||
debug << "[entity-id:" << id.toString() << "]";
|
||||
return debug;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,80 +35,80 @@ EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM -
|
|||
|
||||
EntityItemProperties::EntityItemProperties() :
|
||||
|
||||
CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE),
|
||||
CONSTRUCT_PROPERTY(position, 0),
|
||||
CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS),
|
||||
CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION),
|
||||
CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY),
|
||||
CONSTRUCT_PROPERTY(velocity, ENTITY_ITEM_DEFAULT_VELOCITY),
|
||||
CONSTRUCT_PROPERTY(gravity, ENTITY_ITEM_DEFAULT_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(acceleration, ENTITY_ITEM_DEFAULT_ACCELERATION),
|
||||
CONSTRUCT_PROPERTY(damping, ENTITY_ITEM_DEFAULT_DAMPING),
|
||||
CONSTRUCT_PROPERTY(restitution, ENTITY_ITEM_DEFAULT_RESTITUTION),
|
||||
CONSTRUCT_PROPERTY(friction, ENTITY_ITEM_DEFAULT_FRICTION),
|
||||
CONSTRUCT_PROPERTY(lifetime, ENTITY_ITEM_DEFAULT_LIFETIME),
|
||||
CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT),
|
||||
CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL),
|
||||
CONSTRUCT_PROPERTY(color, ),
|
||||
CONSTRUCT_PROPERTY(modelURL, ""),
|
||||
CONSTRUCT_PROPERTY(compoundShapeURL, ""),
|
||||
CONSTRUCT_PROPERTY(animationURL, ""),
|
||||
CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS),
|
||||
CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
|
||||
CONSTRUCT_PROPERTY(animationIsPlaying, ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
|
||||
CONSTRUCT_PROPERTY(registrationPoint, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT),
|
||||
CONSTRUCT_PROPERTY(angularVelocity, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY),
|
||||
CONSTRUCT_PROPERTY(angularDamping, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING),
|
||||
CONSTRUCT_PROPERTY(ignoreForCollisions, ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS),
|
||||
CONSTRUCT_PROPERTY(collisionsWillMove, ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE),
|
||||
CONSTRUCT_PROPERTY(isSpotlight, false),
|
||||
CONSTRUCT_PROPERTY(intensity, 1.0f),
|
||||
CONSTRUCT_PROPERTY(exponent, 0.0f),
|
||||
CONSTRUCT_PROPERTY(cutoff, PI),
|
||||
CONSTRUCT_PROPERTY(locked, ENTITY_ITEM_DEFAULT_LOCKED),
|
||||
CONSTRUCT_PROPERTY(textures, ""),
|
||||
CONSTRUCT_PROPERTY(animationSettings, ""),
|
||||
CONSTRUCT_PROPERTY(userData, ENTITY_ITEM_DEFAULT_USER_DATA),
|
||||
CONSTRUCT_PROPERTY(simulatorID, ENTITY_ITEM_DEFAULT_SIMULATOR_ID),
|
||||
CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT),
|
||||
CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT),
|
||||
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
|
||||
CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR),
|
||||
CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE),
|
||||
CONSTRUCT_PROPERTY(maxParticles, ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES),
|
||||
CONSTRUCT_PROPERTY(lifespan, ParticleEffectEntityItem::DEFAULT_LIFESPAN),
|
||||
CONSTRUCT_PROPERTY(emitRate, ParticleEffectEntityItem::DEFAULT_EMIT_RATE),
|
||||
CONSTRUCT_PROPERTY(emitDirection, ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH),
|
||||
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
|
||||
CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
|
||||
CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR),
|
||||
CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightAmbientIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightDirection, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME),
|
||||
CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT),
|
||||
CONSTRUCT_PROPERTY(sourceUrl, ""),
|
||||
CONSTRUCT_PROPERTY(visible, ENTITY_ITEM_DEFAULT_VISIBLE),
|
||||
CONSTRUCT_PROPERTY(position, 0),
|
||||
CONSTRUCT_PROPERTY(dimensions, ENTITY_ITEM_DEFAULT_DIMENSIONS),
|
||||
CONSTRUCT_PROPERTY(rotation, ENTITY_ITEM_DEFAULT_ROTATION),
|
||||
CONSTRUCT_PROPERTY(density, ENTITY_ITEM_DEFAULT_DENSITY),
|
||||
CONSTRUCT_PROPERTY(velocity, ENTITY_ITEM_DEFAULT_VELOCITY),
|
||||
CONSTRUCT_PROPERTY(gravity, ENTITY_ITEM_DEFAULT_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(acceleration, ENTITY_ITEM_DEFAULT_ACCELERATION),
|
||||
CONSTRUCT_PROPERTY(damping, ENTITY_ITEM_DEFAULT_DAMPING),
|
||||
CONSTRUCT_PROPERTY(restitution, ENTITY_ITEM_DEFAULT_RESTITUTION),
|
||||
CONSTRUCT_PROPERTY(friction, ENTITY_ITEM_DEFAULT_FRICTION),
|
||||
CONSTRUCT_PROPERTY(lifetime, ENTITY_ITEM_DEFAULT_LIFETIME),
|
||||
CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT),
|
||||
CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL),
|
||||
CONSTRUCT_PROPERTY(color, ),
|
||||
CONSTRUCT_PROPERTY(modelURL, ""),
|
||||
CONSTRUCT_PROPERTY(compoundShapeURL, ""),
|
||||
CONSTRUCT_PROPERTY(animationURL, ""),
|
||||
CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS),
|
||||
CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
|
||||
CONSTRUCT_PROPERTY(animationIsPlaying, ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
|
||||
CONSTRUCT_PROPERTY(registrationPoint, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT),
|
||||
CONSTRUCT_PROPERTY(angularVelocity, ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY),
|
||||
CONSTRUCT_PROPERTY(angularDamping, ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING),
|
||||
CONSTRUCT_PROPERTY(ignoreForCollisions, ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS),
|
||||
CONSTRUCT_PROPERTY(collisionsWillMove, ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE),
|
||||
CONSTRUCT_PROPERTY(isSpotlight, false),
|
||||
CONSTRUCT_PROPERTY(intensity, 1.0f),
|
||||
CONSTRUCT_PROPERTY(exponent, 0.0f),
|
||||
CONSTRUCT_PROPERTY(cutoff, ENTITY_ITEM_DEFAULT_CUTOFF),
|
||||
CONSTRUCT_PROPERTY(locked, ENTITY_ITEM_DEFAULT_LOCKED),
|
||||
CONSTRUCT_PROPERTY(textures, ""),
|
||||
CONSTRUCT_PROPERTY(animationSettings, ""),
|
||||
CONSTRUCT_PROPERTY(userData, ENTITY_ITEM_DEFAULT_USER_DATA),
|
||||
CONSTRUCT_PROPERTY(simulatorID, ENTITY_ITEM_DEFAULT_SIMULATOR_ID),
|
||||
CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT),
|
||||
CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT),
|
||||
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
|
||||
CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR),
|
||||
CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE),
|
||||
CONSTRUCT_PROPERTY(maxParticles, ParticleEffectEntityItem::DEFAULT_MAX_PARTICLES),
|
||||
CONSTRUCT_PROPERTY(lifespan, ParticleEffectEntityItem::DEFAULT_LIFESPAN),
|
||||
CONSTRUCT_PROPERTY(emitRate, ParticleEffectEntityItem::DEFAULT_EMIT_RATE),
|
||||
CONSTRUCT_PROPERTY(emitDirection, ParticleEffectEntityItem::DEFAULT_EMIT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH),
|
||||
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
|
||||
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
|
||||
CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
|
||||
CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR),
|
||||
CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightAmbientIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightDirection, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME),
|
||||
CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT),
|
||||
CONSTRUCT_PROPERTY(sourceUrl, ""),
|
||||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(0),
|
||||
_created(UNKNOWN_CREATED_TIME),
|
||||
_type(EntityTypes::Unknown),
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(0),
|
||||
_created(UNKNOWN_CREATED_TIME),
|
||||
_type(EntityTypes::Unknown),
|
||||
|
||||
_glowLevel(0.0f),
|
||||
_localRenderAlpha(1.0f),
|
||||
_glowLevel(0.0f),
|
||||
_localRenderAlpha(1.0f),
|
||||
|
||||
_glowLevelChanged(false),
|
||||
_localRenderAlphaChanged(false),
|
||||
_glowLevelChanged(false),
|
||||
_localRenderAlphaChanged(false),
|
||||
|
||||
_defaultSettings(true),
|
||||
_naturalDimensions(1.0f, 1.0f, 1.0f)
|
||||
_defaultSettings(true),
|
||||
_naturalDimensions(1.0f, 1.0f, 1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
EntityItemProperties::~EntityItemProperties() {
|
||||
EntityItemProperties::~EntityItemProperties() {
|
||||
}
|
||||
|
||||
void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sittingPoints) {
|
||||
|
@ -118,11 +118,11 @@ void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sitting
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItemProperties::setAnimationSettings(const QString& value) {
|
||||
void EntityItemProperties::setAnimationSettings(const QString& value) {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
|
@ -130,37 +130,37 @@ void EntityItemProperties::setAnimationSettings(const QString& value) {
|
|||
float fps = settingsMap["fps"].toFloat();
|
||||
setAnimationFPS(fps);
|
||||
}
|
||||
|
||||
|
||||
if (settingsMap.contains("frameIndex")) {
|
||||
float frameIndex = settingsMap["frameIndex"].toFloat();
|
||||
setAnimationFrameIndex(frameIndex);
|
||||
}
|
||||
|
||||
|
||||
if (settingsMap.contains("running")) {
|
||||
bool running = settingsMap["running"].toBool();
|
||||
setAnimationIsPlaying(running);
|
||||
}
|
||||
|
||||
_animationSettings = value;
|
||||
_animationSettingsChanged = true;
|
||||
_animationSettings = value;
|
||||
_animationSettingsChanged = true;
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getAnimationSettings() const {
|
||||
QString EntityItemProperties::getAnimationSettings() const {
|
||||
// the animations setting is a JSON string that may contain various animation settings.
|
||||
// if it includes fps, frameIndex, or running, those values will be parsed out and
|
||||
// will over ride the regular animation settings
|
||||
QString value = _animationSettings;
|
||||
|
||||
|
||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||
|
||||
QVariant fpsValue(getAnimationFPS());
|
||||
settingsMap["fps"] = fpsValue;
|
||||
|
||||
|
||||
QVariant frameIndexValue(getAnimationFrameIndex());
|
||||
settingsMap["frameIndex"] = frameIndexValue;
|
||||
|
||||
|
||||
QVariant runningValue(getAnimationIsPlaying());
|
||||
settingsMap["running"] = runningValue;
|
||||
|
||||
|
@ -180,28 +180,28 @@ void EntityItemProperties::debugDump() const {
|
|||
qCDebug(entities) << " _dimensions=" << getDimensions();
|
||||
qCDebug(entities) << " _modelURL=" << _modelURL;
|
||||
qCDebug(entities) << " _compoundShapeURL=" << _compoundShapeURL;
|
||||
|
||||
|
||||
getAtmosphere().debugDump();
|
||||
getSkybox().debugDump();
|
||||
|
||||
|
||||
qCDebug(entities) << " changed properties...";
|
||||
EntityPropertyFlags props = getChangedProperties();
|
||||
props.debugDumpBits();
|
||||
}
|
||||
|
||||
void EntityItemProperties::setCreated(quint64 usecTime) {
|
||||
_created = usecTime;
|
||||
void EntityItemProperties::setCreated(quint64 usecTime) {
|
||||
_created = usecTime;
|
||||
if (_lastEdited < _created) {
|
||||
_lastEdited = _created;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItemProperties::setLastEdited(quint64 usecTime) {
|
||||
_lastEdited = usecTime > _created ? usecTime : _created;
|
||||
void EntityItemProperties::setLastEdited(quint64 usecTime) {
|
||||
_lastEdited = usecTime > _created ? usecTime : _created;
|
||||
}
|
||||
|
||||
const char* shapeTypeNames[] = {"none", "box", "sphere", "ellipsoid", "plane", "compound", "capsule-x",
|
||||
"capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"};
|
||||
"capsule-y", "capsule-z", "cylinder-x", "cylinder-y", "cylinder-z"};
|
||||
|
||||
QHash<QString, ShapeType> stringToShapeTypeLookup;
|
||||
|
||||
|
@ -336,25 +336,22 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, keyLightDirection);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
|
||||
|
||||
|
||||
changedProperties += _stage.getChangedProperties();
|
||||
changedProperties += _atmosphere.getChangedProperties();
|
||||
changedProperties += _skybox.getChangedProperties();
|
||||
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults) const {
|
||||
QScriptValue properties = engine->newObject();
|
||||
EntityItemProperties defaultEntityProperties;
|
||||
|
||||
|
||||
if (_idSet) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(isKnownID, (_id != UNKNOWN_ENTITY_ID));
|
||||
} else {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(isKnownID, false);
|
||||
}
|
||||
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type));
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(position);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(dimensions);
|
||||
|
@ -414,14 +411,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(name);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(collisionSoundURL);
|
||||
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightColor);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightIntensity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightAmbientIntensity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightDirection);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundMode, getBackgroundModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(sourceUrl);
|
||||
|
||||
|
||||
// Sitting properties support
|
||||
if (!skipDefaults) {
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -435,7 +432,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
sittingPoints.setProperty("length", _sittingPoints.size());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable
|
||||
}
|
||||
|
||||
|
||||
if (!skipDefaults) {
|
||||
AABox aaBox = getAABox();
|
||||
QScriptValue boundingBox = engine->newObject();
|
||||
|
@ -449,7 +446,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
boundingBox.setProperty("dimensions", boundingBoxDimensions);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(boundingBox, boundingBox); // gettable, but not settable
|
||||
}
|
||||
|
||||
|
||||
QString textureNamesList = _textureNames.join(",\n");
|
||||
if (!skipDefaults) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(originalTextures, textureNamesList); // gettable, but not settable
|
||||
|
@ -458,7 +455,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
_stage.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_atmosphere.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_skybox.copyToScriptValue(properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -467,7 +464,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
if (typeScriptValue.isValid()) {
|
||||
setType(typeScriptValue.toVariant().toString());
|
||||
}
|
||||
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(position, glmVec3, setPosition);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dimensions, glmVec3, setDimensions);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(rotation, glmQuat, setRotation);
|
||||
|
@ -519,14 +516,14 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(marketplaceID, QString, setMarketplaceID);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
|
||||
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightColor, xColor, setKeyLightColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightIntensity, float, setKeyLightIntensity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightAmbientIntensity, float, setKeyLightAmbientIntensity);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightDirection, glmVec3, setKeyLightDirection);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
||||
|
||||
|
||||
_stage.copyFromScriptValue(object, _defaultSettings);
|
||||
_atmosphere.copyFromScriptValue(object, _defaultSettings);
|
||||
_skybox.copyFromScriptValue(object, _defaultSettings);
|
||||
|
@ -545,8 +542,8 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP
|
|||
properties.copyFromScriptValue(object);
|
||||
}
|
||||
|
||||
// TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the
|
||||
// encodeEntityEditPacket() method to communicate the the caller which properties couldn't fit in the buffer. Similar
|
||||
// TODO: Implement support for edit packets that can span an MTU sized buffer. We need to implement a mechanism for the
|
||||
// encodeEntityEditPacket() method to communicate the the caller which properties couldn't fit in the buffer. Similar
|
||||
// to how we handle this in the Octree streaming case.
|
||||
//
|
||||
// TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer
|
||||
|
@ -561,14 +558,14 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP
|
|||
// TODO: Implement support for script and visible properties.
|
||||
//
|
||||
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut) {
|
||||
unsigned char* bufferOut, int sizeIn, int& sizeOut) {
|
||||
OctreePacketData ourDataPacket(false, sizeIn); // create a packetData object to add out packet details too.
|
||||
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro
|
||||
|
||||
|
||||
bool success = true; // assume the best
|
||||
OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best
|
||||
sizeOut = 0;
|
||||
|
||||
|
||||
// TODO: We need to review how jurisdictions should be handled for entities. (The old Models and Particles code
|
||||
// didn't do anything special for jurisdictions, so we're keeping that same behavior here.)
|
||||
//
|
||||
|
@ -578,43 +575,35 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
glm::vec3 rootPosition(0);
|
||||
float rootScale = 0.5f;
|
||||
unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale);
|
||||
|
||||
|
||||
success = packetData->startSubTree(octcode);
|
||||
delete[] octcode;
|
||||
|
||||
// assuming we have rome to fit our octalCode, proceed...
|
||||
if (success) {
|
||||
|
||||
|
||||
// Now add our edit content details...
|
||||
bool isNewEntityItem = (id.id == NEW_ENTITY);
|
||||
|
||||
|
||||
// id
|
||||
// encode our ID as a byte count coded byte stream
|
||||
QByteArray encodedID = id.id.toRfc4122(); // NUM_BYTES_RFC4122_UUID
|
||||
|
||||
QByteArray encodedID = id.toRfc4122(); // NUM_BYTES_RFC4122_UUID
|
||||
|
||||
// encode our ID as a byte count coded byte stream
|
||||
ByteCountCoded<quint32> tokenCoder;
|
||||
QByteArray encodedToken;
|
||||
|
||||
// special case for handling "new" modelItems
|
||||
if (isNewEntityItem) {
|
||||
// encode our creator token as a byte count coded byte stream
|
||||
tokenCoder = id.creatorTokenID;
|
||||
encodedToken = tokenCoder;
|
||||
}
|
||||
|
||||
|
||||
// encode our type as a byte count coded byte stream
|
||||
ByteCountCoded<quint32> typeCoder = (quint32)properties.getType();
|
||||
QByteArray encodedType = typeCoder;
|
||||
|
||||
|
||||
quint64 updateDelta = 0; // this is an edit so by definition, it's update is in sync
|
||||
ByteCountCoded<quint64> updateDeltaCoder = updateDelta;
|
||||
QByteArray encodedUpdateDelta = updateDeltaCoder;
|
||||
|
||||
|
||||
EntityPropertyFlags propertyFlags(PROP_LAST_ITEM);
|
||||
EntityPropertyFlags requestedProperties = properties.getChangedProperties();
|
||||
EntityPropertyFlags propertiesDidntFit = requestedProperties;
|
||||
|
||||
|
||||
// TODO: we need to handle the multi-pass form of this, similar to how we handle entity data
|
||||
//
|
||||
// If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item,
|
||||
|
@ -622,46 +611,46 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
//if (modelTreeElementExtraEncodeData && modelTreeElementExtraEncodeData->includedItems.contains(getEntityItemID())) {
|
||||
// requestedProperties = modelTreeElementExtraEncodeData->includedItems.value(getEntityItemID());
|
||||
//}
|
||||
|
||||
|
||||
LevelDetails entityLevel = packetData->startLevel();
|
||||
|
||||
|
||||
// Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this
|
||||
// timestamp for clock skew
|
||||
quint64 lastEdited = properties.getLastEdited();
|
||||
bool successLastEditedFits = packetData->appendValue(lastEdited);
|
||||
|
||||
|
||||
bool successIDFits = packetData->appendValue(encodedID);
|
||||
if (isNewEntityItem && successIDFits) {
|
||||
if (successIDFits) {
|
||||
successIDFits = packetData->appendValue(encodedToken);
|
||||
}
|
||||
bool successTypeFits = packetData->appendValue(encodedType);
|
||||
|
||||
|
||||
// NOTE: We intentionally do not send "created" times in edit messages. This is because:
|
||||
// 1) if the edit is to an existing entity, the created time can not be changed
|
||||
// 2) if the edit is to a new entity, the created time is the last edited time
|
||||
|
||||
|
||||
// TODO: Should we get rid of this in this in edit packets, since this has to always be 0?
|
||||
bool successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta);
|
||||
|
||||
|
||||
int propertyFlagsOffset = packetData->getUncompressedByteOffset();
|
||||
QByteArray encodedPropertyFlags = propertyFlags;
|
||||
int oldPropertyFlagsLength = encodedPropertyFlags.length();
|
||||
bool successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags);
|
||||
int propertyCount = 0;
|
||||
|
||||
|
||||
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
||||
&& successLastUpdatedFits && successPropertyFlagsFits;
|
||||
|
||||
&& successLastUpdatedFits && successPropertyFlagsFits;
|
||||
|
||||
int startOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
||||
|
||||
if (headerFits) {
|
||||
bool successPropertyFits;
|
||||
propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item
|
||||
|
||||
|
||||
// These items would go here once supported....
|
||||
// PROP_PAGED_PROPERTY,
|
||||
// PROP_CUSTOM_PROPERTIES_INCLUDED,
|
||||
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_POSITION, properties.getPosition());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete
|
||||
APPEND_ENTITY_PROPERTY(PROP_ROTATION, properties.getRotation());
|
||||
|
@ -688,7 +677,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
if (properties.getType() == EntityTypes::Web) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight());
|
||||
|
@ -707,7 +696,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, properties.getAnimationSettings());
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType()));
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::Light) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, properties.getIsSpotlight());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
|
@ -715,8 +704,13 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_EXPONENT, properties.getExponent());
|
||||
APPEND_ENTITY_PROPERTY(PROP_CUTOFF, properties.getCutoff());
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::ParticleEffect) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, properties.getAnimationFPS());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, properties.getAnimationFrameIndex());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, properties.getAnimationIsPlaying());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, properties.getAnimationSettings());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LIFESPAN, properties.getLifespan());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMIT_RATE, properties.getEmitRate());
|
||||
|
@ -725,24 +719,24 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, properties.getLocalGravity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, properties.getParticleRadius());
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::Zone) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, properties.getKeyLightColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, properties.getKeyLightIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, properties.getKeyLightAmbientIntensity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, properties.getKeyLightDirection());
|
||||
|
||||
|
||||
_staticStage.setProperties(properties);
|
||||
_staticStage.appentToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState );
|
||||
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)properties.getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL());
|
||||
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, (uint32_t)properties.getBackgroundMode());
|
||||
|
||||
_staticAtmosphere.setProperties(properties);
|
||||
_staticAtmosphere.appentToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState );
|
||||
|
||||
|
||||
_staticSkybox.setProperties(properties);
|
||||
_staticSkybox.appentToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState );
|
||||
}
|
||||
|
@ -753,41 +747,41 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
||||
|
||||
encodedPropertyFlags = propertyFlags;
|
||||
int newPropertyFlagsLength = encodedPropertyFlags.length();
|
||||
packetData->updatePriorBytes(propertyFlagsOffset,
|
||||
(const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length());
|
||||
|
||||
packetData->updatePriorBytes(propertyFlagsOffset,
|
||||
(const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length());
|
||||
|
||||
// if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet.
|
||||
if (newPropertyFlagsLength < oldPropertyFlagsLength) {
|
||||
int oldSize = packetData->getUncompressedSize();
|
||||
|
||||
|
||||
const unsigned char* modelItemData = packetData->getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength);
|
||||
int modelItemDataLength = endOfEntityItemData - startOfEntityItemData;
|
||||
int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength;
|
||||
packetData->updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength);
|
||||
|
||||
|
||||
int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength);
|
||||
packetData->setUncompressedSize(newSize);
|
||||
|
||||
|
||||
} else {
|
||||
assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown
|
||||
}
|
||||
|
||||
|
||||
packetData->endLevel(entityLevel);
|
||||
} else {
|
||||
packetData->discardLevel(entityLevel);
|
||||
appendState = OctreeElement::NONE; // if we got here, then we didn't include the item
|
||||
}
|
||||
|
||||
|
||||
// If any part of the model items didn't fit, then the element is considered partial
|
||||
if (appendState != OctreeElement::COMPLETED) {
|
||||
|
||||
|
||||
// TODO: handle mechanism for handling partial fitting data!
|
||||
// add this item into our list for the next appendElementData() pass
|
||||
//modelTreeElementExtraEncodeData->includedItems.insert(getEntityItemID(), propertiesDidntFit);
|
||||
|
||||
|
||||
// for now, if it's not complete, it's not successful
|
||||
success = false;
|
||||
}
|
||||
|
@ -812,7 +806,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
return success;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// TODO:
|
||||
// how to handle lastEdited?
|
||||
// how to handle lastUpdated?
|
||||
// consider handling case where no properties are included... we should just ignore this packet...
|
||||
|
@ -829,17 +823,17 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
// TODO: Implement support for script and visible properties.
|
||||
//
|
||||
bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
|
||||
EntityItemID& entityID, EntityItemProperties& properties) {
|
||||
EntityItemID& entityID, EntityItemProperties& properties) {
|
||||
bool valid = false;
|
||||
|
||||
|
||||
const unsigned char* dataAt = data;
|
||||
processedBytes = 0;
|
||||
|
||||
|
||||
// the first part of the data is an octcode, this is a required element of the edit packet format, but we don't
|
||||
// actually use it, we do need to skip it and read to the actual data we care about.
|
||||
int octets = numberOfThreeBitSectionsInCode(data);
|
||||
int bytesToReadOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
|
||||
|
||||
// we don't actually do anything with this octcode...
|
||||
dataAt += bytesToReadOfOctcode;
|
||||
processedBytes += bytesToReadOfOctcode;
|
||||
|
@ -852,48 +846,20 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
dataAt += sizeof(lastEdited);
|
||||
processedBytes += sizeof(lastEdited);
|
||||
properties.setLastEdited(lastEdited);
|
||||
|
||||
|
||||
// NOTE: We intentionally do not send "created" times in edit messages. This is because:
|
||||
// 1) if the edit is to an existing entity, the created time can not be changed
|
||||
// 2) if the edit is to a new entity, the created time is the last edited time
|
||||
|
||||
|
||||
// encoded id
|
||||
QByteArray encodedID((const char*)dataAt, NUM_BYTES_RFC4122_UUID); // maximum possible size
|
||||
QUuid editID = QUuid::fromRfc4122(encodedID);
|
||||
dataAt += encodedID.size();
|
||||
processedBytes += encodedID.size();
|
||||
|
||||
bool isNewEntityItem = (editID == NEW_ENTITY);
|
||||
|
||||
if (isNewEntityItem) {
|
||||
// If this is a NEW_ENTITY, then we assume that there's an additional uint32_t creatorToken, that
|
||||
// we want to send back to the creator as an map to the actual id
|
||||
|
||||
QByteArray encodedToken((const char*)dataAt, (bytesToRead - processedBytes));
|
||||
ByteCountCoded<quint32> tokenCoder = encodedToken;
|
||||
quint32 creatorTokenID = tokenCoder;
|
||||
encodedToken = tokenCoder; // determine true bytesToRead
|
||||
dataAt += encodedToken.size();
|
||||
processedBytes += encodedToken.size();
|
||||
|
||||
//newEntityItem.setCreatorTokenID(creatorTokenID);
|
||||
//newEntityItem._newlyCreated = true;
|
||||
|
||||
entityID.id = NEW_ENTITY;
|
||||
entityID.creatorTokenID = creatorTokenID;
|
||||
entityID.isKnownID = false;
|
||||
|
||||
valid = true;
|
||||
|
||||
// created time is lastEdited time
|
||||
properties.setCreated(lastEdited);
|
||||
} else {
|
||||
entityID.id = editID;
|
||||
entityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN;
|
||||
entityID.isKnownID = true;
|
||||
valid = true;
|
||||
}
|
||||
|
||||
|
||||
entityID = editID;
|
||||
valid = true;
|
||||
|
||||
// Entity Type...
|
||||
QByteArray encodedType((const char*)dataAt, (bytesToRead - processedBytes));
|
||||
ByteCountCoded<quint32> typeCoder = encodedType;
|
||||
|
@ -902,18 +868,18 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
encodedType = typeCoder; // determine true bytesToRead
|
||||
dataAt += encodedType.size();
|
||||
processedBytes += encodedType.size();
|
||||
|
||||
|
||||
// Update Delta - when was this item updated relative to last edit... this really should be 0
|
||||
// TODO: Should we get rid of this in this in edit packets, since this has to always be 0?
|
||||
// TODO: do properties need to handle lastupdated???
|
||||
|
||||
|
||||
// last updated is stored as ByteCountCoded delta from lastEdited
|
||||
QByteArray encodedUpdateDelta((const char*)dataAt, (bytesToRead - processedBytes));
|
||||
ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta;
|
||||
encodedUpdateDelta = updateDeltaCoder; // determine true bytesToRead
|
||||
dataAt += encodedUpdateDelta.size();
|
||||
processedBytes += encodedUpdateDelta.size();
|
||||
|
||||
|
||||
// TODO: Do we need this lastUpdated?? We don't seem to use it.
|
||||
//quint64 updateDelta = updateDeltaCoder;
|
||||
//quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited
|
||||
|
@ -923,7 +889,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
processedBytes += propertyFlags.getEncodedLength();
|
||||
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation);
|
||||
|
@ -946,11 +912,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USER_DATA, QString, setUserData);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATOR_ID, QUuid, setSimulatorID);
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight);
|
||||
|
@ -977,8 +943,14 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::ParticleEffect) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, float, setMaxParticles);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFESPAN, float, setLifespan);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate);
|
||||
|
@ -987,15 +959,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCAL_GRAVITY, float, setLocalGravity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
|
||||
}
|
||||
|
||||
|
||||
if (properties.getType() == EntityTypes::Zone) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_COLOR, xColor, setKeyLightColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_INTENSITY, float, setKeyLightIntensity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setKeyLightAmbientIntensity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_DIRECTION, glm::vec3, setKeyLightDirection);
|
||||
|
||||
|
||||
properties.getStage().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
|
||||
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
|
||||
|
@ -1006,20 +978,20 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
// NOTE: This version will only encode the portion of the edit message immediately following the
|
||||
// header it does not include the send times and sequence number because that is handled by the
|
||||
// header it does not include the send times and sequence number because that is handled by the
|
||||
// edit packet sender...
|
||||
bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityItemID,
|
||||
unsigned char* outputBuffer, size_t maxLength, size_t& outputLength) {
|
||||
|
||||
bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityItemID,
|
||||
unsigned char* outputBuffer, size_t maxLength, size_t& outputLength) {
|
||||
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
uint16_t numberOfIds = 1; // only one entity ID in this message
|
||||
|
||||
|
||||
if (maxLength < sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID) {
|
||||
qCDebug(entities) << "ERROR - encodeEraseEntityMessage() called with buffer that is too small!";
|
||||
outputLength = 0;
|
||||
|
@ -1028,14 +1000,14 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
|
|||
memcpy(copyAt, &numberOfIds, sizeof(numberOfIds));
|
||||
copyAt += sizeof(numberOfIds);
|
||||
outputLength = sizeof(numberOfIds);
|
||||
|
||||
QUuid entityID = entityItemID.id;
|
||||
|
||||
QUuid entityID = entityItemID;
|
||||
QByteArray encodedEntityID = entityID.toRfc4122();
|
||||
|
||||
|
||||
memcpy(copyAt, encodedEntityID.constData(), NUM_BYTES_RFC4122_UUID);
|
||||
copyAt += NUM_BYTES_RFC4122_UUID;
|
||||
outputLength += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1073,7 +1045,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_isSpotlightChanged = true;
|
||||
_ignoreForCollisionsChanged = true;
|
||||
_collisionsWillMoveChanged = true;
|
||||
|
||||
|
||||
_intensityChanged = true;
|
||||
_exponentChanged = true;
|
||||
_cutoffChanged = true;
|
||||
|
@ -1085,7 +1057,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_textColorChanged = true;
|
||||
_backgroundColorChanged = true;
|
||||
_shapeTypeChanged = true;
|
||||
|
||||
|
||||
_maxParticlesChanged = true;
|
||||
_lifespanChanged = true;
|
||||
_emitRateChanged = true;
|
||||
|
@ -1095,7 +1067,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_particleRadiusChanged = true;
|
||||
|
||||
_marketplaceIDChanged = true;
|
||||
|
||||
|
||||
_keyLightColorChanged = true;
|
||||
_keyLightIntensityChanged = true;
|
||||
_keyLightAmbientIntensityChanged = true;
|
||||
|
@ -1105,39 +1077,39 @@ void EntityItemProperties::markAllChanged() {
|
|||
_stage.markAllChanged();
|
||||
_atmosphere.markAllChanged();
|
||||
_skybox.markAllChanged();
|
||||
|
||||
|
||||
_sourceUrlChanged = true;
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
/// This accounts for the registration point (upon which rotation occurs around).
|
||||
///
|
||||
AACube EntityItemProperties::getMaximumAACube() const {
|
||||
///
|
||||
AACube EntityItemProperties::getMaximumAACube() const {
|
||||
// * we know that the position is the center of rotation
|
||||
glm::vec3 centerOfRotation = _position; // also where _registration point is
|
||||
|
||||
|
||||
// * we know that the registration point is the center of rotation
|
||||
// * we can calculate the length of the furthest extent from the registration point
|
||||
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
|
||||
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
|
||||
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
|
||||
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
|
||||
|
||||
|
||||
// * we know that if you rotate in any direction you would create a sphere
|
||||
// that has a radius of the length of furthest extent from registration point
|
||||
float radius = glm::length(furthestExtentFromRegistration);
|
||||
|
||||
|
||||
// * we know that the minimum bounding cube of this maximum possible sphere is
|
||||
// (center - radius) to (center + radius)
|
||||
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
|
||||
float diameter = radius * 2.0f;
|
||||
|
||||
|
||||
return AACube(minimumCorner, diameter);
|
||||
}
|
||||
|
||||
// The minimum bounding box for the entity.
|
||||
AABox EntityItemProperties::getAABox() const {
|
||||
|
||||
AABox EntityItemProperties::getAABox() const {
|
||||
|
||||
// _position represents the position of the registration point.
|
||||
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
|
||||
|
||||
|
|
|
@ -70,9 +70,6 @@ public:
|
|||
{ return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; }
|
||||
EntityPropertyFlags getChangedProperties() const;
|
||||
|
||||
/// used by EntityScriptingInterface to return EntityItemProperties for unknown models
|
||||
void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; }
|
||||
|
||||
AACube getMaximumAACube() const;
|
||||
AABox getAABox() const;
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ const float ENTITY_ITEM_DEFAULT_FRICTION = 0.5f;
|
|||
const bool ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS = false;
|
||||
const bool ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE = false;
|
||||
|
||||
const float ENTITY_ITEM_DEFAULT_CUTOFF = PI / 2;
|
||||
|
||||
const QString ENTITY_ITEM_DEFAULT_NAME = QString("");
|
||||
|
||||
#endif // hifi_EntityItemPropertiesDefaults_h
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
|
||||
EntityScriptingInterface::EntityScriptingInterface() :
|
||||
_nextCreatorTokenID(0),
|
||||
_entityTree(NULL)
|
||||
{
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -29,7 +28,7 @@ EntityScriptingInterface::EntityScriptingInterface() :
|
|||
}
|
||||
|
||||
void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
|
||||
EntityItemID entityID, const EntityItemProperties& properties) {
|
||||
EntityItemID entityID, const EntityItemProperties& properties) {
|
||||
getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);
|
||||
}
|
||||
|
||||
|
@ -47,7 +46,6 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
|
|||
if (_entityTree) {
|
||||
disconnect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);
|
||||
disconnect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);
|
||||
disconnect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID);
|
||||
disconnect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);
|
||||
}
|
||||
|
||||
|
@ -56,7 +54,6 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
|
|||
if (_entityTree) {
|
||||
connect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);
|
||||
connect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);
|
||||
connect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID);
|
||||
connect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);
|
||||
}
|
||||
}
|
||||
|
@ -71,14 +68,11 @@ void bidForSimulationOwnership(EntityItemProperties& properties) {
|
|||
|
||||
|
||||
|
||||
EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
|
||||
|
||||
// The application will keep track of creatorTokenID
|
||||
uint32_t creatorTokenID = EntityItemID::getNextCreatorTokenID();
|
||||
QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
|
||||
|
||||
EntityItemProperties propertiesWithSimID = properties;
|
||||
|
||||
EntityItemID id(NEW_ENTITY, creatorTokenID, false);
|
||||
EntityItemID id = EntityItemID(QUuid::createUuid());
|
||||
|
||||
// If we have a local entity tree set, then also update it.
|
||||
bool success = true;
|
||||
|
@ -98,34 +92,17 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
|
|||
|
||||
// queue the packet
|
||||
if (success) {
|
||||
queueEntityMessage(PacketTypeEntityAddOrEdit, id, propertiesWithSimID);
|
||||
queueEntityMessage(PacketTypeEntityAdd, id, propertiesWithSimID);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) {
|
||||
EntityItemID actualID = entityID;
|
||||
|
||||
if (!entityID.isKnownID) {
|
||||
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
|
||||
if (actualID == UNKNOWN_ENTITY_ID) {
|
||||
return entityID; // bailing early
|
||||
}
|
||||
|
||||
// found it!
|
||||
entityID.id = actualID.id;
|
||||
entityID.isKnownID = true;
|
||||
}
|
||||
return entityID;
|
||||
}
|
||||
|
||||
EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID entityID) {
|
||||
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
|
||||
EntityItemProperties results;
|
||||
EntityItemID identity = identifyEntity(entityID);
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity));
|
||||
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(EntityItemID(identity)));
|
||||
|
||||
if (entity) {
|
||||
results = entity->getProperties();
|
||||
|
@ -142,8 +119,6 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID
|
|||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
results.setIsUnknownID();
|
||||
}
|
||||
_entityTree->unlock();
|
||||
}
|
||||
|
@ -151,67 +126,42 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID
|
|||
return results;
|
||||
}
|
||||
|
||||
EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const EntityItemProperties& properties) {
|
||||
EntityItemID actualID = entityID;
|
||||
// if the entity is unknown, attempt to look it up
|
||||
if (!entityID.isKnownID) {
|
||||
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
|
||||
if (actualID.id != UNKNOWN_ENTITY_ID) {
|
||||
entityID.id = actualID.id;
|
||||
entityID.isKnownID = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a local entity tree set, then also update it. We can do this even if we don't know
|
||||
// the actual id, because we can edit out local entities just with creatorTokenID
|
||||
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& properties) {
|
||||
EntityItemID entityID(id);
|
||||
// If we have a local entity tree set, then also update it.
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForWrite();
|
||||
_entityTree->updateEntity(entityID, properties);
|
||||
_entityTree->unlock();
|
||||
}
|
||||
|
||||
// if at this point, we know the id, send the update to the entity server
|
||||
if (entityID.isKnownID) {
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
if (properties.getType() == EntityTypes::Unknown) {
|
||||
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
// we need to change the outgoing properties, so we make a copy, modify, and send.
|
||||
EntityItemProperties modifiedProperties = properties;
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
modifiedProperties.setType(entity->getType());
|
||||
bidForSimulationOwnership(modifiedProperties);
|
||||
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, modifiedProperties);
|
||||
return entityID;
|
||||
}
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
if (properties.getType() == EntityTypes::Unknown) {
|
||||
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
// we need to change the outgoing properties, so we make a copy, modify, and send.
|
||||
EntityItemProperties modifiedProperties = properties;
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
modifiedProperties.setType(entity->getType());
|
||||
bidForSimulationOwnership(modifiedProperties);
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, modifiedProperties);
|
||||
return id;
|
||||
}
|
||||
|
||||
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
|
||||
}
|
||||
|
||||
return entityID;
|
||||
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, properties);
|
||||
return id;
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {
|
||||
|
||||
EntityItemID actualID = entityID;
|
||||
|
||||
// if the entity is unknown, attempt to look it up
|
||||
if (!entityID.isKnownID) {
|
||||
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
|
||||
if (actualID.id != UNKNOWN_ENTITY_ID) {
|
||||
entityID.id = actualID.id;
|
||||
entityID.isKnownID = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::deleteEntity(QUuid id) {
|
||||
EntityItemID entityID(id);
|
||||
bool shouldDelete = true;
|
||||
|
||||
// If we have a local entity tree set, then also update it.
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForWrite();
|
||||
|
||||
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(actualID));
|
||||
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
if (entity) {
|
||||
if (entity->getLocked()) {
|
||||
shouldDelete = false;
|
||||
|
@ -224,13 +174,13 @@ void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {
|
|||
}
|
||||
|
||||
// if at this point, we know the id, and we should still delete the entity, send the update to the entity server
|
||||
if (shouldDelete && entityID.isKnownID) {
|
||||
if (shouldDelete) {
|
||||
getEntityPacketSender()->queueEraseEntityMessage(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
|
||||
EntityItemID result(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false);
|
||||
QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
|
||||
EntityItemID result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||
|
@ -251,8 +201,8 @@ void EntityScriptingInterface::dumpTree() const {
|
|||
}
|
||||
}
|
||||
|
||||
QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {
|
||||
QVector<EntityItemID> result;
|
||||
QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {
|
||||
QVector<QUuid> result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
QVector<const EntityItem*> entities;
|
||||
|
@ -266,8 +216,8 @@ QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& ce
|
|||
return result;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
|
||||
QVector<EntityItemID> result;
|
||||
QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
|
||||
QVector<QUuid> result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
AABox box(corner, dimensions);
|
||||
|
@ -403,9 +353,8 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
value.intersects = object.property("intersects").toVariant().toBool();
|
||||
value.accurate = object.property("accurate").toVariant().toBool();
|
||||
QScriptValue entityIDValue = object.property("entityID");
|
||||
if (entityIDValue.isValid()) {
|
||||
EntityItemIDfromScriptValue(entityIDValue, value.entityID);
|
||||
}
|
||||
// EntityItemIDfromScriptValue(entityIDValue, value.entityID);
|
||||
quuidFromScriptValue(entityIDValue, value.entityID);
|
||||
QScriptValue entityPropertiesValue = object.property("properties");
|
||||
if (entityPropertiesValue.isValid()) {
|
||||
EntityItemPropertiesFromScriptValue(entityPropertiesValue, value.properties);
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
RayToEntityIntersectionResult();
|
||||
bool intersects;
|
||||
bool accurate;
|
||||
EntityItemID entityID;
|
||||
QUuid entityID;
|
||||
EntityItemProperties properties;
|
||||
float distance;
|
||||
BoxFace face;
|
||||
|
@ -70,34 +70,31 @@ public slots:
|
|||
Q_INVOKABLE bool canRez();
|
||||
|
||||
/// adds a model with the specific properties
|
||||
Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties);
|
||||
|
||||
/// identify a recently created model to determine its true ID
|
||||
Q_INVOKABLE EntityItemID identifyEntity(EntityItemID entityID);
|
||||
Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties);
|
||||
|
||||
/// gets the current model properties for a specific model
|
||||
/// this function will not find return results in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE EntityItemProperties getEntityProperties(EntityItemID entityID);
|
||||
Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID);
|
||||
|
||||
/// edits a model updating only the included properties, will return the identified EntityItemID in case of
|
||||
/// successful edit, if the input entityID is for an unknown model this function will have no effect
|
||||
Q_INVOKABLE EntityItemID editEntity(EntityItemID entityID, const EntityItemProperties& properties);
|
||||
Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties);
|
||||
|
||||
/// deletes a model
|
||||
Q_INVOKABLE void deleteEntity(EntityItemID entityID);
|
||||
Q_INVOKABLE void deleteEntity(QUuid entityID);
|
||||
|
||||
/// finds the closest model to the center point, within the radius
|
||||
/// will return a EntityItemID.isKnownID = false if no models are in the radius
|
||||
/// this function will not find any models in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE EntityItemID findClosestEntity(const glm::vec3& center, float radius) const;
|
||||
Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const;
|
||||
|
||||
/// finds models within the search sphere specified by the center point and radius
|
||||
/// this function will not find any models in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE QVector<EntityItemID> findEntities(const glm::vec3& center, float radius) const;
|
||||
Q_INVOKABLE QVector<QUuid> findEntities(const glm::vec3& center, float radius) const;
|
||||
|
||||
/// finds models within the search sphere specified by the center point and radius
|
||||
/// this function will not find any models in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE QVector<EntityItemID> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
|
||||
Q_INVOKABLE QVector<QUuid> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
|
||||
|
||||
/// If the scripting context has visible entities, this will determine a ray intersection, the results
|
||||
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
|
||||
|
@ -145,7 +142,6 @@ signals:
|
|||
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
|
||||
void clearingEntities();
|
||||
|
||||
private:
|
||||
|
@ -155,7 +151,6 @@ private:
|
|||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking);
|
||||
|
||||
uint32_t _nextCreatorTokenID;
|
||||
EntityTree* _entityTree;
|
||||
};
|
||||
|
||||
|
|
|
@ -65,7 +65,8 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
|||
bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
||||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PacketTypeEntityAddOrEdit:
|
||||
case PacketTypeEntityAdd:
|
||||
case PacketTypeEntityEdit:
|
||||
case PacketTypeEntityErase:
|
||||
return true;
|
||||
default:
|
||||
|
@ -230,19 +231,11 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: This method is used in the client and the server tree. In the client, it's possible to create EntityItems
|
||||
// that do not yet have known IDs. In the server tree however we don't want to have entities without known IDs.
|
||||
bool recordCreationTime = false;
|
||||
if (!entityID.isKnownID) {
|
||||
if (getIsServer()) {
|
||||
qCDebug(entities) << "UNEXPECTED!!! ----- EntityTree::addEntity()... (getIsSever() && !entityID.isKnownID)";
|
||||
return result;
|
||||
}
|
||||
if (properties.getCreated() == UNKNOWN_CREATED_TIME) {
|
||||
// the entity's creation time was not specified in properties, which means this is a NEW entity
|
||||
// and we must record its creation time
|
||||
recordCreationTime = true;
|
||||
}
|
||||
if (properties.getCreated() == UNKNOWN_CREATED_TIME) {
|
||||
// the entity's creation time was not specified in properties, which means this is a NEW entity
|
||||
// and we must record its creation time
|
||||
recordCreationTime = true;
|
||||
}
|
||||
|
||||
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
|
||||
|
@ -375,7 +368,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
// set up the deleted entities ID
|
||||
quint64 deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID().id);
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID());
|
||||
_recentlyDeletedEntitiesLock.unlock();
|
||||
}
|
||||
|
||||
|
@ -389,95 +382,6 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
}
|
||||
}
|
||||
|
||||
/// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs.
|
||||
/// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating
|
||||
/// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to
|
||||
/// the server, and the server eventually sends back to the client two messages that can come in varying order. The first
|
||||
/// message would be a typical query/viewing data message conversation in which the viewer "sees" the newly created entity.
|
||||
/// Those entities that have been seen, will have the authoritative "known ID". Therefore there is a potential that there can
|
||||
/// be two copies of the same entity in the tree: the "local only" "creator token" version of the entity and the "seen"
|
||||
/// "knownID" version of the entity. The server also sends an "entityAdded" message to the client which contains the mapping
|
||||
/// of the creator token to the known ID. These messages can come in any order, so we need to handle the follow cases:
|
||||
///
|
||||
/// Case A: The local edit occurs, the addEntity message arrives, the "viewed data" has not yet arrived.
|
||||
/// In this case, we can expect that our local tree has only one copy of the entity (the creator token),
|
||||
/// and we only really need to fix up that entity with a new version of the ID that includes the knownID
|
||||
///
|
||||
/// Case B: The local edit occurs, the "viewed data" for the new entity arrives, then the addEntity message arrives.
|
||||
/// In this case, we can expect that our local tree has two copies of the entity (the creator token, and the
|
||||
/// known ID version). We end up with two version of the entity because the server sends viewers only the
|
||||
/// known ID version without a creator token. And we don't yet know the mapping until we get the mapping message.
|
||||
/// In this case we need to fix up that entity with a new version of the ID that includes the knownID and
|
||||
/// we need to delete the extra copy of the entity.
|
||||
///
|
||||
/// This method handles both of these cases.
|
||||
///
|
||||
/// NOTE: unlike some operations on the tree, this process does not mark the tree as being changed. This is because
|
||||
/// we're not changing the content of the tree, we're only changing the internal IDs that map entities from creator
|
||||
/// based to known IDs. This means we don't have to recurse the tree to mark the changed path as dirty.
|
||||
void EntityTree::handleAddEntityResponse(const QByteArray& packet) {
|
||||
|
||||
if (!getIsClient()) {
|
||||
qCDebug(entities) << "UNEXPECTED!!! EntityTree::handleAddEntityResponse() with !getIsClient() ***";
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data());
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||
int bytesRead = numBytesPacketHeader;
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
uint32_t creatorTokenID;
|
||||
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
||||
dataAt += sizeof(creatorTokenID);
|
||||
bytesRead += sizeof(creatorTokenID);
|
||||
|
||||
QUuid entityID = QUuid::fromRfc4122(packet.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
|
||||
dataAt += NUM_BYTES_RFC4122_UUID;
|
||||
|
||||
// First, look for the existing entity in the tree..
|
||||
EntityItemID searchEntityID;
|
||||
searchEntityID.id = entityID;
|
||||
searchEntityID.creatorTokenID = creatorTokenID;
|
||||
|
||||
lockForWrite();
|
||||
|
||||
// find the creator token version, it's containing element, and the entity itself
|
||||
EntityItem* foundEntity = NULL;
|
||||
EntityItemID creatorTokenVersion = searchEntityID.convertToCreatorTokenVersion();
|
||||
EntityItemID knownIDVersion = searchEntityID.convertToKnownIDVersion();
|
||||
|
||||
_changedEntityIDs[creatorTokenVersion] = knownIDVersion;
|
||||
|
||||
// First look for and find the "viewed version" of this entity... it's possible we got
|
||||
// the known ID version sent to us between us creating our local version, and getting this
|
||||
// remapping message. If this happened, we actually want to find and delete that version of
|
||||
// the entity.
|
||||
EntityTreeElement* knownIDVersionContainingElement = getContainingElement(knownIDVersion);
|
||||
if (knownIDVersionContainingElement) {
|
||||
foundEntity = knownIDVersionContainingElement->getEntityWithEntityItemID(knownIDVersion);
|
||||
if (foundEntity) {
|
||||
knownIDVersionContainingElement->removeEntityWithEntityItemID(knownIDVersion);
|
||||
setContainingElement(knownIDVersion, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
EntityTreeElement* creatorTokenContainingElement = getContainingElement(creatorTokenVersion);
|
||||
if (creatorTokenContainingElement) {
|
||||
foundEntity = creatorTokenContainingElement->getEntityWithEntityItemID(creatorTokenVersion);
|
||||
if (foundEntity) {
|
||||
creatorTokenContainingElement->updateEntityItemID(creatorTokenVersion, knownIDVersion);
|
||||
setContainingElement(creatorTokenVersion, NULL);
|
||||
setContainingElement(knownIDVersion, creatorTokenContainingElement);
|
||||
|
||||
// because the ID of the entity is switching, we need to emit these signals for any
|
||||
// listeners who care about the changing of IDs
|
||||
emit changingEntityID(creatorTokenVersion, knownIDVersion);
|
||||
}
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
class FindNearPointArgs {
|
||||
public:
|
||||
|
@ -635,30 +539,12 @@ EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /
|
|||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (containingElement) {
|
||||
foundEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!foundEntity && _changedEntityIDs.contains(entityID)) {
|
||||
foundEntity = containingElement->getEntityWithEntityItemID(_changedEntityIDs[entityID]);
|
||||
}
|
||||
}
|
||||
return foundEntity;
|
||||
}
|
||||
|
||||
EntityItemID EntityTree::assignEntityID(const EntityItemID& entityItemID) {
|
||||
if (!getIsServer()) {
|
||||
qCDebug(entities) << "UNEXPECTED!!! assignEntityID should only be called on a server tree. entityItemID:" << entityItemID;
|
||||
return entityItemID;
|
||||
}
|
||||
|
||||
if (getContainingElement(entityItemID)) {
|
||||
qCDebug(entities) << "UNEXPECTED!!! don't call assignEntityID() for existing entityIDs. entityItemID:" << entityItemID;
|
||||
return entityItemID;
|
||||
}
|
||||
|
||||
// The EntityItemID is responsible for assigning actual IDs and keeping track of them.
|
||||
return entityItemID.assignActualIDForToken();
|
||||
}
|
||||
|
||||
int EntityTree::processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
|
||||
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) {
|
||||
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) {
|
||||
|
||||
if (!getIsServer()) {
|
||||
qCDebug(entities) << "UNEXPECTED!!! processEditPacketData() should only be called on a server tree.";
|
||||
|
@ -674,40 +560,34 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
break;
|
||||
}
|
||||
|
||||
case PacketTypeEntityAddOrEdit: {
|
||||
case PacketTypeEntityAdd:
|
||||
case PacketTypeEntityEdit: {
|
||||
EntityItemID entityItemID;
|
||||
EntityItemProperties properties;
|
||||
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength,
|
||||
processedBytes, entityItemID, properties);
|
||||
processedBytes, entityItemID, properties);
|
||||
|
||||
// If we got a valid edit packet, then it could be a new entity or it could be an update to
|
||||
// an existing entity... handle appropriately
|
||||
if (validEditPacket) {
|
||||
|
||||
// If this is a knownID, then it should exist in our tree
|
||||
if (entityItemID.isKnownID) {
|
||||
// search for the entity by EntityItemID
|
||||
EntityItem* existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
|
||||
// search for the entity by EntityItemID
|
||||
EntityItem* existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
if (existingEntity && packetType == PacketTypeEntityEdit) {
|
||||
// if the EntityItem exists, then update it
|
||||
if (existingEntity) {
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
updateEntity(entityItemID, properties, senderNode);
|
||||
existingEntity->markAsChangedOnServer();
|
||||
} else {
|
||||
qCDebug(entities) << "User attempted to edit an unknown entity. ID:" << entityItemID;
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
} else {
|
||||
updateEntity(entityItemID, properties, senderNode);
|
||||
existingEntity->markAsChangedOnServer();
|
||||
} else if (packetType == PacketTypeEntityAdd) {
|
||||
if (senderNode->getCanRez()) {
|
||||
// this is a new entity... assign a new entityID
|
||||
entityItemID = assignEntityID(entityItemID);
|
||||
if (wantEditLogging()) {
|
||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] adding entity.";
|
||||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
properties.setCreated(properties.getLastEdited());
|
||||
EntityItem* newEntity = addEntity(entityItemID, properties);
|
||||
if (newEntity) {
|
||||
newEntity->markAsChangedOnServer();
|
||||
|
@ -720,8 +600,11 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID() << "] attempted to add an entity.";
|
||||
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
|
||||
<< "] attempted to add an entity.";
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1026,66 +909,15 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
|
|||
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ {
|
||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||
EntityTreeElement* element = _entityToElementMap.value(entityItemID);
|
||||
if (!element && entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN){
|
||||
// check the creator token version too...
|
||||
EntityItemID creatorTokenOnly;
|
||||
creatorTokenOnly.id = UNKNOWN_ENTITY_ID;
|
||||
creatorTokenOnly.creatorTokenID = entityItemID.creatorTokenID;
|
||||
creatorTokenOnly.isKnownID = false;
|
||||
element = _entityToElementMap.value(creatorTokenOnly);
|
||||
}
|
||||
|
||||
// If we still didn't find the entity, but the ID was in our changed entityIDs, search for the new ID version
|
||||
if (!element && _changedEntityIDs.contains(entityItemID)) {
|
||||
element = getContainingElement(_changedEntityIDs[entityItemID]);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||
void EntityTree::resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element) {
|
||||
if (entityItemID.id == UNKNOWN_ENTITY_ID) {
|
||||
//assert(entityItemID.id != UNKNOWN_ENTITY_ID);
|
||||
qCDebug(entities) << "UNEXPECTED! resetContainingElement() called with UNKNOWN_ENTITY_ID. entityItemID:" << entityItemID;
|
||||
return;
|
||||
}
|
||||
if (entityItemID.creatorTokenID == UNKNOWN_ENTITY_TOKEN) {
|
||||
//assert(entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN);
|
||||
qCDebug(entities) << "UNEXPECTED! resetContainingElement() called with UNKNOWN_ENTITY_TOKEN. entityItemID:" << entityItemID;
|
||||
return;
|
||||
}
|
||||
if (!element) {
|
||||
//assert(element);
|
||||
qCDebug(entities) << "UNEXPECTED! resetContainingElement() called with NULL element. entityItemID:" << entityItemID;
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the old version with the creatorTokenID
|
||||
EntityItemID creatorTokenVersion;
|
||||
creatorTokenVersion.id = UNKNOWN_ENTITY_ID;
|
||||
creatorTokenVersion.isKnownID = false;
|
||||
creatorTokenVersion.creatorTokenID = entityItemID.creatorTokenID;
|
||||
_entityToElementMap.remove(creatorTokenVersion);
|
||||
|
||||
// set the new version with both creator token and real ID
|
||||
_entityToElementMap[entityItemID] = element;
|
||||
}
|
||||
|
||||
void EntityTree::setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element) {
|
||||
// TODO: do we need to make this thread safe? Or is it acceptable as is
|
||||
|
||||
// If we're a sever side tree, we always remove the creator tokens from our map items
|
||||
EntityItemID storedEntityItemID = entityItemID;
|
||||
|
||||
if (getIsServer()) {
|
||||
storedEntityItemID.creatorTokenID = UNKNOWN_ENTITY_TOKEN;
|
||||
}
|
||||
|
||||
if (element) {
|
||||
_entityToElementMap[storedEntityItemID] = element;
|
||||
_entityToElementMap[entityItemID] = element;
|
||||
} else {
|
||||
_entityToElementMap.remove(storedEntityItemID);
|
||||
_entityToElementMap.remove(entityItemID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1174,14 +1006,14 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
|
|||
|
||||
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
EntityItemID newID(NEW_ENTITY, EntityItemID::getNextCreatorTokenID(), false);
|
||||
EntityItemID newID(QUuid::createUuid());
|
||||
args->newEntityIDs->append(newID);
|
||||
EntityItemProperties properties = entities[i]->getProperties();
|
||||
properties.setPosition(properties.getPosition() + args->root);
|
||||
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
|
||||
|
||||
// queue the packet to send to the server
|
||||
args->packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, newID, properties);
|
||||
args->packetSender->queueEditEntityMessage(PacketTypeEntityAdd, newID, properties);
|
||||
|
||||
// also update the local tree instantly (note: this is not our tree, but an alternate tree)
|
||||
if (args->localTree) {
|
||||
|
@ -1226,7 +1058,7 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
|
||||
EntityItem* entity = addEntity(entityItemID, properties);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "adding Entity failed:" << entityItemID << entity->getType();
|
||||
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,6 @@ public:
|
|||
|
||||
int processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
int processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
|
||||
void handleAddEntityResponse(const QByteArray& packet);
|
||||
|
||||
EntityItemFBXService* getFBXService() const { return _fbxService; }
|
||||
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
|
||||
|
@ -148,7 +147,6 @@ public:
|
|||
|
||||
EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/;
|
||||
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);
|
||||
void resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);
|
||||
void debugDumpMap();
|
||||
virtual void dumpTree();
|
||||
virtual void pruneTree();
|
||||
|
@ -198,7 +196,6 @@ private:
|
|||
EntityItemFBXService* _fbxService;
|
||||
|
||||
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
|
||||
QHash<EntityItemID, EntityItemID> _changedEntityIDs;
|
||||
|
||||
EntitySimulation* _simulation;
|
||||
|
||||
|
|
|
@ -567,19 +567,6 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
|
|||
return false;
|
||||
}
|
||||
|
||||
void EntityTreeElement::updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID) {
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
EntityItem* thisEntity = (*_entityItems)[i];
|
||||
|
||||
EntityItemID thisEntityID = thisEntity->getEntityItemID();
|
||||
|
||||
if (thisEntityID == creatorTokenEntityID) {
|
||||
thisEntity->setID(knownIDEntityID.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const {
|
||||
const EntityItem* closestEntity = NULL;
|
||||
float closestEntityDistance = FLT_MAX;
|
||||
|
|
|
@ -151,9 +151,6 @@ public:
|
|||
bool updateEntity(const EntityItem& entity);
|
||||
void addEntityItem(EntityItem* entity);
|
||||
|
||||
|
||||
void updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID);
|
||||
|
||||
const EntityItem* getClosestEntity(glm::vec3 position) const;
|
||||
|
||||
/// finds all entities that touch a sphere
|
||||
|
|
|
@ -46,7 +46,7 @@ void LightEntityItem::setDimensions(const glm::vec3& value) {
|
|||
// If we are a spotlight, treat the z value as our radius or length, and
|
||||
// recalculate the x/y dimensions to properly encapsulate the spotlight.
|
||||
const float length = value.z;
|
||||
const float width = length * glm::tan(glm::radians(_cutoff));
|
||||
const float width = length * glm::sin(glm::radians(_cutoff));
|
||||
_dimensions = glm::vec3(width, width, length);
|
||||
} else {
|
||||
float maxDimension = glm::max(value.x, value.y, value.z);
|
||||
|
@ -73,7 +73,7 @@ void LightEntityItem::setIsSpotlight(bool value) {
|
|||
|
||||
if (_isSpotlight) {
|
||||
const float length = _dimensions.z;
|
||||
const float width = length * glm::tan(glm::radians(_cutoff));
|
||||
const float width = length * glm::sin(glm::radians(_cutoff));
|
||||
_dimensions = glm::vec3(width, width, length);
|
||||
} else {
|
||||
float maxDimension = glm::max(_dimensions.x, _dimensions.y, _dimensions.z);
|
||||
|
@ -89,7 +89,7 @@ void LightEntityItem::setCutoff(float value) {
|
|||
// If we are a spotlight, adjusting the cutoff will affect the area we encapsulate,
|
||||
// so update the dimensions to reflect this.
|
||||
const float length = _dimensions.z;
|
||||
const float width = length * glm::tan(glm::radians(_cutoff));
|
||||
const float width = length * glm::sin(glm::radians(_cutoff));
|
||||
_dimensions = glm::vec3(width, width, length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,46 +55,46 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
|
|||
static bool firstCall = true;
|
||||
if (firstCall) {
|
||||
NodeType::init();
|
||||
|
||||
|
||||
// register the SharedNodePointer meta-type for signals/slots
|
||||
qRegisterMetaType<SharedNodePointer>();
|
||||
firstCall = false;
|
||||
}
|
||||
|
||||
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);
|
||||
qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||
|
||||
|
||||
if (dtlsListenPort > 0) {
|
||||
// only create the DTLS socket during constructor if a custom port is passed
|
||||
_dtlsSocket = new QUdpSocket(this);
|
||||
|
||||
|
||||
_dtlsSocket->bind(QHostAddress::AnyIPv4, dtlsListenPort);
|
||||
qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
|
||||
}
|
||||
|
||||
|
||||
const int LARGER_BUFFER_SIZE = 1048576;
|
||||
changeSocketBufferSizes(LARGER_BUFFER_SIZE);
|
||||
|
||||
|
||||
// check for local socket updates every so often
|
||||
const int LOCAL_SOCKET_UPDATE_INTERVAL_MSECS = 5 * 1000;
|
||||
QTimer* localSocketUpdate = new QTimer(this);
|
||||
connect(localSocketUpdate, &QTimer::timeout, this, &LimitedNodeList::updateLocalSockAddr);
|
||||
localSocketUpdate->start(LOCAL_SOCKET_UPDATE_INTERVAL_MSECS);
|
||||
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, &QTimer::timeout, this, &LimitedNodeList::removeSilentNodes);
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS);
|
||||
|
||||
|
||||
// check the local socket right now
|
||||
updateLocalSockAddr();
|
||||
|
||||
|
||||
_packetStatTimer.start();
|
||||
}
|
||||
|
||||
void LimitedNodeList::setSessionUUID(const QUuid& sessionUUID) {
|
||||
QUuid oldUUID = _sessionUUID;
|
||||
_sessionUUID = sessionUUID;
|
||||
|
||||
|
||||
if (sessionUUID != oldUUID) {
|
||||
qCDebug(networking) << "NodeList UUID changed from" << uuidStringWithoutCurlyBraces(oldUUID)
|
||||
<< "to" << uuidStringWithoutCurlyBraces(_sessionUUID);
|
||||
|
@ -120,16 +120,16 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
|||
if (!_dtlsSocket) {
|
||||
// DTLS socket getter called but no DTLS socket exists, create it now
|
||||
_dtlsSocket = new QUdpSocket(this);
|
||||
|
||||
|
||||
_dtlsSocket->bind(QHostAddress::AnyIPv4, 0, QAbstractSocket::DontShareAddress);
|
||||
|
||||
|
||||
// we're using DTLS and our socket is good to go, so make the required DTLS changes
|
||||
// DTLS requires that IP_DONTFRAG be set
|
||||
// This is not accessible on some platforms (OS X) so we need to make sure DTLS still works without it
|
||||
|
||||
|
||||
qCDebug(networking) << "LimitedNodeList DTLS socket is listening on" << _dtlsSocket->localPort();
|
||||
}
|
||||
|
||||
|
||||
return *_dtlsSocket;
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ void LimitedNodeList::changeSocketBufferSizes(int numBytes) {
|
|||
if (i == 0) {
|
||||
bufferOpt = QAbstractSocket::SendBufferSizeSocketOption;
|
||||
bufferTypeString = "send";
|
||||
|
||||
|
||||
} else {
|
||||
bufferOpt = QAbstractSocket::ReceiveBufferSizeSocketOption;
|
||||
bufferTypeString = "receive";
|
||||
|
@ -148,7 +148,7 @@ void LimitedNodeList::changeSocketBufferSizes(int numBytes) {
|
|||
int oldBufferSize = _nodeSocket.socketOption(bufferOpt).toInt();
|
||||
if (oldBufferSize < numBytes) {
|
||||
int newBufferSize = _nodeSocket.socketOption(bufferOpt).toInt();
|
||||
|
||||
|
||||
qCDebug(networking) << "Changed socket" << bufferTypeString << "buffer size from" << oldBufferSize << "to"
|
||||
<< newBufferSize << "bytes";
|
||||
} else {
|
||||
|
@ -162,13 +162,13 @@ void LimitedNodeList::changeSocketBufferSizes(int numBytes) {
|
|||
bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
||||
PacketType checkType = packetTypeForPacket(packet);
|
||||
int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());
|
||||
|
||||
|
||||
if (packet[numPacketTypeBytes] != versionForPacketType(checkType)
|
||||
&& checkType != PacketTypeStunResponse) {
|
||||
PacketType mismatchType = packetTypeForPacket(packet);
|
||||
|
||||
|
||||
static QMultiMap<QUuid, PacketType> versionDebugSuppressMap;
|
||||
|
||||
|
||||
QUuid senderUUID = uuidFromPacketHeader(packet);
|
||||
if (!versionDebugSuppressMap.contains(senderUUID, checkType)) {
|
||||
qCDebug(networking) << "Packet version mismatch on" << packetTypeForPacket(packet) << "- Sender"
|
||||
|
@ -176,13 +176,13 @@ bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
|||
<< qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";
|
||||
|
||||
emit packetVersionMismatch();
|
||||
|
||||
|
||||
versionDebugSuppressMap.insert(senderUUID, checkType);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!NON_VERIFIED_PACKETS.contains(checkType)) {
|
||||
// figure out which node this is from
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
|
@ -192,26 +192,26 @@ bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
|||
return true;
|
||||
} else {
|
||||
static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
|
||||
|
||||
|
||||
QUuid senderUUID = uuidFromPacketHeader(packet);
|
||||
if (!hashDebugSuppressMap.contains(senderUUID, checkType)) {
|
||||
qCDebug(networking) << "Packet hash mismatch on" << checkType << "- Sender"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
|
||||
|
||||
hashDebugSuppressMap.insert(senderUUID, checkType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID");
|
||||
|
||||
|
||||
qCDebug(networking) << "Packet of type" << checkType << "received from unknown node with UUID"
|
||||
<< qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet)));
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* a
|
|||
} else {
|
||||
emit dataReceived(NodeType::Unassigned, incomingPacket.size());
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -235,14 +235,14 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSock
|
|||
// stat collection for packets
|
||||
++_numCollectedPackets;
|
||||
_numCollectedBytes += datagram.size();
|
||||
|
||||
|
||||
qint64 bytesWritten = _nodeSocket.writeDatagram(datagram,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
|
||||
if (bytesWritten < 0) {
|
||||
qCDebug(networking) << "ERROR in writeDatagram:" << _nodeSocket.error() << "-" << _nodeSocket.errorString();
|
||||
}
|
||||
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
|
|||
}
|
||||
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
|
||||
// if we're here and the connection secret is null, debug out - this could be a problem
|
||||
if (destinationNode->getConnectionSecret().isNull()) {
|
||||
qDebug() << "LimitedNodeList::writeDatagram called for verified datagram with null connection secret for"
|
||||
|
@ -278,21 +278,21 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
|
|||
}
|
||||
|
||||
// perform replacement of hash and optionally also sequence number in the header
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
PacketSequenceNumber sequenceNumber = getNextSequenceNumberForPacket(destinationNode->getUUID(), packetType);
|
||||
replaceHashAndSequenceNumberInPacket(datagramCopy, destinationNode->getConnectionSecret(),
|
||||
sequenceNumber, packetType);
|
||||
} else {
|
||||
replaceHashInPacket(datagramCopy, destinationNode->getConnectionSecret(), packetType);
|
||||
}
|
||||
|
||||
|
||||
emit dataSent(destinationNode->getType(), datagram.size());
|
||||
auto bytesWritten = writeDatagram(datagramCopy, *destinationSockAddr);
|
||||
// Keep track of per-destination-node bandwidth
|
||||
destinationNode->recordBytesSent(bytesWritten);
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
// didn't have a destinationNode to send to, return 0
|
||||
return 0;
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, cons
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PacketType packetType = packetTypeForPacket(datagram);
|
||||
|
||||
// optionally peform sequence number replacement in the header
|
||||
|
@ -328,7 +328,7 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const QByteArray& datagram, cons
|
|||
return writeDatagram(datagram, *destinationSockAddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// didn't have a destinationNode to send to, return 0
|
||||
return 0;
|
||||
}
|
||||
|
@ -348,11 +348,11 @@ qint64 LimitedNodeList::writeUnverifiedDatagram(const char* data, qint64 size, c
|
|||
}
|
||||
|
||||
PacketSequenceNumber LimitedNodeList::getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType) {
|
||||
// Thanks to std::map and std::unordered_map this line either default constructs the
|
||||
// Thanks to std::map and std::unordered_map this line either default constructs the
|
||||
// PacketTypeSequenceMap and the PacketSequenceNumber or returns the existing value.
|
||||
// We use the postfix increment so that the stored value is incremented and the next
|
||||
// We use the postfix increment so that the stored value is incremented and the next
|
||||
// return gives the correct value.
|
||||
|
||||
|
||||
return _packetSequenceNumbers[nodeUUID][packetType]++;
|
||||
}
|
||||
|
||||
|
@ -367,21 +367,21 @@ void LimitedNodeList::processNodeData(const HifiSockAddr& senderSockAddr, const
|
|||
|
||||
int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& matchingNode, const QByteArray &packet) {
|
||||
QMutexLocker locker(&matchingNode->getMutex());
|
||||
|
||||
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
|
||||
// if this was a sequence numbered packet we should store the last seq number for
|
||||
// a packet of this type for this node
|
||||
PacketType packetType = packetTypeForPacket(packet);
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
matchingNode->setLastSequenceNumberForPacketType(sequenceNumberFromHeader(packet, packetType), packetType);
|
||||
}
|
||||
|
||||
|
||||
NodeData* linkedData = matchingNode->getLinkedData();
|
||||
if (!linkedData && linkedDataCreateCallback) {
|
||||
linkedDataCreateCallback(matchingNode.data());
|
||||
}
|
||||
|
||||
|
||||
if (linkedData) {
|
||||
QMutexLocker linkedDataLocker(&linkedData->getMutex());
|
||||
return linkedData->parseData(packet);
|
||||
|
@ -391,42 +391,42 @@ int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& match
|
|||
|
||||
int LimitedNodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) {
|
||||
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
|
||||
|
||||
|
||||
if (matchingNode) {
|
||||
return updateNodeWithDataFromPacket(matchingNode, packet);
|
||||
}
|
||||
|
||||
|
||||
// we weren't able to match the sender address to the address we have for this node, unlock and don't parse
|
||||
return 0;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::nodeWithUUID(const QUuid& nodeUUID) {
|
||||
QReadLocker readLocker(&_nodeMutex);
|
||||
|
||||
|
||||
NodeHash::const_iterator it = _nodeHash.find(nodeUUID);
|
||||
return it == _nodeHash.cend() ? SharedNodePointer() : it->second;
|
||||
}
|
||||
|
||||
SharedNodePointer LimitedNodeList::sendingNodeForPacket(const QByteArray& packet) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(packet);
|
||||
|
||||
|
||||
// return the matching node, or NULL if there is no match
|
||||
return nodeWithUUID(nodeUUID);
|
||||
}
|
||||
|
||||
void LimitedNodeList::eraseAllNodes() {
|
||||
qCDebug(networking) << "Clearing the NodeList. Deleting all nodes in list.";
|
||||
|
||||
|
||||
QSet<SharedNodePointer> killedNodes;
|
||||
eachNode([&killedNodes](const SharedNodePointer& node){
|
||||
killedNodes.insert(node);
|
||||
});
|
||||
|
||||
|
||||
// iterate the current nodes, emit that they are dying and remove them from the hash
|
||||
_nodeMutex.lockForWrite();
|
||||
_nodeHash.clear();
|
||||
_nodeMutex.unlock();
|
||||
|
||||
|
||||
foreach(const SharedNodePointer& killedNode, killedNodes) {
|
||||
handleNodeKill(killedNode);
|
||||
}
|
||||
|
@ -438,17 +438,17 @@ void LimitedNodeList::reset() {
|
|||
|
||||
void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) {
|
||||
_nodeMutex.lockForRead();
|
||||
|
||||
|
||||
NodeHash::iterator it = _nodeHash.find(nodeUUID);
|
||||
if (it != _nodeHash.end()) {
|
||||
SharedNodePointer matchingNode = it->second;
|
||||
|
||||
|
||||
_nodeMutex.unlock();
|
||||
|
||||
|
||||
_nodeMutex.lockForWrite();
|
||||
_nodeHash.unsafe_erase(it);
|
||||
_nodeMutex.unlock();
|
||||
|
||||
|
||||
handleNodeKill(matchingNode);
|
||||
} else {
|
||||
_nodeMutex.unlock();
|
||||
|
@ -472,34 +472,34 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool canAdjustLocks, bool canRez) {
|
||||
NodeHash::const_iterator it = _nodeHash.find(uuid);
|
||||
|
||||
|
||||
if (it != _nodeHash.end()) {
|
||||
SharedNodePointer& matchingNode = it->second;
|
||||
|
||||
|
||||
matchingNode->setPublicSocket(publicSocket);
|
||||
matchingNode->setLocalSocket(localSocket);
|
||||
matchingNode->setCanAdjustLocks(canAdjustLocks);
|
||||
matchingNode->setCanRez(canRez);
|
||||
|
||||
|
||||
return matchingNode;
|
||||
} else {
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez);
|
||||
SharedNodePointer newNodePointer(newNode);
|
||||
|
||||
|
||||
_nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer));
|
||||
|
||||
|
||||
qCDebug(networking) << "Added" << *newNode;
|
||||
|
||||
|
||||
emit nodeAdded(newNodePointer);
|
||||
|
||||
|
||||
return newNodePointer;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned LimitedNodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
||||
unsigned n = 0;
|
||||
|
||||
|
||||
eachNode([&](const SharedNodePointer& node){
|
||||
if (destinationNodeTypes.contains(node->getType())) {
|
||||
writeDatagram(packet, node);
|
||||
|
@ -516,35 +516,35 @@ QByteArray LimitedNodeList::constructPingPacket(PingType_t pingType, bool isVeri
|
|||
|
||||
QByteArray pingPacket = byteArrayWithUUIDPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing,
|
||||
packetUUID);
|
||||
|
||||
|
||||
QDataStream packetStream(&pingPacket, QIODevice::Append);
|
||||
|
||||
|
||||
packetStream << pingType;
|
||||
packetStream << usecTimestampNow();
|
||||
|
||||
|
||||
return pingPacket;
|
||||
}
|
||||
|
||||
QByteArray LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID) {
|
||||
QDataStream pingPacketStream(pingPacket);
|
||||
pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket));
|
||||
|
||||
|
||||
PingType_t typeFromOriginalPing;
|
||||
pingPacketStream >> typeFromOriginalPing;
|
||||
|
||||
|
||||
quint64 timeFromOriginalPing;
|
||||
pingPacketStream >> timeFromOriginalPing;
|
||||
|
||||
|
||||
PacketType replyType = (packetTypeForPacket(pingPacket) == PacketTypePing)
|
||||
? PacketTypePingReply : PacketTypeUnverifiedPingReply;
|
||||
|
||||
|
||||
QUuid packetUUID = packetHeaderID.isNull() ? _sessionUUID : packetHeaderID;
|
||||
|
||||
QByteArray replyPacket = byteArrayWithUUIDPopulatedHeader(replyType, packetUUID);
|
||||
QDataStream packetStream(&replyPacket, QIODevice::Append);
|
||||
|
||||
|
||||
packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow();
|
||||
|
||||
|
||||
return replyPacket;
|
||||
}
|
||||
|
||||
|
@ -566,9 +566,9 @@ void LimitedNodeList::resetPacketStats() {
|
|||
}
|
||||
|
||||
void LimitedNodeList::removeSilentNodes() {
|
||||
|
||||
|
||||
QSet<SharedNodePointer> killedNodes;
|
||||
|
||||
|
||||
eachNodeHashIterator([&](NodeHash::iterator& it){
|
||||
SharedNodePointer node = it->second;
|
||||
node->getMutex().lock();
|
||||
|
@ -582,10 +582,10 @@ void LimitedNodeList::removeSilentNodes() {
|
|||
// we didn't erase this node, push the iterator forwards
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
node->getMutex().unlock();
|
||||
});
|
||||
|
||||
|
||||
foreach(const SharedNodePointer& killedNode, killedNodes) {
|
||||
handleNodeKill(killedNode);
|
||||
}
|
||||
|
@ -595,38 +595,38 @@ const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442;
|
|||
const int NUM_BYTES_STUN_HEADER = 20;
|
||||
|
||||
void LimitedNodeList::sendSTUNRequest() {
|
||||
|
||||
|
||||
unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER];
|
||||
|
||||
|
||||
int packetIndex = 0;
|
||||
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||
|
||||
|
||||
// leading zeros + message type
|
||||
const uint16_t REQUEST_MESSAGE_TYPE = htons(0x0001);
|
||||
memcpy(stunRequestPacket + packetIndex, &REQUEST_MESSAGE_TYPE, sizeof(REQUEST_MESSAGE_TYPE));
|
||||
packetIndex += sizeof(REQUEST_MESSAGE_TYPE);
|
||||
|
||||
|
||||
// message length (no additional attributes are included)
|
||||
uint16_t messageLength = 0;
|
||||
memcpy(stunRequestPacket + packetIndex, &messageLength, sizeof(messageLength));
|
||||
packetIndex += sizeof(messageLength);
|
||||
|
||||
|
||||
memcpy(stunRequestPacket + packetIndex, &RFC_5389_MAGIC_COOKIE_NETWORK_ORDER, sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER));
|
||||
packetIndex += sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
|
||||
|
||||
// transaction ID (random 12-byte unsigned integer)
|
||||
const uint NUM_TRANSACTION_ID_BYTES = 12;
|
||||
QUuid randomUUID = QUuid::createUuid();
|
||||
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
|
||||
|
||||
|
||||
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||
_stunSockAddr.getAddress(), _stunSockAddr.getPort());
|
||||
}
|
||||
|
||||
void LimitedNodeList::rebindNodeSocket() {
|
||||
quint16 oldPort = _nodeSocket.localPort();
|
||||
|
||||
|
||||
_nodeSocket.close();
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, oldPort);
|
||||
}
|
||||
|
@ -636,125 +636,125 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) {
|
|||
// and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
|
||||
const int NUM_BYTES_MESSAGE_TYPE_AND_LENGTH = 4;
|
||||
const uint16_t XOR_MAPPED_ADDRESS_TYPE = htons(0x0020);
|
||||
|
||||
|
||||
const uint32_t RFC_5389_MAGIC_COOKIE_NETWORK_ORDER = htonl(RFC_5389_MAGIC_COOKIE);
|
||||
|
||||
|
||||
int attributeStartIndex = NUM_BYTES_STUN_HEADER;
|
||||
|
||||
|
||||
if (memcmp(packet.data() + NUM_BYTES_MESSAGE_TYPE_AND_LENGTH,
|
||||
&RFC_5389_MAGIC_COOKIE_NETWORK_ORDER,
|
||||
sizeof(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER)) == 0) {
|
||||
|
||||
|
||||
// enumerate the attributes to find XOR_MAPPED_ADDRESS_TYPE
|
||||
while (attributeStartIndex < packet.size()) {
|
||||
if (memcmp(packet.data() + attributeStartIndex, &XOR_MAPPED_ADDRESS_TYPE, sizeof(XOR_MAPPED_ADDRESS_TYPE)) == 0) {
|
||||
const int NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH = 4;
|
||||
const int NUM_BYTES_FAMILY_ALIGN = 1;
|
||||
const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN;
|
||||
|
||||
|
||||
uint8_t addressFamily = 0;
|
||||
memcpy(&addressFamily, packet.data() + byteIndex, sizeof(addressFamily));
|
||||
|
||||
|
||||
byteIndex += sizeof(addressFamily);
|
||||
|
||||
|
||||
if (addressFamily == IPV4_FAMILY_NETWORK_ORDER) {
|
||||
// grab the X-Port
|
||||
uint16_t xorMappedPort = 0;
|
||||
memcpy(&xorMappedPort, packet.data() + byteIndex, sizeof(xorMappedPort));
|
||||
|
||||
|
||||
uint16_t newPublicPort = ntohs(xorMappedPort) ^ (ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER) >> 16);
|
||||
|
||||
|
||||
byteIndex += sizeof(xorMappedPort);
|
||||
|
||||
|
||||
// grab the X-Address
|
||||
uint32_t xorMappedAddress = 0;
|
||||
memcpy(&xorMappedAddress, packet.data() + byteIndex, sizeof(xorMappedAddress));
|
||||
|
||||
|
||||
uint32_t stunAddress = ntohl(xorMappedAddress) ^ ntohl(RFC_5389_MAGIC_COOKIE_NETWORK_ORDER);
|
||||
|
||||
|
||||
QHostAddress newPublicAddress = QHostAddress(stunAddress);
|
||||
|
||||
|
||||
if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) {
|
||||
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
|
||||
|
||||
|
||||
qCDebug(networking, "New public socket received from STUN server is %s:%hu",
|
||||
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
|
||||
_publicSockAddr.getPort());
|
||||
|
||||
emit publicSockAddrChanged(_publicSockAddr);
|
||||
|
||||
emit publicSockAddrChanged(_publicSockAddr);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// push forward attributeStartIndex by the length of this attribute
|
||||
const int NUM_BYTES_ATTRIBUTE_TYPE = 2;
|
||||
|
||||
|
||||
uint16_t attributeLength = 0;
|
||||
memcpy(&attributeLength, packet.data() + attributeStartIndex + NUM_BYTES_ATTRIBUTE_TYPE,
|
||||
sizeof(attributeLength));
|
||||
attributeLength = ntohs(attributeLength);
|
||||
|
||||
|
||||
attributeStartIndex += NUM_BYTES_MESSAGE_TYPE_AND_LENGTH + attributeLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LimitedNodeList::updateLocalSockAddr() {
|
||||
HifiSockAddr newSockAddr(getLocalAddress(), _nodeSocket.localPort());
|
||||
if (newSockAddr != _localSockAddr) {
|
||||
|
||||
|
||||
if (_localSockAddr.isNull()) {
|
||||
qCDebug(networking) << "Local socket is" << newSockAddr;
|
||||
} else {
|
||||
qCDebug(networking) << "Local socket has changed from" << _localSockAddr << "to" << newSockAddr;
|
||||
}
|
||||
|
||||
|
||||
_localSockAddr = newSockAddr;
|
||||
|
||||
|
||||
emit localSockAddrChanged(_localSockAddr);
|
||||
}
|
||||
}
|
||||
|
||||
void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr,
|
||||
QUuid headerID, const QUuid& connectionRequestID) {
|
||||
|
||||
|
||||
if (headerID.isNull()) {
|
||||
headerID = _sessionUUID;
|
||||
}
|
||||
|
||||
|
||||
QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(PacketTypeIceServerHeartbeat, headerID);
|
||||
QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append);
|
||||
|
||||
|
||||
iceDataStream << _publicSockAddr << _localSockAddr;
|
||||
|
||||
|
||||
if (!connectionRequestID.isNull()) {
|
||||
iceDataStream << connectionRequestID;
|
||||
|
||||
|
||||
qCDebug(networking) << "Sending packet to ICE server to request connection info for peer with ID"
|
||||
<< uuidStringWithoutCurlyBraces(connectionRequestID);
|
||||
}
|
||||
|
||||
|
||||
writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr);
|
||||
}
|
||||
|
||||
void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* parent, quint16 localPort) {
|
||||
// save our local port to shared memory so that assignment client children know how to talk to this parent
|
||||
QSharedMemory* sharedPortMem = new QSharedMemory(key, parent);
|
||||
|
||||
|
||||
// attempt to create the shared memory segment
|
||||
if (sharedPortMem->create(sizeof(localPort)) || sharedPortMem->attach()) {
|
||||
sharedPortMem->lock();
|
||||
memcpy(sharedPortMem->data(), &localPort, sizeof(localPort));
|
||||
sharedPortMem->unlock();
|
||||
|
||||
|
||||
qCDebug(networking) << "Wrote local listening port" << localPort << "to shared memory at key" << key;
|
||||
} else {
|
||||
qWarning() << "Failed to create and attach to shared memory to share local port with assignment-client children.";
|
||||
|
@ -762,22 +762,15 @@ void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* p
|
|||
}
|
||||
|
||||
|
||||
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, QSharedMemory*& sharedMem,
|
||||
quint16& localPort) {
|
||||
if (!sharedMem) {
|
||||
sharedMem = new QSharedMemory(key, this);
|
||||
|
||||
if (!sharedMem->attach(QSharedMemory::ReadOnly)) {
|
||||
qWarning() << "Could not attach to shared memory at key" << key;
|
||||
}
|
||||
}
|
||||
|
||||
if (sharedMem->isAttached()) {
|
||||
sharedMem->lock();
|
||||
memcpy(&localPort, sharedMem->data(), sizeof(localPort));
|
||||
sharedMem->unlock();
|
||||
bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quint16& localPort) {
|
||||
QSharedMemory sharedMem(key);
|
||||
if (!sharedMem.attach(QSharedMemory::ReadOnly)) {
|
||||
qWarning() << "Could not attach to shared memory at key" << key;
|
||||
return false;
|
||||
} else {
|
||||
sharedMem.lock();
|
||||
memcpy(&localPort, sharedMem.data(), sizeof(localPort));
|
||||
sharedMem.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ namespace PingType {
|
|||
class LimitedNodeList : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
|
||||
public:
|
||||
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
|
@ -89,22 +89,22 @@ public:
|
|||
|
||||
bool getThisNodeCanRez() const { return _thisNodeCanRez; }
|
||||
void setThisNodeCanRez(bool canRez);
|
||||
|
||||
|
||||
void rebindNodeSocket();
|
||||
QUdpSocket& getNodeSocket() { return _nodeSocket; }
|
||||
QUdpSocket& getDTLSSocket();
|
||||
|
||||
|
||||
bool packetVersionAndHashMatch(const QByteArray& packet);
|
||||
|
||||
QByteArray byteArrayWithPopulatedHeader(PacketType packetType)
|
||||
QByteArray byteArrayWithPopulatedHeader(PacketType packetType)
|
||||
{ return byteArrayWithUUIDPopulatedHeader(packetType, _sessionUUID); }
|
||||
int populatePacketHeader(QByteArray& packet, PacketType packetType)
|
||||
int populatePacketHeader(QByteArray& packet, PacketType packetType)
|
||||
{ return populatePacketHeaderWithUUID(packet, packetType, _sessionUUID); }
|
||||
int populatePacketHeader(char* packet, PacketType packetType)
|
||||
int populatePacketHeader(char* packet, PacketType packetType)
|
||||
{ return populatePacketHeaderWithUUID(packet, packetType, _sessionUUID); }
|
||||
|
||||
qint64 readDatagram(QByteArray& incomingPacket, QHostAddress* address, quint16 * port);
|
||||
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
|
||||
|
@ -120,16 +120,16 @@ public:
|
|||
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
|
||||
|
||||
void (*linkedDataCreateCallback)(Node *);
|
||||
|
||||
|
||||
int size() const { return _nodeHash.size(); }
|
||||
|
||||
SharedNodePointer nodeWithUUID(const QUuid& nodeUUID);
|
||||
SharedNodePointer sendingNodeForPacket(const QByteArray& packet);
|
||||
|
||||
|
||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket,
|
||||
bool canAdjustLocks, bool canRez);
|
||||
|
||||
|
||||
const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; }
|
||||
const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; }
|
||||
|
||||
|
@ -144,21 +144,21 @@ public:
|
|||
|
||||
void getPacketStats(float &packetsPerSecond, float &bytesPerSecond);
|
||||
void resetPacketStats();
|
||||
|
||||
|
||||
QByteArray constructPingPacket(PingType_t pingType = PingType::Agnostic, bool isVerified = true,
|
||||
const QUuid& packetHeaderID = QUuid());
|
||||
QByteArray constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID = QUuid());
|
||||
|
||||
|
||||
virtual void sendSTUNRequest();
|
||||
virtual bool processSTUNResponse(const QByteArray& packet);
|
||||
|
||||
|
||||
void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr,
|
||||
QUuid headerID = QUuid(), const QUuid& connectRequestID = QUuid());
|
||||
|
||||
|
||||
template<typename NodeLambda>
|
||||
void eachNode(NodeLambda functor) {
|
||||
QReadLocker readLock(&_nodeMutex);
|
||||
|
||||
|
||||
for (NodeHash::const_iterator it = _nodeHash.cbegin(); it != _nodeHash.cend(); ++it) {
|
||||
functor(it->second);
|
||||
}
|
||||
|
@ -178,44 +178,44 @@ public:
|
|||
template<typename BreakableNodeLambda>
|
||||
void eachNodeBreakable(BreakableNodeLambda functor) {
|
||||
QReadLocker readLock(&_nodeMutex);
|
||||
|
||||
|
||||
for (NodeHash::const_iterator it = _nodeHash.cbegin(); it != _nodeHash.cend(); ++it) {
|
||||
if (!functor(it->second)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename PredLambda>
|
||||
SharedNodePointer nodeMatchingPredicate(const PredLambda predicate) {
|
||||
QReadLocker readLock(&_nodeMutex);
|
||||
|
||||
|
||||
for (NodeHash::const_iterator it = _nodeHash.cbegin(); it != _nodeHash.cend(); ++it) {
|
||||
if (predicate(it->second)) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
void putLocalPortIntoSharedMemory(const QString key, QObject* parent, quint16 localPort);
|
||||
bool getLocalServerPortFromSharedMemory(const QString key, QSharedMemory*& sharedMem, quint16& localPort);
|
||||
|
||||
bool getLocalServerPortFromSharedMemory(const QString key, quint16& localPort);
|
||||
|
||||
public slots:
|
||||
void reset();
|
||||
void eraseAllNodes();
|
||||
|
||||
|
||||
void removeSilentNodes();
|
||||
|
||||
|
||||
void updateLocalSockAddr();
|
||||
|
||||
|
||||
void killNodeWithUUID(const QUuid& nodeUUID);
|
||||
signals:
|
||||
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
|
||||
void nodeAdded(SharedNodePointer);
|
||||
void nodeKilled(SharedNodePointer);
|
||||
|
||||
|
||||
void localSockAddrChanged(const HifiSockAddr& localSockAddr);
|
||||
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
|
||||
|
||||
|
@ -226,18 +226,18 @@ signals:
|
|||
void dataReceived(const quint8 channel_type, const int bytes);
|
||||
|
||||
void packetVersionMismatch();
|
||||
|
||||
|
||||
protected:
|
||||
LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
||||
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr);
|
||||
|
||||
PacketSequenceNumber getNextSequenceNumberForPacket(const QUuid& nodeUUID, PacketType packetType);
|
||||
|
||||
|
||||
void changeSocketBufferSizes(int numBytes);
|
||||
|
||||
|
||||
void handleNodeKill(const SharedNodePointer& node);
|
||||
|
||||
QUuid _sessionUUID;
|
||||
|
@ -258,17 +258,17 @@ protected:
|
|||
bool _thisNodeCanRez;
|
||||
|
||||
std::unordered_map<QUuid, PacketTypeSequenceMap, UUIDHasher> _packetSequenceNumbers;
|
||||
|
||||
|
||||
template<typename IteratorLambda>
|
||||
void eachNodeHashIterator(IteratorLambda functor) {
|
||||
QWriteLocker writeLock(&_nodeMutex);
|
||||
NodeHash::iterator it = _nodeHash.begin();
|
||||
|
||||
|
||||
while (it != _nodeHash.end()) {
|
||||
functor(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LimitedNodeList_h
|
||||
|
|
|
@ -335,12 +335,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
if (_domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
|
||||
|| _domainHandler.getHostname() == "localhost") {
|
||||
|
||||
static QSharedMemory* localDSPortSharedMem = NULL;
|
||||
|
||||
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY,
|
||||
localDSPortSharedMem,
|
||||
domainPort);
|
||||
getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, domainPort);
|
||||
qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
|
||||
_domainHandler.setPort(domainPort);
|
||||
}
|
||||
|
|
|
@ -70,9 +70,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return 1;
|
||||
case PacketTypeStopNode:
|
||||
return 1;
|
||||
case PacketTypeEntityAddOrEdit:
|
||||
case PacketTypeEntityAdd:
|
||||
case PacketTypeEntityEdit:
|
||||
case PacketTypeEntityData:
|
||||
return VERSION_ENTITIES_HAVE_FRICTION;
|
||||
return VERSION_NO_ENTITY_ID_SWAP;
|
||||
case PacketTypeEntityErase:
|
||||
return 2;
|
||||
case PacketTypeAudioStreamStats:
|
||||
|
@ -86,49 +87,50 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
|
||||
QString nameForPacketType(PacketType packetType) {
|
||||
switch (packetType) {
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnknown);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeStunResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainList);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypePing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypePingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeKillAvatar);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeInjectAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMixedAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMicrophoneAudioNoEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMicrophoneAudioWithEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeBulkAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSilentAudioFrame);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEnvironmentData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectionDenied);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdiction);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdictionRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarIdentity);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarBillboard);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainServerRequireDTLS);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeNodeJsonStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAddOrEdit);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityErase);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAddResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeDataNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeStopNode);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeat);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeatResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnknown);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeStunResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainList);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypePing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypePingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeKillAvatar);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeInjectAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMixedAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMicrophoneAudioNoEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMicrophoneAudioWithEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeBulkAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSilentAudioFrame);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEnvironmentData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectionDenied);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdiction);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdictionRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarIdentity);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarBillboard);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainServerRequireDTLS);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeNodeJsonStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityErase);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeDataNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeStopNode);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeat);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeatResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAdd);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEdit);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleEntitiesFix);
|
||||
default:
|
||||
return QString("Type: ") + QString::number((int)packetType);
|
||||
}
|
||||
|
@ -166,14 +168,14 @@ int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUui
|
|||
memset(position, 0, NUM_BYTES_MD5_HASH);
|
||||
position += NUM_BYTES_MD5_HASH;
|
||||
}
|
||||
|
||||
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
// Pack zeros for the number of bytes that the sequence number requires.
|
||||
// The LimitedNodeList will handle packing in the sequence number when sending out the packet.
|
||||
memset(position, 0, sizeof(PacketSequenceNumber));
|
||||
position += sizeof(PacketSequenceNumber);
|
||||
// Pack zeros for the number of bytes that the sequence number requires.
|
||||
// The LimitedNodeList will handle packing in the sequence number when sending out the packet.
|
||||
memset(position, 0, sizeof(PacketSequenceNumber));
|
||||
position += sizeof(PacketSequenceNumber);
|
||||
}
|
||||
|
||||
|
||||
// return the number of bytes written for pointer pushing
|
||||
return position - packet;
|
||||
}
|
||||
|
@ -193,7 +195,7 @@ int numBytesForArithmeticCodedPacketType(PacketType packetType) {
|
|||
}
|
||||
|
||||
int numBytesForPacketHeaderGivenPacketType(PacketType packetType) {
|
||||
return numBytesForArithmeticCodedPacketType(packetType)
|
||||
return numBytesForArithmeticCodedPacketType(packetType)
|
||||
+ numHashBytesForType(packetType)
|
||||
+ numSequenceNumberBytesForType(packetType)
|
||||
+ NUM_STATIC_HEADER_BYTES;
|
||||
|
@ -213,7 +215,7 @@ QUuid uuidFromPacketHeader(const QByteArray& packet) {
|
|||
}
|
||||
|
||||
int hashOffsetForPacketType(PacketType packetType) {
|
||||
return numBytesForArithmeticCodedPacketType(packetType) + NUM_STATIC_HEADER_BYTES;
|
||||
return numBytesForArithmeticCodedPacketType(packetType) + NUM_STATIC_HEADER_BYTES;
|
||||
}
|
||||
|
||||
int sequenceNumberOffsetForPacketType(PacketType packetType) {
|
||||
|
@ -233,13 +235,13 @@ PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketTy
|
|||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
PacketSequenceNumber result = DEFAULT_SEQUENCE_NUMBER;
|
||||
|
||||
if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) {
|
||||
memcpy(&result, packet.data() + sequenceNumberOffsetForPacketType(packetType), sizeof(PacketSequenceNumber));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -248,7 +250,7 @@ void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, Packet
|
|||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH,
|
||||
packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH,
|
||||
hashForPacketAndConnectionUUID(packet, connectionUUID));
|
||||
}
|
||||
|
||||
|
@ -256,17 +258,17 @@ void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequ
|
|||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
packet.replace(sequenceNumberOffsetForPacketType(packetType),
|
||||
|
||||
packet.replace(sequenceNumberOffsetForPacketType(packetType),
|
||||
sizeof(PacketSequenceNumber), reinterpret_cast<char*>(&sequenceNumber), sizeof(PacketSequenceNumber));
|
||||
}
|
||||
}
|
||||
|
||||
void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber,
|
||||
void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber,
|
||||
PacketType packetType) {
|
||||
if (packetType == PacketTypeUnknown) {
|
||||
packetType = packetTypeForPacket(packet);
|
||||
}
|
||||
|
||||
|
||||
replaceHashInPacket(packet, connectionUUID, packetType);
|
||||
replaceSequenceNumberInPacket(packet, sequenceNumber, packetType);
|
||||
}
|
||||
|
|
|
@ -68,9 +68,9 @@ enum PacketType {
|
|||
PacketTypeNodeJsonStats,
|
||||
PacketTypeEntityQuery, // 40
|
||||
PacketTypeEntityData,
|
||||
PacketTypeEntityAddOrEdit,
|
||||
PacketTypeEntityAdd,
|
||||
PacketTypeEntityErase,
|
||||
PacketTypeEntityAddResponse,
|
||||
PacketTypeEntityEdit,
|
||||
PacketTypeOctreeDataNack, // 45
|
||||
PacketTypeStopNode,
|
||||
PacketTypeAudioEnvironment,
|
||||
|
@ -79,7 +79,8 @@ enum PacketType {
|
|||
PacketTypeIceServerHeartbeat, // 50
|
||||
PacketTypeIceServerHeartbeatResponse,
|
||||
PacketTypeUnverifiedPing,
|
||||
PacketTypeUnverifiedPingReply
|
||||
PacketTypeUnverifiedPingReply,
|
||||
PacketTypeParticleEntitiesFix
|
||||
};
|
||||
|
||||
typedef char PacketVersion;
|
||||
|
@ -90,17 +91,17 @@ const PacketSequenceNumber DEFAULT_SEQUENCE_NUMBER = 0;
|
|||
typedef std::map<PacketType, PacketSequenceNumber> PacketTypeSequenceMap;
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
|
||||
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
|
||||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
||||
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode
|
||||
<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse;
|
||||
<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse
|
||||
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
|
||||
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
|
||||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
||||
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode
|
||||
<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse;
|
||||
|
||||
const QSet<PacketType> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeAvatarData;
|
||||
<< PacketTypeAvatarData;
|
||||
|
||||
const int NUM_BYTES_MD5_HASH = 16;
|
||||
const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID;
|
||||
|
@ -178,5 +179,7 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23;
|
|||
const PacketVersion VERSION_ENTITIES_HAVE_LINE_TYPE = 24;
|
||||
const PacketVersion VERSION_ENTITIES_HAVE_COLLISION_SOUND_URL = 25;
|
||||
const PacketVersion VERSION_ENTITIES_HAVE_FRICTION = 26;
|
||||
const PacketVersion VERSION_NO_ENTITY_ID_SWAP = 27;
|
||||
const PacketVersion VERSION_ENTITIES_PARTICLE_FIX = 28;
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -20,10 +20,7 @@ OctreeHeadlessViewer::OctreeHeadlessViewer() :
|
|||
_boundaryLevelAdjust(0),
|
||||
_maxPacketsPerSecond(DEFAULT_MAX_OCTREE_PPS)
|
||||
{
|
||||
_viewFrustum.setFieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES);
|
||||
_viewFrustum.setAspectRatio(DEFAULT_ASPECT_RATIO);
|
||||
_viewFrustum.setNearClip(DEFAULT_NEAR_CLIP);
|
||||
_viewFrustum.setFarClip(DEFAULT_FAR_CLIP);
|
||||
_viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
||||
}
|
||||
|
||||
OctreeHeadlessViewer::~OctreeHeadlessViewer() {
|
||||
|
@ -67,7 +64,8 @@ void OctreeHeadlessViewer::queryOctree() {
|
|||
_octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
|
||||
_octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
|
||||
_octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
|
||||
_octreeQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition());
|
||||
_octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
|
||||
|
||||
_octreeQuery.setOctreeSizeScale(getVoxelSizeScale());
|
||||
_octreeQuery.setBoundaryLevelAdjust(getBoundaryLevelAdjust());
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -37,6 +37,36 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) {
|
|||
_direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f));
|
||||
}
|
||||
|
||||
// Order cooresponds to the order defined in the BoxVertex enum.
|
||||
static const glm::vec4 NDC_VALUES[8] = {
|
||||
glm::vec4(-1, -1, -1, 1),
|
||||
glm::vec4(1, -1, -1, 1),
|
||||
glm::vec4(1, 1, -1, 1),
|
||||
glm::vec4(-1, 1, -1, 1),
|
||||
glm::vec4(-1, -1, 1, 1),
|
||||
glm::vec4(1, -1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(-1, 1, 1, 1),
|
||||
};
|
||||
|
||||
void ViewFrustum::setProjection(const glm::mat4& projection) {
|
||||
_projection = projection;
|
||||
_inverseProjection = glm::inverse(projection);
|
||||
|
||||
// compute our dimensions the usual way
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_corners[i] = _inverseProjection * NDC_VALUES[i];
|
||||
_corners[i] /= _corners[i].w;
|
||||
}
|
||||
_nearClip = -_corners[BOTTOM_LEFT_NEAR].z;
|
||||
_farClip = -_corners[BOTTOM_LEFT_FAR].z;
|
||||
_aspectRatio = (_corners[TOP_RIGHT_NEAR].x - _corners[BOTTOM_LEFT_NEAR].x) /
|
||||
(_corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_LEFT_NEAR].y);
|
||||
glm::vec3 right = glm::normalize(glm::vec3(_corners[TOP_RIGHT_NEAR]));
|
||||
glm::vec3 left = glm::normalize(glm::vec3(_corners[TOP_LEFT_NEAR]));
|
||||
_fieldOfView = abs(glm::degrees(glm::angle(right, left)));
|
||||
}
|
||||
|
||||
// ViewFrustum::calculateViewFrustum()
|
||||
//
|
||||
// Description: this will calculate the view frustum bounds for a given position and direction
|
||||
|
@ -45,48 +75,16 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) {
|
|||
// http://www.lighthouse3d.com/tutorials/view-frustum-culling/view-frustums-shape/
|
||||
//
|
||||
void ViewFrustum::calculate() {
|
||||
if (_orthographic) {
|
||||
calculateOrthographic();
|
||||
return;
|
||||
}
|
||||
|
||||
// compute the off-axis frustum parameters as we would for glFrustum
|
||||
float left, right, bottom, top, nearVal, farVal;
|
||||
glm::vec4 nearClipPlane, farClipPlane;
|
||||
computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
|
||||
// start with the corners of the near frustum window
|
||||
glm::vec3 topLeft(left, top, -nearVal);
|
||||
glm::vec3 topRight(right, top, -nearVal);
|
||||
glm::vec3 bottomLeft(left, bottom, -nearVal);
|
||||
glm::vec3 bottomRight(right, bottom, -nearVal);
|
||||
|
||||
// find the intersections of the rays through the corners with the clip planes in view space,
|
||||
// then transform them to world space
|
||||
glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction)) *
|
||||
glm::translate(_eyeOffsetPosition) * glm::mat4_cast(_eyeOffsetOrientation);
|
||||
_farTopLeft = glm::vec3(worldMatrix * glm::vec4(topLeft *
|
||||
(-farClipPlane.w / glm::dot(topLeft, glm::vec3(farClipPlane))), 1.0f));
|
||||
_farTopRight = glm::vec3(worldMatrix * glm::vec4(topRight *
|
||||
(-farClipPlane.w / glm::dot(topRight, glm::vec3(farClipPlane))), 1.0f));
|
||||
_farBottomLeft = glm::vec3(worldMatrix * glm::vec4(bottomLeft *
|
||||
(-farClipPlane.w / glm::dot(bottomLeft, glm::vec3(farClipPlane))), 1.0f));
|
||||
_farBottomRight = glm::vec3(worldMatrix * glm::vec4(bottomRight *
|
||||
(-farClipPlane.w / glm::dot(bottomRight, glm::vec3(farClipPlane))), 1.0f));
|
||||
_nearTopLeft = glm::vec3(worldMatrix * glm::vec4(topLeft *
|
||||
(-nearClipPlane.w / glm::dot(topLeft, glm::vec3(nearClipPlane))), 1.0f));
|
||||
_nearTopRight = glm::vec3(worldMatrix * glm::vec4(topRight *
|
||||
(-nearClipPlane.w / glm::dot(topRight, glm::vec3(nearClipPlane))), 1.0f));
|
||||
_nearBottomLeft = glm::vec3(worldMatrix * glm::vec4(bottomLeft *
|
||||
(-nearClipPlane.w / glm::dot(bottomLeft, glm::vec3(nearClipPlane))), 1.0f));
|
||||
_nearBottomRight = glm::vec3(worldMatrix * glm::vec4(bottomRight *
|
||||
(-nearClipPlane.w / glm::dot(bottomRight, glm::vec3(nearClipPlane))), 1.0f));
|
||||
|
||||
// compute the offset position and axes in world space
|
||||
_offsetPosition = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
_offsetDirection = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
|
||||
_offsetUp = glm::vec3(worldMatrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
|
||||
_offsetRight = glm::vec3(worldMatrix * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction));
|
||||
glm::vec4 v;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
v = worldMatrix * _corners[i];
|
||||
v /= v.w;
|
||||
_cornersWorld[i] = glm::vec3(v);
|
||||
}
|
||||
|
||||
// compute the six planes
|
||||
// The planes are defined such that the normal points towards the inside of the view frustum.
|
||||
|
@ -99,73 +97,26 @@ void ViewFrustum::calculate() {
|
|||
// the function set3Points assumes that the points are given in counter clockwise order, assume you
|
||||
// are inside the frustum, facing the plane. Start with any point, and go counter clockwise for
|
||||
// three consecutive points
|
||||
|
||||
_planes[TOP_PLANE ].set3Points(_nearTopRight,_nearTopLeft,_farTopLeft);
|
||||
_planes[BOTTOM_PLANE].set3Points(_nearBottomLeft,_nearBottomRight,_farBottomRight);
|
||||
_planes[LEFT_PLANE ].set3Points(_nearBottomLeft,_farBottomLeft,_farTopLeft);
|
||||
_planes[RIGHT_PLANE ].set3Points(_farBottomRight,_nearBottomRight,_nearTopRight);
|
||||
_planes[NEAR_PLANE ].set3Points(_nearBottomRight,_nearBottomLeft,_nearTopLeft);
|
||||
_planes[FAR_PLANE ].set3Points(_farBottomLeft,_farBottomRight,_farTopRight);
|
||||
_planes[TOP_PLANE].set3Points(_cornersWorld[TOP_RIGHT_NEAR], _cornersWorld[TOP_LEFT_NEAR], _cornersWorld[TOP_LEFT_FAR]);
|
||||
_planes[BOTTOM_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_RIGHT_FAR]);
|
||||
_planes[LEFT_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[TOP_LEFT_FAR]);
|
||||
_planes[RIGHT_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[TOP_RIGHT_FAR]);
|
||||
_planes[NEAR_PLANE].set3Points(_cornersWorld[BOTTOM_RIGHT_NEAR], _cornersWorld[BOTTOM_LEFT_NEAR], _cornersWorld[TOP_LEFT_NEAR]);
|
||||
_planes[FAR_PLANE].set3Points(_cornersWorld[BOTTOM_LEFT_FAR], _cornersWorld[BOTTOM_RIGHT_FAR], _cornersWorld[TOP_RIGHT_FAR]);
|
||||
|
||||
// Also calculate our projection matrix in case people want to project points...
|
||||
// Projection matrix : Field of View, ratio, display range : near to far
|
||||
const float CLIP_NUDGE = 1.0f;
|
||||
float farClip = (_farClip != _nearClip) ? _farClip : _nearClip + CLIP_NUDGE; // don't allow near and far to be equal
|
||||
glm::mat4 projection = glm::perspective(_fieldOfView, _aspectRatio, _nearClip, farClip);
|
||||
glm::vec3 lookAt = _position + _direction;
|
||||
glm::mat4 view = glm::lookAt(_position, lookAt, _up);
|
||||
|
||||
// Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it)
|
||||
_ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around
|
||||
_ourModelViewProjectionMatrix = _projection * view; // Remember, matrix multiplication is the other way around
|
||||
|
||||
// Set up our keyhole bounding box...
|
||||
glm::vec3 corner = _position - _keyholeRadius;
|
||||
_keyholeBoundingCube = AACube(corner,(_keyholeRadius * 2.0f));
|
||||
}
|
||||
|
||||
void ViewFrustum::calculateOrthographic() {
|
||||
float halfWidth = _width * 0.5f;
|
||||
float halfHeight = _height * 0.5f;
|
||||
|
||||
// find the corners of the view box in world space
|
||||
glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction)) *
|
||||
glm::translate(_eyeOffsetPosition) * glm::mat4_cast(_eyeOffsetOrientation);
|
||||
_farTopLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, halfHeight, -_farClip, 1.0f));
|
||||
_farTopRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, halfHeight, -_farClip, 1.0f));
|
||||
_farBottomLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, -halfHeight, -_farClip, 1.0f));
|
||||
_farBottomRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, -halfHeight, -_farClip, 1.0f));
|
||||
_nearTopLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, halfHeight, -_nearClip, 1.0f));
|
||||
_nearTopRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, halfHeight, -_nearClip, 1.0f));
|
||||
_nearBottomLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, -halfHeight, -_nearClip, 1.0f));
|
||||
_nearBottomRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, -halfHeight, -_nearClip, 1.0f));
|
||||
|
||||
// compute the offset position and axes in world space
|
||||
_offsetPosition = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
_offsetDirection = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
|
||||
_offsetUp = glm::vec3(worldMatrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
|
||||
_offsetRight = glm::vec3(worldMatrix * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
|
||||
_planes[TOP_PLANE].set3Points(_nearTopRight, _nearTopLeft, _farTopLeft);
|
||||
_planes[BOTTOM_PLANE].set3Points(_nearBottomLeft, _nearBottomRight, _farBottomRight);
|
||||
_planes[LEFT_PLANE].set3Points(_nearBottomLeft, _farBottomLeft, _farTopLeft);
|
||||
_planes[RIGHT_PLANE].set3Points(_farBottomRight, _nearBottomRight, _nearTopRight);
|
||||
_planes[NEAR_PLANE].set3Points(_nearBottomRight, _nearBottomLeft, _nearTopLeft);
|
||||
_planes[FAR_PLANE].set3Points(_farBottomLeft, _farBottomRight, _farTopRight);
|
||||
|
||||
// Also calculate our projection matrix in case people want to project points...
|
||||
// Projection matrix : Field of View, ratio, display range : near to far
|
||||
glm::mat4 projection = glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, _nearClip, _farClip);
|
||||
glm::vec3 lookAt = _position + _direction;
|
||||
glm::mat4 view = glm::lookAt(_position, lookAt, _up);
|
||||
|
||||
// Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it)
|
||||
_ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around
|
||||
|
||||
// Set up our keyhole bounding box...
|
||||
glm::vec3 corner = _position - _keyholeRadius;
|
||||
_keyholeBoundingCube = AACube(corner, (_keyholeRadius * 2.0f));
|
||||
}
|
||||
|
||||
//enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE };
|
||||
const char* ViewFrustum::debugPlaneName (int plane) const {
|
||||
switch (plane) {
|
||||
|
@ -305,7 +256,6 @@ ViewFrustum::location ViewFrustum::pointInFrustum(const glm::vec3& point) const
|
|||
return keyholeResult; // escape early will be the value from checking the keyhole
|
||||
}
|
||||
}
|
||||
|
||||
return regularResult;
|
||||
}
|
||||
|
||||
|
@ -429,9 +379,7 @@ bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const {
|
|||
testMatches(compareTo._aspectRatio, _aspectRatio) &&
|
||||
testMatches(compareTo._nearClip, _nearClip) &&
|
||||
testMatches(compareTo._farClip, _farClip) &&
|
||||
testMatches(compareTo._focalLength, _focalLength) &&
|
||||
testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition) &&
|
||||
testMatches(compareTo._eyeOffsetOrientation, _eyeOffsetOrientation);
|
||||
testMatches(compareTo._focalLength, _focalLength);
|
||||
|
||||
if (!result && debug) {
|
||||
qCDebug(octree, "ViewFrustum::matches()... result=%s", debug::valueOf(result));
|
||||
|
@ -466,15 +414,6 @@ bool ViewFrustum::matches(const ViewFrustum& compareTo, bool debug) const {
|
|||
qCDebug(octree, "%s -- compareTo._focalLength=%f _focalLength=%f",
|
||||
(testMatches(compareTo._focalLength, _focalLength) ? "MATCHES " : "NO MATCH"),
|
||||
compareTo._focalLength, _focalLength);
|
||||
qCDebug(octree, "%s -- compareTo._eyeOffsetPosition=%f,%f,%f _eyeOffsetPosition=%f,%f,%f",
|
||||
(testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition) ? "MATCHES " : "NO MATCH"),
|
||||
compareTo._eyeOffsetPosition.x, compareTo._eyeOffsetPosition.y, compareTo._eyeOffsetPosition.z,
|
||||
_eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z);
|
||||
qCDebug(octree, "%s -- compareTo._eyeOffsetOrientation=%f,%f,%f,%f _eyeOffsetOrientation=%f,%f,%f,%f",
|
||||
(testMatches(compareTo._eyeOffsetOrientation, _eyeOffsetOrientation) ? "MATCHES " : "NO MATCH"),
|
||||
compareTo._eyeOffsetOrientation.x, compareTo._eyeOffsetOrientation.y,
|
||||
compareTo._eyeOffsetOrientation.z, compareTo._eyeOffsetOrientation.w,
|
||||
_eyeOffsetOrientation.x, _eyeOffsetOrientation.y, _eyeOffsetOrientation.z, _eyeOffsetOrientation.w);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -485,9 +424,6 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const
|
|||
const float POSITION_SIMILAR_ENOUGH = 5.0f; // 5 meters
|
||||
float positionDistance = glm::distance(_position, compareTo._position);
|
||||
|
||||
const float EYEOFFSET_POSITION_SIMILAR_ENOUGH = 0.15f; // 0.15 meters
|
||||
float eyeOffsetpositionDistance = glm::distance(_eyeOffsetPosition, compareTo._eyeOffsetPosition);
|
||||
|
||||
// Compute the angular distance between the two orientations
|
||||
const float ORIENTATION_SIMILAR_ENOUGH = 10.0f; // 10 degrees in any direction
|
||||
glm::quat dQOrientation = _orientation * glm::inverse(compareTo._orientation);
|
||||
|
@ -496,23 +432,14 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const
|
|||
angleOrientation = 0.0f;
|
||||
}
|
||||
|
||||
glm::quat dQEyeOffsetOrientation = _eyeOffsetOrientation * glm::inverse(compareTo._eyeOffsetOrientation);
|
||||
float angleEyeOffsetOrientation = compareTo._eyeOffsetOrientation == _eyeOffsetOrientation
|
||||
? 0.0f : glm::degrees(glm::angle(dQEyeOffsetOrientation));
|
||||
if (isNaN(angleEyeOffsetOrientation)) {
|
||||
angleEyeOffsetOrientation = 0.0f;
|
||||
}
|
||||
|
||||
bool result =
|
||||
testMatches(0, positionDistance, POSITION_SIMILAR_ENOUGH) &&
|
||||
testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) &&
|
||||
testMatches(0, positionDistance, POSITION_SIMILAR_ENOUGH) &&
|
||||
testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) &&
|
||||
testMatches(compareTo._fieldOfView, _fieldOfView) &&
|
||||
testMatches(compareTo._aspectRatio, _aspectRatio) &&
|
||||
testMatches(compareTo._nearClip, _nearClip) &&
|
||||
testMatches(compareTo._farClip, _farClip) &&
|
||||
testMatches(compareTo._focalLength, _focalLength) &&
|
||||
testMatches(0, eyeOffsetpositionDistance, EYEOFFSET_POSITION_SIMILAR_ENOUGH) &&
|
||||
testMatches(0, angleEyeOffsetOrientation, ORIENTATION_SIMILAR_ENOUGH);
|
||||
testMatches(compareTo._focalLength, _focalLength);
|
||||
|
||||
|
||||
if (!result && debug) {
|
||||
|
@ -529,7 +456,7 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const
|
|||
qCDebug(octree, "%s -- angleOrientation=%f",
|
||||
(testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"),
|
||||
angleOrientation);
|
||||
|
||||
|
||||
qCDebug(octree, "%s -- compareTo._fieldOfView=%f _fieldOfView=%f",
|
||||
(testMatches(compareTo._fieldOfView, _fieldOfView) ? "MATCHES " : "NO MATCH"),
|
||||
compareTo._fieldOfView, _fieldOfView);
|
||||
|
@ -545,19 +472,6 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const
|
|||
qCDebug(octree, "%s -- compareTo._focalLength=%f _focalLength=%f",
|
||||
(testMatches(compareTo._focalLength, _focalLength) ? "MATCHES " : "NO MATCH"),
|
||||
compareTo._focalLength, _focalLength);
|
||||
|
||||
qCDebug(octree, "%s -- compareTo._eyeOffsetPosition=%f,%f,%f _eyeOffsetPosition=%f,%f,%f",
|
||||
(testMatches(compareTo._eyeOffsetPosition, _eyeOffsetPosition, POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"),
|
||||
compareTo._eyeOffsetPosition.x, compareTo._eyeOffsetPosition.y, compareTo._eyeOffsetPosition.z,
|
||||
_eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z);
|
||||
|
||||
qCDebug(octree, "%s -- eyeOffsetpositionDistance=%f",
|
||||
(testMatches(0,eyeOffsetpositionDistance, EYEOFFSET_POSITION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"),
|
||||
eyeOffsetpositionDistance);
|
||||
|
||||
qCDebug(octree, "%s -- angleEyeOffsetOrientation=%f",
|
||||
(testMatches(0, angleEyeOffsetOrientation, ORIENTATION_SIMILAR_ENOUGH) ? "IS SIMILAR ENOUGH " : "IS NOT SIMILAR ENOUGH"),
|
||||
angleEyeOffsetOrientation);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -570,39 +484,19 @@ PickRay ViewFrustum::computePickRay(float x, float y) {
|
|||
}
|
||||
|
||||
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
|
||||
origin = _nearTopLeft + x * (_nearTopRight - _nearTopLeft) + y * (_nearBottomLeft - _nearTopLeft);
|
||||
direction = glm::normalize(origin - (_position + _orientation * _eyeOffsetPosition));
|
||||
origin = _cornersWorld[TOP_LEFT_NEAR] + x * (_cornersWorld[TOP_RIGHT_NEAR] - _cornersWorld[TOP_LEFT_NEAR]) +
|
||||
y * (_cornersWorld[BOTTOM_LEFT_NEAR] - _cornersWorld[TOP_LEFT_NEAR]);
|
||||
direction = glm::normalize(origin - _position);
|
||||
}
|
||||
|
||||
void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearValue, float& farValue,
|
||||
glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
|
||||
// compute our dimensions the usual way
|
||||
float hheight = _nearClip * tanf(_fieldOfView * 0.5f * RADIANS_PER_DEGREE);
|
||||
float hwidth = _aspectRatio * hheight;
|
||||
if (isOrthographic()) {
|
||||
hheight = getHeight();
|
||||
hwidth = getWidth();
|
||||
}
|
||||
|
||||
// get our frustum corners in view space
|
||||
glm::mat4 eyeMatrix = glm::mat4_cast(glm::inverse(_eyeOffsetOrientation)) * glm::translate(-_eyeOffsetPosition);
|
||||
glm::vec4 corners[8];
|
||||
float farScale = _farClip / _nearClip;
|
||||
corners[0] = eyeMatrix * glm::vec4(-hwidth, -hheight, -_nearClip, 1.0f);
|
||||
corners[1] = eyeMatrix * glm::vec4(hwidth, -hheight, -_nearClip, 1.0f);
|
||||
corners[2] = eyeMatrix * glm::vec4(hwidth, hheight, -_nearClip, 1.0f);
|
||||
corners[3] = eyeMatrix * glm::vec4(-hwidth, hheight, -_nearClip, 1.0f);
|
||||
corners[4] = eyeMatrix * glm::vec4(-hwidth * farScale, -hheight * farScale, -_farClip, 1.0f);
|
||||
corners[5] = eyeMatrix * glm::vec4(hwidth * farScale, -hheight * farScale, -_farClip, 1.0f);
|
||||
corners[6] = eyeMatrix * glm::vec4(hwidth * farScale, hheight * farScale, -_farClip, 1.0f);
|
||||
corners[7] = eyeMatrix * glm::vec4(-hwidth * farScale, hheight * farScale, -_farClip, 1.0f);
|
||||
|
||||
// find the minimum and maximum z values, which will be our near and far clip distances
|
||||
nearValue = FLT_MAX;
|
||||
farValue = -FLT_MAX;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
nearValue = min(nearValue, -corners[i].z);
|
||||
farValue = max(farValue, -corners[i].z);
|
||||
nearValue = min(nearValue, -_corners[i].z);
|
||||
farValue = max(farValue, -_corners[i].z);
|
||||
}
|
||||
|
||||
// make sure the near clip isn't too small to be valid
|
||||
|
@ -610,9 +504,9 @@ void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom
|
|||
nearValue = max(MIN_NEAR, nearValue);
|
||||
|
||||
// get the near/far normal and use it to find the clip planes
|
||||
glm::vec4 normal = eyeMatrix * glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, corners[0]));
|
||||
farClipPlane = glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, corners[4]));
|
||||
glm::vec4 normal = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
nearClipPlane = glm::vec4(-normal.x, -normal.y, -normal.z, glm::dot(normal, _corners[0]));
|
||||
farClipPlane = glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _corners[4]));
|
||||
|
||||
// compute the focal proportion (zero is near clip, one is far clip)
|
||||
float focalProportion = (_focalLength - _nearClip) / (_farClip - _nearClip);
|
||||
|
@ -623,7 +517,7 @@ void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom
|
|||
bottom = FLT_MAX;
|
||||
top = -FLT_MAX;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
glm::vec4 corner = glm::mix(corners[i], corners[i + 4], focalProportion);
|
||||
glm::vec4 corner = glm::mix(_corners[i], _corners[i + 4], focalProportion);
|
||||
glm::vec4 intersection = corner * (-nearValue / corner.z);
|
||||
left = min(left, intersection.x);
|
||||
right = max(right, intersection.x);
|
||||
|
@ -644,9 +538,6 @@ void ViewFrustum::printDebugDetails() const {
|
|||
qCDebug(octree, "_nearClip=%f", _nearClip);
|
||||
qCDebug(octree, "_farClip=%f", _farClip);
|
||||
qCDebug(octree, "_focalLength=%f", _focalLength);
|
||||
qCDebug(octree, "_eyeOffsetPosition=%f,%f,%f", _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z );
|
||||
qCDebug(octree, "_eyeOffsetOrientation=%f,%f,%f,%f", _eyeOffsetOrientation.x, _eyeOffsetOrientation.y, _eyeOffsetOrientation.z,
|
||||
_eyeOffsetOrientation.w );
|
||||
}
|
||||
|
||||
glm::vec2 ViewFrustum::projectPoint(glm::vec3 point, bool& pointInView) const {
|
||||
|
@ -846,20 +737,7 @@ float ViewFrustum::distanceToCamera(const glm::vec3& point) const {
|
|||
}
|
||||
|
||||
void ViewFrustum::evalProjectionMatrix(glm::mat4& proj) const {
|
||||
if (isOrthographic()) {
|
||||
glm::vec3 frustumCenter = glm::inverse( _orientation) * _position;
|
||||
|
||||
proj = glm::ortho(frustumCenter.x -0.5f * getWidth(),
|
||||
frustumCenter.x +0.5f * getWidth(),
|
||||
frustumCenter.y -0.5f * getHeight(),
|
||||
frustumCenter.y +0.5f * getHeight(),
|
||||
-getFarClip(), -getNearClip());
|
||||
} else {
|
||||
float left, right, bottom, top, near, far;
|
||||
glm::vec4 clip0, clip1;
|
||||
computeOffAxisFrustum(left, right, bottom, top, near, far, clip0, clip1);
|
||||
proj = glm::perspective(glm::radians(getFieldOfView()), getAspectRatio(), getNearClip(), getFarClip());
|
||||
}
|
||||
proj = _projection;
|
||||
}
|
||||
|
||||
void ViewFrustum::evalViewTransform(Transform& view) const {
|
||||
|
|
|
@ -50,19 +50,11 @@ public:
|
|||
const glm::vec3& getRight() const { return _right; }
|
||||
|
||||
// setters for lens attributes
|
||||
void setOrthographic(bool orthographic) { _orthographic = orthographic; }
|
||||
void setWidth(float width) { _width = width; }
|
||||
void setHeight(float height) { _height = height; }
|
||||
void setFieldOfView(float f) { _fieldOfView = f; }
|
||||
void setAspectRatio(float a) { _aspectRatio = a; }
|
||||
void setNearClip(float n) { _nearClip = n; }
|
||||
void setFarClip(float f) { _farClip = f; }
|
||||
void setFocalLength(float length) { _focalLength = length; }
|
||||
void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; }
|
||||
void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; }
|
||||
void setProjection(const glm::mat4 & projection);
|
||||
void getFocalLength(float focalLength) { _focalLength = focalLength; }
|
||||
|
||||
// getters for lens attributes
|
||||
bool isOrthographic() const { return _orthographic; }
|
||||
const glm::mat4 getProjection() const { return _projection; };
|
||||
float getWidth() const { return _width; }
|
||||
float getHeight() const { return _height; }
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
|
@ -70,23 +62,16 @@ public:
|
|||
float getNearClip() const { return _nearClip; }
|
||||
float getFarClip() const { return _farClip; }
|
||||
float getFocalLength() const { return _focalLength; }
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
|
||||
const glm::vec3& getOffsetPosition() const { return _offsetPosition; }
|
||||
const glm::vec3& getOffsetDirection() const { return _offsetDirection; }
|
||||
const glm::vec3& getOffsetUp() const { return _offsetUp; }
|
||||
const glm::vec3& getOffsetRight() const { return _offsetRight; }
|
||||
const glm::vec3& getFarTopLeft() const { return _cornersWorld[TOP_LEFT_FAR]; }
|
||||
const glm::vec3& getFarTopRight() const { return _cornersWorld[TOP_RIGHT_FAR]; }
|
||||
const glm::vec3& getFarBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_FAR]; }
|
||||
const glm::vec3& getFarBottomRight() const { return _cornersWorld[BOTTOM_RIGHT_FAR]; }
|
||||
|
||||
const glm::vec3& getFarTopLeft() const { return _farTopLeft; }
|
||||
const glm::vec3& getFarTopRight() const { return _farTopRight; }
|
||||
const glm::vec3& getFarBottomLeft() const { return _farBottomLeft; }
|
||||
const glm::vec3& getFarBottomRight() const { return _farBottomRight; }
|
||||
|
||||
const glm::vec3& getNearTopLeft() const { return _nearTopLeft; }
|
||||
const glm::vec3& getNearTopRight() const { return _nearTopRight; }
|
||||
const glm::vec3& getNearBottomLeft() const { return _nearBottomLeft; }
|
||||
const glm::vec3& getNearBottomRight() const { return _nearBottomRight; }
|
||||
const glm::vec3& getNearTopLeft() const { return _cornersWorld[TOP_LEFT_NEAR]; }
|
||||
const glm::vec3& getNearTopRight() const { return _cornersWorld[TOP_RIGHT_NEAR]; }
|
||||
const glm::vec3& getNearBottomLeft() const { return _cornersWorld[BOTTOM_LEFT_NEAR]; }
|
||||
const glm::vec3& getNearBottomRight() const { return _cornersWorld[BOTTOM_RIGHT_NEAR]; }
|
||||
|
||||
// get/set for keyhole attribute
|
||||
void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; }
|
||||
|
@ -132,49 +117,33 @@ private:
|
|||
ViewFrustum::location cubeInKeyhole(const AACube& cube) const;
|
||||
ViewFrustum::location boxInKeyhole(const AABox& box) const;
|
||||
|
||||
void calculateOrthographic();
|
||||
|
||||
// camera location/orientation attributes
|
||||
glm::vec3 _position = glm::vec3(0.0f); // the position in world-frame
|
||||
glm::quat _orientation = glm::quat();
|
||||
glm::vec3 _position; // the position in world-frame
|
||||
glm::quat _orientation;
|
||||
|
||||
// Lens attributes
|
||||
glm::mat4 _projection;
|
||||
|
||||
// calculated for orientation
|
||||
glm::vec3 _direction = IDENTITY_FRONT;
|
||||
glm::vec3 _up = IDENTITY_UP;
|
||||
glm::vec3 _right = IDENTITY_RIGHT;
|
||||
|
||||
// Lens attributes
|
||||
bool _orthographic = false;
|
||||
// keyhole attributes
|
||||
float _keyholeRadius = DEFAULT_KEYHOLE_RADIUS;
|
||||
AACube _keyholeBoundingCube;
|
||||
|
||||
// Calculated values
|
||||
glm::mat4 _inverseProjection;
|
||||
float _width = 1.0f;
|
||||
float _height = 1.0f;
|
||||
float _aspectRatio = 1.0f;
|
||||
float _nearClip = DEFAULT_NEAR_CLIP;
|
||||
float _farClip = DEFAULT_FAR_CLIP;
|
||||
float _focalLength = 0.25f;
|
||||
glm::vec3 _eyeOffsetPosition = glm::vec3(0.0f);
|
||||
glm::quat _eyeOffsetOrientation = glm::quat();
|
||||
|
||||
// in Degrees, doesn't apply to HMD like Oculus
|
||||
float _fieldOfView = DEFAULT_FIELD_OF_VIEW_DEGREES;
|
||||
|
||||
// keyhole attributes
|
||||
float _keyholeRadius = DEFAULT_KEYHOLE_RADIUS;
|
||||
AACube _keyholeBoundingCube;
|
||||
|
||||
|
||||
// Calculated values
|
||||
glm::vec3 _offsetPosition = glm::vec3(0.0f);
|
||||
glm::vec3 _offsetDirection = glm::vec3(0.0f);
|
||||
glm::vec3 _offsetUp = glm::vec3(0.0f);
|
||||
glm::vec3 _offsetRight = glm::vec3(0.0f);
|
||||
glm::vec3 _farTopLeft = glm::vec3(0.0f);
|
||||
glm::vec3 _farTopRight = glm::vec3(0.0f);
|
||||
glm::vec3 _farBottomLeft = glm::vec3(0.0f);
|
||||
glm::vec3 _farBottomRight = glm::vec3(0.0f);
|
||||
glm::vec3 _nearTopLeft = glm::vec3(0.0f);
|
||||
glm::vec3 _nearTopRight = glm::vec3(0.0f);
|
||||
glm::vec3 _nearBottomLeft = glm::vec3(0.0f);
|
||||
glm::vec3 _nearBottomRight = glm::vec3(0.0f);
|
||||
glm::vec4 _corners[8];
|
||||
glm::vec3 _cornersWorld[8];
|
||||
enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE };
|
||||
::Plane _planes[6]; // How will this be used?
|
||||
|
||||
|
|
|
@ -337,7 +337,6 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
|
|||
|
||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) {
|
||||
assert(_entity);
|
||||
assert(_entity->isKnownID());
|
||||
|
||||
bool active = _body->isActive();
|
||||
if (!active) {
|
||||
|
@ -437,7 +436,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
|||
qCDebug(physics) << "EntityMotionState::sendUpdate()... calling queueEditEntityMessage()...";
|
||||
#endif
|
||||
|
||||
entityPacketSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
|
||||
entityPacketSender->queueEditEntityMessage(PacketTypeEntityEdit, id, properties);
|
||||
_entity->setLastBroadcast(usecTimestampNow());
|
||||
} else {
|
||||
#ifdef WANT_DEBUG
|
||||
|
|
|
@ -188,7 +188,7 @@ void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motio
|
|||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||
EntityItem* entity = entityState->getEntity();
|
||||
if (entity) {
|
||||
if (entity->isKnownID() && entityState->isCandidateForOwnership(sessionID)) {
|
||||
if (entityState->isCandidateForOwnership(sessionID)) {
|
||||
_outgoingChanges.insert(entityState);
|
||||
}
|
||||
_entitiesToSort.insert(entityState->getEntity());
|
||||
|
|
|
@ -321,7 +321,8 @@ void ScriptEngine::init() {
|
|||
qScriptRegisterMetaType(this, EntityItemPropertiesToScriptValue, EntityItemPropertiesFromScriptValue);
|
||||
qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue);
|
||||
qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue);
|
||||
qScriptRegisterSequenceMetaType<QVector<EntityItemID> >(this);
|
||||
qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
|
||||
qScriptRegisterSequenceMetaType<QVector<EntityItemID>>(this);
|
||||
|
||||
qScriptRegisterSequenceMetaType<QVector<glm::vec2> >(this);
|
||||
qScriptRegisterSequenceMetaType<QVector<glm::quat> >(this);
|
||||
|
|
|
@ -159,16 +159,17 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa
|
|||
}
|
||||
}
|
||||
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) {
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing) {
|
||||
int dotIndex = keyPath.indexOf('.');
|
||||
|
||||
QString firstKey = (dotIndex == -1) ? keyPath : keyPath.mid(0, dotIndex);
|
||||
|
||||
if (variantMap.contains(firstKey)) {
|
||||
if (shouldCreateIfMissing || variantMap.contains(firstKey)) {
|
||||
if (dotIndex == -1) {
|
||||
return &variantMap[firstKey];
|
||||
} else if (variantMap[firstKey].canConvert(QMetaType::QVariantMap)) {
|
||||
return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1));
|
||||
return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1),
|
||||
shouldCreateIfMissing);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,6 @@ private:
|
|||
void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
|
||||
};
|
||||
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath);
|
||||
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath, bool shouldCreateIfMissing = false);
|
||||
|
||||
#endif // hifi_HifiConfigVariantMap_h
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue