Merge branch 'tablet-ui-tablet-is-overlay' of github.com:sethalves/hifi into tablet-ui-edit-js-tablet-as-overlay

This commit is contained in:
Seth Alves 2017-02-24 14:27:30 -08:00
commit fcd3c09404
14 changed files with 122 additions and 67 deletions

View file

@ -384,18 +384,20 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
if (includeThisAvatar) { if (includeThisAvatar) {
numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122());
numAvatarDataBytes += avatarPacketList->write(bytes); numAvatarDataBytes += avatarPacketList->write(bytes);
_stats.numOthersIncluded++;
// increment the number of avatars sent to this reciever if (detail != AvatarData::NoData) {
nodeData->incrementNumAvatarsSentLastFrame(); _stats.numOthersIncluded++;
// set the last sent sequence number for this sender on the receiver // increment the number of avatars sent to this reciever
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), nodeData->incrementNumAvatarsSentLastFrame();
otherNodeData->getLastReceivedSequenceNumber());
// remember the last time we sent details about this other node to the receiver // set the last sent sequence number for this sender on the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), start); nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
otherNodeData->getLastReceivedSequenceNumber());
// remember the last time we sent details about this other node to the receiver
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
}
} }
avatarPacketList->endSegment(); avatarPacketList->endSegment();

View file

@ -13,6 +13,7 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
import "../styles-uit" import "../styles-uit"
import "../controls-uit" as HifiControls import "../controls-uit" as HifiControls
@ -29,7 +30,9 @@ Rectangle {
property int myCardHeight: 90 property int myCardHeight: 90
property int rowHeight: 70 property int rowHeight: 70
property int actionButtonWidth: 55 property int actionButtonWidth: 55
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth property int actionButtonAllowance: actionButtonWidth * 2
property int minNameCardWidth: palContainer.width - (actionButtonAllowance * 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
property int nameCardWidth: minNameCardWidth + (iAmAdmin ? 0 : actionButtonAllowance)
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring. property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities. property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
@ -52,6 +55,16 @@ Rectangle {
letterboxMessage.visible = true letterboxMessage.visible = true
letterboxMessage.popupRadius = 0 letterboxMessage.popupRadius = 0
} }
Settings {
id: settings
category: "pal"
property bool filtered: false
property int nearDistance: 30
}
function refreshWithFilter() {
// We should just be able to set settings.filtered to filter.checked, but see #3249, so send to .js for saving.
pal.sendToScript({method: 'refresh', params: {filter: filter.checked && {distance: settings.nearDistance}}});
}
// This is the container for the PAL // This is the container for the PAL
Rectangle { Rectangle {
@ -88,11 +101,32 @@ Rectangle {
audioLevel: myData.audioLevel audioLevel: myData.audioLevel
isMyCard: true isMyCard: true
// Size // Size
width: nameCardWidth width: minNameCardWidth
height: parent.height height: parent.height
// Anchors // Anchors
anchors.left: parent.left anchors.left: parent.left
} }
Row {
HifiControls.CheckBox {
id: filter
checked: settings.filtered
text: "in view"
boxSize: reload.height * 0.70
onCheckedChanged: refreshWithFilter()
}
HifiControls.GlyphButton {
id: reload
glyph: hifi.glyphs.reload
width: reload.height
onClicked: refreshWithFilter()
}
spacing: 50
anchors {
right: parent.right
top: parent.top
topMargin: 10
}
}
} }
// Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle // Rectangles used to cover up rounded edges on bottom of MyInfo Rectangle
Rectangle { Rectangle {
@ -306,45 +340,7 @@ Rectangle {
} }
} }
} }
// Refresh button
Rectangle {
// Size
width: hifi.dimensions.tableHeaderHeight-1
height: hifi.dimensions.tableHeaderHeight-1
// Anchors
anchors.left: table.left
anchors.leftMargin: 4
anchors.top: table.top
// Style
color: hifi.colors.tableBackgroundLight
// Actual refresh icon
HiFiGlyphs {
id: reloadButton
text: hifi.glyphs.reloadSmall
// Size
size: parent.width*1.5
// Anchors
anchors.fill: parent
// Style
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.darkGray
}
MouseArea {
id: reloadButtonArea
// Anchors
anchors.fill: parent
hoverEnabled: true
// Everyone likes a responsive refresh button!
// So use onPressed instead of onClicked
onPressed: {
reloadButton.color = hifi.colors.lightGrayText
pal.sendToScript({method: 'refresh'})
}
onReleased: reloadButton.color = (containsMouse ? hifi.colors.baseGrayHighlight : hifi.colors.darkGray)
onEntered: reloadButton.color = hifi.colors.baseGrayHighlight
onExited: reloadButton.color = (pressed ? hifi.colors.lightGrayText: hifi.colors.darkGray)
}
}
// Separator between user and admin functions // Separator between user and admin functions
Rectangle { Rectangle {
// Size // Size
@ -501,7 +497,7 @@ Rectangle {
if (alreadyRefreshed === true) { if (alreadyRefreshed === true) {
letterbox('', '', 'The last editor of this object is either you or not among this list of users.'); letterbox('', '', 'The last editor of this object is either you or not among this list of users.');
} else { } else {
pal.sendToScript({method: 'refresh', params: message.params}); pal.sendToScript({method: 'refresh', params: {selected: message.params}});
} }
} else { } else {
// If we've already refreshed the PAL and found the avatar in the model // If we've already refreshed the PAL and found the avatar in the model

View file

@ -192,6 +192,8 @@ QVariantMap Camera::getViewFrustum() {
result["orientation"].setValue(frustum.getOrientation()); result["orientation"].setValue(frustum.getOrientation());
result["projection"].setValue(frustum.getProjection()); result["projection"].setValue(frustum.getProjection());
result["centerRadius"].setValue(frustum.getCenterRadius()); result["centerRadius"].setValue(frustum.getCenterRadius());
result["fieldOfView"].setValue(frustum.getFieldOfView());
result["aspectRatio"].setValue(frustum.getAspectRatio());
return result; return result;
} }

View file

@ -85,7 +85,7 @@ AvatarManager::AvatarManager(QObject* parent) :
// immediately remove that avatar instead of waiting for the absence of packets from avatar mixer // immediately remove that avatar instead of waiting for the absence of packets from avatar mixer
connect(nodeList.data(), &NodeList::ignoredNode, this, [=](const QUuid& nodeID, bool enabled) { connect(nodeList.data(), &NodeList::ignoredNode, this, [=](const QUuid& nodeID, bool enabled) {
if (enabled) { if (enabled) {
removeAvatar(nodeID); removeAvatar(nodeID, KillAvatarReason::AvatarIgnored);
} }
}); });
} }

View file

@ -259,6 +259,8 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3
} }
void Base3DOverlay::locationChanged(bool tellPhysics) { void Base3DOverlay::locationChanged(bool tellPhysics) {
SpatiallyNestable::locationChanged(tellPhysics);
auto itemID = getRenderItemID(); auto itemID = getRenderItemID();
if (render::Item::isValidID(itemID)) { if (render::Item::isValidID(itemID)) {
render::ScenePointer scene = qApp->getMain3DScene(); render::ScenePointer scene = qApp->getMain3DScene();
@ -266,8 +268,6 @@ void Base3DOverlay::locationChanged(bool tellPhysics) {
pendingChanges.updateItem(itemID); pendingChanges.updateItem(itemID);
scene->enqueuePendingChanges(pendingChanges); scene->enqueuePendingChanges(pendingChanges);
} }
// Overlays can't currently have children.
// SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
} }
void Base3DOverlay::parentDeleted() { void Base3DOverlay::parentDeleted() {

View file

@ -53,7 +53,7 @@ namespace render {
return overlay->getBounds(); return overlay->getBounds();
} }
template <> int payloadGetLayer(const Overlay::Pointer& overlay) { template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
// MAgic number while we are defining the layering mechanism: // Magic number while we are defining the layering mechanism:
const int LAYER_NO_AA = 3; const int LAYER_NO_AA = 3;
const int LAYER_2D = 2; const int LAYER_2D = 2;
const int LAYER_3D_FRONT = 1; const int LAYER_3D_FRONT = 1;

View file

@ -182,7 +182,7 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo
void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
qCDebug(avatars) << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) qCDebug(avatars) << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID())
<< "from AvatarHashMap"; << "from AvatarHashMap" << removalReason;
emit avatarRemovedEvent(removedAvatar->getSessionUUID()); emit avatarRemovedEvent(removedAvatar->getSessionUUID());
} }

