mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 16:13:28 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into js_clipboard
This commit is contained in:
commit
3a8a880a8b
18 changed files with 560 additions and 264 deletions
18
interface/resources/icons/backButton.svg
Normal file
18
interface/resources/icons/backButton.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 2000" enable-background="new 0 0 1000 2000" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M11,15.5c0-0.4,0.1-0.7,0.4-0.9l6.8-6.8c0.3-0.3,0.6-0.4,1-0.4c0.4,0,0.7,0.1,0.9,0.4L21,8.5
|
||||
c0.3,0.2,0.4,0.6,0.4,0.9s-0.1,0.7-0.4,0.9l-3.1,3.1h10c0.8,0,1.2,0.6,1.2,1.3v1.3c0,0.7-0.5,1.3-1.2,1.3h-10l3.1,3.1
|
||||
c0.3,0.3,0.4,0.6,0.4,1c0,0.4-0.1,0.7-0.4,1l-0.8,0.8c-0.3,0.2-0.6,0.4-0.9,0.4c-0.4,0-0.7-0.1-1-0.4l-6.8-6.8
|
||||
C11.2,16.2,11,15.9,11,15.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M41,27.5c0,2.5-2,4.5-4.5,4.5h-32C2,32,0,30,0,27.5v-23C0,2,2,0,4.5,0h32C39,0,41,2,41,4.5V27.5z M40,4.5
|
||||
C40,2.6,38.4,1,36.5,1h-32C2.6,1,1,2.6,1,4.5v23C1,29.4,2.6,31,4.5,31h32c1.9,0,3.5-1.6,3.5-3.5V4.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
19
interface/resources/icons/forwardButton.svg
Normal file
19
interface/resources/icons/forwardButton.svg
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0.6 -2 48.7 34.1" enable-background="new 0.6 -2 48.7 34.1" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#666666" d="M30.7,14.8l-6.8,6.8c-0.3,0.2-0.6,0.4-1,0.4c-0.4,0-0.7-0.1-0.9-0.4l-0.8-0.8c-0.3-0.3-0.4-0.6-0.4-1
|
||||
c0-0.4,0.1-0.7,0.4-1l3.1-3.1h-10c-0.8,0-1.2-0.6-1.2-1.3v-1.3c0-0.7,0.5-1.3,1.2-1.3h10l-3.1-3.1c-0.3-0.2-0.4-0.6-0.4-0.9
|
||||
s0.1-0.7,0.4-0.9l0.8-0.8c0.3-0.3,0.6-0.4,0.9-0.4c0.4,0,0.7,0.1,1,0.4l6.8,6.8c0.3,0.2,0.4,0.6,0.4,0.9S30.9,14.6,30.7,14.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M42,25.5c0,2.5-2,4.5-4.5,4.5h-32C3,30,1,28,1,25.5v-23C1,0,3-2,5.5-2h32C40-2,42,0,42,2.5V25.5z M41,2.5
|
||||
C41,0.6,39.4-1,37.5-1h-32C3.6-1,2,0.6,2,2.5v23C2,27.4,3.6,29,5.5,29h32c1.9,0,3.5-1.6,3.5-3.5V2.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
18
interface/resources/icons/toParentButton.svg
Normal file
18
interface/resources/icons/toParentButton.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 42.5 33.5" enable-background="new 0 0 42.5 33.5" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M36.5,32h-32C2,32,0,30,0,27.5v-23C0,2,2,0,4.5,0h32C39,0,41,2,41,4.5v23C41,30,39,32,36.5,32z M4.5,1
|
||||
C2.6,1,1,2.6,1,4.5v23C1,29.4,2.6,31,4.5,31h32c1.9,0,3.5-1.6,3.5-3.5v-23C40,2.6,38.4,1,36.5,1H4.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path fill="#8C8C8C" d="M29.8,19.6c-0.3-0.6-0.9-0.6-1.5-0.6H27v-1.9c0-1.1-1.3-2.1-2.4-2.1H23v1h1.6c0.5,0,0.4,0.7,0.4,1.1V19h-7.7
|
||||
c-0.8,0-1.6,0.2-2.1,0.9L13,22.5v-7.6c0-0.5,0.1-0.9,0.6-0.9h2.9c0.5,0,0.6,0.4,0.6,0.9v0.6c0,0.4,0.6,0.8,1,0.8V14h0.2
|
||||
c-0.3-0.7-1-1-1.8-1h-2.9c-1.1,0-1.6,0.8-1.6,1.9v8.6c0,1.1,0.5,1.6,1.6,1.6h11c0.8,0,1.8-0.3,2.3-0.9l2.6-3.1
|
||||
c0.2-0.3,0.4-0.5,0.4-0.9C29.9,19.9,29.9,19.8,29.8,19.6z M28.6,20.4L26,23.5C25.7,23.8,25,24,24.6,24h-11c-0.2,0-0.5,0.1-0.5-0.2
|
||||
c0-0.1,0.1-0.2,0.2-0.3l2.6-3.1c0.3-0.4,0.9-0.5,1.4-0.5h11c0.2,0,0.5-0.1,0.5,0.2C28.8,20.3,28.7,20.3,28.6,20.4z"/>
|
||||
<path fill="#666666" d="M22.6,11.1h3l-4.5-4.5l-4.5,4.5h3v7.5h3V11.1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -20,12 +20,23 @@ QLabel#infoLabel {
|
|||
color: #666666;
|
||||
}
|
||||
|
||||
QProgressBar {
|
||||
border: 0px;
|
||||
border-radius: 0px;
|
||||
background-color: #BFE4E4;
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
QProgressBar::chunk {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 18px;
|
||||
padding: 17px 0px 15px;
|
||||
width: 107px;
|
||||
width: 120px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
@ -51,6 +62,21 @@ QPushButton#cancelButton {
|
|||
margin-right: 11px;
|
||||
}
|
||||
|
||||
#backButton {
|
||||
background-image: url(resources/icons/backButton.svg);
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#forwardButton {
|
||||
background-image: url(resources/icons/forwardButton.svg);
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#toParentButton {
|
||||
background-image: url(resources/icons/toParentButton.svg);
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
QSidebar, QTreeView {
|
||||
border: 1px solid #C5C5C5;
|
||||
font-size: 14px;
|
||||
|
|
|
@ -310,8 +310,13 @@ Application::~Application() {
|
|||
delete idleTimer;
|
||||
|
||||
Menu::getInstance()->saveSettings();
|
||||
|
||||
_rearMirrorTools->saveSettings(_settings);
|
||||
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
if (_voxelImporter) {
|
||||
_voxelImporter->saveSettings(_settings);
|
||||
delete _voxelImporter;
|
||||
}
|
||||
_settings->sync();
|
||||
|
||||
// let the avatar mixer know we're out
|
||||
|
@ -332,7 +337,6 @@ Application::~Application() {
|
|||
|
||||
storeSizeAndPosition();
|
||||
saveScripts();
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
|
||||
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
|
||||
Menu::getInstance()->deleteLater();
|
||||
|
@ -650,6 +654,9 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
|
|||
}
|
||||
glFrustum(left, right, bottom, top, nearVal, farVal);
|
||||
|
||||
// save matrix
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat*)&_projectionMatrix);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
|
@ -1297,8 +1304,14 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
pasteVoxels();
|
||||
}
|
||||
|
||||
} else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
if (Menu::getInstance()->isVoxelModeActionChecked()) {
|
||||
deleteVoxelUnderCursor();
|
||||
}
|
||||
if (_pasteMode) {
|
||||
_pasteMode = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1712,13 +1725,19 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) {
|
|||
void Application::importVoxels() {
|
||||
if (!_voxelImporter) {
|
||||
_voxelImporter = new VoxelImporter(_window);
|
||||
_voxelImporter->init(_settings);
|
||||
_voxelImporter->loadSettings(_settings);
|
||||
}
|
||||
|
||||
if (_voxelImporter->exec()) {
|
||||
qDebug("[DEBUG] Import succeeded.");
|
||||
if (!_voxelImporter->exec()) {
|
||||
qDebug() << "[DEBUG] Import succeeded." << endl;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::VoxelSelectMode, true);
|
||||
_pasteMode = true;
|
||||
} else {
|
||||
qDebug("[DEBUG] Import failed.");
|
||||
qDebug() << "[DEBUG] Import failed." << endl;
|
||||
if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) {
|
||||
_sharedVoxelSystem.killLocalVoxels();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
}
|
||||
}
|
||||
|
||||
// restore the main window's active state
|
||||
|
@ -1787,10 +1806,11 @@ void Application::pasteVoxels(const VoxelDetail& sourceVoxel) {
|
|||
}
|
||||
|
||||
pasteVoxelsToOctalCode(octalCodeDestination);
|
||||
|
||||
|
||||
if (calculatedOctCode) {
|
||||
delete[] calculatedOctCode;
|
||||
}
|
||||
_pasteMode = false;
|
||||
}
|
||||
|
||||
void Application::findAxisAlignment() {
|
||||
|
@ -1862,6 +1882,7 @@ void Application::init() {
|
|||
|
||||
VoxelTreeElement::removeUpdateHook(&_sharedVoxelSystem);
|
||||
|
||||
// Cleanup of the original shared tree
|
||||
_sharedVoxelSystem.init();
|
||||
VoxelTree* tmpTree = _sharedVoxelSystem.getTree();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
|
@ -1953,12 +1974,11 @@ void Application::init() {
|
|||
_audio.init(_glWidget);
|
||||
|
||||
_rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings);
|
||||
|
||||
connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView()));
|
||||
connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors()));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Application::closeMirrorView() {
|
||||
|
@ -2961,6 +2981,15 @@ void Application::loadTranslatedViewMatrix(const glm::vec3& translation) {
|
|||
translation.z + _viewMatrixTranslation.z);
|
||||
}
|
||||
|
||||
void Application::getModelViewMatrix(glm::dmat4* modelViewMatrix) {
|
||||
(*modelViewMatrix) =_untranslatedViewMatrix;
|
||||
(*modelViewMatrix)[3] = _untranslatedViewMatrix * glm::vec4(_viewMatrixTranslation, 1);
|
||||
}
|
||||
|
||||
void Application::getProjectionMatrix(glm::dmat4* projectionMatrix) {
|
||||
*projectionMatrix = _projectionMatrix;
|
||||
}
|
||||
|
||||
void Application::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
|
||||
|
||||
|
|
|
@ -193,6 +193,9 @@ public:
|
|||
|
||||
const glm::mat4& getShadowMatrix() const { return _shadowMatrix; }
|
||||
|
||||
void getModelViewMatrix(glm::dmat4* modelViewMatrix);
|
||||
void getProjectionMatrix(glm::dmat4* projectionMatrix);
|
||||
|
||||
/// Computes the off-axis frustum parameters for the view frustum, taking mirroring into account.
|
||||
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
|
||||
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
|
||||
|
@ -405,6 +408,7 @@ private:
|
|||
|
||||
glm::mat4 _untranslatedViewMatrix;
|
||||
glm::vec3 _viewMatrixTranslation;
|
||||
glm::mat4 _projectionMatrix;
|
||||
|
||||
glm::mat4 _shadowMatrix;
|
||||
|
||||
|
|
|
@ -16,14 +16,12 @@
|
|||
|
||||
const QString WINDOW_NAME = QObject::tr("Import Voxels");
|
||||
const QString IMPORT_BUTTON_NAME = QObject::tr("Import");
|
||||
const QString LOADING_BUTTON_NAME = QObject::tr("Loading ...");
|
||||
const QString PLACE_BUTTON_NAME = QObject::tr("Place voxels");
|
||||
const QString IMPORT_INFO = QObject::tr("<b>Import</b> %1 as voxels");
|
||||
const QString CANCEL_BUTTON_NAME = QObject::tr("Cancel");
|
||||
const QString INFO_LABEL_TEXT = QObject::tr("<div style='line-height:20px;'>"
|
||||
"This will load the selected file into Hifi and allow you<br/>"
|
||||
"to place it with %1-V; you must be in select or<br/>"
|
||||
"add mode (S or V keys will toggle mode) to place.</div>");
|
||||
|
||||
const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
const QString DOWNLOAD_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
const int SHORT_FILE_EXTENSION = 4;
|
||||
const int SECOND_INDEX_LETTER = 1;
|
||||
|
||||
|
@ -66,7 +64,7 @@ QIcon HiFiIconProvider::icon(const QFileInfo &info) const {
|
|||
if (info.isDir()) {
|
||||
if (info.absoluteFilePath() == QDir::homePath()) {
|
||||
return QIcon("resources/icons/home.svg");
|
||||
} else if (info.absoluteFilePath() == DESKTOP_LOCATION) {
|
||||
} else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)) {
|
||||
return QIcon("resources/icons/desktop.svg");
|
||||
} else if (info.absoluteFilePath() == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) {
|
||||
return QIcon("resources/icons/documents.svg");
|
||||
|
@ -95,108 +93,114 @@ QString HiFiIconProvider::type(const QFileInfo &info) const {
|
|||
}
|
||||
|
||||
ImportDialog::ImportDialog(QWidget* parent) :
|
||||
QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, NULL),
|
||||
QFileDialog(parent, WINDOW_NAME, DOWNLOAD_LOCATION, NULL),
|
||||
_progressBar(this),
|
||||
_importButton(IMPORT_BUTTON_NAME, this),
|
||||
_cancelButton(CANCEL_BUTTON_NAME, this),
|
||||
fileAccepted(false) {
|
||||
_mode(importMode) {
|
||||
|
||||
setOption(QFileDialog::DontUseNativeDialog, true);
|
||||
setFileMode(QFileDialog::ExistingFile);
|
||||
setViewMode(QFileDialog::Detail);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString cmdString = ("Command");
|
||||
#else
|
||||
QString cmdString = ("Control");
|
||||
#endif
|
||||
QLabel* infoLabel = new QLabel(QString(INFO_LABEL_TEXT).arg(cmdString));
|
||||
infoLabel->setObjectName("infoLabel");
|
||||
|
||||
QGridLayout* gridLayout = (QGridLayout*) layout();
|
||||
gridLayout->addWidget(infoLabel, 2, 0, 2, 1);
|
||||
gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
|
||||
gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
|
||||
|
||||
setImportTypes();
|
||||
setLayout();
|
||||
|
||||
connect(&_importButton, SIGNAL(pressed()), SLOT(import()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
|
||||
connect(&_cancelButton, SIGNAL(pressed()), SLOT(close()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
}
|
||||
|
||||
ImportDialog::~ImportDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void ImportDialog::import() {
|
||||
fileAccepted = true;
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void ImportDialog::accept() {
|
||||
// do nothing if import is not enable
|
||||
if (!_importButton.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
_progressBar.setRange(0, 100);
|
||||
|
||||
if (!fileAccepted) {
|
||||
fileAccepted = true;
|
||||
emit accepted();
|
||||
} else {
|
||||
QFileDialog::accept();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::reject() {
|
||||
QFileDialog::reject();
|
||||
}
|
||||
|
||||
int ImportDialog::exec() {
|
||||
// deselect selected file
|
||||
selectFile(" ");
|
||||
return QFileDialog::exec();
|
||||
connect(&_importButton, SIGNAL(pressed()), SLOT(accept()));
|
||||
connect(&_cancelButton, SIGNAL(pressed()), SIGNAL(canceled()));
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
}
|
||||
|
||||
void ImportDialog::reset() {
|
||||
_importButton.setEnabled(false);
|
||||
setMode(importMode);
|
||||
_progressBar.setValue(0);
|
||||
}
|
||||
|
||||
void ImportDialog::saveCurrentFile(QString filename) {
|
||||
if (!filename.isEmpty() && QFileInfo(filename).isFile()) {
|
||||
_currentFile = filename;
|
||||
_importButton.setEnabled(true);
|
||||
} else {
|
||||
_currentFile.clear();
|
||||
_importButton.setEnabled(false);
|
||||
void ImportDialog::setMode(dialogMode mode) {
|
||||
_mode = mode;
|
||||
|
||||
switch (_mode) {
|
||||
case loadingMode:
|
||||
_importButton.setEnabled(false);
|
||||
_importButton.setText(LOADING_BUTTON_NAME);
|
||||
findChild<QWidget*>("sidebar")->setEnabled(false);
|
||||
findChild<QWidget*>("treeView")->setEnabled(false);
|
||||
findChild<QWidget*>("backButton")->setEnabled(false);
|
||||
findChild<QWidget*>("forwardButton")->setEnabled(false);
|
||||
findChild<QWidget*>("toParentButton")->setEnabled(false);
|
||||
break;
|
||||
case placeMode:
|
||||
_progressBar.setValue(100);
|
||||
_importButton.setEnabled(true);
|
||||
_importButton.setText(PLACE_BUTTON_NAME);
|
||||
findChild<QWidget*>("sidebar")->setEnabled(false);
|
||||
findChild<QWidget*>("treeView")->setEnabled(false);
|
||||
findChild<QWidget*>("backButton")->setEnabled(false);
|
||||
findChild<QWidget*>("forwardButton")->setEnabled(false);
|
||||
findChild<QWidget*>("toParentButton")->setEnabled(false);
|
||||
break;
|
||||
case importMode:
|
||||
default:
|
||||
_progressBar.setValue(0);
|
||||
_importButton.setEnabled(true);
|
||||
_importButton.setText(IMPORT_BUTTON_NAME);
|
||||
findChild<QWidget*>("sidebar")->setEnabled(true);
|
||||
findChild<QWidget*>("treeView")->setEnabled(true);
|
||||
findChild<QWidget*>("backButton")->setEnabled(true);
|
||||
findChild<QWidget*>("forwardButton")->setEnabled(true);
|
||||
findChild<QWidget*>("toParentButton")->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDialog::setLayout() {
|
||||
void ImportDialog::setProgressBarValue(int value) {
|
||||
_progressBar.setValue(value);
|
||||
}
|
||||
|
||||
void ImportDialog::accept() {
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void ImportDialog::saveCurrentFile(QString filename) {
|
||||
_currentFile = QFileInfo(filename).isFile() ? filename : "";
|
||||
}
|
||||
|
||||
void ImportDialog::setLayout() {
|
||||
QGridLayout* gridLayout = (QGridLayout*) layout();
|
||||
gridLayout->addWidget(&_progressBar, 2, 0, 2, 1);
|
||||
gridLayout->addWidget(&_cancelButton, 2, 1, 2, 1);
|
||||
gridLayout->addWidget(&_importButton, 2, 2, 2, 1);
|
||||
|
||||
// set ObjectName used in qss for styling
|
||||
_progressBar.setObjectName("progressBar");
|
||||
_importButton.setObjectName("importButton");
|
||||
_cancelButton.setObjectName("cancelButton");
|
||||
|
||||
// set fixed size
|
||||
_importButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
_cancelButton.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
_cancelButton.setFlat(true);
|
||||
int progressBarHeight = 7;
|
||||
_progressBar.setFixedHeight(progressBarHeight);
|
||||
_progressBar.setTextVisible(false);
|
||||
|
||||
QSize BUTTON_SIZE = QSize(43, 33);
|
||||
QPushButton* button = (QPushButton*) findChild<QWidget*>("backButton");
|
||||
button->setIcon(QIcon());
|
||||
button->setFixedSize(BUTTON_SIZE);
|
||||
button = (QPushButton*) findChild<QWidget*>("forwardButton");
|
||||
button->setIcon(QIcon());
|
||||
button->setFixedSize(BUTTON_SIZE);
|
||||
button = (QPushButton*) findChild<QWidget*>("toParentButton");
|
||||
button->setIcon(QIcon());
|
||||
button->setFixedSize(BUTTON_SIZE);
|
||||
|
||||
// hide unused embedded widgets in QFileDialog
|
||||
QWidget* widget = findChild<QWidget*>("lookInCombo");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("backButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("forwardButton");
|
||||
widget->hide();
|
||||
|
||||
widget = findChild<QWidget*>("toParentButton");
|
||||
widget->hide();
|
||||
|
||||
|
||||
widget = findChild<QWidget*>("newFolderButton");
|
||||
widget->hide();
|
||||
|
||||
|
@ -230,7 +234,7 @@ void ImportDialog::setLayout() {
|
|||
|
||||
widget = findChild<QWidget*>("treeView");
|
||||
widget->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFile styleSheet("resources/styles/import_dialog.qss");
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
|
@ -281,11 +285,6 @@ void ImportDialog::setImportTypes() {
|
|||
setIconProvider(new HiFiIconProvider(iconsMap));
|
||||
setNameFilter(importFormatsFilterList);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString cmdString = ("Command");
|
||||
#else
|
||||
QString cmdString = ("Control");
|
||||
#endif
|
||||
setLabelText(QFileDialog::LookIn, QString(IMPORT_INFO).arg(importFormatsInfo));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <QFileDialog>
|
||||
#include <QPushButton>
|
||||
#include <QProgressBar>
|
||||
#include <QLabel>
|
||||
#include <QFileIconProvider>
|
||||
#include <QHash>
|
||||
|
@ -26,37 +27,42 @@ public:
|
|||
QHash<QString, QString> iconsMap;
|
||||
};
|
||||
|
||||
enum dialogMode {
|
||||
importMode,
|
||||
loadingMode,
|
||||
placeMode
|
||||
};
|
||||
|
||||
class ImportDialog : public QFileDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ImportDialog(QWidget* parent = NULL);
|
||||
~ImportDialog();
|
||||
|
||||
void reset();
|
||||
|
||||
|
||||
QString getCurrentFile() const { return _currentFile; }
|
||||
|
||||
dialogMode getMode() const { return _mode; }
|
||||
void setMode(dialogMode mode);
|
||||
|
||||
signals:
|
||||
void accepted();
|
||||
|
||||
void canceled();
|
||||
|
||||
public slots:
|
||||
int exec();
|
||||
void import();
|
||||
void accept();
|
||||
void reject();
|
||||
void setProgressBarValue(int value);
|
||||
|
||||
private slots:
|
||||
void saveCurrentFile(QString);
|
||||
void accept();
|
||||
void saveCurrentFile(QString filename);
|
||||
|
||||
private:
|
||||
QString _currentFile;
|
||||
QProgressBar _progressBar;
|
||||
QPushButton _importButton;
|
||||
QPushButton _cancelButton;
|
||||
|
||||
dialogMode _mode;
|
||||
|
||||
void setLayout();
|
||||
void setImportTypes();
|
||||
bool fileAccepted;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ImportDialog__) */
|
||||
|
|
|
@ -785,6 +785,11 @@ void Menu::editPreferences() {
|
|||
skeletonURLEdit->setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString());
|
||||
form->addRow("Skeleton URL:", skeletonURLEdit);
|
||||
|
||||
QString displayNameString = applicationInstance->getAvatar()->getDisplayName();
|
||||
QLineEdit* displayNameEdit = new QLineEdit(displayNameString);
|
||||
displayNameEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Display name:", displayNameEdit);
|
||||
|
||||
QSlider* pupilDilation = new QSlider(Qt::Horizontal);
|
||||
pupilDilation->setValue(applicationInstance->getAvatar()->getHead()->getPupilDilation() * pupilDilation->maximum());
|
||||
form->addRow("Pupil Dilation:", pupilDilation);
|
||||
|
@ -857,7 +862,14 @@ void Menu::editPreferences() {
|
|||
applicationInstance->getAvatar()->setSkeletonModelURL(skeletonModelURL);
|
||||
shouldDispatchIdentityPacket = true;
|
||||
}
|
||||
|
||||
QString displayNameStr(displayNameEdit->text());
|
||||
|
||||
if (displayNameStr != displayNameString) {
|
||||
applicationInstance->getAvatar()->setDisplayName(displayNameStr);
|
||||
shouldDispatchIdentityPacket = true;
|
||||
}
|
||||
|
||||
if (shouldDispatchIdentityPacket) {
|
||||
applicationInstance->getAvatar()->sendIdentityPacket();
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include <QFileInfo>
|
||||
#include <QThreadPool>
|
||||
|
||||
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
||||
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
||||
|
||||
class ImportTask : public QObject, public QRunnable {
|
||||
public:
|
||||
ImportTask(const QString &filename);
|
||||
|
@ -21,18 +24,16 @@ private:
|
|||
QString _filename;
|
||||
};
|
||||
|
||||
const QString SETTINGS_GROUP_NAME = "VoxelImport";
|
||||
const QString IMPORT_DIALOG_SETTINGS_KEY = "ImportDialogSettings";
|
||||
|
||||
VoxelImporter::VoxelImporter(QWidget* parent) :
|
||||
QObject(parent),
|
||||
_voxelTree(true),
|
||||
_importDialog(parent),
|
||||
_currentTask(NULL),
|
||||
_nextTask(NULL)
|
||||
_task(NULL),
|
||||
_didImport(false)
|
||||
{
|
||||
connect(&_importDialog, &QFileDialog::currentChanged, this, &VoxelImporter::preImport);
|
||||
connect(&_importDialog, &QFileDialog::accepted, this, &VoxelImporter::import);
|
||||
connect(&_voxelTree, SIGNAL(importProgress(int)), &_importDialog, SLOT(setProgressBarValue(int)));
|
||||
connect(&_importDialog, SIGNAL(canceled()), this, SLOT(cancel()));
|
||||
connect(&_importDialog, SIGNAL(accepted()), this, SLOT(import()));
|
||||
}
|
||||
|
||||
void VoxelImporter::saveSettings(QSettings* settings) {
|
||||
|
@ -41,145 +42,106 @@ void VoxelImporter::saveSettings(QSettings* settings) {
|
|||
settings->endGroup();
|
||||
}
|
||||
|
||||
void VoxelImporter::init(QSettings* settings) {
|
||||
void VoxelImporter::loadSettings(QSettings* settings) {
|
||||
settings->beginGroup(SETTINGS_GROUP_NAME);
|
||||
_importDialog.restoreState(settings->value(IMPORT_DIALOG_SETTINGS_KEY).toByteArray());
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
VoxelImporter::~VoxelImporter() {
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
_nextTask = NULL;
|
||||
}
|
||||
|
||||
if (_currentTask) {
|
||||
disconnect(_currentTask, 0, 0, 0);
|
||||
_voxelTree.cancelImport();
|
||||
_currentTask = NULL;
|
||||
}
|
||||
cleanupTask();
|
||||
}
|
||||
|
||||
void VoxelImporter::reset() {
|
||||
_voxelTree.eraseAllOctreeElements();
|
||||
_importDialog.reset();
|
||||
_filename = "";
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
_nextTask = NULL;
|
||||
}
|
||||
|
||||
if (_currentTask) {
|
||||
_voxelTree.cancelImport();
|
||||
}
|
||||
cleanupTask();
|
||||
}
|
||||
|
||||
int VoxelImporter::exec() {
|
||||
reset();
|
||||
|
||||
int ret = _importDialog.exec();
|
||||
|
||||
if (!ret) {
|
||||
reset();
|
||||
} else {
|
||||
_importDialog.reset();
|
||||
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
|
||||
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->getRoot(),
|
||||
Application::getInstance()->getClipboard(),
|
||||
true);
|
||||
voxelSystem->changeTree(Application::getInstance()->getClipboard());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int VoxelImporter::preImport() {
|
||||
QString filename = _importDialog.getCurrentFile();
|
||||
|
||||
if (!QFileInfo(filename).isFile()) {
|
||||
return 0;
|
||||
}
|
||||
_importDialog.exec();
|
||||
|
||||
_filename = filename;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VoxelImporter::import() {
|
||||
QString filename = _importDialog.getCurrentFile();
|
||||
|
||||
if (!QFileInfo(filename).isFile()) {
|
||||
_importDialog.reject();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_filename == filename) {
|
||||
if (_currentTask) {
|
||||
connect(_currentTask, SIGNAL(destroyed()), &_importDialog, SLOT(accept()));
|
||||
} else {
|
||||
_importDialog.accept();
|
||||
}
|
||||
if (!_didImport) {
|
||||
// if the import is rejected, we make sure to cleanup before leaving
|
||||
cleanupTask();
|
||||
return 1;
|
||||
}
|
||||
|
||||
_filename = filename;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
connect(_nextTask, SIGNAL(destroyed()), &_importDialog, SLOT(accept()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
_didImport = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VoxelImporter::launchTask() {
|
||||
if (_nextTask != NULL) {
|
||||
_currentTask = _nextTask;
|
||||
_nextTask = NULL;
|
||||
void VoxelImporter::import() {
|
||||
switch (_importDialog.getMode()) {
|
||||
case loadingMode:
|
||||
_importDialog.setMode(placeMode);
|
||||
return;
|
||||
case placeMode:
|
||||
// Means the user chose to import
|
||||
_didImport = true;
|
||||
_importDialog.close();
|
||||
return;
|
||||
case importMode:
|
||||
default:
|
||||
QString filename = _importDialog.getCurrentFile();
|
||||
// if it's not a file, we ignore the call
|
||||
if (!QFileInfo(filename).isFile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's prepare the dialog window for import
|
||||
_importDialog.setMode(loadingMode);
|
||||
|
||||
// If not already done, we switch to the local tree
|
||||
if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
|
||||
Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
|
||||
}
|
||||
|
||||
// Creation and launch of the import task on the thread pool
|
||||
_task = new ImportTask(filename);
|
||||
connect(_task, SIGNAL(destroyed()), SLOT(import()));
|
||||
QThreadPool::globalInstance()->start(_task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
|
||||
Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
|
||||
}
|
||||
void VoxelImporter::cancel() {
|
||||
switch (_importDialog.getMode()) {
|
||||
case loadingMode:
|
||||
disconnect(_task, 0, 0, 0);
|
||||
cleanupTask();
|
||||
case placeMode:
|
||||
_importDialog.setMode(importMode);
|
||||
break;
|
||||
case importMode:
|
||||
default:
|
||||
_importDialog.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QThreadPool::globalInstance()->start(_currentTask);
|
||||
} else {
|
||||
_currentTask = NULL;
|
||||
void VoxelImporter::cleanupTask() {
|
||||
// If a task is running, we cancel it and put the pointer to null
|
||||
if (_task) {
|
||||
_task = NULL;
|
||||
_voxelTree.cancelImport();
|
||||
}
|
||||
}
|
||||
|
||||
ImportTask::ImportTask(const QString &filename)
|
||||
: _filename(filename) {
|
||||
: _filename(filename)
|
||||
{
|
||||
setAutoDelete(true);
|
||||
}
|
||||
|
||||
void ImportTask::run() {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
// We start by cleaning up the shared voxel system just in case
|
||||
voxelSystem->killLocalVoxels();
|
||||
|
||||
// Then we call the righ method for the job
|
||||
if (_filename.endsWith(".png", Qt::CaseInsensitive)) {
|
||||
voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
|
||||
} else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) {
|
||||
|
@ -187,8 +149,10 @@ void ImportTask::run() {
|
|||
} else if (_filename.endsWith(".schematic", Qt::CaseInsensitive)) {
|
||||
voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data());
|
||||
} else {
|
||||
qDebug("[ERROR] Invalid file extension.");
|
||||
// We should never get here.
|
||||
qDebug() << "[ERROR] Invalid file extension." << endl;
|
||||
}
|
||||
|
||||
|
||||
// Here we reaverage the tree so that he is ready for preview
|
||||
voxelSystem->getTree()->reaverageOctreeElements();
|
||||
}
|
||||
|
|
|
@ -23,28 +23,25 @@ public:
|
|||
VoxelImporter(QWidget* parent = NULL);
|
||||
~VoxelImporter();
|
||||
|
||||
void init(QSettings* settings);
|
||||
void reset();
|
||||
void loadSettings(QSettings* settings);
|
||||
void saveSettings(QSettings* settings);
|
||||
|
||||
VoxelTree* getVoxelTree() { return &_voxelTree; }
|
||||
|
||||
public slots:
|
||||
int exec();
|
||||
int preImport();
|
||||
int import();
|
||||
|
||||
private slots:
|
||||
void launchTask();
|
||||
void import();
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
VoxelTree _voxelTree;
|
||||
ImportDialog _importDialog;
|
||||
|
||||
QString _filename;
|
||||
|
||||
ImportTask* _currentTask;
|
||||
ImportTask* _nextTask;
|
||||
ImportTask* _task;
|
||||
bool _didImport;
|
||||
|
||||
void cleanupTask();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelImporter__) */
|
||||
|
|
|
@ -56,6 +56,9 @@ const float HEAD_RATE_MAX = 50.f;
|
|||
const int NUM_BODY_CONE_SIDES = 9;
|
||||
const float CHAT_MESSAGE_SCALE = 0.0015f;
|
||||
const float CHAT_MESSAGE_HEIGHT = 0.1f;
|
||||
const float DISPLAYNAME_FADE_TIME = 0.5f;
|
||||
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
|
||||
const float DISPLAYNAME_ALPHA = 0.95f;
|
||||
|
||||
Avatar::Avatar() :
|
||||
AvatarData(),
|
||||
|
@ -107,7 +110,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
if (_scale != _targetScale) {
|
||||
setScale(_targetScale);
|
||||
}
|
||||
|
||||
|
||||
// copy velocity so we can use it later for acceleration
|
||||
glm::vec3 oldVelocity = getVelocity();
|
||||
|
||||
|
@ -135,7 +138,23 @@ void Avatar::simulate(float 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
|
||||
// Fade out => alpha(t) = factor ^ t => alpha(t+dt) = alpha(t) * factor^(dt)
|
||||
// Fade in => alpha(t) = 1 - factor^t => alpha(t+dt) = 1-(1-alpha(t))*coef^(dt)
|
||||
// factor^(dt) = coef
|
||||
float coef = pow(DISPLAYNAME_FADE_FACTOR, deltaTime);
|
||||
if (_displayNameTargetAlpha < _displayNameAlpha) {
|
||||
// Fading out
|
||||
_displayNameAlpha *= coef;
|
||||
} else {
|
||||
// Fading in
|
||||
_displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef;
|
||||
}
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
||||
|
@ -143,19 +162,35 @@ void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
|||
_mouseRayDirection = direction;
|
||||
}
|
||||
|
||||
static TextRenderer* textRenderer() {
|
||||
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
return renderer;
|
||||
enum TextRendererType {
|
||||
CHAT,
|
||||
DISPLAYNAME
|
||||
};
|
||||
|
||||
static TextRenderer* textRenderer(TextRendererType type) {
|
||||
static TextRenderer* chatRenderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
static TextRenderer* displayNameRenderer = new TextRenderer(SANS_FONT_FAMILY, 12, -1, false, TextRenderer::NO_EFFECT);
|
||||
|
||||
switch(type) {
|
||||
case CHAT:
|
||||
return chatRenderer;
|
||||
case DISPLAYNAME:
|
||||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
void Avatar::render(bool forceRenderHead) {
|
||||
|
||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||
float lengthToTarget = glm::length(toTarget);
|
||||
|
||||
{
|
||||
// glow when moving in the distance
|
||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||
const float GLOW_DISTANCE = 5.0f;
|
||||
Glower glower(_moving && glm::length(toTarget) > GLOW_DISTANCE ? 1.0f : 0.0f);
|
||||
|
||||
const float GLOW_DISTANCE = 5.0f;
|
||||
Glower glower(_moving && lengthToTarget > GLOW_DISTANCE ? 1.0f : 0.0f);
|
||||
|
||||
// render body
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) {
|
||||
_skeletonModel.renderCollisionProxies(0.7f);
|
||||
|
@ -166,10 +201,10 @@ void Avatar::render(bool forceRenderHead) {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
|
||||
renderBody(forceRenderHead);
|
||||
}
|
||||
|
||||
|
||||
// render sphere when far away
|
||||
const float MAX_ANGLE = 10.f;
|
||||
float height = getHeight();
|
||||
float height = getSkeletonHeight();
|
||||
glm::vec3 delta = height * (getHead()->getCameraOrientation() * IDENTITY_UP) / 2.f;
|
||||
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
||||
|
||||
|
@ -182,13 +217,15 @@ void Avatar::render(bool forceRenderHead) {
|
|||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
const float DISPLAYNAME_DISTANCE = 4.0f;
|
||||
setShowDisplayName(lengthToTarget < DISPLAYNAME_DISTANCE);
|
||||
renderDisplayName();
|
||||
|
||||
|
||||
if (!_chatMessage.empty()) {
|
||||
int width = 0;
|
||||
int lastWidth = 0;
|
||||
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
||||
width += (lastWidth = textRenderer()->computeWidth(*it));
|
||||
width += (lastWidth = textRenderer(CHAT)->computeWidth(*it));
|
||||
}
|
||||
glPushMatrix();
|
||||
|
||||
|
@ -207,7 +244,7 @@ void Avatar::render(bool forceRenderHead) {
|
|||
glDisable(GL_LIGHTING);
|
||||
glDepthMask(false);
|
||||
if (_keyState == NO_KEY_DOWN) {
|
||||
textRenderer()->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
|
||||
} else {
|
||||
// rather than using substr and allocating a new string, just replace the last
|
||||
|
@ -215,10 +252,10 @@ void Avatar::render(bool forceRenderHead) {
|
|||
int lastIndex = _chatMessage.size() - 1;
|
||||
char lastChar = _chatMessage[lastIndex];
|
||||
_chatMessage[lastIndex] = '\0';
|
||||
textRenderer()->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
_chatMessage[lastIndex] = lastChar;
|
||||
glColor3f(0, 1, 0);
|
||||
textRenderer()->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
|
||||
textRenderer(CHAT)->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
|
||||
}
|
||||
glEnable(GL_LIGHTING);
|
||||
glDepthMask(true);
|
||||
|
@ -254,6 +291,93 @@ void Avatar::renderBody(bool forceRenderHead) {
|
|||
getHand()->render(false);
|
||||
}
|
||||
|
||||
void Avatar::renderDisplayName() {
|
||||
|
||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
glPushMatrix();
|
||||
glm::vec3 textPosition = getPosition() + getBodyUpDirection() * ((getSkeletonHeight() + getHeadHeight()) / 1.5f);
|
||||
glTranslatef(textPosition.x, textPosition.y, textPosition.z);
|
||||
|
||||
// we need "always facing camera": we must remove the camera rotation from the stack
|
||||
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
|
||||
|
||||
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
|
||||
// We project a unit vector and check the difference in screen coordinates, to check which is the
|
||||
// correction scale needed
|
||||
// save the matrices for later scale correction factor
|
||||
glm::dmat4 modelViewMatrix;
|
||||
glm::dmat4 projectionMatrix;
|
||||
GLint viewportMatrix[4];
|
||||
Application::getInstance()->getModelViewMatrix(&modelViewMatrix);
|
||||
Application::getInstance()->getProjectionMatrix(&projectionMatrix);
|
||||
glGetIntegerv(GL_VIEWPORT, viewportMatrix);
|
||||
GLdouble result0[3], result1[3];
|
||||
|
||||
glm::dvec3 upVector(modelViewMatrix[1]);
|
||||
|
||||
glm::dvec3 testPoint0 = glm::dvec3(textPosition);
|
||||
glm::dvec3 testPoint1 = glm::dvec3(textPosition) + upVector;
|
||||
|
||||
bool success;
|
||||
success = gluProject(testPoint0.x, testPoint0.y, testPoint0.z,
|
||||
(GLdouble*)&modelViewMatrix, (GLdouble*)&projectionMatrix, viewportMatrix,
|
||||
&result0[0], &result0[1], &result0[2]);
|
||||
success = success &&
|
||||
gluProject(testPoint1.x, testPoint1.y, testPoint1.z,
|
||||
(GLdouble*)&modelViewMatrix, (GLdouble*)&projectionMatrix, viewportMatrix,
|
||||
&result1[0], &result1[1], &result1[2]);
|
||||
|
||||
if (success) {
|
||||
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||
float scaleFactor = (textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f;
|
||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||
|
||||
// draw a gray background
|
||||
QFontMetrics metrics = textRenderer(DISPLAYNAME)->metrics();
|
||||
int bottom = -metrics.descent(), top = bottom + metrics.height();
|
||||
int left = -_displayNameWidth/2, right = _displayNameWidth/2;
|
||||
const int border = 5;
|
||||
bottom -= border;
|
||||
left -= border;
|
||||
top += border;
|
||||
right += border;
|
||||
|
||||
// We are drawing coplanar textures with depth: need the polygon offset
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(1.0f, 1.0f);
|
||||
|
||||
glColor4f(0.2f, 0.2f, 0.2f, _displayNameAlpha);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(left, bottom);
|
||||
glVertex2f(right, bottom);
|
||||
glVertex2f(right, top);
|
||||
glVertex2f(left, top);
|
||||
glEnd();
|
||||
|
||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||
glColor4f(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||
|
||||
QByteArray ba = _displayName.toLocal8Bit();
|
||||
const char* text = ba.data();
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
textRenderer(DISPLAYNAME)->draw(-_displayNameWidth / 2, 0, text);
|
||||
|
||||
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
float minDistance = FLT_MAX;
|
||||
float modelDistance;
|
||||
|
@ -363,6 +487,15 @@ void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) {
|
|||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL);
|
||||
}
|
||||
|
||||
void Avatar::setDisplayName(const QString& displayName) {
|
||||
AvatarData::setDisplayName(displayName);
|
||||
int width = 0;
|
||||
for (int i = 0; i < displayName.size(); i++) {
|
||||
width += (textRenderer(DISPLAYNAME)->computeWidth(displayName[i].toLatin1()));
|
||||
}
|
||||
_displayNameWidth = width;
|
||||
}
|
||||
|
||||
int Avatar::parseData(const QByteArray& packet) {
|
||||
// change in position implies movement
|
||||
glm::vec3 oldPosition = _position;
|
||||
|
@ -448,11 +581,16 @@ void Avatar::setScale(float scale) {
|
|||
}
|
||||
}
|
||||
|
||||
float Avatar::getHeight() const {
|
||||
float Avatar::getSkeletonHeight() const {
|
||||
Extents extents = _skeletonModel.getBindExtents();
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
float Avatar::getHeadHeight() const {
|
||||
Extents extents = getHead()->getFaceModel().getBindExtents();
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
|
||||
if (!collision._data || collision._type != MODEL_COLLISION) {
|
||||
return false;
|
||||
|
@ -491,3 +629,21 @@ float Avatar::getPelvisToHeadLength() const {
|
|||
return glm::distance(_position, getHead()->getPosition());
|
||||
}
|
||||
|
||||
void Avatar::setShowDisplayName(bool showDisplayName) {
|
||||
// For myAvatar, the alpha update is not done (called in simulate for other avatars)
|
||||
if (Application::getInstance()->getAvatar() == this) {
|
||||
if (showDisplayName) {
|
||||
_displayNameAlpha = DISPLAYNAME_ALPHA;
|
||||
} else {
|
||||
_displayNameAlpha = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (showDisplayName) {
|
||||
_displayNameTargetAlpha = DISPLAYNAME_ALPHA;
|
||||
} else {
|
||||
_displayNameTargetAlpha = 0.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -113,18 +113,23 @@ public:
|
|||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
|
||||
void setShowDisplayName(bool showDisplayName);
|
||||
|
||||
int parseData(const QByteArray& packet);
|
||||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
|
||||
|
||||
/// \return true if we expect the avatar would move as a result of the collision
|
||||
bool collisionWouldMoveAvatar(CollisionInfo& collision) const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
void applyCollision(CollisionInfo& collision);
|
||||
|
||||
float getBoundingRadius() const { return 0.5f * getHeight(); }
|
||||
float getBoundingRadius() const { return 0.5f * getSkeletonHeight(); }
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
|
@ -154,7 +159,8 @@ protected:
|
|||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
|
||||
float getHeight() const;
|
||||
float getSkeletonHeight() const;
|
||||
float getHeadHeight() const;
|
||||
float getPelvisFloatingHeight() const;
|
||||
float getPelvisToHeadLength() const;
|
||||
|
||||
|
@ -163,6 +169,8 @@ private:
|
|||
bool _initialized;
|
||||
|
||||
void renderBody(bool forceRenderHead);
|
||||
|
||||
void renderDisplayName();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
// Created by Stephen Birarda on 1/23/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
#include <string>
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <UUID.h>
|
||||
|
@ -71,6 +74,8 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
|
|||
"Application::renderAvatars()");
|
||||
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors);
|
||||
|
||||
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
|
||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||
|
@ -184,8 +189,9 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) {
|
|||
while (!identityStream.atEnd()) {
|
||||
|
||||
QUrl faceMeshURL, skeletonURL;
|
||||
identityStream >> nodeUUID >> faceMeshURL >> skeletonURL;
|
||||
|
||||
QString displayName;
|
||||
identityStream >> nodeUUID >> faceMeshURL >> skeletonURL >> displayName;
|
||||
|
||||
// mesh URL for a UUID, find avatar in our list
|
||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
|
||||
if (matchingAvatar) {
|
||||
|
@ -198,6 +204,10 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) {
|
|||
if (avatar->getSkeletonModelURL() != skeletonURL) {
|
||||
avatar->setSkeletonModelURL(skeletonURL);
|
||||
}
|
||||
|
||||
if (avatar->getDisplayName() != displayName) {
|
||||
avatar->setDisplayName(displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
if (_collisionFlags != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getHeight() * COLLISION_RADIUS_SCALE;
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
|
||||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
|
@ -610,6 +610,7 @@ void MyAvatar::saveData(QSettings* settings) {
|
|||
|
||||
settings->setValue("faceModelURL", _faceModelURL);
|
||||
settings->setValue("skeletonModelURL", _skeletonModelURL);
|
||||
settings->setValue("displayName", _displayName);
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
@ -637,6 +638,7 @@ void MyAvatar::loadData(QSettings* settings) {
|
|||
|
||||
setFaceModelURL(settings->value("faceModelURL").toUrl());
|
||||
setSkeletonModelURL(settings->value("skeletonModelURL").toUrl());
|
||||
setDisplayName(settings->value("displayName").toString());
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
@ -689,7 +691,9 @@ void MyAvatar::updateLookAtTargetAvatar(glm::vec3 &eyePosition) {
|
|||
(avatar->getScale() / avatar->getHead()->getScale()) + avatar->getHead()->getScalePivot();
|
||||
_lookAtTargetAvatar = avatarPointer;
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
|
||||
}
|
||||
_lookAtTargetAvatar.clear();
|
||||
}
|
||||
|
@ -854,7 +858,7 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
|||
float pelvisFloatingHeight = getPelvisFloatingHeight();
|
||||
if (Application::getInstance()->getEnvironment()->findCapsulePenetration(
|
||||
_position - up * (pelvisFloatingHeight - radius),
|
||||
_position + up * (getHeight() - pelvisFloatingHeight + radius), radius, penetration)) {
|
||||
_position + up * (getSkeletonHeight() - pelvisFloatingHeight + radius), radius, penetration)) {
|
||||
_lastCollisionPosition = _position;
|
||||
updateCollisionSound(penetration, deltaTime, ENVIRONMENT_COLLISION_FREQUENCY);
|
||||
applyHardCollision(penetration, ENVIRONMENT_SURFACE_ELASTICITY, ENVIRONMENT_SURFACE_DAMPING);
|
||||
|
@ -869,7 +873,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
|||
float pelvisFloatingHeight = getPelvisFloatingHeight();
|
||||
if (Application::getInstance()->getVoxels()->findCapsulePenetration(
|
||||
_position - glm::vec3(0.0f, pelvisFloatingHeight - radius, 0.0f),
|
||||
_position + glm::vec3(0.0f, getHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
|
||||
_position + glm::vec3(0.0f, getSkeletonHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
|
||||
_lastCollisionPosition = _position;
|
||||
updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
QString getUserString() const;
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
||||
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
const QUuid& getUUID() { return _uuid; }
|
||||
|
||||
|
@ -45,6 +45,8 @@ public:
|
|||
private:
|
||||
|
||||
void setUsername(const QString& username);
|
||||
|
||||
void setDisplayName(const QString& displaName);
|
||||
|
||||
QString _username;
|
||||
QUuid _uuid;
|
||||
|
|
|
@ -35,7 +35,10 @@ AvatarData::AvatarData() :
|
|||
_keyState(NO_KEY_DOWN),
|
||||
_isChatCirclingEnabled(false),
|
||||
_headData(NULL),
|
||||
_handData(NULL)
|
||||
_handData(NULL),
|
||||
_displayNameWidth(0),
|
||||
_displayNameTargetAlpha(0.0f),
|
||||
_displayNameAlpha(0.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -270,7 +273,8 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
|||
|
||||
QUuid avatarUUID;
|
||||
QUrl faceModelURL, skeletonModelURL;
|
||||
packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL;
|
||||
QString displayName;
|
||||
packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> displayName;
|
||||
|
||||
bool hasIdentityChanged = false;
|
||||
|
||||
|
@ -283,15 +287,20 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
|||
setSkeletonModelURL(skeletonModelURL);
|
||||
hasIdentityChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (displayName != _displayName) {
|
||||
setDisplayName(displayName);
|
||||
hasIdentityChanged = true;
|
||||
}
|
||||
|
||||
return hasIdentityChanged;
|
||||
}
|
||||
|
||||
QByteArray AvatarData::identityByteArray() {
|
||||
QByteArray identityData;
|
||||
QDataStream identityStream(&identityData, QIODevice::Append);
|
||||
|
||||
identityStream << QUuid() << _faceModelURL << _skeletonModelURL;
|
||||
|
||||
identityStream << QUuid() << _faceModelURL << _skeletonModelURL << _displayName;
|
||||
|
||||
return identityData;
|
||||
}
|
||||
|
@ -308,6 +317,13 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
qDebug() << "Changing skeleton model for avatar to" << _skeletonModelURL.toString();
|
||||
}
|
||||
|
||||
void AvatarData::setDisplayName(const QString& displayName) {
|
||||
_displayName = displayName;
|
||||
|
||||
qDebug() << "Changing display name for avatar to" << displayName;
|
||||
}
|
||||
|
||||
|
||||
void AvatarData::setClampedTargetScale(float targetScale) {
|
||||
|
||||
targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
|
|
|
@ -150,11 +150,13 @@ public:
|
|||
|
||||
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||
const QString& getDisplayName() const { return _displayName; }
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
virtual void setDisplayName(const QString& displayName);
|
||||
|
||||
virtual float getBoundingRadius() const { return 1.f; }
|
||||
|
||||
protected:
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _handPosition;
|
||||
|
@ -183,6 +185,12 @@ protected:
|
|||
|
||||
QUrl _faceModelURL;
|
||||
QUrl _skeletonModelURL;
|
||||
QString _displayName;
|
||||
|
||||
int _displayNameWidth;
|
||||
float _displayNameTargetAlpha;
|
||||
float _displayNameAlpha;
|
||||
|
||||
private:
|
||||
// privatize the copy constructor and assignment operator so they cannot be called
|
||||
AvatarData(const AvatarData&);
|
||||
|
|
Loading…
Reference in a new issue