Merge branch 'master' of https://github.com/highfidelity/hifi into newEditGrab

This commit is contained in:
David Back 2018-01-19 18:20:49 -08:00
commit c722b4b04d
31 changed files with 417 additions and 104 deletions

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 100 100.8" style="enable-background:new 0 0 100 100.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M26.7,83.9c7.3,1.2,14.8,1.8,22.1,1.8c0.4,0,0.8,0,1.2,0c7.8-0.1,15.6-0.8,23.4-2.2l0,0
c5.7-1.1,11.3-6.6,12.5-12.3C87.3,64.2,88,57,88,50s-0.7-14.2-2.1-21.2c-1.2-5.6-6.8-11.1-12.5-12.2c-7.7-1.4-15.6-2.2-23.4-2.2
c-7.7-0.1-15.6,0.5-23.4,1.8c-5.7,1-11.4,6.5-12.6,12.3c-1.4,7.2-2.1,14.4-2.1,21.6s0.7,14.4,2.1,21.7
C15.3,77.4,20.9,82.9,26.7,83.9z M20.9,29.8c0.6-2.9,4-6.3,6.9-6.8c7-1.1,14-1.7,21-1.7c0.4,0,0.8,0,1.2,0
c7.4,0.1,14.8,0.8,22.1,2.1c2.9,0.6,6.4,3.9,6.9,6.7c1.3,6.6,1.9,13.3,1.9,19.9c0,6.6-0.6,13.3-1.9,19.8c-0.6,2.8-4,6.2-6.9,6.8
c-7.3,1.3-14.8,2.1-22.1,2.1c-7.4,0.1-14.8-0.5-22.1-1.7c-2.9-0.5-6.3-3.9-6.9-6.7c-1.3-6.7-2-13.5-2-20.3
C19,43.3,19.6,36.4,20.9,29.8z"/>
<path class="st0" d="M32.3,61.4c-0.5,1.3-0.1,2.8,0.9,3.8c0.3,0.3,7.2,6.6,15.9,6.6c0.8,0,1.7-0.1,2.6-0.2
c9.8-1.5,15.5-11.1,15.8-11.5c0.7-1.2,0.6-2.8-0.2-3.9c-0.9-1.1-2.3-1.6-3.7-1.3c-9.2,2.5-18.6,3.9-28.1,4.2
C34,59.1,32.8,60,32.3,61.4z"/>
<circle class="st0" cx="36.5" cy="42.8" r="9"/>
<path class="st0" d="M61.4,44.1h6.1c1.9,0,3.3-1.5,3.3-3.3c0-1.9-1.5-3.3-3.3-3.3h-6.1c-1.9,0-3.3,1.5-3.3,3.3
C58.1,42.7,59.6,44.1,61.4,44.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View file

@ -209,6 +209,7 @@
#include "commerce/QmlCommerce.h"
#include "webbrowser/WebBrowserSuggestionsEngine.h"
#include <DesktopPreviewProvider.h>
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
@ -631,6 +632,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<PointerScriptingInterface>();
DependencyManager::set<PickScriptingInterface>();
DependencyManager::set<Cursor::Manager>();
DependencyManager::set<DesktopPreviewProvider>();
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
@ -5763,6 +5765,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get<Snapshot>().data());

View file

@ -175,4 +175,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const
result = pickResult->toVariantMap();
}
return result;
}
}
void PointerScriptingInterface::setDoesHover(unsigned int uid, bool hover) const {
DependencyManager::get<PointerManager>()->setDoesHover(uid, hover);
}

View file

