From 106c8bffd842fc28a4c3c32cba8351c15f61f9f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 9 May 2014 16:08:06 -0700 Subject: [PATCH 01/14] first cut at animations in models --- assignment-client/CMakeLists.txt | 1 + examples/placeModelsWithHands.js | 22 +++ interface/CMakeLists.txt | 1 + interface/src/models/ModelTreeRenderer.cpp | 62 +++++-- interface/src/models/ModelTreeRenderer.h | 6 +- interface/src/renderer/Model.cpp | 21 ++- interface/src/renderer/Model.h | 2 + .../src/AnimationCache.cpp | 4 +- .../src/AnimationCache.h | 3 + .../src/AnimationObject.cpp | 0 .../src/AnimationObject.h | 0 libraries/fbx/src/FBXReader.cpp | 1 + libraries/fbx/src/FBXReader.h | 2 + libraries/models/CMakeLists.txt | 1 + libraries/models/src/ModelItem.cpp | 156 +++++++++++++++++- libraries/models/src/ModelItem.h | 34 +++- libraries/networking/src/PacketHeaders.cpp | 2 + libraries/particles/CMakeLists.txt | 1 + libraries/script-engine/CMakeLists.txt | 1 + libraries/script-engine/src/ScriptEngine.h | 5 +- 20 files changed, 297 insertions(+), 28 deletions(-) rename libraries/{script-engine => animation}/src/AnimationCache.cpp (98%) rename libraries/{script-engine => animation}/src/AnimationCache.h (96%) rename libraries/{script-engine => animation}/src/AnimationObject.cpp (100%) rename libraries/{script-engine => animation}/src/AnimationObject.h (100%) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index e783001228..6436ffbd75 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -33,6 +33,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}") diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index e1ac151fe4..f16945472d 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -37,6 +37,7 @@ var radiusMinimum = 0.05; var radiusMaximum = 0.5; var modelURLs = [ + "http://www.fungibleinsight.com/faces/beta.fst", "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/topHat.fst", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx", @@ -48,6 +49,19 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; +var animationURLs = [ + "http://www.fungibleinsight.com/faces/gangnam_style_2.fbx", + "", + "", + "", + "", + "", + "", + "", + "", + "", +]; + var currentModelURL = 1; var numModels = modelURLs.length; @@ -214,10 +228,18 @@ function checkControllerSide(whichSide) { modelRotation: palmRotation, modelURL: modelURLs[currentModelURL] }; + + if (animationURLs[currentModelURL] !== "") { + properties.animationURL = animationURLs[currentModelURL]; + } debugPrint("modelRadius=" +modelRadius); newModel = Models.addModel(properties); + + print("just added model... newModel=" + newModel.creatorTokenID); + print("properties.animationURL=" + properties.animationURL); + if (whichSide == LEFT_PALM) { leftModelAlreadyInHand = true; leftHandModel = newModel; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0a56109260..4d02415df2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -127,6 +127,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") # find any optional libraries diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index c762182290..e5c55ef066 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -20,11 +20,16 @@ ModelTreeRenderer::ModelTreeRenderer() : } ModelTreeRenderer::~ModelTreeRenderer() { - // delete the models in _modelsItemModels - foreach(Model* model, _modelsItemModels) { + // delete the models in _knownModelsItemModels + foreach(Model* model, _knownModelsItemModels) { delete model; } - _modelsItemModels.clear(); + _knownModelsItemModels.clear(); + + foreach(Model* model, _unknownModelsItemModels) { + delete model; + } + _unknownModelsItemModels.clear(); } void ModelTreeRenderer::init() { @@ -43,17 +48,27 @@ void ModelTreeRenderer::render(RenderMode renderMode) { OctreeRenderer::render(renderMode); } -Model* ModelTreeRenderer::getModel(const QString& url) { +Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; - // if we don't already have this model then create it and initialize it - if (_modelsItemModels.find(url) == _modelsItemModels.end()) { - model = new Model(); - model->init(); - model->setURL(QUrl(url)); - _modelsItemModels[url] = model; + if (modelItem.isKnownID()) { + if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) { + model = _knownModelsItemModels[modelItem.getID()]; + } else { + model = new Model(); + model->init(); + model->setURL(QUrl(modelItem.getModelURL())); + _knownModelsItemModels[modelItem.getID()] = model; + } } else { - model = _modelsItemModels[url]; + if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) { + model = _unknownModelsItemModels[modelItem.getCreatorTokenID()]; + } else { + model = new Model(); + model->init(); + model->setURL(QUrl(modelItem.getModelURL())); + _unknownModelsItemModels[modelItem.getCreatorTokenID()] = model; + } } return model; } @@ -63,7 +78,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // we need to iterate the actual modelItems of the element ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; - const QList& modelItems = modelTreeElement->getModels(); + QList& modelItems = modelTreeElement->getModels(); uint16_t numberOfModels = modelItems.size(); @@ -139,7 +154,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) } for (uint16_t i = 0; i < numberOfModels; i++) { - const ModelItem& modelItem = modelItems[i]; + ModelItem& modelItem = modelItems[i]; // render modelItem aspoints AABox modelBox = modelItem.getAABox(); modelBox.scale(TREE_SCALE); @@ -156,7 +171,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glPushMatrix(); const float alpha = 1.0f; - Model* model = getModel(modelItem.getModelURL()); + Model* model = getModel(modelItem); model->setScaleToFit(true, radius * 2.0f); model->setSnapModelToCenter(true); @@ -167,6 +182,25 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); + + qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); + qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); + qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); + + // handle animations.. + if (modelItem.hasAnimation()) { + if (!modelItem.jointsMapped()) { + QStringList modelJointNames = model->getJointNames(); + modelItem.mapJoints(modelJointNames); + } + + QVector frameData = modelItem.getAnimationFrame(); + for (int i = 0; i < frameData.size(); i++) { + model->setJointState(i, true, frameData[i]); + } + } + + // make sure to simulate so everything gets set up correctly for rendering model->simulate(0.0f); // TODO: should we allow modelItems to have alpha on their models? diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index 7af5bbf317..e0b8d7d0a2 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -49,9 +49,9 @@ public: virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); protected: - Model* getModel(const QString& url); - - QMap _modelsItemModels; + Model* getModel(const ModelItem& modelItem); + QMap _knownModelsItemModels; + QMap _unknownModelsItemModels; }; #endif // hifi_ModelTreeRenderer_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f46fd48beb..b2c1665c2f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -430,7 +430,8 @@ Extents Model::getMeshExtents() const { return Extents(); } const Extents& extents = _geometry->getFBXGeometry().meshExtents; - Extents scaledExtents = { extents.minimum * _scale, extents.maximum * _scale }; + glm::vec3 scale = _scale * _geometry->getFBXGeometry().fstScaled; + Extents scaledExtents = { extents.minimum * scale, extents.maximum * scale }; return scaledExtents; } @@ -438,8 +439,13 @@ Extents Model::getUnscaledMeshExtents() const { if (!isActive()) { return Extents(); } + const Extents& extents = _geometry->getFBXGeometry().meshExtents; - return extents; + + // even though our caller asked for "unscaled" we need to include any fst scaling + float scale = _geometry->getFBXGeometry().fstScaled; + Extents scaledExtents = { extents.minimum * scale, extents.maximum * scale }; + return scaledExtents; } bool Model::getJointState(int index, glm::quat& rotation) const { @@ -573,6 +579,17 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) return true; } +QStringList Model::getJointNames() const { + if (QThread::currentThread() != thread()) { + QStringList result; + QMetaObject::invokeMethod(const_cast(this), "getJointNames", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QStringList, result)); + return result; + } + return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); +} + + void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 1a469c8122..b7a42930dc 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -182,6 +182,8 @@ public: bool getJointPosition(int jointIndex, glm::vec3& position) const; bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; + + QStringList getJointNames() const; void clearShapes(); void rebuildShapes(); diff --git a/libraries/script-engine/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp similarity index 98% rename from libraries/script-engine/src/AnimationCache.cpp rename to libraries/animation/src/AnimationCache.cpp index 8e1493f075..ce7e4cdf36 100644 --- a/libraries/script-engine/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -36,7 +36,8 @@ QSharedPointer AnimationCache::createResource(const QUrl& url, const Q } Animation::Animation(const QUrl& url) : - Resource(url) { + Resource(url), + _isValid(false) { } class AnimationReader : public QRunnable { @@ -93,6 +94,7 @@ QVector Animation::getFrames() const { void Animation::setGeometry(const FBXGeometry& geometry) { _geometry = geometry; finishedLoading(true); + _isValid = true; } void Animation::downloadFinished(QNetworkReply* reply) { diff --git a/libraries/script-engine/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h similarity index 96% rename from libraries/script-engine/src/AnimationCache.h rename to libraries/animation/src/AnimationCache.h index 23183adf10..392443e7b5 100644 --- a/libraries/script-engine/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -53,6 +53,8 @@ public: Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QVector getFrames() const; + + bool isValid() const { return _isValid; } protected: @@ -63,6 +65,7 @@ protected: private: FBXGeometry _geometry; + bool _isValid; }; #endif // hifi_AnimationCache_h diff --git a/libraries/script-engine/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp similarity index 100% rename from libraries/script-engine/src/AnimationObject.cpp rename to libraries/animation/src/AnimationObject.cpp diff --git a/libraries/script-engine/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h similarity index 100% rename from libraries/script-engine/src/AnimationObject.h rename to libraries/animation/src/AnimationObject.h diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1fc03ceb66..f743a383d1 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1385,6 +1385,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // get offset transform from mapping float offsetScale = mapping.value("scale", 1.0f).toFloat(); + geometry.fstScaled = offsetScale; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 51e7380181..567a32c1cb 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -212,6 +212,8 @@ public: Extents bindExtents; Extents meshExtents; + float fstScaled; + QVector animationFrames; QVector attachments; diff --git a/libraries/models/CMakeLists.txt b/libraries/models/CMakeLists.txt index 062352e50c..1e70942872 100644 --- a/libraries/models/CMakeLists.txt +++ b/libraries/models/CMakeLists.txt @@ -28,6 +28,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # for streamable link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index af8500bf25..785183da56 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -85,6 +85,12 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& _shouldDie = false; _modelURL = MODEL_DEFAULT_MODEL_URL; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; + + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _frameIndex = 0.0f; + _jointMappingCompleted = false; + _lastAnimated = now; setProperties(properties); } @@ -110,6 +116,12 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t _shouldDie = false; _modelURL = MODEL_DEFAULT_MODEL_URL; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; + + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _frameIndex = 0.0f; + _jointMappingCompleted = false; + _lastAnimated = now; } bool ModelItem::appendModelData(OctreePacketData* packetData) const { @@ -150,6 +162,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { if (success) { success = packetData->appendValue(getModelRotation()); } + + // animationURL + if (success) { + uint16_t animationURLLength = _animationURL.size() + 1; // include NULL + success = packetData->appendValue(animationURLLength); + if (success) { + success = packetData->appendRawData((const unsigned char*)qPrintable(_animationURL), animationURLLength); + } + } + return success; } @@ -215,7 +237,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(modelURLLength); bytesRead += sizeof(modelURLLength); QString modelURLString((const char*)dataAt); - _modelURL = modelURLString; + setModelURL(modelURLString); dataAt += modelURLLength; bytesRead += modelURLLength; @@ -224,6 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += bytes; bytesRead += bytes; + // animationURL + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + bytesRead += sizeof(animationURLLength); + QString animationURLString((const char*)dataAt); + setAnimationURL(animationURLString); + dataAt += animationURLLength; + bytesRead += animationURLLength; + +qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + + //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); } return bytesRead; @@ -346,6 +381,21 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes += bytes; } + // animationURL + if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + processedBytes += sizeof(animationURLLength); + QString tempString((const char*)dataAt); + newModelItem._animationURL = tempString; + dataAt += animationURLLength; + processedBytes += animationURLLength; + +qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); + + } + const bool wantDebugging = false; if (wantDebugging) { qDebug("ModelItem::fromEditPacket()..."); @@ -476,6 +526,21 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id sizeOut += bytes; } + // animationURL + if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { + uint16_t urlLength = properties.getAnimationURL().size() + 1; + memcpy(copyAt, &urlLength, sizeof(urlLength)); + copyAt += sizeof(urlLength); + sizeOut += sizeof(urlLength); + memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); + copyAt += urlLength; + sizeOut += urlLength; + +qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable(properties.getAnimationURL()); + + } + + bool wantDebugging = false; if (wantDebugging) { qDebug("encodeModelItemEditMessageDetails()...."); @@ -521,6 +586,66 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi } } + +QMap ModelItem::_loadedAnimations; // TODO: cleanup?? +AnimationCache ModelItem::_animationCache; + +Animation* ModelItem::getAnimation(const QString& url) { + AnimationPointer animation; + + // if we don't already have this model then create it and initialize it + if (_loadedAnimations.find(url) == _loadedAnimations.end()) { + animation = _animationCache.getAnimation(url); + _loadedAnimations[url] = animation; + } else { + animation = _loadedAnimations[url]; + } + return animation.data(); +} + +void ModelItem::mapJoints(const QStringList& modelJointNames) { + // if we don't have animation, or we're already joint mapped then bail early + if (!hasAnimation() || _jointMappingCompleted) { + return; + } + + Animation* myAnimation = getAnimation(_animationURL); + + if (!_jointMappingCompleted) { + QStringList animationJointNames = myAnimation->getJointNames(); + if (modelJointNames.size() > 0 && animationJointNames.size() > 0) { + _jointMapping.resize(modelJointNames.size()); + for (int i = 0; i < modelJointNames.size(); i++) { + _jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]); + } + _jointMappingCompleted = true; + } + } +} + +QVector ModelItem::getAnimationFrame() { + QVector frameData; + if (hasAnimation() && _jointMappingCompleted) { + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; + Animation* myAnimation = getAnimation(_animationURL); + QVector frames = myAnimation->getFrames(); + int frameIndex = (int)std::floor(_frameIndex) % frames.size(); + QVector rotations = frames[frameIndex].rotations; + frameData.resize(_jointMapping.size()); + for (int j = 0; j < _jointMapping.size(); j++) { + int rotationIndex = _jointMapping[j]; + if (rotationIndex != -1 && rotationIndex < rotations.size()) { + frameData[j] = rotations[rotationIndex]; + } + } + } + return frameData; +} + void ModelItem::update(const quint64& now) { _lastUpdated = now; setShouldDie(getShouldDie()); @@ -547,6 +672,7 @@ ModelItemProperties::ModelItemProperties() : _shouldDie(false), _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), + _animationURL(""), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -558,6 +684,7 @@ ModelItemProperties::ModelItemProperties() : _shouldDieChanged(false), _modelURLChanged(false), _modelRotationChanged(false), + _animationURLChanged(false), _defaultSettings(true) { } @@ -589,6 +716,11 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_MODEL_ROTATION; } + if (_animationURLChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; + } + + return changedBits; } @@ -611,6 +743,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation); properties.setProperty("modelRotation", modelRotation); + properties.setProperty("animationURL", _animationURL); if (_idSet) { properties.setProperty("id", _id); @@ -707,6 +840,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue animationURL = object.property("animationURL"); + if (animationURL.isValid()) { + QString newAnimationURL; + newAnimationURL = animationURL.toVariant().toString(); + if (_defaultSettings || newAnimationURL != _animationURL) { + _animationURL = newAnimationURL; + _animationURLChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -741,7 +884,14 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { modelItem.setModelRotation(_modelRotation); somethingChanged = true; } - + + if (_animationURLChanged) { + modelItem.setAnimationURL(_animationURL); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; + } + if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -761,6 +911,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _shouldDie = modelItem.getShouldDie(); _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); + _animationURL = modelItem.getAnimationURL(); _id = modelItem.getID(); _idSet = true; @@ -772,6 +923,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _shouldDieChanged = false; _modelURLChanged = false; _modelRotationChanged = false; + _animationURLChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 9edcf482c0..72a12c9b2c 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,14 +40,16 @@ const uint32_t UNKNOWN_MODEL_ID = 0xFFFFFFFF; const uint16_t MODEL_PACKET_CONTAINS_RADIUS = 1; const uint16_t MODEL_PACKET_CONTAINS_POSITION = 2; const uint16_t MODEL_PACKET_CONTAINS_COLOR = 4; -const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 512; -const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 1024; -const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 2048; +const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8; +const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16; +const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; +const QString MODEL_DEFAULT_ANIMATION_URL(""); /// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of @@ -69,6 +72,7 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } + const QString& getAnimationURL() const { return _animationURL; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -82,6 +86,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } + void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -97,6 +102,7 @@ private: QString _modelURL; glm::quat _modelRotation; + QString _animationURL; uint32_t _id; bool _idSet; @@ -109,6 +115,7 @@ private: bool _modelURLChanged; bool _modelRotationChanged; + bool _animationURLChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -178,6 +185,8 @@ public: bool hasModel() const { return !_modelURL.isEmpty(); } const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } + bool hasAnimation() const { return !_animationURL.isEmpty(); } + const QString& getAnimationURL() const { return _animationURL; } ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); } ModelItemProperties getProperties() const; @@ -196,6 +205,7 @@ public: bool getShouldDie() const { return _shouldDie; } uint32_t getCreatorTokenID() const { return _creatorTokenID; } bool isNewlyCreated() const { return _newlyCreated; } + bool isKnownID() const { return getID() != UNKNOWN_MODEL_ID; } /// set position in domain scale units (0.0 - 1.0) void setPosition(const glm::vec3& value) { _position = value; } @@ -215,6 +225,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } + void setAnimationURL(const QString& url) { _animationURL = url; } void setProperties(const ModelItemProperties& properties); @@ -239,6 +250,10 @@ public: static uint32_t getNextCreatorTokenID(); static void handleAddModelResponse(const QByteArray& packet); + void mapJoints(const QStringList& modelJointNames); + QVector getAnimationFrame(); + bool jointsMapped() const { return _jointMappingCompleted; }; + protected: glm::vec3 _position; rgbColor _color; @@ -256,10 +271,23 @@ protected: quint64 _lastUpdated; quint64 _lastEdited; + quint64 _lastAnimated; + + QString _animationURL; + float _frameIndex; // we keep this as a float and round to int only when we need the exact index + bool _jointMappingCompleted; + QVector _jointMapping; + // used by the static interfaces for creator token ids static uint32_t _nextCreatorTokenID; static std::map _tokenIDsToIDs; + + + static Animation* getAnimation(const QString& url); + static QMap _loadedAnimations; + static AnimationCache _animationCache; + }; #endif // hifi_ModelItem_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 0785b81581..b9eee6e0c9 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -66,6 +66,8 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeOctreeStats: return 1; + case PacketTypeModelData: + return 1; default: return 0; } diff --git a/libraries/particles/CMakeLists.txt b/libraries/particles/CMakeLists.txt index 1cb60756a2..8cd2f30012 100644 --- a/libraries/particles/CMakeLists.txt +++ b/libraries/particles/CMakeLists.txt @@ -25,6 +25,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # link ZLIB and GnuTLS find_package(ZLIB) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index ee918ff864..0374ad570c 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -27,6 +27,7 @@ link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # link ZLIB find_package(ZLIB) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 09d41e3e2e..96cc874453 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -18,13 +18,12 @@ #include #include +#include #include -#include - #include #include +#include -#include "AnimationCache.h" #include "AbstractControllerScriptingInterface.h" #include "Quat.h" #include "ScriptUUID.h" From d8488b34efafaf54caa5e3cb1516333cf598b05c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 9 May 2014 16:08:16 -0700 Subject: [PATCH 02/14] first cut at animations in models --- libraries/animation/CMakeLists.txt | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 libraries/animation/CMakeLists.txt diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt new file mode 100644 index 0000000000..36088ba4bd --- /dev/null +++ b/libraries/animation/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 2.8) + +if (WIN32) + cmake_policy (SET CMP0020 NEW) +endif (WIN32) + +set(ROOT_DIR ../..) +set(MACRO_DIR "${ROOT_DIR}/cmake/macros") + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME animation) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME}) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} "${ROOT_DIR}") + +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") + +# link ZLIB +find_package(ZLIB) +find_package(GnuTLS REQUIRED) + +# add a definition for ssize_t so that windows doesn't bail on gnutls.h +if (WIN32) + add_definitions(-Dssize_t=long) +endif () + +include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}") +target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" "${GNUTLS_LIBRARY}" Qt5::Widgets) From 3ffd4cd65dd8675e5561b77d35b423b189c9da73 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 09:13:53 -0700 Subject: [PATCH 03/14] removed some debug --- interface/src/models/ModelTreeRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index e5c55ef066..36a22621b2 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -183,9 +183,11 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); + /* qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); + */ // handle animations.. if (modelItem.hasAnimation()) { From 02ca7c75eb1feb084609f47445915f668499fc94 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 11:02:10 -0700 Subject: [PATCH 04/14] first cut at adding version parsing to model items, not really working --- libraries/models/src/ModelItem.cpp | 23 ++++++++++++----------- libraries/models/src/ModelItem.h | 2 ++ libraries/octree/src/Octree.cpp | 6 ++++-- libraries/octree/src/Octree.h | 7 +++++-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 785183da56..1c735c2075 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -246,18 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += bytes; bytesRead += bytes; - // animationURL - uint16_t animationURLLength; - memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); - dataAt += sizeof(animationURLLength); - bytesRead += sizeof(animationURLLength); - QString animationURLString((const char*)dataAt); - setAnimationURL(animationURLString); - dataAt += animationURLLength; - bytesRead += animationURLLength; - -qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + if (args.bitstreamVersion >= VERSION_MODELS_HAVE_ANIMATION) { + // animationURL + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + bytesRead += sizeof(animationURLLength); + QString animationURLString((const char*)dataAt); + setAnimationURL(animationURLString); + dataAt += animationURLLength; + bytesRead += animationURLLength; + qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + } //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 72a12c9b2c..cafd150e6d 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -51,6 +51,8 @@ const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; const QString MODEL_DEFAULT_ANIMATION_URL(""); +const PacketVersion VERSION_MODELS_HAVE_ANIMATION = 1; + /// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of /// model item properties via JavaScript hashes/QScriptValues diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5b766ecdd7..9f08adb1d9 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1556,6 +1556,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, bool Octree::readFromSVOFile(const char* fileName) { bool fileOk = false; + PacketVersion gotVersion = 0; std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); if(file.is_open()) { emit importSize(1.0f, 1.0f, 1.0f); @@ -1587,7 +1588,7 @@ bool Octree::readFromSVOFile(const char* fileName) { dataAt += sizeof(expectedType); dataLength -= sizeof(expectedType); PacketVersion expectedVersion = versionForPacketType(expectedType); - PacketVersion gotVersion = *dataAt; + gotVersion = *dataAt; if (gotVersion == expectedVersion) { dataAt += sizeof(expectedVersion); dataLength -= sizeof(expectedVersion); @@ -1602,7 +1603,8 @@ bool Octree::readFromSVOFile(const char* fileName) { fileOk = true; // assume the file is ok } if (fileOk) { - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); } delete[] entireFile; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 4a17cb3c1d..0b9cc31a2a 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -170,6 +170,7 @@ public: QUuid sourceUUID; SharedNodePointer sourceNode; bool wantImportProgress; + PacketVersion bitstreamVersion; ReadBitstreamToTreeParams( bool includeColor = WANT_COLOR, @@ -177,13 +178,15 @@ public: OctreeElement* destinationElement = NULL, QUuid sourceUUID = QUuid(), SharedNodePointer sourceNode = SharedNodePointer(), - bool wantImportProgress = false) : + bool wantImportProgress = false, + PacketVersion bitstreamVersion = 0) : includeColor(includeColor), includeExistsBits(includeExistsBits), destinationElement(destinationElement), sourceUUID(sourceUUID), sourceNode(sourceNode), - wantImportProgress(wantImportProgress) + wantImportProgress(wantImportProgress), + bitstreamVersion(bitstreamVersion) {} }; From 8fe74d006a96429f606365f14cc82e4bc46a1698 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 11:40:15 -0700 Subject: [PATCH 05/14] correctly handle reading models.svo files with older version --- libraries/models/src/ModelItem.cpp | 3 +++ libraries/models/src/ModelTree.h | 1 + libraries/octree/src/Octree.cpp | 12 +++++++----- libraries/octree/src/Octree.h | 4 ++++ libraries/octree/src/OctreeRenderer.cpp | 3 ++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 1c735c2075..a818105521 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -188,6 +188,7 @@ int ModelItem::expectedBytes() { } int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { + int bytesRead = 0; if (bytesLeftToRead >= expectedBytes()) { int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; @@ -258,6 +259,8 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT bytesRead += animationURLLength; qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + } else { + qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index ac25cdc003..10ef62c0a0 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -36,6 +36,7 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return true; } virtual PacketType expectedDataPacketType() const { return PacketTypeModelData; } + virtual bool canProcessVersion(PacketVersion thisVersion) const { return true; } // we support all versions virtual bool handlesEditPacketType(PacketType packetType) const; virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9f08adb1d9..12ccf98c70 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1587,14 +1587,16 @@ bool Octree::readFromSVOFile(const char* fileName) { if (gotType == expectedType) { dataAt += sizeof(expectedType); dataLength -= sizeof(expectedType); - PacketVersion expectedVersion = versionForPacketType(expectedType); gotVersion = *dataAt; - if (gotVersion == expectedVersion) { - dataAt += sizeof(expectedVersion); - dataLength -= sizeof(expectedVersion); + if (canProcessVersion(gotVersion)) { + dataAt += sizeof(gotVersion); + dataLength -= sizeof(gotVersion); fileOk = true; + qDebug("SVO file version match. Expected: %d Got: %d", + versionForPacketType(expectedDataPacketType()), gotVersion); } else { - qDebug("SVO file version mismatch. Expected: %d Got: %d", expectedVersion, gotVersion); + qDebug("SVO file version mismatch. Expected: %d Got: %d", + versionForPacketType(expectedDataPacketType()), gotVersion); } } else { qDebug("SVO file type mismatch. Expected: %c Got: %c", expectedType, gotType); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 0b9cc31a2a..84212586f8 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -203,6 +203,9 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return false; } virtual PacketType expectedDataPacketType() const { return PacketTypeUnknown; } + virtual bool canProcessVersion(PacketVersion thisVersion) const { + return thisVersion == versionForPacketType(expectedDataPacketType()); } + virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); } virtual bool handlesEditPacketType(PacketType packetType) const { return false; } virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; } @@ -306,6 +309,7 @@ public: bool getIsViewing() const { return _isViewing; } void setIsViewing(bool isViewing) { _isViewing = isViewing; } + signals: void importSize(float x, float y, float z); diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index c1ce3cb218..1a85518181 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -64,6 +64,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); QUuid sourceUUID = uuidFromPacketHeader(dataByteArray); PacketType expectedType = getExpectedPacketType(); + PacketVersion expectedVersion = _tree->expectedVersion(); // TODO: would be better to read this from the packet! if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); @@ -115,7 +116,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, - sourceUUID, sourceNode); + sourceUUID, sourceNode, false, expectedVersion); _tree->lockForWrite(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); From e4d2f075867f7fec8903008ede141682117dcea3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 12:51:45 -0700 Subject: [PATCH 06/14] fixed bug in writing small SVO files --- libraries/models/src/ModelItem.cpp | 3 ++- libraries/models/src/ModelTreeElement.cpp | 2 +- libraries/octree/src/Octree.cpp | 7 +------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index a818105521..aedd1fef47 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -263,7 +263,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } - //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); + //qDebug() << "ModelItem::readModelDataFromBuffer()... "; debugDump(); } return bytesRead; } @@ -417,6 +417,7 @@ void ModelItem::debugDump() const { qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z); qDebug(" radius:%f", getRadius()); qDebug(" color:%d,%d,%d", _color[0], _color[1], _color[2]); + qDebug() << " modelURL:" << qPrintable(getModelURL()); } bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id, const ModelItemProperties& properties, diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 5c5d5100cf..4edcdd3e92 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -324,7 +324,7 @@ int ModelTreeElement::readElementDataFromBuffer(const unsigned char* data, int b dataAt += sizeof(numberOfModels); bytesLeftToRead -= (int)sizeof(numberOfModels); bytesRead += sizeof(numberOfModels); - + if (bytesLeftToRead >= (int)(numberOfModels * expectedBytesPerModel)) { for (uint16_t i = 0; i < numberOfModels; i++) { ModelItem tempModel; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 12ccf98c70..128677ff2b 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -335,10 +335,8 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); int theseBytesRead = 0; theseBytesRead += octalCodeBytes; - theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes, bufferSizeBytes - (bytesRead + octalCodeBytes), args); - // skip bitstream to new startPoint bitstreamAt += theseBytesRead; bytesRead += theseBytesRead; @@ -1619,7 +1617,6 @@ bool Octree::readFromSVOFile(const char* fileName) { } void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { - std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { @@ -1642,13 +1639,12 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { nodeBag.insert(_rootElement); } - static OctreePacketData packetData; + OctreePacketData packetData; int bytesWritten = 0; bool lastPacketWritten = false; while (!nodeBag.isEmpty()) { OctreeElement* subTree = nodeBag.extract(); - lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params); @@ -1670,7 +1666,6 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { if (!lastPacketWritten) { file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); } - } file.close(); } From 5309c5ad8bd0e7228c4b23a98e76d68d1b4d9ba0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 13:44:44 -0700 Subject: [PATCH 07/14] add more properties to animated models --- examples/placeModelsWithHands.js | 1 + libraries/models/src/ModelItem.cpp | 132 +++++++++++++++++- libraries/models/src/ModelItem.h | 19 ++- .../models/src/ModelsScriptingInterface.h | 14 ++ 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index f16945472d..bd0915da54 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -231,6 +231,7 @@ function checkControllerSide(whichSide) { if (animationURLs[currentModelURL] !== "") { properties.animationURL = animationURLs[currentModelURL]; + properties.isAnimationPlaying = true; } debugPrint("modelRadius=" +modelRadius); diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index aedd1fef47..9dd1173259 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -88,7 +88,9 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _isAnimationPlaying = false; _frameIndex = 0.0f; + _jointMappingCompleted = false; _lastAnimated = now; @@ -119,6 +121,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _isAnimationPlaying = false; _frameIndex = 0.0f; _jointMappingCompleted = false; _lastAnimated = now; @@ -172,6 +175,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { } } + // isAnimationPlaying + if (success) { + success = packetData->appendValue(getIsAnimationPlaying()); + } + + // frameIndex + if (success) { + success = packetData->appendValue(getFrameIndex()); + } + return success; } @@ -259,6 +272,17 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT bytesRead += animationURLLength; qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + + // isAnimationPlaying + memcpy(&_isAnimationPlaying, dataAt, sizeof(_isAnimationPlaying)); + dataAt += sizeof(_isAnimationPlaying); + bytesRead += sizeof(_isAnimationPlaying); + + // frameIndex + memcpy(&_frameIndex, dataAt, sizeof(_frameIndex)); + dataAt += sizeof(_frameIndex); + bytesRead += sizeof(_frameIndex); + } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } @@ -395,9 +419,25 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; - qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); + } + // isAnimationPlaying + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { + + memcpy(&newModelItem._isAnimationPlaying, dataAt, sizeof(newModelItem._isAnimationPlaying)); + dataAt += sizeof(newModelItem._isAnimationPlaying); + processedBytes += sizeof(newModelItem._isAnimationPlaying); + } + + // frameIndex + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { + + memcpy(&newModelItem._frameIndex, dataAt, sizeof(newModelItem._frameIndex)); + dataAt += sizeof(newModelItem._frameIndex); + processedBytes += sizeof(newModelItem._frameIndex); } const bool wantDebugging = false; @@ -545,6 +585,30 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable } + // isAnimationPlaying + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { + + bool isAnimationPlaying = properties.getIsAnimationPlaying(); + memcpy(copyAt, &isAnimationPlaying, sizeof(isAnimationPlaying)); + copyAt += sizeof(isAnimationPlaying); + sizeOut += sizeof(isAnimationPlaying); + + +qDebug() << "encodeModelItemEditMessageDetails()... isAnimationPlaying=" << isAnimationPlaying; + } + + // frameIndex + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { + + float frameIndex = properties.getFrameIndex(); + memcpy(copyAt, &frameIndex, sizeof(frameIndex)); + copyAt += sizeof(frameIndex); + sizeOut += sizeof(frameIndex); + +qDebug() << "encodeModelItemEditMessageDetails()... frameIndex=" << frameIndex; + } bool wantDebugging = false; if (wantDebugging) { @@ -631,11 +695,16 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) { QVector ModelItem::getAnimationFrame() { QVector frameData; if (hasAnimation() && _jointMappingCompleted) { - quint64 now = usecTimestampNow(); - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; + + // only advance the frame index if we're playing + if (getIsAnimationPlaying()) { + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; + } + Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); int frameIndex = (int)std::floor(_frameIndex) % frames.size(); @@ -678,6 +747,8 @@ ModelItemProperties::ModelItemProperties() : _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _animationURL(""), + _isAnimationPlaying(false), + _frameIndex(0.0), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -690,6 +761,8 @@ ModelItemProperties::ModelItemProperties() : _modelURLChanged(false), _modelRotationChanged(false), _animationURLChanged(false), + _isAnimationPlayingChanged(false), + _frameIndexChanged(false), _defaultSettings(true) { } @@ -725,6 +798,13 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; } + if (_isAnimationPlayingChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING; + } + + if (_frameIndexChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME; + } return changedBits; } @@ -749,6 +829,8 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("modelRotation", modelRotation); properties.setProperty("animationURL", _animationURL); + properties.setProperty("isAnimationPlaying", _isAnimationPlaying); + properties.setProperty("frameIndex", _frameIndex); if (_idSet) { properties.setProperty("id", _id); @@ -855,6 +937,26 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue isAnimationPlaying = object.property("isAnimationPlaying"); + if (isAnimationPlaying.isValid()) { + bool newIsAnimationPlaying; + newIsAnimationPlaying = isAnimationPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _isAnimationPlaying) { + _isAnimationPlaying = newIsAnimationPlaying; + _isAnimationPlayingChanged = true; + } + } + + QScriptValue frameIndex = object.property("frameIndex"); + if (frameIndex.isValid()) { + float newFrameIndex; + newFrameIndex = frameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _frameIndex) { + _frameIndex = newFrameIndex; + _frameIndexChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -897,6 +999,20 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } + if (_isAnimationPlayingChanged) { + modelItem.setIsAnimationPlaying(_isAnimationPlaying); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _isAnimationPlaying=" << _isAnimationPlaying; + } + + if (_frameIndexChanged) { + modelItem.setFrameIndex(_frameIndex); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _frameIndex=" << _frameIndex; + } + if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -917,6 +1033,8 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); _animationURL = modelItem.getAnimationURL(); + _isAnimationPlaying = modelItem.getIsAnimationPlaying(); + _frameIndex = modelItem.getFrameIndex(); _id = modelItem.getID(); _idSet = true; @@ -929,6 +1047,8 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURLChanged = false; _modelRotationChanged = false; _animationURLChanged = false; + _isAnimationPlayingChanged = false; + _frameIndexChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index cafd150e6d..a6c317d122 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -44,6 +44,8 @@ const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8; const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16; const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_PLAYING = 128; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FRAME = 256; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container @@ -75,6 +77,8 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } const QString& getAnimationURL() const { return _animationURL; } + float getFrameIndex() const { return _frameIndex; } + bool getIsAnimationPlaying() const { return _isAnimationPlaying; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -89,6 +93,8 @@ public: void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } + void setFrameIndex(float value) { _frameIndex = value; _frameIndexChanged = true; } + void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; _isAnimationPlayingChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -105,6 +111,8 @@ private: QString _modelURL; glm::quat _modelRotation; QString _animationURL; + bool _isAnimationPlaying; + float _frameIndex; uint32_t _id; bool _idSet; @@ -118,6 +126,8 @@ private: bool _modelURLChanged; bool _modelRotationChanged; bool _animationURLChanged; + bool _isAnimationPlayingChanged; + bool _frameIndexChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -228,6 +238,8 @@ public: void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setAnimationURL(const QString& url) { _animationURL = url; } + void setFrameIndex(float value) { _frameIndex = value; } + void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; } void setProperties(const ModelItemProperties& properties); @@ -254,7 +266,10 @@ public: void mapJoints(const QStringList& modelJointNames); QVector getAnimationFrame(); - bool jointsMapped() const { return _jointMappingCompleted; }; + bool jointsMapped() const { return _jointMappingCompleted; } + + bool getIsAnimationPlaying() const { return _isAnimationPlaying; } + float getFrameIndex() const { return _frameIndex; } protected: glm::vec3 _position; @@ -277,6 +292,8 @@ protected: QString _animationURL; float _frameIndex; // we keep this as a float and round to int only when we need the exact index + bool _isAnimationPlaying; + bool _jointMappingCompleted; QVector _jointMapping; diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index bf8e193f25..f08ec715a9 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -59,6 +59,20 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models QVector findModels(const glm::vec3& center, float radius) const; + /* + /// pauses the model animation. + ModelItemID pauseModelAnimation(ModelItemID modelID); + + /// plays the model animation. + ModelItemID playModelAnimation(ModelItemID modelID); + + /// gets the current frame of the model animation. + float getModelAnimationFrame(ModelItemID modelID); + + /// gets the current frame of the model animation. + void setModelAnimationFrame(ModelItemID modelID, float frame); + */ + signals: void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision); From 0fc33f575ffece93f0a928ba50523598c884e871 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 14:07:54 -0700 Subject: [PATCH 08/14] moving frame advancement to update() --- libraries/models/src/ModelItem.cpp | 35 +++++++++++++++-------- libraries/models/src/ModelTreeElement.cpp | 5 ++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 9dd1173259..f9b1092c84 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -283,6 +283,8 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(_frameIndex); bytesRead += sizeof(_frameIndex); + qDebug() << "readModelDataFromBuffer()... _frameIndex=" << _frameIndex; + } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } @@ -695,16 +697,6 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) { QVector ModelItem::getAnimationFrame() { QVector frameData; if (hasAnimation() && _jointMappingCompleted) { - - // only advance the frame index if we're playing - if (getIsAnimationPlaying()) { - quint64 now = usecTimestampNow(); - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; - } - Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); int frameIndex = (int)std::floor(_frameIndex) % frames.size(); @@ -720,9 +712,28 @@ QVector ModelItem::getAnimationFrame() { return frameData; } -void ModelItem::update(const quint64& now) { - _lastUpdated = now; +void ModelItem::update(const quint64& updateTime) { + _lastUpdated = updateTime; setShouldDie(getShouldDie()); + +//qDebug() << "ModelItem::update() now=" << now; + + // only advance the frame index if we're playing + if (getIsAnimationPlaying()) { + + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; +qDebug() << "ModelItem::update() now=" << now; +qDebug() << " updateTime=" << updateTime; +qDebug() << " _lastAnimated=" << _lastAnimated; +qDebug() << " deltaTime=" << deltaTime; + + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; +qDebug() << " _frameIndex=" << _frameIndex; + + } } void ModelItem::copyChangedProperties(const ModelItem& other) { diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 4edcdd3e92..e8b37c478b 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -102,10 +102,15 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { +//qDebug() << "ModelTreeElement::update()..."; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { ModelItem& model = (*modelItr); + + // TODO: this _lastChanged isn't actually changing because we're not marking this element as changed. + // how do we want to handle this??? We really only want to consider an element changed when it is + // edited... not just animated... model.update(_lastChanged); // If the model wants to die, or if it's left our bounding box, then move it From a56567a3693aa5018d5f27c306a3a285389abe7b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 13 May 2014 09:27:31 -0700 Subject: [PATCH 09/14] add support for adjustable FPS --- examples/placeModelsWithHands.js | 2 +- libraries/models/src/ModelItem.cpp | 228 ++++++++++++------ libraries/models/src/ModelItem.h | 37 +-- .../models/src/ModelsScriptingInterface.h | 5 +- 4 files changed, 176 insertions(+), 96 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index bd0915da54..47b4615924 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -231,7 +231,7 @@ function checkControllerSide(whichSide) { if (animationURLs[currentModelURL] !== "") { properties.animationURL = animationURLs[currentModelURL]; - properties.isAnimationPlaying = true; + properties.animationIsPlaying = true; } debugPrint("modelRadius=" +modelRadius); diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index f9b1092c84..474a6a38a4 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -88,8 +88,9 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _isAnimationPlaying = false; - _frameIndex = 0.0f; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _jointMappingCompleted = false; _lastAnimated = now; @@ -121,8 +122,9 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _isAnimationPlaying = false; - _frameIndex = 0.0f; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _jointMappingCompleted = false; _lastAnimated = now; } @@ -175,14 +177,19 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { } } - // isAnimationPlaying + // animationIsPlaying if (success) { - success = packetData->appendValue(getIsAnimationPlaying()); + success = packetData->appendValue(getAnimationIsPlaying()); } - // frameIndex + // animationFrameIndex if (success) { - success = packetData->appendValue(getFrameIndex()); + success = packetData->appendValue(getAnimationFrameIndex()); + } + + // animationFPS + if (success) { + success = packetData->appendValue(getAnimationFPS()); } return success; @@ -273,17 +280,24 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); - // isAnimationPlaying - memcpy(&_isAnimationPlaying, dataAt, sizeof(_isAnimationPlaying)); - dataAt += sizeof(_isAnimationPlaying); - bytesRead += sizeof(_isAnimationPlaying); + // animationIsPlaying + memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying)); + dataAt += sizeof(_animationIsPlaying); + bytesRead += sizeof(_animationIsPlaying); - // frameIndex - memcpy(&_frameIndex, dataAt, sizeof(_frameIndex)); - dataAt += sizeof(_frameIndex); - bytesRead += sizeof(_frameIndex); + // animationFrameIndex + memcpy(&_animationFrameIndex, dataAt, sizeof(_animationFrameIndex)); + dataAt += sizeof(_animationFrameIndex); + bytesRead += sizeof(_animationFrameIndex); - qDebug() << "readModelDataFromBuffer()... _frameIndex=" << _frameIndex; + qDebug() << "readModelDataFromBuffer()... _animationFrameIndex=" << _animationFrameIndex; + + // animationFPS + memcpy(&_animationFPS, dataAt, sizeof(_animationFPS)); + dataAt += sizeof(_animationFPS); + bytesRead += sizeof(_animationFPS); + + qDebug() << "readModelDataFromBuffer()... _animationFPS=" << _animationFPS; } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; @@ -424,22 +438,31 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); } - // isAnimationPlaying + // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { - memcpy(&newModelItem._isAnimationPlaying, dataAt, sizeof(newModelItem._isAnimationPlaying)); - dataAt += sizeof(newModelItem._isAnimationPlaying); - processedBytes += sizeof(newModelItem._isAnimationPlaying); + memcpy(&newModelItem._animationIsPlaying, dataAt, sizeof(newModelItem._animationIsPlaying)); + dataAt += sizeof(newModelItem._animationIsPlaying); + processedBytes += sizeof(newModelItem._animationIsPlaying); } - // frameIndex + // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { - memcpy(&newModelItem._frameIndex, dataAt, sizeof(newModelItem._frameIndex)); - dataAt += sizeof(newModelItem._frameIndex); - processedBytes += sizeof(newModelItem._frameIndex); + memcpy(&newModelItem._animationFrameIndex, dataAt, sizeof(newModelItem._animationFrameIndex)); + dataAt += sizeof(newModelItem._animationFrameIndex); + processedBytes += sizeof(newModelItem._animationFrameIndex); + } + + // animationFPS + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { + + memcpy(&newModelItem._animationFPS, dataAt, sizeof(newModelItem._animationFPS)); + dataAt += sizeof(newModelItem._animationFPS); + processedBytes += sizeof(newModelItem._animationFPS); } const bool wantDebugging = false; @@ -587,29 +610,41 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable } - // isAnimationPlaying + // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { - bool isAnimationPlaying = properties.getIsAnimationPlaying(); - memcpy(copyAt, &isAnimationPlaying, sizeof(isAnimationPlaying)); - copyAt += sizeof(isAnimationPlaying); - sizeOut += sizeof(isAnimationPlaying); + bool animationIsPlaying = properties.getAnimationIsPlaying(); + memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); + copyAt += sizeof(animationIsPlaying); + sizeOut += sizeof(animationIsPlaying); -qDebug() << "encodeModelItemEditMessageDetails()... isAnimationPlaying=" << isAnimationPlaying; +qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << animationIsPlaying; } - // frameIndex + // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { - float frameIndex = properties.getFrameIndex(); - memcpy(copyAt, &frameIndex, sizeof(frameIndex)); - copyAt += sizeof(frameIndex); - sizeOut += sizeof(frameIndex); + float animationFrameIndex = properties.getAnimationFrameIndex(); + memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); + copyAt += sizeof(animationFrameIndex); + sizeOut += sizeof(animationFrameIndex); -qDebug() << "encodeModelItemEditMessageDetails()... frameIndex=" << frameIndex; +qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << animationFrameIndex; + } + + // animationFPS + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { + + float animationFPS = properties.getAnimationFPS(); + memcpy(copyAt, &animationFPS, sizeof(animationFPS)); + copyAt += sizeof(animationFPS); + sizeOut += sizeof(animationFPS); + +qDebug() << "encodeModelItemEditMessageDetails()... animationFPS=" << animationFPS; } bool wantDebugging = false; @@ -699,8 +734,8 @@ QVector ModelItem::getAnimationFrame() { if (hasAnimation() && _jointMappingCompleted) { Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); - int frameIndex = (int)std::floor(_frameIndex) % frames.size(); - QVector rotations = frames[frameIndex].rotations; + int animationFrameIndex = (int)std::floor(_animationFrameIndex) % frames.size(); + QVector rotations = frames[animationFrameIndex].rotations; frameData.resize(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { int rotationIndex = _jointMapping[j]; @@ -716,23 +751,29 @@ void ModelItem::update(const quint64& updateTime) { _lastUpdated = updateTime; setShouldDie(getShouldDie()); -//qDebug() << "ModelItem::update() now=" << now; + quint64 now = usecTimestampNow(); // only advance the frame index if we're playing - if (getIsAnimationPlaying()) { + if (getAnimationIsPlaying()) { - quint64 now = usecTimestampNow(); float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; -qDebug() << "ModelItem::update() now=" << now; -qDebug() << " updateTime=" << updateTime; -qDebug() << " _lastAnimated=" << _lastAnimated; -qDebug() << " deltaTime=" << deltaTime; - + + const bool wantDebugging = false; + if (wantDebugging) { + qDebug() << "ModelItem::update() now=" << now; + qDebug() << " updateTime=" << updateTime; + qDebug() << " _lastAnimated=" << _lastAnimated; + qDebug() << " deltaTime=" << deltaTime; + } _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; -qDebug() << " _frameIndex=" << _frameIndex; + _animationFrameIndex += deltaTime * _animationFPS; + if (wantDebugging) { + qDebug() << " _animationFrameIndex=" << _animationFrameIndex; + } + + } else { + _lastAnimated = now; } } @@ -758,8 +799,9 @@ ModelItemProperties::ModelItemProperties() : _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _animationURL(""), - _isAnimationPlaying(false), - _frameIndex(0.0), + _animationIsPlaying(false), + _animationFrameIndex(0.0), + _animationFPS(MODEL_DEFAULT_ANIMATION_FPS), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -772,8 +814,9 @@ ModelItemProperties::ModelItemProperties() : _modelURLChanged(false), _modelRotationChanged(false), _animationURLChanged(false), - _isAnimationPlayingChanged(false), - _frameIndexChanged(false), + _animationIsPlayingChanged(false), + _animationFrameIndexChanged(false), + _animationFPSChanged(false), _defaultSettings(true) { } @@ -809,14 +852,18 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; } - if (_isAnimationPlayingChanged) { + if (_animationIsPlayingChanged) { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING; } - if (_frameIndexChanged) { + if (_animationFrameIndexChanged) { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME; } + if (_animationFPSChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FPS; + } + return changedBits; } @@ -840,8 +887,9 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("modelRotation", modelRotation); properties.setProperty("animationURL", _animationURL); - properties.setProperty("isAnimationPlaying", _isAnimationPlaying); - properties.setProperty("frameIndex", _frameIndex); + properties.setProperty("animationIsPlaying", _animationIsPlaying); + properties.setProperty("animationFrameIndex", _animationFrameIndex); + properties.setProperty("animationFPS", _animationFPS); if (_idSet) { properties.setProperty("id", _id); @@ -948,23 +996,34 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } - QScriptValue isAnimationPlaying = object.property("isAnimationPlaying"); - if (isAnimationPlaying.isValid()) { + QScriptValue animationIsPlaying = object.property("animationIsPlaying"); + if (animationIsPlaying.isValid()) { bool newIsAnimationPlaying; - newIsAnimationPlaying = isAnimationPlaying.toVariant().toBool(); - if (_defaultSettings || newIsAnimationPlaying != _isAnimationPlaying) { - _isAnimationPlaying = newIsAnimationPlaying; - _isAnimationPlayingChanged = true; + newIsAnimationPlaying = animationIsPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) { + _animationIsPlaying = newIsAnimationPlaying; + _animationIsPlayingChanged = true; } } - QScriptValue frameIndex = object.property("frameIndex"); - if (frameIndex.isValid()) { + QScriptValue animationFrameIndex = object.property("animationFrameIndex"); + if (animationFrameIndex.isValid()) { float newFrameIndex; - newFrameIndex = frameIndex.toVariant().toFloat(); - if (_defaultSettings || newFrameIndex != _frameIndex) { - _frameIndex = newFrameIndex; - _frameIndexChanged = true; + newFrameIndex = animationFrameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _animationFrameIndex) { + _animationFrameIndex = newFrameIndex; + _animationFrameIndexChanged = true; + } + } + + QScriptValue animationFPS = object.property("animationFPS"); + if (animationFPS.isValid()) { + float newFPS; + newFPS = animationFPS.toVariant().toFloat(); + if (_defaultSettings || newFPS != _animationFPS) { + _animationFPS = newFPS; + _animationFPSChanged = true; +qDebug() << "ModelItemProperties::copyFromScriptValue()... _animationFPS=" << _animationFPS; } } @@ -1010,18 +1069,25 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } - if (_isAnimationPlayingChanged) { - modelItem.setIsAnimationPlaying(_isAnimationPlaying); + if (_animationIsPlayingChanged) { + modelItem.setAnimationIsPlaying(_animationIsPlaying); somethingChanged = true; -qDebug() << "ModelItemProperties::copyToModelItem()... _isAnimationPlaying=" << _isAnimationPlaying; +qDebug() << "ModelItemProperties::copyToModelItem()... _animationIsPlaying=" << _animationIsPlaying; } - if (_frameIndexChanged) { - modelItem.setFrameIndex(_frameIndex); + if (_animationFrameIndexChanged) { + modelItem.setAnimationFrameIndex(_animationFrameIndex); somethingChanged = true; -qDebug() << "ModelItemProperties::copyToModelItem()... _frameIndex=" << _frameIndex; +qDebug() << "ModelItemProperties::copyToModelItem()... _animationFrameIndex=" << _animationFrameIndex; + } + + if (_animationFPSChanged) { + modelItem.setAnimationFPS(_animationFPS); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _animationFPS=" << _animationFPS; } if (somethingChanged) { @@ -1044,8 +1110,9 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); _animationURL = modelItem.getAnimationURL(); - _isAnimationPlaying = modelItem.getIsAnimationPlaying(); - _frameIndex = modelItem.getFrameIndex(); + _animationIsPlaying = modelItem.getAnimationIsPlaying(); + _animationFrameIndex = modelItem.getAnimationFrameIndex(); + _animationFPS = modelItem.getAnimationFPS(); _id = modelItem.getID(); _idSet = true; @@ -1058,8 +1125,9 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURLChanged = false; _modelRotationChanged = false; _animationURLChanged = false; - _isAnimationPlayingChanged = false; - _frameIndexChanged = false; + _animationIsPlayingChanged = false; + _animationFrameIndexChanged = false; + _animationFPSChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index a6c317d122..18074fbe13 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -46,12 +46,14 @@ const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_PLAYING = 128; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FRAME = 256; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FPS = 512; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; const QString MODEL_DEFAULT_ANIMATION_URL(""); +const float MODEL_DEFAULT_ANIMATION_FPS = 30.0f; const PacketVersion VERSION_MODELS_HAVE_ANIMATION = 1; @@ -77,8 +79,9 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } const QString& getAnimationURL() const { return _animationURL; } - float getFrameIndex() const { return _frameIndex; } - bool getIsAnimationPlaying() const { return _isAnimationPlaying; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFPS() const { return _animationFPS; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -93,8 +96,9 @@ public: void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } - void setFrameIndex(float value) { _frameIndex = value; _frameIndexChanged = true; } - void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; _isAnimationPlayingChanged = true; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; } + void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -111,8 +115,9 @@ private: QString _modelURL; glm::quat _modelRotation; QString _animationURL; - bool _isAnimationPlaying; - float _frameIndex; + bool _animationIsPlaying; + float _animationFrameIndex; + float _animationFPS; uint32_t _id; bool _idSet; @@ -126,8 +131,9 @@ private: bool _modelURLChanged; bool _modelRotationChanged; bool _animationURLChanged; - bool _isAnimationPlayingChanged; - bool _frameIndexChanged; + bool _animationIsPlayingChanged; + bool _animationFrameIndexChanged; + bool _animationFPSChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -238,8 +244,9 @@ public: void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setAnimationURL(const QString& url) { _animationURL = url; } - void setFrameIndex(float value) { _frameIndex = value; } - void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; } + void setAnimationFPS(float value) { _animationFPS = value; } void setProperties(const ModelItemProperties& properties); @@ -268,8 +275,9 @@ public: QVector getAnimationFrame(); bool jointsMapped() const { return _jointMappingCompleted; } - bool getIsAnimationPlaying() const { return _isAnimationPlaying; } - float getFrameIndex() const { return _frameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + float getAnimationFPS() const { return _animationFPS; } protected: glm::vec3 _position; @@ -291,8 +299,9 @@ protected: quint64 _lastAnimated; QString _animationURL; - float _frameIndex; // we keep this as a float and round to int only when we need the exact index - bool _isAnimationPlaying; + float _animationFrameIndex; // we keep this as a float and round to int only when we need the exact index + bool _animationIsPlaying; + float _animationFPS; bool _jointMappingCompleted; QVector _jointMapping; diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index f08ec715a9..8447e5018d 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -65,7 +65,10 @@ public slots: /// plays the model animation. ModelItemID playModelAnimation(ModelItemID modelID); - + */ + + + /* /// gets the current frame of the model animation. float getModelAnimationFrame(ModelItemID modelID); From 0c34d9e065299e35374a38ad58c2e7a88e627337 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 12:59:59 -0700 Subject: [PATCH 10/14] cleanup remove some debug --- interface/src/models/ModelTreeRenderer.cpp | 6 ---- libraries/models/src/ModelItem.cpp | 35 ++----------------- libraries/models/src/ModelTreeElement.cpp | 1 - .../models/src/ModelsScriptingInterface.h | 17 --------- 4 files changed, 2 insertions(+), 57 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 36a22621b2..ae60683745 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -183,12 +183,6 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); - /* - qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); - qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); - qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); - */ - // handle animations.. if (modelItem.hasAnimation()) { if (!modelItem.jointsMapped()) { diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 474a6a38a4..7a4f9e3edc 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -278,8 +278,6 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += animationURLLength; bytesRead += animationURLLength; - qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); - // animationIsPlaying memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying)); dataAt += sizeof(_animationIsPlaying); @@ -290,26 +288,16 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(_animationFrameIndex); bytesRead += sizeof(_animationFrameIndex); - qDebug() << "readModelDataFromBuffer()... _animationFrameIndex=" << _animationFrameIndex; - // animationFPS memcpy(&_animationFPS, dataAt, sizeof(_animationFPS)); dataAt += sizeof(_animationFPS); bytesRead += sizeof(_animationFPS); - - qDebug() << "readModelDataFromBuffer()... _animationFPS=" << _animationFPS; - - } else { - qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } - - //qDebug() << "ModelItem::readModelDataFromBuffer()... "; debugDump(); } return bytesRead; } ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ModelTree* tree, bool& valid) { - ModelItem newModelItem; // id and _lastUpdated will get set here... const unsigned char* dataAt = data; processedBytes = 0; @@ -419,7 +407,8 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& } // modelRotation - if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { int bytes = unpackOrientationQuatFromBytes(dataAt, newModelItem._modelRotation); dataAt += bytes; processedBytes += bytes; @@ -435,7 +424,6 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; -qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); } // animationIsPlaying @@ -605,9 +593,6 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); copyAt += urlLength; sizeOut += urlLength; - -qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable(properties.getAnimationURL()); - } // animationIsPlaying @@ -618,9 +603,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); copyAt += sizeof(animationIsPlaying); sizeOut += sizeof(animationIsPlaying); - - -qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << animationIsPlaying; } // animationFrameIndex @@ -631,8 +613,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << anim memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); copyAt += sizeof(animationFrameIndex); sizeOut += sizeof(animationFrameIndex); - -qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << animationFrameIndex; } // animationFPS @@ -643,8 +623,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << ani memcpy(copyAt, &animationFPS, sizeof(animationFPS)); copyAt += sizeof(animationFPS); sizeOut += sizeof(animationFPS); - -qDebug() << "encodeModelItemEditMessageDetails()... animationFPS=" << animationFPS; } bool wantDebugging = false; @@ -1023,7 +1001,6 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { if (_defaultSettings || newFPS != _animationFPS) { _animationFPS = newFPS; _animationFPSChanged = true; -qDebug() << "ModelItemProperties::copyFromScriptValue()... _animationFPS=" << _animationFPS; } } @@ -1065,29 +1042,21 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { if (_animationURLChanged) { modelItem.setAnimationURL(_animationURL); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } if (_animationIsPlayingChanged) { modelItem.setAnimationIsPlaying(_animationIsPlaying); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationIsPlaying=" << _animationIsPlaying; } if (_animationFrameIndexChanged) { modelItem.setAnimationFrameIndex(_animationFrameIndex); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationFrameIndex=" << _animationFrameIndex; } if (_animationFPSChanged) { modelItem.setAnimationFPS(_animationFPS); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationFPS=" << _animationFPS; } if (somethingChanged) { diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index e8b37c478b..c5dce04fe2 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -102,7 +102,6 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { -//qDebug() << "ModelTreeElement::update()..."; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index 8447e5018d..bf8e193f25 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -59,23 +59,6 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models QVector findModels(const glm::vec3& center, float radius) const; - /* - /// pauses the model animation. - ModelItemID pauseModelAnimation(ModelItemID modelID); - - /// plays the model animation. - ModelItemID playModelAnimation(ModelItemID modelID); - */ - - - /* - /// gets the current frame of the model animation. - float getModelAnimationFrame(ModelItemID modelID); - - /// gets the current frame of the model animation. - void setModelAnimationFrame(ModelItemID modelID, float frame); - */ - signals: void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision); From 7c44162209ffc816108c5180e428587e7b4fc105 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:00:35 -0700 Subject: [PATCH 11/14] animated model example --- examples/animatedModelExample.js | 131 +++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 examples/animatedModelExample.js diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js new file mode 100644 index 0000000000..150ca36bcd --- /dev/null +++ b/examples/animatedModelExample.js @@ -0,0 +1,131 @@ +// +// animatedModelExample.js +// examples +// +// Created by Brad Hefta-Gaub on 12/31/13. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates creating and editing a model +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var count = 0; +var moveUntil = 6000; +var stopAfter = moveUntil + 100; + +var pitch = 0.0; +var yaw = 0.0; +var roll = 0.0; +var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll) + +var originalProperties = { + position: { x: 10, + y: 0, + z: 0 }, + + radius : 1, + + color: { red: 0, + green: 255, + blue: 0 }, + + modelURL: "http://www.fungibleinsight.com/faces/beta.fst", + modelRotation: rotation, + animationURL: "http://www.fungibleinsight.com/faces/gangnam_style_2.fbx", + animationIsPlaying: true, +}; + +var positionDelta = { x: 0, y: 0, z: 0 }; + + +var modelID = Models.addModel(originalProperties); +print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID); + +var isPlaying = true; +var playPauseEveryWhile = 360; +var animationFPS = 30; +var adjustFPSEveryWhile = 120; + +function moveModel(deltaTime) { + +//print("count =" + count); + +print("(count % playPauseEveryWhile)=" + (count % playPauseEveryWhile)); + + if (count % playPauseEveryWhile == 0) { + isPlaying = !isPlaying; + print("isPlaying=" + isPlaying); + } + +print("(count % adjustFPSEveryWhile)=" + (count % adjustFPSEveryWhile)); + + if (count % adjustFPSEveryWhile == 0) { + +print("considering adjusting animationFPS=" + animationFPS); + + if (animationFPS == 30) { + animationFPS = 10; + } else if (animationFPS == 10) { + animationFPS = 60; + } else if (animationFPS == 60) { + animationFPS = 30; + } + print("animationFPS=" + animationFPS); + isPlaying = true; + print("always start playing if we change the FPS -- isPlaying=" + isPlaying); + } + + if (count >= moveUntil) { + + // delete it... + if (count == moveUntil) { + print("calling Models.deleteModel()"); + Models.deleteModel(modelID); + } + + // stop it... + if (count >= stopAfter) { + print("calling Script.stop()"); + Script.stop(); + } + + count++; + return; // break early + } + + count++; + + //print("modelID.creatorTokenID = " + modelID.creatorTokenID); + + if (true) { + var newProperties = { + //position: { + // x: originalProperties.position.x + (count * positionDelta.x), + // y: originalProperties.position.y + (count * positionDelta.y), + // z: originalProperties.position.z + (count * positionDelta.z) + //}, + animationIsPlaying: isPlaying, + animationFPS: animationFPS, + }; + + + //print("modelID = " + modelID); + //print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); + + Models.editModel(modelID, newProperties); + } +} + + +// register the call back so it fires before each data send +Script.update.connect(moveModel); + + +Script.scriptEnding.connect(function () { + print("cleaning up..."); + print("modelID="+ modelID.creatorTokenID + ", id:" + modelID.id); + Models.deleteModel(modelID); +}); + From 0a8dc78c963d3796041c3d50ee819ec7f2b1dc3f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:08:32 -0700 Subject: [PATCH 12/14] tweaks to animation script example --- examples/animatedModelExample.js | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js index 150ca36bcd..5199eb419f 100644 --- a/examples/animatedModelExample.js +++ b/examples/animatedModelExample.js @@ -37,9 +37,6 @@ var originalProperties = { animationIsPlaying: true, }; -var positionDelta = { x: 0, y: 0, z: 0 }; - - var modelID = Models.addModel(originalProperties); print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID); @@ -47,24 +44,17 @@ var isPlaying = true; var playPauseEveryWhile = 360; var animationFPS = 30; var adjustFPSEveryWhile = 120; +var resetFrameEveryWhile = 600; function moveModel(deltaTime) { - -//print("count =" + count); - -print("(count % playPauseEveryWhile)=" + (count % playPauseEveryWhile)); - + var somethingChanged = false; if (count % playPauseEveryWhile == 0) { isPlaying = !isPlaying; print("isPlaying=" + isPlaying); + somethingChanged = true; } -print("(count % adjustFPSEveryWhile)=" + (count % adjustFPSEveryWhile)); - if (count % adjustFPSEveryWhile == 0) { - -print("considering adjusting animationFPS=" + animationFPS); - if (animationFPS == 30) { animationFPS = 10; } else if (animationFPS == 10) { @@ -75,6 +65,12 @@ print("considering adjusting animationFPS=" + animationFPS); print("animationFPS=" + animationFPS); isPlaying = true; print("always start playing if we change the FPS -- isPlaying=" + isPlaying); + somethingChanged = true; + } + + if (count % resetFrameEveryWhile == 0) { + resetFrame = true; + somethingChanged = true; } if (count >= moveUntil) { @@ -99,20 +95,17 @@ print("considering adjusting animationFPS=" + animationFPS); //print("modelID.creatorTokenID = " + modelID.creatorTokenID); - if (true) { + if (somethingChanged) { var newProperties = { - //position: { - // x: originalProperties.position.x + (count * positionDelta.x), - // y: originalProperties.position.y + (count * positionDelta.y), - // z: originalProperties.position.z + (count * positionDelta.z) - //}, animationIsPlaying: isPlaying, animationFPS: animationFPS, }; - - - //print("modelID = " + modelID); - //print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); + + if (resetFrame) { + print("resetting the frame!"); + newProperties.animationFrameIndex = 0; + resetFrame = false; + } Models.editModel(modelID, newProperties); } From fa20e8ff950e6bd8453b96abe138c558555cadee Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:17:10 -0700 Subject: [PATCH 13/14] fix the agent support for scripting models --- assignment-client/src/Agent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9b2134ba44..e6c14d06da 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -250,6 +250,12 @@ void Agent::run() { _particleViewer.init(); _scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree()); + _scriptEngine.registerGlobalObject("ModelViewer", &_modelViewer); + JurisdictionListener* modelJL = _scriptEngine.getModelsScriptingInterface()->getJurisdictionListener(); + _modelViewer.setJurisdictionListener(modelJL); + _modelViewer.init(); + _scriptEngine.getModelsScriptingInterface()->setModelTree(_modelViewer.getTree()); + _scriptEngine.setScriptContents(scriptContents); _scriptEngine.run(); setFinished(true); From f6c1d3e635b2e7d26175c25153a06ac148876e2e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 14:00:31 -0700 Subject: [PATCH 14/14] clean up animations --- libraries/models/src/ModelItem.cpp | 19 ++++++++++++++++++- libraries/models/src/ModelItem.h | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 7a4f9e3edc..c04f9a76ae 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -671,9 +671,26 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi } -QMap ModelItem::_loadedAnimations; // TODO: cleanup?? +QMap ModelItem::_loadedAnimations; // TODO: improve cleanup by leveraging the AnimationPointer(s) AnimationCache ModelItem::_animationCache; +// This class/instance will cleanup the animations once unloaded. +class ModelAnimationsBookkeeper { +public: + ~ModelAnimationsBookkeeper() { + ModelItem::cleanupLoadedAnimations(); + } +}; + +ModelAnimationsBookkeeper modelAnimationsBookkeeperInstance; + +void ModelItem::cleanupLoadedAnimations() { + foreach(AnimationPointer animation, _loadedAnimations) { + animation.clear(); + } + _loadedAnimations.clear(); +} + Animation* ModelItem::getAnimation(const QString& url) { AnimationPointer animation; diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 18074fbe13..847e58e7c2 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -278,6 +278,8 @@ public: bool getAnimationIsPlaying() const { return _animationIsPlaying; } float getAnimationFrameIndex() const { return _animationFrameIndex; } float getAnimationFPS() const { return _animationFPS; } + + static void cleanupLoadedAnimations(); protected: glm::vec3 _position;