From f57f3c11386a8b474acd4cea790542f8e342625d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 2 Nov 2015 15:25:42 -0800 Subject: [PATCH 01/46] Disabled writing to depth buffer for particles and no longer sorting the, --- .../src/RenderableParticleEffectEntityItem.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a7bdffc020..bc82159fbd 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -204,8 +204,6 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // sort particles back to front // NOTE: this is view frustum might be one frame out of date. auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); - ::zSortAxis = frustum->getDirection(); - qSort(particleDetails.begin(), particleDetails.end(), zSort); // allocate vertices _vertices.clear(); @@ -312,7 +310,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { if (!_untexturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setDepthTest(true, false, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); @@ -324,7 +322,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { if (!_texturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setDepthTest(true, false, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); From 0ed868077d23b851aae8dbfb6b370df2f925e2e3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 10:02:51 -0800 Subject: [PATCH 02/46] Added additive blending as option for particle effect --- .../RenderableParticleEffectEntityItem.cpp | 34 +++++++++++++++---- .../src/RenderableParticleEffectEntityItem.h | 6 ++-- .../entities/src/EntityItemProperties.cpp | 10 ++++++ libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + .../entities/src/ParticleEffectEntityItem.cpp | 11 +++++- .../entities/src/ParticleEffectEntityItem.h | 9 ++++- 7 files changed, 61 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index bc82159fbd..db0972a180 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -114,8 +114,7 @@ namespace render { } } -gpu::PipelinePointer RenderableParticleEffectEntityItem::_texturedPipeline; -gpu::PipelinePointer RenderableParticleEffectEntityItem::_untexturedPipeline; + EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -203,8 +202,17 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // sort particles back to front // NOTE: this is view frustum might be one frame out of date. + auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); + // No need to sort if we're doing additive blending + if (_additiveBlending != true) { + ::zSortAxis = frustum->getDirection(); + qSort(particleDetails.begin(), particleDetails.end(), zSort); + } + + + // allocate vertices _vertices.clear(); @@ -307,12 +315,21 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { } void RenderableParticleEffectEntityItem::createPipelines() { + bool writeToDepthBuffer = false; + gpu::State::BlendArg destinationColorBlendArg; + if (_additiveBlending) { + destinationColorBlendArg = gpu::State::ONE; + } + else { + destinationColorBlendArg = gpu::State::INV_SRC_ALPHA; + writeToDepthBuffer = true; + } if (!_untexturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - gpu::State::ONE, gpu::State::FACTOR_ALPHA, + destinationColorBlendArg, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(untextured_particle_vert))); auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(untextured_particle_frag))); @@ -322,13 +339,18 @@ void RenderableParticleEffectEntityItem::createPipelines() { if (!_texturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, false, gpu::LESS_EQUAL); + + + bool writeToDepthBuffer = !_additiveBlending; + qDebug() << "ADDITIVE BLENDING" << _additiveBlending; + state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, + destinationColorBlendArg, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert))); auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + } } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 5d69d19026..678f7eb904 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -38,11 +38,11 @@ protected: uint32_t rgba; }; - static void createPipelines(); + void createPipelines(); std::vector _vertices; - static gpu::PipelinePointer _untexturedPipeline; - static gpu::PipelinePointer _texturedPipeline; + gpu::PipelinePointer _untexturedPipeline; + gpu::PipelinePointer _texturedPipeline; render::ScenePointer _scene; NetworkTexturePointer _texture; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 002f1bb527..4f5d256969 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -195,6 +195,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); + CHECK_PROPERTY_CHANGE(PROP_ADDITIVE_BLENDING, additiveBlending); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); @@ -351,6 +352,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ADDITIVE_BLENDING, additiveBlending); + } // Models only @@ -502,6 +505,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(additiveBlending, bool, setAdditiveBlending); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel); @@ -650,6 +654,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float); + ADD_PROPERTY_TO_MAP(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); @@ -959,6 +964,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); + APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, properties.getAdditiveBlending()); } if (properties.getType() == EntityTypes::Zone) { @@ -1241,6 +1247,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); } if (properties.getType() == EntityTypes::Zone) { @@ -1579,6 +1586,9 @@ QList EntityItemProperties::listChangedProperties() { if (alphaFinishChanged()) { out += "alphaFinish"; } + if (additiveBlendingChanged()) { + out += "additiveBlending"; + } if (modelURLChanged()) { out += "modelURL"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b95f4d35f4..84a5aeca5d 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -159,6 +159,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD); DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); + DEFINE_PROPERTY(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool, ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 65060c8d45..b11d4d738b 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -142,6 +142,7 @@ enum EntityPropertyList { PROP_POLAR_FINISH, PROP_AZIMUTH_START, PROP_AZIMUTH_FINISH, + PROP_ADDITIVE_BLENDING, PROP_ANIMATION_LOOP, PROP_ANIMATION_FIRST_FRAME, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index ddd79375b3..00ff0ed72a 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -94,6 +94,7 @@ const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f; const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS; const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; +const bool ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING = false; EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -121,7 +122,8 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _alphaMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), _alphaFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), _particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)), - _particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) + _particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) , + _additiveBlending(DEFAULT_ADDITIVE_BLENDING) { _type = EntityTypes::ParticleEffect; @@ -355,6 +357,8 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(additiveBlending, getAdditiveBlending); + return properties; } @@ -392,6 +396,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(additiveBlending, setAdditiveBlending); if (somethingChanged) { bool wantDebug = false; @@ -435,6 +440,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch if (args.bitstreamVersion < VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) { // OLD PROP_EMIT_VELOCITY FAKEOUT SKIP_ENTITY_PROPERTY(PROP_EMIT_SPEED, glm::vec3); + SKIP_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool) } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_MODIFICATIONS) { @@ -481,6 +487,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); + READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); } return bytesRead; @@ -520,6 +527,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_POLAR_FINISH; requestedProperties += PROP_AZIMUTH_START; requestedProperties += PROP_AZIMUTH_FINISH; + requestedProperties += PROP_ADDITIVE_BLENDING; return requestedProperties; } @@ -562,6 +570,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); + APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, getAdditiveBlending()); } bool ParticleEffectEntityItem::isEmittingParticles() const { diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 029d65cfc0..6560a7bc33 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -209,6 +209,12 @@ public: } } + static const bool DEFAULT_ADDITIVE_BLENDING; + bool getAdditiveBlending() const { return _additiveBlending; } + void setAdditiveBlending(bool additiveBlending) { + _additiveBlending = additiveBlending; + } + protected: bool isAnimatingSomething() const; @@ -219,7 +225,6 @@ protected: void extendBounds(const glm::vec3& point); void integrateParticle(quint32 index, float deltaTime); quint32 getLivingParticleCount() const; - // the properties of this entity rgbColor _color; xColor _colorStart = DEFAULT_COLOR; @@ -284,6 +289,8 @@ protected: // bounding volume glm::vec3 _particleMaxBound; glm::vec3 _particleMinBound; + + bool _additiveBlending; }; #endif // hifi_ParticleEffectEntityItem_h From 777fbe6d834ce8bbdd5f810a42714a7d5af850bc Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 12:43:41 -0800 Subject: [PATCH 03/46] moved additive_blending flag to proper location --- libraries/entities/src/EntityPropertyFlags.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b11d4d738b..e5fa2983e2 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -142,7 +142,6 @@ enum EntityPropertyList { PROP_POLAR_FINISH, PROP_AZIMUTH_START, PROP_AZIMUTH_FINISH, - PROP_ADDITIVE_BLENDING, PROP_ANIMATION_LOOP, PROP_ANIMATION_FIRST_FRAME, @@ -150,6 +149,8 @@ enum EntityPropertyList { PROP_ANIMATION_HOLD, PROP_ANIMATION_START_AUTOMATICALLY, + PROP_ADDITIVE_BLENDING, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, From 0c8a7129357fb3c5a2d46ff89ed735142b3d3af0 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 15:36:11 -0800 Subject: [PATCH 04/46] removed comment --- .../entities-renderer/src/RenderableParticleEffectEntityItem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index db0972a180..ddeccc141c 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -342,7 +342,6 @@ void RenderableParticleEffectEntityItem::createPipelines() { bool writeToDepthBuffer = !_additiveBlending; - qDebug() << "ADDITIVE BLENDING" << _additiveBlending; state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, destinationColorBlendArg, gpu::State::FACTOR_ALPHA, From 60f35ce8c7aec8eb613ad5000544bf968c80e089 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 15:46:58 -0800 Subject: [PATCH 05/46] discarding pixel if alpha is below threshold --- libraries/entities-renderer/src/textured_particle.slf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 58a3103323..995f0db824 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -18,5 +18,8 @@ out vec4 outFragColor; void main(void) { vec4 color = texture(colorMap, _texCoord0); + if (color.a < 0.1) { + discard; + } outFragColor = color * _color; } From 470f814776e4e183f73e98bb6c74edb3228eb6ef Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 16:39:16 -0800 Subject: [PATCH 06/46] Fixed glitches with large particles being sorted along wrong axis --- .../src/RenderableParticleEffectEntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index ddeccc141c..2d0c40ac56 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -218,10 +218,10 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // build vertices from particle positions and radiuses glm::vec3 frustumPosition = frustum->getPosition(); + glm::vec3 dir = frustum->getDirection(); for (auto&& particle : particleDetails) { - glm::vec3 particleDirection = particle.position - frustumPosition; - glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), particleDirection)); - glm::vec3 up = glm::normalize(glm::cross(right, particleDirection)); + glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir)); + glm::vec3 up = glm::normalize(glm::cross(right, dir)); glm::vec3 upOffset = up * particle.radius; glm::vec3 rightOffset = right * particle.radius; From 31fe5fa953d9bdaf210d7f1309e921539f3c9917 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 17:15:10 -0800 Subject: [PATCH 07/46] Bump version num, use different shader for additive blending effect --- .../RenderableParticleEffectEntityItem.cpp | 10 +++++++- .../src/textured_particle.slf | 3 --- .../src/textured_particle_alpha_discard.slf | 25 +++++++++++++++++++ .../entities/src/ParticleEffectEntityItem.cpp | 4 ++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 6 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 libraries/entities-renderer/src/textured_particle_alpha_discard.slf diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 2d0c40ac56..9307aee410 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -23,6 +23,7 @@ #include "untextured_particle_frag.h" #include "textured_particle_vert.h" #include "textured_particle_frag.h" +#include "textured_particle_alpha_discard_frag.h" class ParticlePayload { public: @@ -347,7 +348,14 @@ void RenderableParticleEffectEntityItem::createPipelines() { destinationColorBlendArg, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert))); - auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); + gpu::ShaderPointer fragShader; + if (_additiveBlending) { + fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); + } + else { + //If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts + fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag))); + } auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 995f0db824..58a3103323 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -18,8 +18,5 @@ out vec4 outFragColor; void main(void) { vec4 color = texture(colorMap, _texCoord0); - if (color.a < 0.1) { - discard; - } outFragColor = color * _color; } diff --git a/libraries/entities-renderer/src/textured_particle_alpha_discard.slf b/libraries/entities-renderer/src/textured_particle_alpha_discard.slf new file mode 100644 index 0000000000..389744449a --- /dev/null +++ b/libraries/entities-renderer/src/textured_particle_alpha_discard.slf @@ -0,0 +1,25 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// fragment shader +// +// Copyright 2015 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 +// + +uniform sampler2D colorMap; + +in vec4 _color; +in vec2 _texCoord0; + +out vec4 outFragColor; + +void main(void) { + vec4 color = texture(colorMap, _texCoord0); + if (color.a < 0.1) { + discard; + } + outFragColor = color * _color; +} diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 00ff0ed72a..263d7dce0c 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -440,7 +440,6 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch if (args.bitstreamVersion < VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) { // OLD PROP_EMIT_VELOCITY FAKEOUT SKIP_ENTITY_PROPERTY(PROP_EMIT_SPEED, glm::vec3); - SKIP_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool) } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_MODIFICATIONS) { @@ -487,6 +486,9 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); + } + + if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING) { READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index a46a9693ac..24034ff9b3 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS; + return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 553c12f8e3..82d905bf28 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -145,5 +145,6 @@ const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45; const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; +const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; #endif // hifi_PacketHeaders_h From af0c0e6a1b53329e10cab139887936e3da1c6a76 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 17:32:22 -0800 Subject: [PATCH 08/46] spacing fix --- .../src/RenderableParticleEffectEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 9307aee410..6f3b40eb1e 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -354,7 +354,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { } else { //If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts - fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag))); + fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag))); } auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); From 4e5b806934a188599aa43e0d606c7bae7260fc93 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 4 Nov 2015 16:05:29 -0800 Subject: [PATCH 09/46] particle effect entities no longer intersect with rays --- libraries/entities/src/EntityTreeElement.cpp | 4 +++- libraries/entities/src/ParticleEffectEntityItem.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 57f49f2354..7ada138d02 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -18,6 +18,7 @@ #include "EntityItemProperties.h" #include "EntityTree.h" #include "EntityTreeElement.h" +#include "EntityTypes.h" EntityTreeElement::EntityTreeElement(unsigned char* octalCode) : OctreeElement() { init(octalCode); @@ -591,7 +592,8 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - if (localDistance < distance) { + // Never intersect with particle effect entities + if (localDistance < distance && EntityTypes::getEntityTypeName(entity->getType()) != "ParticleEffect") { distance = localDistance; face = localFace; surfaceNormal = localSurfaceNormal; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 6560a7bc33..e3c5cd895a 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -215,6 +215,8 @@ public: _additiveBlending = additiveBlending; } + virtual bool supportsDetailedRayIntersection() const { return false; } + protected: bool isAnimatingSomething() const; From 20d95080f1c0a6a252e1ee7e4364451c93db672c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 12:03:45 -0800 Subject: [PATCH 10/46] IK fix for avatars exported from Blender This should fix the issue with the hips moving erratically when arm IK is enabled. The main issue is that the IK system assumed that the "Hips" joint was the root of the skeleton. For Blender avatar this is not the case as it inserts an "Armature" node at the root instead. --- libraries/animation/src/AnimInverseKinematics.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 42e9472819..6355426dae 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -156,11 +156,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (pivotIndex == -1) { + if (pivotIndex == -1 || pivotIndex == _hipsIndex) { continue; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == -1) { + if (pivotsParentIndex == -1 || pivotIndex == _hipsIndex) { // TODO?: handle case where tip's parent is root? continue; } @@ -173,7 +173,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != -1 && parentIndex != _hipsIndex) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -295,7 +295,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (parentIndex != -1) { + if (parentIndex != -1 && parentIndex != _hipsIndex) { const glm::quat& targetRotation = target.getRotation(); // compute tip's new parent-relative rotation // Q = Qp * q --> q' = Qp^ * Q From c6b42f5fbc4cbcdd3a75969ddd7a4243493c0a1b Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 5 Nov 2015 18:48:24 -0800 Subject: [PATCH 11/46] Added a second way to create mapping --- examples/controllers/controllerMappings.js | 68 +++++++++++++--------- examples/controllers/rightClickExample.js | 10 ---- 2 files changed, 39 insertions(+), 39 deletions(-) delete mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index a44833f80a..1bcd05a4cc 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -4,68 +4,74 @@ // examples // // Created by Sam Gondelman on 6/2/15 +// Rewritten by Alessandro Signa on 11/05/15 // Copyright 2015 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 // -// This allows to change the input mapping making a sense of how the new mapping works +// This allows to change the input mapping to easly understand of how the new mapping works. +// Two different ways are presented: the first one uses a JSON file to create the mapping, the second one declares the new routes explicitly one at a time. +// You shuold prefer the first method if you have a lot of new routes, the second one if you want to express the action as a function. /* -This function returns a JSON body. It's in charge to modify the mouse/keyboard mapping. The keyboard inputs are mapped directly to actions. +This function returns a JSON body. It's in charge to modify the standard controller and the mouse/keyboard mapping. + +The Standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) +This example will overwrite the mapping of the left axis (Standard.LY, Standard.LX). +It's possible to find all the standard inputs (and their mapping) into standard.json +To try these changes you need a controller, not the keyboard. + +The keyboard/mouse inputs are mapped directly to actions since the keyboard doesn't have its default mapping passing through the Standard controller. If this new mapping contains inputs which are defined in the standard mapping, these will overwrite the old ones(Keyboard.W, Keyboard.RightMouseButton). If this new mapping contains inputs which are not defined in the standard, these will be added to the mapping(Keyboard.M). */ myFirstMapping = function() { return { - "name": "example", + "name": "controllerMapping_First", "channels": [ - { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.M", "to": "Actions.YAW_LEFT" }, - - { "from": "Keyboard.RightMouseButton", "to": "Actions.YAW_RIGHT" } - ] -} -} -/* -This JSON is in charge to modify the Standard controller mapping. -The standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) -These new inputs will overwrite the standard ones. -It's possible to find all the standard inputs (and their mapping) into standard.json -To try the mySecondMapping effect you need a controller, not the keyboard. -*/ -mySecondMapping = function() { -return { - "name": "example2", - "channels": [ { "from": "Standard.LY", "to": "Actions.Yaw" }, { "from": "Standard.LX", "to": "Actions.Yaw" }, + + { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.M", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.LeftMouseButton", "to": "Actions.Up" } + ] } } -var tryFirst = true; + +var firstWay = true; var mapping; -if(tryFirst){ +var MAPPING_NAME; + + +if(firstWay){ var myFirstMappingJSON = myFirstMapping(); print('myfirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); + mapping.enable(); }else{ - var mySecondMappingJSON = mySecondMapping(); - print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); - mapping = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); + MAPPING_NAME = "controllerMapping_Second"; + var mapping2 = Controller.newMapping(MAPPING_NAME); + mapping2.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); + }); + mapping2.from(Controller.Standard.LX).to(Controller.Actions.Yaw); + Controller.enableMapping(MAPPING_NAME); } -mapping.enable(); /* -//-----------------some info prints----------------------- +//-----------------some info prints that you would like to enable----------------------- Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); @@ -94,5 +100,9 @@ Controller.hardwareChanged.connect(function () { Script.scriptEnding.connect(function () { - mapping.disable(); + if(firstWay){ + mapping.disable(); + } else { + Controller.disableMapping(MAPPING_NAME); + } }); \ No newline at end of file diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js deleted file mode 100644 index c3e6ea8f3d..0000000000 --- a/examples/controllers/rightClickExample.js +++ /dev/null @@ -1,10 +0,0 @@ -var MAPPING_NAME = "com.highfidelity.rightClickExample"; -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { - print("Keyboard.RightMouseClicked"); -}); -Controller.enableMapping(MAPPING_NAME); - -Script.scriptEnding.connect(function () { - Controller.disableMapping(MAPPING_NAME); -}); \ No newline at end of file From 4ed8a1e5d116a663bdf3e0642e21a239548999d1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 6 Nov 2015 09:25:53 -0800 Subject: [PATCH 12/46] changes to send an update to entity-server when AvatarActionHold releases an entity --- interface/src/avatar/AvatarActionHold.h | 2 +- libraries/entities/src/EntityTree.cpp | 18 ++++++++++++++++-- libraries/entities/src/EntityTree.h | 1 + libraries/physics/src/EntityMotionState.cpp | 11 ++++++++--- libraries/physics/src/EntityMotionState.h | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 6badf97e9e..95623d5046 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressLocationEdits() { return true; } + virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); } private: static const uint16_t holdVersion; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 24c13ae28e..8e32158362 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -611,6 +611,16 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit return foundEntity; } +void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties) { + if (properties.simulationOwnerChanged()) { + int simIndex = changedProperties.indexOf("simulationOwner"); + if (simIndex >= 0) { + SimulationOwner simOwner = properties.getSimulationOwner(); + changedProperties[simIndex] = QString("simulationOwner:") + QString::number((int)simOwner.getPriority()); + } + } +} + int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -661,7 +671,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { - qCDebug(entities) << "edit" << entityItemID.toString() << properties.listChangedProperties(); + QList changedProperties = properties.listChangedProperties(); + fixupTerseEditLogging(properties, changedProperties); + qCDebug(entities) << "edit" << entityItemID.toString() << changedProperties; } endLogging = usecTimestampNow(); @@ -689,7 +701,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { - qCDebug(entities) << "add" << entityItemID.toString() << properties.listChangedProperties(); + QList changedProperties = properties.listChangedProperties(); + fixupTerseEditLogging(properties, changedProperties); + qCDebug(entities) << "add" << entityItemID.toString() << changedProperties; } endLogging = usecTimestampNow(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1957787a60..c177840199 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -77,6 +77,7 @@ public: virtual bool canProcessVersion(PacketVersion thisVersion) const { return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; } virtual bool handlesEditPacketType(PacketType packetType) const; + void fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties); virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 42aaea33c2..c8a0f87b6d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -79,8 +79,13 @@ EntityMotionState::~EntityMotionState() { assert(!_entity); } -void EntityMotionState::updateServerPhysicsVariables() { +void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { assert(entityTreeIsLocked()); + if (_entity->getSimulatorID() == sessionID) { + // don't slam these values if we are the simulation owner + return; + } + _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); @@ -92,7 +97,7 @@ void EntityMotionState::updateServerPhysicsVariables() { // virtual bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { assert(entityTreeIsLocked()); - updateServerPhysicsVariables(); + updateServerPhysicsVariables(engine->getSessionID()); ObjectMotionState::handleEasyChanges(flags, engine); if (flags & Simulation::DIRTY_SIMULATOR_ID) { @@ -129,7 +134,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) // virtual bool EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { - updateServerPhysicsVariables(); + updateServerPhysicsVariables(engine->getSessionID()); return ObjectMotionState::handleHardAndEasyChanges(flags, engine); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index f3a2e80070..188e7096b9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -28,7 +28,7 @@ public: EntityMotionState(btCollisionShape* shape, EntityItemPointer item); virtual ~EntityMotionState(); - void updateServerPhysicsVariables(); + void updateServerPhysicsVariables(const QUuid& sessionID); virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); From 6d3a79af83e754c107a2f6b488b805395bd0cd2c Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 6 Nov 2015 12:46:08 -0800 Subject: [PATCH 13/46] changed the behaviour of Standard.LY input --- interface/resources/controllers/standard.json | 13 +++++++++++-- interface/src/Application.cpp | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5de188c0b6..8a24543b74 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -14,8 +14,17 @@ ] }, { "from": "Standard.RX", "to": "Actions.Yaw" }, - { "from": "Standard.RY", "when": "!Application.InHMD", "to": "Actions.Pitch" }, - + + { "from": "Standard.RY", + "when": "Application.Grounded", + "to": "Actions.Up", + "filters": + [ + { "type": "deadZone", "min": 0.95 }, + "invert" + ] + }, + { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718d06c6e5..571c6ca6af 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -646,6 +646,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); })); + _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { + return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); + })); userInputMapper->registerDevice(_applicationStateDevice); From c2894749096321502d23b8dc7a986365e6873b86 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 6 Nov 2015 14:25:01 -0800 Subject: [PATCH 14/46] avatr mover --- examples/avatarMover/avatarMover.js | 188 +++++++++++++++++++++ examples/avatarMover/avatarMoverSpawner.js | 6 + 2 files changed, 194 insertions(+) create mode 100644 examples/avatarMover/avatarMover.js create mode 100644 examples/avatarMover/avatarMoverSpawner.js diff --git a/examples/avatarMover/avatarMover.js b/examples/avatarMover/avatarMover.js new file mode 100644 index 0000000000..a47bd01ed5 --- /dev/null +++ b/examples/avatarMover/avatarMover.js @@ -0,0 +1,188 @@ +(function() { + this.defaultRange = 5; + this.acceleration = { + x: 0, + y: 0, + z: 0 + }; + this.onColor = { + red: 77, + green: 11, + blue: 111 + }; + this.offColor = { + red: 200, + green: 0, + blue: 0 + }; + var self = this; + //Default forward direction of mover object + this.forward = { + x: 0, + y: 0, + z: -1 + }; + this.isMoving = false; + this.velocity = { + x: 0, + y: 0, + z: 0 + }; + this.defaultThrust = 500; + this.maxRotMixVal = 0.01; + this.minRotMixVal = this.maxRotMixVal * 0.5; + this.minThrustPercentage = 0.2; + this.userData = {}; + + + this.getUserData = function() { + if (this.properties.userData) { + this.userData = JSON.parse(this.properties.userData); + } + } + + this.updateUserData = function() { + Entities.editEntity(this.entityId, { + userData: JSON.stringify(this.userData) + }); + } + + + this.toggleMover = function() { + if (!this.userData.active) { + this.activate(); + } else if (this.userData.active) { + this.deactivate(); + } + + } + + this.clickReleaseOnEntity = function(entityId, mouseEvent) { + this.entityId = entityId + if (mouseEvent.isLeftButton) { + this.toggleMover(); + } + } + + this.activate = function() { + //activate a light at the movers position + this.properties = Entities.getEntityProperties(this.entityId); + this.getUserData(); + this.userData.active = true; + this.initUserData(); + var lightPos = this.properties.position; + lightPos.y += .1; + this.light = Entities.addEntity({ + type: "Light", + position: lightPos, + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 2 + }, + color: this.onColor, + intensity: 10 + // rotation: {x : 0, y: Math.PI/2, z: 0} + }); + + this.field = Overlays.addOverlay("sphere", { + position: this.properties.position, + size: this.userData.range, + solid: false, + color: { + red: 250, + green: 10, + blue: 10 + }, + }) + + //change color + Entities.editEntity(this.entityId, { + color: this.onColor, + }); + } + + this.initUserData = function() { + this.userData.range = this.userData.range || this.defaultRange; + this.userData.thrust = this.userData.thrust || this.defaultThrust; + this.updateUserData(); + } + + this.updateOverlays = function() { + if (this.field) { + Overlays.editOverlay(this.field, { + size: this.userData.range + }); + } + } + + + this.deactivate = function() { + this.userData.active = false; + this.updateUserData(); + Entities.editEntity(this.entityId, { + color: this.offColor + }); + this.cleanUp(); + } + + this.scriptEnding = function() { + this.cleanUp(); + } + + this.update = function(deltaTime) { + self.properties = Entities.getEntityProperties(self.entityId); + self.getUserData(); + self.updateOverlays(); + if (!self.userData.active) { + return; + } + self.distance = Vec3.distance(MyAvatar.position, self.properties.position); + if (self.distance < self.userData.range) { + self.rotationMixVal = map(self.distance, 0, self.userData.range, self.maxRotMixVal, self.minRotMixVal); + + //We want to extract yaw from rotated object so avatars do not pith or roll, as they will be stuck that way. + self.sanitizedRotation = Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(self.properties.rotation).y, 0); + self.newOrientation = Quat.mix(MyAvatar.orientation, self.sanitizedRotation, self.rotationMixVal); + MyAvatar.orientation = self.newOrientation; + + self.rotatedDir = { + x: self.forward.x, + y: self.forward.y, + z: self.forward.z + }; + self.rotatedDir = Vec3.multiplyQbyV(self.properties.rotation, self.rotatedDir); + + self.thrust = map(self.distance, 0, self.userData.range, self.userData.thrust, self.userData.thrust * self.minThrustPercentage); + self.direction = Vec3.normalize(self.rotatedDir); + self.velocity = Vec3.multiply(self.direction, self.thrust); + MyAvatar.addThrust(Vec3.multiply(self.velocity, deltaTime)); + } + + } + + + this.preload = function(entityId) { + this.entityId = entityId; + } + + this.unload = function() { + Script.update.disconnect(this.update); + this.cleanUp(); + } + + + this.cleanUp = function() { + Entities.deleteEntity(this.light); + Overlays.deleteOverlay(this.field); + } + + function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + } + + Script.scriptEnding.connect(this.scriptEnding); + Script.update.connect(this.update); + +}); \ No newline at end of file diff --git a/examples/avatarMover/avatarMoverSpawner.js b/examples/avatarMover/avatarMoverSpawner.js new file mode 100644 index 0000000000..0bff3b02c0 --- /dev/null +++ b/examples/avatarMover/avatarMoverSpawner.js @@ -0,0 +1,6 @@ +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/arrow.fbx"; + +var avatarMover = Entities.addEntity({ + type: "Model", + modelURL: modelURL +}) \ No newline at end of file From 4513b638dbec8e15149801d1b408fdcdccb2ee98 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 14:40:10 -0800 Subject: [PATCH 15/46] Adding clip serialization implementation, tests --- libraries/recording/src/recording/Clip.cpp | 27 +-- libraries/recording/src/recording/Clip.h | 19 +- libraries/recording/src/recording/Frame.h | 4 + libraries/recording/src/recording/Logging.cpp | 11 + libraries/recording/src/recording/Logging.h | 16 ++ .../recording/src/recording/Recorder.cpp | 2 +- .../src/recording/impl/BufferClip.cpp | 18 +- .../recording/src/recording/impl/BufferClip.h | 10 +- .../recording/src/recording/impl/FileClip.cpp | 196 ++++++++++++++++-- .../recording/src/recording/impl/FileClip.h | 29 ++- tests/recording/CMakeLists.txt | 22 +- tests/recording/src/Constants.h | 2 + tests/recording/src/FrameTests.cpp | 4 + tests/recording/src/FrameTests.h | 3 + tests/recording/src/RecorderTests.cpp | 4 + tests/recording/src/RecorderTests.h | 4 + tests/recording/src/main.cpp | 114 ++++++++++ 17 files changed, 427 insertions(+), 58 deletions(-) create mode 100644 libraries/recording/src/recording/Logging.cpp create mode 100644 libraries/recording/src/recording/Logging.h create mode 100644 tests/recording/src/main.cpp diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index ef59532f09..09acf0579f 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -16,33 +16,34 @@ using namespace recording; Clip::Pointer Clip::fromFile(const QString& filePath) { - return std::make_shared(filePath); + auto result = std::make_shared(filePath); + if (result->frameCount() == 0) { + return Clip::Pointer(); + } + return result; } -void Clip::toFile(Clip::Pointer clip, const QString& filePath) { - // FIXME +void Clip::toFile(const QString& filePath, Clip::Pointer clip) { + FileClip::write(filePath, clip->duplicate()); +} + +Clip::Pointer Clip::newClip() { + return std::make_shared(); } Clip::Pointer Clip::duplicate() { Clip::Pointer result = std::make_shared(); + Locker lock(_mutex); float currentPosition = position(); seek(0); Frame::Pointer frame = nextFrame(); while (frame) { - result->appendFrame(frame); + result->addFrame(frame); + frame = nextFrame(); } seek(currentPosition); return result; } - -#if 0 -Clip::Pointer Clip::fromIODevice(QIODevice * device) { - return std::make_shared(device); -} - -void Clip::fromIODevice(Clip::Pointer clip, QIODevice * device) { -} -#endif \ No newline at end of file diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index ca77ba8969..e7034ef077 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -12,35 +12,44 @@ #include "Forward.h" +#include + #include class QIODevice; namespace recording { -class Clip : public QObject { +class Clip { public: using Pointer = std::shared_ptr; - Clip(QObject* parent = nullptr) : QObject(parent) {} virtual ~Clip() {} Pointer duplicate(); + virtual float duration() const = 0; + virtual size_t frameCount() const = 0; + virtual void seek(float offset) = 0; virtual float position() const = 0; virtual FramePointer peekFrame() const = 0; virtual FramePointer nextFrame() = 0; virtual void skipFrame() = 0; - virtual void appendFrame(FramePointer) = 0; - + virtual void addFrame(FramePointer) = 0; static Pointer fromFile(const QString& filePath); - static void toFile(Pointer clip, const QString& filePath); + static void toFile(const QString& filePath, Pointer clip); + static Pointer newClip(); protected: + using Mutex = std::recursive_mutex; + using Locker = std::unique_lock; + virtual void reset() = 0; + + mutable Mutex _mutex; }; } diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index 2834637a6b..0fb95c4b2e 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -29,6 +29,10 @@ public: float timeOffset { 0 }; QByteArray data; + Frame() {} + Frame(FrameType type, float timeOffset, const QByteArray& data) + : type(type), timeOffset(timeOffset), data(data) {} + static FrameType registerFrameType(const QString& frameTypeName); static QMap getFrameTypes(); static QMap getFrameTypeNames(); diff --git a/libraries/recording/src/recording/Logging.cpp b/libraries/recording/src/recording/Logging.cpp new file mode 100644 index 0000000000..5673e6e175 --- /dev/null +++ b/libraries/recording/src/recording/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 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 "Logging.h" + +Q_LOGGING_CATEGORY(recordingLog, "hifi.recording") diff --git a/libraries/recording/src/recording/Logging.h b/libraries/recording/src/recording/Logging.h new file mode 100644 index 0000000000..a1b28329d7 --- /dev/null +++ b/libraries/recording/src/recording/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 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_Controllers_Logging_h +#define hifi_Controllers_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(recordingLog) + +#endif diff --git a/libraries/recording/src/recording/Recorder.cpp b/libraries/recording/src/recording/Recorder.cpp index a38e4252b9..b2e7399cd4 100644 --- a/libraries/recording/src/recording/Recorder.cpp +++ b/libraries/recording/src/recording/Recorder.cpp @@ -51,7 +51,7 @@ void Recorder::recordFrame(FrameType type, QByteArray frameData) { frame->type = type; frame->data = frameData; frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND; - _clip->appendFrame(frame); + _clip->addFrame(frame); } ClipPointer Recorder::getClip() { diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index 378fee2558..4d5a910d42 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -51,11 +51,15 @@ FramePointer BufferClip::nextFrame() { return result; } -void BufferClip::appendFrame(FramePointer newFrame) { +void BufferClip::addFrame(FramePointer newFrame) { + if (newFrame->timeOffset < 0.0f) { + throw std::runtime_error("Frames may not have negative time offsets"); + } auto currentPosition = position(); seek(newFrame->timeOffset); { Locker lock(_mutex); + _frames.insert(_frames.begin() + _frameIndex, newFrame); } seek(currentPosition); @@ -72,3 +76,15 @@ void BufferClip::reset() { Locker lock(_mutex); _frameIndex = 0; } + +float BufferClip::duration() const { + if (_frames.empty()) { + return 0; + } + return (*_frames.rbegin())->timeOffset; +} + +size_t BufferClip::frameCount() const { + return _frames.size(); +} + diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index 8fd78a9091..b40687a4ec 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -20,25 +20,23 @@ class BufferClip : public Clip { public: using Pointer = std::shared_ptr; - BufferClip(QObject* parent = nullptr) : Clip(parent) {} virtual ~BufferClip() {} + virtual float duration() const override; + virtual size_t frameCount() const override; + virtual void seek(float offset) override; virtual float position() const override; virtual FramePointer peekFrame() const override; virtual FramePointer nextFrame() override; virtual void skipFrame() override; - virtual void appendFrame(FramePointer) override; + virtual void addFrame(FramePointer) override; private: - using Mutex = std::mutex; - using Locker = std::unique_lock; - virtual void reset() override; std::vector _frames; - mutable Mutex _mutex; mutable size_t _frameIndex { 0 }; }; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index 354a97c5db..be7230e3f8 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -8,42 +8,197 @@ #include "FileClip.h" -#include "../Frame.h" - #include +#include +#include +#include + +#include + +#include "../Frame.h" +#include "../Logging.h" + + using namespace recording; -static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t) + 1; +static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t); -FileClip::FileClip(const QString& fileName, QObject* parent) : Clip(parent), _file(fileName) { - auto size = _file.size(); - _map = _file.map(0, size, QFile::MapPrivateOption); +static const QString FRAME_TYPE_MAP = QStringLiteral("frameTypes"); - auto current = _map; +using FrameHeaderList = std::list; +using FrameTranslationMap = QMap; + +FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) { + FrameTranslationMap results; + auto headerObj = doc.object(); + if (headerObj.contains(FRAME_TYPE_MAP)) { + auto frameTypeObj = headerObj[FRAME_TYPE_MAP].toObject(); + auto currentFrameTypes = Frame::getFrameTypes(); + for (auto frameTypeName : frameTypeObj.keys()) { + qDebug() << frameTypeName; + if (!currentFrameTypes.contains(frameTypeName)) { + continue; + } + FrameType currentTypeEnum = currentFrameTypes[frameTypeName]; + FrameType storedTypeEnum = static_cast(frameTypeObj[frameTypeName].toInt()); + results[storedTypeEnum] = currentTypeEnum; + } + } + return results; +} + + +FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) { + using FrameHeader = FileClip::FrameHeader; + FrameHeaderList results; + auto current = start; auto end = current + size; // Read all the frame headers - while (end - current < MINIMUM_FRAME_SIZE) { + // FIXME move to Frame::readHeader? + while (end - current >= MINIMUM_FRAME_SIZE) { FrameHeader header; memcpy(&(header.type), current, sizeof(FrameType)); current += sizeof(FrameType); - memcpy(&(header.timeOffset), current, sizeof(FrameType)); + memcpy(&(header.timeOffset), current, sizeof(float)); current += sizeof(float); memcpy(&(header.size), current, sizeof(uint16_t)); current += sizeof(uint16_t); - header.fileOffset = current - _map; + header.fileOffset = current - start; if (end - current < header.size) { + current = end; break; } - - _frameHeaders.push_back(header); + current += header.size; + results.push_back(header); } + return results; +} + + +FileClip::FileClip(const QString& fileName) : _file(fileName) { + auto size = _file.size(); + bool opened = _file.open(QIODevice::ReadOnly); + if (!opened) { + qCWarning(recordingLog) << "Unable to open file " << fileName; + return; + } + _map = _file.map(0, size, QFile::MapPrivateOption); + if (!_map) { + qCWarning(recordingLog) << "Unable to map file " << fileName; + return; + } + + FrameHeaderList parsedFrameHeaders = parseFrameHeaders(_map, size); + + // Verify that at least one frame exists and that the first frame is a header + if (0 == parsedFrameHeaders.size()) { + qWarning() << "No frames found, invalid file"; + return; + } + + // Grab the file header + { + auto fileHeaderFrameHeader = *parsedFrameHeaders.begin(); + parsedFrameHeaders.pop_front(); + if (fileHeaderFrameHeader.type != Frame::TYPE_HEADER) { + qWarning() << "Missing header frame, invalid file"; + return; + } + + QByteArray fileHeaderData((char*)_map + fileHeaderFrameHeader.fileOffset, fileHeaderFrameHeader.size); + _fileHeader = QJsonDocument::fromBinaryData(fileHeaderData); + } + + // Find the type enum translation map and fix up the frame headers + { + FrameTranslationMap translationMap = parseTranslationMap(_fileHeader); + if (translationMap.empty()) { + qWarning() << "Header missing frame type map, invalid file"; + return; + } + + // Update the loaded headers with the frame data + _frameHeaders.reserve(parsedFrameHeaders.size()); + for (auto& frameHeader : parsedFrameHeaders) { + if (!translationMap.contains(frameHeader.type)) { + continue; + } + frameHeader.type = translationMap[frameHeader.type]; + _frameHeaders.push_back(frameHeader); + } + } +} + +// FIXME move to frame? +bool writeFrame(QIODevice& output, const Frame& frame) { + auto written = output.write((char*)&(frame.type), sizeof(FrameType)); + if (written != sizeof(FrameType)) { + return false; + } + written = output.write((char*)&(frame.timeOffset), sizeof(float)); + if (written != sizeof(float)) { + return false; + } + uint16_t dataSize = frame.data.size(); + written = output.write((char*)&dataSize, sizeof(uint16_t)); + if (written != sizeof(uint16_t)) { + return false; + } + if (dataSize != 0) { + written = output.write(frame.data); + if (written != dataSize) { + return false; + } + } + return true; +} + +bool FileClip::write(const QString& fileName, Clip::Pointer clip) { + qCDebug(recordingLog) << "Writing clip to file " << fileName; + + if (0 == clip->frameCount()) { + return false; + } + + QFile outputFile(fileName); + if (!outputFile.open(QFile::Truncate | QFile::WriteOnly)) { + return false; + } + + Finally closer([&] { outputFile.close(); }); + { + auto frameTypes = Frame::getFrameTypes(); + QJsonObject frameTypeObj; + for (const auto& frameTypeName : frameTypes.keys()) { + frameTypeObj[frameTypeName] = frameTypes[frameTypeName]; + } + + QJsonObject rootObject; + rootObject.insert(FRAME_TYPE_MAP, frameTypeObj); + QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData(); + if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }))) { + return false; + } + } + + clip->seek(0); + for (auto frame = clip->nextFrame(); frame; frame = clip->nextFrame()) { + if (!writeFrame(outputFile, *frame)) { + return false; + } + } + outputFile.close(); + return true; } FileClip::~FileClip() { Locker lock(_mutex); _file.unmap(_map); _map = nullptr; + if (_file.isOpen()) { + _file.close(); + } } void FileClip::seek(float offset) { @@ -72,7 +227,9 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const { const FrameHeader& header = _frameHeaders[frameIndex]; result->type = header.type; result->timeOffset = header.timeOffset; - result->data.insert(0, reinterpret_cast(_map)+header.fileOffset, header.size); + if (header.size) { + result->data.insert(0, reinterpret_cast(_map)+header.fileOffset, header.size); + } } return result; } @@ -99,7 +256,18 @@ void FileClip::reset() { _frameIndex = 0; } -void FileClip::appendFrame(FramePointer) { +void FileClip::addFrame(FramePointer) { throw std::runtime_error("File clips are read only"); } +float FileClip::duration() const { + if (_frameHeaders.empty()) { + return 0; + } + return _frameHeaders.rbegin()->timeOffset; +} + +size_t FileClip::frameCount() const { + return _frameHeaders.size(); +} + diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h index 9b13adc9ef..08eacd8337 100644 --- a/libraries/recording/src/recording/impl/FileClip.h +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -13,6 +13,7 @@ #include "../Clip.h" #include +#include #include @@ -22,22 +23,25 @@ class FileClip : public Clip { public: using Pointer = std::shared_ptr; - FileClip(const QString& file, QObject* parent = nullptr); + FileClip(const QString& file); virtual ~FileClip(); + virtual float duration() const override; + virtual size_t frameCount() const override; + virtual void seek(float offset) override; virtual float position() const override; virtual FramePointer peekFrame() const override; virtual FramePointer nextFrame() override; - virtual void appendFrame(FramePointer) override; virtual void skipFrame() override; + virtual void addFrame(FramePointer) override; -private: - using Mutex = std::mutex; - using Locker = std::unique_lock; + const QJsonDocument& getHeader() { + return _fileHeader; + } - virtual void reset() override; + static bool write(const QString& filePath, Clip::Pointer clip); struct FrameHeader { FrameType type; @@ -46,15 +50,20 @@ private: quint64 fileOffset; }; - using FrameHeaders = std::vector; +private: + + virtual void reset() override; + + + using FrameHeaderVector = std::vector; FramePointer readFrame(uint32_t frameIndex) const; - mutable Mutex _mutex; + QJsonDocument _fileHeader; QFile _file; uint32_t _frameIndex { 0 }; - uchar* _map; - FrameHeaders _frameHeaders; + uchar* _map { nullptr }; + FrameHeaderVector _frameHeaders; }; } diff --git a/tests/recording/CMakeLists.txt b/tests/recording/CMakeLists.txt index f1e130956d..a523947f52 100644 --- a/tests/recording/CMakeLists.txt +++ b/tests/recording/CMakeLists.txt @@ -1,10 +1,16 @@ +set(TARGET_NAME recording-test) +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Test) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") +link_hifi_libraries(shared recording) +copy_dlls_beside_windows_executable() +# FIXME convert to unit tests # Declare dependencies -macro (setup_testcase_dependencies) - # link in the shared libraries - link_hifi_libraries(shared recording) - - copy_dlls_beside_windows_executable() -endmacro () - -setup_hifi_testcase() +#macro (setup_testcase_dependencies) +# # link in the shared libraries +# link_hifi_libraries(shared recording) +# +# copy_dlls_beside_windows_executable() +#endmacro () +#setup_hifi_testcase() diff --git a/tests/recording/src/Constants.h b/tests/recording/src/Constants.h index 37758bfd68..2aa19fe786 100644 --- a/tests/recording/src/Constants.h +++ b/tests/recording/src/Constants.h @@ -11,6 +11,8 @@ #ifndef hifi_Constants_h #define hifi_Constants_h +#include + static const QString HEADER_NAME = "com.highfidelity.recording.Header"; static const QString TEST_NAME = "com.highfidelity.recording.Test"; diff --git a/tests/recording/src/FrameTests.cpp b/tests/recording/src/FrameTests.cpp index f61d45c197..ebd7f90e31 100644 --- a/tests/recording/src/FrameTests.cpp +++ b/tests/recording/src/FrameTests.cpp @@ -8,6 +8,9 @@ #include "FrameTests.h" #include "Constants.h" + +#if 0 + #include "../QTestExtensions.h" #include @@ -27,3 +30,4 @@ void FrameTests::registerFrameTypeTest() { QCOMPARE(backMap[recording::Frame::TYPE_HEADER], HEADER_NAME); } +#endif diff --git a/tests/recording/src/FrameTests.h b/tests/recording/src/FrameTests.h index 04f73532a5..bdf4542846 100644 --- a/tests/recording/src/FrameTests.h +++ b/tests/recording/src/FrameTests.h @@ -10,6 +10,7 @@ #ifndef hifi_FrameTests_h #define hifi_FrameTests_h +#if 0 #include class FrameTests : public QObject { @@ -18,4 +19,6 @@ private slots: void registerFrameTypeTest(); }; +#endif + #endif // hifi_FrameTests_h diff --git a/tests/recording/src/RecorderTests.cpp b/tests/recording/src/RecorderTests.cpp index 76b4b46577..b102a3c931 100644 --- a/tests/recording/src/RecorderTests.cpp +++ b/tests/recording/src/RecorderTests.cpp @@ -8,6 +8,9 @@ #include "RecorderTests.h" #include "Constants.h" + +#if 0 + #include "../QTestExtensions.h" #include @@ -23,3 +26,4 @@ void RecorderTests::recorderTest() { //QCOMPARE(recoreder.isRecording(), false); } +#endif diff --git a/tests/recording/src/RecorderTests.h b/tests/recording/src/RecorderTests.h index 8e97a828a2..9bfd8e2d10 100644 --- a/tests/recording/src/RecorderTests.h +++ b/tests/recording/src/RecorderTests.h @@ -10,6 +10,8 @@ #ifndef hifi_RecorderTests_h #define hifi_RecorderTests_h +#if 0 + #include class RecorderTests : public QObject { @@ -19,3 +21,5 @@ private slots: }; #endif + +#endif diff --git a/tests/recording/src/main.cpp b/tests/recording/src/main.cpp new file mode 100644 index 0000000000..836d8b5ac1 --- /dev/null +++ b/tests/recording/src/main.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +#include +#include + +#include "Constants.h" + +#define QVERIFY Q_ASSERT + +using namespace recording; +FrameType TEST_FRAME_TYPE { Frame::TYPE_INVALID }; + +void testFrameTypeRegistration() { + TEST_FRAME_TYPE = Frame::registerFrameType(TEST_NAME); + QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_INVALID); + QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_HEADER); + + auto forwardMap = recording::Frame::getFrameTypes(); + QVERIFY(forwardMap.count(TEST_NAME) == 1); + QVERIFY(forwardMap[TEST_NAME] == TEST_FRAME_TYPE); + QVERIFY(forwardMap[HEADER_NAME] == recording::Frame::TYPE_HEADER); + + auto backMap = recording::Frame::getFrameTypeNames(); + QVERIFY(backMap.count(TEST_FRAME_TYPE) == 1); + QVERIFY(backMap[TEST_FRAME_TYPE] == TEST_NAME); + QVERIFY(backMap[recording::Frame::TYPE_HEADER] == HEADER_NAME); +} + +void testFilePersist() { + QTemporaryFile file; + QString fileName; + if (file.open()) { + fileName = file.fileName(); + file.close(); + } + auto readClip = Clip::fromFile(fileName); + QVERIFY(Clip::Pointer() == readClip); + auto writeClip = Clip::newClip(); + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 5.0f, QByteArray())); + QVERIFY(writeClip->frameCount() == 1); + QVERIFY(writeClip->duration() == 5.0f); + + Clip::toFile(fileName, writeClip); + readClip = Clip::fromFile(fileName); + QVERIFY(readClip != Clip::Pointer()); + QVERIFY(readClip->frameCount() == 1); + QVERIFY(readClip->duration() == 5.0f); + readClip->seek(0); + writeClip->seek(0); + + size_t count = 0; + for (auto readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(); readFrame && writeFrame; + readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(), ++count) { + QVERIFY(readFrame->type == writeFrame->type); + QVERIFY(readFrame->timeOffset == writeFrame->timeOffset); + QVERIFY(readFrame->data == writeFrame->data); + } + QVERIFY(readClip->frameCount() == count); + + + writeClip = Clip::newClip(); + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 5.0f, QByteArray())); + // Simulate an unknown frametype + writeClip->addFrame(std::make_shared(Frame::TYPE_INVALID - 1, 10.0f, QByteArray())); + QVERIFY(writeClip->frameCount() == 2); + QVERIFY(writeClip->duration() == 10.0f); + Clip::toFile(fileName, writeClip); + + // Verify that the read version of the clip ignores the unknown frame type + readClip = Clip::fromFile(fileName); + QVERIFY(readClip != Clip::Pointer()); + QVERIFY(readClip->frameCount() == 1); + QVERIFY(readClip->duration() == 5.0f); +} + +void testClipOrdering() { + auto writeClip = Clip::newClip(); + // simulate our of order addition of frames + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 10.0f, QByteArray())); + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 5.0f, QByteArray())); + QVERIFY(writeClip->frameCount() == 2); + QVERIFY(writeClip->duration() == 10.0f); + + QVERIFY(std::numeric_limits::max() == writeClip->position()); + writeClip->seek(0); + QVERIFY(5.0f == writeClip->position()); + float lastFrameTimeOffset { 0 }; + for (auto writeFrame = writeClip->nextFrame(); writeFrame; writeFrame = writeClip->nextFrame()) { + QVERIFY(writeClip->position() >= lastFrameTimeOffset); + } +} + +#ifdef Q_OS_WIN32 +void myMessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg) { + OutputDebugStringA(msg.toLocal8Bit().toStdString().c_str()); + OutputDebugStringA("\n"); +} +#endif + +int main(int, const char**) { +#ifdef Q_OS_WIN32 + qInstallMessageHandler(myMessageHandler); +#endif + testFrameTypeRegistration(); + testFilePersist(); + testClipOrdering(); +} \ No newline at end of file From ced1a4899de4edc4edd1f7793bcc7c0b457ebd5f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 15:10:23 -0800 Subject: [PATCH 16/46] promises --- examples/libraries/promise.js | 222 +++++++++++++++++++++++++++ examples/libraries/promiseExample.js | 18 +++ 2 files changed, 240 insertions(+) create mode 100644 examples/libraries/promise.js create mode 100644 examples/libraries/promiseExample.js diff --git a/examples/libraries/promise.js b/examples/libraries/promise.js new file mode 100644 index 0000000000..cffa294715 --- /dev/null +++ b/examples/libraries/promise.js @@ -0,0 +1,222 @@ +// Copyright (c) 2014 Taylor Hakes +// Copyright (c) 2014 Forbes Lindesay + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +function promiseMaker() { + + // Use polyfill for setImmediate for performance gains + var asap = (typeof setImmediate === 'function' && setImmediate) || + function(fn) { + Script.setTimeout(fn, 1); + }; + + // Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + } + } + + var isArray = Array.isArray || function(value) { + return Object.prototype.toString.call(value) === "[object Array]" + }; + + function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + this._state = null; + this._value = null; + this._deferreds = [] + + doResolve(fn, bind(resolve, this), bind(reject, this)) + } + + function handle(deferred) { + var me = this; + if (this._state === null) { + this._deferreds.push(deferred); + return + } + asap(function() { + var cb = me._state ? deferred.onFulfilled : deferred.onRejected + if (cb === null) { + (me._state ? deferred.resolve : deferred.reject)(me._value); + return; + } + var ret; + try { + ret = cb(me._value); + } catch (e) { + deferred.reject(e); + return; + } + deferred.resolve(ret); + }) + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then; + if (typeof then === 'function') { + doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); + return; + } + } + this._state = true; + this._value = newValue; + finale.call(this); + } catch (e) { + reject.call(this, e); + } + } + + function reject(newValue) { + this._state = false; + this._value = newValue; + finale.call(this); + } + + function finale() { + for (var i = 0, len = this._deferreds.length; i < len; i++) { + handle.call(this, this._deferreds[i]); + } + this._deferreds = null; + } + + function Handler(onFulfilled, onRejected, resolve, reject) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function(value) { + if (done) return; + done = true; + onFulfilled(value); + }, function(reason) { + if (done) return; + done = true; + onRejected(reason); + }) + } catch (ex) { + if (done) return; + done = true; + onRejected(ex); + } + } + + Promise.prototype['catch'] = function(onRejected) { + return this.then(null, onRejected); + }; + + Promise.prototype.then = function(onFulfilled, onRejected) { + var me = this; + return new Promise(function(resolve, reject) { + handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); + }) + }; + + Promise.all = function() { + var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); + + return new Promise(function(resolve, reject) { + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call(val, function(val) { + res(i, val) + }, reject); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise.resolve = function(value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function(resolve) { + resolve(value); + }); + }; + + Promise.reject = function(value) { + return new Promise(function(resolve, reject) { + reject(value); + }); + }; + + Promise.race = function(values) { + return new Promise(function(resolve, reject) { + for (var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); + }; + + /** + * Set the immediate function to execute callbacks + * @param fn {function} Function to execute + * @private + */ + Promise._setImmediateFn = function _setImmediateFn(fn) { + asap = fn; + }; + + + return Promise + +} + +loadPromise = function() { + return promiseMaker(); +} \ No newline at end of file diff --git a/examples/libraries/promiseExample.js b/examples/libraries/promiseExample.js new file mode 100644 index 0000000000..817a78d2b0 --- /dev/null +++ b/examples/libraries/promiseExample.js @@ -0,0 +1,18 @@ +Script.include('promise.js'); +var Promise = loadPromise(); +var prom = new Promise(function(resolve, reject) { + print('making a promise') + // do a thing, possibly async, then… + var thing = true; + if (thing) { + resolve("Stuff worked!"); + } else { + print('ERROR') + reject(new Error("It broke")); + } +}); + +// Do something when async done +prom.then(function(result) { + print('result ' + result); +}); \ No newline at end of file From c1690bcf322d24bdc200e86201db6f4ef44db20c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 6 Nov 2015 15:19:14 -0800 Subject: [PATCH 17/46] restoring avatarMover app post S3-loss event --- examples/avatarMover/avatarMoverSpawner.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/examples/avatarMover/avatarMoverSpawner.js b/examples/avatarMover/avatarMoverSpawner.js index 0bff3b02c0..526446ecdb 100644 --- a/examples/avatarMover/avatarMoverSpawner.js +++ b/examples/avatarMover/avatarMoverSpawner.js @@ -1,6 +1,18 @@ var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/arrow.fbx"; +var scriptURL = Script.resolvePath('avatarMover.js'); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); var avatarMover = Entities.addEntity({ type: "Model", - modelURL: modelURL -}) \ No newline at end of file + modelURL: modelURL, + position: center, + userData: JSON.stringify({range: 5}), + script: scriptURL +}); + + +function cleanup() { + Entities.deleteEntity(avatarMover); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 7c433f47c01956a2c424e538d08478abe27a10e9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 3 Nov 2015 17:39:21 -0800 Subject: [PATCH 18/46] Breaking up input devices and input plugins classes. --- interface/src/Application.cpp | 8 +- interface/src/Menu.cpp | 2 - .../openvr/OpenVrDisplayPlugin.cpp | 25 ++--- .../openvr/OpenVrDisplayPlugin.h | 2 + .../display-plugins/openvr/OpenVrHelpers.cpp | 73 +++++++++++++ .../display-plugins/openvr/OpenVrHelpers.h | 6 + .../src/input-plugins/KeyboardMouseDevice.cpp | 74 +++++++------ .../src/input-plugins/KeyboardMouseDevice.h | 49 ++++++--- .../src/input-plugins/SixenseManager.cpp | 101 +++++++++-------- .../src/input-plugins/SixenseManager.h | 91 +++++++++------- .../src/input-plugins/SpacemouseManager.cpp | 15 +-- .../src/input-plugins/SpacemouseManager.h | 3 - .../input-plugins/ViveControllerManager.cpp | 103 +++++++----------- .../src/input-plugins/ViveControllerManager.h | 54 +++++---- tests/controllers/src/main.cpp | 2 +- 15 files changed, 346 insertions(+), 262 deletions(-) create mode 100644 libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8bff0f87dc..7a564bbbf0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -653,7 +653,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : userInputMapper->registerDevice(_applicationStateDevice); // Setup the keyboardMouseDevice and the user input mapper with the default bindings - userInputMapper->registerDevice(_keyboardMouseDevice); + userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); @@ -729,8 +729,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Now that menu is initalized we can sync myAvatar with it's state. getMyAvatar()->updateMotionBehaviorFromMenu(); +// FIXME spacemouse code still needs cleanup +#if 0 // the 3Dconnexion device wants to be initiliazed after a window is displayed. SpacemouseManager::getInstance().init(); +#endif auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); @@ -1850,9 +1853,12 @@ void Application::focusOutEvent(QFocusEvent* event) { } } +// FIXME spacemouse code still needs cleanup +#if 0 //SpacemouseDevice::getInstance().focusOutEvent(); //SpacemouseManager::getInstance().getDevice()->focusOutEvent(); SpacemouseManager::getInstance().ManagerFocusOutEvent(); +#endif // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 162b713948..92ff39a489 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -465,8 +465,6 @@ Menu::Menu() { avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, &SpacemouseManager::getInstance(), SLOT(toggleSpacemouse(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 174bf1bf36..bb39c7bb7a 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -37,8 +37,6 @@ const QString & OpenVrDisplayPlugin::getName() const { return NAME; } -vr::IVRSystem* _hmd{ nullptr }; -int hmdRefCount = 0; static vr::IVRCompositor* _compositor{ nullptr }; vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; @@ -78,24 +76,17 @@ mat4 toGlm(const vr::HmdMatrix34_t& m) { } bool OpenVrDisplayPlugin::isSupported() const { - bool success = vr::VR_IsHmdPresent(); - if (success) { - vr::HmdError eError = vr::HmdError_None; - auto hmd = vr::VR_Init(&eError); - success = (hmd != nullptr); - vr::VR_Shutdown(); - } + auto hmd = acquireOpenVrSystem(); + bool success = nullptr != hmd; + releaseOpenVrSystem(); return success; } void OpenVrDisplayPlugin::activate() { _container->setIsOptionChecked(StandingHMDSensorMode, true); - hmdRefCount++; - vr::HmdError eError = vr::HmdError_None; if (!_hmd) { - _hmd = vr::VR_Init(&eError); - Q_ASSERT(eError == vr::HmdError_None); + _hmd = acquireOpenVrSystem(); } Q_ASSERT(_hmd); @@ -114,6 +105,7 @@ void OpenVrDisplayPlugin::activate() { }); + vr::HmdError eError = vr::HmdError_None; _compositor = (vr::IVRCompositor*)vr::VR_GetGenericInterface(vr::IVRCompositor_Version, &eError); Q_ASSERT(eError == vr::HmdError_None); Q_ASSERT(_compositor); @@ -133,11 +125,8 @@ void OpenVrDisplayPlugin::activate() { void OpenVrDisplayPlugin::deactivate() { _container->setIsOptionChecked(StandingHMDSensorMode, false); - - hmdRefCount--; - - if (hmdRefCount == 0 && _hmd) { - vr::VR_Shutdown(); + if (_hmd) { + releaseOpenVrSystem(); _hmd = nullptr; } _compositor = nullptr; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h index 7849623552..15d37d9de8 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h @@ -10,6 +10,7 @@ #include #if defined(Q_OS_WIN) +#include #include "../WindowOpenGLDisplayPlugin.h" @@ -39,6 +40,7 @@ protected: virtual void finishFrame() override; private: + vr::IVRSystem* _hmd { nullptr }; static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp new file mode 100644 index 0000000000..44d30962bd --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp @@ -0,0 +1,73 @@ +// +// Created by Bradley Austin Davis on 2015/11/01 +// Copyright 2015 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 "OpenVrHelpers.h" + +#if defined(Q_OS_WIN) + +#include + +#include +#include + +using Mutex = std::mutex; +using Lock = std::unique_lock; + +static int refCount { 0 }; +static Mutex mutex; +static vr::IVRSystem* activeHmd { nullptr }; +static bool hmdPresent = vr::VR_IsHmdPresent(); + +static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; + +vr::IVRSystem* acquireOpenVrSystem() { + if (hmdPresent) { + Lock lock(mutex); + if (!activeHmd) { + qDebug() << "openvr: No vr::IVRSystem instance active, building"; + vr::HmdError eError = vr::HmdError_None; + activeHmd = vr::VR_Init(&eError); + qDebug() << "openvr display: HMD is " << activeHmd << " error is " << eError; + } + if (activeHmd) { + qDebug() << "openvr: incrementing refcount"; + ++refCount; + } + } + return activeHmd; +} + +void releaseOpenVrSystem() { + if (activeHmd) { + Lock lock(mutex); + qDebug() << "openvr: decrementing refcount"; + --refCount; + if (0 == refCount) { + qDebug() << "openvr: zero refcount, deallocate VR system"; + // Avoid spamming the VR system with activate/deactivate calls at system startup by + // putting in a delay before we destory the shutdown the VR subsystem + + // FIXME releasing the VR system at all seems to trigger an exception deep inside the Oculus DLL. + // disabling for now. + //QTimer* releaseTimer = new QTimer(); + //releaseTimer->singleShot(RELEASE_OPENVR_HMD_DELAY_MS, [releaseTimer] { + // Lock lock(mutex); + // qDebug() << "Delayed openvr destroy activated"; + // if (0 == refCount && nullptr != activeHmd) { + // qDebug() << "Delayed openvr destroy: releasing resources"; + // activeHmd = nullptr; + // vr::VR_Shutdown(); + // } else { + // qDebug() << "Delayed openvr destroy: HMD still in use"; + // } + // releaseTimer->deleteLater(); + //}); + } + } +} + +#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h index 761bef8cfc..3e445d90ba 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h @@ -7,10 +7,16 @@ // #pragma once +#include + #if defined(Q_OS_WIN) #include #include #include #include + +vr::IVRSystem* acquireOpenVrSystem(); +void releaseOpenVrSystem(); + #endif diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 2157a3a010..e91ea90aaf 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -19,8 +19,8 @@ const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; -void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { - _axisStateMap.clear(); +void KeyboardMouseDevice::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); // For touch event, we need to check that the last event is not too long ago // Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly @@ -35,26 +35,30 @@ void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { } } -void KeyboardMouseDevice::focusOutEvent() { +void KeyboardMouseDevice::InputDevice::update(float deltaTime, bool jointsCaptured) { + _axisStateMap.clear(); +} + +void KeyboardMouseDevice::InputDevice::focusOutEvent() { _buttonPressedMap.clear(); -}; +} void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) { - auto input = makeInput((Qt::Key) event->key()); - auto result = _buttonPressedMap.insert(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::Key) event->key()); + auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel()); if (!result.second) { // key pressed again ? without catching the release event ? } } void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) { - auto input = makeInput((Qt::Key) event->key()); - _buttonPressedMap.erase(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::Key) event->key()); + _inputDevice->_buttonPressedMap.erase(input.getChannel()); } void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { - auto input = makeInput((Qt::MouseButton) event->button()); - auto result = _buttonPressedMap.insert(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); + auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel()); if (!result.second) { // key pressed again ? without catching the release event ? } @@ -65,32 +69,32 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic } void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { - auto input = makeInput((Qt::MouseButton) event->button()); - _buttonPressedMap.erase(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); + _inputDevice->_buttonPressedMap.erase(input.getChannel()); // if we pressed and released at the same location, then create a "_CLICKED" input for this button // we might want to add some small tolerance to this so if you do a small drag it still counts as // a clicked. if (_mousePressAt == event->pos()) { - _buttonPressedMap.insert(makeInput((Qt::MouseButton) event->button(), true).getChannel()); + _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } void KeyboardMouseDevice::eraseMouseClicked() { - _buttonPressedMap.erase(makeInput(Qt::LeftButton, true).getChannel()); - _buttonPressedMap.erase(makeInput(Qt::MiddleButton, true).getChannel()); - _buttonPressedMap.erase(makeInput(Qt::RightButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::LeftButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::MiddleButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel()); } void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { QPoint currentPos = event->pos(); QPoint currentMove = currentPos - _lastCursor; - _axisStateMap[makeInput(MOUSE_AXIS_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_POS] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_NEG] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); // Y mouse is inverted positive is pointing up the screen - _axisStateMap[makeInput(MOUSE_AXIS_Y_POS).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_POS] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _lastCursor = currentPos; @@ -100,10 +104,10 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { auto currentMove = event->angleDelta() / 120.0f; - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); } glm::vec2 evalAverageTouchPoints(const QList& points) { @@ -138,17 +142,17 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { } else { auto currentMove = currentPos - _lastTouch; - _axisStateMap[makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); - _axisStateMap[makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); // Y mouse is inverted positive is pointing up the screen - _axisStateMap[makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); - _axisStateMap[makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); } _lastTouch = currentPos; } -controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::Key code) const { auto shortCode = (uint16_t)(code & KEYBOARD_MASK); if (shortCode != code) { shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys @@ -156,7 +160,7 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clicked) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::MouseButton code, bool clicked) const { switch (code) { case Qt::LeftButton: return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_LEFT_CLICKED : @@ -172,19 +176,19 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clic }; } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); } -controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { +controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const { using namespace controller; static QVector availableInputs; static std::once_flag once; @@ -229,7 +233,7 @@ controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { return availableInputs; } -QString KeyboardMouseDevice::getDefaultMappingConfig() const { +QString KeyboardMouseDevice::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 5d86821db1..b0578e3a99 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -24,7 +24,7 @@ class QKeyEvent; class QMouseEvent; class QWheelEvent; -class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice { +class KeyboardMouseDevice : public InputPlugin { Q_OBJECT public: enum KeyboardChannel { @@ -64,22 +64,20 @@ public: TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, }; - KeyboardMouseDevice() : InputDevice("Keyboard") {} + KeyboardMouseDevice() {} + + virtual ~KeyboardMouseDevice() {} + + controller::InputDevice::Pointer getInputDevice() { return _inputDevice; } // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } const QString& getName() const override { return NAME; } - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); @@ -94,21 +92,36 @@ public: void wheelEvent(QWheelEvent* event); - // Let's make it easy for Qt because we assume we love Qt forever - controller::Input makeInput(Qt::Key code) const; - controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; - controller::Input makeInput(MouseAxisChannel axis) const; - controller::Input makeInput(TouchAxisChannel axis) const; - controller::Input makeInput(TouchButtonChannel button) const; - static const QString NAME; protected: + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Keyboard") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + // Let's make it easy for Qt because we assume we love Qt forever + controller::Input makeInput(Qt::Key code) const; + controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; + controller::Input makeInput(MouseAxisChannel axis) const; + controller::Input makeInput(TouchAxisChannel axis) const; + controller::Input makeInput(TouchButtonChannel button) const; + + friend class KeyboardMouseDevice; + }; + QPoint _lastCursor; QPoint _mousePressAt; glm::vec2 _lastTouch; + std::shared_ptr _inputDevice { std::make_shared() }; + bool _isTouching = false; - std::chrono::high_resolution_clock _clock; std::chrono::high_resolution_clock::time_point _lastTouchTime; }; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 024eb86182..33b4332430 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -30,6 +30,11 @@ #ifdef HAVE_SIXENSE #include "sixense.h" + +#ifdef __APPLE__ +static QLibrary* _sixenseLibrary { nullptr }; +#endif + #endif // TODO: This should not be here @@ -39,13 +44,10 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") #ifdef HAVE_SIXENSE -const int CALIBRATION_STATE_IDLE = 0; -const int CALIBRATION_STATE_IN_PROGRESS = 1; -const int CALIBRATION_STATE_COMPLETE = 2; -const glm::vec3 DEFAULT_AVATAR_POSITION(-0.25f, -0.35f, -0.3f); // in hydra frame - -const float CONTROLLER_THRESHOLD = 0.35f; +const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame +const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; +const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; #endif @@ -66,14 +68,6 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; const float DEFAULT_REACH_LENGTH = 1.5f; -static std::shared_ptr instance; -SixenseManager::SixenseManager() : - InputDevice("Hydra"), - _reachLength(DEFAULT_REACH_LENGTH) -{ - instance = std::shared_ptr(this); -} - bool SixenseManager::isSupported() const { #ifdef HAVE_SIXENSE @@ -91,8 +85,6 @@ bool SixenseManager::isSupported() const { void SixenseManager::activate() { InputPlugin::activate(); #ifdef HAVE_SIXENSE - _calibrationState = CALIBRATION_STATE_IDLE; - _avatarPosition = DEFAULT_AVATAR_POSITION; _container->addMenu(MENU_PATH); _container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, @@ -100,7 +92,7 @@ void SixenseManager::activate() { true, true); auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); + userInputMapper->registerDevice(_inputDevice); #ifdef __APPLE__ @@ -139,12 +131,12 @@ void SixenseManager::deactivate() { _container->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); _container->removeMenu(MENU_PATH); - _poseStateMap.clear(); - _collectedSamples.clear(); + _inputDevice->_poseStateMap.clear(); + _inputDevice->_collectedSamples.clear(); - if (_deviceID != controller::Input::INVALID_DEVICE) { + if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(_deviceID); + userInputMapper->removeDevice(_inputDevice->_deviceID); } #ifdef __APPLE__ @@ -170,7 +162,15 @@ void SixenseManager::setSixenseFilter(bool filter) { #endif } -void SixenseManager::update(float deltaTime, bool jointsCaptured) { +void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); + if (_inputDevice->_calibrationState == CALIBRATION_STATE_COMPLETE) { + _container->requestReset(); + _inputDevice->_calibrationState = CALIBRATION_STATE_IDLE; + } +} + +void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // FIXME - Some of the code in update() will crash if you haven't actually activated the // plugin. But we want register with the UserInputMapper if we don't call this. // We need to clean this up. @@ -297,7 +297,7 @@ const float MINIMUM_ARM_REACH = 0.3f; // meters const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired -void SixenseManager::updateCalibration(void* controllersX) { +void SixenseManager::InputDevice::updateCalibration(void* controllersX) { auto controllers = reinterpret_cast(controllersX); const sixenseControllerData* dataLeft = controllers; const sixenseControllerData* dataRight = controllers + 1; @@ -309,26 +309,25 @@ void SixenseManager::updateCalibration(void* controllersX) { } switch (_calibrationState) { case CALIBRATION_STATE_COMPLETE: - { - // compute calibration results - _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands - glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); - glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); - xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); - _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); - _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); - const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; - _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; - _container->requestReset(); - qCDebug(inputplugins, "succeess: sixense calibration"); - } - break; + { + // compute calibration results + _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands + glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); + glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); + xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); + _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); + _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); + const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; + _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; + qCDebug(inputplugins, "succeess: sixense calibration"); + } + break; + default: + _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); break; } - - _calibrationState = CALIBRATION_STATE_IDLE; return; } @@ -382,15 +381,15 @@ void SixenseManager::updateCalibration(void* controllersX) { #endif // HAVE_SIXENSE -void SixenseManager::focusOutEvent() { +void SixenseManager::InputDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; -void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { +void SixenseManager::InputDevice::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { } -void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { +void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { using namespace controller; if (buttons & BUTTON_0) { _buttonPressedMap.insert(left ? BACK : START); @@ -415,7 +414,7 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { } } -void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { +void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { #ifdef HAVE_SIXENSE auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; @@ -521,7 +520,7 @@ static const auto R4 = controller::Y; using namespace controller; -controller::Input::NamedVector SixenseManager::getAvailableInputs() const { +controller::Input::NamedVector SixenseManager::InputDevice::getAvailableInputs() const { using namespace controller; static const Input::NamedVector availableInputs { makePair(L0, "L0"), @@ -551,7 +550,7 @@ controller::Input::NamedVector SixenseManager::getAvailableInputs() const { }; -QString SixenseManager::getDefaultMappingConfig() const { +QString SixenseManager::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json"; return MAPPING_JSON; } @@ -562,9 +561,9 @@ void SixenseManager::saveSettings() const { QString idString = getID(); settings.beginGroup(idString); { - settings.setVec3Value(QString("avatarPosition"), _avatarPosition); - settings.setQuatValue(QString("avatarRotation"), _avatarRotation); - settings.setValue(QString("reachLength"), QVariant(_reachLength)); + settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition); + settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.setValue(QString("reachLength"), QVariant(_inputDevice->_reachLength)); } settings.endGroup(); } @@ -574,9 +573,9 @@ void SixenseManager::loadSettings() { QString idString = getID(); settings.beginGroup(idString); { - settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); - settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); - settings.getFloatValueIfValid(QString("reachLength"), _reachLength); + settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition); + settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.getFloatValueIfValid(QString("reachLength"), _inputDevice->_reachLength); } settings.endGroup(); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 5b5cb7ccfa..7a686dc423 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -44,10 +44,11 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; // Handles interaction with the Sixense SDK (e.g., Razer Hydra). -class SixenseManager : public InputPlugin, public controller::InputDevice { +class SixenseManager : public InputPlugin { Q_OBJECT public: - SixenseManager(); + SixenseManager() {} + virtual ~SixenseManager() {} // Plugin functions virtual bool isSupported() const override; @@ -58,15 +59,8 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } - - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; virtual void saveSettings() const override; virtual void loadSettings() override; @@ -75,38 +69,57 @@ public slots: void setSixenseFilter(bool filter); private: - void handleButtonEvent(unsigned int buttons, bool left); - void handleAxisEvent(float x, float y, float trigger, bool left); - void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - - void updateCalibration(void* controllers); - - int _calibrationState; - - // these are calibration results - glm::vec3 _avatarPosition; // in hydra-frame - glm::quat _avatarRotation; // in hydra-frame - float _reachLength; - - // these are measured values used to compute the calibration results - quint64 _lockExpiry; - glm::vec3 _averageLeft; - glm::vec3 _averageRight; - glm::vec3 _reachLeft; - glm::vec3 _reachRight; - float _lastDistance; - bool _useSixenseFilter = true; - - static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s + static const int CALIBRATION_STATE_IDLE = 0; + static const int CALIBRATION_STATE_IN_PROGRESS = 1; + static const int CALIBRATION_STATE_COMPLETE = 2; + static const glm::vec3 DEFAULT_AVATAR_POSITION; + static const float CONTROLLER_THRESHOLD; + static const float DEFAULT_REACH_LENGTH; + + using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; using MovingAverageMap = std::map< int, Samples >; - MovingAverageMap _collectedSamples; - -#ifdef __APPLE__ - QLibrary* _sixenseLibrary { nullptr }; -#endif + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Hydra") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + void handleButtonEvent(unsigned int buttons, bool left); + void handleAxisEvent(float x, float y, float trigger, bool left); + void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); + void updateCalibration(void* controllers); + + friend class SixenseManager; + + MovingAverageMap _collectedSamples; + + int _calibrationState { CALIBRATION_STATE_IDLE }; + // these are calibration results + glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame + glm::quat _avatarRotation; // in hydra-frame + float _reachLength { DEFAULT_REACH_LENGTH }; + float _lastDistance; + // these are measured values used to compute the calibration results + quint64 _lockExpiry; + glm::vec3 _averageLeft; + glm::vec3 _averageRight; + glm::vec3 _reachLeft; + glm::vec3 _reachRight; + }; + + + + bool _useSixenseFilter = true; + std::shared_ptr _inputDevice { std::make_shared() }; + static const QString NAME; static const QString HYDRA_ID_STRING; }; diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp index 43e6ee48a8..fe90470cb4 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -21,11 +21,10 @@ const float MAX_AXIS = 75.0f; // max forward = 2x speed -static std::shared_ptr instance; -SpacemouseDevice::SpacemouseDevice() : -InputDevice("Spacemouse") +static std::shared_ptr instance = std::make_shared(); + +SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") { - instance = std::shared_ptr(this); } void SpacemouseDevice::focusOutEvent() { @@ -118,14 +117,6 @@ void SpacemouseDevice::update(float deltaTime, bool jointsCaptured) { // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached } -SpacemouseManager& SpacemouseManager::getInstance() { - static SpacemouseManager sharedInstance; - if (instance == nullptr) { - new SpacemouseDevice(); - } - return sharedInstance; -} - void SpacemouseManager::ManagerFocusOutEvent() { instance->focusOutEvent(); } diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index 08ac954c94..6253fa7f9d 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -23,7 +23,6 @@ class SpacemouseManager : public QObject { Q_OBJECT public: - static SpacemouseManager& getInstance(); void ManagerFocusOutEvent(); void init(); void destroy() {}; @@ -92,7 +91,6 @@ class SpacemouseManager : public QObject, public QAbstractNativeEventFilter { public: SpacemouseManager() {}; - static SpacemouseManager& getInstance(); void init(); void destroy(); bool Is3dmouseAttached(); @@ -169,7 +167,6 @@ private: class SpacemouseManager : public QObject { Q_OBJECT public: - static SpacemouseManager& getInstance(); void init(); void destroy(); bool Is3dmouseAttached(); diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 69b2b5b2c6..ec0c35cc96 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -27,12 +27,13 @@ #include #ifdef Q_OS_WIN -extern vr::IVRSystem* _hmd; -extern int hmdRefCount; extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; #endif +vr::IVRSystem* acquireOpenVrSystem(); +void releaseOpenVrSystem(); + const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; @@ -44,28 +45,11 @@ const QString MENU_NAME = "Vive Controllers"; const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString RENDER_CONTROLLERS = "Render Hand Controllers"; -static std::shared_ptr instance; - -ViveControllerManager::ViveControllerManager() : - InputDevice("Vive"), - _trackedControllers(0), - _modelLoaded(false), - _leftHandRenderID(0), - _rightHandRenderID(0), - _renderControllers(false) -{ - instance = std::shared_ptr(this); -} - bool ViveControllerManager::isSupported() const { #ifdef Q_OS_WIN - bool success = vr::VR_IsHmdPresent(); - if (success) { - vr::HmdError eError = vr::HmdError_None; - auto hmd = vr::VR_Init(&eError); - success = (hmd != nullptr); - vr::VR_Shutdown(); - } + auto hmd = acquireOpenVrSystem(); + bool success = hmd != nullptr; + releaseOpenVrSystem(); return success; #else return false; @@ -80,11 +64,8 @@ void ViveControllerManager::activate() { [this] (bool clicked) { this->setRenderControllers(clicked); }, true, true); - hmdRefCount++; if (!_hmd) { - vr::HmdError eError = vr::HmdError_None; - _hmd = vr::VR_Init(&eError); - Q_ASSERT(eError == vr::HmdError_None); + _hmd = acquireOpenVrSystem(); } Q_ASSERT(_hmd); @@ -138,7 +119,7 @@ void ViveControllerManager::activate() { // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); + userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; } @@ -149,18 +130,17 @@ void ViveControllerManager::deactivate() { _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); _container->removeMenu(MENU_PATH); - hmdRefCount--; - - if (hmdRefCount == 0 && _hmd) { - vr::VR_Shutdown(); + if (_hmd) { + releaseOpenVrSystem(); _hmd = nullptr; } - _poseStateMap.clear(); + + _inputDevice->_poseStateMap.clear(); #endif // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(_deviceID); + userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; } @@ -177,8 +157,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint //pendingChanges.updateItem(_leftHandRenderID, ); - controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; - controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; + controller::Pose leftHand = _inputDevice->_poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; + controller::Pose rightHand = _inputDevice->_poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); @@ -223,15 +203,28 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); } -void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { + +void ViveControllerManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); + auto userInputMapper = DependencyManager::get(); + + if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) { + userInputMapper->removeDevice(_inputDevice->_deviceID); + _registeredWithInputMapper = false; + _inputDevice->_poseStateMap.clear(); + } + + if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) { + userInputMapper->registerDevice(_inputDevice); + _registeredWithInputMapper = true; + UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); + } +} + +void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef Q_OS_WIN _poseStateMap.clear(); - // TODO: This shouldn't be necessary - if (!_hmd) { - return; - } - _buttonPressedMap.clear(); PerformanceTimer perfTimer("ViveControllerManager::update"); @@ -279,33 +272,17 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } } - auto userInputMapper = DependencyManager::get(); - - if (numTrackedControllers == 0) { - if (_registeredWithInputMapper) { - userInputMapper->removeDevice(_deviceID); - _registeredWithInputMapper = false; - _poseStateMap.clear(); - } - } - - if (_trackedControllers == 0 && numTrackedControllers > 0) { - userInputMapper->registerDevice(instance); - _registeredWithInputMapper = true; - UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); - } - _trackedControllers = numTrackedControllers; #endif } -void ViveControllerManager::focusOutEvent() { +void ViveControllerManager::InputDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { +void ViveControllerManager::InputDevice::handleAxisEvent(uint32_t axis, float x, float y, bool left) { #ifdef Q_OS_WIN //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; @@ -320,7 +297,7 @@ void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, boo } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) { +void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool left) { #ifdef Q_OS_WIN if (!pressed) { return; @@ -342,7 +319,7 @@ void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, boo #endif } -void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { +void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool left) { glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::quat_cast(mat); @@ -409,7 +386,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation); } -controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const { +controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { using namespace controller; QVector availableInputs{ // Trackpad analogs @@ -450,7 +427,7 @@ controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const return availableInputs; } -QString ViveControllerManager::getDefaultMappingConfig() const { +QString ViveControllerManager::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index 938f2f9ba9..a925733327 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -24,10 +24,15 @@ #include #include -class ViveControllerManager : public InputPlugin, public controller::InputDevice { +namespace vr { + class IVRSystem; +} + +class ViveControllerManager : public InputPlugin { Q_OBJECT public: - ViveControllerManager(); + ViveControllerManager() {} + virtual ~ViveControllerManager() {} // Plugin functions virtual bool isSupported() const override; @@ -37,40 +42,51 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } - - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges); void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } private: + class InputDevice : public controller::InputDevice { + public: + InputDevice(vr::IVRSystem*& hmd) : controller::InputDevice("Vive"), _hmd(hmd) {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + void handleButtonEvent(uint32_t button, bool pressed, bool left); + void handleAxisEvent(uint32_t axis, float x, float y, bool left); + void handlePoseEvent(const mat4& mat, bool left); + + int _trackedControllers { 0 }; + vr::IVRSystem*& _hmd; + friend class ViveControllerManager; + }; + void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - void handleButtonEvent(uint32_t button, bool pressed, bool left); - void handleAxisEvent(uint32_t axis, float x, float y, bool left); - void handlePoseEvent(const mat4& mat, bool left); - int _trackedControllers; - bool _modelLoaded; + bool _registeredWithInputMapper { false }; + bool _modelLoaded { false }; model::Geometry _modelGeometry; gpu::TexturePointer _texture; - int _leftHandRenderID; - int _rightHandRenderID; + int _leftHandRenderID { 0 }; + int _rightHandRenderID { 0 }; - bool _renderControllers; + bool _renderControllers { false }; + vr::IVRSystem* _hmd { nullptr }; + std::shared_ptr _inputDevice { std::make_shared(_hmd) }; static const QString NAME; - bool _registeredWithInputMapper { false }; }; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 6e2732f1d8..6cc2cfc6eb 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,7 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)); + userInputMapper->registerDevice(dynamic_cast(inputPlugin.get())->getInputDevice()); } inputPlugin->pluginUpdate(0, false); } From 619746676b39770d38d5b04165cf352446377591 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 6 Nov 2015 15:31:03 -0800 Subject: [PATCH 19/46] Fix ScriptAvatar (ac agent), and provide a simple working example assignment client script. --- .../src/avatars/ScriptableAvatar.cpp | 20 +++++-------- examples/acScripts/animatedAvatarAgent.js | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 examples/acScripts/animatedAvatarAgent.js diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 161be954ff..a78939256d 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -73,24 +73,20 @@ void ScriptableAvatar::update(float deltatime) { const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); - for (int i = 0; i < modelJoints.size(); i++) { - int mapping = animationJoints.indexOf(modelJoints[i]); - if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { - JointData& data = _jointData[i]; + for (int i = 0; i < animationJoints.size(); i++) { + const QString& name = animationJoints[i]; + int mapping = getJointIndex(name); + if (mapping != -1 && !_maskedJoints.contains(name)) { + JointData& data = _jointData[mapping]; auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); - auto newTranslation = floorFrame.translations.at(i) * (1.0f - frameFraction) + - ceilFrame.translations.at(i) * frameFraction; - + // We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in), + // but we don't do so for MyAvatar yet, so let's not be different here. if (data.rotation != newRotation) { data.rotation = newRotation; data.rotationSet = true; } - if (data.translation != newTranslation) { - data.translation = newTranslation; - data.translationSet = true; - } - } + } } } else { _animation.clear(); diff --git a/examples/acScripts/animatedAvatarAgent.js b/examples/acScripts/animatedAvatarAgent.js new file mode 100644 index 0000000000..4e550e9789 --- /dev/null +++ b/examples/acScripts/animatedAvatarAgent.js @@ -0,0 +1,29 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*global Agent, Avatar, Script, Entities, Vec3, print*/ +// +// animatedAvatar.js +// examples/acScripts +// +// Created by Howard Stearns 11/6/15 +// Copyright 2015 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 +// +// An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'. +// In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'. + +var origin = {x: 500, y: 502, z: 500}; +var spread = 10; // meters +var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35}; +Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy +Avatar.displayName = "'Bot"; +var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start. + +Agent.isAvatar = true; +Script.setTimeout(function () { + Avatar.position = Vec3.sum(origin, {x: Math.random() * spread, y: 0, z: Math.random() * spread}); + print("Starting at", JSON.stringify(Avatar.position)); + Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame); +}, millisecondsToWaitBeforeStarting); From f7d4a3821fd7a4d611986917db55779b30b2d7bc Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 15:42:56 -0800 Subject: [PATCH 20/46] add more methods --- examples/libraries/stringHelpers.js | 428 +++++++++++++++++++++++++++- 1 file changed, 419 insertions(+), 9 deletions(-) diff --git a/examples/libraries/stringHelpers.js b/examples/libraries/stringHelpers.js index 0fae9035f0..81f990ef7f 100644 --- a/examples/libraries/stringHelpers.js +++ b/examples/libraries/stringHelpers.js @@ -1,39 +1,36 @@ - - - if (typeof String.prototype.fileName !== "function") { - String.prototype.fileName = function () { + String.prototype.fileName = function() { return this.replace(/^(.*[\/\\])*/, ""); }; } if (typeof String.prototype.fileBase !== "function") { - String.prototype.fileBase = function () { + String.prototype.fileBase = function() { var filename = this.fileName(); return filename.slice(0, filename.indexOf(".")); }; } if (typeof String.prototype.fileType !== "function") { - String.prototype.fileType = function () { + String.prototype.fileType = function() { return this.slice(this.lastIndexOf(".") + 1); }; } if (typeof String.prototype.path !== "function") { - String.prototype.path = function () { + String.prototype.path = function() { return this.replace(/[\\\/][^\\\/]*$/, ""); }; } if (typeof String.prototype.regExpEscape !== "function") { - String.prototype.regExpEscape = function () { + String.prototype.regExpEscape = function() { return this.replace(/([$\^.+*?|\\\/{}()\[\]])/g, '\\$1'); }; } if (typeof String.prototype.toArrayBuffer !== "function") { - String.prototype.toArrayBuffer = function () { + String.prototype.toArrayBuffer = function() { var length, buffer, view, @@ -64,3 +61,416 @@ if (typeof String.prototype.toArrayBuffer !== "function") { return buffer; }; } +// Copyright Mathias Bynens + +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: + +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/*! https://mths.be/includes v1.0.0 by @mathias */ +if (!String.prototype.includes) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var toString = {}.toString; + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var indexOf = ''.indexOf; + var includes = function(search) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + return indexOf.call(string, searchString, pos) != -1; + }; + if (defineProperty) { + defineProperty(String.prototype, 'includes', { + 'value': includes, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.includes = includes; + } + }()); +} + +/*! https://mths.be/startswith v0.2.0 by @mathias */ +if (!String.prototype.startsWith) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var toString = {}.toString; + var startsWith = function(search) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'startsWith', { + 'value': startsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.startsWith = startsWith; + } + }()); +} +if (!String.prototype.endsWith) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var toString = {}.toString; + var endsWith = function(search) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var pos = stringLength; + if (arguments.length > 1) { + var position = arguments[1]; + if (position !== undefined) { + // `ToInteger` + pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + } + } + var end = Math.min(Math.max(pos, 0), stringLength); + var start = end - searchLength; + if (start < 0) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'endsWith', { + 'value': endsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.endsWith = endsWith; + } + }()); +} + +/*! https://mths.be/repeat v0.2.0 by @mathias */ +if (!String.prototype.repeat) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var repeat = function(count) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + // `ToInteger` + var n = count ? Number(count) : 0; + if (n != n) { // better `isNaN` + n = 0; + } + // Account for out-of-bounds indices + if (n < 0 || n == Infinity) { + throw RangeError(); + } + var result = ''; + while (n) { + if (n % 2 == 1) { + result += string; + } + if (n > 1) { + string += string; + } + n >>= 1; + } + return result; + }; + if (defineProperty) { + defineProperty(String.prototype, 'repeat', { + 'value': repeat, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.repeat = repeat; + } + }()); +} + +if (!String.prototype.at) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements. + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (exception) {} + return result; + }()); + var at = function(position) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + var size = string.length; + // `ToInteger` + var index = position ? Number(position) : 0; + if (index != index) { // better `isNaN` + index = 0; + } + // Account for out-of-bounds indices + // The odd lower bound is because the ToInteger operation is + // going to round `n` to `0` for `-1 < n <= 0`. + if (index <= -1 || index >= size) { + return ''; + } + // Second half of `ToInteger` + index = index | 0; + // Get the first code unit and code unit value + var cuFirst = string.charCodeAt(index); + var cuSecond; + var nextIndex = index + 1; + var len = 1; + if ( // Check if it’s the start of a surrogate pair. + cuFirst >= 0xD800 && cuFirst <= 0xDBFF && // high surrogate + size > nextIndex // there is a next code unit + ) { + cuSecond = string.charCodeAt(nextIndex); + if (cuSecond >= 0xDC00 && cuSecond <= 0xDFFF) { // low surrogate + len = 2; + } + } + return string.slice(index, index + len); + }; + if (defineProperty) { + defineProperty(String.prototype, 'at', { + 'value': at, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.at = at; + } + }()); +} + +/*! https://mths.be/codepointat v0.2.0 by @mathias */ +if (!String.prototype.codePointAt) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var codePointAt = function(position) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + var size = string.length; + // `ToInteger` + var index = position ? Number(position) : 0; + if (index != index) { // better `isNaN` + index = 0; + } + // Account for out-of-bounds indices: + if (index < 0 || index >= size) { + return undefined; + } + // Get the first code unit + var first = string.charCodeAt(index); + var second; + if ( // check if it’s the start of a surrogate pair + first >= 0xD800 && first <= 0xDBFF && // high surrogate + size > index + 1 // there is a next code unit + ) { + second = string.charCodeAt(index + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } + } + return first; + }; + if (defineProperty) { + defineProperty(String.prototype, 'codePointAt', { + 'value': codePointAt, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.codePointAt = codePointAt; + } + }()); +} + +/*! https://mths.be/fromcodepoint v0.2.1 by @mathias */ +if (!String.fromCodePoint) { + (function() { + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var stringFromCharCode = String.fromCharCode; + var floor = Math.floor; + var fromCodePoint = function(_) { + var MAX_SIZE = 0x4000; + var codeUnits = []; + var highSurrogate; + var lowSurrogate; + var index = -1; + var length = arguments.length; + if (!length) { + return ''; + } + var result = ''; + while (++index < length) { + var codePoint = Number(arguments[index]); + if (!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity` + codePoint < 0 || // not a valid Unicode code point + codePoint > 0x10FFFF || // not a valid Unicode code point + floor(codePoint) != codePoint // not an integer + ) { + throw RangeError('Invalid code point: ' + codePoint); + } + if (codePoint <= 0xFFFF) { // BMP code point + codeUnits.push(codePoint); + } else { // Astral code point; split in surrogate halves + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + codePoint -= 0x10000; + highSurrogate = (codePoint >> 10) + 0xD800; + lowSurrogate = (codePoint % 0x400) + 0xDC00; + codeUnits.push(highSurrogate, lowSurrogate); + } + if (index + 1 == length || codeUnits.length > MAX_SIZE) { + result += stringFromCharCode.apply(null, codeUnits); + codeUnits.length = 0; + } + } + return result; + }; + if (defineProperty) { + defineProperty(String, 'fromCodePoint', { + 'value': fromCodePoint, + 'configurable': true, + 'writable': true + }); + } else { + String.fromCodePoint = fromCodePoint; + } + }()); +} \ No newline at end of file From bf70ae4724fd16cd04d220d0a76a5481bc800929 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 15:59:00 -0800 Subject: [PATCH 21/46] PR feedback --- .../src/display-plugins/Logging.cpp | 11 +++++++++++ .../src/display-plugins/Logging.h | 16 ++++++++++++++++ .../src/display-plugins/openvr/OpenVrHelpers.cpp | 8 +++++--- .../src/input-plugins/KeyboardMouseDevice.h | 10 ++++------ .../src/input-plugins/SixenseManager.h | 4 +--- .../src/input-plugins/ViveControllerManager.h | 2 -- tests/controllers/src/main.cpp | 2 +- 7 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 libraries/display-plugins/src/display-plugins/Logging.cpp create mode 100644 libraries/display-plugins/src/display-plugins/Logging.h diff --git a/libraries/display-plugins/src/display-plugins/Logging.cpp b/libraries/display-plugins/src/display-plugins/Logging.cpp new file mode 100644 index 0000000000..00cd4cf0f9 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 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 "Logging.h" + +Q_LOGGING_CATEGORY(displayPlugins, "hifi.plugins.display") diff --git a/libraries/display-plugins/src/display-plugins/Logging.h b/libraries/display-plugins/src/display-plugins/Logging.h new file mode 100644 index 0000000000..79b20b5dcc --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 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_DisplayPlugins_Logging_h +#define hifi_DisplayPlugins_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(displayPlugins) + +#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp index 44d30962bd..f8e810beaf 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp @@ -14,6 +14,8 @@ #include #include +#include "../Logging.h" + using Mutex = std::mutex; using Lock = std::unique_lock; @@ -28,13 +30,13 @@ vr::IVRSystem* acquireOpenVrSystem() { if (hmdPresent) { Lock lock(mutex); if (!activeHmd) { - qDebug() << "openvr: No vr::IVRSystem instance active, building"; + qCDebug(displayPlugins) << "openvr: No vr::IVRSystem instance active, building"; vr::HmdError eError = vr::HmdError_None; activeHmd = vr::VR_Init(&eError); - qDebug() << "openvr display: HMD is " << activeHmd << " error is " << eError; + qCDebug(displayPlugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; } if (activeHmd) { - qDebug() << "openvr: incrementing refcount"; + qCDebug(displayPlugins) << "openvr: incrementing refcount"; ++refCount; } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b0578e3a99..4abdc44478 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -64,12 +64,6 @@ public: TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, }; - KeyboardMouseDevice() {} - - virtual ~KeyboardMouseDevice() {} - - controller::InputDevice::Pointer getInputDevice() { return _inputDevice; } - // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } @@ -116,6 +110,10 @@ protected: friend class KeyboardMouseDevice; }; +public: + const std::shared_ptr& getInputDevice() const { return _inputDevice; } + +protected: QPoint _lastCursor; QPoint _mousePressAt; glm::vec2 _lastTouch; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 7a686dc423..062a27390c 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -47,9 +47,7 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public InputPlugin { Q_OBJECT public: - SixenseManager() {} - virtual ~SixenseManager() {} - + // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index a925733327..02bdecb10a 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -31,8 +31,6 @@ namespace vr { class ViveControllerManager : public InputPlugin { Q_OBJECT public: - ViveControllerManager() {} - virtual ~ViveControllerManager() {} // Plugin functions virtual bool isSupported() const override; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 6cc2cfc6eb..664a894e44 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,7 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - userInputMapper->registerDevice(dynamic_cast(inputPlugin.get())->getInputDevice()); + userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)->getInputDevice()); } inputPlugin->pluginUpdate(0, false); } From 5893bfeb06ab622383f0f81514708b0183a3b3d9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 16:19:49 -0800 Subject: [PATCH 22/46] add user timing --- examples/libraries/usertiming.js | 547 ++++++++++++++++++++++++ examples/libraries/usertimingExample.js | 16 + 2 files changed, 563 insertions(+) create mode 100644 examples/libraries/usertiming.js create mode 100644 examples/libraries/usertimingExample.js diff --git a/examples/libraries/usertiming.js b/examples/libraries/usertiming.js new file mode 100644 index 0000000000..61554384e1 --- /dev/null +++ b/examples/libraries/usertiming.js @@ -0,0 +1,547 @@ +/* eslint-env browser,amd,node */ +// +// usertiming.js +// +// A polyfill for UserTiming (http://www.w3.org/TR/user-timing/) +// +// Copyright 2013 Nic Jansma +// http://nicj.net +// +// https://github.com/nicjansma/usertiming.js +// +// Licensed under the MIT license +// + +function userTiming() { + "use strict"; + + // allow running in Node.js environment + if (typeof window === "undefined") { + window = {}; + } + + // prepare base perf object + if (typeof window.performance === "undefined") { + window.performance = {}; + } + + // We need to keep a global reference to the window.performance object to + // prevent any added properties from being garbage-collected in Safari 8. + // https://bugs.webkit.org/show_bug.cgi?id=137407 + window._perfRefForUserTimingPolyfill = window.performance; + + // + // Note what we shimmed + // + window.performance.userTimingJsNow = false; + window.performance.userTimingJsNowPrefixed = false; + window.performance.userTimingJsUserTiming = false; + window.performance.userTimingJsUserTimingPrefixed = false; + window.performance.userTimingJsPerformanceTimeline = false; + window.performance.userTimingJsPerformanceTimelinePrefixed = false; + + // for prefixed support + var prefixes = []; + var methods = []; + var methodTest = null; + var i, j; + + // + // window.performance.now() shim + // http://www.w3.org/TR/hr-time/ + // + if (typeof window.performance.now !== "function") { + window.performance.userTimingJsNow = true; + + // copy prefixed version over if it exists + methods = ["webkitNow", "msNow", "mozNow"]; + + for (i = 0; i < methods.length; i++) { + if (typeof window.performance[methods[i]] === "function") { + window.performance.now = window.performance[methods[i]]; + + window.performance.userTimingJsNowPrefixed = true; + + break; + } + } + + // + // now() should be a DOMHighResTimeStamp, which is defined as being a time relative + // to navigationStart of the PerformanceTiming (PT) interface. If this browser supports + // PT, use that as our relative start. Otherwise, use "now" as the start and all other + // now() calls will be relative to our initialization. + // + + var nowOffset = +(new Date()); + if (window.performance.timing && window.performance.timing.navigationStart) { + nowOffset = window.performance.timing.navigationStart; + } + + if (typeof window.performance.now !== "function") { + // No browser support, fall back to Date.now + if (Date.now) { + window.performance.now = function() { + return Date.now() - nowOffset; + }; + } else { + // no Date.now support, get the time from new Date() + window.performance.now = function() { + return +(new Date()) - nowOffset; + }; + } + } + } + + // + // PerformanceTimeline (PT) shims + // http://www.w3.org/TR/performance-timeline/ + // + + /** + * Adds an object to our internal Performance Timeline array. + * + * Will be blank if the environment supports PT. + */ + var addToPerformanceTimeline = function() {}; + + /** + * Clears the specified entry types from our timeline array. + * + * Will be blank if the environment supports PT. + */ + var clearEntriesFromPerformanceTimeline = function() {}; + + // performance timeline array + var performanceTimeline = []; + + // whether or not the timeline will require sort on getEntries() + var performanceTimelineRequiresSort = false; + + // whether or not ResourceTiming is natively supported but UserTiming is + // not (eg Firefox 35) + var hasNativeGetEntriesButNotUserTiming = false; + + // + // If getEntries() and mark() aren't defined, we'll assume + // we have to shim at least some PT functions. + // + if (typeof window.performance.getEntries !== "function" || + typeof window.performance.mark !== "function") { + + if (typeof window.performance.getEntries === "function" && + typeof window.performance.mark !== "function") { + hasNativeGetEntriesButNotUserTiming = true; + } + + window.performance.userTimingJsPerformanceTimeline = true; + + // copy prefixed version over if it exists + prefixes = ["webkit", "moz"]; + methods = ["getEntries", "getEntriesByName", "getEntriesByType"]; + + for (i = 0; i < methods.length; i++) { + for (j = 0; j < prefixes.length; j++) { + // prefixed method will likely have an upper-case first letter + methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1); + + if (typeof window.performance[methodTest] === "function") { + window.performance[methods[i]] = window.performance[methodTest]; + + window.performance.userTimingJsPerformanceTimelinePrefixed = true; + } + } + } + + /** + * Adds an object to our internal Performance Timeline array. + * + * @param {Object} obj PerformanceEntry + */ + addToPerformanceTimeline = function(obj) { + performanceTimeline.push(obj); + + // + // If we insert a measure, its startTime may be out of order + // from the rest of the entries because the use can use any + // mark as the start time. If so, note we have to sort it before + // returning getEntries(); + // + if (obj.entryType === "measure") { + performanceTimelineRequiresSort = true; + } + }; + + /** + * Ensures our PT array is in the correct sorted order (by startTime) + */ + var ensurePerformanceTimelineOrder = function() { + if (!performanceTimelineRequiresSort) { + return; + } + + // + // Measures, which may be in this list, may enter the list in + // an unsorted order. For example: + // + // 1. measure("a") + // 2. mark("start_mark") + // 3. measure("b", "start_mark") + // 4. measure("c") + // 5. getEntries() + // + // When calling #5, we should return [a,c,b] because technically the start time + // of c is "0" (navigationStart), which will occur before b's start time due to the mark. + // + performanceTimeline.sort(function(a, b) { + return a.startTime - b.startTime; + }); + + performanceTimelineRequiresSort = false; + }; + + /** + * Clears the specified entry types from our timeline array. + * + * @param {string} entryType Entry type (eg "mark" or "measure") + * @param {string} [name] Entry name (optional) + */ + clearEntriesFromPerformanceTimeline = function(entryType, name) { + // clear all entries from the perf timeline + i = 0; + while (i < performanceTimeline.length) { + if (performanceTimeline[i].entryType !== entryType) { + // unmatched entry type + i++; + continue; + } + + if (typeof name !== "undefined" && performanceTimeline[i].name !== name) { + // unmatched name + i++; + continue; + } + + // this entry matches our criteria, remove just it + performanceTimeline.splice(i, 1); + } + }; + + if (typeof window.performance.getEntries !== "function" || hasNativeGetEntriesButNotUserTiming) { + var origGetEntries = window.performance.getEntries; + + /** + * Gets all entries from the Performance Timeline. + * http://www.w3.org/TR/performance-timeline/#dom-performance-getentries + * + * NOTE: This will only ever return marks and measures. + * + * @returns {PerformanceEntry[]} Array of PerformanceEntrys + */ + window.performance.getEntries = function() { + ensurePerformanceTimelineOrder(); + + // get a copy of all of our entries + var entries = performanceTimeline.slice(0); + + // if there was a native version of getEntries, add that + if (hasNativeGetEntriesButNotUserTiming && origGetEntries) { + // merge in native + Array.prototype.push.apply(entries, origGetEntries.call(window.performance)); + + // sort by startTime + entries.sort(function(a, b) { + return a.startTime - b.startTime; + }); + } + + return entries; + }; + } + + if (typeof window.performance.getEntriesByType !== "function" || hasNativeGetEntriesButNotUserTiming) { + var origGetEntriesByType = window.performance.getEntriesByType; + + /** + * Gets all entries from the Performance Timeline of the specified type. + * http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbytype + * + * NOTE: This will only work for marks and measures. + * + * @param {string} entryType Entry type (eg "mark" or "measure") + * + * @returns {PerformanceEntry[]} Array of PerformanceEntrys + */ + window.performance.getEntriesByType = function(entryType) { + // we only support marks/measures + if (typeof entryType === "undefined" || + (entryType !== "mark" && entryType !== "measure")) { + + if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByType) { + // native version exists, forward + return origGetEntriesByType.call(window.performance, entryType); + } + + return []; + } + + // see note in ensurePerformanceTimelineOrder() on why this is required + if (entryType === "measure") { + ensurePerformanceTimelineOrder(); + } + + // find all entries of entryType + var entries = []; + for (i = 0; i < performanceTimeline.length; i++) { + if (performanceTimeline[i].entryType === entryType) { + entries.push(performanceTimeline[i]); + } + } + + return entries; + }; + } + + if (typeof window.performance.getEntriesByName !== "function" || hasNativeGetEntriesButNotUserTiming) { + var origGetEntriesByName = window.performance.getEntriesByName; + + /** + * Gets all entries from the Performance Timeline of the specified + * name, and optionally, type. + * http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbyname + * + * NOTE: This will only work for marks and measures. + * + * @param {string} name Entry name + * @param {string} [entryType] Entry type (eg "mark" or "measure") + * + * @returns {PerformanceEntry[]} Array of PerformanceEntrys + */ + window.performance.getEntriesByName = function(name, entryType) { + if (entryType && entryType !== "mark" && entryType !== "measure") { + if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) { + // native version exists, forward + return origGetEntriesByName.call(window.performance, name, entryType); + } + + return []; + } + + // see note in ensurePerformanceTimelineOrder() on why this is required + if (typeof entryType !== "undefined" && entryType === "measure") { + ensurePerformanceTimelineOrder(); + } + + // find all entries of the name and (optionally) type + var entries = []; + for (i = 0; i < performanceTimeline.length; i++) { + if (typeof entryType !== "undefined" && + performanceTimeline[i].entryType !== entryType) { + continue; + } + + if (performanceTimeline[i].name === name) { + entries.push(performanceTimeline[i]); + } + } + + if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) { + // merge in native + Array.prototype.push.apply(entries, origGetEntriesByName.call(window.performance, name, entryType)); + + // sort by startTime + entries.sort(function(a, b) { + return a.startTime - b.startTime; + }); + } + + return entries; + }; + } + } + + // + // UserTiming support + // + if (typeof window.performance.mark !== "function") { + window.performance.userTimingJsUserTiming = true; + + // copy prefixed version over if it exists + prefixes = ["webkit", "moz", "ms"]; + methods = ["mark", "measure", "clearMarks", "clearMeasures"]; + + for (i = 0; i < methods.length; i++) { + for (j = 0; j < prefixes.length; j++) { + // prefixed method will likely have an upper-case first letter + methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1); + + if (typeof window.performance[methodTest] === "function") { + window.performance[methods[i]] = window.performance[methodTest]; + + window.performance.userTimingJsUserTimingPrefixed = true; + } + } + } + + // only used for measure(), to quickly see the latest timestamp of a mark + var marks = {}; + + if (typeof window.performance.mark !== "function") { + /** + * UserTiming mark + * http://www.w3.org/TR/user-timing/#dom-performance-mark + * + * @param {string} markName Mark name + */ + window.performance.mark = function(markName) { + var now = window.performance.now(); + + // mark name is required + if (typeof markName === "undefined") { + throw new SyntaxError("Mark name must be specified"); + } + + // mark name can't be a NT timestamp + if (window.performance.timing && markName in window.performance.timing) { + throw new SyntaxError("Mark name is not allowed"); + } + + if (!marks[markName]) { + marks[markName] = []; + } + + marks[markName].push(now); + + // add to perf timeline as well + addToPerformanceTimeline({ + entryType: "mark", + name: markName, + startTime: now, + duration: 0 + }); + }; + } + + if (typeof window.performance.clearMarks !== "function") { + /** + * UserTiming clear marks + * http://www.w3.org/TR/user-timing/#dom-performance-clearmarks + * + * @param {string} markName Mark name + */ + window.performance.clearMarks = function(markName) { + if (!markName) { + // clear all marks + marks = {}; + } else { + marks[markName] = []; + } + + clearEntriesFromPerformanceTimeline("mark", markName); + }; + } + + if (typeof window.performance.measure !== "function") { + /** + * UserTiming measure + * http://www.w3.org/TR/user-timing/#dom-performance-measure + * + * @param {string} measureName Measure name + * @param {string} [startMark] Start mark name + * @param {string} [endMark] End mark name + */ + window.performance.measure = function(measureName, startMark, endMark) { + var now = window.performance.now(); + + if (typeof measureName === "undefined") { + throw new SyntaxError("Measure must be specified"); + } + + // if there isn't a startMark, we measure from navigationStart to now + if (!startMark) { + // add to perf timeline as well + addToPerformanceTimeline({ + entryType: "measure", + name: measureName, + startTime: 0, + duration: now + }); + + return; + } + + // + // If there is a startMark, check for it first in the NavigationTiming interface, + // then check our own marks. + // + var startMarkTime = 0; + if (window.performance.timing && startMark in window.performance.timing) { + // mark cannot have a timing of 0 + if (startMark !== "navigationStart" && window.performance.timing[startMark] === 0) { + throw new Error(startMark + " has a timing of 0"); + } + + // time is the offset of this mark to navigationStart's time + startMarkTime = window.performance.timing[startMark] - window.performance.timing.navigationStart; + } else if (startMark in marks) { + startMarkTime = marks[startMark][marks[startMark].length - 1]; + } else { + throw new Error(startMark + " mark not found"); + } + + // + // If there is a endMark, check for it first in the NavigationTiming interface, + // then check our own marks. + // + var endMarkTime = now; + + if (endMark) { + endMarkTime = 0; + + if (window.performance.timing && endMark in window.performance.timing) { + // mark cannot have a timing of 0 + if (endMark !== "navigationStart" && window.performance.timing[endMark] === 0) { + throw new Error(endMark + " has a timing of 0"); + } + + // time is the offset of this mark to navigationStart's time + endMarkTime = window.performance.timing[endMark] - window.performance.timing.navigationStart; + } else if (endMark in marks) { + endMarkTime = marks[endMark][marks[endMark].length - 1]; + } else { + throw new Error(endMark + " mark not found"); + } + } + + // add to our measure array + var duration = endMarkTime - startMarkTime; + + // add to perf timeline as well + addToPerformanceTimeline({ + entryType: "measure", + name: measureName, + startTime: startMarkTime, + duration: duration + }); + }; + } + + if (typeof window.performance.clearMeasures !== "function") { + /** + * UserTiming clear measures + * http://www.w3.org/TR/user-timing/#dom-performance-clearmeasures + * + * @param {string} measureName Measure name + */ + window.performance.clearMeasures = function(measureName) { + clearEntriesFromPerformanceTimeline("measure", measureName); + }; + } + } + + return window +} + +loadUserTiming = function() { + return userTiming(); +} \ No newline at end of file diff --git a/examples/libraries/usertimingExample.js b/examples/libraries/usertimingExample.js new file mode 100644 index 0000000000..a43496a6b9 --- /dev/null +++ b/examples/libraries/usertimingExample.js @@ -0,0 +1,16 @@ +Script.include('usertiming.js'); +var timing = loadUserTiming(); +//set a mark +timing.performance.mark('mark_fully_loaded') + +var count = 0; +Script.setTimeout(function() { + //do something that takes time + //and set another mark + timing.performance.mark('mark_fully_loaded2') + //measure time between marks (first parameter is a name for the measurement) + timing.performance.measure('howlong', 'mark_fully_loaded', 'mark_fully_loaded2'); + //you can also get the marks, but the measure is usually what you're looking for + var items = timing.performance.getEntriesByType('measure'); + print('items is:::' + JSON.stringify(items)) +}, 1000) \ No newline at end of file From f478261ed0b0da3cd70bb78a3a49ba1474720c2c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 16:21:53 -0800 Subject: [PATCH 23/46] cleanup --- examples/libraries/usertiming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/usertiming.js b/examples/libraries/usertiming.js index 61554384e1..f477ed0bb7 100644 --- a/examples/libraries/usertiming.js +++ b/examples/libraries/usertiming.js @@ -1,9 +1,9 @@ -/* eslint-env browser,amd,node */ // // usertiming.js // // A polyfill for UserTiming (http://www.w3.org/TR/user-timing/) // +// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015 // Copyright 2013 Nic Jansma // http://nicj.net // From 298ac650d8ea36f12d877d28ce35ac585e6d4c8f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 21:25:49 -0700 Subject: [PATCH 24/46] Isolate sixense dynamic linking for OSX --- .../src/input-plugins/SixenseManager.cpp | 101 +++--------------- .../src/input-plugins/SixenseManager.h | 18 ---- .../src/input-plugins/SixenseSupportOSX.cpp | 97 +++++++++++++++++ 3 files changed, 109 insertions(+), 107 deletions(-) create mode 100644 libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 33b4332430..24b1b60356 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -9,12 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "SixenseManager.h" + +#ifdef HAVE_SIXENSE +#include "sixense.h" +#endif #include #include #include +// TODO: This should not be here +#include +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + #include #include #include @@ -25,40 +34,12 @@ #include #include -#include "SixenseManager.h" - - -#ifdef HAVE_SIXENSE - #include "sixense.h" - -#ifdef __APPLE__ -static QLibrary* _sixenseLibrary { nullptr }; -#endif - -#endif - -// TODO: This should not be here -#include -Q_DECLARE_LOGGING_CATEGORY(inputplugins) -Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") - -#ifdef HAVE_SIXENSE - +#include "UserActivityLogger.h" const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; -#endif - -#ifdef __APPLE__ -typedef int (*SixenseBaseFunction)(); -typedef int (*SixenseTakeIntFunction)(int); -#ifdef HAVE_SIXENSE -typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*); -#endif -#endif - const QString SixenseManager::NAME = "Sixense"; const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra"; @@ -94,31 +75,6 @@ void SixenseManager::activate() { auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); -#ifdef __APPLE__ - - if (!_sixenseLibrary) { - -#ifdef SIXENSE_LIB_FILENAME - _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME); -#else - const QString SIXENSE_LIBRARY_NAME = "libsixense_x64"; - QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/" - + SIXENSE_LIBRARY_NAME; - - _sixenseLibrary = new QLibrary(frameworkSixenseLibrary); -#endif - } - - if (_sixenseLibrary->load()){ - qCDebug(inputplugins) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName(); - } else { - qCDebug(inputplugins) << "Sixense library at" << _sixenseLibrary->fileName() << "failed to load." - << "Continuing without hydra support."; - return; - } - - SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit"); -#endif loadSettings(); sixenseInit(); #endif @@ -139,26 +95,13 @@ void SixenseManager::deactivate() { userInputMapper->removeDevice(_inputDevice->_deviceID); } -#ifdef __APPLE__ - SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit"); -#endif - sixenseExit(); - -#ifdef __APPLE__ - delete _sixenseLibrary; -#endif - #endif } void SixenseManager::setSixenseFilter(bool filter) { #ifdef HAVE_SIXENSE -#ifdef __APPLE__ - SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled"); -#endif - int newFilter = filter ? 1 : 0; - sixenseSetFilterEnabled(newFilter); + sixenseSetFilterEnabled(filter ? 1 : 0); #endif } @@ -180,11 +123,6 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); -#ifdef __APPLE__ - SixenseBaseFunction sixenseGetNumActiveControllers = - (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); -#endif - auto userInputMapper = DependencyManager::get(); static const float MAX_DISCONNECTED_TIME = 2.0f; @@ -213,24 +151,11 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // FIXME send this message once when we've positively identified hydra hardware //UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); -#ifdef __APPLE__ - SixenseBaseFunction sixenseGetMaxControllers = - (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); -#endif - int maxControllers = sixenseGetMaxControllers(); // we only support two controllers sixenseControllerData controllers[2]; -#ifdef __APPLE__ - SixenseTakeIntFunction sixenseIsControllerEnabled = - (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled"); - - SixenseTakeIntAndSixenseControllerData sixenseGetNewestData = - (SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData"); -#endif - int numActiveControllers = 0; for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) { if (!sixenseIsControllerEnabled(i)) { @@ -479,8 +404,6 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos glm::vec3 velocity(0.0f); glm::quat angularVelocity; - - if (prevPose.isValid() && deltaTime > std::numeric_limits::epsilon()) { velocity = (position - prevPose.getTranslation()) / deltaTime; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 062a27390c..bf657d347b 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -12,18 +12,6 @@ #ifndef hifi_SixenseManager_h #define hifi_SixenseManager_h -#ifdef HAVE_SIXENSE - #include - #include - #include "sixense.h" - -#ifdef __APPLE__ - #include - #include -#endif - -#endif - #include #include @@ -31,8 +19,6 @@ #include "InputPlugin.h" -class QLibrary; - const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 const unsigned int BUTTON_1 = 1U << 5; const unsigned int BUTTON_2 = 1U << 6; @@ -75,7 +61,6 @@ private: static const float CONTROLLER_THRESHOLD; static const float DEFAULT_REACH_LENGTH; - using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; using MovingAverageMap = std::map< int, Samples >; @@ -113,9 +98,6 @@ private: glm::vec3 _reachRight; }; - - - bool _useSixenseFilter = true; std::shared_ptr _inputDevice { std::make_shared() }; static const QString NAME; diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp new file mode 100644 index 0000000000..c5c3f7f14a --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -0,0 +1,97 @@ +// +// SixenseSupportOSX.cpp +// +// +// Created by Clement on 10/20/15. +// Copyright 2015 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 +// + +#ifdef __APPLE__ +#include "sixense.h" + +#include +#include +#include + +using std::string; +using std::unique_ptr; +using SixenseBaseFunction = int (*)(); +using SixenseTakeIntFunction = int (*)(int); +using SixenseTakeIntAndSixenseControllerData = int (*)(int, sixenseControllerData*); + +static unique_ptr SIXENSE; + +bool loadSixense() { + if (!SIXENSE) { + static const QString LIBRARY_PATH = +#ifdef SIXENSE_LIB_FILENAME + SIXENSE_LIB_FILENAME; +#else + QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"; +#endif + SIXENSE.reset(new QLibrary(LIBRARY_PATH)); + } + + if (SIXENSE->load()){ + qDebug() << "Loaded sixense library for hydra support -" << SIXENSE->fileName(); + } else { + qDebug() << "Sixense library at" << SIXENSE->fileName() << "failed to load:" << SIXENSE->errorString(); + qDebug() << "Continuing without hydra support."; + } + return SIXENSE->isLoaded(); +} + +void unloadSixense() { + SIXENSE->unload(); +} + +template +Func resolve(const char* name) { + Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); + auto func = reinterpret_cast(SIXENSE->resolve(name)); + Q_ASSERT_X(func, __FUNCTION__, string("Could not resolve ").append(name).c_str()); + return func; +} + +// sixense.h wrapper for OSX dynamic linking +int sixenseInit() { + loadSixense(); + auto sixenseInit = resolve("sixenseInit"); + return sixenseInit(); +} + +int sixenseExit() { + auto sixenseExit = resolve("sixenseExit"); + auto returnCode = sixenseExit(); + unloadSixense(); + return returnCode; +} + +int sixenseSetFilterEnabled(int input) { + auto sixenseSetFilterEnabled = resolve("sixenseSetFilterEnabled"); + return sixenseSetFilterEnabled(input); +} + +int sixenseGetNumActiveControllers() { + auto sixenseGetNumActiveControllers = resolve("sixenseGetNumActiveControllers"); + return sixenseGetNumActiveControllers(); +} + +int sixenseGetMaxControllers() { + auto sixenseGetMaxControllers = resolve("sixenseGetMaxControllers"); + return sixenseGetMaxControllers(); +} + +int sixenseIsControllerEnabled(int input) { + auto sixenseIsControllerEnabled = resolve("sixenseIsControllerEnabled"); + return sixenseIsControllerEnabled(input); +} + +int sixenseGetNewestData(int input1, sixenseControllerData* intput2) { + auto sixenseGetNewestData = resolve("sixenseGetNewestData"); + return sixenseGetNewestData(input1, intput2); +} +#endif From 96aa5be457d1455348ffdf5e90f38898c79eaf3a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 23:06:44 -0700 Subject: [PATCH 25/46] Implement all Sixense methods for OS X --- .../src/input-plugins/SixenseSupportOSX.cpp | 102 +++++++++++++----- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index c5c3f7f14a..a890d7c138 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -24,7 +24,7 @@ using SixenseTakeIntAndSixenseControllerData = int (*)(int, sixenseControllerDat static unique_ptr SIXENSE; -bool loadSixense() { +void loadSixense() { if (!SIXENSE) { static const QString LIBRARY_PATH = #ifdef SIXENSE_LIB_FILENAME @@ -41,57 +41,107 @@ bool loadSixense() { qDebug() << "Sixense library at" << SIXENSE->fileName() << "failed to load:" << SIXENSE->errorString(); qDebug() << "Continuing without hydra support."; } - return SIXENSE->isLoaded(); } void unloadSixense() { SIXENSE->unload(); } -template -Func resolve(const char* name) { +template +int call(const char* name, Args... args) { Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); - auto func = reinterpret_cast(SIXENSE->resolve(name)); + auto func = reinterpret_cast(SIXENSE->resolve(name)); Q_ASSERT_X(func, __FUNCTION__, string("Could not resolve ").append(name).c_str()); - return func; + return func(args...); } // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); - auto sixenseInit = resolve("sixenseInit"); - return sixenseInit(); + return call(__FUNCTION__); } - int sixenseExit() { - auto sixenseExit = resolve("sixenseExit"); - auto returnCode = sixenseExit(); + auto returnCode = call(__FUNCTION__); unloadSixense(); return returnCode; } -int sixenseSetFilterEnabled(int input) { - auto sixenseSetFilterEnabled = resolve("sixenseSetFilterEnabled"); - return sixenseSetFilterEnabled(input); +int sixenseGetMaxBases() { + return call(__FUNCTION__); } - -int sixenseGetNumActiveControllers() { - auto sixenseGetNumActiveControllers = resolve("sixenseGetNumActiveControllers"); - return sixenseGetNumActiveControllers(); +int sixenseSetActiveBase(int i) { + return call(__FUNCTION__, i); +} +int sixenseIsBaseConnected(int i) { + return call(__FUNCTION__, i); } int sixenseGetMaxControllers() { - auto sixenseGetMaxControllers = resolve("sixenseGetMaxControllers"); - return sixenseGetMaxControllers(); + return call(__FUNCTION__); +} +int sixenseIsControllerEnabled(int which) { + return call(__FUNCTION__, which); +} +int sixenseGetNumActiveControllers() { + return call(__FUNCTION__); } -int sixenseIsControllerEnabled(int input) { - auto sixenseIsControllerEnabled = resolve("sixenseIsControllerEnabled"); - return sixenseIsControllerEnabled(input); +int sixenseGetHistorySize() { + return call(__FUNCTION__); } -int sixenseGetNewestData(int input1, sixenseControllerData* intput2) { - auto sixenseGetNewestData = resolve("sixenseGetNewestData"); - return sixenseGetNewestData(input1, intput2); +int sixenseGetData(int which, int index_back, sixenseControllerData* data) { + return call(__FUNCTION__, which, index_back, data); +} +int sixenseGetAllData(int index_back, sixenseAllControllerData* data) { + return call(__FUNCTION__, index_back, data); +} +int sixenseGetNewestData(int which, sixenseControllerData* data) { + return call(__FUNCTION__, which, data); +} +int sixenseGetAllNewestData(sixenseAllControllerData* data) { + return call(__FUNCTION__, data); +} + +int sixenseSetHemisphereTrackingMode(int which_controller, int state) { + return call(__FUNCTION__, which_controller, state); +} +int sixenseGetHemisphereTrackingMode(int which_controller, int* state) { + return call(__FUNCTION__, which_controller, state); +} +int sixenseAutoEnableHemisphereTracking(int which_controller) { + return call(__FUNCTION__, which_controller); +} + +int sixenseSetHighPriorityBindingEnabled(int on_or_off) { + return call(__FUNCTION__, on_or_off); +} +int sixenseGetHighPriorityBindingEnabled(int* on_or_off) { + return call(__FUNCTION__, on_or_off); +} + +int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) { + return call(__FUNCTION__, controller_id, duration_100ms, pattern_id); +} + +int sixenseSetFilterEnabled(int on_or_off) { + return call(__FUNCTION__, on_or_off); +} +int sixenseGetFilterEnabled(int* on_or_off) { + return call(__FUNCTION__, on_or_off); +} + +int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) { + return call(__FUNCTION__, near_range, near_val, far_range, far_val); +} +int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) { + return call(__FUNCTION__, near_range, near_val, far_range, far_val); +} + +int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) { + return call(__FUNCTION__, red, green, blue); +} +int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) { + return call(__FUNCTION__, red, green, blue); } #endif From 503e03d4a811b67c7beade04bc030c45a494cab3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 23:17:58 -0700 Subject: [PATCH 26/46] Remove unused `using` --- .../src/input-plugins/SixenseSupportOSX.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index a890d7c138..99af9c0ffc 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -16,13 +16,7 @@ #include #include -using std::string; -using std::unique_ptr; -using SixenseBaseFunction = int (*)(); -using SixenseTakeIntFunction = int (*)(int); -using SixenseTakeIntAndSixenseControllerData = int (*)(int, sixenseControllerData*); - -static unique_ptr SIXENSE; +static std::unique_ptr SIXENSE; void loadSixense() { if (!SIXENSE) { @@ -51,7 +45,7 @@ template int call(const char* name, Args... args) { Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); auto func = reinterpret_cast(SIXENSE->resolve(name)); - Q_ASSERT_X(func, __FUNCTION__, string("Could not resolve ").append(name).c_str()); + Q_ASSERT_X(func, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); return func(args...); } From e3b54f0272a25a8f38ddd4721109f4f1eae4a939 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 23:41:20 -0700 Subject: [PATCH 27/46] Cleanup --- .../src/input-plugins/SixenseSupportOSX.cpp | 81 ++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 99af9c0ffc..4c8a285d76 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -1,6 +1,6 @@ // // SixenseSupportOSX.cpp -// +// libraries/input-plugins/src/input-plugins // // Created by Clement on 10/20/15. // Copyright 2015 High Fidelity, Inc. @@ -9,14 +9,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(HAVE_SIXENSE) #include "sixense.h" #include #include #include -static std::unique_ptr SIXENSE; +using Library = std::unique_ptr; +static Library SIXENSE; + +struct Callable { + template int operator() (Args... args){ + return reinterpret_cast(function)(args...); + } + QFunctionPointer function; +}; + +Callable resolve(const Library& library, const char* name) { + Q_ASSERT_X(library && library->isLoaded(), __FUNCTION__, "Sixense library not loaded"); + auto function = library->resolve(name); + Q_ASSERT_X(function, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); + return Callable { function }; +} +#define FOREWARD resolve(SIXENSE, __FUNCTION__) + void loadSixense() { if (!SIXENSE) { @@ -36,106 +53,98 @@ void loadSixense() { qDebug() << "Continuing without hydra support."; } } - void unloadSixense() { SIXENSE->unload(); } -template -int call(const char* name, Args... args) { - Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); - auto func = reinterpret_cast(SIXENSE->resolve(name)); - Q_ASSERT_X(func, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); - return func(args...); -} // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); - return call(__FUNCTION__); + return FOREWARD(); } int sixenseExit() { - auto returnCode = call(__FUNCTION__); + auto returnCode = FOREWARD(); unloadSixense(); return returnCode; } int sixenseGetMaxBases() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseSetActiveBase(int i) { - return call(__FUNCTION__, i); + return FOREWARD(i); } int sixenseIsBaseConnected(int i) { - return call(__FUNCTION__, i); + return FOREWARD(i); } int sixenseGetMaxControllers() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseIsControllerEnabled(int which) { - return call(__FUNCTION__, which); + return FOREWARD(which); } int sixenseGetNumActiveControllers() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseGetHistorySize() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseGetData(int which, int index_back, sixenseControllerData* data) { - return call(__FUNCTION__, which, index_back, data); + return FOREWARD(which, index_back, data); } int sixenseGetAllData(int index_back, sixenseAllControllerData* data) { - return call(__FUNCTION__, index_back, data); + return FOREWARD(index_back, data); } int sixenseGetNewestData(int which, sixenseControllerData* data) { - return call(__FUNCTION__, which, data); + return FOREWARD(which, data); } int sixenseGetAllNewestData(sixenseAllControllerData* data) { - return call(__FUNCTION__, data); + return FOREWARD(data); } int sixenseSetHemisphereTrackingMode(int which_controller, int state) { - return call(__FUNCTION__, which_controller, state); + return FOREWARD(which_controller, state); } int sixenseGetHemisphereTrackingMode(int which_controller, int* state) { - return call(__FUNCTION__, which_controller, state); + return FOREWARD(which_controller, state); } int sixenseAutoEnableHemisphereTracking(int which_controller) { - return call(__FUNCTION__, which_controller); + return FOREWARD(which_controller); } int sixenseSetHighPriorityBindingEnabled(int on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseGetHighPriorityBindingEnabled(int* on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) { - return call(__FUNCTION__, controller_id, duration_100ms, pattern_id); + return FOREWARD(controller_id, duration_100ms, pattern_id); } int sixenseSetFilterEnabled(int on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseGetFilterEnabled(int* on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) { - return call(__FUNCTION__, near_range, near_val, far_range, far_val); + return FOREWARD(near_range, near_val, far_range, far_val); } int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) { - return call(__FUNCTION__, near_range, near_val, far_range, far_val); + return FOREWARD(near_range, near_val, far_range, far_val); } int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) { - return call(__FUNCTION__, red, green, blue); + return FOREWARD(red, green, blue); } int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) { - return call(__FUNCTION__, red, green, blue); + return FOREWARD(red, green, blue); } #endif From 6ad20e417b41ddce22c7c13bc8d6811742b21600 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 21 Oct 2015 18:27:32 -0700 Subject: [PATCH 28/46] Typo --- .../src/input-plugins/SixenseSupportOSX.cpp | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 4c8a285d76..3ac2d197c2 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -32,7 +32,7 @@ Callable resolve(const Library& library, const char* name) { Q_ASSERT_X(function, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); return Callable { function }; } -#define FOREWARD resolve(SIXENSE, __FUNCTION__) +#define FORWARD resolve(SIXENSE, __FUNCTION__) void loadSixense() { @@ -61,90 +61,90 @@ void unloadSixense() { // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); - return FOREWARD(); + return FORWARD(); } int sixenseExit() { - auto returnCode = FOREWARD(); + auto returnCode = FORWARD(); unloadSixense(); return returnCode; } int sixenseGetMaxBases() { - return FOREWARD(); + return FORWARD(); } int sixenseSetActiveBase(int i) { - return FOREWARD(i); + return FORWARD(i); } int sixenseIsBaseConnected(int i) { - return FOREWARD(i); + return FORWARD(i); } int sixenseGetMaxControllers() { - return FOREWARD(); + return FORWARD(); } int sixenseIsControllerEnabled(int which) { - return FOREWARD(which); + return FORWARD(which); } int sixenseGetNumActiveControllers() { - return FOREWARD(); + return FORWARD(); } int sixenseGetHistorySize() { - return FOREWARD(); + return FORWARD(); } int sixenseGetData(int which, int index_back, sixenseControllerData* data) { - return FOREWARD(which, index_back, data); + return FORWARD(which, index_back, data); } int sixenseGetAllData(int index_back, sixenseAllControllerData* data) { - return FOREWARD(index_back, data); + return FORWARD(index_back, data); } int sixenseGetNewestData(int which, sixenseControllerData* data) { - return FOREWARD(which, data); + return FORWARD(which, data); } int sixenseGetAllNewestData(sixenseAllControllerData* data) { - return FOREWARD(data); + return FORWARD(data); } int sixenseSetHemisphereTrackingMode(int which_controller, int state) { - return FOREWARD(which_controller, state); + return FORWARD(which_controller, state); } int sixenseGetHemisphereTrackingMode(int which_controller, int* state) { - return FOREWARD(which_controller, state); + return FORWARD(which_controller, state); } int sixenseAutoEnableHemisphereTracking(int which_controller) { - return FOREWARD(which_controller); + return FORWARD(which_controller); } int sixenseSetHighPriorityBindingEnabled(int on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseGetHighPriorityBindingEnabled(int* on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) { - return FOREWARD(controller_id, duration_100ms, pattern_id); + return FORWARD(controller_id, duration_100ms, pattern_id); } int sixenseSetFilterEnabled(int on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseGetFilterEnabled(int* on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) { - return FOREWARD(near_range, near_val, far_range, far_val); + return FORWARD(near_range, near_val, far_range, far_val); } int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) { - return FOREWARD(near_range, near_val, far_range, far_val); + return FORWARD(near_range, near_val, far_range, far_val); } int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) { - return FOREWARD(red, green, blue); + return FORWARD(red, green, blue); } int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) { - return FOREWARD(red, green, blue); + return FORWARD(red, green, blue); } #endif From 0a65bb145b0b4a1cb9445999619b576af93578ee Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Nov 2015 11:08:20 -0800 Subject: [PATCH 29/46] Some more code cleanup --- interface/src/ui/ApplicationCompositor.cpp | 13 +------ .../src/input-plugins/SixenseManager.cpp | 38 +++++++++---------- .../src/input-plugins/SixenseManager.h | 23 +++-------- 3 files changed, 24 insertions(+), 50 deletions(-) diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 2a2a45b67b..5b7bbc2aba 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -473,18 +473,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { continue; } - int controllerButtons = 0; - - //Check for if we should toggle or drag the magnification window - if (controllerButtons & BUTTON_3) { - if (isPressed[index] == false) { - //We are now dragging the window - isPressed[index] = true; - //set the pressed time in us - pressedTime[index] = usecTimestampNow(); - stateWhenPressed[index] = _magActive[index]; - } - } else if (isPressed[index]) { + if (isPressed[index]) { isPressed[index] = false; //If the button was only pressed for < 250 ms //then disable it. diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 24b1b60356..56f79ce3c1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -24,21 +24,25 @@ Q_DECLARE_LOGGING_CATEGORY(inputplugins) Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") +#include #include #include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include "UserActivityLogger.h" +static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 +static const unsigned int BUTTON_1 = 1U << 5; +static const unsigned int BUTTON_2 = 1U << 6; +static const unsigned int BUTTON_3 = 1U << 3; +static const unsigned int BUTTON_4 = 1U << 4; +static const unsigned int BUTTON_FWD = 1U << 7; +static const unsigned int BUTTON_TRIGGER = 1U << 8; const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; -const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; const QString SixenseManager::NAME = "Sixense"; const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra"; @@ -47,7 +51,6 @@ const QString MENU_PARENT = "Avatar"; const QString MENU_NAME = "Sixense"; const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; -const float DEFAULT_REACH_LENGTH = 1.5f; bool SixenseManager::isSupported() const { #ifdef HAVE_SIXENSE @@ -65,11 +68,11 @@ bool SixenseManager::isSupported() const { void SixenseManager::activate() { InputPlugin::activate(); + #ifdef HAVE_SIXENSE - _container->addMenu(MENU_PATH); _container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, - [this] (bool clicked) { this->setSixenseFilter(clicked); }, + [this] (bool clicked) { setSixenseFilter(clicked); }, true, true); auto userInputMapper = DependencyManager::get(); @@ -96,6 +99,7 @@ void SixenseManager::deactivate() { } sixenseExit(); + saveSettings(); #endif } @@ -123,8 +127,6 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); - auto userInputMapper = DependencyManager::get(); - static const float MAX_DISCONNECTED_TIME = 2.0f; static bool disconnected { false }; static float disconnectedInterval { 0.0f }; @@ -218,9 +220,9 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // (4) assume that the orb is on a flat surface (yAxis is UP) // (5) compute the forward direction (zAxis = xAxis cross yAxis) -const float MINIMUM_ARM_REACH = 0.3f; // meters -const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters -const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired +static const float MINIMUM_ARM_REACH = 0.3f; // meters +static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters +static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired void SixenseManager::InputDevice::updateCalibration(void* controllersX) { auto controllers = reinterpret_cast(controllersX); @@ -240,14 +242,12 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) { glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); - _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; qCDebug(inputplugins, "succeess: sixense calibration"); } break; - default: _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); @@ -441,8 +441,6 @@ static const auto R2 = controller::A; static const auto R3 = controller::B; static const auto R4 = controller::Y; -using namespace controller; - controller::Input::NamedVector SixenseManager::InputDevice::getAvailableInputs() const { using namespace controller; static const Input::NamedVector availableInputs { @@ -486,7 +484,6 @@ void SixenseManager::saveSettings() const { { settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition); settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation); - settings.setValue(QString("reachLength"), QVariant(_inputDevice->_reachLength)); } settings.endGroup(); } @@ -498,7 +495,6 @@ void SixenseManager::loadSettings() { { settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition); settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation); - settings.getFloatValueIfValid(QString("reachLength"), _inputDevice->_reachLength); } settings.endGroup(); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index bf657d347b..bdfbd539cb 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -19,21 +19,10 @@ #include "InputPlugin.h" -const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 -const unsigned int BUTTON_1 = 1U << 5; -const unsigned int BUTTON_2 = 1U << 6; -const unsigned int BUTTON_3 = 1U << 3; -const unsigned int BUTTON_4 = 1U << 4; -const unsigned int BUTTON_FWD = 1U << 7; -const unsigned int BUTTON_TRIGGER = 1U << 8; - -const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; - // Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public InputPlugin { Q_OBJECT public: - // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } @@ -52,17 +41,18 @@ public: public slots: void setSixenseFilter(bool filter); -private: +private: static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s static const int CALIBRATION_STATE_IDLE = 0; static const int CALIBRATION_STATE_IN_PROGRESS = 1; static const int CALIBRATION_STATE_COMPLETE = 2; static const glm::vec3 DEFAULT_AVATAR_POSITION; static const float CONTROLLER_THRESHOLD; - static const float DEFAULT_REACH_LENGTH; - - using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; - using MovingAverageMap = std::map< int, Samples >; + + template + using SampleAverage = MovingAverage; + using Samples = std::pair, SampleAverage>; + using MovingAverageMap = std::map; class InputDevice : public controller::InputDevice { public: @@ -88,7 +78,6 @@ private: glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame glm::quat _avatarRotation; // in hydra-frame - float _reachLength { DEFAULT_REACH_LENGTH }; float _lastDistance; // these are measured values used to compute the calibration results quint64 _lockExpiry; From d95709ee731aaa9fa031963e021b10d32062ddfc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 16:45:23 -0800 Subject: [PATCH 30/46] Added input plugins logging category --- .../src/input-plugins/InputPluginsLogging.cpp | 14 ++++++++++++++ .../src/input-plugins/InputPluginsLogging.h | 18 ++++++++++++++++++ .../src/input-plugins/SixenseManager.cpp | 5 ----- .../src/input-plugins/SixenseSupportOSX.cpp | 2 ++ 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp create mode 100644 libraries/input-plugins/src/input-plugins/InputPluginsLogging.h diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp new file mode 100644 index 0000000000..c96941a8ca --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp @@ -0,0 +1,14 @@ +// +// InputPluginsLogging.cpp +// +// +// Created by Clement on 11/6/15. +// Copyright 2015 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 "InputPluginsLogging.h" + +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h new file mode 100644 index 0000000000..6460469190 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h @@ -0,0 +1,18 @@ +// +// InputPluginsLogging.h +// +// +// Created by Clement on 11/6/15. +// Copyright 2015 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_InputPluginsLogging_h +#define hifi_InputPluginsLogging_h + +#include +Q_DECLARE_LOGGING_CATEGORY(inputplugins) + +#endif // hifi_InputPluginsLogging_h diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 56f79ce3c1..9340cbf950 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -19,11 +19,6 @@ #include #include -// TODO: This should not be here -#include -Q_DECLARE_LOGGING_CATEGORY(inputplugins) -Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") - #include #include #include diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 3ac2d197c2..4a4eab2ee1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -16,6 +16,8 @@ #include #include +#include "InputPluginsLogging.h" + using Library = std::unique_ptr; static Library SIXENSE; From 6323728d7a5092073ec445663ff2d8ea276ad8b1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 16:56:48 -0800 Subject: [PATCH 31/46] Added perfect forwarding of arguments --- .../src/input-plugins/SixenseManager.cpp | 4 +++- .../src/input-plugins/SixenseSupportOSX.cpp | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 9340cbf950..3085b377f0 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -12,7 +12,7 @@ #include "SixenseManager.h" #ifdef HAVE_SIXENSE -#include "sixense.h" +#include #endif #include @@ -28,6 +28,8 @@ #include #include +#include "InputPluginsLogging.h" + static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 static const unsigned int BUTTON_1 = 1U << 5; static const unsigned int BUTTON_2 = 1U << 6; diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 4a4eab2ee1..378c99de76 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -9,11 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Mock implementation of sixense.h to hide dynamic linking on OS X #if defined(__APPLE__) && defined(HAVE_SIXENSE) -#include "sixense.h" +#include + +#include #include -#include #include #include "InputPluginsLogging.h" @@ -22,8 +24,9 @@ using Library = std::unique_ptr; static Library SIXENSE; struct Callable { - template int operator() (Args... args){ - return reinterpret_cast(function)(args...); + template + int operator() (Args&&... args){ + return reinterpret_cast(function)(std::forward(args)...); } QFunctionPointer function; }; From 0c60538952e82a3b0ce5afe338ca490ec88048ee Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 17:47:02 -0800 Subject: [PATCH 32/46] Ensure that dependencies of runtime plugins are included in the DLL copy --- cmake/macros/CopyDllsBesideWindowsExecutable.cmake | 7 +++++++ cmake/templates/FixupBundlePostBuild.cmake.in | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake b/cmake/macros/CopyDllsBesideWindowsExecutable.cmake index b57c781eff..69fd20a57b 100644 --- a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake +++ b/cmake/macros/CopyDllsBesideWindowsExecutable.cmake @@ -18,12 +18,19 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE) @ONLY ) + if (APPLE) + set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins") + else() + set(PLUGIN_PATH "plugins") + endif() + # add a post-build command to copy DLLs beside the executable add_custom_command( TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -DBUNDLE_EXECUTABLE=$ + -DBUNDLE_PLUGIN_DIR=$/${PLUGIN_PATH} -P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake ) diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index 4afe4de403..ee686a6967 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -41,4 +41,16 @@ function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) endfunction() message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@") -fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@") \ No newline at end of file + +message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}") + +if (APPLE) + set(PLUGIN_EXTENSION "dylib") +elseif (WIN32) + set(PLUGIN_EXTENSION "dll") +else() + set(PLUGIN_EXTENSION "so") +endif() + +file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") +fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@") \ No newline at end of file From 11733c039f5311c09de192e76d286cd9298f7269 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 17:16:28 -0800 Subject: [PATCH 33/46] Allow shared libraries to access core global objects --- interface/src/Menu.cpp | 17 +++---------- interface/src/Menu.h | 4 +--- libraries/shared/src/DependencyManager.cpp | 9 ++++++- libraries/shared/src/DependencyManager.h | 22 ++++++++--------- libraries/shared/src/SharedUtil.h | 28 +++++++++++++++++++++- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 92ff39a489..2d60b9d79f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -44,22 +44,11 @@ #include "Menu.h" -Menu* Menu::_instance = NULL; +static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; Menu* Menu::getInstance() { - static QMutex menuInstanceMutex; - - // lock the menu instance mutex to make sure we don't race and create two menus and crash - menuInstanceMutex.lock(); - - if (!_instance) { - qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); - _instance = new Menu(); - } - - menuInstanceMutex.unlock(); - - return _instance; + static Menu& instance = globalInstace(MENU_PROPERTY_NAME); + return &instance; } Menu::Menu() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 162fad1b9f..dfa2cfa41b 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -57,6 +57,7 @@ private: class Menu : public QMenuBar { Q_OBJECT public: + Menu(); static Menu* getInstance(); void loadSettings(); @@ -103,9 +104,6 @@ public slots: void setIsOptionChecked(const QString& menuOption, bool isChecked); private: - static Menu* _instance; - Menu(); - typedef void(*settingsAction)(Settings&, QAction&); static void loadAction(Settings& settings, QAction& action); static void saveAction(Settings& settings, QAction& action); diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index 5f78f6bcd5..54d4e08259 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -11,7 +11,14 @@ #include "DependencyManager.h" -DependencyManager DependencyManager::_manager; +#include "SharedUtil.h" + +static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger"; + +DependencyManager& DependencyManager::manager() { + static DependencyManager& instance = globalInstace(DEPENDENCY_PROPERTY_NAME); + return instance; +} QSharedPointer& DependencyManager::safeGet(size_t hashCode) { return _instanceHash[hashCode]; diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index db41e72e1e..c0568bc752 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -62,8 +62,8 @@ public: static void registerInheritance(); private: - static DependencyManager _manager; - + static DependencyManager& manager(); + template size_t getHashCode(); @@ -75,11 +75,11 @@ private: template QSharedPointer DependencyManager::get() { - static size_t hashCode = _manager.getHashCode(); + static size_t hashCode = manager().getHashCode(); static QWeakPointer instance; if (instance.isNull()) { - instance = qSharedPointerCast(_manager.safeGet(hashCode)); + instance = qSharedPointerCast(manager().safeGet(hashCode)); if (instance.isNull()) { qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name(); @@ -91,9 +91,9 @@ QSharedPointer DependencyManager::get() { template QSharedPointer DependencyManager::set(Args&&... args) { - static size_t hashCode = _manager.getHashCode(); + static size_t hashCode = manager().getHashCode(); - QSharedPointer& instance = _manager.safeGet(hashCode); + QSharedPointer& instance = manager().safeGet(hashCode); instance.clear(); // Clear instance before creation of new one to avoid edge cases QSharedPointer newInstance(new T(args...), &T::customDeleter); QSharedPointer storedInstance = qSharedPointerCast(newInstance); @@ -104,9 +104,9 @@ QSharedPointer DependencyManager::set(Args&&... args) { template QSharedPointer DependencyManager::set(Args&&... args) { - static size_t hashCode = _manager.getHashCode(); + static size_t hashCode = manager().getHashCode(); - QSharedPointer& instance = _manager.safeGet(hashCode); + QSharedPointer& instance = manager().safeGet(hashCode); instance.clear(); // Clear instance before creation of new one to avoid edge cases QSharedPointer newInstance(new I(args...), &I::customDeleter); QSharedPointer storedInstance = qSharedPointerCast(newInstance); @@ -117,15 +117,15 @@ QSharedPointer DependencyManager::set(Args&&... args) { template void DependencyManager::destroy() { - static size_t hashCode = _manager.getHashCode(); - _manager.safeGet(hashCode).clear(); + static size_t hashCode = manager().getHashCode(); + manager().safeGet(hashCode).clear(); } template void DependencyManager::registerInheritance() { size_t baseHashCode = typeid(Base).hash_code(); size_t derivedHashCode = typeid(Derived).hash_code(); - _manager._inheritanceHash.insert(baseHashCode, derivedHashCode); + manager()._inheritanceHash.insert(baseHashCode, derivedHashCode); } template diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index cd4f734d40..4c547417c8 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -13,6 +13,7 @@ #define hifi_SharedUtil_h #include +#include #include #include @@ -20,7 +21,32 @@ #include // not on windows, not needed for mac or windows #endif -#include +#include +#include + +// Provides efficient access to a named global type. By storing the value +// in the QApplication by name we can implement the singleton pattern and +// have the single instance function across DLL boundaries. +template +T& globalInstace(const char* propertyName, Args&&... args) { + static T *instance { nullptr }; + static std::mutex mutex; + if (!instance) { + std::unique_lock lock(mutex); + if (!instance) { + auto variant = qApp->property(propertyName); + if (variant.isNull()) { + auto* typedInstance = new T(args...); + void* voidInstance = typedInstance; + variant = QVariant::fromValue(voidInstance); + qApp->setProperty(propertyName, variant); + } + void* returnedVoidInstance = variant.value(); + instance = static_cast(returnedVoidInstance); + } + } + return *instance; +} const int BYTES_PER_COLOR = 3; const int BYTES_PER_FLAGS = 1; From 37a9538f104b6efec4b7cfc34f9474c6ba746be4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 19:10:31 -0800 Subject: [PATCH 34/46] PR feedback, ensure destruction of shared objects --- interface/src/Menu.cpp | 4 ++-- libraries/shared/src/DependencyManager.cpp | 5 +++-- libraries/shared/src/SharedUtil.h | 19 +++++++++++-------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2d60b9d79f..3ed31e609a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -47,8 +47,8 @@ static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; Menu* Menu::getInstance() { - static Menu& instance = globalInstace(MENU_PROPERTY_NAME); - return &instance; + static Menu* instance = globalInstace(MENU_PROPERTY_NAME); + return instance; } Menu::Menu() { diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index 54d4e08259..5100d99691 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -12,12 +12,13 @@ #include "DependencyManager.h" #include "SharedUtil.h" +#include "Finally.h" static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger"; DependencyManager& DependencyManager::manager() { - static DependencyManager& instance = globalInstace(DEPENDENCY_PROPERTY_NAME); - return instance; + static DependencyManager* instance = globalInstace(DEPENDENCY_PROPERTY_NAME); + return *instance; } QSharedPointer& DependencyManager::safeGet(size_t hashCode) { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 4c547417c8..097444a62c 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -28,24 +28,27 @@ // in the QApplication by name we can implement the singleton pattern and // have the single instance function across DLL boundaries. template -T& globalInstace(const char* propertyName, Args&&... args) { - static T *instance { nullptr }; +T* globalInstace(const char* propertyName, Args&&... args) { + static std::shared_ptr instancePtr; + static T *resultInstance { nullptr }; static std::mutex mutex; - if (!instance) { + if (!resultInstance) { std::unique_lock lock(mutex); - if (!instance) { + if (!resultInstance) { auto variant = qApp->property(propertyName); if (variant.isNull()) { - auto* typedInstance = new T(args...); - void* voidInstance = typedInstance; + // Since we're building the object, store it in a shared_ptr so it's + // destroyed by the destructor of the static instancePtr + instancePtr = std::make_shared(args...); + void* voidInstance = &(*instancePtr); variant = QVariant::fromValue(voidInstance); qApp->setProperty(propertyName, variant); } void* returnedVoidInstance = variant.value(); - instance = static_cast(returnedVoidInstance); + resultInstance = static_cast(returnedVoidInstance); } } - return *instance; + return resultInstance; } const int BYTES_PER_COLOR = 3; From 314c11e8d42760d6afde193410bfcd5ce2501096 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 6 Nov 2015 16:59:46 -0800 Subject: [PATCH 35/46] Make life easier for Sam. --- libraries/animation/src/Rig.cpp | 4 ++++ libraries/avatars/src/AvatarData.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4d66097265..7926b268b5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -300,6 +300,8 @@ void Rig::setJointAnimatinoPriority(int index, float newPriority) { } } +// Deprecated. +// WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; @@ -350,6 +352,8 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const return true; } +// Deprecated. +// WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3abd63bf63..9079f15f53 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -240,7 +240,8 @@ public: Q_INVOKABLE void setHandState(char s) { _handState = s; } Q_INVOKABLE char getHandState() const { return _handState; } - const QVector& getJointData() const { return _jointData; } + const QVector& getRawJointData() const { return _jointData; } + void setRawJointData(QVector data) { _jointData = data; } Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); From d8f169bd4e11c535a70fa6df6b01760f1ccf105e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 17:04:55 -0800 Subject: [PATCH 36/46] add collision sounds to bballs --- unpublishedScripts/basketballsResetter.js | 2 +- unpublishedScripts/hiddenEntityReset.js | 3 ++- unpublishedScripts/masterReset.js | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/unpublishedScripts/basketballsResetter.js b/unpublishedScripts/basketballsResetter.js index 4d02296e8e..791ac1c0cf 100644 --- a/unpublishedScripts/basketballsResetter.js +++ b/unpublishedScripts/basketballsResetter.js @@ -82,7 +82,7 @@ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; z: 0 }, collisionsWillMove: true, - collisionsSoundURL: basketballCollisionSoundURL, + collisionSoundURL: basketballCollisionSoundURL, ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index ef0557a54f..0773cc5640 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -308,6 +308,7 @@ z: 0 }, collisionsWillMove: true, + collisionSoundURL: 'http://hifi-public.s3.amazonaws.com/sounds/basketball/basketball.wav', ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ @@ -1258,4 +1259,4 @@ }; // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); +}); \ No newline at end of file diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index a8e156617a..aa7a1912de 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -248,7 +248,7 @@ MasterReset = function() { }, grabbableKey: { grabbable: false, - wantsTrigger:true + wantsTrigger: true } }) }); @@ -289,6 +289,7 @@ MasterReset = function() { z: 0 }, collisionsWillMove: true, + collisionSoundURL: 'http://hifi-public.s3.amazonaws.com/sounds/basketball/basketball.wav', ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ @@ -334,7 +335,7 @@ MasterReset = function() { name: "Basketball Resetter", script: basketballResetterScriptURL, dimensions: dimensions, - visible:false, + visible: false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -367,7 +368,7 @@ MasterReset = function() { name: "Target Resetter", script: targetsResetterScriptURL, dimensions: dimensions, - visible:false, + visible: false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -1238,4 +1239,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; +}; \ No newline at end of file From 137a2c1c48bfe67d5f4713342297cf1ce1176a07 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 17:15:34 -0800 Subject: [PATCH 37/46] Fix file headers --- .../input-plugins/src/input-plugins/InputPluginsLogging.cpp | 2 +- libraries/input-plugins/src/input-plugins/InputPluginsLogging.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp index c96941a8ca..43a708e5c7 100644 --- a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp @@ -1,6 +1,6 @@ // // InputPluginsLogging.cpp -// +// libraries/input-plugins/src/input-plugins // // Created by Clement on 11/6/15. // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h index 6460469190..f82ffdbe2e 100644 --- a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h @@ -1,6 +1,6 @@ // // InputPluginsLogging.h -// +// libraries/input-plugins/src/input-plugins // // Created by Clement on 11/6/15. // Copyright 2015 High Fidelity, Inc. From 6122fa145db9beadb66581223c80a48cc11901fc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 17:02:56 -0800 Subject: [PATCH 38/46] More PR feedback --- interface/src/Menu.cpp | 2 +- libraries/shared/src/DependencyManager.cpp | 4 ++-- libraries/shared/src/SharedUtil.h | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3ed31e609a..24033325f6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -47,7 +47,7 @@ static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; Menu* Menu::getInstance() { - static Menu* instance = globalInstace(MENU_PROPERTY_NAME); + static Menu* instance = globalInstance(MENU_PROPERTY_NAME); return instance; } diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index 5100d99691..a870feab98 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -17,10 +17,10 @@ static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger"; DependencyManager& DependencyManager::manager() { - static DependencyManager* instance = globalInstace(DEPENDENCY_PROPERTY_NAME); + static DependencyManager* instance = globalInstance(DEPENDENCY_PROPERTY_NAME); return *instance; } QSharedPointer& DependencyManager::safeGet(size_t hashCode) { return _instanceHash[hashCode]; -} \ No newline at end of file +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 097444a62c..89ccba1479 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -27,10 +27,10 @@ // Provides efficient access to a named global type. By storing the value // in the QApplication by name we can implement the singleton pattern and // have the single instance function across DLL boundaries. -template -T* globalInstace(const char* propertyName, Args&&... args) { - static std::shared_ptr instancePtr; - static T *resultInstance { nullptr }; +template +T* globalInstance(const char* propertyName, Args&&... args) { + static std::unique_ptr instancePtr; + static T* resultInstance { nullptr }; static std::mutex mutex; if (!resultInstance) { std::unique_lock lock(mutex); @@ -39,7 +39,8 @@ T* globalInstace(const char* propertyName, Args&&... args) { if (variant.isNull()) { // Since we're building the object, store it in a shared_ptr so it's // destroyed by the destructor of the static instancePtr - instancePtr = std::make_shared(args...); + instancePtr = std::unique_ptr(new T(std::forward(args)...)); + void* voidInstance = &(*instancePtr); variant = QVariant::fromValue(voidInstance); qApp->setProperty(propertyName, variant); From 68a72b0f4161ea565bbd5f2a4d19efb74aecfbcd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 17:22:12 -0800 Subject: [PATCH 39/46] Bit of cleanup --- .../src/input-plugins/SixenseSupportOSX.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 378c99de76..f6cec5d67f 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -20,6 +20,10 @@ #include "InputPluginsLogging.h" +#ifndef SIXENSE_LIB_FILENAME +#define SIXENSE_LIB_FILENAME QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64" +#endif + using Library = std::unique_ptr; static Library SIXENSE; @@ -41,15 +45,9 @@ Callable resolve(const Library& library, const char* name) { void loadSixense() { - if (!SIXENSE) { - static const QString LIBRARY_PATH = -#ifdef SIXENSE_LIB_FILENAME - SIXENSE_LIB_FILENAME; -#else - QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"; -#endif - SIXENSE.reset(new QLibrary(LIBRARY_PATH)); - } + Q_ASSERT_X(!(SIXENSE && SIXENSE->isLoaded()), __FUNCTION__, "Sixense library already loaded"); + SIXENSE.reset(new QLibrary(SIXENSE_LIB_FILENAME)); + Q_CHECK_PTR(SIXENSE); if (SIXENSE->load()){ qDebug() << "Loaded sixense library for hydra support -" << SIXENSE->fileName(); From 4fbff5d667ee2f5c9a4022802d37993eb4ada7a4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 21:39:53 -0800 Subject: [PATCH 40/46] Update usertimingExample.js --- examples/libraries/usertimingExample.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/libraries/usertimingExample.js b/examples/libraries/usertimingExample.js index a43496a6b9..a1a0800d3d 100644 --- a/examples/libraries/usertimingExample.js +++ b/examples/libraries/usertimingExample.js @@ -1,16 +1,16 @@ Script.include('usertiming.js'); var timing = loadUserTiming(); //set a mark -timing.performance.mark('mark_fully_loaded') +timing.performance.mark('firstMark') + +//do something that takes time -- we're just going to set a timeout here as an example -var count = 0; Script.setTimeout(function() { - //do something that takes time //and set another mark - timing.performance.mark('mark_fully_loaded2') + timing.performance.mark('secondMark') //measure time between marks (first parameter is a name for the measurement) - timing.performance.measure('howlong', 'mark_fully_loaded', 'mark_fully_loaded2'); - //you can also get the marks, but the measure is usually what you're looking for - var items = timing.performance.getEntriesByType('measure'); - print('items is:::' + JSON.stringify(items)) -}, 1000) \ No newline at end of file + timing.performance.measure('howlong', 'firstMark', 'secondMark'); + //you can also get the marks by changing the type below + var measures = timing.performance.getEntriesByType('measure'); + print('measures:::' + JSON.stringify(measures)) +}, 1000) From 369eec2abdadae81760536359acd8e79935c8ebd Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 22:05:30 -0800 Subject: [PATCH 41/46] Update usertimingExample.js --- examples/libraries/usertimingExample.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/libraries/usertimingExample.js b/examples/libraries/usertimingExample.js index a1a0800d3d..cceb43435a 100644 --- a/examples/libraries/usertimingExample.js +++ b/examples/libraries/usertimingExample.js @@ -1,16 +1,18 @@ Script.include('usertiming.js'); var timing = loadUserTiming(); //set a mark -timing.performance.mark('firstMark') +timing.performance.mark('firstMark'); //do something that takes time -- we're just going to set a timeout here as an example Script.setTimeout(function() { //and set another mark - timing.performance.mark('secondMark') - //measure time between marks (first parameter is a name for the measurement) + timing.performance.mark('secondMark'); + + //measure time between marks (first parameter is a name for the measurement) timing.performance.measure('howlong', 'firstMark', 'secondMark'); - //you can also get the marks by changing the type below + + //you can also get the marks by changing the type var measures = timing.performance.getEntriesByType('measure'); print('measures:::' + JSON.stringify(measures)) }, 1000) From 34d98cac5eca2f8f0fcdb09a70e2cbf9ea1f45de Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 22:12:03 -0800 Subject: [PATCH 42/46] Update usertiming.js --- examples/libraries/usertiming.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/libraries/usertiming.js b/examples/libraries/usertiming.js index f477ed0bb7..9351972e37 100644 --- a/examples/libraries/usertiming.js +++ b/examples/libraries/usertiming.js @@ -3,7 +3,6 @@ // // A polyfill for UserTiming (http://www.w3.org/TR/user-timing/) // -// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015 // Copyright 2013 Nic Jansma // http://nicj.net // @@ -11,6 +10,7 @@ // // Licensed under the MIT license // +// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015 function userTiming() { "use strict"; @@ -544,4 +544,4 @@ function userTiming() { loadUserTiming = function() { return userTiming(); -} \ No newline at end of file +} From bf4320e5ccd507d15e7b5367399f2570f3a54b77 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 22:55:33 -0800 Subject: [PATCH 43/46] re-add this example --- examples/controllers/rightClickExample.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js new file mode 100644 index 0000000000..c3e6ea8f3d --- /dev/null +++ b/examples/controllers/rightClickExample.js @@ -0,0 +1,10 @@ +var MAPPING_NAME = "com.highfidelity.rightClickExample"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); +}); +Controller.enableMapping(MAPPING_NAME); + +Script.scriptEnding.connect(function () { + Controller.disableMapping(MAPPING_NAME); +}); \ No newline at end of file From 2ecc4f8a5c88dd32d3d3ba05f8a01c3a119d23f1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 9 Nov 2015 08:58:24 -0800 Subject: [PATCH 44/46] Prevent sixense manager from triggering continuous reset --- .../src/input-plugins/SixenseManager.cpp | 37 +++++++++---------- .../src/input-plugins/SixenseManager.h | 18 ++++++++- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3085b377f0..1413ef0ce1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -108,19 +108,13 @@ void SixenseManager::setSixenseFilter(bool filter) { void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { _inputDevice->update(deltaTime, jointsCaptured); - if (_inputDevice->_calibrationState == CALIBRATION_STATE_COMPLETE) { + if (_inputDevice->_requestReset) { _container->requestReset(); - _inputDevice->_calibrationState = CALIBRATION_STATE_IDLE; + _inputDevice->_requestReset = false; } } void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { - // FIXME - Some of the code in update() will crash if you haven't actually activated the - // plugin. But we want register with the UserInputMapper if we don't call this. - // We need to clean this up. - //if (!_activated) { - // return; - //} #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); @@ -221,21 +215,23 @@ static const float MINIMUM_ARM_REACH = 0.3f; // meters static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired -void SixenseManager::InputDevice::updateCalibration(void* controllersX) { - auto controllers = reinterpret_cast(controllersX); +static bool calibrationRequested(sixenseControllerData* controllers) { + return (controllers[0].buttons == BUTTON_FWD && controllers[1].buttons == BUTTON_FWD); +} + +void SixenseManager::InputDevice::updateCalibration(sixenseControllerData* controllers) { const sixenseControllerData* dataLeft = controllers; const sixenseControllerData* dataRight = controllers + 1; - // calibration only happpens while both hands are holding BUTTON_FORWARD - if (dataLeft->buttons != BUTTON_FWD || dataRight->buttons != BUTTON_FWD) { - if (_calibrationState == CALIBRATION_STATE_IDLE) { - return; - } + // Calibration buttons aren't set, so check the state, and request a reset if necessary. + if (!calibrationRequested(controllers)) { switch (_calibrationState) { + case CALIBRATION_STATE_IDLE: + return; case CALIBRATION_STATE_COMPLETE: { // compute calibration results - _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands + _avatarPosition = -0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); @@ -243,16 +239,19 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) { const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; qCDebug(inputplugins, "succeess: sixense calibration"); + _requestReset = true; } break; default: - _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); break; } + + _calibrationState = CALIBRATION_STATE_IDLE; return; } + // Calibration buttons are set, continue calibration work // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. const float* pos = dataLeft->pos; glm::vec3 positionLeft(pos[0], pos[1], pos[2]); @@ -261,6 +260,7 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) { glm::vec3 positionRight(pos[0], pos[1], pos[2]); positionRight *= METERS_PER_MILLIMETER; + // Gather initial calibration data if (_calibrationState == CALIBRATION_STATE_IDLE) { float reach = glm::distance(positionLeft, positionRight); if (reach > 2.0f * MINIMUM_ARM_REACH) { @@ -308,9 +308,6 @@ void SixenseManager::InputDevice::focusOutEvent() { _buttonPressedMap.clear(); }; -void SixenseManager::InputDevice::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { -} - void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { using namespace controller; if (buttons & BUTTON_0) { diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index bdfbd539cb..8452e1f096 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -19,6 +19,20 @@ #include "InputPlugin.h" +class QLibrary; + +const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 +const unsigned int BUTTON_1 = 1U << 5; +const unsigned int BUTTON_2 = 1U << 6; +const unsigned int BUTTON_3 = 1U << 3; +const unsigned int BUTTON_4 = 1U << 4; +const unsigned int BUTTON_FWD = 1U << 7; +const unsigned int BUTTON_TRIGGER = 1U << 8; + +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; + +struct _sixenseControllerData; + // Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public InputPlugin { Q_OBJECT @@ -65,9 +79,8 @@ private: virtual void focusOutEvent() override; void handleButtonEvent(unsigned int buttons, bool left); - void handleAxisEvent(float x, float y, float trigger, bool left); void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - void updateCalibration(void* controllers); + void updateCalibration(_sixenseControllerData* controllers); friend class SixenseManager; @@ -79,6 +92,7 @@ private: glm::quat _avatarRotation; // in hydra-frame float _lastDistance; + bool _requestReset { false }; // these are measured values used to compute the calibration results quint64 _lockExpiry; glm::vec3 _averageLeft; From 07be03dc7e244ec4dcc27f18fcc1c595deff5a93 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 9 Nov 2015 09:27:31 -0800 Subject: [PATCH 45/46] Bad merge fix, PR feedback --- .../src/input-plugins/SixenseManager.cpp | 16 ++++++++-------- .../src/input-plugins/SixenseManager.h | 15 ++------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 1413ef0ce1..18fdc9ddad 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -147,14 +147,14 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { int maxControllers = sixenseGetMaxControllers(); // we only support two controllers - sixenseControllerData controllers[2]; + SixenseControllerData controllers[2]; int numActiveControllers = 0; for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) { if (!sixenseIsControllerEnabled(i)) { continue; } - sixenseControllerData* data = controllers + numActiveControllers; + SixenseControllerData* data = controllers + numActiveControllers; ++numActiveControllers; sixenseGetNewestData(i, data); @@ -215,21 +215,21 @@ static const float MINIMUM_ARM_REACH = 0.3f; // meters 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) { +static bool calibrationRequested(SixenseControllerData* controllers) { return (controllers[0].buttons == BUTTON_FWD && controllers[1].buttons == BUTTON_FWD); } -void SixenseManager::InputDevice::updateCalibration(sixenseControllerData* controllers) { - const sixenseControllerData* dataLeft = controllers; - const sixenseControllerData* dataRight = controllers + 1; +void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* controllers) { + const SixenseControllerData* dataLeft = controllers; + const SixenseControllerData* dataRight = controllers + 1; // Calibration buttons aren't set, so check the state, and request a reset if necessary. if (!calibrationRequested(controllers)) { switch (_calibrationState) { case CALIBRATION_STATE_IDLE: return; - case CALIBRATION_STATE_COMPLETE: - { + + case CALIBRATION_STATE_COMPLETE: { // compute calibration results _avatarPosition = -0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 8452e1f096..348a7a4590 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -19,19 +19,8 @@ #include "InputPlugin.h" -class QLibrary; - -const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 -const unsigned int BUTTON_1 = 1U << 5; -const unsigned int BUTTON_2 = 1U << 6; -const unsigned int BUTTON_3 = 1U << 3; -const unsigned int BUTTON_4 = 1U << 4; -const unsigned int BUTTON_FWD = 1U << 7; -const unsigned int BUTTON_TRIGGER = 1U << 8; - -const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; - struct _sixenseControllerData; +using SixenseControllerData = _sixenseControllerData; // Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public InputPlugin { @@ -80,7 +69,7 @@ private: void handleButtonEvent(unsigned int buttons, bool left); void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - void updateCalibration(_sixenseControllerData* controllers); + void updateCalibration(SixenseControllerData* controllers); friend class SixenseManager; From ef7caa8da9ddd02763b06978ce0c8359f7ec7c6e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 9 Nov 2015 10:48:12 -0800 Subject: [PATCH 46/46] Add new collision hull and sound to ping pong gun --- examples/toybox/ping_pong_gun/createPingPongGun.js | 10 +++++----- unpublishedScripts/hiddenEntityReset.js | 8 +++++--- unpublishedScripts/masterReset.js | 8 +++++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index cfeaba7f4e..9639f75320 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -14,8 +14,8 @@ Script.include("../../utilities.js"); var scriptURL = Script.resolvePath('pingPongGun.js'); var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx' -var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - +var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj'; +var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav'; var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, @@ -25,9 +25,8 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType:'box', - // shapeType: 'compound', - // compoundShapeURL: COLLISION_HULL_URL, + shapeType: 'compound', + compoundShapeURL: COLLISION_HULL_URL, script: scriptURL, position: center, dimensions: { @@ -36,6 +35,7 @@ var pingPongGun = Entities.addEntity({ z: 0.47 }, collisionsWillMove: true, + collisionSoundURL: COLLISION_SOUND_URL }); function cleanUp() { diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 0773cc5640..3dd66580c5 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -887,8 +887,8 @@ function createPingPongBallGun() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj'; + var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav'; var position = { x: 548.6, y: 495.4, @@ -900,7 +900,8 @@ var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType: 'box', + shapeType: 'compound', + compoundShapeURL: COLLISION_SOUND_URL, script: pingPongScriptURL, position: position, rotation: rotation, @@ -915,6 +916,7 @@ z: 0.47 }, collisionsWillMove: true, + collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ resetMe: { resetMe: true diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index aa7a1912de..7236d04a9d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -869,8 +869,8 @@ MasterReset = function() { function createPingPongBallGun() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj'; + var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav'; var position = { x: 548.6, y: 495.4, @@ -882,7 +882,8 @@ MasterReset = function() { var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType: 'box', + shapeType: 'compound', + compoundShapeURL:COLLISION_SOUND_URL, script: pingPongScriptURL, position: position, rotation: rotation, @@ -897,6 +898,7 @@ MasterReset = function() { z: 0.47 }, collisionsWillMove: true, + collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ resetMe: { resetMe: true