@ -202,6 +202,14 @@ public:
*/
Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); }
/**jsdoc
* Sets whether or not a pointer should generate hover events.
* @function Pointers.setDoesHover
* @param {boolean} uid - The ID of the Pointer, as returned by {@link Pointers.createPointer}.
* @param {boolean} hover - If <code>true</code> then the pointer generates hover events, otherwise it does not.
*/
Q_INVOKABLE void setDoesHover(unsigned int uid, bool hove) const;
/**jsdoc
* Check if a Pointer is associated with the left hand.
* @function Pointers.isLeftHand

View file

@ -277,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString&
if (!result.isEmpty()) {
setPreviousBrowseLocation(QFileInfo(result).absolutePath());
}
emit openFileChanged(result);
emit browseChanged(result);
emit openFileChanged(result); // Deprecated signal; to be removed in due course.
});
}

View file

@ -197,18 +197,19 @@ public slots:
/**jsdoc
* Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A
* {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user
* {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user
* cancels the dialog.
* @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen.
* @function Window.browseAsync
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
* function onOpenFileChanged(filename) {
* function onBrowseChanged(filename) {
* print("File: " + filename);
* }
* Window.openFileChanged.connect(onOpenFileChanged);
* Window.browseChanged.connect(onBrowseChanged);
*
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
* print("Script continues without waiting");
@ -659,9 +660,18 @@ signals:
*/
void saveFileChanged(QString filename);
/**jsdoc
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
* @function Window.browseChanged
* @param {string} filename - The path and name of the file the user chose in the dialog.
* @returns {Signal}
*/
void browseChanged(QString filename);
/**jsdoc
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
* @function Window.openFileChanged
* @deprecated This signal is being replaced with {@link Window.browseChanged|browseChanged} and will be removed.
* @param {string} filename - The path and name of the file the user chose in the dialog.
* @returns {Signal}
*/

View file

@ -33,6 +33,7 @@
#include "../Logging.h"
#include "../CompositorHelper.h"
#include "DesktopPreviewProvider.h"
#include "render-utils/hmd_ui_vert.h"
#include "render-utils/hmd_ui_frag.h"
@ -254,17 +255,9 @@ void HmdDisplayPlugin::internalPresent() {
swapBuffers();
} else if (_clearPreviewFlag) {
QImage image;
if (_vsyncEnabled) {
image = QImage(PathUtils::resourcesPath() + "images/preview.png");
} else {
image = QImage(PathUtils::resourcesPath() + "images/preview-disabled.png");
}
image = image.mirrored();
image = image.convertToFormat(QImage::Format_RGBA8888);
if (!_previewTexture) {
_previewTexture = gpu::Texture::createStrict(
QImage image = DesktopPreviewProvider::getInstance()->getPreviewDisabledImage(_vsyncEnabled);
_previewTexture = gpu::Texture::createStrict(
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA),
image.width(), image.height(),
gpu::Texture::MAX_NUM_MIPS,
@ -274,7 +267,6 @@ void HmdDisplayPlugin::internalPresent() {
_previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA));
_previewTexture->assignStoredMip(0, image.byteCount(), image.constBits());
_previewTexture->setAutoGenerateMips(true);
}
auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions()));

View file

@ -1394,8 +1394,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
// That is where _currentFrame and _lastAnimated were updated.
if (_animating) {
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
if (!jointsMapped()) {
mapJoints(entity, model->getJointNames());
//else the joint have been mapped before but we have a new animation to load
} else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) {
_animation = DependencyManager::get<AnimationCache>()->getAnimation(entity->getAnimationURL());
_jointMappingCompleted = false;
mapJoints(entity, model->getJointNames());
}
if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) {
animate(entity);

View file

@ -368,6 +368,7 @@ public:
void* getPhysicsInfo() const { return _physicsInfo; }
void setPhysicsInfo(void* data) { _physicsInfo = data; }
EntityTreeElementPointer getElement() const { return _element; }
EntityTreePointer getTree() const;
virtual SpatialParentTree* getParentTree() const override;

View file

@ -256,25 +256,32 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
assert(_entity);
assert(entityTreeIsLocked());
measureBodyAcceleration();
bool positionSuccess;
_entity->setWorldPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
if (!positionSuccess) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
"setPosition failed.*");
qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
// If transform or velocities are flagged as dirty it means a network or scripted change
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
// these physics simulation results.
uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES);
if (!flags) {
// flags are clear
_entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation()));
_entity->setWorldVelocity(getBodyLinearVelocity());
_entity->setWorldAngularVelocity(getBodyAngularVelocity());
_entity->setLastSimulated(usecTimestampNow());
} else {
// only set properties NOT flagged
if (!(flags & Simulation::DIRTY_TRANSFORM)) {
_entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation()));
}
if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) {
_entity->setWorldVelocity(getBodyLinearVelocity());
}
if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) {
_entity->setWorldAngularVelocity(getBodyAngularVelocity());
}
if (flags != (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) {
_entity->setLastSimulated(usecTimestampNow());
}
}
bool orientationSuccess;
_entity->setWorldOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
if (!orientationSuccess) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
"setOrientation failed.*");
qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID();
}
_entity->setVelocity(getBodyLinearVelocity());
_entity->setAngularVelocity(getBodyAngularVelocity());
_entity->setLastSimulated(usecTimestampNow());
if (_entity->getSimulatorID().isNull()) {
_loopsWithoutOwner++;
@ -530,9 +537,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
if (!_body->isActive()) {
// make sure all derivatives are zero
_entity->setVelocity(Vectors::ZERO);
_entity->setAngularVelocity(Vectors::ZERO);
_entity->setAcceleration(Vectors::ZERO);
zeroCleanObjectVelocities();
_numInactiveUpdates++;
} else {
glm::vec3 gravity = _entity->getGravity();
@ -559,9 +564,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
if (movingSlowly) {
// velocities might not be zero, but we'll fake them as such, which will hopefully help convince
// other simulating observers to deactivate their own copies
glm::vec3 zero(0.0f);
_entity->setVelocity(zero);
_entity->setAngularVelocity(zero);
zeroCleanObjectVelocities();
}
}
_numInactiveUpdates = 0;
@ -818,3 +821,22 @@ bool EntityMotionState::shouldBeLocallyOwned() const {
void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) {
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
}
void EntityMotionState::zeroCleanObjectVelocities() const {
// If transform or velocities are flagged as dirty it means a network or scripted change
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
// these physics simulation results.
uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES);
if (!flags) {
_entity->setWorldVelocity(glm::vec3(0.0f));
_entity->setWorldAngularVelocity(glm::vec3(0.0f));
} else {
if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) {
_entity->setWorldVelocity(glm::vec3(0.0f));
}
if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) {
_entity->setWorldAngularVelocity(glm::vec3(0.0f));
}
}
_entity->setAcceleration(glm::vec3(0.0f));
}

View file

@ -87,6 +87,7 @@ public:
protected:
// changes _outgoingPriority only if priority is larger
void upgradeOutgoingPriority(uint8_t priority);
void zeroCleanObjectVelocities() const;
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
bool entityTreeIsLocked() const;

View file

@ -64,6 +64,12 @@ bool Pointer::isMouse() const {
return DependencyManager::get<PickManager>()->isMouse(_pickUID);
}
void Pointer::setDoesHover(bool doesHover) {
withWriteLock([&] {
_hover = doesHover;
});
}
void Pointer::update(unsigned int pointerID) {
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
withReadLock([&] {
@ -95,7 +101,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
}
// Hover events
bool doHover = shouldHover(pickResult);
bool doHover = _hover && shouldHover(pickResult);
Pointer::PickedObject hoveredObject = getHoveredObject(pickResult);
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
hoveredEvent.setType(PointerEvent::Move);
@ -104,7 +111,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
hoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave);
// if shouldHover && !_prevDoHover, only send hoverBegin
if (_enabled && _hover && doHover && !_prevDoHover) {
if (_enabled && doHover && !_prevDoHover) {
if (hoveredObject.type == ENTITY) {
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
} else if (hoveredObject.type == OVERLAY) {
@ -112,7 +119,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
} else if (hoveredObject.type == HUD) {
emit pointerManager->hoverBeginHUD(hoveredEvent);
}
} else if (_enabled && _hover && doHover) {
} else if (_enabled && doHover) {
if (hoveredObject.type == OVERLAY) {
if (_prevHoveredObject.type == OVERLAY) {
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
@ -229,7 +236,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin
}
// if we disable the pointer or disable hovering, send hoverEnd events after triggerEnd
if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) {
if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover)) {
if (_prevHoveredObject.type == ENTITY) {
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
} else if (_prevHoveredObject.type == OVERLAY) {

View file

@ -62,6 +62,8 @@ public:
virtual void setLength(float length) {}
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) {}
virtual void setDoesHover(bool hover);
void update(unsigned int pointerID);
virtual void updateVisuals(const PickResultPointer& pickResult) = 0;
void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult);
@ -101,7 +103,6 @@ private:
std::unordered_map<std::string, PickedObject> _triggeredObjects;
PointerEvent::Button chooseButton(const std::string& button);
};
#endif // hifi_Pick_h

View file

@ -122,6 +122,13 @@ void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, boo
}
}
void PointerManager::setDoesHover(unsigned int uid, bool hover) const {
auto pointer = find(uid);
if (pointer) {
pointer->setDoesHover(hover);
}
}
bool PointerManager::isLeftHand(unsigned int uid) {
auto pointer = find(uid);
if (pointer) {

View file

@ -37,6 +37,7 @@ public:
void setLength(unsigned int uid, float length) const;
void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const;
void setDoesHover(unsigned int uid, bool hover) const;
void update();

View file

@ -186,7 +186,7 @@ float specularDistribution(SurfaceData surface) {
// Add geometric factors G1(n,l) and G1(n,v)
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
denom *= surface.smithInvG1NdotV * smithInvG1NdotL;
// Don't divide by PI as it will be done later
// Don't divide by PI as this is part of the light normalization factor
float power = surface.roughness4 / denom;
return power;
}
@ -202,12 +202,11 @@ vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) {
vec3 specular = fresnelColor * power * angleAttenuation;
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
diffuse /= 3.1415926;
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
// has a multiplier of PI, says Naty Hoffman.
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
// page 23 paragraph "Punctual light sources")
return vec4(specular, diffuse);
}
@ -222,9 +221,9 @@ vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) {
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
diffuse /= 3.1415926;
// Diffuse is divided by PI but specular isn't because an infinitesimal volume light source
// has a multiplier of PI, says Naty Hoffman.
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
// page 23 paragraph "Punctual light sources")
return vec4(specular, diffuse);
@ -239,8 +238,9 @@ vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) {
float power = specularDistribution(surface);
vec3 specular = fresnelColor * power * angleAttenuation;
// Specular isn't divided by PI because an infinitesimal volume light source
// has a multiplier of PI, says Naty Hoffman.
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
// page 23 paragraph "Punctual light sources")
return vec4(specular, 0.f);

View file

@ -253,6 +253,7 @@ glm::vec2 getFacingDir2D(const glm::mat4& m);
inline bool isNaN(const glm::vec3& value) { return isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value.x) || isNaN(value.y) || isNaN(value.z); }
inline bool isNaN(const glm::mat3& value) { return isNaN(value * glm::vec3(1.0f)); }
glm::mat4 orthoInverse(const glm::mat4& m);

View file

@ -464,6 +464,36 @@ glm::vec3 SpatiallyNestable::localToWorldDimensions(const glm::vec3& dimensions,
return dimensions;
}
void SpatiallyNestable::setWorldTransform(const glm::vec3& position, const glm::quat& orientation) {
// guard against introducing NaN into the transform
if (isNaN(orientation) || isNaN(position)) {
return;
}
bool changed = false;
bool success = true;
Transform parentTransform = getParentTransform(success);
_transformLock.withWriteLock([&] {
Transform myWorldTransform;
Transform::mult(myWorldTransform, parentTransform, _transform);
if (myWorldTransform.getRotation() != orientation) {
changed = true;
myWorldTransform.setRotation(orientation);
}
if (myWorldTransform.getTranslation() != position) {
changed = true;
myWorldTransform.setTranslation(position);
}
if (changed) {
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
_translationChanged = usecTimestampNow();
}
});
if (success && changed) {
locationChanged(false);
}
}
glm::vec3 SpatiallyNestable::getWorldPosition(bool& success) const {
return getTransform(success).getTranslation();
}

View file

@ -87,6 +87,7 @@ public:
virtual Transform getParentTransform(bool& success, int depth = 0) const;
void setWorldTransform(const glm::vec3& position, const glm::quat& orientation);
virtual glm::vec3 getWorldPosition(bool& success) const;
virtual glm::vec3 getWorldPosition() const;
virtual void setWorldPosition(const glm::vec3& position, bool& success, bool tellPhysics = true);