View file

@ -1419,8 +1419,7 @@ QVector<QUuid> EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& pare
return; return;
} }
parent->forEachChild([&](SpatiallyNestablePointer child) { parent->forEachChild([&](SpatiallyNestablePointer child) {
if (child->getParentJointIndex() == jointIndex && if (child->getParentJointIndex() == jointIndex) {
child->getNestableType() != NestableType::Overlay) {
result.push_back(child->getID()); result.push_back(child->getID());
} }
}); });

View file

@ -44,6 +44,8 @@ public:
// Mutable, but must retain structure of vector // Mutable, but must retain structure of vector
using NetworkMaterials = std::vector<std::shared_ptr<NetworkMaterial>>; using NetworkMaterials = std::vector<std::shared_ptr<NetworkMaterial>>;
bool isGeometryLoaded() const { return (bool)_fbxGeometry; }
const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; } const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; }
const GeometryMeshes& getMeshes() const { return *_meshes; } const GeometryMeshes& getMeshes() const { return *_meshes; }
const std::shared_ptr<const NetworkMaterial> getShapeMaterial(int shapeID) const; const std::shared_ptr<const NetworkMaterial> getShapeMaterial(int shapeID) const;

View file

@ -114,7 +114,7 @@ public:
void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
bool isLoaded() const { return (bool)_renderGeometry; } bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); }
void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; }
bool isWireframe() const { return _isWireframe; } bool isWireframe() const { return _isWireframe; }

View file

@ -150,7 +150,6 @@ signals:
private: private:
bool getRequestsDomainListData(); bool getRequestsDomainListData();
void setRequestsDomainListData(bool requests); void setRequestsDomainListData(bool requests);
bool _requestsDomainListData;
}; };

View file

@ -3231,9 +3231,13 @@ function MyController(hand) {
} }
_this.previouslyUnhooked[childID] = now; _this.previouslyUnhooked[childID] = now;
// we don't know if it's an entity or an overlay
Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex }); Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex });
Overlays.editOverlay(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex });
} else { } else {
Entities.editEntity(childID, { parentID: NULL_UUID }); Entities.editEntity(childID, { parentID: NULL_UUID });
Overlays.editOverlay(childID, { parentID: NULL_UUID });
} }
} }
}); });

View file

