mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 19:55:07 +02:00
merge fixes
This commit is contained in:
commit
739a02c022
43 changed files with 960 additions and 690 deletions
14
interface/resources/styles/checked.svg
Normal file
14
interface/resources/styles/checked.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, 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"
|
||||
width="17.186px" height="14px" viewBox="0 0 17.186 14" enable-background="new 0 0 17.186 14" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M12,9.446v1.989C12,11.747,11.987,12,11.675,12H2.806C2.494,12,2,11.747,2,11.435V2.565
|
||||
C2,2.253,2.494,2,2.806,2H11V0H2.806C1.392,0,0,1.151,0,2.565v8.869C0,12.849,1.392,14,2.806,14h8.869
|
||||
C13.09,14,14,12.849,14,11.435V8.435L12,9.446z"/>
|
||||
<path fill="#333333" d="M9.568,10.138c-0.3,0.299-0.769,0.299-1.069,0L4.466,6.105c-0.3-0.301-0.3-0.768,0-1.069l1.032-1.032
|
||||
c0.3-0.299,0.769-0.299,1.069,0L9.033,6.47l6.068-6.068c0.3-0.299,0.769-0.299,1.069,0l1.032,1.032c0.3,0.301,0.3,0.768,0,1.069
|
||||
L9.568,10.138z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
|
@ -1,7 +1,58 @@
|
|||
|
||||
QPlainTextEdit {
|
||||
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
|
||||
font-size: 16px;
|
||||
padding-left: 28px;
|
||||
padding-top: 7px;
|
||||
color: #333333;
|
||||
background-color: #FFFFFF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QLineEdit {
|
||||
font-family: Inconsolata, Lucida Console, Andale Mono, Monaco;
|
||||
padding-left: 7px;
|
||||
background-color: #CCCCCC;
|
||||
border-width: 0;
|
||||
border-top-right-radius: 9px;
|
||||
border-bottom-right-radius: 9px;
|
||||
color: #333333;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
QPushButton#searchButton {
|
||||
background: url(resources/styles/search.svg);
|
||||
background-repeat: none;
|
||||
background-position: left center;
|
||||
background-origin: content;
|
||||
padding-left: 7px;
|
||||
background-color: #CCCCCC;
|
||||
border-width: 0;
|
||||
border-top-left-radius: 9px;
|
||||
border-bottom-left-radius: 9px;
|
||||
}
|
||||
|
||||
QPushButton#revealLogButton {
|
||||
background: url(resources/styles/txt-file.svg);
|
||||
background-repeat: none;
|
||||
background-position: left center;
|
||||
background-origin: content;
|
||||
padding-left: 10px;
|
||||
background-color: #333333;
|
||||
color: #BBBBBB;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QCheckBox {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
QCheckBox::indicator:unchecked {
|
||||
image: url(resources/styles/unchecked.svg);
|
||||
}
|
||||
|
||||
QCheckBox::indicator:checked {
|
||||
image: url(resources/styles/checked.svg);
|
||||
}
|
||||
|
|
12
interface/resources/styles/search.svg
Normal file
12
interface/resources/styles/search.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, 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"
|
||||
width="12px" height="12px" viewBox="0 0 12 12" enable-background="new 0 0 12 12" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M10.716,11.573c-0.228,0-0.449-0.094-0.603-0.254L7.815,9.028C7.032,9.57,6.094,9.858,5.143,9.858
|
||||
c-2.606,0-4.716-2.11-4.716-4.716s2.11-4.716,4.716-4.716s4.716,2.11,4.716,4.716c0,0.951-0.288,1.889-0.831,2.673l2.297,2.297
|
||||
c0.154,0.154,0.248,0.375,0.248,0.603C11.573,11.185,11.185,11.573,10.716,11.573z M5.143,2.142c-1.655,0-3.001,1.346-3.001,3.001
|
||||
s1.346,3.001,3.001,3.001s3.001-1.346,3.001-3.001S6.797,2.142,5.143,2.142z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 929 B |
15
interface/resources/styles/txt-file.svg
Normal file
15
interface/resources/styles/txt-file.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, 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"
|
||||
width="12.927px" height="15.106px" viewBox="0 0 12.927 15.106" enable-background="new 0 0 12.927 15.106" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#CCCCCC" d="M12.148,13.667c0,0.503-0.408,0.911-0.911,0.911H0.911C0.408,14.578,0,14.17,0,13.667V0.911
|
||||
C0,0.408,0.408,0,0.911,0h6.074c0.503,0,1.206,0.294,1.557,0.646l2.961,2.961c0.351,0.351,0.646,1.054,0.646,1.557V13.667z
|
||||
M10.934,13.363V6.074H6.985c-0.503,0-0.911-0.408-0.911-0.911V1.215H1.215v12.148H10.934z M9.719,8.2
|
||||
c0,0.171-0.133,0.304-0.304,0.304H2.733C2.562,8.504,2.43,8.371,2.43,8.2V7.593c0-0.171,0.133-0.304,0.304-0.304h6.682
|
||||
c0.171,0,0.304,0.133,0.304,0.304V8.2z M9.719,10.63c0,0.171-0.133,0.304-0.304,0.304H2.733c-0.171,0-0.304-0.133-0.304-0.304
|
||||
v-0.607c0-0.171,0.133-0.304,0.304-0.304h6.682c0.171,0,0.304,0.133,0.304,0.304V10.63z M10.857,4.859
|
||||
c-0.057-0.161-0.142-0.323-0.208-0.389L7.678,1.5C7.612,1.433,7.45,1.348,7.289,1.291v3.568H10.857z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
11
interface/resources/styles/unchecked.svg
Normal file
11
interface/resources/styles/unchecked.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.2, 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"
|
||||
width="17.186px" height="14px" viewBox="0 0 17.186 14" enable-background="new 0 0 17.186 14" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#666666" d="M14,11.435C14,12.851,12.851,14,11.435,14H2.565C1.149,14,0,12.851,0,11.435V2.565C0,1.149,1.149,0,2.565,0
|
||||
h8.869C12.851,0,14,1.149,14,2.565V11.435z M12,2.565C12,2.253,11.747,2,11.435,2H2.565C2.253,2,2,2.253,2,2.565v8.869
|
||||
C2,11.747,2.253,12,2.565,12h8.869C11.747,12,12,11.747,12,11.435V2.565z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 825 B |
34
interface/src/AbstractLoggerInterface.h
Normal file
34
interface/src/AbstractLoggerInterface.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AbstractLoggerInterface.h
|
||||
// interface
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/22/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__AbstractLoggerInterface__
|
||||
#define __interface__AbstractLoggerInterface__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class AbstractLoggerInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline bool extraDebugging() { return _extraDebugging; };
|
||||
inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; };
|
||||
|
||||
virtual void addMessage(QString) = 0;
|
||||
virtual QStringList getLogData() = 0;
|
||||
virtual void locateLog() = 0;
|
||||
|
||||
signals:
|
||||
void logReceived(QString message);
|
||||
|
||||
private:
|
||||
bool _extraDebugging;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__AbstractLoggerInterface__) */
|
|
@ -51,12 +51,12 @@
|
|||
#include "Application.h"
|
||||
#include "DataServerClient.h"
|
||||
#include "InterfaceVersion.h"
|
||||
#include "LogDisplay.h"
|
||||
#include "Menu.h"
|
||||
#include "Swatch.h"
|
||||
#include "Util.h"
|
||||
#include "devices/LeapManager.h"
|
||||
#include "devices/OculusManager.h"
|
||||
#include "devices/TV3DManager.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
#include "ui/TextRenderer.h"
|
||||
#include "InfoView.h"
|
||||
|
@ -88,7 +88,7 @@ const float MIRROR_REARVIEW_BODY_DISTANCE = 1.f;
|
|||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {
|
||||
fprintf(stdout, "%s", message.toLocal8Bit().constData());
|
||||
LogDisplay::instance.addMessage(message.toLocal8Bit().constData());
|
||||
Application::getInstance()->getLogger()->addMessage(message.toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||
|
@ -141,7 +141,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_recentMaxPackets(0),
|
||||
_resetRecentMaxPacketsSoon(true),
|
||||
_swatch(NULL),
|
||||
_pasteMode(false)
|
||||
_pasteMode(false),
|
||||
_logger(new FileLogger())
|
||||
{
|
||||
_applicationStartupTime = startup_time;
|
||||
|
||||
|
@ -267,7 +268,8 @@ Application::~Application() {
|
|||
|
||||
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
|
||||
Menu::getInstance()->deleteLater();
|
||||
|
||||
|
||||
delete _logger;
|
||||
delete _settings;
|
||||
delete _followMode;
|
||||
delete _glWidget;
|
||||
|
@ -439,7 +441,10 @@ void Application::paintGL() {
|
|||
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::display(whichCamera);
|
||||
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
_glowEffect.prepare();
|
||||
TV3DManager::display(whichCamera);
|
||||
_glowEffect.render();
|
||||
} else {
|
||||
_glowEffect.prepare();
|
||||
|
||||
|
@ -474,8 +479,10 @@ void Application::paintGL() {
|
|||
_mirrorCamera.update(1.0f/_fps);
|
||||
|
||||
// set the bounds of rear mirror view
|
||||
glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height());
|
||||
glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(), _mirrorViewRect.width(), _mirrorViewRect.height());
|
||||
glViewport(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(),
|
||||
_mirrorViewRect.width(), _mirrorViewRect.height());
|
||||
glScissor(_mirrorViewRect.x(), _glWidget->height() - _mirrorViewRect.y() - _mirrorViewRect.height(),
|
||||
_mirrorViewRect.width(), _mirrorViewRect.height());
|
||||
bool updateViewFrustum = false;
|
||||
updateProjectionMatrix(_mirrorCamera, updateViewFrustum);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
@ -506,7 +513,6 @@ void Application::paintGL() {
|
|||
// restore absolute translations
|
||||
_myAvatar.getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
|
||||
_myAvatar.getHead().getFaceModel().setTranslation(absoluteFaceTranslation);
|
||||
|
||||
} else {
|
||||
displaySide(_mirrorCamera, true);
|
||||
}
|
||||
|
@ -531,7 +537,8 @@ void Application::paintGL() {
|
|||
void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) {
|
||||
if (OculusManager::isConnected()) {
|
||||
OculusManager::configureCamera(camera, width, height);
|
||||
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
TV3DManager::configureCamera(camera, width, height);
|
||||
} else {
|
||||
camera.setAspectRatio((float)width / height);
|
||||
camera.setFieldOfView(Menu::getInstance()->getFieldOfView());
|
||||
|
@ -910,7 +917,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_J:
|
||||
if (isShifted) {
|
||||
_viewFrustum.setFocalLength(_viewFrustum.getFocalLength() - 0.1f);
|
||||
|
||||
if (TV3DManager::isConnected()) {
|
||||
TV3DManager::configureCamera(_myCamera, _glWidget->width(),_glWidget->height());
|
||||
}
|
||||
} else {
|
||||
_myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(-0.001, 0, 0));
|
||||
}
|
||||
|
@ -920,6 +929,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_M:
|
||||
if (isShifted) {
|
||||
_viewFrustum.setFocalLength(_viewFrustum.getFocalLength() + 0.1f);
|
||||
if (TV3DManager::isConnected()) {
|
||||
TV3DManager::configureCamera(_myCamera, _glWidget->width(),_glWidget->height());
|
||||
}
|
||||
|
||||
} else {
|
||||
_myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0.001, 0, 0));
|
||||
|
@ -1346,7 +1358,7 @@ void Application::idle() {
|
|||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
|
||||
// details normally.
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
|
||||
bool showWarnings = getLogger()->extraDebugging();
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()");
|
||||
|
||||
timeval check;
|
||||
|
@ -1839,6 +1851,13 @@ void Application::init() {
|
|||
"trigger",
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
TV3DManager::connect();
|
||||
if (TV3DManager::isConnected()) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
|
||||
"trigger",
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
LeapManager::initialize();
|
||||
|
||||
|
@ -2426,7 +2445,7 @@ void Application::updateCamera(float deltaTime) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateCamera()");
|
||||
|
||||
if (!OculusManager::isConnected()) {
|
||||
if (!OculusManager::isConnected() && !TV3DManager::isConnected()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
|
@ -2676,7 +2695,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node
|
|||
return;
|
||||
}
|
||||
|
||||
bool wantExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
|
||||
bool wantExtraDebugging = getLogger()->extraDebugging();
|
||||
|
||||
// These will be the same for all servers, so we can set them up once and then reuse for each server we send to.
|
||||
_voxelQuery.setWantLowResMoving(!Menu::getInstance()->isOptionChecked(MenuOption::DisableLowRes));
|
||||
|
@ -3360,11 +3379,6 @@ void Application::displayOverlay() {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMap)) {
|
||||
renderCoverageMap();
|
||||
}
|
||||
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Log)) {
|
||||
LogDisplay::instance.render(_glWidget->width(), _glWidget->height());
|
||||
}
|
||||
|
||||
// Show chat entry field
|
||||
if (_chatEntryOn) {
|
||||
|
@ -3811,7 +3825,7 @@ void Application::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) {
|
|||
return;
|
||||
}
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... Avatars...");
|
||||
"Application::renderAvatars()");
|
||||
|
||||
if (!selfAvatarOnly) {
|
||||
// Render avatars of other nodes
|
||||
|
@ -4100,7 +4114,7 @@ void Application::resetSensors() {
|
|||
if (OculusManager::isConnected()) {
|
||||
OculusManager::reset();
|
||||
}
|
||||
|
||||
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
_myAvatar.reset();
|
||||
_myTransmitter.resetLevels();
|
||||
|
@ -4367,7 +4381,7 @@ void* Application::networkReceive(void* args) {
|
|||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
|
||||
|
||||
bool wantExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
|
||||
bool wantExtraDebugging = app->getLogger()->extraDebugging();
|
||||
if (wantExtraDebugging && app->_incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) {
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(app->_incomingPacket);
|
||||
unsigned char* dataAt = app->_incomingPacket + numBytesPacketHeader;
|
||||
|
@ -4492,7 +4506,7 @@ void Application::loadScript() {
|
|||
|
||||
void Application::toggleLogDialog() {
|
||||
if (! _logDialog) {
|
||||
_logDialog = new LogDialog(_glWidget);
|
||||
_logDialog = new LogDialog(_glWidget, getLogger());
|
||||
_logDialog->show();
|
||||
} else {
|
||||
_logDialog->close();
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#include "ui/RearMirrorTools.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "FileLogger.h"
|
||||
#include "ParticleTreeRenderer.h"
|
||||
#include "ParticleEditHandle.h"
|
||||
#include "ControllerScriptingInterface.h"
|
||||
|
@ -201,7 +202,8 @@ public:
|
|||
|
||||
VoxelShader& getVoxelShader() { return _voxelShader; }
|
||||
PointShader& getPointShader() { return _pointShader; }
|
||||
|
||||
FileLogger* getLogger() { return _logger; }
|
||||
|
||||
glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); }
|
||||
NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
|
||||
NodeToJurisdictionMap& getParticleServerJurisdictions() { return _particleServerJurisdictions; }
|
||||
|
@ -254,7 +256,6 @@ private slots:
|
|||
void shrinkMirrorView();
|
||||
void resetSensors();
|
||||
|
||||
|
||||
private:
|
||||
void resetCamerasOnResizeGL(Camera& camera, int width, int height);
|
||||
void updateProjectionMatrix();
|
||||
|
@ -509,6 +510,8 @@ private:
|
|||
std::vector<Avatar*> _avatarFades;
|
||||
ControllerScriptingInterface _controllerScriptingInterface;
|
||||
QPointer<LogDialog> _logDialog;
|
||||
|
||||
FileLogger* _logger;
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
44
interface/src/FileLogger.cpp
Normal file
44
interface/src/FileLogger.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// FileLogger.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/22/13.
|
||||
//
|
||||
//
|
||||
|
||||
#include "FileLogger.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include <FileUtils.h>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
|
||||
FileLogger::FileLogger() : _logData(NULL) {
|
||||
setExtraDebugging(false);
|
||||
_fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
QDir logDir(_fileName);
|
||||
if (!logDir.exists(_fileName)) {
|
||||
logDir.mkdir(_fileName);
|
||||
}
|
||||
|
||||
QHostAddress clientAddress = QHostAddress(getHostOrderLocalAddress());
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
_fileName.append(QString("/hifi-log_%1_%2.txt").arg(clientAddress.toString(), now.toString("yyyy-MM-dd_hh.mm.ss")));
|
||||
}
|
||||
|
||||
void FileLogger::addMessage(QString message) {
|
||||
QMutexLocker locker(&_mutex);
|
||||
emit logReceived(message);
|
||||
_logData.append(message);
|
||||
|
||||
QFile file(_fileName);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||
QTextStream out(&file);
|
||||
out << message;
|
||||
}
|
||||
}
|
||||
|
||||
void FileLogger::locateLog() {
|
||||
FileUtils::LocateFile(_fileName);
|
||||
}
|
32
interface/src/FileLogger.h
Normal file
32
interface/src/FileLogger.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// FileLogger.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/22/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef hifi_FileLogger_h
|
||||
#define hifi_FileLogger_h
|
||||
|
||||
#include "AbstractLoggerInterface.h"
|
||||
#include <QMutex>
|
||||
|
||||
class FileLogger : public AbstractLoggerInterface {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileLogger();
|
||||
|
||||
virtual void addMessage(QString);
|
||||
virtual QStringList getLogData() { return _logData; };
|
||||
virtual void locateLog();
|
||||
|
||||
private:
|
||||
QStringList _logData;
|
||||
QString _fileName;
|
||||
QMutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,291 +0,0 @@
|
|||
//
|
||||
// LogDisplay.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 4/14/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include "LogDisplay.h"
|
||||
#include "Util.h"
|
||||
|
||||
using namespace std;
|
||||
FILE* const LogDisplay::DEFAULT_STREAM = 0l;
|
||||
|
||||
//
|
||||
// Singleton constructor
|
||||
//
|
||||
LogDisplay LogDisplay::instance;
|
||||
|
||||
//
|
||||
// State management
|
||||
//
|
||||
|
||||
LogDisplay::LogDisplay() :
|
||||
_textRenderer(SANS_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT),
|
||||
_stream(DEFAULT_STREAM),
|
||||
_chars(0l),
|
||||
_lines(0l),
|
||||
_logWidth(DEFAULT_CONSOLE_WIDTH) {
|
||||
|
||||
pthread_mutex_init(& _mutex, 0l);
|
||||
|
||||
// allocate twice as much (so we have spare space for a copy not to block
|
||||
// logging from other threads during 'render')
|
||||
_chars = new char[CHARACTER_BUFFER_SIZE * 2];
|
||||
_charsEnd = _chars + CHARACTER_BUFFER_SIZE;
|
||||
_lines = new char*[LINE_BUFFER_SIZE * 2];
|
||||
_linesEnd = _lines + LINE_BUFFER_SIZE;
|
||||
|
||||
// initialize the log to all empty lines
|
||||
_chars[0] = '\0';
|
||||
_writePos = _chars;
|
||||
_writeLineStartPos = _chars;
|
||||
_lastLinePos = _lines;
|
||||
_writtenInLine = 0;
|
||||
memset(_lines, 0, LINE_BUFFER_SIZE * sizeof(char*));
|
||||
|
||||
setCharacterSize(DEFAULT_CHAR_WIDTH, DEFAULT_CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
|
||||
LogDisplay::~LogDisplay() {
|
||||
|
||||
delete[] _chars;
|
||||
delete[] _lines;
|
||||
}
|
||||
|
||||
void LogDisplay::setStream(FILE* stream) {
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
_stream = stream;
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
void LogDisplay::setLogWidth(unsigned pixels) {
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
_logWidth = pixels;
|
||||
_lineLength = _logWidth / _charWidth;
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
void LogDisplay::setCharacterSize(unsigned width, unsigned height) {
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
_charWidth = width;
|
||||
_charHeight = height;
|
||||
_lineLength = _logWidth / _charWidth;
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
//
|
||||
// Logging
|
||||
//
|
||||
|
||||
void LogDisplay::addMessage(const char* ptr) {
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
emit logReceived(ptr);
|
||||
|
||||
// T-pipe, if requested
|
||||
if (_stream != 0l) {
|
||||
fprintf(_stream, "%s", ptr);
|
||||
}
|
||||
|
||||
while (*ptr != '\0') {
|
||||
// process the characters
|
||||
char c = *ptr++;
|
||||
|
||||
if (c == '\t') {
|
||||
|
||||
// found TAB -> write SPACE
|
||||
c = ' ';
|
||||
|
||||
} else if (c == '\n') {
|
||||
|
||||
// found LF -> write NUL (c == '\0' tells us to wrap, below)
|
||||
c = '\0';
|
||||
}
|
||||
*_writePos++ = c;
|
||||
|
||||
if (_writePos == _charsEnd) {
|
||||
// reached the end of the circular character buffer? -> start over
|
||||
_writePos = _chars;
|
||||
}
|
||||
|
||||
if (c == '\0') {
|
||||
|
||||
// new line? store its start to the line buffer and mark next line as empty
|
||||
++_lastLinePos;
|
||||
|
||||
if (_lastLinePos == _linesEnd) {
|
||||
_lastLinePos = _lines;
|
||||
_lastLinePos[1] = 0l;
|
||||
} else if (_lastLinePos + 1 != _linesEnd) {
|
||||
_lastLinePos[1] = 0l;
|
||||
} else {
|
||||
_lines[0] = 0l;
|
||||
}
|
||||
*_lastLinePos = _writeLineStartPos;
|
||||
|
||||
// debug mode: make sure all line pointers we write here are valid
|
||||
assert(! (_lastLinePos < _lines || _lastLinePos >= _linesEnd));
|
||||
assert(! (*_lastLinePos < _chars || *_lastLinePos >= _charsEnd));
|
||||
|
||||
// terminate line, unless done already
|
||||
if (c != '\0') {
|
||||
*_writePos++ = '\0';
|
||||
|
||||
if (_writePos == _charsEnd) {
|
||||
_writePos = _chars;
|
||||
}
|
||||
}
|
||||
|
||||
// remember start position in character buffer for next line and reset character count
|
||||
_writeLineStartPos = _writePos;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
QStringList LogDisplay::getLogData() {
|
||||
// wait for adding new log data whilr iterating over _lines
|
||||
pthread_mutex_lock(& _mutex);
|
||||
QStringList list;
|
||||
int i = 0;
|
||||
while (_lines[i] != *_lastLinePos) {
|
||||
list.append(_lines[i++]);
|
||||
}
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
return list;
|
||||
}
|
||||
|
||||
//
|
||||
// Rendering
|
||||
//
|
||||
|
||||
void LogDisplay::render(unsigned screenWidth, unsigned screenHeight) {
|
||||
|
||||
// rendering might take some time, so create a local copy of the portion we need
|
||||
// instead of having to hold the mutex all the time
|
||||
pthread_mutex_lock(& _mutex);
|
||||
|
||||
// determine number of visible lines (integer division rounded up)
|
||||
unsigned showLines = (screenHeight + _charHeight - 1) / _charHeight;
|
||||
|
||||
char** lastLine = _lastLinePos;
|
||||
char** firstLine = _lastLinePos;
|
||||
|
||||
if (! *lastLine) {
|
||||
// empty log
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
// scan for first line
|
||||
for (int n = 2; n <= showLines; ++n) {
|
||||
|
||||
char** prevFirstLine = firstLine;
|
||||
--firstLine;
|
||||
if (firstLine < _lines) {
|
||||
firstLine = _linesEnd - 1;
|
||||
}
|
||||
if (! *firstLine) {
|
||||
firstLine = prevFirstLine;
|
||||
showLines = n - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// debug mode: make sure all line pointers we find here are valid
|
||||
assert(! (firstLine < _lines || firstLine >= _linesEnd));
|
||||
assert(! (*firstLine < _chars || *firstLine >= _charsEnd));
|
||||
}
|
||||
|
||||
// copy the line buffer portion into a contiguous region at _linesEnd
|
||||
if (firstLine <= lastLine) {
|
||||
|
||||
memcpy(_linesEnd, firstLine, showLines * sizeof(char*));
|
||||
|
||||
} else {
|
||||
|
||||
unsigned atEnd = _linesEnd - firstLine;
|
||||
memcpy(_linesEnd, firstLine, atEnd * sizeof(char*));
|
||||
memcpy(_linesEnd + atEnd, _lines, (showLines - atEnd) * sizeof(char*));
|
||||
}
|
||||
|
||||
// copy relevant char buffer portion and determine information to remap the pointers
|
||||
char* firstChar = *firstLine;
|
||||
char* lastChar = *lastLine + strlen(*lastLine) + 1;
|
||||
ptrdiff_t charOffset = _charsEnd - firstChar, charOffsetBeforeFirst = 0;
|
||||
if (firstChar <= lastChar) {
|
||||
|
||||
memcpy(_charsEnd, firstChar, lastChar - firstChar + 1);
|
||||
|
||||
} else {
|
||||
|
||||
unsigned atEnd = _charsEnd - firstChar;
|
||||
memcpy(_charsEnd, firstChar, atEnd);
|
||||
memcpy(_charsEnd + atEnd, _chars, lastChar + 1 - _chars);
|
||||
|
||||
charOffsetBeforeFirst = _charsEnd + atEnd - _chars;
|
||||
}
|
||||
|
||||
// determine geometry information from font metrics
|
||||
QFontMetrics const& fontMetrics = _textRenderer.metrics();
|
||||
int yStep = fontMetrics.lineSpacing();
|
||||
// scale
|
||||
float xScale = float(_charWidth) / fontMetrics.width('*');
|
||||
float yScale = float(_charHeight) / yStep;
|
||||
// scaled translation
|
||||
int xStart = int((screenWidth - _logWidth) / xScale);
|
||||
int yStart = screenHeight / yScale - fontMetrics.descent();
|
||||
|
||||
// first line to render
|
||||
char** line = _linesEnd + showLines;
|
||||
|
||||
// ok, now the lock can be released - we have all we need
|
||||
// and won't hold it while talking to OpenGL
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
|
||||
glPushMatrix();
|
||||
glScalef(xScale, yScale, 1.0f);
|
||||
glColor3ub(GLubyte(TEXT_COLOR >> 16),
|
||||
GLubyte((TEXT_COLOR >> 8) & 0xff),
|
||||
GLubyte(TEXT_COLOR & 0xff));
|
||||
for (int y = yStart; y > 0; y -= yStep) {
|
||||
|
||||
// debug mode: check line pointer is valid
|
||||
assert(! (line < _linesEnd || line >= _linesEnd + (_linesEnd - _lines)));
|
||||
|
||||
// get character pointer
|
||||
if (--line < _linesEnd) {
|
||||
break;
|
||||
}
|
||||
char* chars = *line;
|
||||
|
||||
// debug mode: check char pointer we find is valid
|
||||
assert(! (chars < _chars || chars >= _charsEnd));
|
||||
|
||||
// remap character pointer it to copied buffer
|
||||
chars += chars >= firstChar ? charOffset : charOffsetBeforeFirst;
|
||||
|
||||
// debug mode: check char pointer is still valid (in new range)
|
||||
assert(! (chars < _charsEnd || chars >= _charsEnd + (_charsEnd - _chars)));
|
||||
|
||||
// render the string
|
||||
_textRenderer.draw(xStart, y, chars);
|
||||
|
||||
//fprintf(stderr, "LogDisplay::render, message = \"%s\"\n", chars);
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
//
|
||||
// LogDisplay.h
|
||||
// interface
|
||||
//
|
||||
// Created by Tobias Schwinger on 4/14/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__LogDisplay__
|
||||
#define __interface__LogDisplay__
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "ui/TextRenderer.h"
|
||||
|
||||
class LogDisplay : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
static LogDisplay instance;
|
||||
|
||||
void render(unsigned screenWidth, unsigned screenHeight);
|
||||
|
||||
// log formatted message
|
||||
void addMessage(const char* message);
|
||||
|
||||
// settings
|
||||
|
||||
static unsigned const TEXT_COLOR = 0xb299ff; // text foreground color (bytes, RGB)
|
||||
|
||||
static FILE* const DEFAULT_STREAM; // = stdout; // stream to also log to (defined in .cpp)
|
||||
static unsigned const DEFAULT_CHAR_WIDTH = 5; // width of a single character
|
||||
static unsigned const DEFAULT_CHAR_HEIGHT = 16; // height of a single character
|
||||
static unsigned const DEFAULT_CONSOLE_WIDTH = 400; // width of the (right-aligned) log console
|
||||
|
||||
void setStream(FILE* stream);
|
||||
void setLogWidth(unsigned pixels);
|
||||
void setCharacterSize(unsigned width, unsigned height);
|
||||
|
||||
// limits
|
||||
|
||||
static unsigned const CHARACTER_BUFFER_SIZE = 16384; // number of character that are buffered
|
||||
static unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered
|
||||
static unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message
|
||||
|
||||
QStringList getLogData();
|
||||
|
||||
signals:
|
||||
void logReceived(QString message);
|
||||
|
||||
private:
|
||||
// use static 'instance' to access the single instance
|
||||
LogDisplay();
|
||||
~LogDisplay();
|
||||
|
||||
// don't copy/assign
|
||||
LogDisplay(LogDisplay const&); // = delete;
|
||||
LogDisplay& operator=(LogDisplay const&); // = delete;
|
||||
|
||||
TextRenderer _textRenderer;
|
||||
FILE* _stream; // FILE as secondary destination for log messages
|
||||
char* _chars; // character buffer base address
|
||||
char* _charsEnd; // character buffer, exclusive end
|
||||
char** _lines; // line buffer base address
|
||||
char** _linesEnd; // line buffer, exclusive end
|
||||
|
||||
char* _writePos; // character position to write to
|
||||
char* _writeLineStartPos; // character position where line being written starts
|
||||
char** _lastLinePos; // last line in the log
|
||||
unsigned _writtenInLine; // character counter for line wrapping
|
||||
unsigned _lineLength; // number of characters before line wrap
|
||||
|
||||
unsigned _logWidth; // width of the log in pixels
|
||||
unsigned _charWidth; // width of a character in pixels
|
||||
unsigned _charHeight; // height of a character in pixels
|
||||
|
||||
pthread_mutex_t _mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -232,6 +232,8 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false);
|
||||
|
||||
|
||||
QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size");
|
||||
|
||||
|
@ -497,8 +499,6 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::ExtraDebugging);
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_V,
|
||||
this,
|
||||
|
|
|
@ -178,12 +178,12 @@ namespace MenuOption {
|
|||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString DontRenderVoxels = "Don't call _voxels.render()";
|
||||
const QString DontCallOpenGLForVoxels = "Don't call glDrawRangeElementsEXT() for Voxels";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableOcclusionCulling = "Enable Occlusion Culling";
|
||||
const QString EnableVoxelPacketCompression = "Enable Voxel Packet Compression";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString ExportVoxels = "Export Voxels";
|
||||
const QString ExtraDebugging = "Extra Debugging";
|
||||
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString FaceMode = "Cycle Face Mode";
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Menu.h"
|
||||
#include "Application.h"
|
||||
#include "VoxelHideShowThread.h"
|
||||
|
||||
VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) :
|
||||
|
@ -30,8 +30,8 @@ bool VoxelHideShowThread::process() {
|
|||
_theSystem->checkForCulling();
|
||||
uint64_t end = usecTimestampNow();
|
||||
uint64_t elapsed = end - start;
|
||||
|
||||
bool showExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
|
||||
|
||||
bool showExtraDebugging = Application::getInstance()->getLogger()->extraDebugging();
|
||||
if (showExtraDebugging && elapsed > USECS_PER_FRAME) {
|
||||
printf("VoxelHideShowThread::process()... checkForCulling took %llu\n", elapsed);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
"VoxelPacketProcessor::processPacket()");
|
||||
|
||||
const int WAY_BEHIND = 300;
|
||||
if (packetsToProcessCount() > WAY_BEHIND && Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
|
||||
|
||||
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
|
||||
qDebug("VoxelPacketProcessor::processPacket() packets to process=%d\n", packetsToProcessCount());
|
||||
}
|
||||
ssize_t messageLength = packetLength;
|
||||
|
|
|
@ -612,7 +612,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
|
|||
lockTree();
|
||||
VoxelPacketData packetData(packetIsCompressed);
|
||||
packetData.loadFinalizedContent(dataAt, sectionLength);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
|
||||
if (Application::getInstance()->getLogger()->extraDebugging()) {
|
||||
qDebug("VoxelSystem::parseData() ... Got Packet Section"
|
||||
" color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
|
||||
" subsection:%d sectionLength:%d uncompressed:%d\n",
|
||||
|
@ -973,7 +973,7 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo
|
|||
// not render these Voxels. We need to think about ways to keep the entire scene intact but maybe lower quality
|
||||
// possibly shifting down to lower LOD or something. This debug message is to help identify, if/when/how this
|
||||
// state actually occurs.
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
|
||||
if (Application::getInstance()->getLogger()->extraDebugging()) {
|
||||
qDebug("OHHHH NOOOOOO!!!! updateNodeInArrays() BAILING (_voxelsInWriteArrays >= _maxVoxels)\n");
|
||||
}
|
||||
return 0;
|
||||
|
@ -1964,7 +1964,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) {
|
|||
setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
|
||||
}
|
||||
|
||||
bool extraDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
|
||||
bool extraDebugDetails = Application::getInstance()->getLogger()->extraDebugging();
|
||||
if (extraDebugDetails) {
|
||||
qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n",
|
||||
args.nodesScanned, args.nodesRemoved, args.nodesInside,
|
||||
|
|
|
@ -190,13 +190,10 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
|||
// update avatar skeleton
|
||||
_skeleton.update(deltaTime, getOrientation(), _position);
|
||||
|
||||
|
||||
|
||||
// if this is not my avatar, then hand position comes from transmitted data
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = _handPosition;
|
||||
|
||||
//update the movement of the hand and process handshaking with other avatars...
|
||||
updateHandMovementAndTouching(deltaTime, enableHandMovement);
|
||||
|
||||
_hand.simulate(deltaTime, false);
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll));
|
||||
|
@ -227,18 +224,6 @@ void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) {
|
|||
_mouseRayDirection = direction;
|
||||
}
|
||||
|
||||
void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovement) {
|
||||
// reset hand and arm positions according to hand movement
|
||||
enableHandMovement |= updateLeapHandPositions();
|
||||
|
||||
//constrain right arm length and re-adjust elbow position as it bends
|
||||
// NOTE - the following must be called on all avatars - not just _isMine
|
||||
if (enableHandMovement) {
|
||||
updateArmIKAndConstraints(deltaTime, AVATAR_JOINT_RIGHT_FINGERTIPS);
|
||||
updateArmIKAndConstraints(deltaTime, AVATAR_JOINT_LEFT_FINGERTIPS);
|
||||
}
|
||||
}
|
||||
|
||||
static TextRenderer* textRenderer() {
|
||||
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||
return renderer;
|
||||
|
@ -314,93 +299,6 @@ void Avatar::render(bool forceRenderHead) {
|
|||
}
|
||||
}
|
||||
|
||||
// returns true if the Leap controls any of the avatar's hands.
|
||||
bool Avatar::updateLeapHandPositions() {
|
||||
bool returnValue = false;
|
||||
// If there are leap-interaction hands visible, see if we can use them as the endpoints for IK
|
||||
if (getHand().getPalms().size() > 0) {
|
||||
PalmData const* leftLeapHand = NULL;
|
||||
PalmData const* rightLeapHand = NULL;
|
||||
// Look through all of the palms available (there may be more than two), and pick
|
||||
// the leftmost and rightmost. If there's only one, we'll use a heuristic below
|
||||
// to decode whether it's the left or right.
|
||||
for (size_t i = 0; i < getHand().getPalms().size(); ++i) {
|
||||
PalmData& palm = getHand().getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
if (!rightLeapHand || !leftLeapHand) {
|
||||
rightLeapHand = leftLeapHand = &palm;
|
||||
}
|
||||
else if (palm.getRawPosition().x > rightLeapHand->getRawPosition().x) {
|
||||
rightLeapHand = &palm;
|
||||
}
|
||||
else if (palm.getRawPosition().x < leftLeapHand->getRawPosition().x) {
|
||||
leftLeapHand = &palm;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there's only one palm visible. Decide if it's the left or right
|
||||
if (leftLeapHand == rightLeapHand && leftLeapHand) {
|
||||
if (leftLeapHand->getRawPosition().x > 0) {
|
||||
leftLeapHand = NULL;
|
||||
}
|
||||
else {
|
||||
rightLeapHand = NULL;
|
||||
}
|
||||
}
|
||||
if (leftLeapHand) {
|
||||
_skeleton.joint[ AVATAR_JOINT_LEFT_FINGERTIPS ].position = leftLeapHand->getPosition();
|
||||
returnValue = true;
|
||||
}
|
||||
if (rightLeapHand) {
|
||||
_skeleton.joint[ AVATAR_JOINT_RIGHT_FINGERTIPS ].position = rightLeapHand->getPosition();
|
||||
returnValue = true;
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void Avatar::updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJointID) {
|
||||
Skeleton::AvatarJoint& fingerJoint = _skeleton.joint[fingerTipJointID];
|
||||
Skeleton::AvatarJoint& wristJoint = _skeleton.joint[fingerJoint.parent];
|
||||
Skeleton::AvatarJoint& elbowJoint = _skeleton.joint[wristJoint.parent];
|
||||
Skeleton::AvatarJoint& shoulderJoint = _skeleton.joint[elbowJoint.parent];
|
||||
|
||||
// determine the arm vector
|
||||
glm::vec3 armVector = fingerJoint.position;
|
||||
armVector -= shoulderJoint.position;
|
||||
|
||||
// test to see if right hand is being dragged beyond maximum arm length
|
||||
float distance = glm::length(armVector);
|
||||
|
||||
// don't let right hand get dragged beyond maximum arm length...
|
||||
float armLength = _skeletonModel.isActive() ?
|
||||
_skeletonModel.getRightArmLength() : _skeleton.getArmLength();
|
||||
const float ARM_RETRACTION = 0.75f;
|
||||
float retractedArmLength = armLength * ARM_RETRACTION;
|
||||
if (distance > retractedArmLength) {
|
||||
// reset right hand to be constrained to maximum arm length
|
||||
fingerJoint.position = shoulderJoint.position;
|
||||
glm::vec3 armNormal = armVector / distance;
|
||||
armVector = armNormal * retractedArmLength;
|
||||
distance = retractedArmLength;
|
||||
glm::vec3 constrainedPosition = shoulderJoint.position;
|
||||
constrainedPosition += armVector;
|
||||
fingerJoint.position = constrainedPosition;
|
||||
}
|
||||
|
||||
// set elbow position
|
||||
glm::vec3 newElbowPosition = shoulderJoint.position + armVector * ONE_HALF;
|
||||
|
||||
glm::vec3 perpendicular = glm::cross(getBodyRightDirection(), armVector);
|
||||
|
||||
newElbowPosition += perpendicular * (1.0f - (_maxArmLength / distance)) * ONE_HALF;
|
||||
elbowJoint.position = newElbowPosition;
|
||||
|
||||
// set wrist position
|
||||
const float wristPosRatio = 0.7f;
|
||||
wristJoint.position = elbowJoint.position + (fingerJoint.position - elbowJoint.position) * wristPosRatio;
|
||||
}
|
||||
|
||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::vec3 currentUp = orientation * IDENTITY_UP;
|
||||
|
|
|
@ -227,8 +227,6 @@ protected:
|
|||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
bool updateLeapHandPositions();
|
||||
void updateArmIKAndConstraints(float deltaTime, AvatarJointID fingerTipJointID);
|
||||
void setScale(const float scale);
|
||||
|
||||
|
||||
|
@ -249,7 +247,6 @@ private:
|
|||
void renderBody(bool forceRenderHead);
|
||||
void initializeBodyBalls();
|
||||
void resetBodyBalls();
|
||||
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -62,8 +62,8 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
_grabDeltaVelocity(0, 0, 0),
|
||||
_grabStartRotation(0, 0, 0, 1),
|
||||
_grabCurrentRotation(0, 0, 0, 1),
|
||||
_throwInjector(QUrl("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw")),
|
||||
_catchInjector(QUrl("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"))
|
||||
_throwSound(QUrl("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw")),
|
||||
_catchSound(QUrl("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw"))
|
||||
{
|
||||
for (int i = 0; i < MAX_HANDS; i++) {
|
||||
_toyBallInHand[i] = false;
|
||||
|
@ -72,10 +72,6 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
}
|
||||
_lastControllerButtons = 0;
|
||||
|
||||
// the throw and catch sounds should not loopback, we'll play them locally
|
||||
_throwInjector.setShouldLoopback(false);
|
||||
_catchInjector.setShouldLoopback(false);
|
||||
|
||||
// Make some bucky balls for the avatar
|
||||
_bballIsGrabbed[0] = 0;
|
||||
_bballIsGrabbed[1] = 0;
|
||||
|
@ -267,12 +263,15 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
//printf(">>>>>>> caught... handID:%d particle ID:%d _toyBallInHand[handID] = true\n", handID, closestParticle->getID());
|
||||
_ballParticleEditHandles[handID] = caughtParticle;
|
||||
caughtParticle = NULL;
|
||||
// Play a catch sound!
|
||||
_catchInjector.setPosition(targetPosition);
|
||||
|
||||
// inject the catch sound to the mixer and play it locally
|
||||
_catchInjector.injectViaThread(app->getAudio());
|
||||
app->getAudio()->startDrumSound(1.0, 300, 0.75, 0.015);
|
||||
// use the threadSound static method to inject the catch sound
|
||||
// pass an AudioInjectorOptions struct to set position and disable loopback
|
||||
AudioInjectorOptions injectorOptions;
|
||||
injectorOptions.position = targetPosition;
|
||||
injectorOptions.shouldLoopback = false;
|
||||
injectorOptions.loopbackAudioInterface = app->getAudio();
|
||||
|
||||
AudioInjector::threadSound(&_catchSound, injectorOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,11 +353,14 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f
|
|||
delete _ballParticleEditHandles[handID];
|
||||
_ballParticleEditHandles[handID] = NULL;
|
||||
|
||||
// move the throw injector to inject from the position of the ball
|
||||
_throwInjector.setPosition(ballPosition);
|
||||
// use the threadSound static method to inject the throw sound
|
||||
// pass an AudioInjectorOptions struct to set position and disable loopback
|
||||
AudioInjectorOptions injectorOptions;
|
||||
injectorOptions.position = targetPosition;
|
||||
injectorOptions.shouldLoopback = false;
|
||||
injectorOptions.loopbackAudioInterface = app->getAudio();
|
||||
|
||||
// inject the throw sound and play it locally
|
||||
_throwInjector.injectViaThread(app->getAudio());
|
||||
AudioInjector::threadSound(&_throwSound, injectorOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,8 +119,8 @@ private:
|
|||
glm::quat _grabStartRotation;
|
||||
glm::quat _grabCurrentRotation;
|
||||
|
||||
AudioInjector _throwInjector;
|
||||
AudioInjector _catchInjector;
|
||||
Sound _throwSound;
|
||||
Sound _catchSound;
|
||||
|
||||
glm::vec3 _bballPosition[NUM_BBALLS];
|
||||
glm::vec3 _bballVelocity[NUM_BBALLS];
|
||||
|
|
|
@ -746,9 +746,15 @@ void MyAvatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMov
|
|||
pointDirection = glm::normalize(projectedVector);
|
||||
}
|
||||
}
|
||||
const float FAR_AWAY_POINT = TREE_SCALE;
|
||||
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = _mouseRayOrigin + pointDirection * FAR_AWAY_POINT;
|
||||
pointing = true;
|
||||
glm::vec3 shoulderPosition;
|
||||
if (_skeletonModel.getRightShoulderPosition(shoulderPosition)) {
|
||||
glm::vec3 farVector = _mouseRayOrigin + pointDirection * (float)TREE_SCALE - shoulderPosition;
|
||||
const float ARM_RETRACTION = 0.75f;
|
||||
float retractedLength = _skeletonModel.getRightArmLength() * ARM_RETRACTION;
|
||||
_skeleton.joint[AVATAR_JOINT_RIGHT_FINGERTIPS].position = shoulderPosition +
|
||||
glm::normalize(farVector) * retractedLength;
|
||||
pointing = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Set right hand position and state to be transmitted, and also tell AvatarTouch about it
|
||||
|
|
|
@ -42,7 +42,7 @@ void SkeletonModel::simulate(float deltaTime) {
|
|||
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
|
||||
restoreRightHandPosition(HAND_RESTORATION_RATE);
|
||||
} else {
|
||||
setRightHandPosition(_owningAvatar->getHandPosition());
|
||||
applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition());
|
||||
}
|
||||
restoreLeftHandPosition(HAND_RESTORATION_RATE);
|
||||
|
||||
|
@ -125,6 +125,29 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
|
|||
return firstIndex.value < secondIndex.value;
|
||||
}
|
||||
|
||||
void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) {
|
||||
if (jointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
setJointPosition(jointIndex, position);
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::vec3 handPosition, elbowPosition;
|
||||
getJointPosition(jointIndex, handPosition);
|
||||
getJointPosition(geometry.joints.at(jointIndex).parentIndex, elbowPosition);
|
||||
glm::vec3 forearmVector = handPosition - elbowPosition;
|
||||
float forearmLength = glm::length(forearmVector);
|
||||
if (forearmLength < EPSILON) {
|
||||
return;
|
||||
}
|
||||
glm::quat handRotation;
|
||||
getJointRotation(jointIndex, handRotation, true);
|
||||
|
||||
// align hand with forearm
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), false);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJointIndices,
|
||||
const QVector<int>& fingertipJointIndices, PalmData& palm) {
|
||||
if (jointIndex == -1) {
|
||||
|
|
|
@ -28,6 +28,8 @@ public:
|
|||
|
||||
protected:
|
||||
|
||||
void applyHandPosition(int jointIndex, const glm::vec3& position);
|
||||
|
||||
void applyPalmData(int jointIndex, const QVector<int>& fingerJointIndices,
|
||||
const QVector<int>& fingertipJointIndices, PalmData& palm);
|
||||
|
||||
|
|
132
interface/src/devices/TV3DManager.cpp
Normal file
132
interface/src/devices/TV3DManager.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
//
|
||||
// TV3DManager.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/24/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
#include "InterfaceConfig.h"
|
||||
#include "TV3DManager.h"
|
||||
#include "Menu.h"
|
||||
|
||||
int TV3DManager::_screenWidth = 1;
|
||||
int TV3DManager::_screenHeight = 1;
|
||||
double TV3DManager::_aspect = 1.0;
|
||||
eyeFrustum TV3DManager::_leftEye;
|
||||
eyeFrustum TV3DManager::_rightEye;
|
||||
|
||||
|
||||
bool TV3DManager::isConnected() {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::Enable3DTVMode);
|
||||
}
|
||||
|
||||
void TV3DManager::connect() {
|
||||
Application* app = Application::getInstance();
|
||||
int width = app->getGLWidget()->width();
|
||||
int height = app->getGLWidget()->height();
|
||||
Camera& camera = *app->getCamera();
|
||||
|
||||
configureCamera(camera, width, height);
|
||||
}
|
||||
|
||||
|
||||
// The basic strategy of this stereoscopic rendering is explained here:
|
||||
// http://www.orthostereo.com/geometryopengl.html
|
||||
void TV3DManager::setFrustum(Camera& whichCamera) {
|
||||
const double DTR = 0.0174532925; // degree to radians
|
||||
const double IOD = 0.05; //intraocular distance
|
||||
double fovy = whichCamera.getFieldOfView(); // field of view in y-axis
|
||||
double nearZ = whichCamera.getNearClip(); // near clipping plane
|
||||
double screenZ = Application::getInstance()->getViewFrustum()->getFocalLength(); // screen projection plane
|
||||
|
||||
double top = nearZ * tan(DTR * fovy / 2); //sets top of frustum based on fovy and near clipping plane
|
||||
double right = _aspect * top; // sets right of frustum based on aspect ratio
|
||||
double frustumshift = (IOD / 2) * nearZ / screenZ;
|
||||
|
||||
_leftEye.top = top;
|
||||
_leftEye.bottom = -top;
|
||||
_leftEye.left = -right + frustumshift;
|
||||
_leftEye.right = right + frustumshift;
|
||||
_leftEye.modelTranslation = IOD / 2;
|
||||
|
||||
_rightEye.top = top;
|
||||
_rightEye.bottom = -top;
|
||||
_rightEye.left = -right - frustumshift;
|
||||
_rightEye.right = right - frustumshift;
|
||||
_rightEye.modelTranslation = -IOD / 2;
|
||||
}
|
||||
|
||||
void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int screenHeight) {
|
||||
if (screenHeight == 0) {
|
||||
screenHeight = 1; // prevent divide by 0
|
||||
}
|
||||
_screenWidth = screenWidth;
|
||||
_screenHeight = screenHeight;
|
||||
_aspect= (double)_screenWidth / (double)_screenHeight;
|
||||
setFrustum(whichCamera);
|
||||
|
||||
glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
}
|
||||
|
||||
void TV3DManager::display(Camera& whichCamera) {
|
||||
double nearZ = whichCamera.getNearClip(); // near clipping plane
|
||||
double farZ = whichCamera.getFarClip(); // far clipping plane
|
||||
|
||||
// left eye portal
|
||||
int portalX = 0;
|
||||
int portalY = 0;
|
||||
int portalW = Application::getInstance()->getGLWidget()->width() / 2;
|
||||
int portalH = Application::getInstance()->getGLWidget()->height();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
// render left side view
|
||||
glViewport(portalX, portalY, portalW, portalH);
|
||||
glScissor(portalX, portalY, portalW, portalH);
|
||||
|
||||
glPushMatrix();
|
||||
{
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_leftEye.left, _leftEye.right, _leftEye.bottom, _leftEye.top, nearZ, farZ); // set left view frustum
|
||||
glTranslatef(_leftEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// render right side view
|
||||
portalX = Application::getInstance()->getGLWidget()->width() / 2;
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
// render left side view
|
||||
glViewport(portalX, portalY, portalW, portalH);
|
||||
glScissor(portalX, portalY, portalW, portalH);
|
||||
glPushMatrix();
|
||||
{
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity(); // reset projection matrix
|
||||
glFrustum(_rightEye.left, _rightEye.right, _rightEye.bottom, _rightEye.top, nearZ, farZ); // set left view frustum
|
||||
glTranslatef(_rightEye.modelTranslation, 0.0, 0.0); // translate to cancel parallax
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Application::getInstance()->displaySide(whichCamera);
|
||||
}
|
||||
glPopMatrix();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// reset the viewport to how we started
|
||||
glViewport(0, 0, Application::getInstance()->getGLWidget()->width(), Application::getInstance()->getGLWidget()->height());
|
||||
}
|
41
interface/src/devices/TV3DManager.h
Normal file
41
interface/src/devices/TV3DManager.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// TV3DManager.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/24/2013
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__TV3DManager__
|
||||
#define __hifi__TV3DManager__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class Camera;
|
||||
|
||||
struct eyeFrustum {
|
||||
double left;
|
||||
double right;
|
||||
double bottom;
|
||||
double top;
|
||||
float modelTranslation;
|
||||
};
|
||||
|
||||
|
||||
/// Handles interaction with 3D TVs
|
||||
class TV3DManager {
|
||||
public:
|
||||
static void connect();
|
||||
static bool isConnected();
|
||||
static void configureCamera(Camera& camera, int screenWidth, int screenHeight);
|
||||
static void display(Camera& whichCamera);
|
||||
private:
|
||||
static void setFrustum(Camera& whichCamera);
|
||||
static int _screenWidth;
|
||||
static int _screenHeight;
|
||||
static double _aspect;
|
||||
static eyeFrustum _leftEye;
|
||||
static eyeFrustum _rightEye;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__TV3DManager__) */
|
|
@ -500,6 +500,10 @@ bool Model::setLeftHandRotation(const glm::quat& rotation) {
|
|||
return setJointRotation(getLeftHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool Model::getLeftShoulderPosition(glm::vec3& position) const {
|
||||
return getJointPosition(getLastFreeJointIndex(getLeftHandJointIndex()), position);
|
||||
}
|
||||
|
||||
float Model::getLeftArmLength() const {
|
||||
return getLimbLength(getLeftHandJointIndex());
|
||||
}
|
||||
|
@ -516,6 +520,10 @@ bool Model::setRightHandRotation(const glm::quat& rotation) {
|
|||
return setJointRotation(getRightHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool Model::getRightShoulderPosition(glm::vec3& position) const {
|
||||
return getJointPosition(getLastFreeJointIndex(getRightHandJointIndex()), position);
|
||||
}
|
||||
|
||||
float Model::getRightArmLength() const {
|
||||
return getLimbLength(getRightHandJointIndex());
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
/// Returns the index of the parent of the indexed joint, or -1 if not found.
|
||||
int getParentJointIndex(int jointIndex) const;
|
||||
|
||||
/// Returns the index of the last free ancestor or the indexed joint, or -1 if not found.
|
||||
/// Returns the index of the last free ancestor of the indexed joint, or -1 if not found.
|
||||
int getLastFreeJointIndex(int jointIndex) const;
|
||||
|
||||
/// Returns the position of the head joint.
|
||||
|
@ -95,7 +95,11 @@ public:
|
|||
/// \return whether or not the left hand joint was found
|
||||
bool setLeftHandRotation(const glm::quat& rotation);
|
||||
|
||||
/// Returns the extended length from the left hand to its first free ancestor.
|
||||
/// Gets the position of the left shoulder.
|
||||
/// \return whether or not the left shoulder joint was found
|
||||
bool getLeftShoulderPosition(glm::vec3& position) const;
|
||||
|
||||
/// Returns the extended length from the left hand to its last free ancestor.
|
||||
float getLeftArmLength() const;
|
||||
|
||||
/// Sets the position of the right hand using inverse kinematics.
|
||||
|
@ -111,6 +115,10 @@ public:
|
|||
/// \return whether or not the right hand joint was found
|
||||
bool setRightHandRotation(const glm::quat& rotation);
|
||||
|
||||
/// Gets the position of the right shoulder.
|
||||
/// \return whether or not the right shoulder joint was found
|
||||
bool getRightShoulderPosition(glm::vec3& position) const;
|
||||
|
||||
/// Returns the extended length from the right hand to its first free ancestor.
|
||||
float getRightArmLength() const;
|
||||
|
||||
|
|
|
@ -12,64 +12,158 @@
|
|||
|
||||
#include "SharedUtil.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "LogDisplay.h"
|
||||
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int TOP_BAR_HEIGHT = 46;
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int MINIMAL_WIDTH = 570;
|
||||
const int ELEMENT_MARGIN = 7;
|
||||
const int ELEMENT_HEIGHT = 32;
|
||||
const int SEARCH_BUTTON_LEFT = 25;
|
||||
const int SEARCH_BUTTON_WIDTH = 20;
|
||||
const int SEARCH_TEXT_WIDTH = 240;
|
||||
const int CHECKBOX_MARGIN = 12;
|
||||
const int CHECKBOX_WIDTH = 140;
|
||||
const int REVEAL_BUTTON_WIDTH = 122;
|
||||
const float INITIAL_HEIGHT_RATIO = 0.6f;
|
||||
const QString HIGHLIGHT_COLOR = "#3366CC";
|
||||
|
||||
int cursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
|
||||
int blockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
|
||||
int qTextCursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
|
||||
int qTextBlockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
|
||||
|
||||
LogDialog::LogDialog(QWidget* parent) : QDialog(parent, Qt::Dialog) {
|
||||
LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Dialog) {
|
||||
|
||||
_logger = logger;
|
||||
setWindowTitle("Log");
|
||||
|
||||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
QFile styleSheet("resources/styles/log_dialog.qss");
|
||||
|
||||
if (styleSheet.open(QIODevice::ReadOnly)) {
|
||||
setStyleSheet(styleSheet.readAll());
|
||||
}
|
||||
|
||||
initControls();
|
||||
|
||||
QDesktopWidget desktop;
|
||||
QRect screen = desktop.screenGeometry();
|
||||
resize(INITIAL_WIDTH, static_cast<int>(screen.height() * INITIAL_HEIGHT_RATIO));
|
||||
move(screen.center() - rect().center());
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setMinimumWidth(MINIMAL_WIDTH);
|
||||
}
|
||||
|
||||
LogDialog::~LogDialog() {
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void LogDialog::showEvent(QShowEvent *e) {
|
||||
void LogDialog::initControls() {
|
||||
|
||||
int left;
|
||||
_searchButton = new QPushButton(this);
|
||||
// set object name for css styling
|
||||
_searchButton->setObjectName("searchButton");
|
||||
left = SEARCH_BUTTON_LEFT;
|
||||
_searchButton->setGeometry(left, ELEMENT_MARGIN, SEARCH_BUTTON_WIDTH, ELEMENT_HEIGHT);
|
||||
left += SEARCH_BUTTON_WIDTH;
|
||||
_searchButton->show();
|
||||
connect(_searchButton, SIGNAL(clicked()), SLOT(handleSearchButton()));
|
||||
|
||||
_searchTextBox = new QLineEdit(this);
|
||||
// disable blue outline in Mac
|
||||
_searchTextBox->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
_searchTextBox->setGeometry(left, ELEMENT_MARGIN, SEARCH_TEXT_WIDTH, ELEMENT_HEIGHT);
|
||||
left += SEARCH_TEXT_WIDTH + CHECKBOX_MARGIN;
|
||||
_searchTextBox->show();
|
||||
connect(_searchTextBox, SIGNAL(textChanged(QString)), SLOT(handleSearchTextChanged(QString)));
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
_extraDebuggingBox->setGeometry(left, ELEMENT_MARGIN, CHECKBOX_WIDTH, ELEMENT_HEIGHT);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
}
|
||||
_extraDebuggingBox->show();
|
||||
connect(_extraDebuggingBox, SIGNAL(stateChanged(int)), SLOT(handleExtraDebuggingCheckbox(int)));
|
||||
|
||||
_revealLogButton = new QPushButton("Reveal log file", this);
|
||||
// set object name for css styling
|
||||
_revealLogButton->setObjectName("revealLogButton");
|
||||
_revealLogButton->show();
|
||||
connect(_revealLogButton, SIGNAL(clicked()), SLOT(handleRevealButton()));
|
||||
|
||||
_logTextBox = new QPlainTextEdit(this);
|
||||
_logTextBox->setReadOnly(true);
|
||||
_logTextBox->show();
|
||||
_highlighter = new KeywordHighlighter(_logTextBox->document());
|
||||
|
||||
}
|
||||
|
||||
void LogDialog::showEvent(QShowEvent*) {
|
||||
connect(_logger, SIGNAL(logReceived(QString)), this, SLOT(appendLogLine(QString)));
|
||||
showLogData();
|
||||
}
|
||||
|
||||
void LogDialog::resizeEvent(QResizeEvent*) {
|
||||
_logTextBox->setGeometry(0, TOP_BAR_HEIGHT, width(), height() - TOP_BAR_HEIGHT);
|
||||
_revealLogButton->setGeometry(width() - ELEMENT_MARGIN - REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_MARGIN,
|
||||
REVEAL_BUTTON_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
}
|
||||
|
||||
void LogDialog::appendLogLine(QString logLine) {
|
||||
if (isVisible()) {
|
||||
pthread_mutex_lock(& _mutex);
|
||||
if (logLine.contains(_searchTerm, Qt::CaseInsensitive)) {
|
||||
_logTextBox->appendPlainText(logLine.simplified());
|
||||
}
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
_logTextBox->ensureCursorVisible();
|
||||
}
|
||||
}
|
||||
|
||||
void LogDialog::handleSearchButton() {
|
||||
_searchTextBox->setFocus();
|
||||
}
|
||||
|
||||
void LogDialog::handleRevealButton() {
|
||||
_logger->locateLog();
|
||||
}
|
||||
|
||||
void LogDialog::handleExtraDebuggingCheckbox(const int state) {
|
||||
_logger->setExtraDebugging(state != 0);
|
||||
}
|
||||
|
||||
void LogDialog::handleSearchTextChanged(const QString searchText) {
|
||||
_searchTerm = searchText;
|
||||
_highlighter->keyword = searchText;
|
||||
showLogData();
|
||||
}
|
||||
|
||||
void LogDialog::showLogData() {
|
||||
_logTextBox->clear();
|
||||
|
||||
pthread_mutex_lock(& _mutex);
|
||||
QStringList _logData = LogDisplay::instance.getLogData();
|
||||
|
||||
connect(&LogDisplay::instance, &LogDisplay::logReceived, this, &LogDialog::appendLogLine);
|
||||
for(int i = 0; i < _logData.size(); ++i) {
|
||||
QStringList _logData = _logger->getLogData();
|
||||
for (int i = 0; i < _logData.size(); ++i) {
|
||||
appendLogLine(_logData[i]);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
}
|
||||
|
||||
void LogDialog::resizeEvent(QResizeEvent *e) {
|
||||
_logTextBox->resize(width(), height());
|
||||
KeywordHighlighter::KeywordHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent), keywordFormat() {
|
||||
keywordFormat.setForeground(QColor(HIGHLIGHT_COLOR));
|
||||
}
|
||||
|
||||
void LogDialog::appendLogLine(QString logLine) {
|
||||
if (isVisible()) {
|
||||
pthread_mutex_lock(& _mutex);
|
||||
_logTextBox->appendPlainText(logLine.simplified());
|
||||
pthread_mutex_unlock(& _mutex);
|
||||
_logTextBox->ensureCursorVisible();
|
||||
void KeywordHighlighter::highlightBlock(const QString &text) {
|
||||
|
||||
if (keyword.isNull() || keyword.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = text.indexOf(keyword, 0, Qt::CaseInsensitive);
|
||||
int length = keyword.length();
|
||||
|
||||
while (index >= 0) {
|
||||
setFormat(index, length, keywordFormat);
|
||||
index = text.indexOf(keyword, index + length, Qt::CaseInsensitive);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,25 +11,62 @@
|
|||
|
||||
#include <QDialog>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#include "AbstractLoggerInterface.h"
|
||||
|
||||
class KeywordHighlighter : public QSyntaxHighlighter {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KeywordHighlighter(QTextDocument *parent = 0);
|
||||
QString keyword;
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text);
|
||||
|
||||
private:
|
||||
QTextCharFormat keywordFormat;
|
||||
|
||||
};
|
||||
|
||||
class LogDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogDialog(QWidget* parent);
|
||||
LogDialog(QWidget*, AbstractLoggerInterface*);
|
||||
~LogDialog();
|
||||
|
||||
public slots:
|
||||
void appendLogLine(QString logLine);
|
||||
|
||||
private slots:
|
||||
void handleSearchButton();
|
||||
void handleRevealButton();
|
||||
void handleExtraDebuggingCheckbox(const int);
|
||||
void handleSearchTextChanged(const QString);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* e);
|
||||
void showEvent(QShowEvent* e);
|
||||
void resizeEvent(QResizeEvent*);
|
||||
void showEvent(QShowEvent*);
|
||||
|
||||
private:
|
||||
QPushButton* _searchButton;
|
||||
QLineEdit* _searchTextBox;
|
||||
QCheckBox* _extraDebuggingBox;
|
||||
QPushButton* _revealLogButton;
|
||||
QPlainTextEdit* _logTextBox;
|
||||
pthread_mutex_t _mutex;
|
||||
QString _searchTerm;
|
||||
KeywordHighlighter* _highlighter;
|
||||
|
||||
AbstractLoggerInterface* _logger;
|
||||
|
||||
void initControls();
|
||||
void showLogData();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,16 +2,10 @@
|
|||
// AudioInjector.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/19/2013.
|
||||
// Created by Stephen Birarda on 1/2/2014.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -24,59 +18,54 @@
|
|||
|
||||
int abstractAudioPointerMeta = qRegisterMetaType<AbstractAudioInterface*>("AbstractAudioInterface*");
|
||||
|
||||
AudioInjector::AudioInjector(const QUrl& sampleURL) :
|
||||
_currentSendPosition(0),
|
||||
_sourceURL(sampleURL),
|
||||
_position(0,0,0),
|
||||
_orientation(),
|
||||
_volume(1.0f),
|
||||
_shouldLoopback(true)
|
||||
AudioInjector::AudioInjector(Sound* sound, AudioInjectorOptions injectorOptions) :
|
||||
_thread(NULL),
|
||||
_sound(sound),
|
||||
_volume(injectorOptions.volume),
|
||||
_shouldLoopback(injectorOptions.shouldLoopback),
|
||||
_position(injectorOptions.position),
|
||||
_orientation(injectorOptions.orientation),
|
||||
_loopbackAudioInterface(injectorOptions.loopbackAudioInterface)
|
||||
{
|
||||
_thread = new QThread();
|
||||
|
||||
// we want to live on our own thread
|
||||
moveToThread(&_thread);
|
||||
connect(&_thread, SIGNAL(started()), this, SLOT(startDownload()));
|
||||
_thread.start();
|
||||
moveToThread(_thread);
|
||||
}
|
||||
|
||||
void AudioInjector::startDownload() {
|
||||
// assume we have a QApplication or QCoreApplication instance and use the
|
||||
// QNetworkAccess manager to grab the raw audio file at the given URL
|
||||
void AudioInjector::threadSound(Sound* sound, AudioInjectorOptions injectorOptions) {
|
||||
AudioInjector* injector = new AudioInjector(sound, injectorOptions);
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
||||
this, SLOT(replyFinished(QNetworkReply*)));
|
||||
// start injecting when the injector thread starts
|
||||
connect(injector->_thread, SIGNAL(started()), injector, SLOT(injectAudio()));
|
||||
|
||||
manager->get(QNetworkRequest(_sourceURL));
|
||||
// connect the right slots and signals so that the AudioInjector is killed once the injection is complete
|
||||
connect(injector, SIGNAL(finished()), injector, SLOT(deleteLater()));
|
||||
connect(injector, SIGNAL(finished()), injector->_thread, SLOT(quit()));
|
||||
connect(injector->_thread, SIGNAL(finished()), injector->_thread, SLOT(deleteLater()));
|
||||
|
||||
injector->_thread->start();
|
||||
}
|
||||
|
||||
void AudioInjector::replyFinished(QNetworkReply* reply) {
|
||||
// replace our samples array with the downloaded data
|
||||
_sampleByteArray = reply->readAll();
|
||||
}
|
||||
const uchar MAX_INJECTOR_VOLUME = 0xFF;
|
||||
|
||||
void AudioInjector::injectViaThread(AbstractAudioInterface* localAudioInterface) {
|
||||
// use Qt::AutoConnection so that this is called on our thread, if appropriate
|
||||
QMetaObject::invokeMethod(this, "injectAudio", Qt::AutoConnection, Q_ARG(AbstractAudioInterface*, localAudioInterface));
|
||||
}
|
||||
|
||||
void AudioInjector::injectAudio(AbstractAudioInterface* localAudioInterface) {
|
||||
void AudioInjector::injectAudio() {
|
||||
|
||||
QByteArray soundByteArray = _sound->getByteArray();
|
||||
|
||||
// make sure we actually have samples downloaded to inject
|
||||
if (_sampleByteArray.size()) {
|
||||
if (soundByteArray.size()) {
|
||||
// give our sample byte array to the local audio interface, if we have it, so it can be handled locally
|
||||
if (localAudioInterface) {
|
||||
if (_loopbackAudioInterface) {
|
||||
// assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly
|
||||
QMetaObject::invokeMethod(localAudioInterface, "handleAudioByteArray",
|
||||
QMetaObject::invokeMethod(_loopbackAudioInterface, "handleAudioByteArray",
|
||||
Qt::AutoConnection,
|
||||
Q_ARG(QByteArray, _sampleByteArray));
|
||||
Q_ARG(QByteArray, soundByteArray));
|
||||
|
||||
}
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
// reset the current send position to the beginning
|
||||
_currentSendPosition = 0;
|
||||
|
||||
// setup the packet for injected audio
|
||||
unsigned char injectedAudioPacket[MAX_PACKET_SIZE];
|
||||
unsigned char* currentPacketPosition = injectedAudioPacket;
|
||||
|
@ -121,14 +110,16 @@ void AudioInjector::injectAudio(AbstractAudioInterface* localAudioInterface) {
|
|||
gettimeofday(&startTime, NULL);
|
||||
int nextFrame = 0;
|
||||
|
||||
int currentSendPosition = 0;
|
||||
|
||||
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
|
||||
while (_currentSendPosition < _sampleByteArray.size()) {
|
||||
while (currentSendPosition < soundByteArray.size()) {
|
||||
|
||||
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
||||
_sampleByteArray.size() - _currentSendPosition);
|
||||
soundByteArray.size() - currentSendPosition);
|
||||
|
||||
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
||||
memcpy(currentPacketPosition, _sampleByteArray.data() + _currentSendPosition,
|
||||
memcpy(currentPacketPosition, soundByteArray.data() + currentSendPosition,
|
||||
bytesToCopy);
|
||||
|
||||
|
||||
|
@ -143,11 +134,11 @@ void AudioInjector::injectAudio(AbstractAudioInterface* localAudioInterface) {
|
|||
audioMixer->getActiveSocket()->getPort());
|
||||
}
|
||||
|
||||
_currentSendPosition += bytesToCopy;
|
||||
currentSendPosition += bytesToCopy;
|
||||
|
||||
// send two packets before the first sleep so the mixer can start playback right away
|
||||
|
||||
if (_currentSendPosition != bytesToCopy && _currentSendPosition < _sampleByteArray.size()) {
|
||||
if (currentSendPosition != bytesToCopy && currentSendPosition < soundByteArray.size()) {
|
||||
// not the first packet and not done
|
||||
// sleep for the appropriate time
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
|
@ -157,6 +148,7 @@ void AudioInjector::injectAudio(AbstractAudioInterface* localAudioInterface) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
emit finished();
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// AudioInjector.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 12/19/2013.
|
||||
// Created by Stephen Birarda on 1/2/2014.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
|
@ -11,44 +11,46 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
class AbstractAudioInterface;
|
||||
class QNetworkReply;
|
||||
#include "Sound.h"
|
||||
|
||||
const uchar MAX_INJECTOR_VOLUME = 0xFF;
|
||||
class AbstractAudioInterface;
|
||||
|
||||
struct AudioInjectorOptions {
|
||||
AudioInjectorOptions() : position(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
volume(1.0f),
|
||||
orientation(glm::quat()),
|
||||
shouldLoopback(true),
|
||||
loopbackAudioInterface(NULL) {};
|
||||
|
||||
glm::vec3 position;
|
||||
float volume;
|
||||
const glm::quat orientation;
|
||||
bool shouldLoopback;
|
||||
AbstractAudioInterface* loopbackAudioInterface;
|
||||
};
|
||||
|
||||
class AudioInjector : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioInjector(const QUrl& sampleURL);
|
||||
|
||||
int size() const { return _sampleByteArray.size(); }
|
||||
|
||||
void setPosition(const glm::vec3& position) { _position = position; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
void setVolume(float volume) { _volume = std::max(fabsf(volume), 1.0f); }
|
||||
void setShouldLoopback(bool shouldLoopback) { _shouldLoopback = shouldLoopback; }
|
||||
public slots:
|
||||
void injectViaThread(AbstractAudioInterface* localAudioInterface = NULL);
|
||||
|
||||
static void threadSound(Sound* sound, AudioInjectorOptions injectorOptions = AudioInjectorOptions());
|
||||
private:
|
||||
QByteArray _sampleByteArray;
|
||||
int _currentSendPosition;
|
||||
QThread _thread;
|
||||
QUrl _sourceURL;
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
AudioInjector(Sound* sound, AudioInjectorOptions injectorOptions);
|
||||
|
||||
QThread* _thread;
|
||||
Sound* _sound;
|
||||
float _volume;
|
||||
uchar _shouldLoopback;
|
||||
|
||||
glm::vec3 _position;
|
||||
glm::quat _orientation;
|
||||
AbstractAudioInterface* _loopbackAudioInterface;
|
||||
private slots:
|
||||
void startDownload();
|
||||
void replyFinished(QNetworkReply* reply);
|
||||
void injectAudio(AbstractAudioInterface* localAudioInterface);
|
||||
void injectAudio();
|
||||
signals:
|
||||
void finished();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AudioInjector__) */
|
||||
|
|
29
libraries/audio/src/Sound.cpp
Normal file
29
libraries/audio/src/Sound.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Sound.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/2/2014.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include "Sound.h"
|
||||
|
||||
Sound::Sound(const QUrl& sampleURL) {
|
||||
// assume we have a QApplication or QCoreApplication instance and use the
|
||||
// QNetworkAccess manager to grab the raw audio file at the given URL
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
||||
this, SLOT(replyFinished(QNetworkReply*)));
|
||||
|
||||
manager->get(QNetworkRequest(sampleURL));
|
||||
}
|
||||
|
||||
void Sound::replyFinished(QNetworkReply* reply) {
|
||||
// replace our byte array with the downloaded data
|
||||
_byteArray = reply->readAll();
|
||||
}
|
28
libraries/audio/src/Sound.h
Normal file
28
libraries/audio/src/Sound.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Sound.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/2/2014.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Sound__
|
||||
#define __hifi__Sound__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class Sound : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Sound(const QUrl& sampleURL);
|
||||
|
||||
const QByteArray& getByteArray() { return _byteArray; }
|
||||
private:
|
||||
QByteArray _byteArray;
|
||||
private slots:
|
||||
void replyFinished(QNetworkReply* reply);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Sound__) */
|
|
@ -41,6 +41,8 @@ enum KeyState
|
|||
DELETE_KEY_DOWN
|
||||
};
|
||||
|
||||
const glm::vec3 vec3Zero(0.0f);
|
||||
|
||||
class JointData;
|
||||
|
||||
class AvatarData : public NodeData {
|
||||
|
@ -103,6 +105,17 @@ public:
|
|||
|
||||
void setHeadData(HeadData* headData) { _headData = headData; }
|
||||
void setHandData(HandData* handData) { _handData = handData; }
|
||||
|
||||
virtual const glm::vec3& getVelocity() const { return vec3Zero; }
|
||||
|
||||
/// Checks for penetration between the described sphere and the avatar.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param penetration[out] the vector in which to store the penetration
|
||||
/// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model
|
||||
/// \return whether or not the sphere penetrated
|
||||
virtual bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius,
|
||||
glm::vec3& penetration, int skeletonSkipIndex = -1) { return false; }
|
||||
|
||||
protected:
|
||||
QUuid _uuid;
|
||||
|
|
|
@ -572,7 +572,7 @@ bool findRayIntersectionOp(OctreeElement* node, void* extraData) {
|
|||
|
||||
bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElement*& node, float& distance, BoxFace& face) {
|
||||
RayArgs args = { origin / (float)TREE_SCALE, direction, node, distance, face };
|
||||
RayArgs args = { origin / static_cast<float>(TREE_SCALE), direction, node, distance, face };
|
||||
recurseTreeWithOperation(findRayIntersectionOp, &args);
|
||||
return args.found;
|
||||
}
|
||||
|
@ -600,7 +600,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
if (element->hasContent()) {
|
||||
glm::vec3 elementPenetration;
|
||||
if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) {
|
||||
args->penetration = addPenetrations(args->penetration, elementPenetration * (float)TREE_SCALE);
|
||||
args->penetration = addPenetrations(args->penetration, elementPenetration * static_cast<float>(TREE_SCALE));
|
||||
args->found = true;
|
||||
}
|
||||
}
|
||||
|
@ -610,7 +610,12 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration,
|
||||
void** penetratedObject) {
|
||||
|
||||
SphereArgs args = { center / (float)TREE_SCALE, radius / TREE_SCALE, penetration, false, NULL };
|
||||
SphereArgs args = {
|
||||
center / static_cast<float>(TREE_SCALE),
|
||||
radius / static_cast<float>(TREE_SCALE),
|
||||
penetration,
|
||||
false,
|
||||
NULL };
|
||||
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
recurseTreeWithOperation(findSpherePenetrationOp, &args);
|
||||
if (penetratedObject) {
|
||||
|
@ -642,7 +647,7 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
|
|||
if (node->hasContent()) {
|
||||
glm::vec3 nodePenetration;
|
||||
if (box.findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) {
|
||||
args->penetration = addPenetrations(args->penetration, nodePenetration * (float)TREE_SCALE);
|
||||
args->penetration = addPenetrations(args->penetration, nodePenetration * static_cast<float>(TREE_SCALE));
|
||||
args->found = true;
|
||||
}
|
||||
}
|
||||
|
@ -650,7 +655,11 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
|
|||
}
|
||||
|
||||
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) {
|
||||
CapsuleArgs args = { start / (float)TREE_SCALE, end / (float)TREE_SCALE, radius / TREE_SCALE, penetration };
|
||||
CapsuleArgs args = {
|
||||
start / static_cast<float>(TREE_SCALE),
|
||||
end / static_cast<float>(TREE_SCALE),
|
||||
radius / static_cast<float>(TREE_SCALE),
|
||||
penetration };
|
||||
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
recurseTreeWithOperation(findCapsulePenetrationOp, &args);
|
||||
return args.found;
|
||||
|
@ -719,8 +728,8 @@ int Octree::encodeTreeBitstream(OctreeElement* node,
|
|||
// if childBytesWritten == 1 then something went wrong... that's not possible
|
||||
assert(childBytesWritten != 1);
|
||||
|
||||
// if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some reason
|
||||
// couldn't be written... so reset them here... This isn't true for the non-color included case
|
||||
// if includeColor and childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some
|
||||
// reason couldn't be written... so reset them here... This isn't true for the non-color included case
|
||||
if (params.includeColor && childBytesWritten == 2) {
|
||||
childBytesWritten = 0;
|
||||
//params.stopReason = EncodeBitstreamParams::UNKNOWN; // possibly should be DIDNT_FIT...
|
||||
|
@ -887,9 +896,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node,
|
|||
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
|
||||
|
||||
// At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level
|
||||
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees. There could be sub trees
|
||||
// below this point, which might take many more bytes, but that's ok, because we can always mark our subtrees as
|
||||
// not existing and stop the packet at this point, then start up with a new packet for the remaining sub trees.
|
||||
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees.
|
||||
// There could be sub trees below this point, which might take many more bytes, but that's ok, because we can
|
||||
// always mark our subtrees as not existing and stop the packet at this point, then start up with a new packet
|
||||
// for the remaining sub trees.
|
||||
unsigned char childrenExistInTreeBits = 0;
|
||||
unsigned char childrenExistInPacketBits = 0;
|
||||
unsigned char childrenColoredBits = 0;
|
||||
|
@ -943,7 +953,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node,
|
|||
if (params.stats && childNode) {
|
||||
params.stats->traversed(childNode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for each child node in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
|
||||
|
@ -1066,7 +1075,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node,
|
|||
params.stats->skippedNoChange(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -466,15 +466,14 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz
|
|||
|
||||
|
||||
void Particle::update() {
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - _lastUpdated; // making this signed slightly improves clock skew behavior
|
||||
float timeElapsed = (float)((float)elapsed/(float)USECS_PER_SECOND);
|
||||
|
||||
float elapsed = static_cast<float>(now - _lastUpdated);
|
||||
_lastUpdated = now;
|
||||
float timeElapsed = elapsed / static_cast<float>(USECS_PER_SECOND);
|
||||
|
||||
// calculate our default shouldDie state... then allow script to change it if it wants...
|
||||
float velocityScalar = glm::length(getVelocity());
|
||||
const float STILL_MOVING = 0.05 / TREE_SCALE;
|
||||
const float STILL_MOVING = 0.05f / static_cast<float>(TREE_SCALE);
|
||||
bool isStillMoving = (velocityScalar > STILL_MOVING);
|
||||
const float REALLY_OLD = 30.0f; // 30 seconds
|
||||
bool isReallyOld = (getLifetime() > REALLY_OLD);
|
||||
|
@ -510,8 +509,6 @@ void Particle::update() {
|
|||
_velocity -= dampingResistance * timeElapsed;
|
||||
//printf("applying damping to Particle timeElapsed=%f\n",timeElapsed);
|
||||
}
|
||||
|
||||
_lastUpdated = now;
|
||||
}
|
||||
|
||||
void Particle::runUpdateScript() {
|
||||
|
|
|
@ -79,8 +79,8 @@ public:
|
|||
uint64_t getLastEdited() const { return _lastEdited; }
|
||||
|
||||
/// lifetime of the particle in seconds
|
||||
float getLifetime() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
|
||||
float getEditedAgo() const { return (float)(usecTimestampNow() - _lastEdited) / (float)USECS_PER_SECOND; }
|
||||
float getLifetime() const { return static_cast<float>(usecTimestampNow() - _created) / static_cast<float>(USECS_PER_SECOND); }
|
||||
float getEditedAgo() const { return static_cast<float>(usecTimestampNow() - _lastEdited) / static_cast<float>(USECS_PER_SECOND); }
|
||||
uint32_t getID() const { return _id; }
|
||||
bool getShouldDie() const { return _shouldDie; }
|
||||
QString getScript() const { return _script; }
|
||||
|
|
|
@ -67,10 +67,10 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) {
|
|||
}
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
|
||||
glm::vec3 center = particle->getPosition() * (float)TREE_SCALE;
|
||||
float radius = particle->getRadius() * (float)TREE_SCALE;
|
||||
const float VOXEL_ELASTICITY = 1.4f;
|
||||
const float VOXEL_DAMPING = 0.0;
|
||||
glm::vec3 center = particle->getPosition() * static_cast<float>(TREE_SCALE);
|
||||
float radius = particle->getRadius() * static_cast<float>(TREE_SCALE);
|
||||
const float VOXEL_ELASTICITY = 0.4f; // fraction of momentum conserved at collision
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
VoxelDetail* voxelDetails = NULL;
|
||||
|
@ -88,10 +88,10 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
|
|||
}
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particle) {
|
||||
glm::vec3 center = particle->getPosition() * (float)TREE_SCALE;
|
||||
float radius = particle->getRadius() * (float)TREE_SCALE;
|
||||
glm::vec3 center = particle->getPosition() * static_cast<float>(TREE_SCALE);
|
||||
float radius = particle->getRadius() * static_cast<float>(TREE_SCALE);
|
||||
const float VOXEL_ELASTICITY = 1.4f;
|
||||
const float VOXEL_DAMPING = 0.0;
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
Particle* penetratedParticle;
|
||||
|
@ -132,10 +132,10 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
}
|
||||
|
||||
//printf("updateCollisionWithAvatars()...\n");
|
||||
glm::vec3 center = particle->getPosition() * (float)TREE_SCALE;
|
||||
float radius = particle->getRadius() * (float)TREE_SCALE;
|
||||
glm::vec3 center = particle->getPosition() * static_cast<float>(TREE_SCALE);
|
||||
float radius = particle->getRadius() * static_cast<float>(TREE_SCALE);
|
||||
const float VOXEL_ELASTICITY = 1.4f;
|
||||
const float VOXEL_DAMPING = 0.0;
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
const PalmData* collidingPalm = NULL;
|
||||
|
@ -144,23 +144,34 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
if (_selfAvatar) {
|
||||
AvatarData* avatar = (AvatarData*)_selfAvatar;
|
||||
//printf("updateCollisionWithAvatars()..._selfAvatar=%p\n", avatar);
|
||||
|
||||
|
||||
// check hands...
|
||||
const HandData* handData = avatar->getHandData();
|
||||
|
||||
// if the particle penetrates the hand, then apply a hard collision
|
||||
// TODO: combine hand and collision check into one. Note: would need to supply
|
||||
// CollisionInfo class rather than just vec3 (penetration) so we can get back
|
||||
// added velocity.
|
||||
|
||||
if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) {
|
||||
// TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart.
|
||||
// apply a hard collision when ball collides with hand
|
||||
penetration /= (float)TREE_SCALE;
|
||||
updateCollisionSound(particle, penetration, VOXEL_COLLISION_FREQUENCY);
|
||||
|
||||
// determine if the palm that collided was moving, if so, then we add that palm velocity as well...
|
||||
glm::vec3 addedVelocity = NO_ADDED_VELOCITY;
|
||||
if (collidingPalm) {
|
||||
glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)TREE_SCALE;
|
||||
glm::vec3 palmVelocity = collidingPalm->getVelocity() / static_cast<float>(TREE_SCALE);
|
||||
//printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z);
|
||||
addedVelocity = palmVelocity;
|
||||
}
|
||||
|
||||
applyHardCollision(particle, penetration, VOXEL_ELASTICITY, VOXEL_DAMPING, addedVelocity);
|
||||
} else if (avatar->findSpherePenetration(center, radius, penetration)) {
|
||||
// apply hard collision when particle collides with avatar
|
||||
penetration /= (float)TREE_SCALE;
|
||||
updateCollisionSound(particle, penetration, VOXEL_COLLISION_FREQUENCY);
|
||||
glm::vec3 addedVelocity = avatar->getVelocity();
|
||||
applyHardCollision(particle, penetration, VOXEL_ELASTICITY, VOXEL_DAMPING, addedVelocity);
|
||||
}
|
||||
}
|
||||
|
@ -170,27 +181,33 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
//qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
AvatarData* avatar = (AvatarData*)node->getLinkedData();
|
||||
// TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart.
|
||||
AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
|
||||
//printf("updateCollisionWithAvatars()...avatar=%p\n", avatar);
|
||||
|
||||
// check hands...
|
||||
const HandData* handData = avatar->getHandData();
|
||||
|
||||
// if the particle penetrates the hand, then apply a hard collision
|
||||
if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) {
|
||||
// apply a hard collision when ball collides with hand
|
||||
penetration /= (float)TREE_SCALE;
|
||||
updateCollisionSound(particle, penetration, VOXEL_COLLISION_FREQUENCY);
|
||||
|
||||
// determine if the palm that collided was moving, if so, then we add that palm velocity as well...
|
||||
glm::vec3 addedVelocity = NO_ADDED_VELOCITY;
|
||||
if (collidingPalm) {
|
||||
glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)TREE_SCALE;
|
||||
glm::vec3 palmVelocity = collidingPalm->getVelocity() / static_cast<float>(TREE_SCALE);
|
||||
//printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z);
|
||||
addedVelocity = palmVelocity;
|
||||
}
|
||||
|
||||
applyHardCollision(particle, penetration, VOXEL_ELASTICITY, VOXEL_DAMPING, addedVelocity);
|
||||
|
||||
} else if (avatar->findSpherePenetration(center, radius, penetration)) {
|
||||
penetration /= (float)TREE_SCALE;
|
||||
updateCollisionSound(particle, penetration, VOXEL_COLLISION_FREQUENCY);
|
||||
glm::vec3 addedVelocity = avatar->getVelocity();
|
||||
applyHardCollision(particle, penetration, VOXEL_ELASTICITY, VOXEL_DAMPING, addedVelocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,21 +220,22 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::
|
|||
// Update the particle in response to a hard collision. Position will be reset exactly
|
||||
// to outside the colliding surface. Velocity will be modified according to elasticity.
|
||||
//
|
||||
// if elasticity = 1.0, collision is inelastic.
|
||||
// if elasticity > 1.0, collision is elastic.
|
||||
// if elasticity = 0.0, collision is inelastic (vel normal to collision is lost)
|
||||
// if elasticity = 1.0, collision is 100% elastic.
|
||||
//
|
||||
glm::vec3 position = particle->getPosition();
|
||||
glm::vec3 velocity = particle->getVelocity();
|
||||
|
||||
position -= penetration;
|
||||
static float HALTING_VELOCITY = 0.2f / (float) TREE_SCALE;
|
||||
// cancel out the velocity component in the direction of penetration
|
||||
float penetrationLength = glm::length(penetration);
|
||||
const float EPSILON = 0.0f;
|
||||
float velocityDotPenetration = glm::dot(velocity, penetration);
|
||||
if (velocityDotPenetration > EPSILON) {
|
||||
position -= penetration;
|
||||
static float HALTING_VELOCITY = 0.2f / static_cast<float>(TREE_SCALE);
|
||||
// cancel out the velocity component in the direction of penetration
|
||||
|
||||
if (penetrationLength > EPSILON) {
|
||||
float penetrationLength = glm::length(penetration);
|
||||
glm::vec3 direction = penetration / penetrationLength;
|
||||
velocity -= glm::dot(velocity, direction) * direction * elasticity;
|
||||
velocity -= (glm::dot(velocity, direction) * (1.0f + elasticity)) * direction;
|
||||
velocity += addedVelocity;
|
||||
velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f);
|
||||
if (glm::length(velocity) < HALTING_VELOCITY) {
|
||||
|
@ -235,7 +253,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::
|
|||
particleEditHandle.updateParticle(position, particle->getRadius(), particle->getXColor(), velocity,
|
||||
particle->getGravity(), particle->getDamping(), particle->getInHand(), particle->getScript());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) {
|
||||
|
||||
|
@ -244,12 +262,12 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm
|
|||
const float COLLISION_LOUDNESS = 1.f;
|
||||
const float DURATION_SCALING = 0.004f;
|
||||
const float NOISE_SCALING = 0.1f;
|
||||
glm::vec3 velocity = particle->getVelocity() * (float)TREE_SCALE;
|
||||
glm::vec3 velocity = particle->getVelocity() * static_cast<float>(TREE_SCALE);
|
||||
|
||||
/*
|
||||
// how do we want to handle this??
|
||||
//
|
||||
glm::vec3 gravity = particle->getGravity() * (float)TREE_SCALE;
|
||||
glm::vec3 gravity = particle->getGravity() * static_cast<float>(TREE_SCALE);
|
||||
|
||||
if (glm::length(gravity) > EPSILON) {
|
||||
// If gravity is on, remove the effect of gravity on velocity for this
|
||||
|
@ -271,4 +289,4 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm
|
|||
fmin(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f),
|
||||
1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
56
libraries/shared/src/FileUtils.cpp
Normal file
56
libraries/shared/src/FileUtils.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// FileUtils.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/23/13.
|
||||
//
|
||||
//
|
||||
|
||||
#include "FileUtils.h"
|
||||
#include <QtCore>
|
||||
#include <QDesktopServices>
|
||||
|
||||
void FileUtils::LocateFile(QString filePath) {
|
||||
|
||||
// adopted from
|
||||
// http://stackoverflow.com/questions/3490336/how-to-reveal-in-finder-or-show-in-explorer-with-qt
|
||||
// and
|
||||
// http://lynxline.com/show-in-finder-show-in-explorer/
|
||||
|
||||
QFileInfo fileInfo(filePath);
|
||||
if (!fileInfo.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
#ifdef Q_OS_MAC
|
||||
QStringList args;
|
||||
args << "-e";
|
||||
args << "tell application \"Finder\"";
|
||||
args << "-e";
|
||||
args << "activate";
|
||||
args << "-e";
|
||||
args << "select POSIX file \"" + fileInfo.absoluteFilePath().toUtf8() + "\"";
|
||||
args << "-e";
|
||||
args << "end tell";
|
||||
success = QProcess::startDetached("osascript", args);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
QStringList args;
|
||||
// don't send `select` command switch if `filePath` is folder
|
||||
if (!fileInfo.isDir()) {
|
||||
args << "/select,";
|
||||
}
|
||||
args += QDir::toNativeSeparators(fileInfo.absoluteFilePath().toUtf8());
|
||||
success = QProcess::startDetached("explorer", args);
|
||||
|
||||
#endif
|
||||
|
||||
// fallback, open enclosing folder
|
||||
if (!success) {
|
||||
const QString folder = fileInfo.path();
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(folder));
|
||||
}
|
||||
}
|
21
libraries/shared/src/FileUtils.h
Normal file
21
libraries/shared/src/FileUtils.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// FileUtils.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 12/23/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef hifi_FileUtils_h
|
||||
#define hifi_FileUtils_h
|
||||
|
||||
#include <QString>
|
||||
|
||||
class FileUtils {
|
||||
|
||||
public:
|
||||
static void LocateFile(QString);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -294,9 +294,9 @@ void NodeList::clear() {
|
|||
Node* node = nodeBucket[i % NODES_PER_BUCKET];
|
||||
|
||||
node->lock();
|
||||
delete node;
|
||||
notifyHooksOfKilledNode(&*node);
|
||||
|
||||
node = NULL;
|
||||
delete node;
|
||||
}
|
||||
|
||||
_numNodes = 0;
|
||||
|
|
|
@ -63,7 +63,7 @@ public:
|
|||
|
||||
public slots:
|
||||
glm::vec3 getPosition() const { return glm::vec3(_voxelDetail->x, _voxelDetail->y, _voxelDetail->z); }
|
||||
xColor getColor() const { return { _voxelDetail->red, _voxelDetail->green, _voxelDetail->blue }; }
|
||||
xColor getColor() const { xColor color = { _voxelDetail->red, _voxelDetail->green, _voxelDetail->blue }; return color; }
|
||||
float getScale() const { return _voxelDetail->s; }
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in a new issue