View file

@ -0,0 +1,46 @@
#include "DesktopPreviewProvider.h"
#include <PathUtils.h>
#include <QMetaEnum>
#include <QtPlugin>
#include <cassert>
DesktopPreviewProvider::DesktopPreviewProvider() {
}
constexpr const char* DesktopPreviewProvider::imagePaths[];
QSharedPointer<DesktopPreviewProvider> DesktopPreviewProvider::getInstance() {
static QSharedPointer<DesktopPreviewProvider> instance = DependencyManager::get<DesktopPreviewProvider>();
return instance;
}
QImage DesktopPreviewProvider::getPreviewDisabledImage(bool vsyncEnabled) const {
auto imageIndex = vsyncEnabled ? VSYNC : m_previewDisabledReason;
assert(imageIndex >= 0 && imageIndex <= VSYNC);
return !m_previewDisabled[imageIndex].isNull() ? m_previewDisabled[imageIndex] : loadPreviewImage(m_previewDisabled[imageIndex], PathUtils::resourcesPath() + imagePaths[imageIndex]);
}
void DesktopPreviewProvider::setPreviewDisabledReason(PreviewDisabledReasons reason) {
if (reason == VSYNC) {
qDebug() << "Preview disabled reason can't be forced to " << QMetaEnum::fromType<DesktopPreviewProvider::PreviewDisabledReasons>().valueToKey(reason);
return; // Not settable via this interface, as VSYNC is controlled by HMD plugin..
}
m_previewDisabledReason = reason;
}
void DesktopPreviewProvider::setPreviewDisabledReason(const QString& reasonString) {
PreviewDisabledReasons reason = USER;
bool ok = false;
reason = (PreviewDisabledReasons) QMetaEnum::fromType<DesktopPreviewProvider::PreviewDisabledReasons>().keyToValue(reasonString.toLatin1().data(), &ok);
if (ok) {
setPreviewDisabledReason(reason);
}
}
QImage& DesktopPreviewProvider::loadPreviewImage(QImage& image, const QString& path) const {
return image = QImage(path).mirrored().convertToFormat(QImage::Format_RGBA8888);
}

View file

@ -0,0 +1,47 @@
//
// Created by Alexander Ivash on 2018/01/08
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <DependencyManager.h>
#include <QImage>
class DesktopPreviewProvider : public QObject, public Dependency {
SINGLETON_DEPENDENCY
Q_OBJECT
DesktopPreviewProvider();
DesktopPreviewProvider(const DesktopPreviewProvider& other) = delete;
constexpr static const char* imagePaths[] = {
"images/preview-disabled.png", // USER
"images/preview-privacy.png", // SECURE_SCREEN
"images/preview.png", // VSYNC
};
public:
enum PreviewDisabledReasons {
USER = 0,
SECURE_SCREEN,
VSYNC // Not settable via this interface, as VSYNC is controlled by HMD plugin..
};
Q_ENUM(PreviewDisabledReasons)
static QSharedPointer<DesktopPreviewProvider> getInstance();
QImage getPreviewDisabledImage(bool vsyncEnabled) const;
void setPreviewDisabledReason(PreviewDisabledReasons reason);
public slots:
void setPreviewDisabledReason(const QString& reason);
private:
QImage& loadPreviewImage(QImage& image, const QString& path) const;
PreviewDisabledReasons m_previewDisabledReason = { USER };
mutable QImage m_previewDisabled[3];
};

View file