@ -34,7 +34,7 @@ var TABLET_NATURAL_DIMENSIONS = {x: 33.797, y: 50.129, z: 2.269};
var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png"; var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png"; // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx"; var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx";
// var TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx"; var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
// returns object with two fields: // returns object with two fields:
// * position - position in front of the user // * position - position in front of the user
@ -112,11 +112,18 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
this.dpi = DEFAULT_DPI * (DEFAULT_WIDTH / this.width); this.dpi = DEFAULT_DPI * (DEFAULT_WIDTH / this.width);
} }
var modelURL;
if (Settings.getValue("tabletVisibleToOthers")) {
modelURL = TABLET_MODEL_PATH;
} else {
modelURL = LOCAL_TABLET_MODEL_PATH;
}
var tabletProperties = { var tabletProperties = {
name: "WebTablet Tablet", name: "WebTablet Tablet",
type: "Model", type: "Model",
modelURL: TABLET_MODEL_PATH, modelURL: modelURL,
url: TABLET_MODEL_PATH, // for overlay url: modelURL, // for overlay
grabbable: true, // for overlay grabbable: true, // for overlay
userData: JSON.stringify({ userData: JSON.stringify({
"grabbableKey": {"grabbable": true} "grabbableKey": {"grabbable": true}

View file

@ -37,6 +37,15 @@ var conserveResources = true;
Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/controllers.js");
function projectVectorOntoPlane(normalizedVector, planeNormal) {
return Vec3.cross(planeNormal, Vec3.cross(normalizedVector, planeNormal));
}
function angleBetweenVectorsInPlane(from, to, normal) {
var projectedFrom = projectVectorOntoPlane(from, normal);
var projectedTo = projectVectorOntoPlane(to, normal);
return Vec3.orientedAngle(projectedFrom, projectedTo, normal);
}
// //
// Overlays. // Overlays.
// //
@ -229,7 +238,11 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
break; break;
case 'refresh': case 'refresh':
removeOverlays(); removeOverlays();
populateUserList(message.params); // If filter is specified from .qml instead of through settings, update the settings.
if (message.params.filter !== undefined) {
Settings.setValue('pal/filtered', !!message.params.filter);
}
populateUserList(message.params.selected);
UserActivityLogger.palAction("refresh", ""); UserActivityLogger.palAction("refresh", "");
break; break;
case 'updateGain': case 'updateGain':
@ -271,13 +284,42 @@ function addAvatarNode(id) {
color: color(selected, false, 0.0), color: color(selected, false, 0.0),
ignoreRayIntersection: false}, selected, !conserveResources); ignoreRayIntersection: false}, selected, !conserveResources);
} }
// Each open/refresh will capture a stable set of avatarsOfInterest, within the specified filter.
var avatarsOfInterest = {};
function populateUserList(selectData) { function populateUserList(selectData) {
var filter = Settings.getValue('pal/filtered') && {distance: Settings.getValue('pal/nearDistance')};
var data = [], avatars = AvatarList.getAvatarIdentifiers(); var data = [], avatars = AvatarList.getAvatarIdentifiers();
conserveResources = avatars.length > 20; avatarsOfInterest = {};
var myPosition = filter && Camera.position,
frustum = filter && Camera.frustum,
verticalHalfAngle = filter && (frustum.fieldOfView / 2),
horizontalHalfAngle = filter && (verticalHalfAngle * frustum.aspectRatio),
orientation = filter && Camera.orientation,
front = filter && Quat.getFront(orientation),
verticalAngleNormal = filter && Quat.getRight(orientation),
horizontalAngleNormal = filter && Quat.getUp(orientation);
avatars.forEach(function (id) { // sorting the identifiers is just an aid for debugging avatars.forEach(function (id) { // sorting the identifiers is just an aid for debugging
var avatar = AvatarList.getAvatar(id); var avatar = AvatarList.getAvatar(id);
var name = avatar.sessionDisplayName;
if (!name) {
// Either we got a data packet but no identity yet, or something is really messed up. In any case,
// we won't be able to do anything with this user, so don't include them.
// In normal circumstances, a refresh will bring in the new user, but if we're very heavily loaded,
// we could be losing and gaining people randomly.
print('No avatar identity data for', id);
return;
}
if (id && myPosition && (Vec3.distance(avatar.position, myPosition) > filter.distance)) {
return;
}
var normal = id && filter && Vec3.normalize(Vec3.subtract(avatar.position, myPosition));
var horizontal = normal && angleBetweenVectorsInPlane(normal, front, horizontalAngleNormal);
var vertical = normal && angleBetweenVectorsInPlane(normal, front, verticalAngleNormal);
if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) {
return;
}
var avatarPalDatum = { var avatarPalDatum = {
displayName: avatar.sessionDisplayName, displayName: name,
userName: '', userName: '',
sessionId: id || '', sessionId: id || '',
audioLevel: 0.0, audioLevel: 0.0,
@ -289,10 +331,12 @@ function populateUserList(selectData) {
addAvatarNode(id); // No overlay for ourselves addAvatarNode(id); // No overlay for ourselves
// Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin. // Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin.
Users.requestUsernameFromID(id); Users.requestUsernameFromID(id);
avatarsOfInterest[id] = true;
} }
data.push(avatarPalDatum); data.push(avatarPalDatum);
print('PAL data:', JSON.stringify(avatarPalDatum)); print('PAL data:', JSON.stringify(avatarPalDatum));
}); });
conserveResources = Object.keys(avatarsOfInterest).length > 20;
sendToQml({ method: 'users', params: data }); sendToQml({ method: 'users', params: data });
if (selectData) { if (selectData) {
selectData[2] = true; selectData[2] = true;
@ -317,8 +361,8 @@ var pingPong = true;
function updateOverlays() { function updateOverlays() {
var eye = Camera.position; var eye = Camera.position;
AvatarList.getAvatarIdentifiers().forEach(function (id) { AvatarList.getAvatarIdentifiers().forEach(function (id) {
if (!id) { if (!id || !avatarsOfInterest[id]) {
return; // don't update ourself return; // don't update ourself, or avatars we're not interested in
} }
var avatar = AvatarList.getAvatar(id); var avatar = AvatarList.getAvatar(id);
if (!avatar) { if (!avatar) {