diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 6b83d87732..9ea1d2f942 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1123,37 +1123,7 @@ void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entit entity->setModel({}); } -void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelPointer& model) { - if (!_animation || !_animation->isLoaded()) { - return; - } - - QVector jointsData; - - const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy - int frameCount = frames.size(); - if (frameCount <= 0) { - return; - } - - { - float currentFrame = fmod(entity->getAnimationCurrentFrame(), (float)(frameCount)); - if (currentFrame < 0.0f) { - currentFrame += (float)frameCount; - } - int currentIntegerFrame = (int)(glm::floor(currentFrame)); - if (currentIntegerFrame == _lastKnownCurrentFrame) { - return; - } - _lastKnownCurrentFrame = currentIntegerFrame; - } - - if (_jointMapping.size() != model->getJointStateCount()) { - qCWarning(entitiesrenderer) << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch" - << _jointMapping.size() << model->getJointStateCount(); - return; - } - +void ModelEntityRenderer::updateJointData(const QVector& translations, const QVector& rotations, const TypedEntityPointer& entity, const ModelPointer& model) { QStringList animationJointNames = _animation->getHFMModel().getJointNames(); auto& hfmJoints = _animation->getHFMModel().joints; @@ -1162,10 +1132,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelP bool allowTranslation = entity->getAnimationAllowTranslation(); - const QVector& rotations = frames[_lastKnownCurrentFrame].rotations; - const QVector& translations = frames[_lastKnownCurrentFrame].translations; - - jointsData.resize(_jointMapping.size()); + QVector jointsData(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { int index = _jointMapping[j]; @@ -1206,6 +1173,58 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelP entity->copyAnimationJointDataToModel(); } +void ModelEntityRenderer::animate(const TypedEntityPointer& entity, const ModelPointer& model) { + if (!_animation || !_animation->isLoaded()) { + return; + } + + const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy + int frameCount = frames.size(); + if (frameCount <= 0) { + return; + } + + float currentFrame = fmod(entity->getAnimationCurrentFrame(), (float)(frameCount)); + if (currentFrame < 0.0f) { + currentFrame += (float)frameCount; + } + + const bool smoothFrames = entity->getAnimationSmoothFrames(); + const int currentIntegerFrame = (int)(glm::floor(currentFrame)); + if (!smoothFrames && currentIntegerFrame == _lastKnownCurrentIntegerFrame) { + return; + } + _lastKnownCurrentIntegerFrame = currentIntegerFrame; + + if (_jointMapping.size() != model->getJointStateCount()) { + qCWarning(entitiesrenderer) << "RenderableModelEntityItem::getAnimationFrame -- joint count mismatch" + << _jointMapping.size() << model->getJointStateCount(); + return; + } + + if (smoothFrames) { + QVector rotations = frames[_lastKnownCurrentIntegerFrame].rotations; + QVector translations = frames[_lastKnownCurrentIntegerFrame].translations; + + const int nextIntegerFrame = entity->getAnimationNextFrame(_lastKnownCurrentIntegerFrame, frameCount); + + const QVector& nextRotations = frames[nextIntegerFrame].rotations; + const QVector& nextTranslations = frames[nextIntegerFrame].translations; + + const float frac = glm::fract(currentFrame); + for (int i = 0; i < translations.size(); i++) { + translations[i] = glm::mix(translations[i], nextTranslations[i], frac); + } + for (int i = 0; i < rotations.size(); i++) { + rotations[i] = glm::slerp(rotations[i], nextRotations[i], frac); + } + + updateJointData(translations, rotations, entity, model); + } else { + updateJointData(frames[_lastKnownCurrentIntegerFrame].translations, frames[_lastKnownCurrentIntegerFrame].rotations, entity, model); + } +} + bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (entity->blendshapesChanged()) { return true; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 425d082f01..4fd05f39b9 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -175,6 +175,7 @@ protected: private: void animate(const TypedEntityPointer& entity, const ModelPointer& model); + void updateJointData(const QVector& translations, const QVector& rotations, const TypedEntityPointer& entity, const ModelPointer& model); void mapJoints(const TypedEntityPointer& entity, const ModelPointer& model); // Transparency is handled in ModelMeshPartPayload @@ -184,7 +185,7 @@ private: ModelPointer _model; QString _textures; bool _texturesLoaded { false }; - int _lastKnownCurrentFrame { -1 }; + int _lastKnownCurrentIntegerFrame { -1 }; #ifdef MODEL_ENTITY_USE_FADE_EFFECT bool _hasTransitioned{ false }; #endif diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index d15ee3d4cf..e44bc135e1 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -32,20 +32,8 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b (a._lastFrame == b._lastFrame) && (a._fps == b._fps) && (a._allowTranslation == b._allowTranslation) && - (a._url == b._url); -} - -bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { - return - (a._currentFrame != b._currentFrame) || - (a._running != b._running) || - (a._loop != b._loop) || - (a._hold != b._hold) || - (a._firstFrame != b._firstFrame) || - (a._lastFrame != b._lastFrame) || - (a._fps != b._fps) || - (a._allowTranslation != b._allowTranslation) || - (a._url != b._url); + (a._url == b._url) && + (a._smoothFrames == b._smoothFrames); } @@ -66,6 +54,8 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b * it isn't. * @property {boolean} hold=false - true if the rotations and translations of the last frame played are * maintained when the animation stops playing, false if they aren't. + * @property {boolean} smoothFrames=true - true if the frames of the animation should be linearly interpolated to + * create smoother movement, false if the frames should not be interpolated. */ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); @@ -77,6 +67,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_SMOOTH_FRAMES, Animation, animation, SmoothFrames, smoothFrames); } @@ -96,6 +87,7 @@ void AnimationPropertyGroup::copyFromScriptValue(const ScriptValue& object, cons COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, smoothFrames, bool, setSmoothFrames); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); @@ -113,6 +105,7 @@ void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { COPY_PROPERTY_IF_CHANGED(firstFrame); COPY_PROPERTY_IF_CHANGED(lastFrame); COPY_PROPERTY_IF_CHANGED(hold); + COPY_PROPERTY_IF_CHANGED(smoothFrames); } void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { @@ -120,19 +113,23 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { // if it includes fps, currentFrame, or running, those values will be parsed out and // will over ride the regular animation settings + bool allowTranslation = getAllowTranslation(); float fps = getFPS(); float currentFrame = getCurrentFrame(); bool running = getRunning(); + bool loop = getLoop(); float firstFrame = getFirstFrame(); float lastFrame = getLastFrame(); - bool loop = getLoop(); bool hold = getHold(); - bool allowTranslation = getAllowTranslation(); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonObject settingsAsJsonObject = settingsAsJson.object(); QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); + if (settingsMap.contains("allowTranslation")) { + allowTranslation = settingsMap["allowTranslation"].toBool(); + } + if (settingsMap.contains("fps")) { fps = settingsMap["fps"].toFloat(); } @@ -150,30 +147,25 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { firstFrame = settingsMap["firstFrame"].toFloat(); } + if (settingsMap.contains("loop")) { + loop = settingsMap["loop"].toBool(); + } + if (settingsMap.contains("lastFrame")) { lastFrame = settingsMap["lastFrame"].toFloat(); } - if (settingsMap.contains("loop")) { - running = settingsMap["loop"].toBool(); - } - if (settingsMap.contains("hold")) { - running = settingsMap["hold"].toBool(); + hold = settingsMap["hold"].toBool(); } - if (settingsMap.contains("allowTranslation")) { - allowTranslation = settingsMap["allowTranslation"].toBool(); - } - - setAllowTranslation(allowTranslation); setFPS(fps); setCurrentFrame(currentFrame); setRunning(running); + setLoop(loop); setFirstFrame(firstFrame); setLastFrame(lastFrame); - setLoop(loop); setHold(hold); } @@ -213,6 +205,9 @@ void AnimationPropertyGroup::listChangedProperties(QList& out) { if (holdChanged()) { out << "animation-hold"; } + if (smoothFramesChanged()) { + out << "animation-smoothFrames"; + } } @@ -234,6 +229,7 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, getSmoothFrames()); return true; } @@ -253,6 +249,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, bool, setSmoothFrames); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation); @@ -263,7 +260,8 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); - + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_SMOOTH_FRAMES, SmoothFrames); + processedBytes += bytesRead; Q_UNUSED(somethingChanged); @@ -281,6 +279,7 @@ void AnimationPropertyGroup::markAllChanged() { _firstFrameChanged = true; _lastFrameChanged = true; _holdChanged = true; + _smoothFramesChanged = true; } EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { @@ -295,6 +294,7 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_SMOOTH_FRAMES, smoothFrames); return changedProperties; } @@ -309,6 +309,7 @@ void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) con COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, SmoothFrames, getSmoothFrames); } bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -323,6 +324,7 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, SmoothFrames, smoothFrames, setSmoothFrames); return somethingChanged; } @@ -338,6 +340,7 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP requestedProperties += PROP_ANIMATION_FIRST_FRAME; requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_HOLD; + requestedProperties += PROP_ANIMATION_SMOOTH_FRAMES; return requestedProperties; } @@ -361,6 +364,7 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, getSmoothFrames()); } int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -380,6 +384,7 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, bool, setSmoothFrames); return bytesRead; } diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index c980119f8e..b90417d78e 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -91,11 +91,12 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold - DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); + DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true); + DEFINE_PROPERTY(PROP_ANIMATION_SMOOTH_FRAMES, SmoothFrames, smoothFrames, bool, true); protected: friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); - friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); + friend bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b) { return !(a == b); } void setFromOldAnimationSettings(const QString& value); }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7d5c01fd25..91be6e5b94 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2905,6 +2905,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); + ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_SMOOTH_FRAMES, Animation, animation, SmoothFrames, smoothFrames); } // Light diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 591725c2ea..b27c9ed5e8 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -237,6 +237,7 @@ enum EntityPropertyList { PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_16, PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_17, PROP_ANIMATION_HOLD = PROP_DERIVED_18, + PROP_ANIMATION_SMOOTH_FRAMES = PROP_DERIVED_19, // Light PROP_IS_SPOTLIGHT = PROP_DERIVED_0, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5f88d7191c..f8ae726b90 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2892,6 +2892,11 @@ bool EntityTree::readFromMap(QVariantMap& map, const bool isImport) { } } + // Before, animations weren't smoothed + if (contentVersion < (int)EntityVersion::AnimationSmoothFrames && properties.getType() == EntityTypes::EntityType::Model) { + properties.getAnimation().setSmoothFrames(false); + } + EntityItemPointer entity = addEntity(entityItemID, properties, isImport); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 45d41c3a7e..8910961c50 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -34,12 +34,10 @@ EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const E } ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID), - _blendshapeCoefficientsVector((int)Blendshapes::BlendshapeCount, 0.0f) + _blendshapeCoefficientsVector((int)Blendshapes::BlendshapeCount, 0.0f), + _lastAnimated(usecTimestampNow()) { - _lastAnimated = usecTimestampNow(); - // set the last animated when interface (re)starts _type = EntityTypes::Model; - _lastKnownCurrentFrame = -1; _visuallyReady = false; } @@ -643,6 +641,22 @@ bool ModelEntityItem::isAnimatingSomething() const { }); } +bool ModelEntityItem::getAnimationSmoothFrames() const { + return resultWithReadLock([&] { + return _animationProperties.getSmoothFrames(); + }); +} + +int ModelEntityItem::getAnimationNextFrame(int currentFrame, int frameCount) const { + return resultWithReadLock([&] { + int result = currentFrame + 1; + if (result > _animationProperties.getLastFrame() || result > (frameCount - 1)) { + result = _animationProperties.getFirstFrame(); + } + return std::max(result, 0); + }); +} + bool ModelEntityItem::applyNewAnimationProperties(AnimationPropertyGroup newProperties) { // call applyNewAnimationProperties() whenever trying to update _animationProperties // because there is some reset logic we need to do whenever the animation "config" properties change diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index a00327251c..2e1995be88 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -56,8 +56,6 @@ public: void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; - // TODO: Move these to subclasses, or other appropriate abstraction - // getters/setters applicable to models and particles glm::u8vec3 getColor() const; void setColor(const glm::u8vec3& value); @@ -89,6 +87,8 @@ public: float getAnimationCurrentFrame() const; bool getAnimationAllowTranslation() const; bool isAnimatingSomething() const; + bool getAnimationSmoothFrames() const; + int getAnimationNextFrame(int currentFrame, int frameCount) const; void setRelayParentJoints(bool relayJoints); bool getRelayParentJoints() const; @@ -148,7 +148,6 @@ protected: }; QVector _localJointData; - int _lastKnownCurrentFrame{-1}; glm::u8vec3 _color; glm::vec3 _modelScale { 1.0f }; @@ -167,8 +166,8 @@ protected: ShapeType _shapeType { SHAPE_TYPE_NONE }; private: - uint64_t _lastAnimated{ 0 }; - float _currentFrame{ -1.0f }; + uint64_t _lastAnimated { 0 }; + float _currentFrame { -1.0f }; QVector _blendshapeCoefficientsVector; bool _blendshapesChanged { false }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 46e2fe61c7..6c637abedc 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -294,6 +294,7 @@ enum class EntityVersion : PacketVersion { EntityTags, WantsKeyboardFocus, AudioZones, + AnimationSmoothFrames, SoundEntities, // Add new versions above here diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index efb33fd422..795974da91 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -215,6 +215,9 @@ "animation.fps": { "tooltip": "The speed of the animation." }, + "animation.smoothFrames": { + "tooltip": "If enabled, the frames of the animation will be linearly interpolated to create smoother movement." + }, "textures": { "tooltip": "A JSON string containing a texture. Use a name from the Original Texture property to override it." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 797156081e..b5aeb3d69d 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -729,6 +729,11 @@ const GROUPS = [ type: "number-draggable", propertyID: "animation.fps", }, + { + label: "Smooth Animation", + type: "bool", + propertyID: "animation.smoothFrames", + }, { label: "Texture", type: "textarea",