@ -562,9 +562,11 @@
break;
case 'disableHmdPreview':
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN");
Menu.setIsOptionChecked("Disable Preview", true);
break;
case 'maybeEnableHmdPreview':
DesktopPreviewProvider.setPreviewDisabledReason("USER");
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
break;
case 'passphraseReset':
@ -635,7 +637,11 @@
// -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string
// value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML.
function onTabletScreenChanged(type, url) {
onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE);
var onWalletScreenNow = (type === "QML" && url === WALLET_QML_SOURCE);
if (!onWalletScreenNow && onWalletScreen) {
DesktopPreviewProvider.setPreviewDisabledReason("USER");
}
onWalletScreen = onWalletScreenNow;
wireEventBridge(onWalletScreen);
// Change button to active when window is first openend, false otherwise.
if (button) {

View file

@ -44,7 +44,12 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
this.highVarianceCount = 0;
this.veryhighVarianceCount = 0;
this.tabletID = null;
this.TABLET_UI_UUIDS = [];
this.blacklist = [];
this.leftPointerDoesHover = true;
this.leftPointerDoesHoverChanged = false;
this.rightPointerDoesHover = true;
this.rightPointerDoesHoverChanged = false;
this.pointerManager = new PointerManager();
// a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are
@ -122,6 +127,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
return getControllerWorldLocation(Controller.Standard.RightHand, true);
};
this.isTabletID = function (uuid) {
return _this.TABLET_UI_UUIDS.indexOf(uuid) !== -1;
};
this.updateTimings = function () {
_this.intervalCount++;
var thisInterval = Date.now();
@ -148,11 +157,35 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
this.setIgnorePointerItems = function() {
if (HMD.tabletID !== this.tabletID) {
this.tabletID = HMD.tabletID;
this.TABLET_UI_UUIDS = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID];
Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist);
Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist);
}
};
this.updateDoesHover = function(handLaser, doesHover) {
if (handLaser.doesHover !== undefined) {
if (handLaser.hand === LEFT_HAND && _this.leftPointerDoesHover !== doesHover) {
_this.leftPointerDoesHover = doesHover;
_this.leftPointerDoesHoverChanged = true;
} else if (handLaser.hand === RIGHT_HAND && _this.rightPointerDoesHover !== doesHover) {
_this.rightPointerDoesHover = doesHover;
_this.rightPointerDoesHoverChanged = true;
}
}
}
this.updateHovering = function () {
if (_this.leftPointerDoesHoverChanged) {
Pointers.setDoesHover(_this.leftPointer, _this.leftPointerDoesHover);
_this.leftPointerDoesHoverChanged = false;
}
if (_this.rightPointerDoesHoverChanged) {
Pointers.setDoesHover(_this.rightPointer, _this.rightPointerDoesHover);
_this.rightPointerDoesHoverChanged = false;
}
};
this.update = function () {
try {
_this.updateInternal();
@ -324,6 +357,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
_this.runningPluginNames[orderedPluginName] = true;
_this.markSlots(candidatePlugin, orderedPluginName);
_this.pointerManager.makePointerVisible(candidatePlugin.parameters.handLaser);
_this.updateDoesHover(candidatePlugin.parameters.handLaser,
candidatePlugin.parameters.handLaser.doesHover);
if (DEBUG) {
print("controllerDispatcher running " + orderedPluginName);
}
@ -354,12 +389,15 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Script.beginProfileRange("dispatch.run." + runningPluginName);
}
var runningness = plugin.run(controllerData, deltaTime);
if (!runningness.active) {
if (runningness.active) {
_this.updateDoesHover(plugin.parameters.handLaser, plugin.parameters.handLaser.doesHover);
} else {
// plugin is finished running, for now. remove it from the list
// of running plugins and mark its activity-slots as "not in use"
delete _this.runningPluginNames[runningPluginName];
_this.markSlots(plugin, false);
_this.pointerManager.makePointerInvisible(plugin.parameters.handLaser);
_this.updateDoesHover(plugin.parameters.handLaser, true);
if (DEBUG) {
print("controllerDispatcher stopping " + runningPluginName);
}
@ -372,6 +410,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
}
}
_this.pointerManager.updatePointersRenderState(controllerData.triggerClicks, controllerData.triggerValues);
_this.updateHovering();
if (PROFILE) {
Script.endProfileRange("dispatch.run");
}

View file

@ -370,20 +370,23 @@ Script.include("/~/system/libraries/Xform.js");
};
this.isReady = function (controllerData) {
if (this.notPointingAtEntity(controllerData)) {
return makeRunningValues(false, [], []);
}
if (HMD.active) {
if (this.notPointingAtEntity(controllerData)) {
return makeRunningValues(false, [], []);
}
this.distanceHolding = false;
this.distanceRotating = false;
this.distanceHolding = false;
this.distanceRotating = false;
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
this.prepareDistanceRotatingData(controllerData);
return makeRunningValues(true, [], []);
} else {
this.destroyContextOverlay();
return makeRunningValues(false, [], []);
if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) {
this.prepareDistanceRotatingData(controllerData);
return makeRunningValues(true, [], []);
} else {
this.destroyContextOverlay();
return makeRunningValues(false, [], []);
}
}
return makeRunningValues(false, [], []);
};
this.run = function (controllerData) {

View file

@ -89,7 +89,7 @@
this.isReady = function (controllerData) {
var otherModuleRunning = this.getOtherModule().running;
if (!otherModuleRunning) {
if (!otherModuleRunning && HMD.active) {
if (this.processLaser(controllerData)) {
this.running = true;
return ControllerDispatcherUtils.makeRunningValues(true, [], []);

View file

@ -87,41 +87,89 @@ Script.include("/~/system/libraries/controllers.js");
return MyAvatar.getDominantHand() === "right" ? 1 : 0;
};
this.dominantHandOverride = false;
this.letOtherHandRunFirst = function (controllerData, pointingAt) {
// If both hands are ready to run, let the other hand run first if it is the dominant hand so that it gets the
// highlight.
var isOtherTriggerPressed = controllerData.triggerValues[this.otherHand] > TRIGGER_OFF_VALUE;
var isLetOtherHandRunFirst = !this.getOtherModule().running
&& this.getDominantHand() === this.otherHand
&& (this.parameters.handLaser.allwaysOn || isOtherTriggerPressed);
if (isLetOtherHandRunFirst) {
var otherHandPointingAt = controllerData.rayPicks[this.otherHand].objectID;
if (this.isTabletID(otherHandPointingAt)) {
otherHandPointingAt = HMD.tabletID;
}
isLetOtherHandRunFirst = pointingAt === otherHandPointingAt;
}
return isLetOtherHandRunFirst;
};
this.hoverItem = null;
this.isTabletID = function (uuid) {
return [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID].indexOf(uuid) !== -1;
};
this.isReady = function(controllerData) {
var otherModuleRunning = this.getOtherModule().running;
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE
&& controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE;
if ((!otherModuleRunning || isTriggerPressed)
&& (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) {
if (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) {
this.updateAllwaysOn();
if (isTriggerPressed) {
this.dominantHandOverride = true; // Override dominant hand.
this.getOtherModule().dominantHandOverride = false;
}
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
if (this.parameters.handLaser.allwaysOn || isTriggerPressed) {
return makeRunningValues(true, [], []);
var pointingAt = controllerData.rayPicks[this.hand].objectID;
if (this.isTabletID(pointingAt)) {
pointingAt = HMD.tabletID;
}
if (!this.letOtherHandRunFirst(controllerData, pointingAt)) {
if (pointingAt !== this.getOtherModule().hoverItem) {
this.parameters.handLaser.doesHover = true;
this.hoverItem = pointingAt;
} else {
this.parameters.handLaser.doesHover = false;
this.hoverItem = null;
}
return makeRunningValues(true, [], []);
}
}
}
this.parameters.handLaser.doesHover = false;
this.hoverItem = null;
return makeRunningValues(false, [], []);
};
this.run = function(controllerData, deltaTime) {
var otherModuleRunning = this.getOtherModule().running;
otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand.
otherModuleRunning = otherModuleRunning || this.getOtherModule().dominantHandOverride; // Override dominant hand.
var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData);
if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE
|| this.parameters.handLaser.allwaysOn
var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE;
if (!grabModuleNeedsToRun && (isTriggerPressed || this.parameters.handLaser.allwaysOn
&& (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) {
this.running = true;
var pointingAt = controllerData.rayPicks[this.hand].objectID;
if (this.isTabletID(pointingAt)) {
pointingAt = HMD.tabletID;
}
if (pointingAt !== this.getOtherModule().hoverItem || isTriggerPressed) {
this.parameters.handLaser.doesHover = true;
this.hoverItem = pointingAt;
} else {
this.parameters.handLaser.doesHover = false;
this.hoverItem = null;
}
return makeRunningValues(true, [], []);
}
this.deleteContextOverlay();
this.running = false;
this.dominantHandOverride = false;
this.parameters.handLaser.doesHover = false;
this.hoverItem = null;
return makeRunningValues(false, [], []);
};
}

View file

@ -448,7 +448,7 @@ var toolBar = (function () {
});
addButton("importEntitiesButton", "assets-01.svg", function() {
Window.openFileChanged.connect(onFileOpenChanged);
Window.browseChanged.connect(onFileOpenChanged);
Window.browseAsync("Select Model to Import", "", "*.json");
});
@ -1497,7 +1497,7 @@ function onFileOpenChanged(filename) {
// disconnect the event, otherwise the requests will stack up
try {
// Not all calls to onFileOpenChanged() connect an event.
Window.openFileChanged.disconnect(onFileOpenChanged);
Window.browseChanged.disconnect(onFileOpenChanged);
} catch (e) {
// Ignore.
}
@ -1549,7 +1549,7 @@ function handeMenuEvent(menuItem) {
}
} else if (menuItem === "Import Entities" || menuItem === "Import Entities from URL") {
if (menuItem === "Import Entities") {
Window.openFileChanged.connect(onFileOpenChanged);
Window.browseChanged.connect(onFileOpenChanged);
Window.browseAsync("Select Model to Import", "", "*.json");
} else {
Window.promptTextChanged.connect(onPromptTextChanged);

View file

@ -43,7 +43,7 @@ var activeTimer = false; // used to cancel active timer if a user plays an amima
var activeEmote = false; // to keep track of the currently playing emote
button = tablet.addButton({
//icon: "icons/tablet-icons/emote.svg", // TODO - we need graphics for this
icon: "icons/tablet-icons/EmoteAppIcon.svg",
text: EMOTE_LABEL,
sortOrder: EMOTE_APP_SORT_ORDER
});

View file

@ -44,11 +44,11 @@
input[type=button] {
font-family: 'Raleway';
font-weight: bold;
font-size: 13px;
font-size: 20px;
text-transform: uppercase;
vertical-align: top;
height: 28px;
min-width: 120px;
height: 105px;
min-width: 190px;
padding: 0px 18px;
margin-right: 6px;
border-radius: 5px;
@ -98,14 +98,14 @@
</div>
<div class="content">
<p>Click an emotion to Emote:<p>
<p><input type="button" class="emote-button white" value="Crying"></p>
<p><input type="button" class="emote-button white" value="Surprised"></p>
<p><input type="button" class="emote-button white" value="Dancing"></p>
<p><input type="button" class="emote-button white" value="Cheering"></p>
<p><input type="button" class="emote-button white" value="Waving"></p>
<p><input type="button" class="emote-button white" value="Fall"></p>
<p><input type="button" class="emote-button white" value="Pointing"></p>
<p><input type="button" class="emote-button white" value="Clapping"></p>
<p><input type="button" class="emote-button white" value="Crying">
<input type="button" class="emote-button white" value="Surprised"></p>
<p><input type="button" class="emote-button white" value="Dancing">
<input type="button" class="emote-button white" value="Cheering"></p>
<p><input type="button" class="emote-button white" value="Waving">
<input type="button" class="emote-button white" value="Fall"></p>
<p><input type="button" class="emote-button white" value="Pointing">
<input type="button" class="emote-button white" value="Clapping"></p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

View file

@ -539,8 +539,14 @@
return startingUp;
}
function onDomainConnectionRefused(reason) {
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
function onDomainConnectionRefused(reason, reasonCode) {
// the "login error" reason means that the DS couldn't decrypt the username signature
// since this eventually resolves itself for good actors we don't need to show a notification for it
var LOGIN_ERROR_REASON_CODE = 2;
if (reasonCode != LOGIN_ERROR_REASON_CODE) {
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED);
}
}
function onEditError(msg) {