first cut at animations in models

This commit is contained in:
ZappoMan 2014-05-09 16:08:06 -07:00
parent 6762d4d63c
commit 106c8bffd8
20 changed files with 297 additions and 28 deletions

View file

@ -33,6 +33,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(networking ${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(script-engine ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}")

View file

@ -37,6 +37,7 @@ var radiusMinimum = 0.05;
var radiusMaximum = 0.5; var radiusMaximum = 0.5;
var modelURLs = [ var modelURLs = [
"http://www.fungibleinsight.com/faces/beta.fst",
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/topHat.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/Feisar_Ship.FBX",
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.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", "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 currentModelURL = 1;
var numModels = modelURLs.length; var numModels = modelURLs.length;
@ -214,10 +228,18 @@ function checkControllerSide(whichSide) {
modelRotation: palmRotation, modelRotation: palmRotation,
modelURL: modelURLs[currentModelURL] modelURL: modelURLs[currentModelURL]
}; };
if (animationURLs[currentModelURL] !== "") {
properties.animationURL = animationURLs[currentModelURL];
}
debugPrint("modelRadius=" +modelRadius); debugPrint("modelRadius=" +modelRadius);
newModel = Models.addModel(properties); newModel = Models.addModel(properties);
print("just added model... newModel=" + newModel.creatorTokenID);
print("properties.animationURL=" + properties.animationURL);
if (whichSide == LEFT_PALM) { if (whichSide == LEFT_PALM) {
leftModelAlreadyInHand = true; leftModelAlreadyInHand = true;
leftHandModel = newModel; leftHandModel = newModel;

View file

@ -127,6 +127,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(audio ${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}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}")
# find any optional libraries # find any optional libraries

View file

@ -20,11 +20,16 @@ ModelTreeRenderer::ModelTreeRenderer() :
} }
ModelTreeRenderer::~ModelTreeRenderer() { ModelTreeRenderer::~ModelTreeRenderer() {
// delete the models in _modelsItemModels // delete the models in _knownModelsItemModels
foreach(Model* model, _modelsItemModels) { foreach(Model* model, _knownModelsItemModels) {
delete model; delete model;
} }
_modelsItemModels.clear(); _knownModelsItemModels.clear();
foreach(Model* model, _unknownModelsItemModels) {
delete model;
}
_unknownModelsItemModels.clear();
} }
void ModelTreeRenderer::init() { void ModelTreeRenderer::init() {
@ -43,17 +48,27 @@ void ModelTreeRenderer::render(RenderMode renderMode) {
OctreeRenderer::render(renderMode); OctreeRenderer::render(renderMode);
} }
Model* ModelTreeRenderer::getModel(const QString& url) { Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) {
Model* model = NULL; Model* model = NULL;
// if we don't already have this model then create it and initialize it if (modelItem.isKnownID()) {
if (_modelsItemModels.find(url) == _modelsItemModels.end()) { if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) {
model = new Model(); model = _knownModelsItemModels[modelItem.getID()];
model->init(); } else {
model->setURL(QUrl(url)); model = new Model();
_modelsItemModels[url] = model; model->init();
model->setURL(QUrl(modelItem.getModelURL()));
_knownModelsItemModels[modelItem.getID()] = model;
}
} else { } 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; return model;
} }
@ -63,7 +78,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
// we need to iterate the actual modelItems of the element // we need to iterate the actual modelItems of the element
ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; ModelTreeElement* modelTreeElement = (ModelTreeElement*)element;
const QList<ModelItem>& modelItems = modelTreeElement->getModels(); QList<ModelItem>& modelItems = modelTreeElement->getModels();
uint16_t numberOfModels = modelItems.size(); uint16_t numberOfModels = modelItems.size();
@ -139,7 +154,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
} }
for (uint16_t i = 0; i < numberOfModels; i++) { for (uint16_t i = 0; i < numberOfModels; i++) {
const ModelItem& modelItem = modelItems[i]; ModelItem& modelItem = modelItems[i];
// render modelItem aspoints // render modelItem aspoints
AABox modelBox = modelItem.getAABox(); AABox modelBox = modelItem.getAABox();
modelBox.scale(TREE_SCALE); modelBox.scale(TREE_SCALE);
@ -156,7 +171,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
glPushMatrix(); glPushMatrix();
const float alpha = 1.0f; const float alpha = 1.0f;
Model* model = getModel(modelItem.getModelURL()); Model* model = getModel(modelItem);
model->setScaleToFit(true, radius * 2.0f); model->setScaleToFit(true, radius * 2.0f);
model->setSnapModelToCenter(true); model->setSnapModelToCenter(true);
@ -167,6 +182,25 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
// set the position // set the position
model->setTranslation(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<glm::quat> 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); model->simulate(0.0f);
// TODO: should we allow modelItems to have alpha on their models? // TODO: should we allow modelItems to have alpha on their models?

View file

@ -49,9 +49,9 @@ public:
virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE);
protected: protected:
Model* getModel(const QString& url); Model* getModel(const ModelItem& modelItem);
QMap<uint32_t, Model*> _knownModelsItemModels;
QMap<QString, Model*> _modelsItemModels; QMap<uint32_t, Model*> _unknownModelsItemModels;
}; };
#endif // hifi_ModelTreeRenderer_h #endif // hifi_ModelTreeRenderer_h

View file

@ -430,7 +430,8 @@ Extents Model::getMeshExtents() const {
return Extents(); return Extents();
} }
const Extents& extents = _geometry->getFBXGeometry().meshExtents; 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; return scaledExtents;
} }
@ -438,8 +439,13 @@ Extents Model::getUnscaledMeshExtents() const {
if (!isActive()) { if (!isActive()) {
return Extents(); return Extents();
} }
const Extents& extents = _geometry->getFBXGeometry().meshExtents; 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 { 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; return true;
} }
QStringList Model::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(const_cast<Model*>(this), "getJointNames", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QStringList, result));
return result;
}
return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList();
}
void Model::clearShapes() { void Model::clearShapes() {
for (int i = 0; i < _jointShapes.size(); ++i) { for (int i = 0; i < _jointShapes.size(); ++i) {
delete _jointShapes[i]; delete _jointShapes[i];

View file

@ -182,6 +182,8 @@ public:
bool getJointPosition(int jointIndex, glm::vec3& position) const; bool getJointPosition(int jointIndex, glm::vec3& position) const;
bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
QStringList getJointNames() const;
void clearShapes(); void clearShapes();
void rebuildShapes(); void rebuildShapes();

View file

@ -36,7 +36,8 @@ QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const Q
} }
Animation::Animation(const QUrl& url) : Animation::Animation(const QUrl& url) :
Resource(url) { Resource(url),
_isValid(false) {
} }
class AnimationReader : public QRunnable { class AnimationReader : public QRunnable {
@ -93,6 +94,7 @@ QVector<FBXAnimationFrame> Animation::getFrames() const {
void Animation::setGeometry(const FBXGeometry& geometry) { void Animation::setGeometry(const FBXGeometry& geometry) {
_geometry = geometry; _geometry = geometry;
finishedLoading(true); finishedLoading(true);
_isValid = true;
} }
void Animation::downloadFinished(QNetworkReply* reply) { void Animation::downloadFinished(QNetworkReply* reply) {

View file

@ -53,6 +53,8 @@ public:
Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QStringList getJointNames() const;
Q_INVOKABLE QVector<FBXAnimationFrame> getFrames() const; Q_INVOKABLE QVector<FBXAnimationFrame> getFrames() const;
bool isValid() const { return _isValid; }
protected: protected:
@ -63,6 +65,7 @@ protected:
private: private:
FBXGeometry _geometry; FBXGeometry _geometry;
bool _isValid;
}; };
#endif // hifi_AnimationCache_h #endif // hifi_AnimationCache_h

View file

@ -1385,6 +1385,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
// get offset transform from mapping // get offset transform from mapping
float offsetScale = mapping.value("scale", 1.0f).toFloat(); float offsetScale = mapping.value("scale", 1.0f).toFloat();
geometry.fstScaled = offsetScale;
glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(),
mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); mapping.value("ry").toFloat(), mapping.value("rz").toFloat())));
geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(),

View file

@ -212,6 +212,8 @@ public:
Extents bindExtents; Extents bindExtents;
Extents meshExtents; Extents meshExtents;
float fstScaled;
QVector<FBXAnimationFrame> animationFrames; QVector<FBXAnimationFrame> animationFrames;
QVector<FBXAttachment> attachments; QVector<FBXAttachment> attachments;

View file

@ -28,6 +28,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
# for streamable # for streamable
link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}")

