mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels
This commit is contained in:
commit
5e908134ea
19 changed files with 569 additions and 134 deletions
|
@ -15,7 +15,7 @@ set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense)
|
|||
if (DEFINED ENV{JOB_ID})
|
||||
set(BUILD_SEQ $ENV{JOB_ID})
|
||||
else ()
|
||||
set(BUILD_SEQ "0")
|
||||
set(BUILD_SEQ "dev")
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
|
@ -80,6 +80,8 @@ find_package(Qt5OpenGL REQUIRED)
|
|||
find_package(Qt5Svg REQUIRED)
|
||||
find_package(Qt5WebKit REQUIRED)
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
find_package(Qt5UiTools REQUIRED)
|
||||
|
||||
if (APPLE)
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME Interface)
|
||||
|
@ -146,7 +148,7 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
|||
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
|
||||
endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets)
|
||||
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools)
|
||||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories(
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc.. All rights reserved.
|
||||
//
|
||||
|
||||
const int BUILD_VERSION = @BUILD_SEQ@;
|
||||
const QString BUILD_VERSION = "@BUILD_SEQ@";
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[INFO]
|
||||
name=Interface
|
||||
version=0.0.1
|
||||
organizationName=High Fidelity
|
||||
organizationDomain=highfidelity.io
|
||||
organizationDomain=highfidelity.io
|
||||
|
|
182
interface/resources/ui/updateDialog.ui
Normal file
182
interface/resources/ui/updateDialog.ui
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>750</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Update Required</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255, 255, 255);</string>
|
||||
</property>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>751</width>
|
||||
<height>71</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(236, 236, 236);</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QLabel" name="updateDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>240</x>
|
||||
<y>10</y>
|
||||
<width>271</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>36</pointsize>
|
||||
<stylestrategy>PreferAntialias</stylestrategy>
|
||||
<kerning>false</kerning>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(51, 51, 51);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Update Required</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QLabel" name="updateContent">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>100</x>
|
||||
<y>110</y>
|
||||
<width>561</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>18</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>240</y>
|
||||
<width>364</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="downloadButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="skipButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -33,8 +33,10 @@
|
|||
#include <QMenuBar>
|
||||
#include <QMouseEvent>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QObject>
|
||||
#include <QWheelEvent>
|
||||
#include <QSettings>
|
||||
#include <QShortcut>
|
||||
|
@ -43,6 +45,8 @@
|
|||
#include <QtDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QDesktopServices>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamAttributes>
|
||||
|
||||
#include <AudioInjector.h>
|
||||
#include <NodeTypes.h>
|
||||
|
@ -91,6 +95,9 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f;
|
|||
const float MIRROR_REARVIEW_DISTANCE = 0.65f;
|
||||
const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f;
|
||||
|
||||
const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml";
|
||||
const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion";
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {
|
||||
QString messageWithNewLine = message + "\n";
|
||||
fprintf(stdout, "%s", messageWithNewLine.toLocal8Bit().constData());
|
||||
|
@ -160,8 +167,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
// call Menu getInstance static method to set up the menu
|
||||
_window->setMenuBar(Menu::getInstance());
|
||||
|
||||
qDebug("[VERSION] Build sequence: %i", BUILD_VERSION);
|
||||
|
||||
unsigned int listenPort = 0; // bind to an ephemeral port by default
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
|
||||
|
@ -195,9 +200,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
applicationInfo.beginGroup("INFO");
|
||||
|
||||
setApplicationName(applicationInfo.value("name").toString());
|
||||
setApplicationVersion(applicationInfo.value("version").toString());
|
||||
setApplicationVersion(BUILD_VERSION);
|
||||
setOrganizationName(applicationInfo.value("organizationName").toString());
|
||||
setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
_settings = new QSettings(this);
|
||||
|
||||
|
@ -232,6 +239,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_window->setCentralWidget(_glWidget);
|
||||
|
||||
restoreSizeAndPosition();
|
||||
loadScripts();
|
||||
_window->setVisible(true);
|
||||
_glWidget->setFocusPolicy(Qt::StrongFocus);
|
||||
_glWidget->setFocus();
|
||||
|
@ -255,7 +263,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
// Set the sixense filtering
|
||||
_sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
|
||||
|
||||
|
||||
checkVersion();
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -270,7 +279,7 @@ Application::~Application() {
|
|||
_audio.thread()->wait();
|
||||
|
||||
storeSizeAndPosition();
|
||||
|
||||
saveScripts();
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
|
||||
VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown
|
||||
|
@ -1384,6 +1393,7 @@ void Application::idle() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::terminate() {
|
||||
// Close serial port
|
||||
// close(serial_fd);
|
||||
|
@ -3959,7 +3969,7 @@ void Application::attachNewHeadToNode(Node* newNode) {
|
|||
|
||||
void Application::updateWindowTitle(){
|
||||
QString title = "";
|
||||
QString buildVersion = " (build " + QString::number(BUILD_VERSION) + ")";
|
||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||
QString username = _profile.getUsername();
|
||||
if(!username.isEmpty()){
|
||||
title += _profile.getUsername();
|
||||
|
@ -4237,13 +4247,38 @@ void Application::packetSentNotification(ssize_t length) {
|
|||
_bandwidthMeter.outputStream(BandwidthMeter::VOXELS).updateValue(length);
|
||||
}
|
||||
|
||||
void Application::loadScript() {
|
||||
// shut down and stop any existing script
|
||||
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
QString suggestedName = desktopLocation.append("/script.js");
|
||||
void Application::loadScripts(){
|
||||
// loads all saved scripts
|
||||
QSettings* settings = new QSettings(this);
|
||||
int size = settings->beginReadArray("Settings");
|
||||
for(int i=0; i<size; ++i){
|
||||
settings->setArrayIndex(i);
|
||||
QString string = settings->value("script").toString();
|
||||
loadScript(string);
|
||||
}
|
||||
settings->endArray();
|
||||
|
||||
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName,
|
||||
tr("JavaScript Files (*.js)"));
|
||||
}
|
||||
|
||||
void Application::saveScripts(){
|
||||
// saves all current running scripts
|
||||
QSettings* settings = new QSettings(this);
|
||||
settings->beginWriteArray("Settings");
|
||||
for(int i=0; i<_activeScripts.size(); ++i){
|
||||
settings->setArrayIndex(i);
|
||||
settings->setValue("script", _activeScripts.at(i));
|
||||
}
|
||||
settings->endArray();
|
||||
|
||||
}
|
||||
|
||||
void Application::removeScriptName(const QString& fileNameString)
|
||||
{
|
||||
_activeScripts.removeOne(fileNameString);
|
||||
}
|
||||
|
||||
void Application::loadScript(const QString& fileNameString){
|
||||
_activeScripts.append(fileNameString);
|
||||
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
|
||||
const char* fileName = fileNameAscii.data();
|
||||
|
||||
|
@ -4270,9 +4305,7 @@ void Application::loadScript() {
|
|||
// start the script on a new thread...
|
||||
bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself
|
||||
|
||||
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(),
|
||||
&_controllerScriptingInterface);
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), &_controllerScriptingInterface);
|
||||
scriptEngine->setupMenuItems();
|
||||
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
|
@ -4286,8 +4319,9 @@ void Application::loadScript() {
|
|||
connect(workerThread, SIGNAL(started()), scriptEngine, SLOT(run()));
|
||||
|
||||
// when the thread is terminated, add both scriptEngine and thread to the deleteLater queue
|
||||
connect(scriptEngine, SIGNAL(finished()), scriptEngine, SLOT(deleteLater()));
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater()));
|
||||
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(removeScriptName(const QString&)));
|
||||
|
||||
// when the application is about to quit, stop our script engine so it unwinds properly
|
||||
connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop()));
|
||||
|
@ -4301,6 +4335,17 @@ void Application::loadScript() {
|
|||
_window->activateWindow();
|
||||
}
|
||||
|
||||
void Application::loadDialog() {
|
||||
// shut down and stop any existing script
|
||||
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
QString suggestedName = desktopLocation.append("/script.js");
|
||||
|
||||
QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Open Script"), suggestedName,
|
||||
tr("JavaScript Files (*.js)"));
|
||||
|
||||
loadScript(fileNameString);
|
||||
}
|
||||
|
||||
void Application::toggleLogDialog() {
|
||||
if (! _logDialog) {
|
||||
_logDialog = new LogDialog(_glWidget, getLogger());
|
||||
|
@ -4310,7 +4355,6 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void Application::initAvatarAndViewFrustum() {
|
||||
updateAvatar(0.f);
|
||||
}
|
||||
|
@ -4357,3 +4401,72 @@ void Application::updateLocalOctreeCache(bool firstTime) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::checkVersion() {
|
||||
QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL)));
|
||||
latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
connect(Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest), SIGNAL(finished()), SLOT(parseVersionXml()));
|
||||
}
|
||||
|
||||
void Application::parseVersionXml() {
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
QString operatingSystem("win");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString operatingSystem("mac");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QString operatingSystem("ubuntu");
|
||||
#endif
|
||||
|
||||
QString releaseDate;
|
||||
QString releaseNotes;
|
||||
QString latestVersion;
|
||||
QUrl downloadUrl;
|
||||
QObject* sender = QObject::sender();
|
||||
|
||||
QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender));
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
QXmlStreamReader::TokenType token = xml.readNext();
|
||||
|
||||
if (token == QXmlStreamReader::StartElement) {
|
||||
if (xml.name() == "ReleaseDate") {
|
||||
xml.readNext();
|
||||
releaseDate = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == "ReleaseNotes") {
|
||||
xml.readNext();
|
||||
releaseNotes = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == "Version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == operatingSystem) {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) {
|
||||
new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl);
|
||||
}
|
||||
sender->deleteLater();
|
||||
}
|
||||
|
||||
bool Application::shouldSkipVersion(QString latestVersion) {
|
||||
QFile skipFile(SKIP_FILENAME);
|
||||
skipFile.open(QIODevice::ReadWrite);
|
||||
QString skipVersion(skipFile.readAll());
|
||||
return (skipVersion == latestVersion || applicationVersion() == "dev");
|
||||
}
|
||||
|
||||
void Application::skipVersion(QString latestVersion) {
|
||||
QFile skipFile(SKIP_FILENAME);
|
||||
skipFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
skipFile.seek(0);
|
||||
skipFile.write(latestVersion.toStdString().c_str());
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QSettings>
|
||||
#include <QTouchEvent>
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QPointer>
|
||||
|
||||
#include <NetworkPacket.h>
|
||||
|
@ -64,6 +65,7 @@
|
|||
#include "ui/RearMirrorTools.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
#include "FileLogger.h"
|
||||
#include "ParticleTreeRenderer.h"
|
||||
#include "ParticleEditHandle.h"
|
||||
|
@ -103,7 +105,10 @@ public:
|
|||
~Application();
|
||||
|
||||
void restoreSizeAndPosition();
|
||||
void loadScript(const QString& fileNameString);
|
||||
void loadScripts();
|
||||
void storeSizeAndPosition();
|
||||
void saveScripts();
|
||||
void initializeGL();
|
||||
void paintGL();
|
||||
void resizeGL(int width, int height);
|
||||
|
@ -202,6 +207,8 @@ public:
|
|||
/// set a voxel which is to be rendered with a highlight
|
||||
void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; }
|
||||
void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; }
|
||||
|
||||
void skipVersion(QString latestVersion);
|
||||
|
||||
public slots:
|
||||
void domainChanged(const QString& domainHostname);
|
||||
|
@ -221,7 +228,7 @@ public slots:
|
|||
void doKillLocalVoxels();
|
||||
void decreaseVoxelSize();
|
||||
void increaseVoxelSize();
|
||||
void loadScript();
|
||||
void loadDialog();
|
||||
void toggleLogDialog();
|
||||
void initAvatarAndViewFrustum();
|
||||
|
||||
|
@ -249,6 +256,10 @@ private slots:
|
|||
void restoreMirrorView();
|
||||
void shrinkMirrorView();
|
||||
void resetSensors();
|
||||
|
||||
void parseVersionXml();
|
||||
|
||||
void removeScriptName(const QString& fileNameString);
|
||||
|
||||
private:
|
||||
void resetCamerasOnResizeGL(Camera& camera, int width, int height);
|
||||
|
@ -373,6 +384,7 @@ private:
|
|||
Faceshift _faceshift;
|
||||
|
||||
SixenseManager _sixenseManager;
|
||||
QStringList _activeScripts;
|
||||
|
||||
Camera _myCamera; // My view onto the world
|
||||
Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode
|
||||
|
@ -495,6 +507,10 @@ private:
|
|||
|
||||
QString getLocalVoxelCacheFileName();
|
||||
void updateLocalOctreeCache(bool firstTime = false);
|
||||
|
||||
void checkVersion();
|
||||
void displayUpdateDialog();
|
||||
bool shouldSkipVersion(QString latestVersion);
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__Application__) */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QtCore/QObject>
|
||||
|
||||
#include <AbstractControllerScriptingInterface.h>
|
||||
class PalmData;
|
||||
|
||||
/// handles scripting of input controller commands from JS
|
||||
class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
|
||||
|
|
|
@ -91,7 +91,7 @@ Menu::Menu() :
|
|||
SLOT(login())));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadScript()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog()));
|
||||
_activeScriptsMenu = fileMenu->addMenu("Running Scripts");
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Voxels");
|
||||
|
|
|
@ -32,7 +32,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
// we need to iterate the actual particles of the element
|
||||
ParticleTreeElement* particleTreeElement = (ParticleTreeElement*)element;
|
||||
|
||||
const std::vector<Particle>& particles = particleTreeElement->getParticles();
|
||||
const QList<Particle>& particles = particleTreeElement->getParticles();
|
||||
|
||||
uint16_t numberOfParticles = particles.size();
|
||||
|
||||
|
|
61
interface/src/ui/UpdateDialog.cpp
Normal file
61
interface/src/ui/UpdateDialog.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// UpdateDialog.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Leonardo Murillo <leo@highfidelity.io> on 1/8/14.
|
||||
// Copyright (c) 2013, 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QTextBlock>
|
||||
#include <QtGui>
|
||||
#include <QtUiTools>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QFrame>
|
||||
|
||||
#include "Application.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) :
|
||||
QWidget(parent, Qt::Widget),
|
||||
_latestVersion(latestVersion),
|
||||
_downloadUrl(downloadURL) {
|
||||
|
||||
QUiLoader updateDialogLoader;
|
||||
QWidget* updateDialog;
|
||||
QFile updateDialogUi("resources/ui/updateDialog.ui");
|
||||
updateDialogUi.open(QFile::ReadOnly);
|
||||
updateDialog = updateDialogLoader.load(&updateDialogUi, this);
|
||||
|
||||
QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \
|
||||
Please download and install the most recent release to access the latest features and bug fixes.")
|
||||
.arg(Application::getInstance()->applicationVersion(), latestVersion);
|
||||
|
||||
|
||||
updateDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QPushButton* downloadButton = updateDialog->findChild<QPushButton*>("downloadButton");
|
||||
QPushButton* skipButton = updateDialog->findChild<QPushButton*>("skipButton");
|
||||
QPushButton* closeButton = updateDialog->findChild<QPushButton*>("closeButton");
|
||||
QLabel* updateContent = updateDialog->findChild<QLabel*>("updateContent");
|
||||
|
||||
updateContent->setText(updateRequired);
|
||||
|
||||
connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload()));
|
||||
connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip()));
|
||||
connect(closeButton, SIGNAL(released()), this, SLOT(close()));
|
||||
updateDialog->show();
|
||||
}
|
||||
|
||||
void UpdateDialog::handleDownload() {
|
||||
QDesktopServices::openUrl(_downloadUrl);
|
||||
Application::getInstance()->quit();
|
||||
}
|
||||
|
||||
void UpdateDialog::handleSkip() {
|
||||
Application::getInstance()->skipVersion(_latestVersion);
|
||||
this->close();
|
||||
}
|
29
interface/src/ui/UpdateDialog.h
Normal file
29
interface/src/ui/UpdateDialog.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// UpdateDialog.h
|
||||
// interface
|
||||
//
|
||||
// Created by Leonardo Murillo <leo@highfidelity.io> on 1/8/14.
|
||||
// Copyright (c) 2013, 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__UpdateDialog__
|
||||
#define __hifi__UpdateDialog__
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class UpdateDialog : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UpdateDialog(QWidget* parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL);
|
||||
|
||||
private:
|
||||
QString _latestVersion;
|
||||
QUrl _downloadUrl;
|
||||
|
||||
private slots:
|
||||
void handleDownload();
|
||||
void handleSkip();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__UpdateDialog__) */
|
|
@ -474,21 +474,21 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz
|
|||
}
|
||||
}
|
||||
|
||||
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity during the shortest expected frame period
|
||||
const float MIN_EXPECTED_FRAME_PERIOD = 0.005f; // 1/200th of a second
|
||||
const float MIN_VALID_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
|
||||
|
||||
void Particle::update() {
|
||||
uint64_t now = usecTimestampNow();
|
||||
float elapsed = static_cast<float>(now - _lastUpdated);
|
||||
void Particle::update(const uint64_t& now) {
|
||||
float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND);
|
||||
_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.05f / static_cast<float>(TREE_SCALE);
|
||||
bool isStillMoving = (velocityScalar > STILL_MOVING);
|
||||
const float REALLY_OLD = 30.0f; // 30 seconds
|
||||
bool isReallyOld = (getLifetime() > REALLY_OLD);
|
||||
float speed = glm::length(_velocity);
|
||||
bool isStopped = (speed < MIN_VALID_SPEED);
|
||||
const uint64_t REALLY_OLD = 30 * USECS_PER_SECOND; // 30 seconds
|
||||
bool isReallyOld = ((now - _created) > REALLY_OLD);
|
||||
bool isInHand = getInHand();
|
||||
bool shouldDie = getShouldDie() || (!isInHand && !isStillMoving && isReallyOld);
|
||||
bool shouldDie = getShouldDie() || (!isInHand && isStopped && isReallyOld);
|
||||
setShouldDie(shouldDie);
|
||||
|
||||
runUpdateScript(); // allow the javascript to alter our state
|
||||
|
|
|
@ -117,7 +117,7 @@ public:
|
|||
|
||||
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
|
||||
|
||||
void update();
|
||||
void update(const uint64_t& now);
|
||||
void collisionWithParticle(Particle* other);
|
||||
void collisionWithVoxel(VoxelDetail* voxel);
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <AbstractAudioInterface.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <HeadData.h>
|
||||
#include <HandData.h>
|
||||
|
||||
|
@ -43,7 +42,7 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr
|
|||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
// iterate the particles...
|
||||
std::vector<Particle>& particles = particleTreeElement->getParticles();
|
||||
QList<Particle>& particles = particleTreeElement->getParticles();
|
||||
uint16_t numberOfParticles = particles.size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
Particle* particle = &particles[i];
|
||||
|
@ -72,18 +71,18 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
|
|||
glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
|
||||
float radius = particle->getRadius() * (float)(TREE_SCALE);
|
||||
const float ELASTICITY = 0.4f;
|
||||
const float DAMPING = 0.0f;
|
||||
const float DAMPING = 0.05f;
|
||||
const float COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
CollisionInfo collisionInfo;
|
||||
VoxelDetail* voxelDetails = NULL;
|
||||
if (_voxels->findSpherePenetration(center, radius, penetration, (void**)&voxelDetails)) {
|
||||
if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) {
|
||||
|
||||
// let the particles run their collision scripts if they have them
|
||||
particle->collisionWithVoxel(voxelDetails);
|
||||
|
||||
penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, penetration, ELASTICITY, DAMPING);
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo);
|
||||
|
||||
delete voxelDetails; // cleanup returned details
|
||||
}
|
||||
|
@ -136,6 +135,10 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
|
|||
}
|
||||
}
|
||||
|
||||
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame
|
||||
const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second
|
||||
const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE);
|
||||
|
||||
void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
||||
|
||||
// particles that are in hand, don't collide with avatars
|
||||
|
@ -146,18 +149,18 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
|
||||
float radius = particle->getRadius() * (float)(TREE_SCALE);
|
||||
const float ELASTICITY = 0.9f;
|
||||
const float DAMPING = 0.0f;
|
||||
const float DAMPING = 0.1f;
|
||||
const float COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
|
||||
// first check the selfAvatar if set...
|
||||
if (_selfAvatar) {
|
||||
AvatarData* avatar = (AvatarData*)_selfAvatar;
|
||||
CollisionInfo collision;
|
||||
if (avatar->findSphereCollision(center, radius, collision)) {
|
||||
collision._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collision._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collision._penetration) < 0.f) {
|
||||
CollisionInfo collisionInfo;
|
||||
if (avatar->findSphereCollision(center, radius, collisionInfo)) {
|
||||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// only collide when particle and collision point are moving toward each other
|
||||
// (doing this prevents some "collision snagging" when particle penetrates the object)
|
||||
|
||||
|
@ -165,17 +168,20 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float SLOW_PADDLE_SPEED = 5.0e-5f;
|
||||
float attenuationFactor = glm::length(collision._addedVelocity) / SLOW_PADDLE_SPEED;
|
||||
float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collision._addedVelocity *= attenuationFactor;
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
// HACK END
|
||||
|
||||
collision._penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, collision._penetration, elasticity, DAMPING, collision._addedVelocity);
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, elasticity, damping, collisionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,36 +191,37 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
|
|||
//qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
|
||||
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
|
||||
AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
|
||||
CollisionInfo collision;
|
||||
if (avatar->findSphereCollision(center, radius, collision)) {
|
||||
collision._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collision._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collision._penetration) < 0.f) {
|
||||
CollisionInfo collisionInfo;
|
||||
if (avatar->findSphereCollision(center, radius, collisionInfo)) {
|
||||
collisionInfo._addedVelocity /= (float)(TREE_SCALE);
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
|
||||
if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
|
||||
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar.
|
||||
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
|
||||
// TODO: make this less hacky when we have more per-collision details
|
||||
float elasticity = ELASTICITY;
|
||||
float SLOW_PADDLE_SPEED = 5.0e-5f;
|
||||
float attenuationFactor = glm::length(collision._addedVelocity) / SLOW_PADDLE_SPEED;
|
||||
float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
|
||||
float damping = DAMPING;
|
||||
if (attenuationFactor < 1.f) {
|
||||
collision._addedVelocity *= attenuationFactor;
|
||||
collisionInfo._addedVelocity *= attenuationFactor;
|
||||
elasticity *= attenuationFactor;
|
||||
// NOTE: the math below keeps the damping piecewise continuous,
|
||||
// while ramping it up to 1.0 when attenuationFactor = 0
|
||||
damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING);
|
||||
}
|
||||
// HACK END
|
||||
|
||||
collision._penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, collision._penetration, ELASTICITY, DAMPING, collision._addedVelocity);
|
||||
collisionInfo._penetration /= (float)(TREE_SCALE);
|
||||
updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
|
||||
applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity
|
||||
void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration,
|
||||
float elasticity, float damping, const glm::vec3& addedVelocity) {
|
||||
void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo) {
|
||||
//
|
||||
// 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.
|
||||
|
@ -226,20 +233,19 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::
|
|||
glm::vec3 velocity = particle->getVelocity();
|
||||
|
||||
const float EPSILON = 0.0f;
|
||||
float velocityDotPenetration = glm::dot(velocity, penetration);
|
||||
if (velocityDotPenetration > EPSILON) {
|
||||
position -= penetration;
|
||||
static float HALTING_VELOCITY = 0.2f / (float)(TREE_SCALE);
|
||||
// cancel out the velocity component in the direction of penetration
|
||||
glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity;
|
||||
float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration);
|
||||
if (velocityDotPenetration < EPSILON) {
|
||||
// particle is moving into collision surface
|
||||
position -= collisionInfo._penetration;
|
||||
|
||||
float penetrationLength = glm::length(penetration);
|
||||
glm::vec3 direction = penetration / penetrationLength;
|
||||
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) {
|
||||
// If moving really slowly after a collision, and not applying forces, stop altogether
|
||||
velocity *= 0.f;
|
||||
if (glm::length(relativeVelocity) < HALTING_SPEED) {
|
||||
// static friction kicks in and particle moves with colliding object
|
||||
velocity = collisionInfo._addedVelocity;
|
||||
} else {
|
||||
glm::vec3 direction = glm::normalize(collisionInfo._penetration);
|
||||
velocity += glm::dot(relativeVelocity, direction) * (1.0f + elasticity) * direction; // dynamic reflection
|
||||
velocity += glm::clamp(damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction
|
||||
}
|
||||
}
|
||||
const bool wantDebug = false;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QtScript/QScriptEngine>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
|
@ -46,8 +47,7 @@ public:
|
|||
void updateCollisionWithVoxels(Particle* particle);
|
||||
void updateCollisionWithParticles(Particle* particle);
|
||||
void updateCollisionWithAvatars(Particle* particle);
|
||||
void applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping,
|
||||
const glm::vec3& addedVelocity = NO_ADDED_VELOCITY);
|
||||
void applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo);
|
||||
void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency);
|
||||
|
||||
private:
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
#include "ParticleTree.h"
|
||||
#include "ParticleTreeElement.h"
|
||||
|
||||
ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement() {
|
||||
ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement(), _particles(NULL) {
|
||||
init(octalCode);
|
||||
};
|
||||
|
||||
ParticleTreeElement::~ParticleTreeElement() {
|
||||
_voxelMemoryUsage -= sizeof(ParticleTreeElement);
|
||||
delete _particles;
|
||||
_particles = NULL;
|
||||
}
|
||||
|
||||
// This will be called primarily on addChildAt(), which means we're adding a child of our
|
||||
|
@ -31,6 +33,7 @@ OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) c
|
|||
|
||||
void ParticleTreeElement::init(unsigned char* octalCode) {
|
||||
OctreeElement::init(octalCode);
|
||||
_particles = new QList<Particle>;
|
||||
_voxelMemoryUsage += sizeof(ParticleTreeElement);
|
||||
}
|
||||
|
||||
|
@ -45,12 +48,12 @@ bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const
|
|||
bool success = true; // assume the best...
|
||||
|
||||
// write our particles out...
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
success = packetData->appendValue(numberOfParticles);
|
||||
|
||||
if (success) {
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
const Particle& particle = _particles[i];
|
||||
const Particle& particle = (*_particles)[i];
|
||||
success = particle.appendParticleData(packetData);
|
||||
if (!success) {
|
||||
break;
|
||||
|
@ -62,35 +65,40 @@ bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const
|
|||
|
||||
void ParticleTreeElement::update(ParticleTreeUpdateArgs& args) {
|
||||
markWithChangedTime();
|
||||
// TODO: early exit when _particles is empty
|
||||
|
||||
// update our contained particles
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
_particles[i].update();
|
||||
QList<Particle>::iterator particleItr = _particles->begin();
|
||||
while(particleItr != _particles->end()) {
|
||||
Particle& particle = (*particleItr);
|
||||
particle.update(_lastChanged);
|
||||
|
||||
// If the particle wants to die, or if it's left our bounding box, then move it
|
||||
// into the arguments moving particles. These will be added back or deleted completely
|
||||
if (_particles[i].getShouldDie() || !_box.contains(_particles[i].getPosition())) {
|
||||
args._movingParticles.push_back(_particles[i]);
|
||||
if (particle.getShouldDie() || !_box.contains(particle.getPosition())) {
|
||||
args._movingParticles.push_back(particle);
|
||||
|
||||
// erase this particle
|
||||
_particles.erase(_particles.begin()+i);
|
||||
|
||||
// reduce our index since we just removed this item
|
||||
i--;
|
||||
numberOfParticles--;
|
||||
particleItr = _particles->erase(particleItr);
|
||||
} else {
|
||||
++particleItr;
|
||||
}
|
||||
}
|
||||
// TODO: if _particles is empty after while loop consider freeing memory in _particles if
|
||||
// internal array is too big (QList internal array does not decrease size except in dtor and
|
||||
// assignment operator). Otherwise _particles could become a "resource leak" for large
|
||||
// roaming piles of particles.
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const {
|
||||
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
glm::vec3 particleCenter = _particles[i].getPosition();
|
||||
float particleRadius = _particles[i].getRadius();
|
||||
QList<Particle>::iterator particleItr = _particles->begin();
|
||||
QList<Particle>::const_iterator particleEnd = _particles->end();
|
||||
while(particleItr != particleEnd) {
|
||||
Particle& particle = (*particleItr);
|
||||
glm::vec3 particleCenter = particle.getPosition();
|
||||
float particleRadius = particle.getRadius();
|
||||
|
||||
// don't penetrate yourself
|
||||
if (particleCenter == center && particleRadius == radius) {
|
||||
|
@ -102,23 +110,28 @@ bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float r
|
|||
const bool IN_HAND_PARTICLES_DONT_COLLIDE = false;
|
||||
if (IN_HAND_PARTICLES_DONT_COLLIDE) {
|
||||
// don't penetrate if the particle is "inHand" -- they don't collide
|
||||
if (_particles[i].getInHand()) {
|
||||
return false;
|
||||
if (particle.getInHand()) {
|
||||
++particleItr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (findSphereSpherePenetration(center, radius, particleCenter, particleRadius, penetration)) {
|
||||
*penetratedObject = (void*)&_particles[i];
|
||||
// return true on first valid particle penetration
|
||||
*penetratedObject = (void*)(&particle);
|
||||
return true;
|
||||
}
|
||||
++particleItr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::containsParticle(const Particle& particle) const {
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
// TODO: remove this method and force callers to use getParticleWithID() instead
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
uint32_t particleID = particle.getID();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if (_particles[i].getID() == particle.getID()) {
|
||||
if ((*_particles)[i].getID() == particleID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -126,13 +139,17 @@ bool ParticleTreeElement::containsParticle(const Particle& particle) const {
|
|||
}
|
||||
|
||||
bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
||||
// NOTE: this method must first lookup the particle by ID, hence it is O(N)
|
||||
// and "particle is not found" is worst-case (full N) but maybe we don't care?
|
||||
// (guaranteed that num particles per elemen is small?)
|
||||
const bool wantDebug = false;
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if (_particles[i].getID() == particle.getID()) {
|
||||
int difference = _particles[i].getLastUpdated() - particle.getLastUpdated();
|
||||
bool changedOnServer = _particles[i].getLastEdited() < particle.getLastEdited();
|
||||
bool localOlder = _particles[i].getLastUpdated() < particle.getLastUpdated();
|
||||
Particle& thisParticle = (*_particles)[i];
|
||||
if (thisParticle.getID() == particle.getID()) {
|
||||
int difference = thisParticle.getLastUpdated() - particle.getLastUpdated();
|
||||
bool changedOnServer = thisParticle.getLastEdited() < particle.getLastEdited();
|
||||
bool localOlder = thisParticle.getLastUpdated() < particle.getLastUpdated();
|
||||
if (changedOnServer || localOlder) {
|
||||
if (wantDebug) {
|
||||
printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
|
||||
|
@ -140,7 +157,7 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
|||
(localOlder ? "OLDER" : "NEWER"),
|
||||
difference, debug::valueOf(particle.isNewlyCreated()) );
|
||||
}
|
||||
_particles[i].copyChangedProperties(particle);
|
||||
thisParticle.copyChangedProperties(particle);
|
||||
} else {
|
||||
if (wantDebug) {
|
||||
printf(">>> IGNORING SERVER!!! Would've caused jutter! <<< "
|
||||
|
@ -159,22 +176,23 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
|
|||
const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const {
|
||||
const Particle* closestParticle = NULL;
|
||||
float closestParticleDistance = FLT_MAX;
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
float distanceToParticle = glm::distance(position, _particles[i].getPosition());
|
||||
float distanceToParticle = glm::distance(position, (*_particles)[i].getPosition());
|
||||
if (distanceToParticle < closestParticleDistance) {
|
||||
closestParticle = &_particles[i];
|
||||
closestParticle = &(*_particles)[i];
|
||||
}
|
||||
}
|
||||
return closestParticle;
|
||||
}
|
||||
|
||||
const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
||||
// NOTE: this lookup is O(N) but maybe we don't care? (guaranteed that num particles per elemen is small?)
|
||||
const Particle* foundParticle = NULL;
|
||||
uint16_t numberOfParticles = _particles.size();
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if (_particles[i].getID() == id) {
|
||||
foundParticle = &_particles[i];
|
||||
if ((*_particles)[i].getID() == id) {
|
||||
foundParticle = &(*_particles)[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +247,7 @@ bool ParticleTreeElement::collapseChildren() {
|
|||
|
||||
|
||||
void ParticleTreeElement::storeParticle(const Particle& particle, Node* senderNode) {
|
||||
_particles.push_back(particle);
|
||||
_particles->push_back(particle);
|
||||
markWithChangedTime();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
#ifndef __hifi__ParticleTreeElement__
|
||||
#define __hifi__ParticleTreeElement__
|
||||
|
||||
#include <vector>
|
||||
//#include <vector>
|
||||
|
||||
#include <OctreeElement.h>
|
||||
#include <QList>
|
||||
|
||||
#include "Particle.h"
|
||||
#include "ParticleTree.h"
|
||||
|
@ -22,7 +23,7 @@ class ParticleTreeElement;
|
|||
|
||||
class ParticleTreeUpdateArgs {
|
||||
public:
|
||||
std::vector<Particle> _movingParticles;
|
||||
QList<Particle> _movingParticles;
|
||||
};
|
||||
|
||||
class ParticleTreeElement : public OctreeElement {
|
||||
|
@ -34,7 +35,6 @@ class ParticleTreeElement : public OctreeElement {
|
|||
|
||||
public:
|
||||
virtual ~ParticleTreeElement();
|
||||
virtual void init(unsigned char * octalCode);
|
||||
|
||||
// type safe versions of OctreeElement methods
|
||||
ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); }
|
||||
|
@ -79,9 +79,9 @@ public:
|
|||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
const std::vector<Particle>& getParticles() const { return _particles; }
|
||||
std::vector<Particle>& getParticles() { return _particles; }
|
||||
bool hasParticles() const { return _particles.size() > 0; }
|
||||
const QList<Particle>& getParticles() const { return *_particles; }
|
||||
QList<Particle>& getParticles() { return *_particles; }
|
||||
bool hasParticles() const { return _particles->size() > 0; }
|
||||
|
||||
void update(ParticleTreeUpdateArgs& args);
|
||||
void setTree(ParticleTree* tree) { _myTree = tree; }
|
||||
|
@ -93,10 +93,12 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
virtual void init(unsigned char * octalCode);
|
||||
|
||||
void storeParticle(const Particle& particle, Node* senderNode = NULL);
|
||||
|
||||
ParticleTree* _myTree;
|
||||
std::vector<Particle> _particles;
|
||||
QList<Particle>* _particles;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleTreeElement__) */
|
||||
|
|
|
@ -38,17 +38,20 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng
|
|||
return soundScriptValue;
|
||||
}
|
||||
|
||||
ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems,
|
||||
const char* scriptMenuName, AbstractMenuInterface* menu,
|
||||
ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, AbstractMenuInterface* menu,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface) {
|
||||
_scriptContents = scriptContents;
|
||||
_isFinished = false;
|
||||
_isRunning = false;
|
||||
_isInitialized = false;
|
||||
_fileNameString = fileNameString;
|
||||
|
||||
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
|
||||
const char* scriptMenuName = fileNameAscii.data();
|
||||
|
||||
// some clients will use these menu features
|
||||
_wantMenuItems = wantMenuItems;
|
||||
if (scriptMenuName) {
|
||||
if (!fileNameString.isEmpty()) {
|
||||
_scriptMenuName = "Stop ";
|
||||
_scriptMenuName.append(scriptMenuName);
|
||||
_scriptMenuName.append(QString(" [%1]").arg(_scriptNumber));
|
||||
|
@ -233,9 +236,11 @@ void ScriptEngine::run() {
|
|||
thread()->quit();
|
||||
}
|
||||
|
||||
emit finished();
|
||||
emit finished(_fileNameString);
|
||||
|
||||
_isRunning = false;
|
||||
}
|
||||
|
||||
void ScriptEngine::stop() {
|
||||
_isFinished = true;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class ScriptEngine : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
|
||||
const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL,
|
||||
const QString& scriptMenuName = QString(""), AbstractMenuInterface* menu = NULL,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
||||
|
||||
~ScriptEngine();
|
||||
|
@ -58,7 +58,7 @@ signals:
|
|||
void willSendAudioDataCallback();
|
||||
void willSendVisualDataCallback();
|
||||
void scriptEnding();
|
||||
void finished();
|
||||
void finished(const QString& fileNameString);
|
||||
|
||||
protected:
|
||||
QString _scriptContents;
|
||||
|
@ -74,6 +74,7 @@ private:
|
|||
AudioScriptingInterface _audioScriptingInterface;
|
||||
bool _wantMenuItems;
|
||||
QString _scriptMenuName;
|
||||
QString _fileNameString;
|
||||
AbstractMenuInterface* _menu;
|
||||
static int _scriptNumber;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue