From 47f7d55e2c5481f5c158beec2e91cdb7b211a27e Mon Sep 17 00:00:00 2001 From: "Babiuch, Ryan Nicholas" Date: Mon, 1 Feb 2016 08:32:52 -0600 Subject: [PATCH 01/79] Working energy usage for entity manipulation. - Working example in examples/example/ui/MyEnergyBar.js --- examples/example/ui/MyEnergyBar.js | 67 +++++++++++++++++++ examples/example/ui/energyBar.js | 44 ++---------- interface/src/avatar/MyAvatar.cpp | 44 +++++++++++- interface/src/avatar/MyAvatar.h | 16 +++++ .../entities/src/EntityScriptingInterface.cpp | 59 +++++++++++++++- .../entities/src/EntityScriptingInterface.h | 14 +++- 6 files changed, 202 insertions(+), 42 deletions(-) create mode 100644 examples/example/ui/MyEnergyBar.js diff --git a/examples/example/ui/MyEnergyBar.js b/examples/example/ui/MyEnergyBar.js new file mode 100644 index 0000000000..ac7ef0d39c --- /dev/null +++ b/examples/example/ui/MyEnergyBar.js @@ -0,0 +1,67 @@ +Script.include("../../libraries/utils.js"); +var energyColor = {red: 0, green: 200, blue: 0}; +var lowEnergyColor = {red: 255, green: 0, blue: 0}; +var totalWidth = 200; +var paddingRight = 50; +var xPosition = Window.innerWidth - totalWidth - paddingRight; +var lowEnergyThreshold = 0.3; +var currentEnergy = 1.0; +var energyLossRate = 0.003; +var energyChargeRate = 0.003; +var isGrabbing = false; +var refractoryPeriod = 2000; + +var lastAvatarVelocity = MyAvatar.getVelocity(); +var lastAvatarPosition = MyAvatar.position; + +var background = Overlays.addOverlay("text", { + x: xPosition, + y: 20, + width: totalWidth, + height: 10, + backgroundColor: {red: 255, green: 0, blue: 0} +}) + +var bar = Overlays.addOverlay("text", { + x: xPosition, + y: 20, + width: totalWidth, + height: 10, + backgroundColor: energyColor +}); + + +// Takes an energy value between 0 and 1 and sets energy bar width appropriately +function setEnergy(energy) { + energy = clamp(energy, 0, 1); + var barWidth = totalWidth * energy; + var color = energy <= lowEnergyThreshold ? lowEnergyColor: energyColor; + Overlays.editOverlay(bar, { width: barWidth, backgroundColor: color}); +} + +function update() { + currentEnergy = clamp(MyAvatar.energy, 0, 1); + setEnergy(currentEnergy); +} + +function cleanup() { + Overlays.deleteOverlay(background); + Overlays.deleteOverlay(bar); +} + +function energyChanged(newValue) { + Entities.currentAvatarEnergy = newValue; +} + +function debitAvatarEnergy(value) { + MyAvatar.energy = MyAvatar.energy - value; +} +function calculateCost(mass, oldVelocity, newVelocity) { + return mass * (newVelocity - oldVelocity); +} + +Entities.addCostFunction(calculateCost); +Entities.debitEnergySource.connect(debitAvatarEnergy); +MyAvatar.energyChanged.connect(energyChanged); +Script.update.connect(update); +Script.scriptEnding.connect(cleanup); diff --git a/examples/example/ui/energyBar.js b/examples/example/ui/energyBar.js index a45b09f6d4..498eef2751 100644 --- a/examples/example/ui/energyBar.js +++ b/examples/example/ui/energyBar.js @@ -51,45 +51,8 @@ function setEnergy(energy) { Overlays.editOverlay(bar, { width: barWidth, backgroundColor: color}); } -function avatarAccelerationEnergy() { - var AVATAR_MOVEMENT_ENERGY_CONSTANT = 0.001; - var velocity = MyAvatar.getVelocity(); - var dV = Math.abs(Vec3.length(velocity) - Vec3.length(lastAvatarVelocity)); - var dE = Vec3.length(lastAvatarVelocity) * dV * AVATAR_MOVEMENT_ENERGY_CONSTANT; - lastAvatarVelocity = velocity; - return dE; -} - -function teleported() { - var MAX_AVATAR_MOVEMENT_PER_FRAME = 30.0; - var position = MyAvatar.position; - var dP = Vec3.length(Vec3.subtract(position, lastAvatarPosition)); - lastAvatarPosition = position; - return (dP > MAX_AVATAR_MOVEMENT_PER_FRAME); -} - -function audioEnergy() { - var AUDIO_ENERGY_CONSTANT = 0.000001; - return MyAvatar.audioLoudness * AUDIO_ENERGY_CONSTANT; -} - function update() { - // refill energy - currentEnergy += energyChargeRate; - - // Avatar acceleration - currentEnergy -= avatarAccelerationEnergy(); - - // Teleport cost - if (teleported()) { - currentEnergy = 0; - } - - // Making sounds - currentEnergy -= audioEnergy(); - - - currentEnergy = clamp(currentEnergy, 0, 1); + currentEnergy = clamp(MyAvatar.energy, 0, 1); setEnergy(currentEnergy); } @@ -98,5 +61,10 @@ function cleanup() { Overlays.deleteOverlay(bar); } +function energyChanged(newValue) { + Entities.currentAvatarEnergy = newValue; +} + +MyAvatar.energyChanged.connect(energyChanged); Script.update.connect(update); Script.scriptEnding.connect(cleanup); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3e54ba99b9..8ab55d450c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -295,8 +295,20 @@ void MyAvatar::update(float deltaTime) { auto audio = DependencyManager::get(); head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); - - simulate(deltaTime); + + simulate(deltaTime); + + currentEnergy += energyChargeRate; + currentEnergy -= getAccelerationEnergy(); + currentEnergy -= getAudioEnergy(); + + if(didTeleport()) { + currentEnergy = 0.0f; + } + currentEnergy = max(0.0f, min(currentEnergy,1.0f)); + emit energyChanged(currentEnergy); + + } extern QByteArray avatarStateToFrame(const AvatarData* _avatar); @@ -1883,3 +1895,31 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co } } +float MyAvatar::getAccelerationEnergy() { + glm::vec3 velocity = getVelocity(); + int changeInVelocity = abs(velocity.length() - priorVelocity.length()); + float changeInEnergy = priorVelocity.length()*changeInVelocity*AVATAR_MOVEMENT_ENERGY_CONSTANT; + priorVelocity = velocity; + + return changeInEnergy; +} + +float MyAvatar::getEnergy() { + return currentEnergy; +} + +void MyAvatar::setEnergy(float value) { + currentEnergy = value; +} + +float MyAvatar::getAudioEnergy() { + return getAudioLoudness()*AUDIO_ENERGY_CONSTANT; +} + +bool MyAvatar::didTeleport() { + glm::vec3 pos = getPosition(); + glm::vec3 changeInPosition = pos - lastPosition; + lastPosition = pos; + return (changeInPosition.length() > MAX_AVATAR_MOVEMENT_PER_FRAME); +} + diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ed6c3cb883..a596d9c7cd 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -78,6 +78,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose) Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose) Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose) + Q_PROPERTY(float energy READ getEnergy WRITE setEnergy) public: MyAvatar(RigPointer rig); @@ -272,6 +273,7 @@ signals: void transformChanged(); void newCollisionSoundURL(const QUrl& url); void collisionWithEntity(const Collision& collision); + void energyChanged(float newEnergy); private: @@ -408,6 +410,20 @@ private: AtRestDetector _hmdAtRestDetector; bool _lastIsMoving { false }; + + float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f }; + float AUDIO_ENERGY_CONSTANT { 0.000001f }; + float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f }; + float currentEnergy { 0.0f }; + float energyChargeRate { 0.003f }; + glm::vec3 priorVelocity; + glm::vec3 lastPosition; + float getAudioEnergy(); + float getAccelerationEnergy(); + float getEnergy(); + void setEnergy(float value); + bool didTeleport(); + }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8fd7be912e..9c61d7c297 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - #include "EntityScriptingInterface.h" #include "EntityItemID.h" @@ -122,6 +121,21 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); + auto dimensions = propertiesWithSimID.getDimensions(); + float volume = dimensions.x*dimensions.y*dimensions.z; + auto density = propertiesWithSimID.getDensity(); + auto newVelocity = propertiesWithSimID.getVelocity().length(); + double cost = calculateCost(density*volume, 0, newVelocity); + cost *= ENTITY_MANIPULATION_MULTIPLIER; //try this as a constant for now + + if(cost > _currentAvatarEnergy) { + return QUuid(); + } else { + //debit the avatar energy and continue + emit debitEnergySource(cost); + } + + EntityItemID id = EntityItemID(QUuid::createUuid()); // If we have a local entity tree set, then also update it. @@ -211,6 +225,21 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) { EntityItemProperties properties = scriptSideProperties; + + auto dimensions = properties.getDimensions(); + float volume = dimensions.x*dimensions.y*dimensions.z; + auto density = properties.getDensity(); + auto newVelocity = properties.getVelocity().length(); + double cost = calculateCost(density*volume, 0, newVelocity); + cost *= ENTITY_MANIPULATION_MULTIPLIER; + + if(cost > _currentAvatarEnergy) { + return QUuid(); + } else { + //debit the avatar energy and continue + emit debitEnergySource(cost); + } + EntityItemID entityID(id); if (!_entityTree) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); @@ -316,6 +345,21 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { _entityTree->withWriteLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { + + auto dimensions = entity->getDimensions(); + float volume = dimensions.x*dimensions.y*dimensions.z; + auto density = entity->getDensity(); + auto velocity = entity->getVelocity().length(); + double cost = calculateCost(density*volume, velocity, 0); + cost *= ENTITY_MANIPULATION_MULTIPLIER; + + if(cost > _currentAvatarEnergy) { + return; + } else { + //debit the avatar energy and continue + emit debitEnergySource(cost); + } + if (entity->getLocked()) { shouldDelete = false; } else { @@ -992,3 +1036,16 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) { Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID)); return result; } + +void EntityScriptingInterface::addCostFunction(QScriptValue costFunction) { + _costFunction = &costFunction; +} + +double EntityScriptingInterface::calculateCost(float mass, float oldVelocity,float newVelocity) { + return std::abs(mass * (newVelocity - oldVelocity)); +} + +void EntityScriptingInterface::setCurrentAvatarEnergy(float energy) { + // qCDebug(entities) << "NEW AVATAR ENERGY IN ENTITY SCRIPTING INTERFACE: " << energy; + _currentAvatarEnergy = energy; +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 641da7518e..ff98ab4eb7 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include @@ -57,6 +59,8 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT + + Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy) public: EntityScriptingInterface(); @@ -67,7 +71,8 @@ public: void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; } - + double calculateCost(float mass, float oldVelocity, float newVelocity); + Q_INVOKABLE void addCostFunction(QScriptValue costFunction); public slots: // returns true if the DomainServer will allow this Node/Avatar to make changes @@ -163,6 +168,7 @@ public slots: Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name); Q_INVOKABLE QStringList getJointNames(const QUuid& entityID); + signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); @@ -188,6 +194,7 @@ signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); void clearingEntities(); + void debitEnergySource(double value); private: bool actionWorker(const QUuid& entityID, std::function actor); @@ -205,6 +212,11 @@ private: EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; + QScriptValue* _costFunction = nullptr; + float _currentAvatarEnergy; + float getCurrentAvatarEnergy() { return _currentAvatarEnergy; } + void setCurrentAvatarEnergy(float energy); + float ENTITY_MANIPULATION_MULTIPLIER = { 0.01f }; }; #endif // hifi_EntityScriptingInterface_h From 671f27e5bc3950cc75884a5b8b0e6b6221011cb5 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 28 Jan 2016 12:05:59 -0800 Subject: [PATCH 02/79] Move DrawStencil to use ShapePlumber --- .../render-utils/src/RenderDeferredTask.cpp | 33 ++++++++----------- .../render-utils/src/RenderDeferredTask.h | 4 +-- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d76d0a77de..9ae76bfe02 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -256,25 +256,19 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon } } -gpu::PipelinePointer DrawStencilDeferred::_opaquePipeline; -const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() { - if (!_opaquePipeline) { - const gpu::int8 STENCIL_OPAQUE = 1; - auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - +DrawStencilDeferred::DrawStencilDeferred() : _shapePlumber{ std::make_shared() } { + const gpu::int8 STENCIL_OPAQUE = 1; + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)); + auto program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram((*program)); - gpu::Shader::makeProgram((*program)); + auto state = std::make_shared(); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); + state->setColorWriteMask(0); - auto state = std::make_shared(); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); - state->setColorWriteMask(0); - - _opaquePipeline = gpu::Pipeline::create(program, state); - } - return _opaquePipeline; + _shapePlumber->addPipeline(ShapeKey::Filter::Builder(), program, state); } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { @@ -294,11 +288,12 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - batch.setPipeline(getOpaquePipeline()); + // We only need to fetch this once + static const auto& pipeline = _shapePlumber->pickPipeline(args, ShapeKey()); + batch.setPipeline(pipeline->pipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.setResourceTexture(0, nullptr); - }); args->_batch = nullptr; } diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 8d773a9b21..2ebb2e4e12 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -68,14 +68,14 @@ protected: class DrawStencilDeferred { public: - static const gpu::PipelinePointer& getOpaquePipeline(); + DrawStencilDeferred(); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); using JobModel = render::Job::Model; protected: - static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable + render::ShapePlumberPointer _shapePlumber; }; class DrawBackgroundDeferred { From df894d364a032fcc31837838e802621c96a7dbb6 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 28 Jan 2016 12:25:31 -0800 Subject: [PATCH 03/79] Move DrawOverlay3D to use ShapePlumber --- .../render-utils/src/RenderDeferredTask.cpp | 39 ++++++++++--------- .../render-utils/src/RenderDeferredTask.h | 5 +-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 9ae76bfe02..15a37998ca 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -128,7 +128,7 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { addJob("DrawStatus", opaques, DrawStatus(statusIconMap)); } - addJob("DrawOverlay3D", shapePlumber); + addJob("DrawOverlay3D"); addJob("HitEffect"); @@ -180,22 +180,26 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont }); } -// TODO: Move this to the shapePlumber -gpu::PipelinePointer DrawOverlay3D::_opaquePipeline; -const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() { - if (!_opaquePipeline) { - auto vs = gpu::Shader::createVertex(std::string(overlay3D_vert)); - auto ps = gpu::Shader::createPixel(std::string(overlay3D_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - - auto state = std::make_shared(); - state->setDepthTest(false); - // additive blending - state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); +DrawOverlay3D::DrawOverlay3D() : _shapePlumber{ std::make_shared() } { + auto vs = gpu::Shader::createVertex(std::string(overlay3D_vert)); + auto ps = gpu::Shader::createPixel(std::string(overlay3D_frag)); + auto program = gpu::Shader::createProgram(vs, ps); - _opaquePipeline = gpu::Pipeline::create(program, state); - } - return _opaquePipeline; + auto opaqueState = std::make_shared(); + opaqueState->setDepthTest(false); + opaqueState->setBlendFunction(true, + // Additive blending + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + auto transparentState = std::make_shared(); + transparentState->setDepthTest(false); + transparentState->setBlendFunction(true, + // For transparency, keep the highlight intensity + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + _shapePlumber->addPipeline(ShapeKey::Filter::Builder().withOpaque(), program, opaqueState); + _shapePlumber->addPipeline(ShapeKey::Filter::Builder().withTranslucent(), program, transparentState); } void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { @@ -246,9 +250,8 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon batch.setViewTransform(viewMat); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - - batch.setPipeline(getOpaquePipeline()); batch.setResourceTexture(0, args->_whiteTexture); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, _maxDrawn); }); args->_batch = nullptr; diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 2ebb2e4e12..894a91cdc3 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -106,15 +106,12 @@ public: using Config = DrawOverlay3DConfig; using JobModel = render::Job::Model; - DrawOverlay3D(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawOverlay3D(); void configure(const Config& config) { _maxDrawn = config.maxDrawn; } void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - static const gpu::PipelinePointer& getOpaquePipeline(); - protected: - static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable render::ShapePlumberPointer _shapePlumber; int _maxDrawn; // initialized by Config }; From 5d49eacf83c3e4e850acffd38c6a8292293f786d Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 28 Jan 2016 15:51:56 -0800 Subject: [PATCH 04/79] Move pipeline init to separate file --- .../render-utils/src/RenderDeferredTask.cpp | 228 +---------------- .../render-utils/src/RenderPipelines.cpp | 234 ++++++++++++++++++ 2 files changed, 240 insertions(+), 222 deletions(-) create mode 100644 libraries/render-utils/src/RenderPipelines.cpp diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 15a37998ca..8a47280526 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -4,7 +4,7 @@ // render-utils/src/ // // Created by Sam Gateau on 5/29/15. -// Copyright 20154 High Fidelity, Inc. +// Copyright 2016 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 @@ -15,7 +15,6 @@ #include #include #include -#include #include "DebugDeferredBuffer.h" #include "DeferredLightingEffect.h" @@ -31,34 +30,11 @@ #include "RenderDeferredTask.h" -#include "model_vert.h" -#include "model_shadow_vert.h" -#include "model_normal_map_vert.h" -#include "model_lightmap_vert.h" -#include "model_lightmap_normal_map_vert.h" -#include "skin_model_vert.h" -#include "skin_model_shadow_vert.h" -#include "skin_model_normal_map_vert.h" - -#include "model_frag.h" -#include "model_shadow_frag.h" -#include "model_normal_map_frag.h" -#include "model_normal_specular_map_frag.h" -#include "model_specular_map_frag.h" -#include "model_lightmap_frag.h" -#include "model_lightmap_normal_map_frag.h" -#include "model_lightmap_normal_specular_map_frag.h" -#include "model_lightmap_specular_map_frag.h" -#include "model_translucent_frag.h" - -#include "overlay3D_vert.h" -#include "overlay3D_frag.h" - -#include "drawOpaqueStencil_frag.h" - using namespace render; -void initDeferredPipelines(render::ShapePlumber& plumber); +extern void initOverlay3DPipelines(render::ShapePlumber& plumber); +extern void initStencilPipelines(render::ShapePlumber& plumber); +extern void initDeferredPipelines(render::ShapePlumber& plumber); void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { DependencyManager::get()->prepare(renderContext->args); @@ -181,25 +157,7 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont } DrawOverlay3D::DrawOverlay3D() : _shapePlumber{ std::make_shared() } { - auto vs = gpu::Shader::createVertex(std::string(overlay3D_vert)); - auto ps = gpu::Shader::createPixel(std::string(overlay3D_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - - auto opaqueState = std::make_shared(); - opaqueState->setDepthTest(false); - opaqueState->setBlendFunction(true, - // Additive blending - gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - auto transparentState = std::make_shared(); - transparentState->setDepthTest(false); - transparentState->setBlendFunction(true, - // For transparency, keep the highlight intensity - gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - _shapePlumber->addPipeline(ShapeKey::Filter::Builder().withOpaque(), program, opaqueState); - _shapePlumber->addPipeline(ShapeKey::Filter::Builder().withTranslucent(), program, transparentState); + initOverlay3DPipelines(*_shapePlumber); } void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { @@ -260,18 +218,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon } DrawStencilDeferred::DrawStencilDeferred() : _shapePlumber{ std::make_shared() } { - const gpu::int8 STENCIL_OPAQUE = 1; - auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); - - auto state = std::make_shared(); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); - state->setColorWriteMask(0); - - _shapePlumber->addPipeline(ShapeKey::Filter::Builder(), program, state); + initStencilPipelines(*_shapePlumber); } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { @@ -411,166 +358,3 @@ void Blit::run(const SceneContextPointer& sceneContext, const RenderContextPoint } }); } - -void pipelineBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { - if (pipeline.locations->normalFittingMapUnit > -1) { - batch.setResourceTexture(pipeline.locations->normalFittingMapUnit, - DependencyManager::get()->getNormalFittingTexture()); - } -} - -void initDeferredPipelines(render::ShapePlumber& plumber) { - using Key = render::ShapeKey; - using ShaderPointer = gpu::ShaderPointer; - - auto addPipeline = [&plumber](const Key& key, const ShaderPointer& vertexShader, const ShaderPointer& pixelShader) { - auto state = std::make_shared(); - - // Cull backface - state->setCullMode(gpu::State::CULL_BACK); - - // Z test depends on transparency - state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); - - // Blend if transparent - state->setBlendFunction(key.isTranslucent(), - // For transparency, keep the highlight intensity - gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); - plumber.addPipeline(key, program, state, &pipelineBatchSetter); - - // Add a wireframe version - if (!key.isWireFrame()) { - auto wireFrameKey = Key::Builder(key).withWireframe(); - auto wireFrameState = std::make_shared(state->getValues()); - - wireFrameState->setFillMode(gpu::State::FILL_LINE); - - plumber.addPipeline(wireFrameKey, program, wireFrameState, &pipelineBatchSetter); - } - }; - - // Vertex shaders - auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); - auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); - auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); - auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); - auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); - auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); - auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - - // Pixel shaders - auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); - auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); - auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); - auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); - auto modelLightmapNormalMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)); - auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); - auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); - - // Fill the pipelineLib - addPipeline( - Key::Builder(), - modelVertex, modelPixel); - - addPipeline( - Key::Builder().withTangents(), - modelNormalMapVertex, modelNormalMapPixel); - - addPipeline( - Key::Builder().withSpecular(), - modelVertex, modelSpecularMapPixel); - - addPipeline( - Key::Builder().withTangents().withSpecular(), - modelNormalMapVertex, modelNormalSpecularMapPixel); - - - addPipeline( - Key::Builder().withTranslucent(), - modelVertex, modelTranslucentPixel); - // FIXME Ignore lightmap for translucents meshpart - addPipeline( - Key::Builder().withTranslucent().withLightmap(), - modelVertex, modelTranslucentPixel); - - addPipeline( - Key::Builder().withTangents().withTranslucent(), - modelNormalMapVertex, modelTranslucentPixel); - - addPipeline( - Key::Builder().withSpecular().withTranslucent(), - modelVertex, modelTranslucentPixel); - - addPipeline( - Key::Builder().withTangents().withSpecular().withTranslucent(), - modelNormalMapVertex, modelTranslucentPixel); - - - addPipeline( - Key::Builder().withLightmap(), - modelLightmapVertex, modelLightmapPixel); - - addPipeline( - Key::Builder().withLightmap().withTangents(), - modelLightmapNormalMapVertex, modelLightmapNormalMapPixel); - - addPipeline( - Key::Builder().withLightmap().withSpecular(), - modelLightmapVertex, modelLightmapSpecularMapPixel); - - addPipeline( - Key::Builder().withLightmap().withTangents().withSpecular(), - modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel); - - - addPipeline( - Key::Builder().withSkinned(), - skinModelVertex, modelPixel); - - addPipeline( - Key::Builder().withSkinned().withTangents(), - skinModelNormalMapVertex, modelNormalMapPixel); - - addPipeline( - Key::Builder().withSkinned().withSpecular(), - skinModelVertex, modelSpecularMapPixel); - - addPipeline( - Key::Builder().withSkinned().withTangents().withSpecular(), - skinModelNormalMapVertex, modelNormalSpecularMapPixel); - - - addPipeline( - Key::Builder().withSkinned().withTranslucent(), - skinModelVertex, modelTranslucentPixel); - - addPipeline( - Key::Builder().withSkinned().withTangents().withTranslucent(), - skinModelNormalMapVertex, modelTranslucentPixel); - - addPipeline( - Key::Builder().withSkinned().withSpecular().withTranslucent(), - skinModelVertex, modelTranslucentPixel); - - addPipeline( - Key::Builder().withSkinned().withTangents().withSpecular().withTranslucent(), - skinModelNormalMapVertex, modelTranslucentPixel); - - - addPipeline( - Key::Builder().withDepthOnly(), - modelShadowVertex, modelShadowPixel); - - - addPipeline( - Key::Builder().withSkinned().withDepthOnly(), - skinModelShadowVertex, modelShadowPixel); -} - diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp new file mode 100644 index 0000000000..a7e9081d9a --- /dev/null +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -0,0 +1,234 @@ + +// +// RenderPipelines.cpp +// render-utils/src/ +// +// Created by Zach Pomerantz on 1/28/2016. +// Copyright 2016 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 +#include + +#include "TextureCache.h" +#include "render/DrawTask.h" + +#include "model_vert.h" +#include "model_shadow_vert.h" +#include "model_normal_map_vert.h" +#include "model_lightmap_vert.h" +#include "model_lightmap_normal_map_vert.h" +#include "skin_model_vert.h" +#include "skin_model_shadow_vert.h" +#include "skin_model_normal_map_vert.h" + +#include "model_frag.h" +#include "model_shadow_frag.h" +#include "model_normal_map_frag.h" +#include "model_normal_specular_map_frag.h" +#include "model_specular_map_frag.h" +#include "model_lightmap_frag.h" +#include "model_lightmap_normal_map_frag.h" +#include "model_lightmap_normal_specular_map_frag.h" +#include "model_lightmap_specular_map_frag.h" +#include "model_translucent_frag.h" + +#include "overlay3D_vert.h" +#include "overlay3D_frag.h" + +#include "drawOpaqueStencil_frag.h" + +using namespace render; + +void initOverlay3DPipelines(ShapePlumber& plumber) { + auto vs = gpu::Shader::createVertex(std::string(overlay3D_vert)); + auto ps = gpu::Shader::createPixel(std::string(overlay3D_frag)); + auto program = gpu::Shader::createProgram(vs, ps); + + auto opaqueState = std::make_shared(); + opaqueState->setDepthTest(false); + opaqueState->setBlendFunction(false); + + plumber.addPipeline(ShapeKey::Filter::Builder().withOpaque(), program, opaqueState); +} + +void initStencilPipelines(ShapePlumber& plumber) { + const gpu::int8 STENCIL_OPAQUE = 1; + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)); + auto program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram((*program)); + + auto state = std::make_shared(); + state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); + state->setColorWriteMask(0); + + plumber.addPipeline(ShapeKey::Filter::Builder(), program, state); +} + +void pipelineBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { + if (pipeline.locations->normalFittingMapUnit > -1) { + batch.setResourceTexture(pipeline.locations->normalFittingMapUnit, + DependencyManager::get()->getNormalFittingTexture()); + } +} + +void initDeferredPipelines(render::ShapePlumber& plumber) { + using Key = render::ShapeKey; + using ShaderPointer = gpu::ShaderPointer; + + auto addPipeline = [&plumber](const Key& key, const ShaderPointer& vertexShader, const ShaderPointer& pixelShader) { + auto state = std::make_shared(); + + // Cull backface + state->setCullMode(gpu::State::CULL_BACK); + + // Z test depends on transparency + state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); + + // Blend if transparent + state->setBlendFunction(key.isTranslucent(), + // For transparency, keep the highlight intensity + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); + plumber.addPipeline(key, program, state, &pipelineBatchSetter); + + // Add a wireframe version + if (!key.isWireFrame()) { + auto wireFrameKey = Key::Builder(key).withWireframe(); + auto wireFrameState = std::make_shared(state->getValues()); + + wireFrameState->setFillMode(gpu::State::FILL_LINE); + + plumber.addPipeline(wireFrameKey, program, wireFrameState, &pipelineBatchSetter); + } + }; + + // Vertex shaders + auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); + auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); + auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); + auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); + auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); + auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); + auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); + auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); + + // Pixel shaders + auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); + auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); + auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); + auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); + auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); + auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); + auto modelLightmapNormalMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)); + auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); + auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); + + // Fill the pipelineLib + addPipeline( + Key::Builder(), + modelVertex, modelPixel); + + addPipeline( + Key::Builder().withTangents(), + modelNormalMapVertex, modelNormalMapPixel); + + addPipeline( + Key::Builder().withSpecular(), + modelVertex, modelSpecularMapPixel); + + addPipeline( + Key::Builder().withTangents().withSpecular(), + modelNormalMapVertex, modelNormalSpecularMapPixel); + + + addPipeline( + Key::Builder().withTranslucent(), + modelVertex, modelTranslucentPixel); + // FIXME Ignore lightmap for translucents meshpart + addPipeline( + Key::Builder().withTranslucent().withLightmap(), + modelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withTangents().withTranslucent(), + modelNormalMapVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSpecular().withTranslucent(), + modelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withTangents().withSpecular().withTranslucent(), + modelNormalMapVertex, modelTranslucentPixel); + + + addPipeline( + Key::Builder().withLightmap(), + modelLightmapVertex, modelLightmapPixel); + + addPipeline( + Key::Builder().withLightmap().withTangents(), + modelLightmapNormalMapVertex, modelLightmapNormalMapPixel); + + addPipeline( + Key::Builder().withLightmap().withSpecular(), + modelLightmapVertex, modelLightmapSpecularMapPixel); + + addPipeline( + Key::Builder().withLightmap().withTangents().withSpecular(), + modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel); + + + addPipeline( + Key::Builder().withSkinned(), + skinModelVertex, modelPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents(), + skinModelNormalMapVertex, modelNormalMapPixel); + + addPipeline( + Key::Builder().withSkinned().withSpecular(), + skinModelVertex, modelSpecularMapPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents().withSpecular(), + skinModelNormalMapVertex, modelNormalSpecularMapPixel); + + + addPipeline( + Key::Builder().withSkinned().withTranslucent(), + skinModelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents().withTranslucent(), + skinModelNormalMapVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSkinned().withSpecular().withTranslucent(), + skinModelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents().withSpecular().withTranslucent(), + skinModelNormalMapVertex, modelTranslucentPixel); + + + addPipeline( + Key::Builder().withDepthOnly(), + modelShadowVertex, modelShadowPixel); + + + addPipeline( + Key::Builder().withSkinned().withDepthOnly(), + skinModelShadowVertex, modelShadowPixel); +} + From e4ef3ae8697f8431c8f4191a43eda3b3b2f0d289 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Feb 2016 15:06:53 -0800 Subject: [PATCH 05/79] automatically save adjustments to offset of equipped items --- examples/attachedEntitiesManager.js | 37 ++++++++++++---------- examples/controllers/handControllerGrab.js | 10 +++--- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index f44564d6dc..01f8f861c9 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -116,6 +116,7 @@ function AttachedEntitiesManager() { manager.checkIfWearable(parsedMessage.grabbedEntity, parsedMessage.joint) // manager.saveAttachedEntities(); } else if (parsedMessage.action === 'shared-release') { + manager.updateRelativeOffsets(parsedMessage.grabbedEntity); // manager.saveAttachedEntities(); } else if (parsedMessage.action === 'equip') { // manager.saveAttachedEntities(); @@ -173,11 +174,12 @@ function AttachedEntitiesManager() { parentJointIndex: bestJointIndex }; - if (!this.avatarIsInDressingRoom() && - bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) { - // don't snap the entity to the preferred position if the avatar is in the dressing room. - wearProps.localPosition = bestJointOffset[0]; - wearProps.localRotation = bestJointOffset[1]; + if (bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) { + if (!this.avatarIsInDressingRoom()) { + // don't snap the entity to the preferred position if the avatar is in the dressing room. + wearProps.localPosition = bestJointOffset[0]; + wearProps.localRotation = bestJointOffset[1]; + } } Entities.editEntity(grabbedEntity, wearProps); } else if (props.parentID != NULL_UUID) { @@ -189,12 +191,19 @@ function AttachedEntitiesManager() { } } - this.updateRelativeOffsets = function(entityID, props) { + this.updateRelativeOffsets = function(entityID) { // save the preferred (current) relative position and rotation into the user-data of the entity - var wearableData = getEntityCustomData('wearable', entityID, DEFAULT_WEARABLE_DATA); - var currentJointName = MyAvatar.getJointNames()[props.parentJointIndex]; - wearableData.joints[currentJointName] = [props.localPosition, props.localRotation]; - setEntityCustomData('wearable', entityID, wearableData); + var props = Entities.getEntityProperties(entityID); + if (props.parentID == MyAvatar.sessionUUID) { + grabData = getEntityCustomData('grabKey', entityID, {}); + grabbableData = getEntityCustomData('grabbableKey', entityID, {}); + var wearableData = getEntityCustomData('wearable', entityID, DEFAULT_WEARABLE_DATA); + var currentJointName = MyAvatar.getJointNames()[props.parentJointIndex]; + wearableData.joints[currentJointName] = [props.localPosition, props.localRotation]; + setEntityCustomData('wearable', entityID, wearableData); + return true; + } + return false; } this.saveAttachedEntities = function() { @@ -203,12 +212,8 @@ function AttachedEntitiesManager() { var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE); for (i = 0; i < nearbyEntities.length; i++) { var entityID = nearbyEntities[i]; - var props = Entities.getEntityProperties(entityID); - if (props.parentID == MyAvatar.sessionUUID) { - grabData = getEntityCustomData('grabKey', entityID, {}); - grabbableData = getEntityCustomData('grabbableKey', entityID, {}); - this.updateRelativeOffsets(entityID, props); - props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them + if (this.updateRelativeOffsets(entityID)) { + var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them this.scrubProperties(props); saveData.push(props); } diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 838134cfff..842c54b86d 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1217,10 +1217,12 @@ function MyController(hand) { this.hasPresetOffsets = function() { var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}}); - var allowedJoints = wearableData.joints; - var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; - if (handJointName in allowedJoints) { - return true; + if ("joints" in wearableData) { + var allowedJoints = wearableData.joints; + var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; + if (handJointName in allowedJoints) { + return true; + } } } From b038e4b7e7b10e82b23a4b9f0b29a08ad174ed42 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 1 Feb 2016 16:48:00 -0800 Subject: [PATCH 06/79] calibrate using either the 1 or 2 button on each hand --- plugins/hifiSixense/src/SixenseManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index 3377aac14c..b23982f948 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -282,8 +282,8 @@ void SixenseManager::InputDevice::setDebugDrawCalibrated(bool flag) { // the calibration sequence is: // (1) reach arm straight out to the sides (xAxis is to the left) -// (2) press BUTTON_FWD on both hands and hold for one second -// (3) release both BUTTON_FWDs +// (2) press either BUTTON_1 or BUTTON_2 on both hands and hold for one second +// (3) release both buttons // // The code will: // (4) assume that the orb is on a flat surface (yAxis is UP) @@ -294,7 +294,8 @@ static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired static bool calibrationRequested(SixenseControllerData* controllers) { - return (controllers[0].buttons == BUTTON_FWD && controllers[1].buttons == BUTTON_FWD); + return (((controllers[0].buttons == BUTTON_1) || (controllers[0].buttons == BUTTON_2)) && + ((controllers[1].buttons == BUTTON_1) || (controllers[1].buttons == BUTTON_2))); } void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* controllers) { From 25fe8583c8c5ef6a6d26a9b0f27590537be9cf70 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 1 Feb 2016 16:48:15 -0800 Subject: [PATCH 07/79] decrease all volume --- .../DomainContent/CellScience/Scripts/playBackgroundAudio.js | 2 +- .../DomainContent/CellScience/Scripts/showButtonToPlaySound.js | 2 +- .../DomainContent/CellScience/Scripts/showIdentification.js | 2 +- unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js b/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js index 0cb348c3f4..4bde42fb38 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js @@ -20,7 +20,7 @@ stereo: true, loop: true, localOnly: true, - volume: 0.5 + volume: 0.2 }; this.sound = SoundCache.getSound(self.soundURL); diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js b/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js index e505a532e3..7e51dcd9d1 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js @@ -24,7 +24,7 @@ stereo: true, loop: false, localOnly: true, - volume: 0.5 + volume: 0.2 }; this.sound = SoundCache.getSound(this.soundURL); } diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js b/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js index c0b0cb54d4..bfc6c70292 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js @@ -22,7 +22,7 @@ stereo: true, loop: false, localOnly: true, - volume: 0.5, + volume: 0.2, position: this.position }; this.sound = SoundCache.getSound(this.soundURL); diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js b/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js index d69f33e7c9..c644cbe4f9 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js @@ -25,7 +25,7 @@ loop: false, localOnly: false, position: this.position, - volume: 0.5 + volume: 0.2 }; this.teleportSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/whoosh.wav"); //print('Script.clearTimeout PRELOADING A ZOOM ENTITY') From 822a64a08b7366923e01705369274f139c4b91f0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 1 Feb 2016 17:16:27 -0800 Subject: [PATCH 08/79] decrease volume --- .../DomainContent/CellScience/Scripts/playBackgroundAudio.js | 2 +- .../DomainContent/CellScience/importCellScience.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js b/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js index 4bde42fb38..e709f6e06e 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js @@ -8,7 +8,7 @@ (function() { var self = this; var baseURL = "https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/"; - var version = 8; + var version = 9; this.preload = function(entityId) { self.soundPlaying = false; self.entityId = entityId; diff --git a/unpublishedScripts/DomainContent/CellScience/importCellScience.js b/unpublishedScripts/DomainContent/CellScience/importCellScience.js index b456c4a1ce..58888584a9 100644 --- a/unpublishedScripts/DomainContent/CellScience/importCellScience.js +++ b/unpublishedScripts/DomainContent/CellScience/importCellScience.js @@ -5,7 +5,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var version = 1003; +var version = 1004; var cellLayout; var baseLocation = "https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/"; From 0b6cfbe50aef5461ac6b60b478ef812468947da7 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 1 Feb 2016 17:48:31 -0800 Subject: [PATCH 09/79] Only connect render config if dirty signal present --- libraries/render/src/render/Task.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index 148117eed6..93f3681c1c 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -257,7 +257,13 @@ public: QConfigPointer config = _jobs.back().getConfiguration(); config->setParent(_config.get()); config->setObjectName(name.c_str()); - QObject::connect(config.get(), SIGNAL(dirty()), _config.get(), SLOT(refresh())); + + // Connect dirty->refresh if defined + static const char* DIRTY_SIGNAL = "dirty()"; + if (config->metaObject()->indexOfSignal(DIRTY_SIGNAL) != -1) { + QObject::connect(config.get(), SIGNAL(dirty()), _config.get(), SLOT(refresh())); + } + return _jobs.back().getOutput(); } template const Varying addJob(std::string name, A&&... args) { From 2501be16cbc3bf5760699b1d1e7f8562da82526b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Feb 2016 18:11:27 -0800 Subject: [PATCH 10/79] base palm position on finger joint rather than an offset from hand joint. double size of near-grab sphere --- examples/controllers/handControllerGrab.js | 42 ++++++++++++++++++++-- interface/src/avatar/Avatar.cpp | 12 +++++-- interface/src/avatar/SkeletonModel.cpp | 16 +++++++++ interface/src/avatar/SkeletonModel.h | 3 ++ 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 074b72c9f4..3695c6c3f8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -69,13 +69,14 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // near grabbing // -var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected +var GRAB_RADIUS = 0.06; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed +var SHOW_GRAB_SPHERE = false; // draw a green sphere to show the grab search position and size // // equip @@ -256,6 +257,7 @@ function MyController(hand) { //for visualizations this.overlayLine = null; this.particleBeamObject = null; + this.grabSphere = null; //for lights this.spotlight = null; @@ -336,6 +338,7 @@ function MyController(hand) { } this.setState = function(newState) { + this.grabSphereOff(); if (WANT_DEBUG || WANT_DEBUG_STATE) { print("STATE (" + this.hand + "): " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); @@ -416,6 +419,37 @@ function MyController(hand) { } } + this.grabSphereOn = function() { + var color = {red: 0, green: 255, blue: 0}; + if (this.grabSphere === null) { + var sphereProperties = { + position: this.getHandPosition(), + size: GRAB_RADIUS*2, + color: color, + alpha: 0.1, + solid: true, + visible: true + } + this.grabSphere = Overlays.addOverlay("sphere", sphereProperties); + } else { + Overlays.editOverlay(this.grabSphere, { + position: this.getHandPosition(), + size: GRAB_RADIUS*2, + color: color, + alpha: 0.1, + solid: true, + visible: true + }); + } + } + + this.grabSphereOff = function() { + if (this.grabSphere !== null) { + Overlays.deleteOverlay(this.grabSphere); + this.grabSphere = null; + } + }; + this.overlayLineOn = function(closePoint, farPoint, color) { if (this.overlayLine === null) { var lineProperties = { @@ -644,7 +678,6 @@ function MyController(hand) { this.searchSphereDistance = DEFAULT_SEARCH_SPHERE_DISTANCE; this.intersectionDistance = 0.0; } - }; this.particleBeamOff = function() { @@ -749,6 +782,11 @@ function MyController(hand) { // the trigger is being pressed, so do a ray test to see what we are hitting var handPosition = this.getHandPosition(); + + if (SHOW_GRAB_SPHERE) { + this.grabSphereOn(); + } + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index a552b77f1f..786e409d92 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1132,8 +1132,12 @@ glm::quat Avatar::getRightPalmRotation() const { glm::vec3 Avatar::getUncachedLeftPalmPosition() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat leftPalmRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftPalmRotation); glm::vec3 leftPalmPosition; + if (_skeletonModel.getLeftGrabPosition(leftPalmPosition)) { + return leftPalmPosition; + } + // avatar didn't have a LeftHandMiddle1 joint, fall back on this: + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftPalmRotation); getSkeletonModel().getLeftHandPosition(leftPalmPosition); leftPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftPalmRotation); return leftPalmPosition; @@ -1149,8 +1153,12 @@ glm::quat Avatar::getUncachedLeftPalmRotation() const { glm::vec3 Avatar::getUncachedRightPalmPosition() const { assert(QThread::currentThread() == thread()); // main thread access only glm::quat rightPalmRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightPalmRotation); glm::vec3 rightPalmPosition; + if (_skeletonModel.getRightGrabPosition(rightPalmPosition)) { + return rightPalmPosition; + } + // avatar didn't have a RightHandMiddle1 joint, fall back on this: + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightPalmRotation); getSkeletonModel().getRightHandPosition(rightPalmPosition); rightPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightPalmRotation); return rightPalmPosition; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 6198a1b0a0..7a871236c5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -235,6 +235,22 @@ void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { } } +bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const { + int index = _rig->indexOfJoint("LeftHandMiddle1"); + if (index >= 0) { + return getJointPositionInWorldFrame(index, position); + } + return false; +} + +bool SkeletonModel::getRightGrabPosition(glm::vec3& position) const { + int index = _rig->indexOfJoint("RightHandMiddle1"); + if (index >= 0) { + return getJointPositionInWorldFrame(index, position); + } + return false; +} + bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const { return getJointPositionInWorldFrame(getLeftHandJointIndex(), position); } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 7541a002dc..b57d54020d 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -39,6 +39,9 @@ public: /// Returns the index of the right hand joint, or -1 if not found. int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; } + bool getLeftGrabPosition(glm::vec3& position) const; + bool getRightGrabPosition(glm::vec3& position) const; + /// Retrieve the position of the left hand /// \return true whether or not the position was found bool getLeftHandPosition(glm::vec3& position) const; From 1e69c12e87c1eb20d38cf5cdb80200e252bfb889 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Feb 2016 18:47:26 -0800 Subject: [PATCH 11/79] move grab position to front of palm --- interface/src/avatar/SkeletonModel.cpp | 46 ++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7a871236c5..1163b4dff0 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -236,17 +236,51 @@ void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { } bool SkeletonModel::getLeftGrabPosition(glm::vec3& position) const { - int index = _rig->indexOfJoint("LeftHandMiddle1"); - if (index >= 0) { - return getJointPositionInWorldFrame(index, position); + int knuckleIndex = _rig->indexOfJoint("LeftHandMiddle1"); + int handIndex = _rig->indexOfJoint("LeftHand"); + if (knuckleIndex >= 0 && handIndex >= 0) { + glm::quat handRotation; + glm::vec3 knucklePosition; + glm::vec3 handPosition; + if (!getJointPositionInWorldFrame(knuckleIndex, knucklePosition)) { + return false; + } + if (!getJointPositionInWorldFrame(handIndex, handPosition)) { + return false; + } + if (!getJointRotationInWorldFrame(handIndex, handRotation)) { + return false; + } + float halfPalmLength = glm::distance(knucklePosition, handPosition) * 0.5f; + // z azis is standardized to be out of the palm. move from the knuckle-joint away from the palm + // by 1/2 the palm length. + position = knucklePosition + handRotation * (glm::vec3(0.0f, 0.0f, 1.0f) * halfPalmLength); + return true; } return false; } bool SkeletonModel::getRightGrabPosition(glm::vec3& position) const { - int index = _rig->indexOfJoint("RightHandMiddle1"); - if (index >= 0) { - return getJointPositionInWorldFrame(index, position); + int knuckleIndex = _rig->indexOfJoint("RightHandMiddle1"); + int handIndex = _rig->indexOfJoint("RightHand"); + if (knuckleIndex >= 0 && handIndex >= 0) { + glm::quat handRotation; + glm::vec3 knucklePosition; + glm::vec3 handPosition; + if (!getJointPositionInWorldFrame(knuckleIndex, knucklePosition)) { + return false; + } + if (!getJointPositionInWorldFrame(handIndex, handPosition)) { + return false; + } + if (!getJointRotationInWorldFrame(handIndex, handRotation)) { + return false; + } + float halfPalmLength = glm::distance(knucklePosition, handPosition) * 0.5f; + // z azis is standardized to be out of the palm. move from the knuckle-joint away from the palm + // by 1/2 the palm length. + position = knucklePosition + handRotation * (glm::vec3(0.0f, 0.0f, 1.0f) * halfPalmLength); + return true; } return false; } From eff02d3e3ac4bb480ca6beb1283b44cd5d8d2bd2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Sun, 10 Jan 2016 10:11:47 -0800 Subject: [PATCH 12/79] Fix Agents bidding on simulation ownership --- assignment-client/src/Agent.cpp | 2 +- assignment-client/src/AssignmentClient.cpp | 2 +- interface/src/Application.cpp | 2 +- libraries/entities/src/EntityScriptingInterface.cpp | 7 ++++--- libraries/entities/src/EntityScriptingInterface.h | 5 +++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 07d15c9e26..3ff7eafd6b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -469,7 +469,7 @@ void Agent::aboutToFinish() { } // our entity tree is going to go away so tell that to the EntityScriptingInterface - DependencyManager::get()->setEntityTree(NULL); + DependencyManager::get()->setEntityTree(nullptr); // cleanup the AssetClient thread QThread* assetThread = DependencyManager::get()->thread(); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 3bd38c4ae7..85a2c95b4d 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -60,7 +60,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); auto animationCache = DependencyManager::set(); - auto entityScriptingInterface = DependencyManager::set(); + auto entityScriptingInterface = DependencyManager::set(false); DependencyManager::registerInheritance(); auto actionFactory = DependencyManager::set(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ab5edec527..f7d2bd9ada 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -348,7 +348,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); + DependencyManager::set(true); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8fd7be912e..6bfcc66f1e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -25,8 +25,9 @@ #include "ZoneEntityItem.h" -EntityScriptingInterface::EntityScriptingInterface() : - _entityTree(NULL) +EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) : + _entityTree(NULL), + _bidOnSimulationOwnership(bidOnSimulationOwnership) { auto nodeList = DependencyManager::get(); connect(nodeList.data(), &NodeList::canAdjustLocksChanged, this, &EntityScriptingInterface::canAdjustLocksChanged); @@ -255,7 +256,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& properties.setType(entity->getType()); bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges(); bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges; - if (hasPhysicsChanges) { + if (_bidOnSimulationOwnership && hasPhysicsChanges) { auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 641da7518e..934a194360 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -58,7 +58,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT public: - EntityScriptingInterface(); + EntityScriptingInterface(bool bidOnSimulationOwnership); EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); } virtual NodeType_t getServerNodeType() const { return NodeType::EntityServer; } @@ -204,7 +204,8 @@ private: bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); EntityTreePointer _entityTree; - EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; + EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr }; + bool _bidOnSimulationOwnership { false }; }; #endif // hifi_EntityScriptingInterface_h From e9fd439ffd0210b0720b084bcecf9a1b91c07eb3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 28 Jan 2016 18:27:49 -0800 Subject: [PATCH 13/79] Added inAir animations --- .../defaultAvatar_full/avatar-animation.json | 80 +++++++++++++++++-- interface/src/avatar/SkeletonModel.cpp | 9 ++- libraries/animation/src/Rig.cpp | 38 ++++++++- libraries/animation/src/Rig.h | 11 ++- libraries/physics/src/CharacterController.h | 1 + 5 files changed, 125 insertions(+), 14 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 42ff64abc6..46912120db 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -247,7 +247,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -263,7 +264,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -278,7 +280,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -293,7 +296,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -308,7 +312,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -323,7 +328,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -338,7 +344,8 @@ { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -353,7 +360,8 @@ { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningRight", "state": "turnRight" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -385,8 +393,18 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ + { "var": "isAway", "state": "awayIntro" }, { "var": "isNotFlying", "state": "idle" } ] + }, + { + "id": "inAir", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isNotInAir", "state": "idle" } + ] } ] }, @@ -685,6 +703,52 @@ "loopFlag": true }, "children": [] + }, + { + "id": "inAir", + "type": "blendLinear", + "data": { + "alpha": 0.0, + "alphaVar": "inAirAlpha" + }, + "children": [ + { + "id": "inAirPreApex", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 0.0, + "endFrame": 0.0, + "timeScale": 0.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "inAirApex", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 6.0, + "endFrame": 6.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "inAirPostApex", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 11.0, + "endFrame": 11.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] } ] } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 6198a1b0a0..50cbbbbb00 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -133,7 +133,14 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); - _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), myAvatar->isHovering()); + Rig::CharacterControllerState ccState = Rig::CharacterControllerState::Ground; + if (myAvatar->getCharacterController()->isHovering()) { + ccState = Rig::CharacterControllerState::Hover; + } else if (myAvatar->getCharacterController()->isJumping()) { + ccState = Rig::CharacterControllerState::Jump; + } + + _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), ccState); // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4d72bfbaea..94bc23cf06 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -504,7 +504,7 @@ static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s -void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, bool isHovering) { +void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -572,11 +572,16 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec - if (isHovering) { + if (ccState == CharacterControllerState::Hover) { if (_desiredState != RigRole::Hover) { _desiredStateAge = 0.0f; } _desiredState = RigRole::Hover; + } else if (ccState == CharacterControllerState::Jump) { + if (_desiredState != RigRole::Jump) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Jump; } else { float moveThresh; if (_state != RigRole::Move) { @@ -662,6 +667,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); } } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { @@ -682,6 +689,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); } else if (_state == RigRole::Idle ) { // default anim vars to notMoving and notTurning _animVars.set("isMovingForward", false); @@ -694,7 +703,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - } else { + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Hover) { // flying. _animVars.set("isMovingForward", false); _animVars.set("isMovingBackward", false); @@ -706,6 +717,27 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", true); _animVars.set("isNotFlying", false); + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Jump) { + // jumping in-air + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + _animVars.set("isFlying", false); + _animVars.set("isNotFlying", true); + _animVars.set("isInAir", true); + _animVars.set("isNotInAir", false); + + // compute blend based on velocity + const float JUMP_SPEED = 3.5f; + float alpha = glm::clamp(-worldVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f; + _animVars.set("inAirAlpha", alpha); } t += deltaTime; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index cf96c6dc16..50d775d7f5 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -73,6 +73,12 @@ public: glm::quat rightOrientation = glm::quat(); // rig space (z forward) }; + enum class CharacterControllerState { + Ground = 0, + Jump, + Hover + }; + virtual ~Rig() {} void destroyAnimGraph(); @@ -141,7 +147,7 @@ public: glm::mat4 getJointTransform(int jointIndex) const; // Start or stop animations as needed. - void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, bool isHovering); + void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState); // Regardless of who started the animations or how many, update the joints. void updateAnimations(float deltaTime, glm::mat4 rootTransform); @@ -271,7 +277,8 @@ public: Idle = 0, Turn, Move, - Hover + Hover, + Jump }; RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index c1c64a1a02..3c9fc476ca 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -75,6 +75,7 @@ public: glm::vec3 getLinearVelocity() const; + bool isJumping() const { return _isJumping; } bool isHovering() const { return _isHovering; } void setHovering(bool enabled); From 61c55ebf6c96857559280116a830dec7aa5c410a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 29 Jan 2016 11:57:14 -0800 Subject: [PATCH 14/79] Updated character controller with a state enumeration Also, adjusted checkForSupport logic such that very slanted walls are not considered support. --- interface/src/avatar/MyAvatar.cpp | 7 +-- interface/src/avatar/MyAvatar.h | 2 - .../src/avatar/MyCharacterController.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 20 ++++--- libraries/animation/src/Rig.cpp | 8 +-- libraries/animation/src/Rig.h | 4 +- libraries/physics/src/CharacterController.cpp | 60 +++++++++---------- libraries/physics/src/CharacterController.h | 17 +++--- 8 files changed, 62 insertions(+), 58 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0c0b51bda2..0aadaed150 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1510,7 +1510,8 @@ void MyAvatar::updatePosition(float deltaTime) { // rotate velocity into camera frame glm::quat rotation = getHead()->getCameraOrientation(); glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity; - glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering()); + bool isHovering = _characterController.getState() == CharacterController::State::Hover; + glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering); newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity); // rotate back into world-frame @@ -1579,10 +1580,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } -bool MyAvatar::isHovering() const { - return _characterController.isHovering(); -} - void MyAvatar::increaseSize() { if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { _targetScale *= (1.0f + SCALING_RATIO); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 760f390de9..ac77784077 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -239,8 +239,6 @@ public: glm::quat getCustomListenOrientation() { return _customListenOrientation; } void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; } - bool isHovering() const; - public slots: void increaseSize(); void decreaseSize(); diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index c7f2945757..07156e9294 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -67,7 +67,7 @@ void MyCharacterController::updateShapeIfNecessary() { _rigidBody->setAngularFactor(0.0f); _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatar->getOrientation()), glmToBullet(_avatar->getPosition()))); - if (_isHovering) { + if (_state == State::Hover) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); } else { _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 50cbbbbb00..c8f14578b4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -72,6 +72,18 @@ void SkeletonModel::initJointStates() { emit skeletonLoaded(); } +Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { + switch (state) { + default: + case CharacterController::State::Ground: + return Rig::CharacterControllerState::Ground; + case CharacterController::State::InAir: + return Rig::CharacterControllerState::InAir; + case CharacterController::State::Hover: + return Rig::CharacterControllerState::Hover; + }; +} + // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Head* head = _owningAvatar->getHead(); @@ -133,13 +145,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); - Rig::CharacterControllerState ccState = Rig::CharacterControllerState::Ground; - if (myAvatar->getCharacterController()->isHovering()) { - ccState = Rig::CharacterControllerState::Hover; - } else if (myAvatar->getCharacterController()->isJumping()) { - ccState = Rig::CharacterControllerState::Jump; - } - + Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), ccState); // evaluate AnimGraph animation and update jointStates. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 94bc23cf06..33c8f33ac8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -577,11 +577,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge = 0.0f; } _desiredState = RigRole::Hover; - } else if (ccState == CharacterControllerState::Jump) { - if (_desiredState != RigRole::Jump) { + } else if (ccState == CharacterControllerState::InAir) { + if (_desiredState != RigRole::InAir) { _desiredStateAge = 0.0f; } - _desiredState = RigRole::Jump; + _desiredState = RigRole::InAir; } else { float moveThresh; if (_state != RigRole::Move) { @@ -719,7 +719,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotFlying", false); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); - } else if (_state == RigRole::Jump) { + } else if (_state == RigRole::InAir) { // jumping in-air _animVars.set("isMovingForward", false); _animVars.set("isMovingBackward", false); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 50d775d7f5..dc07a32efd 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -75,7 +75,7 @@ public: enum class CharacterControllerState { Ground = 0, - Jump, + InAir, Hover }; @@ -278,7 +278,7 @@ public: Turn, Move, Hover, - Jump + InAir }; RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index a30feec150..055f846793 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,4 +1,4 @@ -// + // // CharacterControllerInterface.cpp // libraries/physcis/src // @@ -51,10 +51,7 @@ CharacterController::CharacterController() { _followDesiredBodyTransform.setIdentity(); _followTimeRemaining = 0.0f; _jumpSpeed = JUMP_SPEED; - _isOnGround = false; - _isJumping = false; - _isFalling = false; - _isHovering = true; + _state = State::Hover; _isPushingUp = false; _jumpToHoverStart = 0; _followTime = 0.0f; @@ -107,6 +104,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } } +static const float COS_PI_OVER_THREE = cosf(PI / 3.0f); + bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) const { int numManifolds = collisionWorld->getDispatcher()->getNumManifolds(); for (int i = 0; i < numManifolds; i++) { @@ -119,8 +118,10 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) cons btManifoldPoint& pt = contactManifold->getContactPoint(j); // check to see if contact point is touching the bottom sphere of the capsule. + // and the contact normal is not slanted too much. float contactPointY = (obA == _rigidBody) ? pt.m_localPointA.getY() : pt.m_localPointB.getY(); - if (contactPointY < -_halfHeight) { + btVector3 normal = (obA == _rigidBody) ? pt.m_normalWorldOnB : -pt.m_normalWorldOnB; + if (contactPointY < -_halfHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) { return true; } } @@ -165,7 +166,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { } const btScalar MIN_SPEED = 0.001f; - if (_isHovering) { + if (_state == State::Hover) { if (desiredSpeed < MIN_SPEED) { if (actualSpeed < MIN_SPEED) { _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); @@ -255,9 +256,9 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::jump() { // check for case where user is holding down "jump" key... - // we'll eventually tansition to "hover" - if (!_isJumping) { - if (!_isHovering) { + // we'll eventually transition to "hover" + if (_state != State::InAir) { + if (_state != State::Hover) { _jumpToHoverStart = usecTimestampNow(); _pendingFlags |= PENDING_FLAG_JUMP; } @@ -266,7 +267,7 @@ void CharacterController::jump() { const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { _isPushingUp = true; - setHovering(true); + setState(State::Hover); } } } @@ -276,19 +277,19 @@ bool CharacterController::onGround() const { return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport; } -void CharacterController::setHovering(bool hover) { - if (hover != _isHovering) { - _isHovering = hover; - _isJumping = false; - +void CharacterController::setState(State desiredState) { + if (desiredState == State::Hover && _state != State::Hover) { + // hover enter if (_rigidBody) { - if (hover) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); - } + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } + } else if (_state == State::Hover && desiredState != State::Hover) { + // hover exit + if (_rigidBody) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } } + _state = desiredState; } void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { @@ -335,9 +336,8 @@ void CharacterController::setEnabled(bool enabled) { _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; } _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; - _isOnGround = false; } - setHovering(true); + setState(State::Hover); _enabled = enabled; } } @@ -345,7 +345,7 @@ void CharacterController::setEnabled(bool enabled) { void CharacterController::updateUpAxis(const glm::quat& rotation) { btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (!_isHovering) { + if (_state != State::Hover) { const btScalar MIN_UP_ERROR = 0.01f; if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); @@ -425,23 +425,23 @@ void CharacterController::preSimulation() { if (rayCallback.hasHit()) { _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; const btScalar MIN_HOVER_HEIGHT = 3.0f; - if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { - setHovering(false); + if (_state == State::Hover && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { + setState(State::InAir); } // TODO: use collision events rather than ray-trace test to disable jumping const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) { - _isJumping = false; + if (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport) { + setState(State::Ground); } } else if (!_hasSupport) { _floorDistance = FLT_MAX; - setHovering(true); + setState(State::Hover); } if (_pendingFlags & PENDING_FLAG_JUMP) { _pendingFlags &= ~ PENDING_FLAG_JUMP; if (onGround()) { - _isJumping = true; + setState(State::InAir); btVector3 velocity = _rigidBody->getLinearVelocity(); velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 3c9fc476ca..c8b360237d 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -75,9 +75,13 @@ public: glm::vec3 getLinearVelocity() const; - bool isJumping() const { return _isJumping; } - bool isHovering() const { return _isHovering; } - void setHovering(bool enabled); + enum class State { + Ground = 0, + InAir, + Hover + }; + + State getState() const { return _state; } void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); @@ -87,6 +91,8 @@ public: bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); protected: + void setState(State state); + void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld) const; @@ -117,10 +123,7 @@ protected: btQuaternion _followAngularDisplacement; bool _enabled; - bool _isOnGround; - bool _isJumping; - bool _isFalling; - bool _isHovering; + State _state; bool _isPushingUp; btDynamicsWorld* _dynamicsWorld { nullptr }; From 2936811484541b5b6133f8e1560393a70a3a8e56 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 29 Jan 2016 18:05:18 -0800 Subject: [PATCH 15/79] Added takeoff animation, WIP Character controller still has some jump/in-air bugs. --- .../defaultAvatar_full/avatar-animation.json | 39 ++++++++++++++++--- interface/src/avatar/SkeletonModel.cpp | 2 + libraries/animation/src/Rig.cpp | 35 +++++++++++++++++ libraries/animation/src/Rig.h | 2 + libraries/physics/src/CharacterController.cpp | 33 ++++++++++------ libraries/physics/src/CharacterController.h | 2 + 6 files changed, 97 insertions(+), 16 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 46912120db..37da0c37cc 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -248,6 +248,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -265,6 +266,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -281,6 +283,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -297,6 +300,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -313,6 +317,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -329,6 +334,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -345,6 +351,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -361,6 +368,7 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -397,10 +405,19 @@ { "var": "isNotFlying", "state": "idle" } ] }, + { + "id": "takeoff", + "interpTarget": 0, + "interpDuration": 6, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isNotTakeoff", "state": "inAir" } + ] + }, { "id": "inAir", - "interpTarget": 3, - "interpDuration": 3, + "interpTarget": 0, + "interpDuration": 6, "transitions": [ { "var": "isAway", "state": "awayIntro" }, { "var": "isNotInAir", "state": "idle" } @@ -704,6 +721,18 @@ }, "children": [] }, + { + "id": "takeoff", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_takeoff.fbx", + "startFrame": 1.0, + "endFrame": 2.5, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, { "id": "inAir", "type": "blendLinear", @@ -720,7 +749,7 @@ "startFrame": 0.0, "endFrame": 0.0, "timeScale": 0.0, - "loopFlag": true + "loopFlag": false }, "children": [] }, @@ -732,7 +761,7 @@ "startFrame": 6.0, "endFrame": 6.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": false }, "children": [] }, @@ -744,7 +773,7 @@ "startFrame": 11.0, "endFrame": 11.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": false }, "children": [] } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c8f14578b4..92bdef686d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -77,6 +77,8 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle default: case CharacterController::State::Ground: return Rig::CharacterControllerState::Ground; + case CharacterController::State::Takeoff: + return Rig::CharacterControllerState::Takeoff; case CharacterController::State::InAir: return Rig::CharacterControllerState::InAir; case CharacterController::State::Hover: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 33c8f33ac8..9e811fa358 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -582,6 +582,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge = 0.0f; } _desiredState = RigRole::InAir; + } else if (ccState == CharacterControllerState::Takeoff) { + if (_desiredState != RigRole::Takeoff) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Takeoff; } else { float moveThresh; if (_state != RigRole::Move) { @@ -667,6 +672,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); } @@ -689,8 +696,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Idle ) { // default anim vars to notMoving and notTurning _animVars.set("isMovingForward", false); @@ -703,8 +713,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Hover) { // flying. _animVars.set("isMovingForward", false); @@ -717,8 +730,28 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", true); _animVars.set("isNotFlying", false); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); + + } else if (_state == RigRole::Takeoff) { + // jumping in-air + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + _animVars.set("isFlying", false); + _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", true); + _animVars.set("isNotTakeoff", false); + _animVars.set("isInAir", true); + _animVars.set("isNotInAir", false); + } else if (_state == RigRole::InAir) { // jumping in-air _animVars.set("isMovingForward", false); @@ -731,6 +764,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", true); _animVars.set("isNotInAir", false); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index dc07a32efd..e12af16e41 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -75,6 +75,7 @@ public: enum class CharacterControllerState { Ground = 0, + Takeoff, InAir, Hover }; @@ -278,6 +279,7 @@ public: Turn, Move, Hover, + Takeoff, InAir }; RigRole _state { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 055f846793..93b924d9bc 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -54,6 +54,7 @@ CharacterController::CharacterController() { _state = State::Hover; _isPushingUp = false; _jumpToHoverStart = 0; + _takeoffToInAirStart = 0; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity(); @@ -257,18 +258,18 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::jump() { // check for case where user is holding down "jump" key... // we'll eventually transition to "hover" - if (_state != State::InAir) { - if (_state != State::Hover) { - _jumpToHoverStart = usecTimestampNow(); - _pendingFlags |= PENDING_FLAG_JUMP; - } - } else { + if (_state == State::Hover) { quint64 now = usecTimestampNow(); const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { _isPushingUp = true; setState(State::Hover); } + } else { + if (_state != State::Takeoff) { + _jumpToHoverStart = usecTimestampNow(); + _pendingFlags |= PENDING_FLAG_JUMP; + } } } @@ -430,7 +431,7 @@ void CharacterController::preSimulation() { } // TODO: use collision events rather than ray-trace test to disable jumping const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport) { + if (_state != State::Takeoff && (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport)) { setState(State::Ground); } } else if (!_hasSupport) { @@ -438,15 +439,25 @@ void CharacterController::preSimulation() { setState(State::Hover); } + quint64 now = usecTimestampNow(); + if (_pendingFlags & PENDING_FLAG_JUMP) { _pendingFlags &= ~ PENDING_FLAG_JUMP; if (onGround()) { - setState(State::InAir); - btVector3 velocity = _rigidBody->getLinearVelocity(); - velocity += _jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); + setState(State::Takeoff); + _takeoffToInAirStart = now; } } + + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; + if (_state == State::Takeoff && (now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { + setState(State::InAir); + + _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; + btVector3 velocity = _rigidBody->getLinearVelocity(); + velocity += _jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } } _followTime = 0.0f; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index c8b360237d..5470104a1a 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -77,6 +77,7 @@ public: enum class State { Ground = 0, + Takeoff, InAir, Hover }; @@ -107,6 +108,7 @@ protected: glm::vec3 _boxScale; // used to compute capsule shape + quint64 _takeoffToInAirStart; quint64 _jumpToHoverStart; btScalar _halfHeight; From 47f3ce3786e97f744d7338bd5cc694511df05891 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 Feb 2016 14:18:30 -0800 Subject: [PATCH 16/79] CharacterController jump is more reliable. --- libraries/animation/src/Rig.cpp | 7 ++ libraries/physics/src/CharacterController.cpp | 80 +++++++++++++------ 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9e811fa358..64935691c8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -624,6 +624,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; + // Skip hystersis timer for jump transitions. + if (_desiredState == RigRole::Takeoff) { + _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; + } else if (_state == RigRole::InAir && _desiredState != RigRole::InAir) { + _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; + } + if ((_desiredStateAge >= STATE_CHANGE_HYSTERESIS_TIMER) && _desiredState != _state) { _state = _desiredState; _desiredStateAge = 0.0f; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 93b924d9bc..ad6c9545d2 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,4 +1,4 @@ - // +// // CharacterControllerInterface.cpp // libraries/physcis/src // @@ -15,11 +15,14 @@ #include "PhysicsCollisionGroups.h" #include "ObjectMotionState.h" +#include "PhysicsLogging.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; +#define DEBUG_STATE_CHANGE + // helper class for simple ray-traces from character class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback { public: @@ -220,7 +223,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. // Rather then add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). - // These two computations must be kept in sync. + const float MINIMUM_TIME_REMAINING = 0.005f; const float MAX_DISPLACEMENT = 0.5f * _radius; _followTimeRemaining -= dt; @@ -258,18 +261,21 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::jump() { // check for case where user is holding down "jump" key... // we'll eventually transition to "hover" - if (_state == State::Hover) { - quint64 now = usecTimestampNow(); - const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); - if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { - _isPushingUp = true; - setState(State::Hover); - } - } else { - if (_state != State::Takeoff) { - _jumpToHoverStart = usecTimestampNow(); + if (_state != State::Takeoff) { + if (_state == State::InAir) { + quint64 now = usecTimestampNow(); + if (!_isPushingUp) { + _isPushingUp = true; + _jumpToHoverStart = now; + } + const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; + if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { + setState(State::Hover); + } + } else { _pendingFlags |= PENDING_FLAG_JUMP; } + } } @@ -278,19 +284,41 @@ bool CharacterController::onGround() const { return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport; } -void CharacterController::setState(State desiredState) { - if (desiredState == State::Hover && _state != State::Hover) { - // hover enter - if (_rigidBody) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } - } else if (_state == State::Hover && desiredState != State::Hover) { - // hover exit - if (_rigidBody) { - _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); - } +#ifdef DEBUG_STATE_CHANGE +static const char* stateToStr(CharacterController::State state) { + switch (state) { + case CharacterController::State::Ground: + return "Ground"; + case CharacterController::State::Takeoff: + return "Takeoff"; + case CharacterController::State::InAir: + return "InAir"; + case CharacterController::State::Hover: + return "Hover"; + default: + return "Unknown"; + } +} +#endif // #ifdef DEBUG_STATE_CHANGE + +void CharacterController::setState(State desiredState) { + if (desiredState != _state) { +#ifdef DEBUG_STATE_CHANGE + qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "<-" << stateToStr(_state); +#endif + if (desiredState == State::Hover && _state != State::Hover) { + // hover enter + if (_rigidBody) { + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } + } else if (_state == State::Hover && desiredState != State::Hover) { + // hover exit + if (_rigidBody) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } + } + _state = desiredState; } - _state = desiredState; } void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { @@ -411,6 +439,7 @@ void CharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { // slam body to where it is supposed to be _rigidBody->setWorldTransform(_characterBodyTransform); + btVector3 velocity = _rigidBody->getLinearVelocity(); // scan for distant floor // rayStart is at center of bottom sphere @@ -431,7 +460,7 @@ void CharacterController::preSimulation() { } // TODO: use collision events rather than ray-trace test to disable jumping const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_state != State::Takeoff && (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport)) { + if (_state != State::Takeoff && (velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { setState(State::Ground); } } else if (!_hasSupport) { @@ -454,7 +483,6 @@ void CharacterController::preSimulation() { setState(State::InAir); _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; - btVector3 velocity = _rigidBody->getLinearVelocity(); velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } From 8b5cf3e49abbd8f4d6f32edb1f1f06cbd8989b4b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 Feb 2016 20:43:41 -0800 Subject: [PATCH 17/79] CharacterController: refined state machine. --- libraries/physics/src/CharacterController.cpp | 201 +++++++++--------- libraries/physics/src/CharacterController.h | 6 +- 2 files changed, 109 insertions(+), 98 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index ad6c9545d2..f241be8b35 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -56,7 +56,8 @@ CharacterController::CharacterController() { _jumpSpeed = JUMP_SPEED; _state = State::Hover; _isPushingUp = false; - _jumpToHoverStart = 0; + _jumpButtonDownStart = 0; + _jumpButtonDownCount = 0; _takeoffToInAirStart = 0; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); @@ -158,68 +159,57 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { } void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { - btVector3 actualVelocity = _rigidBody->getLinearVelocity(); - btScalar actualSpeed = actualVelocity.length(); - - btVector3 desiredVelocity = _walkVelocity; - btScalar desiredSpeed = desiredVelocity.length(); - - const btScalar MIN_UP_PUSH = 0.1f; - if (desiredVelocity.dot(_currentUp) < MIN_UP_PUSH) { - _isPushingUp = false; - } const btScalar MIN_SPEED = 0.001f; - if (_state == State::Hover) { - if (desiredSpeed < MIN_SPEED) { - if (actualSpeed < MIN_SPEED) { - _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; - btScalar tau = glm::max(dt / HOVER_BRAKING_TIMESCALE, 1.0f); - _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); - } - } else { - const btScalar HOVER_ACCELERATION_TIMESCALE = 0.1f; - btScalar tau = dt / HOVER_ACCELERATION_TIMESCALE; - _rigidBody->setLinearVelocity(actualVelocity - tau * (actualVelocity - desiredVelocity)); + + btVector3 actualVelocity = _rigidBody->getLinearVelocity(); + if (actualVelocity.length() < MIN_SPEED) { + actualVelocity = btVector3(0.0f, 0.0f, 0.0f); + } + + btVector3 desiredVelocity = _walkVelocity; + if (desiredVelocity.length() < MIN_SPEED) { + desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); + } + + // decompose into horizontal and vertical components. + btVector3 actualVertVelocity = actualVelocity.dot(_currentUp) * _currentUp; + btVector3 actualHorizVelocity = actualVelocity - actualVertVelocity; + btVector3 desiredVertVelocity = desiredVelocity.dot(_currentUp) * _currentUp; + btVector3 desiredHorizVelocity = desiredVelocity - desiredVertVelocity; + + btVector3 finalVelocity; + + switch (_state) { + case State::Ground: + case State::Takeoff: + { + // horizontal ground control + const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; + btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; + finalVelocity = tau * desiredHorizVelocity + (1.0f - tau) * actualHorizVelocity + actualVertVelocity; } - } else { - if (onGround()) { - // walking on ground - if (desiredSpeed < MIN_SPEED) { - if (actualSpeed < MIN_SPEED) { - _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; - btScalar tau = dt / HOVER_BRAKING_TIMESCALE; - _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); - } - } else { - // TODO: modify desiredVelocity using floor normal - const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; - btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; - btVector3 velocityCorrection = tau * (desiredVelocity - actualVelocity); - // subtract vertical component - velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; - _rigidBody->setLinearVelocity(actualVelocity + velocityCorrection); - } - } else { - // transitioning to flying - btVector3 velocityCorrection = desiredVelocity - actualVelocity; + break; + case State::InAir: + { + // horizontal air control + const btScalar IN_AIR_ACCELERATION_TIMESCALE = 2.0f; + btScalar tau = dt / IN_AIR_ACCELERATION_TIMESCALE; + finalVelocity = tau * desiredHorizVelocity + (1.0f - tau) * actualHorizVelocity + actualVertVelocity; + } + break; + case State::Hover: + { + // vertical and horizontal air control const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f; btScalar tau = dt / FLY_ACCELERATION_TIMESCALE; - if (!_isPushingUp) { - // actually falling --> compute a different velocity attenuation factor - const btScalar FALL_ACCELERATION_TIMESCALE = 2.0f; - tau = dt / FALL_ACCELERATION_TIMESCALE; - // zero vertical component - velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; - } - _rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection); + finalVelocity = tau * desiredVelocity + (1.0f - tau) * actualVelocity; } + break; } + _rigidBody->setLinearVelocity(finalVelocity); + // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. // Rather then add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). @@ -259,24 +249,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { } void CharacterController::jump() { - // check for case where user is holding down "jump" key... - // we'll eventually transition to "hover" - if (_state != State::Takeoff) { - if (_state == State::InAir) { - quint64 now = usecTimestampNow(); - if (!_isPushingUp) { - _isPushingUp = true; - _jumpToHoverStart = now; - } - const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { - setState(State::Hover); - } - } else { - _pendingFlags |= PENDING_FLAG_JUMP; - } - - } + _pendingFlags |= PENDING_FLAG_JUMP; } bool CharacterController::onGround() const { @@ -441,6 +414,9 @@ void CharacterController::preSimulation() { _rigidBody->setWorldTransform(_characterBodyTransform); btVector3 velocity = _rigidBody->getLinearVelocity(); + btVector3 actualVertVelocity = velocity.dot(_currentUp) * _currentUp; + btVector3 actualHorizVelocity = velocity - actualVertVelocity; + // scan for distant floor // rayStart is at center of bottom sphere btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp; @@ -454,41 +430,74 @@ void CharacterController::preSimulation() { _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); if (rayCallback.hasHit()) { _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; - const btScalar MIN_HOVER_HEIGHT = 3.0f; - if (_state == State::Hover && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { - setState(State::InAir); - } - // TODO: use collision events rather than ray-trace test to disable jumping - const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_state != State::Takeoff && (velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { - setState(State::Ground); - } - } else if (!_hasSupport) { + } else { _floorDistance = FLT_MAX; - setState(State::Hover); } + const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; + const btScalar MIN_HOVER_HEIGHT = 2.5f; + const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; + const btScalar UPWARD_VELOCITY_THRESHOLD = 0.05f; + const btScalar MAX_FLYING_SPEED = 30.0f; + quint64 now = usecTimestampNow(); - if (_pendingFlags & PENDING_FLAG_JUMP) { - _pendingFlags &= ~ PENDING_FLAG_JUMP; - if (onGround()) { - setState(State::Takeoff); - _takeoffToInAirStart = now; + // record a time stamp when the jump button was first pressed. + if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { + if (_pendingFlags & PENDING_FLAG_JUMP) { + _jumpButtonDownStart = now; + _jumpButtonDownCount++; } } - const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; - if (_state == State::Takeoff && (now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { - setState(State::InAir); + bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; + bool wantsToGoUp = !jumpButtonHeld && _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD; + bool tooFast = actualHorizVelocity.length() > (MAX_FLYING_SPEED / 2.0f); - _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; - velocity += _jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); + switch (_state) { + case State::Ground: + if (!rayCallback.hasHit() && !_hasSupport) { + setState(State::Hover); + } else if (_pendingFlags & PENDING_FLAG_JUMP) { + _takeOffJumpButtonID = _jumpButtonDownCount; + setState(State::Takeoff); + } + break; + case State::Takeoff: + if (!rayCallback.hasHit() && !_hasSupport) { + setState(State::Hover); + } else if ((now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { + setState(State::InAir); + _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; + velocity += _jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } + break; + case State::InAir: { + if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { + setState(State::Ground); + } else if ((jumpButtonHeld && ((_takeOffJumpButtonID != _jumpButtonDownCount) || (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD)) || wantsToGoUp) { + setState(State::Hover); + } + break; + } + case State::Hover: + if (!jumpButtonHeld && !wantsToGoUp && _floorDistance < MIN_HOVER_HEIGHT && !tooFast) { + setState(State::InAir); + } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !tooFast) { + setState(State::Ground); + } + break; } } - _followTime = 0.0f; + /// _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD + + _previousFlags = _pendingFlags; + _pendingFlags &= ~PENDING_FLAG_JUMP; + + _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity(); } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 5470104a1a..97bda5c5d5 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -109,7 +109,9 @@ protected: glm::vec3 _boxScale; // used to compute capsule shape quint64 _takeoffToInAirStart; - quint64 _jumpToHoverStart; + quint64 _jumpButtonDownStart; + quint32 _jumpButtonDownCount; + quint32 _takeOffJumpButtonID; btScalar _halfHeight; btScalar _radius; @@ -131,7 +133,7 @@ protected: btDynamicsWorld* _dynamicsWorld { nullptr }; btRigidBody* _rigidBody { nullptr }; uint32_t _pendingFlags { 0 }; - + uint32_t _previousFlags { 0 }; }; #endif // hifi_CharacterControllerInterface_h From c4e1509aa214948bbe2428714d71c9f5133f5186 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 11:19:17 -0800 Subject: [PATCH 18/79] CharacterController: better debug support of internal state machine Head is no longer visible when flying fast, in first-person HMD mode. --- interface/src/avatar/MyAvatar.cpp | 12 +++-- libraries/physics/src/CharacterController.cpp | 46 +++++++++++-------- libraries/physics/src/CharacterController.h | 6 +++ 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0aadaed150..4253091533 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1311,21 +1311,23 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { _prevShouldDrawHead = shouldDrawHead; } -const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; +const float RENDER_HEAD_CUTOFF_DISTANCE = 0.5f; bool MyAvatar::cameraInsideHead() const { const Head* head = getHead(); const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); - return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); + return glm::length(cameraPosition - getDefaultEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { - return ((renderArgs->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) || - (qApp->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || - !cameraInsideHead()); + bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; + bool firstPerson = qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON; + bool insideHead = cameraInsideHead(); + return !defaultMode || !firstPerson || !insideHead; } void MyAvatar::updateOrientation(float deltaTime) { + // Smoothly rotate body with arrow keys float targetSpeed = _driveKeys[YAW] * _yawSpeed; if (targetSpeed != 0.0f) { diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index f241be8b35..160b227127 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -21,7 +21,11 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; -#define DEBUG_STATE_CHANGE +#ifdef DEBUG_STATE_CHANGE +#define SET_STATE(desiredState, reason) setState(desiredState, reason) +#else +#define SET_STATE(desiredState, reason) setState(desiredState) +#endif // helper class for simple ray-traces from character class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback { @@ -65,7 +69,6 @@ CharacterController::CharacterController() { _hasSupport = false; _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; - } bool CharacterController::needsRemoval() const { @@ -274,10 +277,14 @@ static const char* stateToStr(CharacterController::State state) { } #endif // #ifdef DEBUG_STATE_CHANGE +#ifdef DEBUG_STATE_CHANGE +void CharacterController::setState(State desiredState, const char* reason) { +#else void CharacterController::setState(State desiredState) { +#endif if (desiredState != _state) { #ifdef DEBUG_STATE_CHANGE - qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "<-" << stateToStr(_state); + qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif if (desiredState == State::Hover && _state != State::Hover) { // hover enter @@ -339,7 +346,7 @@ void CharacterController::setEnabled(bool enabled) { } _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; } - setState(State::Hover); + SET_STATE(State::Hover, "setEnabled"); _enabled = enabled; } } @@ -438,8 +445,8 @@ void CharacterController::preSimulation() { const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - const btScalar UPWARD_VELOCITY_THRESHOLD = 0.05f; - const btScalar MAX_FLYING_SPEED = 30.0f; + const btScalar UPWARD_VELOCITY_THRESHOLD = 0.1f; + const btScalar MAX_WALKING_SPEED = 2.5f; quint64 now = usecTimestampNow(); @@ -452,23 +459,22 @@ void CharacterController::preSimulation() { } bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; - bool wantsToGoUp = !jumpButtonHeld && _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD; - bool tooFast = actualHorizVelocity.length() > (MAX_FLYING_SPEED / 2.0f); + bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); switch (_state) { case State::Ground: if (!rayCallback.hasHit() && !_hasSupport) { - setState(State::Hover); + SET_STATE(State::Hover, "no ground"); } else if (_pendingFlags & PENDING_FLAG_JUMP) { _takeOffJumpButtonID = _jumpButtonDownCount; - setState(State::Takeoff); + SET_STATE(State::Takeoff, "jump pressed"); } break; case State::Takeoff: if (!rayCallback.hasHit() && !_hasSupport) { - setState(State::Hover); + SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { - setState(State::InAir); + SET_STATE(State::InAir, "takeoff done"); _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); @@ -476,17 +482,19 @@ void CharacterController::preSimulation() { break; case State::InAir: { if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { - setState(State::Ground); - } else if ((jumpButtonHeld && ((_takeOffJumpButtonID != _jumpButtonDownCount) || (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD)) || wantsToGoUp) { - setState(State::Hover); + SET_STATE(State::Ground, "hit ground"); + } else if (jumpButtonHeld && (_takeOffJumpButtonID != _jumpButtonDownCount)) { + SET_STATE(State::Hover, "double jump button"); + } else if (jumpButtonHeld && (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD) { + SET_STATE(State::Hover, "jump button held"); } break; } case State::Hover: - if (!jumpButtonHeld && !wantsToGoUp && _floorDistance < MIN_HOVER_HEIGHT && !tooFast) { - setState(State::InAir); - } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !tooFast) { - setState(State::Ground); + if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { + SET_STATE(State::InAir, "near ground"); + } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !flyingFast) { + SET_STATE(State::Ground, "touching ground"); } break; } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 97bda5c5d5..bb4a135ca3 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -31,6 +31,8 @@ class btRigidBody; class btCollisionWorld; class btDynamicsWorld; +//#define DEBUG_STATE_CHANGE + class CharacterController : public btCharacterControllerInterface { public: CharacterController(); @@ -92,7 +94,11 @@ public: bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); protected: +#ifdef DEBUG_STATE_CHANGE + void setState(State state, const char* reason); +#else void setState(State state); +#endif void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld) const; From 5adf9adb0e8321e21e50cba65764f7e5fd0d8a78 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 12:00:17 -0800 Subject: [PATCH 19/79] Make mirror rectaqngle move with mirror controls --- interface/src/Application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ab5edec527..883b082d0e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1362,6 +1362,9 @@ void Application::paintGL() { renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; renderArgs._blitFramebuffer = DependencyManager::get()->getSelfieFramebuffer(); + auto inputs = AvatarInputs::getInstance(); + _mirrorViewRect.moveTo(inputs->x(), inputs->y()); + renderRearViewMirror(&renderArgs, _mirrorViewRect); renderArgs._blitFramebuffer.reset(); From 2c26b32341fa53453aa6bacf4aed4557c53e960f Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 12:00:58 -0800 Subject: [PATCH 20/79] Make QML based tool-windows close properly --- libraries/ui/src/QmlWindowClass.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 3874b85a12..0e834fa379 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -220,18 +220,7 @@ QmlWindowClass::QmlWindowClass(QObject* qmlWindow) } QmlWindowClass::~QmlWindowClass() { - if (_qmlWindow) { - if (_toolWindow) { - auto offscreenUi = DependencyManager::get(); - auto toolWindow = offscreenUi->getToolWindow(); - auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::QueuedConnection, - Q_ARG(QVariant, _source)); - Q_ASSERT(invokeResult); - } else { - _qmlWindow->deleteLater(); - } - _qmlWindow = nullptr; - } + close(); } void QmlWindowClass::registerObject(const QString& name, QObject* object) { @@ -331,14 +320,18 @@ void QmlWindowClass::setTitle(const QString& title) { } void QmlWindowClass::close() { - DependencyManager::get()->executeOnUiThread([this] { - if (_qmlWindow) { - _qmlWindow->setProperty("destroyOnInvisible", true); - _qmlWindow->setProperty("visible", false); + if (_qmlWindow) { + if (_toolWindow) { + auto offscreenUi = DependencyManager::get(); + auto toolWindow = offscreenUi->getToolWindow(); + auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::QueuedConnection, + Q_ARG(QVariant, _source)); + Q_ASSERT(invokeResult); + } else { _qmlWindow->deleteLater(); - _qmlWindow = nullptr; } - }); + _qmlWindow = nullptr; + } } void QmlWindowClass::hasClosed() { From 656608e2ffaff0f21effbe461b8f36437addd6d1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 12:08:52 -0800 Subject: [PATCH 21/79] Fixes for away.js --- .../meshes/defaultAvatar_full/avatar-animation.json | 1 + libraries/animation/src/Rig.cpp | 11 +++++++---- libraries/animation/src/Rig.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 37da0c37cc..a4e39969b4 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -377,6 +377,7 @@ "interpTarget": 30, "interpDuration": 30, "transitions": [ + { "var": "isNotAway", "state": "awayOutro" }, { "var": "awayIntroOnDone", "state": "away"} ] }, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 64935691c8..2ea9d782d5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -784,11 +784,14 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos t += deltaTime; - if (_enableInverseKinematics) { - _animVars.set("ikOverlayAlpha", 1.0f); - } else { - _animVars.set("ikOverlayAlpha", 0.0f); + if (_enableInverseKinematics != _lastEnableInverseKinematics) { + if (_enableInverseKinematics) { + _animVars.set("ikOverlayAlpha", 1.0f); + } else { + _animVars.set("ikOverlayAlpha", 0.0f); + } } + _lastEnableInverseKinematics = _enableInverseKinematics; } _lastFront = front; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e12af16e41..a360140b16 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -301,6 +301,7 @@ public: std::map _origRoleAnimations; std::vector _prefetchedAnimations; + bool _lastEnableInverseKinematics { false }; bool _enableInverseKinematics { true }; private: From 25632b63b70a4b25c55066a1781a972503fbd81e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 2 Feb 2016 13:00:35 -0800 Subject: [PATCH 22/79] Adjust Entities.addEntity to not bid on simulation from AC Script --- .../entities/src/EntityScriptingInterface.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 6bfcc66f1e..5d793d7547 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -131,17 +131,20 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties _entityTree->withWriteLock([&] { EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID); if (entity) { - // This Node is creating a new object. If it's in motion, set this Node as the simulator. - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); - propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); if (propertiesWithSimID.parentRelatedPropertyChanged()) { // due to parenting, the server may not know where something is in world-space, so include the bounding cube. propertiesWithSimID.setQueryAACube(entity->getQueryAACube()); } - // and make note of it now, so we can act on it right away. - entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); + if (_bidOnSimulationOwnership) { + // This Node is creating a new object. If it's in motion, set this Node as the simulator. + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + + // and make note of it now, so we can act on it right away. + propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); + entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY); + } entity->setLastBroadcast(usecTimestampNow()); } else { From cfff7657310c6e1f7fd85be9cf9bf82b14abe9ad Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 14:21:34 -0800 Subject: [PATCH 23/79] allow for absolute path handling at entity-server --- assignment-client/src/octree/OctreeServer.cpp | 27 ++++++++++++------- assignment-client/src/octree/OctreeServer.h | 2 +- .../resources/describe-settings.json | 6 ++--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 81e25cc7ba..95c869c874 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1030,13 +1030,12 @@ void OctreeServer::readConfiguration() { qDebug() << "wantPersist=" << _wantPersist; if (_wantPersist) { - QString persistFilename; - if (!readOptionString(QString("persistFilename"), settingsSectionObject, persistFilename)) { - persistFilename = getMyDefaultPersistFilename(); + if (!readOptionString("persistFilePath", settingsSectionObject, _persistFilePath) + && !readOptionString("persistFilename", settingsSectionObject, _persistFilePath)) { + _persistFilePath = getMyDefaultPersistFilename(); } - strcpy(_persistFilename, qPrintable(persistFilename)); - qDebug("persistFilename=%s", _persistFilename); + qDebug() << "persistFilePath=" << _persistFilePath; _persistAsFileType = "json.gz"; @@ -1145,8 +1144,16 @@ void OctreeServer::domainSettingsRequestComplete() { if (_wantPersist) { // If persist filename does not exist, let's see if there is one beside the application binary // If there is, let's copy it over to our target persist directory - auto persistPath = ServerPathUtils::getDataFilePath("entities/" + QString(_persistFilename)); - if (!QFile::exists(persistPath)) { + QDir persistPath { _persistFilePath }; + QString absoluteFilePath = persistPath.path(); + + if (persistPath.isRelative()) { + // if the domain settings passed us a relative path, make an absolute path that is relative to the + // default data directory + absoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); + } + + if (!QFile::exists(absoluteFilePath)) { qDebug() << "Persist file does not exist, checking for existence of persist file next to application"; static const QString OLD_DEFAULT_PERSIST_FILENAME = "resources/models.json.gz"; @@ -1154,7 +1161,7 @@ void OctreeServer::domainSettingsRequestComplete() { // This is the old persist path, based on the current persist filename, which could // be a custom filename set by the user. - auto oldPersistPath = QDir(oldResourcesDirectory).absoluteFilePath(_persistFilename); + auto oldPersistPath = QDir(oldResourcesDirectory).absoluteFilePath(_persistFilePath); // This is the old default persist path. auto oldDefaultPersistPath = QDir(oldResourcesDirectory).absoluteFilePath(OLD_DEFAULT_PERSIST_FILENAME); @@ -1181,14 +1188,14 @@ void OctreeServer::domainSettingsRequestComplete() { if (shouldCopy) { qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistPath; - QFile::copy(pathToCopyFrom, persistPath); + QFile::copy(pathToCopyFrom, absoluteFilePath); } else { qDebug() << "No existing persist file found"; } } // now set up PersistThread - _persistThread = new OctreePersistThread(_tree, persistPath, _persistInterval, + _persistThread = new OctreePersistThread(_tree, absoluteFilePath, _persistInterval, _wantBackup, _settings, _debugTimestampNow, _persistAsFileType); _persistThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index f1aa9531e8..1430715571 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -169,7 +169,7 @@ protected: int _statusPort; QString _statusHost; - char _persistFilename[MAX_FILENAME_LENGTH]; + QString _persistFilePath; QString _persistAsFileType; int _packetsPerClientPerInterval; int _packetsTotalPerInterval; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e38aa2a75a..f024ba5648 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -383,9 +383,9 @@ "assignment-types": [6], "settings": [ { - "name": "persistFilename", - "label": "Entities Filename", - "help": "the path to the file entities are stored in. Make sure the path exists.", + "name": "persistFilePath", + "label": "Entities File Path", + "help": "The path to the file entities are stored in. If path is relative it will be relative to the application data directory.", "placeholder": "models.json.gz", "default": "models.json.gz", "advanced": true From cede14fdc6bfacbac590a504698342c250f19cd8 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 14:01:44 -0800 Subject: [PATCH 24/79] Don't cut off title of windows without close icons --- interface/resources/qml/windows/DefaultFrame.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml index a082db5a92..c58f9ca545 100644 --- a/interface/resources/qml/windows/DefaultFrame.qml +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -6,8 +6,10 @@ import "../controls" Frame { id: frame + property bool wideTopMargin: (window && (window.closable || window.title)); + Rectangle { - anchors { margins: -iconSize; topMargin: -iconSize * ((window && window.closable) ? 2 : 1); } + anchors { margins: -iconSize; topMargin: -iconSize * (wideTopMargin ? 2 : 1); } anchors.fill: parent; color: "#7f7f7f7f"; radius: 3; From 4d91b8b7d625271d44bf551b7a88f7a3bfb60585 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 14:38:17 -0800 Subject: [PATCH 25/79] fix check for existing persist file directory --- assignment-client/src/octree/OctreeServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 95c869c874..c35d3d1cf8 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1179,14 +1179,14 @@ void OctreeServer::domainSettingsRequestComplete() { pathToCopyFrom = oldDefaultPersistPath; } - QDir persistFileDirectory = QDir(persistPath).filePath(".."); + QDir persistFileDirectory { QDir { absoluteFilePath }.dirName() }; if (!persistFileDirectory.exists()) { qDebug() << "Creating data directory " << persistFileDirectory.absolutePath(); persistFileDirectory.mkpath("."); } if (shouldCopy) { - qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << persistPath; + qDebug() << "Old persist file found, copying from " << pathToCopyFrom << " to " << absoluteFilePath; QFile::copy(pathToCopyFrom, absoluteFilePath); } else { From 13d58003bec98ce646c9676eaa5bdf040b391a35 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 15:02:12 -0800 Subject: [PATCH 26/79] migrate persistFilename to persistFilePath in DS settings --- .../resources/describe-settings.json | 2 +- .../src/DomainServerSettingsManager.cpp | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index f024ba5648..f34d7ec8fb 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 1.0, + "version": 1.1, "settings": [ { "name": "metaverse", diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index e10007784f..a0e647ed89 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -124,6 +124,30 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // reload the master and user config so that the merged config is right _configMap.loadMasterAndUserConfig(argumentList); } + } else if (oldVersion < 1.1) { + static const QString ENTITY_FILE_NAME_KEYPATH = "entity_server_settings.persistFilename"; + static const QString ENTITY_FILE_PATH_KEYPATH = "entity_server_settings.persistFilePath"; + + // this was prior to change of poorly named entitiesFileName to entitiesFilePath + QVariant* persistFileNameVariant = valueForKeyPath(_configMap.getMergedConfig(), ENTITY_FILE_NAME_KEYPATH); + if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType::QString)) { + QString persistFileName = persistFileNameVariant->toString(); + + qDebug() << "Migrating persistFilename to persistFilePath for entity-server settings"; + + // grab the persistFilePath option, create it if it doesn't exist + QVariant* persistFilePath = valueForKeyPath(_configMap.getUserConfig(), ENTITY_FILE_PATH_KEYPATH, true); + + // write the migrated value + *persistFilePath = persistFileName; + + // write the new settings to the json file + persistToFile(); + + // reload the master and user config so that the merged config is right + _configMap.loadMasterAndUserConfig(argumentList); + } + } } From 6cc3b2b47f82022ea00925887fa6ffbb9029e200 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 15:04:28 -0800 Subject: [PATCH 27/79] Fixed unused variable warnings on OSX and Linux --- interface/src/avatar/MyAvatar.cpp | 1 - libraries/physics/src/CharacterController.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4253091533..b99430cae3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1314,7 +1314,6 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { const float RENDER_HEAD_CUTOFF_DISTANCE = 0.5f; bool MyAvatar::cameraInsideHead() const { - const Head* head = getHead(); const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); return glm::length(cameraPosition - getDefaultEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 160b227127..941428f03a 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -445,7 +445,6 @@ void CharacterController::preSimulation() { const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - const btScalar UPWARD_VELOCITY_THRESHOLD = 0.1f; const btScalar MAX_WALKING_SPEED = 2.5f; quint64 now = usecTimestampNow(); From 9633e40c6e61a80f4fa9f6bba10c6208e6b9589e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 15:15:19 -0800 Subject: [PATCH 28/79] add code to remove old persistFilename setting --- .../src/DomainServerSettingsManager.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index a0e647ed89..98bb63241e 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -125,11 +125,13 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList _configMap.loadMasterAndUserConfig(argumentList); } } else if (oldVersion < 1.1) { - static const QString ENTITY_FILE_NAME_KEYPATH = "entity_server_settings.persistFilename"; - static const QString ENTITY_FILE_PATH_KEYPATH = "entity_server_settings.persistFilePath"; + static const QString ENTITY_SERVER_SETTINGS_KEY = "entity_server_settings"; + static const QString ENTITY_FILE_NAME_KEY = "persistFilename"; + static const QString ENTITY_FILE_PATH_KEYPATH = ENTITY_SERVER_SETTINGS_KEY + ".persistFilePath"; // this was prior to change of poorly named entitiesFileName to entitiesFilePath - QVariant* persistFileNameVariant = valueForKeyPath(_configMap.getMergedConfig(), ENTITY_FILE_NAME_KEYPATH); + QVariant* persistFileNameVariant = valueForKeyPath(_configMap.getMergedConfig(), + ENTITY_SERVER_SETTINGS_KEY + "." + ENTITY_FILE_NAME_KEY); if (persistFileNameVariant && persistFileNameVariant->canConvert(QMetaType::QString)) { QString persistFileName = persistFileNameVariant->toString(); @@ -141,6 +143,15 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList // write the migrated value *persistFilePath = persistFileName; + // remove the old setting + QVariant* entityServerVariant = valueForKeyPath(_configMap.getUserConfig(), ENTITY_SERVER_SETTINGS_KEY); + if (entityServerVariant && entityServerVariant->canConvert(QMetaType::QVariantMap)) { + QVariantMap entityServerMap = entityServerVariant->toMap(); + entityServerMap.remove(ENTITY_FILE_NAME_KEY); + + *entityServerVariant = entityServerMap; + } + // write the new settings to the json file persistToFile(); From ff239a7c6afe3e68748fc379ddedd12a48ca693b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 15:17:45 -0800 Subject: [PATCH 29/79] add helper text for extension to persistFilePath --- domain-server/resources/describe-settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index f34d7ec8fb..65949e98a0 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -385,7 +385,7 @@ { "name": "persistFilePath", "label": "Entities File Path", - "help": "The path to the file entities are stored in. If path is relative it will be relative to the application data directory.", + "help": "The path to the file entities are stored in. If this path is relative it will be relative to the application data directory. The entities file extension should be .json.gz.", "placeholder": "models.json.gz", "default": "models.json.gz", "advanced": true From f24f2749e14b41e7a8f942c52ebaa36c36d0af72 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 15:19:12 -0800 Subject: [PATCH 30/79] Make users.js show QML window for edit friends --- examples/users.js | 17 ++++++++++- interface/src/Application.cpp | 16 ---------- interface/src/Application.h | 2 -- .../GlobalServicesScriptingInterface.cpp | 3 -- .../GlobalServicesScriptingInterface.h | 3 +- libraries/ui/src/OffscreenUi.cpp | 29 ++++++++++--------- libraries/ui/src/QmlWebWindowClass.cpp | 5 +++- 7 files changed, 36 insertions(+), 39 deletions(-) diff --git a/examples/users.js b/examples/users.js index f63184625d..0f9be42835 100644 --- a/examples/users.js +++ b/examples/users.js @@ -235,7 +235,12 @@ var usersWindow = (function () { FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, FRIENDS_BUTTON_COLOR = { red: 225, green: 225, blue: 225 }, FRIENDS_BUTTON_ALPHA = 0.95, + FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends", + FRIENDS_WINDOW_WIDTH = 290, + FRIENDS_WINDOW_HEIGHT = 500, + FRIENDS_WINDOW_TITLE = "Add/Remove Friends", friendsButton, + friendsWindow, OPTION_BACKGROUND_COLOR = { red: 60, green: 60, blue: 60 }, OPTION_BACKGROUND_ALPHA = 0.1, @@ -643,7 +648,17 @@ var usersWindow = (function () { } if (clickedOverlay === friendsButton) { - GlobalServices.editFriends(); + if (!friendsWindow) { + friendsWindow = new OverlayWebWindow({ + title: FRIENDS_WINDOW_TITLE, + width: FRIENDS_WINDOW_WIDTH, + height: FRIENDS_WINDOW_HEIGHT, + visible: false + }); + } + friendsWindow.setURL(FRIENDS_WINDOW_URL); + friendsWindow.setVisible(true); + friendsWindow.raise(); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 883b082d0e..d05440b2e6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4615,22 +4615,6 @@ void Application::activeChanged(Qt::ApplicationState state) { break; } } -void Application::showFriendsWindow() { - const QString FRIENDS_WINDOW_OBJECT_NAME = "FriendsWindow"; - const QString FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; - const QString FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; - const int FRIENDS_WINDOW_WIDTH = 290; - const int FRIENDS_WINDOW_HEIGHT = 500; - auto webWindowClass = _window->findChildren(FRIENDS_WINDOW_OBJECT_NAME); - if (webWindowClass.empty()) { - auto friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, - FRIENDS_WINDOW_HEIGHT); - friendsWindow->setParent(_window); - friendsWindow->setObjectName(FRIENDS_WINDOW_OBJECT_NAME); - connect(friendsWindow, &WebWindowClass::closed, &WebWindowClass::deleteLater); - friendsWindow->setVisible(true); - } -} void Application::postLambdaEvent(std::function f) { if (this->thread() == QThread::currentThread()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 4b32d8879b..ef8a5377fd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -245,8 +245,6 @@ public slots: void handleLocalServerConnection(); void readArgumentsFromLocalSocket(); - void showFriendsWindow(); - void packageModel(); void openUrl(const QUrl& url); diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.cpp b/interface/src/scripting/GlobalServicesScriptingInterface.cpp index e764473107..7dac0247bd 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.cpp +++ b/interface/src/scripting/GlobalServicesScriptingInterface.cpp @@ -144,6 +144,3 @@ void GlobalServicesScriptingInterface::updateDownloadInfo() { emit downloadInfoChanged(getDownloadInfo()); } -void GlobalServicesScriptingInterface::editFriends() { - QMetaObject::invokeMethod(qApp, "showFriendsWindow"); -} diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.h b/interface/src/scripting/GlobalServicesScriptingInterface.h index e38bfc0eb6..11d8735187 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.h +++ b/interface/src/scripting/GlobalServicesScriptingInterface.h @@ -45,8 +45,7 @@ public: public slots: DownloadInfoResult getDownloadInfo(); void updateDownloadInfo(); - void editFriends(); - + private slots: void loggedOut(); void checkDownloadInfo(); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 8e52507243..1d471d5419 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -45,6 +45,20 @@ private: bool _navigationFocused { false }; }; +QString fixupHifiUrl(const QString& urlString) { + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + QUrl url(urlString); + QUrlQuery query(url); + if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + AccountManager& accountManager = AccountManager::getInstance(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + return url.toString(); + } + return urlString; +} + class UrlHandler : public QObject { Q_OBJECT public: @@ -60,20 +74,7 @@ public: // FIXME hack for authentication, remove when we migrate to Qt 5.6 Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { - static const QString ACCESS_TOKEN_PARAMETER = "access_token"; - static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; - QString result = originalUrl; - QUrl url(originalUrl); - QUrlQuery query(url); - if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { - qDebug() << "Updating URL with auth token"; - AccountManager& accountManager = AccountManager::getInstance(); - query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); - url.setQuery(query.query()); - result = url.toString(); - } - - return result; + return fixupHifiUrl(originalUrl); } }; diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 96e99654c8..f12fb51b19 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -49,8 +49,11 @@ QString QmlWebWindowClass::getURL() const { return result.toString(); } +// HACK find a good place to declare and store this +extern QString fixupHifiUrl(const QString& urlString); + void QmlWebWindowClass::setURL(const QString& urlString) { DependencyManager::get()->executeOnUiThread([=] { - _qmlWindow->setProperty(URL_PROPERTY, urlString); + _qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString)); }); } From bc7fda0ae9c69f6153d1fc6b56a19009a01fe440 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 15:19:47 -0800 Subject: [PATCH 31/79] Set desirable global settings for QML web views --- interface/resources/qml/hifi/Desktop.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 9d527c697a..5951101194 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtWebEngine 1.1; import "../desktop" import ".." @@ -7,6 +8,13 @@ import ".." Desktop { id: desktop + Component.onCompleted: { + WebEngine.settings.javascriptCanOpenWindows = false; + WebEngine.settings.javascriptCanAccessClipboard = false; + WebEngine.settings.spatialNavigationEnabled = true; + WebEngine.settings.localContentCanAccessRemoteUrls = true; + } + // The tool window, one instance property alias toolWindow: toolWindow ToolWindow { id: toolWindow } From 90b78feb1ee27ecd3dd35ab31f453f4912362e5b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 15:37:34 -0800 Subject: [PATCH 32/79] force models file to end in .json.gz --- assignment-client/src/octree/OctreeServer.cpp | 8 +++++++- domain-server/resources/describe-settings.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index c35d3d1cf8..9a5b6ec0fd 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1153,6 +1153,11 @@ void OctreeServer::domainSettingsRequestComplete() { absoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } + // force the persist file to end with .json.gz + if (!absoluteFilePath.endsWith(".json.gz", Qt::CaseInsensitive)) { + absoluteFilePath += ".json.gz"; + } + if (!QFile::exists(absoluteFilePath)) { qDebug() << "Persist file does not exist, checking for existence of persist file next to application"; @@ -1179,7 +1184,8 @@ void OctreeServer::domainSettingsRequestComplete() { pathToCopyFrom = oldDefaultPersistPath; } - QDir persistFileDirectory { QDir { absoluteFilePath }.dirName() }; + QDir persistFileDirectory { QDir::cleanPath(absoluteFilePath + "/..") }; + if (!persistFileDirectory.exists()) { qDebug() << "Creating data directory " << persistFileDirectory.absolutePath(); persistFileDirectory.mkpath("."); diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 65949e98a0..870573ef6c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -385,7 +385,7 @@ { "name": "persistFilePath", "label": "Entities File Path", - "help": "The path to the file entities are stored in. If this path is relative it will be relative to the application data directory. The entities file extension should be .json.gz.", + "help": "The path to the file entities are stored in. If this path is relative it will be relative to the application data directory. The filename must end in .json.gz.", "placeholder": "models.json.gz", "default": "models.json.gz", "advanced": true From 99d1fa08fdd2fc51cad7f20f9f1ac0df361305be Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 15:49:40 -0800 Subject: [PATCH 33/79] handle incorrect casing in persist file extension --- assignment-client/src/octree/OctreeServer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 9a5b6ec0fd..2eb7d00af8 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1154,8 +1154,11 @@ void OctreeServer::domainSettingsRequestComplete() { } // force the persist file to end with .json.gz - if (!absoluteFilePath.endsWith(".json.gz", Qt::CaseInsensitive)) { - absoluteFilePath += ".json.gz"; + if (!absoluteFilePath.endsWith(_persistAsFileType, Qt::CaseInsensitive)) { + absoluteFilePath += _persistAsFileType; + } else { + // make sure the casing of .json.gz is correct + absoluteFilePath.replace(_persistAsFileType, _persistAsFileType, Qt::CaseInsensitive); } if (!QFile::exists(absoluteFilePath)) { From 8ab15770b8c7840fe09612ab2b3351266be9681f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 15:52:12 -0800 Subject: [PATCH 34/79] use absolutePath for absolute persist path --- assignment-client/src/octree/OctreeServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 2eb7d00af8..558b59edf7 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1145,7 +1145,7 @@ void OctreeServer::domainSettingsRequestComplete() { // If persist filename does not exist, let's see if there is one beside the application binary // If there is, let's copy it over to our target persist directory QDir persistPath { _persistFilePath }; - QString absoluteFilePath = persistPath.path(); + QString absoluteFilePath = persistPath.absolutePath(); if (persistPath.isRelative()) { // if the domain settings passed us a relative path, make an absolute path that is relative to the From 2c1623ed427b326a844c276e663a4ed9d377ac1f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 2 Feb 2016 16:07:14 -0800 Subject: [PATCH 35/79] use extension with preceeding period --- assignment-client/src/octree/OctreeServer.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 558b59edf7..69c54a17d5 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1153,12 +1153,14 @@ void OctreeServer::domainSettingsRequestComplete() { absoluteFilePath = QDir(ServerPathUtils::getDataFilePath("entities/")).absoluteFilePath(_persistFilePath); } + static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; + // force the persist file to end with .json.gz - if (!absoluteFilePath.endsWith(_persistAsFileType, Qt::CaseInsensitive)) { - absoluteFilePath += _persistAsFileType; + if (!absoluteFilePath.endsWith(ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive)) { + absoluteFilePath += ENTITY_PERSIST_EXTENSION; } else { // make sure the casing of .json.gz is correct - absoluteFilePath.replace(_persistAsFileType, _persistAsFileType, Qt::CaseInsensitive); + absoluteFilePath.replace(ENTITY_PERSIST_EXTENSION, ENTITY_PERSIST_EXTENSION, Qt::CaseInsensitive); } if (!QFile::exists(absoluteFilePath)) { From 30e97c2a76bcdafa30d2f2cef9866917de680fc6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Feb 2016 16:34:06 -0800 Subject: [PATCH 36/79] run attachedEntitiesManager.js by default, but without a UI --- examples/attachedEntitiesManager.js | 75 ++++++++++++++++------------- examples/defaultScripts.js | 1 + 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index 01f8f861c9..c44ac66a44 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -22,41 +22,44 @@ var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.4; var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0; var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES"; var DRESSING_ROOM_DISTANCE = 2.0; +var SHOW_TOOL_BAR = false; // tool bar -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -var BUTTON_SIZE = 32; -var PADDING = 3; -Script.include(["libraries/toolBars.js"]); -var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) { - return { - x: (BUTTON_SIZE + PADDING), - y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING) - }; -}); -var saveButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/save.png", - color: { - red: 255, - green: 255, - blue: 255 - }, - alpha: 1 -}); -var loadButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/load.png", - color: { - red: 255, - green: 255, - blue: 255 - }, - alpha: 1 -}); +if (SHOW_TOOL_BAR) { + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + var BUTTON_SIZE = 32; + var PADDING = 3; + Script.include(["libraries/toolBars.js"]); + var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) { + return { + x: (BUTTON_SIZE + PADDING), + y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING) + }; + }); + var saveButton = toolBar.addOverlay("image", { + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: "http://headache.hungry.com/~seth/hifi/save.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1 + }); + var loadButton = toolBar.addOverlay("image", { + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: "http://headache.hungry.com/~seth/hifi/load.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1 + }); +} function mousePressEvent(event) { @@ -73,10 +76,14 @@ function mousePressEvent(event) { } function scriptEnding() { - toolBar.cleanup(); + if (SHOW_TOOL_BAR) { + toolBar.cleanup(); + } } -Controller.mousePressEvent.connect(mousePressEvent); +if (SHOW_TOOL_BAR) { + Controller.mousePressEvent.connect(mousePressEvent); +} Script.scriptEnding.connect(scriptEnding); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 5ca62470ee..35af5f4eae 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -21,3 +21,4 @@ Script.load("controllers/squeezeHands.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); +Script.load("attachedEntitiesManager.js"); From e6abc026c832e22c3e6c8b4342e59f4de5546c39 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 17:02:29 -0800 Subject: [PATCH 37/79] AnimClip: mirror animation support --- libraries/animation/src/AnimClip.cpp | 26 +++++++++++-- libraries/animation/src/AnimClip.h | 8 +++- libraries/animation/src/AnimNodeLoader.cpp | 11 +++++- libraries/animation/src/AnimPose.cpp | 5 +++ libraries/animation/src/AnimPose.h | 1 + libraries/animation/src/AnimSkeleton.cpp | 45 +++++++++++++++++++++- libraries/animation/src/AnimSkeleton.h | 5 +++ libraries/animation/src/Rig.cpp | 4 +- 8 files changed, 97 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 90cd85e727..f8e045fc3b 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -15,12 +15,13 @@ bool AnimClip::usePreAndPostPoseFromAnim = false; -AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag) : +AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), _endFrame(endFrame), _timeScale(timeScale), _loopFlag(loopFlag), + _mirrorFlag(mirrorFlag), _frame(startFrame) { loadURL(url); @@ -49,6 +50,12 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, } if (_anim.size()) { + + // lazy creation of mirrored animation frames. + if (_mirrorFlag && _anim.size() != _mirrorAnim.size()) { + buildMirrorAnim(); + } + int prevIndex = (int)glm::floor(_frame); int nextIndex; if (_loopFlag && _frame >= _endFrame) { @@ -63,8 +70,8 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); - const AnimPoseVec& prevFrame = _anim[prevIndex]; - const AnimPoseVec& nextFrame = _anim[nextIndex]; + const AnimPoseVec& prevFrame = _mirrorFlag ? _mirrorAnim[prevIndex] : _anim[prevIndex]; + const AnimPoseVec& nextFrame = _mirrorFlag ? _mirrorAnim[nextIndex] : _anim[nextIndex]; float alpha = glm::fract(_frame); ::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]); @@ -162,9 +169,22 @@ void AnimClip::copyFromNetworkAnim() { } } + // mirrorAnim will be re-built on demand, if needed. + _mirrorAnim.clear(); + _poses.resize(skeletonJointCount); } +void AnimClip::buildMirrorAnim() { + assert(_skeleton); + + _mirrorAnim.clear(); + _mirrorAnim.reserve(_anim.size()); + for (auto& relPoses : _anim) { + _mirrorAnim.push_back(relPoses); + _skeleton->mirrorRelativePoses(_mirrorAnim.back()); + } +} const AnimPoseVec& AnimClip::getPosesInternal() const { return _poses; diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 7d58ae4f9a..bb4f61ffc0 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -27,7 +27,7 @@ public: static bool usePreAndPostPoseFromAnim; - AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag); + AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); virtual ~AnimClip() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; @@ -49,12 +49,16 @@ public: bool getLoopFlag() const { return _loopFlag; } void setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } + bool getMirrorFlag() const { return _mirrorFlag; } + void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; } + void loadURL(const QString& url); protected: virtual void setCurrentFrameInternal(float frame) override; void copyFromNetworkAnim(); + void buildMirrorAnim(); // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; @@ -64,12 +68,14 @@ protected: // _anim[frame][joint] std::vector _anim; + std::vector _mirrorAnim; QString _url; float _startFrame; float _endFrame; float _timeScale; bool _loopFlag; + bool _mirrorFlag; float _frame; QString _startFrameVar; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 568da8dd63..b26f34f531 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -145,6 +145,14 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { } \ bool NAME = NAME##_VAL.toBool() +#define READ_OPTIONAL_BOOL(NAME, JSON_OBJ, DEFAULT) \ + auto NAME##_VAL = JSON_OBJ.value(#NAME); \ + bool NAME = DEFAULT; \ + if (NAME##_VAL.isBool()) { \ + NAME = NAME##_VAL.toBool(); \ + } \ + do {} while (0) + #define READ_FLOAT(NAME, JSON_OBJ, ID, URL, ERROR_RETURN) \ auto NAME##_VAL = JSON_OBJ.value(#NAME); \ if (!NAME##_VAL.isDouble()) { \ @@ -222,13 +230,14 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_FLOAT(endFrame, jsonObj, id, jsonUrl, nullptr); READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr); + READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false); READ_OPTIONAL_STRING(startFrameVar, jsonObj); READ_OPTIONAL_STRING(endFrameVar, jsonObj); READ_OPTIONAL_STRING(timeScaleVar, jsonObj); READ_OPTIONAL_STRING(loopFlagVar, jsonObj); - auto node = std::make_shared(id, url, startFrame, endFrame, timeScale, loopFlag); + auto node = std::make_shared(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag); if (!startFrameVar.isEmpty()) { node->setStartFrameVar(startFrameVar); diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 0c6af2d5bd..439ef5336f 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -51,6 +51,11 @@ AnimPose AnimPose::inverse() const { return AnimPose(glm::inverse(static_cast(*this))); } +// mirror about x-axis without applying negative scale. +AnimPose AnimPose::mirror() const { + return AnimPose(scale, glm::quat(rot.w, rot.x, -rot.y, -rot.z), glm::vec3(-trans.x, trans.y, trans.z)); +} + AnimPose::operator glm::mat4() const { glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h index 852d84ec1b..6ffa9bb321 100644 --- a/libraries/animation/src/AnimPose.h +++ b/libraries/animation/src/AnimPose.h @@ -30,6 +30,7 @@ struct AnimPose { AnimPose operator*(const AnimPose& rhs) const; AnimPose inverse() const; + AnimPose mirror() const; operator glm::mat4() const; glm::vec3 scale; diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 8f45b785d1..2d37be9b87 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -87,7 +87,8 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { // poses start off relative and leave in absolute frame - for (int i = 0; i < (int)poses.size() && i < (int)_joints.size(); ++i) { + int lastIndex = std::min((int)poses.size(), (int)_joints.size()); + for (int i = 0; i < lastIndex; ++i) { int parentIndex = _joints[i].parentIndex; if (parentIndex != -1) { poses[i] = poses[parentIndex] * poses[i]; @@ -95,6 +96,30 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { } } +void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const { + // poses start off absolute and leave in relative frame + int lastIndex = std::min((int)poses.size(), (int)_joints.size()); + for (int i = lastIndex - 1; i >= 0; --i) { + int parentIndex = _joints[i].parentIndex; + if (parentIndex != -1) { + poses[i] = poses[parentIndex].inverse() * poses[i]; + } + } +} + +void AnimSkeleton::mirrorRelativePoses(AnimPoseVec& poses) const { + convertRelativePosesToAbsolute(poses); + mirrorAbsolutePoses(poses); + convertAbsolutePosesToRelative(poses); +} + +void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { + AnimPoseVec temp = poses; + for (int i = 0; i < (int)poses.size(); i++) { + poses[_mirrorMap[i]] = temp[i].mirror(); + } +} + void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; @@ -150,6 +175,24 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) } } } + + // build mirror map. + _mirrorMap.reserve(_joints.size()); + for (int i = 0; i < (int)joints.size(); i++) { + int mirrorJointIndex = -1; + if (_joints[i].name.startsWith("Left")) { + QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } else if (_joints[i].name.startsWith("Right")) { + QString mirrorJointName = QString(_joints[i].name).replace(0, 5, "Left"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } + if (mirrorJointIndex >= 0) { + _mirrorMap.push_back(mirrorJointIndex); + } else { + _mirrorMap.push_back(i); + } + } } #ifndef NDEBUG diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 757f4e5c3e..fc246bc4c0 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -53,6 +53,10 @@ public: AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const; void convertRelativePosesToAbsolute(AnimPoseVec& poses) const; + void convertAbsolutePosesToRelative(AnimPoseVec& poses) const; + + void mirrorRelativePoses(AnimPoseVec& poses) const; + void mirrorAbsolutePoses(AnimPoseVec& poses) const; #ifndef NDEBUG void dump() const; @@ -69,6 +73,7 @@ protected: AnimPoseVec _absoluteDefaultPoses; AnimPoseVec _relativePreRotationPoses; AnimPoseVec _relativePostRotationPoses; + std::vector _mirrorMap; // no copies AnimSkeleton(const AnimSkeleton&) = delete; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4d72bfbaea..1e75df0ccc 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -120,7 +120,7 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f _origRoleAnimations[role] = node; const float REFERENCE_FRAMES_PER_SECOND = 30.0f; float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; - auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop); + auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop, false); AnimNode::Pointer parent = node->getParent(); parent->replaceChild(node, clipNode); } else { @@ -152,7 +152,7 @@ void Rig::prefetchAnimation(const QString& url) { // This will begin loading the NetworkGeometry for the given URL. // which should speed us up if we request it later via overrideAnimation. - auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false); + auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false, false); _prefetchedAnimations.push_back(clipNode); } From 03d5bc885b5631272669e0ca7b80e6b5f9bb52a0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 17:10:15 -0800 Subject: [PATCH 38/79] AnimClip: added mirrorFlag anim var --- libraries/animation/src/AnimClip.cpp | 1 + libraries/animation/src/AnimClip.h | 2 ++ libraries/animation/src/AnimNodeLoader.cpp | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f8e045fc3b..e8f429f22c 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -38,6 +38,7 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, _endFrame = animVars.lookup(_endFrameVar, _endFrame); _timeScale = animVars.lookup(_timeScaleVar, _timeScale); _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); + _mirrorFlag = animVars.lookup(_mirrorFlagVar, _mirrorFlag); float frame = animVars.lookup(_frameVar, _frame); _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index bb4f61ffc0..7989f6d172 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,6 +36,7 @@ public: void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; } void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; } + void setMirrorFlagVar(const QString& mirrorFlagVar) { _mirrorFlagVar = mirrorFlagVar; } void setFrameVar(const QString& frameVar) { _frameVar = frameVar; } float getStartFrame() const { return _startFrame; } @@ -82,6 +83,7 @@ protected: QString _endFrameVar; QString _timeScaleVar; QString _loopFlagVar; + QString _mirrorFlagVar; QString _frameVar; // no copies diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index b26f34f531..52f44a8cd9 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -236,6 +236,7 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& READ_OPTIONAL_STRING(endFrameVar, jsonObj); READ_OPTIONAL_STRING(timeScaleVar, jsonObj); READ_OPTIONAL_STRING(loopFlagVar, jsonObj); + READ_OPTIONAL_STRING(mirrorFlagVar, jsonObj); auto node = std::make_shared(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag); @@ -251,6 +252,9 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& if (!loopFlagVar.isEmpty()) { node->setLoopFlagVar(loopFlagVar); } + if (!mirrorFlagVar.isEmpty()) { + node->setMirrorFlagVar(mirrorFlagVar); + } return node; } From aa10af2851f25284dd1ed4c8ee0ceed4f517add1 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 2 Feb 2016 17:10:21 -0800 Subject: [PATCH 39/79] Revert "Move DrawStencil to use ShapePlumber" This reverts commit 671f27e5bc3950cc75884a5b8b0e6b6221011cb5. --- .../render-utils/src/RenderDeferredTask.cpp | 15 ++++++---- .../render-utils/src/RenderDeferredTask.h | 9 +++--- .../render-utils/src/RenderPipelines.cpp | 28 +++++++++---------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 8a47280526..e21b8ce799 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -32,8 +32,8 @@ using namespace render; +extern void initStencilPipeline(gpu::PipelinePointer& pipeline); extern void initOverlay3DPipelines(render::ShapePlumber& plumber); -extern void initStencilPipelines(render::ShapePlumber& plumber); extern void initDeferredPipelines(render::ShapePlumber& plumber); void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { @@ -217,8 +217,12 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon } } -DrawStencilDeferred::DrawStencilDeferred() : _shapePlumber{ std::make_shared() } { - initStencilPipelines(*_shapePlumber); +gpu::PipelinePointer DrawStencilDeferred::_opaquePipeline; +const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() { + if (!_opaquePipeline) { + initStencilPipeline(_opaquePipeline); + } + return _opaquePipeline; } void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { @@ -238,12 +242,11 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); - // We only need to fetch this once - static const auto& pipeline = _shapePlumber->pickPipeline(args, ShapeKey()); + batch.setPipeline(getOpaquePipeline()); - batch.setPipeline(pipeline->pipeline); batch.draw(gpu::TRIANGLE_STRIP, 4); batch.setResourceTexture(0, nullptr); + }); args->_batch = nullptr; } diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 894a91cdc3..0be2e0e808 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -68,14 +68,13 @@ protected: class DrawStencilDeferred { public: - DrawStencilDeferred(); - - void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - using JobModel = render::Job::Model; + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + static const gpu::PipelinePointer& getOpaquePipeline(); + protected: - render::ShapePlumberPointer _shapePlumber; + static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable }; class DrawBackgroundDeferred { diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index a7e9081d9a..d2e880aea3 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -43,19 +43,7 @@ using namespace render; -void initOverlay3DPipelines(ShapePlumber& plumber) { - auto vs = gpu::Shader::createVertex(std::string(overlay3D_vert)); - auto ps = gpu::Shader::createPixel(std::string(overlay3D_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - - auto opaqueState = std::make_shared(); - opaqueState->setDepthTest(false); - opaqueState->setBlendFunction(false); - - plumber.addPipeline(ShapeKey::Filter::Builder().withOpaque(), program, opaqueState); -} - -void initStencilPipelines(ShapePlumber& plumber) { +void initStencilPipeline(gpu::PipelinePointer& pipeline) { const gpu::int8 STENCIL_OPAQUE = 1; auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); auto ps = gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)); @@ -67,7 +55,19 @@ void initStencilPipelines(ShapePlumber& plumber) { state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); state->setColorWriteMask(0); - plumber.addPipeline(ShapeKey::Filter::Builder(), program, state); + pipeline = gpu::Pipeline::create(program, state); +} + +void initOverlay3DPipelines(ShapePlumber& plumber) { + auto vs = gpu::Shader::createVertex(std::string(overlay3D_vert)); + auto ps = gpu::Shader::createPixel(std::string(overlay3D_frag)); + auto program = gpu::Shader::createProgram(vs, ps); + + auto opaqueState = std::make_shared(); + opaqueState->setDepthTest(false); + opaqueState->setBlendFunction(false); + + plumber.addPipeline(ShapeKey::Filter::Builder().withOpaque(), program, opaqueState); } void pipelineBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { From 61daed537617919b859e99622b32ccaeee550a88 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 2 Feb 2016 17:39:43 -0800 Subject: [PATCH 40/79] fix userdata for pistol --- unpublishedScripts/hiddenEntityReset.js | 48 ++++++++++++------------- unpublishedScripts/masterReset.js | 48 ++++++++++++------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 995ebdaad6..903388bb43 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -299,31 +299,31 @@ }, dynamic: true, userData: JSON.stringify({ + wearable: { + joints: { + RightHand: [{ + x: 0.07079616189002991, + y: 0.20177987217903137, + z: 0.06374628841876984 + }, { + x: -0.5863648653030396, + y: -0.46007341146469116, + z: 0.46949487924575806, + w: -0.4733745753765106 + }], + LeftHand: [{ + x: 0.1802254319190979, + y: 0.13442856073379517, + z: 0.08504903316497803 + }, { + x: 0.2198076844215393, + y: -0.7377811074256897, + z: 0.2780133783817291, + w: 0.574519157409668 + }] + } + }, grabbableKey: { - wearable: { - joints: { - RightHand: [{ - x: 0.07079616189002991, - y: 0.20177987217903137, - z: 0.06374628841876984 - }, { - x: -0.5863648653030396, - y: -0.46007341146469116, - z: 0.46949487924575806, - w: -0.4733745753765106 - }], - LeftHand: [{ - x: 0.1802254319190979, - y: 0.13442856073379517, - z: 0.08504903316497803 - }, { - x: 0.2198076844215393, - y: -0.7377811074256897, - z: 0.2780133783817291, - w: 0.574519157409668 - }] - } - }, invertSolidWhileHeld: true }, resetMe: { diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 8777ad5269..306ae294fd 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -283,31 +283,31 @@ MasterReset = function() { damping: 0.5, collisionSoundURL: "http://hifi-content.s3.amazonaws.com/james/pistol/sounds/drop.wav", userData: JSON.stringify({ + wearable: { + joints: { + RightHand: [{ + x: 0.07079616189002991, + y: 0.20177987217903137, + z: 0.06374628841876984 + }, { + x: -0.5863648653030396, + y: -0.46007341146469116, + z: 0.46949487924575806, + w: -0.4733745753765106 + }], + LeftHand: [{ + x: 0.1802254319190979, + y: 0.13442856073379517, + z: 0.08504903316497803 + }, { + x: 0.2198076844215393, + y: -0.7377811074256897, + z: 0.2780133783817291, + w: 0.574519157409668 + }] + } + }, grabbableKey: { - wearable: { - joints: { - RightHand: [{ - x: 0.07079616189002991, - y: 0.20177987217903137, - z: 0.06374628841876984 - }, { - x: -0.5863648653030396, - y: -0.46007341146469116, - z: 0.46949487924575806, - w: -0.4733745753765106 - }], - LeftHand: [{ - x: 0.1802254319190979, - y: 0.13442856073379517, - z: 0.08504903316497803 - }, { - x: 0.2198076844215393, - y: -0.7377811074256897, - z: 0.2780133783817291, - w: 0.574519157409668 - }] - } - }, invertSolidWhileHeld: true }, resetMe: { From 7dd5bca17fad848eac97056fffa2e68a3ff61795 Mon Sep 17 00:00:00 2001 From: "Babiuch, Ryan Nicholas" Date: Wed, 3 Feb 2016 10:46:28 -0600 Subject: [PATCH 41/79] Bug fixes and debt reductions on energy calculations. - removed superfluous code interfacing with scripts - favor floats over doubles in cost calculations - default avatar energy is maximum float value for cases where energy script is not loaded in order to still manipulate entities. --- examples/example/ui/MyEnergyBar.js | 4 ---- libraries/entities/src/EntityScriptingInterface.cpp | 6 +----- libraries/entities/src/EntityScriptingInterface.h | 8 +++----- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/examples/example/ui/MyEnergyBar.js b/examples/example/ui/MyEnergyBar.js index ac7ef0d39c..4c263b9843 100644 --- a/examples/example/ui/MyEnergyBar.js +++ b/examples/example/ui/MyEnergyBar.js @@ -56,11 +56,7 @@ function energyChanged(newValue) { function debitAvatarEnergy(value) { MyAvatar.energy = MyAvatar.energy - value; } -function calculateCost(mass, oldVelocity, newVelocity) { - return mass * (newVelocity - oldVelocity); -} -Entities.addCostFunction(calculateCost); Entities.debitEnergySource.connect(debitAvatarEnergy); MyAvatar.energyChanged.connect(energyChanged); Script.update.connect(update); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 9c61d7c297..3ba2ef0465 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1037,11 +1037,7 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) { return result; } -void EntityScriptingInterface::addCostFunction(QScriptValue costFunction) { - _costFunction = &costFunction; -} - -double EntityScriptingInterface::calculateCost(float mass, float oldVelocity,float newVelocity) { +float EntityScriptingInterface::calculateCost(float mass, float oldVelocity,float newVelocity) { return std::abs(mass * (newVelocity - oldVelocity)); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index ff98ab4eb7..ac56dd3739 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -71,8 +71,7 @@ public: void setEntityTree(EntityTreePointer modelTree); EntityTreePointer getEntityTree() { return _entityTree; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; } - double calculateCost(float mass, float oldVelocity, float newVelocity); - Q_INVOKABLE void addCostFunction(QScriptValue costFunction); + float calculateCost(float mass, float oldVelocity, float newVelocity); public slots: // returns true if the DomainServer will allow this Node/Avatar to make changes @@ -194,7 +193,7 @@ signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); void clearingEntities(); - void debitEnergySource(double value); + void debitEnergySource(float value); private: bool actionWorker(const QUuid& entityID, std::function actor); @@ -212,8 +211,7 @@ private: EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; - QScriptValue* _costFunction = nullptr; - float _currentAvatarEnergy; + float _currentAvatarEnergy = { FLT_MAX }; float getCurrentAvatarEnergy() { return _currentAvatarEnergy; } void setCurrentAvatarEnergy(float energy); float ENTITY_MANIPULATION_MULTIPLIER = { 0.01f }; From 963c71a47698536a979479990f0e712ddfea401b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 3 Feb 2016 09:28:49 -0800 Subject: [PATCH 42/79] Fix ES rejecting unowned entity physics updates --- libraries/entities/src/EntityTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 34dd809510..1405121d2e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -194,7 +194,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI // the entire update is suspect --> ignore it return false; } - } else { + } else if (simulationBlocked) { simulationBlocked = senderID != entity->getSimulatorID(); } if (simulationBlocked) { From 51b335c9020d20686c25d8658068367e39f39edb Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 3 Feb 2016 09:53:14 -0800 Subject: [PATCH 43/79] reduce cellscience volume --- .../DomainContent/CellScience/Scripts/playBackgroundAudio.js | 4 +--- .../CellScience/Scripts/showButtonToPlaySound.js | 2 +- .../DomainContent/CellScience/Scripts/showIdentification.js | 2 +- unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js | 3 ++- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js b/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js index e709f6e06e..856b9f8f67 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/playBackgroundAudio.js @@ -20,7 +20,7 @@ stereo: true, loop: true, localOnly: true, - volume: 0.2 + volume: 0.035 }; this.sound = SoundCache.getSound(self.soundURL); @@ -36,7 +36,6 @@ } } - this.enterEntity = function(entityID) { print("entering audio zone"); if (self.sound.downloaded) { @@ -49,7 +48,6 @@ } - this.leaveEntity = function(entityID) { print("leaving audio area " + self.userData.name); if (self.soundPlaying !== false) { diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js b/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js index 7e51dcd9d1..65fddd7ad3 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/showButtonToPlaySound.js @@ -24,7 +24,7 @@ stereo: true, loop: false, localOnly: true, - volume: 0.2 + volume: 0.035 }; this.sound = SoundCache.getSound(this.soundURL); } diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js b/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js index bfc6c70292..2e37f3a51f 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/showIdentification.js @@ -22,7 +22,7 @@ stereo: true, loop: false, localOnly: true, - volume: 0.2, + volume: 0.035, position: this.position }; this.sound = SoundCache.getSound(this.soundURL); diff --git a/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js b/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js index c644cbe4f9..101ce54a5e 100644 --- a/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js +++ b/unpublishedScripts/DomainContent/CellScience/Scripts/zoom.js @@ -25,8 +25,9 @@ loop: false, localOnly: false, position: this.position, - volume: 0.2 + volume: 0.035 }; + this.teleportSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/whoosh.wav"); //print('Script.clearTimeout PRELOADING A ZOOM ENTITY') print(" portal destination is " + portalDestination); From fff603e4e2f5a48567f7fb390cebdcb0c07a019d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Feb 2016 10:21:27 -0800 Subject: [PATCH 44/79] Removed comment --- libraries/physics/src/CharacterController.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 941428f03a..d16c406658 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -499,8 +499,6 @@ void CharacterController::preSimulation() { } } - /// _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD - _previousFlags = _pendingFlags; _pendingFlags &= ~PENDING_FLAG_JUMP; From ad2a7bfc3c4048bae461a0de08244ab0108c92fc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 Feb 2016 10:54:08 -0800 Subject: [PATCH 45/79] code review --- examples/attachedEntitiesManager.js | 4 ++-- examples/controllers/handControllerGrab.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index c44ac66a44..1cdd61ad39 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -40,7 +40,7 @@ if (SHOW_TOOL_BAR) { var saveButton = toolBar.addOverlay("image", { width: BUTTON_SIZE, height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/save.png", + imageURL: ".../save.png", color: { red: 255, green: 255, @@ -51,7 +51,7 @@ if (SHOW_TOOL_BAR) { var loadButton = toolBar.addOverlay("image", { width: BUTTON_SIZE, height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/load.png", + imageURL: ".../load.png", color: { red: 255, green: 255, diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 842c54b86d..ee3184d78f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1224,6 +1224,7 @@ function MyController(hand) { return true; } } + return false; } this.getPresetPosition = function() { From 26abca92aeab150f198f5d21da12e62cf9881288 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Feb 2016 10:55:07 -0800 Subject: [PATCH 46/79] don't move the re-used mute environment packet --- assignment-client/src/audio/AudioMixer.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 54032e993f..ca60528a71 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -553,15 +553,24 @@ void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer mes if (sendingNode->isAllowedEditor()) { qDebug() << "Received a mute environment packet of" << message->getSize() << "bytes"; - - auto newPacket = NLPacket::create(PacketType::MuteEnvironment, message->getSize()); - // Copy payload - newPacket->write(message->getRawMessage(), message->getSize()); + + glm::vec3 position; + float radius; + + auto newPacket = NLPacket::create(PacketType::MuteEnvironment, sizeof(position) + sizeof(radius)); + + // read the position and radius from the sent packet + message->readPrimitive(&position); + message->readPrimitive(&radius); + + // write them to our packet + newPacket->writePrimitive(position); + newPacket->writePrimitive(radius); nodeList->eachNode([&](const SharedNodePointer& node){ if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != sendingNode) { - nodeList->sendPacket(std::move(newPacket), *node); + nodeList->sendUnreliablePacket(*newPacket, *node); } }); } From 0e20392e663d887c4dbc21927493cdedadb85cba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Feb 2016 11:07:16 -0800 Subject: [PATCH 47/79] remove debug for mute environment packet --- assignment-client/src/audio/AudioMixer.cpp | 102 ++++++++++----------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ca60528a71..227ac843bb 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -458,7 +458,7 @@ int AudioMixer::prepareMixForListeningNode(Node* node) { if (otherNodeStream->getType() == PositionalAudioStream::Microphone) { streamUUID = otherNode->getUUID(); } - + // clear out the pre-mix samples before filling it up with this source memset(_preMixSamples, 0, sizeof(_preMixSamples)); @@ -498,7 +498,7 @@ void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) { break; } } - + AudioMixerClientData* nodeData = static_cast(node->getLinkedData()); AvatarAudioStream* stream = nodeData->getAvatarAudioStream(); bool dataChanged = (stream->hasReverb() != hasReverb) || @@ -550,10 +550,8 @@ void AudioMixer::handleNodeAudioPacket(QSharedPointer message, void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer message, SharedNodePointer sendingNode) { auto nodeList = DependencyManager::get(); - - if (sendingNode->isAllowedEditor()) { - qDebug() << "Received a mute environment packet of" << message->getSize() << "bytes"; + if (sendingNode->isAllowedEditor()) { glm::vec3 position; float radius; @@ -658,185 +656,185 @@ void AudioMixer::sendStatsPacket() { } void AudioMixer::run() { - + qDebug() << "Waiting for connection to domain to request settings from domain-server."; - + // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::settingsReceived, this, &AudioMixer::domainSettingsRequestComplete); connect(&domainHandler, &DomainHandler::settingsReceiveFail, this, &AudioMixer::domainSettingsRequestFailed); - + ThreadedAssignment::commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer); } void AudioMixer::domainSettingsRequestComplete() { auto nodeList = DependencyManager::get(); - + nodeList->addNodeTypeToInterestSet(NodeType::Agent); - + nodeList->linkedDataCreateCallback = [](Node* node) { node->setLinkedData(std::unique_ptr { new AudioMixerClientData }); }; - + DomainHandler& domainHandler = nodeList->getDomainHandler(); const QJsonObject& settingsObject = domainHandler.getSettingsObject(); - + // check the settings object to see if we have anything we can parse out parseSettingsObject(settingsObject); - + // queue up a connection to start broadcasting mixes now that we're ready to go QMetaObject::invokeMethod(this, "broadcastMixes", Qt::QueuedConnection); } void AudioMixer::broadcastMixes() { - auto nodeList = DependencyManager::get(); - + auto nodeList = DependencyManager::get(); + int64_t nextFrame = 0; QElapsedTimer timer; timer.start(); - + int64_t usecToSleep = AudioConstants::NETWORK_FRAME_USECS; - + const int TRAILING_AVERAGE_FRAMES = 100; int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; - + while (!_isFinished) { const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; - + const float RATIO_BACK_OFF = 0.02f; - + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; - + if (usecToSleep < 0) { usecToSleep = 0; } - + _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + (usecToSleep * CURRENT_FRAME_RATIO / (float) AudioConstants::NETWORK_FRAME_USECS); - + float lastCutoffRatio = _performanceThrottlingRatio; bool hasRatioChanged = false; - + if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { // we're struggling - change our min required loudness to reduce some load _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); - + qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; hasRatioChanged = true; } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { // we've recovered and can back off the required loudness _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; - + if (_performanceThrottlingRatio < 0) { _performanceThrottlingRatio = 0; } - + qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; hasRatioChanged = true; } - + if (hasRatioChanged) { // set out min audability threshold from the new ratio _minAudibilityThreshold = LOUDNESS_TO_DISTANCE_RATIO / (2.0f * (1.0f - _performanceThrottlingRatio)); qDebug() << "Minimum audability required to be mixed is now" << _minAudibilityThreshold; - + framesSinceCutoffEvent = 0; } } - + if (!hasRatioChanged) { ++framesSinceCutoffEvent; } - + quint64 now = usecTimestampNow(); if (now - _lastPerSecondCallbackTime > USECS_PER_SECOND) { perSecondActions(); _lastPerSecondCallbackTime = now; } - + nodeList->eachNode([&](const SharedNodePointer& node) { - + if (node->getLinkedData()) { AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData(); - + // this function will attempt to pop a frame from each audio stream. // a pointer to the popped data is stored as a member in InboundAudioStream. // That's how the popped audio data will be read for mixing (but only if the pop was successful) nodeData->checkBuffersBeforeFrameSend(); - + // if the stream should be muted, send mute packet if (nodeData->getAvatarAudioStream() && shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) { auto mutePacket = NLPacket::create(PacketType::NoisyMute, 0); nodeList->sendPacket(std::move(mutePacket), *node); } - + if (node->getType() == NodeType::Agent && node->getActiveSocket() && nodeData->getAvatarAudioStream()) { - + int streamsMixed = prepareMixForListeningNode(node.data()); - + std::unique_ptr mixPacket; - + if (streamsMixed > 0) { int mixPacketBytes = sizeof(quint16) + AudioConstants::NETWORK_FRAME_BYTES_STEREO; mixPacket = NLPacket::create(PacketType::MixedAudio, mixPacketBytes); - + // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); mixPacket->writePrimitive(sequence); - + // pack mixed audio samples mixPacket->write(reinterpret_cast(_mixSamples), AudioConstants::NETWORK_FRAME_BYTES_STEREO); } else { int silentPacketBytes = sizeof(quint16) + sizeof(quint16); mixPacket = NLPacket::create(PacketType::SilentAudioFrame, silentPacketBytes); - + // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); mixPacket->writePrimitive(sequence); - + // pack number of silent audio samples quint16 numSilentSamples = AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; mixPacket->writePrimitive(numSilentSamples); } - + // Send audio environment sendAudioEnvironmentPacket(node); - + // send mixed audio packet nodeList->sendPacket(std::move(mixPacket), *node); nodeData->incrementOutgoingMixedAudioSequenceNumber(); - + // send an audio stream stats packet if it's time if (_sendAudioStreamStats) { nodeData->sendAudioStreamStatsPackets(node); _sendAudioStreamStats = false; } - + ++_sumListeners; } } }); - + ++_numStatFrames; - + // since we're a while loop we need to help Qt's event processing QCoreApplication::processEvents(); - + if (_isFinished) { // at this point the audio-mixer is done // check if we have a deferred delete event to process (which we should once finished) QCoreApplication::sendPostedEvents(this, QEvent::DeferredDelete); break; } - + usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - (timer.nsecsElapsed() / 1000); if (usecToSleep > 0) { @@ -1114,5 +1112,3 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) { } } } - - From 93530fca72fa36ff5422cc5d8d97f4f25c034d6c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 1 Feb 2016 16:20:59 -0800 Subject: [PATCH 48/79] fix the last of the current override warnings for OS X --- interface/src/Application.h | 10 +++++----- interface/src/avatar/Avatar.h | 8 ++++---- interface/src/avatar/AvatarMotionState.h | 4 ++-- interface/src/ui/overlays/ImageOverlay.h | 2 +- interface/src/ui/overlays/TextOverlay.h | 2 +- libraries/animation/src/SwingTwistConstraint.h | 2 +- .../src/RenderableBoxEntityItem.h | 4 ++-- .../src/RenderableLightEntityItem.h | 6 +++--- .../src/RenderableLineEntityItem.h | 2 +- .../src/RenderableSphereEntityItem.h | 4 ++-- .../src/RenderableTextEntityItem.h | 2 +- .../src/RenderableWebEntityItem.h | 4 ++-- libraries/physics/src/EntityMotionState.h | 16 ++++++++-------- 13 files changed, 33 insertions(+), 33 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 4b32d8879b..3d2cb6f351 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -167,11 +167,11 @@ public: virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; } virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) override; - virtual ViewFrustum* getCurrentViewFrustum() { return getDisplayViewFrustum(); } - virtual QThread* getMainThread() { return thread(); } - virtual PickRay computePickRay(float x, float y) const; - virtual glm::vec3 getAvatarPosition() const; - virtual qreal getDevicePixelRatio(); + virtual ViewFrustum* getCurrentViewFrustum() override { return getDisplayViewFrustum(); } + virtual QThread* getMainThread() override { return thread(); } + virtual PickRay computePickRay(float x, float y) const override; + virtual glm::vec3 getAvatarPosition() const override; + virtual qreal getDevicePixelRatio() override; void setActiveDisplayPlugin(const QString& pluginName); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index db247f3e85..b23b64b384 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,10 +164,10 @@ public: virtual void setOrientation(const glm::quat& orientation) override; // these call through to the SpatiallyNestable versions, but they are here to expose these to javascript. - Q_INVOKABLE virtual const QUuid getParentID() const { return SpatiallyNestable::getParentID(); } - Q_INVOKABLE virtual void setParentID(const QUuid& parentID); - Q_INVOKABLE virtual quint16 getParentJointIndex() const { return SpatiallyNestable::getParentJointIndex(); } - Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex); + Q_INVOKABLE virtual const QUuid getParentID() const override { return SpatiallyNestable::getParentID(); } + Q_INVOKABLE virtual void setParentID(const QUuid& parentID) override; + Q_INVOKABLE virtual quint16 getParentJointIndex() const override { return SpatiallyNestable::getParentJointIndex(); } + Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override; // NOT thread safe, must be called on main thread. glm::vec3 getUncachedLeftPalmPosition() const; diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 715c38186b..04aa5ea57c 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -61,7 +61,7 @@ public: void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } - virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const; + virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; friend class AvatarManager; friend class Avatar; @@ -72,7 +72,7 @@ protected: ~AvatarMotionState(); virtual bool isReadyToComputeShape() const override { return true; } - virtual btCollisionShape* computeNewShape(); + virtual btCollisionShape* computeNewShape() override; // The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState // instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor. diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index 224cb42045..40da461822 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -20,7 +20,7 @@ class ImageOverlay : public QmlOverlay { public: static QString const TYPE; - virtual QString getType() const { return TYPE; } + virtual QString getType() const override { return TYPE; } static QUrl const URL; ImageOverlay(); diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index 7c6e133ebd..53c1805345 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -30,7 +30,7 @@ public: void setText(const QString& text); - TextOverlay* createClone() const; + TextOverlay* createClone() const override; QSizeF textSize(const QString& text) const; // Pixels }; diff --git a/libraries/animation/src/SwingTwistConstraint.h b/libraries/animation/src/SwingTwistConstraint.h index f36dc851ea..f73bbfb233 100644 --- a/libraries/animation/src/SwingTwistConstraint.h +++ b/libraries/animation/src/SwingTwistConstraint.h @@ -51,7 +51,7 @@ public: virtual bool apply(glm::quat& rotation) const override; void setLowerSpine(bool lowerSpine) { _lowerSpine = lowerSpine; } - virtual bool isLowerSpine() const { return _lowerSpine; } + virtual bool isLowerSpine() const override { return _lowerSpine; } // SwingLimitFunction is an implementation of the constraint check described in the paper: // "The Parameterization of Joint Rotation with the Unit Quaternion" by Quang Liu and Edmond C. Prakash diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.h b/libraries/entities-renderer/src/RenderableBoxEntityItem.h index 9addfd813a..67f881dbd8 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.h @@ -22,8 +22,8 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableBoxEntityItem(const EntityItemID& entityItemID) : BoxEntityItem(entityItemID) { } - virtual void render(RenderArgs* args); - virtual void setUserData(const QString& value); + virtual void render(RenderArgs* args) override; + virtual void setUserData(const QString& value) override; SIMPLE_RENDERABLE() private: diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.h b/libraries/entities-renderer/src/RenderableLightEntityItem.h index aac1a4a998..2db913db0d 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.h @@ -20,12 +20,12 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableLightEntityItem(const EntityItemID& entityItemID) : LightEntityItem(entityItemID) { } - virtual void render(RenderArgs* args); - virtual bool supportsDetailedRayIntersection() const { return true; } + virtual void render(RenderArgs* args) override; + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; SIMPLE_RENDERABLE(); }; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h index 9af8c0c8ba..1227c6e63d 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -24,7 +24,7 @@ public: _lineVerticesID(GeometryCache::UNKNOWN_ID) { } - virtual void render(RenderArgs* args); + virtual void render(RenderArgs* args) override; SIMPLE_RENDERABLE(); diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.h b/libraries/entities-renderer/src/RenderableSphereEntityItem.h index 737bee3134..5efe49854a 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.h +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.h @@ -22,8 +22,8 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); RenderableSphereEntityItem(const EntityItemID& entityItemID) : SphereEntityItem(entityItemID) { } - virtual void render(RenderArgs* args); - virtual void setUserData(const QString& value); + virtual void render(RenderArgs* args) override; + virtual void setUserData(const QString& value) override; SIMPLE_RENDERABLE(); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index 149df946f7..cbe2b11c27 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -25,7 +25,7 @@ public: RenderableTextEntityItem(const EntityItemID& entityItemID) : TextEntityItem(entityItemID) { } ~RenderableTextEntityItem() { delete _textRenderer; } - virtual void render(RenderArgs* args); + virtual void render(RenderArgs* args) override; SIMPLE_RENDERABLE(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 799a414151..da1ddbf1a1 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -25,8 +25,8 @@ public: RenderableWebEntityItem(const EntityItemID& entityItemID); ~RenderableWebEntityItem(); - virtual void render(RenderArgs* args); - virtual void setSourceUrl(const QString& value); + virtual void render(RenderArgs* args) override; + virtual void setSourceUrl(const QString& value) override; void setProxyWindow(QWindow* proxyWindow); QObject* getEventHandler(); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 152ae7be23..2c999d0aca 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -29,13 +29,13 @@ public: virtual ~EntityMotionState(); void updateServerPhysicsVariables(); - virtual bool handleEasyChanges(uint32_t& flags); - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine); + virtual bool handleEasyChanges(uint32_t& flags) override; + virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; /// \return PhysicsMotionType based on params set in EntityItem - virtual PhysicsMotionType computePhysicsMotionType() const; + virtual PhysicsMotionType computePhysicsMotionType() const override; - virtual bool isMoving() const; + virtual bool isMoving() const override; // this relays incoming position/rotation to the RigidBody virtual void getWorldTransform(btTransform& worldTrans) const override; @@ -48,8 +48,8 @@ public: bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID); void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step); - virtual uint32_t getIncomingDirtyFlags(); - virtual void clearIncomingDirtyFlags(); + virtual uint32_t getIncomingDirtyFlags() override; + virtual void clearIncomingDirtyFlags() override; void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } @@ -80,7 +80,7 @@ public: virtual QString getName() const override; - virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const; + virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; // eternal logic can suggest a simuator priority bid for the next outgoing update void setOutgoingPriority(quint8 priority); @@ -93,7 +93,7 @@ protected: #endif virtual bool isReadyToComputeShape() const override; - virtual btCollisionShape* computeNewShape(); + virtual btCollisionShape* computeNewShape() override; virtual void setMotionType(PhysicsMotionType motionType); // In the glorious future (when entities lib depends on physics lib) the EntityMotionState will be From 3cb2f9c4b7ae96dcb12826087f510d14348926c1 Mon Sep 17 00:00:00 2001 From: "Babiuch, Ryan Nicholas" Date: Wed, 3 Feb 2016 13:29:42 -0600 Subject: [PATCH 49/79] Exposed energy cost multiplier to scripting. - field: costMultiplier --- examples/example/ui/MyEnergyBar.js | 2 +- .../entities/src/EntityScriptingInterface.cpp | 14 +++++++++++--- libraries/entities/src/EntityScriptingInterface.h | 6 +++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/example/ui/MyEnergyBar.js b/examples/example/ui/MyEnergyBar.js index 4c263b9843..c24f998e1a 100644 --- a/examples/example/ui/MyEnergyBar.js +++ b/examples/example/ui/MyEnergyBar.js @@ -56,7 +56,7 @@ function energyChanged(newValue) { function debitAvatarEnergy(value) { MyAvatar.energy = MyAvatar.energy - value; } - +Entities.costMultiplier = 0.002; Entities.debitEnergySource.connect(debitAvatarEnergy); MyAvatar.energyChanged.connect(energyChanged); Script.update.connect(update); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3ba2ef0465..5ae6f99622 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -126,7 +126,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties auto density = propertiesWithSimID.getDensity(); auto newVelocity = propertiesWithSimID.getVelocity().length(); double cost = calculateCost(density*volume, 0, newVelocity); - cost *= ENTITY_MANIPULATION_MULTIPLIER; //try this as a constant for now + cost *= costMultiplier; if(cost > _currentAvatarEnergy) { return QUuid(); @@ -231,7 +231,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& auto density = properties.getDensity(); auto newVelocity = properties.getVelocity().length(); double cost = calculateCost(density*volume, 0, newVelocity); - cost *= ENTITY_MANIPULATION_MULTIPLIER; + cost *= costMultiplier; if(cost > _currentAvatarEnergy) { return QUuid(); @@ -351,7 +351,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { auto density = entity->getDensity(); auto velocity = entity->getVelocity().length(); double cost = calculateCost(density*volume, velocity, 0); - cost *= ENTITY_MANIPULATION_MULTIPLIER; + cost *= costMultiplier; if(cost > _currentAvatarEnergy) { return; @@ -1045,3 +1045,11 @@ void EntityScriptingInterface::setCurrentAvatarEnergy(float energy) { // qCDebug(entities) << "NEW AVATAR ENERGY IN ENTITY SCRIPTING INTERFACE: " << energy; _currentAvatarEnergy = energy; } + +float EntityScriptingInterface::getCostMultiplier() { + return costMultiplier; +} + +void EntityScriptingInterface::setCostMultiplier(float value) { + costMultiplier = value; +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index ac56dd3739..3bb86fba53 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -61,6 +61,7 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende Q_OBJECT Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy) + Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) public: EntityScriptingInterface(); @@ -214,7 +215,10 @@ private: float _currentAvatarEnergy = { FLT_MAX }; float getCurrentAvatarEnergy() { return _currentAvatarEnergy; } void setCurrentAvatarEnergy(float energy); - float ENTITY_MANIPULATION_MULTIPLIER = { 0.01f }; + + float costMultiplier = { 0.01f }; + float getCostMultiplier(); + void setCostMultiplier(float value); }; #endif // hifi_EntityScriptingInterface_h From ea9d84bdc47556b9e31dad5385b566840417723c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Feb 2016 13:18:29 -0800 Subject: [PATCH 50/79] handle downloading of absolute persist file contents --- assignment-client/src/octree/OctreeServer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 69c54a17d5..31cab68cdf 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -289,6 +289,8 @@ void OctreeServer::initHTTPManager(int port) { _httpManager = new HTTPManager(QHostAddress::AnyIPv4, port, documentRoot, this, this); } +const QString PERSIST_FILE_DOWNLOAD_PATH = "/models.json.gz"; + bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { #ifdef FORCE_CRASH @@ -310,7 +312,6 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url #endif bool showStats = false; - QString persistFile = "/" + getPersistFilename(); if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { if (url.path() == "/") { @@ -320,7 +321,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url _tree->resetEditStats(); resetSendingStats(); showStats = true; - } else if ((url.path() == persistFile) || (url.path() == persistFile + "/")) { + } else if ((url.path() == PERSIST_FILE_DOWNLOAD_PATH) || (url.path() == PERSIST_FILE_DOWNLOAD_PATH + "/")) { if (_persistFileDownload) { QByteArray persistFileContents = getPersistFileContents(); if (persistFileContents.length() > 0) { @@ -374,9 +375,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += "\r\n"; if (_persistFileDownload) { - statsString += QString("Persist file: %1\r\n").arg(persistFile); + statsString += QString("Persist file: Click to Download\r\n").arg(PERSIST_FILE_DOWNLOAD_PATH); } else { - statsString += QString("Persist file: %1\r\n").arg(persistFile); + statsString += QString("Persist file: %1\r\n").arg(_persistFilePath); } } else { From c1d45b0c784c2b94407a3d16189baa50439d9ae2 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 17:09:58 -0800 Subject: [PATCH 51/79] Remove declarative form creation from WindowScriptingInterface --- examples/edit.js | 2 - examples/libraries/entityPropertyDialogBox.js | 457 ------------------ .../scripting/WindowScriptingInterface.cpp | 431 +---------------- .../src/scripting/WindowScriptingInterface.h | 29 +- 4 files changed, 8 insertions(+), 911 deletions(-) delete mode 100644 examples/libraries/entityPropertyDialogBox.js diff --git a/examples/edit.js b/examples/edit.js index cc5921efd1..3e46160522 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -23,7 +23,6 @@ Script.include([ "libraries/ToolTip.js", - "libraries/entityPropertyDialogBox.js", "libraries/entityCameraTool.js", "libraries/gridTool.js", "libraries/entityList.js", @@ -32,7 +31,6 @@ Script.include([ var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; -var entityPropertyDialogBox = EntityPropertyDialogBox; var lightOverlayManager = new LightOverlayManager(); diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js deleted file mode 100644 index b04d797b77..0000000000 --- a/examples/libraries/entityPropertyDialogBox.js +++ /dev/null @@ -1,457 +0,0 @@ -// -// entityPropertyDialogBox.js -// examples -// -// Created by Brad hefta-Gaub on 10/1/14. -// Copyright 2014 High Fidelity, Inc. -// -// This script implements a class useful for building tools for editing entities. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var DEGREES_TO_RADIANS = Math.PI / 180.0; -var RADIANS_TO_DEGREES = 180.0 / Math.PI; - -EntityPropertyDialogBox = (function () { - var that = {}; - - var propertiesForEditedEntity; - var editEntityFormArray; - var decimals = 3; - var dimensionX; - var dimensionY; - var dimensionZ; - var rescalePercentage; - var editModelID = -1; - var previousAnimationIsPlaying; - var previousAnimationCurrentFrame; - - that.cleanup = function () { - }; - - that.openDialog = function (entityID) { - print(" Edit Properties.... about to edit properties..."); - - editModelID = entityID; - propertiesForEditedEntity = Entities.getEntityProperties(editModelID); - var properties = propertiesForEditedEntity; - - var array = new Array(); - var index = 0; - - array.push({ label: "Entity Type:" + properties.type, type: "header" }); - index++; - array.push({ label: "ID:", value: properties.id }); - index++; - array.push({ label: "Locked:", type: "checkbox", value: properties.locked }); - index++; - - if (properties.type == "Model") { - array.push({ label: "Model URL:", value: properties.modelURL }); - index++; - array.push({ label: "Shape Type:", value: properties.shapeType }); - index++; - array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL }); - index++; - array.push({ label: "Animation URL:", value: properties.animation.url }); - index++; - array.push({ label: "Animation is playing:", type: "checkbox", value: properties.animation.running }); - previousAnimationIsPlaying = properties.animation.running; - index++; - array.push({ label: "Animation FPS:", value: properties.animation.fps }); - index++; - array.push({ label: "Animation Frame:", value: properties.animation.currentFrame }); - previousAnimationCurrentFrame = properties.animation.currentFrame; - index++; - array.push({ label: "Textures:", value: properties.textures }); - index++; - array.push({ label: "Original Textures:\n" + properties.originalTextures, type: "header" }); - index++; - } - - if (properties.type == "Text") { - array.push({ label: "Text:", value: properties.text }); - index++; - array.push({ label: "Line Height:", value: properties.lineHeight }); - index++; - array.push({ label: "Text Color:", type: "header" }); - index++; - array.push({ label: "Red:", value: properties.textColor.red }); - index++; - array.push({ label: "Green:", value: properties.textColor.green }); - index++; - array.push({ label: "Blue:", value: properties.textColor.blue }); - index++; - array.push({ label: "Background Color:", type: "header" }); - index++; - array.push({ label: "Red:", value: properties.backgroundColor.red }); - index++; - array.push({ label: "Green:", value: properties.backgroundColor.green }); - index++; - array.push({ label: "Blue:", value: properties.backgroundColor.blue }); - index++; - } - - if (properties.type == "PolyVox") { - array.push({ label: "Voxel Space Size:", type: "header" }); - index++; - - array.push({ label: "X:", value: properties.voxelVolumeSize.x.toFixed(decimals) }); - index++; - array.push({ label: "Y:", value: properties.voxelVolumeSize.y.toFixed(decimals) }); - index++; - array.push({ label: "Z:", value: properties.voxelVolumeSize.z.toFixed(decimals) }); - index++; - - array.push({ label: "Surface Extractor", value: properties.voxelSurfaceStyle }); - index++; - - array.push({ label: "X-axis Texture URL:", value: properties.xTextureURL }); - index++; - array.push({ label: "Y-axis Texture URL:", value: properties.yTextureURL }); - index++; - array.push({ label: "Z-axis Texture URL:", value: properties.zTextureURL }); - index++; - } - - array.push({ label: "Position:", type: "header" }); - index++; - array.push({ label: "X:", value: properties.position.x.toFixed(decimals) }); - index++; - array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) }); - index++; - array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) }); - index++; - - array.push({ label: "Registration X:", value: properties.registrationPoint.x.toFixed(decimals) }); - index++; - array.push({ label: "Registration Y:", value: properties.registrationPoint.y.toFixed(decimals) }); - index++; - array.push({ label: "Registration Z:", value: properties.registrationPoint.z.toFixed(decimals) }); - index++; - - array.push({ label: "Rotation:", type: "header" }); - index++; - var angles = Quat.safeEulerAngles(properties.rotation); - array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) }); - index++; - array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) }); - index++; - array.push({ label: "Roll:", value: angles.z.toFixed(decimals) }); - index++; - - array.push({ label: "Dimensions:", type: "header" }); - index++; - array.push({ label: "Width:", value: properties.dimensions.x.toFixed(decimals) }); - dimensionX = index; - index++; - array.push({ label: "Height:", value: properties.dimensions.y.toFixed(decimals) }); - dimensionY = index; - index++; - array.push({ label: "Depth:", value: properties.dimensions.z.toFixed(decimals) }); - dimensionZ = index; - index++; - array.push({ label: "", type: "inlineButton", buttonLabel: "Reset to Natural Dimensions", name: "resetDimensions" }); - index++; - array.push({ label: "Rescale Percentage:", value: 100 }); - rescalePercentage = index; - index++; - array.push({ label: "", type: "inlineButton", buttonLabel: "Rescale", name: "rescaleDimensions" }); - index++; - - array.push({ label: "Velocity:", type: "header" }); - index++; - array.push({ label: "Linear X:", value: properties.velocity.x.toFixed(decimals) }); - index++; - array.push({ label: "Linear Y:", value: properties.velocity.y.toFixed(decimals) }); - index++; - array.push({ label: "Linear Z:", value: properties.velocity.z.toFixed(decimals) }); - index++; - array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) }); - index++; - // NOTE: angular velocity is in radians/sec but we display degrees/sec for users - array.push({ label: "Angular Pitch:", value: (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(decimals) }); - index++; - array.push({ label: "Angular Yaw:", value: (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(decimals) }); - index++; - array.push({ label: "Angular Roll:", value: (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(decimals) }); - index++; - array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) }); - index++; - - array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) }); - index++; - array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) }); - index++; - array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) }); - index++; - - array.push({ label: "Acceleration X:", value: properties.acceleration.x.toFixed(decimals) }); - index++; - array.push({ label: "Acceleration Y:", value: properties.acceleration.y.toFixed(decimals) }); - index++; - array.push({ label: "Acceleration Z:", value: properties.acceleration.z.toFixed(decimals) }); - index++; - - array.push({ label: "Collisions:", type: "header" }); - index++; - array.push({ label: "Density:", value: properties.density.toFixed(decimals) }); - index++; - array.push({ label: "Collisionless:", type: "checkbox", value: properties.collisionless }); - index++; - array.push({ label: "Dynamic:", type: "checkbox", value: properties.dynamic }); - index++; - array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL }); - index++; - - array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) }); - index++; - - array.push({ label: "Visible:", type: "checkbox", value: properties.visible }); - index++; - - array.push({ label: "Script:", value: properties.script }); - index++; - - if (properties.type == "Box" || properties.type == "Sphere") { - array.push({ label: "Color:", type: "header" }); - index++; - array.push({ label: "Red:", value: properties.color.red }); - index++; - array.push({ label: "Green:", value: properties.color.green }); - index++; - array.push({ label: "Blue:", value: properties.color.blue }); - index++; - } - - if (properties.type == "Light") { - array.push({ label: "Light Properties:", type: "header" }); - index++; - array.push({ label: "Is Spot Light:", value: properties.isSpotlight }); - index++; - array.push({ label: "Diffuse Red:", value: properties.diffuseColor.red }); - index++; - array.push({ label: "Diffuse Green:", value: properties.diffuseColor.green }); - index++; - array.push({ label: "Diffuse Blue:", value: properties.diffuseColor.blue }); - index++; - array.push({ label: "Ambient Red:", value: properties.ambientColor.red }); - index++; - array.push({ label: "Ambient Green:", value: properties.ambientColor.green }); - index++; - array.push({ label: "Ambient Blue:", value: properties.ambientColor.blue }); - index++; - array.push({ label: "Specular Red:", value: properties.specularColor.red }); - index++; - array.push({ label: "Specular Green:", value: properties.specularColor.green }); - index++; - array.push({ label: "Specular Blue:", value: properties.specularColor.blue }); - index++; - array.push({ label: "Constant Attenuation:", value: properties.constantAttenuation }); - index++; - array.push({ label: "Linear Attenuation:", value: properties.linearAttenuation }); - index++; - array.push({ label: "Quadratic Attenuation:", value: properties.quadraticAttenuation }); - index++; - array.push({ label: "Exponent:", value: properties.exponent }); - index++; - array.push({ label: "Cutoff (in degrees):", value: properties.cutoff }); - index++; - } - - array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); - index++; - - array.push({ button: "Cancel" }); - index++; - - editEntityFormArray = array; - Window.nonBlockingForm("Edit Properties", array); - }; - - Window.inlineButtonClicked.connect(function (name) { - if (name == "previewCamera") { - Camera.mode = "entity"; - Camera.cameraEntity = propertiesForEditedEntity.id; - } - - if (name == "resetDimensions") { - Window.reloadNonBlockingForm([ - { value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX }, - { value: propertiesForEditedEntity.naturalDimensions.y.toFixed(decimals), oldIndex: dimensionY }, - { value: propertiesForEditedEntity.naturalDimensions.z.toFixed(decimals), oldIndex: dimensionZ } - ]); - } - - if (name == "rescaleDimensions") { - var peekValues = editEntityFormArray; - Window.peekNonBlockingFormResult(peekValues); - var peekX = peekValues[dimensionX].value; - var peekY = peekValues[dimensionY].value; - var peekZ = peekValues[dimensionZ].value; - var peekRescale = peekValues[rescalePercentage].value; - var rescaledX = peekX * peekRescale / 100.0; - var rescaledY = peekY * peekRescale / 100.0; - var rescaledZ = peekZ * peekRescale / 100.0; - - Window.reloadNonBlockingForm([ - { value: rescaledX.toFixed(decimals), oldIndex: dimensionX }, - { value: rescaledY.toFixed(decimals), oldIndex: dimensionY }, - { value: rescaledZ.toFixed(decimals), oldIndex: dimensionZ }, - { value: 100, oldIndex: rescalePercentage } - ]); - } - - }); - Window.nonBlockingFormClosed.connect(function() { - array = editEntityFormArray; - if (Window.getNonBlockingFormResult(array)) { - var properties = propertiesForEditedEntity; - var index = 0; - index++; // skip type header - index++; // skip id item - properties.locked = array[index++].value; - if (properties.type == "Model") { - properties.modelURL = array[index++].value; - properties.shapeType = array[index++].value; - properties.compoundShapeURL = array[index++].value; - properties.animation.url = array[index++].value; - - var newAnimationIsPlaying = array[index++].value; - if (previousAnimationIsPlaying != newAnimationIsPlaying) { - properties.animation.running = newAnimationIsPlaying; - } else { - delete properties.animation.running; - } - - properties.animation.fps = array[index++].value; - - var newAnimationCurrentFrame = array[index++].value; - if (previousAnimationCurrentFrame != newAnimationCurrentFrame) { - properties.animation.currentFrame = newAnimationCurrentFrame; - } else { - delete properties.animation.currentFrame; - } - - properties.textures = array[index++].value; - index++; // skip textureNames label - } - - if (properties.type == "Text") { - properties.text = array[index++].value; - properties.lineHeight = array[index++].value; - - index++; // skip header - properties.textColor.red = array[index++].value; - properties.textColor.green = array[index++].value; - properties.textColor.blue = array[index++].value; - - index++; // skip header - properties.backgroundColor.red = array[index++].value; - properties.backgroundColor.green = array[index++].value; - properties.backgroundColor.blue = array[index++].value; - } - - if (properties.type == "PolyVox") { - properties.shapeType = array[index++].value; - - index++; // skip header - properties.voxelVolumeSize.x = array[index++].value; - properties.voxelVolumeSize.y = array[index++].value; - properties.voxelVolumeSize.z = array[index++].value; - properties.voxelSurfaceStyle = array[index++].value; - properties.xTextureURL = array[index++].value; - properties.yTextureURL = array[index++].value; - properties.zTextureURL = array[index++].value; - } - - index++; // skip header - properties.position.x = array[index++].value; - properties.position.y = array[index++].value; - properties.position.z = array[index++].value; - properties.registrationPoint.x = array[index++].value; - properties.registrationPoint.y = array[index++].value; - properties.registrationPoint.z = array[index++].value; - - index++; // skip header - var angles = Quat.safeEulerAngles(properties.rotation); - angles.x = array[index++].value; - angles.y = array[index++].value; - angles.z = array[index++].value; - properties.rotation = Quat.fromVec3Degrees(angles); - - index++; // skip header - properties.dimensions.x = array[index++].value; - properties.dimensions.y = array[index++].value; - properties.dimensions.z = array[index++].value; - index++; // skip reset button - index++; // skip rescale percentage - index++; // skip rescale button - - index++; // skip header - properties.velocity.x = array[index++].value; - properties.velocity.y = array[index++].value; - properties.velocity.z = array[index++].value; - properties.damping = array[index++].value; - - // NOTE: angular velocity is in radians/sec but we display degrees/sec for users - properties.angularVelocity.x = array[index++].value * DEGREES_TO_RADIANS; - properties.angularVelocity.y = array[index++].value * DEGREES_TO_RADIANS; - properties.angularVelocity.z = array[index++].value * DEGREES_TO_RADIANS; - properties.angularDamping = array[index++].value; - - properties.gravity.x = array[index++].value; - properties.gravity.y = array[index++].value; - properties.gravity.z = array[index++].value; - - properties.acceleration.x = array[index++].value; - properties.acceleration.y = array[index++].value; - properties.acceleration.z = array[index++].value; - - index++; // skip header - properties.density = array[index++].value; - properties.collisionless = array[index++].value; - properties.dynamic = array[index++].value; - - properties.lifetime = array[index++].value; - properties.visible = array[index++].value; - properties.script = array[index++].value; - - if (properties.type == "Box" || properties.type == "Sphere") { - index++; // skip header - properties.color.red = array[index++].value; - properties.color.green = array[index++].value; - properties.color.blue = array[index++].value; - } - if (properties.type == "Light") { - index++; // skip header - properties.isSpotlight = array[index++].value; - properties.diffuseColor.red = array[index++].value; - properties.diffuseColor.green = array[index++].value; - properties.diffuseColor.blue = array[index++].value; - properties.ambientColor.red = array[index++].value; - properties.ambientColor.green = array[index++].value; - properties.ambientColor.blue = array[index++].value; - properties.specularColor.red = array[index++].value; - properties.specularColor.green = array[index++].value; - properties.specularColor.blue = array[index++].value; - properties.constantAttenuation = array[index++].value; - properties.linearAttenuation = array[index++].value; - properties.quadraticAttenuation = array[index++].value; - properties.exponent = array[index++].value; - properties.cutoff = array[index++].value; - } - - Entities.editEntity(editModelID, properties); - if (typeof(selectionDisplay) != "undefined") { - selectionDisplay.select(editModelID, false); - } - } - modelSelected = false; - }); - - return that; - -}()); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 73c0098995..5d5837ebd8 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -27,11 +27,7 @@ #include "WindowScriptingInterface.h" -WindowScriptingInterface::WindowScriptingInterface() : - _editDialog(NULL), - _nonBlockingFormActive(false), - _formResult(QDialog::Rejected) -{ +WindowScriptingInterface::WindowScriptingInterface() { const DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged); connect(qApp, &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); @@ -98,14 +94,6 @@ QScriptValue WindowScriptingInterface::confirm(const QString& message) { return retVal; } -QScriptValue WindowScriptingInterface::form(const QString& title, QScriptValue form) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showForm", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(const QString&, title), Q_ARG(QScriptValue, form)); - return retVal; -} - QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { QScriptValue retVal; QMetaObject::invokeMethod(this, "showPrompt", Qt::BlockingQueuedConnection, @@ -139,32 +127,6 @@ QScriptValue WindowScriptingInterface::s3Browse(const QString& nameFilter) { return retVal; } -void WindowScriptingInterface::nonBlockingForm(const QString& title, QScriptValue form) { - QMetaObject::invokeMethod(this, "showNonBlockingForm", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, title), Q_ARG(QScriptValue, form)); -} - -void WindowScriptingInterface::reloadNonBlockingForm(QScriptValue newValues) { - QMetaObject::invokeMethod(this, "doReloadNonBlockingForm", Qt::BlockingQueuedConnection, - Q_ARG(QScriptValue, newValues)); -} - - -QScriptValue WindowScriptingInterface::getNonBlockingFormResult(QScriptValue form) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "doGetNonBlockingFormResult", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(QScriptValue, form)); - return retVal; -} - -QScriptValue WindowScriptingInterface::peekNonBlockingFormResult(QScriptValue form) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "doPeekNonBlockingFormResult", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(QScriptValue, form)); - return retVal; -} /// Display an alert box /// \param const QString& message message to display @@ -217,402 +179,23 @@ void WindowScriptingInterface::inlineButtonClicked() { emit inlineButtonClicked(name); } -QString WindowScriptingInterface::jsRegExp2QtRegExp(QString string) { +QString WindowScriptingInterface::jsRegExp2QtRegExp(const QString& string) { // Converts string representation of RegExp from JavaScript format to Qt format. return string.mid(1, string.length() - 2) // No enclosing slashes. .replace("\\/", "/"); // No escaping of forward slash. } -void WindowScriptingInterface::showNonBlockingForm(const QString& title, QScriptValue form) { - if (!form.isArray() || (form.isArray() && form.property("length").toInt32() <= 0)) { - return; - } - - // what should we do if someone calls us while we still think we have a dialog showing??? - if (_nonBlockingFormActive) { - qDebug() << "Show Non-Blocking Form called when form already active."; - return; - } - - _form = form; - _editDialog = createForm(title, _form); - _nonBlockingFormActive = true; - - connect(_editDialog, SIGNAL(accepted()), this, SLOT(nonBlockingFormAccepted())); - connect(_editDialog, SIGNAL(rejected()), this, SLOT(nonBlockingFormRejected())); - - _editDialog->setModal(true); - _editDialog->show(); -} - -void WindowScriptingInterface::doReloadNonBlockingForm(QScriptValue newValues) { - if (!newValues.isArray() || (newValues.isArray() && newValues.property("length").toInt32() <= 0)) { - return; - } - - // what should we do if someone calls us while we still think we have a dialog showing??? - if (!_editDialog) { - qDebug() << "Reload Non-Blocking Form called when no form is active."; - return; - } - - for (int i = 0; i < newValues.property("length").toInt32(); ++i) { - QScriptValue item = newValues.property(i); - - if (item.property("oldIndex").isValid()) { - int oldIndex = item.property("oldIndex").toInt32(); - QScriptValue oldItem = _form.property(oldIndex); - if (oldItem.isValid()) { - QLineEdit* originalEdit = _edits[oldItem.property("editIndex").toInt32()]; - originalEdit->setText(item.property("value").toString()); - } - } - } -} - - -bool WindowScriptingInterface::nonBlockingFormActive() { - return _nonBlockingFormActive; -} - -QScriptValue WindowScriptingInterface::doPeekNonBlockingFormResult(QScriptValue array) { - QScriptValue retVal; - - int e = -1; - int d = -1; - int c = -1; - int h = -1; - for (int i = 0; i < _form.property("length").toInt32(); ++i) { - QScriptValue item = _form.property(i); - QScriptValue value = item.property("value"); - - if (item.property("button").toString() != "") { - // Nothing to do - } else if (item.property("type").toString() == "inlineButton") { - // Nothing to do - } else if (item.property("type").toString() == "header") { - // Nothing to do - } else if (item.property("directory").toString() != "") { - d += 1; - value = _directories.at(d)->property("path").toString(); - item.setProperty("directory", value); - _form.setProperty(i, item); - } else if (item.property("options").isArray()) { - c += 1; - item.setProperty("value", - _combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ? - item.property("options").property(_combos.at(c)->currentIndex()) : - array.engine()->undefinedValue() - ); - _form.setProperty(i, item); - } else if (item.property("type").toString() == "checkbox") { - h++; - value = _checks.at(h)->checkState() == Qt::Checked; - item.setProperty("value", value); - _form.setProperty(i, item); - } else { - e += 1; - bool ok = true; - if (value.isNumber()) { - value = _edits.at(e)->text().toDouble(&ok); - } else if (value.isString()) { - value = _edits.at(e)->text(); - } else if (value.isBool()) { - if (_edits.at(e)->text() == "true") { - value = true; - } else if (_edits.at(e)->text() == "false") { - value = false; - } else { - ok = false; - } - } - if (ok) { - item.setProperty("value", value); - _form.setProperty(i, item); - } - } - } - - array = _form; - return (_formResult == QDialog::Accepted); -} - -QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue array) { - QScriptValue retVal; - - if (_formResult == QDialog::Accepted) { - int e = -1; - int d = -1; - int c = -1; - int h = -1; - for (int i = 0; i < _form.property("length").toInt32(); ++i) { - QScriptValue item = _form.property(i); - QScriptValue value = item.property("value"); - - if (item.property("button").toString() != "") { - // Nothing to do - } else if (item.property("type").toString() == "inlineButton") { - // Nothing to do - } else if (item.property("type").toString() == "header") { - // Nothing to do - } else if (item.property("directory").toString() != "") { - d += 1; - value = _directories.at(d)->property("path").toString(); - item.setProperty("directory", value); - _form.setProperty(i, item); - } else if (item.property("options").isArray()) { - c += 1; - item.setProperty("value", - _combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ? - item.property("options").property(_combos.at(c)->currentIndex()) : - array.engine()->undefinedValue() - ); - _form.setProperty(i, item); - } else if (item.property("type").toString() == "checkbox") { - h++; - value = _checks.at(h)->checkState() == Qt::Checked; - item.setProperty("value", value); - _form.setProperty(i, item); - } else { - e += 1; - bool ok = true; - if (value.isNumber()) { - value = _edits.at(e)->text().toDouble(&ok); - } else if (value.isString()) { - value = _edits.at(e)->text(); - } else if (value.isBool()) { - if (_edits.at(e)->text() == "true") { - value = true; - } else if (_edits.at(e)->text() == "false") { - value = false; - } else { - ok = false; - } - } - if (ok) { - item.setProperty("value", value); - _form.setProperty(i, item); - } - } - } - } - - delete _editDialog; - _editDialog = NULL; - _form = QScriptValue(); - _edits.clear(); - _directories.clear(); - _combos.clear(); - _checks.clear(); - - array = _form; - return (_formResult == QDialog::Accepted); -} - - -/// Display a form layout with an edit box -/// \param const QString& title title to display -/// \param const QScriptValue form to display as an array of objects: -/// - label, value -/// - label, directory, title, display regexp, validate regexp, error message -/// - button ("Cancel") -/// \return QScriptValue `true` if 'OK' was clicked, `false` otherwise -QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptValue form) { - if (form.isArray() && form.property("length").toInt32() <= 0) { - return false; - } - QDialog* editDialog = createForm(title, form); - - int result = editDialog->exec(); - - if (result == QDialog::Accepted) { - int e = -1; - int d = -1; - int c = -1; - int h = -1; - for (int i = 0; i < form.property("length").toInt32(); ++i) { - QScriptValue item = form.property(i); - QScriptValue value = item.property("value"); - - if (item.property("button").toString() != "") { - // Nothing to do - } else if (item.property("type").toString() == "inlineButton") { - // Nothing to do - } else if (item.property("type").toString() == "header") { - // Nothing to do - } else if (item.property("directory").toString() != "") { - d += 1; - value = _directories.at(d)->property("path").toString(); - item.setProperty("directory", value); - form.setProperty(i, item); - } else if (item.property("options").isArray()) { - c += 1; - item.setProperty("value", - _combos.at(c)->currentIndex() < item.property("options").property("length").toInt32() ? - item.property("options").property(_combos.at(c)->currentIndex()) : - form.engine()->undefinedValue() - ); - form.setProperty(i, item); - } else if (item.property("type").toString() == "checkbox") { - h++; - value = _checks.at(h)->checkState() == Qt::Checked; - item.setProperty("value", value); - form.setProperty(i, item); - } else { - e += 1; - bool ok = true; - if (value.isNumber()) { - value = _edits.at(e)->text().toDouble(&ok); - } else if (value.isString()) { - value = _edits.at(e)->text(); - } else if (value.isBool()) { - if (_edits.at(e)->text() == "true") { - value = true; - } else if (_edits.at(e)->text() == "false") { - value = false; - } else { - ok = false; - } - } - if (ok) { - item.setProperty("value", value); - form.setProperty(i, item); - } - } - } - } - - delete editDialog; - _combos.clear(); - _checks.clear(); - _edits.clear(); - _directories.clear(); - return (result == QDialog::Accepted); -} - - - -QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue form) { - QDialog* editDialog = new QDialog(qApp->getWindow()); - editDialog->setWindowTitle(title); - - bool cancelButton = false; - - QVBoxLayout* layout = new QVBoxLayout(); - editDialog->setLayout(layout); - - QScrollArea* area = new QScrollArea(); - layout->addWidget(area); - area->setWidgetResizable(true); - QWidget* container = new QWidget(); - QFormLayout* formLayout = new QFormLayout(); - container->setLayout(formLayout); - container->sizePolicy().setHorizontalStretch(1); - formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows); - formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); - formLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop); - formLayout->setLabelAlignment(Qt::AlignLeft); - - area->setWidget(container); - - for (int i = 0; i < form.property("length").toInt32(); ++i) { - QScriptValue item = form.property(i); - - if (item.property("button").toString() != "") { - cancelButton = cancelButton || item.property("button").toString().toLower() == "cancel"; - - } else if (item.property("directory").toString() != "") { - QString path = item.property("directory").toString(); - QString title = item.property("title").toString(); - if (title == "") { - title = "Choose Directory"; - } - QString displayAsString = item.property("displayAs").toString(); - QRegExp displayAs = QRegExp(displayAsString != "" ? jsRegExp2QtRegExp(displayAsString) : "^(.*)$"); - QString validateAsString = item.property("validateAs").toString(); - QRegExp validateAs = QRegExp(validateAsString != "" ? jsRegExp2QtRegExp(validateAsString) : ".*"); - QString errorMessage = item.property("errorMessage").toString(); - if (errorMessage == "") { - errorMessage = "Invalid directory"; - } - - QPushButton* directory = new QPushButton(displayAs.cap(1)); - directory->setProperty("title", title); - directory->setProperty("path", path); - directory->setProperty("displayAs", displayAs); - directory->setProperty("validateAs", validateAs); - directory->setProperty("errorMessage", errorMessage); - displayAs.indexIn(path); - directory->setText(displayAs.cap(1) != "" ? displayAs.cap(1) : "."); - - directory->setMinimumWidth(200); - _directories.push_back(directory); - - formLayout->addRow(new QLabel(item.property("label").toString()), directory); - connect(directory, SIGNAL(clicked(bool)), SLOT(chooseDirectory())); - - } else if (item.property("type").toString() == "inlineButton") { - QString buttonLabel = item.property("buttonLabel").toString(); - - QPushButton* inlineButton = new QPushButton(buttonLabel); - inlineButton->setMinimumWidth(200); - inlineButton->setProperty("name", item.property("name").toString()); - formLayout->addRow(new QLabel(item.property("label").toString()), inlineButton); - connect(inlineButton, SIGNAL(clicked(bool)), SLOT(inlineButtonClicked())); - - } else if (item.property("type").toString() == "header") { - formLayout->addRow(new QLabel(item.property("label").toString())); - } else if (item.property("options").isArray()) { - QComboBox* combo = new QComboBox(); - combo->setMinimumWidth(200); - qint32 options_count = item.property("options").property("length").toInt32(); - for (qint32 i = 0; i < options_count; i++) { - combo->addItem(item.property("options").property(i).toString()); - } - _combos.push_back(combo); - formLayout->addRow(new QLabel(item.property("label").toString()), combo); - } else if (item.property("type").toString() == "checkbox") { - QCheckBox* check = new QCheckBox(); - check->setTristate(false); - check->setCheckState(item.property("value").toString() == "true" ? Qt::Checked : Qt::Unchecked); - _checks.push_back(check); - formLayout->addRow(new QLabel(item.property("label").toString()), check); - } else { - QLineEdit* edit = new QLineEdit(item.property("value").toString()); - edit->setMinimumWidth(200); - int editIndex = _edits.size(); - _edits.push_back(edit); - item.setProperty("editIndex", editIndex); - formLayout->addRow(new QLabel(item.property("label").toString()), edit); - } - } - - QDialogButtonBox* buttons = new QDialogButtonBox( - QDialogButtonBox::Ok - | (cancelButton ? QDialogButtonBox::Cancel : QDialogButtonBox::NoButton) - ); - connect(buttons, SIGNAL(accepted()), editDialog, SLOT(accept())); - connect(buttons, SIGNAL(rejected()), editDialog, SLOT(reject())); - layout->addWidget(buttons); - - return editDialog; -} - /// Display a prompt with a text box /// \param const QString& message message to display /// \param const QString& defaultText default text in the text box /// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise. QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const QString& defaultText) { - QInputDialog promptDialog(qApp->getWindow()); - promptDialog.setWindowTitle(""); - promptDialog.setLabelText(message); - promptDialog.setTextValue(defaultText); - promptDialog.setFixedSize(600, 200); - - if (promptDialog.exec() == QDialog::Accepted) { - return QScriptValue(promptDialog.textValue()); + bool ok = false; + QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText, &ok); + if (!ok) { + return QScriptValue::NullValue; } - return QScriptValue::NullValue; + return QScriptValue(result); } /// Display a file dialog. If `directory` is an invalid file or directory the browser will start at the current diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 4b287a24e9..225628b698 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -43,59 +43,32 @@ public slots: void raiseMainWindow(); QScriptValue alert(const QString& message = ""); QScriptValue confirm(const QString& message = ""); - QScriptValue form(const QString& title, QScriptValue array); QScriptValue prompt(const QString& message = "", const QString& defaultText = ""); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue s3Browse(const QString& nameFilter = ""); - void nonBlockingForm(const QString& title, QScriptValue array); - void reloadNonBlockingForm(QScriptValue array); - QScriptValue getNonBlockingFormResult(QScriptValue array); - QScriptValue peekNonBlockingFormResult(QScriptValue array); - signals: void domainChanged(const QString& domainHostname); void inlineButtonClicked(const QString& name); - void nonBlockingFormClosed(); void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reason); private slots: QScriptValue showAlert(const QString& message); QScriptValue showConfirm(const QString& message); - QScriptValue showForm(const QString& title, QScriptValue form); QScriptValue showPrompt(const QString& message, const QString& defaultText); QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter, QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen); QScriptValue showS3Browse(const QString& nameFilter); - void showNonBlockingForm(const QString& title, QScriptValue array); - void doReloadNonBlockingForm(QScriptValue array); - bool nonBlockingFormActive(); - QScriptValue doGetNonBlockingFormResult(QScriptValue array); - QScriptValue doPeekNonBlockingFormResult(QScriptValue array); - void chooseDirectory(); void inlineButtonClicked(); - void nonBlockingFormAccepted() { _nonBlockingFormActive = false; _formResult = QDialog::Accepted; emit nonBlockingFormClosed(); } - void nonBlockingFormRejected() { _nonBlockingFormActive = false; _formResult = QDialog::Rejected; emit nonBlockingFormClosed(); } - WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); private: - QString jsRegExp2QtRegExp(QString string); - QDialog* createForm(const QString& title, QScriptValue form); - - QDialog* _editDialog; - QScriptValue _form; - bool _nonBlockingFormActive; - int _formResult; - QVector _combos; - QVector _checks; - QVector _edits; - QVector _directories; + QString jsRegExp2QtRegExp(const QString& string); }; #endif // hifi_WindowScriptingInterface_h From 0a99086617c7b8152680a6a3ca9d05caf13f572d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 18:14:33 -0800 Subject: [PATCH 52/79] Moving to QML based dialogs --- .../scripting/WindowScriptingInterface.cpp | 167 +++--------------- .../src/scripting/WindowScriptingInterface.h | 26 +-- libraries/ui/src/OffscreenUi.cpp | 4 + libraries/ui/src/OffscreenUi.h | 1 + 4 files changed, 38 insertions(+), 160 deletions(-) diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 5d5837ebd8..3e8d0d0360 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -9,20 +9,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include -#include +#include #include #include -#include #include "Application.h" #include "DomainHandler.h" #include "MainWindow.h" #include "Menu.h" #include "OffscreenUi.h" -#include "ui/ModelsBrowser.h" #include "WebWindowClass.h" #include "WindowScriptingInterface.h" @@ -81,131 +76,31 @@ QScriptValue WindowScriptingInterface::getCursorPositionY() { return QCursor::pos().y(); } -QScriptValue WindowScriptingInterface::alert(const QString& message) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showAlert", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, retVal), Q_ARG(const QString&, message)); - return retVal; -} - -QScriptValue WindowScriptingInterface::confirm(const QString& message) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showConfirm", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), Q_ARG(const QString&, message)); - return retVal; -} - -QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showPrompt", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(const QString&, message), Q_ARG(const QString&, defaultText)); - return retVal; -} - -QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showBrowse", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(const QString&, title), Q_ARG(const QString&, directory), Q_ARG(const QString&, nameFilter)); - return retVal; -} - -QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showBrowse", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(const QString&, title), Q_ARG(const QString&, directory), Q_ARG(const QString&, nameFilter), - Q_ARG(QFileDialog::AcceptMode, QFileDialog::AcceptSave)); - return retVal; -} - -QScriptValue WindowScriptingInterface::s3Browse(const QString& nameFilter) { - QScriptValue retVal; - QMetaObject::invokeMethod(this, "showS3Browse", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, retVal), - Q_ARG(const QString&, nameFilter)); - return retVal; -} - - /// Display an alert box /// \param const QString& message message to display /// \return QScriptValue::UndefinedValue -QScriptValue WindowScriptingInterface::showAlert(const QString& message) { +void WindowScriptingInterface::alert(const QString& message) { OffscreenUi::warning("", message); - return QScriptValue::UndefinedValue; } /// Display a confirmation box with the options 'Yes' and 'No' /// \param const QString& message message to display /// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise -QScriptValue WindowScriptingInterface::showConfirm(const QString& message) { - bool confirm = false; - if (QMessageBox::Yes == OffscreenUi::question("", message)) { - confirm = true; - } - return QScriptValue(confirm); -} - -void WindowScriptingInterface::chooseDirectory() { - QPushButton* button = reinterpret_cast(sender()); - - QString title = button->property("title").toString(); - QString path = button->property("path").toString(); - QRegExp displayAs = button->property("displayAs").toRegExp(); - QRegExp validateAs = button->property("validateAs").toRegExp(); - QString errorMessage = button->property("errorMessage").toString(); - - QString directory = QFileDialog::getExistingDirectory(button, title, path); - if (directory.isEmpty()) { - return; - } - - if (!validateAs.exactMatch(directory)) { - OffscreenUi::warning(NULL, "Invalid Directory", errorMessage); - return; - } - - button->setProperty("path", directory); - - displayAs.indexIn(directory); - QString buttonText = displayAs.cap(1) != "" ? displayAs.cap(1) : "."; - button->setText(buttonText); -} - -void WindowScriptingInterface::inlineButtonClicked() { - QPushButton* button = reinterpret_cast(sender()); - QString name = button->property("name").toString(); - emit inlineButtonClicked(name); -} - -QString WindowScriptingInterface::jsRegExp2QtRegExp(const QString& string) { - // Converts string representation of RegExp from JavaScript format to Qt format. - return string.mid(1, string.length() - 2) // No enclosing slashes. - .replace("\\/", "/"); // No escaping of forward slash. +QScriptValue WindowScriptingInterface::confirm(const QString& message) { + return QScriptValue((QMessageBox::Yes == OffscreenUi::question("", message))); } /// Display a prompt with a text box /// \param const QString& message message to display /// \param const QString& defaultText default text in the text box /// \return QScriptValue string text value in text box if the dialog was accepted, `null` otherwise. -QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const QString& defaultText) { +QScriptValue WindowScriptingInterface::prompt(const QString& message, const QString& defaultText) { bool ok = false; QString result = OffscreenUi::getText(nullptr, "", message, QLineEdit::Normal, defaultText, &ok); - if (!ok) { - return QScriptValue::NullValue; - } - return QScriptValue(result); + return ok ? QScriptValue(result) : QScriptValue::NullValue; } -/// Display a file dialog. If `directory` is an invalid file or directory the browser will start at the current -/// working directory. -/// \param const QString& title title of the window -/// \param const QString& directory directory to start the file browser at -/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` -/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QString& directory, const QString& nameFilter, - QFileDialog::AcceptMode acceptMode) { +QString fixupPathForMac(const QString& directory) { // On OS X `directory` does not work as expected unless a file is included in the path, so we append a bogus // filename if the directory is valid. QString path = ""; @@ -214,35 +109,31 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS fileInfo.setFile(directory, "__HIFI_INVALID_FILE__"); path = fileInfo.filePath(); } - - QFileDialog fileDialog(qApp->getWindow(), title, path, nameFilter); - fileDialog.setAcceptMode(acceptMode); - QUrl fileUrl(directory); - if (acceptMode == QFileDialog::AcceptSave) { - // TODO -- Setting this breaks the dialog on Linux. Does it help something on other platforms? - // fileDialog.setFileMode(QFileDialog::Directory); - fileDialog.selectFile(fileUrl.fileName()); - } - if (fileDialog.exec()) { - return QScriptValue(fileDialog.selectedFiles().first()); - } - return QScriptValue::NullValue; + return path; } -/// Display a browse window for S3 models -/// \param const QString& nameFilter filter to filter filenames +/// Display an open file dialog. If `directory` is an invalid file or directory the browser will start at the current +/// working directory. +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the file browser at +/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` /// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` -QScriptValue WindowScriptingInterface::showS3Browse(const QString& nameFilter) { - ModelsBrowser browser(FSTReader::ENTITY_MODEL); - if (nameFilter != "") { - browser.setNameFilter(nameFilter); - } - QEventLoop loop; - connect(&browser, &ModelsBrowser::selected, &loop, &QEventLoop::quit); - QMetaObject::invokeMethod(&browser, "browse", Qt::QueuedConnection); - loop.exec(); - - return browser.getSelectedFile(); +QScriptValue WindowScriptingInterface::browse(const QString& title, const QString& directory, const QString& nameFilter) { + QString path = fixupPathForMac(directory); + QString result = OffscreenUi::getOpenFileName(nullptr, title, path, nameFilter); + return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); +} + +/// Display a save file dialog. If `directory` is an invalid file or directory the browser will start at the current +/// working directory. +/// \param const QString& title title of the window +/// \param const QString& directory directory to start the file browser at +/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog` +/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` +QScriptValue WindowScriptingInterface::save(const QString& title, const QString& directory, const QString& nameFilter) { + QString path = fixupPathForMac(directory); + QString result = OffscreenUi::getSaveFileName(nullptr, title, path, nameFilter); + return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result); } int WindowScriptingInterface::getInnerWidth() { diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 225628b698..d00e63b49e 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -12,12 +12,9 @@ #ifndef hifi_WindowScriptingInterface_h #define hifi_WindowScriptingInterface_h -#include -#include -#include -#include -#include -#include +#include +#include +#include class WebWindowClass; @@ -41,34 +38,19 @@ public slots: QScriptValue hasFocus(); void setFocus(); void raiseMainWindow(); - QScriptValue alert(const QString& message = ""); + void alert(const QString& message = ""); QScriptValue confirm(const QString& message = ""); QScriptValue prompt(const QString& message = "", const QString& defaultText = ""); QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = ""); - QScriptValue s3Browse(const QString& nameFilter = ""); signals: void domainChanged(const QString& domainHostname); - void inlineButtonClicked(const QString& name); void svoImportRequested(const QString& url); void domainConnectionRefused(const QString& reason); private slots: - QScriptValue showAlert(const QString& message); - QScriptValue showConfirm(const QString& message); - QScriptValue showPrompt(const QString& message, const QString& defaultText); - QScriptValue showBrowse(const QString& title, const QString& directory, const QString& nameFilter, - QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen); - QScriptValue showS3Browse(const QString& nameFilter); - - void chooseDirectory(); - void inlineButtonClicked(); - WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); - -private: - QString jsRegExp2QtRegExp(const QString& string); }; #endif // hifi_WindowScriptingInterface_h diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 1d471d5419..3ac12d014f 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -520,5 +520,9 @@ QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, cons return DependencyManager::get()->fileOpenDialog(caption, dir, filter, selectedFilter, options); } +QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { + return QFileDialog::getSaveFileName((QWidget*)ignored, caption, dir, filter, selectedFilter, options); +} + #include "OffscreenUi.moc" diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index ec5ba433cc..73a9ca7c47 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -91,6 +91,7 @@ public: Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // Compatibility with QFileDialog::getOpenFileName static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); // input dialog compatibility From 320c4d27b52b1a14e9892c8a26387d9447229295 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 3 Feb 2016 10:33:11 -0800 Subject: [PATCH 53/79] Force Qt (rather than OS) rendering in a number of places --- interface/resources/qml/controls/CheckBox.qml | 5 +++-- interface/resources/qml/controls/TextField.qml | 7 +++++++ interface/resources/qml/dialogs/FileDialog.qml | 3 +++ interface/resources/qml/dialogs/QueryDialog.qml | 2 +- interface/resources/qml/dialogs/RunningScripts.qml | 4 ++-- .../resources/qml/dialogs/preferences/AvatarPreference.qml | 2 ++ .../qml/dialogs/preferences/BrowsablePreference.qml | 2 ++ .../qml/dialogs/preferences/CheckBoxPreference.qml | 1 + .../qml/dialogs/preferences/EditablePreference.qml | 2 ++ .../resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml | 2 ++ .../resources/qml/hifi/dialogs/ModelBrowserDialog.qml | 2 ++ .../resources/qml/hifi/dialogs/attachments/Attachment.qml | 3 ++- 12 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 interface/resources/qml/controls/TextField.qml diff --git a/interface/resources/qml/controls/CheckBox.qml b/interface/resources/qml/controls/CheckBox.qml index fe836a0e89..91586299a7 100644 --- a/interface/resources/qml/controls/CheckBox.qml +++ b/interface/resources/qml/controls/CheckBox.qml @@ -10,7 +10,8 @@ Original.CheckBox { } label: Text { text: control.text + renderType: Text.QtRendering } } - -} \ No newline at end of file + +} diff --git a/interface/resources/qml/controls/TextField.qml b/interface/resources/qml/controls/TextField.qml new file mode 100644 index 0000000000..df536a1de5 --- /dev/null +++ b/interface/resources/qml/controls/TextField.qml @@ -0,0 +1,7 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +TextField { + style: TextFieldStyle { renderType: Text.QtRendering } +} diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 96a0c95e88..3a26c3ca27 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -2,6 +2,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.4 import Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 +import QtQuick.Controls.Styles 1.4 import ".." import "../windows" @@ -82,6 +83,7 @@ ModalWindow { TextField { id: currentDirectory height: homeButton.height + style: TextFieldStyle { renderType: Text.QtRendering } anchors { left: navControls.right; right: parent.right; top: parent.top; margins: 8 } property var lastValidFolder: helper.urlToPath(model.folder) onLastValidFolderChanged: text = lastValidFolder; @@ -181,6 +183,7 @@ ModalWindow { TextField { id: currentSelection + style: TextFieldStyle { renderType: Text.QtRendering } anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top } readOnly: true activeFocusOnTab: false diff --git a/interface/resources/qml/dialogs/QueryDialog.qml b/interface/resources/qml/dialogs/QueryDialog.qml index fecd69a4c6..948bbb1295 100644 --- a/interface/resources/qml/dialogs/QueryDialog.qml +++ b/interface/resources/qml/dialogs/QueryDialog.qml @@ -62,7 +62,7 @@ ModalWindow { Item { anchors { top: mainTextContainer.bottom; bottom: buttons.top; left: parent.left; right: parent.right; margins: d.spacing } // FIXME make a text field type that can be bound to a history for autocompletion - TextField { + VrControls.TextField { id: textResult focus: items ? false : true visible: items ? false : true diff --git a/interface/resources/qml/dialogs/RunningScripts.qml b/interface/resources/qml/dialogs/RunningScripts.qml index 8b148c6bef..c9c9062bbd 100644 --- a/interface/resources/qml/dialogs/RunningScripts.qml +++ b/interface/resources/qml/dialogs/RunningScripts.qml @@ -219,7 +219,7 @@ Window { } } - TextField { + HifiControls.TextField { id: filterEdit anchors.left: parent.left anchors.right: parent.right @@ -252,7 +252,7 @@ Window { TableViewColumn { title: "Name"; role: "display"; } } - TextField { + HifiControls.TextField { id: selectedScript readOnly: true anchors.left: parent.left diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index 1375fc392a..a92392799d 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 Preference { id: root @@ -50,6 +51,7 @@ Preference { id: dataTextField placeholderText: root.placeholderText text: preference.value + style: TextFieldStyle { renderType: Text.QtRendering } anchors { top: labelText.bottom left: parent.left diff --git a/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml b/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml index 7d310be468..637cc6c02c 100644 --- a/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml +++ b/interface/resources/qml/dialogs/preferences/BrowsablePreference.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import "../../dialogs" @@ -30,6 +31,7 @@ Preference { id: dataTextField placeholderText: root.placeholderText text: preference.value + style: TextFieldStyle { renderType: Text.QtRendering } anchors { top: labelText.bottom left: parent.left diff --git a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml index ab37f30427..f28f3ab90b 100644 --- a/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml +++ b/interface/resources/qml/dialogs/preferences/CheckBoxPreference.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import "../../controls" Preference { id: root diff --git a/interface/resources/qml/dialogs/preferences/EditablePreference.qml b/interface/resources/qml/dialogs/preferences/EditablePreference.qml index 53dc7ed5d0..ddd5600bca 100644 --- a/interface/resources/qml/dialogs/preferences/EditablePreference.qml +++ b/interface/resources/qml/dialogs/preferences/EditablePreference.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 Preference { id: root @@ -24,6 +25,7 @@ Preference { TextField { id: dataTextField placeholderText: preference.placeholderText + style: TextFieldStyle { renderType: Text.QtRendering } anchors { top: labelText.bottom left: parent.left diff --git a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml index 252e4c629e..5de0864df5 100644 --- a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml @@ -1,6 +1,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.XmlListModel 2.0 +import QtQuick.Controls.Styles 1.4 import "../../windows" import "../../js/Utils.js" as Utils @@ -27,6 +28,7 @@ ModalWindow { TextField { id: filterEdit anchors { left: parent.left; right: parent.right; top: parent.top } + style: TextFieldStyle { renderType: Text.QtRendering } placeholderText: "filter" onTextChanged: tableView.model.filter = text } diff --git a/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml index 252e4c629e..b103279d4c 100644 --- a/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml +++ b/interface/resources/qml/hifi/dialogs/ModelBrowserDialog.qml @@ -1,6 +1,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.XmlListModel 2.0 +import QtQuick.Controls.Styles 1.4 import "../../windows" import "../../js/Utils.js" as Utils @@ -26,6 +27,7 @@ ModalWindow { TextField { id: filterEdit + style: TextFieldStyle { renderType: Text.QtRendering } anchors { left: parent.left; right: parent.right; top: parent.top } placeholderText: "filter" onTextChanged: tableView.model.filter = text diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml index 31a1895e58..8dc5d8ba4b 100644 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import "../../../windows" import "../../../controls" as VrControls @@ -31,7 +32,7 @@ Item { height: modelChooserButton.height anchors { left: parent.left; right: parent.right; } Text { id: urlLabel; text: "Model URL:"; width: 80; anchors.verticalCenter: modelUrl.verticalCenter } - TextField { + VrControls.TextField { id: modelUrl; height: jointChooser.height; anchors { left: urlLabel.right; leftMargin: 8; rightMargin: 8; right: modelChooserButton.left } From 8f85abfec8c214eea0aeaeffe5b8db2756ee3524 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 3 Feb 2016 10:59:50 -0800 Subject: [PATCH 54/79] Support 'naked' selection globs in file dialog --- .../qml/dialogs/fileDialog/FileTypeSelection.qml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml index a3ab652e32..57ad2028ad 100644 --- a/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml +++ b/interface/resources/qml/dialogs/fileDialog/FileTypeSelection.qml @@ -17,9 +17,13 @@ VrControls.ComboBox { onCurrentTextChanged: { var globRegex = /\((.*)\)$/ var globs = globRegex.exec(currentText); - if (!globs[1]) { - console.warn("Unable to parse filter " + currentText); - return; + if (!globs || !globs[1]) { + globRegex = /^(\*.*)$/ + globs = globRegex.exec(currentText); + if (!globs || !globs[1]) { + console.warn("Unable to parse filter " + currentText); + return; + } } currentFilter = globs[1].split(" "); } From 60d97c45affa260571218f9e732f6b4df9b7151a Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 3 Feb 2016 14:49:50 -0800 Subject: [PATCH 55/79] Support save file dialog --- interface/resources/qml/desktop/Desktop.qml | 3 +- .../resources/qml/dialogs/FileDialog.qml | 84 ++++++++++++++++--- .../resources/qml/dialogs/MessageDialog.qml | 4 + libraries/ui/src/FileDialogHelper.cpp | 52 +++++++++++- libraries/ui/src/FileDialogHelper.h | 6 ++ libraries/ui/src/OffscreenUi.cpp | 63 ++++++++++---- libraries/ui/src/OffscreenUi.h | 8 +- 7 files changed, 186 insertions(+), 34 deletions(-) diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 7fb5cd3127..0286c45ac3 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -262,11 +262,10 @@ FocusScope { } Component { id: fileDialogBuilder; FileDialog { } } - function fileOpenDialog(properties) { + function fileDialog(properties) { return fileDialogBuilder.createObject(desktop, properties); } - MenuMouseHandler { id: menuPopperUpper } function popupMenu(point) { menuPopperUpper.popup(desktop, rootMenu.items, point); diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 3a26c3ca27..142ed198c0 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -3,6 +3,7 @@ import QtQuick.Controls 1.4 import Qt.labs.folderlistmodel 2.1 import Qt.labs.settings 1.0 import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.2 as OriginalDialogs import ".." import "../windows" @@ -39,7 +40,6 @@ ModalWindow { property bool showHidden: false; // FIXME implement property bool multiSelect: false; - // FIXME implement property bool saveDialog: false; property var helper: fileDialogHelper property alias model: fileTableView.model @@ -124,6 +124,8 @@ ModalWindow { currentSelectionIsFolder = fileTableView.model.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { currentSelection.text = helper.urlToPath(currentSelectionUrl); + } else { + currentSelection.text = "" } } @@ -175,8 +177,7 @@ ModalWindow { if (isFolder) { fileTableView.model.folder = file } else { - root.selectedFile(file); - root.destroy(); + okAction.trigger(); } } } @@ -185,8 +186,10 @@ ModalWindow { id: currentSelection style: TextFieldStyle { renderType: Text.QtRendering } anchors { right: root.selectDirectory ? parent.right : selectionType.left; rightMargin: 8; left: parent.left; leftMargin: 8; top: selectionType.top } - readOnly: true - activeFocusOnTab: false + readOnly: !root.saveDialog + activeFocusOnTab: !readOnly + onActiveFocusChanged: if (activeFocus) { selectAll(); } + onAccepted: okAction.trigger(); } FileTypeSelection { @@ -206,25 +209,82 @@ ModalWindow { spacing: 8 Button { id: openButton - text: root.selectDirectory ? "Choose" : "Open" - enabled: currentSelection.text ? true : false - onClicked: { selectedFile(d.currentSelectionUrl); root.visible = false; } - Keys.onReturnPressed: { selectedFile(d.currentSelectionUrl); root.visible = false; } - + action: okAction + Keys.onReturnPressed: okAction.trigger() KeyNavigation.up: selectionType KeyNavigation.left: selectionType KeyNavigation.right: cancelButton } Button { id: cancelButton - text: "Cancel" + action: cancelAction KeyNavigation.up: selectionType KeyNavigation.left: openButton KeyNavigation.right: fileTableView.contentItem Keys.onReturnPressed: { canceled(); root.enabled = false } - onClicked: { canceled(); root.visible = false; } } } + + Action { + id: okAction + text: root.saveDialog ? "Save" : (root.selectDirectory ? "Choose" : "Open") + enabled: currentSelection.text ? true : false + onTriggered: { + if (root.saveDialog) { + // Handle the ambiguity between different cases + // * typed name (with or without extension) + // * full path vs relative vs filename only + var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); + + if (!selection) { + desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) + return; + } + + if (helper.urlIsDir(selection)) { + root.dir = selection; + currentSelection.text = ""; + return; + } + + // Check if the file is a valid target + if (!helper.urlIsWritable(selection)) { + desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Warning, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + text: "Unable to write to location " + selection + }) + return; + } + + if (helper.urlExists(selection)) { + var messageBox = desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Question, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + text: "Do you wish to overwrite " + selection + "?", + }); + var result = messageBox.exec(); + if (OriginalDialogs.StandardButton.Yes !== result) { + return; + } + } + + selectedFile(d.currentSelectionUrl); + root.destroy() + } else { + selectedFile(d.currentSelectionUrl); + root.destroy() + } + + } + + } + + Action { + id: cancelAction + text: "Cancel" + onTriggered: { canceled(); root.visible = false; } + } } Keys.onPressed: { diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index ca00da1aa3..3b7cc2c9a8 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -25,6 +25,10 @@ ModalWindow { destroy(); } + function exec() { + return OffscreenUi.waitForMessageBoxResult(root); + } + property alias detailedText: detailedText.text property alias text: mainTextContainer.text property alias informativeText: informativeTextContainer.text diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index f8a0929702..f1cbb22f56 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -12,6 +12,8 @@ #include #include +#include +#include QUrl FileDialogHelper::home() { @@ -26,7 +28,11 @@ QString FileDialogHelper::urlToPath(const QUrl& url) { return url.toLocalFile(); } -bool FileDialogHelper::validPath(const QString& path) { +bool FileDialogHelper::fileExists(const QString& path) { + return QFile(path).exists(); +} + +bool FileDialogHelper::validPath(const QString& path) { return QFile(path).exists(); } @@ -38,3 +44,47 @@ QUrl FileDialogHelper::pathToUrl(const QString& path) { return QUrl::fromLocalFile(path); } + +QUrl FileDialogHelper::saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters) { + qDebug() << "Calling save helper with " << saveText << " " << currentFolder << " " << selectionFilters; + + QFileInfo fileInfo(saveText); + + // Check if it's a relative path and if it is resolve to the absolute path + { + if (fileInfo.isRelative()) { + fileInfo = QFileInfo(currentFolder.toLocalFile() + "/" + fileInfo.filePath()); + } + } + + // Check if we need to append an extension, but only if the current resolved path isn't a directory + if (!fileInfo.isDir()) { + QString fileName = fileInfo.fileName(); + if (!fileName.contains(".") && selectionFilters.size() == 1) { + const QRegularExpression extensionRe{ ".*(\\.[a-zA-Z0-9]+)$" }; + QString filter = selectionFilters[0]; + auto match = extensionRe.match(filter); + if (match.hasMatch()) { + fileInfo = QFileInfo(fileInfo.filePath() + match.captured(1)); + } + } + } + + return QUrl::fromLocalFile(fileInfo.absoluteFilePath()); +} + +bool FileDialogHelper::urlIsDir(const QUrl& url) { + return QFileInfo(url.toLocalFile()).isDir(); +} + +bool FileDialogHelper::urlIsFile(const QUrl& url) { + return QFileInfo(url.toLocalFile()).isFile(); +} + +bool FileDialogHelper::urlExists(const QUrl& url) { + return QFileInfo(url.toLocalFile()).exists(); +} + +bool FileDialogHelper::urlIsWritable(const QUrl& url) { + return QFileInfo(url.toLocalFile()).isWritable(); +} diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index edb702eeda..0142473533 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -48,9 +48,15 @@ public: Q_INVOKABLE QUrl home(); Q_INVOKABLE QStringList standardPath(StandardLocation location); Q_INVOKABLE QString urlToPath(const QUrl& url); + Q_INVOKABLE bool urlIsDir(const QUrl& url); + Q_INVOKABLE bool urlIsFile(const QUrl& url); + Q_INVOKABLE bool urlExists(const QUrl& url); + Q_INVOKABLE bool urlIsWritable(const QUrl& url); + Q_INVOKABLE bool fileExists(const QString& path); Q_INVOKABLE bool validPath(const QString& path); Q_INVOKABLE bool validFolder(const QString& path); Q_INVOKABLE QUrl pathToUrl(const QString& path); + Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters); }; diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 3ac12d014f..fa40fedb9b 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -105,6 +105,7 @@ void OffscreenUi::create(QOpenGLContext* context) { OffscreenQmlSurface::create(context); auto rootContext = getRootContext(); + rootContext->setContextProperty("OffscreenUi", this); rootContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags()); rootContext->setContextProperty("urlHandler", new UrlHandler()); rootContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); @@ -219,7 +220,7 @@ QQuickItem* OffscreenUi::createMessageBox(QMessageBox::Icon icon, const QString& return qvariant_cast(result); } -QMessageBox::StandardButton OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) { +int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) { if (!messageBox) { return QMessageBox::NoButton; } @@ -241,7 +242,7 @@ QMessageBox::StandardButton OffscreenUi::messageBox(QMessageBox::Icon icon, cons return result; } - return waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton)); + return static_cast(waitForMessageBoxResult(createMessageBox(icon, title, text, buttons, defaultButton))); } QMessageBox::StandardButton OffscreenUi::critical(const QString& title, const QString& text, @@ -478,6 +479,26 @@ private slots: } }; + +QString OffscreenUi::fileDialog(const QVariantMap& properties) { + QVariant buildDialogResult; + bool invokeResult = QMetaObject::invokeMethod(_desktop, "fileDialog", + Q_RETURN_ARG(QVariant, buildDialogResult), + Q_ARG(QVariant, QVariant::fromValue(properties))); + + if (!invokeResult) { + qWarning() << "Failed to create file open dialog"; + return QString(); + } + + QVariant result = FileDialogListener(qvariant_cast(buildDialogResult)).waitForResult(); + if (!result.isValid()) { + return QString(); + } + qDebug() << result.toString(); + return result.toUrl().toLocalFile(); +} + QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { if (QThread::currentThread() != thread()) { QString result; @@ -497,23 +518,31 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, map.insert("dir", QUrl::fromLocalFile(dir)); map.insert("filter", filter); map.insert("options", static_cast(options)); + return fileDialog(map); +} - QVariant buildDialogResult; - bool invokeResult = QMetaObject::invokeMethod(_desktop, "fileOpenDialog", - Q_RETURN_ARG(QVariant, buildDialogResult), - Q_ARG(QVariant, QVariant::fromValue(map))); - - if (!invokeResult) { - qWarning() << "Failed to create file open dialog"; - return QString(); +QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { + if (QThread::currentThread() != thread()) { + QString result; + QMetaObject::invokeMethod(this, "fileSaveDialog", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QString, result), + Q_ARG(QString, caption), + Q_ARG(QString, dir), + Q_ARG(QString, filter), + Q_ARG(QString*, selectedFilter), + Q_ARG(QFileDialog::Options, options)); + return result; } - QVariant result = FileDialogListener(qvariant_cast(buildDialogResult)).waitForResult(); - if (!result.isValid()) { - return QString(); - } - qDebug() << result.toString(); - return result.toUrl().toLocalFile(); + // FIXME support returning the selected filter... somehow? + QVariantMap map; + map.insert("caption", caption); + map.insert("dir", QUrl::fromLocalFile(dir)); + map.insert("filter", filter); + map.insert("options", static_cast(options)); + map.insert("saveDialog", true); + + return fileDialog(map); } QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { @@ -521,7 +550,7 @@ QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, cons } QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) { - return QFileDialog::getSaveFileName((QWidget*)ignored, caption, dir, filter, selectedFilter, options); + return DependencyManager::get()->fileSaveDialog(caption, dir, filter, selectedFilter, options); } diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 73a9ca7c47..de479853f3 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -47,7 +47,7 @@ public: // Must be called from the main thread QQuickItem* createMessageBox(QMessageBox::Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton); // Must be called from the main thread - QMessageBox::StandardButton waitForMessageBoxResult(QQuickItem* messageBox); + Q_INVOKABLE int waitForMessageBoxResult(QQuickItem* messageBox); /// Same design as QMessageBox::critical(), will block, returns result static QMessageBox::StandardButton critical(void* ignored, const QString& title, const QString& text, @@ -87,10 +87,12 @@ public: QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); - // file dialog compatibility Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + // Compatibility with QFileDialog::getOpenFileName static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + // Compatibility with QFileDialog::getSaveFileName static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); @@ -105,6 +107,8 @@ public: static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone); private: + QString fileDialog(const QVariantMap& properties); + QQuickItem* _desktop { nullptr }; QQuickItem* _toolWindow { nullptr }; }; From 027a2166f5cc3d6445bfdcec8c3c03934241047b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Feb 2016 15:44:06 -0800 Subject: [PATCH 56/79] MyAvatar: Use head to turn while flying in HMD mode --- interface/src/avatar/MyAvatar.cpp | 41 +++++++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 3 +++ 2 files changed, 44 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b99430cae3..70c815b609 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1361,6 +1361,37 @@ void MyAvatar::updateOrientation(float deltaTime) { totalBodyYaw += _driveKeys[STEP_YAW]; } + // use head/HMD orientation to turn while flying + if (getCharacterController()->getState() == CharacterController::State::Hover) { + + // This is the direction the user desires to fly in. + glm::vec3 desiredFacing = getHead()->getCameraOrientation() * Vectors::UNIT_Z; + desiredFacing.y = 0.0f; + + // This is our reference frame, it is captured when the user begins to move. + glm::vec3 referenceFacing = transformVector(_sensorToWorldMatrix, _hoverReferenceCameraFacing); + referenceFacing.y = 0.0f; + referenceFacing = glm::normalize(referenceFacing); + glm::vec3 referenceRight(referenceFacing.z, 0.0f, -referenceFacing.x); + const float HOVER_FLY_ROTATION_PERIOD = 0.5f; + float tau = glm::clamp(deltaTime / HOVER_FLY_ROTATION_PERIOD, 0.0f, 1.0f); + + // new facing is a linear interpolation between the desired and reference vectors. + glm::vec3 newFacing = glm::normalize((1.0f - tau) * referenceFacing + tau * desiredFacing); + + // calcualte the signed delta yaw angle to apply so that we match our newFacing. + float sign = copysignf(1.0f, glm::dot(desiredFacing, referenceRight)); + float deltaAngle = sign * acosf(glm::clamp(glm::dot(referenceFacing, newFacing), -1.0f, 1.0f)); + + // speedFactor is 0 when we are at rest adn 1.0 when we are at max flying speed. + const float MAX_FLYING_SPEED = 30.0f; + float speedFactor = glm::min(glm::length(getVelocity()) / MAX_FLYING_SPEED, 1.0f); + + // apply our delta, but scale it by the speed factor, so we turn faster when we are flying faster. + totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI)); + } + + // update body orientation by movement inputs setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); @@ -1537,6 +1568,16 @@ void MyAvatar::updatePosition(float deltaTime) { // update _moving flag based on speed const float MOVING_SPEED_THRESHOLD = 0.01f; _moving = speed > MOVING_SPEED_THRESHOLD; + + + // capture the head rotation, in sensor space, when the user first indicates they would like to move/fly. + if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) { + _hoverReferenceCameraFacingIsCaptured = true; + // transform the camera facing vector into sensor space. + _hoverReferenceCameraFacing = transformVector(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z); + } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) { + _hoverReferenceCameraFacingIsCaptured = false; + } } void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ac77784077..c272c24315 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -413,6 +413,9 @@ private: AtRestDetector _hmdAtRestDetector; bool _lastIsMoving { false }; + + bool _hoverReferenceCameraFacingIsCaptured { false }; + glm::vec3 _hoverReferenceCameraFacing; // hmd sensor space }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); From d2068678f4ae1e99077f59a79453b45f720baf00 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Feb 2016 16:08:11 -0800 Subject: [PATCH 57/79] MyAvatar: bumped up size of head, w.r.t. head cauterization. This is to avoid some issues where face is visible when looking straight down. Specifically, with the Albert avatar. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 70c815b609..9382f9c233 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1311,7 +1311,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { _prevShouldDrawHead = shouldDrawHead; } -const float RENDER_HEAD_CUTOFF_DISTANCE = 0.5f; +const float RENDER_HEAD_CUTOFF_DISTANCE = 0.6f; bool MyAvatar::cameraInsideHead() const { const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); From 058bba7b55941e9f0f1b00789ddd5ab2269a13d3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Feb 2016 17:13:35 -0800 Subject: [PATCH 58/79] move avatar invokables from AvatarManager to AvatarHashMap --- interface/src/avatar/AvatarManager.cpp | 11 ----------- interface/src/avatar/AvatarManager.h | 5 +---- libraries/avatars/src/AvatarHashMap.cpp | 10 ++++++++++ libraries/avatars/src/AvatarHashMap.h | 6 ++++++ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 833ed26cc9..e12ff8f857 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -268,17 +268,6 @@ QVector AvatarManager::getLocalLights() const { return _localLights; } -QVector AvatarManager::getAvatarIdentifiers() { - QReadLocker locker(&_hashLock); - return _avatarHash.keys().toVector(); -} - -AvatarData* AvatarManager::getAvatar(QUuid avatarID) { - // Null/Default-constructed QUuids will return MyAvatar - return getAvatarBySessionID(avatarID).get(); -} - - void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { result.clear(); result.swap(_motionStatesToRemoveFromPhysics); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 72fcb3f862..2aff98a1d2 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -39,7 +39,7 @@ public: void init(); MyAvatar* getMyAvatar() { return _myAvatar.get(); } - AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID); + AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) override; void updateMyAvatar(float deltaTime); void updateOtherAvatars(float deltaTime); @@ -56,9 +56,6 @@ public: Q_INVOKABLE void setLocalLights(const QVector& localLights); Q_INVOKABLE QVector getLocalLights() const; - // Currently, your own avatar will be included as the null avatar id. - Q_INVOKABLE QVector getAvatarIdentifiers(); - Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID); void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 845a6a6245..ef7ff9684a 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -22,6 +22,16 @@ AvatarHashMap::AvatarHashMap() { connect(DependencyManager::get().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } +QVector AvatarHashMap::getAvatarIdentifiers() { + QReadLocker locker(&_hashLock); + return _avatarHash.keys().toVector(); +} + +AvatarData* AvatarHashMap::getAvatar(QUuid avatarID) { + // Null/Default-constructed QUuids will return MyAvatar + return getAvatarBySessionID(avatarID).get(); +} + bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { auto hashCopy = getHashCopy(); foreach(const AvatarSharedPointer& sharedAvatar, hashCopy) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index cb6c6cb0cc..0949b3ccea 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -34,6 +34,12 @@ public: AvatarHash getHashCopy() { QReadLocker lock(&_hashLock); return _avatarHash; } int size() { return _avatarHash.size(); } + // Currently, your own avatar will be included as the null avatar id. + Q_INVOKABLE QVector getAvatarIdentifiers(); + Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID); + + AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); } + signals: void avatarAddedEvent(const QUuid& sessionUUID); void avatarRemovedEvent(const QUuid& sessionUUID); From 816fe52c5b1c38a8c9d7aadfe38e6287b6e65faf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 3 Feb 2016 17:31:38 -0800 Subject: [PATCH 59/79] mark getAvatarBySessionID as virtual --- libraries/avatars/src/AvatarHashMap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 0949b3ccea..ee1197367c 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -38,7 +38,7 @@ public: Q_INVOKABLE QVector getAvatarIdentifiers(); Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID); - AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); } + virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); } signals: void avatarAddedEvent(const QUuid& sessionUUID); From dca7ff967cdfa9b0fdcbde2d7b7e4871fced78a6 Mon Sep 17 00:00:00 2001 From: "Babiuch, Ryan Nicholas" Date: Thu, 4 Feb 2016 08:26:56 -0600 Subject: [PATCH 60/79] Conform to coding standard. --- interface/src/avatar/MyAvatar.cpp | 4 ++-- .../entities/src/EntityScriptingInterface.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9d5c39ffc9..e906bf2485 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1866,7 +1866,7 @@ glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, co float MyAvatar::getAccelerationEnergy() { glm::vec3 velocity = getVelocity(); int changeInVelocity = abs(velocity.length() - priorVelocity.length()); - float changeInEnergy = priorVelocity.length()*changeInVelocity*AVATAR_MOVEMENT_ENERGY_CONSTANT; + float changeInEnergy = priorVelocity.length() * changeInVelocity * AVATAR_MOVEMENT_ENERGY_CONSTANT; priorVelocity = velocity; return changeInEnergy; @@ -1881,7 +1881,7 @@ void MyAvatar::setEnergy(float value) { } float MyAvatar::getAudioEnergy() { - return getAudioLoudness()*AUDIO_ENERGY_CONSTANT; + return getAudioLoudness() * AUDIO_ENERGY_CONSTANT; } bool MyAvatar::didTeleport() { diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 44044f0231..790a1e1919 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -123,10 +123,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); auto dimensions = propertiesWithSimID.getDimensions(); - float volume = dimensions.x*dimensions.y*dimensions.z; + float volume = dimensions.x * dimensions.y * dimensions.z; auto density = propertiesWithSimID.getDensity(); auto newVelocity = propertiesWithSimID.getVelocity().length(); - double cost = calculateCost(density*volume, 0, newVelocity); + double cost = calculateCost(density * volume, 0, newVelocity); cost *= costMultiplier; if(cost > _currentAvatarEnergy) { @@ -231,10 +231,10 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& EntityItemProperties properties = scriptSideProperties; auto dimensions = properties.getDimensions(); - float volume = dimensions.x*dimensions.y*dimensions.z; + float volume = dimensions.x * dimensions.y * dimensions.z; auto density = properties.getDensity(); auto newVelocity = properties.getVelocity().length(); - double cost = calculateCost(density*volume, 0, newVelocity); + double cost = calculateCost(density * volume, 0, newVelocity); cost *= costMultiplier; if(cost > _currentAvatarEnergy) { @@ -351,10 +351,10 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { if (entity) { auto dimensions = entity->getDimensions(); - float volume = dimensions.x*dimensions.y*dimensions.z; + float volume = dimensions.x * dimensions.y * dimensions.z; auto density = entity->getDensity(); auto velocity = entity->getVelocity().length(); - double cost = calculateCost(density*volume, velocity, 0); + double cost = calculateCost(density * volume, velocity, 0); cost *= costMultiplier; if(cost > _currentAvatarEnergy) { @@ -1041,7 +1041,7 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) { return result; } -float EntityScriptingInterface::calculateCost(float mass, float oldVelocity,float newVelocity) { +float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { return std::abs(mass * (newVelocity - oldVelocity)); } From 0069849da3f310b5427abfed1a8cdedf1ef15618 Mon Sep 17 00:00:00 2001 From: "Babiuch, Ryan Nicholas" Date: Thu, 4 Feb 2016 08:55:23 -0600 Subject: [PATCH 61/79] Consider existing velocity in calculating energy costs when editing entities. --- .../entities/src/EntityScriptingInterface.cpp | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 790a1e1919..3d252abc11 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -126,7 +126,7 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties float volume = dimensions.x * dimensions.y * dimensions.z; auto density = propertiesWithSimID.getDensity(); auto newVelocity = propertiesWithSimID.getVelocity().length(); - double cost = calculateCost(density * volume, 0, newVelocity); + float cost = calculateCost(density * volume, 0, newVelocity); cost *= costMultiplier; if(cost > _currentAvatarEnergy) { @@ -136,7 +136,6 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties emit debitEnergySource(cost); } - EntityItemID id = EntityItemID(QUuid::createUuid()); // If we have a local entity tree set, then also update it. @@ -234,19 +233,23 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& float volume = dimensions.x * dimensions.y * dimensions.z; auto density = properties.getDensity(); auto newVelocity = properties.getVelocity().length(); - double cost = calculateCost(density * volume, 0, newVelocity); - cost *= costMultiplier; - - if(cost > _currentAvatarEnergy) { - return QUuid(); - } else { - //debit the avatar energy and continue - emit debitEnergySource(cost); - } + float oldVelocity = { 0.0f }; EntityItemID entityID(id); if (!_entityTree) { queueEntityMessage(PacketType::EntityEdit, entityID, properties); + + //if there is no local entity entity tree, no existing velocity, use 0. + float cost = calculateCost(density * volume, oldVelocity, newVelocity); + cost *= costMultiplier; + + if(cost > _currentAvatarEnergy) { + return QUuid(); + } else { + //debit the avatar energy and continue + emit debitEnergySource(cost); + } + return id; } // If we have a local entity tree set, then also update it. @@ -260,6 +263,9 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& if (!entity) { return; } + //existing entity, retrieve old velocity for check down below + oldVelocity = entity->getVelocity().length(); + if (!scriptSideProperties.parentIDChanged()) { properties.setParentID(entity->getParentID()); } @@ -275,6 +281,16 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } properties = convertLocationFromScriptSemantics(properties); updatedEntity = _entityTree->updateEntity(entityID, properties); + + float cost = calculateCost(density * volume, oldVelocity, newVelocity); + cost *= costMultiplier; + + if(cost > _currentAvatarEnergy) { + updatedEntity = false; + } else { + //debit the avatar energy and continue + emit debitEnergySource(cost); + } }); if (!updatedEntity) { @@ -354,7 +370,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { float volume = dimensions.x * dimensions.y * dimensions.z; auto density = entity->getDensity(); auto velocity = entity->getVelocity().length(); - double cost = calculateCost(density * volume, velocity, 0); + float cost = calculateCost(density * volume, velocity, 0); cost *= costMultiplier; if(cost > _currentAvatarEnergy) { From a8e092272c6a7c9e401562c991dba25f9ffda734 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Feb 2016 10:32:58 -0800 Subject: [PATCH 62/79] AnimStateMachine: added new State parameter interpType interpType defines how the interpolation between two states is performed. * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them. * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual blend factor is not known at the start of the interp or is might change dramatically during the interp. --- libraries/animation/src/AnimNodeLoader.cpp | 26 ++++++++++- libraries/animation/src/AnimStateMachine.cpp | 45 +++++++++++++++++--- libraries/animation/src/AnimStateMachine.h | 23 +++++++++- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 568da8dd63..723d4deb29 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -67,6 +67,16 @@ static AnimNode::Type stringToAnimNodeType(const QString& str) { return AnimNode::Type::NumTypes; } +static AnimStateMachine::InterpType stringToInterpType(const QString& str) { + if (str == "snapshotBoth") { + return AnimStateMachine::InterpType::SnapshotBoth; + } else if (str == "snapshotPrev") { + return AnimStateMachine::InterpType::SnapshotPrev; + } else { + return AnimStateMachine::InterpType::NumTypes; + } +} + static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) { switch (type) { case AnimManipulator::JointVar::Type::AbsoluteRotation: return "absoluteRotation"; @@ -465,9 +475,11 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, READ_STRING(id, stateObj, nodeId, jsonUrl, false); READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false); READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false); + READ_OPTIONAL_STRING(interpType, stateObj); READ_OPTIONAL_STRING(interpTargetVar, stateObj); READ_OPTIONAL_STRING(interpDurationVar, stateObj); + READ_OPTIONAL_STRING(interpTypeVar, stateObj); auto iter = childMap.find(id); if (iter == childMap.end()) { @@ -475,7 +487,16 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, return false; } - auto statePtr = std::make_shared(id, iter->second, interpTarget, interpDuration); + AnimStateMachine::InterpType interpTypeEnum = AnimStateMachine::InterpType::SnapshotBoth; // default value + if (!interpType.isEmpty()) { + interpTypeEnum = stringToInterpType(interpType); + if (interpTypeEnum == AnimStateMachine::InterpType::NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad interpType on stateMachine state, nodeId = " << nodeId << "stateId =" << id << "url = " << jsonUrl.toDisplayString(); + return false; + } + } + + auto statePtr = std::make_shared(id, iter->second, interpTarget, interpDuration, interpTypeEnum); assert(statePtr); if (!interpTargetVar.isEmpty()) { @@ -484,6 +505,9 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, if (!interpDurationVar.isEmpty()) { statePtr->setInterpDurationVar(interpDurationVar); } + if (!interpTypeVar.isEmpty()) { + statePtr->setInterpTypeVar(interpTypeVar); + } smNode->addState(statePtr); stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr)); diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 3f5a81a861..e8f9c944b7 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -52,8 +52,25 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl if (_duringInterp) { _alpha += _alphaVel * dt; if (_alpha < 1.0f) { - if (_poses.size() > 0 && _nextPoses.size() > 0 && _prevPoses.size() > 0) { - ::blend(_poses.size(), &_prevPoses[0], &_nextPoses[0], _alpha, &_poses[0]); + AnimPoseVec* nextPoses = nullptr; + AnimPoseVec* prevPoses = nullptr; + AnimPoseVec localNextPoses; + if (_interpType == InterpType::SnapshotBoth) { + // interp between both snapshots + prevPoses = &_prevPoses; + nextPoses = &_nextPoses; + } else if (_interpType == InterpType::SnapshotPrev) { + // interp between the prev snapshot and evaluated next target. + // this is useful for interping into a blend + localNextPoses = currentStateNode->evaluate(animVars, dt, triggersOut); + prevPoses = &_prevPoses; + nextPoses = &localNextPoses; + } else { + assert(false); + } + + if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) { + ::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]); } } else { _duringInterp = false; @@ -86,16 +103,32 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe _alpha = 0.0f; float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration)); _alphaVel = FRAMES_PER_SECOND / duration; - _prevPoses = _poses; - nextStateNode->setCurrentFrame(desiredState->_interpTarget); + _interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType); // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); + + if (_interpType == InterpType::SnapshotBoth) { + // snapshot previous pose. + _prevPoses = _poses; + // snapshot next pose at the target frame. + nextStateNode->setCurrentFrame(desiredState->_interpTarget); + _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); + } else if (_interpType == InterpType::SnapshotPrev) { + // snapshot previoius pose + _prevPoses = _poses; + // no need to evaluate _nextPoses we will do it dynamically during the interp, + // however we need to set the current frame. + nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration); + } else { + assert(false); + } + #if WANT_DEBUG - qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget; + qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType; #endif + _currentState = desiredState; } diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index dda56235d5..6a28ef1825 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -31,13 +31,27 @@ // visible after interpolation is complete. // * interpDuration - (frames) The total length of time it will take to interp between the current pose and the // interpTarget frame. +// * interpType - How the interpolation is performed. +// * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the +// interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them. +// * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is +// evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose +// to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual +// blend factor is not known at the start of the interp or is might change dramatically during the interp. class AnimStateMachine : public AnimNode { public: friend class AnimNodeLoader; friend bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl); + enum class InterpType { + SnapshotBoth = 0, + SnapshotPrev, + NumTypes + }; + protected: + class State { public: friend AnimStateMachine; @@ -55,14 +69,16 @@ protected: State::Pointer _state; }; - State(const QString& id, int childIndex, float interpTarget, float interpDuration) : + State(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType) : _id(id), _childIndex(childIndex), _interpTarget(interpTarget), - _interpDuration(interpDuration) {} + _interpDuration(interpDuration), + _interpType(interpType) {} void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; } void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; } + void setInterpTypeVar(const QString& interpTypeVar) { _interpTypeVar = interpTypeVar; } int getChildIndex() const { return _childIndex; } const QString& getID() const { return _id; } @@ -78,9 +94,11 @@ protected: int _childIndex; float _interpTarget; // frames float _interpDuration; // frames + InterpType _interpType; QString _interpTargetVar; QString _interpDurationVar; + QString _interpTypeVar; std::vector _transitions; @@ -115,6 +133,7 @@ protected: // interpolation state bool _duringInterp = false; + InterpType _interpType { InterpType::SnapshotBoth }; float _alphaVel = 0.0f; float _alpha = 0.0f; AnimPoseVec _prevPoses; From e3351c05a27579e6b53d987e26c801a0f93d661d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 14:26:59 -0800 Subject: [PATCH 63/79] make some methods const --- libraries/entities/src/EntityItem.cpp | 2 +- libraries/entities/src/EntityItem.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 99dff84407..b2102ed740 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -987,7 +987,7 @@ EntityTreePointer EntityItem::getTree() const { return tree; } -bool EntityItem::wantTerseEditLogging() { +bool EntityItem::wantTerseEditLogging() const { EntityTreePointer tree = getTree(); return tree ? tree->wantTerseEditLogging() : false; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c8bf62f83e..8271aedb15 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -104,7 +104,7 @@ public: quint64 getLastBroadcast() const { return _lastBroadcast; } void setLastBroadcast(quint64 lastBroadcast) { _lastBroadcast = lastBroadcast; } - void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); } + void markAsChangedOnServer() { _changedOnServer = usecTimestampNow(); } quint64 getLastChangedOnServer() const { return _changedOnServer; } // TODO: eventually only include properties changed since the params.lastViewFrustumSent time @@ -351,14 +351,14 @@ public: void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElementPointer getElement() const { return _element; } EntityTreePointer getTree() const; - bool wantTerseEditLogging(); + bool wantTerseEditLogging() const; glm::mat4 getEntityToWorldMatrix() const; glm::mat4 getWorldToEntityMatrix() const; glm::vec3 worldToEntity(const glm::vec3& point) const; glm::vec3 entityToWorld(const glm::vec3& point) const; - quint64 getLastEditedFromRemote() { return _lastEditedFromRemote; } + quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; } void getAllTerseUpdateProperties(EntityItemProperties& properties) const; From 05fb866bb5cfdacb46a33d83bf1a3210d764d960 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 14:27:14 -0800 Subject: [PATCH 64/79] fix spelling typo in comment --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 0a72789485..8e0983f62a 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -910,7 +910,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem success = packetData->startSubTree(octcode); delete[] octcode; - // assuming we have rome to fit our octalCode, proceed... + // assuming we have room to fit our octalCode, proceed... if (success) { // Now add our edit content details... From 381049acb3d0393c7b52f3e31ccbf7498927799f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 14:28:59 -0800 Subject: [PATCH 65/79] clear simulation ownership when owners vanish --- .../entities/src/SimpleEntitySimulation.cpp | 44 ++++++++++++------- .../entities/src/SimpleEntitySimulation.h | 1 + 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index 120b536161..f0461abb97 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -15,34 +15,46 @@ #include "SimpleEntitySimulation.h" #include "EntitiesLogging.h" -const quint64 AUTO_REMOVE_SIMULATION_OWNER_USEC = 2 * USECS_PER_SECOND; +const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND; void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { - // If an Entity has a simulation owner and we don't get an update for some amount of time, - // clear the owner. This guards against an interface failing to release the Entity when it - // has finished simulating it. - auto nodeList = DependencyManager::get(); + if (_entitiesWithSimulator.size() == 0) { + return; + } + + if (now < _nextSimulationExpiry) { + // nothing has expired yet + return; + } + + // If an Entity has a simulation owner but there has been no update for a while: clear the owner. + // If an Entity goes ownerless for too long: zero velocity and remove from _entitiesWithSimulator. + _nextSimulationExpiry = now + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD; QMutexLocker lock(&_mutex); SetOfEntities::iterator itemItr = _entitiesWithSimulator.begin(); while (itemItr != _entitiesWithSimulator.end()) { EntityItemPointer entity = *itemItr; - if (entity->getSimulatorID().isNull()) { - itemItr = _entitiesWithSimulator.erase(itemItr); - } else if (now - entity->getLastChangedOnServer() >= AUTO_REMOVE_SIMULATION_OWNER_USEC) { - SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID()); - if (ownerNode.isNull() || !ownerNode->isAlive()) { - qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID(); - entity->clearSimulationOwnership(); - itemItr = _entitiesWithSimulator.erase(itemItr); + quint64 expiry = entity->getLastChangedOnServer() + MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD; + if (expiry < now) { + if (entity->getSimulatorID().isNull()) { + // no simulators are volunteering // zero the velocity on this entity so that it doesn't drift far away entity->setVelocity(glm::vec3(0.0f)); + // remove from list + itemItr = _entitiesWithSimulator.erase(itemItr); + continue; } else { - ++itemItr; + // the simulator has stopped updating this object + // clear ownership and restart timer, giving nearby simulators time to volunteer + qCDebug(entities) << "auto-removing simulation owner " << entity.getSimulatorID(); + entity->clearSimulationOwnership(); + entity->markAsChangedOnServer(); } - } else { - ++itemItr; + } else if (expiry < _nextSimulationExpiry) { + _nextSimulationExpiry = expiry; } + ++itemItr; } } diff --git a/libraries/entities/src/SimpleEntitySimulation.h b/libraries/entities/src/SimpleEntitySimulation.h index 83c51525a8..53a7574bf2 100644 --- a/libraries/entities/src/SimpleEntitySimulation.h +++ b/libraries/entities/src/SimpleEntitySimulation.h @@ -29,6 +29,7 @@ protected: virtual void clearEntitiesInternal() override; SetOfEntities _entitiesWithSimulator; + quint64 _nextSimulationExpiry { 0 }; }; #endif // hifi_SimpleEntitySimulation_h From e51edaa1170f5532d706feb7f6887f1feb60c534 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 2 Feb 2016 16:46:07 -0800 Subject: [PATCH 66/79] update some comments --- libraries/physics/src/EntityMotionState.cpp | 29 +++++++++---------- libraries/physics/src/EntityMotionState.h | 6 ++-- .../physics/src/PhysicalEntitySimulation.cpp | 3 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 84d2282e7a..52151b7136 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -104,25 +104,25 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) { if (flags & Simulation::DIRTY_SIMULATOR_ID) { _loopsWithoutOwner = 0; if (_entity->getSimulatorID().isNull()) { - // simulation ownership is being removed - // remove the ACTIVATION flag because this object is coming to rest - // according to a remote simulation and we don't want to wake it up again + // simulation ownership has been removed by an external simulator + // --> clear the ACTIVATION flag and outgoing priority because this object is coming to rest flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; - // hint to Bullet that the object is deactivating _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = NO_PRORITY; - } else { + } else { + // this entity's simulation is owned by someone, so we push its ownership expiry into the future _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) { - // we own the simulation or our priority looses to (or ties with) remote + // either we already own the simulation or our old outgoing priority momentarily looses to current owner + // so we clear it _outgoingPriority = NO_PRORITY; } } } if (flags & Simulation::DIRTY_SIMULATOR_OWNERSHIP) { - // (DIRTY_SIMULATOR_OWNERSHIP really means "we should bid for ownership with SCRIPT priority") - // we're manipulating this object directly via script, so we artificially - // manipulate the logic to trigger an immediate bid for ownership + // The DIRTY_SIMULATOR_OWNERSHIP bit really means "we should bid for ownership at SCRIPT priority". + // Since that bit is set there must be a local script that is updating the physics properties of the objects + // therefore we upgrade _outgoingPriority to trigger a bid for ownership. setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); } if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { @@ -203,7 +203,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { _loopsWithoutOwner++; if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextOwnershipBid) { - //qDebug() << "Warning -- claiming something I saw moving." << getName(); setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY); } } @@ -235,9 +234,8 @@ btCollisionShape* EntityMotionState::computeNewShape() { } bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { - if (!_body || !_entity) { - return false; - } + assert(_body); + assert(_entity); assert(entityTreeIsLocked()); return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); } @@ -374,10 +372,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s } if (_entity->getSimulatorID() != sessionID) { - // we don't own the simulation, but maybe we should... + // we don't own the simulation if (_outgoingPriority != NO_PRORITY) { + // but we would like to own it if (_outgoingPriority < _entity->getSimulationPriority()) { - // our priority loses to remote, so we don't bother to bid + // but our priority loses to remote, so we don't bother trying _outgoingPriority = NO_PRORITY; return false; } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 2c999d0aca..7e350a4bae 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -106,7 +106,7 @@ protected: // Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid. EntityItem* _entity; - bool _sentInactive; // true if body was inactive when we sent last update + bool _triedToReleaseOwnership; // true if we tried to release ownership // these are for the prediction of the remote server's simple extrapolation uint32_t _lastStep; // last step of server extrapolation @@ -124,8 +124,8 @@ protected: float _measuredDeltaTime; quint8 _accelerationNearlyGravityCount; - quint64 _nextOwnershipBid = NO_PRORITY; - uint32_t _loopsWithoutOwner; + quint64 _nextOwnershipBid { 0 }; + quint64 _orphanExpiry { 0 }; quint8 _outgoingPriority = NO_PRORITY; }; diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index d2c91f29dd..d5e8d972fe 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -249,6 +249,7 @@ void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& motionStates, const QUuid& sessionID) { QMutexLocker lock(&_mutex); + // walk the motionStates looking for those that correspond to entities for (auto stateItr : motionStates) { ObjectMotionState* state = &(*stateItr); @@ -273,7 +274,7 @@ void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& return; } - // send outgoing packets + // look for entities that need outgoing packets QSet::iterator stateItr = _outgoingChanges.begin(); while (stateItr != _outgoingChanges.end()) { EntityMotionState* state = *stateItr; From 91a2f86482667b7c300b778e3a620d595d89ee67 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:22:26 -0800 Subject: [PATCH 67/79] update some comments --- libraries/physics/src/PhysicalEntitySimulation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index d5e8d972fe..71c78b8b86 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -274,13 +274,15 @@ void PhysicalEntitySimulation::handleOutgoingChanges(const VectorOfMotionStates& return; } - // look for entities that need outgoing packets + // look for entities to prune or update QSet::iterator stateItr = _outgoingChanges.begin(); while (stateItr != _outgoingChanges.end()) { EntityMotionState* state = *stateItr; if (!state->isCandidateForOwnership(sessionID)) { + // prune stateItr = _outgoingChanges.erase(stateItr); } else if (state->shouldSendUpdate(numSubsteps, sessionID)) { + // update state->sendUpdate(_entityPacketSender, sessionID, numSubsteps); ++stateItr; } else { From 959f924b1d3fd3e05cea16b934b853579f69e0bf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:22:48 -0800 Subject: [PATCH 68/79] flag entity as changed when changing simulatorID --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b2102ed740..a4a646f532 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -652,6 +652,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef } if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + somethingChanged = true; } } { // When we own the simulation we don't accept updates to the entity's transform/velocities From af57f5d12085acdec96472fb55f9880fcbc2546b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:24:08 -0800 Subject: [PATCH 69/79] add simple and fast DirtyOctreeElementOperator --- .../octree/src/DirtyOctreeElementOperator.cpp | 30 +++++++++++++++++++ .../octree/src/DirtyOctreeElementOperator.h | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 libraries/octree/src/DirtyOctreeElementOperator.cpp create mode 100644 libraries/octree/src/DirtyOctreeElementOperator.h diff --git a/libraries/octree/src/DirtyOctreeElementOperator.cpp b/libraries/octree/src/DirtyOctreeElementOperator.cpp new file mode 100644 index 0000000000..0f7e6d9f04 --- /dev/null +++ b/libraries/octree/src/DirtyOctreeElementOperator.cpp @@ -0,0 +1,30 @@ +// +// DirtyOctreeElementOperator.cpp +// libraries/entities/src +// +// Created by Andrew Meawdows 2016.02.04 +// Copyright 2016 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 "DirtyOctreeElementOperator.h" + +DirtyOctreeElementOperator::DirtyOctreeElementOperator(OctreeElementPointer element) + : _element(element) { + assert(_element.get()); + _point = _element->getAACube().calcCenter(); +} + +bool DirtyOctreeElementOperator::preRecursion(OctreeElementPointer element) { + if (element == _element) { + return false; + } + return element->getAACube().contains(_point); +} + +bool DirtyOctreeElementOperator::postRecursion(OctreeElementPointer element) { + element->markWithChangedTime(); + return true; +} diff --git a/libraries/octree/src/DirtyOctreeElementOperator.h b/libraries/octree/src/DirtyOctreeElementOperator.h new file mode 100644 index 0000000000..a5eec780a0 --- /dev/null +++ b/libraries/octree/src/DirtyOctreeElementOperator.h @@ -0,0 +1,30 @@ +// +// DirtyOctreeElementOperator.h +// libraries/entities/src +// +// Created by Andrew Meawdows 2016.02.04 +// Copyright 2016 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 +// + +#ifndef hifi_DirtyOctreeElementOperator_h +#define hifi_DirtyOctreeElementOperator_h + +#include "Octree.h" + +class DirtyOctreeElementOperator : public RecurseOctreeOperator { +public: + DirtyOctreeElementOperator(OctreeElementPointer element); + + ~DirtyOctreeElementOperator() {} + + virtual bool preRecursion(OctreeElementPointer element); + virtual bool postRecursion(OctreeElementPointer element); +private: + glm::vec3 _point; + OctreeElementPointer _element; +}; + +#endif // hifi_DirtyOctreeElementOperator_h From 2da46ff26a16d8f812d12745735c121856c5e1f8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Feb 2016 13:24:30 -0800 Subject: [PATCH 70/79] server-side release ownership --- .../entities/src/SimpleEntitySimulation.cpp | 16 +++- libraries/entities/src/SimulationOwner.cpp | 9 +-- libraries/entities/src/SimulationOwner.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 80 +++++++++++-------- libraries/physics/src/EntityMotionState.h | 27 +++---- 5 files changed, 76 insertions(+), 60 deletions(-) diff --git a/libraries/entities/src/SimpleEntitySimulation.cpp b/libraries/entities/src/SimpleEntitySimulation.cpp index f0461abb97..bdf27f4440 100644 --- a/libraries/entities/src/SimpleEntitySimulation.cpp +++ b/libraries/entities/src/SimpleEntitySimulation.cpp @@ -11,8 +11,11 @@ //#include -#include "EntityItem.h" #include "SimpleEntitySimulation.h" + +#include + +#include "EntityItem.h" #include "EntitiesLogging.h" const quint64 MIN_SIMULATION_OWNERSHIP_UPDATE_PERIOD = 2 * USECS_PER_SECOND; @@ -40,17 +43,22 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) { if (entity->getSimulatorID().isNull()) { // no simulators are volunteering // zero the velocity on this entity so that it doesn't drift far away - entity->setVelocity(glm::vec3(0.0f)); + entity->setVelocity(Vectors::ZERO); + entity->setAngularVelocity(Vectors::ZERO); + entity->setAcceleration(Vectors::ZERO); // remove from list itemItr = _entitiesWithSimulator.erase(itemItr); continue; } else { // the simulator has stopped updating this object // clear ownership and restart timer, giving nearby simulators time to volunteer - qCDebug(entities) << "auto-removing simulation owner " << entity.getSimulatorID(); + qCDebug(entities) << "auto-removing simulation owner " << entity->getSimulatorID(); entity->clearSimulationOwnership(); - entity->markAsChangedOnServer(); } + entity->markAsChangedOnServer(); + // dirty all the tree elements that contain the entity + DirtyOctreeElementOperator op(entity->getElement()); + getEntityTree()->recurseTreeWithOperator(&op); } else if (expiry < _nextSimulationExpiry) { _nextSimulationExpiry = expiry; } diff --git a/libraries/entities/src/SimulationOwner.cpp b/libraries/entities/src/SimulationOwner.cpp index 24f6784954..669d811df9 100644 --- a/libraries/entities/src/SimulationOwner.cpp +++ b/libraries/entities/src/SimulationOwner.cpp @@ -16,11 +16,11 @@ #include -// static +// static const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1; -SimulationOwner::SimulationOwner(const SimulationOwner& other) +SimulationOwner::SimulationOwner(const SimulationOwner& other) : _id(other._id), _priority(other._priority), _expiry(other._expiry) { } @@ -48,11 +48,6 @@ void SimulationOwner::clear() { void SimulationOwner::setPriority(quint8 priority) { _priority = priority; - if (_priority == 0) { - // when priority is zero we clear everything - _expiry = 0; - _id = QUuid(); - } } void SimulationOwner::promotePriority(quint8 priority) { diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index a848ad6e84..bd17128003 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -18,10 +18,10 @@ #include #include -const quint8 NO_PRORITY = 0x00; +const quint8 ZERO_SIMULATION_PRIORITY = 0x00; // Simulation observers will bid to simulate unowned active objects at the lowest possible priority -// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it +// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it // to RECRUIT priority so that other volunteers don't accidentally take over. const quint8 VOLUNTEER_SIMULATION_PRIORITY = 0x01; const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 52151b7136..155186e891 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -26,10 +26,7 @@ #include "EntityTree.h" #endif -static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; -static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; - -const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50; +const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS @@ -52,8 +49,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer ObjectMotionState(shape), _entityPtr(entity), _entity(entity.get()), - _sentInactive(true), - _lastStep(0), _serverPosition(0.0f), _serverRotation(), _serverVelocity(0.0f), @@ -61,13 +56,16 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _serverGravity(0.0f), _serverAcceleration(0.0f), _serverActionData(QByteArray()), - _lastMeasureStep(0), _lastVelocity(glm::vec3(0.0f)), _measuredAcceleration(glm::vec3(0.0f)), - _measuredDeltaTime(0.0f), - _accelerationNearlyGravityCount(0), _nextOwnershipBid(0), - _loopsWithoutOwner(0) + _measuredDeltaTime(0.0f), + _lastMeasureStep(0), + _lastStep(0), + _loopsWithoutOwner(0), + _accelerationNearlyGravityCount(0), + _numInactiveUpdates(1), + _outgoingPriority(ZERO_SIMULATION_PRIORITY) { _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity); @@ -102,20 +100,28 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) { ObjectMotionState::handleEasyChanges(flags); if (flags & Simulation::DIRTY_SIMULATOR_ID) { - _loopsWithoutOwner = 0; if (_entity->getSimulatorID().isNull()) { // simulation ownership has been removed by an external simulator - // --> clear the ACTIVATION flag and outgoing priority because this object is coming to rest - flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; - _body->setActivationState(WANTS_DEACTIVATION); - _outgoingPriority = NO_PRORITY; + if (glm::length2(_entity->getVelocity()) == 0.0f) { + // this object is coming to rest --> clear the ACTIVATION flag and outgoing priority + flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; + _body->setActivationState(WANTS_DEACTIVATION); + _outgoingPriority = ZERO_SIMULATION_PRIORITY; + _loopsWithoutOwner = 0; + } else { + // unowned object is still moving --> we should volunteer to own it + // TODO? put a delay in here proportional to distance from object? + setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY); + _loopsWithoutOwner = LOOPS_FOR_SIMULATION_ORPHAN; + _nextOwnershipBid = 0; + } } else { // this entity's simulation is owned by someone, so we push its ownership expiry into the future _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) { // either we already own the simulation or our old outgoing priority momentarily looses to current owner // so we clear it - _outgoingPriority = NO_PRORITY; + _outgoingPriority = ZERO_SIMULATION_PRIORITY; } } } @@ -237,10 +243,11 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { assert(_body); assert(_entity); assert(entityTreeIsLocked()); - return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); + return _outgoingPriority != ZERO_SIMULATION_PRIORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { + // NOTE: we only get here if we think we own the simulation assert(_body); // if we've never checked before, our _lastStep will be 0, and we need to initialize our state if (_lastStep == 0) { @@ -251,7 +258,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); - _sentInactive = true; + _numInactiveUpdates = 1; return false; } @@ -264,16 +271,21 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { int numSteps = simulationStep - _lastStep; float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP; - const float INACTIVE_UPDATE_PERIOD = 0.5f; - if (_sentInactive) { + if (_numInactiveUpdates > 0) { + const uint8_t MAX_NUM_INACTIVE_UPDATES = 3; + if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) { + // clear local ownership (stop sending updates) and let the server clear itself + _entity->clearSimulationOwnership(); + return false; + } // we resend the inactive update every INACTIVE_UPDATE_PERIOD // until it is removed from the outgoing updates // (which happens when we don't own the simulation and it isn't touching our simulation) + const float INACTIVE_UPDATE_PERIOD = 0.5f; return (dt > INACTIVE_UPDATE_PERIOD); } - bool isActive = _body->isActive(); - if (!isActive) { + if (!_body->isActive()) { // object has gone inactive but our last send was moving --> send non-moving update immediately return true; } @@ -373,11 +385,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s if (_entity->getSimulatorID() != sessionID) { // we don't own the simulation - if (_outgoingPriority != NO_PRORITY) { + if (_outgoingPriority != ZERO_SIMULATION_PRIORITY) { // but we would like to own it if (_outgoingPriority < _entity->getSimulationPriority()) { // but our priority loses to remote, so we don't bother trying - _outgoingPriority = NO_PRORITY; + _outgoingPriority = ZERO_SIMULATION_PRIORITY; return false; } return usecTimestampNow() > _nextOwnershipBid; @@ -399,10 +411,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _entity->setVelocity(zero); _entity->setAngularVelocity(zero); _entity->setAcceleration(zero); - _sentInactive = true; + _numInactiveUpdates++; } else { + const uint8_t STEPS_TO_DECIDE_BALLISTIC = 4; float gravityLength = glm::length(_entity->getGravity()); float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength); + const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; if (accVsGravity < ACCELERATION_EQUIVALENT_EPSILON_RATIO * gravityLength) { // acceleration measured during the most recent simulation step was close to gravity. if (getAccelerationNearlyGravityCount() < STEPS_TO_DECIDE_BALLISTIC) { @@ -439,7 +453,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _entity->setVelocity(zero); _entity->setAngularVelocity(zero); } - _sentInactive = false; + _numInactiveUpdates = 0; } // remember properties for local server prediction @@ -487,12 +501,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q // we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID // but we remember that we do still own it... and rely on the server to tell us that we don't properties.clearSimulationOwner(); - _outgoingPriority = NO_PRORITY; + _outgoingPriority = ZERO_SIMULATION_PRIORITY; } // else the ownership is not changing so we don't bother to pack it } else { // we don't own the simulation for this entity yet, but we're sending a bid for it - properties.setSimulationOwner(sessionID, glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY)); + properties.setSimulationOwner(sessionID, glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY)); _nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS; } @@ -557,7 +571,7 @@ void EntityMotionState::clearIncomingDirtyFlags() { } // virtual -quint8 EntityMotionState::getSimulationPriority() const { +uint8_t EntityMotionState::getSimulationPriority() const { return _entity->getSimulationPriority(); } @@ -567,7 +581,7 @@ QUuid EntityMotionState::getSimulatorID() const { return _entity->getSimulatorID(); } -void EntityMotionState::bump(quint8 priority) { +void EntityMotionState::bump(uint8_t priority) { setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority)); } @@ -600,7 +614,7 @@ void EntityMotionState::measureBodyAcceleration() { if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { _loopsWithoutOwner = 0; _lastStep = ObjectMotionState::getWorldSimulationStep(); - _sentInactive = false; + _numInactiveUpdates = 0; } } } @@ -630,6 +644,6 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma _entity->computeCollisionGroupAndFinalMask(group, mask); } -void EntityMotionState::setOutgoingPriority(quint8 priority) { - _outgoingPriority = glm::max(_outgoingPriority, priority); +void EntityMotionState::setOutgoingPriority(uint8_t priority) { + _outgoingPriority = glm::max(_outgoingPriority, priority); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 7e350a4bae..cab8448dd9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -53,7 +53,7 @@ public: void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } - quint8 getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } + uint8_t getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } virtual float getObjectRestitution() const override { return _entity->getRestitution(); } virtual float getObjectFriction() const override { return _entity->getFriction(); } @@ -69,9 +69,9 @@ public: virtual const QUuid getObjectID() const override { return _entity->getID(); } - virtual quint8 getSimulationPriority() const override; + virtual uint8_t getSimulationPriority() const override; virtual QUuid getSimulatorID() const override; - virtual void bump(quint8 priority) override; + virtual void bump(uint8_t priority) override; EntityItemPointer getEntity() const { return _entityPtr.lock(); } @@ -83,7 +83,7 @@ public: virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; // eternal logic can suggest a simuator priority bid for the next outgoing update - void setOutgoingPriority(quint8 priority); + void setOutgoingPriority(uint8_t priority); friend class PhysicalEntitySimulation; @@ -106,10 +106,6 @@ protected: // Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid. EntityItem* _entity; - bool _triedToReleaseOwnership; // true if we tried to release ownership - - // these are for the prediction of the remote server's simple extrapolation - uint32_t _lastStep; // last step of server extrapolation glm::vec3 _serverPosition; // in simulation-frame (not world-frame) glm::quat _serverRotation; glm::vec3 _serverVelocity; @@ -118,15 +114,18 @@ protected: glm::vec3 _serverAcceleration; QByteArray _serverActionData; - uint32_t _lastMeasureStep; glm::vec3 _lastVelocity; glm::vec3 _measuredAcceleration; - float _measuredDeltaTime; - - quint8 _accelerationNearlyGravityCount; quint64 _nextOwnershipBid { 0 }; - quint64 _orphanExpiry { 0 }; - quint8 _outgoingPriority = NO_PRORITY; + + float _measuredDeltaTime; + uint32_t _lastMeasureStep; + uint32_t _lastStep; // last step of server extrapolation + + uint8_t _loopsWithoutOwner; + uint8_t _accelerationNearlyGravityCount; + uint8_t _numInactiveUpdates { 1 }; + uint8_t _outgoingPriority { ZERO_SIMULATION_PRIORITY }; }; #endif // hifi_EntityMotionState_h From 64597f9d9f86c71dfe2d6e3a9b915a04d60d6032 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 4 Feb 2016 13:32:52 -0800 Subject: [PATCH 71/79] Fixing save dialog issues saving new files --- .../resources/qml/dialogs/FileDialog.qml | 91 ++++++++++--------- libraries/ui/src/FileDialogHelper.cpp | 9 +- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 142ed198c0..a482b76c70 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -229,55 +229,64 @@ ModalWindow { id: okAction text: root.saveDialog ? "Save" : (root.selectDirectory ? "Choose" : "Open") enabled: currentSelection.text ? true : false + onTriggered: okActionTimer.start(); + } + + Timer { + id: okActionTimer + interval: 50 + running: false + repeat: false onTriggered: { - if (root.saveDialog) { - // Handle the ambiguity between different cases - // * typed name (with or without extension) - // * full path vs relative vs filename only - var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); + if (!root.saveDialog) { + selectedFile(d.currentSelectionUrl); + root.destroy() + return; + } - if (!selection) { - desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) - return; - } - if (helper.urlIsDir(selection)) { - root.dir = selection; - currentSelection.text = ""; - return; - } + // Handle the ambiguity between different cases + // * typed name (with or without extension) + // * full path vs relative vs filename only + var selection = helper.saveHelper(currentSelection.text, root.dir, selectionType.currentFilter); - // Check if the file is a valid target - if (!helper.urlIsWritable(selection)) { - desktop.messageBox({ - icon: OriginalDialogs.StandardIcon.Warning, - buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, - text: "Unable to write to location " + selection - }) - return; - } + if (!selection) { + desktop.messageBox({ icon: OriginalDialogs.StandardIcon.Warning, text: "Unable to parse selection" }) + return; + } - if (helper.urlExists(selection)) { - var messageBox = desktop.messageBox({ - icon: OriginalDialogs.StandardIcon.Question, - buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, - text: "Do you wish to overwrite " + selection + "?", - }); - var result = messageBox.exec(); - if (OriginalDialogs.StandardButton.Yes !== result) { - return; - } - } + if (helper.urlIsDir(selection)) { + root.dir = selection; + currentSelection.text = ""; + return; + } - selectedFile(d.currentSelectionUrl); - root.destroy() - } else { - selectedFile(d.currentSelectionUrl); - root.destroy() - } + // Check if the file is a valid target + if (!helper.urlIsWritable(selection)) { + desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Warning, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + text: "Unable to write to location " + selection + }) + return; + } + if (helper.urlExists(selection)) { + var messageBox = desktop.messageBox({ + icon: OriginalDialogs.StandardIcon.Question, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + text: "Do you wish to overwrite " + selection + "?", + }); + var result = messageBox.exec(); + if (OriginalDialogs.StandardButton.Yes !== result) { + return; + } + } + + console.log("Selecting " + selection) + selectedFile(selection); + root.destroy(); } - } Action { diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index f1cbb22f56..56b9695444 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -86,5 +86,12 @@ bool FileDialogHelper::urlExists(const QUrl& url) { } bool FileDialogHelper::urlIsWritable(const QUrl& url) { - return QFileInfo(url.toLocalFile()).isWritable(); + QFileInfo fileInfo(url.toLocalFile()); + // Is the file writable? + if (fileInfo.exists()) { + return fileInfo.isWritable(); + } + + // No file, get the parent directory and check if writable + return QFileInfo(fileInfo.absoluteDir().absolutePath()).isWritable(); } From 42e9a4ebf04d3a21051ea9d76378b0dc13fad5c3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 4 Feb 2016 13:43:50 -0800 Subject: [PATCH 72/79] fix ATP SendQueue failure to receive after re-activity --- libraries/networking/src/udt/Connection.cpp | 10 +++++++++- libraries/networking/src/udt/Connection.h | 2 ++ libraries/networking/src/udt/SendQueue.cpp | 13 ++++++++----- libraries/networking/src/udt/SendQueue.h | 7 ++++--- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index e63d44e72f..3b5b26d05d 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -82,8 +82,12 @@ void Connection::resetRTT() { SendQueue& Connection::getSendQueue() { if (!_sendQueue) { + + // we may have a sequence number from the previous inactive queue - re-use that so that the + // receiver is getting the sequence numbers it expects (given that the connection must still be active) + // Lasily create send queue - _sendQueue = SendQueue::create(_parentSocket, _destination); + _sendQueue = SendQueue::create(_parentSocket, _destination, _inactiveSendQueueSequenceNumber); #ifdef UDT_CONNECTION_DEBUG qCDebug(networking) << "Created SendQueue for connection to" << _destination; @@ -105,6 +109,10 @@ SendQueue& Connection::getSendQueue() { } void Connection::queueInactive() { + // get the current sequence number from the send queue, this is to be re-used if the send + // queue is re-activated for this connection + _inactiveSendQueueSequenceNumber = _sendQueue->getCurrentSequenceNumber(); + // tell our current send queue to go down and reset our ptr to it to null stopSendQueue(); diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index f8c4acd7d9..b58b7ec570 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -139,6 +139,8 @@ private: SequenceNumber _lastSentACK; // The last sent ACK SequenceNumber _lastSentACK2; // The last sent ACK sub-sequence number in an ACK2 + + SequenceNumber _inactiveSendQueueSequenceNumber { 0 }; int _acksDuringSYN { 1 }; // The number of non-SYN ACKs sent during SYN int _lightACKsDuringSYN { 1 }; // The number of lite ACKs sent during SYN interval diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index ba99efdcea..4d6e431b45 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -52,11 +52,11 @@ private: Mutex2& _mutex2; }; -std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destination) { +std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber) { Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*"); - auto queue = std::unique_ptr(new SendQueue(socket, destination)); - + auto queue = std::unique_ptr(new SendQueue(socket, destination, currentSequenceNumber)); + // Setup queue private thread QThread* thread = new QThread; thread->setObjectName("Networking: SendQueue " + destination.objectName()); // Name thread for easier debug @@ -74,10 +74,12 @@ std::unique_ptr SendQueue::create(Socket* socket, HifiSockAddr destin return queue; } -SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) : +SendQueue::SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber) : _socket(socket), - _destination(dest) + _destination(dest), + _currentSequenceNumber(currentSequenceNumber) { + } void SendQueue::queuePacket(std::unique_ptr packet) { @@ -389,6 +391,7 @@ bool SendQueue::isInactive(bool sentAPacket) { static const int NUM_TIMEOUTS_BEFORE_INACTIVE = 16; static const int MIN_SECONDS_BEFORE_INACTIVE_MS = 5 * 1000; if (_timeoutExpiryCount >= NUM_TIMEOUTS_BEFORE_INACTIVE && + _lastReceiverResponse > 0 && (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse) > MIN_SECONDS_BEFORE_INACTIVE_MS) { // If the flow window has been full for over CONSIDER_INACTIVE_AFTER, // then signal the queue is inactive and return so it can be cleaned up diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index 3fe4006139..5428e7a26d 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -50,7 +50,8 @@ public: Stopped }; - static std::unique_ptr create(Socket* socket, HifiSockAddr destination); + static std::unique_ptr create(Socket* socket, HifiSockAddr destination, + SequenceNumber currentSequenceNumber = SequenceNumber()); void queuePacket(std::unique_ptr packet); void queuePacketList(std::unique_ptr packetList); @@ -83,7 +84,7 @@ private slots: void run(); private: - SendQueue(Socket* socket, HifiSockAddr dest); + SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber); SendQueue(SendQueue& other) = delete; SendQueue(SendQueue&& other) = delete; @@ -108,7 +109,7 @@ private: std::atomic _lastACKSequenceNumber { 0 }; // Last ACKed sequence number - SequenceNumber _currentSequenceNumber; // Last sequence number sent out + SequenceNumber _currentSequenceNumber { 0 }; // Last sequence number sent out std::atomic _atomicCurrentSequenceNumber { 0 }; // Atomic for last sequence number sent out std::atomic _packetSendPeriod { 0 }; // Interval between two packet send event in microseconds, set from CC From dc0e038535485d921ebcf051b72744487e9f2464 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 4 Feb 2016 13:51:43 -0800 Subject: [PATCH 73/79] Add drive selection to the file dialog --- .../resources/qml/dialogs/FileDialog.qml | 19 +++++++++++++++++++ libraries/ui/src/FileDialogHelper.cpp | 8 ++++++++ libraries/ui/src/FileDialogHelper.h | 1 + tests/ui/qml/main.qml | 8 +------- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index a482b76c70..10dd4897e3 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -36,6 +36,7 @@ ModalWindow { // Set from OffscreenUi::getOpenFile() property int options; // <-- FIXME unused + property bool selectDirectory: false; property bool showHidden: false; // FIXME implement @@ -43,10 +44,18 @@ ModalWindow { property bool saveDialog: false; property var helper: fileDialogHelper property alias model: fileTableView.model + property var drives: helper.drives() signal selectedFile(var file); signal canceled(); + Component.onCompleted: { + console.log("Helper " + helper + " drives " + drives) + drivesSelector.onCurrentTextChanged.connect(function(){ + root.dir = helper.pathToUrl(drivesSelector.currentText); + }) + } + Rectangle { anchors.fill: parent color: "white" @@ -78,6 +87,16 @@ ModalWindow { size: 32 onClicked: d.navigateHome(); } + + VrControls.ComboBox { + id: drivesSelector + width: 48 + height: homeButton.height + model: drives + visible: drives.length > 1 + currentIndex: 0 + + } } TextField { diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index 56b9695444..82ad877573 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -95,3 +95,11 @@ bool FileDialogHelper::urlIsWritable(const QUrl& url) { // No file, get the parent directory and check if writable return QFileInfo(fileInfo.absoluteDir().absolutePath()).isWritable(); } + +QStringList FileDialogHelper::drives() { + QStringList result; + for (const auto& drive : QDir::drives()) { + result << drive.absolutePath(); + } + return result; +} diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index 0142473533..2119b77917 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -47,6 +47,7 @@ public: Q_INVOKABLE QUrl home(); Q_INVOKABLE QStringList standardPath(StandardLocation location); + Q_INVOKABLE QStringList drives(); Q_INVOKABLE QString urlToPath(const QUrl& url); Q_INVOKABLE bool urlIsDir(const QUrl& url); Q_INVOKABLE bool urlIsFile(const QUrl& url); diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index d20b580b5a..a55f042227 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -118,13 +118,7 @@ ApplicationWindow { Button { text: "Open File" property var builder: Component { - FileDialog { - folder: "file:///C:/users/bdavis"; - filterModel: ListModel { - ListElement { text: "Javascript Files (*.js)"; filter: "*.js" } - ListElement { text: "All Files (*.*)"; filter: "*.*" } - } - } + FileDialog { } } onClicked: { From 8ca8550f2643b23fcb61831cfdf7d5efe26c84e7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Feb 2016 17:56:07 -0800 Subject: [PATCH 74/79] MyAvatar: Standing Takeoff and In-Air Animations Now there are two sets of of jump takeoff and in-air animations. * Run - Used when the character jumps or falls with a small forward velocity. * Standing - Used when the character jumps or falls in-place or backward. CharacterController * increased takeoff duration to 250 ms * increased takeoff to fly duration to 1100 ms * added standing jump and in-air animations * added 250 milisecond delay between ground and hover, to prevent going into hover when walking over cracks. * take-off to in-air transitions now use the new snapshotPrev interp type for a smoother tweening. --- .../defaultAvatar_full/avatar-animation.json | 154 ++++++++++++++---- libraries/animation/src/Rig.cpp | 53 ++++-- libraries/animation/src/Rig.h | 2 +- libraries/physics/src/CharacterController.cpp | 51 +++--- libraries/physics/src/CharacterController.h | 7 +- 5 files changed, 200 insertions(+), 67 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index a4e39969b4..8980abf740 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -248,8 +248,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -266,8 +268,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -283,8 +287,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -300,8 +306,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -317,8 +325,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -334,8 +344,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -351,8 +363,10 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -368,8 +382,10 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, - { "var": "isTakeoff", "state": "takeoff" }, - { "var": "isInAir", "state": "inAir" } + { "var": "isTakeoffStand", "state": "takeoffStand" }, + { "var": "isTakeoffRun", "state": "takeoffRun" }, + { "var": "isInAirStand", "state": "inAirStand" }, + { "var": "isInAirRun", "state": "inAirRun" } ] }, { @@ -407,18 +423,38 @@ ] }, { - "id": "takeoff", + "id": "takeoffStand", "interpTarget": 0, "interpDuration": 6, "transitions": [ { "var": "isAway", "state": "awayIntro" }, - { "var": "isNotTakeoff", "state": "inAir" } + { "var": "isNotTakeoff", "state": "inAirStand" } ] }, { - "id": "inAir", + "id": "takeoffRun", "interpTarget": 0, "interpDuration": 6, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isNotTakeoff", "state": "inAirRun" } + ] + }, + { + "id": "inAirStand", + "interpTarget": 0, + "interpDuration": 6, + "interpType": "snapshotPrev", + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isNotInAir", "state": "idle" } + ] + }, + { + "id": "inAirRun", + "interpTarget": 0, + "interpDuration": 6, + "interpType": "snapshotPrev", "transitions": [ { "var": "isAway", "state": "awayIntro" }, { "var": "isNotInAir", "state": "idle" } @@ -723,19 +759,31 @@ "children": [] }, { - "id": "takeoff", + "id": "takeoffStand", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_takeoff.fbx", - "startFrame": 1.0, - "endFrame": 2.5, + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_takeoff.fbx", + "startFrame": 17.0, + "endFrame": 25.0, "timeScale": 1.0, "loopFlag": false }, "children": [] }, { - "id": "inAir", + "id": "takeoffRun", + "type": "clip", + "data": { + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_takeoff.fbx", + "startFrame": 1.0, + "endFrame": 2.5, + "timeScale": 0.01, + "loopFlag": false + }, + "children": [] + }, + { + "id": "inAirStand", "type": "blendLinear", "data": { "alpha": 0.0, @@ -743,10 +791,10 @@ }, "children": [ { - "id": "inAirPreApex", + "id": "inAirStandPreApex", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_apex.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 0.0, @@ -755,10 +803,56 @@ "children": [] }, { - "id": "inAirApex", + "id": "inAirStandApex", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_apex.fbx", + "startFrame": 1.0, + "endFrame": 1.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "inAirStandPostApex", + "type": "clip", + "data": { + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_standing_apex.fbx", + "startFrame": 2.0, + "endFrame": 2.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + } + ] + }, + { + "id": "inAirRun", + "type": "blendLinear", + "data": { + "alpha": 0.0, + "alphaVar": "inAirAlpha" + }, + "children": [ + { + "id": "inAirRunPreApex", + "type": "clip", + "data": { + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 0.0, + "endFrame": 0.0, + "timeScale": 0.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "inAirRunApex", + "type": "clip", + "data": { + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", "startFrame": 6.0, "endFrame": 6.0, "timeScale": 1.0, @@ -767,10 +861,10 @@ "children": [] }, { - "id": "inAirPostApex", + "id": "inAirRunPostApex", "type": "clip", "data": { - "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", "startFrame": 11.0, "endFrame": 11.0, "timeScale": 1.0, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2ea9d782d5..5d8b6d8fc4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -627,6 +627,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // Skip hystersis timer for jump transitions. if (_desiredState == RigRole::Takeoff) { _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; + } else if (_state == RigRole::Takeoff && _desiredState == RigRole::InAir) { + _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; } else if (_state == RigRole::InAir && _desiredState != RigRole::InAir) { _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; } @@ -679,9 +681,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - _animVars.set("isTakeoff", false); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); - _animVars.set("isInAir", false); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); } } else if (_state == RigRole::Turn) { @@ -703,9 +707,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - _animVars.set("isTakeoff", false); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); - _animVars.set("isInAir", false); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); } else if (_state == RigRole::Idle ) { @@ -720,9 +726,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - _animVars.set("isTakeoff", false); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); - _animVars.set("isInAir", false); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); } else if (_state == RigRole::Hover) { @@ -737,9 +745,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", true); _animVars.set("isNotFlying", false); - _animVars.set("isTakeoff", false); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); - _animVars.set("isInAir", false); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", true); } else if (_state == RigRole::Takeoff) { @@ -754,9 +764,19 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - _animVars.set("isTakeoff", true); + + bool takeOffRun = forwardSpeed > 0.1f; + if (takeOffRun) { + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", true); + } else { + _animVars.set("isTakeoffStand", true); + _animVars.set("isTakeoffRun", false); + } + _animVars.set("isNotTakeoff", false); - _animVars.set("isInAir", true); + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", false); _animVars.set("isNotInAir", false); } else if (_state == RigRole::InAir) { @@ -771,9 +791,18 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - _animVars.set("isTakeoff", false); + _animVars.set("isTakeoffStand", false); + _animVars.set("isTakeoffRun", false); _animVars.set("isNotTakeoff", true); - _animVars.set("isInAir", true); + + bool inAirRun = forwardSpeed > 0.1f; + if (inAirRun) { + _animVars.set("isInAirStand", false); + _animVars.set("isInAirRun", true); + } else { + _animVars.set("isInAirStand", true); + _animVars.set("isInAirRun", false); + } _animVars.set("isNotInAir", false); // compute blend based on velocity diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index a360140b16..e4668d6c2a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -301,7 +301,7 @@ public: std::map _origRoleAnimations; std::vector _prefetchedAnimations; - bool _lastEnableInverseKinematics { false }; + bool _lastEnableInverseKinematics { true }; bool _enableInverseKinematics { true }; private: diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index d16c406658..2b2496345b 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -60,9 +60,10 @@ CharacterController::CharacterController() { _jumpSpeed = JUMP_SPEED; _state = State::Hover; _isPushingUp = false; - _jumpButtonDownStart = 0; + _rayHitStartTime = 0; + _takeoffToInAirStartTime = 0; + _jumpButtonDownStartTime = 0; _jumpButtonDownCount = 0; - _takeoffToInAirStart = 0; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity(); @@ -417,6 +418,8 @@ glm::vec3 CharacterController::getLinearVelocity() const { void CharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { + quint64 now = usecTimestampNow(); + // slam body to where it is supposed to be _rigidBody->setWorldTransform(_characterBodyTransform); btVector3 velocity = _rigidBody->getLinearVelocity(); @@ -432,27 +435,30 @@ void CharacterController::preSimulation() { btScalar rayLength = _radius + MAX_FALL_HEIGHT; btVector3 rayEnd = rayStart - rayLength * _currentUp; + const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; + const btScalar MIN_HOVER_HEIGHT = 2.5f; + const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; + const btScalar MAX_WALKING_SPEED = 2.5f; + const quint64 RAY_HIT_START_PERIOD = 500 * MSECS_PER_SECOND; + ClosestNotMe rayCallback(_rigidBody); rayCallback.m_closestHitFraction = 1.0f; _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); - if (rayCallback.hasHit()) { + bool rayHasHit = rayCallback.hasHit(); + if (rayHasHit) { + _rayHitStartTime = now; _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; + } else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { + rayHasHit = true; } else { _floorDistance = FLT_MAX; } - const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; - const btScalar MIN_HOVER_HEIGHT = 2.5f; - const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - const btScalar MAX_WALKING_SPEED = 2.5f; - - quint64 now = usecTimestampNow(); - // record a time stamp when the jump button was first pressed. if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { if (_pendingFlags & PENDING_FLAG_JUMP) { - _jumpButtonDownStart = now; + _jumpButtonDownStartTime = now; _jumpButtonDownCount++; } } @@ -462,19 +468,22 @@ void CharacterController::preSimulation() { switch (_state) { case State::Ground: - if (!rayCallback.hasHit() && !_hasSupport) { - SET_STATE(State::Hover, "no ground"); - } else if (_pendingFlags & PENDING_FLAG_JUMP) { - _takeOffJumpButtonID = _jumpButtonDownCount; + if (!rayHasHit && !_hasSupport) { + SET_STATE(State::Hover, "no ground detected"); + } else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) { + _takeoffJumpButtonID = _jumpButtonDownCount; + _takeoffToInAirStartTime = now; SET_STATE(State::Takeoff, "jump pressed"); + } else if (rayHasHit && !_hasSupport && _floorDistance > JUMP_PROXIMITY_THRESHOLD) { + SET_STATE(State::InAir, "falling"); } break; case State::Takeoff: - if (!rayCallback.hasHit() && !_hasSupport) { + if (!rayHasHit && !_hasSupport) { SET_STATE(State::Hover, "no ground"); - } else if ((now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { + } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { SET_STATE(State::InAir, "takeoff done"); - _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; + _takeoffToInAirStartTime = now + USECS_PER_SECOND * 86500.0f; velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } @@ -482,9 +491,9 @@ void CharacterController::preSimulation() { case State::InAir: { if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { SET_STATE(State::Ground, "hit ground"); - } else if (jumpButtonHeld && (_takeOffJumpButtonID != _jumpButtonDownCount)) { + } else if (jumpButtonHeld && (_takeoffJumpButtonID != _jumpButtonDownCount)) { SET_STATE(State::Hover, "double jump button"); - } else if (jumpButtonHeld && (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD) { + } else if (jumpButtonHeld && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { SET_STATE(State::Hover, "jump button held"); } break; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index bb4a135ca3..86ef350812 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -114,10 +114,11 @@ protected: glm::vec3 _boxScale; // used to compute capsule shape - quint64 _takeoffToInAirStart; - quint64 _jumpButtonDownStart; + quint64 _rayHitStartTime; + quint64 _takeoffToInAirStartTime; + quint64 _jumpButtonDownStartTime; quint32 _jumpButtonDownCount; - quint32 _takeOffJumpButtonID; + quint32 _takeoffJumpButtonID; btScalar _halfHeight; btScalar _radius; From 51189cfc503aea5d8646723ad13b971d30fe8728 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Feb 2016 17:56:41 -0800 Subject: [PATCH 75/79] AnimInverseKinematics: opened up UpLeg and Leg constraints This improves the quality of the jump animations, while IK is enabled. --- .../animation/src/AnimInverseKinematics.cpp | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 9f08ce455a..dc589a5f05 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -590,17 +590,24 @@ void AnimInverseKinematics::initConstraints() { stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); stConstraint->setTwistLimits(-PI / 4.0f, PI / 4.0f); - // these directions are approximate swing limits in root-frame - // NOTE: they don't need to be normalized std::vector swungDirections; - swungDirections.push_back(glm::vec3(mirror * 0.25f, 0.0f, 1.0f)); - swungDirections.push_back(glm::vec3(mirror * -0.5f, 0.0f, 1.0f)); - swungDirections.push_back(glm::vec3(mirror * -1.0f, 0.0f, 1.0f)); - swungDirections.push_back(glm::vec3(mirror * -1.0f, 0.0f, 0.0f)); - swungDirections.push_back(glm::vec3(mirror * -0.5f, -0.5f, -1.0f)); - swungDirections.push_back(glm::vec3(mirror * 0.0f, -0.75f, -1.0f)); - swungDirections.push_back(glm::vec3(mirror * 0.25f, -1.0f, 0.0f)); - swungDirections.push_back(glm::vec3(mirror * 0.25f, -1.0f, 1.0f)); + float deltaTheta = PI / 4.0f; + float theta = 0.0f; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.25f, sin(theta))); + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.0f, sin(theta))); + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), -0.25f, sin(theta))); // posterior + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.0f, sin(theta))); + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.25f, sin(theta))); + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.5f, sin(theta))); + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.5f, sin(theta))); // anterior + theta += deltaTheta; + swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.5f, sin(theta))); // rotate directions into joint-frame glm::quat invAbsoluteRotation = glm::inverse(absolutePoses[i].rot); @@ -755,7 +762,7 @@ void AnimInverseKinematics::initConstraints() { // we determine the max/min angles by rotating the swing limit lines from parent- to child-frame // then measure the angles to swing the yAxis into alignment const float MIN_KNEE_ANGLE = 0.0f; - const float MAX_KNEE_ANGLE = 3.0f * PI / 4.0f; + const float MAX_KNEE_ANGLE = 7.0f * PI / 8.0f; glm::quat invReferenceRotation = glm::inverse(referenceRotation); glm::vec3 minSwingAxis = invReferenceRotation * glm::angleAxis(MIN_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y; glm::vec3 maxSwingAxis = invReferenceRotation * glm::angleAxis(MAX_KNEE_ANGLE, hingeAxis) * Vectors::UNIT_Y; From bfeace78f71b755970013ee1d5ed7336107ae47f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Feb 2016 18:51:48 -0800 Subject: [PATCH 76/79] AnimInverseKinematics: warning fixes --- .../animation/src/AnimInverseKinematics.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index dc589a5f05..92d8240510 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -593,21 +593,21 @@ void AnimInverseKinematics::initConstraints() { std::vector swungDirections; float deltaTheta = PI / 4.0f; float theta = 0.0f; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.25f, sin(theta))); + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta))); theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.0f, sin(theta))); + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.0f, sinf(theta))); theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), -0.25f, sin(theta))); // posterior + swungDirections.push_back(glm::vec3(mirror * cosf(theta), -0.25f, sinf(theta))); // posterior theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.0f, sin(theta))); + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.0f, sinf(theta))); theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.25f, sin(theta))); + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.25f, sinf(theta))); theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.5f, sin(theta))); + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta))); theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.5f, sin(theta))); // anterior + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta))); // anterior theta += deltaTheta; - swungDirections.push_back(glm::vec3(mirror * cos(theta), 0.5f, sin(theta))); + swungDirections.push_back(glm::vec3(mirror * cosf(theta), 0.5f, sinf(theta))); // rotate directions into joint-frame glm::quat invAbsoluteRotation = glm::inverse(absolutePoses[i].rot); From f13e31c7fc34b3ef985aec622d1cfc3e538ae06c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Feb 2016 10:53:58 -0800 Subject: [PATCH 77/79] CharacterController: removed unnecessary code. --- libraries/physics/src/CharacterController.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 2b2496345b..1f61bb9a59 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -483,7 +483,6 @@ void CharacterController::preSimulation() { SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { SET_STATE(State::InAir, "takeoff done"); - _takeoffToInAirStartTime = now + USECS_PER_SECOND * 86500.0f; velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } From 47694b90ee68ceb5264b74a58a2ebbd2a6091c4c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 5 Feb 2016 11:47:21 -0800 Subject: [PATCH 78/79] Fix inverted boolean that was preventing other people's avatars from mouth-animating. --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 786e409d92..b67d176852 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -214,7 +214,7 @@ void Avatar::simulate(float deltaTime) { Head* head = getHead(); head->setPosition(headPosition); head->setScale(getUniformScale()); - head->simulate(deltaTime, false, _shouldAnimate); + head->simulate(deltaTime, false, !_shouldAnimate); } } From 39bb68931fd7759b4a25dbcb87933e0be5e8e4a6 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 5 Feb 2016 15:48:34 -0800 Subject: [PATCH 79/79] updated gun position --- unpublishedScripts/hiddenEntityReset.js | 44 ++++++++++++------------- unpublishedScripts/masterReset.js | 44 ++++++++++++------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 903388bb43..4a64b34510 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -299,35 +299,35 @@ }, dynamic: true, userData: JSON.stringify({ - wearable: { - joints: { - RightHand: [{ - x: 0.07079616189002991, - y: 0.20177987217903137, - z: 0.06374628841876984 + "wearable": { + "joints": { + "RightHand": [{ + "x": 0.07079616189002991, + "y": 0.20177987217903137, + "z": 0.06374628841876984 }, { - x: -0.5863648653030396, - y: -0.46007341146469116, - z: 0.46949487924575806, - w: -0.4733745753765106 + "x": -0.5863648653030396, + "y": -0.46007341146469116, + "z": 0.46949487924575806, + "w": -0.4733745753765106 }], - LeftHand: [{ - x: 0.1802254319190979, - y: 0.13442856073379517, - z: 0.08504903316497803 + "LeftHand": [{ + "x": 0.0012094751000404358, + "y": 0.1991066336631775, + "z": 0.079972043633461 }, { - x: 0.2198076844215393, - y: -0.7377811074256897, - z: 0.2780133783817291, - w: 0.574519157409668 + "x": 0.29249316453933716, + "y": -0.6115763187408447, + "z": 0.5668558478355408, + "w": 0.46807748079299927 }] } }, - grabbableKey: { - invertSolidWhileHeld: true + "grabbableKey": { + "invertSolidWhileHeld": true }, - resetMe: { - resetMe: true + "resetMe": { + "resetMe": true } }) }); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 306ae294fd..c5669b9ac0 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -283,35 +283,35 @@ MasterReset = function() { damping: 0.5, collisionSoundURL: "http://hifi-content.s3.amazonaws.com/james/pistol/sounds/drop.wav", userData: JSON.stringify({ - wearable: { - joints: { - RightHand: [{ - x: 0.07079616189002991, - y: 0.20177987217903137, - z: 0.06374628841876984 + "wearable": { + "joints": { + "RightHand": [{ + "x": 0.07079616189002991, + "y": 0.20177987217903137, + "z": 0.06374628841876984 }, { - x: -0.5863648653030396, - y: -0.46007341146469116, - z: 0.46949487924575806, - w: -0.4733745753765106 + "x": -0.5863648653030396, + "y": -0.46007341146469116, + "z": 0.46949487924575806, + "w": -0.4733745753765106 }], - LeftHand: [{ - x: 0.1802254319190979, - y: 0.13442856073379517, - z: 0.08504903316497803 + "LeftHand": [{ + "x": 0.0012094751000404358, + "y": 0.1991066336631775, + "z": 0.079972043633461 }, { - x: 0.2198076844215393, - y: -0.7377811074256897, - z: 0.2780133783817291, - w: 0.574519157409668 + "x": 0.29249316453933716, + "y": -0.6115763187408447, + "z": 0.5668558478355408, + "w": 0.46807748079299927 }] } }, - grabbableKey: { - invertSolidWhileHeld: true + "grabbableKey": { + "invertSolidWhileHeld": true }, - resetMe: { - resetMe: true + "resetMe": { + "resetMe": true } }) });