diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp
index 7132b5c38a..5e103cf767 100644
--- a/assignment-client/src/main.cpp
+++ b/assignment-client/src/main.cpp
@@ -17,8 +17,6 @@
#include "AssignmentClientMonitor.h"
int main(int argc, char* argv[]) {
- initialiseUsecTimestampNow();
-
#ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp
index 871c16a215..1d9e837f0a 100644
--- a/domain-server/src/main.cpp
+++ b/domain-server/src/main.cpp
@@ -23,8 +23,6 @@
#include "DomainServer.h"
int main(int argc, char* argv[]) {
- initialiseUsecTimestampNow();
-
#ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
diff --git a/examples/editVoxels.js b/examples/editVoxels.js
index 12c571b09c..453ac28649 100644
--- a/examples/editVoxels.js
+++ b/examples/editVoxels.js
@@ -1088,7 +1088,6 @@ function keyPressEvent(event) {
red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
- Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s);
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
setAudioPosition();
initialVoxelSound.playRandom();
@@ -1394,7 +1393,6 @@ function checkControllers() {
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
- Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
newColor.red, newColor.green, newColor.blue);
diff --git a/examples/swissArmyJetpack.js b/examples/swissArmyJetpack.js
new file mode 100644
index 0000000000..9bb5bea267
--- /dev/null
+++ b/examples/swissArmyJetpack.js
@@ -0,0 +1,142 @@
+//
+// swissArmyJetpack.js
+// examples
+//
+// Created by Andrew Meadows 2014.04.24
+// Copyright 2014 High Fidelity, Inc.
+//
+// This is a work in progress. It will eventually be able to move the avatar around,
+// toggle collision groups, modify avatar movement options, and other stuff (maybe trigger animations).
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+var numberOfButtons = 3;
+
+var enabledColors = new Array();
+enabledColors[0] = { red: 255, green: 0, blue: 0};
+enabledColors[1] = { red: 0, green: 255, blue: 0};
+enabledColors[2] = { red: 0, green: 0, blue: 255};
+
+var disabledColors = new Array();
+disabledColors[0] = { red: 90, green: 75, blue: 75};
+disabledColors[1] = { red: 75, green: 90, blue: 75};
+disabledColors[2] = { red: 75, green: 90, blue: 90};
+
+var buttons = new Array();
+var labels = new Array();
+
+var labelContents = new Array();
+labelContents[0] = "Collide with Avatars";
+labelContents[1] = "Collide with Voxels";
+labelContents[2] = "Collide with Particles";
+var groupBits = 0;
+
+var buttonStates = new Array();
+
+var disabledOffsetT = 0;
+var enabledOffsetT = 55;
+
+var buttonX = 50;
+var buttonY = 200;
+var buttonWidth = 30;
+var buttonHeight = 54;
+var textX = buttonX + buttonWidth + 10;
+
+for (i = 0; i < numberOfButtons; i++) {
+ var offsetS = 12
+ var offsetT = disabledOffsetT;
+
+ buttons[i] = Overlays.addOverlay("image", {
+ //x: buttonX + (buttonWidth * i),
+ x: buttonX,
+ y: buttonY + (buttonHeight * i),
+ width: buttonWidth,
+ height: buttonHeight,
+ subImage: { x: offsetS, y: offsetT, width: buttonWidth, height: buttonHeight },
+ imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg",
+ color: disabledColors[i],
+ alpha: 1,
+ });
+
+ labels[i] = Overlays.addOverlay("text", {
+ x: textX,
+ y: buttonY + (buttonHeight * i) + 12,
+ width: 150,
+ height: 50,
+ color: { red: 0, green: 0, blue: 0},
+ textColor: { red: 255, green: 0, blue: 0},
+ topMargin: 4,
+ leftMargin: 4,
+ text: labelContents[i]
+ });
+
+ buttonStates[i] = false;
+}
+
+function updateButton(i, enabled) {
+ var offsetY = disabledOffsetT;
+ var buttonColor = disabledColors[i];
+ groupBits
+ if (enabled) {
+ offsetY = enabledOffsetT;
+ buttonColor = enabledColors[i];
+ if (i == 0) {
+ groupBits |= COLLISION_GROUP_AVATARS;
+ } else if (i == 1) {
+ groupBits |= COLLISION_GROUP_VOXELS;
+ } else if (i == 2) {
+ groupBits |= COLLISION_GROUP_PARTICLES;
+ }
+ } else {
+ if (i == 0) {
+ groupBits &= ~COLLISION_GROUP_AVATARS;
+ } else if (i == 1) {
+ groupBits &= ~COLLISION_GROUP_VOXELS;
+ } else if (i == 2) {
+ groupBits &= ~COLLISION_GROUP_PARTICLES;
+ }
+ }
+ MyAvatar.collisionGroups = groupBits;
+
+ Overlays.editOverlay(buttons[i], { subImage: { y: offsetY } } );
+ Overlays.editOverlay(buttons[i], { color: buttonColor } );
+ buttonStates[i] = enabled;
+}
+
+// When our script shuts down, we should clean up all of our overlays
+function scriptEnding() {
+ for (i = 0; i < numberOfButtons; i++) {
+ print("adebug deleting overlay " + i);
+ Overlays.deleteOverlay(buttons[i]);
+ Overlays.deleteOverlay(labels[i]);
+ }
+}
+Script.scriptEnding.connect(scriptEnding);
+
+
+// Our update() function is called at approximately 60fps, and we will use it to animate our various overlays
+function update(deltaTime) {
+ if (groupBits != MyAvatar.collisionGroups) {
+ groupBits = MyAvatar.collisionGroups;
+ updateButton(0, groupBits & COLLISION_GROUP_AVATARS);
+ updateButton(1, groupBits & COLLISION_GROUP_VOXELS);
+ updateButton(2, groupBits & COLLISION_GROUP_PARTICLES);
+ }
+}
+Script.update.connect(update);
+
+
+// we also handle click detection in our mousePressEvent()
+function mousePressEvent(event) {
+ var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
+ for (i = 0; i < numberOfButtons; i++) {
+ if (clickedOverlay == buttons[i]) {
+ var enabled = !(buttonStates[i]);
+ updateButton(i, enabled);
+ }
+ }
+}
+Controller.mousePressEvent.connect(mousePressEvent);
+
diff --git a/interface/resources/images/pin.svg b/interface/resources/images/pin.svg
deleted file mode 100644
index ec968a1ec1..0000000000
--- a/interface/resources/images/pin.svg
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
diff --git a/interface/resources/images/pinned.svg b/interface/resources/images/pinned.svg
deleted file mode 100644
index bda6f0e747..0000000000
--- a/interface/resources/images/pinned.svg
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 4adb2f772a..97b5c05f25 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -857,7 +857,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_G:
if (isShifted) {
- Menu::getInstance()->triggerOption(MenuOption::Gravity);
+ Menu::getInstance()->triggerOption(MenuOption::ObeyGravity);
}
break;
@@ -3520,35 +3520,30 @@ void Application::parseVersionXml() {
QString operatingSystem("ubuntu");
#endif
- QString releaseDate;
- QString releaseNotes;
QString latestVersion;
QUrl downloadUrl;
+ QString releaseNotes("Unavailable");
QObject* sender = QObject::sender();
QXmlStreamReader xml(qobject_cast(sender));
+
while (!xml.atEnd() && !xml.hasError()) {
- QXmlStreamReader::TokenType token = xml.readNext();
-
- if (token == QXmlStreamReader::StartElement) {
- if (xml.name() == "ReleaseDate") {
+ if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) {
+ while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) {
+ if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") {
+ xml.readNext();
+ latestVersion = xml.text().toString();
+ }
+ if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") {
+ xml.readNext();
+ downloadUrl = QUrl(xml.text().toString());
+ }
xml.readNext();
- releaseDate = xml.text().toString();
- }
- if (xml.name() == "ReleaseNotes") {
- xml.readNext();
- releaseNotes = xml.text().toString();
- }
- if (xml.name() == "Version") {
- xml.readNext();
- latestVersion = xml.text().toString();
- }
- if (xml.name() == operatingSystem) {
- xml.readNext();
- downloadUrl = QUrl(xml.text().toString());
}
}
+ xml.readNext();
}
+
if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) {
new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl);
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 5983b0d1a9..325770a8df 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -16,6 +16,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index c182f6e842..68e38615bf 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -644,10 +644,10 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
const int NUM_INITIAL_PACKETS_DISCARD = 3;
const int STANDARD_DEVIATION_SAMPLE_COUNT = 500;
- _timeSinceLastRecieved.start();
_totalPacketsReceived++;
- double timeDiff = (double)_timeSinceLastRecieved.nsecsElapsed() / 1000000.0; // ns to ms
+ double timeDiff = (double)_timeSinceLastReceived.nsecsElapsed() / 1000000.0; // ns to ms
+ _timeSinceLastReceived.start();
// Discard first few received packets for computing jitter (often they pile up on start)
if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) {
@@ -1265,7 +1265,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
// setup a procedural audio output device
_proceduralAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
- _timeSinceLastRecieved.start();
+ _timeSinceLastReceived.start();
// setup spatial audio ringbuffer
int numFrameSamples = _outputFormat.sampleRate() * _desiredOutputFormat.channelCount();
diff --git a/interface/src/Audio.h b/interface/src/Audio.h
index e1b8a7dddc..277c606d4b 100644
--- a/interface/src/Audio.h
+++ b/interface/src/Audio.h
@@ -129,7 +129,7 @@ private:
QString _outputAudioDeviceName;
StDev _stdev;
- QElapsedTimer _timeSinceLastRecieved;
+ QElapsedTimer _timeSinceLastReceived;
float _averagedLatency;
float _measuredJitter;
int16_t _jitterBufferSamples;
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index a7012d838d..44117df55c 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -27,6 +27,7 @@
#include
#include
#include
+#include
#include
#include
@@ -186,9 +187,9 @@ Menu::Menu() :
QAction::PreferencesRole);
addDisabledActionAndSeparator(editMenu, "Physics");
- addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false);
-
-
+ QObject* avatar = appInstance->getAvatar();
+ addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyGravity, Qt::SHIFT | Qt::Key_G, true,
+ avatar, SLOT(updateMotionBehaviorFlags()));
addAvatarCollisionSubMenu(editMenu);
@@ -507,7 +508,7 @@ void Menu::loadSettings(QSettings* settings) {
// MyAvatar caches some menu options, so we have to update them whenever we load settings.
// TODO: cache more settings in MyAvatar that are checked with very high frequency.
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
- myAvatar->updateCollisionFlags();
+ myAvatar->updateCollisionGroups();
if (lockedSettings) {
Application::getInstance()->unlockSettings();
@@ -970,6 +971,17 @@ void Menu::goToUser(const QString& user) {
connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
}
+/// Open a url, shortcutting any "hifi" scheme URLs to the local application.
+void Menu::openUrl(const QUrl& url) {
+ if (url.scheme() == "hifi") {
+ QString path = url.toString(QUrl::RemoveScheme);
+ path = path.remove(QRegExp("^:?/*"));
+ goTo(path);
+ } else {
+ QDesktopServices::openUrl(url);
+ }
+}
+
void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData) {
QMessageBox msgBox;
msgBox.setText("Both user and location exists with same name");
@@ -1145,23 +1157,22 @@ void Menu::showScriptEditor() {
void Menu::showChat() {
QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_chatWindow) {
- mainWindow->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow());
+ _chatWindow = new ChatWindow(mainWindow);
}
- if (!_chatWindow->toggleViewAction()->isChecked()) {
- const QRect& windowGeometry = mainWindow->geometry();
- _chatWindow->move(windowGeometry.topRight().x() - _chatWindow->width(),
- windowGeometry.topRight().y() + (windowGeometry.height() / 2) - (_chatWindow->height() / 2));
-
- _chatWindow->resize(0, _chatWindow->height());
- _chatWindow->toggleViewAction()->trigger();
+ if (_chatWindow->isHidden()) {
+ _chatWindow->show();
}
}
void Menu::toggleChat() {
#ifdef HAVE_QXMPP
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
- if (!_chatAction->isEnabled() && _chatWindow && _chatWindow->toggleViewAction()->isChecked()) {
- _chatWindow->toggleViewAction()->trigger();
+ if (!_chatAction->isEnabled() && _chatWindow) {
+ if (_chatWindow->isHidden()) {
+ _chatWindow->show();
+ } else {
+ _chatWindow->hide();
+ }
}
#endif
}
@@ -1370,13 +1381,13 @@ void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) {
Application* appInstance = Application::getInstance();
QObject* avatar = appInstance->getAvatar();
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment,
- 0, false, avatar, SLOT(updateCollisionFlags()));
+ 0, false, avatar, SLOT(updateCollisionGroups()));
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars,
- 0, true, avatar, SLOT(updateCollisionFlags()));
+ 0, true, avatar, SLOT(updateCollisionGroups()));
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels,
- 0, false, avatar, SLOT(updateCollisionFlags()));
+ 0, false, avatar, SLOT(updateCollisionGroups()));
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles,
- 0, true, avatar, SLOT(updateCollisionFlags()));
+ 0, true, avatar, SLOT(updateCollisionGroups()));
}
QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 09b5fabfc8..bc70f8f83f 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -154,6 +154,7 @@ public slots:
void goTo();
void goToUser(const QString& user);
void pasteToVoxel();
+ void openUrl(const QUrl& url);
void toggleLoginMenuItem();
@@ -314,7 +315,7 @@ namespace MenuOption {
const QString GoTo = "Go To...";
const QString GoToDomain = "Go To Domain...";
const QString GoToLocation = "Go To Location...";
- const QString Gravity = "Use Gravity";
+ const QString ObeyGravity = "Obey Gravity";
const QString HandsCollideWithSelf = "Collide With Self";
const QString HeadMouse = "Head Mouse";
const QString IncreaseAvatarSize = "Increase Avatar Size";
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index fe685b89f8..b060c3421a 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -47,16 +47,14 @@ Avatar::Avatar() :
AvatarData(),
_skeletonModel(this),
_bodyYawDelta(0.0f),
- _mode(AVATAR_MODE_STANDING),
_velocity(0.0f, 0.0f, 0.0f),
- _thrust(0.0f, 0.0f, 0.0f),
_leanScale(0.5f),
_scale(1.0f),
_worldUpDirection(DEFAULT_UP_DIRECTION),
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
_mouseRayDirection(0.0f, 0.0f, 0.0f),
_moving(false),
- _collisionFlags(0),
+ _collisionGroups(0),
_initialized(false),
_shouldRenderBillboard(true)
{
@@ -138,20 +136,9 @@ void Avatar::simulate(float deltaTime) {
head->simulate(deltaTime, false, _shouldRenderBillboard);
}
- // use speed and angular velocity to determine walking vs. standing
- float speed = glm::length(_velocity);
- if (speed + fabs(_bodyYawDelta) > 0.2) {
- _mode = AVATAR_MODE_WALKING;
- } else {
- _mode = AVATAR_MODE_INTERACTING;
- }
-
// update position by velocity, and subtract the change added earlier for gravity
_position += _velocity * deltaTime;
- // Zero thrust out now that we've added it to velocity in this frame
- _thrust = glm::vec3(0, 0, 0);
-
// update animation for display name fade in/out
if ( _displayNameTargetAlpha != _displayNameAlpha) {
// the alpha function is
@@ -166,7 +153,7 @@ void Avatar::simulate(float deltaTime) {
// Fading in
_displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef;
}
- _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01? _displayNameTargetAlpha : _displayNameAlpha;
+ _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
}
}
@@ -563,7 +550,7 @@ bool Avatar::findCollisions(const QVector& shapes, CollisionList&
}
bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
- if (_collisionFlags & COLLISION_GROUP_PARTICLES) {
+ if (_collisionGroups & COLLISION_GROUP_PARTICLES) {
return false;
}
bool collided = false;
@@ -753,19 +740,19 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
glEnd();
}
-void Avatar::updateCollisionFlags() {
- _collisionFlags = 0;
+void Avatar::updateCollisionGroups() {
+ _collisionGroups = 0;
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithEnvironment)) {
- _collisionFlags |= COLLISION_GROUP_ENVIRONMENT;
+ _collisionGroups |= COLLISION_GROUP_ENVIRONMENT;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) {
- _collisionFlags |= COLLISION_GROUP_AVATARS;
+ _collisionGroups |= COLLISION_GROUP_AVATARS;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) {
- _collisionFlags |= COLLISION_GROUP_VOXELS;
+ _collisionGroups |= COLLISION_GROUP_VOXELS;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
- _collisionFlags |= COLLISION_GROUP_PARTICLES;
+ _collisionGroups |= COLLISION_GROUP_PARTICLES;
}
}
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index ecf1be4899..bcf3487e09 100755
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -46,13 +46,6 @@ enum DriveKeys {
MAX_DRIVE_KEYS
};
-enum AvatarMode {
- AVATAR_MODE_STANDING = 0,
- AVATAR_MODE_WALKING,
- AVATAR_MODE_INTERACTING,
- NUM_AVATAR_MODES
-};
-
enum ScreenTintLayer {
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
SCREEN_TINT_BEFORE_AVATARS,
@@ -70,6 +63,7 @@ class Texture;
class Avatar : public AvatarData {
Q_OBJECT
+ Q_PROPERTY(quint32 collisionGroups READ getCollisionGroups WRITE setCollisionGroups)
public:
Avatar();
@@ -155,8 +149,11 @@ public:
virtual float getBoundingRadius() const;
void updateShapePositions();
+ quint32 getCollisionGroups() const { return _collisionGroups; }
+ virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); }
+
public slots:
- void updateCollisionFlags();
+ void updateCollisionGroups();
signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
@@ -164,9 +161,7 @@ signals:
protected:
SkeletonModel _skeletonModel;
float _bodyYawDelta;
- AvatarMode _mode;
glm::vec3 _velocity;
- glm::vec3 _thrust;
float _leanScale;
float _scale;
glm::vec3 _worldUpDirection;
@@ -175,7 +170,7 @@ protected:
float _stringLength;
bool _moving; ///< set when position is changing
- uint32_t _collisionFlags;
+ quint32 _collisionGroups;
// protected methods...
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index fb547da13e..26dbc09d5b 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -12,6 +12,7 @@
#include
#include
+#include
#include
#include
@@ -54,16 +55,13 @@ MyAvatar::MyAvatar() :
_shouldJump(false),
_gravity(0.0f, -1.0f, 0.0f),
_distanceToNearestAvatar(std::numeric_limits::max()),
- _elapsedTimeMoving(0.0f),
- _elapsedTimeStopped(0.0f),
- _elapsedTimeSinceCollision(0.0f),
_lastCollisionPosition(0, 0, 0),
_speedBrakes(false),
+ _thrust(0.0f),
_isThrustOn(false),
_thrustMultiplier(1.0f),
- _moveTarget(0,0,0),
+ _motionBehaviors(0),
_lastBodyPenetration(0.0f),
- _moveTargetStepCounter(0),
_lookAtTargetAvatar(),
_shouldRender(true),
_billboardValid(false),
@@ -97,11 +95,6 @@ void MyAvatar::reset() {
setOrientation(glm::quat(glm::vec3(0.0f)));
}
-void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
- _moveTarget = moveTarget;
- _moveTargetStepCounter = 0;
-}
-
void MyAvatar::update(float deltaTime) {
Head* head = getHead();
head->relaxLean(deltaTime);
@@ -132,7 +125,7 @@ void MyAvatar::update(float deltaTime) {
head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
- if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
+ if (_motionBehaviors & AVATAR_MOTION_OBEY_GRAVITY) {
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
} else {
setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
@@ -145,17 +138,6 @@ void MyAvatar::simulate(float deltaTime) {
glm::quat orientation = getOrientation();
- // Update movement timers
- _elapsedTimeSinceCollision += deltaTime;
- const float VELOCITY_MOVEMENT_TIMER_THRESHOLD = 0.2f;
- if (glm::length(_velocity) < VELOCITY_MOVEMENT_TIMER_THRESHOLD) {
- _elapsedTimeMoving = 0.0f;
- _elapsedTimeStopped += deltaTime;
- } else {
- _elapsedTimeStopped = 0.0f;
- _elapsedTimeMoving += deltaTime;
- }
-
if (_scale != _targetScale) {
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
setScale(scale);
@@ -269,34 +251,11 @@ void MyAvatar::simulate(float deltaTime) {
// update the euler angles
setOrientation(orientation);
- const float WALKING_SPEED_THRESHOLD = 0.2f;
- // use speed and angular velocity to determine walking vs. standing
- float speed = glm::length(_velocity);
- if (speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) {
- _mode = AVATAR_MODE_WALKING;
- } else {
- _mode = AVATAR_MODE_INTERACTING;
- }
-
// update moving flag based on speed
const float MOVING_SPEED_THRESHOLD = 0.01f;
+ float speed = glm::length(_velocity);
_moving = speed > MOVING_SPEED_THRESHOLD;
- // If a move target is set, update position explicitly
- const float MOVE_FINISHED_TOLERANCE = 0.1f;
- const float MOVE_SPEED_FACTOR = 2.0f;
- const int MOVE_TARGET_MAX_STEPS = 250;
- if ((glm::length(_moveTarget) > EPSILON) && (_moveTargetStepCounter < MOVE_TARGET_MAX_STEPS)) {
- if (glm::length(_position - _moveTarget) > MOVE_FINISHED_TOLERANCE) {
- _position += (_moveTarget - _position) * (deltaTime * MOVE_SPEED_FACTOR);
- _moveTargetStepCounter++;
- } else {
- // Move completed
- _moveTarget = glm::vec3(0,0,0);
- _moveTargetStepCounter = 0;
- }
- }
-
updateChatCircle(deltaTime);
_position += _velocity * deltaTime;
@@ -324,10 +283,10 @@ void MyAvatar::simulate(float deltaTime) {
head->simulate(deltaTime, true);
// Zero thrust out now that we've added it to velocity in this frame
- _thrust = glm::vec3(0.0f);
+ _thrust *= glm::vec3(0.0f);
// now that we're done stepping the avatar forward in time, compute new collisions
- if (_collisionFlags != 0) {
+ if (_collisionGroups != 0) {
Camera* myCamera = Application::getInstance()->getCamera();
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
@@ -335,15 +294,15 @@ void MyAvatar::simulate(float deltaTime) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
radius *= COLLISION_RADIUS_SCALAR;
}
- if (_collisionFlags) {
+ if (_collisionGroups) {
updateShapePositions();
- if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
+ if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
updateCollisionWithEnvironment(deltaTime, radius);
}
- if (_collisionFlags & COLLISION_GROUP_VOXELS) {
+ if (_collisionGroups & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
}
- if (_collisionFlags & COLLISION_GROUP_AVATARS) {
+ if (_collisionGroups & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
}
}
@@ -451,8 +410,6 @@ void MyAvatar::renderDebugBodyPoints() {
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(0.15, 10, 10);
glPopMatrix();
-
-
}
// virtual
@@ -830,7 +787,6 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
// cancel out the velocity component in the direction of penetration
float penetrationLength = glm::length(penetration);
if (penetrationLength > EPSILON) {
- _elapsedTimeSinceCollision = 0.0f;
glm::vec3 direction = penetration / penetrationLength;
_velocity -= glm::dot(_velocity, direction) * direction * (1.0f + elasticity);
_velocity *= glm::clamp(1.0f - damping, 0.0f, 1.0f);
@@ -867,7 +823,7 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.0f),
frequency * (1.0f + velocityTangentToCollision / velocityTowardCollision),
std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.0f),
- 1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true);
+ 1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false);
}
}
@@ -1184,8 +1140,31 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
setPosition(newPosition);
emit transformChanged();
+ } else {
+ QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
}
-
+}
+
+void MyAvatar::updateMotionBehaviors() {
+ _motionBehaviors = 0;
+ if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyGravity)) {
+ _motionBehaviors |= AVATAR_MOTION_OBEY_GRAVITY;
+ }
+}
+
+void MyAvatar::setCollisionGroups(quint32 collisionGroups) {
+ Avatar::setCollisionGroups(collisionGroups & VALID_COLLISION_GROUPS);
+ Menu* menu = Menu::getInstance();
+ menu->setIsOptionChecked(MenuOption::CollideWithEnvironment, (bool)(_collisionGroups & COLLISION_GROUP_ENVIRONMENT));
+ menu->setIsOptionChecked(MenuOption::CollideWithAvatars, (bool)(_collisionGroups & COLLISION_GROUP_AVATARS));
+ menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS));
+ menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES));
+}
+
+void MyAvatar::setMotionBehaviors(quint32 flags) {
+ _motionBehaviors = flags;
+ Menu* menu = Menu::getInstance();
+ menu->setIsOptionChecked(MenuOption::ObeyGravity, (bool)(_motionBehaviors & AVATAR_MOTION_OBEY_GRAVITY));
}
void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) {
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 71c74f7c91..9b6b13568f 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -25,9 +25,12 @@ enum AvatarHandState
NUM_HAND_STATES
};
+const quint32 AVATAR_MOTION_OBEY_GRAVITY = 1U << 0;
+
class MyAvatar : public Avatar {
Q_OBJECT
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
+ Q_PROPERTY(quint32 motionBehaviors READ getMotionBehaviors WRITE setMotionBehaviors)
public:
MyAvatar();
@@ -54,10 +57,7 @@ public:
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
// getters
- AvatarMode getMode() const { return _mode; }
float getLeanScale() const { return _leanScale; }
- float getElapsedTimeStopped() const { return _elapsedTimeStopped; }
- float getElapsedTimeMoving() const { return _elapsedTimeMoving; }
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
glm::vec3 getGravity() const { return _gravity; }
@@ -91,6 +91,10 @@ public:
virtual void setFaceModelURL(const QUrl& faceModelURL);
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
+ virtual void setCollisionGroups(quint32 collisionGroups);
+ void setMotionBehaviors(quint32 flags);
+ quint32 getMotionBehaviors() const { return _motionBehaviors; }
+
void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration);
public slots:
@@ -107,6 +111,8 @@ public slots:
glm::vec3 getThrust() { return _thrust; };
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
+ void updateMotionBehaviors();
+
signals:
void transformChanged();
@@ -118,16 +124,17 @@ private:
float _driveKeys[MAX_DRIVE_KEYS];
glm::vec3 _gravity;
float _distanceToNearestAvatar; // How close is the nearest avatar?
- float _elapsedTimeMoving; // Timers to drive camera transitions when moving
- float _elapsedTimeStopped;
- float _elapsedTimeSinceCollision;
+
+ // motion stuff
glm::vec3 _lastCollisionPosition;
bool _speedBrakes;
+ glm::vec3 _thrust; // final acceleration for the current frame
bool _isThrustOn;
float _thrustMultiplier;
- glm::vec3 _moveTarget;
+
+ quint32 _motionBehaviors;
+
glm::vec3 _lastBodyPenetration;
- int _moveTargetStepCounter;
QWeakPointer _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition;
bool _shouldRender;
diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp
index 8009551b6c..f80c331df4 100644
--- a/interface/src/location/LocationManager.cpp
+++ b/interface/src/location/LocationManager.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
+
#include "Application.h"
#include "LocationManager.h"
@@ -118,6 +120,8 @@ void LocationManager::checkForMultipleDestinations() {
Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData);
return;
}
+
+ QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
}
}
diff --git a/interface/src/main.cpp b/interface/src/main.cpp
index 6f9dc5e3bd..2bb0633f24 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -16,7 +16,6 @@
#include
int main(int argc, const char * argv[]) {
- initialiseUsecTimestampNow();
QElapsedTimer startupTime;
startupTime.start();
diff --git a/interface/src/ui/ChatInputArea.cpp b/interface/src/ui/ChatInputArea.cpp
new file mode 100644
index 0000000000..3e8fc84fe2
--- /dev/null
+++ b/interface/src/ui/ChatInputArea.cpp
@@ -0,0 +1,19 @@
+//
+// ChatInputArea.cpp
+// interface/src/ui
+//
+// Created by Ryan Huffman on 4/24/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "ChatInputArea.h"
+
+ChatInputArea::ChatInputArea(QWidget* parent) : QTextEdit(parent) {
+};
+
+void ChatInputArea::insertFromMimeData(const QMimeData* source) {
+ insertPlainText(source->text());
+};
diff --git a/interface/src/ui/ChatInputArea.h b/interface/src/ui/ChatInputArea.h
new file mode 100644
index 0000000000..31d1584df7
--- /dev/null
+++ b/interface/src/ui/ChatInputArea.h
@@ -0,0 +1,27 @@
+//
+// ChatInputArea.h
+// interface/src/ui
+//
+// Created by Ryan Huffman on 4/11/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_ChatInputArea_h
+#define hifi_ChatInputArea_h
+
+#include
+#include
+
+class ChatInputArea : public QTextEdit {
+ Q_OBJECT
+public:
+ ChatInputArea(QWidget* parent);
+
+protected:
+ void insertFromMimeData(const QMimeData* source);
+};
+
+#endif // hifi_ChatInputArea_h
diff --git a/interface/src/ui/ChatMessageArea.cpp b/interface/src/ui/ChatMessageArea.cpp
index f15b788990..1e16a8a2db 100644
--- a/interface/src/ui/ChatMessageArea.cpp
+++ b/interface/src/ui/ChatMessageArea.cpp
@@ -9,13 +9,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "Application.h"
#include "ChatMessageArea.h"
#include
#include
-ChatMessageArea::ChatMessageArea() : QTextBrowser() {
+ChatMessageArea::ChatMessageArea(bool useFixedHeight) : QTextBrowser(), _useFixedHeight(useFixedHeight) {
+ setOpenLinks(false);
+
connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
this, &ChatMessageArea::updateLayout);
+ connect(this, &QTextBrowser::anchorClicked,
+ Menu::getInstance(), &Menu::openUrl);
}
void ChatMessageArea::setHtml(const QString& html) {
@@ -34,7 +39,15 @@ void ChatMessageArea::setHtml(const QString& html) {
}
void ChatMessageArea::updateLayout() {
- setFixedHeight(document()->size().height());
+ if (_useFixedHeight) {
+ setFixedHeight(document()->size().height());
+ updateGeometry();
+ emit sizeChanged(size());
+ }
+}
+
+void ChatMessageArea::setSize(const QSize& size) {
+ setFixedHeight(size.height());
updateGeometry();
}
diff --git a/interface/src/ui/ChatMessageArea.h b/interface/src/ui/ChatMessageArea.h
index 1c49c60b08..57199538fd 100644
--- a/interface/src/ui/ChatMessageArea.h
+++ b/interface/src/ui/ChatMessageArea.h
@@ -19,15 +19,19 @@ const int CHAT_MESSAGE_LINE_HEIGHT = 130;
class ChatMessageArea : public QTextBrowser {
Q_OBJECT
public:
- ChatMessageArea();
+ ChatMessageArea(bool useFixedHeight = true);
virtual void setHtml(const QString& html);
public slots:
void updateLayout();
+ void setSize(const QSize& size);
+
+signals:
+ void sizeChanged(QSize newSize);
protected:
virtual void wheelEvent(QWheelEvent* event);
-
+ bool _useFixedHeight;
};
#endif // hifi_ChatMessageArea_h
diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp
index 635f1f3d10..e0802c6bc5 100644
--- a/interface/src/ui/ChatWindow.cpp
+++ b/interface/src/ui/ChatWindow.cpp
@@ -12,12 +12,10 @@
#include
#include
#include
-#include
#include
#include
#include
#include
-#include
#include "Application.h"
#include "FlowLayout.h"
@@ -31,32 +29,40 @@
const int NUM_MESSAGES_TO_TIME_STAMP = 20;
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
+const QRegularExpression regexHifiLinks("([#@]\\S+)");
-ChatWindow::ChatWindow() :
+ChatWindow::ChatWindow(QWidget* parent) :
+ FramelessDialog(parent, 0, POSITION_RIGHT),
ui(new Ui::ChatWindow),
numMessagesAfterLastTimeStamp(0),
_mousePressed(false),
_mouseStartPosition()
{
- ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose, false);
- // remove the title bar (see the Qt docs on setTitleBarWidget), but we keep it for undocking
- //
- titleBar = titleBarWidget();
- setTitleBarWidget(new QWidget());
+ ui->setupUi(this);
FlowLayout* flowLayout = new FlowLayout(0, 4, 4);
ui->usersWidget->setLayout(flowLayout);
- ui->messagesGridLayout->setColumnStretch(0, 1);
- ui->messagesGridLayout->setColumnStretch(1, 3);
-
ui->messagePlainTextEdit->installEventFilter(this);
+ ui->messagePlainTextEdit->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+
+ QTextCursor cursor(ui->messagePlainTextEdit->textCursor());
+
+ cursor.movePosition(QTextCursor::Start);
+
+ QTextBlockFormat format = cursor.blockFormat();
+ format.setLineHeight(130, QTextBlockFormat::ProportionalHeight);
+
+ cursor.setBlockFormat(format);
+
+ ui->messagePlainTextEdit->setTextCursor(cursor);
if (!AccountManager::getInstance().isLoggedIn()) {
ui->connectingToXMPPLabel->setText(tr("You must be logged in to chat with others."));
}
-
+
#ifdef HAVE_QXMPP
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
if (xmppClient.isConnected()) {
@@ -89,36 +95,17 @@ ChatWindow::~ChatWindow() {
delete ui;
}
-void ChatWindow::mousePressEvent(QMouseEvent *e) {
- if (e->button() == Qt::LeftButton && isFloating()) {
- _mousePressed = true;
- _mouseStartPosition = e->pos();
- }
-}
-
-void ChatWindow::mouseMoveEvent(QMouseEvent *e) {
- if (_mousePressed) {
- move(mapToParent(e->pos() - _mouseStartPosition));
- }
-}
-
-void ChatWindow::mouseReleaseEvent( QMouseEvent *e ) {
- if ( e->button() == Qt::LeftButton ) {
- _mousePressed = false;
- }
-}
-
void ChatWindow::keyPressEvent(QKeyEvent* event) {
- QDockWidget::keyPressEvent(event);
if (event->key() == Qt::Key_Escape) {
hide();
+ } else {
+ FramelessDialog::keyPressEvent(event);
}
}
void ChatWindow::showEvent(QShowEvent* event) {
- QDockWidget::showEvent(event);
+ FramelessDialog::showEvent(event);
if (!event->spontaneous()) {
- activateWindow();
ui->messagePlainTextEdit->setFocus();
}
}
@@ -141,18 +128,20 @@ bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
message.setBody(messageText);
XmppClient::getInstance().getXMPPClient().sendPacket(message);
#endif
- ui->messagePlainTextEdit->document()->clear();
+ QTextCursor cursor = ui->messagePlainTextEdit->textCursor();
+ cursor.select(QTextCursor::Document);
+ cursor.removeSelectedText();
}
return true;
}
- } else {
- if (event->type() != QEvent::MouseButtonRelease) {
- return false;
+ } else if (event->type() == QEvent::MouseButtonRelease) {
+ QVariant userVar = sender->property("user");
+ if (userVar.isValid()) {
+ Menu::getInstance()->goToUser("@" + userVar.toString());
+ return true;
}
- QString user = sender->property("user").toString();
- Menu::getInstance()->goToUser(user);
}
- return false;
+ return FramelessDialog::eventFilter(sender, event);
}
#ifdef HAVE_QXMPP
@@ -175,16 +164,17 @@ void ChatWindow::addTimeStamp() {
timeString.chop(1);
if (!timeString.isEmpty()) {
QLabel* timeLabel = new QLabel(timeString);
- timeLabel->setStyleSheet("color: palette(shadow);"
- "background-color: palette(highlight);"
+ timeLabel->setStyleSheet("color: #333333;"
+ "background-color: white;"
+ "font-size: 14pt;"
"padding: 4px;");
timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- timeLabel->setAlignment(Qt::AlignHCenter);
+ timeLabel->setAlignment(Qt::AlignLeft);
- bool atBottom = isAtBottom();
+ bool atBottom = isNearBottom();
- ui->messagesGridLayout->addWidget(timeLabel, ui->messagesGridLayout->rowCount(), 0, 1, 2);
- ui->messagesGridLayout->parentWidget()->updateGeometry();
+ ui->messagesVBoxLayout->addWidget(timeLabel);
+ ui->messagesVBoxLayout->parentWidget()->updateGeometry();
Application::processEvents();
numMessagesAfterLastTimeStamp = 0;
@@ -249,6 +239,7 @@ void ChatWindow::participantsChanged() {
"padding-bottom: 2px;"
"padding-left: 2px;"
"border: 1px solid palette(shadow);"
+ "font-size: 14pt;"
"font-weight: bold");
userLabel->setProperty("user", participantName);
userLabel->setCursor(Qt::PointingHandCursor);
@@ -262,15 +253,11 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
return;
}
- QLabel* userLabel = new QLabel(getParticipantName(message.from()));
- userLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- userLabel->setStyleSheet("padding: 2px; font-weight: bold");
- userLabel->setAlignment(Qt::AlignTop | Qt::AlignRight);
+ // Update background if this is a message from the current user
+ bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername();
- ChatMessageArea* messageArea = new ChatMessageArea();
-
- messageArea->setOpenLinks(true);
- messageArea->setOpenExternalLinks(true);
+ // Create message area
+ ChatMessageArea* messageArea = new ChatMessageArea(true);
messageArea->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
messageArea->setTextInteractionFlags(Qt::TextBrowserInteraction);
messageArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -281,22 +268,30 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
"padding-left: 2px;"
"padding-top: 2px;"
"padding-right: 20px;"
+ "margin: 0px;"
+ "color: #333333;"
+ "font-size: 14pt;"
"background-color: rgba(0, 0, 0, 0%);"
"border: 0;");
- bool fromSelf = getParticipantName(message.from()) == AccountManager::getInstance().getUsername();
+ QString userLabel = getParticipantName(message.from());
if (fromSelf) {
- userLabel->setStyleSheet(userLabel->styleSheet() + "; background-color: #e1e8ea");
- messageArea->setStyleSheet(messageArea->styleSheet() + "; background-color: #e1e8ea");
+ userLabel = "" + userLabel + ": ";
+ messageArea->setStyleSheet(messageArea->styleSheet() + "background-color: #e1e8ea");
+ } else {
+ userLabel = "" + userLabel + ": ";
}
- messageArea->setHtml(message.body().replace(regexLinks, "\\1"));
+ messageArea->document()->setDefaultStyleSheet("a { text-decoration: none; font-weight: bold; color: #267077;}");
+ QString messageText = message.body().toHtmlEscaped();
+ messageText = messageText.replace(regexLinks, "\\1");
+ messageText = messageText.replace(regexHifiLinks, "\\1");
+ messageArea->setHtml(userLabel + messageText);
- bool atBottom = isAtBottom();
- ui->messagesGridLayout->addWidget(userLabel, ui->messagesGridLayout->rowCount(), 0);
- ui->messagesGridLayout->addWidget(messageArea, ui->messagesGridLayout->rowCount() - 1, 1);
+ bool atBottom = isNearBottom();
- ui->messagesGridLayout->parentWidget()->updateGeometry();
+ ui->messagesVBoxLayout->addWidget(messageArea);
+ ui->messagesVBoxLayout->parentWidget()->updateGeometry();
Application::processEvents();
if (atBottom || fromSelf) {
@@ -313,26 +308,13 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
#endif
-bool ChatWindow::isAtBottom() {
+bool ChatWindow::isNearBottom() {
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
- return verticalScrollBar->sliderPosition() == verticalScrollBar->maximum();
+ return verticalScrollBar->value() >= verticalScrollBar->maximum() - Ui::AUTO_SCROLL_THRESHOLD;
}
// Scroll chat message area to bottom.
void ChatWindow::scrollToBottom() {
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
- verticalScrollBar->setSliderPosition(verticalScrollBar->maximum());
-}
-
-void ChatWindow::togglePinned() {
- QMainWindow* mainWindow = Application::getInstance()->getWindow();
- mainWindow->removeDockWidget(this);
- if (ui->togglePinnedButton->isChecked()) {
- mainWindow->addDockWidget(ui->togglePinnedButton->isChecked() ? Qt::RightDockWidgetArea : Qt::NoDockWidgetArea, this);
- }
- if (!this->toggleViewAction()->isChecked()) {
- this->toggleViewAction()->trigger();
- }
- this->setFloating(!ui->togglePinnedButton->isChecked());
- setTitleBarWidget(ui->togglePinnedButton->isChecked()?new QWidget():titleBar);
+ verticalScrollBar->setValue(verticalScrollBar->maximum());
}
diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h
index 6a807f9b81..104fbe1746 100644
--- a/interface/src/ui/ChatWindow.h
+++ b/interface/src/ui/ChatWindow.h
@@ -17,6 +17,7 @@
#include
#include
+#include "FramelessDialog.h"
#ifdef HAVE_QXMPP
@@ -26,37 +27,38 @@
#endif
namespace Ui {
+
+
+// Maximum amount the chat can be scrolled up in order to auto scroll.
+const int AUTO_SCROLL_THRESHOLD = 20;
+
+
class ChatWindow;
}
-class ChatWindow : public QDockWidget {
+class ChatWindow : public FramelessDialog {
Q_OBJECT
public:
- ChatWindow();
+ ChatWindow(QWidget* parent);
~ChatWindow();
- virtual void keyPressEvent(QKeyEvent *event);
- virtual void showEvent(QShowEvent* event);
-
- virtual void mousePressEvent(QMouseEvent *e);
- virtual void mouseMoveEvent(QMouseEvent *e);
- virtual void mouseReleaseEvent(QMouseEvent *e);
-
protected:
bool eventFilter(QObject* sender, QEvent* event);
+ virtual void keyPressEvent(QKeyEvent *event);
+ virtual void showEvent(QShowEvent* event);
+
private:
#ifdef HAVE_QXMPP
QString getParticipantName(const QString& participant);
#endif
void startTimerForTimeStamps();
void addTimeStamp();
- bool isAtBottom();
+ bool isNearBottom();
void scrollToBottom();
Ui::ChatWindow* ui;
- QWidget* titleBar;
int numMessagesAfterLastTimeStamp;
QDateTime lastMessageStamp;
bool _mousePressed;
@@ -65,7 +67,6 @@ private:
private slots:
void connected();
void timeout();
- void togglePinned();
#ifdef HAVE_QXMPP
void error(QXmppClient::Error error);
void participantsChanged();
diff --git a/interface/src/ui/FramelessDialog.cpp b/interface/src/ui/FramelessDialog.cpp
index 18e3bca89a..4919e99db6 100644
--- a/interface/src/ui/FramelessDialog.cpp
+++ b/interface/src/ui/FramelessDialog.cpp
@@ -14,8 +14,13 @@
const int RESIZE_HANDLE_WIDTH = 7;
-FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags) :
-QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) {
+FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags, Position position) :
+ QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint),
+ _isResizing(false),
+ _resizeInitialWidth(0),
+ _selfHidden(false),
+ _position(position) {
+
setAttribute(Qt::WA_DeleteOnClose);
// handle rezize and move events
@@ -29,29 +34,37 @@ bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) {
switch (event->type()) {
case QEvent::Move:
if (sender == parentWidget()) {
- // move to upper left corner on app move
- move(parentWidget()->geometry().topLeft());
+ resizeAndPosition(false);
}
break;
case QEvent::Resize:
if (sender == parentWidget()) {
- // keep full app height on resizing the app
- setFixedHeight(parentWidget()->size().height());
+ resizeAndPosition(false);
}
break;
case QEvent::WindowStateChange:
if (parentWidget()->isMinimized()) {
- setHidden(true);
- } else {
+ if (isVisible()) {
+ _selfHidden = true;
+ setHidden(true);
+ }
+ } else if (_selfHidden) {
+ _selfHidden = false;
setHidden(false);
}
break;
case QEvent::ApplicationDeactivate:
// hide on minimize and focus lost
- setHidden(true);
+ if (isVisible()) {
+ _selfHidden = true;
+ setHidden(true);
+ }
break;
case QEvent::ApplicationActivate:
- setHidden(false);
+ if (_selfHidden) {
+ _selfHidden = false;
+ setHidden(false);
+ }
break;
default:
break;
@@ -70,21 +83,38 @@ void FramelessDialog::setStyleSheetFile(const QString& fileName) {
}
void FramelessDialog::showEvent(QShowEvent* event) {
- // move to upper left corner
- move(parentWidget()->geometry().topLeft());
+ resizeAndPosition();
+}
+void FramelessDialog::resizeAndPosition(bool resizeParent) {
// keep full app height
setFixedHeight(parentWidget()->size().height());
// resize parrent if width is smaller than this dialog
- if (parentWidget()->size().width() < size().width()) {
+ if (resizeParent && parentWidget()->size().width() < size().width()) {
parentWidget()->resize(size().width(), parentWidget()->size().height());
}
+
+ if (_position == POSITION_LEFT) {
+ // move to upper left corner
+ move(parentWidget()->geometry().topLeft());
+ } else if (_position == POSITION_RIGHT) {
+ // move to upper right corner
+ QPoint pos = parentWidget()->geometry().topRight();
+ pos.setX(pos.x() - size().width());
+ move(pos);
+ }
}
+
void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) {
- if (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH && mouseEvent->button() == Qt::LeftButton) {
- _isResizing = true;
- QApplication::setOverrideCursor(Qt::SizeHorCursor);
+ if (mouseEvent->button() == Qt::LeftButton) {
+ bool hitLeft = _position == POSITION_LEFT && abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH;
+ bool hitRight = _position == POSITION_RIGHT && mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH;
+ if (hitLeft || hitRight) {
+ _isResizing = true;
+ _resizeInitialWidth = size().width();
+ QApplication::setOverrideCursor(Qt::SizeHorCursor);
+ }
}
}
@@ -95,6 +125,14 @@ void FramelessDialog::mouseReleaseEvent(QMouseEvent* mouseEvent) {
void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) {
if (_isResizing) {
- resize(mouseEvent->pos().x(), size().height());
+ if (_position == POSITION_LEFT) {
+ resize(mouseEvent->pos().x(), size().height());
+ } else if (_position == POSITION_RIGHT) {
+ setUpdatesEnabled(false);
+ resize(_resizeInitialWidth - mouseEvent->pos().x(), size().height());
+ resizeAndPosition();
+ _resizeInitialWidth = size().width();
+ setUpdatesEnabled(true);
+ }
}
}
diff --git a/interface/src/ui/FramelessDialog.h b/interface/src/ui/FramelessDialog.h
index db9f6dfd6c..828602a5db 100644
--- a/interface/src/ui/FramelessDialog.h
+++ b/interface/src/ui/FramelessDialog.h
@@ -19,7 +19,9 @@ class FramelessDialog : public QDialog {
Q_OBJECT
public:
- FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0);
+ enum Position { POSITION_LEFT, POSITION_RIGHT };
+
+ FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT);
void setStyleSheetFile(const QString& fileName);
protected:
@@ -31,7 +33,12 @@ protected:
bool eventFilter(QObject* sender, QEvent* event);
private:
+ void resizeAndPosition(bool resizeParent = true);
+
bool _isResizing;
+ int _resizeInitialWidth;
+ bool _selfHidden; ///< true when the dialog itself because of a window event (deactivation or minimization)
+ Position _position;
};
diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp
index 36508e94d1..7a70b743bd 100644
--- a/interface/src/ui/PreferencesDialog.cpp
+++ b/interface/src/ui/PreferencesDialog.cpp
@@ -19,7 +19,7 @@ const int SCROLL_PANEL_BOTTOM_MARGIN = 30;
const int OK_BUTTON_RIGHT_MARGIN = 30;
const int BUTTONS_TOP_MARGIN = 24;
-PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags) {
+PreferencesDialog::PreferencesDialog(QWidget* parent, Qt::WindowFlags flags) : FramelessDialog(parent, flags, POSITION_LEFT) {
ui.setupUi(this);
setStyleSheetFile("styles/preferences.qss");
@@ -38,26 +38,34 @@ void PreferencesDialog::accept() {
void PreferencesDialog::setHeadUrl(QString modelUrl) {
ui.faceURLEdit->setText(modelUrl);
- setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
}
void PreferencesDialog::setSkeletonUrl(QString modelUrl) {
ui.skeletonURLEdit->setText(modelUrl);
- setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
}
void PreferencesDialog::openHeadModelBrowser() {
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
+ show();
+
ModelsBrowser modelBrowser(Head);
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl);
modelBrowser.browse();
+
+ setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
+ show();
}
void PreferencesDialog::openBodyModelBrowser() {
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
+ show();
+
ModelsBrowser modelBrowser(Skeleton);
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl);
modelBrowser.browse();
+
+ setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
+ show();
}
void PreferencesDialog::openSnapshotLocationBrowser() {
@@ -176,6 +184,7 @@ void PreferencesDialog::savePreferences() {
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value());
+ Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value());
Application::getInstance()->resizeGL(Application::getInstance()->getGLWidget()->width(),
Application::getInstance()->getGLWidget()->height());
diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui
index 0372e00c09..4d223b2665 100644
--- a/interface/ui/chatWindow.ui
+++ b/interface/ui/chatWindow.ui
@@ -1,13 +1,13 @@
ChatWindow
-
+
0
0
400
- 608
+ 440
@@ -16,127 +16,95 @@
238
-
- font-family: Helvetica, Arial, sans-serif;
-
-
- QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable
-
-
- Qt::NoDockWidgetArea
-
Chat
-
-
-
- 0
-
-
- 8
-
-
- 8
-
-
- 8
-
-
- 8
-
- -
-
-
-
- 0
- 0
-
-
-
- Connecting to XMPP...
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- font-weight: bold; color: palette(shadow); margin-bottom: 4px;
-
-
- online now:
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16
- 16
-
-
-
- Qt::NoFocus
-
-
-
-
-
-
- ../resources/images/pin.svg
- ../resources/images/pinned.svg../resources/images/pin.svg
-
-
- true
-
-
- true
-
-
- false
-
-
- true
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16
- 16
-
-
-
- Qt::NoFocus
-
-
- QPushButton {
+
+ font-family: Helvetica, Arial, sans-serif;
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 0
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Connecting to XMPP...
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ font-weight: bold; color: palette(shadow); margin-bottom: 4px;
+
+
+ online now:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+ Qt::NoFocus
+
+
+ QPushButton {
background-color: rgba( 0, 0, 0, 0% );
border: none;
image: url(../resources/images/close.svg)
@@ -148,50 +116,104 @@ QPushButton:pressed {
border: none;
image: url(../resources/images/close_down.svg)
}
-
-
-
-
-
- true
-
-
-
-
-
- -
-
-
- -
-
-
- margin-top: 12px;
-
-
- Qt::ScrollBarAlwaysOff
-
-
- true
-
-
-
-
- 0
- 0
- 382
- 16
-
+
+
+
+
+
+ true
+
+
+
+
+
+ -
+
+
+ #usersWidget {
+ margin-right: 20px;
+}
+
+
+ -
+
+
+ margin-top: 12px;
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 382
+ 16
+
+
+
+
+ 0
+ 0
+
+
+
+ margin-top: 0px;
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+ -
+
-
+
0
0
-
- margin-top: 0px;
+
+
+ 0
+ 78
+
-
+
+ #chatFrame {
+border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;
+}
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+ 0
+
+
0
@@ -204,69 +226,65 @@ QPushButton:pressed {
0
-
- 0
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 60
+
+
+
+
+ Helvetica,Arial,sans-serif
+ 14
+
+
+
+
+
+
+ QFrame::NoFrame
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ true
+
+
+ false
+
+
+
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 60
-
-
-
- border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;
-
-
- QFrame::NoFrame
-
-
- Qt::ScrollBarAlwaysOff
-
-
- QAbstractScrollArea::AdjustToContents
-
-
- true
-
-
-
-
-
+
+
+
+
+
+
+ ChatInputArea
+ QTextEdit
+
+
+
- messagePlainTextEdit
messagesScrollArea
-
- togglePinnedButton
- clicked()
- ChatWindow
- togglePinned()
-
-
- 390
- 42
-
-
- 550
- 42
-
-
-
closeButton
clicked()
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index d1e4edf2ea..278923026d 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -282,10 +282,13 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
QUrl grantURL = _authURL;
grantURL.setPath("/oauth/token");
+ const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
+
QByteArray postData;
postData.append("grant_type=password&");
postData.append("username=" + login + "&");
- postData.append("password=" + password);
+ postData.append("password=" + password + "&");
+ postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp
index ebfb954bd8..5e812c06c3 100644
--- a/libraries/octree/src/Octree.cpp
+++ b/libraries/octree/src/Octree.cpp
@@ -542,6 +542,19 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co
return node;
}
+OctreeElement* Octree::getOctreeEnclosingElementAt(float x, float y, float z, float s) const {
+ unsigned char* octalCode = pointToOctalCode(x,y,z,s);
+ OctreeElement* node = nodeForOctalCode(_rootNode, octalCode, NULL);
+
+ delete[] octalCode; // cleanup memory
+#ifdef HAS_AUDIT_CHILDREN
+ if (node) {
+ node->auditChildren("Octree::getOctreeElementAt()");
+ }
+#endif // def HAS_AUDIT_CHILDREN
+ return node;
+}
+
OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, float s) {
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index 0b9f1657ee..a11e73ab04 100644
--- a/libraries/octree/src/Octree.h
+++ b/libraries/octree/src/Octree.h
@@ -210,7 +210,15 @@ public:
void reaverageOctreeElements(OctreeElement* startNode = NULL);
void deleteOctreeElementAt(float x, float y, float z, float s);
+
+ /// Find the voxel at position x,y,z,s
+ /// \return pointer to the OctreeElement or NULL if none at x,y,z,s.
OctreeElement* getOctreeElementAt(float x, float y, float z, float s) const;
+
+ /// Find the voxel at position x,y,z,s
+ /// \return pointer to the OctreeElement or to the smallest enclosing parent if none at x,y,z,s.
+ OctreeElement* getOctreeEnclosingElementAt(float x, float y, float z, float s) const;
+
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp
index ad8702adc2..e7d34680a7 100644
--- a/libraries/octree/src/OctreeSceneStats.cpp
+++ b/libraries/octree/src/OctreeSceneStats.cpp
@@ -820,11 +820,7 @@ const char* OctreeSceneStats::getItemValue(Item item) {
void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
bool wasStatsPacket, int nodeClockSkewUsec) {
- _incomingPacket++;
- _incomingBytes += packet.size();
- if (!wasStatsPacket) {
- _incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
- }
+ const bool wantExtraDebugging = false;
int numBytesPacketHeader = numBytesForPacketHeader(packet);
const unsigned char* dataAt = reinterpret_cast(packet.data()) + numBytesPacketHeader;
@@ -842,12 +838,43 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
int flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
+
+ if (wantExtraDebugging) {
+ qDebug() << "sentAt:" << sentAt << " usecs";
+ qDebug() << "arrivedAt:" << arrivedAt << " usecs";
+ qDebug() << "nodeClockSkewUsec:" << nodeClockSkewUsec << " usecs";
+ qDebug() << "flightTime:" << flightTime << " usecs";
+ }
+
+ // Guard against possible corrupted packets... with bad timestamps
+ const int MAX_RESONABLE_FLIGHT_TIME = 200 * USECS_PER_SECOND; // 200 seconds is more than enough time for a packet to arrive
+ const int MIN_RESONABLE_FLIGHT_TIME = 0;
+ if (flightTime > MAX_RESONABLE_FLIGHT_TIME || flightTime < MIN_RESONABLE_FLIGHT_TIME) {
+ qDebug() << "ignoring unreasonable packet... flightTime:" << flightTime;
+ return; // ignore any packets that are unreasonable
+ }
+
+ // Guard against possible corrupted packets... with bad sequence numbers
+ const int MAX_RESONABLE_SEQUENCE_OFFSET = 2000;
+ const int MIN_RESONABLE_SEQUENCE_OFFSET = -2000;
+ int sequenceOffset = (sequence - _incomingLastSequence);
+ if (sequenceOffset > MAX_RESONABLE_SEQUENCE_OFFSET || sequenceOffset < MIN_RESONABLE_SEQUENCE_OFFSET) {
+ qDebug() << "ignoring unreasonable packet... sequence:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
+ return; // ignore any packets that are unreasonable
+ }
+
+ // track packets here...
+ _incomingPacket++;
+ _incomingBytes += packet.size();
+ if (!wasStatsPacket) {
+ _incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
+ }
+
const int USECS_PER_MSEC = 1000;
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
// track out of order and possibly lost packets...
- const bool wantExtraDebugging = false;
if (sequence == _incomingLastSequence) {
if (wantExtraDebugging) {
qDebug() << "last packet duplicate got:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 09c3c63ebd..ed05658538 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
#include
@@ -230,8 +231,13 @@ void ScriptEngine::init() {
registerGlobalObject("Voxels", &_voxelsScriptingInterface);
- QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
- _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
+ // constants
+ QScriptValue globalObject = _engine.globalObject();
+ globalObject.setProperty("TREE_SCALE", _engine.newVariant(QVariant(TREE_SCALE)));
+ globalObject.setProperty("COLLISION_GROUP_ENVIRONMENT", _engine.newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT)));
+ globalObject.setProperty("COLLISION_GROUP_AVATARS", _engine.newVariant(QVariant(COLLISION_GROUP_AVATARS)));
+ globalObject.setProperty("COLLISION_GROUP_VOXELS", _engine.newVariant(QVariant(COLLISION_GROUP_VOXELS)));
+ globalObject.setProperty("COLLISION_GROUP_PARTICLES", _engine.newVariant(QVariant(COLLISION_GROUP_PARTICLES)));
// let the VoxelPacketSender know how frequently we plan to call it
_voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS);
diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h
index 7db965fe64..510728daa6 100644
--- a/libraries/shared/src/CollisionInfo.h
+++ b/libraries/shared/src/CollisionInfo.h
@@ -27,6 +27,7 @@ const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
const quint32 COLLISION_GROUP_AVATARS = 1U << 1;
const quint32 COLLISION_GROUP_VOXELS = 1U << 2;
const quint32 COLLISION_GROUP_PARTICLES = 1U << 3;
+const quint32 VALID_COLLISION_GROUPS = 0x0f;
// CollisionInfo contains details about the collision between two things: BodyA and BodyB.
// The assumption is that the context that analyzes the collision knows about BodyA but
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index bbb73cae4c..c65b7505ee 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -30,27 +30,22 @@
#include "OctalCode.h"
#include "SharedUtil.h"
-
-static qint64 TIME_REFERENCE = 0; // in usec
-static QElapsedTimer timestampTimer;
static int usecTimestampNowAdjust = 0; // in usec
-
-void initialiseUsecTimestampNow() {
- static bool initialised = false;
- if (initialised) {
- qDebug() << "[WARNING] Double initialisation of usecTimestampNow().";
- return;
- }
-
- TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
- initialised = true;
-}
-
void usecTimestampNowForceClockSkew(int clockSkew) {
::usecTimestampNowAdjust = clockSkew;
}
quint64 usecTimestampNow() {
+ static bool usecTimestampNowIsInitialized = false;
+ static qint64 TIME_REFERENCE = 0; // in usec
+ static QElapsedTimer timestampTimer;
+
+ if (!usecTimestampNowIsInitialized) {
+ TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
+ timestampTimer.start();
+ usecTimestampNowIsInitialized = true;
+ }
+
// usec nsec to usec usec
return TIME_REFERENCE + timestampTimer.nsecsElapsed() / 1000 + ::usecTimestampNowAdjust;
}
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 54d599070d..4a3fe2a129 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -60,7 +60,6 @@ static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
const int BITS_IN_BYTE = 8;
-void initialiseUsecTimestampNow();
quint64 usecTimestampNow();
void usecTimestampNowForceClockSkew(int clockSkew);
diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp
index bb9de72e9d..b1ddf2e5b0 100644
--- a/libraries/voxels/src/VoxelTree.cpp
+++ b/libraries/voxels/src/VoxelTree.cpp
@@ -42,7 +42,11 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s) {
}
VoxelTreeElement* VoxelTree::getVoxelAt(float x, float y, float z, float s) const {
- return (VoxelTreeElement*)getOctreeElementAt(x, y, z, s);
+ return static_cast(getOctreeElementAt(x, y, z, s));
+}
+
+VoxelTreeElement* VoxelTree::getEnclosingVoxelAt(float x, float y, float z, float s) const {
+ return static_cast(getOctreeEnclosingElementAt(x, y, z, s));
}
void VoxelTree::createVoxel(float x, float y, float z, float s,
diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h
index e0bc1d9a47..eb24c182b2 100644
--- a/libraries/voxels/src/VoxelTree.h
+++ b/libraries/voxels/src/VoxelTree.h
@@ -29,7 +29,15 @@ public:
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
void deleteVoxelAt(float x, float y, float z, float s);
+
+ /// Find the voxel at position x,y,z,s
+ /// \return pointer to the VoxelTreeElement or NULL if none at x,y,z,s.
VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const;
+
+ /// Find the voxel at position x,y,z,s
+ /// \return pointer to the VoxelTreeElement or to the smallest enclosing parent if none at x,y,z,s.
+ VoxelTreeElement* getEnclosingVoxelAt(float x, float y, float z, float s) const;
+
void createVoxel(float x, float y, float z, float s,
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
diff --git a/libraries/voxels/src/VoxelTreeCommands.cpp b/libraries/voxels/src/VoxelTreeCommands.cpp
index f0f092fd04..39e08d3bc2 100644
--- a/libraries/voxels/src/VoxelTreeCommands.cpp
+++ b/libraries/voxels/src/VoxelTreeCommands.cpp
@@ -13,6 +13,50 @@
#include "VoxelTreeCommands.h"
+
+
+struct SendVoxelsOperationArgs {
+ const unsigned char* newBaseOctCode;
+ VoxelEditPacketSender* packetSender;
+};
+
+bool sendVoxelsOperation(OctreeElement* element, void* extraData) {
+ VoxelTreeElement* voxel = static_cast(element);
+ SendVoxelsOperationArgs* args = static_cast(extraData);
+ if (voxel->isColored()) {
+ const unsigned char* nodeOctalCode = voxel->getOctalCode();
+ unsigned char* codeColorBuffer = NULL;
+ int codeLength = 0;
+ int bytesInCode = 0;
+ int codeAndColorLength;
+
+ // If the newBase is NULL, then don't rebase
+ if (args->newBaseOctCode) {
+ codeColorBuffer = rebaseOctalCode(nodeOctalCode, args->newBaseOctCode, true);
+ codeLength = numberOfThreeBitSectionsInCode(codeColorBuffer);
+ bytesInCode = bytesRequiredForCodeLength(codeLength);
+ codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
+ } else {
+ codeLength = numberOfThreeBitSectionsInCode(nodeOctalCode);
+ bytesInCode = bytesRequiredForCodeLength(codeLength);
+ codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
+ codeColorBuffer = new unsigned char[codeAndColorLength];
+ memcpy(codeColorBuffer, nodeOctalCode, bytesInCode);
+ }
+
+ // copy the colors over
+ codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX];
+ codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX];
+ codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX];
+ args->packetSender->queueVoxelEditMessage(PacketTypeVoxelSetDestructive,
+ codeColorBuffer, codeAndColorLength);
+
+ delete[] codeColorBuffer;
+ }
+ return true; // keep going
+}
+
+
AddVoxelCommand::AddVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender, QUndoCommand* parent) :
QUndoCommand("Add Voxel", parent),
_tree(tree),
@@ -43,11 +87,40 @@ DeleteVoxelCommand::DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, Voxe
QUndoCommand("Delete Voxel", parent),
_tree(tree),
_packetSender(packetSender),
- _voxel(voxel)
+ _voxel(voxel),
+ _oldTree(NULL)
{
+ _tree->lockForRead();
+ VoxelTreeElement* element = _tree->getEnclosingVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
+ if (element->getScale() == _voxel.s) {
+ if (!element->hasContent() && !element->isLeaf()) {
+ _oldTree = new VoxelTree();
+ _tree->copySubTreeIntoNewTree(element, _oldTree, false);
+ } else {
+ _voxel.red = element->getColor()[0];
+ _voxel.green = element->getColor()[1];
+ _voxel.blue = element->getColor()[2];
+ }
+ } else if (element->hasContent() && element->isLeaf()) {
+ _voxel.red = element->getColor()[0];
+ _voxel.green = element->getColor()[1];
+ _voxel.blue = element->getColor()[2];
+ } else {
+ _voxel.s = 0.0f;
+ qDebug() << "No element for delete.";
+ }
+ _tree->unlock();
+}
+
+DeleteVoxelCommand::~DeleteVoxelCommand() {
+ delete _oldTree;
}
void DeleteVoxelCommand::redo() {
+ if (_voxel.s == 0.0f) {
+ return;
+ }
+
if (_tree) {
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
}
@@ -57,10 +130,32 @@ void DeleteVoxelCommand::redo() {
}
void DeleteVoxelCommand::undo() {
- if (_tree) {
- _tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
+ if (_voxel.s == 0.0f) {
+ return;
}
- if (_packetSender) {
- _packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
+
+ if (_oldTree) {
+ VoxelTreeElement* element = _oldTree->getVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
+ if (element) {
+ if (_tree) {
+ _tree->lockForWrite();
+ _oldTree->copySubTreeIntoNewTree(element, _tree, false);
+ _tree->unlock();
+ }
+ if (_packetSender) {
+ SendVoxelsOperationArgs args;
+ args.newBaseOctCode = NULL;
+ args.packetSender = _packetSender;
+ _oldTree->recurseTreeWithOperation(sendVoxelsOperation, &args);
+ _packetSender->releaseQueuedMessages();
+ }
+ }
+ } else {
+ if (_tree) {
+ _tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
+ }
+ if (_packetSender) {
+ _packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
+ }
}
}
diff --git a/libraries/voxels/src/VoxelTreeCommands.h b/libraries/voxels/src/VoxelTreeCommands.h
index 4f2610577e..8df1f0dc14 100644
--- a/libraries/voxels/src/VoxelTreeCommands.h
+++ b/libraries/voxels/src/VoxelTreeCommands.h
@@ -36,6 +36,7 @@ private:
class DeleteVoxelCommand : public QUndoCommand {
public:
DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
+ ~DeleteVoxelCommand();
virtual void redo();
virtual void undo();
@@ -44,6 +45,7 @@ private:
VoxelTree* _tree;
VoxelEditPacketSender* _packetSender;
VoxelDetail _voxel;
+ VoxelTree* _oldTree;
};
#endif // hifi_VoxelTreeCommands_h
diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp
index 08dcfd3f47..15503db454 100644
--- a/libraries/voxels/src/VoxelsScriptingInterface.cpp
+++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp
@@ -76,32 +76,16 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
if (_tree) {
if (_undoStack) {
AddVoxelCommand* addCommand = new AddVoxelCommand(_tree,
- addVoxelDetail,
- getVoxelPacketSender());
-
- VoxelTreeElement* deleteVoxelElement = _tree->getVoxelAt(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s);
- if (deleteVoxelElement) {
- nodeColor color;
- memcpy(&color, &deleteVoxelElement->getColor(), sizeof(nodeColor));
- VoxelDetail deleteVoxelDetail = {addVoxelDetail.x,
- addVoxelDetail.y,
- addVoxelDetail.z,
- addVoxelDetail.s,
- color[0],
- color[1],
- color[2]};
- DeleteVoxelCommand* delCommand = new DeleteVoxelCommand(_tree,
- deleteVoxelDetail,
- getVoxelPacketSender());
- _undoStack->beginMacro(addCommand->text());
- // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
- _undoStack->push(delCommand);
- _undoStack->push(addCommand);
- _undoStack->endMacro();
- } else {
- // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
- _undoStack->push(addCommand);
- }
+ addVoxelDetail,
+ getVoxelPacketSender());
+ DeleteVoxelCommand* deleteCommand = new DeleteVoxelCommand(_tree,
+ addVoxelDetail,
+ getVoxelPacketSender());
+ _undoStack->beginMacro(addCommand->text());
+ // As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
+ _undoStack->push(deleteCommand);
+ _undoStack->push(addCommand);
+ _undoStack->endMacro();
} else {
// queue the destructive add
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);