View file

@ -85,6 +85,12 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties&
_shouldDie = false; _shouldDie = false;
_modelURL = MODEL_DEFAULT_MODEL_URL; _modelURL = MODEL_DEFAULT_MODEL_URL;
_modelRotation = MODEL_DEFAULT_MODEL_ROTATION; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION;
// animation related
_animationURL = MODEL_DEFAULT_ANIMATION_URL;
_frameIndex = 0.0f;
_jointMappingCompleted = false;
_lastAnimated = now;
setProperties(properties); setProperties(properties);
} }
@ -110,6 +116,12 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t
_shouldDie = false; _shouldDie = false;
_modelURL = MODEL_DEFAULT_MODEL_URL; _modelURL = MODEL_DEFAULT_MODEL_URL;
_modelRotation = MODEL_DEFAULT_MODEL_ROTATION; _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 { bool ModelItem::appendModelData(OctreePacketData* packetData) const {
@ -150,6 +162,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const {
if (success) { if (success) {
success = packetData->appendValue(getModelRotation()); 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; return success;
} }
@ -215,7 +237,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT
dataAt += sizeof(modelURLLength); dataAt += sizeof(modelURLLength);
bytesRead += sizeof(modelURLLength); bytesRead += sizeof(modelURLLength);
QString modelURLString((const char*)dataAt); QString modelURLString((const char*)dataAt);
_modelURL = modelURLString; setModelURL(modelURLString);
dataAt += modelURLLength; dataAt += modelURLLength;
bytesRead += modelURLLength; bytesRead += modelURLLength;
@ -224,6 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT
dataAt += bytes; dataAt += bytes;
bytesRead += 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(); //printf("ModelItem::readModelDataFromBuffer()... "); debugDump();
} }
return bytesRead; return bytesRead;
@ -346,6 +381,21 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int&
processedBytes += bytes; 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; const bool wantDebugging = false;
if (wantDebugging) { if (wantDebugging) {
qDebug("ModelItem::fromEditPacket()..."); qDebug("ModelItem::fromEditPacket()...");
@ -476,6 +526,21 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id
sizeOut += bytes; 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; bool wantDebugging = false;
if (wantDebugging) { if (wantDebugging) {
qDebug("encodeModelItemEditMessageDetails()...."); qDebug("encodeModelItemEditMessageDetails()....");
@ -521,6 +586,66 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi
} }
} }
QMap<QString, AnimationPointer> 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<glm::quat> ModelItem::getAnimationFrame() {
QVector<glm::quat> 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<FBXAnimationFrame> frames = myAnimation->getFrames();
int frameIndex = (int)std::floor(_frameIndex) % frames.size();
QVector<glm::quat> 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) { void ModelItem::update(const quint64& now) {
_lastUpdated = now; _lastUpdated = now;
setShouldDie(getShouldDie()); setShouldDie(getShouldDie());
@ -547,6 +672,7 @@ ModelItemProperties::ModelItemProperties() :
_shouldDie(false), _shouldDie(false),
_modelURL(""), _modelURL(""),
_modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION),
_animationURL(""),
_id(UNKNOWN_MODEL_ID), _id(UNKNOWN_MODEL_ID),
_idSet(false), _idSet(false),
@ -558,6 +684,7 @@ ModelItemProperties::ModelItemProperties() :
_shouldDieChanged(false), _shouldDieChanged(false),
_modelURLChanged(false), _modelURLChanged(false),
_modelRotationChanged(false), _modelRotationChanged(false),
_animationURLChanged(false),
_defaultSettings(true) _defaultSettings(true)
{ {
} }
@ -589,6 +716,11 @@ uint16_t ModelItemProperties::getChangedBits() const {
changedBits += MODEL_PACKET_CONTAINS_MODEL_ROTATION; changedBits += MODEL_PACKET_CONTAINS_MODEL_ROTATION;
} }
if (_animationURLChanged) {
changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL;
}
return changedBits; return changedBits;
} }
@ -611,6 +743,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const
QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation); QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation);
properties.setProperty("modelRotation", modelRotation); properties.setProperty("modelRotation", modelRotation);
properties.setProperty("animationURL", _animationURL);
if (_idSet) { if (_idSet) {
properties.setProperty("id", _id); 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(); _lastEdited = usecTimestampNow();
} }
@ -741,7 +884,14 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const {
modelItem.setModelRotation(_modelRotation); modelItem.setModelRotation(_modelRotation);
somethingChanged = true; somethingChanged = true;
} }
if (_animationURLChanged) {
modelItem.setAnimationURL(_animationURL);
somethingChanged = true;
qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL;
}
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;
if (wantDebug) { if (wantDebug) {
@ -761,6 +911,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
_shouldDie = modelItem.getShouldDie(); _shouldDie = modelItem.getShouldDie();
_modelURL = modelItem.getModelURL(); _modelURL = modelItem.getModelURL();
_modelRotation = modelItem.getModelRotation(); _modelRotation = modelItem.getModelRotation();
_animationURL = modelItem.getAnimationURL();
_id = modelItem.getID(); _id = modelItem.getID();
_idSet = true; _idSet = true;
@ -772,6 +923,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
_shouldDieChanged = false; _shouldDieChanged = false;
_modelURLChanged = false; _modelURLChanged = false;
_modelRotationChanged = false; _modelRotationChanged = false;
_animationURLChanged = false;
_defaultSettings = false; _defaultSettings = false;
} }

View file

@ -18,6 +18,7 @@
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <AnimationCache.h>
#include <CollisionInfo.h> #include <CollisionInfo.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <OctreePacketData.h> #include <OctreePacketData.h>
@ -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_RADIUS = 1;
const uint16_t MODEL_PACKET_CONTAINS_POSITION = 2; const uint16_t MODEL_PACKET_CONTAINS_POSITION = 2;
const uint16_t MODEL_PACKET_CONTAINS_COLOR = 4; const uint16_t MODEL_PACKET_CONTAINS_COLOR = 4;
const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 512; const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8;
const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 1024; const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16;
const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 2048; 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 MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE;
const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
const QString MODEL_DEFAULT_MODEL_URL(""); const QString MODEL_DEFAULT_MODEL_URL("");
const glm::quat MODEL_DEFAULT_MODEL_ROTATION; 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 /// 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 /// 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 QString& getModelURL() const { return _modelURL; }
const glm::quat& getModelRotation() const { return _modelRotation; } const glm::quat& getModelRotation() const { return _modelRotation; }
const QString& getAnimationURL() const { return _animationURL; }
quint64 getLastEdited() const { return _lastEdited; } quint64 getLastEdited() const { return _lastEdited; }
uint16_t getChangedBits() const; uint16_t getChangedBits() const;
@ -82,6 +86,7 @@ public:
// model related properties // model related properties
void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; }
void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = 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 /// used by ModelScriptingInterface to return ModelItemProperties for unknown models
void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; }
@ -97,6 +102,7 @@ private:
QString _modelURL; QString _modelURL;
glm::quat _modelRotation; glm::quat _modelRotation;
QString _animationURL;
uint32_t _id; uint32_t _id;
bool _idSet; bool _idSet;
@ -109,6 +115,7 @@ private:
bool _modelURLChanged; bool _modelURLChanged;
bool _modelRotationChanged; bool _modelRotationChanged;
bool _animationURLChanged;
bool _defaultSettings; bool _defaultSettings;
}; };
Q_DECLARE_METATYPE(ModelItemProperties); Q_DECLARE_METATYPE(ModelItemProperties);
@ -178,6 +185,8 @@ public:
bool hasModel() const { return !_modelURL.isEmpty(); } bool hasModel() const { return !_modelURL.isEmpty(); }
const QString& getModelURL() const { return _modelURL; } const QString& getModelURL() const { return _modelURL; }
const glm::quat& getModelRotation() const { return _modelRotation; } 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); } ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); }
ModelItemProperties getProperties() const; ModelItemProperties getProperties() const;
@ -196,6 +205,7 @@ public:
bool getShouldDie() const { return _shouldDie; } bool getShouldDie() const { return _shouldDie; }
uint32_t getCreatorTokenID() const { return _creatorTokenID; } uint32_t getCreatorTokenID() const { return _creatorTokenID; }
bool isNewlyCreated() const { return _newlyCreated; } bool isNewlyCreated() const { return _newlyCreated; }
bool isKnownID() const { return getID() != UNKNOWN_MODEL_ID; }
/// set position in domain scale units (0.0 - 1.0) /// set position in domain scale units (0.0 - 1.0)
void setPosition(const glm::vec3& value) { _position = value; } void setPosition(const glm::vec3& value) { _position = value; }
@ -215,6 +225,7 @@ public:
// model related properties // model related properties
void setModelURL(const QString& url) { _modelURL = url; } void setModelURL(const QString& url) { _modelURL = url; }
void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; }
void setAnimationURL(const QString& url) { _animationURL = url; }
void setProperties(const ModelItemProperties& properties); void setProperties(const ModelItemProperties& properties);
@ -239,6 +250,10 @@ public:
static uint32_t getNextCreatorTokenID(); static uint32_t getNextCreatorTokenID();
static void handleAddModelResponse(const QByteArray& packet); static void handleAddModelResponse(const QByteArray& packet);
void mapJoints(const QStringList& modelJointNames);
QVector<glm::quat> getAnimationFrame();
bool jointsMapped() const { return _jointMappingCompleted; };
protected: protected:
glm::vec3 _position; glm::vec3 _position;
rgbColor _color; rgbColor _color;
@ -256,10 +271,23 @@ protected:
quint64 _lastUpdated; quint64 _lastUpdated;
quint64 _lastEdited; 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<int> _jointMapping;
// used by the static interfaces for creator token ids // used by the static interfaces for creator token ids
static uint32_t _nextCreatorTokenID; static uint32_t _nextCreatorTokenID;
static std::map<uint32_t,uint32_t> _tokenIDsToIDs; static std::map<uint32_t,uint32_t> _tokenIDsToIDs;
static Animation* getAnimation(const QString& url);
static QMap<QString, AnimationPointer> _loadedAnimations;
static AnimationCache _animationCache;
}; };
#endif // hifi_ModelItem_h #endif // hifi_ModelItem_h

View file

@ -66,6 +66,8 @@ PacketVersion versionForPacketType(PacketType type) {
return 1; return 1;
case PacketTypeOctreeStats: case PacketTypeOctreeStats:
return 1; return 1;
case PacketTypeModelData:
return 1;
default: default:
return 0; return 0;
} }

View file

@ -25,6 +25,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
# link ZLIB and GnuTLS # link ZLIB and GnuTLS
find_package(ZLIB) find_package(ZLIB)

View file

@ -27,6 +27,7 @@ link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}")
link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}")
# link ZLIB # link ZLIB
find_package(ZLIB) find_package(ZLIB)

View file

@ -18,13 +18,12 @@
#include <QtCore/QUrl> #include <QtCore/QUrl>
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <AnimationCache.h>
#include <AudioScriptingInterface.h> #include <AudioScriptingInterface.h>
#include <VoxelsScriptingInterface.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <AvatarHashMap.h> #include <AvatarHashMap.h>
#include <VoxelsScriptingInterface.h>
#include "AnimationCache.h"
#include "AbstractControllerScriptingInterface.h" #include "AbstractControllerScriptingInterface.h"
#include "Quat.h" #include "Quat.h"
#include "ScriptUUID.h" #include "ScriptUUID.h"