diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5f7899ae74..7103e72f54 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -402,6 +402,8 @@ void EntityTreeRenderer::update(bool simulate) { PerformanceTimer perfTimer("ETRupdate"); if (_tree && !_shuttingDown) { EntityTreePointer tree = std::static_pointer_cast(_tree); + + // here we update _currentFrame and _lastAnimated and sync with the server properties. tree->update(simulate); // Update the rendereable entities as needed @@ -736,7 +738,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { PickRay ray = _viewState->computePickRay(event->x(), event->y()); RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); if (rayPickResult.intersects && rayPickResult.entity) { - //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; + // qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID, diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c992eb5dc4..e578e4858d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -63,13 +63,17 @@ bool ModelEntityWrapper::isModelLoaded() const { EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; } RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, bool dimensionsInitialized) : ModelEntityWrapper(entityItemID), _dimensionsInitialized(dimensionsInitialized) { + + } RenderableModelEntityItem::~RenderableModelEntityItem() { } @@ -464,7 +468,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { shapeInfo.setParams(type, dimensions, getCompoundShapeURL()); } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { // TODO: assert we never fall in here when model not fully loaded - //assert(_model && _model->isLoaded()); + // assert(_model && _model->isLoaded()); updateModelBounds(); model->updateGeometry(); @@ -974,9 +978,6 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit entity->setModel({}); } -bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { - return !(a == b); -} void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { if (!_animation || !_animation->isLoaded()) { @@ -991,19 +992,12 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - if (!_lastAnimated) { - _lastAnimated = usecTimestampNow(); - return; - } - - auto now = usecTimestampNow(); - auto interval = now - _lastAnimated; - _lastAnimated = now; - float deltaTime = (float)interval / (float)USECS_PER_SECOND; - _currentFrame += (deltaTime * _renderAnimationProperties.getFPS()); - { - int animationCurrentFrame = (int)(glm::floor(_currentFrame)) % frameCount; + // the current frame is set on the server in update() in ModelEntityItem.cpp + int animationCurrentFrame = (int)(glm::floor(entity->getAnimationCurrentFrame())); + + // in the case where the last frame is greater than the framecount then clamp + // it to the end of the animation until it loops around. if (animationCurrentFrame < 0 || animationCurrentFrame > frameCount) { animationCurrentFrame = 0; } @@ -1039,10 +1033,10 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { glm::mat4 translationMat; if (allowTranslation) { - if(index < translations.size()){ + if (index < translations.size()) { translationMat = glm::translate(translations[index]); } - } else if (index < animationJointNames.size()){ + } else if (index < animationJointNames.size()) { QString jointName = fbxJoints[index].name; // Pushing this here so its not done on every entity, with the exceptions of those allowing for translation if (originalFbxIndices.contains(jointName)) { @@ -1317,25 +1311,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (model->getRenderItemsNeedUpdate()) { model->updateRenderItems(); } - - { - DETAILED_PROFILE_RANGE(simulation_physics, "CheckAnimation"); - // make a copy of the animation properites - auto newAnimationProperties = entity->getAnimationProperties(); - if (newAnimationProperties != _renderAnimationProperties) { - withWriteLock([&] { - _renderAnimationProperties = newAnimationProperties; - _currentFrame = _renderAnimationProperties.getCurrentFrame(); - }); - } - } - + + // The code to deal with the change of properties is now in ModelEntityItem.cpp + // That is where _currentFrame and _lastAnimated were updated. if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); } - animate(entity); + if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) { + animate(entity); + } emit requestRenderUpdate(); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 0272bed575..b4f2665692 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -33,7 +33,7 @@ namespace render { namespace entities { class ModelEntityRenderer; } } -//#define MODEL_ENTITY_USE_FADE_EFFECT +// #define MODEL_ENTITY_USE_FADE_EFFECT class ModelEntityWrapper : public ModelEntityItem { using Parent = ModelEntityItem; friend class render::entities::ModelEntityRenderer; @@ -133,7 +133,7 @@ class ModelEntityRenderer : public TypedEntityRenderergetAnimationProperties(); + + if (_previousAnimationProperties != currentAnimationProperties) { + withWriteLock([&] { + // if we hit start animation or change the first or last frame then restart the animation + if ((currentAnimationProperties.getFirstFrame() != _previousAnimationProperties.getFirstFrame()) || + (currentAnimationProperties.getLastFrame() != _previousAnimationProperties.getLastFrame()) || + (currentAnimationProperties.getRunning() && !_previousAnimationProperties.getRunning())) { + + // when we start interface and the property is are set then the current frame is initialized to -1 + if (_currentFrame < 0) { + // don't reset _lastAnimated here because we need the timestamp from the ModelEntityItem constructor for when the properties were set + _currentFrame = currentAnimationProperties.getCurrentFrame(); + setAnimationCurrentFrame(_currentFrame); + } else { + _lastAnimated = usecTimestampNow(); + _currentFrame = currentAnimationProperties.getFirstFrame(); + setAnimationCurrentFrame(currentAnimationProperties.getFirstFrame()); + } + } else if (!currentAnimationProperties.getRunning() && _previousAnimationProperties.getRunning()) { + _currentFrame = currentAnimationProperties.getFirstFrame(); + setAnimationCurrentFrame(_currentFrame); + } else if (currentAnimationProperties.getCurrentFrame() != _previousAnimationProperties.getCurrentFrame()) { + // don't reset _lastAnimated here because the currentFrame was set with the previous setting of _lastAnimated + _currentFrame = currentAnimationProperties.getCurrentFrame(); + } + + }); + _previousAnimationProperties = this->getAnimationProperties(); + + } + + if (isAnimatingSomething()) { + if (!(getAnimationFirstFrame() < 0) && !(getAnimationFirstFrame() > getAnimationLastFrame())) { + updateFrameCount(); + } + } + } + + EntityItem::update(now); +} + +bool ModelEntityItem::needsToCallUpdate() const { + + return true; +} + +void ModelEntityItem::updateFrameCount() { + + if (_currentFrame < 0.0f) { + return; + } + + if (!_lastAnimated) { + _lastAnimated = usecTimestampNow(); + return; + } + + auto now = usecTimestampNow(); + + // update the interval since the last animation. + auto interval = now - _lastAnimated; + _lastAnimated = now; + + // if fps is negative then increment timestamp and return. + if (getAnimationFPS() < 0.0f) { + return; + } + + int updatedFrameCount = getAnimationLastFrame() - getAnimationFirstFrame() + 1; + + if (!getAnimationHold() && getAnimationIsPlaying()) { + float deltaTime = (float)interval / (float)USECS_PER_SECOND; + _currentFrame += (deltaTime * getAnimationFPS()); + if (_currentFrame > getAnimationLastFrame()) { + if (getAnimationLoop()) { + _currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1); + } else { + _currentFrame = getAnimationLastFrame(); + } + } else if (_currentFrame < getAnimationFirstFrame()) { + if (getAnimationFirstFrame() < 0) { + _currentFrame = 0; + } else { + _currentFrame = getAnimationFirstFrame(); + } + } + // qCDebug(entities) << "in update frame " << _currentFrame; + setAnimationCurrentFrame(_currentFrame); + } + + +} + void ModelEntityItem::debugDump() const { qCDebug(entities) << "ModelEntityItem id:" << getEntityItemID(); qCDebug(entities) << " edited ago:" << getEditedAgo(); @@ -538,6 +639,13 @@ void ModelEntityItem::setAnimationLoop(bool loop) { }); } +bool ModelEntityItem::getAnimationLoop() const { + return resultWithReadLock([&] { + return _animationProperties.getLoop(); + }); +} + + void ModelEntityItem::setAnimationHold(bool hold) { withWriteLock([&] { _animationProperties.setHold(hold); @@ -573,8 +681,9 @@ float ModelEntityItem::getAnimationLastFrame() const { return _animationProperties.getLastFrame(); }); } + bool ModelEntityItem::getAnimationIsPlaying() const { - return resultWithReadLock([&] { + return resultWithReadLock([&] { return _animationProperties.getRunning(); }); } @@ -585,8 +694,15 @@ float ModelEntityItem::getAnimationCurrentFrame() const { }); } -bool ModelEntityItem::isAnimatingSomething() const { +float ModelEntityItem::getAnimationFPS() const { return resultWithReadLock([&] { + return _animationProperties.getFPS(); + }); +} + + +bool ModelEntityItem::isAnimatingSomething() const { + return resultWithReadLock([&] { return !_animationProperties.getURL().isEmpty() && _animationProperties.getRunning() && (_animationProperties.getFPS() != 0.0f); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 2c3ef3aa2d..7fee022011 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -17,6 +17,7 @@ #include #include "AnimationPropertyGroup.h" + class ModelEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); @@ -46,8 +47,11 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - //virtual void update(const quint64& now) override; - //virtual bool needsToCallUpdate() const override; + // update() and needstocallupdate() added back for the entity property fix + virtual void update(const quint64& now) override; + virtual bool needsToCallUpdate() const override; + void updateFrameCount(); + virtual void debugDump() const override; void setShapeType(ShapeType type) override; @@ -90,6 +94,7 @@ public: bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); }; void setAnimationLoop(bool loop); + bool getAnimationLoop() const; void setAnimationHold(bool hold); bool getAnimationHold() const; @@ -102,6 +107,7 @@ public: bool getAnimationIsPlaying() const; float getAnimationCurrentFrame() const; + float getAnimationFPS() const; bool isAnimatingSomething() const; static const QString DEFAULT_TEXTURES; @@ -147,7 +153,7 @@ protected: }; QVector _localJointData; - int _lastKnownCurrentFrame; + int _lastKnownCurrentFrame{-1}; rgbColor _color; QString _modelURL; @@ -160,6 +166,11 @@ protected: QString _textures; ShapeType _shapeType = SHAPE_TYPE_NONE; + +private: + uint64_t _lastAnimated{ 0 }; + AnimationPropertyGroup _previousAnimationProperties; + float _currentFrame{ -1.0f }; }; #endif // hifi_ModelEntityItem_h