diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2155a11d91..35738c56b1 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -136,7 +136,7 @@ Item { Text { color: root.fontColor; font.pixelSize: root.fontSize - text: "Velocity: " + root.velocity.toFixed(1) + text: "Speed: " + root.speed.toFixed(1) } Text { color: root.fontColor; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f5765f9259..3e54ba99b9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1085,6 +1085,8 @@ void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { nextAttitude(position, orientation); _bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix); + setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity()); + // now that physics has adjusted our position, we can update attachements. Avatar::simulateAttachments(deltaTime); } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ae9e37b591..1ee26a865d 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -162,7 +162,7 @@ void Stats::updateStats(bool force) { MyAvatar* myAvatar = avatarManager->getMyAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); STAT_UPDATE(position, QVector3D(avatarPos.x, avatarPos.y, avatarPos.z)); - STAT_UPDATE_FLOAT(velocity, glm::length(myAvatar->getVelocity()), 0.1f); + STAT_UPDATE_FLOAT(speed, glm::length(myAvatar->getVelocity()), 0.1f); STAT_UPDATE_FLOAT(yaw, myAvatar->getBodyYaw(), 0.1f); if (_expanded || force) { SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index fc01fe7f23..bebfbf6f70 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -47,7 +47,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, entitiesPing, 0) STATS_PROPERTY(int, assetPing, 0) STATS_PROPERTY(QVector3D, position, QVector3D(0, 0, 0) ) - STATS_PROPERTY(float, velocity, 0) + STATS_PROPERTY(float, speed, 0) STATS_PROPERTY(float, yaw, 0) STATS_PROPERTY(int, avatarMixerInKbps, 0) STATS_PROPERTY(int, avatarMixerInPps, 0) @@ -138,7 +138,7 @@ signals: void entitiesPingChanged(); void assetPingChanged(); void positionChanged(); - void velocityChanged(); + void speedChanged(); void yawChanged(); void avatarMixerInKbpsChanged(); void avatarMixerInPpsChanged(); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index e47a12960e..f9c8873779 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -246,7 +246,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector MIN_HIPS_OFFSET_LENGTH) { // but only if offset is long enough float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength); - _relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset; + if (_hipsParentIndex == -1) { + // the hips are the root so _hipsOffset is in the correct frame + _relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset; + } else { + // the hips are NOT the root so we need to transform _hipsOffset into hips local-frame + glm::quat hipsFrameRotation = _relativePoses[_hipsParentIndex].rot; + int index = _skeleton->getParentIndex(_hipsParentIndex); + while (index != -1) { + hipsFrameRotation *= _relativePoses[index].rot; + index = _skeleton->getParentIndex(index); + } + _relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + + glm::inverse(glm::normalize(hipsFrameRotation)) * (scaleFactor * _hipsOffset); + } } solveWithCyclicCoordinateDescent(targets); @@ -621,7 +634,7 @@ void AnimInverseKinematics::initConstraints() { } else if (baseName.startsWith("Spine", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_SPINE_TWIST = PI / 8.0f; + const float MAX_SPINE_TWIST = PI / 12.0f; stConstraint->setTwistLimits(-MAX_SPINE_TWIST, MAX_SPINE_TWIST); std::vector minDots; @@ -645,11 +658,11 @@ void AnimInverseKinematics::initConstraints() { } else if (0 == baseName.compare("Neck", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_NECK_TWIST = PI / 6.0f; + const float MAX_NECK_TWIST = PI / 9.0f; stConstraint->setTwistLimits(-MAX_NECK_TWIST, MAX_NECK_TWIST); std::vector minDots; - const float MAX_NECK_SWING = PI / 4.0f; + const float MAX_NECK_SWING = PI / 8.0f; minDots.push_back(cosf(MAX_NECK_SWING)); stConstraint->setSwingLimits(minDots); @@ -657,11 +670,11 @@ void AnimInverseKinematics::initConstraints() { } else if (0 == baseName.compare("Head", Qt::CaseInsensitive)) { SwingTwistConstraint* stConstraint = new SwingTwistConstraint(); stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot); - const float MAX_HEAD_TWIST = PI / 8.0f; + const float MAX_HEAD_TWIST = PI / 9.0f; stConstraint->setTwistLimits(-MAX_HEAD_TWIST, MAX_HEAD_TWIST); std::vector minDots; - const float MAX_HEAD_SWING = PI / 6.0f; + const float MAX_HEAD_SWING = PI / 10.0f; minDots.push_back(cosf(MAX_HEAD_SWING)); stConstraint->setSwingLimits(minDots); @@ -775,9 +788,13 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele initConstraints(); _headIndex = _skeleton->nameToJointIndex("Head"); _hipsIndex = _skeleton->nameToJointIndex("Hips"); + + // also cache the _hipsParentIndex for later + _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); } else { clearConstraints(); _headIndex = -1; _hipsIndex = -1; + _hipsParentIndex = -1; } } diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index 971d2d5ff1..825577b1ae 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -79,13 +79,14 @@ protected: AnimPoseVec _relativePoses; // current relative poses // experimental data for moving hips during IK - int _headIndex = -1; - int _hipsIndex = -1; - glm::vec3 _hipsOffset = Vectors::ZERO; + glm::vec3 _hipsOffset { Vectors::ZERO }; + int _headIndex { -1 }; + int _hipsIndex { -1 }; + int _hipsParentIndex { -1 }; // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // during the the cyclic coordinate descent algorithm - int _maxTargetIndex = 0; + int _maxTargetIndex { 0 }; }; #endif // hifi_AnimInverseKinematics_h diff --git a/libraries/animation/src/AnimationLoop.cpp b/libraries/animation/src/AnimationLoop.cpp index 1e04af5dfa..f6a2877966 100644 --- a/libraries/animation/src/AnimationLoop.cpp +++ b/libraries/animation/src/AnimationLoop.cpp @@ -18,7 +18,7 @@ const float AnimationLoop::MAXIMUM_POSSIBLE_FRAME = 100000.0f; AnimationLoop::AnimationLoop() : _fps(30.0f), - _loop(false), + _loop(true), _hold(false), _startAutomatically(false), _firstFrame(0.0f), diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 661509c3d2..217b8b0d53 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -181,6 +181,15 @@ int64_t AudioInjector::injectNextFrame() { // make sure we actually have samples downloaded to inject if (_audioData.size()) { + int sampleSize = (_options.stereo ? 2 : 1) * sizeof(AudioConstants::AudioSample); + auto numSamples = static_cast(_audioData.size() / sampleSize); + auto targetSize = numSamples * sampleSize; + if (targetSize != _audioData.size()) { + qDebug() << "Resizing audio that doesn't end at multiple of sample size, resizing from " + << _audioData.size() << " to " << targetSize; + _audioData.resize(targetSize); + } + _outgoingSequenceNumber = 0; _nextFrame = 0; @@ -272,7 +281,7 @@ int64_t AudioInjector::injectNextFrame() { _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); _currentSendOffset += bytesToCopy; totalBytesLeftToCopy -= bytesToCopy; - if (_currentSendOffset >= _audioData.size()) { + if (_options.loop && _currentSendOffset >= _audioData.size()) { _currentSendOffset = 0; } } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 65432e2f65..a30feec150 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -390,6 +390,14 @@ glm::quat CharacterController::getFollowAngularDisplacement() const { return bulletToGLM(_followAngularDisplacement); } +glm::vec3 CharacterController::getFollowVelocity() const { + if (_followTime > 0.0f) { + return bulletToGLM(_followLinearDisplacement) / _followTime; + } else { + return glm::vec3(); + } +} + glm::vec3 CharacterController::getLinearVelocity() const { glm::vec3 velocity(0.0f); if (_rigidBody) { diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 224632ea6b..c1c64a1a02 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -71,6 +71,7 @@ public: float getFollowTime() const { return _followTime; } glm::vec3 getFollowLinearDisplacement() const; glm::quat getFollowAngularDisplacement() const; + glm::vec3 getFollowVelocity() const; glm::vec3 getLinearVelocity() const; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index aced46e70e..9247ee17b3 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -90,15 +90,16 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { initDeferredPipelines(*shapePlumber); // CPU: Fetch the renderOpaques - auto fetchedOpaques = addJob("FetchOpaque"); - auto culledOpaques = addJob>("CullOpaque", fetchedOpaques, cullFunctor); - auto opaques = addJob("DepthSortOpaque", culledOpaques); + const auto fetchedOpaques = addJob("FetchOpaque"); + const auto culledOpaques = addJob>("CullOpaque", fetchedOpaques, cullFunctor); + const auto opaques = addJob("DepthSortOpaque", culledOpaques); // CPU only, create the list of renderedTransparents items - auto fetchedTransparents = addJob("FetchTransparent", FetchItems( + const auto fetchedTransparents = addJob("FetchTransparent", FetchItems( ItemFilter::Builder::transparentShape().withoutLayered())); - auto culledTransparents = addJob>("CullTransparent", fetchedTransparents, cullFunctor); - auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); + const auto culledTransparents = + addJob>("CullTransparent", fetchedTransparents, cullFunctor); + const auto transparents = addJob("DepthSortTransparent", culledTransparents, DepthSortItems(false)); // GPU Jobs: Start preparing the deferred and lighting buffer addJob("PrepareDeferred"); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 11b1753895..32188e4cc9 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -105,16 +105,16 @@ RenderShadowTask::RenderShadowTask(CullFunctor cullFunctor) : Task(std::make_sha } // CPU: Fetch shadow-casting opaques - auto fetchedItems = addJob("FetchShadowMap"); + const auto fetchedItems = addJob("FetchShadowMap"); // CPU: Cull against KeyLight frustum (nearby viewing camera) - auto culledItems = addJob>("CullShadowMap", fetchedItems, cullFunctor); + const auto culledItems = addJob>("CullShadowMap", fetchedItems, cullFunctor); // CPU: Sort by pipeline - auto sortedShapes = addJob("PipelineSortShadowSort", culledItems); + const auto sortedShapes = addJob("PipelineSortShadowSort", culledItems); // CPU: Sort front to back - auto shadowShapes = addJob("DepthSortShadowMap", sortedShapes); + const auto shadowShapes = addJob("DepthSortShadowMap", sortedShapes); // GPU: Render to shadow map addJob("RenderShadowMap", shadowShapes, shapePlumber); diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h index ac05fbe68a..148117eed6 100644 --- a/libraries/render/src/render/Task.h +++ b/libraries/render/src/render/Task.h @@ -52,6 +52,7 @@ protected: class Job; class Task; +class JobNoIO {}; // A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled) class JobConfig : public QObject { @@ -101,16 +102,16 @@ template void jobConfigure(T& data, const C& configuration) { template void jobConfigure(T&, const JobConfig&) { // nop, as the default JobConfig was used, so the data does not need a configure method } -template void jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { +template void jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const JobNoIO& input, JobNoIO& output) { data.run(sceneContext, renderContext); } -template void jobRunI(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input) { +template void jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, JobNoIO& output) { data.run(sceneContext, renderContext, input); } -template void jobRunO(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, O& output) { +template void jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const JobNoIO& input, O& output) { data.run(sceneContext, renderContext, output); } -template void jobRunIO(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, O& output) { +template void jobRun(T& data, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, O& output) { data.run(sceneContext, renderContext, input, output); } @@ -118,6 +119,7 @@ class Job { public: using Config = JobConfig; using QConfigPointer = std::shared_ptr; + using None = JobNoIO; // The guts of a job class Concept { @@ -138,84 +140,7 @@ public: }; using ConceptPointer = std::shared_ptr; - template class Model : public Concept { - public: - using Data = T; - - Data _data; - - Model(Data data = Data()) : Concept(std::make_shared()), _data(data) { - applyConfiguration(); - } - - void applyConfiguration() { - jobConfigure(_data, *std::static_pointer_cast(_config)); - } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - renderContext->jobConfig = std::static_pointer_cast(_config); - if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { - jobRun(_data, sceneContext, renderContext); - } - renderContext->jobConfig.reset(); - } - }; - - template class ModelI : public Concept { - public: - using Data = T; - using Input = I; - - Data _data; - Varying _input; - - const Varying getInput() const { return _input; } - - ModelI(const Varying& input, Data data = Data()) : Concept(std::make_shared()), _data(data), _input(input) { - applyConfiguration(); - } - - void applyConfiguration() { - jobConfigure(_data, *std::static_pointer_cast(_config)); - } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - renderContext->jobConfig = std::static_pointer_cast(_config); - if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { - jobRunI(_data, sceneContext, renderContext, _input.get()); - } - renderContext->jobConfig.reset(); - } - }; - - template class ModelO : public Concept { - public: - using Data = T; - using Output = O; - - Data _data; - Varying _output; - - const Varying getOutput() const { return _output; } - - ModelO(Data data = Data()) : Concept(std::make_shared()), _data(data), _output(Output()) { - applyConfiguration(); - } - - void applyConfiguration() { - jobConfigure(_data, *std::static_pointer_cast(_config)); - } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - renderContext->jobConfig = std::static_pointer_cast(_config); - if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { - jobRunO(_data, sceneContext, renderContext, _output.edit()); - } - renderContext->jobConfig.reset(); - } - }; - - template class ModelIO : public Concept { + template class Model : public Concept { public: using Data = T; using Input = I; @@ -228,7 +153,7 @@ public: const Varying getInput() const { return _input; } const Varying getOutput() const { return _output; } - ModelIO(const Varying& input, Data data = Data()) : Concept(std::make_shared()), _data(data), _input(input), _output(Output()) { + Model(const Varying& input, Data data = Data()) : Concept(std::make_shared()), _data(data), _input(input), _output(Output()) { applyConfiguration(); } @@ -239,11 +164,14 @@ public: void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { - jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); + jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); } renderContext->jobConfig.reset(); } }; + template using ModelI = Model; + template using ModelO = Model; + template using ModelIO = Model; Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} @@ -278,17 +206,24 @@ class Task { public: using Config = TaskConfig; using QConfigPointer = Job::QConfigPointer; + using None = Job::None; - template class Model : public Job::Concept { + template class Model : public Job::Concept { public: using Data = T; + using Input = I; + using Output = O; Data _data; + Varying _input; + Varying _output; - Model(Data data = Data()) : Concept(std::make_shared()), _data(data) { + const Varying getInput() const { return _input; } + const Varying getOutput() const { return _output; } + + Model(const Varying& input, Data data = Data()) : Concept(std::make_shared()), _data(data), _input(input), _output(Output()) { _config = _data._config; // use the data's config std::static_pointer_cast(_config)->init(&_data); - applyConfiguration(); } @@ -299,11 +234,14 @@ public: void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { renderContext->jobConfig = std::static_pointer_cast(_config); if (renderContext->jobConfig->alwaysEnabled || renderContext->jobConfig->enabled) { - jobRun(_data, sceneContext, renderContext); + jobRun(_data, sceneContext, renderContext, _input.get(), _output.edit()); } renderContext->jobConfig.reset(); } }; + template using ModelI = Model; + template using ModelO = Model; + template using ModelIO = Model; using Jobs = std::vector; @@ -311,15 +249,21 @@ public: Task() : _config{ std::make_shared() } {} template Task(std::shared_ptr config) : _config{ config } {} - // Queue a new job to the container; returns the job's output - template const Varying addJob(std::string name, A&&... args) { - _jobs.emplace_back(name, std::make_shared(std::forward(args)...)); + // Create a new job in the container's queue; returns the job's output + template const Varying addJob(std::string name, const Varying& input, A&&... args) { + _jobs.emplace_back(name, std::make_shared( + input, + typename T::JobModel::Data(std::forward(args)...))); QConfigPointer config = _jobs.back().getConfiguration(); config->setParent(_config.get()); config->setObjectName(name.c_str()); QObject::connect(config.get(), SIGNAL(dirty()), _config.get(), SLOT(refresh())); return _jobs.back().getOutput(); } + template const Varying addJob(std::string name, A&&... args) { + const auto input = Varying(typename T::JobModel::Input()); + return addJob(name, input, std::forward(args)...); + } std::shared_ptr getConfiguration() { auto config = std::static_pointer_cast(_config); @@ -335,7 +279,7 @@ public: } protected: - template friend class Model; + template friend class Model; QConfigPointer _config; Jobs _jobs; diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 0ba1c72e72..8d88d40ef0 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -1110,9 +1110,9 @@ collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", name: "dice", position: { - x: 541, - y: 494.96, - z: 509.1 + x: 541.61, + y: 495.21, + z: 508.52 }, dimensions: { x: 0.09, @@ -1144,16 +1144,15 @@ var dice1 = Entities.addEntity(diceProps); diceProps.position = { - x: 541.05, - y: 494.96, - z: 509.0 + x: 541.52, + y: 495.21, + z: 508.41 }; var dice2 = Entities.addEntity(diceProps); } - function createGates() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 8ff1333a78..34d42d2d1a 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -1098,9 +1098,9 @@ MasterReset = function() { collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", name: "dice", position: { - x: 541, - y: 494.96, - z: 509.1 + x: 541.61, + y: 495.21, + z: 508.52 }, dimensions: { x: 0.09, @@ -1132,16 +1132,15 @@ MasterReset = function() { var dice1 = Entities.addEntity(diceProps); diceProps.position = { - x: 541.05, - y: 494.96, - z: 509.0 + x: 541.52, + y: 495.21, + z: 508.41 }; var dice2 = Entities.addEntity(diceProps); } - - + function createGates() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx';