mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 16:50:43 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into desktopEquip
This commit is contained in:
commit
3b251c0c49
26 changed files with 305 additions and 250 deletions
|
@ -17,6 +17,7 @@ StackView {
|
||||||
id: stack
|
id: stack
|
||||||
initialItem: inputConfiguration
|
initialItem: inputConfiguration
|
||||||
property alias messageVisible: imageMessageBox.visible
|
property alias messageVisible: imageMessageBox.visible
|
||||||
|
property alias selectedPlugin: box.currentText
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: inputConfiguration
|
id: inputConfiguration
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
|
@ -105,7 +105,7 @@ Rectangle {
|
||||||
|
|
||||||
RalewayBold {
|
RalewayBold {
|
||||||
size: 12
|
size: 12
|
||||||
text: "Vive HMD"
|
text: stack.selectedPlugin + " HMD"
|
||||||
color: hifi.colors.lightGrayText
|
color: hifi.colors.lightGrayText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,7 +809,7 @@ Rectangle {
|
||||||
RalewayBold {
|
RalewayBold {
|
||||||
id: viveDesktopText
|
id: viveDesktopText
|
||||||
size: 10
|
size: 10
|
||||||
text: "Use Vive devices in desktop mode"
|
text: "Use " + stack.selectedPlugin + " devices in desktop mode"
|
||||||
color: hifi.colors.white
|
color: hifi.colors.white
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
|
|
|
@ -4741,7 +4741,7 @@ void Application::updateLOD(float deltaTime) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::pushPostUpdateLambda(void* key, std::function<void()> func) {
|
void Application::pushPostUpdateLambda(void* key, const std::function<void()>& func) {
|
||||||
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
|
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
|
||||||
_postUpdateLambdas[key] = func;
|
_postUpdateLambdas[key] = func;
|
||||||
}
|
}
|
||||||
|
@ -7351,7 +7351,7 @@ void Application::windowMinimizedChanged(bool minimized) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::postLambdaEvent(std::function<void()> f) {
|
void Application::postLambdaEvent(const std::function<void()>& f) {
|
||||||
if (this->thread() == QThread::currentThread()) {
|
if (this->thread() == QThread::currentThread()) {
|
||||||
f();
|
f();
|
||||||
} else {
|
} else {
|
||||||
|
@ -7359,6 +7359,15 @@ void Application::postLambdaEvent(std::function<void()> f) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::sendLambdaEvent(const std::function<void()>& f) {
|
||||||
|
if (this->thread() == QThread::currentThread()) {
|
||||||
|
f();
|
||||||
|
} else {
|
||||||
|
LambdaEvent event(f);
|
||||||
|
QCoreApplication::sendEvent(this, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Application::initPlugins(const QStringList& arguments) {
|
void Application::initPlugins(const QStringList& arguments) {
|
||||||
QCommandLineOption display("display", "Preferred displays", "displays");
|
QCommandLineOption display("display", "Preferred displays", "displays");
|
||||||
QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays");
|
QCommandLineOption disableDisplays("disable-displays", "Displays to disable", "displays");
|
||||||
|
|
|
@ -136,7 +136,8 @@ public:
|
||||||
Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted);
|
Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runningMarkerExisted);
|
||||||
~Application();
|
~Application();
|
||||||
|
|
||||||
void postLambdaEvent(std::function<void()> f) override;
|
void postLambdaEvent(const std::function<void()>& f) override;
|
||||||
|
void sendLambdaEvent(const std::function<void()>& f) override;
|
||||||
|
|
||||||
QString getPreviousScriptLocation();
|
QString getPreviousScriptLocation();
|
||||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||||
|
@ -240,7 +241,7 @@ public:
|
||||||
|
|
||||||
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
|
||||||
|
|
||||||
bool isAboutToQuit() const override { return _aboutToQuit; }
|
bool isAboutToQuit() const { return _aboutToQuit; }
|
||||||
bool isPhysicsEnabled() const { return _physicsEnabled; }
|
bool isPhysicsEnabled() const { return _physicsEnabled; }
|
||||||
|
|
||||||
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
||||||
|
@ -264,7 +265,7 @@ public:
|
||||||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||||
|
|
||||||
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) override;
|
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
|
||||||
|
|
||||||
void updateMyAvatarLookAtPosition();
|
void updateMyAvatarLookAtPosition();
|
||||||
|
|
||||||
|
|
|
@ -65,14 +65,10 @@ const QString Web3DOverlay::TYPE = "web3d";
|
||||||
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
const QString Web3DOverlay::QML = "Web3DOverlay.qml";
|
||||||
|
|
||||||
static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) {
|
static auto qmlSurfaceDeleter = [](OffscreenQmlSurface* surface) {
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([surface] {
|
AbstractViewStateInterface::instance()->sendLambdaEvent([surface] {
|
||||||
if (AbstractViewStateInterface::instance()->isAboutToQuit()) {
|
|
||||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||||
// if the application has already stopped its event loop, delete must be explicit
|
// if the application has already stopped its event loop, delete must be explicit
|
||||||
delete surface;
|
delete surface;
|
||||||
} else {
|
|
||||||
surface->deleteLater();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
|
|
||||||
#include "EntitiesRendererLogging.h"
|
#include "EntitiesRendererLogging.h"
|
||||||
|
#include <NetworkingConstants.h>
|
||||||
|
|
||||||
using namespace render;
|
using namespace render;
|
||||||
using namespace render::entities;
|
using namespace render::entities;
|
||||||
|
@ -45,6 +45,7 @@ static int DEFAULT_MAX_FPS = 10;
|
||||||
static int YOUTUBE_MAX_FPS = 30;
|
static int YOUTUBE_MAX_FPS = 30;
|
||||||
|
|
||||||
static QTouchDevice _touchDevice;
|
static QTouchDevice _touchDevice;
|
||||||
|
static const char* URL_PROPERTY = "url";
|
||||||
|
|
||||||
WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) {
|
WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString& urlString) {
|
||||||
if (urlString.isEmpty()) {
|
if (urlString.isEmpty()) {
|
||||||
|
@ -52,7 +53,7 @@ WebEntityRenderer::ContentType WebEntityRenderer::getContentType(const QString&
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUrl url(urlString);
|
const QUrl url(urlString);
|
||||||
if (url.scheme() == "http" || url.scheme() == "https" ||
|
if (url.scheme() == URL_SCHEME_HTTP || url.scheme() == URL_SCHEME_HTTPS ||
|
||||||
urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) {
|
urlString.toLower().endsWith(".htm") || urlString.toLower().endsWith(".html")) {
|
||||||
return ContentType::HtmlContent;
|
return ContentType::HtmlContent;
|
||||||
}
|
}
|
||||||
|
@ -164,6 +165,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
||||||
if (urlChanged) {
|
if (urlChanged) {
|
||||||
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
if (newContentType != ContentType::HtmlContent || currentContentType != ContentType::HtmlContent) {
|
||||||
destroyWebSurface();
|
destroyWebSurface();
|
||||||
|
// If we destroyed the surface, the URL change will be implicitly handled by the re-creation
|
||||||
|
urlChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
|
@ -185,8 +188,8 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlChanged) {
|
if (urlChanged && _contentType == ContentType::HtmlContent) {
|
||||||
_webSurface->getRootItem()->setProperty("url", _lastSourceUrl);
|
_webSurface->getRootItem()->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_contextPosition != entity->getWorldPosition()) {
|
if (_contextPosition != entity->getWorldPosition()) {
|
||||||
|
@ -254,6 +257,14 @@ bool WebEntityRenderer::hasWebSurface() {
|
||||||
return (bool)_webSurface && _webSurface->getRootItem();
|
return (bool)_webSurface && _webSurface->getRootItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const auto WebSurfaceDeleter = [](OffscreenQmlSurface* webSurface) {
|
||||||
|
AbstractViewStateInterface::instance()->sendLambdaEvent([webSurface] {
|
||||||
|
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
||||||
|
// if the application has already stopped its event loop, delete must be explicit
|
||||||
|
delete webSurface;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
|
if (_currentWebCount >= MAX_CONCURRENT_WEB_VIEWS) {
|
||||||
qWarning() << "Too many concurrent web views to create new view";
|
qWarning() << "Too many concurrent web views to create new view";
|
||||||
|
@ -261,20 +272,9 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
++_currentWebCount;
|
++_currentWebCount;
|
||||||
auto deleter = [](OffscreenQmlSurface* webSurface) {
|
|
||||||
AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] {
|
|
||||||
if (AbstractViewStateInterface::instance()->isAboutToQuit()) {
|
|
||||||
// WebEngineView may run other threads (wasapi), so they must be deleted for a clean shutdown
|
|
||||||
// if the application has already stopped its event loop, delete must be explicit
|
|
||||||
delete webSurface;
|
|
||||||
} else {
|
|
||||||
webSurface->deleteLater();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME use the surface cache instead of explicit creation
|
// FIXME use the surface cache instead of explicit creation
|
||||||
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), WebSurfaceDeleter);
|
||||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||||
// and the current rendering load)
|
// and the current rendering load)
|
||||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||||
|
@ -302,7 +302,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||||
}
|
}
|
||||||
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
_webSurface->load("controls/WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
||||||
item->setProperty("url", _lastSourceUrl);
|
item->setProperty(URL_PROPERTY, _lastSourceUrl);
|
||||||
});
|
});
|
||||||
} else if (_contentType == ContentType::QmlContent) {
|
} else if (_contentType == ContentType::QmlContent) {
|
||||||
_webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) {
|
_webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) {
|
||||||
|
@ -327,6 +327,11 @@ void WebEntityRenderer::destroyWebSurface() {
|
||||||
if (webSurface) {
|
if (webSurface) {
|
||||||
--_currentWebCount;
|
--_currentWebCount;
|
||||||
QQuickItem* rootItem = webSurface->getRootItem();
|
QQuickItem* rootItem = webSurface->getRootItem();
|
||||||
|
// Explicitly set the web URL to an empty string, in an effort to get a
|
||||||
|
// faster shutdown of any chromium processes interacting with audio
|
||||||
|
if (rootItem && _contentType == ContentType::HtmlContent) {
|
||||||
|
rootItem->setProperty(URL_PROPERTY, "");
|
||||||
|
}
|
||||||
|
|
||||||
if (rootItem && rootItem->objectName() == "tabletRoot") {
|
if (rootItem && rootItem->objectName() == "tabletRoot") {
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
|
|
|
@ -69,7 +69,6 @@ private:
|
||||||
graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); }
|
graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); }
|
||||||
graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; }
|
graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; }
|
||||||
|
|
||||||
bool _needsInitialSimulation{ true };
|
|
||||||
glm::vec3 _lastPosition;
|
glm::vec3 _lastPosition;
|
||||||
glm::vec3 _lastDimensions;
|
glm::vec3 _lastDimensions;
|
||||||
glm::quat _lastRotation;
|
glm::quat _lastRotation;
|
||||||
|
|
|
@ -216,7 +216,6 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
||||||
|
|
||||||
|
|
||||||
bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
|
bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
|
||||||
|
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
bool overwriteLocalData = true;
|
bool overwriteLocalData = true;
|
||||||
bool somethingChanged = false;
|
bool somethingChanged = false;
|
||||||
|
@ -360,3 +359,21 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
|
||||||
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold);
|
READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold);
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AnimationPropertyGroup::getNumFrames() const {
|
||||||
|
return _lastFrame - _firstFrame + 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AnimationPropertyGroup::computeLoopedFrame(float frame) const {
|
||||||
|
float numFrames = getNumFrames();
|
||||||
|
if (numFrames > 1.0f) {
|
||||||
|
frame = getFirstFrame() + fmodf(frame - getFirstFrame(), numFrames);
|
||||||
|
} else {
|
||||||
|
frame = getFirstFrame();
|
||||||
|
}
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationPropertyGroup::isValidAndRunning() const {
|
||||||
|
return getRunning() && (getFPS() > 0.0f) && (getNumFrames() > 1.0f) && !(getURL().isEmpty());
|
||||||
|
}
|
||||||
|
|
|
@ -77,6 +77,10 @@ public:
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
bool& somethingChanged) override;
|
bool& somethingChanged) override;
|
||||||
|
|
||||||
|
float getNumFrames() const;
|
||||||
|
float computeLoopedFrame(float frame) const;
|
||||||
|
bool isValidAndRunning() const;
|
||||||
|
|
||||||
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, "");
|
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, "");
|
||||||
DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f);
|
DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f);
|
||||||
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f);
|
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f);
|
||||||
|
|
|
@ -597,7 +597,7 @@ protected:
|
||||||
//
|
//
|
||||||
|
|
||||||
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
|
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
|
||||||
uint32_t _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
std::atomic_uint _flags { 0 }; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
||||||
|
|
||||||
// these backpointers are only ever set/cleared by friends:
|
// these backpointers are only ever set/cleared by friends:
|
||||||
EntityTreeElementPointer _element; // set by EntityTreeElement
|
EntityTreeElementPointer _element; // set by EntityTreeElement
|
||||||
|
|
|
@ -82,12 +82,12 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
|
||||||
|
|
||||||
bool somethingChangedInAnimations = _animationProperties.setProperties(properties);
|
withWriteLock([&] {
|
||||||
|
AnimationPropertyGroup animationProperties = _animationProperties;
|
||||||
if (somethingChangedInAnimations) {
|
animationProperties.setProperties(properties);
|
||||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
bool somethingChangedInAnimations = applyNewAnimationProperties(animationProperties);
|
||||||
}
|
|
||||||
somethingChanged = somethingChanged || somethingChangedInAnimations;
|
somethingChanged = somethingChanged || somethingChangedInAnimations;
|
||||||
|
});
|
||||||
|
|
||||||
if (somethingChanged) {
|
if (somethingChanged) {
|
||||||
bool wantDebug = false;
|
bool wantDebug = false;
|
||||||
|
@ -118,12 +118,16 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||||
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||||
|
|
||||||
|
// grab a local copy of _animationProperties to avoid multiple locks
|
||||||
int bytesFromAnimation;
|
int bytesFromAnimation;
|
||||||
withWriteLock([&] {
|
withReadLock([&] {
|
||||||
// Note: since we've associated our _animationProperties with our _animationLoop, the readEntitySubclassDataFromBuffer()
|
AnimationPropertyGroup animationProperties = _animationProperties;
|
||||||
// will automatically read into the animation loop
|
bytesFromAnimation = animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||||
bytesFromAnimation = _animationProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
|
||||||
propertyFlags, overwriteLocalData, animationPropertiesChanged);
|
propertyFlags, overwriteLocalData, animationPropertiesChanged);
|
||||||
|
if (animationPropertiesChanged) {
|
||||||
|
applyNewAnimationProperties(animationProperties);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
bytesRead += bytesFromAnimation;
|
bytesRead += bytesFromAnimation;
|
||||||
|
@ -131,11 +135,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||||
|
|
||||||
if (animationPropertiesChanged) {
|
|
||||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
|
||||||
somethingChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector<bool>, setJointRotationsSet);
|
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS_SET, QVector<bool>, setJointRotationsSet);
|
||||||
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector<glm::quat>, setJointRotations);
|
READ_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, QVector<glm::quat>, setJointRotations);
|
||||||
READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
READ_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
||||||
|
@ -194,98 +193,38 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
||||||
|
|
||||||
// added update function back for property fix
|
// added update function back for property fix
|
||||||
void ModelEntityItem::update(const quint64& now) {
|
void ModelEntityItem::update(const quint64& now) {
|
||||||
|
assert(_lastAnimated > 0);
|
||||||
|
|
||||||
{
|
// increment timestamp before checking "hold"
|
||||||
auto currentAnimationProperties = this->getAnimationProperties();
|
|
||||||
|
|
||||||
if (_previousAnimationProperties != currentAnimationProperties) {
|
|
||||||
withWriteLock([&] {
|
|
||||||
// if we hit start animation or change the first or last frame then restart the animation
|
|
||||||
if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) ||
|
|
||||||
(currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) ||
|
|
||||||
(currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) {
|
|
||||||
|
|
||||||
// when we start interface and the property is are set then the current frame is initialized to -1
|
|
||||||
if (_currentFrame < 0) {
|
|
||||||
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
|
|
||||||
_currentFrame = currentAnimationProperties.getCurrentFrame();
|
|
||||||
setAnimationCurrentFrame(_currentFrame);
|
|
||||||
} else {
|
|
||||||
_lastAnimated = usecTimestampNow();
|
|
||||||
_currentFrame = currentAnimationProperties.getFirstFrame();
|
|
||||||
setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame());
|
|
||||||
}
|
|
||||||
} else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) {
|
|
||||||
_currentFrame = currentAnimationProperties.getFirstFrame();
|
|
||||||
setAnimationCurrentFrame(_currentFrame);
|
|
||||||
} else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) {
|
|
||||||
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
|
|
||||||
_currentFrame = currentAnimationProperties.getCurrentFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
_previousAnimationProperties = this->getAnimationProperties();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAnimatingSomething()) {
|
|
||||||
if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) {
|
|
||||||
updateFrameCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItem::update(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ModelEntityItem::needsToCallUpdate() const {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelEntityItem::updateFrameCount() {
|
|
||||||
|
|
||||||
if (_currentFrame < 0.0f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_lastAnimated) {
|
|
||||||
_lastAnimated = usecTimestampNow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto now = usecTimestampNow();
|
|
||||||
|
|
||||||
// update the interval since the last animation.
|
|
||||||
auto interval = now - _lastAnimated;
|
auto interval = now - _lastAnimated;
|
||||||
_lastAnimated = now;
|
_lastAnimated = now;
|
||||||
|
|
||||||
// if fps is negative then increment timestamp and return.
|
// grab a local copy of _animationProperties to avoid multiple locks
|
||||||
if (getAnimationFPS() < 0.0f) {
|
auto animationProperties = getAnimationProperties();
|
||||||
|
|
||||||
|
// bail on "hold"
|
||||||
|
if (animationProperties.getHold()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1;
|
// increment animation frame
|
||||||
|
_currentFrame += (animationProperties.getFPS() * ((float)interval) / (float)USECS_PER_SECOND);
|
||||||
if (!getAnimationHold() && getAnimationIsPlaying()) {
|
if (_currentFrame > animationProperties.getLastFrame() + 1.0f) {
|
||||||
float deltaTime = (float)interval / (float)USECS_PER_SECOND;
|
if (animationProperties.getLoop()) {
|
||||||
_currentFrame += (deltaTime * getAnimationFPS());
|
_currentFrame = animationProperties.computeLoopedFrame(_currentFrame);
|
||||||
if (_currentFrame > getAnimationLastFrame() + 1) {
|
|
||||||
if (getAnimationLoop() && getAnimationFirstFrame() != getAnimationLastFrame()) {
|
|
||||||
_currentFrame = getAnimationFirstFrame() + (int)(_currentFrame - getAnimationFirstFrame()) % updatedFrameCount;
|
|
||||||
} else {
|
} else {
|
||||||
_currentFrame = getAnimationLastFrame();
|
_currentFrame = animationProperties.getLastFrame();
|
||||||
}
|
}
|
||||||
} else if (_currentFrame < getAnimationFirstFrame()) {
|
} else if (_currentFrame < animationProperties.getFirstFrame()) {
|
||||||
if (getAnimationFirstFrame() < 0) {
|
if (animationProperties.getFirstFrame() < 0.0f) {
|
||||||
_currentFrame = 0;
|
_currentFrame = 0.0f;
|
||||||
} else {
|
} else {
|
||||||
_currentFrame = getAnimationFirstFrame();
|
_currentFrame = animationProperties.getFirstFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// qCDebug(entities) << "in update frame " << _currentFrame;
|
|
||||||
setAnimationCurrentFrame(_currentFrame);
|
setAnimationCurrentFrame(_currentFrame);
|
||||||
}
|
|
||||||
|
EntityItem::update(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelEntityItem::debugDump() const {
|
void ModelEntityItem::debugDump() const {
|
||||||
|
@ -361,67 +300,61 @@ void ModelEntityItem::setAnimationURL(const QString& url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelEntityItem::setAnimationSettings(const QString& value) {
|
void ModelEntityItem::setAnimationSettings(const QString& value) {
|
||||||
|
// NOTE: this method only called for old bitstream format
|
||||||
|
|
||||||
|
withWriteLock([&] {
|
||||||
|
auto animationProperties = _animationProperties;
|
||||||
|
|
||||||
// the animations setting is a JSON string that may contain various animation settings.
|
// the animations setting is a JSON string that may contain various animation settings.
|
||||||
// if it includes fps, currentFrame, or running, those values will be parsed out and
|
// if it includes fps, currentFrame, or running, those values will be parsed out and
|
||||||
// will over ride the regular animation settings
|
// will over ride the regular animation settings
|
||||||
|
|
||||||
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
|
||||||
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
QJsonObject settingsAsJsonObject = settingsAsJson.object();
|
||||||
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
QVariantMap settingsMap = settingsAsJsonObject.toVariantMap();
|
||||||
if (settingsMap.contains("fps")) {
|
if (settingsMap.contains("fps")) {
|
||||||
float fps = settingsMap["fps"].toFloat();
|
float fps = settingsMap["fps"].toFloat();
|
||||||
setAnimationFPS(fps);
|
animationProperties.setFPS(fps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// old settings used frameIndex
|
// old settings used frameIndex
|
||||||
if (settingsMap.contains("frameIndex")) {
|
if (settingsMap.contains("frameIndex")) {
|
||||||
float currentFrame = settingsMap["frameIndex"].toFloat();
|
float currentFrame = settingsMap["frameIndex"].toFloat();
|
||||||
#ifdef WANT_DEBUG
|
animationProperties.setCurrentFrame(currentFrame);
|
||||||
if (!getAnimationURL().isEmpty()) {
|
|
||||||
qCDebug(entities) << "ModelEntityItem::setAnimationSettings() calling setAnimationFrameIndex()...";
|
|
||||||
qCDebug(entities) << " model URL:" << getModelURL();
|
|
||||||
qCDebug(entities) << " animation URL:" << getAnimationURL();
|
|
||||||
qCDebug(entities) << " settings:" << value;
|
|
||||||
qCDebug(entities) << " settingsMap[frameIndex]:" << settingsMap["frameIndex"];
|
|
||||||
qCDebug(entities" currentFrame: %20.5f", currentFrame);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
setAnimationCurrentFrame(currentFrame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsMap.contains("running")) {
|
if (settingsMap.contains("running")) {
|
||||||
bool running = settingsMap["running"].toBool();
|
bool running = settingsMap["running"].toBool();
|
||||||
if (running != getAnimationIsPlaying()) {
|
if (running != animationProperties.getRunning()) {
|
||||||
setAnimationIsPlaying(running);
|
animationProperties.setRunning(running);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsMap.contains("firstFrame")) {
|
if (settingsMap.contains("firstFrame")) {
|
||||||
float firstFrame = settingsMap["firstFrame"].toFloat();
|
float firstFrame = settingsMap["firstFrame"].toFloat();
|
||||||
setAnimationFirstFrame(firstFrame);
|
animationProperties.setFirstFrame(firstFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsMap.contains("lastFrame")) {
|
if (settingsMap.contains("lastFrame")) {
|
||||||
float lastFrame = settingsMap["lastFrame"].toFloat();
|
float lastFrame = settingsMap["lastFrame"].toFloat();
|
||||||
setAnimationLastFrame(lastFrame);
|
animationProperties.setLastFrame(lastFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsMap.contains("loop")) {
|
if (settingsMap.contains("loop")) {
|
||||||
bool loop = settingsMap["loop"].toBool();
|
bool loop = settingsMap["loop"].toBool();
|
||||||
setAnimationLoop(loop);
|
animationProperties.setLoop(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsMap.contains("hold")) {
|
if (settingsMap.contains("hold")) {
|
||||||
bool hold = settingsMap["hold"].toBool();
|
bool hold = settingsMap["hold"].toBool();
|
||||||
setAnimationHold(hold);
|
animationProperties.setHold(hold);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsMap.contains("allowTranslation")) {
|
if (settingsMap.contains("allowTranslation")) {
|
||||||
bool allowTranslation = settingsMap["allowTranslation"].toBool();
|
bool allowTranslation = settingsMap["allowTranslation"].toBool();
|
||||||
setAnimationAllowTranslation(allowTranslation);
|
animationProperties.setAllowTranslation(allowTranslation);
|
||||||
}
|
}
|
||||||
_flags |= Simulation::DIRTY_UPDATEABLE;
|
applyNewAnimationProperties(animationProperties);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelEntityItem::setAnimationIsPlaying(bool value) {
|
void ModelEntityItem::setAnimationIsPlaying(bool value) {
|
||||||
|
@ -713,11 +646,45 @@ float ModelEntityItem::getAnimationFPS() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ModelEntityItem::isAnimatingSomething() const {
|
bool ModelEntityItem::isAnimatingSomething() const {
|
||||||
return resultWithReadLock<bool>([&] {
|
return resultWithReadLock<bool>([&] {
|
||||||
return !_animationProperties.getURL().isEmpty() &&
|
return _animationProperties.isValidAndRunning();
|
||||||
_animationProperties.getRunning() &&
|
|
||||||
(_animationProperties.getFPS() != 0.0f);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProperties) {
|
||||||
|
// call applyNewAnimationProperties() whenever trying to update _animationProperties
|
||||||
|
// because there is some reset logic we need to do whenever the animation "config" properties change
|
||||||
|
// NOTE: this private method is always called inside withWriteLock()
|
||||||
|
|
||||||
|
// if we hit start animation or change the first or last frame then restart the animation
|
||||||
|
if ((newProperties.getFirstFrame() != _animationProperties.getFirstFrame()) ||
|
||||||
|
(newProperties.getLastFrame() != _animationProperties.getLastFrame()) ||
|
||||||
|
(newProperties.getRunning() && !_animationProperties.getRunning())) {
|
||||||
|
|
||||||
|
// when we start interface and the property is are set then the current frame is initialized to -1
|
||||||
|
if (_currentFrame < 0.0f) {
|
||||||
|
// don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set
|
||||||
|
_currentFrame = newProperties.getCurrentFrame();
|
||||||
|
newProperties.setCurrentFrame(_currentFrame);
|
||||||
|
} else {
|
||||||
|
_lastAnimated = usecTimestampNow();
|
||||||
|
_currentFrame = newProperties.getFirstFrame();
|
||||||
|
newProperties.setCurrentFrame(newProperties.getFirstFrame());
|
||||||
|
}
|
||||||
|
} else if (!newProperties.getRunning() && _animationProperties.getRunning()) {
|
||||||
|
_currentFrame = newProperties.getFirstFrame();
|
||||||
|
newProperties.setCurrentFrame(_currentFrame);
|
||||||
|
} else if (newProperties.getCurrentFrame() != _animationProperties.getCurrentFrame()) {
|
||||||
|
// don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated
|
||||||
|
_currentFrame = newProperties.getCurrentFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally apply the changes
|
||||||
|
bool somethingChanged = newProperties != _animationProperties;
|
||||||
|
if (somethingChanged) {
|
||||||
|
_animationProperties = newProperties;
|
||||||
|
_flags |= Simulation::DIRTY_UPDATEABLE;
|
||||||
|
}
|
||||||
|
return somethingChanged;
|
||||||
|
}
|
||||||
|
|
|
@ -46,10 +46,9 @@ public:
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
bool& somethingChanged) override;
|
bool& somethingChanged) override;
|
||||||
|
|
||||||
// update() and needstocallupdate() added back for the entity property fix
|
|
||||||
virtual void update(const quint64& now) override;
|
virtual void update(const quint64& now) override;
|
||||||
virtual bool needsToCallUpdate() const override;
|
bool needsToCallUpdate() const override { return isAnimatingSomething(); }
|
||||||
void updateFrameCount();
|
|
||||||
|
|
||||||
virtual void debugDump() const override;
|
virtual void debugDump() const override;
|
||||||
|
|
||||||
|
@ -132,6 +131,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setAnimationSettings(const QString& value); // only called for old bitstream format
|
void setAnimationSettings(const QString& value); // only called for old bitstream format
|
||||||
|
bool applyNewAnimationProperties(AnimationPropertyGroup newProperties);
|
||||||
ShapeType computeTrueShapeType() const;
|
ShapeType computeTrueShapeType() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -172,7 +172,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _lastAnimated{ 0 };
|
uint64_t _lastAnimated{ 0 };
|
||||||
AnimationPropertyGroup _previousAnimationProperties;
|
|
||||||
float _currentFrame{ -1.0f };
|
float _currentFrame{ -1.0f };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,6 @@ class PolyLineEntityItem : public EntityItem {
|
||||||
QString getTextures() const;
|
QString getTextures() const;
|
||||||
void setTextures(const QString& textures);
|
void setTextures(const QString& textures);
|
||||||
|
|
||||||
virtual bool needsToCallUpdate() const override { return true; }
|
|
||||||
|
|
||||||
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; }
|
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; }
|
||||||
|
|
||||||
bool pointsChanged() const { return _pointsChanged; }
|
bool pointsChanged() const { return _pointsChanged; }
|
||||||
|
|
|
@ -32,7 +32,8 @@ QStringList InputConfiguration::inputPlugins() {
|
||||||
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
|
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
|
||||||
QString pluginName = plugin->getName();
|
QString pluginName = plugin->getName();
|
||||||
if (pluginName == QString("OpenVR")) {
|
if (pluginName == QString("OpenVR")) {
|
||||||
inputPlugins << QString("Vive");
|
QString headsetName = plugin->getDeviceName();
|
||||||
|
inputPlugins << headsetName;
|
||||||
} else {
|
} else {
|
||||||
inputPlugins << pluginName;
|
inputPlugins << pluginName;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +55,8 @@ QStringList InputConfiguration::activeInputPlugins() {
|
||||||
if (plugin->configurable()) {
|
if (plugin->configurable()) {
|
||||||
QString pluginName = plugin->getName();
|
QString pluginName = plugin->getName();
|
||||||
if (pluginName == QString("OpenVR")) {
|
if (pluginName == QString("OpenVR")) {
|
||||||
activePlugins << QString("Vive");
|
QString headsetName = plugin->getDeviceName();
|
||||||
|
activePlugins << headsetName;
|
||||||
} else {
|
} else {
|
||||||
activePlugins << pluginName;
|
activePlugins << pluginName;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +76,7 @@ QString InputConfiguration::configurationLayout(QString pluginName) {
|
||||||
|
|
||||||
QString sourcePath;
|
QString sourcePath;
|
||||||
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
|
for (auto plugin : PluginManager::getInstance()->getInputPlugins()) {
|
||||||
if (plugin->getName() == pluginName) {
|
if (plugin->getName() == pluginName || plugin->getDeviceName() == pluginName) {
|
||||||
return plugin->configurationLayout();
|
return plugin->configurationLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
virtual void setConfigurationSettings(const QJsonObject configurationSettings) { }
|
virtual void setConfigurationSettings(const QJsonObject configurationSettings) { }
|
||||||
virtual QJsonObject configurationSettings() { return QJsonObject(); }
|
virtual QJsonObject configurationSettings() { return QJsonObject(); }
|
||||||
virtual QString configurationLayout() { return QString(); }
|
virtual QString configurationLayout() { return QString(); }
|
||||||
|
virtual QString getDeviceName() { return QString(); }
|
||||||
virtual void calibrate() {}
|
virtual void calibrate() {}
|
||||||
virtual bool uncalibrate() { return false; }
|
virtual bool uncalibrate() { return false; }
|
||||||
virtual bool configurable() { return false; }
|
virtual bool configurable() { return false; }
|
||||||
|
|
|
@ -66,14 +66,10 @@ OffscreenSurface::OffscreenSurface()
|
||||||
}
|
}
|
||||||
|
|
||||||
OffscreenSurface::~OffscreenSurface() {
|
OffscreenSurface::~OffscreenSurface() {
|
||||||
disconnect(qApp);
|
delete _sharedObject;
|
||||||
_sharedObject->destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) {
|
bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) {
|
||||||
if (!_sharedObject) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
hifi::qml::impl::TextureAndFence typedTextureAndFence;
|
hifi::qml::impl::TextureAndFence typedTextureAndFence;
|
||||||
bool result = _sharedObject->fetchTexture(typedTextureAndFence);
|
bool result = _sharedObject->fetchTexture(typedTextureAndFence);
|
||||||
textureAndFence = typedTextureAndFence;
|
textureAndFence = typedTextureAndFence;
|
||||||
|
|
|
@ -49,8 +49,8 @@ RenderEventHandler::RenderEventHandler(SharedObject* shared, QThread* targetThre
|
||||||
qFatal("Unable to create new offscreen GL context");
|
qFatal("Unable to create new offscreen GL context");
|
||||||
}
|
}
|
||||||
|
|
||||||
moveToThread(targetThread);
|
|
||||||
_canvas.moveToThreadWithContext(targetThread);
|
_canvas.moveToThreadWithContext(targetThread);
|
||||||
|
moveToThread(targetThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderEventHandler::onInitalize() {
|
void RenderEventHandler::onInitalize() {
|
||||||
|
@ -160,11 +160,8 @@ void RenderEventHandler::onQuit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
_shared->shutdownRendering(_canvas, _currentSize);
|
_shared->shutdownRendering(_canvas, _currentSize);
|
||||||
// Release the reference to the shared object. This will allow it to
|
_canvas.doneCurrent();
|
||||||
// be destroyed (should happen on it's own thread).
|
_canvas.moveToThreadWithContext(qApp->thread());
|
||||||
_shared->deleteLater();
|
moveToThread(qApp->thread());
|
||||||
|
|
||||||
deleteLater();
|
|
||||||
|
|
||||||
QThread::currentThread()->quit();
|
QThread::currentThread()->quit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,26 +72,35 @@ SharedObject::SharedObject() {
|
||||||
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit);
|
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &SharedObject::onAboutToQuit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SharedObject::~SharedObject() {
|
SharedObject::~SharedObject() {
|
||||||
if (_quickWindow) {
|
// After destroy returns, the rendering thread should be gone
|
||||||
_quickWindow->destroy();
|
destroy();
|
||||||
_quickWindow = nullptr;
|
|
||||||
|
// _renderTimer is created with `this` as the parent, so need no explicit destruction
|
||||||
|
|
||||||
|
// Destroy the event hand
|
||||||
|
if (_renderObject) {
|
||||||
|
delete _renderObject;
|
||||||
|
_renderObject = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_renderControl) {
|
if (_renderControl) {
|
||||||
_renderControl->deleteLater();
|
delete _renderControl;
|
||||||
_renderControl = nullptr;
|
_renderControl = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_renderThread) {
|
if (_quickWindow) {
|
||||||
_renderThread->quit();
|
_quickWindow->destroy();
|
||||||
_renderThread->deleteLater();
|
delete _quickWindow;
|
||||||
|
_quickWindow = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_rootItem) {
|
// _rootItem is parented to the quickWindow, so needs no explicit destruction
|
||||||
_rootItem->deleteLater();
|
//if (_rootItem) {
|
||||||
_rootItem = nullptr;
|
// delete _rootItem;
|
||||||
}
|
// _rootItem = nullptr;
|
||||||
|
//}
|
||||||
|
|
||||||
releaseEngine(_qmlContext->engine());
|
releaseEngine(_qmlContext->engine());
|
||||||
}
|
}
|
||||||
|
@ -119,6 +128,10 @@ void SharedObject::create(OffscreenSurface* surface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedObject::setRootItem(QQuickItem* rootItem) {
|
void SharedObject::setRootItem(QQuickItem* rootItem) {
|
||||||
|
if (_quit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_rootItem = rootItem;
|
_rootItem = rootItem;
|
||||||
_rootItem->setSize(_quickWindow->size());
|
_rootItem->setSize(_quickWindow->size());
|
||||||
|
|
||||||
|
@ -127,7 +140,6 @@ void SharedObject::setRootItem(QQuickItem* rootItem) {
|
||||||
_renderThread->setObjectName(objectName());
|
_renderThread->setObjectName(objectName());
|
||||||
_renderThread->start();
|
_renderThread->start();
|
||||||
|
|
||||||
|
|
||||||
// Create event handler for the render thread
|
// Create event handler for the render thread
|
||||||
_renderObject = new RenderEventHandler(this, _renderThread);
|
_renderObject = new RenderEventHandler(this, _renderThread);
|
||||||
QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize));
|
QCoreApplication::postEvent(this, new OffscreenEvent(OffscreenEvent::Initialize));
|
||||||
|
@ -137,35 +149,43 @@ void SharedObject::setRootItem(QQuickItem* rootItem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedObject::destroy() {
|
void SharedObject::destroy() {
|
||||||
|
// `destroy` is idempotent, it can be called multiple times without issues
|
||||||
if (_quit) {
|
if (_quit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_rootItem) {
|
if (!_rootItem) {
|
||||||
deleteLater();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_paused = true;
|
_paused = true;
|
||||||
if (_renderTimer) {
|
if (_renderTimer) {
|
||||||
|
_renderTimer->stop();
|
||||||
QObject::disconnect(_renderTimer);
|
QObject::disconnect(_renderTimer);
|
||||||
_renderTimer->deleteLater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_renderControl) {
|
||||||
QObject::disconnect(_renderControl);
|
QObject::disconnect(_renderControl);
|
||||||
|
}
|
||||||
|
|
||||||
QObject::disconnect(qApp);
|
QObject::disconnect(qApp);
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
_quit = true;
|
_quit = true;
|
||||||
|
if (_renderObject) {
|
||||||
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit), Qt::HighEventPriority);
|
QCoreApplication::postEvent(_renderObject, new OffscreenEvent(OffscreenEvent::Quit), Qt::HighEventPriority);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Block until the rendering thread has stopped
|
// Block until the rendering thread has stopped
|
||||||
// FIXME this is undesirable because this is blocking the main thread,
|
// FIXME this is undesirable because this is blocking the main thread,
|
||||||
// but I haven't found a reliable way to do this only at application
|
// but I haven't found a reliable way to do this only at application
|
||||||
// shutdown
|
// shutdown
|
||||||
|
if (_renderThread) {
|
||||||
_renderThread->wait();
|
_renderThread->wait();
|
||||||
|
delete _renderThread;
|
||||||
|
_renderThread = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,15 +37,25 @@ public:
|
||||||
|
|
||||||
virtual glm::vec3 getAvatarPosition() const = 0;
|
virtual glm::vec3 getAvatarPosition() const = 0;
|
||||||
|
|
||||||
virtual bool isAboutToQuit() const = 0;
|
// Unfortunately, having this here is a bad idea. Lots of objects connect to
|
||||||
virtual void postLambdaEvent(std::function<void()> f) = 0;
|
// the aboutToQuit signal, and it's impossible to know the order in which
|
||||||
|
// the receivers will be called, so this might return false negatives
|
||||||
|
//virtual bool isAboutToQuit() const = 0;
|
||||||
|
|
||||||
|
// Queue code to execute on the main thread.
|
||||||
|
// If called from the main thread, the lambda will execute synchronously
|
||||||
|
virtual void postLambdaEvent(const std::function<void()>& f) = 0;
|
||||||
|
// Synchronously execute code on the main thread. This function will
|
||||||
|
// not return until the code is executed, regardles of which thread it
|
||||||
|
// is called from
|
||||||
|
virtual void sendLambdaEvent(const std::function<void()>& f) = 0;
|
||||||
|
|
||||||
virtual qreal getDevicePixelRatio() = 0;
|
virtual qreal getDevicePixelRatio() = 0;
|
||||||
|
|
||||||
virtual render::ScenePointer getMain3DScene() = 0;
|
virtual render::ScenePointer getMain3DScene() = 0;
|
||||||
virtual render::EnginePointer getRenderEngine() = 0;
|
virtual render::EnginePointer getRenderEngine() = 0;
|
||||||
|
|
||||||
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) = 0;
|
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) = 0;
|
||||||
|
|
||||||
virtual bool isHMDMode() const = 0;
|
virtual bool isHMDMode() const = 0;
|
||||||
|
|
||||||
|
@ -54,5 +64,4 @@ public:
|
||||||
static void setInstance(AbstractViewStateInterface* instance);
|
static void setInstance(AbstractViewStateInterface* instance);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_AbstractViewStateInterface_h
|
#endif // hifi_AbstractViewStateInterface_h
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
Q_DECLARE_LOGGING_CATEGORY(displayplugins)
|
||||||
|
|
||||||
const char* OpenVrDisplayPlugin::NAME { "OpenVR (Vive)" };
|
|
||||||
const char* StandingHMDSensorMode { "Standing HMD Sensor Mode" }; // this probably shouldn't be hardcoded here
|
const char* StandingHMDSensorMode { "Standing HMD Sensor Mode" }; // this probably shouldn't be hardcoded here
|
||||||
const char* OpenVrThreadedSubmit { "OpenVR Threaded Submit" }; // this probably shouldn't be hardcoded here
|
const char* OpenVrThreadedSubmit { "OpenVR Threaded Submit" }; // this probably shouldn't be hardcoded here
|
||||||
|
|
||||||
|
@ -410,6 +409,15 @@ void OpenVrDisplayPlugin::init() {
|
||||||
emit deviceConnected(getName());
|
emit deviceConnected(getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString OpenVrDisplayPlugin::getName() const {
|
||||||
|
std::string headsetName = getOpenVrDeviceName();
|
||||||
|
if (headsetName == "HTC") {
|
||||||
|
headsetName += " Vive";
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString::fromStdString(headsetName);
|
||||||
|
}
|
||||||
|
|
||||||
bool OpenVrDisplayPlugin::internalActivate() {
|
bool OpenVrDisplayPlugin::internalActivate() {
|
||||||
if (!_system) {
|
if (!_system) {
|
||||||
_system = acquireOpenVrSystem();
|
_system = acquireOpenVrSystem();
|
||||||
|
@ -444,7 +452,6 @@ bool OpenVrDisplayPlugin::internalActivate() {
|
||||||
|
|
||||||
_openVrDisplayActive = true;
|
_openVrDisplayActive = true;
|
||||||
_container->setIsOptionChecked(StandingHMDSensorMode, true);
|
_container->setIsOptionChecked(StandingHMDSensorMode, true);
|
||||||
|
|
||||||
_system->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y);
|
_system->GetRecommendedRenderTargetSize(&_renderTargetSize.x, &_renderTargetSize.y);
|
||||||
// Recommended render target size is per-eye, so double the X size for
|
// Recommended render target size is per-eye, so double the X size for
|
||||||
// left + right eyes
|
// left + right eyes
|
||||||
|
|
|
@ -36,7 +36,7 @@ class OpenVrDisplayPlugin : public HmdDisplayPlugin {
|
||||||
using Parent = HmdDisplayPlugin;
|
using Parent = HmdDisplayPlugin;
|
||||||
public:
|
public:
|
||||||
bool isSupported() const override;
|
bool isSupported() const override;
|
||||||
const QString getName() const override { return NAME; }
|
const QString getName() const override;
|
||||||
|
|
||||||
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override;
|
glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override;
|
||||||
|
@ -78,7 +78,6 @@ private:
|
||||||
vr::IVRSystem* _system { nullptr };
|
vr::IVRSystem* _system { nullptr };
|
||||||
std::atomic<vr::EDeviceActivityLevel> _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown };
|
std::atomic<vr::EDeviceActivityLevel> _hmdActivityLevel { vr::k_EDeviceActivityLevel_Unknown };
|
||||||
std::atomic<uint32_t> _keyboardSupressionCount{ 0 };
|
std::atomic<uint32_t> _keyboardSupressionCount{ 0 };
|
||||||
static const char* NAME;
|
|
||||||
|
|
||||||
vr::HmdMatrix34_t _lastGoodHMDPose;
|
vr::HmdMatrix34_t _lastGoodHMDPose;
|
||||||
mat4 _sensorResetMat;
|
mat4 _sensorResetMat;
|
||||||
|
|
|
@ -66,6 +66,22 @@ bool oculusViaOpenVR() {
|
||||||
return enableDebugOpenVR && isOculusPresent() && vr::VR_IsHmdPresent();
|
return enableDebugOpenVR && isOculusPresent() && vr::VR_IsHmdPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getOpenVrDeviceName() {
|
||||||
|
auto system = acquireOpenVrSystem();
|
||||||
|
std::string trackingSystemName = "";
|
||||||
|
if (system) {
|
||||||
|
uint32_t HmdTrackingIndex = 0;
|
||||||
|
uint32_t bufferLength = system->GetStringTrackedDeviceProperty(HmdTrackingIndex, vr::Prop_TrackingSystemName_String, NULL, 0, NULL);
|
||||||
|
if (bufferLength > 0) {
|
||||||
|
char* stringBuffer = new char[bufferLength];
|
||||||
|
system->GetStringTrackedDeviceProperty(HmdTrackingIndex, vr::Prop_ManufacturerName_String, stringBuffer, bufferLength, NULL);
|
||||||
|
trackingSystemName = stringBuffer;
|
||||||
|
delete[] stringBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trackingSystemName;
|
||||||
|
}
|
||||||
|
|
||||||
bool openVrSupported() {
|
bool openVrSupported() {
|
||||||
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR");
|
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR");
|
||||||
static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <controllers/Forward.h>
|
#include <controllers/Forward.h>
|
||||||
#include <plugins/Forward.h>
|
#include <plugins/Forward.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
bool oculusViaOpenVR(); // is the user using Oculus via OpenVR
|
bool oculusViaOpenVR(); // is the user using Oculus via OpenVR
|
||||||
bool openVrSupported();
|
bool openVrSupported();
|
||||||
|
@ -26,6 +27,7 @@ void enableOpenVrKeyboard(PluginContainer* container);
|
||||||
void disableOpenVrKeyboard();
|
void disableOpenVrKeyboard();
|
||||||
bool isOpenVrKeyboardShown();
|
bool isOpenVrKeyboardShown();
|
||||||
QString getVrSettingString(const char* section, const char* setting);
|
QString getVrSettingString(const char* section, const char* setting);
|
||||||
|
std::string getOpenVrDeviceName();
|
||||||
|
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "ViveControllerManager.h"
|
#include "ViveControllerManager.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
|
@ -339,6 +340,12 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle
|
||||||
_validTrackedObjects.clear();
|
_validTrackedObjects.clear();
|
||||||
_trackedControllers = 0;
|
_trackedControllers = 0;
|
||||||
|
|
||||||
|
if (_headsetName == "") {
|
||||||
|
_headsetName = getOpenVrDeviceName();
|
||||||
|
if (_headsetName == "HTC") {
|
||||||
|
_headsetName += " Vive";
|
||||||
|
}
|
||||||
|
}
|
||||||
// While the keyboard is open, we defer strictly to the keyboard values
|
// While the keyboard is open, we defer strictly to the keyboard values
|
||||||
if (isOpenVrKeyboardShown()) {
|
if (isOpenVrKeyboardShown()) {
|
||||||
_axisStateMap.clear();
|
_axisStateMap.clear();
|
||||||
|
|
|
@ -52,6 +52,8 @@ public:
|
||||||
bool activate() override;
|
bool activate() override;
|
||||||
void deactivate() override;
|
void deactivate() override;
|
||||||
|
|
||||||
|
QString getDeviceName() { return QString::fromStdString(_inputDevice->_headsetName); }
|
||||||
|
|
||||||
void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||||
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||||
|
|
||||||
|
@ -161,6 +163,7 @@ private:
|
||||||
HandConfig _handConfig { HandConfig::HandController };
|
HandConfig _handConfig { HandConfig::HandController };
|
||||||
FilteredStick _filteredLeftStick;
|
FilteredStick _filteredLeftStick;
|
||||||
FilteredStick _filteredRightStick;
|
FilteredStick _filteredRightStick;
|
||||||
|
std::string _headsetName {""};
|
||||||
|
|
||||||
std::vector<PuckPosePair> _validTrackedObjects;
|
std::vector<PuckPosePair> _validTrackedObjects;
|
||||||
std::map<uint32_t, glm::mat4> _pucksPostOffset;
|
std::map<uint32_t, glm::mat4> _pucksPostOffset;
|
||||||
|
|
|
@ -453,8 +453,8 @@ protected:
|
||||||
return vec3();
|
return vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAboutToQuit() const override { return false; }
|
void postLambdaEvent(const std::function<void()>& f) override {}
|
||||||
void postLambdaEvent(std::function<void()> f) override {}
|
void sendLambdaEvent(const std::function<void()>& f) override {}
|
||||||
|
|
||||||
qreal getDevicePixelRatio() override {
|
qreal getDevicePixelRatio() override {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
@ -469,7 +469,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<void*, std::function<void()>> _postUpdateLambdas;
|
std::map<void*, std::function<void()>> _postUpdateLambdas;
|
||||||
void pushPostUpdateLambda(void* key, std::function<void()> func) override {
|
void pushPostUpdateLambda(void* key, const std::function<void()>& func) override {
|
||||||
_postUpdateLambdas[key] = func;
|
_postUpdateLambdas[key] = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue