mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 05:23:09 +02:00
Merge pull request #2856 from ZappoMan/modelserver
Animation Support in Models
This commit is contained in:
commit
186f7766fc
28 changed files with 715 additions and 50 deletions
|
@ -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}")
|
||||||
|
|
||||||
|
|
|
@ -250,6 +250,12 @@ void Agent::run() {
|
||||||
_particleViewer.init();
|
_particleViewer.init();
|
||||||
_scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree());
|
_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.setScriptContents(scriptContents);
|
||||||
_scriptEngine.run();
|
_scriptEngine.run();
|
||||||
setFinished(true);
|
setFinished(true);
|
||||||
|
|
124
examples/animatedModelExample.js
Normal file
124
examples/animatedModelExample.js
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
//
|
||||||
|
// 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 modelID = Models.addModel(originalProperties);
|
||||||
|
print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID);
|
||||||
|
|
||||||
|
var isPlaying = true;
|
||||||
|
var playPauseEveryWhile = 360;
|
||||||
|
var animationFPS = 30;
|
||||||
|
var adjustFPSEveryWhile = 120;
|
||||||
|
var resetFrameEveryWhile = 600;
|
||||||
|
|
||||||
|
function moveModel(deltaTime) {
|
||||||
|
var somethingChanged = false;
|
||||||
|
if (count % playPauseEveryWhile == 0) {
|
||||||
|
isPlaying = !isPlaying;
|
||||||
|
print("isPlaying=" + isPlaying);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % adjustFPSEveryWhile == 0) {
|
||||||
|
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);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % resetFrameEveryWhile == 0) {
|
||||||
|
resetFrame = true;
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (somethingChanged) {
|
||||||
|
var newProperties = {
|
||||||
|
animationIsPlaying: isPlaying,
|
||||||
|
animationFPS: animationFPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (resetFrame) {
|
||||||
|
print("resetting the frame!");
|
||||||
|
newProperties.animationFrameIndex = 0;
|
||||||
|
resetFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
|
@ -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,19 @@ function checkControllerSide(whichSide) {
|
||||||
modelRotation: palmRotation,
|
modelRotation: palmRotation,
|
||||||
modelURL: modelURLs[currentModelURL]
|
modelURL: modelURLs[currentModelURL]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (animationURLs[currentModelURL] !== "") {
|
||||||
|
properties.animationURL = animationURLs[currentModelURL];
|
||||||
|
properties.animationIsPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -128,6 +128,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
|
||||||
|
|
|
@ -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,21 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
|
|
||||||
// set the position
|
// set the position
|
||||||
model->setTranslation(position);
|
model->setTranslation(position);
|
||||||
|
|
||||||
|
// 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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -433,7 +433,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,8 +442,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 {
|
||||||
|
@ -576,6 +582,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];
|
||||||
|
|
|
@ -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();
|
||||||
|
|
37
libraries/animation/CMakeLists.txt
Normal file
37
libraries/animation/CMakeLists.txt
Normal file
|
@ -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)
|
|
@ -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) {
|
|
@ -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
|
|
@ -1398,6 +1398,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(),
|
||||||
|
|
|
@ -211,6 +211,8 @@ public:
|
||||||
Extents bindExtents;
|
Extents bindExtents;
|
||||||
Extents meshExtents;
|
Extents meshExtents;
|
||||||
|
|
||||||
|
float fstScaled;
|
||||||
|
|
||||||
QVector<FBXAnimationFrame> animationFrames;
|
QVector<FBXAnimationFrame> animationFrames;
|
||||||
|
|
||||||
QVector<FBXAttachment> attachments;
|
QVector<FBXAttachment> attachments;
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
|
@ -85,6 +85,15 @@ 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;
|
||||||
|
_animationIsPlaying = false;
|
||||||
|
_animationFrameIndex = 0.0f;
|
||||||
|
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
|
||||||
|
|
||||||
|
_jointMappingCompleted = false;
|
||||||
|
_lastAnimated = now;
|
||||||
|
|
||||||
setProperties(properties);
|
setProperties(properties);
|
||||||
}
|
}
|
||||||
|
@ -110,6 +119,14 @@ 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;
|
||||||
|
_animationIsPlaying = false;
|
||||||
|
_animationFrameIndex = 0.0f;
|
||||||
|
_animationFPS = MODEL_DEFAULT_ANIMATION_FPS;
|
||||||
|
_jointMappingCompleted = false;
|
||||||
|
_lastAnimated = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModelItem::appendModelData(OctreePacketData* packetData) const {
|
bool ModelItem::appendModelData(OctreePacketData* packetData) const {
|
||||||
|
@ -150,6 +167,31 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationIsPlaying
|
||||||
|
if (success) {
|
||||||
|
success = packetData->appendValue(getAnimationIsPlaying());
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationFrameIndex
|
||||||
|
if (success) {
|
||||||
|
success = packetData->appendValue(getAnimationFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationFPS
|
||||||
|
if (success) {
|
||||||
|
success = packetData->appendValue(getAnimationFPS());
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +208,7 @@ int ModelItem::expectedBytes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
||||||
|
|
||||||
int bytesRead = 0;
|
int bytesRead = 0;
|
||||||
if (bytesLeftToRead >= expectedBytes()) {
|
if (bytesLeftToRead >= expectedBytes()) {
|
||||||
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
|
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
|
||||||
|
@ -215,7 +258,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,13 +267,37 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT
|
||||||
dataAt += bytes;
|
dataAt += bytes;
|
||||||
bytesRead += bytes;
|
bytesRead += bytes;
|
||||||
|
|
||||||
//printf("ModelItem::readModelDataFromBuffer()... "); debugDump();
|
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;
|
||||||
|
|
||||||
|
// animationIsPlaying
|
||||||
|
memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying));
|
||||||
|
dataAt += sizeof(_animationIsPlaying);
|
||||||
|
bytesRead += sizeof(_animationIsPlaying);
|
||||||
|
|
||||||
|
// animationFrameIndex
|
||||||
|
memcpy(&_animationFrameIndex, dataAt, sizeof(_animationFrameIndex));
|
||||||
|
dataAt += sizeof(_animationFrameIndex);
|
||||||
|
bytesRead += sizeof(_animationFrameIndex);
|
||||||
|
|
||||||
|
// animationFPS
|
||||||
|
memcpy(&_animationFPS, dataAt, sizeof(_animationFPS));
|
||||||
|
dataAt += sizeof(_animationFPS);
|
||||||
|
bytesRead += sizeof(_animationFPS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ModelTree* tree, bool& valid) {
|
ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ModelTree* tree, bool& valid) {
|
||||||
|
|
||||||
ModelItem newModelItem; // id and _lastUpdated will get set here...
|
ModelItem newModelItem; // id and _lastUpdated will get set here...
|
||||||
const unsigned char* dataAt = data;
|
const unsigned char* dataAt = data;
|
||||||
processedBytes = 0;
|
processedBytes = 0;
|
||||||
|
@ -340,12 +407,52 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int&
|
||||||
}
|
}
|
||||||
|
|
||||||
// modelRotation
|
// 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);
|
int bytes = unpackOrientationQuatFromBytes(dataAt, newModelItem._modelRotation);
|
||||||
dataAt += bytes;
|
dataAt += bytes;
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationIsPlaying
|
||||||
|
if (isNewModelItem || ((packetContainsBits &
|
||||||
|
MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) {
|
||||||
|
|
||||||
|
memcpy(&newModelItem._animationIsPlaying, dataAt, sizeof(newModelItem._animationIsPlaying));
|
||||||
|
dataAt += sizeof(newModelItem._animationIsPlaying);
|
||||||
|
processedBytes += sizeof(newModelItem._animationIsPlaying);
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationFrameIndex
|
||||||
|
if (isNewModelItem || ((packetContainsBits &
|
||||||
|
MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) {
|
||||||
|
|
||||||
|
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;
|
const bool wantDebugging = false;
|
||||||
if (wantDebugging) {
|
if (wantDebugging) {
|
||||||
qDebug("ModelItem::fromEditPacket()...");
|
qDebug("ModelItem::fromEditPacket()...");
|
||||||
|
@ -363,6 +470,7 @@ void ModelItem::debugDump() const {
|
||||||
qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z);
|
qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z);
|
||||||
qDebug(" radius:%f", getRadius());
|
qDebug(" radius:%f", getRadius());
|
||||||
qDebug(" color:%d,%d,%d", _color[0], _color[1], _color[2]);
|
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,
|
bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id, const ModelItemProperties& properties,
|
||||||
|
@ -476,6 +584,47 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationIsPlaying
|
||||||
|
if (isNewModelItem || ((packetContainsBits &
|
||||||
|
MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) {
|
||||||
|
|
||||||
|
bool animationIsPlaying = properties.getAnimationIsPlaying();
|
||||||
|
memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying));
|
||||||
|
copyAt += sizeof(animationIsPlaying);
|
||||||
|
sizeOut += sizeof(animationIsPlaying);
|
||||||
|
}
|
||||||
|
|
||||||
|
// animationFrameIndex
|
||||||
|
if (isNewModelItem || ((packetContainsBits &
|
||||||
|
MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) {
|
||||||
|
|
||||||
|
float animationFrameIndex = properties.getAnimationFrameIndex();
|
||||||
|
memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex));
|
||||||
|
copyAt += sizeof(animationFrameIndex);
|
||||||
|
sizeOut += sizeof(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);
|
||||||
|
}
|
||||||
|
|
||||||
bool wantDebugging = false;
|
bool wantDebugging = false;
|
||||||
if (wantDebugging) {
|
if (wantDebugging) {
|
||||||
qDebug("encodeModelItemEditMessageDetails()....");
|
qDebug("encodeModelItemEditMessageDetails()....");
|
||||||
|
@ -521,9 +670,106 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelItem::update(const quint64& now) {
|
|
||||||
_lastUpdated = now;
|
QMap<QString, AnimationPointer> 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;
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
Animation* myAnimation = getAnimation(_animationURL);
|
||||||
|
QVector<FBXAnimationFrame> frames = myAnimation->getFrames();
|
||||||
|
int animationFrameIndex = (int)std::floor(_animationFrameIndex) % frames.size();
|
||||||
|
QVector<glm::quat> rotations = frames[animationFrameIndex].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& updateTime) {
|
||||||
|
_lastUpdated = updateTime;
|
||||||
setShouldDie(getShouldDie());
|
setShouldDie(getShouldDie());
|
||||||
|
|
||||||
|
quint64 now = usecTimestampNow();
|
||||||
|
|
||||||
|
// only advance the frame index if we're playing
|
||||||
|
if (getAnimationIsPlaying()) {
|
||||||
|
|
||||||
|
float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND;
|
||||||
|
|
||||||
|
const bool wantDebugging = false;
|
||||||
|
if (wantDebugging) {
|
||||||
|
qDebug() << "ModelItem::update() now=" << now;
|
||||||
|
qDebug() << " updateTime=" << updateTime;
|
||||||
|
qDebug() << " _lastAnimated=" << _lastAnimated;
|
||||||
|
qDebug() << " deltaTime=" << deltaTime;
|
||||||
|
}
|
||||||
|
_lastAnimated = now;
|
||||||
|
_animationFrameIndex += deltaTime * _animationFPS;
|
||||||
|
|
||||||
|
if (wantDebugging) {
|
||||||
|
qDebug() << " _animationFrameIndex=" << _animationFrameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_lastAnimated = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelItem::copyChangedProperties(const ModelItem& other) {
|
void ModelItem::copyChangedProperties(const ModelItem& other) {
|
||||||
|
@ -547,6 +793,10 @@ ModelItemProperties::ModelItemProperties() :
|
||||||
_shouldDie(false),
|
_shouldDie(false),
|
||||||
_modelURL(""),
|
_modelURL(""),
|
||||||
_modelRotation(MODEL_DEFAULT_MODEL_ROTATION),
|
_modelRotation(MODEL_DEFAULT_MODEL_ROTATION),
|
||||||
|
_animationURL(""),
|
||||||
|
_animationIsPlaying(false),
|
||||||
|
_animationFrameIndex(0.0),
|
||||||
|
_animationFPS(MODEL_DEFAULT_ANIMATION_FPS),
|
||||||
|
|
||||||
_id(UNKNOWN_MODEL_ID),
|
_id(UNKNOWN_MODEL_ID),
|
||||||
_idSet(false),
|
_idSet(false),
|
||||||
|
@ -558,6 +808,10 @@ ModelItemProperties::ModelItemProperties() :
|
||||||
_shouldDieChanged(false),
|
_shouldDieChanged(false),
|
||||||
_modelURLChanged(false),
|
_modelURLChanged(false),
|
||||||
_modelRotationChanged(false),
|
_modelRotationChanged(false),
|
||||||
|
_animationURLChanged(false),
|
||||||
|
_animationIsPlayingChanged(false),
|
||||||
|
_animationFrameIndexChanged(false),
|
||||||
|
_animationFPSChanged(false),
|
||||||
_defaultSettings(true)
|
_defaultSettings(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -589,6 +843,22 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_animationIsPlayingChanged) {
|
||||||
|
changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_animationFrameIndexChanged) {
|
||||||
|
changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_animationFPSChanged) {
|
||||||
|
changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FPS;
|
||||||
|
}
|
||||||
|
|
||||||
return changedBits;
|
return changedBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,6 +881,10 @@ 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);
|
||||||
|
properties.setProperty("animationIsPlaying", _animationIsPlaying);
|
||||||
|
properties.setProperty("animationFrameIndex", _animationFrameIndex);
|
||||||
|
properties.setProperty("animationFPS", _animationFPS);
|
||||||
|
|
||||||
if (_idSet) {
|
if (_idSet) {
|
||||||
properties.setProperty("id", _id);
|
properties.setProperty("id", _id);
|
||||||
|
@ -707,6 +981,46 @@ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue animationIsPlaying = object.property("animationIsPlaying");
|
||||||
|
if (animationIsPlaying.isValid()) {
|
||||||
|
bool newIsAnimationPlaying;
|
||||||
|
newIsAnimationPlaying = animationIsPlaying.toVariant().toBool();
|
||||||
|
if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) {
|
||||||
|
_animationIsPlaying = newIsAnimationPlaying;
|
||||||
|
_animationIsPlayingChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue animationFrameIndex = object.property("animationFrameIndex");
|
||||||
|
if (animationFrameIndex.isValid()) {
|
||||||
|
float newFrameIndex;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_lastEdited = usecTimestampNow();
|
_lastEdited = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -741,7 +1055,27 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const {
|
||||||
modelItem.setModelRotation(_modelRotation);
|
modelItem.setModelRotation(_modelRotation);
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_animationURLChanged) {
|
||||||
|
modelItem.setAnimationURL(_animationURL);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_animationIsPlayingChanged) {
|
||||||
|
modelItem.setAnimationIsPlaying(_animationIsPlaying);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_animationFrameIndexChanged) {
|
||||||
|
modelItem.setAnimationFrameIndex(_animationFrameIndex);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_animationFPSChanged) {
|
||||||
|
modelItem.setAnimationFPS(_animationFPS);
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (somethingChanged) {
|
if (somethingChanged) {
|
||||||
bool wantDebug = false;
|
bool wantDebug = false;
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
|
@ -761,6 +1095,10 @@ 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();
|
||||||
|
_animationIsPlaying = modelItem.getAnimationIsPlaying();
|
||||||
|
_animationFrameIndex = modelItem.getAnimationFrameIndex();
|
||||||
|
_animationFPS = modelItem.getAnimationFPS();
|
||||||
|
|
||||||
_id = modelItem.getID();
|
_id = modelItem.getID();
|
||||||
_idSet = true;
|
_idSet = true;
|
||||||
|
@ -772,6 +1110,10 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) {
|
||||||
_shouldDieChanged = false;
|
_shouldDieChanged = false;
|
||||||
_modelURLChanged = false;
|
_modelURLChanged = false;
|
||||||
_modelRotationChanged = false;
|
_modelRotationChanged = false;
|
||||||
|
_animationURLChanged = false;
|
||||||
|
_animationIsPlayingChanged = false;
|
||||||
|
_animationFrameIndexChanged = false;
|
||||||
|
_animationFPSChanged = false;
|
||||||
_defaultSettings = false;
|
_defaultSettings = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,22 @@ 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 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 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("");
|
||||||
|
const float MODEL_DEFAULT_ANIMATION_FPS = 30.0f;
|
||||||
|
|
||||||
|
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
|
/// 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 +78,10 @@ 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; }
|
||||||
|
float getAnimationFrameIndex() const { return _animationFrameIndex; }
|
||||||
|
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
|
||||||
|
float getAnimationFPS() const { return _animationFPS; }
|
||||||
|
|
||||||
quint64 getLastEdited() const { return _lastEdited; }
|
quint64 getLastEdited() const { return _lastEdited; }
|
||||||
uint16_t getChangedBits() const;
|
uint16_t getChangedBits() const;
|
||||||
|
@ -82,6 +95,10 @@ 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; }
|
||||||
|
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
|
/// 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 +114,10 @@ private:
|
||||||
|
|
||||||
QString _modelURL;
|
QString _modelURL;
|
||||||
glm::quat _modelRotation;
|
glm::quat _modelRotation;
|
||||||
|
QString _animationURL;
|
||||||
|
bool _animationIsPlaying;
|
||||||
|
float _animationFrameIndex;
|
||||||
|
float _animationFPS;
|
||||||
|
|
||||||
uint32_t _id;
|
uint32_t _id;
|
||||||
bool _idSet;
|
bool _idSet;
|
||||||
|
@ -109,6 +130,10 @@ private:
|
||||||
|
|
||||||
bool _modelURLChanged;
|
bool _modelURLChanged;
|
||||||
bool _modelRotationChanged;
|
bool _modelRotationChanged;
|
||||||
|
bool _animationURLChanged;
|
||||||
|
bool _animationIsPlayingChanged;
|
||||||
|
bool _animationFrameIndexChanged;
|
||||||
|
bool _animationFPSChanged;
|
||||||
bool _defaultSettings;
|
bool _defaultSettings;
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(ModelItemProperties);
|
Q_DECLARE_METATYPE(ModelItemProperties);
|
||||||
|
@ -178,6 +203,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 +223,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 +243,10 @@ 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 setAnimationFrameIndex(float value) { _animationFrameIndex = value; }
|
||||||
|
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; }
|
||||||
|
void setAnimationFPS(float value) { _animationFPS = value; }
|
||||||
|
|
||||||
void setProperties(const ModelItemProperties& properties);
|
void setProperties(const ModelItemProperties& properties);
|
||||||
|
|
||||||
|
@ -239,6 +271,16 @@ 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; }
|
||||||
|
|
||||||
|
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
|
||||||
|
float getAnimationFrameIndex() const { return _animationFrameIndex; }
|
||||||
|
float getAnimationFPS() const { return _animationFPS; }
|
||||||
|
|
||||||
|
static void cleanupLoadedAnimations();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
glm::vec3 _position;
|
glm::vec3 _position;
|
||||||
rgbColor _color;
|
rgbColor _color;
|
||||||
|
@ -256,10 +298,26 @@ protected:
|
||||||
|
|
||||||
quint64 _lastUpdated;
|
quint64 _lastUpdated;
|
||||||
quint64 _lastEdited;
|
quint64 _lastEdited;
|
||||||
|
quint64 _lastAnimated;
|
||||||
|
|
||||||
|
QString _animationURL;
|
||||||
|
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<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
|
||||||
|
|
|
@ -36,6 +36,7 @@ public:
|
||||||
// own definition. Implement these to allow your octree based server to support editing
|
// own definition. Implement these to allow your octree based server to support editing
|
||||||
virtual bool getWantSVOfileVersions() const { return true; }
|
virtual bool getWantSVOfileVersions() const { return true; }
|
||||||
virtual PacketType expectedDataPacketType() const { return PacketTypeModelData; }
|
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 bool handlesEditPacketType(PacketType packetType) const;
|
||||||
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
|
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
|
||||||
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode);
|
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode);
|
||||||
|
|
|
@ -106,6 +106,10 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) {
|
||||||
QList<ModelItem>::iterator modelItr = _modelItems->begin();
|
QList<ModelItem>::iterator modelItr = _modelItems->begin();
|
||||||
while(modelItr != _modelItems->end()) {
|
while(modelItr != _modelItems->end()) {
|
||||||
ModelItem& model = (*modelItr);
|
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);
|
model.update(_lastChanged);
|
||||||
|
|
||||||
// If the model wants to die, or if it's left our bounding box, then move it
|
// If the model wants to die, or if it's left our bounding box, then move it
|
||||||
|
@ -324,7 +328,7 @@ int ModelTreeElement::readElementDataFromBuffer(const unsigned char* data, int b
|
||||||
dataAt += sizeof(numberOfModels);
|
dataAt += sizeof(numberOfModels);
|
||||||
bytesLeftToRead -= (int)sizeof(numberOfModels);
|
bytesLeftToRead -= (int)sizeof(numberOfModels);
|
||||||
bytesRead += sizeof(numberOfModels);
|
bytesRead += sizeof(numberOfModels);
|
||||||
|
|
||||||
if (bytesLeftToRead >= (int)(numberOfModels * expectedBytesPerModel)) {
|
if (bytesLeftToRead >= (int)(numberOfModels * expectedBytesPerModel)) {
|
||||||
for (uint16_t i = 0; i < numberOfModels; i++) {
|
for (uint16_t i = 0; i < numberOfModels; i++) {
|
||||||
ModelItem tempModel;
|
ModelItem tempModel;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -335,10 +335,8 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long
|
||||||
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
|
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
|
||||||
int theseBytesRead = 0;
|
int theseBytesRead = 0;
|
||||||
theseBytesRead += octalCodeBytes;
|
theseBytesRead += octalCodeBytes;
|
||||||
|
|
||||||
theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes,
|
theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes,
|
||||||
bufferSizeBytes - (bytesRead + octalCodeBytes), args);
|
bufferSizeBytes - (bytesRead + octalCodeBytes), args);
|
||||||
|
|
||||||
// skip bitstream to new startPoint
|
// skip bitstream to new startPoint
|
||||||
bitstreamAt += theseBytesRead;
|
bitstreamAt += theseBytesRead;
|
||||||
bytesRead += theseBytesRead;
|
bytesRead += theseBytesRead;
|
||||||
|
@ -1556,6 +1554,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
||||||
|
|
||||||
bool Octree::readFromSVOFile(const char* fileName) {
|
bool Octree::readFromSVOFile(const char* fileName) {
|
||||||
bool fileOk = false;
|
bool fileOk = false;
|
||||||
|
PacketVersion gotVersion = 0;
|
||||||
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
|
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
|
||||||
if(file.is_open()) {
|
if(file.is_open()) {
|
||||||
emit importSize(1.0f, 1.0f, 1.0f);
|
emit importSize(1.0f, 1.0f, 1.0f);
|
||||||
|
@ -1586,14 +1585,16 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
||||||
if (gotType == expectedType) {
|
if (gotType == expectedType) {
|
||||||
dataAt += sizeof(expectedType);
|
dataAt += sizeof(expectedType);
|
||||||
dataLength -= sizeof(expectedType);
|
dataLength -= sizeof(expectedType);
|
||||||
PacketVersion expectedVersion = versionForPacketType(expectedType);
|
gotVersion = *dataAt;
|
||||||
PacketVersion gotVersion = *dataAt;
|
if (canProcessVersion(gotVersion)) {
|
||||||
if (gotVersion == expectedVersion) {
|
dataAt += sizeof(gotVersion);
|
||||||
dataAt += sizeof(expectedVersion);
|
dataLength -= sizeof(gotVersion);
|
||||||
dataLength -= sizeof(expectedVersion);
|
|
||||||
fileOk = true;
|
fileOk = true;
|
||||||
|
qDebug("SVO file version match. Expected: %d Got: %d",
|
||||||
|
versionForPacketType(expectedDataPacketType()), gotVersion);
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
qDebug("SVO file type mismatch. Expected: %c Got: %c", expectedType, gotType);
|
qDebug("SVO file type mismatch. Expected: %c Got: %c", expectedType, gotType);
|
||||||
|
@ -1602,7 +1603,8 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
||||||
fileOk = true; // assume the file is ok
|
fileOk = true; // assume the file is ok
|
||||||
}
|
}
|
||||||
if (fileOk) {
|
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);
|
readBitstreamToTree(dataAt, dataLength, args);
|
||||||
}
|
}
|
||||||
delete[] entireFile;
|
delete[] entireFile;
|
||||||
|
@ -1615,7 +1617,6 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
|
void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
|
||||||
|
|
||||||
std::ofstream file(fileName, std::ios::out|std::ios::binary);
|
std::ofstream file(fileName, std::ios::out|std::ios::binary);
|
||||||
|
|
||||||
if(file.is_open()) {
|
if(file.is_open()) {
|
||||||
|
@ -1638,13 +1639,12 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
|
||||||
nodeBag.insert(_rootElement);
|
nodeBag.insert(_rootElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OctreePacketData packetData;
|
OctreePacketData packetData;
|
||||||
int bytesWritten = 0;
|
int bytesWritten = 0;
|
||||||
bool lastPacketWritten = false;
|
bool lastPacketWritten = false;
|
||||||
|
|
||||||
while (!nodeBag.isEmpty()) {
|
while (!nodeBag.isEmpty()) {
|
||||||
OctreeElement* subTree = nodeBag.extract();
|
OctreeElement* subTree = nodeBag.extract();
|
||||||
|
|
||||||
lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention
|
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);
|
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
|
||||||
bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params);
|
bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params);
|
||||||
|
@ -1666,7 +1666,6 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
|
||||||
if (!lastPacketWritten) {
|
if (!lastPacketWritten) {
|
||||||
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
|
file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,7 @@ public:
|
||||||
QUuid sourceUUID;
|
QUuid sourceUUID;
|
||||||
SharedNodePointer sourceNode;
|
SharedNodePointer sourceNode;
|
||||||
bool wantImportProgress;
|
bool wantImportProgress;
|
||||||
|
PacketVersion bitstreamVersion;
|
||||||
|
|
||||||
ReadBitstreamToTreeParams(
|
ReadBitstreamToTreeParams(
|
||||||
bool includeColor = WANT_COLOR,
|
bool includeColor = WANT_COLOR,
|
||||||
|
@ -177,13 +178,15 @@ public:
|
||||||
OctreeElement* destinationElement = NULL,
|
OctreeElement* destinationElement = NULL,
|
||||||
QUuid sourceUUID = QUuid(),
|
QUuid sourceUUID = QUuid(),
|
||||||
SharedNodePointer sourceNode = SharedNodePointer(),
|
SharedNodePointer sourceNode = SharedNodePointer(),
|
||||||
bool wantImportProgress = false) :
|
bool wantImportProgress = false,
|
||||||
|
PacketVersion bitstreamVersion = 0) :
|
||||||
includeColor(includeColor),
|
includeColor(includeColor),
|
||||||
includeExistsBits(includeExistsBits),
|
includeExistsBits(includeExistsBits),
|
||||||
destinationElement(destinationElement),
|
destinationElement(destinationElement),
|
||||||
sourceUUID(sourceUUID),
|
sourceUUID(sourceUUID),
|
||||||
sourceNode(sourceNode),
|
sourceNode(sourceNode),
|
||||||
wantImportProgress(wantImportProgress)
|
wantImportProgress(wantImportProgress),
|
||||||
|
bitstreamVersion(bitstreamVersion)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -200,6 +203,9 @@ public:
|
||||||
// own definition. Implement these to allow your octree based server to support editing
|
// own definition. Implement these to allow your octree based server to support editing
|
||||||
virtual bool getWantSVOfileVersions() const { return false; }
|
virtual bool getWantSVOfileVersions() const { return false; }
|
||||||
virtual PacketType expectedDataPacketType() const { return PacketTypeUnknown; }
|
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 bool handlesEditPacketType(PacketType packetType) const { return false; }
|
||||||
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
|
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
|
||||||
const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; }
|
const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; }
|
||||||
|
@ -303,6 +309,7 @@ public:
|
||||||
|
|
||||||
bool getIsViewing() const { return _isViewing; }
|
bool getIsViewing() const { return _isViewing; }
|
||||||
void setIsViewing(bool isViewing) { _isViewing = isViewing; }
|
void setIsViewing(bool isViewing) { _isViewing = isViewing; }
|
||||||
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void importSize(float x, float y, float z);
|
void importSize(float x, float y, float z);
|
||||||
|
|
|
@ -64,6 +64,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
|
||||||
unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray);
|
unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray);
|
||||||
QUuid sourceUUID = uuidFromPacketHeader(dataByteArray);
|
QUuid sourceUUID = uuidFromPacketHeader(dataByteArray);
|
||||||
PacketType expectedType = getExpectedPacketType();
|
PacketType expectedType = getExpectedPacketType();
|
||||||
|
PacketVersion expectedVersion = _tree->expectedVersion(); // TODO: would be better to read this from the packet!
|
||||||
|
|
||||||
if(command == expectedType) {
|
if(command == expectedType) {
|
||||||
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails);
|
PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails);
|
||||||
|
@ -115,7 +116,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar
|
||||||
if (sectionLength) {
|
if (sectionLength) {
|
||||||
// ask the VoxelTree to read the bitstream into the tree
|
// ask the VoxelTree to read the bitstream into the tree
|
||||||
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
|
ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL,
|
||||||
sourceUUID, sourceNode);
|
sourceUUID, sourceNode, false, expectedVersion);
|
||||||
_tree->lockForWrite();
|
_tree->lockForWrite();
|
||||||
OctreePacketData packetData(packetIsCompressed);
|
OctreePacketData packetData(packetIsCompressed);
|
||||||
packetData.loadFinalizedContent(dataAt, sectionLength);
|
packetData.loadFinalizedContent(dataAt, sectionLength);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue