mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 08:04:01 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19597
This commit is contained in:
commit
91d7bd252d
41 changed files with 911 additions and 737 deletions
|
@ -17,8 +17,6 @@
|
|||
#include "AssignmentClientMonitor.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
initialiseUsecTimestampNow();
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include "DomainServer.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
initialiseUsecTimestampNow();
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
142
examples/swissArmyJetpack.js
Normal file
142
examples/swissArmyJetpack.js
Normal file
|
@ -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);
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="44px"
|
||||
height="44px"
|
||||
viewBox="0 0 44 44"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="close.svg">
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Slice 1</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="480"
|
||||
id="namedview14"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.7926636"
|
||||
inkscape:cx="57.156875"
|
||||
inkscape:cy="33.978935"
|
||||
inkscape:window-x="536"
|
||||
inkscape:window-y="258"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<title
|
||||
id="title4">Slice 1</title>
|
||||
<description
|
||||
id="description6">Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<g
|
||||
id="Page-1"
|
||||
sketch:type="MSPage"
|
||||
transform="translate(0.52733383,0.52733392)"
|
||||
style="fill:none;stroke:none" />
|
||||
<rect
|
||||
id="rect2993"
|
||||
width="5.7796612"
|
||||
height="26.101694"
|
||||
x="19.730724"
|
||||
y="16.374792"
|
||||
ry="3.371469" />
|
||||
<rect
|
||||
id="rect2995"
|
||||
width="35.423729"
|
||||
height="2.9830508"
|
||||
x="4.6290293"
|
||||
y="2.3917408"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
id="rect2999"
|
||||
width="31.135593"
|
||||
height="21.067797"
|
||||
x="6.6798768"
|
||||
y="4.0697069"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
style="fill:#ff0000"
|
||||
id="rect3003"
|
||||
width="27.779657"
|
||||
height="17.711861"
|
||||
x="8.171401"
|
||||
y="5.3747911"
|
||||
ry="0.46610171" />
|
||||
<rect
|
||||
style="fill:#999999"
|
||||
id="rect3011"
|
||||
width="3.1694915"
|
||||
height="13.983051"
|
||||
x="21.035809"
|
||||
y="25.883266"
|
||||
ry="0.46610171" />
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
|
@ -1,106 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="44px"
|
||||
height="44px"
|
||||
viewBox="0 0 44 44"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="pin.svg">
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Slice 1</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1058"
|
||||
id="namedview14"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.7926636"
|
||||
inkscape:cx="39.754857"
|
||||
inkscape:cy="33.978935"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
<title
|
||||
id="title4">Slice 1</title>
|
||||
<description
|
||||
id="description6">Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<g
|
||||
id="Page-1"
|
||||
sketch:type="MSPage"
|
||||
transform="translate(0.52733383,0.52733392)"
|
||||
style="fill:none;stroke:none" />
|
||||
<rect
|
||||
style="fill:#b3b3b3"
|
||||
id="rect3013"
|
||||
width="43.768692"
|
||||
height="44.296078"
|
||||
x="0.26366693"
|
||||
y="-0.032378189"
|
||||
ry="0.46610171" />
|
||||
<rect
|
||||
id="rect2993"
|
||||
width="5.7796612"
|
||||
height="26.101694"
|
||||
x="19.730724"
|
||||
y="16.374792"
|
||||
ry="3.371469" />
|
||||
<rect
|
||||
id="rect2995"
|
||||
width="35.423729"
|
||||
height="2.9830508"
|
||||
x="4.6290293"
|
||||
y="2.3917408"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
id="rect2999"
|
||||
width="31.135593"
|
||||
height="21.067797"
|
||||
x="6.6798768"
|
||||
y="4.0697069"
|
||||
ry="1.4915254" />
|
||||
<rect
|
||||
style="fill:#ff0000"
|
||||
id="rect3003"
|
||||
width="27.779657"
|
||||
height="17.711861"
|
||||
x="8.171401"
|
||||
y="5.3747911"
|
||||
ry="0.46610171" />
|
||||
<rect
|
||||
style="fill:#999999"
|
||||
id="rect3011"
|
||||
width="3.1694915"
|
||||
height="13.983051"
|
||||
x="21.035809"
|
||||
y="25.883266"
|
||||
ry="0.46610171" />
|
||||
</svg>
|
Before Width: | Height: | Size: 2.7 KiB |
|
@ -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<QNetworkReply*>(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);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -129,7 +129,7 @@ private:
|
|||
QString _outputAudioDeviceName;
|
||||
|
||||
StDev _stdev;
|
||||
QElapsedTimer _timeSinceLastRecieved;
|
||||
QElapsedTimer _timeSinceLastReceived;
|
||||
float _averagedLatency;
|
||||
float _measuredJitter;
|
||||
int16_t _jitterBufferSamples;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <QSlider>
|
||||
#include <QUuid>
|
||||
#include <QHBoxLayout>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <XmppClient.h>
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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<const Shape*>& 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
@ -54,16 +55,13 @@ MyAvatar::MyAvatar() :
|
|||
_shouldJump(false),
|
||||
_gravity(0.0f, -1.0f, 0.0f),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::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) {
|
||||
|
|
|
@ -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<AvatarData> _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
bool _shouldRender;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
#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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <SharedUtil.h>
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
initialiseUsecTimestampNow();
|
||||
QElapsedTimer startupTime;
|
||||
startupTime.start();
|
||||
|
||||
|
|
19
interface/src/ui/ChatInputArea.cpp
Normal file
19
interface/src/ui/ChatInputArea.cpp
Normal file
|
@ -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());
|
||||
};
|
27
interface/src/ui/ChatInputArea.h
Normal file
27
interface/src/ui/ChatInputArea.h
Normal file
|
@ -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 <QTextBrowser>
|
||||
#include <QMimeData>
|
||||
|
||||
class ChatInputArea : public QTextEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChatInputArea(QWidget* parent);
|
||||
|
||||
protected:
|
||||
void insertFromMimeData(const QMimeData* source);
|
||||
};
|
||||
|
||||
#endif // hifi_ChatInputArea_h
|
|
@ -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 <QAbstractTextDocumentLayout>
|
||||
#include <QWheelEvent>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
#include <QGridLayout>
|
||||
#include <QFrame>
|
||||
#include <QLayoutItem>
|
||||
#include <QMainWindow>
|
||||
#include <QPalette>
|
||||
#include <QScrollBar>
|
||||
#include <QSizePolicy>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#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()) {
|
||||
|
@ -90,36 +96,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();
|
||||
}
|
||||
}
|
||||
|
@ -142,18 +129,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
|
||||
|
@ -176,16 +165,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;
|
||||
|
@ -262,6 +252,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);
|
||||
|
@ -275,15 +266,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);
|
||||
|
@ -294,22 +281,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 = "<b style=\"color: #4a6f91\">" + userLabel + ": </b>";
|
||||
messageArea->setStyleSheet(messageArea->styleSheet() + "background-color: #e1e8ea");
|
||||
} else {
|
||||
userLabel = "<b>" + userLabel + ": </b>";
|
||||
}
|
||||
|
||||
messageArea->setHtml(message.body().replace(regexLinks, "<a href=\"\\1\">\\1</a>"));
|
||||
messageArea->document()->setDefaultStyleSheet("a { text-decoration: none; font-weight: bold; color: #267077;}");
|
||||
QString messageText = message.body().toHtmlEscaped();
|
||||
messageText = messageText.replace(regexLinks, "<a href=\"\\1\">\\1</a>");
|
||||
messageText = messageText.replace(regexHifiLinks, "<a href=\"hifi://\\1\">\\1</a>");
|
||||
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) {
|
||||
|
@ -326,26 +321,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());
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QTimer>
|
||||
|
||||
#include <Application.h>
|
||||
#include "FramelessDialog.h"
|
||||
|
||||
#ifdef HAVE_QXMPP
|
||||
|
||||
|
@ -30,37 +31,38 @@ class QXmppResultSetReply;
|
|||
#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;
|
||||
|
@ -69,7 +71,6 @@ private:
|
|||
private slots:
|
||||
void connected();
|
||||
void timeout();
|
||||
void togglePinned();
|
||||
#ifdef HAVE_QXMPP
|
||||
void error(QXmppClient::Error error);
|
||||
void participantsChanged();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ChatWindow</class>
|
||||
<widget class="QDockWidget" name="ChatWindow">
|
||||
<widget class="QDialog" name="ChatWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>608</height>
|
||||
<height>440</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
|
@ -16,127 +16,95 @@
|
|||
<height>238</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<property name="features">
|
||||
<set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
|
||||
</property>
|
||||
<property name="allowedAreas">
|
||||
<set>Qt::NoDockWidgetArea</set>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Chat</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="connectingToXMPPLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connecting to XMPP...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="numOnlineLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-weight: bold; color: palette(shadow); margin-bottom: 4px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> online now:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="togglePinnedButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>../resources/images/pin.svg</normaloff>
|
||||
<normalon>../resources/images/pinned.svg</normalon>../resources/images/pin.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Helvetica, Arial, sans-serif;</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="connectingToXMPPLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Connecting to XMPP...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="numOnlineLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-weight: bold; color: palette(shadow); margin-bottom: 4px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> online now:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">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)
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="usersWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="messagesScrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 12px;</string>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="usersWidget" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#usersWidget {
|
||||
margin-right: 20px;
|
||||
}</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="messagesScrollArea">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 12px;</string>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 0px;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="messagesVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="chatFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-top: 0px;</string>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>78</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="messagesGridLayout">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#chatFrame {
|
||||
border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -204,69 +226,65 @@ QPushButton:pressed {
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ChatInputArea" name="messagePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="messagePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ChatInputArea</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>ui/ChatInputArea.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>messagePlainTextEdit</tabstop>
|
||||
<tabstop>messagesScrollArea</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>togglePinnedButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ChatWindow</receiver>
|
||||
<slot>togglePinned()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>390</x>
|
||||
<y>42</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>550</x>
|
||||
<y>42</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>closeButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<const unsigned char*>(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;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<VoxelTreeElement*>(getOctreeElementAt(x, y, z, s));
|
||||
}
|
||||
|
||||
VoxelTreeElement* VoxelTree::getEnclosingVoxelAt(float x, float y, float z, float s) const {
|
||||
return static_cast<VoxelTreeElement*>(getOctreeEnclosingElementAt(x, y, z, s));
|
||||
}
|
||||
|
||||
void VoxelTree::createVoxel(float x, float y, float z, float s,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<VoxelTreeElement*>(element);
|
||||
SendVoxelsOperationArgs* args = static_cast<SendVoxelsOperationArgs*>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue